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 ¤t = 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