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 "hub.hh"
23 
24 #include <dcpp/FavoriteManager.h>
25 #include <dcpp/HashManager.h>
26 #include <dcpp/SearchManager.h>
27 #include <dcpp/ShareManager.h>
28 #include <dcpp/UserCommand.h>
29 #include "privatemessage.hh"
30 #include "search.hh"
31 #include "settingsmanager.hh"
32 #include "UserCommandMenu.hh"
33 #include "wulformanager.hh"
34 #include "WulforUtil.hh"
35 
36 using namespace std;
37 using namespace dcpp;
38 
Hub(const string & address,const string & encoding)39 Hub::Hub(const string &address, const string &encoding):
40 	BookEntry(Entry::HUB, address, "hub.glade", address),
41 	client(NULL),
42 	historyIndex(0),
43 	totalShared(0),
44 	selectedTag(NULL),
45 	address(address),
46 	encoding(encoding),
47 	scrollToBottom(TRUE)
48 {
49 	// Configure the dialog
50 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("passwordDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
51 
52 	// Initialize nick treeview
53 	nickView.setView(GTK_TREE_VIEW(getWidget("nickView")), true, "hub");
54 	nickView.insertColumn(N_("User"), G_TYPE_STRING, TreeView::ICON_STRING, 100, "Icon");
55 	nickView.insertColumn(N_("Shared"), G_TYPE_INT64, TreeView::SIZE, 75);
56 	nickView.insertColumn(N_("Description"), G_TYPE_STRING, TreeView::STRING, 85);
57 	nickView.insertColumn(N_("Tag"), G_TYPE_STRING, TreeView::STRING, 100);
58 	nickView.insertColumn(N_("Connection"), G_TYPE_STRING, TreeView::STRING, 85);
59 	nickView.insertColumn(N_("IP"), G_TYPE_STRING, TreeView::STRING, 85);
60 	nickView.insertColumn(N_("Email"), G_TYPE_STRING, TreeView::STRING, 90);
61 	nickView.insertHiddenColumn("Icon", G_TYPE_STRING);
62 	nickView.insertHiddenColumn("Nick Order", G_TYPE_STRING);
63 	nickView.insertHiddenColumn("CID", G_TYPE_STRING);
64 	nickView.finalize();
65 	nickStore = gtk_list_store_newv(nickView.getColCount(), nickView.getGTypes());
66 	gtk_tree_view_set_model(nickView.get(), GTK_TREE_MODEL(nickStore));
67 	g_object_unref(nickStore);
68 	nickSelection = gtk_tree_view_get_selection(nickView.get());
69 	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(nickView.get()), GTK_SELECTION_MULTIPLE);
70 	nickView.setSortColumn_gui("User", "Nick Order");
71 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(nickStore), nickView.col("Nick Order"), GTK_SORT_ASCENDING);
72 	gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(nickView.get(), nickView.col("User")), TRUE);
73 	gtk_tree_view_set_fixed_height_mode(nickView.get(), TRUE);
74 	gtk_tree_view_set_search_equal_func(nickView.get(), onNickListSearch_gui, 0,0);
75 
76 	// Initialize the chat window
77 	if (BOOLSETTING(USE_OEM_MONOFONT))
78 	{
79 		PangoFontDescription *fontDesc = pango_font_description_new();
80 		pango_font_description_set_family(fontDesc, "Mono");
81 		gtk_widget_modify_font(getWidget("chatText"), fontDesc);
82 		pango_font_description_free(fontDesc);
83 	}
84 	chatBuffer = gtk_text_buffer_new(NULL);
85 	gtk_text_view_set_buffer(GTK_TEXT_VIEW(getWidget("chatText")), chatBuffer);
86 	GtkTextIter iter;
87 	gtk_text_buffer_get_end_iter(chatBuffer, &iter);
88 	chatMark = gtk_text_buffer_create_mark(chatBuffer, NULL, &iter, FALSE);
89 	handCursor = gdk_cursor_new(GDK_HAND2);
90 
91 	// Initialize the user command menu
92 	userCommandMenu = new UserCommandMenu(getWidget("usercommandMenu"), ::UserCommand::CONTEXT_CHAT);
93 	addChild(userCommandMenu);
94 
95 	GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(getWidget("chatScroll")));
96 
97 	// Connect the signals to their callback functions.
98 	g_signal_connect(getContainer(), "focus-in-event", G_CALLBACK(onFocusIn_gui), (gpointer)this);
99 	g_signal_connect(nickView.get(), "button-press-event", G_CALLBACK(onNickListButtonPress_gui), (gpointer)this);
100 	g_signal_connect(nickView.get(), "button-release-event", G_CALLBACK(onNickListButtonRelease_gui), (gpointer)this);
101 	g_signal_connect(nickView.get(), "key-release-event", G_CALLBACK(onNickListKeyRelease_gui), (gpointer)this);
102 	g_signal_connect(getWidget("chatEntry"), "activate", G_CALLBACK(onSendMessage_gui), (gpointer)this);
103 	g_signal_connect(getWidget("chatEntry"), "key-press-event", G_CALLBACK(onEntryKeyPress_gui), (gpointer)this);
104 	g_signal_connect(getWidget("chatText"), "motion-notify-event", G_CALLBACK(onChatPointerMoved_gui), (gpointer)this);
105 	g_signal_connect(getWidget("chatText"), "visibility-notify-event", G_CALLBACK(onChatVisibilityChanged_gui), (gpointer)this);
106 	g_signal_connect(adjustment, "value_changed", G_CALLBACK(onChatScroll_gui), (gpointer)this);
107 	g_signal_connect(adjustment, "changed", G_CALLBACK(onChatResize_gui), (gpointer)this);
108 	g_signal_connect(getWidget("copyNickItem"), "activate", G_CALLBACK(onCopyNickItemClicked_gui), (gpointer)this);
109 	g_signal_connect(getWidget("browseItem"), "activate", G_CALLBACK(onBrowseItemClicked_gui), (gpointer)this);
110 	g_signal_connect(getWidget("matchItem"), "activate", G_CALLBACK(onMatchItemClicked_gui), (gpointer)this);
111 	g_signal_connect(getWidget("msgItem"), "activate", G_CALLBACK(onMsgItemClicked_gui), (gpointer)this);
112 	g_signal_connect(getWidget("grantItem"), "activate", G_CALLBACK(onGrantItemClicked_gui), (gpointer)this);
113 	g_signal_connect(getWidget("copyLinkItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
114 	g_signal_connect(getWidget("openLinkItem"), "activate", G_CALLBACK(onOpenLinkClicked_gui), (gpointer)this);
115 	g_signal_connect(getWidget("copyhubItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
116 	g_signal_connect(getWidget("openhubItem"), "activate", G_CALLBACK(onOpenHubClicked_gui), (gpointer)this);
117 	g_signal_connect(getWidget("copyMagnetItem"), "activate", G_CALLBACK(onCopyURIClicked_gui), (gpointer)this);
118 	g_signal_connect(getWidget("searchMagnetItem"), "activate", G_CALLBACK(onSearchMagnetClicked_gui), (gpointer)this);
119 	g_signal_connect(getWidget("magnetPropertiesItem"), "activate", G_CALLBACK(onMagnetPropertiesClicked_gui), (gpointer)this);
120 	g_signal_connect(getWidget("removeUserItem"), "activate", G_CALLBACK(onRemoveUserItemClicked_gui), (gpointer)this);
121 	g_signal_connect(getWidget("favoriteUserItem"), "activate", G_CALLBACK(onAddFavoriteUserClicked_gui), (gpointer)this);
122 
123 	gtk_widget_grab_focus(getWidget("chatEntry"));
124 
125 	// Set the pane position
126 	gint panePosition = WGETI("nick-pane-position");
127 	if (panePosition > 10)
128 	{
129 		gint width;
130 		GtkWindow *window = GTK_WINDOW(WulforManager::get()->getMainWindow()->getContainer());
131 		gtk_window_get_size(window, &width, NULL);
132 		gtk_paned_set_position(GTK_PANED(getWidget("pane")), width - panePosition);
133 	}
134 
135 	history.push_back("");
136 }
137 
~Hub()138 Hub::~Hub()
139 {
140 	disconnect_client();
141 
142 	// Save the pane position
143 	gint width;
144 	GtkWindow *window = GTK_WINDOW(WulforManager::get()->getMainWindow()->getContainer());
145 	gtk_window_get_size(window, &width, NULL);
146 	gint panePosition = width - gtk_paned_get_position(GTK_PANED(getWidget("pane")));
147 	if (panePosition > 10)
148 		WSET("nick-pane-position", panePosition);
149 
150 	gtk_widget_destroy(getWidget("passwordDialog"));
151 
152 	if (handCursor)
153 	{
154 		gdk_cursor_unref(handCursor);
155 		handCursor = NULL;
156 	}
157 }
158 
show()159 void Hub::show()
160 {
161 	// Connect to the hub
162 	typedef Func2<Hub, string, string> F2;
163 	F2 *func = new F2(this, &Hub::connectClient_client, address, encoding);
164 	WulforManager::get()->dispatchClientFunc(func);
165 }
166 
setStatus_gui(string statusBar,string text)167 void Hub::setStatus_gui(string statusBar, string text)
168 {
169 	if (!statusBar.empty() && !text.empty())
170 	{
171 		if (statusBar == "statusMain")
172 			text = "[" + Util::getShortTimeString() + "] " + text;
173 
174 		gtk_statusbar_pop(GTK_STATUSBAR(getWidget(statusBar)), 0);
175 		gtk_statusbar_push(GTK_STATUSBAR(getWidget(statusBar)), 0, text.c_str());
176 	}
177 }
178 
findUser_gui(const string & cid,GtkTreeIter * iter)179 bool Hub::findUser_gui(const string &cid, GtkTreeIter *iter)
180 {
181 	unordered_map<std::string, GtkTreeIter>::const_iterator it = userIters.find(cid);
182 
183 	if (it != userIters.end())
184 	{
185 		if (iter)
186 			*iter = it->second;
187 
188 		return TRUE;
189 	}
190 
191 	return FALSE;
192 }
193 
findNick_gui(const string & nick,GtkTreeIter * iter)194 bool Hub::findNick_gui(const string &nick, GtkTreeIter *iter)
195 {
196 	unordered_map<std::string, std::string>::const_iterator it = userMap.find(nick);
197 
198 	if (it != userMap.end())
199 		return findUser_gui(it->second, iter);
200 
201 	return FALSE;
202 }
203 
updateStats_gui()204 void Hub::updateStats_gui()
205 {
206 	// TRANSLATORS: Count of users appearing in the status bar.
207 	setStatus_gui("statusUsers", P_("%1% User", "%1% Users", % userMap.size(), userMap.size()));
208 	setStatus_gui("statusShared", Util::formatBytes(totalShared));
209 }
210 
updateUser_gui(ParamMap params,bool showJoin)211 void Hub::updateUser_gui(ParamMap params, bool showJoin)
212 {
213 	GtkTreeIter iter;
214 	int64_t shared = Util::toInt64(params["Shared"]);
215 	const string& cid = params["CID"];
216 	const string icon = "linuxdcpp-" + params["Icon"];
217 
218 	if (findUser_gui(cid, &iter))
219 	{
220 		totalShared += shared - nickView.getValue<int64_t>(&iter, "Shared");
221 		string nick = nickView.getString(&iter, "User");
222 
223 		if (nick != params["User"])
224 		{
225 			// User has changed nick, update userMap and remove the old Nick tag
226 			userMap.erase(nick);
227 			removeTag_gui(nick);
228 			userMap[params["User"]] = cid;
229 		}
230 
231 		gtk_list_store_set(nickStore, &iter,
232 			nickView.col("User"), params["User"].c_str(),
233 			nickView.col("Shared"), shared,
234 			nickView.col("Description"), params["Description"].c_str(),
235 			nickView.col("Tag"), params["Tag"].c_str(),
236  			nickView.col("Connection"), params["Connection"].c_str(),
237 			nickView.col("IP"), params["IP"].c_str(),
238 			nickView.col("Email"), params["Email"].c_str(),
239  			nickView.col("Icon"), icon.c_str(),
240 			nickView.col("Nick Order"), params["Nick Order"].c_str(),
241 			nickView.col("CID"), cid.c_str(),
242 			-1);
243 	}
244 	else
245 	{
246 		totalShared += shared;
247 		userMap[params["User"]] = cid;
248 
249 		gtk_list_store_insert_with_values(nickStore, &iter, userMap.size(),
250 			nickView.col("User"), params["User"].c_str(),
251 			nickView.col("Shared"), shared,
252 			nickView.col("Description"), params["Description"].c_str(),
253 			nickView.col("Tag"), params["Tag"].c_str(),
254  			nickView.col("Connection"), params["Connection"].c_str(),
255 			nickView.col("IP"), params["IP"].c_str(),
256 			nickView.col("Email"), params["Email"].c_str(),
257  			nickView.col("Icon"), icon.c_str(),
258 			nickView.col("Nick Order"), params["Nick Order"].c_str(),
259 			nickView.col("CID"), cid.c_str(),
260 			-1);
261 
262 		userIters[cid] = iter;
263 
264 		if (showJoin)
265 			addStatusMessage_gui(F_("%1% has joined", % params["User"]));
266 	}
267 
268 	updateStats_gui();
269 }
270 
removeUser_gui(string cid)271 void Hub::removeUser_gui(string cid)
272 {
273 	GtkTreeIter iter;
274 	string nick;
275 
276 	if (findUser_gui(cid, &iter))
277 	{
278 		nick = nickView.getString(&iter, "User");
279 		totalShared -= nickView.getValue<int64_t>(&iter, "Shared");
280 		gtk_list_store_remove(nickStore, &iter);
281 		removeTag_gui(nick);
282 		userMap.erase(nick);
283 		userIters.erase(cid);
284 		updateStats_gui();
285 	}
286 }
287 
288 /*
289  * Remove nick tag from text view
290  */
removeTag_gui(const std::string & nick)291 void Hub::removeTag_gui(const std::string &nick)
292 {
293 	GtkTextTagTable *textTagTable = gtk_text_buffer_get_tag_table(chatBuffer);
294 	GtkTextTag *tag = gtk_text_tag_table_lookup(textTagTable, nick.c_str());
295 	if (tag)
296 		gtk_text_tag_table_remove(textTagTable, tag);
297 }
298 
clearNickList_gui()299 void Hub::clearNickList_gui()
300 {
301 	// Remove all old nick tags from the text view
302 	unordered_map<string, string>::const_iterator it;
303 	for (it = userMap.begin(); it != userMap.end(); ++it)
304 		removeTag_gui(it->first);
305 
306 	gtk_list_store_clear(nickStore);
307 	userMap.clear();
308 	userIters.clear();
309 	totalShared = 0;
310 	updateStats_gui();
311 }
312 
popupNickMenu_gui()313 void Hub::popupNickMenu_gui()
314 {
315 	// Build user command menu
316 	userCommandMenu->cleanMenu_gui();
317 
318 	GtkTreeIter iter;
319 	GList *list = gtk_tree_selection_get_selected_rows(nickSelection, NULL);
320 
321 	for (GList *i = list; i; i = i->next)
322 	{
323 		GtkTreePath *path = (GtkTreePath *)i->data;
324 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(nickStore), &iter, path))
325  		{
326 			userCommandMenu->addUser(nickView.getString(&iter, "CID"));
327 		}
328 		gtk_tree_path_free(path);
329 	}
330 	g_list_free(list);
331 
332 	userCommandMenu->addHub(client->getHubUrl());
333 	userCommandMenu->buildMenu_gui();
334 
335 	gtk_menu_popup(GTK_MENU(getWidget("nickMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
336 	gtk_widget_show_all(getWidget("nickMenu"));
337 }
338 
getPassword_gui()339 void Hub::getPassword_gui()
340 {
341 	gint ret;
342 
343 	ret = gtk_dialog_run(GTK_DIALOG(getWidget("passwordDialog")));
344 	gtk_widget_hide(getWidget("passwordDialog"));
345 
346 	if (ret == GTK_RESPONSE_OK)
347 	{
348 		string password = gtk_entry_get_text(GTK_ENTRY(getWidget("passwordEntry")));
349 		typedef Func1<Hub, string> F1;
350 		F1 *func = new F1(this, &Hub::setPassword_client, password);
351 		WulforManager::get()->dispatchClientFunc(func);
352 	}
353 	else
354 		client->disconnect(TRUE);
355 }
356 
addStatusMessage_gui(string message)357 void Hub::addStatusMessage_gui(string message)
358 {
359 	if (!message.empty())
360 	{
361 		setStatus_gui("statusMain", message);
362 
363 		if (BOOLSETTING(STATUS_IN_CHAT))
364 		{
365 			string line = "*** " + message;
366 			addMessage_gui(line);
367 		}
368 	}
369 }
370 
addMessage_gui(string message)371 void Hub::addMessage_gui(string message)
372 {
373 	// See lp:541548, Some broken hubs end emotes with \0 instead of |. The core
374 	// then passes the whole command from $ to next | to GUI and the null byte then
375 	// messes the utf8 validation done by gtk_text_buffer_insert. So we drop everything
376 	// after \0.
377 	message = message.c_str();
378 	if (message.empty())
379 		return;
380 
381 	GtkTextIter iter;
382 	string line = "";
383 
384 	// Add a new line if this isn't the first line in buffer.
385 	if (gtk_text_buffer_get_char_count(chatBuffer) > 0)
386 		line = "\n";
387 
388 	if (BOOLSETTING(TIME_STAMPS))
389 		line += "[" + Util::getShortTimeString() + "] ";
390 
391 	line += message;
392 
393 	gtk_text_buffer_get_end_iter(chatBuffer, &iter);
394 	gtk_text_buffer_insert(chatBuffer, &iter, line.c_str(), line.size());
395 
396 	applyTags_gui(line);
397 
398 	gtk_text_buffer_get_end_iter(chatBuffer, &iter);
399 
400 	// Limit size of chat text
401 	if (gtk_text_buffer_get_line_count(chatBuffer) > maxLines)
402 	{
403 		GtkTextIter next;
404 		gtk_text_buffer_get_start_iter(chatBuffer, &iter);
405 		gtk_text_buffer_get_iter_at_line(chatBuffer, &next, 1);
406 		gtk_text_buffer_delete(chatBuffer, &iter, &next);
407 	}
408 }
409 
applyTags_gui(const string & line)410 void Hub::applyTags_gui(const string &line)
411 {
412 	GtkTextIter iter;
413 	GtkTextIter startIter;
414 	GtkTextIter endIter;
415 	bool firstNick = FALSE;
416 	string::size_type start;
417 	string::size_type end = 0;
418 
419 	gtk_text_buffer_get_end_iter(chatBuffer, &iter);
420 
421 	// Tag nicknames and URIs
422 	while ((start = line.find_first_not_of(" \n\r\t", end)) != string::npos)
423 	{
424 		end = line.find_first_of(" \n\r\t", start);
425 		if (end == string::npos)
426 			end = line.size();
427 
428 		// Special case: catch nicks in the form <nick> at the beginning of the line.
429 		if (!firstNick && start < end - 1 && line[start] == '<' && line[end - 1] == '>')
430 		{
431 			++start;
432 			--end;
433 			firstNick = TRUE;
434 		}
435 
436 		GCallback callback = NULL;
437 		bool isNick = FALSE;
438 		string tagName = line.substr(start, end - start);
439 
440 		if (findNick_gui(tagName, NULL))
441 		{
442 			isNick = TRUE;
443 			callback = G_CALLBACK(onNickTagEvent_gui);
444 		}
445 		else
446 		{
447 			if (WulforUtil::isLink(tagName))
448 				callback = G_CALLBACK(onLinkTagEvent_gui);
449 			else if (WulforUtil::isHubURL(tagName))
450 				callback = G_CALLBACK(onHubTagEvent_gui);
451 			else if (WulforUtil::isMagnet(tagName))
452 				callback = G_CALLBACK(onMagnetTagEvent_gui);
453 		}
454 
455 		if (callback)
456 		{
457 			// check for the tag in our buffer
458 			GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(chatBuffer), tagName.c_str());
459 
460 			if (!tag)
461 			{
462 				if (isNick)
463 					tag = gtk_text_buffer_create_tag(chatBuffer, tagName.c_str(), "style", PANGO_STYLE_ITALIC, NULL);
464 				else
465 					tag = gtk_text_buffer_create_tag(chatBuffer, tagName.c_str(), "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
466 
467 				g_signal_connect(tag, "event", callback, (gpointer)this);
468 			}
469 
470 			startIter = endIter = iter;
471 			gtk_text_iter_backward_chars(&startIter, g_utf8_strlen(line.c_str() + start, -1));
472 			gtk_text_iter_backward_chars(&endIter, g_utf8_strlen(line.c_str() + end, -1));
473 			gtk_text_buffer_apply_tag(chatBuffer, tag, &startIter, &endIter);
474 		}
475 	}
476 }
477 
478 /*
479  * Unfortunately, we can't underline the tag on mouse over since it would
480  * underline all the tags with that name.
481  */
updateCursor_gui(GtkWidget * widget)482 void Hub::updateCursor_gui(GtkWidget *widget)
483 {
484 	gint x, y, buf_x, buf_y;
485 	GtkTextIter iter;
486 	GSList *tagList;
487 	GtkTextTag *newTag = NULL;
488 
489 	gdk_window_get_pointer(widget->window, &x, &y, NULL);
490 
491 	// Check for tags under the cursor, and change mouse cursor appropriately
492 	gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_WIDGET, x, y, &buf_x, &buf_y);
493 	gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, buf_x, buf_y);
494 	tagList = gtk_text_iter_get_tags(&iter);
495 
496 	if (tagList != NULL)
497 	{
498 		newTag = GTK_TEXT_TAG(tagList->data);
499 		g_slist_free(tagList);
500 	}
501 
502 	if (newTag != selectedTag)
503 	{
504 		// Cursor is in transition.
505 		if (newTag != NULL)
506 		{
507 			// Cursor is entering a tag.
508 			selectedTagStr = newTag->name;
509 			if (selectedTag == NULL)
510 			{
511 				// Cursor was in neutral space.
512 				gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT), handCursor);
513 			}
514 		}
515 		else
516 		{
517 			// Cursor is entering neutral space.
518 			gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT), NULL);
519 		}
520 		selectedTag = newTag;
521 	}
522 }
523 
onFocusIn_gui(GtkWidget * widget,GdkEventFocus * event,gpointer data)524 gboolean Hub::onFocusIn_gui(GtkWidget *widget, GdkEventFocus *event, gpointer data)
525 {
526 	Hub *hub = (Hub *)data;
527 
528 	gtk_widget_grab_focus(hub->getWidget("chatEntry"));
529 
530 	return TRUE;
531 }
532 
onNickListButtonPress_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)533 gboolean Hub::onNickListButtonPress_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
534 {
535 	Hub *hub = (Hub *)data;
536 
537 	if (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS)
538 		hub->oldType = event->type;
539 
540 	if (event->button == 3)
541 	{
542 		GtkTreePath *path;
543 		if (gtk_tree_view_get_path_at_pos(hub->nickView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
544 		{
545 			bool selected = gtk_tree_selection_path_is_selected(hub->nickSelection, path);
546 			gtk_tree_path_free(path);
547 
548 			if (selected)
549 				return TRUE;
550 		}
551 	}
552 
553 	return FALSE;
554 }
555 
onNickListButtonRelease_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)556 gboolean Hub::onNickListButtonRelease_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
557 {
558 	Hub *hub = (Hub *)data;
559 
560 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
561 	{
562 		if (event->button == 1 && hub->oldType == GDK_2BUTTON_PRESS)
563 		{
564 			hub->onBrowseItemClicked_gui(NULL, data);
565 		}
566 		else if (event->button == 2 && event->type == GDK_BUTTON_RELEASE)
567 		{
568 			hub->onMsgItemClicked_gui(NULL, data);
569 		}
570 		else if (event->button == 3 && event->type == GDK_BUTTON_RELEASE)
571 		{
572 			hub->popupNickMenu_gui();
573 		}
574 	}
575 
576 	return FALSE;
577 }
578 
onNickListKeyRelease_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)579 gboolean Hub::onNickListKeyRelease_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
580 {
581 	Hub *hub = (Hub *)data;
582 
583 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
584 	{
585 		if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
586 		{
587 			hub->popupNickMenu_gui();
588 		}
589 		else if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
590 		{
591 			hub->onBrowseItemClicked_gui(NULL, data);
592 		}
593 	}
594 
595 	return FALSE;
596 }
597 
598 /*
599  * Implements a case-insensitive substring search for UTF-8 strings.
600  */
onNickListSearch_gui(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer data)601 gboolean Hub::onNickListSearch_gui(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data)
602 {
603 	gboolean result = TRUE;
604 	gchar *nick;
605 	gtk_tree_model_get(model, iter, column, &nick, -1);
606 
607 	gchar *keyCasefold = g_utf8_casefold(key, -1);
608 	gchar *nickCasefold = g_utf8_casefold(nick, -1);
609 
610 	// Return false per search equal func API if the key is contained within the nick
611 	if (g_strstr_len(nickCasefold, -1, keyCasefold) != NULL)
612 		result = FALSE;
613 
614 	g_free(nick);
615 	g_free(keyCasefold);
616 	g_free(nickCasefold);
617 
618 	return result;
619 }
620 
onEntryKeyPress_gui(GtkWidget * entry,GdkEventKey * event,gpointer data)621 gboolean Hub::onEntryKeyPress_gui(GtkWidget *entry, GdkEventKey *event, gpointer data)
622 {
623 	Hub *hub = (Hub *)data;
624 
625 	if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
626 	{
627 		size_t index = hub->historyIndex - 1;
628 		if (index >= 0 && index < hub->history.size())
629 		{
630 			hub->historyIndex = index;
631 			gtk_entry_set_text(GTK_ENTRY(entry), hub->history[index].c_str());
632 		}
633 		return TRUE;
634 	}
635 	else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
636 	{
637 		size_t index = hub->historyIndex + 1;
638 		if (index >= 0 && index < hub->history.size())
639 		{
640 			hub->historyIndex = index;
641 			gtk_entry_set_text(GTK_ENTRY(entry), hub->history[index].c_str());
642 		}
643 		return TRUE;
644 	}
645 	else if (event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab)
646 	{
647 		string current;
648 		string::size_type start, end;
649 		string text(gtk_entry_get_text(GTK_ENTRY(entry)));
650 		int curpos = gtk_editable_get_position(GTK_EDITABLE(entry));
651 
652 		// Allow tab to focus other widgets if entry is empty
653 		if (curpos <= 0 && text.empty())
654 			return FALSE;
655 
656 		// Erase ": " at the end of the nick.
657 		if (curpos > 2 && text.substr(curpos - 2, 2) == ": ")
658 		{
659 			text.erase(curpos - 2, 2);
660 			curpos -= 2;
661 		}
662 
663 		start = text.rfind(' ', curpos - 1);
664 		end = text.find(' ', curpos - 1);
665 
666 		// Text to match starts at the beginning
667 		if (start == string::npos)
668 			start = 0;
669 		else
670 			++start;
671 
672 		if (start < end)
673 		{
674 			current = text.substr(start, end - start);
675 
676 			if (hub->completionKey.empty() || Text::toLower(current).find(Text::toLower(hub->completionKey)) == string::npos)
677 				hub->completionKey = current;
678 
679 			GtkTreeIter iter;
680 			bool valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(hub->nickStore), &iter);
681 			bool useNext = (current == hub->completionKey);
682 			string key = Text::toLower(hub->completionKey);
683 			string complete = hub->completionKey;
684 
685 			while (valid)
686 			{
687 				string nick = hub->nickView.getString(&iter, "User");
688 				string::size_type tagEnd = 0;
689 				if (useNext && (tagEnd = Text::toLower(nick).find(key)) != string::npos)
690 				{
691 					if (tagEnd == 0 || nick.find_first_of("]})", tagEnd - 1) == tagEnd - 1)
692 					{
693 						complete = nick;
694 						if (start <= 0)
695 							complete.append(": ");
696 						break;
697 					}
698 				}
699 
700 				if (nick == current)
701 					useNext = TRUE;
702 
703 				valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(hub->nickStore),&iter);
704 			}
705 
706 			text.replace(start, end - start, complete);
707 			gtk_entry_set_text(GTK_ENTRY(entry), text.c_str());
708 			gtk_editable_set_position(GTK_EDITABLE(entry), start + complete.length());
709 		}
710 		else
711 			hub->completionKey.clear();
712 
713 		return TRUE;
714 	}
715 
716 	hub->completionKey.clear();
717 	return FALSE;
718 }
719 
onNickTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)720 gboolean Hub::onNickTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
721 {
722 	Hub *hub = (Hub *)data;
723 
724 	if (event->type == GDK_BUTTON_PRESS)
725 	{
726 		GtkTreeIter nickIter;
727 		if (hub->findNick_gui(tag->name, &nickIter))
728 		{
729 			// Select the user in the nick list view
730 			GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(hub->nickStore), &nickIter);
731 			gtk_tree_view_scroll_to_cell(hub->nickView.get(), path, gtk_tree_view_get_column(hub->nickView.get(), hub->nickView.col("User")), FALSE, 0.0, 0.0);
732 			gtk_tree_view_set_cursor(hub->nickView.get(), path, NULL, FALSE);
733 			gtk_tree_path_free(path);
734 
735 			if (event->button.button == 3)
736 				hub->popupNickMenu_gui();
737 		}
738 
739 		return TRUE;
740 	}
741 	return FALSE;
742 }
743 
onLinkTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)744 gboolean Hub::onLinkTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
745 {
746 	Hub *hub = (Hub *)data;
747 
748 	if (event->type == GDK_BUTTON_PRESS)
749 	{
750 		switch (event->button.button)
751 		{
752 			case 1:
753 				onOpenLinkClicked_gui(NULL, data);
754 				break;
755 			case 3:
756 				// Popup uri context menu
757 				gtk_widget_show_all(hub->getWidget("linkMenu"));
758 				gtk_menu_popup(GTK_MENU(hub->getWidget("linkMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
759 				break;
760 		}
761 		return TRUE;
762 	}
763 	return FALSE;
764 }
765 
onHubTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)766 gboolean Hub::onHubTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
767 {
768 	Hub *hub = (Hub *)data;
769 
770 	if (event->type == GDK_BUTTON_PRESS)
771 	{
772 		switch (event->button.button)
773 		{
774 			case 1:
775 				onOpenHubClicked_gui(NULL, data);
776 				break;
777 			case 3:
778 				// Popup uri context menu
779 				gtk_widget_show_all(hub->getWidget("hubMenu"));
780 				gtk_menu_popup(GTK_MENU(hub->getWidget("hubMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
781 				break;
782 		}
783 		return TRUE;
784 	}
785 	return FALSE;
786 }
787 
onMagnetTagEvent_gui(GtkTextTag * tag,GObject * textView,GdkEvent * event,GtkTextIter * iter,gpointer data)788 gboolean Hub::onMagnetTagEvent_gui(GtkTextTag *tag, GObject *textView, GdkEvent *event, GtkTextIter *iter, gpointer data)
789 {
790 	Hub *hub = (Hub *)data;
791 
792 	if (event->type == GDK_BUTTON_PRESS)
793 	{
794 		switch (event->button.button)
795 		{
796 			case 1:
797 				// Search for magnet
798 				onSearchMagnetClicked_gui(NULL, data);
799 				break;
800 			case 3:
801 				// Popup magnet context menu
802 				gtk_widget_show_all(hub->getWidget("magnetMenu"));
803 				gtk_menu_popup(GTK_MENU(hub->getWidget("magnetMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
804 				break;
805 		}
806 		return TRUE;
807 	}
808 	return FALSE;
809 }
810 
onChatPointerMoved_gui(GtkWidget * widget,GdkEventMotion * event,gpointer data)811 gboolean Hub::onChatPointerMoved_gui(GtkWidget *widget, GdkEventMotion *event, gpointer data)
812 {
813 	Hub *hub = (Hub *)data;
814 
815 	hub->updateCursor_gui(widget);
816 
817 	return FALSE;
818 }
819 
onChatVisibilityChanged_gui(GtkWidget * widget,GdkEventVisibility * event,gpointer data)820 gboolean Hub::onChatVisibilityChanged_gui(GtkWidget *widget, GdkEventVisibility *event, gpointer data)
821 {
822 	Hub *hub = (Hub *)data;
823 
824 	hub->updateCursor_gui(widget);
825 
826 	return FALSE;
827 }
828 
onChatScroll_gui(GtkAdjustment * adjustment,gpointer data)829 void Hub::onChatScroll_gui(GtkAdjustment *adjustment, gpointer data)
830 {
831 	Hub *hub = (Hub *)data;
832 	gdouble value = gtk_adjustment_get_value(adjustment);
833 	hub->scrollToBottom = value >= (adjustment->upper - adjustment->page_size);
834 }
835 
onChatResize_gui(GtkAdjustment * adjustment,gpointer data)836 void Hub::onChatResize_gui(GtkAdjustment *adjustment, gpointer data)
837 {
838 	Hub *hub = (Hub *)data;
839 	gdouble value = gtk_adjustment_get_value(adjustment);
840 
841 	if (hub->scrollToBottom && value < (adjustment->upper - adjustment->page_size))
842 	{
843 		GtkTextIter iter;
844 
845 		gtk_text_buffer_get_end_iter(hub->chatBuffer, &iter);
846 		gtk_text_buffer_move_mark(hub->chatBuffer, hub->chatMark, &iter);
847 		gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(hub->getWidget("chatText")), hub->chatMark, 0, FALSE, 0, 0);
848 	}
849 }
850 
onSendMessage_gui(GtkEntry * entry,gpointer data)851 void Hub::onSendMessage_gui(GtkEntry *entry, gpointer data)
852 {
853 	string text = gtk_entry_get_text(entry);
854 	if (text.empty())
855 		return;
856 
857 	gtk_entry_set_text(entry, "");
858 	Hub *hub = (Hub *)data;
859 	typedef Func1<Hub, string> F1;
860 	F1 *func;
861 	typedef Func2<Hub, string, bool> F2;
862 	F2 *func2;
863 
864 	// Store line in chat history
865 	hub->history.pop_back();
866 	hub->history.push_back(text);
867 	hub->history.push_back("");
868 	hub->historyIndex = hub->history.size() - 1;
869 	if (hub->history.size() > maxHistory + 1)
870 		hub->history.erase(hub->history.begin());
871 
872 	// Process special commands
873 	if (text[0] == '/')
874 	{
875 		string command, param;
876 		string::size_type separator = text.find_first_of(' ');
877 		if (separator != string::npos && text.size() > separator + 1)
878 		{
879 			command = text.substr(1, separator - 1);
880 			param = text.substr(separator + 1);
881 		}
882 		else
883 		{
884 			command = text.substr(1);
885 		}
886 		std::transform(command.begin(), command.end(), command.begin(), (int(*)(int))tolower);
887 
888 		if (command == "away")
889 		{
890 			if (Util::getAway() && param.empty())
891 			{
892 				Util::setAway(FALSE);
893 				Util::setManualAway(FALSE);
894 				hub->addStatusMessage_gui(_("Away mode off"));
895 			}
896 			else
897 			{
898 				Util::setAway(TRUE);
899 				Util::setManualAway(TRUE);
900 				Util::setAwayMessage(param);
901 				hub->addStatusMessage_gui(F_("Away mode on: %1%", % Util::getAwayMessage()));
902 			}
903 		}
904 		else if (command == "back")
905 		{
906 			Util::setAway(FALSE);
907 			hub->addStatusMessage_gui(_("Away mode off"));
908 		}
909 		else if (command == "clear")
910 		{
911 			GtkTextIter startIter, endIter;
912 			gtk_text_buffer_get_start_iter(hub->chatBuffer, &startIter);
913 			gtk_text_buffer_get_end_iter(hub->chatBuffer, &endIter);
914 			gtk_text_buffer_delete(hub->chatBuffer, &startIter, &endIter);
915 		}
916 		else if (command == "close")
917 		{
918 			/// @todo: figure out why this sometimes closes and reopens the tab
919 			WulforManager::get()->getMainWindow()->removeBookEntry_gui(hub);
920 		}
921 		else if (command == "favorite" || command == "fav")
922 		{
923 			WulforManager::get()->dispatchClientFunc(new Func0<Hub>(hub, &Hub::addAsFavorite_client));
924 		}
925 		else if (command == "getlist")
926 		{
927 			if (hub->userMap.find(param) != hub->userMap.end())
928 			{
929 				func2 = new F2(hub, &Hub::getFileList_client, hub->userMap[param], FALSE);
930 				WulforManager::get()->dispatchClientFunc(func2);
931 			}
932 			else
933 				hub->addStatusMessage_gui(_("User not found"));
934 		}
935 		else if (command == "grant")
936 		{
937 			if (hub->userMap.find(param) != hub->userMap.end())
938 			{
939 				func = new F1(hub, &Hub::grantSlot_client, hub->userMap[param]);
940 				WulforManager::get()->dispatchClientFunc(func);
941 			}
942 			else
943 				hub->addStatusMessage_gui(_("User not found"));
944 		}
945 		else if (command == "help")
946 		{
947 			// TRANSLATORS: /commands aren't translatable. So leave them as they are in the help string
948 			hub->addStatusMessage_gui(_("Available commands: /away <message>, /back, /clear, /close, /favorite, "\
949 				 "/getlist <user>, /grant <user>, /help, /join <address>, /me <message>, /pm <user>, /rebuild, /refresh, /userlist"));
950 		}
951 		else if (command == "join" && !param.empty())
952 		{
953 			if (BOOLSETTING(JOIN_OPEN_NEW_WINDOW))
954 			{
955 				// Assumption: new hub is same encoding as current hub.
956 				WulforManager::get()->getMainWindow()->showHub_gui(param, hub->encoding);
957 			}
958 			else
959 			{
960 				typedef Func2<Hub, string, bool> F2;
961 				F2 *func = new F2(hub, &Hub::redirect_client, param, TRUE);
962 				WulforManager::get()->dispatchClientFunc(func);
963 			}
964 		}
965 		else if (command == "me")
966 		{
967 			func2 = new F2(hub, &Hub::sendMessage_client, param, true);
968 			WulforManager::get()->dispatchClientFunc(func2);
969 		}
970 		else if (command == "pm")
971 		{
972 			if (hub->userMap.find(param) != hub->userMap.end())
973 				WulforManager::get()->getMainWindow()->addPrivateMessage_gui(hub->userMap[param], hub->client->getHubUrl());
974 			else
975 				hub->addStatusMessage_gui(_("User not found"));
976 		}
977 		else if (command == "rebuild")
978 		{
979 			WulforManager::get()->dispatchClientFunc(new Func0<Hub>(hub, &Hub::rebuildHashData_client));
980 		}
981 		else if (command == "refresh")
982 		{
983 			WulforManager::get()->dispatchClientFunc(new Func0<Hub>(hub, &Hub::refreshFileList_client));
984 		}
985 		else if (command == "userlist")
986 		{
987 			if (GTK_WIDGET_VISIBLE(hub->getWidget("scrolledwindow2")))
988 				gtk_widget_hide(hub->getWidget("scrolledwindow2"));
989 			else
990 				gtk_widget_show_all(hub->getWidget("scrolledwindow2"));
991 		}
992 		else if (BOOLSETTING(SEND_UNKNOWN_COMMANDS))
993 		{
994 			func2 = new F2(hub, &Hub::sendMessage_client, text, false);
995 			WulforManager::get()->dispatchClientFunc(func2);
996 		}
997 		else
998 		{
999 			hub->addStatusMessage_gui(F_("Unknown command '%1%': Type /help for a list of available commands", % text));
1000 		}
1001 
1002 	}
1003 	else
1004 	{
1005 		func2 = new F2(hub, &Hub::sendMessage_client, text, false);
1006 		WulforManager::get()->dispatchClientFunc(func2);
1007 	}
1008 }
1009 
onCopyNickItemClicked_gui(GtkMenuItem * item,gpointer data)1010 void Hub::onCopyNickItemClicked_gui(GtkMenuItem *item, gpointer data)
1011 {
1012 	Hub *hub = (Hub *)data;
1013 
1014 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1015 	{
1016 		string nicks;
1017 		GtkTreeIter iter;
1018 		GtkTreePath *path;
1019 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1020 
1021 		for (GList *i = list; i; i = i->next)
1022 		{
1023 			path = (GtkTreePath *)i->data;
1024 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1025 			{
1026 				nicks += hub->nickView.getString(&iter, "User") + ' ';
1027 			}
1028 			gtk_tree_path_free(path);
1029 		}
1030 		g_list_free(list);
1031 
1032 		if (!nicks.empty())
1033 		{
1034 			nicks.erase(nicks.length() - 1);
1035 			gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), nicks.c_str(), nicks.length());
1036 		}
1037 	}
1038 }
1039 
onBrowseItemClicked_gui(GtkMenuItem * item,gpointer data)1040 void Hub::onBrowseItemClicked_gui(GtkMenuItem *item, gpointer data)
1041 {
1042 	Hub *hub = (Hub *)data;
1043 
1044 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1045 	{
1046 		string cid;
1047 		GtkTreeIter iter;
1048 		GtkTreePath *path;
1049 		typedef Func2<Hub, string, bool> F2;
1050 		F2 *func;
1051 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1052 
1053 		for (GList *i = list; i; i = i->next)
1054 		{
1055 			path = (GtkTreePath *)i->data;
1056 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1057 			{
1058 				cid = hub->nickView.getString(&iter, "CID");
1059 				func = new F2(hub, &Hub::getFileList_client, cid, FALSE);
1060 				WulforManager::get()->dispatchClientFunc(func);
1061 			}
1062 			gtk_tree_path_free(path);
1063 		}
1064 		g_list_free(list);
1065 	}
1066 }
1067 
onMatchItemClicked_gui(GtkMenuItem * item,gpointer data)1068 void Hub::onMatchItemClicked_gui(GtkMenuItem *item, gpointer data)
1069 {
1070 	Hub *hub = (Hub *)data;
1071 
1072 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1073 	{
1074 		string cid;
1075 		GtkTreeIter iter;
1076 		GtkTreePath *path;
1077 		typedef Func2<Hub, string, bool> F2;
1078 		F2 *func;
1079 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1080 
1081 		for (GList *i = list; i; i = i->next)
1082 		{
1083 			path = (GtkTreePath *)i->data;
1084 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1085 			{
1086 				cid = hub->nickView.getString(&iter, "CID");
1087 				func = new F2(hub, &Hub::getFileList_client, cid, TRUE);
1088 				WulforManager::get()->dispatchClientFunc(func);
1089 			}
1090 			gtk_tree_path_free(path);
1091 		}
1092 		g_list_free(list);
1093 	}
1094 }
1095 
onMsgItemClicked_gui(GtkMenuItem * item,gpointer data)1096 void Hub::onMsgItemClicked_gui(GtkMenuItem *item, gpointer data)
1097 {
1098 	Hub *hub = (Hub *)data;
1099 
1100 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1101 	{
1102 		string cid;
1103 		GtkTreeIter iter;
1104 		GtkTreePath *path;
1105 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1106 		const string &hubUrl = hub->client->getHubUrl();
1107 
1108 		for (GList *i = list; i; i = i->next)
1109 		{
1110 			path = (GtkTreePath *)i->data;
1111 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1112 			{
1113 				cid = hub->nickView.getString(&iter, "CID");
1114 				WulforManager::get()->getMainWindow()->addPrivateMessage_gui(cid, hubUrl);
1115 			}
1116 			gtk_tree_path_free(path);
1117 		}
1118 		g_list_free(list);
1119 	}
1120 }
1121 
onGrantItemClicked_gui(GtkMenuItem * item,gpointer data)1122 void Hub::onGrantItemClicked_gui(GtkMenuItem *item, gpointer data)
1123 {
1124 	Hub *hub = (Hub *)data;
1125 
1126 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1127 	{
1128 		string cid;
1129 		GtkTreeIter iter;
1130 		GtkTreePath *path;
1131 		typedef Func1<Hub, string> F1;
1132 		F1 *func;
1133 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1134 
1135 		for (GList *i = list; i; i = i->next)
1136 		{
1137 			path = (GtkTreePath *)i->data;
1138 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1139 			{
1140 				cid = hub->nickView.getString(&iter, "CID");
1141 				func = new F1(hub, &Hub::grantSlot_client, cid);
1142 				WulforManager::get()->dispatchClientFunc(func);
1143 			}
1144 			gtk_tree_path_free(path);
1145 		}
1146 		g_list_free(list);
1147 	}
1148 }
1149 
onRemoveUserItemClicked_gui(GtkMenuItem * item,gpointer data)1150 void Hub::onRemoveUserItemClicked_gui(GtkMenuItem *item, gpointer data)
1151 {
1152 	Hub *hub = (Hub *)data;
1153 
1154 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1155 	{
1156 		string cid;
1157 		GtkTreeIter iter;
1158 		GtkTreePath *path;
1159 		typedef Func1<Hub, string> F1;
1160 		F1 *func;
1161 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1162 
1163 		for (GList *i = list; i; i = i->next)
1164 		{
1165 			path = (GtkTreePath *)i->data;
1166 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1167 			{
1168 				cid = hub->nickView.getString(&iter, "CID");
1169 				func = new F1(hub, &Hub::removeUserFromQueue_client, cid);
1170 				WulforManager::get()->dispatchClientFunc(func);
1171 			}
1172 			gtk_tree_path_free(path);
1173 		}
1174 		g_list_free(list);
1175 	}
1176 }
1177 
onCopyURIClicked_gui(GtkMenuItem * item,gpointer data)1178 void Hub::onCopyURIClicked_gui(GtkMenuItem *item, gpointer data)
1179 {
1180 	Hub *hub = (Hub *)data;
1181 
1182 	gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), hub->selectedTagStr.c_str(), hub->selectedTagStr.length());
1183 }
1184 
onOpenLinkClicked_gui(GtkMenuItem * item,gpointer data)1185 void Hub::onOpenLinkClicked_gui(GtkMenuItem *item, gpointer data)
1186 {
1187 	Hub *hub = (Hub *)data;
1188 
1189 	WulforUtil::openURI(hub->selectedTagStr);
1190 }
1191 
onOpenHubClicked_gui(GtkMenuItem * item,gpointer data)1192 void Hub::onOpenHubClicked_gui(GtkMenuItem *item, gpointer data)
1193 {
1194 	Hub *hub = (Hub *)data;
1195 
1196 	WulforManager::get()->getMainWindow()->showHub_gui(hub->selectedTagStr);
1197 }
1198 
onSearchMagnetClicked_gui(GtkMenuItem * item,gpointer data)1199 void Hub::onSearchMagnetClicked_gui(GtkMenuItem *item, gpointer data)
1200 {
1201 	Hub *hub = (Hub*)data;
1202 	WulforManager::get()->getMainWindow()->addMagnetSearch_gui(hub->selectedTagStr);
1203 }
1204 
onMagnetPropertiesClicked_gui(GtkMenuItem * item,gpointer data)1205 void Hub::onMagnetPropertiesClicked_gui(GtkMenuItem *item, gpointer data)
1206 {
1207 	Hub *hub = (Hub *)data;
1208 
1209 	WulforManager::get()->getMainWindow()->openMagnetDialog_gui(hub->selectedTagStr);
1210 }
1211 
onAddFavoriteUserClicked_gui(GtkMenuItem * item,gpointer data)1212 void Hub::onAddFavoriteUserClicked_gui(GtkMenuItem *item, gpointer data)
1213 {
1214 	Hub *hub = (Hub *)data;
1215 
1216 	if (gtk_tree_selection_count_selected_rows(hub->nickSelection) > 0)
1217 	{
1218 		string cid, nick;
1219 		GtkTreeIter iter;
1220 		GtkTreePath *path;
1221 		typedef Func1<Hub, string> F1;
1222 		GList *list = gtk_tree_selection_get_selected_rows(hub->nickSelection, NULL);
1223 
1224 		for (GList *i = list; i; i = i->next)
1225 		{
1226 			path = (GtkTreePath *)i->data;
1227 			if (gtk_tree_model_get_iter(GTK_TREE_MODEL(hub->nickStore), &iter, path))
1228 			{
1229 				cid = hub->nickView.getString(&iter, "CID");
1230 				nick = hub->nickView.getString(&iter, "User");
1231 				if (!cid.empty() && nick != hub->client->getMyNick())
1232 				{
1233 					F1 *func = new F1(hub, &Hub::addFavoriteUser_client, cid);
1234 					WulforManager::get()->dispatchClientFunc(func);
1235 				}
1236 			}
1237 			gtk_tree_path_free(path);
1238 		}
1239 		g_list_free(list);
1240 	}
1241 }
1242 
addFavoriteUser_client(const string cid)1243 void Hub::addFavoriteUser_client(const string cid)
1244 {
1245 	UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1246 
1247 	if (user)
1248 	{
1249 		FavoriteManager::getInstance()->addFavoriteUser(user);
1250 	}
1251 }
1252 
connectClient_client(string address,string encoding)1253 void Hub::connectClient_client(string address, string encoding)
1254 {
1255 	dcassert(client == NULL);
1256 
1257 	if (address.substr(0, 6) == "adc://" || address.substr(0, 7) == "adcs://")
1258 		encoding = "UTF-8";
1259 	else if (encoding.empty() || encoding == "Global hub default") // latter for 1.0.3 backwards compatability
1260 		encoding = WGETS("default-charset");
1261 
1262 	if (encoding == WulforUtil::ENCODING_LOCALE)
1263 		encoding = Text::systemCharset;
1264 
1265 	// Only pick "UTF-8" part of "UTF-8 (Unicode)".
1266 	string::size_type i = encoding.find(' ', 0);
1267 	if (i != string::npos)
1268 		encoding = encoding.substr(0, i);
1269 
1270 	client = ClientManager::getInstance()->getClient(address);
1271 	client->setEncoding(encoding);
1272 	client->addListener(this);
1273 	client->connect();
1274 }
1275 
disconnect_client()1276 void Hub::disconnect_client()
1277 {
1278 	if (client)
1279 	{
1280 		client->removeListener(this);
1281 		client->disconnect(TRUE);
1282 		ClientManager::getInstance()->putClient(client);
1283 		client = NULL;
1284 	}
1285 }
1286 
setPassword_client(string password)1287 void Hub::setPassword_client(string password)
1288 {
1289 	if (client && !password.empty())
1290 	{
1291 		client->setPassword(password);
1292 		client->password(password);
1293 	}
1294 }
1295 
sendMessage_client(string message,bool thirdPerson)1296 void Hub::sendMessage_client(string message, bool thirdPerson)
1297 {
1298 	if (client && !message.empty())
1299 		client->hubMessage(message, thirdPerson);
1300 }
1301 
getFileList_client(string cid,bool match)1302 void Hub::getFileList_client(string cid, bool match)
1303 {
1304 	string message;
1305 
1306 	if (!cid.empty())
1307 	{
1308 		try
1309 		{
1310 			UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1311 			if (user)
1312 			{
1313 				if (user == ClientManager::getInstance()->getMe())
1314 				{
1315 					// Don't download file list, open locally instead
1316 					WulforManager::get()->getMainWindow()->openOwnList_client(TRUE);
1317 				}
1318 				else if (match)
1319 				{
1320 					QueueManager::getInstance()->addList(user, client->getHubUrl(), QueueItem::FLAG_MATCH_QUEUE);
1321 				}
1322 				else
1323 				{
1324 					QueueManager::getInstance()->addList(user, client->getHubUrl(), QueueItem::FLAG_CLIENT_VIEW);
1325 				}
1326 			}
1327 			else
1328 			{
1329 				message = _("User not found");
1330 			}
1331 		}
1332 		catch (const Exception &e)
1333 		{
1334 			message = e.getError();
1335 			LogManager::getInstance()->message(message);
1336 		}
1337 	}
1338 
1339 	if (!message.empty())
1340 	{
1341 		typedef Func1<Hub, string> F1;
1342 		F1 *func = new F1(this, &Hub::addStatusMessage_gui, message);
1343 		WulforManager::get()->dispatchGuiFunc(func);
1344 	}
1345 }
1346 
grantSlot_client(string cid)1347 void Hub::grantSlot_client(string cid)
1348 {
1349 	string message = _("User not found");
1350 
1351 	if (!cid.empty())
1352 	{
1353 		UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1354 		if (user)
1355 		{
1356 			UploadManager::getInstance()->reserveSlot(user, client->getHubUrl());
1357 			message = F_("Slot granted to %1%", % WulforUtil::getNicks(user));
1358 		}
1359 	}
1360 
1361 	typedef Func1<Hub, string> F1;
1362 	F1 *func = new F1(this, &Hub::addStatusMessage_gui, message);
1363 	WulforManager::get()->dispatchGuiFunc(func);
1364 }
1365 
removeUserFromQueue_client(std::string cid)1366 void Hub::removeUserFromQueue_client(std::string cid)
1367 {
1368 	if (!cid.empty())
1369 	{
1370 		UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1371 		if (user)
1372 			QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
1373 	}
1374 }
1375 
redirect_client(string address,bool follow)1376 void Hub::redirect_client(string address, bool follow)
1377 {
1378 	if (!address.empty())
1379 	{
1380 		if (ClientManager::getInstance()->isConnected(address))
1381 		{
1382 			string error = _("Unable to connect: already connected to the requested hub");
1383 			typedef Func1<Hub, string> F1;
1384 			F1 *f1 = new F1(this, &Hub::addStatusMessage_gui, error);
1385 			WulforManager::get()->dispatchGuiFunc(f1);
1386 			return;
1387 		}
1388 
1389 		if (follow)
1390 		{
1391 			// the client is dead, long live the client!
1392 			disconnect_client();
1393 
1394 			Func0<Hub> *func = new Func0<Hub>(this, &Hub::clearNickList_gui);
1395 			WulforManager::get()->dispatchGuiFunc(func);
1396 
1397 			connectClient_client(address, encoding);
1398 		}
1399 	}
1400 }
1401 
rebuildHashData_client()1402 void Hub::rebuildHashData_client()
1403 {
1404 	HashManager::getInstance()->rebuild();
1405 }
1406 
refreshFileList_client()1407 void Hub::refreshFileList_client()
1408 {
1409 	try
1410 	{
1411 		ShareManager::getInstance()->setDirty();
1412 		ShareManager::getInstance()->refresh(true);
1413 	}
1414 	catch (const ShareException& e)
1415 	{
1416 	}
1417 }
1418 
addAsFavorite_client()1419 void Hub::addAsFavorite_client()
1420 {
1421 	typedef Func1<Hub, string> F1;
1422 	F1 *func;
1423 
1424 	FavoriteHubEntry *existingHub = FavoriteManager::getInstance()->getFavoriteHubEntry(client->getHubUrl());
1425 
1426 	if (!existingHub)
1427 	{
1428 		FavoriteHubEntry aEntry;
1429 		aEntry.setServer(client->getHubUrl());
1430 		aEntry.setName(client->getHubName());
1431 		aEntry.setDescription(client->getHubDescription());
1432 		aEntry.setConnect(FALSE);
1433 		aEntry.setNick(client->getMyNick());
1434 		aEntry.setEncoding(encoding);
1435 		FavoriteManager::getInstance()->addFavorite(aEntry);
1436 		func = new F1(this, &Hub::addStatusMessage_gui, _("Favorite hub added"));
1437 		WulforManager::get()->dispatchGuiFunc(func);
1438 	}
1439 	else
1440 	{
1441 		func = new F1(this, &Hub::addStatusMessage_gui, _("Favorite hub already exists"));
1442 		WulforManager::get()->dispatchGuiFunc(func);
1443 	}
1444 }
1445 
reconnect_client()1446 void Hub::reconnect_client()
1447 {
1448 	Func0<Hub> *func = new Func0<Hub>(this, &Hub::clearNickList_gui);
1449 	WulforManager::get()->dispatchGuiFunc(func);
1450 
1451 	if (client)
1452 		client->reconnect();
1453 }
1454 
getParams_client(ParamMap & params,const Identity & id)1455 void Hub::getParams_client(ParamMap &params, const Identity &id)
1456 {
1457 	if (id.getUser()->isSet(User::DCPLUSPLUS))
1458 		params["Icon"] = "dc++";
1459 	else
1460 		params["Icon"] = "normal";
1461 
1462 	if (id.getUser()->isSet(User::PASSIVE))
1463 		params["Icon"] += "-fw";
1464 
1465 	if (id.isOp())
1466 	{
1467 		params["Icon"] += "-op";
1468 		params["Nick Order"] = "o" + id.getNick();
1469 	}
1470 	else
1471 	{
1472 		params["Nick Order"] = "u" + id.getNick();
1473 	}
1474 
1475 	params["User"] = id.getNick();
1476 	params["Shared"] = Util::toString(id.getBytesShared());
1477 	params["Description"] = id.getDescription();
1478 	params["Tag"] = id.getTag();
1479 	params["Connection"] = id.getConnection();
1480 	params["IP"] = id.getIp();
1481 	params["Email"] = id.getEmail();
1482 	params["CID"] = id.getUser()->getCID().toBase32();
1483 }
1484 
showJoins_client(const UserPtr & user)1485 bool Hub::showJoins_client(const UserPtr &user)
1486 {
1487 	return BOOLSETTING(SHOW_JOINS) || (BOOLSETTING(FAV_SHOW_JOINS) &&
1488 		FavoriteManager::getInstance()->isFavoriteUser(user));
1489 }
1490 
on(ClientListener::Connecting,Client *)1491 void Hub::on(ClientListener::Connecting, Client *) throw()
1492 {
1493 	typedef Func1<Hub, string> F1;
1494 	F1 *f1 = new F1(this, &Hub::addStatusMessage_gui, F_("Connecting to %1%", % client->getHubUrl()));
1495 	WulforManager::get()->dispatchGuiFunc(f1);
1496 }
1497 
on(ClientListener::Connected,Client *)1498 void Hub::on(ClientListener::Connected, Client *) throw()
1499 {
1500 	typedef Func1<Hub, string> F1;
1501 	F1 *func = new F1(this, &Hub::addStatusMessage_gui, _("Connected"));
1502 	WulforManager::get()->dispatchGuiFunc(func);
1503 }
1504 
on(ClientListener::UserUpdated,Client *,const OnlineUser & user)1505 void Hub::on(ClientListener::UserUpdated, Client *, const OnlineUser &user) throw()
1506 {
1507 	Identity id = user.getIdentity();
1508 
1509 	if (!id.isHidden())
1510 	{
1511 		bool showJoin = showJoins_client(id.getUser());
1512 		ParamMap params;
1513 		getParams_client(params, id);
1514 		typedef Func2<Hub, ParamMap, bool> F2;
1515 		F2 *func = new F2(this, &Hub::updateUser_gui, params, showJoin);
1516 		WulforManager::get()->dispatchGuiFunc(func);
1517 	}
1518 }
1519 
on(ClientListener::UsersUpdated,Client *,const OnlineUserList & list)1520 void Hub::on(ClientListener::UsersUpdated, Client *, const OnlineUserList &list) throw()
1521 {
1522 	Identity id;
1523 	typedef Func2<Hub, ParamMap, bool> F2;
1524 	F2 *func;
1525 
1526 	for (OnlineUserList::const_iterator it = list.begin(); it != list.end(); ++it)
1527 	{
1528 		id = (*it)->getIdentity();
1529 		if (!id.isHidden())
1530 		{
1531 			bool showJoin = showJoins_client(id.getUser());
1532 			ParamMap params;
1533 			getParams_client(params, id);
1534 			func = new F2(this, &Hub::updateUser_gui, params, showJoin);
1535 			WulforManager::get()->dispatchGuiFunc(func);
1536 		}
1537 	}
1538 }
1539 
on(ClientListener::UserRemoved,Client *,const OnlineUser & user)1540 void Hub::on(ClientListener::UserRemoved, Client *, const OnlineUser &user) throw()
1541 {
1542 	string nick = user.getIdentity().getNick();
1543 	string cid = user.getUser()->getCID().toBase32();
1544 	typedef Func1<Hub, string> F1;
1545 	F1 *func;
1546 
1547 	if (showJoins_client(user.getUser()))
1548 	{
1549 		func = new F1(this, &Hub::addStatusMessage_gui, F_("%1% has quit", % nick));
1550 		WulforManager::get()->dispatchGuiFunc(func);
1551 	}
1552 
1553 	func = new F1(this, &Hub::removeUser_gui, cid);
1554 	WulforManager::get()->dispatchGuiFunc(func);
1555 }
1556 
on(ClientListener::Redirect,Client *,const string & address)1557 void Hub::on(ClientListener::Redirect, Client *, const string &address) throw()
1558 {
1559 	// redirect_client() crashes unless I put it into the dispatcher (why?)
1560 	typedef Func2<Hub, string, bool> F2;
1561 	F2 *func = new F2(this, &Hub::redirect_client, address, BOOLSETTING(AUTO_FOLLOW));
1562 	WulforManager::get()->dispatchClientFunc(func);
1563 }
1564 
on(ClientListener::Failed,Client *,const string & reason)1565 void Hub::on(ClientListener::Failed, Client *, const string &reason) throw()
1566 {
1567 	Func0<Hub> *f0 = new Func0<Hub>(this, &Hub::clearNickList_gui);
1568 	WulforManager::get()->dispatchGuiFunc(f0);
1569 
1570 	typedef Func1<Hub, string> F1;
1571 	F1 *f1 = new F1(this, &Hub::addStatusMessage_gui, F_("Connect failed: %1%", % reason));
1572 	WulforManager::get()->dispatchGuiFunc(f1);
1573 }
1574 
on(ClientListener::GetPassword,Client *)1575 void Hub::on(ClientListener::GetPassword, Client *) throw()
1576 {
1577 	if (!client->getPassword().empty())
1578 		client->password(client->getPassword());
1579 	else
1580 	{
1581 		Func0<Hub> *func = new Func0<Hub>(this, &Hub::getPassword_gui);
1582 		WulforManager::get()->dispatchGuiFunc(func);
1583 	}
1584 }
1585 
on(ClientListener::HubUpdated,Client *)1586 void Hub::on(ClientListener::HubUpdated, Client *) throw()
1587 {
1588 	typedef Func1<Hub, string> F1;
1589 	string hubName;
1590 
1591 	if (client->getHubName().empty())
1592 		hubName = client->getAddress() + ":" + Util::toString(client->getPort());
1593 	else
1594 		hubName = client->getHubName();
1595 
1596 	if (!client->getHubDescription().empty())
1597 		hubName += " - " + client->getHubDescription();
1598 
1599 	F1 *func1 = new F1(this, &BookEntry::setLabel_gui, hubName);
1600 	WulforManager::get()->dispatchGuiFunc(func1);
1601 }
1602 
on(ClientListener::Message,Client *,const OnlineUser & from,const string & message,bool thirdPerson)1603 void Hub::on(ClientListener::Message, Client *, const OnlineUser &from, const string &message, bool thirdPerson) throw()
1604 {
1605 	if (!message.empty())
1606 	{
1607 		string line;
1608 
1609 		if (thirdPerson)
1610 			line = "* " + from.getIdentity().getNick() + " " +  message;
1611 		else
1612 			line = "<" + from.getIdentity().getNick() + "> " + message;
1613 
1614 		if (BOOLSETTING(FILTER_MESSAGES))
1615 		{
1616 			if ((message.find("Hub-Security") != string::npos && message.find("was kicked by") != string::npos) ||
1617 				(message.find("is kicking") != string::npos && message.find("because:") != string::npos))
1618 			{
1619 				typedef Func1<Hub, string> F1;
1620 				F1 *func = new F1(this, &Hub::addStatusMessage_gui, line);
1621 				WulforManager::get()->dispatchGuiFunc(func);
1622 				return;
1623 			}
1624 		}
1625 
1626 		if (BOOLSETTING(LOG_MAIN_CHAT))
1627 		{
1628 			StringMap params;
1629 			params["message"] = line;
1630 			client->getHubIdentity().getParams(params, "hub", false);
1631 			params["hubURL"] = client->getHubUrl();
1632 			client->getMyIdentity().getParams(params, "my", true);
1633 			LOG(LogManager::CHAT, params);
1634 		}
1635 
1636 		typedef Func1<Hub, string> F1;
1637 		F1 *func = new F1(this, &Hub::addMessage_gui, line);
1638 		WulforManager::get()->dispatchGuiFunc(func);
1639 
1640 		// Set urgency hint if message contains user's nick
1641 		if (BOOLSETTING(BOLD_HUB) && from.getIdentity().getUser() != client->getMyIdentity().getUser())
1642 		{
1643 			if (message.find(client->getMyIdentity().getNick()) != string::npos)
1644 			{
1645 				typedef Func0<Hub> F0;
1646 				F0 *func = new F0(this, &Hub::setUrgent_gui);
1647 				WulforManager::get()->dispatchGuiFunc(func);
1648 			}
1649 		}
1650 	}
1651 }
1652 
on(ClientListener::StatusMessage,Client *,const string & message,int)1653 void Hub::on(ClientListener::StatusMessage, Client *, const string &message, int /* flag */) throw()
1654 {
1655 	if (!message.empty())
1656 	{
1657 		if (BOOLSETTING(FILTER_MESSAGES))
1658 		{
1659 			if ((message.find("Hub-Security") != string::npos && message.find("was kicked by") != string::npos) ||
1660 				(message.find("is kicking") != string::npos && message.find("because:") != string::npos))
1661 			{
1662 				typedef Func1<Hub, string> F1;
1663 				F1 *func = new F1(this, &Hub::addStatusMessage_gui, message);
1664 				WulforManager::get()->dispatchGuiFunc(func);
1665 				return;
1666 			}
1667 		}
1668 
1669 		if (BOOLSETTING(LOG_STATUS_MESSAGES))
1670 		{
1671 			StringMap params;
1672 			client->getHubIdentity().getParams(params, "hub", FALSE);
1673 			params["hubURL"] = client->getHubUrl();
1674 			client->getMyIdentity().getParams(params, "my", TRUE);
1675 			params["message"] = message;
1676 			LOG(LogManager::STATUS, params);
1677 		}
1678 
1679 		typedef Func1<Hub, string> F1;
1680 		F1 *func = new F1(this, &Hub::addMessage_gui, message);
1681 		WulforManager::get()->dispatchGuiFunc(func);
1682 	}
1683 }
1684 
on(ClientListener::PrivateMessage,Client *,const OnlineUser & from,const OnlineUser & to,const OnlineUser & replyTo,const string & msg,bool thirdPerson)1685 void Hub::on(ClientListener::PrivateMessage, Client *, const OnlineUser &from,
1686 	const OnlineUser& to, const OnlineUser& replyTo, const string &msg, bool thirdPerson) throw()
1687 {
1688 	string error;
1689 	const OnlineUser& user = (replyTo.getUser() == ClientManager::getInstance()->getMe()) ? to : replyTo;
1690 	string line;
1691 
1692  	if (thirdPerson)
1693 		line = "* " + from.getIdentity().getNick() + " " + msg;
1694 	else
1695 		line  = "<" + from.getIdentity().getNick() + "> " + msg;
1696 
1697 	if (user.getIdentity().isHub() && BOOLSETTING(IGNORE_HUB_PMS))
1698 	{
1699 		error = _("Ignored private message from hub");
1700 		typedef Func1<Hub, string> F1;
1701 		F1 *func = new F1(this, &Hub::addStatusMessage_gui, error);
1702 		WulforManager::get()->dispatchGuiFunc(func);
1703 	}
1704 	else if (user.getIdentity().isBot() && BOOLSETTING(IGNORE_BOT_PMS))
1705 	{
1706 		error = F_("Ignored private message from bot %1%", % user.getIdentity().getNick());
1707 		typedef Func1<Hub, string> F1;
1708 		F1 *func = new F1(this, &Hub::addStatusMessage_gui, error);
1709 		WulforManager::get()->dispatchGuiFunc(func);
1710 	}
1711 	else
1712 	{
1713 		typedef Func4<MainWindow, string, string, string, bool> F4;
1714 		F4 *func = new F4(WulforManager::get()->getMainWindow(), &MainWindow::addPrivateMessage_gui,
1715 			user.getUser()->getCID().toBase32(), client->getHubUrl(), line, TRUE);
1716 		WulforManager::get()->dispatchGuiFunc(func);
1717 	}
1718 }
1719 
on(ClientListener::NickTaken,Client *)1720 void Hub::on(ClientListener::NickTaken, Client *) throw()
1721 {
1722 	typedef Func1<Hub, string> F1;
1723 	F1 *func = new F1(this, &Hub::addStatusMessage_gui, _("Nick already taken"));
1724 	WulforManager::get()->dispatchGuiFunc(func);
1725 }
1726 
on(ClientListener::SearchFlood,Client *,const string & msg)1727 void Hub::on(ClientListener::SearchFlood, Client *, const string &msg) throw()
1728 {
1729 	typedef Func1<Hub, string> F1;
1730 	F1 *func = new F1(this, &Hub::addStatusMessage_gui, F_("Search spam detected from %1%", % msg));
1731 	WulforManager::get()->dispatchGuiFunc(func);
1732 }
1733 
1734