Code

Merge and cleanup of GSoC C++-ification project.
[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  *   Abhishek Sharma
8  *
9  * Copyright (c) 2005 Authors
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 #include <functional>
15 #include <algorithm>
16 #include <iostream>
17 #include <time.h>
19 #include <gtkmm.h>
20 #include <glibmm/i18n.h>
22 #include "xml/node.h"
23 #include "xml/repr.h"
25 #include "util/ucompose.hpp"
27 #include "xml/node-observer.h"
29 #include "pedro/pedrodom.h"
31 #include "ui/view/view-widget.h"
33 #include "document-private.h"
34 #include "interface.h"
35 #include "sp-namedview.h"
36 #include "document.h"
37 #include "desktop.h"
38 #include "desktop-handles.h"
40 #include "jabber_whiteboard/invitation-confirm-dialog.h"
41 #include "jabber_whiteboard/message-verifier.h"
42 #include "jabber_whiteboard/session-manager.h"
43 #include "jabber_whiteboard/inkboard-document.h"
44 #include "jabber_whiteboard/defines.h"
46 #include "jabber_whiteboard/dialog/choose-desktop.h"
48 namespace Inkscape {
50 namespace Whiteboard {
52 //#########################################################################
53 //# S E S S I O N    M A N A G E R
54 //#########################################################################
56 SessionManager *sessionManagerInstance = NULL;
58 void SessionManager::showClient()
59 {
60     SessionManager::instance().gui.show();
61 }
63 SessionManager &SessionManager::instance()
64 {
65     if (!sessionManagerInstance) {
66         sessionManagerInstance = new SessionManager();
67     }
68     return *sessionManagerInstance;
69 }
71 SessionManager::SessionManager() 
72 {
73     getClient().addXmppEventListener(*this);
75     this->CheckPendingInvitations = 
76         Glib::signal_timeout().connect(sigc::mem_fun(
77             *this, &SessionManager::checkInvitationQueue), 50);
78 }
80 SessionManager::~SessionManager()
81 {
82     getClient().removeXmppEventListener(*this);
83     getClient().disconnect();
84 }
86 /**
87  * Initiates a shared session with a user or conference room.
88  * 
89  * \param to The recipient to which this desktop will be linked, specified as a JID.
90  * \param type Type of the session; i.e. private message or group chat.
91  */
92 void
93 SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type)
94 {
96     SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to);
97     InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
98     if(inkdoc == NULL) return;
100     if(type == State::WHITEBOARD_PEER) 
101     {
102         ChooseDesktop dialog;
103         int result = dialog.run();
105         if(result == Gtk::RESPONSE_OK)
106         {
107             SPDesktop *desktop = dialog.getDesktop();
109             if(desktop != NULL)
110             {
111                 Inkscape::XML::Document *old_doc =
112                     sp_desktop_document(desktop)->rdoc;
113                 inkdoc->root()->mergeFrom(old_doc->root(),"id");
114             }
115         }else { return; }
116     }
118     char * sessionId = createSessionId(10);
120     inkdoc->setSessionId(sessionId);
122     makeInkboardDesktop(doc);
123     addSession(WhiteboardRecord(sessionId, inkdoc));
125     inkdoc->startSessionNegotiation();
130 void
131 SessionManager::terminateSession(Glib::ustring const& sessionId)
133     WhiteboardList::iterator i = whiteboards.begin();
134     for(; i != whiteboards.end(); ++i) {
135         if ((*i).first == sessionId) 
136             break;
137     }
139     if (i != whiteboards.end()) {
140         (*i).second->terminateSession();
141         whiteboards.erase(i);
142     }
145 void
146 SessionManager::addSession(WhiteboardRecord whiteboard)
148     whiteboards.push_back(whiteboard);
151 InkboardDocument*
152 SessionManager::getInkboardSession(Glib::ustring const& sessionId)
154     WhiteboardList::iterator i = whiteboards.begin();
155     for(; i != whiteboards.end(); ++i) {
156         if ((*i).first == sessionId) {
157             return (*i).second;
158         }
159     }
160     return NULL;
163 void
164 SessionManager::processXmppEvent(const Pedro::XmppEvent &event)
166     int type = event.getType();
168     switch (type) {
169         case Pedro::XmppEvent::EVENT_STATUS:
170             {
171             break;
172             }
173         case Pedro::XmppEvent::EVENT_ERROR:
174             {
175             break;
176             }
177         case Pedro::XmppEvent::EVENT_CONNECTED:
178             {
179             break;
180             }
181         case Pedro::XmppEvent::EVENT_DISCONNECTED:
182             {
183             break;
184             }
185         case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
186         case Pedro::XmppEvent::EVENT_MESSAGE:
187             {
188             Pedro::Element *root = event.getDOM();
190             if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS)
191                 processWhiteboardEvent(event);
193             break;
194             }
195         case Pedro::XmppEvent::EVENT_PRESENCE:
196             {
197             break;
198             }
199         case Pedro::XmppEvent::EVENT_MUC_JOIN:
200             {
201             break;
202             }
203         case Pedro::XmppEvent::EVENT_MUC_LEAVE:
204             {
205             break;
206             }
207         case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
208             {
209             break;
210             }
211         default:
212             {
213             break;
214             }
215     }
218 /**
219  * Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages
220  * are handled here, as they have no InkboardDocument to be handled from, all other messages
221  * are passed to their appropriate Inkboard document, which is identified by the 'session' 
222  * attribute of the 'wb' element
223  *
224  */
225 void
226 SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event)
228     Pedro::Element* root = event.getDOM();
229     if (root == NULL) {
230         g_warning("Received null DOM; ignoring message.");
231         return;
232     }
234     Pedro::DOMString session = root->getTagAttribute("wb", "session");
235     Pedro::DOMString type = root->getTagAttribute("message", "type");
236     Pedro::DOMString domwrapper = root->getFirstChild()->getFirstChild()->getFirstChild()->getName();
238     if (session.empty()) {
239         g_warning("Received incomplete Whiteboard message, missing session identifier; ignoring message.");
240         return;
241     }
243     if(root->exists(Message::CONNECT_REQUEST) && type == State::WHITEBOARD_PEER)
244     {
245         handleIncomingInvitation(Invitation(event.getFrom(),session));
247     }else
248     { 
249         Message::Wrapper wrapper = static_cast< Message::Wrapper >(domwrapper);
250         InkboardDocument* doc = getInkboardSession(session);
252         if(doc != NULL)
253             doc->recieve(wrapper, root->getFirstChild());
254     }
257 char*
258 SessionManager::createSessionId(int size)
260     // Create a random session identifier
261     char * randomString = (char*) malloc (size);
262     for (int n=0; n<size; n++)
263         randomString[n]=rand()%26+'a';
264     randomString[size+1]='\0';
266     return randomString;
269 /**
270  * Adds an invitation to a queue to be executed in SessionManager::_checkInvitationQueue()
271  * as when this method is called, we're still executing in Pedro's context, which causes 
272  * issues when we run a dialog main loop.
273  *
274  */
275 void
276 SessionManager::handleIncomingInvitation(Invitation invitation)
278     // don't insert duplicate invitations
279     if (std::find(invitations.begin(),invitations.end(),invitation) != invitations.end())
280         return;
282     invitations.push_back(invitation);
286 bool
287 SessionManager::checkInvitationQueue()
289     // The user is currently busy with an action.  Defer invitation processing 
290     // until the user is free.
291     int x, y;
292     Gdk::ModifierType mt;
293     Gdk::Display::get_default()->get_pointer(x, y, mt);
294     if (mt & GDK_BUTTON1_MASK) 
295         return true;
297     if (invitations.size() > 0) 
298     {
299         // There's an invitation to process; process it.
300         Invitation invitation = invitations.front();
301         Glib::ustring from = invitation.first;
302         Glib::ustring sessionId = invitation.second;
304         Glib::ustring primary = 
305             "<span weight=\"bold\" size=\"larger\">" + 
306             String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), from) + 
307             "</span>\n\n" + 
308             String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), from);
310         InvitationConfirmDialog dialog(primary);
312         dialog.add_button(_("Accept invitation"), Dialog::ACCEPT_INVITATION);
313         dialog.add_button(_("Decline invitation"), Dialog::DECLINE_INVITATION);
315         Dialog::DialogReply reply = static_cast< Dialog::DialogReply >(dialog.run());
318         SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", State::WHITEBOARD_PEER, from);
320         InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
321         if(inkdoc == NULL) return true;
323         inkdoc->handleState(State::INITIAL,State::CONNECTING);
324         inkdoc->setSessionId(sessionId);
325         addSession(WhiteboardRecord(sessionId, inkdoc));
327         switch (reply) {
329             case Dialog::ACCEPT_INVITATION:{
330                 inkdoc->send(from, Message::PROTOCOL,Message::ACCEPT_INVITATION);
331                 makeInkboardDesktop(doc);
332                 break; }
334             case Dialog::DECLINE_INVITATION: default: {
335                 inkdoc->send(from, Message::PROTOCOL,Message::DECLINE_INVITATION);
336                 terminateSession(sessionId);
337                 break; }
338         }
340         invitations.pop_front();
342     }
344     return true;
347 //#########################################################################
348 //# HELPER FUNCTIONS
349 //#########################################################################
351 SPDocument*
352 makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to)
354     SPDocument* doc;
356     InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to);
357     rdoc->setAttribute("version", "1.0");
358     rdoc->setAttribute("standalone", "no");
359     XML::Node *comment = rdoc->createComment(" Created with Inkscape (http://www.inkscape.org/) ");
360     rdoc->appendChild(comment);
361     GC::release(comment);
363     XML::Node* root = rdoc->createElement(rootname);
364     rdoc->appendChild(root);
365     GC::release(root);
367     Glib::ustring name = String::ucompose(
368         _("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to);
370     doc = SPDocument::createDoc(rdoc, NULL, NULL, name.c_str(), TRUE);
371     g_return_val_if_fail(doc != NULL, NULL);
373     return doc;
376 // TODO: When the switchover to the new GUI is complete, this function should go away
377 // and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop.  
378 // It currently only exists to correctly mimic the desktop creation functionality
379 // in file.cpp.
380 //
381 // \see sp_file_new
382 SPDesktop *makeInkboardDesktop(SPDocument* doc)
384     SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
385     g_return_val_if_fail(dtw != NULL, NULL);
386     doc->doUnref();
388     sp_create_window(dtw, TRUE);
389     SPDesktop *dt = static_cast<SPDesktop*>(dtw->view);
390     sp_namedview_window_from_document(dt);
391     sp_namedview_update_layers_from_document(dt);
393     return dt;
396 }  // namespace Whiteboard
397  
398 }  // namespace Inkscape
401 /*
402   Local Variables:
403   mode:c++
404   c-file-style:"stroustrup"
405   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
406   indent-tabs-mode:nil
407   fill-column:99
408   End:
409 */
410 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :