3dcb8e217bdf08a9aef87406f18e25d4df1ba8bf
1 /**
2 * Whiteboard session manager
3 *
4 * Authors:
5 * David Yip <yipdw@rose-hulman.edu>
6 * Bob Jamison (Pedro port)
7 *
8 * Copyright (c) 2005 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include <functional>
14 #include <algorithm>
15 #include <iostream>
16 #include <time.h>
18 #include <gtkmm.h>
19 #include <glibmm/i18n.h>
21 #include "xml/node.h"
22 #include "xml/repr.h"
24 #include "util/ucompose.hpp"
26 #include "xml/node-observer.h"
28 #include "pedro/pedrodom.h"
30 #include "ui/view/view-widget.h"
32 #include "application/application.h"
33 #include "application/editor.h"
35 #include "document-private.h"
36 #include "interface.h"
37 #include "sp-namedview.h"
38 #include "document.h"
39 #include "desktop.h"
40 #include "desktop-handles.h"
42 #include "jabber_whiteboard/invitation-confirm-dialog.h"
43 #include "jabber_whiteboard/message-verifier.h"
44 #include "jabber_whiteboard/session-manager.h"
45 #include "jabber_whiteboard/inkboard-document.h"
46 #include "jabber_whiteboard/defines.h"
48 #include "jabber_whiteboard/dialog/choose-desktop.h"
50 namespace Inkscape {
52 namespace Whiteboard {
54 //#########################################################################
55 //# S E S S I O N M A N A G E R
56 //#########################################################################
58 SessionManager *sessionManagerInstance = NULL;
60 void SessionManager::showClient()
61 {
62 SessionManager::instance().gui.show();
63 }
65 SessionManager&
66 SessionManager::instance()
67 {
68 if (!sessionManagerInstance)
69 sessionManagerInstance = new SessionManager();
70 return *sessionManagerInstance;
71 }
73 SessionManager::SessionManager()
74 {
75 getClient().addXmppEventListener(*this);
77 this->CheckPendingInvitations =
78 Glib::signal_timeout().connect(sigc::mem_fun(
79 *this, &SessionManager::checkInvitationQueue), 50);
80 }
82 SessionManager::~SessionManager()
83 {
84 getClient().removeXmppEventListener(*this);
85 getClient().disconnect();
86 }
88 /**
89 * Initiates a shared session with a user or conference room.
90 *
91 * \param to The recipient to which this desktop will be linked, specified as a JID.
92 * \param type Type of the session; i.e. private message or group chat.
93 */
94 void
95 SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type)
96 {
98 SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to);
99 InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
100 if(inkdoc == NULL) return;
102 if(type == State::WHITEBOARD_PEER)
103 {
104 ChooseDesktop dialog;
105 int result = dialog.run();
107 if(result == Gtk::RESPONSE_OK)
108 {
109 SPDesktop *desktop = dialog.getDesktop();
111 if(desktop != NULL)
112 {
113 Inkscape::XML::Document *old_doc =
114 sp_desktop_document(desktop)->rdoc;
115 inkdoc->root()->mergeFrom(old_doc->root(),"id");
116 }
117 }else { return; }
118 }
120 char * sessionId = createSessionId(10);
122 inkdoc->setSessionId(sessionId);
124 makeInkboardDesktop(doc);
125 addSession(WhiteboardRecord(sessionId, inkdoc));
127 inkdoc->startSessionNegotiation();
130 }
132 void
133 SessionManager::terminateSession(Glib::ustring const& sessionId)
134 {
135 WhiteboardList::iterator i = whiteboards.begin();
136 for(; i != whiteboards.end(); ++i) {
137 if ((*i).first == sessionId)
138 break;
139 }
141 if (i != whiteboards.end()) {
142 (*i).second->terminateSession();
143 whiteboards.erase(i);
144 }
145 }
147 void
148 SessionManager::addSession(WhiteboardRecord whiteboard)
149 {
150 whiteboards.push_back(whiteboard);
151 }
153 InkboardDocument*
154 SessionManager::getInkboardSession(Glib::ustring const& sessionId)
155 {
156 WhiteboardList::iterator i = whiteboards.begin();
157 for(; i != whiteboards.end(); ++i) {
158 if ((*i).first == sessionId) {
159 return (*i).second;
160 }
161 }
162 return NULL;
163 }
165 void
166 SessionManager::processXmppEvent(const Pedro::XmppEvent &event)
167 {
168 int type = event.getType();
170 switch (type) {
171 case Pedro::XmppEvent::EVENT_STATUS:
172 {
173 break;
174 }
175 case Pedro::XmppEvent::EVENT_ERROR:
176 {
177 break;
178 }
179 case Pedro::XmppEvent::EVENT_CONNECTED:
180 {
181 break;
182 }
183 case Pedro::XmppEvent::EVENT_DISCONNECTED:
184 {
185 break;
186 }
187 case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
188 case Pedro::XmppEvent::EVENT_MESSAGE:
189 {
190 Pedro::Element *root = event.getDOM();
192 if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS)
193 processWhiteboardEvent(event);
195 break;
196 }
197 case Pedro::XmppEvent::EVENT_PRESENCE:
198 {
199 break;
200 }
201 case Pedro::XmppEvent::EVENT_MUC_JOIN:
202 {
203 break;
204 }
205 case Pedro::XmppEvent::EVENT_MUC_LEAVE:
206 {
207 break;
208 }
209 case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
210 {
211 break;
212 }
213 default:
214 {
215 break;
216 }
217 }
218 }
220 /**
221 * Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages
222 * are handled here, as they have no InkboardDocument to be handled from, all other messages
223 * are passed to their appropriate Inkboard document, which is identified by the 'session'
224 * attribute of the 'wb' element
225 *
226 */
227 void
228 SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event)
229 {
230 Pedro::Element* root = event.getDOM();
231 if (root == NULL) {
232 g_warning("Received null DOM; ignoring message.");
233 return;
234 }
236 Pedro::DOMString session = root->getTagAttribute("wb", "session");
237 Pedro::DOMString type = root->getTagAttribute("session", "type");
238 Pedro::DOMString domwrapper = root->getFirstChild()->getFirstChild()->getFirstChild()->getName();
240 if (session.empty()) {
241 g_warning("Received incomplete Whiteboard message, missing session identifier; ignoring message.");
242 return;
243 }
245 if(root->exists(Message::CONNECT_REQUEST) && type == State::WHITEBOARD_PEER)
246 {
247 handleIncomingInvitation(Invitation(event.getFrom(),session));
249 }else
250 {
251 Message::Wrapper wrapper = static_cast< Message::Wrapper >(domwrapper);
252 InkboardDocument* doc = getInkboardSession(session);
253 doc->processInkboardEvent(wrapper, root->getFirstChild());
254 }
256 }
258 char*
259 SessionManager::createSessionId(int size)
260 {
261 // Create a random session identifier
262 char * randomString = (char*) malloc (size);
263 for (int n=0; n<size; n++)
264 randomString[n]=rand()%26+'a';
265 randomString[size+1]='\0';
267 return randomString;
268 }
270 /**
271 * Adds an invitation to a queue to be executed in SessionManager::_checkInvitationQueue()
272 * as when this method is called, we're still executing in Pedro's context, which causes
273 * issues when we run a dialog main loop.
274 *
275 */
276 void
277 SessionManager::handleIncomingInvitation(Invitation invitation)
278 {
279 // don't insert duplicate invitations
280 if (std::find(invitations.begin(),invitations.end(),invitation) != invitations.end())
281 return;
283 invitations.push_back(invitation);
285 }
287 bool
288 SessionManager::checkInvitationQueue()
289 {
290 // The user is currently busy with an action. Defer invitation processing
291 // until the user is free.
292 int x, y;
293 Gdk::ModifierType mt;
294 Gdk::Display::get_default()->get_pointer(x, y, mt);
295 if (mt & GDK_BUTTON1_MASK)
296 return true;
298 if (invitations.size() > 0)
299 {
300 // There's an invitation to process; process it.
301 Invitation invitation = invitations.front();
302 Glib::ustring from = invitation.first;
303 Glib::ustring sessionId = invitation.second;
305 Glib::ustring primary =
306 "<span weight=\"bold\" size=\"larger\">" +
307 String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), from) +
308 "</span>\n\n" +
309 String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), from);
311 InvitationConfirmDialog dialog(primary);
313 dialog.add_button(_("Accept invitation"), Dialog::ACCEPT_INVITATION);
314 dialog.add_button(_("Decline invitation"), Dialog::DECLINE_INVITATION);
316 Dialog::DialogReply reply = static_cast< Dialog::DialogReply >(dialog.run());
319 SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", State::WHITEBOARD_PEER, from);
321 InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
322 if(inkdoc == NULL) return true;
324 inkdoc->handleState(State::INITIAL,State::CONNECTING);
325 inkdoc->setSessionId(sessionId);
326 addSession(WhiteboardRecord(sessionId, inkdoc));
328 switch (reply) {
330 case Dialog::ACCEPT_INVITATION:{
331 inkdoc->sendProtocol(from, Message::PROTOCOL,Message::ACCEPT_INVITATION);
332 makeInkboardDesktop(doc);
333 break; }
335 case Dialog::DECLINE_INVITATION: default: {
336 inkdoc->sendProtocol(from, Message::PROTOCOL,Message::DECLINE_INVITATION);
337 terminateSession(sessionId);
338 break; }
339 }
341 invitations.pop_front();
343 }
345 return true;
346 }
348 //#########################################################################
349 //# HELPER FUNCTIONS
350 //#########################################################################
352 SPDocument*
353 makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to)
354 {
355 SPDocument* doc;
357 InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to);
358 rdoc->setAttribute("version", "1.0");
359 rdoc->setAttribute("standalone", "no");
360 XML::Node *comment = sp_repr_new_comment(" Created with Inkscape (http://www.inkscape.org/) ");
361 rdoc->appendChild(comment);
362 GC::release(comment);
364 XML::Node* root = sp_repr_new(rootname);
365 rdoc->appendChild(root);
366 GC::release(root);
368 Glib::ustring name = String::ucompose(
369 _("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to);
371 doc = sp_document_create(rdoc, NULL, NULL, name.c_str(), TRUE);
372 g_return_val_if_fail(doc != NULL, NULL);
374 return doc;
375 }
377 // TODO: When the switchover to the new GUI is complete, this function should go away
378 // and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop.
379 // It currently only exists to correctly mimic the desktop creation functionality
380 // in file.cpp.
381 //
382 // \see sp_file_new
383 SPDesktop*
384 makeInkboardDesktop(SPDocument* doc)
385 {
386 SPDesktop* dt;
388 if (NSApplication::Application::getNewGui())
389 dt = NSApplication::Editor::createDesktop(doc);
391 else
392 {
393 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
394 g_return_val_if_fail(dtw != NULL, NULL);
395 sp_document_unref(doc);
397 sp_create_window(dtw, TRUE);
398 dt = static_cast<SPDesktop*>(dtw->view);
399 sp_namedview_window_from_document(dt);
400 }
402 return dt;
403 }
405 } // namespace Whiteboard
407 } // namespace Inkscape
410 /*
411 Local Variables:
412 mode:c++
413 c-file-style:"stroustrup"
414 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
415 indent-tabs-mode:nil
416 fill-column:99
417 End:
418 */
419 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :