9382227fbb3d23c584f6ca68bac0044e5eb0b8cb
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
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));
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/");
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)
100 {
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);
107 }
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)
113 {
114 int x, y;
115 Gdk::ModifierType mt;
116 Gdk::Display::get_default()->get_pointer(x, y, mt);
118 if (mt) {
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[IN_WHITEBOARD]) {
125 this->sendMessage(ALREADY_IN_SESSION, 0, "", requesterJID, false);
126 }
128 // Check to see if the user made any modifications to this document. If so,
129 // we want to give them the option of (1) letting us clear their document or (2)
130 // opening a new, blank document for the whiteboard session.
131 Glib::ustring primary = "<span weight=\"bold\" size=\"larger\">" + String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), requesterJID) + "</span>\n\n";
132 Glib::ustring title = String::ucompose(_("Incoming whiteboard invitation from %1"), requesterJID);
134 if (sp_document_repr_root(this->_myDoc)->attribute("sodipodi:modified") == NULL) {
135 primary += String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), requesterJID);
136 } else {
137 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);
138 }
140 // Construct confirmation dialog
141 InvitationConfirmDialog dialog(primary);
143 dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
144 dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
145 dialog.add_button(_("Accept invitation in new document window"), ACCEPT_INVITATION_IN_NEW_WINDOW);
147 bool undecided = true;
148 InvitationResponses resp = static_cast< InvitationResponses >(dialog.run());
150 while(undecided) {
151 if (resp == ACCEPT_INVITATION) {
152 undecided = false;
153 this->clearDocument();
155 // Create a receive queue for the initiator of this request
156 this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
158 this->setupInkscapeInterface();
159 if (dialog.useSessionFile()) {
160 this->session_data->sessionFile = dialog.getSessionFilePath();
161 this->_tryToStartLog();
162 }
163 this->sendConnectRequestResponse(requesterJID, TRUE);
165 } else if (resp == ACCEPT_INVITATION_IN_NEW_WINDOW) {
166 SPDesktop* newdesktop = sp_file_new_default();
167 if (newdesktop != NULL) {
168 undecided = false;
170 // Swap desktops around
172 // Destroy the new desktop's session manager and add this one in
173 delete newdesktop->_whiteboard_session_manager;
174 newdesktop->_whiteboard_session_manager = this;
176 // Assign a new session manager to our old desktop
177 this->_myDesktop->_whiteboard_session_manager = new SessionManager(this->_myDesktop);
179 // Reset our desktop and document pointers
180 this->setDesktop(newdesktop);
182 // Prepare document and send acceptance notification
183 this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
184 this->clearDocument();
185 this->setupInkscapeInterface();
186 if (dialog.useSessionFile()) {
187 this->session_data->sessionFile = dialog.getSessionFilePath();
188 this->_tryToStartLog();
189 }
190 this->sendConnectRequestResponse(requesterJID, TRUE);
192 } else {
193 // We could not create a new desktop; ask the user if she or he wants to
194 // replace the current document and accept the invitation, or reject the invitation.
195 // TRANSLATORS: %1 is a userid here
196 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?";
197 InvitationConfirmDialog replace_dialog(msg);
198 dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
199 dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
200 resp = static_cast< InvitationResponses >(dialog.run());
201 }
202 } else {
203 undecided = false;
204 this->sendMessage(CONNECT_REQUEST_REFUSED_BY_PEER, 0, "", requesterJID, false);
205 }
206 }
207 }
209 // When this method is invoked, it means that the other peer
210 // has accepted our request.
211 void
212 SessionManager::receiveConnectRequestResponse(InvitationResponses response, std::string& sender)
213 {
214 this->session_data->status.set(WAITING_FOR_INVITE_RESPONSE, 0);
216 switch(response) {
217 case ACCEPT_INVITATION:
218 {
220 // Create a receive queue for the other peer.
221 this->session_data->receive_queues[sender] = new ReceiveMessageQueue(this);
223 KeyToNodeMap newids;
224 NodeToKeyMap newnodes;
225 this->_myTracker = new XMLNodeTracker(this);
226 this->setupInkscapeInterface();
227 this->_tryToStartLog();
228 this->resendDocument(this->session_data->recipient, newids, newnodes);
229 this->_myTracker->put(newids, newnodes);
230 // this->_myTracker->dump();
231 this->setupCommitListener();
232 break;
233 }
235 case DECLINE_INVITATION:
236 {
237 // TRANSLATORS: %1 is the peer whom refused our invitation.
238 Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> has refused your whiteboard invitation.</span>\n\n"), sender);
240 // TRANSLATORS: %1 is the peer whom refused our invitation, %2 is our Jabber identity.
241 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));
243 Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
244 dialog.run();
245 break;
247 }
249 case PEER_ALREADY_IN_SESSION:
250 {
251 // TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
252 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);
254 // TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
255 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);
256 Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
257 dialog.run();
259 break;
260 }
261 default:
262 break;
263 }
264 }
266 void
267 SessionManager::receiveConnectRequestResponseChat(gchar const* recipient)
268 {
269 // When responding to connection request responses in chatrooms,
270 // the responding user is already established in the whiteboard session.
271 // Therefore we do not need to perform any setup of observers or dispatchers; the requesting user
272 // will do that.
274 KeyToNodeMap newids;
275 NodeToKeyMap newnodes;
276 this->resendDocument(recipient, newids, newnodes);
277 }
279 bool
280 SessionManager::_pollReceiveConnectRequest(Glib::ustring const recipientJID)
281 {
282 int x, y;
283 Gdk::ModifierType mt;
284 Gdk::Display::get_default()->get_pointer(x, y, mt);
286 if (mt) {
287 return true;
288 } else {
289 this->receiveConnectRequest(recipientJID.c_str());
290 return false;
291 }
292 }
294 }
296 }
298 /*
299 Local Variables:
300 mode:c++
301 c-file-style:"stroustrup"
302 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
303 indent-tabs-mode:nil
304 fill-column:99
305 End:
306 */
307 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :