Code

add basic support for preserving processing instructions in the AST
[inkscape.git] / src / jabber_whiteboard / inkboard-document.cpp
1 /**
2  * Inkscape::Whiteboard::InkboardDocument - Inkboard document implementation
3  *
4  * Authors:
5  * David Yip <yipdw@rose-hulman.edu>
6  *
7  * Copyright (c) 2005 Authors
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
12 #include <glib.h>
13 #include <glibmm.h>
15 #include "jabber_whiteboard/inkboard-document.h"
17 #include "util/ucompose.hpp"
19 #include "jabber_whiteboard/message-utilities.h"
20 #include "jabber_whiteboard/defines.h"
21 #include "jabber_whiteboard/session-manager.h"
22 #include "jabber_whiteboard/node-tracker.h"
24 #include <glibmm.h>
25 #include <glib/gmessages.h>
26 #include <glib/gquark.h>
28 #include "jabber_whiteboard/inkboard-document.h"
29 #include "jabber_whiteboard/defines.h"
31 #include "xml/node.h"
32 #include "xml/event.h"
33 #include "xml/element-node.h"
34 #include "xml/text-node.h"
35 #include "xml/comment-node.h"
36 #include "xml/pi-node.h"
38 #include "util/share.h"
39 #include "util/ucompose.hpp"
41 namespace Inkscape {
43 namespace Whiteboard {
45 InkboardDocument::InkboardDocument(int code, State::SessionType sessionType,
46                                    Glib::ustring const& to)
47 : XML::SimpleNode(code), sessionType(sessionType), recipient(to),
48   _in_transaction(false)
49 {
50     _initBindings();
51 }
53 void
54 InkboardDocument::_initBindings()
55 {
56     this->sm = &SessionManager::instance();
57     this->state = State::INITIAL;
58     this->tracker = new KeyNodeTable();
59     _bindDocument(*this);
60 }
62 void
63 InkboardDocument::setRecipient(Glib::ustring const& val)
64 {
65     this->recipient = val;
66 }
68 Glib::ustring 
69 InkboardDocument::getRecipient() const
70 {
71     return this->recipient;
72 }
74 void
75 InkboardDocument::setSessionId(Glib::ustring const& val)
76 {
77     this->sessionId = val;
78 }
80 Glib::ustring 
81 InkboardDocument::getSessionId() const
82 {
83     return this->sessionId;
84 }
86 void
87 InkboardDocument::startSessionNegotiation()
88 {
89     if(this->sessionType == State::WHITEBOARD_PEER)
90         this->send(recipient, Message::PROTOCOL,Message::CONNECT_REQUEST);
92     else if(this->sessionType == State::WHITEBOARD_MUC)
93     {
94         // Check that the MUC room is whiteboard enabled, if not no need to send 
95         // anything, just set the room to be whiteboard enabled
96     }
97 }
99 void
100 InkboardDocument::terminateSession()
105 void
106 InkboardDocument::recieve(Message::Wrapper &wrapper, Pedro::Element* data)
108     if(this->handleIncomingState(wrapper,data))
109     {
110         if(wrapper == Message::PROTOCOL)
111         {
112             Glib::ustring message = data->getFirstChild()->getFirstChild()->getFirstChild()->getName();
114             if(message == Message::CONNECT_REQUEST)
115             {
116                 // An MUC member requesting document
118             }else if(message == Message::ACCEPT_INVITATION)
119             {
120                 // TODO : Would be nice to create the desktop here
122                 this->send(getRecipient(),Message::PROTOCOL, Message::CONNECTED);
123                 this->send(getRecipient(),Message::PROTOCOL, Message::DOCUMENT_BEGIN);
125                 // Send Document
126                 this->sendDocument(this->root());
128                 this->send(getRecipient(),Message::PROTOCOL, Message::DOCUMENT_END);
130             }else if(message == Message::DECLINE_INVITATION)
131             {
132                 this->sm->terminateSession(this->getSessionId());
133             }
134         }else if(wrapper == Message::NEW || wrapper == Message::CONFIGURE
135                     || wrapper == Message::MOVE || wrapper == Message::REMOVE )
136         {
137             handleChange(wrapper,data->getFirstChild()->getFirstChild());
138         }
139     }else{
140         g_warning("Recieved Message in invalid state = %d", this->state);
141         data->print();
142     }
145 bool
146 InkboardDocument::send(const Glib::ustring &destJid, Message::Wrapper &wrapper, Message::Message &message)
148     if(this->handleOutgoingState(wrapper,message))
149     {
150         Glib::ustring mes;
151         if(wrapper == Message::PROTOCOL)
152             mes = String::ucompose(Vars::PROTOCOL_MESSAGE,wrapper,message);
153         else
154             mes = message;
156         char *finalmessage = const_cast<char* >(String::ucompose(
157             Vars::WHITEBOARD_MESSAGE, this->sessionType, this->sm->getClient().getJid(),
158             destJid, Vars::INKBOARD_XMLNS, this->getSessionId(), mes).c_str());
160         if (!this->sm->getClient().write("%s",finalmessage)) 
161             { return false; }
162         else 
163             { return true; }
165     }else 
166     { 
167         g_warning("Sending Message in invalid state message=%s , state=%d",message.c_str(),this->state);
168         return false; 
169     }
172 void
173 InkboardDocument::sendDocument(Inkscape::XML::Node* root)
175     for(Inkscape::XML::Node *child = root->firstChild();child!=NULL;child=child->next())
176     {
177         Glib::ustring name(child->name());
179         if(name != "svg:metadata" && name != "svg:defs" && name != "sodipodi:namedview")
180         {
181             Glib::ustring parentKey,tempParentKey,key;
183             this->addNodeToTracker(child);
184             Message::Message message = this->composeNewMessage(child);
186             this->send(this->getRecipient(),Message::NEW,message);
188             if(child->childCount() != 0)
189             {
190                 sendDocument(child);
191             }
192         }
193     }
196 bool
197 InkboardDocument::handleOutgoingState(Message::Wrapper &wrapper, Glib::ustring const& message)
199     if(wrapper == Message::PROTOCOL) 
200     {
201         if(message == Message::CONNECT_REQUEST) 
202             return this->handleState(State::INITIAL,State::AWAITING_INVITATION_REPLY);
204         else if(message == Message::ACCEPT_INVITATION)
205             return this->handleState(State::CONNECTING,State::AWAITING_CONNECTED);
207         else if(message == Message::CONNECTED)
208             return this->handleState(State::INVITATION_RECIEVED,State::CONNECTED);
210         else if(message == Message::DOCUMENT_BEGIN)
211             return this->handleState(State::CONNECTED,State::SYNCHRONISING);
213         else if(message == Message::DOCUMENT_END) { 
214             return this->handleState(State::SYNCHRONISING,State::IN_WHITEBOARD);
215         }
217         else 
218             return false;
220     } else 
221         if(this->state == State::SYNCHRONISING && wrapper == Message::NEW)
222             return true;
224     return this->state == State::IN_WHITEBOARD;
227 bool
228 InkboardDocument::handleIncomingState(Message::Wrapper &wrapper, Pedro::Element* data)
230     if(wrapper == Message::PROTOCOL) 
231     {
232         Glib::ustring message = data->getFirstChild()->getFirstChild()->getFirstChild()->getName();
234         if(message == Message::CONNECT_REQUEST)
235             return this->handleState(State::INITIAL,State::CONNECTING);
236         if(message == Message::ACCEPT_INVITATION)
237             return this->handleState(State::AWAITING_INVITATION_REPLY,State::INVITATION_RECIEVED);
239         else if(message == Message::CONNECTED)
240             return this->handleState(State::AWAITING_CONNECTED,State::AWAITING_DOCUMENT_BEGIN);
242         else if(message == Message::DOCUMENT_BEGIN)
243             return this->handleState(State::AWAITING_DOCUMENT_BEGIN,State::SYNCHRONISING);
245         else if(message == Message::DOCUMENT_END)
246             return this->handleState(State::SYNCHRONISING,State::IN_WHITEBOARD);
248         else 
249             return false;
251     } else 
252         if(this->state == State::SYNCHRONISING && wrapper == Message::NEW)
253             return true;
255     return this->state == State::IN_WHITEBOARD;
258 bool 
259 InkboardDocument::handleState(State::SessionState expectedState, State::SessionState newState)
261     if(this->state == expectedState)
262     {
263         this->state = newState;
264         return true;
265     }
267     return false;
271 void
272 InkboardDocument::handleChange(Message::Wrapper &wrapper, Pedro::Element* data)
274     if(wrapper == Message::NEW)
275     {
276         Glib::ustring parent =  data->getTagAttribute("new","parent");
277         Glib::ustring id =      data->getTagAttribute("new","id");
279         signed int index = atoi
280             (data->getTagAttribute("new","index").c_str());
282         Pedro::Element* element = data->getFirstChild();
284         if(parent.size() > 0 && id.size() > 0)
285             this->changeNew(parent,id,index,element);
287     }else if(wrapper == Message::CONFIGURE)
288     {
289         if(data->exists("text"))
290         {
291             Glib::ustring text =    data->getFirstChild()->getValue();
292             Glib::ustring target =  data->getTagAttribute("configure","target");
294             unsigned int version = atoi
295                 (data->getTagAttribute("configure","version").c_str());
297             if(text.size() > 0 && target.size() > 0)
298                 this->changeConfigureText(target,version,text);
300         }else 
301         {
302             Glib::ustring target =      data->getTagAttribute("configure","target");
303             Glib::ustring attribute =   data->getTagAttribute("configure","attribute");
304             Glib::ustring value =       data->getTagAttribute("configure","value");
306             unsigned int version = atoi
307                 (data->getTagAttribute("configure","version").c_str());
309             if(target.size() > 0 && attribute.size() > 0 && value.size() > 0)
310                 this->changeConfigure(target,version,attribute,value);
311         }
312     }else if(wrapper == Message::MOVE)
313     {
314     }else if(wrapper == Message::REMOVE) 
315     {
316     }
319 void
320 InkboardDocument::beginTransaction()
322     g_assert(!_in_transaction);
323     _in_transaction = true;
326 void
327 InkboardDocument::rollback()
329     g_assert(_in_transaction);
330     _in_transaction = false;
333 void 
334 InkboardDocument::commit()
336     g_assert(_in_transaction);
337     _in_transaction = false;
340 XML::Event*
341 InkboardDocument::commitUndoable()
343     g_assert(_in_transaction);
344     _in_transaction = false;
345     return NULL;
348 XML::Node*
349 InkboardDocument::createElement(char const* name)
351     return new XML::ElementNode(g_quark_from_string(name));
354 XML::Node*
355 InkboardDocument::createTextNode(char const* content)
357     return new XML::TextNode(Util::share_string(content));
360 XML::Node*
361 InkboardDocument::createComment(char const* content)
363     return new XML::CommentNode(Util::share_string(content));
366 XML::Node*
367 InkboardDocument::createPI(char const *target, char const* content)
369     return new XML::PINode(g_quark_from_string(target), Util::share_string(content));
374 void InkboardDocument::notifyChildAdded(XML::Node &parent,
375                                         XML::Node &child,
376                                         XML::Node *prev)
378     if (_in_transaction && state == State::IN_WHITEBOARD) {
380         XML::Node *node = (XML::Node *)&child;
382         if(tracker->get(node) == "")
383         {
384             addNodeToTracker(node);
385             Message::Message message = composeNewMessage(node);
387             send(getRecipient(),Message::NEW,message);
388         }
389     }
392 void InkboardDocument::notifyChildRemoved(XML::Node &parent,
393                                           XML::Node &child,
394                                           XML::Node *prev)
396     if (_in_transaction && state == State::IN_WHITEBOARD) 
397     {
398         XML::Node *element = (XML::Node *)&child;
400         Message::Message message = String::ucompose(Vars::REMOVE_MESSAGE,
401             tracker->get(element));
403         send(getRecipient(),Message::REMOVE,message);
404    }
407 void InkboardDocument::notifyChildOrderChanged(XML::Node &parent,
408                                                XML::Node &child,
409                                                XML::Node *old_prev,
410                                                XML::Node *new_prev)
412     if (_in_transaction && state == State::IN_WHITEBOARD) 
413     {
414         XML::Node *element = (XML::Node *)&child;
415         XML::Node *parentElement = (XML::Node *)&parent;
417         unsigned int index = parentElement->_childPosition(*element);
419         Message::Message message = String::ucompose(Vars::MOVE_MESSAGE,
420                 tracker->get(element),index);
422         send(getRecipient(),Message::MOVE,message);
423     }
426 void InkboardDocument::notifyContentChanged(XML::Node &node,
427                                             Util::ptr_shared<char> old_content,
428                                             Util::ptr_shared<char> new_content)
430     if (_in_transaction && state == State::IN_WHITEBOARD) 
431     {
432         XML::Node *element = (XML::Node *)&node;
434         Glib::ustring value(new_content.pointer());
436         Glib::ustring change = tracker->getLastHistory(element,"text");
438         if(change.size() > 0 && change == value)
439             return;
441         if(new_content.pointer())
442         {
443             unsigned int version = tracker->incrementVersion(element);
445             Message::Message message = String::ucompose(Vars::CONFIGURE_TEXT_MESSAGE,
446                 tracker->get(element),version,new_content.pointer());
448             send(getRecipient(),Message::CONFIGURE,message);
449         }
450     }
453 void InkboardDocument::notifyAttributeChanged(XML::Node &node,
454                                               GQuark name,
455                                               Util::ptr_shared<char> old_value,
456                                               Util::ptr_shared<char> new_value)
458     if (_in_transaction && state == State::IN_WHITEBOARD) 
459     {
460         XML::Node *element = (XML::Node *)&node;
462         Glib::ustring value(new_value.pointer());
463         Glib::ustring attribute(g_quark_to_string(name));
465         Glib::ustring change = tracker->getLastHistory(element,attribute);
467         if(change.size() > 0 && change == value)
468             return;
470         if(attribute.size() > 0 && value.size() > 0)
471         {
472             unsigned int version = tracker->incrementVersion(element);
474             Message::Message message = String::ucompose(Vars::CONFIGURE_MESSAGE,
475                 tracker->get(element),version,attribute.c_str(),value.c_str());
477             send(getRecipient(),Message::CONFIGURE,message);
478         }
479     }