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 "sharebrowser.hh"
23 
24 #include <dcpp/FavoriteManager.h>
25 #include <dcpp/ShareManager.h>
26 #include <dcpp/Text.h>
27 #include "search.hh"
28 #include "settingsmanager.hh"
29 #include "UserCommandMenu.hh"
30 #include "wulformanager.hh"
31 #include "WulforUtil.hh"
32 
33 using namespace std;
34 using namespace dcpp;
35 
ShareBrowser(UserPtr user,const std::string & file,const std::string & initialDirectory)36 ShareBrowser::ShareBrowser(UserPtr user, const std::string &file, const std::string &initialDirectory):
37 	BookEntry(Entry::SHARE_BROWSER, WulforUtil::getNicks(user), "sharebrowser.glade", user->getCID().toBase32()),
38 	user(user),
39 	file(file),
40 	initialDirectory(initialDirectory),
41 	listing(user),
42 	shareSize(0),
43 	currentSize(0),
44 	shareItems(0),
45 	currentItems(0),
46 	updateFileView(TRUE),
47 	skipHits(0)
48 {
49 	// Use the nick from the file name in case the user is offline and core only returns CID
50 	nick = WulforUtil::getNicks(user);
51 	if (nick.find(user->getCID().toBase32(), 1) != string::npos)
52 	{
53 		string name = Util::getFileName(file);
54 		string::size_type loc = name.find('.');
55 		nick = name.substr(0, loc);
56 		setLabel_gui(nick);
57 	}
58 
59 	// Configure the dialogs
60 	File::ensureDirectory(SETTING(DOWNLOAD_DIRECTORY));
61 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("findDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
62 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(getWidget("dirChooserDialog")), Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)).c_str());
63 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("dirChooserDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
64 
65 	// Set the pane position
66 	gtk_paned_set_position(GTK_PANED(getWidget("pane")), WGETI("sharebrowser-pane-position"));
67 
68 	// Initialize the file TreeView
69 	fileView.setView(GTK_TREE_VIEW(getWidget("fileView")), true, "sharebrowser");
70 	fileView.insertColumn(N_("Filename"), G_TYPE_STRING, TreeView::ICON_STRING, 400, "Icon");
71 	fileView.insertColumn(N_("Size"), G_TYPE_STRING, TreeView::STRINGR, 100);
72 	fileView.insertColumn(N_("Type"), G_TYPE_STRING, TreeView::STRING, 50);
73 	fileView.insertColumn(N_("TTH"), G_TYPE_STRING, TreeView::STRING, 150);
74 	fileView.insertColumn(N_("Exact Size"), G_TYPE_STRING, TreeView::STRINGR, 120);
75 	fileView.insertHiddenColumn("DL File", G_TYPE_POINTER);
76 	fileView.insertHiddenColumn("Icon", G_TYPE_STRING);
77 	fileView.insertHiddenColumn("Size Order", G_TYPE_INT64);
78 	fileView.insertHiddenColumn("File Order", G_TYPE_STRING);
79 	fileView.finalize();
80 	fileStore = gtk_list_store_newv(fileView.getColCount(), fileView.getGTypes());
81 	gtk_tree_view_set_model(fileView.get(), GTK_TREE_MODEL(fileStore));
82 	g_object_unref(fileStore);
83 	fileSelection = gtk_tree_view_get_selection(fileView.get());
84 	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(fileView.get()), GTK_SELECTION_MULTIPLE);
85 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), fileView.col("File Order"), GTK_SORT_ASCENDING);
86 	gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(fileView.get(), fileView.col("Filename")), TRUE);
87 	gtk_tree_view_set_fixed_height_mode(fileView.get(), TRUE);
88 	fileView.setSortColumn_gui("Filename", "File Order");
89 	fileView.setSortColumn_gui("Size", "Size Order");
90 	fileView.setSortColumn_gui("Exact Size", "Size Order");
91 
92 	// Initialize the directory treeview
93 	dirView.setView(GTK_TREE_VIEW(getWidget("dirView")));
94 	dirView.insertColumn("Dir", G_TYPE_STRING, TreeView::ICON_STRING, -1, "Icon");
95 	dirView.insertHiddenColumn("DL Dir", G_TYPE_POINTER);
96 	dirView.insertHiddenColumn("Icon", G_TYPE_STRING);
97 	dirView.finalize();
98 	dirStore = gtk_tree_store_newv(dirView.getColCount(), dirView.getGTypes());
99 	gtk_tree_view_set_model(dirView.get(), GTK_TREE_MODEL(dirStore));
100 	g_object_unref(dirStore);
101 	dirSelection = gtk_tree_view_get_selection(dirView.get());
102 	gtk_tree_view_set_enable_tree_lines(dirView.get(), TRUE);
103 
104 	// Initialize the user command menus
105 	fileUserCommandMenu = new UserCommandMenu(getWidget("fileUserCommandMenu"), ::UserCommand::CONTEXT_FILELIST);
106 	addChild(fileUserCommandMenu);
107 	dirUserCommandMenu = new UserCommandMenu(getWidget("dirUserCommandMenu"), ::UserCommand::CONTEXT_FILELIST);
108 	addChild(dirUserCommandMenu);
109 
110 	// Connect the signals to their callback functions.
111 	g_signal_connect(fileView.get(), "button-press-event", G_CALLBACK(onButtonPressed_gui), (gpointer)this);
112 	g_signal_connect(fileView.get(), "button-release-event", G_CALLBACK(onFileButtonReleased_gui), (gpointer)this);
113 	g_signal_connect(fileView.get(), "key-release-event", G_CALLBACK(onFileKeyReleased_gui), (gpointer)this);
114 	g_signal_connect(dirView.get(), "button-press-event", G_CALLBACK(onButtonPressed_gui), (gpointer)this);
115 	g_signal_connect(dirView.get(), "button-release-event", G_CALLBACK(onDirButtonReleased_gui), (gpointer)this);
116 	g_signal_connect(dirView.get(), "key-release-event", G_CALLBACK(onDirKeyReleased_gui), (gpointer)this);
117 	g_signal_connect(getWidget("matchButton"), "clicked", G_CALLBACK(onMatchButtonClicked_gui), (gpointer)this);
118 	g_signal_connect(getWidget("findButton"), "clicked", G_CALLBACK(onFindButtonClicked_gui), (gpointer)this);
119 	g_signal_connect(getWidget("nextButton"), "clicked", G_CALLBACK(onNextButtonClicked_gui), (gpointer)this);
120 	g_signal_connect(getWidget("dirDownloadItem"), "activate", G_CALLBACK(onDownloadDirClicked_gui), (gpointer)this);
121 	g_signal_connect(getWidget("fileDownloadItem"), "activate", G_CALLBACK(onDownloadClicked_gui), (gpointer)this);
122 	g_signal_connect(getWidget("searchForAlternatesItem"), "activate", G_CALLBACK(onSearchAlternatesClicked_gui), (gpointer)this);
123 	g_signal_connect(getWidget("copyMagnetItem"), "activate", G_CALLBACK(onCopyMagnetClicked_gui), (gpointer)this);
124 }
125 
~ShareBrowser()126 ShareBrowser::~ShareBrowser()
127 {
128 	// Save the pane position
129 	int panePosition = gtk_paned_get_position(GTK_PANED(getWidget("pane")));
130 	WSET("sharebrowser-pane-position", panePosition);
131 
132 	gtk_widget_destroy(getWidget("findDialog"));
133 	gtk_widget_destroy(getWidget("dirChooserDialog"));
134 }
135 
show()136 void ShareBrowser::show()
137 {
138 	buildList_gui();
139 	openDir_gui(initialDirectory);
140 	updateStatus_gui();
141 	WulforManager::get()->getMainWindow()->setMainStatus_gui(_("File list loaded"));
142 }
143 
buildList_gui()144 void ShareBrowser::buildList_gui()
145 {
146 	// Load the xml file containing the share list.
147 	try
148 	{
149 		listing.loadFile(file);
150 
151 		// Set name of root entry to user nick.
152 		listing.getRoot()->setName(nick);
153 
154 		// Add entries to dir tree view starting with the root entry.
155 		buildDirs_gui(listing.getRoot(), NULL);
156 	}
157 	catch (const Exception &e)
158 	{
159 		setStatus_gui("mainStatus", F_("Unable to load file list: %1%", % e.getError()));
160 	}
161 }
162 
163 /*
164  * Selects the directory in the tree view, and shows that directory's contents.
165  */
openDir_gui(const string & dir)166 void ShareBrowser::openDir_gui(const string &dir)
167 {
168 	GtkTreeIter iter;
169 
170 	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dirStore), &iter))
171 	{
172 		GtkTreePath *path;
173 		DirectoryListing::Directory *directory;
174 
175 		if (findDir_gui(dir, &iter))
176 			path = gtk_tree_model_get_path(GTK_TREE_MODEL(dirStore), &iter);
177 		else
178 			path = gtk_tree_path_new_first();
179 
180 		directory = dirView.getValue<gpointer, DirectoryListing::Directory *>(&iter, "DL Dir");
181 
182 		gtk_tree_view_expand_to_path(dirView.get(), path);
183 		gtk_tree_view_scroll_to_cell(dirView.get(), path, gtk_tree_view_get_column(dirView.get(), 0), FALSE, 0.0, 0.0);
184 		gtk_tree_view_set_cursor(dirView.get(), path, NULL, FALSE);
185 		gtk_tree_path_free(path);
186 
187 		updateFiles_gui(directory);
188 	}
189 }
190 
findDir_gui(const string & dir,GtkTreeIter * parent)191 bool ShareBrowser::findDir_gui(const string &dir, GtkTreeIter *parent)
192 {
193 	if (dir.empty())
194 		return TRUE;
195 
196 	string::size_type i = dir.find_first_of(PATH_SEPARATOR);
197 	const string &current = dir.substr(0, i);
198 	GtkTreeIter iter;
199 	bool valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(dirStore), &iter, parent);
200 
201 	while (valid)
202 	{
203 		if (dirView.getString(&iter, "Dir") == current)
204 		{
205 			*parent = iter;
206 			return findDir_gui(dir.substr(i + 1), parent);
207 		}
208 
209 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dirStore), &iter);
210 	}
211 
212 	return FALSE;
213 }
214 
buildDirs_gui(DirectoryListing::Directory * dir,GtkTreeIter * iter)215 void ShareBrowser::buildDirs_gui(DirectoryListing::Directory *dir, GtkTreeIter *iter)
216 {
217 	DirectoryListing::Directory::Iter it;
218 	DirectoryListing::File::Iter file;
219 	GtkTreeIter newIter;
220 
221 	gtk_tree_store_append(dirStore, &newIter, iter);
222 	gtk_tree_store_set(dirStore, &newIter, dirView.col("Dir"), dir->getName().c_str(), -1);
223 
224 	gtk_tree_store_set(dirStore, &newIter,
225 		dirView.col("DL Dir"), (gpointer)dir,
226 		dirView.col("Icon"), GTK_STOCK_DIRECTORY,
227 		-1);
228 
229 	for (file = dir->files.begin(); file != dir->files.end(); ++file)
230 	{
231 		shareItems++;
232 		shareSize += (*file)->getSize();
233 	}
234 
235 	// Recursive call for all subdirs of current dir.
236 	std::sort(dir->directories.begin(), dir->directories.end(), DirectoryListing::Directory::DirSort());
237 	for (it = dir->directories.begin(); it != dir->directories.end(); ++it)
238 		buildDirs_gui(*it, &newIter);
239 }
240 
updateFiles_gui(DirectoryListing::Directory * dir)241 void ShareBrowser::updateFiles_gui(DirectoryListing::Directory *dir)
242 {
243 	DirectoryListing::Directory::List *dirs = &(dir->directories);
244 	DirectoryListing::Directory::Iter it_dir;
245 	DirectoryListing::File::List *files = &(dir->files);
246 	DirectoryListing::File::Iter it_file;
247 	GtkTreeIter iter;
248 	int64_t size;
249 	gint sortColumn;
250 	GtkSortType sortType;
251 
252 	currentSize = 0;
253 	currentItems = 0;
254 
255 	gtk_list_store_clear(fileStore);
256 
257 	gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(fileStore), &sortColumn, &sortType);
258 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, sortType);
259 
260 	// Add directories to the store.
261 	for (it_dir = dirs->begin(); it_dir != dirs->end(); ++it_dir)
262 	{
263 		gtk_list_store_append(fileStore, &iter);
264 		gtk_list_store_set(fileStore, &iter,
265 			fileView.col("Filename"), Util::getFileName((*it_dir)->getName()).c_str(),
266 			fileView.col("File Order"), Util::getFileName("d"+(*it_dir)->getName()).c_str(),
267 			-1);
268 
269 		size = (*it_dir)->getTotalSize(false);
270 		gtk_list_store_set(fileStore, &iter,
271 			fileView.col("Icon"), GTK_STOCK_DIRECTORY,
272 			fileView.col("Size"), Util::formatBytes(size).c_str(),
273 			fileView.col("Exact Size"), Util::formatExactSize(size).c_str(),
274 			fileView.col("Size Order"), size,
275 			fileView.col("Type"), _("Directory"),
276 			fileView.col("DL File"), (gpointer)(*it_dir),
277 			fileView.col("TTH"), "",
278 			-1);
279 
280 		currentSize += size;
281 		currentItems++;
282 	}
283 
284 	// Add files to the store.
285 	for (it_file = files->begin(); it_file != files->end(); ++it_file)
286 	{
287 		gtk_list_store_append(fileStore, &iter);
288 
289 		// If ext is empty we cannot do substr on it.
290 		string ext = Util::getFileExt((*it_file)->getName());
291 		if (ext.length() > 0)
292 			ext = ext.substr(1);
293 
294 		gtk_list_store_set(fileStore, &iter,
295 			fileView.col("Filename"), Util::getFileName((*it_file)->getName()).c_str(),
296 			fileView.col("Type"), ext.c_str(),
297 			fileView.col("File Order"), Util::getFileName("f"+(*it_file)->getName()).c_str(),
298 			-1);
299 
300 		size = (*it_file)->getSize();
301 		gtk_list_store_set(fileStore, &iter,
302 			fileView.col("Icon"), GTK_STOCK_FILE,
303 			fileView.col("Size"), Util::formatBytes(size).c_str(),
304 			fileView.col("Exact Size"), Util::formatExactSize(size).c_str(),
305 			fileView.col("Size Order"), size,
306 			fileView.col("DL File"), (gpointer)(*it_file),
307 			fileView.col("TTH"), (*it_file)->getTTH().toBase32().c_str(),
308 			-1);
309 
310 		currentSize += size;
311 		currentItems++;
312 	}
313 
314 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), sortColumn, sortType);
315 	gtk_tree_view_scroll_to_point(fileView.get(), 0, 0);
316 	updateStatus_gui();
317 	updateFileView = TRUE;
318 }
319 
updateStatus_gui()320 void ShareBrowser::updateStatus_gui()
321 {
322 	string files = P_("File: %1%", "Files: %1%", % currentItems, currentItems);
323 	setStatus_gui("itemsStatus", files);
324 
325 	// TRANSLATORS: Size of currently visible folder.
326 	string size = F_("Size: %1%", % Util::formatBytes(currentSize));
327 	setStatus_gui("sizeStatus", size);
328 
329 	string totalFiles = P_("Total File: %1%", "Total Files: %1%", % shareItems, shareItems);
330 	setStatus_gui("filesStatus", totalFiles);
331 
332 	string totalSize = F_("Total Size: %1%", % Util::formatBytes(shareSize));
333 	setStatus_gui("totalStatus", totalSize);
334 }
335 
setStatus_gui(string statusBar,std::string msg)336 void ShareBrowser::setStatus_gui(string statusBar, std::string msg)
337 {
338 	gtk_statusbar_pop(GTK_STATUSBAR(getWidget(statusBar)), 0);
339 	gtk_statusbar_push(GTK_STATUSBAR(getWidget(statusBar)), 0, msg.c_str());
340 }
341 
fileViewSelected_gui()342 void ShareBrowser::fileViewSelected_gui()
343 {
344 	gpointer ptr;
345 	string fileOrder;
346 	GtkTreeIter iter, parentIter;
347 	GtkTreeModel *m = GTK_TREE_MODEL(fileStore);
348 	GList *list = gtk_tree_selection_get_selected_rows(fileSelection, NULL);
349 	GtkTreePath *path = (GtkTreePath *)g_list_nth_data(list, 0);
350 
351 	if (gtk_tree_model_get_iter(m, &iter, path))
352 	{
353 		ptr = fileView.getValue<gpointer>(&iter, "DL File");
354 		fileOrder = fileView.getString(&iter, "File Order");
355 
356 		if (fileOrder[0] == 'd' && gtk_tree_selection_get_selected(dirSelection, NULL, &parentIter))
357 		{
358 			gtk_tree_path_free(path);
359 			m = GTK_TREE_MODEL(dirStore);
360 			gboolean valid = gtk_tree_model_iter_children(m, &iter, &parentIter);
361 
362 			while (valid && ptr != dirView.getValue<gpointer>(&iter, "DL Dir"))
363 				valid = gtk_tree_model_iter_next(m, &iter);
364 
365 			path = gtk_tree_model_get_path(m, &iter);
366 			gtk_tree_view_expand_to_path(dirView.get(), path);
367 			gtk_tree_view_set_cursor(dirView.get(), path, NULL, FALSE);
368 
369 			updateFiles_gui((DirectoryListing::Directory *)ptr);
370 		}
371 		else
372 			downloadSelectedFiles_gui(Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)));
373 	}
374 
375 	gtk_tree_path_free(path);
376 	g_list_free(list);
377 }
378 
downloadSelectedFiles_gui(const string & target)379 void ShareBrowser::downloadSelectedFiles_gui(const string &target)
380 {
381 	gpointer ptr;
382 	string fileOrder;
383 	string filename;
384 	GtkTreeIter iter;
385 	GtkTreePath *path;
386 	DirectoryListing::File *file;
387 	DirectoryListing::Directory *dir;
388 	GList *list = gtk_tree_selection_get_selected_rows(fileSelection, NULL);
389 
390 	for (GList *i = list; i; i = i->next)
391 	{
392 		path = (GtkTreePath *)i->data;
393 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileStore), &iter, path))
394 		{
395 			ptr = fileView.getValue<gpointer>(&iter, "DL File");
396 			fileOrder = fileView.getString(&iter, "File Order");
397 
398 			if (fileOrder[0] == 'd')
399 			{
400 				dir = (DirectoryListing::Directory *)ptr;
401 
402 				typedef Func2<ShareBrowser, DirectoryListing::Directory *, string> F2;
403 				F2 * func = new F2(this, &ShareBrowser::downloadDir_client, dir, target);
404 				WulforManager::get()->dispatchClientFunc(func);
405 			}
406 			else
407 			{
408 				file = (DirectoryListing::File *)ptr;
409 
410 				string filename = Util::getFileName(file->getName());
411 
412 				typedef Func2<ShareBrowser, DirectoryListing::File *, string> F2;
413 				F2 * func = new F2(this, &ShareBrowser::downloadFile_client, file, target + filename);
414 				WulforManager::get()->dispatchClientFunc(func);
415 			}
416 		}
417 		gtk_tree_path_free(path);
418 	}
419 	g_list_free(list);
420 }
421 
downloadSelectedDirs_gui(const string & target)422 void ShareBrowser::downloadSelectedDirs_gui(const string &target)
423 {
424 	DirectoryListing::Directory *dir;
425 	GtkTreeIter iter;
426 
427 	if (gtk_tree_selection_get_selected(dirSelection, NULL, &iter))
428 	{
429 		dir = dirView.getValue<gpointer, DirectoryListing::Directory *>(&iter, "DL Dir");
430 
431 		typedef Func2<ShareBrowser, DirectoryListing::Directory *, string> F2;
432 		F2 * func = new F2(this, &ShareBrowser::downloadDir_client, dir, target);
433 		WulforManager::get()->dispatchClientFunc(func);
434 	}
435 }
436 
popupFileMenu_gui()437 void ShareBrowser::popupFileMenu_gui()
438 {
439 	GtkWidget *menuItem;
440 
441 	// Clean menus
442 	gtk_container_foreach(GTK_CONTAINER(getWidget("fileDownloadMenu")), (GtkCallback)gtk_widget_destroy, NULL);
443 	fileUserCommandMenu->cleanMenu_gui();
444 
445 	// Build file download menu
446 	StringPairList spl = FavoriteManager::getInstance()->getFavoriteDirs();
447 	if (spl.size() > 0)
448 	{
449 		for (StringPairIter i = spl.begin(); i != spl.end(); ++i)
450 		{
451 			menuItem = gtk_menu_item_new_with_label(i->second.c_str());
452 			g_object_set_data_full(G_OBJECT(menuItem), "fav", g_strdup(i->first.c_str()), g_free);
453 			g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadFavoriteClicked_gui), (gpointer)this);
454 			gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("fileDownloadMenu")), menuItem);
455 		}
456 		menuItem = gtk_separator_menu_item_new();
457 		gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("fileDownloadMenu")), menuItem);
458 	}
459 
460 	menuItem = gtk_menu_item_new_with_label(_("_Browse..."));
461 	gtk_menu_item_set_use_underline(GTK_MENU_ITEM(menuItem), TRUE);
462 	g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadToClicked_gui), (gpointer)this);
463 	gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("fileDownloadMenu")), menuItem);
464 
465 	// Build user command menu
466 	StringList hubs = WulforUtil::getHubAddress(listing.getUser()->getCID());
467 	fileUserCommandMenu->addHub(hubs);
468 	GtkTreeIter iter;
469 	GList *list = gtk_tree_selection_get_selected_rows(fileSelection, NULL);
470 	string cid = listing.getUser()->getCID().toBase32();
471 
472 	for (GList *i = list; i; i = i->next)
473 	{
474 		GtkTreePath *path = (GtkTreePath *)i->data;
475 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileStore), &iter, path))
476 		{
477 			string filepath;
478 			string fileOrder = fileView.getString(&iter, "File Order");
479 			gpointer ptr = fileView.getValue<gpointer>(&iter, "DL File");
480 
481 			if (fileOrder[0] == 'd')
482 			{
483 				DirectoryListing::Directory *dir = (DirectoryListing::Directory *)ptr;
484 				filepath = listing.getPath(dir->getParent());
485 			}
486 			else
487 			{
488 				DirectoryListing::File *file = (DirectoryListing::File *)ptr;
489 				filepath = listing.getPath(file);
490 			}
491 
492 			fileUserCommandMenu->addFile(cid,
493 				fileView.getString(&iter, "Filename"),
494 				filepath,
495 				fileView.getValue<int64_t>(&iter, "Size Order"),
496 				fileView.getString(&iter, "TTH"));
497 		}
498 		gtk_tree_path_free(path);
499 	}
500 	g_list_free(list);
501 	fileUserCommandMenu->buildMenu_gui();
502 
503 	gtk_menu_popup(GTK_MENU(getWidget("fileMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
504 	gtk_widget_show_all(getWidget("fileMenu"));
505 }
506 
popupDirMenu_gui()507 void ShareBrowser::popupDirMenu_gui()
508 {
509 	GtkWidget *menuItem;
510 
511 	// Clean menus
512 	gtk_container_foreach(GTK_CONTAINER(getWidget("dirDownloadMenu")), (GtkCallback)gtk_widget_destroy, NULL);
513 	dirUserCommandMenu->cleanMenu_gui();
514 
515 	StringPairList spl = FavoriteManager::getInstance()->getFavoriteDirs();
516 	if (spl.size() > 0)
517 	{
518 		for (StringPairIter i = spl.begin(); i != spl.end(); ++i)
519 		{
520 			menuItem = gtk_menu_item_new_with_label(i->second.c_str());
521 			g_object_set_data_full(G_OBJECT(menuItem), "fav", g_strdup(i->first.c_str()), g_free);
522 			g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadFavoriteDirClicked_gui), (gpointer)this);
523 			gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("dirDownloadMenu")), menuItem);
524 		}
525 		menuItem = gtk_separator_menu_item_new();
526 		gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("dirDownloadMenu")), menuItem);
527 	}
528 
529 	menuItem = gtk_menu_item_new_with_label(_("_Browse..."));
530 	gtk_menu_item_set_use_underline(GTK_MENU_ITEM(menuItem), TRUE);
531 	g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadDirToClicked_gui), (gpointer)this);
532 	gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("dirDownloadMenu")), menuItem);
533 
534 	// Add user commands.
535 	GtkTreeIter iter;
536 	if (gtk_tree_selection_get_selected(dirSelection, NULL, &iter))
537 	{
538 		string filename;
539 		string filepath;
540 		string cid = listing.getUser()->getCID().toBase32();
541 		StringList hubs = WulforUtil::getHubAddress(listing.getUser()->getCID());
542 		DirectoryListing::Directory *dir = dirView.getValue<DirectoryListing::Directory *>(&iter, "DL Dir");
543 
544 		if (dir != listing.getRoot())
545 		{
546 			filename = dirView.getString(&iter, "Dir");
547 			filepath = listing.getPath(dir->getParent());
548 		}
549 
550 		dirUserCommandMenu->addFile(cid, filename, filepath);
551 		dirUserCommandMenu->addHub(hubs);
552 		dirUserCommandMenu->buildMenu_gui();
553 	}
554 
555 	gtk_menu_popup(GTK_MENU(getWidget("dirMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
556 	gtk_widget_show_all(getWidget("dirMenu"));
557 }
558 
559 /*
560  * Searches the directories iteratively for the requested pattern. Uses a pre-order
561  * traversal method, with the exception that it searches the parent's dir name first.
562  * Instead of keeping track of the last directory its search ended at, it counts
563  * the number of matches and re-searches the listing, skipping matches until it
564  * reaches the newest one. Slightly slower, but simpler.
565  */
find_gui()566 void ShareBrowser::find_gui()
567 {
568 	string name;
569 	bool findLeafNode = TRUE;
570 	int cursorPos, hits = 0;
571 	DirectoryListing::Directory *dir;
572 	DirectoryListing::File::Iter file;
573 	GtkTreeIter iter;
574 	GtkTreeModel *m = GTK_TREE_MODEL(dirStore);
575 	GtkTreePath *dirPath = gtk_tree_path_new_first();
576 
577 	if (gtk_tree_path_get_depth(dirPath) == 0 || !gtk_tree_model_get_iter(m, &iter, dirPath))
578 	{
579 		gtk_tree_path_free(dirPath);
580 		return;
581 	}
582 
583 	gint sortColumn;
584 	GtkSortType sortType;
585 	gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(fileStore), &sortColumn, &sortType);
586 	if (sortColumn != fileView.col("File Order") || sortType != GTK_SORT_ASCENDING)
587 	{
588 		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), fileView.col("File Order"), GTK_SORT_ASCENDING);
589 		gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(fileView.get(), fileView.col("Filename")), TRUE);
590 	}
591 
592 	while (TRUE)
593 	{
594 		// Drill down until we reach a leaf node (e.g. a dir with no child dirs).
595 		if (findLeafNode)
596 		{
597 			do
598 			{
599 				name = Text::toLower(dirView.getString(&iter, "Dir"));
600 				// We found a matching directory name.
601 				if (name.find(search, 0) != string::npos && hits++ == skipHits)
602 				{
603 					skipHits = hits;
604 					gtk_tree_view_expand_to_path(dirView.get(), dirPath);
605 					gtk_tree_view_set_cursor(dirView.get(), dirPath, NULL, FALSE);
606 					dir = dirView.getValue<gpointer, DirectoryListing::Directory *>(&iter, "DL Dir");
607 					updateFiles_gui(dir);
608 					gtk_widget_grab_focus(GTK_WIDGET(dirView.get()));
609 					updateFileView = FALSE;
610 					gtk_tree_path_free(dirPath);
611 					setStatus_gui("mainStatus", _("Found a match"));
612 					return;
613 				}
614 				gtk_tree_path_down(dirPath);
615 			}
616 			while (gtk_tree_model_get_iter(m, &iter, dirPath));
617 		}
618 
619 		// Come back up one directory. If we can't, then we've returned to the root and are done.
620 		if (!gtk_tree_path_up(dirPath) || gtk_tree_path_get_depth(dirPath) == 0 ||
621 			!gtk_tree_model_get_iter(m, &iter, dirPath))
622 		{
623 			setStatus_gui("mainStatus", _("No matches"));
624 			gtk_tree_path_free(dirPath);
625 			return;
626 		}
627 
628 		// Search the files that are contained in this directory.
629 		dir = dirView.getValue<gpointer, DirectoryListing::Directory *>(&iter, "DL Dir");
630 		std::sort(dir->files.begin(), dir->files.end(), DirectoryListing::File::FileSort());
631 
632 		for (file = dir->files.begin(), cursorPos = dir->directories.size(); file != dir->files.end(); file++, cursorPos++)
633 		{
634 			name = Text::toLower((*file)->getName());
635 
636 			// We found a matching file. Update the cursors and the fileView if necessary.
637 			if (name.find(search, 0) != string::npos && hits++ == skipHits)
638 			{
639 				if (updateFileView)
640 				{
641 					gtk_tree_view_expand_to_path(dirView.get(), dirPath);
642 					gtk_tree_view_set_cursor(dirView.get(), dirPath, NULL, FALSE);
643 					updateFiles_gui(dir);
644 					updateFileView = FALSE;
645 				}
646 
647 				skipHits = hits;
648 				// Keeping track of the current index allows us to quickly get the path to the file.
649 				GtkTreePath *path = gtk_tree_path_new_from_string(Util::toString(cursorPos).c_str());
650 				gtk_tree_view_set_cursor(fileView.get(), path, NULL, FALSE);
651 				gtk_widget_grab_focus(GTK_WIDGET(fileView.get()));
652 				gtk_tree_path_free(path);
653 				gtk_tree_path_free(dirPath);
654 				setStatus_gui("mainStatus", _("Found a match"));
655 				return;
656 			}
657 		}
658 		updateFileView = TRUE;
659 
660 		// Determine if we are to go to the next sibling or back to the parent dir.
661 		gtk_tree_path_next(dirPath);
662 		if (!gtk_tree_model_get_iter(m, &iter, dirPath))
663 			findLeafNode = FALSE;
664 		else
665 			findLeafNode = TRUE;
666 	}
667 }
668 
onButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)669 gboolean ShareBrowser::onButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
670 {
671 	ShareBrowser *sb = (ShareBrowser *)data;
672 	sb->oldType = event->type;
673 
674 	if (event->button == 3)
675 	{
676 		GtkTreePath *path;
677 
678 		if (gtk_tree_view_get_path_at_pos(sb->fileView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
679 		{
680 			bool selected = gtk_tree_selection_path_is_selected(sb->fileSelection, path);
681 			gtk_tree_path_free(path);
682 
683 			if (selected)
684 				return TRUE;
685 		}
686 	}
687 
688 	return FALSE;
689 }
690 
onFileButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)691 gboolean ShareBrowser::onFileButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
692 {
693 	ShareBrowser *sb = (ShareBrowser *)data;
694 	gint count = gtk_tree_selection_count_selected_rows(sb->fileSelection);
695 
696 	if (count > 0 && event->type == GDK_BUTTON_RELEASE && event->button == 3)
697 		sb->popupFileMenu_gui();
698 	else if (count == 1 && sb->oldType == GDK_2BUTTON_PRESS && event->button == 1)
699 		sb->fileViewSelected_gui();
700 
701 	return FALSE;
702 }
703 
onFileKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)704 gboolean ShareBrowser::onFileKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
705 {
706 	ShareBrowser *sb = (ShareBrowser *)data;
707 	gint count = gtk_tree_selection_count_selected_rows(sb->fileSelection);
708 
709 	if (count > 0 && (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK)))
710 		sb->popupFileMenu_gui();
711 	else if (count == 1 && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter))
712 		sb->fileViewSelected_gui();
713 
714 	return FALSE;
715 }
716 
onDirButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)717 gboolean ShareBrowser::onDirButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
718 {
719 	ShareBrowser *sb = (ShareBrowser *)data;
720 	GtkTreeIter iter;
721 	gpointer ptr;
722 
723 	if (!gtk_tree_selection_get_selected(sb->dirSelection, NULL, &iter))
724 		return FALSE;
725 
726 	if (event->button == 1 && sb->oldType == GDK_2BUTTON_PRESS)
727 	{
728 		GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(sb->dirStore), &iter);
729 		if (gtk_tree_view_row_expanded(sb->dirView.get(), path))
730 			gtk_tree_view_collapse_row(sb->dirView.get(), path);
731 		else
732 			gtk_tree_view_expand_row(sb->dirView.get(), path, FALSE);
733 		gtk_tree_path_free(path);
734 	}
735 	else if (event->button == 1 && event->type == GDK_BUTTON_RELEASE)
736 	{
737 		ptr = sb->dirView.getValue<gpointer>(&iter, "DL Dir");
738 		sb->updateFiles_gui((DirectoryListing::Directory *)ptr);
739 	}
740 	else if (event->button == 3 && event->type == GDK_BUTTON_RELEASE)
741 	{
742 		ptr = sb->dirView.getValue<gpointer>(&iter, "DL Dir");
743 		sb->updateFiles_gui((DirectoryListing::Directory *)ptr);
744 		sb->popupDirMenu_gui();
745 	}
746 
747 	return FALSE;
748 }
749 
onDirKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)750 gboolean ShareBrowser::onDirKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
751 {
752 	ShareBrowser *sb = (ShareBrowser *)data;
753 	GtkTreeIter iter;
754 	gpointer ptr;
755 
756 	if (!gtk_tree_selection_get_selected(sb->dirSelection, NULL, &iter))
757 		return FALSE;
758 
759 	if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
760 	{
761 		GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(sb->dirStore), &iter);
762 		if (gtk_tree_view_row_expanded(sb->dirView.get(), path))
763 			gtk_tree_view_collapse_row(sb->dirView.get(), path);
764 		else
765 			gtk_tree_view_expand_row(sb->dirView.get(), path, FALSE);
766 		gtk_tree_path_free(path);
767 	}
768 	else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
769 		event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
770 	{
771 		ptr = sb->dirView.getValue<gpointer>(&iter, "DL Dir");
772 		sb->updateFiles_gui((DirectoryListing::Directory *)ptr);
773 	}
774 	else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
775 	{
776 		ptr = sb->dirView.getValue<gpointer>(&iter, "DL Dir");
777 		sb->updateFiles_gui((DirectoryListing::Directory *)ptr);
778 		sb->popupDirMenu_gui();
779 	}
780 
781 	return FALSE;
782 }
783 
onMatchButtonClicked_gui(GtkWidget * widget,gpointer data)784 void ShareBrowser::onMatchButtonClicked_gui(GtkWidget *widget, gpointer data)
785 {
786 	typedef Func0<ShareBrowser> F0;
787 	F0 *f0 = new F0((ShareBrowser*)data, &ShareBrowser::matchQueue_client);
788 	WulforManager::get()->dispatchClientFunc(f0);
789 }
790 
onFindButtonClicked_gui(GtkWidget * widget,gpointer data)791 void ShareBrowser::onFindButtonClicked_gui(GtkWidget *widget, gpointer data)
792 {
793 	ShareBrowser *sb = (ShareBrowser *)data;
794 	gint ret;
795 
796 	gtk_widget_grab_focus(GTK_WIDGET(sb->getWidget("findEntry")));
797 	ret = gtk_dialog_run(GTK_DIALOG(sb->getWidget("findDialog")));
798 	gtk_widget_hide(sb->getWidget("findDialog"));
799 
800 	if (ret == GTK_RESPONSE_OK)
801 	{
802 		string text = gtk_entry_get_text(GTK_ENTRY(sb->getWidget("findEntry")));
803 		if (!text.empty())
804 		{
805 			sb->search = text;
806 			sb->skipHits = 0;
807 			sb->find_gui();
808 		}
809 		else
810 		{
811 			sb->setStatus_gui("mainStatus", _("No matches"));
812 		}
813 	}
814 }
815 
onNextButtonClicked_gui(GtkWidget * widget,gpointer data)816 void ShareBrowser::onNextButtonClicked_gui(GtkWidget *widget, gpointer data)
817 {
818 	ShareBrowser *sb = (ShareBrowser *)data;
819 	if (!sb->search.empty())
820 		sb->find_gui();
821 	else
822 		sb->setStatus_gui("mainStatus", _("No search text entered"));
823 }
824 
onDownloadClicked_gui(GtkMenuItem * item,gpointer data)825 void ShareBrowser::onDownloadClicked_gui(GtkMenuItem *item, gpointer data)
826 {
827 	ShareBrowser *sb = (ShareBrowser *)data;
828 	sb->downloadSelectedFiles_gui(Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)));
829 }
830 
onDownloadToClicked_gui(GtkMenuItem * item,gpointer data)831 void ShareBrowser::onDownloadToClicked_gui(GtkMenuItem *item, gpointer data)
832 {
833 	ShareBrowser *sb = (ShareBrowser *)data;
834 
835 	gint response = gtk_dialog_run(GTK_DIALOG(sb->getWidget("dirChooserDialog")));
836 	gtk_widget_hide(sb->getWidget("dirChooserDialog"));
837 
838 	if (response == GTK_RESPONSE_OK)
839 	{
840 		gchar *temp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(sb->getWidget("dirChooserDialog")));
841 		if (temp)
842 		{
843 			string path = Text::toUtf8(temp);
844 			g_free(temp);
845 			if (path[path.length() - 1] != PATH_SEPARATOR)
846 				path += PATH_SEPARATOR;
847 
848 			sb->downloadSelectedFiles_gui(path);
849 		}
850 	}
851 }
852 
onDownloadFavoriteClicked_gui(GtkMenuItem * item,gpointer data)853 void ShareBrowser::onDownloadFavoriteClicked_gui(GtkMenuItem *item, gpointer data)
854 {
855 	ShareBrowser *sb = (ShareBrowser *)data;
856 	string target = string((gchar *)g_object_get_data(G_OBJECT(item), "fav"));
857 	sb->downloadSelectedFiles_gui(target);
858 }
859 
onDownloadDirClicked_gui(GtkMenuItem * item,gpointer data)860 void ShareBrowser::onDownloadDirClicked_gui(GtkMenuItem *item, gpointer data)
861 {
862 	ShareBrowser *sb = (ShareBrowser *)data;
863 	sb->downloadSelectedDirs_gui(Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)));
864 }
865 
onDownloadDirToClicked_gui(GtkMenuItem * item,gpointer data)866 void ShareBrowser::onDownloadDirToClicked_gui(GtkMenuItem *item, gpointer data)
867 {
868 	ShareBrowser *sb = (ShareBrowser *)data;
869 
870 	gint response = gtk_dialog_run(GTK_DIALOG(sb->getWidget("dirChooserDialog")));
871 	gtk_widget_hide(sb->getWidget("dirChooserDialog"));
872 
873 	if (response == GTK_RESPONSE_OK)
874 	{
875 		gchar *temp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(sb->getWidget("dirChooserDialog")));
876 		if (temp)
877 		{
878 			string path = Text::toUtf8(temp);
879 			g_free(temp);
880 			if (path[path.length() - 1] != PATH_SEPARATOR)
881 				path += PATH_SEPARATOR;
882 
883 			sb->downloadSelectedDirs_gui(path);
884 		}
885 	}
886 }
887 
onDownloadFavoriteDirClicked_gui(GtkMenuItem * item,gpointer data)888 void ShareBrowser::onDownloadFavoriteDirClicked_gui(GtkMenuItem *item, gpointer data)
889 {
890 	ShareBrowser *sb = (ShareBrowser *)data;
891 	string target = string((gchar *)g_object_get_data(G_OBJECT(item), "fav"));
892 	sb->downloadSelectedDirs_gui(target);
893 }
894 
onSearchAlternatesClicked_gui(GtkMenuItem * item,gpointer data)895 void ShareBrowser::onSearchAlternatesClicked_gui(GtkMenuItem *item, gpointer data)
896 {
897 	ShareBrowser *sb = (ShareBrowser *)data;
898 	GtkTreeIter iter;
899 	GtkTreePath *path;
900 	string fileOrder;
901 	Search *s;
902 	DirectoryListing::File *file;
903 	GList *list = gtk_tree_selection_get_selected_rows(sb->fileSelection, NULL);
904 
905 	for (GList *i = list; i; i = i->next)
906 	{
907 		path = (GtkTreePath *)i->data;
908 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(sb->fileStore), &iter, path))
909 		{
910 			fileOrder = sb->fileView.getString(&iter, "File Order");
911 
912 			if (fileOrder[0] == 'f')
913 			{
914 				file = sb->fileView.getValue<gpointer, DirectoryListing::File *>(&iter, "DL File");
915 				s = WulforManager::get()->getMainWindow()->addSearch_gui();
916 				s->putValue_gui(file->getTTH().toBase32(), 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
917 			}
918 		}
919 		gtk_tree_path_free(path);
920 	}
921 	g_list_free(list);
922 }
923 
onCopyMagnetClicked_gui(GtkMenuItem * item,gpointer data)924 void ShareBrowser::onCopyMagnetClicked_gui(GtkMenuItem* item, gpointer data)
925 {
926 	ShareBrowser *sb = (ShareBrowser *)data;
927 	GtkTreeIter iter;
928 	GtkTreePath *path;
929 	int64_t size;
930 	string magnets, magnet, filename, tth;
931 	GList *list = gtk_tree_selection_get_selected_rows(sb->fileSelection, NULL);
932 
933 	for (GList *i = list; i; i = i->next)
934 	{
935 		path = (GtkTreePath *)i->data;
936 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(sb->fileStore), &iter, path))
937 		{
938 			filename = sb->fileView.getString(&iter, "Filename");
939 			size = sb->fileView.getValue<int64_t>(&iter, "Size Order");
940 			tth = sb->fileView.getString(&iter, "TTH");
941 			magnet = WulforUtil::makeMagnet(filename, size, tth);
942 
943 			if (!magnet.empty())
944 			{
945 				if (!magnets.empty())
946 					magnets += '\n';
947 				magnets += magnet;
948 			}
949 		}
950 		gtk_tree_path_free(path);
951 	}
952 	g_list_free(list);
953 
954 	if (!magnets.empty())
955 		gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), magnets.c_str(), magnets.length());
956 }
957 
downloadFile_client(DirectoryListing::File * file,string target)958 void ShareBrowser::downloadFile_client(DirectoryListing::File *file, string target)
959 {
960 	try
961 	{
962 		listing.download(file, target, FALSE, FALSE);
963 	}
964 	catch (const Exception& e)
965 	{
966 		typedef Func2<ShareBrowser, string, string> F2;
967 		F2 *func = new F2(this, &ShareBrowser::setStatus_gui, "mainStatus", e.getError());
968 		WulforManager::get()->dispatchGuiFunc(func);
969 	}
970 }
971 
downloadDir_client(DirectoryListing::Directory * dir,string target)972 void ShareBrowser::downloadDir_client(DirectoryListing::Directory *dir, string target)
973 {
974 	try
975 	{
976 		listing.download(dir, target, FALSE);
977 	}
978 	catch (const Exception& e)
979 	{
980 		typedef Func2<ShareBrowser, string, string> F2;
981 		F2 *func = new F2(this, &ShareBrowser::setStatus_gui, "mainStatus", e.getError());
982 		WulforManager::get()->dispatchGuiFunc(func);
983 	}
984 }
985 
matchQueue_client()986 void ShareBrowser::matchQueue_client()
987 {
988 	int matched = QueueManager::getInstance()->matchListing(listing, "");
989 	string message = P_("Matched %1% file", "Matched %1% files", % matched, matched);
990 
991 	typedef Func2<ShareBrowser, string, string> F2;
992 	F2 *f = new F2(this, &ShareBrowser::setStatus_gui, "mainStatus", message);
993 	WulforManager::get()->dispatchGuiFunc(f);
994 }
995 
996