Code

Pot and Dutch translation update
[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 "document-private.h"
33 #include "interface.h"
34 #include "sp-namedview.h"
35 #include "document.h"
36 #include "desktop.h"
37 #include "desktop-handles.h"
39 #include "jabber_whiteboard/invitation-confirm-dialog.h"
40 #include "jabber_whiteboard/message-verifier.h"
41 #include "jabber_whiteboard/session-manager.h"
42 #include "jabber_whiteboard/inkboard-document.h"
43 #include "jabber_whiteboard/defines.h"
45 #include "jabber_whiteboard/dialog/choose-desktop.h"
47 namespace Inkscape {
49 namespace Whiteboard {
51 //#########################################################################
52 //# S E S S I O N    M A N A G E R
53 //#########################################################################
55 SessionManager *sessionManagerInstance = NULL;
57 void SessionManager::showClient()
58 {
59         SessionManager::instance().gui.show();
60 }
62 SessionManager&
63 SessionManager::instance()
64 {
65     if (!sessionManagerInstance)
66         sessionManagerInstance = new SessionManager();
67         return *sessionManagerInstance;
68 }
70 SessionManager::SessionManager() 
71 {
72     getClient().addXmppEventListener(*this);
74     this->CheckPendingInvitations = 
75         Glib::signal_timeout().connect(sigc::mem_fun(
76             *this, &SessionManager::checkInvitationQueue), 50);
77 }
79 SessionManager::~SessionManager()
80 {
81     getClient().removeXmppEventListener(*this);
82     getClient().disconnect();
83 }
85 /**
86  * Initiates a shared session with a user or conference room.
87  * 
88  * \param to The recipient to which this desktop will be linked, specified as a JID.
89  * \param type Type of the session; i.e. private message or group chat.
90  */
91 void
92 SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type)
93 {
95     SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to);
96     InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
97     if(inkdoc == NULL) return;
99     if(type == State::WHITEBOARD_PEER) 
100     {
101         ChooseDesktop dialog;
102         int result = dialog.run();
104         if(result == Gtk::RESPONSE_OK)
105         {
106             SPDesktop *desktop = dialog.getDesktop();
108             if(desktop != NULL)
109             {
110                 Inkscape::XML::Document *old_doc =
111                     sp_desktop_document(desktop)->rdoc;
112                 inkdoc->root()->mergeFrom(old_doc->root(),"id");
113             }
114         }else { return; }
115     }
117     char * sessionId = createSessionId(10);
119     inkdoc->setSessionId(sessionId);
121     makeInkboardDesktop(doc);
122     addSession(WhiteboardRecord(sessionId, inkdoc));
124     inkdoc->startSessionNegotiation();
129 void
130 SessionManager::terminateSession(Glib::ustring const& sessionId)
132     WhiteboardList::iterator i = whiteboards.begin();
133     for(; i != whiteboards.end(); ++i) {
134         if ((*i).first == sessionId) 
135             break;
136     }
138     if (i != whiteboards.end()) {
139         (*i).second->terminateSession();
140         whiteboards.erase(i);
141     }
144 void
145 SessionManager::addSession(WhiteboardRecord whiteboard)
147     whiteboards.push_back(whiteboard);
150 InkboardDocument*
151 SessionManager::getInkboardSession(Glib::ustring const& sessionId)
153     WhiteboardList::iterator i = whiteboards.begin();
154     for(; i != whiteboards.end(); ++i) {
155         if ((*i).first == sessionId) {
156             return (*i).second;
157         }
158     }
159     return NULL;
162 void
163 SessionManager::processXmppEvent(const Pedro::XmppEvent &event)
165     int type = event.getType();
167     switch (type) {
168         case Pedro::XmppEvent::EVENT_STATUS:
169             {
170             break;
171             }
172         case Pedro::XmppEvent::EVENT_ERROR:
173             {
174             break;
175             }
176         case Pedro::XmppEvent::EVENT_CONNECTED:
177             {
178             break;
179             }
180         case Pedro::XmppEvent::EVENT_DISCONNECTED:
181             {
182             break;
183             }
184         case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
185         case Pedro::XmppEvent::EVENT_MESSAGE:
186             {
187             Pedro::Element *root = event.getDOM();
189             if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS)
190                 processWhiteboardEvent(event);
192             break;
193             }
194         case Pedro::XmppEvent::EVENT_PRESENCE:
195             {
196             break;
197             }
198         case Pedro::XmppEvent::EVENT_MUC_JOIN:
199             {
200             break;
201             }
202         case Pedro::XmppEvent::EVENT_MUC_LEAVE:
203             {
204             break;
205             }
206         case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
207             {
208             break;
209             }
210         default:
211             {
212             break;
213             }
214     }
217 /**
218  * Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages
219  * are handled here, as they have no InkboardDocument to be handled from, all other messages
220  * are passed to their appropriate Inkboard document, which is identified by the 'session' 
221  * attribute of the 'wb' element
222  *
223  */
224 void
225 SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event)
227     Pedro::Element* root = event.getDOM();
228     if (root == NULL) {
229         g_warning("Received null DOM; ignoring message.");
230         return;
231     }
233     Pedro::DOMString session = root->getTagAttribute("wb", "session");
234     Pedro::DOMString type = root->getTagAttribute("message", "type");
235     Pedro::DOMString domwrapper = root->getFirstChild()->getFirstChild()->getFirstChild()->getName();
237     if (session.empty()) {
238         g_warning("Received incomplete Whiteboard message, missing session identifier; ignoring message.");
239         return;
240     }
242     if(root->exists(Message::CONNECT_REQUEST) && type == State::WHITEBOARD_PEER)
243     {
244         handleIncomingInvitation(Invitation(event.getFrom(),session));
246     }else
247     { 
248         Message::Wrapper wrapper = static_cast< Message::Wrapper >(domwrapper);
249         InkboardDocument* doc = getInkboardSession(session);
251         if(doc != NULL)
252             doc->recieve(wrapper, root->getFirstChild());
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);
319         InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
320         if(inkdoc == NULL) return true;
322         inkdoc->handleState(State::INITIAL,State::CONNECTING);
323         inkdoc->setSessionId(sessionId);
324         addSession(WhiteboardRecord(sessionId, inkdoc));
326         switch (reply) {
328             case Dialog::ACCEPT_INVITATION:{
329                 inkdoc->send(from, Message::PROTOCOL,Message::ACCEPT_INVITATION);
330                 makeInkboardDesktop(doc);
331                 break; }
333             case Dialog::DECLINE_INVITATION: default: {
334                 inkdoc->send(from, Message::PROTOCOL,Message::DECLINE_INVITATION);
335                 terminateSession(sessionId);
336                 break; }
337         }
339         invitations.pop_front();
341     }
343     return true;
346 //#########################################################################
347 //# HELPER FUNCTIONS
348 //#########################################################################
350 SPDocument*
351 makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to)
353     SPDocument* doc;
355     InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to);
356     rdoc->setAttribute("version", "1.0");
357     rdoc->setAttribute("standalone", "no");
358     XML::Node *comment = rdoc->createComment(" Created with Inkscape (http://www.inkscape.org/) ");
359     rdoc->appendChild(comment);
360     GC::release(comment);
362     XML::Node* root = rdoc->createElement(rootname);
363     rdoc->appendChild(root);
364     GC::release(root);
366     Glib::ustring name = String::ucompose(
367         _("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to);
369     doc = sp_document_create(rdoc, NULL, NULL, name.c_str(), TRUE);
370     g_return_val_if_fail(doc != NULL, NULL);
372     return doc;
375 // TODO: When the switchover to the new GUI is complete, this function should go away
376 // and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop.  
377 // It currently only exists to correctly mimic the desktop creation functionality
378 // in file.cpp.
379 //
380 // \see sp_file_new
381 SPDesktop*
382 makeInkboardDesktop(SPDocument* doc)
384     SPDesktop* dt;
386     SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
387     g_return_val_if_fail(dtw != NULL, NULL);
388     sp_document_unref(doc);
390     sp_create_window(dtw, TRUE);
391     dt = static_cast<SPDesktop*>(dtw->view);
392     sp_namedview_window_from_document(dt);
393     sp_namedview_update_layers_from_document(dt);
395     return dt;
398 }  // namespace Whiteboard
399  
400 }  // namespace Inkscape
403 /*
404   Local Variables:
405   mode:c++
406   c-file-style:"stroustrup"
407   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
408   indent-tabs-mode:nil
409   fill-column:99
410   End:
411 */
412 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :