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, this), 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 }
61 void
62 InkboardDocument::setRecipient(Glib::ustring const& val)
63 {
64 this->recipient = val;
65 }
67 Glib::ustring
68 InkboardDocument::getRecipient() const
69 {
70 return this->recipient;
71 }
73 void
74 InkboardDocument::setSessionId(Glib::ustring const& val)
75 {
76 this->sessionId = val;
77 }
79 Glib::ustring
80 InkboardDocument::getSessionId() const
81 {
82 return this->sessionId;
83 }
85 void
86 InkboardDocument::startSessionNegotiation()
87 {
88 if(this->sessionType == State::WHITEBOARD_PEER)
89 this->send(recipient, Message::PROTOCOL,Message::CONNECT_REQUEST);
91 else if(this->sessionType == State::WHITEBOARD_MUC)
92 {
93 // Check that the MUC room is whiteboard enabled, if not no need to send
94 // anything, just set the room to be whiteboard enabled
95 }
96 }
98 void
99 InkboardDocument::terminateSession()
100 {
102 }
104 void
105 InkboardDocument::recieve(Message::Wrapper &wrapper, Pedro::Element* data)
106 {
107 if(this->handleIncomingState(wrapper,data))
108 {
109 if(wrapper == Message::PROTOCOL)
110 {
111 Glib::ustring message = data->getFirstChild()->getFirstChild()->getFirstChild()->getName();
113 if(message == Message::CONNECT_REQUEST)
114 {
115 // An MUC member requesting document
117 }else if(message == Message::ACCEPT_INVITATION)
118 {
119 // TODO : Would be nice to create the desktop here
121 this->send(getRecipient(),Message::PROTOCOL, Message::CONNECTED);
122 this->send(getRecipient(),Message::PROTOCOL, Message::DOCUMENT_BEGIN);
124 // Send Document
125 this->sendDocument(this->root());
127 this->send(getRecipient(),Message::PROTOCOL, Message::DOCUMENT_END);
129 }else if(message == Message::DECLINE_INVITATION)
130 {
131 this->sm->terminateSession(this->getSessionId());
132 }
133 }else if(wrapper == Message::NEW || wrapper == Message::CONFIGURE
134 || wrapper == Message::MOVE || wrapper == Message::REMOVE )
135 {
136 handleChange(wrapper,data->getFirstChild()->getFirstChild());
137 }
138 }else{
139 g_warning("Recieved Message in invalid state = %d", this->state);
140 data->print();
141 }
142 }
144 bool
145 InkboardDocument::send(const Glib::ustring &destJid, Message::Wrapper &wrapper, Message::Message &message)
146 {
147 if(this->handleOutgoingState(wrapper,message))
148 {
149 Glib::ustring mes;
150 if(wrapper == Message::PROTOCOL)
151 mes = String::ucompose(Vars::PROTOCOL_MESSAGE,wrapper,message);
152 else
153 mes = message;
155 char *finalmessage = const_cast<char* >(String::ucompose(
156 Vars::WHITEBOARD_MESSAGE, this->sessionType, this->sm->getClient().getJid(),
157 destJid, Vars::INKBOARD_XMLNS, this->getSessionId(), mes).c_str());
159 if (!this->sm->getClient().write("%s",finalmessage))
160 { return false; }
161 else
162 { return true; }
164 }else
165 {
166 g_warning("Sending Message in invalid state message=%s , state=%d",message.c_str(),this->state);
167 return false;
168 }
169 }
171 void
172 InkboardDocument::sendDocument(Inkscape::XML::Node* root)
173 {
174 for(Inkscape::XML::Node *child = root->firstChild();child!=NULL;child=child->next())
175 {
176 Glib::ustring name(child->name());
178 if(name != "svg:metadata" && name != "svg:defs" && name != "sodipodi:namedview")
179 {
180 Glib::ustring parentKey,tempParentKey,key;
182 this->addNodeToTracker(child);
183 Message::Message message = this->composeNewMessage(child);
185 this->send(this->getRecipient(),Message::NEW,message);
187 if(child->childCount() != 0)
188 {
189 sendDocument(child);
190 }
191 }
192 }
193 }
195 bool
196 InkboardDocument::handleOutgoingState(Message::Wrapper &wrapper, Glib::ustring const& message)
197 {
198 if(wrapper == Message::PROTOCOL)
199 {
200 if(message == Message::CONNECT_REQUEST)
201 return this->handleState(State::INITIAL,State::AWAITING_INVITATION_REPLY);
203 else if(message == Message::ACCEPT_INVITATION)
204 return this->handleState(State::CONNECTING,State::AWAITING_CONNECTED);
206 else if(message == Message::CONNECTED)
207 return this->handleState(State::INVITATION_RECIEVED,State::CONNECTED);
209 else if(message == Message::DOCUMENT_BEGIN)
210 return this->handleState(State::CONNECTED,State::SYNCHRONISING);
212 else if(message == Message::DOCUMENT_END) {
213 return this->handleState(State::SYNCHRONISING,State::IN_WHITEBOARD);
214 }
216 else
217 return false;
219 } else
220 if(this->state == State::SYNCHRONISING && wrapper == Message::NEW)
221 return true;
223 return this->state == State::IN_WHITEBOARD;
224 }
226 bool
227 InkboardDocument::handleIncomingState(Message::Wrapper &wrapper, Pedro::Element* data)
228 {
229 if(wrapper == Message::PROTOCOL)
230 {
231 Glib::ustring message = data->getFirstChild()->getFirstChild()->getFirstChild()->getName();
233 if(message == Message::CONNECT_REQUEST)
234 return this->handleState(State::INITIAL,State::CONNECTING);
235 if(message == Message::ACCEPT_INVITATION)
236 return this->handleState(State::AWAITING_INVITATION_REPLY,State::INVITATION_RECIEVED);
238 else if(message == Message::CONNECTED)
239 return this->handleState(State::AWAITING_CONNECTED,State::AWAITING_DOCUMENT_BEGIN);
241 else if(message == Message::DOCUMENT_BEGIN)
242 return this->handleState(State::AWAITING_DOCUMENT_BEGIN,State::SYNCHRONISING);
244 else if(message == Message::DOCUMENT_END)
245 return this->handleState(State::SYNCHRONISING,State::IN_WHITEBOARD);
247 else
248 return false;
250 } else
251 if(this->state == State::SYNCHRONISING && wrapper == Message::NEW)
252 return true;
254 return this->state == State::IN_WHITEBOARD;
255 }
257 bool
258 InkboardDocument::handleState(State::SessionState expectedState, State::SessionState newState)
259 {
260 if(this->state == expectedState)
261 {
262 this->state = newState;
263 return true;
264 }
266 return false;
267 }
270 void
271 InkboardDocument::handleChange(Message::Wrapper &wrapper, Pedro::Element* data)
272 {
273 if(wrapper == Message::NEW)
274 {
275 Glib::ustring parent = data->getTagAttribute("new","parent");
276 Glib::ustring id = data->getTagAttribute("new","id");
278 signed int index = atoi
279 (data->getTagAttribute("new","index").c_str());
281 Pedro::Element* element = data->getFirstChild();
283 if(parent.size() > 0 && id.size() > 0)
284 this->changeNew(parent,id,index,element);
286 }else if(wrapper == Message::CONFIGURE)
287 {
288 if(data->exists("text"))
289 {
290 Glib::ustring text = data->getFirstChild()->getValue();
291 Glib::ustring target = data->getTagAttribute("configure","target");
293 unsigned int version = atoi
294 (data->getTagAttribute("configure","version").c_str());
296 if(text.size() > 0 && target.size() > 0)
297 this->changeConfigureText(target,version,text);
299 }else
300 {
301 Glib::ustring target = data->getTagAttribute("configure","target");
302 Glib::ustring attribute = data->getTagAttribute("configure","attribute");
303 Glib::ustring value = data->getTagAttribute("configure","value");
305 unsigned int version = atoi
306 (data->getTagAttribute("configure","version").c_str());
308 if(target.size() > 0 && attribute.size() > 0 && value.size() > 0)
309 this->changeConfigure(target,version,attribute,value);
310 }
311 }else if(wrapper == Message::MOVE)
312 {
313 }else if(wrapper == Message::REMOVE)
314 {
315 }
316 }
318 void
319 InkboardDocument::beginTransaction()
320 {
321 g_assert(!_in_transaction);
322 _in_transaction = true;
323 }
325 void
326 InkboardDocument::rollback()
327 {
328 g_assert(_in_transaction);
329 _in_transaction = false;
330 }
332 void
333 InkboardDocument::commit()
334 {
335 g_assert(_in_transaction);
336 _in_transaction = false;
337 }
339 XML::Event*
340 InkboardDocument::commitUndoable()
341 {
342 g_assert(_in_transaction);
343 _in_transaction = false;
344 return NULL;
345 }
347 XML::Node*
348 InkboardDocument::createElement(char const* name)
349 {
350 return new XML::ElementNode(g_quark_from_string(name), this);
351 }
353 XML::Node*
354 InkboardDocument::createTextNode(char const* content)
355 {
356 return new XML::TextNode(Util::share_string(content), this);
357 }
359 XML::Node*
360 InkboardDocument::createComment(char const* content)
361 {
362 return new XML::CommentNode(Util::share_string(content), this);
363 }
365 XML::Node*
366 InkboardDocument::createPI(char const *target, char const* content)
367 {
368 return new XML::PINode(g_quark_from_string(target), Util::share_string(content), this);
369 }
373 void InkboardDocument::notifyChildAdded(XML::Node &/*parent*/,
374 XML::Node &child,
375 XML::Node */*prev*/)
376 {
377 if (_in_transaction && state == State::IN_WHITEBOARD) {
379 XML::Node *node = (XML::Node *)&child;
381 if(tracker->get(node) == "")
382 {
383 addNodeToTracker(node);
384 Message::Message message = composeNewMessage(node);
386 send(getRecipient(),Message::NEW,message);
387 }
388 }
389 }
391 void InkboardDocument::notifyChildRemoved(XML::Node &/*parent*/,
392 XML::Node &child,
393 XML::Node */*prev*/)
394 {
395 if (_in_transaction && state == State::IN_WHITEBOARD)
396 {
397 XML::Node *element = (XML::Node *)&child;
399 Message::Message message = String::ucompose(Vars::REMOVE_MESSAGE,
400 tracker->get(element));
402 send(getRecipient(),Message::REMOVE,message);
403 }
404 }
406 void InkboardDocument::notifyChildOrderChanged(XML::Node &parent,
407 XML::Node &child,
408 XML::Node */*old_prev*/,
409 XML::Node */*new_prev*/)
410 {
411 if (_in_transaction && state == State::IN_WHITEBOARD)
412 {
413 unsigned int index = child.position();
415 Message::Message message = String::ucompose(Vars::MOVE_MESSAGE,
416 tracker->get(&child),index);
418 send(getRecipient(),Message::MOVE,message);
419 }
420 }
422 void InkboardDocument::notifyContentChanged(XML::Node &node,
423 Util::ptr_shared<char> /*old_content*/,
424 Util::ptr_shared<char> new_content)
425 {
426 if (_in_transaction && state == State::IN_WHITEBOARD)
427 {
428 XML::Node *element = (XML::Node *)&node;
430 Glib::ustring value(new_content.pointer());
432 Glib::ustring change = tracker->getLastHistory(element,"text");
434 if(change.size() > 0 && change == value)
435 return;
437 if(new_content.pointer())
438 {
439 unsigned int version = tracker->incrementVersion(element);
441 Message::Message message = String::ucompose(Vars::CONFIGURE_TEXT_MESSAGE,
442 tracker->get(element),version,new_content.pointer());
444 send(getRecipient(),Message::CONFIGURE,message);
445 }
446 }
447 }
449 void InkboardDocument::notifyAttributeChanged(XML::Node &node,
450 GQuark name,
451 Util::ptr_shared<char> /*old_value*/,
452 Util::ptr_shared<char> new_value)
453 {
454 if (_in_transaction && state == State::IN_WHITEBOARD)
455 {
456 XML::Node *element = (XML::Node *)&node;
458 Glib::ustring value(new_value.pointer());
459 Glib::ustring attribute(g_quark_to_string(name));
461 Glib::ustring change = tracker->getLastHistory(element,attribute);
463 if(change.size() > 0 && change == value)
464 return;
466 if(attribute.size() > 0 && value.size() > 0)
467 {
468 unsigned int version = tracker->incrementVersion(element);
470 Message::Message message = String::ucompose(Vars::CONFIGURE_MESSAGE,
471 tracker->get(element),version,attribute.c_str(),value.c_str());
473 send(getRecipient(),Message::CONFIGURE,message);
474 }
475 }
476 }
478 }
480 }