session-manager.cpp revision a4030d5ca449e7e384bc699cd249ee704faaeab0
/**
* Whiteboard session manager
*
* Authors:
* David Yip <yipdw@rose-hulman.edu>
* Bob Jamison (Pedro port)
*
* Copyright (c) 2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <functional>
#include <algorithm>
#include <iostream>
#include <time.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>
#include "xml/node.h"
#include "xml/repr.h"
#include "util/ucompose.hpp"
#include "xml/node-observer.h"
#include "pedro/pedrodom.h"
#include "ui/view/view-widget.h"
#include "document-private.h"
#include "interface.h"
#include "sp-namedview.h"
#include "document.h"
#include "desktop.h"
#include "desktop-handles.h"
#include "jabber_whiteboard/invitation-confirm-dialog.h"
#include "jabber_whiteboard/message-verifier.h"
#include "jabber_whiteboard/session-manager.h"
#include "jabber_whiteboard/inkboard-document.h"
#include "jabber_whiteboard/defines.h"
#include "jabber_whiteboard/dialog/choose-desktop.h"
namespace Inkscape {
namespace Whiteboard {
//#########################################################################
//# S E S S I O N M A N A G E R
//#########################################################################
SessionManager *sessionManagerInstance = NULL;
void SessionManager::showClient()
{
SessionManager::instance().gui.show();
}
SessionManager&
SessionManager::instance()
{
if (!sessionManagerInstance)
sessionManagerInstance = new SessionManager();
return *sessionManagerInstance;
}
SessionManager::SessionManager()
{
getClient().addXmppEventListener(*this);
this->CheckPendingInvitations =
Glib::signal_timeout().connect(sigc::mem_fun(
*this, &SessionManager::checkInvitationQueue), 50);
}
SessionManager::~SessionManager()
{
getClient().removeXmppEventListener(*this);
getClient().disconnect();
}
/**
* Initiates a shared session with a user or conference room.
*
* \param to The recipient to which this desktop will be linked, specified as a JID.
* \param type Type of the session; i.e. private message or group chat.
*/
void
SessionManager::initialiseSession(Glib::ustring const& to, State::SessionType type)
{
SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", type, to);
InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
if(inkdoc == NULL) return;
if(type == State::WHITEBOARD_PEER)
{
ChooseDesktop dialog;
int result = dialog.run();
if(result == Gtk::RESPONSE_OK)
{
SPDesktop *desktop = dialog.getDesktop();
if(desktop != NULL)
{
Inkscape::XML::Document *old_doc =
sp_desktop_document(desktop)->rdoc;
inkdoc->root()->mergeFrom(old_doc->root(),"id");
}
}else { return; }
}
char * sessionId = createSessionId(10);
inkdoc->setSessionId(sessionId);
makeInkboardDesktop(doc);
addSession(WhiteboardRecord(sessionId, inkdoc));
inkdoc->startSessionNegotiation();
}
void
SessionManager::terminateSession(Glib::ustring const& sessionId)
{
WhiteboardList::iterator i = whiteboards.begin();
for(; i != whiteboards.end(); ++i) {
if ((*i).first == sessionId)
break;
}
if (i != whiteboards.end()) {
(*i).second->terminateSession();
whiteboards.erase(i);
}
}
void
SessionManager::addSession(WhiteboardRecord whiteboard)
{
whiteboards.push_back(whiteboard);
}
InkboardDocument*
SessionManager::getInkboardSession(Glib::ustring const& sessionId)
{
WhiteboardList::iterator i = whiteboards.begin();
for(; i != whiteboards.end(); ++i) {
if ((*i).first == sessionId) {
return (*i).second;
}
}
return NULL;
}
void
SessionManager::processXmppEvent(const Pedro::XmppEvent &event)
{
int type = event.getType();
switch (type) {
case Pedro::XmppEvent::EVENT_STATUS:
{
break;
}
case Pedro::XmppEvent::EVENT_ERROR:
{
break;
}
case Pedro::XmppEvent::EVENT_CONNECTED:
{
break;
}
case Pedro::XmppEvent::EVENT_DISCONNECTED:
{
break;
}
case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
case Pedro::XmppEvent::EVENT_MESSAGE:
{
Pedro::Element *root = event.getDOM();
if (root && root->getTagAttribute("wb", "xmlns") == Vars::INKBOARD_XMLNS)
processWhiteboardEvent(event);
break;
}
case Pedro::XmppEvent::EVENT_PRESENCE:
{
break;
}
case Pedro::XmppEvent::EVENT_MUC_JOIN:
{
break;
}
case Pedro::XmppEvent::EVENT_MUC_LEAVE:
{
break;
}
case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
{
break;
}
default:
{
break;
}
}
}
/**
* Handles all incoming messages from pedro within a valid namespace, CONNECT_REQUEST messages
* are handled here, as they have no InkboardDocument to be handled from, all other messages
* are passed to their appropriate Inkboard document, which is identified by the 'session'
* attribute of the 'wb' element
*
*/
void
SessionManager::processWhiteboardEvent(Pedro::XmppEvent const& event)
{
Pedro::Element* root = event.getDOM();
if (root == NULL) {
g_warning("Received null DOM; ignoring message.");
return;
}
Pedro::DOMString session = root->getTagAttribute("wb", "session");
Pedro::DOMString type = root->getTagAttribute("message", "type");
Pedro::DOMString domwrapper = root->getFirstChild()->getFirstChild()->getFirstChild()->getName();
if (session.empty()) {
g_warning("Received incomplete Whiteboard message, missing session identifier; ignoring message.");
return;
}
if(root->exists(Message::CONNECT_REQUEST) && type == State::WHITEBOARD_PEER)
{
handleIncomingInvitation(Invitation(event.getFrom(),session));
}else
{
Message::Wrapper wrapper = static_cast< Message::Wrapper >(domwrapper);
InkboardDocument* doc = getInkboardSession(session);
if(doc != NULL)
doc->recieve(wrapper, root->getFirstChild());
}
}
char*
SessionManager::createSessionId(int size)
{
// Create a random session identifier
char * randomString = (char*) malloc (size);
for (int n=0; n<size; n++)
randomString[n]=rand()%26+'a';
randomString[size+1]='\0';
return randomString;
}
/**
* Adds an invitation to a queue to be executed in SessionManager::_checkInvitationQueue()
* as when this method is called, we're still executing in Pedro's context, which causes
* issues when we run a dialog main loop.
*
*/
void
SessionManager::handleIncomingInvitation(Invitation invitation)
{
// don't insert duplicate invitations
if (std::find(invitations.begin(),invitations.end(),invitation) != invitations.end())
return;
invitations.push_back(invitation);
}
bool
SessionManager::checkInvitationQueue()
{
// The user is currently busy with an action. Defer invitation processing
// until the user is free.
int x, y;
Gdk::ModifierType mt;
Gdk::Display::get_default()->get_pointer(x, y, mt);
if (mt & GDK_BUTTON1_MASK)
return true;
if (invitations.size() > 0)
{
// There's an invitation to process; process it.
Invitation invitation = invitations.front();
Glib::ustring from = invitation.first;
Glib::ustring sessionId = invitation.second;
Glib::ustring primary =
"<span weight=\"bold\" size=\"larger\">" +
String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), from) +
"</span>\n\n" +
String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), from);
InvitationConfirmDialog dialog(primary);
dialog.add_button(_("Accept invitation"), Dialog::ACCEPT_INVITATION);
dialog.add_button(_("Decline invitation"), Dialog::DECLINE_INVITATION);
Dialog::DialogReply reply = static_cast< Dialog::DialogReply >(dialog.run());
SPDocument* doc = makeInkboardDocument(g_quark_from_static_string("xml"), "svg:svg", State::WHITEBOARD_PEER, from);
InkboardDocument* inkdoc = dynamic_cast< InkboardDocument* >(doc->rdoc);
if(inkdoc == NULL) return true;
inkdoc->handleState(State::INITIAL,State::CONNECTING);
inkdoc->setSessionId(sessionId);
addSession(WhiteboardRecord(sessionId, inkdoc));
switch (reply) {
case Dialog::ACCEPT_INVITATION:{
inkdoc->send(from, Message::PROTOCOL,Message::ACCEPT_INVITATION);
makeInkboardDesktop(doc);
break; }
case Dialog::DECLINE_INVITATION: default: {
inkdoc->send(from, Message::PROTOCOL,Message::DECLINE_INVITATION);
terminateSession(sessionId);
break; }
}
invitations.pop_front();
}
return true;
}
//#########################################################################
//# HELPER FUNCTIONS
//#########################################################################
SPDocument*
makeInkboardDocument(int code, gchar const* rootname, State::SessionType type, Glib::ustring const& to)
{
SPDocument* doc;
InkboardDocument* rdoc = new InkboardDocument(g_quark_from_static_string("xml"), type, to);
rdoc->setAttribute("version", "1.0");
rdoc->setAttribute("standalone", "no");
XML::Node *comment = rdoc->createComment(" Created with Inkscape (http://www.inkscape.org/) ");
rdoc->appendChild(comment);
GC::release(comment);
XML::Node* root = rdoc->createElement(rootname);
rdoc->appendChild(root);
GC::release(root);
Glib::ustring name = String::ucompose(
_("Inkboard session (%1 to %2)"), SessionManager::instance().getClient().getJid(), to);
doc = sp_document_create(rdoc, NULL, NULL, name.c_str(), TRUE);
g_return_val_if_fail(doc != NULL, NULL);
return doc;
}
// TODO: When the switchover to the new GUI is complete, this function should go away
// and be replaced with a call to Inkscape::NSApplication::Editor::createDesktop.
// It currently only exists to correctly mimic the desktop creation functionality
// in file.cpp.
//
// \see sp_file_new
SPDesktop*
makeInkboardDesktop(SPDocument* doc)
{
SPDesktop* dt;
SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
g_return_val_if_fail(dtw != NULL, NULL);
sp_document_unref(doc);
sp_create_window(dtw, TRUE);
dt = static_cast<SPDesktop*>(dtw->view);
sp_namedview_window_from_document(dt);
sp_namedview_update_layers_from_document(dt);
return dt;
}
} // namespace Whiteboard
} // namespace Inkscape
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :