Code

empty messages are passed onto all listeners in pedro, gui ignores such messages
[inkscape.git] / src / jabber_whiteboard / message-utilities.cpp
1 /**
2  * Message generation utilities
3  * 
4  * Authors:
5  * David Yip <yipdw@rose-hulman.edu>
6  * Jonas Collaros, Stephen Montgomery
7  *
8  * Copyright (c) 2004-2005 Authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #include <glibmm/i18n.h>
15 #include "util/share.h"
16 #include "util/list.h"
18 #include "xml/node.h"
19 #include "xml/attribute-record.h"
20 #include "xml/repr.h"
22 #include "jabber_whiteboard/defines.h"
23 #include "jabber_whiteboard/node-utilities.h"
24 #include "jabber_whiteboard/message-utilities.h"
25 #include "jabber_whiteboard/node-tracker.h"
27 #include <iostream>
29 namespace Inkscape {
31 namespace Whiteboard {
33 // This method can be instructed to not build a message string but only collect nodes that _would_ be transmitted
34 // and subsequently added to the tracker.  This can be useful in the case where an Inkboard user is the only one
35 // in a chatroom and therefore needs to fill out the node tracker, but does not need to build the message string.
36 // This can be controlled with the only_collect_nodes flag, which will only create pointers to new XML::Nodes 
37 // in the maps referenced by newidsbuf and newnodesbuf.  Passing NULL as the message buffer has the same effect.
38 //
39 // only_collect_nodes defaults to false because most invocations of this method also use the message string.
40 void
41 MessageUtilities::newObjectMessage(Glib::ustring &msgbuf, 
42                        KeyNodeTable& newnodesbuf,
43                        NewChildObjectMessageList& childmsgbuf, 
44                        XMLNodeTracker* xmt, 
45                        Inkscape::XML::Node const* node,
46                        bool only_collect_nodes,
47                        bool collect_children)
48 {
49         // Initialize pointers
50         Glib::ustring id, refid, parentid;
52         gchar const* name = NULL;
53         XML::Node* parent = NULL;
54         XML::Node* ref = NULL;
56         bool only_add_children = false;
58         //g_log(NULL, G_LOG_LEVEL_DEBUG, "newObjectMessage: processing node %p of type %s", node, NodeUtilities::nodeTypeToString(*node).data());
60         if (node != NULL) {
61                 parent = sp_repr_parent(node);
62                 if (parent != NULL) {
63                         //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for parent node %p (on node %p)", parent, node);
64                         parentid = NodeUtilities::findNodeID(*parent, xmt, newnodesbuf);
65                         if (parentid.empty()) {
66                                 g_warning("Parent %p is not being tracked, creating new ID", parent);
67                                 parentid = xmt->generateKey();
68                                 newnodesbuf.put(parentid, parent);
69                         }
71                         if ( node != parent->firstChild() && parent != NULL ) {
72                                 ref = parent->firstChild();
73                                 while (ref->next() != node) {
74                                         ref = ref->next();
75                                 }
76                         }       
77                 }
79                 if (ref != NULL) {
80                         //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for ref node %p (on %p)", ref, node);
81                         refid = NodeUtilities::findNodeID(*ref, xmt, newnodesbuf);
82                         if (refid.empty() && ref != NULL) {
83                                 g_warning("Ref %p is not being tracked, creating new ID", ref);
84                                 refid = xmt->generateKey();
85                                 newnodesbuf.put(refid, ref);
86                         }
87                 }
89                 name = static_cast< gchar const* >(node->name());
90         }
92         // Generate an id for this object and append it onto the list, if 
93         // it's not already in the tracker
94         if (!xmt->isSpecialNode(node->name())) {
95                 if (!xmt->isTracking(*node)) {
96                         id = xmt->generateKey();        
97                         //g_log(NULL, G_LOG_LEVEL_DEBUG, "Inserting %p with id %s", node, id.c_str());
98                         newnodesbuf.put(id, node);
99                 } else {
100                         id = xmt->get(*node);
101                 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Found id %s for node %p; not inserting into new nodes buffers.", id.c_str(), node);
102                 }
103         } else {
104                 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Processing special node; not generating key");
105                 id = xmt->get(*node);
106                 if (id.empty()) {
107                         g_warning("Node %p (name %s) is a special node, but it could not be found in the node tracker (possible unexpected duplicate?)  Generating unique ID anyway.", node, node->name());
108                         id = xmt->generateKey();
109                         newnodesbuf.put(id, node);
110                 }
111                 only_add_children = true;
112         }
114         // If we're only adding children (i.e. this is a special node)
115         // don't process the given node.
116         if( !only_add_children && !id.empty() && msgbuf != NULL && !only_collect_nodes ) {
117                 // <MESSAGE_NEWOBJ>
118                 msgbuf = msgbuf + "<" + MESSAGE_NEWOBJ + ">";
120                 // <MESSAGE_PARENT>
121                 msgbuf = msgbuf + "<" + MESSAGE_PARENT + ">";
123                 if(!parentid.empty()) {
124                         msgbuf += parentid;
125                 }
127                 // </MESSAGE_NEWOBJ><MESSAGE_CHILD>id</MESSAGE_CHILD>
128                 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
130                 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
131                 msgbuf += id;
133                 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
135                 if(!refid.empty()) {
136                         // <MESSAGE_REF>refid</MESSAGE_REF>
137                         msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
138                         
139                         msgbuf += refid;
141                         msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
142                 }
144                 // <MESSAGE_NODETYPE>*node.type()</MESSAGE_NODETYPE>
145                 msgbuf = msgbuf + "<" + MESSAGE_NODETYPE + ">" + NodeUtilities::nodeTypeToString(*node);
146                 msgbuf = msgbuf + "</" + MESSAGE_NODETYPE + ">";
148                 if (node->content() != NULL) {
149                         // <MESSAGE_CONTENT>node->content()</MESSAGE_CONTENT>
150                         msgbuf = msgbuf + "<" + MESSAGE_CONTENT + ">" + node->content();
151                         msgbuf = msgbuf + "</" + MESSAGE_CONTENT + ">";
152                 }
154                 // <MESSAGE_NAME>name</MESSAGE_NAME>
155                 msgbuf = msgbuf + "<" + MESSAGE_NAME + ">";
157                 if( name != NULL )
158                         msgbuf += name;
160                 msgbuf = msgbuf + "</" + MESSAGE_NAME + ">";
162                 // </MESSAGE_NEWOBJ>
163                 msgbuf = msgbuf + "</" + MESSAGE_NEWOBJ + ">";
164         } else if (id.empty()) {
165                 // if ID is NULL, then we have a real problem -- we were not able to find a key
166                 // nor generate one.  The only thing we can really do here is abort, since we have
167                 // no way to let the other client(s) uniquely identify this object.
168                 g_warning(_("ID for new object is NULL even after generation and lookup attempts: the new object will NOT be sent, nor will any of its child objects!"));
169                 return;
170         } else {
172         }
174         //g_log(NULL, G_LOG_LEVEL_DEBUG, "Generated message");
176         if (!only_collect_nodes && msgbuf != NULL && !id.empty()) {
177                 // Collect new object's attributes and append them onto the msgbuf
178                 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = node->attributeList();
180                 for(; attrlist; attrlist++) {
181                         MessageUtilities::objectChangeMessage(msgbuf,
182                          xmt, id, g_quark_to_string(attrlist->key),
183                           NULL, attrlist->value, false);
184                 }
185         }
187         if (!only_collect_nodes)
188                 childmsgbuf.push_back(msgbuf);
190         if (!id.empty() && collect_children) {
191                 Glib::ustring childbuf;
192                 // Collect any child objects of this new object
193                 for ( Inkscape::XML::Node const *child = node->firstChild(); child != NULL; child = child->next() ) {
194                         childbuf.clear();
195                         MessageUtilities::newObjectMessage(childbuf,
196                         newnodesbuf, childmsgbuf, xmt, child, only_collect_nodes);
197                         if (!only_collect_nodes) {
198                                 // we're recursing down the tree, so we're picking up child nodes first
199                                 // and parents afterwards
200 //                              childmsgbuf.push_front(childbuf);
201                         }
203                 }
204         }
207 void
208 MessageUtilities::objectChangeMessage(Glib::ustring &msgbuf, 
209                                     XMLNodeTracker* xmt, 
210                                     const Glib::ustring &id, 
211                                     gchar const* key, 
212                                     gchar const* oldval, 
213                                     gchar const* newval, 
214                                     bool is_interactive)
216     // Construct message
218     // <MESSAGE_CHANGE><MESSAGE_ID>id</MESSAGE_ID>
219     msgbuf = msgbuf + "<" + MESSAGE_CHANGE + ">";
220     msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
221     msgbuf += id;
222     msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
224     // <MESSAGE_KEY>key</MESSAGE_KEY>
225     msgbuf = msgbuf + "<" + MESSAGE_KEY + ">";
226     if (key != NULL) {
227             msgbuf += key;
228     }
229     msgbuf = msgbuf + "</" + MESSAGE_KEY + ">";
231     // <MESSAGE_OLDVAL>oldval</MESSAGE_OLDVAL>
232     msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
233     if (oldval != NULL) {
234             msgbuf += oldval;
235     }
236     msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
238     // <MESSAGE_NEWVAL>newval</MESSAGE_NEWVAL>
239     msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
240     if (newval != NULL) {
241             msgbuf += newval;
242     }
243     msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
245     // <MESSAGE_ISINTERACTIVE>is_interactive</MESSAGE_ISINTERACTIVE>
246     msgbuf = msgbuf + "<" + MESSAGE_ISINTERACTIVE + ">";
247     if (is_interactive) {
248             msgbuf += "true";
249     } else {
250             msgbuf += "false";
251     }
252     msgbuf = msgbuf + "</" + MESSAGE_ISINTERACTIVE + ">";
254     // </MESSAGE_CHANGE>
255     msgbuf = msgbuf + "</" + MESSAGE_CHANGE + ">";
258 void
259 MessageUtilities::objectDeleteMessage(Glib::ustring &msgbuf, 
260                             XMLNodeTracker* xmt,
261                             Inkscape::XML::Node const& parent,
262                             Inkscape::XML::Node const& child,
263                             Inkscape::XML::Node const* prev)
265     /*
266     gchar const* parentid = NULL;
267     gchar const* previd = NULL;
268     gchar const* childid = NULL;
270     childid = child.attribute("id");
271     parentid = parent.attribute("id");
272     if (prev != NULL) {
273             previd = prev->attribute("id");
274     }*/
276     Glib::ustring parentid, previd, childid;
278     childid = xmt->get(child);
279     parentid = xmt->get(parent);
280     previd = xmt->get(*prev);
282     if (childid.empty())
283         return;
284             
285  
286      // <MESSAGE_DELETE><MESSAGE_PARENT>parentid</MESSAGE_PARENT>
287      msgbuf = msgbuf + "<" + MESSAGE_DELETE + ">" + "<" + MESSAGE_PARENT + ">";
288      if (!parentid.empty()) {
289              msgbuf += parentid;
290      }
291      msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
293      // <MESSAGE_CHILD>childid</MESSAGE_CHILD>
294      msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
295      if (!childid.empty()) {
296              msgbuf += childid;
297      }
298      msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
300      // <MESSAGE_REF>previd</MESSAGE_REF>
301      msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
302      if (!previd.empty()) {
303              msgbuf += previd;
304      }
305      msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
307      // </MESSAGE_DELETE>
308      msgbuf = msgbuf + "</" + MESSAGE_DELETE + ">";
311 void
312 MessageUtilities::contentChangeMessage(Glib::ustring& msgbuf, 
313                        const Glib::ustring &nodeid, 
314                        Util::ptr_shared<char> old_value,
315                        Util::ptr_shared<char> new_value)
317     if (nodeid.empty())
318         return;
319         
320      // <MESSAGE_NODECONTENT>
321      msgbuf = msgbuf + "<" + MESSAGE_NODECONTENT + ">";
323      // <MESSAGE_ID>nodeid</MESSAGE_ID>
324      msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
325      msgbuf += nodeid;
326      msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
328      // <MESSAGE_OLDVAL>old_value</MESSAGE_OLDVAL>
329      msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
330      msgbuf += old_value.pointer();
331      msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
333      // <MESSAGE_NEWVAL>new_value</MESSAGE_NEWVAL>
334      msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
335      msgbuf += new_value.pointer();
336      msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
338      // </MESSAGE_NODECONTENT>
339      msgbuf = msgbuf + "</" + MESSAGE_NODECONTENT + ">";
342 void
343 MessageUtilities::childOrderChangeMessage(Glib::ustring& msgbuf, 
344                           const Glib::ustring &childid, 
345                           const Glib::ustring &oldprevid, 
346                           const Glib::ustring &newprevid)
348     if (childid.empty())
349         return;
350         
351     // <MESSAGE_ORDERCHANGE>
352     msgbuf = msgbuf + "<" + MESSAGE_ORDERCHANGE + ">";
354     // <MESSAGE_ID>nodeid</MESSAGE_ID>
355     msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
356     msgbuf += childid;
357     msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
359     // <MESSAGE_OLDVAL>oldprevid</MESSAGE_OLDVAL>
360     /*
361     msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
362     msgbuf += (*oldprevid);
363     msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
364     */
366     // <MESSAGE_NEWVAL>newprevid</MESSAGE_NEWVAL>
367     msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
368     msgbuf += newprevid;
369     msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
371     // </MESSAGE_ORDERCHANGE>
372     msgbuf = msgbuf + "</" + MESSAGE_ORDERCHANGE + ">";
377 bool
378 MessageUtilities::getFirstMessageTag(struct Node& buf, const Glib::ustring &msg)
380     if (msg.empty())
381         return false;
383     // See if we have a valid start tag, i.e. < ... >.  If we do,
384     // continue; if not, stop and return NULL.
385     //
386     // find_first_of returns ULONG_MAX when it cannot find the first
387     // instance of the given character.
389     Glib::ustring::size_type startDelim = msg.find_first_of('<');
390     if (startDelim != ULONG_MAX) {
391             Glib::ustring::size_type endDelim = msg.find_first_of('>');
392             if (endDelim != ULONG_MAX) {
393                     if (endDelim > startDelim) {
394                             buf.tag = msg.substr(startDelim+1, (endDelim-startDelim)-1);
395                             if (buf.tag.find_first_of('/') == ULONG_MAX) { // start tags should not be end tags
398                                     // construct end tag (</buf.data>)
399                                     Glib::ustring endTag(buf.tag);
400                                     endTag.insert(0, "/");
402                                     Glib::ustring::size_type endTagLoc = msg.find(endTag, endDelim);
403                                     if (endTagLoc != ULONG_MAX) {
404                                             buf.data = msg.substr(endDelim+1, ((endTagLoc - 1) - (endDelim + 1)));
405                                             buf.next_pos = endTagLoc + endTag.length() + 1;
407                                             return true;
408                                     }
409                             }
410                     }
411             }
412     }
414     return false;
417 bool
418 MessageUtilities::findTag(struct Node& buf, const Glib::ustring &msg)
420     if (msg.empty())
421         return false;
423     // Read desired tag type out of buffer, and append
424     // < > to it 
426     Glib::ustring searchterm("<");
427     searchterm += buf.tag;
428     searchterm + ">";
430     Glib::ustring::size_type tagStart = msg.find(searchterm, 0);
431     if (tagStart != ULONG_MAX) {
432             // Find ending tag starting at the point at the end of
433             // the start tag.
434             searchterm.insert(1, "/");
435             Glib::ustring::size_type tagEnd = msg.find(searchterm, tagStart + searchterm.length());
436             if (tagEnd != ULONG_MAX) {
437                     Glib::ustring::size_type start = tagStart + searchterm.length();
438                     buf.data = msg.substr(start, tagEnd - start);
439                     return true;
440             }
441     }
442     return false;
445 Glib::ustring
446 MessageUtilities::makeTagWithContent(const Glib::ustring &tagname,
447                                      const Glib::ustring &content)
449     Glib::ustring buf = "<" + tagname + ">";
450     buf += content;
451     buf += "</" + tagname + ">";
452     return buf;
462 /*
463   Local Variables:
464   mode:c++
465   c-file-style:"stroustrup"
466   c-file-offsets:((innamespace . 0)(inline-open . 0))
467   indent-tabs-mode:nil
468   fill-column:99
469   End:
470 */
471 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :