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 "mainwindow.hh"
23 
24 #include <iostream>
25 #include <sstream>
26 #include <iomanip>
27 #include <iterator>
28 
29 #ifdef HAVE_LIBNOTIFY
30 #include <libnotify/notify.h>
31 #endif
32 
33 #include <dcpp/FavoriteManager.h>
34 #include <dcpp/ShareManager.h>
35 #include <dcpp/Text.h>
36 #include <dcpp/Upload.h>
37 #include <dcpp/Download.h>
38 #include <dcpp/ClientManager.h>
39 #include <dcpp/version.h>
40 #include "version.hh"
41 #include "downloadqueue.hh"
42 #include "favoritehubs.hh"
43 #include "favoriteusers.hh"
44 #include "finishedtransfers.hh"
45 #include "func.hh"
46 #include "hub.hh"
47 #include "privatemessage.hh"
48 #include "publichubs.hh"
49 #include "search.hh"
50 #include "settingsmanager.hh"
51 #include "sharebrowser.hh"
52 #include "UserCommandMenu.hh"
53 #include "wulformanager.hh"
54 #include "WulforUtil.hh"
55 
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <fcntl.h>
59 
60 using namespace std;
61 using namespace dcpp;
62 
MainWindow()63 MainWindow::MainWindow():
64 	Entry(Entry::MAIN_WINDOW, "mainwindow.glade"),
65 	transfers(NULL),
66 	lastUpdate(0),
67 	lastUp(0),
68 	lastDown(0),
69 	minimized(FALSE)
70 {
71 	window = GTK_WINDOW(getWidget("mainWindow"));
72 	gtk_window_set_role(window, getID().c_str());
73 
74 	// Configure the dialogs
75 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("exitDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
76 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("connectDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
77 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("flistDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
78 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("ucLineDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
79 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("exitDialog")), window);
80 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("connectDialog")), window);
81 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("flistDialog")), window);
82 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("ucLineDialog")), window);
83 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("magnetDialog")), window);
84 
85 	gtk_about_dialog_set_email_hook((GtkAboutDialogActivateLinkFunc)onAboutDialogActivateLink_gui, (gpointer)this, NULL);
86 	gtk_about_dialog_set_url_hook((GtkAboutDialogActivateLinkFunc)onAboutDialogActivateLink_gui, (gpointer)this, NULL);
87 	const char *aboutCommentsFormat = gtk_about_dialog_get_comments(GTK_ABOUT_DIALOG(getWidget("aboutDialog")));
88 	int len = strlen(aboutCommentsFormat) + strlen(LINUXDCPP_VERSION_STRING) + strlen(VERSIONSTRING) + 1;
89 	char* commentsStr = new char[len];
90 	g_snprintf(commentsStr, len, aboutCommentsFormat, LINUXDCPP_VERSION_STRING, VERSIONSTRING);
91 	gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(getWidget("aboutDialog")), commentsStr);
92 	delete[] commentsStr;
93 
94 	// This has to be set in code in order to activate the link
95 	gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(getWidget("aboutDialog")), "https://launchpad.net/linuxdcpp");
96 	gtk_window_set_transient_for(GTK_WINDOW(getWidget("aboutDialog")), window);
97 
98 	// Set all windows to the default icon
99 	gtk_window_set_default_icon_name(g_get_prgname());
100 
101 	// All notebooks created in glade need one page.
102 	// In our case, this is just a placeholder, so we remove it.
103 	gtk_notebook_remove_page(GTK_NOTEBOOK(getWidget("book")), -1);
104 	g_object_set_data(G_OBJECT(getWidget("book")), "page-rotation-list", NULL);
105 	gtk_widget_set_sensitive(getWidget("closeMenuItem"), FALSE);
106 
107 	// Connect the signals to their callback functions.
108 	g_signal_connect(window, "delete-event", G_CALLBACK(onCloseWindow_gui), (gpointer)this);
109 	g_signal_connect(window, "window-state-event", G_CALLBACK(onWindowState_gui), (gpointer)this);
110 	g_signal_connect(window, "focus-in-event", G_CALLBACK(onFocusIn_gui), (gpointer)this);
111 	g_signal_connect(window, "key-press-event", G_CALLBACK(onKeyPressed_gui), (gpointer)this);
112 	g_signal_connect(getWidget("book"), "switch-page", G_CALLBACK(onPageSwitched_gui), (gpointer)this);
113 	g_signal_connect_after(getWidget("pane"), "realize", G_CALLBACK(onPaneRealized_gui), (gpointer)this);
114 	g_signal_connect(getWidget("favHubs"), "clicked", G_CALLBACK(onFavoriteHubsClicked_gui), (gpointer)this);
115 	g_signal_connect(getWidget("favUsers"), "clicked", G_CALLBACK(onFavoriteUsersClicked_gui), (gpointer)this);
116 	g_signal_connect(getWidget("publicHubs"), "clicked", G_CALLBACK(onPublicHubsClicked_gui), (gpointer)this);
117 	g_signal_connect(getWidget("settings"), "clicked", G_CALLBACK(onPreferencesClicked_gui), (gpointer)this);
118 	g_signal_connect(getWidget("hash"), "clicked", G_CALLBACK(onHashClicked_gui), (gpointer)this);
119 	g_signal_connect(getWidget("search"), "clicked", G_CALLBACK(onSearchClicked_gui), (gpointer)this);
120 	g_signal_connect(getWidget("queue"), "clicked", G_CALLBACK(onDownloadQueueClicked_gui), (gpointer)this);
121 	g_signal_connect(getWidget("finishedDownloads"), "clicked", G_CALLBACK(onFinishedDownloadsClicked_gui), (gpointer)this);
122 	g_signal_connect(getWidget("finishedUploads"), "clicked", G_CALLBACK(onFinishedUploadsClicked_gui), (gpointer)this);
123 	g_signal_connect(getWidget("openFileListMenuItem"), "activate", G_CALLBACK(onOpenFileListClicked_gui), (gpointer)this);
124 	g_signal_connect(getWidget("openOwnListMenuItem"), "activate", G_CALLBACK(onOpenOwnListClicked_gui), (gpointer)this);
125 	g_signal_connect(getWidget("refreshFileListMenuItem"), "activate", G_CALLBACK(onRefreshFileListClicked_gui), (gpointer)this);
126 	g_signal_connect(getWidget("quickConnectMenuItem"), "activate", G_CALLBACK(onConnectClicked_gui), (gpointer)this);
127 	g_signal_connect(getWidget("reconnectMenuItem"), "activate", G_CALLBACK(onReconnectClicked_gui), (gpointer)this);
128 	g_signal_connect(getWidget("settingsMenuItem"), "activate", G_CALLBACK(onPreferencesClicked_gui), (gpointer)this);
129 	g_signal_connect(getWidget("closeMenuItem"), "activate", G_CALLBACK(onCloseClicked_gui), (gpointer)this);
130 	g_signal_connect(getWidget("exitMenuItem"), "activate", G_CALLBACK(onQuitClicked_gui), (gpointer)this);
131 	g_signal_connect(getWidget("favoriteHubsMenuItem"), "activate", G_CALLBACK(onFavoriteHubsClicked_gui), (gpointer)this);
132 	g_signal_connect(getWidget("favoriteUsersMenuItem"), "activate", G_CALLBACK(onFavoriteUsersClicked_gui), (gpointer)this);
133 	g_signal_connect(getWidget("publicHubsMenuItem"), "activate", G_CALLBACK(onPublicHubsClicked_gui), (gpointer)this);
134 	g_signal_connect(getWidget("indexingProgressMenuItem"), "activate", G_CALLBACK(onHashClicked_gui), (gpointer)this);
135 	g_signal_connect(getWidget("searchMenuItem"), "activate", G_CALLBACK(onSearchClicked_gui), (gpointer)this);
136 	g_signal_connect(getWidget("downloadQueueMenuItem"), "activate", G_CALLBACK(onDownloadQueueClicked_gui), (gpointer)this);
137 	g_signal_connect(getWidget("finishedDownloadsMenuItem"), "activate", G_CALLBACK(onFinishedDownloadsClicked_gui), (gpointer)this);
138 	g_signal_connect(getWidget("finishedUploadsMenuItem"), "activate", G_CALLBACK(onFinishedUploadsClicked_gui), (gpointer)this);
139 	g_signal_connect(getWidget("previousTabMenuItem"), "activate", G_CALLBACK(onPreviousTabClicked_gui), (gpointer)this);
140 	g_signal_connect(getWidget("nextTabMenuItem"), "activate", G_CALLBACK(onNextTabClicked_gui), (gpointer)this);
141 	g_signal_connect(getWidget("helpOnlineMenuItem"), "activate", G_CALLBACK(onHelpOnlineItemActivated_gui), (gpointer)this);
142 	g_signal_connect(getWidget("reportProblemMenuItem"), "activate", G_CALLBACK(onReportProblemItemActivated_gui), (gpointer)this);
143 	g_signal_connect(getWidget("translateApplicationMenuItem"), "activate", G_CALLBACK(onTranslateApplicationItemActivated_gui), (gpointer)this);
144 	g_signal_connect(getWidget("aboutMenuItem"), "activate", G_CALLBACK(onAboutClicked_gui), (gpointer)this);
145 
146 	// Load window state and position from settings manager
147 	gint posX = WGETI("main-window-pos-x");
148 	gint posY = WGETI("main-window-pos-y");
149 	gint sizeX = WGETI("main-window-size-x");
150 	gint sizeY = WGETI("main-window-size-y");
151 
152 	gtk_window_move(window, posX, posY);
153 	gtk_window_resize(window, sizeX, sizeY);
154 	if (WGETI("main-window-maximized"))
155 		gtk_window_maximize(window);
156 
157 	GtkWidget *dummy;
158 	GtkRequisition req;
159 	dummy = gtk_statusbar_new();
160 	gtk_widget_size_request(dummy, &req);
161 	gtk_widget_destroy(dummy);
162 	emptyStatusWidth = req.width;
163 
164 	// TRANSLATORS: Shown in status bar on startup.
165 	setMainStatus_gui(F_("Welcome to %1%", % g_get_application_name()));
166 
167 	loadIcons_gui();
168 	showTransfersPane_gui();
169 
170 	// Set up our IPC
171 	createIPCPipe();
172 
173 	// Putting this after all the resizing and moving makes the window appear
174 	// in the correct position instantly, looking slightly more cool
175 	// (seems we have rather poor standards for cool?)
176 	gtk_widget_show_all(GTK_WIDGET(window));
177 
178 	setTabPosition_gui(WGETI("tab-position"));
179 	setToolbarStyle_gui(WGETI("toolbar-style"));
180 
181 	createStatusIcon_gui();
182 }
183 
~MainWindow()184 MainWindow::~MainWindow()
185 {
186 	if (IPC != NULL) {
187 		int fd = g_io_channel_unix_get_fd(IPC);
188 		g_io_channel_shutdown(IPC, FALSE, NULL);
189 		g_io_channel_unref(IPC);
190 		close(fd);
191 		unlink(WulforUtil::getPipePath().c_str());
192 	}
193 
194 	QueueManager::getInstance()->removeListener(this);
195 	TimerManager::getInstance()->removeListener(this);
196 	LogManager::getInstance()->removeListener(this);
197 
198 	GList *list = (GList *)g_object_get_data(G_OBJECT(getWidget("book")), "page-rotation-list");
199 	g_list_free(list);
200 
201 	// Save window state and position
202 	gint posX, posY, sizeX, sizeY, transferPanePosition;
203 	bool maximized = TRUE;
204 	GdkWindowState gdkState;
205 
206 	gtk_window_get_position(window, &posX, &posY);
207 	gtk_window_get_size(window, &sizeX, &sizeY);
208 	gdkState = gdk_window_get_state(GTK_WIDGET(window)->window);
209 	transferPanePosition = sizeY - gtk_paned_get_position(GTK_PANED(getWidget("pane")));
210 
211 	if (!(gdkState & GDK_WINDOW_STATE_MAXIMIZED))
212 	{
213 		maximized = FALSE;
214 		// The get pos/size functions return junk when window is maximized
215 		WSET("main-window-pos-x", posX);
216 		WSET("main-window-pos-y", posY);
217 		WSET("main-window-size-x", sizeX);
218 		WSET("main-window-size-y", sizeY);
219 	}
220 
221 	WSET("main-window-maximized", maximized);
222 	if (transferPanePosition > 10)
223 		WSET("transfer-pane-position", transferPanePosition);
224 
225 	gtk_widget_destroy(GTK_WIDGET(window));
226 	g_object_unref(statusIcon);
227 
228 	#ifdef HAVE_LIBNOTIFY
229 	notify_uninit();
230 	#endif
231 }
232 
getContainer()233 GtkWidget *MainWindow::getContainer()
234 {
235 	return getWidget("mainWindow");
236 }
237 
show()238 void MainWindow::show()
239 {
240 	QueueManager::getInstance()->addListener(this);
241 	TimerManager::getInstance()->addListener(this);
242 	LogManager::getInstance()->addListener(this);
243 
244 	typedef Func0<MainWindow> F0;
245 	F0 *f0 = new F0(this, &MainWindow::startSocket_client);
246 	WulforManager::get()->dispatchClientFunc(f0);
247 
248 	f0 = new F0(this, &MainWindow::autoConnect_client);
249 	WulforManager::get()->dispatchClientFunc(f0);
250 
251 	autoOpen_gui();
252 
253 	if (WGETI("show-preferences-on-startup"))
254 	{
255 		onPreferencesClicked_gui(NULL, (gpointer)this);
256 		WSET("show-preferences-on-startup", 0);
257 	}
258 }
259 
setTitle(const string & text)260 void MainWindow::setTitle(const string& text)
261 {
262 	string title;
263 
264 	if (!text.empty())
265 		title = text + " - " + g_get_application_name();
266 	else
267 		title = g_get_application_name();
268 
269 	gtk_window_set_title(window, title.c_str());
270 }
271 
isActive_gui()272 bool MainWindow::isActive_gui()
273 {
274 	return gtk_window_is_active(window);
275 }
276 
setUrgent_gui()277 void MainWindow::setUrgent_gui()
278 {
279 	gtk_window_set_urgency_hint(window, true);
280 }
281 
282 /*
283  * Create and show Transfers pane
284  */
showTransfersPane_gui()285 void MainWindow::showTransfersPane_gui()
286 {
287 	dcassert(transfers == NULL);
288 
289 	transfers = new Transfers();
290 	gtk_paned_pack2(GTK_PANED(getWidget("pane")), transfers->getContainer(), TRUE, TRUE);
291 	addChild(transfers);
292 	transfers->show();
293 }
294 
295 /*
296  * Load the custom icons or the stock icons as per the setting
297  */
loadIcons_gui()298 void MainWindow::loadIcons_gui()
299 {
300 	WulforUtil::registerIcons();
301 
302 	// Reset the stock IDs manually to force the icon to refresh
303 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("favHubs")), "linuxdcpp-favorite-hubs");
304 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("favUsers")), "linuxdcpp-favorite-users");
305 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("publicHubs")), "linuxdcpp-public-hubs");
306 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("settings")), "linuxdcpp-preferences");
307 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("hash")), "linuxdcpp-hash");
308 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("search")), "linuxdcpp-search");
309 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("queue")), "linuxdcpp-queue");
310 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("finishedDownloads")), "linuxdcpp-finished-downloads");
311 	gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(getWidget("finishedUploads")), "linuxdcpp-finished-uploads");
312 }
313 
autoOpen_gui()314 void MainWindow::autoOpen_gui()
315 {
316 	if (BOOLSETTING(OPEN_PUBLIC))
317 		showPublicHubs_gui();
318 	if (BOOLSETTING(OPEN_QUEUE))
319 		showDownloadQueue_gui();
320 	if (BOOLSETTING(OPEN_FAVORITE_HUBS))
321 		showFavoriteHubs_gui();
322 	if (BOOLSETTING(OPEN_FAVORITE_USERS))
323 		showFavoriteUsers_gui();
324 	if (BOOLSETTING(OPEN_FINISHED_DOWNLOADS))
325 		showFinishedDownloads_gui();
326 	if (BOOLSETTING(OPEN_FINISHED_UPLOADS))
327 		showFinishedUploads_gui();
328 }
329 
addBookEntry_gui(BookEntry * entry)330 void MainWindow::addBookEntry_gui(BookEntry *entry)
331 {
332 	addChild(entry);
333 
334 	GtkWidget *page = entry->getContainer();
335 	GtkWidget *label = entry->getLabelBox();
336 	GtkWidget *closeButton = entry->getCloseButton();
337 	GtkWidget *tabMenuItem = entry->getTabMenuItem();
338 
339 	addTabMenuItem_gui(tabMenuItem, page);
340 
341 	gtk_notebook_append_page(GTK_NOTEBOOK(getWidget("book")), page, label);
342 
343 	g_signal_connect(label, "button-release-event", G_CALLBACK(onButtonReleasePage_gui), (gpointer)entry);
344 	g_signal_connect(closeButton, "button-release-event", G_CALLBACK(onButtonReleasePage_gui), (gpointer)entry);
345 	g_signal_connect(closeButton, "clicked", G_CALLBACK(onCloseBookEntry_gui), (gpointer)entry);
346 
347 	gtk_widget_set_sensitive(getWidget("closeMenuItem"), TRUE);
348 
349 	gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(getWidget("book")), page, TRUE);
350 
351 	entry->show();
352 }
353 
currentPage_gui()354 GtkWidget *MainWindow::currentPage_gui()
355 {
356 	int pageNum = gtk_notebook_get_current_page(GTK_NOTEBOOK(getWidget("book")));
357 
358 	if (pageNum == -1)
359 		return NULL;
360 	else
361 		return gtk_notebook_get_nth_page(GTK_NOTEBOOK(getWidget("book")), pageNum);
362 }
363 
raisePage_gui(GtkWidget * page)364 void MainWindow::raisePage_gui(GtkWidget *page)
365 {
366 	int num = gtk_notebook_page_num(GTK_NOTEBOOK(getWidget("book")), page);
367 	int currentNum = gtk_notebook_get_current_page(GTK_NOTEBOOK(getWidget("book")));
368 
369 	if (num != -1 && num != currentNum)
370 		gtk_notebook_set_current_page(GTK_NOTEBOOK(getWidget("book")), num);
371 }
372 
removeBookEntry_gui(BookEntry * entry)373 void MainWindow::removeBookEntry_gui(BookEntry *entry)
374 {
375 	GtkNotebook *book = GTK_NOTEBOOK(getWidget("book"));
376 	GtkWidget *page = entry->getContainer();
377 	GtkWidget* menuItem = entry->getTabMenuItem();
378 	int num = gtk_notebook_page_num(book, page);
379 	removeChild(entry);
380 
381 	if (num != -1)
382 	{
383 		GList *list = (GList *)g_object_get_data(G_OBJECT(book), "page-rotation-list");
384 		list = g_list_remove(list, (gpointer)page);
385 		g_object_set_data(G_OBJECT(book), "page-rotation-list", (gpointer)list);
386 
387 		// if removing the current page, switch to the previous page in the rotation list
388 		if (num == gtk_notebook_get_current_page(book))
389 		{
390 			GList *prev = g_list_first(list);
391 			if (prev != NULL)
392 			{
393 				gint childNum = gtk_notebook_page_num(book, GTK_WIDGET(prev->data));
394 				gtk_notebook_set_current_page(book, childNum);
395 			}
396 		}
397 		gtk_notebook_remove_page(book, num);
398 
399 		removeTabMenuItem_gui(menuItem);
400 
401 		if (gtk_notebook_get_n_pages(book) == 0)
402 		{
403 			gtk_widget_set_sensitive(getWidget("closeMenuItem"), FALSE);
404 			setTitle(""); // Reset window title to default
405 		}
406 	}
407 }
408 
previousTab_gui()409 void MainWindow::previousTab_gui()
410 {
411 	GtkNotebook *book = GTK_NOTEBOOK(getWidget("book"));
412 
413 	if (gtk_notebook_get_current_page(book) == 0)
414 		gtk_notebook_set_current_page(book, -1);
415 	else
416 		gtk_notebook_prev_page(book);
417 }
418 
nextTab_gui()419 void MainWindow::nextTab_gui()
420 {
421 	GtkNotebook *book = GTK_NOTEBOOK(getWidget("book"));
422 
423 	if (gtk_notebook_get_n_pages(book) - 1 == gtk_notebook_get_current_page(book))
424 		gtk_notebook_set_current_page(book, 0);
425 	else
426 		gtk_notebook_next_page(book);
427 }
428 
addTabMenuItem_gui(GtkWidget * menuItem,GtkWidget * page)429 void MainWindow::addTabMenuItem_gui(GtkWidget* menuItem, GtkWidget* page)
430 {
431 	g_signal_connect(menuItem, "activate", G_CALLBACK(onRaisePage_gui), (gpointer)page);
432 	gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("tabsMenu")), menuItem);
433 	gtk_widget_show_all(getWidget("tabsMenu"));
434 
435 	gtk_widget_set_sensitive(getWidget("previousTabMenuItem"), TRUE);
436 	gtk_widget_set_sensitive(getWidget("nextTabMenuItem"), TRUE);
437 	gtk_widget_set_sensitive(getWidget("tabMenuSeparator"), TRUE);
438 }
439 
removeTabMenuItem_gui(GtkWidget * menuItem)440 void MainWindow::removeTabMenuItem_gui(GtkWidget *menuItem)
441 {
442 	GtkNotebook *book = GTK_NOTEBOOK(getWidget("book"));
443 
444 	gtk_container_remove(GTK_CONTAINER(getWidget("tabsMenu")), menuItem);
445 
446 	if (gtk_notebook_get_n_pages(book) == 0)
447 	{
448 		gtk_widget_set_sensitive(getWidget("previousTabMenuItem"), FALSE);
449 		gtk_widget_set_sensitive(getWidget("nextTabMenuItem"), FALSE);
450 		gtk_widget_set_sensitive(getWidget("tabMenuSeparator"), FALSE);
451 	}
452 }
453 
454 /*
455  * Create status icon.
456  */
createStatusIcon_gui()457 void MainWindow::createStatusIcon_gui()
458 {
459 	statusIcon = gtk_status_icon_new_from_icon_name(g_get_prgname());
460 
461 	g_signal_connect(getWidget("statusIconQuitItem"), "activate", G_CALLBACK(onQuitClicked_gui), (gpointer)this);
462 	g_signal_connect(getWidget("statusIconShowInterfaceItem"), "toggled", G_CALLBACK(onShowInterfaceToggled_gui), (gpointer)this);
463 	g_signal_connect(statusIcon, "activate", G_CALLBACK(onStatusIconActivated_gui), (gpointer)this);
464 	g_signal_connect(statusIcon, "popup-menu", G_CALLBACK(onStatusIconPopupMenu_gui), (gpointer)this);
465 
466 	if (BOOLSETTING(ALWAYS_TRAY))
467 		gtk_status_icon_set_visible(statusIcon, TRUE);
468 	else
469 		gtk_status_icon_set_visible(statusIcon, FALSE);
470 }
471 
updateStatusIconTooltip_gui(string download,string upload)472 void MainWindow::updateStatusIconTooltip_gui(string download, string upload)
473 {
474 	// TRANSLATORS: Notify icon (systray) tooltip
475 	string tooltip = F_("%1%\nDownload: %2%\nUpload: %3%", % g_get_application_name() % download % upload);
476 	gtk_status_icon_set_tooltip(statusIcon, tooltip.c_str());
477 }
478 
showNotification_gui(string title,string body)479 void MainWindow::showNotification_gui(string title, string body)
480 {
481 #ifdef HAVE_LIBNOTIFY
482 	if (!notify_is_initted())
483 		notify_init(g_get_application_name());
484 
485 	NotifyNotification *notification;
486 	#ifdef HAVE_LIBNOTIFY_0_7
487 	notification = notify_notification_new(title.c_str(), body.c_str(), g_get_prgname());
488 	#else
489 	notification = notify_notification_new_with_status_icon(title.c_str(), body.c_str(), g_get_prgname(), statusIcon);
490 	#endif
491 	notify_notification_show(notification, NULL);
492 
493 	g_object_unref(notification);
494 #endif
495 }
496 
setMainStatus_gui(string text,time_t t)497 void MainWindow::setMainStatus_gui(string text, time_t t)
498 {
499 	if (!text.empty())
500 	{
501 		text = "[" + Util::getShortTimeString(t) + "] " + text;
502 		gtk_label_set_text(GTK_LABEL(getWidget("labelStatus")), text.c_str());
503 	}
504 }
505 
setStats_gui(string hubs,string downloadSpeed,string downloaded,string uploadSpeed,string uploaded)506 void MainWindow::setStats_gui(string hubs, string downloadSpeed,
507 	string downloaded, string uploadSpeed, string uploaded)
508 {
509 	gtk_label_set_text(GTK_LABEL(getWidget("labelHubs")), hubs.c_str());
510 	gtk_label_set_text(GTK_LABEL(getWidget("labelDownloadSpeed")), downloadSpeed.c_str());
511 	gtk_label_set_text(GTK_LABEL(getWidget("labelDownloaded")), downloaded.c_str());
512 	gtk_label_set_text(GTK_LABEL(getWidget("labelUploadSpeed")), uploadSpeed.c_str());
513 	gtk_label_set_text(GTK_LABEL(getWidget("labelUploaded")), uploaded.c_str());
514 }
515 
findBookEntry(const EntryType type,const string & id)516 BookEntry* MainWindow::findBookEntry(const EntryType type, const string &id)
517 {
518 	Entry *entry = getChild(type, id);
519 	return dynamic_cast<BookEntry*>(entry);
520 }
521 
showDownloadQueue_gui()522 void MainWindow::showDownloadQueue_gui()
523 {
524 	BookEntry *entry = findBookEntry(Entry::DOWNLOAD_QUEUE);
525 
526 	if (entry == NULL)
527 	{
528 		entry = new DownloadQueue();
529 		addBookEntry_gui(entry);
530 	}
531 
532 	raisePage_gui(entry->getContainer());
533 }
534 
showFavoriteHubs_gui()535 void MainWindow::showFavoriteHubs_gui()
536 {
537 	BookEntry *entry = findBookEntry(Entry::FAVORITE_HUBS);
538 
539 	if (entry == NULL)
540 	{
541 		entry = new FavoriteHubs();
542 		addBookEntry_gui(entry);
543 	}
544 
545 	raisePage_gui(entry->getContainer());
546 }
547 
showFavoriteUsers_gui()548 void MainWindow::showFavoriteUsers_gui()
549 {
550 	BookEntry *entry = findBookEntry(Entry::FAVORITE_USERS);
551 
552 	if (entry == NULL)
553 	{
554 		entry = new FavoriteUsers();
555 		addBookEntry_gui(entry);
556 	}
557 
558 	raisePage_gui(entry->getContainer());
559 }
560 
showFinishedDownloads_gui()561 void MainWindow::showFinishedDownloads_gui()
562 {
563 	BookEntry *entry = findBookEntry(Entry::FINISHED_DOWNLOADS);
564 
565 	if (entry == NULL)
566 	{
567 		entry = FinishedTransfers::createFinishedDownloads();
568 		addBookEntry_gui(entry);
569 	}
570 
571 	raisePage_gui(entry->getContainer());
572 }
573 
showFinishedUploads_gui()574 void MainWindow::showFinishedUploads_gui()
575 {
576 	BookEntry *entry = findBookEntry(Entry::FINISHED_UPLOADS);
577 
578 	if (entry == NULL)
579 	{
580 		entry = FinishedTransfers::createFinishedUploads();
581 		addBookEntry_gui(entry);
582 	}
583 
584 	raisePage_gui(entry->getContainer());
585 }
586 
showHub_gui(string address,string encoding)587 void MainWindow::showHub_gui(string address, string encoding)
588 {
589 	BookEntry *entry = findBookEntry(Entry::HUB, address);
590 
591 	if (entry == NULL)
592 	{
593 		entry = new Hub(address, encoding);
594 		addBookEntry_gui(entry);
595 	}
596 
597 	raisePage_gui(entry->getContainer());
598 }
599 
addPrivateMessage_gui(string cid,string hubUrl,string message,bool useSetting)600 void MainWindow::addPrivateMessage_gui(string cid, string hubUrl, string message, bool useSetting)
601 {
602 	BookEntry *entry = findBookEntry(Entry::PRIVATE_MESSAGE, cid);
603 	bool raise = TRUE;
604 
605 	// If PM is initiated by another user, use setting except if tab is already open.
606 	if (useSetting)
607 		raise = (entry == NULL) ? !BOOLSETTING(POPUNDER_PM) : FALSE;
608 
609 	if (entry == NULL)
610 	{
611 		entry = new PrivateMessage(cid, hubUrl);
612 		addBookEntry_gui(entry);
613 	}
614 
615 	if (!message.empty())
616 	{
617 		dynamic_cast<PrivateMessage*>(entry)->addMessage_gui(message);
618 
619 		if (WGETI("notify-pm") && !isActive_gui())
620 			showNotification_gui(WulforUtil::getNicks(cid),  message);
621 	}
622 
623 	if (raise)
624 		raisePage_gui(entry->getContainer());
625 }
626 
showPublicHubs_gui()627 void MainWindow::showPublicHubs_gui()
628 {
629 	BookEntry *entry = findBookEntry(Entry::PUBLIC_HUBS);
630 
631 	if (entry == NULL)
632 	{
633 		entry = new PublicHubs();
634 		addBookEntry_gui(entry);
635 	}
636 
637 	raisePage_gui(entry->getContainer());
638 }
639 
showShareBrowser_gui(UserPtr user,string filename,string dir,bool useSetting)640 void MainWindow::showShareBrowser_gui(UserPtr user, string filename, string dir, bool useSetting)
641 {
642 	bool raise = useSetting ? !BOOLSETTING(POPUNDER_FILELIST) : TRUE;
643 	BookEntry *entry = findBookEntry(Entry::SHARE_BROWSER, user->getCID().toBase32());
644 
645 	if (entry == NULL)
646 	{
647 		entry = new ShareBrowser(user, filename, dir);
648 		addBookEntry_gui(entry);
649 	}
650 
651 	if (raise)
652 		raisePage_gui(entry->getContainer());
653 }
654 
addSearch_gui()655 Search *MainWindow::addSearch_gui()
656 {
657 	Search *entry = new Search();
658 	addBookEntry_gui(entry);
659 	raisePage_gui(entry->getContainer());
660 	return entry;
661 }
662 
addMagnetSearch_gui(const string & magnet)663 Search *MainWindow::addMagnetSearch_gui(const string& magnet)
664 {
665 	string name;
666 	int64_t size;
667 	string tth;
668 
669 	if (WulforUtil::splitMagnet(magnet, name, size, tth))
670 	{
671 		Search *s = addSearch_gui();
672 		s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
673 		return s;
674 	}
675 	return NULL;
676 }
677 
setTabPosition_gui(int position)678 void MainWindow::setTabPosition_gui(int position)
679 {
680 	GtkPositionType tabPosition;
681 
682 	switch (position)
683 	{
684 		case 0:
685 			tabPosition = GTK_POS_TOP;
686 			break;
687 		case 1:
688 			tabPosition = GTK_POS_LEFT;
689 			break;
690 		case 2:
691 			tabPosition = GTK_POS_RIGHT;
692 			break;
693 		case 3:
694 			tabPosition = GTK_POS_BOTTOM;
695 			break;
696 		default:
697 			tabPosition = GTK_POS_TOP;
698 	}
699 
700 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(getWidget("book")), tabPosition);
701 }
702 
setToolbarStyle_gui(int style)703 void MainWindow::setToolbarStyle_gui(int style)
704 {
705 	GtkToolbarStyle toolbarStyle;
706 
707 	switch (style)
708 	{
709 		case 0:
710 			toolbarStyle = GTK_TOOLBAR_ICONS;
711 			break;
712 		case 1:
713 			toolbarStyle = GTK_TOOLBAR_TEXT;
714 			break;
715 		case 2:
716 			toolbarStyle = GTK_TOOLBAR_BOTH;
717 			break;
718 		case 3:
719 			toolbarStyle = GTK_TOOLBAR_BOTH_HORIZ;
720 			break;
721 		case 4:
722 			gtk_widget_hide(getWidget("toolbar1"));
723 			break;
724 		case 5:
725 			return;
726 		default:
727 			toolbarStyle = GTK_TOOLBAR_BOTH;
728 	}
729 
730 	if (style != 4)
731 	{
732 		gtk_widget_show(getWidget("toolbar1"));
733 		gtk_toolbar_set_style(GTK_TOOLBAR(getWidget("toolbar1")), toolbarStyle);
734 	}
735 }
736 
getUserCommandLines_gui(const string & command,StringMap & ucParams)737 bool MainWindow::getUserCommandLines_gui(const string &command, StringMap &ucParams)
738 {
739 	string name;
740 	string line;
741 	StringMap done;
742 	string::size_type i = 0;
743 	string::size_type j = 0;
744 
745 	while ((i = command.find("%[line:", i)) != string::npos)
746 	{
747 		i += 7;
748 		j = command.find(']', i);
749 		if (j == string::npos)
750 			break;
751 
752 		name = command.substr(i, j - i);
753 		if (done.find(name) == done.end())
754 		{
755 			line.clear();
756 
757 			// TRANSLATORS: Appears in a dialog prompt when a user command requires arguments.
758 			string label = F_("Enter a value for '%1%'", % name);
759 
760 			gtk_label_set_text(GTK_LABEL(getWidget("ucLabel")), label.c_str());
761 			gtk_entry_set_text(GTK_ENTRY(getWidget("ucLineEntry")), "");
762 			gtk_widget_grab_focus(getWidget("ucLineEntry"));
763 
764 			gint response = gtk_dialog_run(GTK_DIALOG(getWidget("ucLineDialog")));
765 			gtk_widget_hide(getWidget("ucLineDialog"));
766 
767 			if (response == GTK_RESPONSE_OK)
768 				line = gtk_entry_get_text(GTK_ENTRY(getWidget("ucLineEntry")));
769 
770 			if (!line.empty())
771 			{
772 				ucParams["line:" + name] = line;
773 				done[name] = line;
774 			}
775 			else
776 				return false;
777 		}
778 		i = j + 1;
779 	}
780 
781 	return true;
782 }
783 
openMagnetDialog_gui(const string & magnet)784 void MainWindow::openMagnetDialog_gui(const string &magnet)
785 {
786 	string name;
787 	int64_t size;
788 	string tth;
789 
790 	WulforUtil::splitMagnet(magnet, name, size, tth);
791 
792 	gtk_entry_set_text(GTK_ENTRY(getWidget("magnetEntry")), magnet.c_str());
793 	gtk_entry_set_text(GTK_ENTRY(getWidget("magnetNameEntry")), name.c_str());
794 	gtk_entry_set_text(GTK_ENTRY(getWidget("magnetSizeEntry")), Util::formatBytes(size).c_str());
795 	gtk_entry_set_text(GTK_ENTRY(getWidget("exactSizeEntry")), Util::formatExactSize(size).c_str());
796 	gtk_entry_set_text(GTK_ENTRY(getWidget("tthEntry")), tth.c_str());
797 
798 	gtk_dialog_run(GTK_DIALOG(getWidget("magnetDialog")));
799 	gtk_widget_hide(getWidget("magnetDialog"));
800 }
801 
showMessageDialog_gui(const string primaryText,const string secondaryText)802 void MainWindow::showMessageDialog_gui(const string primaryText, const string secondaryText)
803 {
804 	if (primaryText.empty())
805 		return;
806 
807 	GtkWidget* dialog = gtk_message_dialog_new(window, GTK_DIALOG_DESTROY_WITH_PARENT,
808 		GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", primaryText.c_str());
809 
810 	if (!secondaryText.empty())
811 		gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondaryText.c_str());
812 
813 	gtk_dialog_run(GTK_DIALOG(dialog));
814 	gtk_widget_destroy(dialog);
815 }
816 
onWindowState_gui(GtkWidget * widget,GdkEventWindowState * event,gpointer data)817 gboolean MainWindow::onWindowState_gui(GtkWidget *widget, GdkEventWindowState *event, gpointer data)
818 {
819 	MainWindow *mw = (MainWindow *)data;
820 
821 	if (!mw->minimized && event->new_window_state & (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_WITHDRAWN))
822 	{
823 		mw->minimized = TRUE;
824 		if (BOOLSETTING(SettingsManager::AUTO_AWAY) && !Util::getAway())
825 			Util::setAway(TRUE);
826 	}
827 	else if (mw->minimized && (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED ||
828 		event->new_window_state == 0))
829 	{
830 		mw->minimized = FALSE;
831 		if (BOOLSETTING(SettingsManager::AUTO_AWAY) && !Util::getManualAway())
832 			Util::setAway(FALSE);
833 	}
834 
835 	return TRUE;
836 }
837 
onFocusIn_gui(GtkWidget * widget,GdkEventFocus * event,gpointer data)838 gboolean MainWindow::onFocusIn_gui(GtkWidget *widget, GdkEventFocus *event, gpointer data)
839 {
840 	MainWindow *mw = (MainWindow *)data;
841 	GtkWidget *child = mw->currentPage_gui();
842 
843 	if (child != NULL)
844 	{
845 		BookEntry *entry = (BookEntry *)g_object_get_data(G_OBJECT(child), "entry");
846 		entry->setActive_gui();
847 	}
848 
849 	gtk_window_set_urgency_hint(mw->window, FALSE);
850 	return FALSE;
851 }
852 
onCloseWindow_gui(GtkWidget * widget,GdkEvent * event,gpointer data)853 gboolean MainWindow::onCloseWindow_gui(GtkWidget *widget, GdkEvent *event, gpointer data)
854 {
855 	MainWindow *mw = (MainWindow *)data;
856 
857 	if (!BOOLSETTING(CONFIRM_EXIT))
858 	{
859 		WulforManager::get()->deleteMainWindow();
860 		return FALSE;
861 	}
862 
863 	int response = gtk_dialog_run(GTK_DIALOG(mw->getWidget("exitDialog")));
864 	gtk_widget_hide(mw->getWidget("exitDialog"));
865 
866 	if (response == GTK_RESPONSE_OK)
867 	{
868 		WulforManager::get()->deleteMainWindow();
869 		return FALSE;
870 	}
871 
872 	return TRUE;
873 }
874 
onKeyPressed_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)875 gboolean MainWindow::onKeyPressed_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
876 {
877 	MainWindow *mw = (MainWindow *)data;
878 
879 	if (event->state & GDK_CONTROL_MASK)
880 	{
881 		if (event->state & GDK_SHIFT_MASK && event->keyval == GDK_ISO_Left_Tab)
882 		{
883 			mw->previousTab_gui();
884 			return TRUE;
885 		}
886 		else if (event->keyval == GDK_Tab)
887 		{
888 			mw->nextTab_gui();
889 			return TRUE;
890 		}
891 		else if (event->keyval == GDK_F4)
892 		{
893 			onCloseClicked_gui(widget, data);
894 			return TRUE;
895 		}
896 	}
897 
898 	return FALSE;
899 }
900 
onButtonReleasePage_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)901 gboolean MainWindow::onButtonReleasePage_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
902 {
903 	gint width, height;
904 	gdk_drawable_get_size(event->window, &width, &height);
905 
906 	// If middle mouse button was released when hovering over tab label
907 	if (event->button == 2 && event->x >= 0 && event->y >= 0
908 		&& event->x < width && event->y < height)
909 	{
910 		BookEntry *entry = (BookEntry *)data;
911 		WulforManager::get()->getMainWindow()->removeBookEntry_gui(entry);
912 		return TRUE;
913 	}
914 
915 	return FALSE;
916 }
917 
onRaisePage_gui(GtkMenuItem * item,gpointer data)918 void MainWindow::onRaisePage_gui(GtkMenuItem *item, gpointer data)
919 {
920 	WulforManager::get()->getMainWindow()->raisePage_gui((GtkWidget *)data);
921 }
922 
onPageSwitched_gui(GtkNotebook * notebook,GtkNotebookPage * page,guint num,gpointer data)923 void MainWindow::onPageSwitched_gui(GtkNotebook *notebook, GtkNotebookPage *page, guint num, gpointer data)
924 {
925 	MainWindow* mw = (MainWindow*)data;
926 	GtkWidget *child = gtk_notebook_get_nth_page(notebook, num);
927 	BookEntry *entry = (BookEntry *)g_object_get_data(G_OBJECT(child), "entry");
928 
929 	if (entry)
930 	{
931 		// Disable "activate" signal on the tab menu item since it can cause
932 		// onPageSwitched_gui to be called multiple times
933 		GtkWidget *item = entry->getTabMenuItem();
934 		g_signal_handlers_block_by_func(item, (gpointer)onRaisePage_gui, child);
935 
936 		entry->setActive_gui();
937 		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry->getTabMenuItem()), TRUE);
938 		mw->setTitle(entry->getLabelText()); // Update window title with selected tab label
939 
940 		g_signal_handlers_unblock_by_func(item, (gpointer)onRaisePage_gui, (gpointer)child);
941 	}
942 
943 	GList *list = (GList *)g_object_get_data(G_OBJECT(notebook), "page-rotation-list");
944 	list = g_list_remove(list, (gpointer)child);
945 	list = g_list_prepend(list, (gpointer)child);
946 	g_object_set_data(G_OBJECT(notebook), "page-rotation-list", (gpointer)list);
947 
948 	// Focus the tab so it will focus its children (e.g. a text entry box)
949 	gtk_widget_grab_focus(child);
950 }
951 
onPaneRealized_gui(GtkWidget * pane,gpointer data)952 void MainWindow::onPaneRealized_gui(GtkWidget *pane, gpointer data)
953 {
954 	MainWindow *mw = (MainWindow *)data;
955 	gint position = WGETI("transfer-pane-position");
956 
957 	if (position > 10)
958 	{
959 		// @todo: fix get window height when maximized
960 		gint height;
961 		gtk_window_get_size(mw->window, NULL, &height);
962 		gtk_paned_set_position(GTK_PANED(pane), height - position);
963 	}
964 }
965 
onConnectClicked_gui(GtkWidget * widget,gpointer data)966 void MainWindow::onConnectClicked_gui(GtkWidget *widget, gpointer data)
967 {
968 	MainWindow *mw = (MainWindow *)data;
969 
970 	gtk_editable_select_region(GTK_EDITABLE(mw->getWidget("connectEntry")), 0, -1);
971 	gtk_widget_grab_focus(mw->getWidget("connectEntry"));
972 	int response = gtk_dialog_run(GTK_DIALOG(mw->getWidget("connectDialog")));
973 	gtk_widget_hide(mw->getWidget("connectDialog"));
974 
975 	if (response == GTK_RESPONSE_OK)
976 	{
977 		string address = gtk_entry_get_text(GTK_ENTRY(mw->getWidget("connectEntry")));
978 		mw->showHub_gui(address);
979 	}
980 }
981 
onFavoriteHubsClicked_gui(GtkWidget * widget,gpointer data)982 void MainWindow::onFavoriteHubsClicked_gui(GtkWidget *widget, gpointer data)
983 {
984 	MainWindow *mw = (MainWindow *)data;
985 	mw->showFavoriteHubs_gui();
986 }
987 
onFavoriteUsersClicked_gui(GtkWidget * widget,gpointer data)988 void MainWindow::onFavoriteUsersClicked_gui(GtkWidget *widget, gpointer data)
989 {
990 	MainWindow *mw = (MainWindow *)data;
991 	mw->showFavoriteUsers_gui();
992 }
993 
onPublicHubsClicked_gui(GtkWidget * widget,gpointer data)994 void MainWindow::onPublicHubsClicked_gui(GtkWidget *widget, gpointer data)
995 {
996 	MainWindow *mw = (MainWindow *)data;
997 	mw->showPublicHubs_gui();
998 }
999 
onPreferencesClicked_gui(GtkWidget * widget,gpointer data)1000 void MainWindow::onPreferencesClicked_gui(GtkWidget *widget, gpointer data)
1001 {
1002 	MainWindow *mw = (MainWindow *)data;
1003 	typedef Func0<MainWindow> F0;
1004 
1005 	unsigned short tcpPort = (unsigned short)SETTING(TCP_PORT);
1006 	unsigned short udpPort = (unsigned short)SETTING(UDP_PORT);
1007 	int lastConn = SETTING(INCOMING_CONNECTIONS);
1008 	bool stockIcons = WGETI("use-system-icons");
1009 
1010 	gint response = WulforManager::get()->openSettingsDialog_gui();
1011 
1012 	if (response == GTK_RESPONSE_OK)
1013 	{
1014 		if (SETTING(INCOMING_CONNECTIONS) != lastConn || SETTING(TCP_PORT) != tcpPort || SETTING(UDP_PORT) != udpPort)
1015 		{
1016 			F0 *func = new F0(mw, &MainWindow::startSocket_client);
1017 			WulforManager::get()->dispatchClientFunc(func);
1018 		}
1019 
1020 		if (BOOLSETTING(ALWAYS_TRAY))
1021 			gtk_status_icon_set_visible(mw->statusIcon, TRUE);
1022 		else
1023 			gtk_status_icon_set_visible(mw->statusIcon, FALSE);
1024 
1025 		mw->setTabPosition_gui(WGETI("tab-position"));
1026 		mw->setToolbarStyle_gui(WGETI("toolbar-style"));
1027 
1028 		// Reload the icons only if the setting has changed
1029 		if (WGETI("use-system-icons") != stockIcons)
1030 			mw->loadIcons_gui();
1031 	}
1032 }
1033 
onHashClicked_gui(GtkWidget * widget,gpointer data)1034 void MainWindow::onHashClicked_gui(GtkWidget *widget, gpointer data)
1035 {
1036 	WulforManager::get()->openHashDialog_gui();
1037 }
1038 
onSearchClicked_gui(GtkWidget * widget,gpointer data)1039 void MainWindow::onSearchClicked_gui(GtkWidget *widget, gpointer data)
1040 {
1041 	MainWindow *mw = (MainWindow *)data;
1042 	mw->addSearch_gui();
1043 }
1044 
onDownloadQueueClicked_gui(GtkWidget * widget,gpointer data)1045 void MainWindow::onDownloadQueueClicked_gui(GtkWidget *widget, gpointer data)
1046 {
1047 	MainWindow *mw = (MainWindow *)data;
1048 	mw->showDownloadQueue_gui();
1049 }
1050 
onFinishedDownloadsClicked_gui(GtkWidget * widget,gpointer data)1051 void MainWindow::onFinishedDownloadsClicked_gui(GtkWidget *widget, gpointer data)
1052 {
1053 	MainWindow *mw = (MainWindow *)data;
1054 	mw->showFinishedDownloads_gui();
1055 }
1056 
onFinishedUploadsClicked_gui(GtkWidget * widget,gpointer data)1057 void MainWindow::onFinishedUploadsClicked_gui(GtkWidget *widget, gpointer data)
1058 {
1059 	MainWindow *mw = (MainWindow *)data;
1060 	mw->showFinishedUploads_gui();
1061 }
1062 
onQuitClicked_gui(GtkWidget * widget,gpointer data)1063 void MainWindow::onQuitClicked_gui(GtkWidget *widget, gpointer data)
1064 {
1065 	MainWindow *mw = (MainWindow *)data;
1066 	gboolean retVal; // Not interested in the value, though.
1067 	g_signal_emit_by_name(mw->window, "delete-event", NULL, &retVal);
1068 }
1069 
onOpenFileListClicked_gui(GtkWidget * widget,gpointer data)1070 void MainWindow::onOpenFileListClicked_gui(GtkWidget *widget, gpointer data)
1071 {
1072 	MainWindow *mw = (MainWindow *)data;
1073 
1074 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(mw->getWidget("flistDialog")), Text::fromUtf8(Util::getListPath()).c_str());
1075 
1076  	int ret = gtk_dialog_run(GTK_DIALOG(mw->getWidget("flistDialog")));
1077 	gtk_widget_hide(mw->getWidget("flistDialog"));
1078 
1079 	if (ret == GTK_RESPONSE_OK)
1080 	{
1081 		gchar *temp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(mw->getWidget("flistDialog")));
1082 		if (temp)
1083 		{
1084 			string path = Text::toUtf8(temp);
1085 			g_free(temp);
1086 
1087 			UserPtr user = DirectoryListing::getUserFromFilename(path);
1088 			if (user)
1089 				mw->showShareBrowser_gui(user, path, "", FALSE);
1090 			else
1091 				mw->setMainStatus_gui(_("Unable to load file list: Invalid file list name"));
1092 		}
1093 	}
1094 }
1095 
onOpenOwnListClicked_gui(GtkWidget * widget,gpointer data)1096 void MainWindow::onOpenOwnListClicked_gui(GtkWidget *widget, gpointer data)
1097 {
1098 	MainWindow *mw = (MainWindow *)data;
1099 	typedef Func1<MainWindow, bool> F1;
1100 	F1 *func = new F1(mw, &MainWindow::openOwnList_client, FALSE);
1101 	WulforManager::get()->dispatchClientFunc(func);
1102 
1103 	mw->setMainStatus_gui(_("Loading file list"));
1104 }
1105 
onRefreshFileListClicked_gui(GtkWidget * widget,gpointer data)1106 void MainWindow::onRefreshFileListClicked_gui(GtkWidget *widget, gpointer data)
1107 {
1108 	typedef Func0<MainWindow> F0;
1109 	F0 *func = new F0((MainWindow *)data, &MainWindow::refreshFileList_client);
1110 	WulforManager::get()->dispatchClientFunc(func);
1111 }
1112 
onReconnectClicked_gui(GtkWidget * widget,gpointer data)1113 void MainWindow::onReconnectClicked_gui(GtkWidget *widget, gpointer data)
1114 {
1115 	MainWindow *mw = (MainWindow *)data;
1116 	GtkWidget *entryWidget = mw->currentPage_gui();
1117 
1118 	if (entryWidget)
1119 	{
1120 		BookEntry *entry = (BookEntry *)g_object_get_data(G_OBJECT(entryWidget), "entry");
1121 
1122 		if (entry && entry->getType() == Entry::HUB)
1123 		{
1124 			Func0<Hub> *func = new Func0<Hub>(dynamic_cast<Hub *>(entry), &Hub::reconnect_client);
1125 			WulforManager::get()->dispatchClientFunc(func);
1126 		}
1127 	}
1128 }
1129 
onCloseClicked_gui(GtkWidget * widget,gpointer data)1130 void MainWindow::onCloseClicked_gui(GtkWidget *widget, gpointer data)
1131 {
1132 	MainWindow *mw = (MainWindow *)data;
1133 	GtkWidget *entryWidget = mw->currentPage_gui();
1134 
1135 	if (entryWidget)
1136 	{
1137 		BookEntry *entry = (BookEntry *)g_object_get_data(G_OBJECT(entryWidget), "entry");
1138 
1139 		if (entry)
1140 			mw->removeBookEntry_gui(entry);
1141 	}
1142 }
1143 
onPreviousTabClicked_gui(GtkWidget * widget,gpointer data)1144 void MainWindow::onPreviousTabClicked_gui(GtkWidget* widget, gpointer data)
1145 {
1146 	MainWindow *mw = (MainWindow *)data;
1147 	mw->previousTab_gui();
1148 }
1149 
onNextTabClicked_gui(GtkWidget * widget,gpointer data)1150 void MainWindow::onNextTabClicked_gui(GtkWidget* widget, gpointer data)
1151 {
1152 	MainWindow *mw = (MainWindow *)data;
1153 	mw->nextTab_gui();
1154 }
1155 
onHelpOnlineItemActivated_gui(GtkMenuItem * item,gpointer data)1156 void MainWindow::onHelpOnlineItemActivated_gui(GtkMenuItem *item, gpointer data)
1157 {
1158 	const string uri = "https://answers.launchpad.net/linuxdcpp";
1159 	WulforUtil::openURI(uri);
1160 }
1161 
onTranslateApplicationItemActivated_gui(GtkMenuItem * item,gpointer data)1162 void MainWindow::onTranslateApplicationItemActivated_gui(GtkMenuItem *item, gpointer data)
1163 {
1164 	const string uri = "https://translations.launchpad.net/linuxdcpp";
1165 	WulforUtil::openURI(uri);
1166 }
1167 
onReportProblemItemActivated_gui(GtkMenuItem * item,gpointer data)1168 void MainWindow::onReportProblemItemActivated_gui(GtkMenuItem *item, gpointer data)
1169 {
1170 	const string uri = "https://bugs.launchpad.net/linuxdcpp";
1171 	WulforUtil::openURI(uri);
1172 }
1173 
onAboutClicked_gui(GtkWidget * widget,gpointer data)1174 void MainWindow::onAboutClicked_gui(GtkWidget *widget, gpointer data)
1175 {
1176 	MainWindow *mw = (MainWindow *)data;
1177 	gtk_dialog_run(GTK_DIALOG(mw->getWidget("aboutDialog")));
1178 	gtk_widget_hide(mw->getWidget("aboutDialog"));
1179 }
1180 
onAboutDialogActivateLink_gui(GtkAboutDialog * dialog,const gchar * link,gpointer data)1181 void MainWindow::onAboutDialogActivateLink_gui(GtkAboutDialog *dialog, const gchar *link, gpointer data)
1182 {
1183 	WulforUtil::openURI(link);
1184 }
1185 
onCloseBookEntry_gui(GtkWidget * widget,gpointer data)1186 void MainWindow::onCloseBookEntry_gui(GtkWidget *widget, gpointer data)
1187 {
1188 	BookEntry *entry = (BookEntry *)data;
1189 	WulforManager::get()->getMainWindow()->removeBookEntry_gui(entry);
1190 }
1191 
onStatusIconActivated_gui(GtkStatusIcon * statusIcon,gpointer data)1192 void MainWindow::onStatusIconActivated_gui(GtkStatusIcon *statusIcon, gpointer data)
1193 {
1194 	MainWindow *mw = (MainWindow *)data;
1195 	GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(mw->getWidget("statusIconShowInterfaceItem"));
1196 
1197 	// Toggle the "Show Interface" check menu item. This will in turn invoke its callback.
1198 	gboolean active = gtk_check_menu_item_get_active(item);
1199 	gtk_check_menu_item_set_active(item, !active);
1200 }
1201 
onStatusIconPopupMenu_gui(GtkStatusIcon * statusIcon,guint button,guint time,gpointer data)1202 void MainWindow::onStatusIconPopupMenu_gui(GtkStatusIcon *statusIcon, guint button, guint time, gpointer data)
1203 {
1204 	MainWindow *mw = (MainWindow *)data;
1205 	GtkMenu *menu = GTK_MENU(mw->getWidget("statusIconMenu"));
1206 	gtk_menu_popup(menu, NULL, NULL, gtk_status_icon_position_menu, statusIcon, button, time);
1207 }
1208 
onShowInterfaceToggled_gui(GtkCheckMenuItem * item,gpointer data)1209 void MainWindow::onShowInterfaceToggled_gui(GtkCheckMenuItem *item, gpointer data)
1210 {
1211 	MainWindow *mw = (MainWindow *)data;
1212 	GtkWindow *win = mw->window;
1213 	static int x, y;
1214 	static bool isMaximized, isIconified;
1215 
1216 	if (GTK_WIDGET_VISIBLE(win))
1217 	{
1218 		GdkWindowState state;
1219 		gtk_window_get_position(win, &x, &y);
1220 		state = gdk_window_get_state(GTK_WIDGET(win)->window);
1221 		isMaximized = (state & GDK_WINDOW_STATE_MAXIMIZED);
1222 		isIconified = (state & GDK_WINDOW_STATE_ICONIFIED);
1223 		gtk_widget_hide(GTK_WIDGET(win));
1224 	}
1225 	else
1226 	{
1227 		gtk_window_move(win, x, y);
1228 		if (isMaximized) gtk_window_maximize(win);
1229 		if (isIconified) gtk_window_iconify(win);
1230 		gtk_widget_show(GTK_WIDGET(win));
1231 	}
1232 }
1233 
autoConnect_client()1234 void MainWindow::autoConnect_client()
1235 {
1236 	FavoriteHubEntry *hub;
1237 	FavoriteHubEntryList &l = FavoriteManager::getInstance()->getFavoriteHubs();
1238 	typedef Func2<MainWindow, string, string> F2;
1239 	F2 *func;
1240 
1241 	for (FavoriteHubEntryList::const_iterator it = l.begin(); it != l.end(); ++it)
1242 	{
1243 		hub = *it;
1244 
1245 		if (hub->getConnect())
1246 		{
1247 			func = new F2(this, &MainWindow::showHub_gui, hub->getServer(), hub->getEncoding());
1248 			WulforManager::get()->dispatchGuiFunc(func);
1249 		}
1250 	}
1251 }
1252 
startSocket_client()1253 void MainWindow::startSocket_client()
1254 {
1255 	SearchManager::getInstance()->disconnect();
1256 	ConnectionManager::getInstance()->disconnect();
1257 
1258 	if (ClientManager::getInstance()->isActive())
1259 	{
1260 		try
1261 		{
1262 			ConnectionManager::getInstance()->listen();
1263 		}
1264 		catch (const Exception &e)
1265 		{
1266 			string primaryText = _("Unable to open TCP/TLS port");
1267 			string secondaryText = _("File transfers will not work correctly until the connection settings are adjusted or any application using the port is closed.");
1268 			typedef Func2<MainWindow, string, string> F2;
1269 			F2* func = new F2(this, &MainWindow::showMessageDialog_gui, primaryText, secondaryText);
1270 			WulforManager::get()->dispatchGuiFunc(func);
1271 
1272 		}
1273 
1274 		try
1275 		{
1276 			SearchManager::getInstance()->listen();
1277 		}
1278 		catch (const Exception &e)
1279 		{
1280 			string primaryText = _("Unable to open UDP port");
1281 			string secondaryText = _("Searching will not work correctly until the connection settings are adjusted or any application using the port is closed.");
1282 			typedef Func2<MainWindow, string, string> F2;
1283 			F2* func = new F2(this, &MainWindow::showMessageDialog_gui, primaryText, secondaryText);
1284 			WulforManager::get()->dispatchGuiFunc(func);
1285 		}
1286 	}
1287 
1288 	ClientManager::getInstance()->infoUpdated();
1289 }
1290 
refreshFileList_client()1291 void MainWindow::refreshFileList_client()
1292 {
1293 	try
1294 	{
1295 		ShareManager::getInstance()->setDirty();
1296 		ShareManager::getInstance()->refresh(TRUE, TRUE, FALSE);
1297 	}
1298 	catch (const ShareException&)
1299 	{
1300 	}
1301 }
1302 
addDownload_client(string name,string tth,int64_t size)1303 void MainWindow::addDownload_client(string name, string tth, int64_t size)
1304 {
1305 	name = SETTING(DOWNLOAD_DIRECTORY) + name;
1306 	UserPtr dummyuser(new User(CID::generate()));
1307 	QueueManager::getInstance()->add(name, size, TTHValue(tth), dummyuser, "");
1308 	QueueManager::getInstance()->removeSource(name, dummyuser, QueueItem::Source::FLAG_REMOVED);
1309 }
1310 
openOwnList_client(bool useSetting)1311 void MainWindow::openOwnList_client(bool useSetting)
1312 {
1313 	UserPtr user = ClientManager::getInstance()->getMe();
1314 	string path = ShareManager::getInstance()->getOwnListFile();
1315 
1316 	typedef Func4<MainWindow, UserPtr, string, string, bool> F4;
1317 	F4 *func = new F4(this, &MainWindow::showShareBrowser_gui, user, path, "", useSetting);
1318 	WulforManager::get()->dispatchGuiFunc(func);
1319 }
1320 
createIPCPipe()1321 void MainWindow::createIPCPipe()
1322 {
1323 	const string pipepath = WulforUtil::getPipePath();
1324 	dcdebug("MainWindow::createIPCPipe(): Pipepath %s\n", pipepath.c_str());
1325 
1326 	int status = mkfifo(pipepath.c_str(), S_IRWXU);
1327 
1328 	if (status == -1 && errno == EEXIST)
1329 	{
1330 		dcdebug("Stale pipe detected. Unlinking...\n");
1331 		unlink(pipepath.c_str());
1332 	}
1333 
1334 	if (status == 0 || mkfifo(pipepath.c_str(), S_IRWXU) == 0)
1335 	{
1336 		int fd = open(pipepath.c_str(), O_NONBLOCK | O_RDWR);
1337 		if (fd >= 0)
1338 		{
1339 			IPC = g_io_channel_unix_new(fd);	// Eh, can this fail?
1340 			g_io_add_watch(IPC, G_IO_IN, onExternalData, this);
1341 			g_io_add_watch(IPC, G_IO_PRI, onExternalData, this);
1342 		}
1343 	}
1344 	else
1345 	{
1346 		dcdebug("MainWindow::MainWindow, mkfifo FAILED!\n");
1347 		IPC = NULL;
1348 	}
1349 }
1350 
onExternalData(GIOChannel * source,GIOCondition cond,gpointer data)1351 gboolean MainWindow::onExternalData(GIOChannel* source, GIOCondition cond, gpointer data)
1352 {
1353 	MainWindow* mw = (MainWindow*)data;
1354 	if (mw->IPC != source)
1355 		return TRUE;
1356 
1357 	if (cond != G_IO_IN && cond != G_IO_PRI)
1358 	{
1359 		if (cond == G_IO_HUP)
1360 			dcdebug("G_IO_HUP\n");
1361 		dcdebug("Weird things in IO Channel\n");
1362 		return FALSE;
1363 	}
1364 
1365 
1366 	/* Currently this doesn't handle the situation where data is written
1367 	 * to pipe without newline. Apparently nothing is read in that case
1368 	 * and onExternalData is called again and again (causing high cpu usage).
1369 	 * So I guess the channel should be cleared somehow. */
1370 	gchar *str;
1371 	gsize len = 0;
1372 	gsize termpos = 0;
1373 	GIOStatus status = g_io_channel_read_line(source, &str, &len, &termpos, NULL);
1374 	if (termpos > 0)
1375 		str[termpos] = 0;	// replace newline with 0
1376 	dcdebug("MainWindow::onExternalData, from IO Channel: %s (%d)\n", str,  (int)len);
1377 	if (status == G_IO_STATUS_NORMAL && len > 0)
1378 	{
1379 		// Handle command
1380 		string cmd(str);
1381 		string arg;
1382 		size_t pos = cmd.find(" ");
1383 		g_free(str);
1384 
1385 		if (pos != string::npos && cmd.length() > pos + 1)
1386 		{
1387 			arg = cmd.substr(pos + 1);
1388 			cmd = cmd.substr(0, pos);
1389 		}
1390 
1391 		if (cmd == "show") {
1392 			gtk_window_present(mw->window);
1393 		}
1394 		else if (cmd == "refresh")
1395 		{
1396 			typedef Func0<MainWindow> F0;
1397 			F0 *func = new F0((MainWindow *)data, &MainWindow::refreshFileList_client);
1398 			WulforManager::get()->dispatchClientFunc(func);
1399 		}
1400 		else if (cmd == "search-magnet")
1401 		{
1402 			mw->addMagnetSearch_gui(arg);
1403 		}
1404 		else if (cmd == "add-magnet")
1405 		{
1406 			string name;
1407 			int64_t size;
1408 			string tth;
1409 
1410 			if (WulforUtil::splitMagnet(arg, name, size, tth))
1411 			{
1412 				if (name == _("Unknown") || size == 0 || tth == _("Unknown"))
1413 					mw->addMagnetSearch_gui(arg);
1414 				else
1415 				{
1416 					typedef Func3<MainWindow, string, string, int64_t> F3;
1417 					F3 *func = new F3(mw, &MainWindow::addDownload_client, name, tth, size);
1418 					WulforManager::get()->dispatchClientFunc(func);
1419 				}
1420 			}
1421 		}
1422 		else if (cmd == "connect")
1423 		{
1424 			mw->showHub_gui(arg);
1425 		}
1426 	}
1427 	else if (status != G_IO_STATUS_AGAIN)
1428 	{
1429 		dcdebug("Weird things in IO Channel\n");
1430 		return FALSE;
1431 	}
1432 
1433 	return TRUE;
1434 }
1435 
on(LogManagerListener::Message,time_t t,const string & message)1436 void MainWindow::on(LogManagerListener::Message, time_t t, const string &message) throw()
1437 {
1438 	typedef Func2<MainWindow, string, time_t> F2;
1439 	F2 *func = new F2(this, &MainWindow::setMainStatus_gui, message, t);
1440 	WulforManager::get()->dispatchGuiFunc(func);
1441 }
1442 
on(QueueManagerListener::Finished,QueueItem * item,const string & dir,int64_t avSpeed)1443 void MainWindow::on(QueueManagerListener::Finished, QueueItem *item, const string& dir, int64_t avSpeed) throw()
1444 {
1445 	if (item->isSet(QueueItem::FLAG_CLIENT_VIEW | QueueItem::FLAG_USER_LIST))
1446 	{
1447 		UserPtr user = item->getDownloads()[0]->getUser();
1448 		string listName = item->getListName();
1449 
1450 		typedef Func4<MainWindow, UserPtr, string, string, bool> F4;
1451 		F4 *func = new F4(this, &MainWindow::showShareBrowser_gui, user, listName, dir, TRUE);
1452 		WulforManager::get()->dispatchGuiFunc(func);
1453 	}
1454 	else if (item->isSet(QueueItem::FLAG_NORMAL) && WGETI("notify-download"))
1455 	{
1456 		typedef Func2<MainWindow, string, string> F2;
1457 		F2 *f2 = new F2(this, &MainWindow::showNotification_gui, _("Download finished"), item->getTargetFileName());
1458 		WulforManager::get()->dispatchGuiFunc(f2);
1459 	}
1460 }
1461 
on(TimerManagerListener::Second,uint32_t ticks)1462 void MainWindow::on(TimerManagerListener::Second, uint32_t ticks) throw()
1463 {
1464 	// Avoid calculating status update if it's not needed
1465 	if (!BOOLSETTING(ALWAYS_TRAY) && minimized)
1466 		return;
1467 
1468 	int64_t diff = (int64_t)((lastUpdate == 0) ? ticks - 1000 : ticks - lastUpdate);
1469 	int64_t downBytes = 0;
1470 	int64_t upBytes = 0;
1471 
1472 	if (diff > 0)
1473 	{
1474 		int64_t downDiff = Socket::getTotalDown() - lastDown;
1475 		int64_t upDiff = Socket::getTotalUp() - lastUp;
1476 		downBytes = (downDiff * 1000) / diff;
1477 		upBytes = (upDiff * 1000) / diff;
1478 	}
1479 
1480 	string hubs = Client::getCounts();
1481 	string downloadSpeed = Util::formatBytes(downBytes) + "/s";
1482 	string downloaded = Util::formatBytes(Socket::getTotalDown());
1483 	string uploadSpeed = Util::formatBytes(upBytes) + "/s";
1484 	string uploaded = Util::formatBytes(Socket::getTotalUp());
1485 
1486 	lastUpdate = ticks;
1487 	lastUp = Socket::getTotalUp();
1488 	lastDown = Socket::getTotalDown();
1489 
1490 	typedef Func5<MainWindow, string, string, string, string, string> F5;
1491 	F5 *func = new F5(this, &MainWindow::setStats_gui, hubs, downloadSpeed, downloaded, uploadSpeed, uploaded);
1492 	WulforManager::get()->dispatchGuiFunc(func);
1493 
1494 	if (BOOLSETTING(ALWAYS_TRAY) && !downloadSpeed.empty() && !uploadSpeed.empty())
1495 	{
1496 		typedef Func2<MainWindow, string, string> F2;
1497 		F2 *f2 = new F2(this, &MainWindow::updateStatusIconTooltip_gui, downloadSpeed, uploadSpeed);
1498 		WulforManager::get()->dispatchGuiFunc(f2);
1499 	}
1500 }
1501 
1502