1 /*
2  * Copyright © 2004-2008 Jens Oknelid, paskharen@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * In addition, as a special exception, compiling, linking, and/or
19  * using OpenSSL with this program is allowed.
20  */
21 
22 #include "privatemessage.hh"
23 
24 #include <dcpp/ClientManager.h>
25 #include <dcpp/FavoriteManager.h>
26 #include "wulformanager.hh"
27 #include "WulforUtil.hh"
28 #include "search.hh"
29 
30 using namespace std;
31 using namespace dcpp;
32 
33 const std::string PrivateMessage::USER_AVAILABLE_ICON = "user-available";
34 const std::string PrivateMessage::USER_OFFLINE_ICON = "user-offline";
35 
PrivateMessage(const string & cid,const string & hubUrl)36 PrivateMessage::PrivateMessage(const string &cid, const string &hubUrl):
37 	BookEntry(Entry::PRIVATE_MESSAGE, cid, "privatemessage.glade", cid),
38 	cid(cid),
39 	hubUrl(hubUrl),
40 	historyIndex(0),
41 	sentAwayMessage(FALSE),
42 	scrollToBottom(TRUE)
43 {
44 	// Intialize the chat window
45 	if (SETTING(USE_OEM_MONOFONT))
46 	{
47 		PangoFontDescription *fontDesc = pango_font_description_new();
48 		pango_font_description_set_family(fontDesc, "Mono");
49 		gtk_widget_modify_font(getWidget("text"), fontDesc);
50 		pango_font_description_free(fontDesc);
51 	}
52 	GtkTextIter iter;
53 	buffer = gtk_text_buffer_new(NULL);
54 	gtk_text_view_set_buffer(GTK_TEXT_VIEW(getWidget("text")), buffer);
55 	gtk_text_buffer_get_end_iter(buffer, &iter);
56 	mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
57 	handCursor = gdk_cursor_new(GDK_HAND2);
58 
59 	/* FIXME: BOOLSETTING(PRIVATE_MESSGE_BEEP_OPEN) has been changed to
60 	 * FIXME: STRINGSETTING(SOUND_PM_WINDOW)
61 
62 	if (BOOLSETTING(PRIVATE_MESSAGE_BEEP_OPEN))
63 		gdk_beep();
64 	*/
65 
66 	GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(getWidget("scroll")));
67 
68 	// Connect the signals to their callback functions.
69 	g_signal_connect(getContainer(), "focus-in-event", G_CALLBACK(onFocusIn_gui), (gpointer)this);
70 	g_signal_connect(getWidget("entry"), "activate", G_CALLBACK(onSendMessage_gui), (gpointer)this);
71 	g_signal_connect(getWidget("entry"), "key-press-event", G_CALLBACK(onKeyPress_gui), (gpointer)this);
72 	g_signal_connect(getWidget("text"), "motion-notify-event", G_CALLBACK(onChatPointerMoved_gui), (gpointer)this);
73 	g_signal_connect(getWidget("text"), "visibility-notify-event", G_CALLBACK(onChatVisibilityChanged_gui), (gpointer)this);
74 	g_signal_connect(adjustment, "value_changed", G_CALLBACK(onChatScroll_gui), (gpointer)this);
75 	g_signal_connect(adjustment, "changed", G_CALLBACK(onChatResize_gui), (gpointer)this);
76 	g_signal_connect(getWidget("copyLinkItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
77 	g_signal_connect(getWidget("openLinkItem"), "activate", G_CALLBACK(onOpenLinkClicked_gui), (gpointer)this);
78 	g_signal_connect(getWidget("copyhubItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
79 	g_signal_connect(getWidget("openhubItem"), "activate", G_CALLBACK(onOpenHubClicked_gui), (gpointer)this);
80 	g_signal_connect(getWidget("copyMagnetItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
81 	g_signal_connect(getWidget("searchMagnetItem"), "activate", G_CALLBACK(onSearchMagnetClicked_gui), (gpointer)this);
82 	g_signal_connect(getWidget("magnetPropertiesItem"), "activate", G_CALLBACK(onMagnetPropertiesClicked_gui), (gpointer)this);
83 
84 	gtk_widget_grab_focus(getWidget("entry"));
85 	history.push_back("");
86 	user = ClientManager::getInstance()->findUser(CID(cid));
87 	isBot = user ? user->isSet(User::BOT) : FALSE;
88 
89 	updateUserStatus_gui(WulforUtil::getNicks(cid), WulforUtil::getHubNames(cid));
90 }
91 
~PrivateMessage()92 PrivateMessage::~PrivateMessage()
93 {
94 	ClientManager::getInstance()->removeListener(this);
95 	if (handCursor)
96 	{
97 		gdk_cursor_unref(handCursor);
98 		handCursor = NULL;
99 	}
100 }
101 
show()102 void PrivateMessage::show()
103 {
104 	ClientManager::getInstance()->addListener(this);
105 }
106 
addMessage_gui(string message)107 void PrivateMessage::addMessage_gui(string message)
108 {
109 	addLine_gui(message);
110 
111 	if (BOOLSETTING(LOG_PRIVATE_CHAT))
112 	{
113 		StringMap params;
114 		params["message"] = message;
115 		params["hubNI"] = WulforUtil::getHubNames(cid);
116 		params["hubURL"] = Util::toString(ClientManager::getInstance()->getHubs(CID(cid)));
117 		params["userCID"] = cid;
118 		params["userNI"] = ClientManager::getInstance()->getNicks(CID(cid))[0];
119 		params["myCID"] = ClientManager::getInstance()->getMe()->getCID().toBase32();
120 		LOG(LogManager::PM, params);
121 	}
122 
123 	if (BOOLSETTING(BOLD_PM))
124 		setUrgent_gui();
125 
126 	/*if (BOOLSETTING(PRIVATE_MESSAGE_BEEP) && !isActive_gui())
127 		gdk_beep();
128 	*/
129 
130 	// Send an away message, but only the first time after setting away mode.
131 	if (!Util::getAway())
132 	{
133 		sentAwayMessage = FALSE;
134 	}
135 	else if (!sentAwayMessage && !(BOOLSETTING(NO_AWAYMSG_TO_BOTS) && isBot))
136 	{
137 		sentAwayMessage = TRUE;
138 		typedef Func1<PrivateMessage, string> F1;
139 		F1 *func = new F1(this, &PrivateMessage::sendMessage_client, Util::getAwayMessage());
140 		WulforManager::get()->dispatchClientFunc(func);
141 	}
142 
143 	/* FIXME: BOOLSETTING(PRIVATE_MESSAGE_BEEP) has been changed to
144 	 * FIXME: STRINGSETTING(SOUND_PM)
145 	if (BOOLSETTING(PRIVATE_MESSAGE_BEEP))
146 	{
147 		MainWindow *mw = WulforManager::get()->getMainWindow();
148 		GdkWindowState state = gdk_window_get_state(mw->getContainer()->window);
149 	 	if ((state & GDK_WINDOW_STATE_ICONIFIED) || mw->currentPage_gui() != getContainer())
150 			gdk_beep();
151 	}
152 	*/
153 }
154 
addStatusMessage_gui(string message)155 void PrivateMessage::addStatusMessage_gui(string message)
156 {
157 	addLine_gui("*** " + message);
158 }
159 
addLine_gui(const string & message)160 void PrivateMessage::addLine_gui(const string &message)
161 {
162 	GtkTextIter iter;
163 	string line = "";
164 	string::size_type start, end = 0;
165 	GtkTextIter i_start, i_end;
166 
167 	// Add a new line if this isn't the first line in buffer.
168 	if (gtk_text_buffer_get_char_count(buffer) > 0)
169 		line = "\n";
170 
171 	if (BOOLSETTING(TIME_STAMPS))
172 		line += "[" + Util::getShortTimeString() + "] ";
173 
174 	line += message;
175 
176 	gtk_text_buffer_get_end_iter(buffer, &iter);
177 	gtk_text_buffer_insert(buffer, &iter, line.c_str(), line.size());
178 
179 	// check for URIs in line
180 	gtk_text_buffer_get_end_iter(buffer, &iter);
181 
182 	while ((start = line.find_first_not_of(" \n\r\t", end)) != string::npos)
183 	{
184 		end = line.find_first_of(" \n\r\t", start);
185 		if (end == string::npos)
186 			end = line.size();
187 
188 		string uri = line.substr(start, end - start);
189 		GCallback callback = NULL;
190 
191 		if (WulforUtil::isLink(uri))
192 			callback = G_CALLBACK(onLinkTagEvent_gui);
193 		else if (WulforUtil::isHubURL(uri))
194 			callback = G_CALLBACK(onHubTagEvent_gui);
195 		else if (WulforUtil::isMagnet(uri))
196 			callback = G_CALLBACK(onMagnetTagEvent_gui);
197 
198 		if (callback)
199 		{
200 			// check for the URI in our buffer
201 			GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), uri.c_str());
202 
203 			if (!tag)
204 			{
205 				tag = gtk_text_buffer_create_tag(buffer, uri.c_str(), "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
206 				g_signal_connect(tag, "event", callback, (gpointer)this);
207 			}
208 
209 			i_start = i_end = iter;
210 			gtk_text_iter_backward_chars(&i_start, g_utf8_strlen(line.c_str() + start, -1));
211 			gtk_text_iter_backward_chars(&i_end, g_utf8_strlen(line.c_str() + end, -1));
212 			gtk_text_buffer_apply_tag(buffer, tag, &i_start, &i_end);
213 		}
214 	}
215 
216 	if (gtk_text_buffer_get_line_count(buffer) > maxLines)
217 	{
218 		GtkTextIter next;
219 		gtk_text_buffer_get_start_iter(buffer, &iter);
220 		gtk_text_buffer_get_iter_at_line(buffer, &next, 1);
221 		gtk_text_buffer_delete(buffer, &iter, &next);
222 	}
223 }
224 
updateCursor(GtkWidget * widget)225 void PrivateMessage::updateCursor(GtkWidget *widget)
226 {
227 	gint x, y, buf_x, buf_y;
228 	GtkTextIter iter;
229 	GSList *tagList;
230 	GtkTextTag *newTag = NULL;
231 
232 	gdk_window_get_pointer(widget->window, &x, &y, NULL);
233 
234 	// Check for tags under the cursor, and change mouse cursor appropriately
235 	gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_WIDGET, x, y, &buf_x, &buf_y);
236 	gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, buf_x, buf_y);
237 	tagList = gtk_text_iter_get_tags(&iter);
238 
239 	if (tagList != NULL)
240 	{
241 		newTag = GTK_TEXT_TAG(tagList->data);
242 		g_slist_free(tagList);
243 	}
244 
245 
246 	if (newTag != selectedTag)
247 	{
248 		// Cursor is in transition.
249 		if (newTag != NULL)
250 		{
251 			// Cursor is entering a tag.
252 			selectedURI = newTag->name;
253 			if (selectedTag == NULL)
254 			{
255 				// Cursor was in neutral space.
256 				gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT), handCursor);
257 			}
258 		}
259 		else
260 		{
261 			// Cursor is entering neutral space.
262 			gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT), NULL);
263 		}
264 
265 		selectedTag = newTag;
266 	}
267 }
268 
updateUserStatus_gui(string nicks,string hubs,bool online,string statusMessage)269 void PrivateMessage::updateUserStatus_gui(string nicks, string hubs, bool online, string statusMessage)
270 {
271 	setLabel_gui(nicks + " - " + hubs);
272 	setIcon_gui(online ? USER_AVAILABLE_ICON : USER_OFFLINE_ICON);
273 
274 	if (!statusMessage.empty())
275 		addStatusMessage_gui(statusMessage);
276 }
277 
onFocusIn_gui(GtkWidget * widget,GdkEventFocus * event,gpointer data)278 gboolean PrivateMessage::onFocusIn_gui(GtkWidget *widget, GdkEventFocus *event, gpointer data)
279 {
280 	PrivateMessage *pm = (PrivateMessage *)data;
281 
282 	gtk_widget_grab_focus(pm->getWidget("entry"));
283 
284 	return TRUE;
285 }
286 
onSendMessage_gui(GtkEntry * entry,gpointer data)287 void PrivateMessage::onSendMessage_gui(GtkEntry *entry, gpointer data)
288 {
289 	string text = gtk_entry_get_text(entry);
290 	if (text.empty())
291 		return;
292 
293 	PrivateMessage *pm = (PrivateMessage *)data;
294 	gtk_entry_set_text(entry, "");
295 
296 	// Store line in chat history
297 	pm->history.pop_back();
298 	pm->history.push_back(text);
299 	pm->history.push_back("");
300 	pm->historyIndex = pm->history.size() - 1;
301 	if (pm->history.size() > maxHistory + 1)
302 		pm->history.erase(pm->history.begin());
303 
304 	// Process special commands
305 	if (text[0] == '/')
306 	{
307 		string command, param;
308 		string::size_type separator = text.find_first_of(' ');
309 		if (separator != string::npos && text.size() > separator + 1)
310 		{
311 			command = text.substr(1, separator - 1);
312 			param = text.substr(separator + 1);
313 		}
314 		else
315 		{
316 			command = text.substr(1);
317 		}
318 		std::transform(command.begin(), command.end(), command.begin(), (int(*)(int))tolower);
319 
320 		if (command == "away")
321 		{
322 			if (Util::getAway() && param.empty())
323 			{
324 				Util::setAway(FALSE);
325 				Util::setManualAway(FALSE);
326 				pm->addStatusMessage_gui(_("Away mode off"));
327 				pm->sentAwayMessage = FALSE;
328 			}
329 			else
330 			{
331 				Util::setAway(TRUE);
332 				Util::setManualAway(TRUE);
333 				Util::setAwayMessage(param);
334 				pm->addStatusMessage_gui(F_("Away mode on: %1%", % Util::getAwayMessage()));
335 			}
336 		}
337 		else if (command == "back")
338 		{
339 			Util::setAway(FALSE);
340 			pm->addStatusMessage_gui(_("Away mode off"));
341 		}
342 		else if (command == "clear")
343 		{
344 			GtkTextIter startIter, endIter;
345 			gtk_text_buffer_get_start_iter(pm->buffer, &startIter);
346 			gtk_text_buffer_get_end_iter(pm->buffer, &endIter);
347 			gtk_text_buffer_delete(pm->buffer, &startIter, &endIter);
348 		}
349 		else if (command == "close")
350 		{
351 			WulforManager::get()->getMainWindow()->removeBookEntry_gui(pm);
352 		}
353 		else if (command == "favorite" || text == "fav")
354 		{
355 			typedef Func0<PrivateMessage> F0;
356 			F0 *func = new F0(pm, &PrivateMessage::addFavoriteUser_client);
357 			WulforManager::get()->dispatchClientFunc(func);
358 			pm->addStatusMessage_gui(_("Added user to favorites list"));
359 		}
360 		else if (command == "getlist")
361 		{
362 			typedef Func0<PrivateMessage> F0;
363 			F0 *func = new F0(pm, &PrivateMessage::getFileList_client);
364 			WulforManager::get()->dispatchClientFunc(func);
365 		}
366 		else if (command == "grant")
367 		{
368 			typedef Func0<PrivateMessage> F0;
369 			F0 *func = new F0(pm, &PrivateMessage::grantSlot_client);
370 			WulforManager::get()->dispatchClientFunc(func);
371 			pm->addStatusMessage_gui(_("Slot granted"));
372 		}
373 		else if (command == "help")
374 		{
375 			// TRANSLATORS: /commands aren't translatable. So leave them as they are in the help string
376 			pm->addStatusMessage_gui(_("Available commands: /away <message>, /back, /clear, /close, /favorite, /getlist, /grant, /help"));
377 		}
378 		else
379 		{
380 			pm->addStatusMessage_gui(F_("Unknown command '%1%': Type /help for a list of available commands", % text));
381 		}
382 	}
383 	else
384 	{
385 		typedef Func1<PrivateMessage, string> F1;
386 		F1 *func = new F1(pm, &PrivateMessage::sendMessage_client, text);
387 		WulforManager::get()->dispatchClientFunc(func);
388 	}
389 }
390 
onKeyPress_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)391 gboolean PrivateMessage::onKeyPress_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
392 {
393 	PrivateMessage *pm = (PrivateMessage *)data;
394 	string text;
395 	size_t index;
396 
397 	if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
398 	{
399 		index = pm->historyIndex - 1;
400 		if (index >= 0 && index < pm->history.size())
401 		{
402 			text = pm->history[index];
403 			pm->historyIndex = index;
404 			gtk_entry_set_text(GTK_ENTRY(widget), text.c_str());
405 		}
406 		return TRUE;
407 	}
408 	else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
409 	{
410 		index = pm->historyIndex + 1;
411 		if (index >= 0 && index < pm->history.size())
412 		{
413 			text = pm->history[index];
414 			pm->historyIndex = index;
415 			gtk_entry_set_text(GTK_ENTRY(widget), text.c_str());
416 		}
417 		return TRUE;
418 	}
419 
420 	return FALSE;
421 }
422 
onLinkTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)423 gboolean PrivateMessage::onLinkTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
424 {
425 	PrivateMessage *pm = (PrivateMessage *)data;
426 
427 	if (event->type == GDK_BUTTON_PRESS)
428 	{
429 		pm->selectedURI = tag->name;
430 
431 		switch (event->button.button)
432 		{
433 			case 1:
434 				onOpenLinkClicked_gui(NULL, data);
435 				break;
436 			case 3:
437 				// Pop-up link context menu
438 				gtk_widget_show_all(pm->getWidget("linkMenu"));
439 				gtk_menu_popup(GTK_MENU(pm->getWidget("linkMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
440 				break;
441 		}
442 		return TRUE;
443 	}
444 	return FALSE;
445 }
446 
onHubTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)447 gboolean PrivateMessage::onHubTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
448 {
449 	PrivateMessage *pm = (PrivateMessage *)data;
450 
451 	if (event->type == GDK_BUTTON_PRESS)
452 	{
453 		pm->selectedURI = tag->name;
454 
455 		switch (event->button.button)
456 		{
457 			case 1:
458 				onOpenHubClicked_gui(NULL, data);
459 				break;
460 			case 3:
461 				// Popup hub context menu
462 				gtk_widget_show_all(pm->getWidget("hubMenu"));
463 				gtk_menu_popup(GTK_MENU(pm->getWidget("hubMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
464 				break;
465 		}
466 		return TRUE;
467 	}
468 	return FALSE;
469 }
470 
onMagnetTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)471 gboolean PrivateMessage::onMagnetTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
472 {
473 	PrivateMessage *pm = (PrivateMessage *)data;
474 
475 	if (event->type == GDK_BUTTON_PRESS)
476 	{
477 		pm->selectedURI = tag->name;
478 
479 		switch (event->button.button)
480 		{
481 			case 1:
482 				// Search for magnet
483 				onSearchMagnetClicked_gui(NULL, data);
484 				break;
485 			case 3:
486 				// Popup magnet context menu
487 				gtk_widget_show_all(pm->getWidget("magnetMenu"));
488 				gtk_menu_popup(GTK_MENU(pm->getWidget("magnetMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
489 				break;
490 		}
491 		return TRUE;
492 	}
493 	return FALSE;
494 }
495 
onChatPointerMoved_gui(GtkWidget * widget,GdkEventMotion * event,gpointer data)496 gboolean PrivateMessage::onChatPointerMoved_gui(GtkWidget* widget, GdkEventMotion* event, gpointer data)
497 {
498 	PrivateMessage *pm = (PrivateMessage *)data;
499 
500 	pm->updateCursor(widget);
501 
502 	return FALSE;
503 }
504 
onChatVisibilityChanged_gui(GtkWidget * widget,GdkEventVisibility * event,gpointer data)505 gboolean PrivateMessage::onChatVisibilityChanged_gui(GtkWidget* widget, GdkEventVisibility* event, gpointer data)
506 {
507 	PrivateMessage *pm = (PrivateMessage *)data;
508 
509 	pm->updateCursor(widget);
510 
511 	return FALSE;
512 }
513 
onChatScroll_gui(GtkAdjustment * adjustment,gpointer data)514 void PrivateMessage::onChatScroll_gui(GtkAdjustment *adjustment, gpointer data)
515 {
516 	PrivateMessage *pm = (PrivateMessage *)data;
517 	gdouble value = gtk_adjustment_get_value(adjustment);
518 	pm->scrollToBottom = value >= (adjustment->upper - adjustment->page_size);
519 }
520 
onChatResize_gui(GtkAdjustment * adjustment,gpointer data)521 void PrivateMessage::onChatResize_gui(GtkAdjustment *adjustment, gpointer data)
522 {
523 	PrivateMessage *pm = (PrivateMessage *)data;
524 	gdouble value = gtk_adjustment_get_value(adjustment);
525 
526 	if (pm->scrollToBottom && value < (adjustment->upper - adjustment->page_size))
527 	{
528 		GtkTextIter iter;
529 
530 		gtk_text_buffer_get_end_iter(pm->buffer, &iter);
531 		gtk_text_buffer_move_mark(pm->buffer, pm->mark, &iter);
532 		gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(pm->getWidget("text")), pm->mark, 0, FALSE, 0, 0);
533 	}
534 }
535 
onCopyURIClicked_gui(GtkMenuItem * item,gpointer data)536 void PrivateMessage::onCopyURIClicked_gui(GtkMenuItem *item, gpointer data)
537 {
538 	PrivateMessage *pm = (PrivateMessage *)data;
539 
540 	gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), pm->selectedURI.c_str(), pm->selectedURI.length());
541 }
542 
onOpenLinkClicked_gui(GtkMenuItem * item,gpointer data)543 void PrivateMessage::onOpenLinkClicked_gui(GtkMenuItem *item, gpointer data)
544 {
545 	PrivateMessage *pm = (PrivateMessage *)data;
546 
547 	WulforUtil::openURI(pm->selectedURI);
548 }
549 
onOpenHubClicked_gui(GtkMenuItem * item,gpointer data)550 void PrivateMessage::onOpenHubClicked_gui(GtkMenuItem *item, gpointer data)
551 {
552 	PrivateMessage *pm = (PrivateMessage *)data;
553 
554 	WulforManager::get()->getMainWindow()->showHub_gui(pm->selectedURI);
555 }
556 
onSearchMagnetClicked_gui(GtkMenuItem * item,gpointer data)557 void PrivateMessage::onSearchMagnetClicked_gui(GtkMenuItem *item, gpointer data)
558 {
559 	PrivateMessage *pm = (PrivateMessage *)data;
560 	string name;
561 	int64_t size;
562 	string tth;
563 
564 	if (WulforUtil::splitMagnet(pm->selectedURI, name, size, tth))
565 	{
566 		Search *s = WulforManager::get()->getMainWindow()->addSearch_gui();
567 		s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
568 	}
569 }
570 
onMagnetPropertiesClicked_gui(GtkMenuItem * item,gpointer data)571 void PrivateMessage::onMagnetPropertiesClicked_gui(GtkMenuItem *item, gpointer data)
572 {
573 	PrivateMessage *pm = (PrivateMessage *)data;
574 
575 	WulforManager::get()->getMainWindow()->openMagnetDialog_gui(pm->selectedURI);
576 }
577 
sendMessage_client(std::string message)578 void PrivateMessage::sendMessage_client(std::string message)
579 {
580 	if (user->isOnline())
581 	{
582 		// FIXME: WTF does the 3rd param (bool thirdPerson) do? A: Used for /me stuff
583 		ClientManager::getInstance()->privateMessage(user, message, false, hubUrl);
584 	}
585 	else
586 	{
587 		typedef Func1<PrivateMessage, string> F1;
588 		F1 *func = new F1(this, &PrivateMessage::addStatusMessage_gui, _("User went offline"));
589 		WulforManager::get()->dispatchGuiFunc(func);
590 	}
591 }
592 
addFavoriteUser_client()593 void PrivateMessage::addFavoriteUser_client()
594 {
595 	FavoriteManager::getInstance()->addFavoriteUser(user);
596 }
597 
getFileList_client()598 void PrivateMessage::getFileList_client()
599 {
600 	try
601 	{
602 		QueueManager::getInstance()->addList(user, hubUrl, QueueItem::FLAG_CLIENT_VIEW);
603 	}
604 	catch (const Exception& e)
605 	{
606 		typedef Func1<PrivateMessage, string> F1;
607 		F1 *func = new F1(this, &PrivateMessage::addStatusMessage_gui, e.getError());
608 		WulforManager::get()->dispatchGuiFunc(func);
609 	}
610 }
611 
grantSlot_client()612 void PrivateMessage::grantSlot_client()
613 {
614 	UploadManager::getInstance()->reserveSlot(user, hubUrl);
615 }
616 
on(ClientManagerListener::UserConnected,const UserPtr & aUser)617 void PrivateMessage::on(ClientManagerListener::UserConnected, const UserPtr& aUser) throw()
618 {
619 	if (aUser == user)
620 	{
621 		string statusMessage;
622 		string nicks = WulforUtil::getNicks(user); // TODO: Use aUser->hubHint on later cores
623 		string hubs = WulforUtil::getHubNames(cid);
624 
625 		if (BOOLSETTING(SHOW_JOINS) || (BOOLSETTING(FAV_SHOW_JOINS) &&
626 			FavoriteManager::getInstance()->isFavoriteUser(user)))
627 		{
628 			statusMessage = nicks + _(" has joined");
629 		}
630 
631 		typedef Func4<PrivateMessage, string, string, bool, string> F4;
632 		F4 *func = new F4(this, &PrivateMessage::updateUserStatus_gui, nicks, hubs, user->isOnline(), statusMessage);
633 		WulforManager::get()->dispatchGuiFunc(func);
634 	}
635 }
636 
on(ClientManagerListener::UserDisconnected,const UserPtr & aUser)637 void PrivateMessage::on(ClientManagerListener::UserDisconnected, const UserPtr& aUser) throw()
638 {
639 	if (aUser == user)
640 	{
641 		string statusMessage;
642 		string nicks = WulforUtil::getNicks(user);
643 		string hubs = WulforUtil::getHubNames(cid);
644 
645 		if (BOOLSETTING(SHOW_JOINS) || (BOOLSETTING(FAV_SHOW_JOINS) &&
646 			FavoriteManager::getInstance()->isFavoriteUser(user)))
647 		{
648 			statusMessage = F_("%1% has quit", % nicks);
649 		}
650 
651 		typedef Func4<PrivateMessage, string, string, bool, string> F4;
652 		F4 *func = new F4(this, &PrivateMessage::updateUserStatus_gui, nicks, hubs, user->isOnline(), statusMessage);
653 		WulforManager::get()->dispatchGuiFunc(func);
654 	}
655 }
656 
on(ClientManagerListener::UserUpdated,const OnlineUser & aUser)657 void PrivateMessage::on(ClientManagerListener::UserUpdated, const OnlineUser& aUser) throw()
658 {
659 	if (aUser.getUser() == user)
660 	{
661 		string nicks = WulforUtil::getNicks(user);
662 		string hubs = WulforUtil::getHubNames(cid);
663 
664 		typedef Func4<PrivateMessage, string, string, bool, string> F4;
665 		F4 *func = new F4(this, &PrivateMessage::updateUserStatus_gui, nicks, hubs, user->isOnline(), "");
666 		WulforManager::get()->dispatchGuiFunc(func);
667 	}
668 }
669 
670