Code

refactored session establishment
[inkscape.git] / src / jabber_whiteboard / session-manager.cpp
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 void
89 SessionManager::processXmppEvent(const Pedro::XmppEvent &event)
90 {
91     int type = event.getType();
93     switch (type) {
94         case Pedro::XmppEvent::EVENT_STATUS:
95             {
96             break;
97             }
98         case Pedro::XmppEvent::EVENT_ERROR:
99             {
100             break;
101             }
102         case Pedro::XmppEvent::EVENT_CONNECTED:
103             {
104             break;
105             }
106         case Pedro::XmppEvent::EVENT_DISCONNECTED:
107             {
108             break;
109             }
110         case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
111         case Pedro::XmppEvent::EVENT_MESSAGE:
112             {
113             g_warning("## SM message:%s\n", event.getFrom().c_str());
114             Pedro::Element *root = event.getDOM();
116             if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS)
117                 processWhiteboardEvent(event);
119             break;
120             }
121         case Pedro::XmppEvent::EVENT_PRESENCE:
122             {
123             break;
124             }
125         case Pedro::XmppEvent::EVENT_MUC_JOIN:
126             {
127             break;
128             }
129         case Pedro::XmppEvent::EVENT_MUC_LEAVE:
130             {
131             break;
132             }
133         case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
134             {
135             break;
136             }
137         default:
138             {
139             break;
140             }
141     }
144 /**
145  * Initiates a shared session with a user or conference room.
146  * 
147  * \param to The recipient to which this desktop will be linked, specified as a JID.
148  * \param type Type of the session; i.e. private message or group chat.
149  */
150 void
151 SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type)
154     SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to);
155     InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
156     if(inkdoc == NULL) return;
158     if(type == State::WHITEBOARD_PEER) 
159     {
160         ChooseDesktop dialog;
161         int result = dialog.run();
163         if(result == Gtk::RESPONSE_OK)
164         {
165             SPDesktop *desktop = dialog.getDesktop();
167             if(desktop != NULL)
168             {
169                 Inkscape::XML::Document *old_doc =
170                     sp_desktop_document(desktop)->rdoc;
171                 inkdoc->root()->mergeFrom(old_doc->root(),"id");
172             }
173         }else { return; }
174     }
176     char * sessionId = createSessionId(10);
178     inkdoc->setSessionIdent(sessionId);
180     addSession(WhiteboardRecord(sessionId, inkdoc));
182     inkdoc->startSessionNegotiation();
187 void
188 SessionManager::terminateSession(Glib::ustring const& sessionId)
190     WhiteboardList::iterator i = whiteboards.begin();
191     for(; i != whiteboards.end(); ++i) {
192         if ((*i).first == sessionId) 
193             break;
194     }
196     if (i != whiteboards.end()) {
197         (*i).second->terminateSession();
198         whiteboards.erase(i);
199     }
202 void
203 SessionManager::addSession(WhiteboardRecord whiteboard)
205     whiteboards.push_back(whiteboard);
208 InkboardDocument*
209 SessionManager::getInkboardSession(Glib::ustring const& sessionId)
211     WhiteboardList::iterator i = whiteboards.begin();
212     for(; i != whiteboards.end(); ++i) {
213         if ((*i).first == sessionId) {
214             return (*i).second;
215         }
216     }
217     return NULL;
221 /**
222  * Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages
223  * are handled here, as they have no InkboardDocument to be handled from, all other messages
224  * are passed to their appropriate Inkboard document, which is identified by the 'session' 
225  * attribute of the 'wb' element
226  *
227  */
228 void
229 SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event)
231     Pedro::Element* root = event.getDOM();
232     if (root == NULL) {
233         g_warning("Received null DOM; ignoring message.");
234         return;
235     }
237     Pedro::DOMString session = root->getTagAttribute("wb", "session");
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))
246         handleIncomingInvitation(Invitation(event.getFrom(),session));
248     else
249     { 
250         Message::Wrapper wrapper = domwrapper.c_str();
251         InkboardDocument* doc = getInkboardSession(session);
252         doc->processInkboardEvent(wrapper, event.getData());
253     }
256 char*
257 SessionManager::createSessionId(int size)
259     // Create a random session identifier
260     char * randomString = (char*) malloc (size);
261     for (int n=0; n<size; n++)
262         randomString[n]=rand()%26+'a';
263     randomString[size+1]='\0';
265     return randomString;
268 /**
269  * Adds an invitation to a queue to be executed in SessionManager::_checkInvitationQueue()
270  * as when this method is called, we're still executing in Pedro's context, which causes 
271  * issues when we run a dialog main loop.
272  *
273  */
274 void
275 SessionManager::handleIncomingInvitation(Invitation invitation)
277     // don't insert duplicate invitations
278     if (std::find(invitations.begin(),invitations.end(),invitation) != invitations.end())
279         return;
281     invitations.push_back(invitation);
285 bool
286 SessionManager::checkInvitationQueue()
288     // The user is currently busy with an action.  Defer invitation processing 
289     // until the user is free.
290     int x, y;
291     Gdk::ModifierType mt;
292     Gdk::Display::get_default()->get_pointer(x, y, mt);
293     if (mt & GDK_BUTTON1_MASK) 
294         return true;
296     if (invitations.size() > 0) 
297     {
298         // There's an invitation to process; process it.
299         Invitation invitation = invitations.front();
300         Glib::ustring from = invitation.first;
301         Glib::ustring sessionId = invitation.second;
303         Glib::ustring primary = 
304             "<span weight=\"bold\" size=\"larger\">" + 
305             String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), from) + 
306             "</span>\n\n" + 
307             String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), from);
309         InvitationConfirmDialog dialog(primary);
311         dialog.add_button(_("Accept invitation"), Dialog::ACCEPT_INVITATION);
312         dialog.add_button(_("Decline invitation"), Dialog::DECLINE_INVITATION);
314         Dialog::DialogReply reply = static_cast< Dialog::DialogReply >(dialog.run());
317         SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", State::WHITEBOARD_PEER, from);
318         InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
319         if(inkdoc == NULL) return true;
321         addSession(WhiteboardRecord(sessionId, inkdoc));
323         switch (reply) {
325             case Dialog::ACCEPT_INVITATION:{
326                 inkdoc->sendProtocol(from, Message::PROTOCOL,Message::ACCEPT_INVITATION);
327                 makeInkboardDesktop(doc);
328                 break; }
330             case Dialog::DECLINE_INVITATION: default: {
331                 inkdoc->sendProtocol(from, Message::PROTOCOL,Message::DECLINE_INVITATION);
332                 terminateSession(sessionId);
333                 break; }
334         }
336         invitations.pop_front();
338     }
340     return true;
343 //#########################################################################
344 //# HELPER FUNCTIONS
345 //#########################################################################
347 SPDocument*
348 makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to)
350     SPDocument* doc;
352     InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to);
353     rdoc->setAttribute("version", "1.0");
354     rdoc->setAttribute("standalone", "no");
355     XML::Node *comment = sp_repr_new_comment(" Created with Inkscape (http://www.inkscape.org/) ");
356     rdoc->appendChild(comment);
357     GC::release(comment);
359     XML::Node* root = sp_repr_new(rootname);
360     rdoc->appendChild(root);
361     GC::release(root);
363     Glib::ustring name = String::ucompose(
364         _("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to);
366     doc = sp_document_create(rdoc, NULL, NULL, name.c_str(), TRUE);
367     g_return_val_if_fail(doc != NULL, NULL);
369     return doc;
372 // TODO: When the switchover to the new GUI is complete, this function should go away
373 // and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop.  
374 // It currently only exists to correctly mimic the desktop creation functionality
375 // in file.cpp.
376 //
377 // \see sp_file_new
378 SPDesktop*
379 makeInkboardDesktop(SPDocument* doc)
381     SPDesktop* dt;
383     if (NSApplication::Application::getNewGui()) 
384         dt = NSApplication::Editor::createDesktop(doc);
386     else 
387     {
388         SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
389         g_return_val_if_fail(dtw != NULL, NULL);
390         sp_document_unref(doc);
392         sp_create_window(dtw, TRUE);
393         dt = static_cast<SPDesktop*>(dtw->view);
394         sp_namedview_window_from_document(dt);
395     }
397     return dt;
400 }  // namespace Whiteboard
401  
402 }  // namespace Inkscape
405 /*
406   Local Variables:
407   mode:c++
408   c-file-style:"stroustrup"
409   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
410   indent-tabs-mode:nil
411   fill-column:99
412   End:
413 */
414 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :