Logo Search packages:      
Sourcecode: inkscape version File versions  Download package

session-manager.cpp

/**
 * Whiteboard session manager
 *
 * Authors:
 * David Yip <yipdw@rose-hulman.edu>
 *
 * Copyright (c) 2005 Authors
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

/*
#include "inkscape.h"
*/

#include <cstring>

#include <glibmm/i18n.h>
#include <gtkmm/dialog.h>
#include <gtkmm/messagedialog.h>

#include "gc-anchored.h"

#include "xml/repr.h"
#include "xml/node-observer.h"

#include "util/ucompose.hpp"

#include "message-context.h"
#include "message-stack.h"
#include "desktop-handles.h"
#include "document.h"
#include "document-private.h"
#include "verbs.h"

#include "jabber_whiteboard/defines.h"
#include "jabber_whiteboard/typedefs.h"
#include "jabber_whiteboard/deserializer.h"
#include "jabber_whiteboard/message-utilities.h"
#include "jabber_whiteboard/message-handler.h"
#include "jabber_whiteboard/node-tracker.h"
#include "jabber_whiteboard/jabber-handlers.h"
#include "jabber_whiteboard/callbacks.h"
#include "jabber_whiteboard/chat-handler.h"
#include "jabber_whiteboard/session-file.h"
#include "jabber_whiteboard/session-file-player.h"
#include "jabber_whiteboard/session-manager.h"
#include "jabber_whiteboard/message-aggregator.h"
#include "jabber_whiteboard/undo-stack-observer.h"
#include "jabber_whiteboard/serializer.h"

//#include "jabber_whiteboard/pedro/pedroxmpp.h"

#include "jabber_whiteboard/message-node.h"
#include "jabber_whiteboard/message-queue.h"

namespace Inkscape {

namespace Whiteboard {

#ifdef WIN32
static bool lm_initialize_called = false;
#endif

00065 SessionData::SessionData(SessionManager *sm)
{
      this->_sm = sm;
      this->recipient = NULL;
      this->connection = NULL;
      this->ssl = NULL;
      this->ignoreFurtherSSLErrors = false;
      this->send_queue = new SendMessageQueue(sm);
      this->sequence_number = 1;
}

SessionData::~SessionData()
{
      this->receive_queues.clear();

      if (this->send_queue) {
            delete this->send_queue;
      }
}

00085 SessionManager::SessionManager(::SPDesktop *desktop) 
{

      // Initialize private members to NULL to facilitate deletion in destructor
      this->_myDoc = NULL;
      this->session_data = NULL;
      this->_myCallbacks = NULL;
      this->_myTracker = NULL;
      this->_myChatHandler = NULL;
      this->_mySessionFile = NULL;
      this->_mySessionPlayer = NULL;
      this->_myMessageHandler = NULL;
      this->_myUndoObserver = NULL;
      this->_mySerializer = NULL;
      this->_myDeserializer = NULL;

      this->setDesktop(desktop);

      if (this->_myDoc == NULL) {
            g_error("Initializing SessionManager on null document object!");
      }


#ifdef WIN32
    //# lm_initialize() must be called before any network code
    if (!lm_initialize_called) {
        lm_initialize();
        lm_initialize_called = true;
      }
#endif

      this->_setVerbSensitivity(INITIAL);
}

SessionManager::~SessionManager()
{

      if (this->session_data) {
            if (this->session_data->status[IN_WHITEBOARD]) {
                  // also calls closeSession
                  this->disconnectFromDocument();
            }
            this->disconnectFromServer();

            if (this->session_data->status[LOGGED_IN]) {
                  // TODO: unref message handlers
            }
      }

      if (this->_mySessionFile) {
            delete this->_mySessionPlayer;
            delete this->_mySessionFile;
      }

      delete this->_myChatHandler;


      // Deletion of _myTracker is done in closeSession;
      // no need to do it here.

      // Deletion is handled separately from session teardown and server disconnection
      // because some teardown methods (e.g. closeSession) require access to members that we will
      // be deleting. Separating deletion from teardown means that we do not have
      // to worry (as much) about proper ordering of the teardown sequence.  (We still need
      // to ensure that destructors in each object being deleted have access to all the
      // members they need, though.)

      // Stop dispatchers
      if (this->_myCallbacks) {
            this->stopSendQueueDispatch();
            this->stopReceiveQueueDispatch();
            delete this->_myCallbacks;
      }

      delete this->_myMessageHandler;

      delete this->session_data;

      Inkscape::GC::release(this->_myDoc);

}

void
00168 SessionManager::setDesktop(::SPDesktop* desktop)
{
      this->_myDesktop = desktop;
      if (this->_myDoc != NULL) {
            Inkscape::GC::release(this->_myDoc);
      }
      if (SP_DT_DOCUMENT(desktop) != NULL) {
            this->_myDoc = SP_DT_DOCUMENT(desktop);
            Inkscape::GC::anchor(this->_myDoc);
      }
}

int
00181 SessionManager::connectToServer(Glib::ustring const& server, Glib::ustring const& port, Glib::ustring const& username, Glib::ustring const& pw, bool usessl)
{
      GError* error = NULL;
      Glib::ustring jid;

      // JID format is username@server/resource
      jid += username + "@" + server + "/" + RESOURCE_NAME;

      LmMessage* m;
      LmMessageHandler* mh;

      if (!this->session_data) {
            this->session_data = new SessionData(this);
      }

      if (!this->_myMessageHandler) {
            this->_myMessageHandler = new MessageHandler(this);
      }

      // Connect to server
      // We need to check to see if this object already exists, because
      // the user may be reusing an old connection that failed due to e.g.
      // authentication failure.
      if (this->session_data->connection) {
            lm_connection_close(this->session_data->connection, &error);
            lm_connection_unref(this->session_data->connection);
      }

      this->session_data->connection = lm_connection_new(server.c_str());

      lm_connection_set_port(this->session_data->connection, atoi(port.c_str()));

      if (usessl) {
            if (lm_ssl_is_supported()) {
                  this->session_data->ssl = lm_ssl_new(NULL, ssl_error_handler, reinterpret_cast< gpointer >(this), NULL);

                  lm_ssl_ref(this->session_data->ssl);
            } else {
                  return SSL_INITIALIZATION_ERROR;
            }
            lm_connection_set_ssl(this->session_data->connection, this->session_data->ssl);
      }

      // Send authorization
      lm_connection_set_jid(this->session_data->connection, jid.c_str());

      //    TODO:
      //    Asynchronous connection and authentication would be nice,
      //    but it's a huge mess of mixing C callbacks and C++ method calls.
      //    I've tried to do it and only managed to severely destabilize the Jabber
      //    server connection routines.
      //
      //    This, of course, is an invitation to anyone more capable than me
      //    to convert this from synchronous to asynchronous Loudmouth calls.
      if (!lm_connection_open_and_block(this->session_data->connection, &error)) {
            if (error != NULL) {
                  g_warning("Failed to open: %s", error->message);
            }
            return FAILED_TO_CONNECT;
      }

      // Authenticate
      if (!lm_connection_authenticate_and_block(this->session_data->connection, username.c_str(), pw.c_str(), RESOURCE_NAME, &error)) {
            if (error != NULL) {
                  g_warning("Failed to authenticate: %s", error->message);
            }
            lm_connection_close(this->session_data->connection, NULL);
            lm_connection_unref(this->session_data->connection);
            this->session_data->connection = NULL;
            return INVALID_AUTH;
      }

      // Register message handler for presence messages
      mh = lm_message_handler_new((LmHandleMessageFunction)presence_handler, reinterpret_cast< gpointer >(this->_myMessageHandler), NULL);
      lm_connection_register_message_handler(this->session_data->connection, mh, LM_MESSAGE_TYPE_PRESENCE, LM_HANDLER_PRIORITY_NORMAL);

      // Register message handler for stream error messages
      mh = lm_message_handler_new((LmHandleMessageFunction)stream_error_handler, reinterpret_cast< gpointer >(this->_myMessageHandler), NULL);
      lm_connection_register_message_handler(this->session_data->connection, mh, LM_MESSAGE_TYPE_STREAM_ERROR, LM_HANDLER_PRIORITY_NORMAL);

      // Register message handler for chat messages
      mh = lm_message_handler_new((LmHandleMessageFunction)default_handler, reinterpret_cast< gpointer >(this->_myMessageHandler), NULL);
      lm_connection_register_message_handler(this->session_data->connection, mh, LM_MESSAGE_TYPE_MESSAGE, LM_HANDLER_PRIORITY_NORMAL);

      // Send presence message to server
      m = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_PRESENCE, LM_MESSAGE_SUB_TYPE_NOT_SET);
      if (!lm_connection_send(this->session_data->connection, m, &error)) {
            if (error != NULL) {
                  g_warning("Presence message could not be sent: %s", error->message);
            }
            lm_connection_close(this->session_data->connection, NULL);
            lm_connection_unref(this->session_data->connection);
            this->session_data->connection = NULL;
            return FAILED_TO_CONNECT;
      }

      this->session_data->status.set(LOGGED_IN, 1);

      this->_myCallbacks = new Callbacks(this);

      lm_message_unref(m);

      this->_setVerbSensitivity(ESTABLISHED_CONNECTION);

      return CONNECT_SUCCESS;
}

LmSSLResponse
00289 SessionManager::handleSSLError(LmSSL* ssl, LmSSLStatus status)
{
      if (this->session_data->ignoreFurtherSSLErrors) {
            return LM_SSL_RESPONSE_CONTINUE;
      }

      Glib::ustring msg;

      // TODO: It'd be nice to provide the user with additional information in some cases,
      // like fingerprints, hostname, etc.
      switch(status) {
            case LM_SSL_STATUS_NO_CERT_FOUND:
                  msg = _("No SSL certificate was found.");
                  break;
            case LM_SSL_STATUS_UNTRUSTED_CERT:
                  msg = _("The SSL certificate provided by the Jabber server is untrusted.");
                  break;
            case LM_SSL_STATUS_CERT_EXPIRED:
                  msg = _("The SSL certificate provided by the Jabber server is expired.");
                  break;
            case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
                  msg = _("The SSL certificate provided by the Jabber server has not been activated.");
                  break;
            case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
                  msg = _("The SSL certificate provided by the Jabber server contains a hostname that does not match the Jabber server's hostname.");
                  break;
            case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH:
                  msg = _("The SSL certificate provided by the Jabber server contains an invalid fingerprint.");
                  break;
            case LM_SSL_STATUS_GENERIC_ERROR:
                  msg = _("An unknown error occurred while setting up the SSL connection.");
                  break;
      }

      // TRANSLATORS: %1 is the message that describes the specific error that occurred when
      // establishing the SSL connection.
      Glib::ustring mainmsg = String::ucompose(_("<span weight=\"bold\" size=\"larger\">%1</span>\n\nDo you wish to continue connecting to the Jabber server?"), msg);

      Gtk::MessageDialog dlg(mainmsg, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, false);
      dlg.add_button(_("Continue connecting and ignore further errors"), 0);
      dlg.add_button(_("Continue connecting, but warn me of further errors"), 1);
      dlg.add_button(_("Cancel connection"), 2);

      switch(dlg.run()) {
            case 0:
                  this->session_data->ignoreFurtherSSLErrors = true;
                        /* FALL-THROUGH */
            case 1:
                  return LM_SSL_RESPONSE_CONTINUE;

            default:
                  return LM_SSL_RESPONSE_STOP;
      }
}

void
00345 SessionManager::disconnectFromServer()
{
      if (this->session_data->connection) {
            GError* error = NULL;

            LmMessage *m;
            this->disconnectFromDocument();
            m = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_PRESENCE, LM_MESSAGE_SUB_TYPE_UNAVAILABLE);
            if (!lm_connection_send(this->session_data->connection, m, &error)) {
                  g_warning("Could not send unavailable presence message: %s", error->message);
            }

            lm_message_unref(m);
            lm_connection_close(this->session_data->connection, NULL);
            lm_connection_unref(this->session_data->connection);
            if (this->session_data->ssl) {
                  lm_ssl_unref(this->session_data->ssl);
            }

            this->session_data->connection = NULL;
            this->session_data->ssl = NULL;
            this->_setVerbSensitivity(INITIAL);
      }
}

void
00371 SessionManager::disconnectFromDocument()
{
      if (this->session_data->status[IN_WHITEBOARD] || !this->session_data->status[IN_CHATROOM]) {
            this->sendMessage(DISCONNECTED_FROM_USER_SIGNAL, 0, "", this->session_data->recipient, false);
      }
      this->closeSession();
      this->_setVerbSensitivity(DISCONNECTED_FROM_SESSION);
}

void
00381 SessionManager::closeSession()
{

      if (this->session_data->status[IN_WHITEBOARD]) {
            this->session_data->status.set(IN_WHITEBOARD, 0);
            this->session_data->receive_queues.clear();
            this->session_data->send_queue->clear();
            this->stopSendQueueDispatch();
            this->stopReceiveQueueDispatch();
      }

      if (this->_myUndoObserver) {
            this->_myDoc->removeUndoObserver(*this->_myUndoObserver);
      }

      delete this->_myUndoObserver;
      delete this->_mySerializer;
      delete this->_myDeserializer;

      this->_myUndoObserver = NULL;
      this->_mySerializer = NULL;
      this->_myDeserializer = NULL;

      if (this->_myTracker) {
            delete this->_myTracker;
            this->_myTracker = NULL;
      }


      this->setRecipient(NULL);
}

void
00414 SessionManager::setRecipient(char const* recipientJID)
{
      if (this->session_data->recipient) {
            free(const_cast< gchar* >(this->session_data->recipient));
      }

      if (recipientJID == NULL) {
            this->session_data->recipient = NULL;
      } else {
            this->session_data->recipient = g_strdup(recipientJID);
      }
}

void
00428 SessionManager::sendChange(Glib::ustring const& msg, MessageType type, std::string const& recipientJID, bool chatroom)
{
      if (!this->session_data->status[IN_WHITEBOARD]) {
            return;
      }

      std::string& recipient = const_cast< std::string& >(recipientJID);
      if (recipient.empty()) {
            recipient = this->session_data->recipient;
      }
            

      switch (type) {
            case DOCUMENT_BEGIN:
            case DOCUMENT_END:
            case CHANGE_NOT_REPEATABLE:
            case CHANGE_REPEATABLE:
            case CHANGE_COMMIT:
            {
                  MessageNode *newNode = new MessageNode(this->session_data->sequence_number++, lm_connection_get_jid(this->session_data->connection), recipient, msg, type, false, chatroom);
                  this->session_data->send_queue->insert(newNode);
                  Inkscape::GC::release(newNode);
                  break;
            }
            default:
                  g_warning("Cannot insert MessageNode with unknown change type into send queue; discarding message.  This may lead to desynchronization!");
                  break;
      }
}


// FIXME:
// This method needs a massive, massive, massive overhaul.
int
00462 SessionManager::sendMessage(MessageType msgtype, unsigned int sequence, Glib::ustring const& msg, char const* recipientJID, bool chatroom)
{
      LmMessage* m;
      GError* error = NULL;
      char* type, * seq;

      if (recipientJID == NULL || recipientJID == "") {
            g_warning("Null recipient JID specified; not sending message.");
            return NO_RECIPIENT_JID;
      } else {
      }

      // create message
      m = lm_message_new(recipientJID, LM_MESSAGE_TYPE_MESSAGE);

      // add sender
      lm_message_node_set_attribute(m->node, "from", lm_connection_get_jid(this->session_data->connection));

      // set message subtype according to whether or not this is
      // destined for a chatroom
      if (chatroom) {
            lm_message_node_set_attribute(m->node, "type", "groupchat");
      } else {
            lm_message_node_set_attribute(m->node, "type", "chat");
      }

      // set protocol version;
      // we are currently fixed at version 1
      lm_message_node_add_child(m->node, MESSAGE_PROTOCOL_VER, MESSAGE_PROTOCOL_V1);

      // add message type
      type = (char *)calloc(TYPE_FIELD_SIZE, sizeof(char));
      snprintf(type, TYPE_FIELD_SIZE, "%i", msgtype);
      lm_message_node_add_child(m->node, MESSAGE_TYPE, type);
      free(type);

      // add message body
      if (!msg.empty()) {
            lm_message_node_add_child(m->node, MESSAGE_BODY, msg.c_str());
      } else {
      }

      // add sequence number
      switch(msgtype) {
            case CHANGE_REPEATABLE:
            case CHANGE_NOT_REPEATABLE:
            case DUMMY_CHANGE:
            case CHANGE_COMMIT:
            case DOCUMENT_BEGIN:
            case DOCUMENT_END:
            case CONNECT_REQUEST_RESPONSE_CHAT:
            case CONNECT_REQUEST_RESPONSE_USER:
            case CHATROOM_SYNCHRONIZE_RESPONSE:
                  seq = (char* )calloc(SEQNUM_FIELD_SIZE, sizeof(char));
                  sprintf(seq, "%u", sequence);
                  lm_message_node_add_child(m->node, MESSAGE_SEQNUM, seq);
                  free(seq);
                  break;

            case CONNECT_REQUEST_USER:
            case CONNECTED_SIGNAL:
            case DISCONNECTED_FROM_USER_SIGNAL:
                  break;

            // Error messages and synchronization requests do not need a sequence number
            case CHATROOM_SYNCHRONIZE_REQUEST:
            case CONNECT_REQUEST_REFUSED_BY_PEER:
            case UNSUPPORTED_PROTOCOL_VERSION:
            case ALREADY_IN_SESSION:
                  break;

            default:
                  g_warning("Outgoing message type not recognized; not sending.");
                  lm_message_unref(m);
                  return UNKNOWN_OUTGOING_TYPE;
      }

      // We want to log messages even if they were not successfully sent,
      // since the user may opt to re-synchronize a session using the session
      // file record.
      if (!msg.empty()) {
            this->_log(msg);
            this->_commitLog();
      }

      // send message

      if (!lm_connection_send(this->session_data->connection, m, &error)) {
            g_warning("Send failed: %s", error->message);
            lm_message_unref(m);
            return CONNECTION_ERROR;
      }

      lm_message_unref(m);
      return SEND_SUCCESS;
}

void
00560 SessionManager::connectionError(Glib::ustring const& errmsg)
{
      Gtk::MessageDialog dlg(errmsg, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE);
      dlg.run();
//    sp_whiteboard_connect_dialog(const_cast< gchar* >(errmsg));
}

void
00568 SessionManager::resendDocument(char const* recipientJID, KeyToNodeMap& newidsbuf, NodeToKeyMap& newnodesbuf)
{
      Glib::ustring docbegin = MessageUtilities::makeTagWithContent(MESSAGE_DOCBEGIN, "");
      this->sendChange(docbegin, DOCUMENT_BEGIN, recipientJID, false);

      Inkscape::XML::Node* root = sp_document_repr_root(this->_myDoc);

      if(root == NULL) {
            return;
    }

      NewChildObjectMessageList newchildren;
      MessageAggregator& agg = MessageAggregator::instance();
      Glib::ustring buf;

    for ( Inkscape::XML::Node *child = root->firstChild() ; child != NULL ; child = child->next() ) {
            // TODO: replace with Serializer methods
            MessageUtilities::newObjectMessage(&buf, newidsbuf, newnodesbuf, newchildren, this->_myTracker, child);

            NewChildObjectMessageList::iterator j = newchildren.begin();
            Glib::ustring aggbuf;

            for(; j != newchildren.end(); j++) {
                  if (!agg.addOne(*j, aggbuf)) {
                        this->sendChange(aggbuf, CHANGE_REPEATABLE, recipientJID, false);
                        aggbuf.clear();
                        agg.addOne(*j, aggbuf);
                  }
            }

            // send remaining changes
            if (!aggbuf.empty()) {
                  this->sendChange(aggbuf, CHANGE_REPEATABLE, recipientJID, false);
                  aggbuf.clear();
            }

            newchildren.clear();
            buf.clear();
    }

      Glib::ustring commit = MessageUtilities::makeTagWithContent(MESSAGE_COMMIT, "");
      this->sendChange(commit, CHANGE_COMMIT, recipientJID, false);
      Glib::ustring docend = MessageUtilities::makeTagWithContent(MESSAGE_DOCEND, "");
      this->sendChange(docend, DOCUMENT_END, recipientJID, false);
}

void
00615 SessionManager::receiveChange(Glib::ustring const& changemsg)
{

      struct Node part;

      Glib::ustring msgcopy = changemsg.c_str();


      while(MessageUtilities::getFirstMessageTag(part, msgcopy) != false) {
            // TODO:
            // Yikes.  This is ugly.
            if (part.tag == MESSAGE_CHANGE) {
                  this->_myDeserializer->deserializeEventChgAttr(part.data);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_NEWOBJ) {
                  this->_myDeserializer->deserializeEventAdd(part.data);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_DELETE) {
                  this->_myDeserializer->deserializeEventDel(part.data);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_DOCUMENT) {
                  // no special handler, just keep going with the rest of the message
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_NODECONTENT) {
                  this->_myDeserializer->deserializeEventChgContent(part.data);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_ORDERCHANGE) {
                  this->_myDeserializer->deserializeEventChgOrder(part.data);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_COMMIT) {
                  // Retrieve the deserialized event log, node actions, and nodes with updated attributes
                  XML::Event* log = this->_myDeserializer->getEventLog();
                  KeyToNodeActionList& node_changes = this->_myDeserializer->getNodeTrackerActions();
                  AttributesUpdatedSet& updated = this->_myDeserializer->getUpdatedAttributeNodeSet();

                  // Make document insensitive to undo
                  gboolean saved = sp_document_get_undo_sensitive(this->_myDoc);
                  sp_document_set_undo_sensitive(this->_myDoc, FALSE);

                  // Replay the log and push it onto the undo stack
                  sp_repr_replay_log(log);

                  // Call updateRepr on changed nodes
                  // This is required for some tools to function properly, i.e. text tool
                  // (TODO: we don't need to update _all_ changed nodes, just their parents)
                  AttributesUpdatedSet::iterator i = updated.begin();
                  for(; i != updated.end(); i++) {
                        SPObject* updated = this->_myDoc->getObjectByRepr(*i);
                        if (updated) {
                              updated->updateRepr();
                        }
                  }

                  // merge the events generated by updateRepr
                  sp_repr_coalesce_log(this->_myDoc->priv->partial, log);
                  this->_myDoc->priv->partial = NULL;

                  this->_myDoc->priv->undo = g_slist_prepend(this->_myDoc->priv->undo, log);

                  // Restore undo sensitivity
                  sp_document_set_undo_sensitive(this->_myDoc, saved);

                  // Add or delete nodes to/from the tracker
                  this->_myTracker->process(node_changes);

                  // Reset deserializer state
                  this->_myDeserializer->reset();
                  break;

            } else if (part.tag == MESSAGE_UNDO) {
                  this->_myUndoObserver->lockObserverFromSending(UndoStackObserver::UNDO_EVENT);
                  sp_document_undo(this->_myDoc);
                  this->_myUndoObserver->unlockObserverFromSending(UndoStackObserver::UNDO_EVENT);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_REDO) {
                  this->_myUndoObserver->lockObserverFromSending(UndoStackObserver::REDO_EVENT);
                  sp_document_redo(this->_myDoc);
                  this->_myUndoObserver->unlockObserverFromSending(UndoStackObserver::REDO_EVENT);
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_DOCBEGIN) {
                  msgcopy.erase(0, part.next_pos);

            } else if (part.tag == MESSAGE_DOCEND) {
                  // Set this to be the new original state of the document
                  sp_document_done(this->document());
                  sp_document_clear_redo(this->document());
                  sp_document_clear_undo(this->document());
                  this->setupCommitListener();
                  msgcopy.erase(0, part.next_pos);

            } else {
                  msgcopy.erase(0, part.next_pos);

            }
      }

      this->_log(changemsg);

      this->_commitLog();
}

bool
00725 SessionManager::isPlayingSessionFile()
{
      return this->session_data->status[PLAYING_SESSION_FILE];
}

void
00731 SessionManager::loadSessionFile(Glib::ustring filename)
{
      if (!this->session_data || !this->session_data->status[IN_WHITEBOARD]) {
            try {
                  if (this->_mySessionFile) {
                        delete this->_mySessionFile;
                  }
                  this->_mySessionFile = new SessionFile(filename, true, false);

                  // Initialize objects needed for session playback
                  if (this->_mySessionPlayer == NULL) {
                        this->_mySessionPlayer = new SessionFilePlayer(16, this);
                  } else {
                        this->_mySessionPlayer->load(this->_mySessionFile);
                  }

                  if (this->_myTracker == NULL) {
                        this->_myTracker = new XMLNodeTracker(this);
                  } else {
                        this->_myTracker->reset();
                  }

                  if (!this->session_data) {
                        this->session_data = new SessionData(this);
                  }

                  if (!this->_myDeserializer) {
                        this->_myDeserializer = new Deserializer(this->node_tracker());
                  }

                  if (!this->_myUndoObserver) {
                        this->setupCommitListener();
                  }

                  this->session_data->status.set(PLAYING_SESSION_FILE, 1);


            } catch (Glib::FileError e) {
                  g_warning("Could not load session file: %s", e.what().data());
            }
      }
}

void
00775 SessionManager::userConnectedToWhiteboard(gchar const* JID)
{
      SP_DT_MSGSTACK(this->_myDesktop)->flashF(Inkscape::INFORMATION_MESSAGE, _("Established whiteboard session with <b>%s</b>."), JID);
}


void
00782 SessionManager::userDisconnectedFromWhiteboard(std::string const& JID)
{

      SP_DT_MSGSTACK(this->_myDesktop)->flashF(Inkscape::INFORMATION_MESSAGE, _("<b>%s</b> has <b>left</b> the whiteboard session."), JID.c_str());

      // Inform the user
      // TRANSLATORS: %1 is the name of the user that disconnected, %2 is the name of the user whom the disconnected user disconnected from.
      // This message is not used in a chatroom context.
      Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> has left the whiteboard session.</span>\n\n"), JID);
      // TRANSLATORS: %1 and %2 are userids
      Glib::ustring secondary = String::ucompose(_("You are still connected to a Jabber server as <b>%2</b>, and may establish a new session to <b>%1</b> or a different user."), JID, lm_connection_get_jid(this->session_data->connection));

      // TODO: parent this dialog to the active desktop
      Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
      /*
      dialog.set_message(primary, true);
      dialog.set_secondary_text(secondary, true);
      */
      dialog.run();
}

void
00804 SessionManager::startSendQueueDispatch()
{
      this->_send_queue_dispatcher = Glib::signal_timeout().connect(sigc::mem_fun(*(this->_myCallbacks), &Callbacks::dispatchSendQueue), SEND_TIMEOUT);
}

void
00810 SessionManager::stopSendQueueDispatch()
{
      if (this->_send_queue_dispatcher) {
            this->_send_queue_dispatcher.disconnect();
      }
}

void
00818 SessionManager::startReceiveQueueDispatch()
{
      this->_receive_queue_dispatcher = Glib::signal_timeout().connect(sigc::mem_fun(*(this->_myCallbacks), &Callbacks::dispatchReceiveQueue), SEND_TIMEOUT);
}

void
00824 SessionManager::stopReceiveQueueDispatch()
{
      if (this->_receive_queue_dispatcher) {
            this->_receive_queue_dispatcher.disconnect();
      }
}

void
00832 SessionManager::clearDocument()
{
      // clear all layers, definitions, and metadata
      XML::Node* rroot = this->_myDoc->rroot;

      // clear definitions
      XML::Node* defs = SP_OBJECT_REPR((SPDefs*)SP_DOCUMENT_DEFS(this->_myDoc));
      g_assert(SP_ROOT(this->_myDoc->root)->defs);

      for(XML::Node* child = defs->firstChild(); child; child = child->next()) {
            defs->removeChild(child);
      }

      // clear layers
      for(XML::Node* child = rroot->firstChild(); child; child = child->next()) {
            if (strcmp(child->name(), "svg:g") == 0) {
                  rroot->removeChild(child);
            }
      }

      // clear metadata
      for(XML::Node* child = rroot->firstChild(); child; child = child->next()) {
            if (strcmp(child->name(), "svg:metadata") == 0) {
                  rroot->removeChild(child);
            }
      }

//    sp_document_done(this->_myDoc);
}

void
00863 SessionManager::setupInkscapeInterface()
{
      this->session_data->status.set(IN_WHITEBOARD, 1);
      this->startSendQueueDispatch();
      this->startReceiveQueueDispatch();
      if (!this->_myTracker) {
            this->_myTracker = new XMLNodeTracker(this);
      }

      this->_mySerializer = new Serializer(this->node_tracker());
      this->_myDeserializer = new Deserializer(this->node_tracker());

      // We're in a whiteboard session now, so set verb sensitivity accordingly
      this->_setVerbSensitivity(ESTABLISHED_SESSION);
}

void
00880 SessionManager::setupCommitListener()
{
      this->_myUndoObserver = new Whiteboard::UndoStackObserver(this);
      this->_myDoc->addUndoObserver(*this->_myUndoObserver);
}

::SPDesktop*
SessionManager::desktop()
{
      return this->_myDesktop;
}

::SPDocument*
SessionManager::document()
{
      return this->_myDoc;
}

Callbacks*
SessionManager::callbacks()
{
      return this->_myCallbacks;
}

Whiteboard::UndoStackObserver*
SessionManager::undo_stack_observer()
{
      return this->_myUndoObserver;
}

Serializer*
SessionManager::serializer()
{
      return this->_mySerializer;
}

XMLNodeTracker*
SessionManager::node_tracker()
{
      return this->_myTracker;
}


ChatMessageHandler*
SessionManager::chat_handler()
{
      return this->_myChatHandler;
}

SessionFilePlayer*
SessionManager::session_player()
{
      return this->_mySessionPlayer;
}

SessionFile*
SessionManager::session_file()
{
      return this->_mySessionFile;
}

void
SessionManager::_log(Glib::ustring const& message)
{
      if (this->_mySessionFile && !this->_mySessionFile->isReadOnly()) {
            this->_mySessionFile->addMessage(message);
      }
}

void
SessionManager::_commitLog()
{
      if (this->_mySessionFile && !this->_mySessionFile->isReadOnly()) {
            this->_mySessionFile->commit();
      }
}

void
SessionManager::_closeLog()
{
      if (this->_mySessionFile) {
            this->_mySessionFile->close();
      }
}

void
00966 SessionManager::startLog(Glib::ustring filename)
{
      try {
            this->_mySessionFile = new SessionFile(filename, false, false);
      } catch (Glib::FileError e) {
            g_warning("Caught I/O error %s while attemping to open file %s for session recording.", e.what().c_str(), filename.c_str());
            throw;
      }
}

void
SessionManager::_tryToStartLog()
{
      if (this->session_data) {
            if (!this->session_data->sessionFile.empty()) {
                  bool undecided = true;
                  while(undecided) {
                        try {
                              this->startLog(this->session_data->sessionFile);
                              undecided = false;
                        } catch (Glib::FileError e) {
                              undecided = true;
                              Glib::ustring msg = String::ucompose(_("Could not open file %1 for session recording.\nThe error encountered was: %2.\n\nYou may select a different location to record the session, or you may opt to not record this session."), this->session_data->sessionFile, e.what());
                              Gtk::MessageDialog dlg(msg, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_NONE, false);
                              dlg.add_button(_("Choose a different location"), 0);
                              dlg.add_button(_("Skip session recording"), 1);
                              switch (dlg.run()) {
                                    case 0:
                                    {
                                          Gtk::FileChooserDialog sessionfiledlg(_("Select a location and filename"), Gtk::FILE_CHOOSER_ACTION_SAVE);
                                          sessionfiledlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
                                          sessionfiledlg.add_button(_("Set filename"), Gtk::RESPONSE_OK);
                                          int result = sessionfiledlg.run();
                                          switch (result) {
                                                case Gtk::RESPONSE_OK:
                                                {
                                                      this->session_data->sessionFile = sessionfiledlg.get_filename();
                                                      break;
                                                }
                                                case Gtk::RESPONSE_CANCEL:
                                                default:
                                                      undecided = false;
                                                      break;
                                          }
                                          break;
                                    }
                                    case 1:
                                    default:
                                          undecided = false;
                                          break;
                              }
                        }
                  }
            }
      }
}

void
SessionManager::_setVerbSensitivity(SensitivityMode mode)
{
      return;

      switch (mode) {
            case ESTABLISHED_CONNECTION:
                  // Upon successful connection, we can disconnect from the server.
                  // We can also start sharing a document with a user or chatroom.
                  // We cannot, however, connect to a new server without first disconnecting.
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_CONNECT)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_DISCONNECT_FROM_SERVER)->sensitive(this->_myDoc, true);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHUSER)->sensitive(this->_myDoc, true);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHCHAT)->sensitive(this->_myDoc, true);
                  break;

            case ESTABLISHED_SESSION:
                  // When we have established a session, we should not permit the user to go and
                  // establish another session from the same document without first disconnecting.
                  //
                  // TODO: Well, actually, we probably _should_, but there's no real reconnection logic just yet.
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHUSER)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHCHAT)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_DISCONNECT_FROM_SESSION)->sensitive(this->_myDoc, true);
                  break;
            case DISCONNECTED_FROM_SESSION:
                  // Upon disconnecting from a session, we can establish a new session and disconnect
                  // from the server, but we cannot disconnect from a session (since we just left it.)
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_DISCONNECT_FROM_SESSION)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHUSER)->sensitive(this->_myDoc, true);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHCHAT)->sensitive(this->_myDoc, true);

            case INITIAL:
            default:
                  // Upon construction, there is no active connection, so we cannot do the following:
                  // (1) disconnect from a session
                  // (2) disconnect from a server
                  // (3) share with a user 
                  // (4) share with a chatroom
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_CONNECT)->sensitive(this->_myDoc, true);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHUSER)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_SHAREWITHCHAT)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_DISCONNECT_FROM_SESSION)->sensitive(this->_myDoc, false);
                  Inkscape::Verb::get(SP_VERB_DIALOG_WHITEBOARD_DISCONNECT_FROM_SERVER)->sensitive(this->_myDoc, false);
                  break;
      };
}

/*
void
SessionManager::Listener::processXmppEvent(Pedro::XmppEvent const& 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_MESSAGE:
                  break;
            case Pedro::XmppEvent::EVENT_PRESENCE:
                  break;
            case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
                  break;
            case Pedro::XmppEvent::EVENT_MUC_JOIN:
                  break;
            case Pedro::XmppEvent::EVENT_MUC_LEAVE:
                  break;
            case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
                  break;
      }
}
*/

}

}


/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :

Generated by  Doxygen 1.6.0   Back to index