Code

cleanup: Remove some commented-out code.
[inkscape.git] / src / jabber_whiteboard / connection-establishment.cpp
1 /**
2  * Whiteboard session manager
3  * Methods for establishing connections and notifying the user of events
4  *
5  * Authors:
6  * David Yip <yipdw@rose-hulman.edu>
7  *
8  * Copyright (c) 2005 Authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #include "util/ucompose.hpp"
14 #include <glibmm/i18n.h>
15 #include <gtkmm/dialog.h>
16 #include <gtkmm/messagedialog.h>
18 #include "desktop.h"
19 #include "file.h"
20 #include "document.h"
22 #include "xml/repr.h"
24 #include "jabber_whiteboard/defines.h"
25 #include "jabber_whiteboard/typedefs.h"
26 #include "jabber_whiteboard/node-tracker.h"
27 #include "jabber_whiteboard/chat-handler.h"
28 #include "jabber_whiteboard/message-queue.h"
29 #include "jabber_whiteboard/session-manager.h"
30 #include "jabber_whiteboard/invitation-confirm-dialog.h"
32 namespace Inkscape {
34 namespace Whiteboard {
36 void
37 SessionManager::sendRequestToUser(std::string const& recipientJID)
38 {
39         /*
40         Glib::ustring doccopy;
41         if (document != NULL) {
42                 doccopy = *document;
43         }
44         */
45         this->session_data->status.set(WAITING_FOR_INVITE_RESPONSE, 1);
46         this->sendMessage(CONNECT_REQUEST_USER, 0, "", recipientJID.c_str(), false);
47 }
49 void
50 SessionManager::sendRequestToChatroom(Glib::ustring const& server, Glib::ustring const& chatroom, Glib::ustring const& handle, Glib::ustring const& password)
51 {
52         // We do not yet use the Basic MUC Protocol for connection establishment etc
53         // <http://www.jabber.org/jeps/jep-0045.html>.  The protocol we use is the
54         // old GroupChat system; extension to MUC is TODO
55         
56         // Compose room@service/nick string for "to" field
57         Glib::ustring dest = String::ucompose("%1@%2/%3", chatroom, server, handle);
58         LmMessage* presence_req = lm_message_new(dest.data(), LM_MESSAGE_TYPE_PRESENCE);
60         // Add 'from' attribute
61         LmMessageNode* preq_root = lm_message_get_node(presence_req);
63         lm_message_node_set_attribute(preq_root, "from", lm_connection_get_jid(this->session_data->connection));
64         
65         // Add <x xmlns='http://jabber.org/protocol/muc/' />
66         // (Not anymore: we don't speak it! -- yipdw)
67         LmMessageNode* xmlns_node = lm_message_node_add_child(preq_root, "x", "");
68         lm_message_node_set_attribute(xmlns_node, "xmlns", "http://jabber.org/protocol/muc/");
69         
70         // If a password was supplied, add it to xmlns_node
71         if (password != NULL) {
72                 lm_message_node_add_child(xmlns_node, "password", password.c_str());
73         }
75         // Create chat message handler and node tracker
76         if (!this->_myChatHandler) {
77                 this->_myChatHandler = new ChatMessageHandler(this);
78         }
80         // Flag ourselves as connecting to a chatroom (but not yet connected)
81         this->session_data->status.set(CONNECTING_TO_CHAT, 1);
82         // Send the message
83         GError *error = NULL;
84         if (!lm_connection_send(this->session_data->connection, presence_req, &error)) {
85                 g_error("Presence message could not be sent to %s: %s", dest.data(), error->message);
86                 this->session_data->status.set(CONNECTING_TO_CHAT, 0);
87         }
89         this->session_data->chat_handle = handle;
90         this->session_data->chat_server = server;
91         this->session_data->chat_name = chatroom;
93         this->setRecipient(String::ucompose("%1@%2", chatroom, server).data());
95         lm_message_unref(presence_req);
96 }
98 void 
99 SessionManager::sendConnectRequestResponse(char const* recipientJID, gboolean accepted_request)
101         if (accepted_request == TRUE) {
102                 this->setRecipient(recipientJID);
103                 this->session_data->status.set(IN_WHITEBOARD, 1);
104         }
106         this->sendMessage(CONNECT_REQUEST_RESPONSE_USER, accepted_request, "", recipientJID, false);
109 // When this method is invoked, it means that the user has received an invitation from another peer
110 // to engage in a whiteboard session (i.e. 1:1 communication).  The user may accept or reject this invitation.
111 void
112 SessionManager::receiveConnectRequest(gchar const* requesterJID)
114         int x, y;
115         Gdk::ModifierType mt;
116         Gdk::Display::get_default()->get_pointer(x, y, mt);
118         if (mt & GDK_BUTTON1_MASK) {
119                 // Attach a polling timeout
120                 this->_notify_incoming_request = Glib::signal_timeout().connect(sigc::bind< 0 >(sigc::mem_fun(*this, &SessionManager::_pollReceiveConnectRequest), requesterJID), 50);
121                 return;
122         }
124         if (this->session_data->status[WAITING_FOR_INVITE_RESPONSE]) {
125                 // Whoops.  Someone tried to invite us while we were inviting someone else 
126                 // (maybe it was the someone we were trying to invite, maybe it was someone else).
127                 //
128                 // Our response is to reject the second request, as we can only handle one
129                 // invitation at a time.  Also, we notify the user (who is waiting for an invitation 
130                 // response) of the rejection event.
131                 this->sendMessage(CONNECT_REQUEST_REFUSED_BY_PEER, 0, "", requesterJID, false);
133                 Glib::ustring primary = "<span weight=\"bold\" size=\"larger\">";
135                 // TRANSLATORS: This string is used to inform an Inkboard user that the following
136                 // scenario has occurred:
137                 // 1.  Alice invites Bob to an Inkboard session.
138                 // 2.  While Alice's invitation is en route, Bob invites Alice to an Inkboard session.
139                 //
140                 // Or, we might have the following scenario:
141                 // 1.  Alice invites Bob to an Inkboard session.
142                 // 2.  While Alice is waiting for Bob's response, Carol sends Alice an invitation.
143                 //
144                 // In the current implementation, we can only handle one invitation at a time,
145                 // so we reject all others.  
146                 //
147                 // This is a fix for bug #1352522.  Probably not the friendliest, but it's about
148                 // the best we can do without changing the protocol.
149                 primary += _("<b>An invitation conflict has occurred.</b>");
150                 primary += "</span>\n\n";
152                 // TRANSLATORS: %1 is the JID of the user who sent us the invitation request.
153                 primary += String::ucompose(_("The Jabber user <b>%1</b> attempted to invite you to a whiteboard session while you were waiting on an invitation response.\n\nThe invitation from <b>%1</b> has been rejected."), requesterJID); 
155                 Gtk::MessageDialog dialog(primary, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, false);
156                 dialog.run();
157                 return;
158         }
160         if (this->session_data->status[IN_WHITEBOARD]) {
161                 this->sendMessage(ALREADY_IN_SESSION, 0, "", requesterJID, false);
162                 return;
163         }
165         // Check to see if the user made any modifications to this document.  If so, 
166         // we want to give them the option of (1) letting us clear their document or (2)
167         // opening a new, blank document for the whiteboard session.
168         Glib::ustring primary = "<span weight=\"bold\" size=\"larger\">" + String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), requesterJID) + "</span>\n\n";
169         Glib::ustring title = String::ucompose(_("Incoming whiteboard invitation from %1"), requesterJID);
171         if (sp_document_repr_root(this->_myDoc)->attribute("sodipodi:modified") == NULL) {
172                 primary += String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), requesterJID);
173         } else {
174                 primary += String::ucompose(_("Would you like to accept %1's invitation in a new document window?\nAccepting the invitation in your current window will discard unsaved changes."), requesterJID);
175         }
177         // Construct confirmation dialog
178         InvitationConfirmDialog dialog(primary);
179         
180         dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
181         dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
182         dialog.add_button(_("Accept invitation in new document window"), ACCEPT_INVITATION_IN_NEW_WINDOW);
184         bool undecided = true;
185         InvitationResponses resp = static_cast< InvitationResponses >(dialog.run());
187         while(undecided) {
188                 if (resp == ACCEPT_INVITATION) {
189                         undecided = false;
190                         this->clearDocument();
191                 
192                         // Create a receive queue for the initiator of this request
193                         this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
194                         
195                         this->setupInkscapeInterface();
196                         if (dialog.useSessionFile()) {
197                                 this->session_data->sessionFile = dialog.getSessionFilePath();
198                                 this->_tryToStartLog();
199                         }
200                         this->sendConnectRequestResponse(requesterJID, TRUE);
202                 } else if (resp == ACCEPT_INVITATION_IN_NEW_WINDOW) {
203                         SPDesktop* newdesktop = sp_file_new_default();
204                         if (newdesktop != NULL) {
205                                 undecided = false;
207                                 // Swap desktops around
209                                 // Destroy the new desktop's session manager and add this one in
210                                 delete newdesktop->_whiteboard_session_manager;
211                                 newdesktop->_whiteboard_session_manager = this;
213                                 // Assign a new session manager to our old desktop
214                                 this->_myDesktop->_whiteboard_session_manager = new SessionManager(this->_myDesktop);
216                                 // Reset our desktop and document pointers
217                                 this->setDesktop(newdesktop);
219                                 // Prepare document and send acceptance notification
220                                 this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
221                                 this->clearDocument();
222                                 this->setupInkscapeInterface();
223                                 if (dialog.useSessionFile()) {
224                                         this->session_data->sessionFile = dialog.getSessionFilePath();
225                                         this->_tryToStartLog();
226                                 }
227                                 this->sendConnectRequestResponse(requesterJID, TRUE);
229                         } else {
230                                 // We could not create a new desktop; ask the user if she or he wants to 
231                                 // replace the current document and accept the invitation, or reject the invitation.
232                                 // TRANSLATORS: %1 is a userid here
233                                 Glib::ustring msg = "<span weight=\"bold\" size=\"larger\">" + String::ucompose(_("A new document window could not be opened for a whiteboard session with <b>%1</b>"), requesterJID) + ".</span>\n\nWould you like to accept the whiteboard connection in the active document or refuse the invitation?";
234                                 InvitationConfirmDialog replace_dialog(msg);
235                                 dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
236                                 dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
237                                 resp = static_cast< InvitationResponses >(dialog.run());
238                         }
239                 } else {
240                         undecided = false;
241                         this->sendMessage(CONNECT_REQUEST_REFUSED_BY_PEER, 0, "", requesterJID, false);
242                 }
243         }
246 // When this method is invoked, it means that the other peer
247 // has accepted our request.
248 void
249 SessionManager::receiveConnectRequestResponse(InvitationResponses response, std::string& sender)
251         this->session_data->status.set(WAITING_FOR_INVITE_RESPONSE, 0);
253         switch(response) {
254                 case ACCEPT_INVITATION:
255                         {
257                         // Create a receive queue for the other peer.
258                         this->session_data->receive_queues[sender] = new ReceiveMessageQueue(this);
259                                 
260                         KeyToNodeMap newids;
261                         NodeToKeyMap newnodes;
262                         this->_myTracker = new XMLNodeTracker(this);
263                         this->setupInkscapeInterface();
264                         this->_tryToStartLog();
265                         this->resendDocument(this->session_data->recipient, newids, newnodes);
266                         this->_myTracker->put(newids, newnodes);
267 //                      this->_myTracker->dump();
268                         this->setupCommitListener();
269                         break;
270                         }
272                 case DECLINE_INVITATION:
273                         {
274                         // TRANSLATORS: %1 is the peer whom refused our invitation.
275                         Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> has refused your whiteboard invitation.</span>\n\n"), sender);
276                         
277                         // TRANSLATORS: %1 is the peer whom refused our invitation, %2 is our Jabber identity.
278                         Glib::ustring secondary = String::ucompose(_("You are still connected to a Jabber server as <b>%2</b>, and may send an invitation to <b>%1</b> again, or you may send an invitation to a different user."), sender, lm_connection_get_jid(this->session_data->connection));
280                         Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
281                         dialog.run();
282                         break;
284                         }
286                 case PEER_ALREADY_IN_SESSION:
287                         {
288                         // TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
289                         Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> is already in a whiteboard session.</span>\n\n"), sender);
291                         // TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
292                         Glib::ustring secondary = String::ucompose(_("You are still connected to a Jabber server as <b>%1</b>, and may send an invitation to a different user."), sender);
293                         Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
294                         dialog.run();
296                         break;
297                         }
298                 default:
299                         break;
300         }
303 void
304 SessionManager::receiveConnectRequestResponseChat(gchar const* recipient)
306         // When responding to connection request responses in chatrooms,
307         // the responding user is already established in the whiteboard session.
308         // Therefore we do not need to perform any setup of observers or dispatchers; the requesting user
309         // will do that.
311         KeyToNodeMap newids;
312         NodeToKeyMap newnodes;
313         this->resendDocument(recipient, newids, newnodes);
316 bool
317 SessionManager::_pollReceiveConnectRequest(Glib::ustring const recipientJID)
319         int x, y;
320         Gdk::ModifierType mt;
321         Gdk::Display::get_default()->get_pointer(x, y, mt);
323         if (mt & GDK_BUTTON1_MASK) {
324                 return true;
325         } else {
326                 this->receiveConnectRequest(recipientJID.c_str());
327                 return false;
328         }
335 /*
336   Local Variables:
337   mode:c++
338   c-file-style:"stroustrup"
339   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
340   indent-tabs-mode:nil
341   fill-column:99
342   End:
343 */
344 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :