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 _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()
101 {
103 }
105 void
106 InkboardDocument::recieve(Message::Wrapper &wrapper, Pedro::Element* data)
107 {
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 }
143 }
145 bool
146 InkboardDocument::send(const Glib::ustring &destJid, Message::Wrapper &wrapper, Message::Message &message)
147 {
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 }
170 }
172 void
173 InkboardDocument::sendDocument(Inkscape::XML::Node* root)
174 {
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 }
194 }
196 bool
197 InkboardDocument::handleOutgoingState(Message::Wrapper &wrapper, Glib::ustring const& message)
198 {
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;
225 }
227 bool
228 InkboardDocument::handleIncomingState(Message::Wrapper &wrapper, Pedro::Element* data)
229 {
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;
256 }
258 bool
259 InkboardDocument::handleState(State::SessionState expectedState, State::SessionState newState)
260 {
261 if(this->state == expectedState)
262 {
263 this->state = newState;
264 return true;
265 }
267 return false;
268 }
271 void
272 InkboardDocument::handleChange(Message::Wrapper &wrapper, Pedro::Element* data)
273 {
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 }
317 }
319 void
320 InkboardDocument::beginTransaction()
321 {
322 g_assert(!_in_transaction);
323 _in_transaction = true;
324 }
326 void
327 InkboardDocument::rollback()
328 {
329 g_assert(_in_transaction);
330 _in_transaction = false;
331 }
333 void
334 InkboardDocument::commit()
335 {
336 g_assert(_in_transaction);
337 _in_transaction = false;
338 }
340 XML::Event*
341 InkboardDocument::commitUndoable()
342 {
343 g_assert(_in_transaction);
344 _in_transaction = false;
345 return NULL;
346 }
348 XML::Node*
349 InkboardDocument::createElement(char const* name)
350 {
351 return new XML::ElementNode(g_quark_from_string(name), this);
352 }
354 XML::Node*
355 InkboardDocument::createTextNode(char const* content)
356 {
357 return new XML::TextNode(Util::share_string(content), this);
358 }
360 XML::Node*
361 InkboardDocument::createComment(char const* content)
362 {
363 return new XML::CommentNode(Util::share_string(content), this);
364 }
366 XML::Node*
367 InkboardDocument::createPI(char const *target, char const* content)
368 {
369 return new XML::PINode(g_quark_from_string(target), Util::share_string(content), this);
370 }
374 void InkboardDocument::notifyChildAdded(XML::Node &/*parent*/,
375 XML::Node &child,
376 XML::Node */*prev*/)
377 {
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 }
390 }
392 void InkboardDocument::notifyChildRemoved(XML::Node &/*parent*/,
393 XML::Node &child,
394 XML::Node */*prev*/)
395 {
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 }
405 }
407 void InkboardDocument::notifyChildOrderChanged(XML::Node &parent,
408 XML::Node &child,
409 XML::Node */*old_prev*/,
410 XML::Node */*new_prev*/)
411 {
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 }
424 }
426 void InkboardDocument::notifyContentChanged(XML::Node &node,
427 Util::ptr_shared<char> /*old_content*/,
428 Util::ptr_shared<char> new_content)
429 {
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 }
451 }
453 void InkboardDocument::notifyAttributeChanged(XML::Node &node,
454 GQuark name,
455 Util::ptr_shared<char> /*old_value*/,
456 Util::ptr_shared<char> new_value)
457 {
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 }
480 }
482 }
484 }