Code

moving trunk for module inkscape
[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/shared-c-string-ptr.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/typedefs.h"
24 #include "jabber_whiteboard/node-utilities.h"
25 #include "jabber_whiteboard/message-utilities.h"
26 #include "jabber_whiteboard/node-tracker.h"
28 #include <iostream>
30 namespace Inkscape {
32 namespace Whiteboard {
34 // This method can be instructed to not build a message string but only collect nodes that _would_ be transmitted
35 // and subsequently added to the tracker.  This can be useful in the case where an Inkboard user is the only one
36 // in a chatroom and therefore needs to fill out the node tracker, but does not need to build the message string.
37 // This can be controlled with the only_collect_nodes flag, which will only create pointers to new XML::Nodes 
38 // in the maps referenced by newidsbuf and newnodesbuf.  Passing NULL as the message buffer has the same effect.
39 //
40 // only_collect_nodes defaults to false because most invocations of this method also use the message string.
41 void
42 MessageUtilities::newObjectMessage(Glib::ustring* msgbuf, KeyToNodeMap& newidsbuf, NodeToKeyMap& newnodesbuf, NewChildObjectMessageList& childmsgbuf, XMLNodeTracker* xmt, Inkscape::XML::Node const* node, bool only_collect_nodes, bool collect_children)
43 {
44         // Initialize pointers
45         std::string id, refid, parentid;
47         gchar const* name = NULL;
48         XML::Node* parent = NULL;
49         XML::Node* ref = NULL;
51         bool only_add_children = false;
54         if (node != NULL) {
55                 parent = sp_repr_parent(node);
56                 if (parent != NULL) {
57                         parentid = NodeUtilities::findNodeID(*parent, xmt, newnodesbuf);
58                         if (parentid.empty()) {
59                                 g_warning("Parent %p is not being tracked, creating new ID", parent);
60                                 parentid = xmt->generateKey();
61                                 newidsbuf[parentid] = parent;
62                                 newnodesbuf[parent] = parentid;
63                         }
65                         if ( node != parent->firstChild() && parent != NULL ) {
66                                 ref = parent->firstChild();
67                                 while (ref->next() != node) {
68                                         ref = ref->next();
69                                 }
70                         }       
71                 }
73                 if (ref != NULL) {
74                         refid = NodeUtilities::findNodeID(*ref, xmt, newnodesbuf);
75                         if (refid.empty() && ref != NULL) {
76                                 g_warning("Ref %p is not being tracked, creating new ID", ref);
77                                 refid = xmt->generateKey();
78                                 newidsbuf[refid] = ref;
79                                 newnodesbuf[ref] = refid;
80                         }
81                 }
83                 name = static_cast< gchar const* >(node->name());
84         }
86         // Generate an id for this object and append it onto the list, if 
87         // it's not already in the tracker
88         if (!xmt->isSpecialNode(node->name())) {
89                 if (!xmt->isTracking(*node)) {
90                         id = xmt->generateKey();        
91                         newidsbuf[id] = node;
92                         newnodesbuf[node] = id;
93                 } else {
94                         id = xmt->get(*node);
95                 }
96         } else {
97                 id = xmt->get(*node);
98                 if (id.empty()) {
99                         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());
100                         id = xmt->generateKey();
101                         newidsbuf[id] = node;
102                         newnodesbuf[node] = id;
103                 }
104                 only_add_children = true;
105         }
107         // If we're only adding children (i.e. this is a special node)
108         // don't process the given node.
109         if( !only_add_children && !id.empty() && msgbuf != NULL && !only_collect_nodes ) {
110                 // <MESSAGE_NEWOBJ>
111                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_NEWOBJ + ">";
113                 // <MESSAGE_PARENT>
114                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_PARENT + ">";
116                 if(!parentid.empty()) {
117                         (*msgbuf) += parentid;
118                 }
120                 // </MESSAGE_NEWOBJ><MESSAGE_CHILD>id</MESSAGE_CHILD>
121                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_PARENT + ">";
123                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_CHILD + ">";
124                 (*msgbuf) += id;
126                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_CHILD + ">";
128                 if(!refid.empty()) {
129                         // <MESSAGE_REF>refid</MESSAGE_REF>
130                         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_REF + ">";
131                         
132                         (*msgbuf) += refid;
134                         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_REF + ">";
135                 }
137                 // <MESSAGE_NODETYPE>*node.type()</MESSAGE_NODETYPE>
138                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_NODETYPE + ">" + NodeUtilities::nodeTypeToString(*node);
139                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_NODETYPE + ">";
141                 if (node->content() != NULL) {
142                         // <MESSAGE_CONTENT>node->content()</MESSAGE_CONTENT>
143                         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_CONTENT + ">" + node->content();
144                         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_CONTENT + ">";
145                 }
147                 // <MESSAGE_NAME>name</MESSAGE_NAME>
148                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_NAME + ">";
150                 if( name != NULL ) {
151                         (*msgbuf) += name;
152                 }
154                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_NAME + ">";
156                 // </MESSAGE_NEWOBJ>
157                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_NEWOBJ + ">";
158         } else if (id.empty()) {
159                 // if ID is NULL, then we have a real problem -- we were not able to find a key
160                 // nor generate one.  The only thing we can really do here is abort, since we have
161                 // no way to let the other client(s) uniquely identify this object.
162                 /* FIXME: If this indicates a programming bug, then don't request translation with
163                  * _(...): it is most useful in untranslated form so that developers may search for
164                  * it when someone reports it in a bug report (as we want users to do for all bugs,
165                  * as indicated by it being a g_warning string).
166                  * 
167                  * Otherwise, if it is not a programming bug but a network error or a bug in the
168                  * remote peer (perhaps running different software) or whatever, then present it in
169                  * an alert box, and avoid use of technical jargon `NULL'.
170                  */
171                 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!"));
172                 return;
173         } else {
175         }
178         if (!only_collect_nodes && msgbuf != NULL && !id.empty()) {
179                 // Collect new object's attributes and append them onto the msgbuf
180                 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = node->attributeList();
182                 for(; attrlist; attrlist++) {
183                         MessageUtilities::objectChangeMessage(msgbuf, xmt, id, g_quark_to_string(attrlist->key), NULL, attrlist->value, false);
184                 }
185         }
187         if (!only_collect_nodes) {
188                 childmsgbuf.push_back(*msgbuf);
189         }
191         if (!id.empty() && collect_children) {
192                 Glib::ustring childbuf;
193                 // Collect any child objects of this new object
194                 for ( Inkscape::XML::Node const *child = node->firstChild(); child != NULL; child = child->next() ) {
195                         childbuf.clear();
196                         MessageUtilities::newObjectMessage(&childbuf, newidsbuf, 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(ustring* msgbuf, XMLNodeTracker* xmt, std::string const id, gchar const* key, gchar const* oldval, gchar const* newval, bool is_interactive)
210         // Construct message
211         
212         // <MESSAGE_CHANGE><MESSAGE_ID>id</MESSAGE_ID>
213         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_CHANGE + ">";
214         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_ID + ">";
215         (*msgbuf) += id;
216         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_ID + ">";
218         // <MESSAGE_KEY>key</MESSAGE_KEY>
219         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_KEY + ">";
220         if (key != NULL) {
221                 (*msgbuf) += key;
222         }
223         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_KEY + ">";
225         // <MESSAGE_OLDVAL>oldval</MESSAGE_OLDVAL>
226         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_OLDVAL + ">";
227         if (oldval != NULL) {
228                 (*msgbuf) += oldval;
229         }
230         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_OLDVAL + ">";
232         // <MESSAGE_NEWVAL>newval</MESSAGE_NEWVAL>
233         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_NEWVAL + ">";
234         if (newval != NULL) {
235                 (*msgbuf) += newval;
236         }
237         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_NEWVAL + ">";
239         // <MESSAGE_ISINTERACTIVE>is_interactive</MESSAGE_ISINTERACTIVE>
240         (*msgbuf) = (*msgbuf) + "<" + MESSAGE_ISINTERACTIVE + ">";
241         if (is_interactive) {
242                 (*msgbuf) += "true";
243         } else {
244                 (*msgbuf) += "false";
245         }
246         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_ISINTERACTIVE + ">";
248         // </MESSAGE_CHANGE>
249         (*msgbuf) = (*msgbuf) + "</" + MESSAGE_CHANGE + ">";
252 void
253 MessageUtilities::objectDeleteMessage(Glib::ustring* msgbuf, XMLNodeTracker* xmt, Inkscape::XML::Node const& parent, Inkscape::XML::Node const& child, Inkscape::XML::Node const* prev)
255         /*
256         gchar const* parentid = NULL;
257         gchar const* previd = NULL;
258         gchar const* childid = NULL;
260         childid = child.attribute("id");
261         parentid = parent.attribute("id");
262         if (prev != NULL) {
263                 previd = prev->attribute("id");
264         }*/
266         std::string parentid, previd, childid;
268         childid = xmt->get(child);
269         parentid = xmt->get(parent);
270         previd = xmt->get(*prev);
272         if (!childid.empty()) {
273                 // <MESSAGE_DELETE><MESSAGE_PARENT>parentid</MESSAGE_PARENT>
274                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_DELETE + ">" + "<" + MESSAGE_PARENT + ">";
275                 if (!parentid.empty()) {
276                         (*msgbuf) += parentid;
277                 }
278                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_PARENT + ">";
280                 // <MESSAGE_CHILD>childid</MESSAGE_CHILD>
281                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_CHILD + ">";
282                 if (!childid.empty()) {
283                         (*msgbuf) += childid;
284                 }
285                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_CHILD + ">";
287                 // <MESSAGE_REF>previd</MESSAGE_REF>
288                 (*msgbuf) = (*msgbuf) + "<" + MESSAGE_REF + ">";
289                 if (!previd.empty()) {
290                         (*msgbuf) += previd;
291                 }
292                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_REF + ">";
294                 // </MESSAGE_DELETE>
295                 (*msgbuf) = (*msgbuf) + "</" + MESSAGE_DELETE + ">";
296         }
299 void
300 MessageUtilities::contentChangeMessage(Glib::ustring& msgbuf, std::string const nodeid, Util::SharedCStringPtr old_value, Util::SharedCStringPtr new_value)
302         if (!nodeid.empty()) {
303                 // <MESSAGE_NODECONTENT>
304                 msgbuf = msgbuf + "<" + MESSAGE_NODECONTENT + ">";
306                 // <MESSAGE_ID>nodeid</MESSAGE_ID>
307                 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
308                 msgbuf += nodeid;
309                 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
311                 // <MESSAGE_OLDVAL>old_value</MESSAGE_OLDVAL>
312                 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
313                 msgbuf += old_value.cString();
314                 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
316                 // <MESSAGE_NEWVAL>new_value</MESSAGE_NEWVAL>
317                 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
318                 msgbuf += new_value.cString();
319                 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
321                 // </MESSAGE_NODECONTENT>
322                 msgbuf = msgbuf + "</" + MESSAGE_NODECONTENT + ">";
323         }
326 void
327 MessageUtilities::childOrderChangeMessage(Glib::ustring& msgbuf, std::string const childid, std::string const oldprevid, std::string const newprevid)
329         if (!childid.empty()) {
330                 // <MESSAGE_ORDERCHANGE>
331                 msgbuf = msgbuf + "<" + MESSAGE_ORDERCHANGE + ">";
333                 // <MESSAGE_ID>nodeid</MESSAGE_ID>
334                 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
335                 msgbuf += childid;
336                 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
338                 // <MESSAGE_OLDVAL>oldprevid</MESSAGE_OLDVAL>
339                 /*
340                 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
341                 msgbuf += (*oldprevid);
342                 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
343                 */
345                 // <MESSAGE_NEWVAL>newprevid</MESSAGE_NEWVAL>
346                 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
347                 msgbuf += newprevid;
348                 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
350                 // </MESSAGE_ORDERCHANGE>
351                 msgbuf = msgbuf + "</" + MESSAGE_ORDERCHANGE + ">";
352         }
356 bool
357 MessageUtilities::getFirstMessageTag(struct Node& buf, Glib::ustring const& msg)
359         if (msg.empty()) {
360                 return false;
361         }
363         // See if we have a valid start tag, i.e. < ... >.  If we do,
364         // continue; if not, stop and return NULL.
365         //
366         // find_first_of returns ULONG_MAX when it cannot find the first
367         // instance of the given character.
369         Glib::ustring::size_type startDelim = msg.find_first_of('<');
370         if (startDelim != ULONG_MAX) {
371                 Glib::ustring::size_type endDelim = msg.find_first_of('>');
372                 if (endDelim != ULONG_MAX) {
373                         if (endDelim > startDelim) {
374                                 buf.tag = msg.substr(startDelim+1, (endDelim-startDelim)-1);
375                                 if (buf.tag.find_first_of('/') == ULONG_MAX) { // start tags should not be end tags
378                                         // construct end tag (</buf.data>)
379                                         Glib::ustring endTag(buf.tag);
380                                         endTag.insert(0, "/");
382                                         Glib::ustring::size_type endTagLoc = msg.find(endTag, endDelim);
383                                         if (endTagLoc != ULONG_MAX) {
384                                                 buf.data = msg.substr(endDelim+1, ((endTagLoc - 1) - (endDelim + 1)));
385                                                 buf.next_pos = endTagLoc + endTag.length() + 1;
387                                                 return true;
388                                         }
389                                 }
390                         }
391                 }
392         }
394         return false;
397 bool
398 MessageUtilities::findTag(struct Node& buf, Glib::ustring const& msg)
400         if (msg.empty()) {
401                 return false;
402         }
404         // Read desired tag type out of buffer, and append
405         // < > to it 
406         
407         Glib::ustring searchterm("<");
408         searchterm += buf.tag;
409         searchterm + ">";
411         Glib::ustring::size_type tagStart = msg.find(searchterm, 0);
412         if (tagStart != ULONG_MAX) {
413                 // Find ending tag starting at the point at the end of
414                 // the start tag.
415                 searchterm.insert(1, "/");
416                 Glib::ustring::size_type tagEnd = msg.find(searchterm, tagStart + searchterm.length());
417                 if (tagEnd != ULONG_MAX) {
418                         Glib::ustring::size_type start = tagStart + searchterm.length();
419                         buf.data = msg.substr(start, tagEnd - start);
420                         return true;
421                 }
422         }
423         return false;
426 Glib::ustring
427 MessageUtilities::makeTagWithContent(Glib::ustring tagname, Glib::ustring content)
429         Glib::ustring buf;
430         buf = "<" + tagname + ">";
431         buf += content;
432         buf += "</" + tagname + ">";
433         return buf;
443 /*
444   Local Variables:
445   mode:c++
446   c-file-style:"stroustrup"
447   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
448   indent-tabs-mode:nil
449   fill-column:99
450   End:
451 */
452 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :