1 /*
2 * Copyright © 2004-2010 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 "search.hh"
23 #include <dcpp/FavoriteManager.h>
24 #include <dcpp/QueueManager.h>
25 #include <dcpp/ShareManager.h>
26 #include <dcpp/StringTokenizer.h>
27 #include <dcpp/Text.h>
28 #include <dcpp/UserCommand.h>
29 #include "UserCommandMenu.hh"
30 #include "wulformanager.hh"
31 #include "settingsmanager.hh"
32 #include "WulforUtil.hh"
33
34 using namespace std;
35 using namespace dcpp;
36
37 GtkTreeModel* Search::searchEntriesModel = NULL;
38
Search()39 Search::Search():
40 BookEntry(Entry::SEARCH, _("Search"), "search.ui", generateID()),
41 previousGrouping(NOGROUPING)
42 {
43 #if GTK_CHECK_VERSION(3,0,0)
44 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(getWidget("progressbar1")), TRUE);
45 #else
46 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusbar2")),FALSE);
47 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusbar3")),FALSE);
48 #endif
49
50 // Initialize variables for search progressbar
51 stop = true;
52 waitingResults = false;
53 searchStartTime = 0;
54 searchEndTime = 1;
55 setProgress_gui("progressbar1", "", 0.0);
56 gtk_widget_hide(getWidget("statusbox"));
57
58 // Initialize the search entries combo box
59 if(!searchEntriesModel)
60 searchEntriesModel = gtk_combo_box_get_model(GTK_COMBO_BOX(getWidget("comboboxentrySearch")));
61 gtk_combo_box_set_model(GTK_COMBO_BOX(getWidget("comboboxentrySearch")), searchEntriesModel);
62 searchEntry = gtk_bin_get_child(GTK_BIN(getWidget("comboboxentrySearch")));
63 gtk_widget_grab_focus(getWidget("comboboxentrySearch"));
64
65 // Configure the dialog
66 File::ensureDirectory(SETTING(DOWNLOAD_DIRECTORY));
67 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(getWidget("dirChooserDialog")), Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)).c_str());
68 gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("dirChooserDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
69
70 // menu
71 g_object_ref_sink(getWidget("mainMenu"));
72
73 // Initialize check button options.
74 onlyFree = BOOLSETTING(SEARCH_ONLY_FREE_SLOTS);
75 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(getWidget("checkbuttonSlots")), onlyFree);
76 gtk_widget_set_sensitive(GTK_WIDGET(getWidget("checkbuttonSlots")), FALSE);
77 gtk_widget_set_sensitive(GTK_WIDGET(getWidget("checkbuttonShared")), FALSE);
78
79 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxSize")), 1);
80 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxUnit")), 2);
81 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxFile")), 0);
82 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")), (int)TTH);
83
84 // Initialize hub list treeview
85 hubView.setView(GTK_TREE_VIEW(getWidget("treeviewHubs")));
86 hubView.insertColumn(_("Search"), G_TYPE_BOOLEAN, TreeView::BOOL, -1);
87 hubView.insertColumn(_("Name"), G_TYPE_STRING, TreeView::STRING, -1);
88 hubView.insertHiddenColumn("Url", G_TYPE_STRING);
89 hubView.finalize();
90 hubStore = gtk_list_store_newv(hubView.getColCount(), hubView.getGTypes());
91 gtk_tree_view_set_model(hubView.get(), GTK_TREE_MODEL(hubStore));
92 g_object_unref(hubStore);
93 GtkTreeViewColumn *col = gtk_tree_view_get_column(hubView.get(), hubView.col(_("Search")));
94 GList *list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
95 GtkCellRenderer *renderer = (GtkCellRenderer *)g_list_nth_data(list, 0);
96 g_list_free(list);
97
98 // Initialize search result treeview
99 resultView.setView(GTK_TREE_VIEW(getWidget("treeviewResult")), TRUE, "search");
100 resultView.insertColumn(_("Filename"), G_TYPE_STRING, TreeView::ICON_STRING, 250, "Icon");
101 resultView.insertColumn(_("Nick"), G_TYPE_STRING, TreeView::STRING, 100);
102 resultView.insertColumn(_("Type"), G_TYPE_STRING, TreeView::STRING, 65);
103 resultView.insertColumn(_("Size"), G_TYPE_STRING, TreeView::STRING, 80);
104 resultView.insertColumn(_("Path"), G_TYPE_STRING, TreeView::STRING, 100);
105 resultView.insertColumn(_("Slots"), G_TYPE_STRING, TreeView::STRING, 50);
106 resultView.insertColumn(_("Connection"), G_TYPE_STRING, TreeView::STRING, 90);
107 resultView.insertColumn(_("Hub"), G_TYPE_STRING, TreeView::STRING, 150);
108 resultView.insertColumn(_("Exact Size"), G_TYPE_STRING, TreeView::STRING, 80);
109 resultView.insertColumn(_("IP"), G_TYPE_STRING, TreeView::STRING, 100);
110 resultView.insertColumn(_("TTH"), G_TYPE_STRING, TreeView::STRING, 125);
111 resultView.insertHiddenColumn("Icon", G_TYPE_STRING);
112 resultView.insertHiddenColumn("Real Size", G_TYPE_INT64);
113 resultView.insertHiddenColumn("Slots Order", G_TYPE_INT);
114 resultView.insertHiddenColumn("File Order", G_TYPE_STRING);
115 resultView.insertHiddenColumn("Hub URL", G_TYPE_STRING);
116 resultView.insertHiddenColumn("CID", G_TYPE_STRING);
117 resultView.insertHiddenColumn("Shared", G_TYPE_BOOLEAN);
118 resultView.insertHiddenColumn("Free Slots", G_TYPE_INT);
119 resultView.finalize();
120 resultStore = gtk_tree_store_newv(resultView.getColCount(), resultView.getGTypes());
121 searchFilterModel = gtk_tree_model_filter_new(GTK_TREE_MODEL(resultStore), NULL);
122 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(searchFilterModel), &Search::searchFilterFunc_gui, (gpointer)this, NULL);
123 sortedFilterModel = gtk_tree_model_sort_new_with_model(searchFilterModel);
124 gtk_tree_view_set_model(resultView.get(), sortedFilterModel);
125 g_object_unref(resultStore);
126 g_object_unref(searchFilterModel);
127 g_object_unref(sortedFilterModel);
128 selection = gtk_tree_view_get_selection(resultView.get());
129 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
130 resultView.setSortColumn_gui(_("Size"), "Real Size");
131 resultView.setSortColumn_gui(_("Exact Size"), "Real Size");
132 resultView.setSortColumn_gui(_("Slots"), "Slots Order");
133 resultView.setSortColumn_gui(_("Filename"), "File Order");
134 gtk_tree_view_set_fixed_height_mode(resultView.get(), TRUE);
135
136 // Initialize the user command menu
137 userCommandMenu = new UserCommandMenu(getWidget("usercommandMenu"), ::UserCommand::CONTEXT_SEARCH);
138 addChild(userCommandMenu);
139
140 // Initialize search types
141 GtkTreeIter iter;
142 GtkComboBox *combo_box = GTK_COMBO_BOX(getWidget("comboboxFile"));
143 GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
144 GtkListStore *store = GTK_LIST_STORE(model);
145 const SettingsManager::SearchTypes &searchTypes = SettingsManager::getInstance()->getSearchTypes();
146
147 // Predefined
148 for (int i = SearchManager::TYPE_ANY; i < SearchManager::TYPE_LAST; i++)
149 {
150 gtk_list_store_append(store, &iter);
151 gtk_list_store_set(store, &iter, 0, SearchManager::getTypeStr(i), -1);
152 }
153
154 // Customs
155 for (SettingsManager::SearchTypesIterC i = searchTypes.begin(), iend = searchTypes.end(); i != iend; ++i)
156 {
157 string type = i->first;
158 if (!(type.size() == 1 && type[0] >= '1' && type[0] <= '7'))
159 {
160 gtk_list_store_append(store, &iter);
161 gtk_list_store_set(store, &iter, 0, type.c_str(), -1);
162 }
163 }
164 gtk_combo_box_set_active(combo_box, WGETI("last-search-type"));
165
166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(getWidget("togglebuttonSidePanel")), TRUE);
167
168 // Connect the signals to their callback functions.
169 g_signal_connect(getContainer(), "focus-in-event", G_CALLBACK(onFocusIn_gui), (gpointer)this);
170 g_signal_connect(getWidget("checkbuttonFilter"), "toggled", G_CALLBACK(onFilterButtonToggled_gui), (gpointer)this);
171 g_signal_connect(getWidget("checkbuttonSlots"), "toggled", G_CALLBACK(onSlotsButtonToggled_gui), (gpointer)this);
172 g_signal_connect(getWidget("checkbuttonShared"), "toggled", G_CALLBACK(onSharedButtonToggled_gui), (gpointer)this);
173 g_signal_connect(renderer, "toggled", G_CALLBACK(onToggledClicked_gui), (gpointer)this);
174 g_signal_connect(resultView.get(), "button-press-event", G_CALLBACK(onButtonPressed_gui), (gpointer)this);
175 g_signal_connect(resultView.get(), "button-release-event", G_CALLBACK(onButtonReleased_gui), (gpointer)this);
176 g_signal_connect(resultView.get(), "key-release-event", G_CALLBACK(onKeyReleased_gui), (gpointer)this);
177 g_signal_connect(searchEntry, "key-press-event", G_CALLBACK(onSearchEntryKeyPressed_gui), (gpointer)this);
178 g_signal_connect(searchEntry, "key-release-event", G_CALLBACK(onKeyReleased_gui), (gpointer)this);
179 g_signal_connect(getWidget("entrySize"), "key-press-event", G_CALLBACK(onSearchEntryKeyPressed_gui), (gpointer)this);
180 g_signal_connect(getWidget("entrySize"), "key-release-event", G_CALLBACK(onKeyReleased_gui), (gpointer)this);
181 g_signal_connect(getWidget("buttonSearch"), "clicked", G_CALLBACK(onSearchButtonClicked_gui), (gpointer)this);
182 g_signal_connect(getWidget("downloadItem"), "activate", G_CALLBACK(onDownloadClicked_gui), (gpointer)this);
183 g_signal_connect(getWidget("downloadWholeDirItem"), "activate", G_CALLBACK(onDownloadDirClicked_gui), (gpointer)this);
184 g_signal_connect(getWidget("searchByTTHItem"), "activate", G_CALLBACK(onSearchByTTHClicked_gui), (gpointer)this);
185 g_signal_connect(getWidget("copyMagnetItem"), "activate", G_CALLBACK(onCopyMagnetClicked_gui), (gpointer)this);
186 g_signal_connect(getWidget("getFileListItem"), "activate", G_CALLBACK(onGetFileListClicked_gui), (gpointer)this);
187 g_signal_connect(getWidget("openPartial"), "activate", G_CALLBACK(onPartialFileListOpen_gui), (gpointer)this);
188 g_signal_connect(getWidget("matchQueueItem"), "activate", G_CALLBACK(onMatchQueueClicked_gui), (gpointer)this);
189 g_signal_connect(getWidget("sendPrivateMessageItem"), "activate", G_CALLBACK(onPrivateMessageClicked_gui), (gpointer)this);
190 g_signal_connect(getWidget("addToFavoritesItem"), "activate", G_CALLBACK(onAddFavoriteUserClicked_gui), (gpointer)this);
191 g_signal_connect(getWidget("grantExtraSlotItem"), "activate", G_CALLBACK(onGrantExtraSlotClicked_gui), (gpointer)this);
192 g_signal_connect(getWidget("removeUserFromQueueItem"), "activate", G_CALLBACK(onRemoveUserFromQueueClicked_gui), (gpointer)this);
193 g_signal_connect(getWidget("removeItem"), "activate", G_CALLBACK(onRemoveClicked_gui), (gpointer)this);
194 g_signal_connect(getWidget("comboboxSize"), "changed", G_CALLBACK(onComboBoxChanged_gui), (gpointer)this);
195 g_signal_connect(getWidget("comboboxentrySearch"), "changed", G_CALLBACK(onComboBoxChanged_gui), (gpointer)this);
196 g_signal_connect(getWidget("comboboxUnit"), "changed", G_CALLBACK(onComboBoxChanged_gui), (gpointer)this);
197 g_signal_connect(getWidget("comboboxFile"), "changed", G_CALLBACK(onComboBoxChanged_gui), (gpointer)this);
198 g_signal_connect(getWidget("comboboxGroupBy"), "changed", G_CALLBACK(onGroupByComboBoxChanged_gui), (gpointer)this);
199 g_signal_connect(getWidget("togglebuttonSidePanel"), "toggled", G_CALLBACK(onSidePanelToggled_gui), (gpointer)this);
200 g_signal_connect(getWidget("buttonClear"), "clicked", G_CALLBACK(onClearButtonClicked_gui), (gpointer)this);
201 }
202
~Search()203 Search::~Search()
204 {
205 ClientManager::getInstance()->removeListener(this);
206 SearchManager::getInstance()->removeListener(this);
207 TimerManager::getInstance()->removeListener(this);
208
209 gtk_widget_destroy(getWidget("dirChooserDialog"));
210 g_object_unref(getWidget("mainMenu"));
211 }
212
show()213 void Search::show()
214 {
215 initHubs_gui();
216 ClientManager::getInstance()->addListener(this);
217 SearchManager::getInstance()->addListener(this);
218 TimerManager::getInstance()->addListener(this);
219 }
220
putValue_gui(const string & str,int64_t size,SearchManager::SizeModes mode,SearchManager::TypeModes type)221 void Search::putValue_gui(const string &str, int64_t size, SearchManager::SizeModes mode, SearchManager::TypeModes type)
222 {
223 gtk_entry_set_text(GTK_ENTRY(searchEntry), str.c_str());
224 gtk_entry_set_text(GTK_ENTRY(getWidget("entrySize")), Util::toString(size).c_str());
225 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxSize")), (int)mode);
226 gtk_combo_box_set_active(GTK_COMBO_BOX(getWidget("comboboxFile")), (int)type);
227
228 search_gui();
229 }
230
initHubs_gui()231 void Search::initHubs_gui()
232 {
233 #ifdef DO_NOT_USE_MUTEX
234 ClientManager::getInstance()->lock();
235 #else // DO_NOT_USE_MUTEX
236 auto lock = ClientManager::getInstance()->lock();
237 #endif // DO_NOT_USE_MUTEX
238
239 Client::List& clients = ClientManager::getInstance()->getClients();
240
241 Client *client = NULL;
242 for (auto it = clients.begin(); it != clients.end(); ++it)
243 {
244 client = *it;
245 if (client->isConnected())
246 addHub_gui(client->getHubName(), client->getHubUrl());
247 }
248
249 #ifdef DO_NOT_USE_MUTEX
250 ClientManager::getInstance()->unlock();
251 #endif // DO_NOT_USE_MUTEX
252 }
253
addHub_gui(string name,string url)254 void Search::addHub_gui(string name, string url)
255 {
256 GtkTreeIter iter;
257 gtk_list_store_append(hubStore, &iter);
258 gtk_list_store_set(hubStore, &iter,
259 hubView.col(_("Search")), TRUE,
260 hubView.col(_("Name")), name.empty() ? url.c_str() : name.c_str(),
261 hubView.col("Url"), url.c_str(),
262 -1);
263 }
264
modifyHub_gui(string name,string url)265 void Search::modifyHub_gui(string name, string url)
266 {
267 GtkTreeIter iter;
268 GtkTreeModel *m = GTK_TREE_MODEL(hubStore);
269 gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
270
271 while (valid)
272 {
273 if (url == hubView.getString(&iter, "Url"))
274 {
275 gtk_list_store_set(hubStore, &iter,
276 hubView.col(_("Name")), name.empty() ? url.c_str() : name.c_str(),
277 hubView.col("Url"), url.c_str(),
278 -1);
279 return;
280 }
281 valid = gtk_tree_model_iter_next(m, &iter);
282 }
283 }
284
removeHub_gui(string url)285 void Search::removeHub_gui(string url)
286 {
287 GtkTreeIter iter;
288 GtkTreeModel *m = GTK_TREE_MODEL(hubStore);
289 gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
290
291 while (valid)
292 {
293 if (url == hubView.getString(&iter, "Url"))
294 {
295 gtk_list_store_remove(hubStore, &iter);
296 return;
297 }
298 valid = gtk_tree_model_iter_next(m, &iter);
299 }
300 }
301
popupMenu_gui()302 void Search::popupMenu_gui()
303 {
304 GtkTreeIter iter;
305 GtkTreePath *path;
306 GList *list = gtk_tree_selection_get_selected_rows(selection, NULL);
307 guint count = g_list_length(list);
308
309 if (count == 1)
310 {
311 path = (GtkTreePath*)list->data; // This will be freed later
312
313 // If it is a parent effectively more than one row is selected
314 if (gtk_tree_model_get_iter(sortedFilterModel, &iter, path) &&
315 gtk_tree_model_iter_has_child(sortedFilterModel, &iter))
316 {
317 gtk_widget_set_sensitive(getWidget("searchByTTHItem"), FALSE);
318 }
319 else
320 {
321 gtk_widget_set_sensitive(getWidget("searchByTTHItem"), TRUE);
322 }
323 }
324 else if (count > 1)
325 {
326 gtk_widget_set_sensitive(getWidget("searchByTTHItem"), FALSE);
327 }
328
329 GtkWidget *menuItem;
330 string tth;
331 bool firstTTH;
332 bool hasTTH;
333
334 // Clean menus
335 gtk_container_foreach(GTK_CONTAINER(getWidget("downloadMenu")), (GtkCallback)gtk_widget_destroy, NULL);
336 gtk_container_foreach(GTK_CONTAINER(getWidget("downloadDirMenu")), (GtkCallback)gtk_widget_destroy, NULL);
337 userCommandMenu->cleanMenu_gui();
338
339 // Build "Download to..." submenu
340
341 // Add favorite download directories
342 StringPairList spl = FavoriteManager::getInstance()->getFavoriteDirs();
343 if (!spl.empty())
344 {
345 for (StringPairIter i = spl.begin(); i != spl.end(); ++i)
346 {
347 menuItem = gtk_menu_item_new_with_label(i->second.c_str());
348 g_object_set_data_full(G_OBJECT(menuItem), "fav", g_strdup(i->first.c_str()), g_free);
349 g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadFavoriteClicked_gui), (gpointer)this);
350 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadMenu")), menuItem);
351 }
352 menuItem = gtk_separator_menu_item_new();
353 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadMenu")), menuItem);
354 }
355
356 // Add Browse item
357 menuItem = gtk_menu_item_new_with_label(_("Browse..."));
358 g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadToClicked_gui), (gpointer)this);
359 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadMenu")), menuItem);
360
361 // Add search results with the same TTH to menu
362 firstTTH = TRUE;
363 hasTTH = FALSE;
364
365 for (GList *i = list; i; i = i->next)
366 {
367 path = (GtkTreePath *)i->data;
368 if (gtk_tree_model_get_iter(sortedFilterModel, &iter, path))
369 {
370 userCommandMenu->addHub(resultView.getString(&iter, "Hub URL"));
371 userCommandMenu->addFile(resultView.getString(&iter, "CID"),
372 resultView.getString(&iter, _("Filename")),
373 resultView.getString(&iter, _("Path")),
374 resultView.getValue<int64_t>(&iter, "Real Size"),
375 resultView.getString(&iter, _("TTH")));
376
377 if (firstTTH)
378 {
379 tth = resultView.getString(&iter, _("TTH"));
380 firstTTH = FALSE;
381 hasTTH = TRUE;
382 }
383 else if (hasTTH)
384 {
385 if (tth.empty() || tth != resultView.getString(&iter, _("TTH")))
386 hasTTH = FALSE; // Can't break here since we have to free all the paths
387 }
388 }
389 gtk_tree_path_free(path);
390 }
391 g_list_free(list);
392
393 if (hasTTH)
394 {
395 StringList targets;
396 QueueManager::getInstance()->getTargets(TTHValue(tth), targets);
397
398 if (!targets.empty())
399 {
400 menuItem = gtk_separator_menu_item_new();
401 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadMenu")), menuItem);
402 for (StringIter i = targets.begin(); i != targets.end(); ++i)
403 {
404 menuItem = gtk_menu_item_new_with_label(i->c_str());
405 g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadToMatchClicked_gui), (gpointer)this);
406 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadMenu")), menuItem);
407 }
408 }
409 }
410
411 // Build "Download whole directory to..." submenu
412
413 spl.clear();
414 spl = FavoriteManager::getInstance()->getFavoriteDirs();
415 if (!spl.empty())
416 {
417 for (StringPairIter i = spl.begin(); i != spl.end(); ++i)
418 {
419 menuItem = gtk_menu_item_new_with_label(i->second.c_str());
420 g_object_set_data_full(G_OBJECT(menuItem), "fav", g_strdup(i->first.c_str()), g_free);
421 g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadFavoriteDirClicked_gui), (gpointer)this);
422 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadDirMenu")), menuItem);
423 }
424 menuItem = gtk_separator_menu_item_new();
425 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadDirMenu")), menuItem);
426 }
427
428 menuItem = gtk_menu_item_new_with_label(_("Browse..."));
429 g_signal_connect(menuItem, "activate", G_CALLBACK(onDownloadDirToClicked_gui), (gpointer)this);
430 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("downloadDirMenu")), menuItem);
431
432 // Build user command menu
433 userCommandMenu->buildMenu_gui();
434
435 gtk_menu_popup(GTK_MENU(getWidget("mainMenu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
436 gtk_widget_show_all(getWidget("mainMenu"));
437 }
438
setStatus_gui(string statusBar,string text)439 void Search::setStatus_gui(string statusBar, string text)
440 {
441 gtk_statusbar_pop(GTK_STATUSBAR(getWidget(statusBar)), 0);
442 gtk_statusbar_push(GTK_STATUSBAR(getWidget(statusBar)), 0, text.c_str());
443 }
444
setProgress_gui(const std::string & progressBar,const std::string & text,float fract)445 void Search::setProgress_gui(const std::string& progressBar, const std::string& text, float fract)
446 {
447 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(getWidget(progressBar)), text.c_str());
448 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(getWidget(progressBar)), fract);
449 }
450
search_gui()451 void Search::search_gui()
452 {
453 if (!stop)
454 {
455 stop = true;
456 waitingResults = false;
457 gtk_button_set_label(GTK_BUTTON(getWidget("buttonSearch")), _("Search"));
458 return;
459 }
460
461 StringList clients;
462 GtkTreeIter iter;
463
464 string text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(getWidget("comboboxentrySearch")));
465 if (text.empty())
466 return;
467
468 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(hubStore), &iter);
469 while (valid)
470 {
471 if (hubView.getValue<gboolean>(&iter, _("Search")))
472 clients.push_back(hubView.getString(&iter, "Url"));
473 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(hubStore), &iter);
474 }
475
476 #ifndef WITH_DHT
477 if (clients.size() < 1)
478 return;
479 #endif
480
481 double lsize = Util::toDouble(gtk_entry_get_text(GTK_ENTRY(getWidget("entrySize"))));
482
483 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxUnit"))))
484 {
485 case 1:
486 lsize *= 1024.0;
487 break;
488 case 2:
489 lsize *= 1024.0 * 1024.0;
490 break;
491 case 3:
492 lsize *= 1024.0 * 1024.0 * 1024.0;
493 break;
494 }
495
496 gtk_tree_store_clear(resultStore);
497 results.clear();
498 int64_t llsize = static_cast<int64_t>(lsize);
499 searchlist = StringTokenizer<string>(text, ' ').getTokens();
500
501 // Strip out terms beginning with -
502 text.clear();
503 for (auto si = searchlist.begin(); si != searchlist.end(); ++si)
504 if ((*si)[0] != '-')
505 text += *si + ' ';
506 text = text.substr(0, std::max(text.size(), static_cast<string::size_type>(1)) - 1);
507
508 SearchManager::SizeModes mode((SearchManager::SizeModes)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxSize"))));
509 if (!llsize)
510 mode = SearchManager::SIZE_DONTCARE;
511
512 int ftype = gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxFile")));
513
514 string ftypeStr;
515 if (ftype > SearchManager::TYPE_ANY && ftype < SearchManager::TYPE_LAST)
516 {
517 ftypeStr = SearchManager::getInstance()->getTypeStr(ftype);
518 }
519 else
520 {
521 ftypeStr = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(getWidget("comboboxFile")));
522 ftype = SearchManager::TYPE_ANY;
523 }
524
525 // Get ADC searchtype extensions if any is selected
526 StringList exts;
527 try
528 {
529 if (ftype == SearchManager::TYPE_ANY)
530 {
531 // Custom searchtype
532 exts = SettingsManager::getInstance()->getExtensions(ftypeStr);
533 }
534 else if ((ftype > SearchManager::TYPE_ANY && ftype < SearchManager::TYPE_DIRECTORY) || ftype == SearchManager::TYPE_CD_IMAGE)
535 {
536 // Predefined searchtype
537 exts = SettingsManager::getInstance()->getExtensions(string(1, '0' + ftype));
538 }
539 }
540 catch (const SearchTypeException&)
541 {
542 ftype = SearchManager::TYPE_ANY;
543 }
544
545 isHash = (ftype == SearchManager::TYPE_TTH);
546
547 if (!isHash)
548 WSET("last-search-type", (int)ftype);
549
550
551 // Add new searches to the dropdown list
552 GtkListStore *store = GTK_LIST_STORE(searchEntriesModel);
553 size_t max = std::max(SETTING(SEARCH_HISTORY) - 1, 0);
554 size_t count = 0;
555 gchar *entry;
556 valid = gtk_tree_model_get_iter_first(searchEntriesModel, &iter);
557 while (valid)
558 {
559 gtk_tree_model_get(searchEntriesModel, &iter, 0, &entry, -1);
560 if (text == string(entry) || count >= max)
561 valid = gtk_list_store_remove(store, &iter);
562 else
563 valid = gtk_tree_model_iter_next(searchEntriesModel, &iter);
564 count++;
565 g_free(entry);
566 }
567
568 gtk_list_store_prepend(store, &iter);
569 gtk_list_store_set(store, &iter, 0, text.c_str(), -1);
570
571 gtk_widget_show_all(getWidget("statusbox"));
572 droppedResult = 0;
573 searchHits = 0;
574 setStatus_gui("statusbar2", _("Found: 0"));
575 setStatus_gui("statusbar3", _("Filtered: 0"));
576 setLabel_gui(text);
577
578 searchStartTime = GET_TICK();
579 target = text;
580
581 dcdebug(_("Sent ADC extensions : %s\n"), Util::toString(";", exts).c_str());
582 uint64_t maxDelayBeforeSearch = SearchManager::getInstance()->search(clients, text, llsize, (SearchManager::TypeModes)ftype, mode, "manual", exts, (void*)this);
583 uint64_t waitingResultsTime = 20000; // just assumption that user receives most of results in 20 seconds
584
585 searchEndTime = searchStartTime + maxDelayBeforeSearch + waitingResultsTime;
586 waitingResults = true;
587 stop = false;
588
589 if (WGETB("clearsearch")) // Only clear if the search was sent.
590 gtk_entry_set_text(GTK_ENTRY(searchEntry), "");
591
592 if (gtk_widget_get_visible(getWidget("sidePanel")) &&
593 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(getWidget("checkDontHideSideOnSearch"))))
594 gtk_widget_hide(getWidget("sidePanel"));
595
596 gtk_widget_set_sensitive(getWidget("comboboxentrySearch"), FALSE);
597 gtk_button_set_label(GTK_BUTTON(getWidget("buttonSearch")), _("Stop"));
598 }
599
addResult_gui(const SearchResultPtr result)600 void Search::addResult_gui(const SearchResultPtr result)
601 {
602 // Check that it's not a duplicate and find parent for grouping
603 GtkTreeIter iter;
604 GtkTreeIter parent;
605 GtkTreeModel *m = GTK_TREE_MODEL(resultStore);
606 bool foundParent = FALSE;
607
608 vector<SearchResultPtr> &existingResults = results[result->getUser()->getCID().toBase32()];
609 for (auto it = existingResults.begin(); it != existingResults.end(); ++it)
610 {
611 // Check if it's a duplicate
612 if (result->getFile() == (*it)->getFile())
613 return;
614 }
615 existingResults.push_back(result);
616
617 dcpp::StringMap resultMap;
618 parseSearchResult_gui(result, resultMap);
619
620 // Find grouping parent
621 GroupType groupBy = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")));
622 string groupColumn = getGroupingColumn(groupBy);
623 string groupStr = resultMap[groupColumn];
624 gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
625
626 while (valid && groupBy != NOGROUPING && !foundParent && !groupStr.empty())
627 {
628 // Found a row which matches the grouping criteria
629 if (resultView.getString(&iter, groupColumn, m) == groupStr)
630 {
631 // Group the row under the existing parent row
632 if (gtk_tree_model_iter_has_child(m, &iter))
633 {
634 parent = iter;
635 }
636 // If two rows that match the grouping criteria
637 // are found, group them under a new parent row
638 else
639 {
640 parent = createParentRow_gui(&iter, groupStr);
641 }
642 foundParent = TRUE;
643 }
644
645 valid = WulforUtil::getNextIter_gui(m, &iter);
646 }
647
648 // Have to use insert with values since appending would cause searchFilterFunc to be
649 // called with empty row which in turn will cause assert failure in treeview::getString
650 gtk_tree_store_insert_with_values(resultStore, &iter, foundParent ? &parent : NULL, -1,
651 resultView.col(_("Nick")), resultMap["Nick"].c_str(),
652 resultView.col(_("Filename")), resultMap["Filename"].c_str(),
653 resultView.col(_("Slots")), resultMap["Slots"].c_str(),
654 resultView.col(_("Size")), resultMap["Size"].c_str(),
655 resultView.col(_("Path")), resultMap["Path"].c_str(),
656 resultView.col(_("Type")), resultMap["Type"].c_str(),
657 resultView.col(_("Connection")), resultMap["Connection"].c_str(),
658 resultView.col(_("Hub")), resultMap["Hub"].c_str(),
659 resultView.col(_("Exact Size")), resultMap["Exact Size"].c_str(),
660 resultView.col(_("IP")), resultMap["IP"].c_str(),
661 resultView.col(_("TTH")), resultMap["TTH"].c_str(),
662 resultView.col("Icon"), resultMap["Icon"].c_str(),
663 resultView.col("File Order"), resultMap["File Order"].c_str(),
664 resultView.col("Real Size"), Util::toInt64(resultMap["Real Size"]),
665 resultView.col("Slots Order"), Util::toInt(resultMap["Slots Order"]),
666 resultView.col("Hub URL"), resultMap["Hub URL"].c_str(),
667 resultView.col("CID"), resultMap["CID"].c_str(),
668 resultView.col("Shared"), Util::toInt(resultMap["Shared"]),
669 resultView.col("Free Slots"), Util::toInt(resultMap["Free Slots"]),
670 -1);
671
672 if (foundParent)
673 updateParentRow_gui(&parent, &iter);
674
675 ++searchHits;
676 setStatus_gui("statusbar2", _("Found: ") + Util::toString(searchHits));
677
678 if (WGETB("bold-search"))
679 setBold_gui();
680 }
681
682 /*
683 * Move top level row to be under a newly created grouping parent.
684 */
createParentRow_gui(GtkTreeIter * child,const string & groupStr,gint position)685 GtkTreeIter Search::createParentRow_gui(GtkTreeIter *child, const string &groupStr, gint position /* = -1 */)
686 {
687 GtkTreeIter parent;
688 GroupType groupBy = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")));
689 string filename = groupStr;
690
691 // As a special case, use the first child's filename for TTH grouping
692 if (groupBy == TTH)
693 filename = resultView.getString(child, _("Filename"), GTK_TREE_MODEL(resultStore));
694
695 // Insert the new parent row
696 gtk_tree_store_insert_with_values(resultStore, &parent, NULL, position,
697 resultView.col("Icon"), GTK_STOCK_DND_MULTIPLE,
698 resultView.col(_("Filename")), filename.c_str(),
699 -1);
700
701 // Move the row to be a child of the new parent
702 GtkTreeIter newChild = WulforUtil::copyRow_gui(resultStore, child, &parent);
703 gtk_tree_store_remove(resultStore, child);
704 *child = newChild;
705
706 return parent;
707 }
708
709
updateParentRow_gui(GtkTreeIter * parent,GtkTreeIter * child)710 void Search::updateParentRow_gui(GtkTreeIter *parent, GtkTreeIter *child)
711 {
712 // Let's make sure we really have children...
713 gint children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(resultStore), parent);
714 dcassert(children != 0);
715
716 string users = Util::toString(children) + _(" user(s)");
717 gtk_tree_store_set(resultStore, parent, resultView.col(_("Nick")), users.c_str(), -1);
718
719 if (!child)
720 return;
721
722 GroupType groupType = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")));
723
724 switch (groupType)
725 {
726 case NOGROUPING:
727 break;
728 case FILENAME:
729 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col("File Order"));
730 break;
731 case FILEPATH:
732 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Path")));
733 break;
734 case SIZE:
735 {
736 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Exact Size")));
737 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Size")));
738 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col("Real Size"));
739 break;
740 }
741 case CONNECTION:
742 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Connection")));
743 break;
744 case TTH:
745 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Exact Size")));
746 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Size")));
747 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col("Real Size"));
748 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("TTH")));
749 break;
750 case NICK:
751 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Nick")));
752 break;
753 case HUB:
754 {
755 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Hub")));
756 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col("Hub URL"));
757 break;
758 }
759 case TYPE:
760 WulforUtil::copyValue_gui(resultStore, child, parent, resultView.col(_("Type")));;
761 break;
762 default:
763 ///@todo: throw an exception
764 break;
765 }
766 }
767
ungroup_gui()768 void Search::ungroup_gui()
769 {
770 GtkTreeIter iter;
771 gint position = 0;
772 GtkTreeModel *m = GTK_TREE_MODEL(resultStore);
773 gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
774
775 while (valid)
776 {
777 // Ungroup parent rows and remove them
778 if (gtk_tree_model_iter_has_child(m, &iter))
779 {
780 GtkTreeIter child = iter;
781 valid = WulforUtil::getNextIter_gui(m, &child, TRUE, FALSE);
782
783 // Move all children out from under the old grouping parent
784 while (valid)
785 {
786 WulforUtil::copyRow_gui(resultStore, &child, NULL, position++);
787 valid = gtk_tree_store_remove(resultStore, &child);
788 }
789
790 // Delete the parent row
791 valid = gtk_tree_store_remove(resultStore, &iter);
792 }
793 else // Non-parent row
794 {
795 ++position;
796 valid = WulforUtil::getNextIter_gui(m, &iter);
797 }
798 }
799 }
800
regroup_gui()801 void Search::regroup_gui()
802 {
803 unordered_map<string, GtkTreeIter> iterMap; // Maps group string -> parent tree iter
804 GtkTreeIter iter;
805 gint position = 0;
806 GtkTreeModel *m = GTK_TREE_MODEL(resultStore);
807 GroupType groupBy = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")));
808 string groupColumn = getGroupingColumn(groupBy);
809 gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
810
811 while (valid)
812 {
813 string groupStr;
814 if (!groupColumn.empty())
815 groupStr = resultView.getString(&iter, groupColumn, m);
816
817 // Don't add parent rows
818 if (gtk_tree_model_iter_has_child(m, &iter) || groupStr.empty())
819 {
820 ++position;
821 valid = WulforUtil::getNextIter_gui(m, &iter);
822 continue;
823 }
824
825 auto mapIter = iterMap.find(groupStr);
826
827 // New non-parent, top-level item
828 if (mapIter == iterMap.end())
829 {
830 ++position;
831 iterMap[groupStr] = iter;
832 valid = WulforUtil::getNextIter_gui(m, &iter);
833 }
834 else // Insert as a child under the grouping parent
835 {
836 GtkTreeIter parent = mapIter->second;
837
838 // If this is the first child to be appended, create a new parent row.
839 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(resultStore), &parent))
840 {
841 GtkTreeIter child = parent;
842 parent = createParentRow_gui(&child, groupStr, position);
843 mapIter->second = parent;
844 }
845
846 // Insert the row as a child
847 GtkTreeIter child = WulforUtil::copyRow_gui(resultStore, &iter, &parent);
848 valid = gtk_tree_store_remove(resultStore, &iter);
849 updateParentRow_gui(&parent,&child);
850 }
851 }
852 }
853
854 /*
855 * We can't rely on the string from the text box since it will be internationalized.
856 */
getGroupingColumn(GroupType groupBy)857 string Search::getGroupingColumn(GroupType groupBy)
858 {
859 string column;
860
861 switch (groupBy)
862 {
863 case Search::NOGROUPING:
864 break;
865 case Search::FILENAME:
866 column = _("Filename");
867 break;
868 case Search::FILEPATH:
869 column = _("Path");
870 break;
871 case Search::SIZE:
872 column = _("Size");
873 break;
874 case Search::CONNECTION:
875 column = _("Connection");
876 break;
877 case Search::TTH:
878 column = _("TTH");
879 break;
880 case Search::NICK:
881 column = _("Nick");
882 break;
883 case Search::HUB:
884 column = _("Hub");
885 break;
886 case Search::TYPE:
887 column = _("Type");
888 break;
889 default:
890 ///@todo: throw an exception
891 break;
892 }
893
894 return column;
895 }
896
download_gui(const string & target)897 void Search::download_gui(const string &target)
898 {
899 if (target.empty() || gtk_tree_selection_count_selected_rows(selection) <= 0)
900 return;
901
902 GtkTreeIter iter;
903 GtkTreePath *path;
904 GroupType groupBy = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(getWidget("comboboxGroupBy")));
905 GList *list = gtk_tree_selection_get_selected_rows(selection, NULL);
906 typedef Func6<Search, string, string, string, int64_t, string, string> F6;
907
908 for (GList *i = list; i; i = i->next)
909 {
910 path = (GtkTreePath *)i->data;
911 if (gtk_tree_model_get_iter(sortedFilterModel, &iter, path))
912 {
913 bool parent = gtk_tree_model_iter_has_child(sortedFilterModel, &iter);
914 string filename = resultView.getString(&iter, _("Filename"));
915
916 do
917 {
918 if (!gtk_tree_model_iter_has_child(sortedFilterModel, &iter))
919 {
920 // User parent filename when grouping by TTH to avoid downloading the same file multiple times
921 if (groupBy != TTH || resultView.getString(&iter, _("Type")) == _("Directory"))
922 {
923 filename = resultView.getString(&iter, _("Path"));
924 filename += resultView.getString(&iter, _("Filename"));
925 }
926
927 string cid = resultView.getString(&iter, "CID");
928 int64_t size = resultView.getValue<int64_t>(&iter, "Real Size");
929 string tth = resultView.getString(&iter, _("TTH"));
930 string hubUrl = resultView.getString(&iter, "Hub URL");
931 F6 *func = new F6(this, &Search::download_client, target, cid, filename, size, tth, hubUrl);
932 WulforManager::get()->dispatchClientFunc(func);
933 }
934 }
935 while (parent && WulforUtil::getNextIter_gui(sortedFilterModel, &iter, TRUE, FALSE));
936 }
937 gtk_tree_path_free(path);
938 }
939 g_list_free(list);
940 }
941
onFocusIn_gui(GtkWidget * widget,GdkEventFocus * event,gpointer data)942 gboolean Search::onFocusIn_gui(GtkWidget *widget, GdkEventFocus *event, gpointer data)
943 {
944 Search *s = (Search *)data;
945 if (!s) return FALSE;
946
947 gtk_widget_grab_focus(s->getWidget("comboboxentrySearch"));
948
949 return TRUE;
950 }
951
onButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)952 gboolean Search::onButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
953 {
954 Search *s = (Search *)data;
955 if (!s) return FALSE;
956 s->oldEventType = event->type;
957
958 if (event->button == 3)
959 {
960 GtkTreePath *path;
961 if (gtk_tree_view_get_path_at_pos(s->resultView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
962 {
963 bool selected = gtk_tree_selection_path_is_selected(s->selection, path);
964 gtk_tree_path_free(path);
965
966 if (selected)
967 return TRUE;
968 }
969 }
970 return FALSE;
971 }
972
onButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)973 gboolean Search::onButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
974 {
975 Search *s = (Search *)data;
976 if (!s) return FALSE;
977 gint count = gtk_tree_selection_count_selected_rows(s->selection);
978
979 if (count > 0 && event->type == GDK_BUTTON_RELEASE && event->button == 3)
980 s->popupMenu_gui();
981 else if (count == 1 && s->oldEventType == GDK_2BUTTON_PRESS && event->button == 1)
982 s->onDownloadClicked_gui(NULL, data);
983
984 return FALSE;
985 }
986
onKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)987 gboolean Search::onKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
988 {
989 Search *s = (Search *)data;
990 if (!s) return FALSE;
991
992 if (widget == GTK_WIDGET(s->resultView.get()))
993 {
994 gint count = gtk_tree_selection_count_selected_rows(s->selection);
995
996 if (count > 0)
997 {
998 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
999 s->onDownloadClicked_gui(NULL, data);
1000 else if (event->keyval == GDK_Delete || event->keyval == GDK_BackSpace)
1001 s->onRemoveClicked_gui(NULL, data);
1002 else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
1003 s->popupMenu_gui();
1004 }
1005 }
1006 else
1007 {
1008 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
1009 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1010 }
1011
1012 return FALSE;
1013 }
1014
onSearchEntryKeyPressed_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)1015 gboolean Search::onSearchEntryKeyPressed_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
1016 {
1017 Search *s = (Search *)data;
1018 if (!s) return FALSE;
1019
1020 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
1021 {
1022 s->search_gui();
1023 }
1024 else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
1025 {
1026 gtk_combo_box_popup(GTK_COMBO_BOX(s->getWidget("comboboxentrySearch")));
1027 return TRUE;
1028 }
1029
1030 return FALSE;
1031 }
1032
onComboBoxChanged_gui(GtkWidget * widget,gpointer data)1033 void Search::onComboBoxChanged_gui(GtkWidget* widget, gpointer data)
1034 {
1035 Search *s = (Search *)data;
1036 if (!s) return;
1037
1038 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
1039 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1040 }
1041
onGroupByComboBoxChanged_gui(GtkWidget * comboBox,gpointer data)1042 void Search::onGroupByComboBoxChanged_gui(GtkWidget *comboBox, gpointer data)
1043 {
1044 Search *s = (Search*)data;
1045 GroupType groupBy = (GroupType)gtk_combo_box_get_active(GTK_COMBO_BOX(comboBox));
1046
1047 s->ungroup_gui();
1048
1049 if (groupBy != NOGROUPING)
1050 {
1051 gtk_widget_set_sensitive(s->getWidget("checkbuttonFilter"), FALSE);
1052 gtk_widget_set_sensitive(s->getWidget("checkbuttonSlots"), FALSE);
1053 gtk_widget_set_sensitive(s->getWidget("checkbuttonShared"), FALSE);
1054 s->regroup_gui();
1055 }
1056 else
1057 {
1058 gtk_widget_set_sensitive(s->getWidget("checkbuttonFilter"), TRUE);
1059 gtk_widget_set_sensitive(s->getWidget("checkbuttonSlots"), FALSE);
1060 gtk_widget_set_sensitive(s->getWidget("checkbuttonShared"), FALSE);
1061 }
1062 }
1063
onSearchButtonClicked_gui(GtkWidget * widget,gpointer data)1064 void Search::onSearchButtonClicked_gui(GtkWidget *widget, gpointer data)
1065 {
1066 Search *s = (Search *)data;
1067 if (!s) return;
1068 s->search_gui();
1069 }
1070
onFilterButtonToggled_gui(GtkToggleButton * button,gpointer data)1071 void Search::onFilterButtonToggled_gui(GtkToggleButton *button, gpointer data)
1072 {
1073 Search *s = (Search *)data;
1074 if (!s) return;
1075 GtkComboBox *comboBox = GTK_COMBO_BOX(s->getWidget("comboboxGroupBy"));
1076
1077 // Disable grouping when filtering within local results
1078 if (gtk_toggle_button_get_active(button))
1079 {
1080 s->previousGrouping = (GroupType)gtk_combo_box_get_active(comboBox);
1081 gtk_combo_box_set_active(comboBox, (int)NOGROUPING);
1082 gtk_widget_set_sensitive(GTK_WIDGET(comboBox), FALSE);
1083 gtk_widget_set_sensitive(s->getWidget("checkbuttonSlots"), TRUE);
1084 gtk_widget_set_sensitive(s->getWidget("checkbuttonShared"), TRUE);
1085 }
1086 else
1087 {
1088 gtk_combo_box_set_active(comboBox, (int)s->previousGrouping);
1089 gtk_widget_set_sensitive(GTK_WIDGET(comboBox), TRUE);
1090 gtk_widget_set_sensitive(s->getWidget("checkbuttonSlots"), FALSE);
1091 gtk_widget_set_sensitive(s->getWidget("checkbuttonShared"), FALSE);
1092
1093 }
1094
1095 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1096 }
1097
onSlotsButtonToggled_gui(GtkToggleButton * button,gpointer data)1098 void Search::onSlotsButtonToggled_gui(GtkToggleButton *button, gpointer data)
1099 {
1100 Search *s = (Search *)data;
1101 if (!s) return;
1102
1103 s->onlyFree = gtk_toggle_button_get_active(button);
1104 if (s->onlyFree != BOOLSETTING(SEARCH_ONLY_FREE_SLOTS))
1105 SettingsManager::getInstance()->set(SettingsManager::SEARCH_ONLY_FREE_SLOTS, s->onlyFree);
1106
1107 // Refilter current view only if "Search within local results" is enabled
1108 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
1109 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1110 }
1111
onSharedButtonToggled_gui(GtkToggleButton * button,gpointer data)1112 void Search::onSharedButtonToggled_gui(GtkToggleButton *button, gpointer data)
1113 {
1114 Search *s = (Search *)data;
1115 if (!s) return;
1116
1117 // Refilter current view only if "Search within local results" is enabled
1118 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
1119 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1120 }
1121
onToggledClicked_gui(GtkCellRendererToggle * cell,gchar * path,gpointer data)1122 void Search::onToggledClicked_gui(GtkCellRendererToggle *cell, gchar *path, gpointer data)
1123 {
1124 Search *s = (Search *)data;
1125 if (!s) return;
1126 GtkTreeIter iter;
1127
1128 if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(s->hubStore), &iter, path))
1129 {
1130 gboolean toggled = s->hubView.getValue<gboolean>(&iter, _("Search"));
1131 gtk_list_store_set(s->hubStore, &iter, s->hubView.col(_("Search")), !toggled, -1);
1132 }
1133
1134 // Refilter current view only if "Search within local results" is enabled
1135 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
1136 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(s->searchFilterModel));
1137 }
1138
onDownloadClicked_gui(GtkMenuItem * item,gpointer data)1139 void Search::onDownloadClicked_gui(GtkMenuItem *item, gpointer data)
1140 {
1141 Search *s = (Search *)data;
1142 if (!s) return;
1143
1144 string target = SETTING(DOWNLOAD_DIRECTORY);
1145 s->download_gui(target);
1146 }
1147
onDownloadFavoriteClicked_gui(GtkMenuItem * item,gpointer data)1148 void Search::onDownloadFavoriteClicked_gui(GtkMenuItem *item, gpointer data)
1149 {
1150 Search *s = (Search *)data;
1151 if (!s) return;
1152 string fav = string((gchar *)g_object_get_data(G_OBJECT(item), "fav"));
1153
1154 s->download_gui(fav);
1155 }
1156
onDownloadToClicked_gui(GtkMenuItem * item,gpointer data)1157 void Search::onDownloadToClicked_gui(GtkMenuItem *item, gpointer data)
1158 {
1159 Search *s = (Search *)data;
1160 if (!s) return;
1161
1162 gint response = gtk_dialog_run(GTK_DIALOG(s->getWidget("dirChooserDialog")));
1163
1164 // Fix crash, if the dialog gets programmatically destroyed.
1165 if (response == GTK_RESPONSE_NONE)
1166 return;
1167
1168 gtk_widget_hide(s->getWidget("dirChooserDialog"));
1169
1170 if (response == GTK_RESPONSE_OK)
1171 {
1172 gchar *temp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(s->getWidget("dirChooserDialog")));
1173
1174 if (temp)
1175 {
1176 string target = Text::toUtf8(temp);
1177 g_free(temp);
1178
1179 if (target[target.length() - 1] != PATH_SEPARATOR)
1180 target += PATH_SEPARATOR;
1181
1182 s->download_gui(target);
1183 }
1184 }
1185 }
1186
onDownloadToMatchClicked_gui(GtkMenuItem * item,gpointer data)1187 void Search::onDownloadToMatchClicked_gui(GtkMenuItem *item, gpointer data)
1188 {
1189 Search *s = (Search *)data;
1190 if (!s) return;
1191
1192 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1193 {
1194 string fileName = WulforUtil::getTextFromMenu(item);
1195 GtkTreeIter iter;
1196 GtkTreePath *path;
1197 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1198 typedef Func5<Search, string, string, int64_t, string, string> F5;
1199
1200 for (GList *i = list; i; i = i->next)
1201 {
1202 path = (GtkTreePath *)i->data;
1203 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1204 {
1205 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1206
1207 do
1208 {
1209 if (!gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter))
1210 {
1211 string cid = s->resultView.getString(&iter, "CID");
1212 int64_t size = s->resultView.getValue<int64_t>(&iter, "Real Size");
1213 string tth = s->resultView.getString(&iter, _("TTH"));
1214 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1215 F5 *func = new F5(s, &Search::addSource_client, fileName, cid, size, tth, hubUrl);
1216 WulforManager::get()->dispatchClientFunc(func);
1217 }
1218 }
1219 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1220 }
1221 gtk_tree_path_free(path);
1222 }
1223 g_list_free(list);
1224 }
1225 }
1226
onDownloadDirClicked_gui(GtkMenuItem * item,gpointer data)1227 void Search::onDownloadDirClicked_gui(GtkMenuItem *item, gpointer data)
1228 {
1229 Search *s = (Search *)data;
1230 if (!s) return;
1231
1232 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1233 {
1234 GtkTreeIter iter;
1235 GtkTreePath *path;
1236 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1237 string target = SETTING(DOWNLOAD_DIRECTORY);
1238 typedef Func4<Search, string, string, string, string> F4;
1239
1240 for (GList *i = list; i; i = i->next)
1241 {
1242 path = (GtkTreePath *)i->data;
1243 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1244 {
1245 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1246
1247 do
1248 {
1249 if (!gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter))
1250 {
1251 string cid = s->resultView.getString(&iter, "CID");
1252 string filename = s->resultView.getString(&iter, _("Path"));
1253 filename += s->resultView.getString(&iter, _("Filename"));
1254 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1255 F4 *func = new F4(s, &Search::downloadDir_client, target, cid, filename, hubUrl);
1256 WulforManager::get()->dispatchClientFunc(func);
1257 }
1258 }
1259 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1260 }
1261 gtk_tree_path_free(path);
1262 }
1263 g_list_free(list);
1264 }
1265 }
1266
onDownloadFavoriteDirClicked_gui(GtkMenuItem * item,gpointer data)1267 void Search::onDownloadFavoriteDirClicked_gui(GtkMenuItem *item, gpointer data)
1268 {
1269 Search *s = (Search *)data;
1270 if (!s) return;
1271 string fav = (gchar *)g_object_get_data(G_OBJECT(item), "fav");
1272
1273 if (!fav.empty() && gtk_tree_selection_count_selected_rows(s->selection) > 0)
1274 {
1275 GtkTreeIter iter;
1276 GtkTreePath *path;
1277 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1278 typedef Func4<Search, string, string, string, string> F4;
1279
1280 for (GList *i = list; i; i = i->next)
1281 {
1282 path = (GtkTreePath *)i->data;
1283 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1284 {
1285 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1286
1287 do
1288 {
1289 if (!gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter))
1290 {
1291 string cid = s->resultView.getString(&iter, "CID");
1292 string filename = s->resultView.getString(&iter, _("Path"));
1293 filename += s->resultView.getString(&iter, _("Filename"));
1294 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1295 F4 *func = new F4(s, &Search::downloadDir_client, fav, cid, filename, hubUrl);
1296 WulforManager::get()->dispatchClientFunc(func);
1297 }
1298 }
1299 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1300 }
1301 gtk_tree_path_free(path);
1302 }
1303 g_list_free(list);
1304 }
1305 }
1306
onDownloadDirToClicked_gui(GtkMenuItem * item,gpointer data)1307 void Search::onDownloadDirToClicked_gui(GtkMenuItem *item, gpointer data)
1308 {
1309 Search *s = (Search *)data;
1310 if (!s) return;
1311
1312 gint response = gtk_dialog_run(GTK_DIALOG(s->getWidget("dirChooserDialog")));
1313
1314 // Fix crash, if the dialog gets programmatically destroyed.
1315 if (response == GTK_RESPONSE_NONE)
1316 return;
1317
1318 gtk_widget_hide(s->getWidget("dirChooserDialog"));
1319
1320 if (response == GTK_RESPONSE_OK)
1321 {
1322 int count = gtk_tree_selection_count_selected_rows(s->selection);
1323 gchar *temp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(s->getWidget("dirChooserDialog")));
1324
1325 if (temp && count > 0)
1326 {
1327 string target = Text::toUtf8(temp);
1328 g_free(temp);
1329
1330 if (target[target.length() - 1] != PATH_SEPARATOR)
1331 target += PATH_SEPARATOR;
1332
1333 GtkTreeIter iter;
1334 GtkTreePath *path;
1335 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1336 typedef Func4<Search, string, string, string, string> F4;
1337
1338 for (GList *i = list; i; i = i->next)
1339 {
1340 path = (GtkTreePath *)i->data;
1341 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1342 {
1343 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1344
1345 do
1346 {
1347 if (!gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter))
1348 {
1349 string cid = s->resultView.getString(&iter, "CID");
1350 string filename = s->resultView.getString(&iter, _("Path"));
1351 filename += s->resultView.getString(&iter, _("Filename"));
1352 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1353 F4 *func = new F4(s, &Search::downloadDir_client, target, cid, filename, hubUrl);
1354 WulforManager::get()->dispatchClientFunc(func);
1355 }
1356 }
1357 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1358 }
1359 gtk_tree_path_free(path);
1360 }
1361 g_list_free(list);
1362 }
1363 }
1364 }
1365
onSearchByTTHClicked_gui(GtkMenuItem * item,gpointer data)1366 void Search::onSearchByTTHClicked_gui(GtkMenuItem *item, gpointer data)
1367 {
1368 Search *s = (Search *)data;
1369 if (!s) return;
1370
1371 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1372 {
1373 GtkTreeIter iter;
1374 GtkTreePath *path;
1375 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1376
1377 for (GList *i = list; i; i = i->next)
1378 {
1379 path = (GtkTreePath *)i->data;
1380 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1381 {
1382 string tth = s->resultView.getString(&iter, _("TTH"));
1383 if (!tth.empty())
1384 {
1385 s = WulforManager::get()->getMainWindow()->addSearch_gui();
1386 s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
1387 }
1388 }
1389 gtk_tree_path_free(path);
1390 }
1391 g_list_free(list);
1392 }
1393 }
1394
onGetFileListClicked_gui(GtkMenuItem * item,gpointer data)1395 void Search::onGetFileListClicked_gui(GtkMenuItem *item, gpointer data)
1396 {
1397 Search *s = (Search *)data;
1398 if (!s) return;
1399
1400 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1401 {
1402 GtkTreeIter iter;
1403 GtkTreePath *path;
1404 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1405 typedef Func5<Search, string, string, bool, string, bool> F5;
1406
1407 for (GList *i = list; i; i = i->next)
1408 {
1409 path = (GtkTreePath *)i->data;
1410 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1411 {
1412 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1413
1414 do
1415 {
1416 string cid = s->resultView.getString(&iter, "CID");
1417 string dir = s->resultView.getString(&iter, _("Path"));
1418 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1419 F5 *func = new F5(s, &Search::getFileList_client, cid, dir, FALSE, hubUrl, TRUE);
1420 WulforManager::get()->dispatchClientFunc(func);
1421 }
1422 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1423 }
1424 gtk_tree_path_free(path);
1425 }
1426 g_list_free(list);
1427 }
1428 }
1429
onPartialFileListOpen_gui(GtkMenuItem * item,gpointer data)1430 void Search::onPartialFileListOpen_gui(GtkMenuItem *item, gpointer data)
1431 {
1432 Search *s = (Search *)data;
1433 if (!s) return;
1434
1435 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1436 {
1437 GtkTreeIter iter;
1438 GtkTreePath *path;
1439 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1440 typedef Func5<Search, string, string, bool, string, bool> F5;
1441
1442 for (GList *i = list; i; i = i->next)
1443 {
1444 path = (GtkTreePath *)i->data;
1445 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1446 {
1447 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1448
1449 do
1450 {
1451 string cid = s->resultView.getString(&iter, "CID");
1452 string dir = s->resultView.getString(&iter, _("Path"));
1453 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1454 F5 *func = new F5(s, &Search::getFileList_client, cid, dir, FALSE, hubUrl, FALSE);
1455 WulforManager::get()->dispatchClientFunc(func);
1456 }
1457 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1458 }
1459 gtk_tree_path_free(path);
1460 }
1461 g_list_free(list);
1462 }
1463 }
1464
onMatchQueueClicked_gui(GtkMenuItem * item,gpointer data)1465 void Search::onMatchQueueClicked_gui(GtkMenuItem *item, gpointer data)
1466 {
1467 Search *s = (Search *)data;
1468 if (!s) return;
1469
1470 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1471 {
1472 GtkTreeIter iter;
1473 GtkTreePath *path;
1474 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1475 typedef Func5<Search, string, string, bool, string, bool> F5;
1476
1477 for (GList *i = list; i; i = i->next)
1478 {
1479 path = (GtkTreePath *)i->data;
1480 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1481 {
1482 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1483
1484 do
1485 {
1486 string cid = s->resultView.getString(&iter, "CID");
1487 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1488 F5 *func = new F5(s, &Search::getFileList_client, cid, "", TRUE, hubUrl, TRUE);
1489 WulforManager::get()->dispatchClientFunc(func);
1490 }
1491 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1492 }
1493 gtk_tree_path_free(path);
1494 }
1495 g_list_free(list);
1496 }
1497 }
1498
onPrivateMessageClicked_gui(GtkMenuItem * item,gpointer data)1499 void Search::onPrivateMessageClicked_gui(GtkMenuItem *item, gpointer data)
1500 {
1501 Search *s = (Search *)data;
1502 if (!s) return;
1503
1504 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1505 {
1506 GtkTreeIter iter;
1507 GtkTreePath *path;
1508 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1509
1510 for (GList *i = list; i; i = i->next)
1511 {
1512 path = (GtkTreePath *)i->data;
1513 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1514 {
1515 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1516
1517 do
1518 {
1519 string cid = s->resultView.getString(&iter, "CID");
1520 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1521 if (!cid.empty())
1522 WulforManager::get()->getMainWindow()->addPrivateMessage_gui(Msg::UNKNOWN, cid, hubUrl);
1523 }
1524 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1525 }
1526 gtk_tree_path_free(path);
1527 }
1528 g_list_free(list);
1529 }
1530 }
1531
onAddFavoriteUserClicked_gui(GtkMenuItem * item,gpointer data)1532 void Search::onAddFavoriteUserClicked_gui(GtkMenuItem *item, gpointer data)
1533 {
1534 Search *s = (Search *)data;
1535 if (!s) return;
1536
1537 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1538 {
1539 string cid;
1540 GtkTreeIter iter;
1541 GtkTreePath *path;
1542 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1543 typedef Func1<Search, string> F1;
1544 F1 *func;
1545
1546 for (GList *i = list; i; i = i->next)
1547 {
1548 path = (GtkTreePath *)i->data;
1549 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1550 {
1551 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1552
1553 do
1554 {
1555 cid = s->resultView.getString(&iter, "CID");
1556 func = new F1(s, &Search::addFavUser_client, cid);
1557 WulforManager::get()->dispatchClientFunc(func);
1558 }
1559 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1560 }
1561 gtk_tree_path_free(path);
1562 }
1563 g_list_free(list);
1564 }
1565 }
1566
onGrantExtraSlotClicked_gui(GtkMenuItem * item,gpointer data)1567 void Search::onGrantExtraSlotClicked_gui(GtkMenuItem *item, gpointer data)
1568 {
1569 Search *s = (Search *)data;
1570 if (!s) return;
1571
1572 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1573 {
1574 GtkTreeIter iter;
1575 GtkTreePath *path;
1576 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1577 typedef Func2<Search, string, string> F2;
1578
1579 for (GList *i = list; i; i = i->next)
1580 {
1581 path = (GtkTreePath *)i->data;
1582 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1583 {
1584 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1585
1586 do
1587 {
1588 string cid = s->resultView.getString(&iter, "CID");
1589 string hubUrl = s->resultView.getString(&iter, "Hub URL");
1590 F2 *func = new F2(s, &Search::grantSlot_client, cid, hubUrl);
1591 WulforManager::get()->dispatchClientFunc(func);
1592 }
1593 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1594 }
1595 gtk_tree_path_free(path);
1596 }
1597 g_list_free(list);
1598 }
1599 }
1600
onRemoveUserFromQueueClicked_gui(GtkMenuItem * item,gpointer data)1601 void Search::onRemoveUserFromQueueClicked_gui(GtkMenuItem *item, gpointer data)
1602 {
1603 Search *s = (Search *)data;
1604 if (!s) return;
1605
1606 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1607 {
1608 string cid;
1609 GtkTreeIter iter;
1610 GtkTreePath *path;
1611 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1612 typedef Func1<Search, string> F1;
1613 F1 *func;
1614
1615 for (GList *i = list; i; i = i->next)
1616 {
1617 path = (GtkTreePath *)i->data;
1618 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1619 {
1620 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1621
1622 do
1623 {
1624 cid = s->resultView.getString(&iter, "CID");
1625 func = new F1(s, &Search::removeSource_client, cid);
1626 WulforManager::get()->dispatchClientFunc(func);
1627 }
1628 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1629 }
1630 gtk_tree_path_free(path);
1631 }
1632 g_list_free(list);
1633 }
1634 }
1635
1636 // Removing a row from treeStore still leaves the SearchResultPtr to results map. This way if a duplicate
1637 // result comes in later it won't be readded, before the results map is cleared with a new search.
onRemoveClicked_gui(GtkMenuItem * item,gpointer data)1638 void Search::onRemoveClicked_gui(GtkMenuItem *item, gpointer data)
1639 {
1640 Search *s = (Search *)data;
1641 if (!s) return;
1642
1643 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1644 {
1645 GtkTreeIter iter;
1646 GtkTreeIter filterIter;
1647 GtkTreePath *path;
1648 vector<GtkTreeIter> remove;
1649 GList *list = g_list_reverse(gtk_tree_selection_get_selected_rows(s->selection, NULL));
1650 GList *refList = NULL;
1651
1652 // Convert it to list of GtkTreeRowReferences since modifying the model with a list of Paths is bad.
1653 for (GList *i = list; i; i = i->next)
1654 {
1655 path = (GtkTreePath *)i->data;
1656 refList = g_list_append(refList, gtk_tree_row_reference_new(s->sortedFilterModel, path));
1657 gtk_tree_path_free(path);
1658 }
1659 g_list_free(list);
1660 for (GList *i = refList; i; i = i->next)
1661 {
1662 path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)i->data);
1663 if (path)
1664 {
1665 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1666 {
1667 // Remove the top-level node and it will remove any children nodes (if applicable)
1668 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(s->sortedFilterModel), &filterIter, &iter);
1669 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(s->searchFilterModel), &iter, &filterIter);
1670 gtk_tree_store_remove(s->resultStore, &iter);
1671 }
1672 gtk_tree_path_free(path);
1673 }
1674 gtk_tree_row_reference_free((GtkTreeRowReference*)i->data);
1675 }
1676 g_list_free(refList);
1677 }
1678 }
1679
onCopyMagnetClicked_gui(GtkMenuItem * item,gpointer data)1680 void Search::onCopyMagnetClicked_gui(GtkMenuItem* item, gpointer data)
1681 {
1682 Search *s = (Search *)data;
1683 if (!s) return;
1684
1685 if (gtk_tree_selection_count_selected_rows(s->selection) > 0)
1686 {
1687 int64_t size;
1688 string magnets, magnet, filename, tth;
1689 GtkTreeIter iter;
1690 GtkTreePath *path;
1691 GList *list = gtk_tree_selection_get_selected_rows(s->selection, NULL);
1692
1693 for (GList *i = list; i; i = i->next)
1694 {
1695 path = (GtkTreePath *)i->data;
1696 if (gtk_tree_model_get_iter(s->sortedFilterModel, &iter, path))
1697 {
1698 bool parent = gtk_tree_model_iter_has_child(s->sortedFilterModel, &iter);
1699
1700 do
1701 {
1702 filename = s->resultView.getString(&iter, _("Filename"));
1703 size = s->resultView.getValue<int64_t>(&iter, "Real Size");
1704 tth = s->resultView.getString(&iter, _("TTH"));
1705 magnet = WulforUtil::makeMagnet(filename, size, tth);
1706
1707 if (!magnet.empty())
1708 {
1709 if (!magnets.empty())
1710 magnets += '\n';
1711 magnets += magnet;
1712 }
1713 }
1714 while (parent && WulforUtil::getNextIter_gui(s->sortedFilterModel, &iter, TRUE, FALSE));
1715 }
1716 gtk_tree_path_free(path);
1717 }
1718 g_list_free(list);
1719
1720 if (!magnets.empty())
1721 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), magnets.c_str(), magnets.length());
1722 }
1723 }
1724
parseSearchResult_gui(SearchResultPtr result,StringMap & resultMap)1725 void Search::parseSearchResult_gui(SearchResultPtr result, StringMap &resultMap)
1726 {
1727 if (result->getType() == SearchResult::TYPE_FILE)
1728 {
1729 string file = WulforUtil::linuxSeparator(result->getFile());
1730 if (file.rfind('/') == tstring::npos)
1731 {
1732 resultMap["Filename"] = file;
1733 }
1734 else
1735 {
1736 resultMap["Filename"] = Util::getFileName(file);
1737 resultMap["Path"] = Util::getFilePath(file);
1738 }
1739
1740 resultMap["File Order"] = "f" + resultMap["Filename"];
1741 resultMap["Type"] = Util::getFileExt(resultMap["Filename"]);
1742 if (!resultMap["Type"].empty() && resultMap["Type"][0] == '.')
1743 resultMap["Type"].erase(0, 1);
1744 resultMap["Size"] = Util::formatBytes(result->getSize());
1745 resultMap["Exact Size"] = Util::formatExactSize(result->getSize());
1746 resultMap["Icon"] = "icon-file";
1747 resultMap["Shared"] = Util::toString(ShareManager::getInstance()->isTTHShared(result->getTTH()));
1748 }
1749 else
1750 {
1751 string path = WulforUtil::linuxSeparator(result->getFile());
1752 resultMap["Filename"] = Util::getLastDir(path) + PATH_SEPARATOR;
1753 resultMap["Path"] = Util::getFilePath(path.substr(0, path.length() - 1)); // getFilePath just returns path unless we chop the last / off
1754 if (resultMap["Path"].find("/") == string::npos)
1755 resultMap["Path"] = "";
1756 resultMap["File Order"] = "d" + resultMap["Filename"];
1757 resultMap["Type"] = _("Directory");
1758 resultMap["Icon"] = "icon-directory";
1759 resultMap["Shared"] = "0";
1760 if (result->getSize() > 0)
1761 {
1762 resultMap["Size"] = Util::formatBytes(result->getSize());
1763 resultMap["Exact Size"] = Util::formatExactSize(result->getSize());
1764 }
1765 }
1766
1767 resultMap["Nick"] = WulforUtil::getNicks(result->getUser(), result->getHubURL());
1768 resultMap["CID"] = result->getUser()->getCID().toBase32();
1769 resultMap["Slots"] = result->getSlotString();
1770 resultMap["Connection"] = ClientManager::getInstance()->getConnection(result->getUser()->getCID());
1771 resultMap["Hub"] = result->getHubName().empty() ? result->getHubURL().c_str() : result->getHubName().c_str();
1772 resultMap["Hub URL"] = result->getHubURL();
1773 resultMap["IP"] = result->getIP();
1774 resultMap["Real Size"] = Util::toString(result->getSize());
1775 if (result->getType() == SearchResult::TYPE_FILE)
1776 resultMap["TTH"] = result->getTTH().toBase32();
1777
1778 // assumption: total slots is never above 999
1779 resultMap["Slots Order"] = Util::toString(-1000 * result->getFreeSlots() - result->getSlots());
1780 resultMap["Free Slots"] = Util::toString(result->getFreeSlots());
1781 }
1782
download_client(string target,string cid,string filename,int64_t size,string tth,string hubUrl)1783 void Search::download_client(string target, string cid, string filename, int64_t size, string tth, string hubUrl)
1784 {
1785 try
1786 {
1787 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1788 if (!user)
1789 return;
1790
1791 // Only files have a TTH
1792 if (!tth.empty())
1793 {
1794 string subdir = Util::getFileName(filename);
1795 QueueManager::getInstance()->add(target + subdir, size, TTHValue(tth), HintedUser(user, hubUrl));
1796 }
1797 else
1798 {
1799 string dir = WulforUtil::windowsSeparator(filename);
1800 QueueManager::getInstance()->addDirectory(dir, HintedUser(user, hubUrl), target);
1801 }
1802 }
1803 catch (const Exception&)
1804 {
1805 }
1806 }
1807
downloadDir_client(string target,string cid,string filename,string hubUrl)1808 void Search::downloadDir_client(string target, string cid, string filename, string hubUrl)
1809 {
1810 try
1811 {
1812 string dir;
1813
1814 // If it's a file (directories are assumed to end in '/')
1815 if (filename[filename.length() - 1] != PATH_SEPARATOR)
1816 {
1817 dir = WulforUtil::windowsSeparator(Util::getFilePath(filename));
1818 }
1819 else
1820 {
1821 dir = WulforUtil::windowsSeparator(filename);
1822 }
1823
1824 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1825 if (user)
1826 {
1827 QueueManager::getInstance()->addDirectory(dir, HintedUser(user, hubUrl), target);
1828 }
1829 }
1830 catch (const Exception&)
1831 {
1832 }
1833 }
1834
addSource_client(string source,string cid,int64_t size,string tth,string hubUrl)1835 void Search::addSource_client(string source, string cid, int64_t size, string tth, string hubUrl)
1836 {
1837 try
1838 {
1839 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1840 if (!tth.empty() && user)
1841 {
1842 QueueManager::getInstance()->add(source, size, TTHValue(tth), HintedUser(user, hubUrl));
1843 }
1844 }
1845 catch (const Exception&)
1846 {
1847 }
1848 }
1849
getFileList_client(string cid,string dir,bool match,string hubUrl,bool full)1850 void Search::getFileList_client(string cid, string dir, bool match, string hubUrl, bool full)
1851 {
1852 if (!cid.empty())
1853 {
1854 try
1855 {
1856 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1857 if (user)
1858 {
1859 QueueItem::FileFlags flags;
1860 if (match)
1861 flags = QueueItem::FLAG_MATCH_QUEUE;
1862 else
1863 flags = QueueItem::FLAG_CLIENT_VIEW;
1864 QueueManager::getInstance()->addList(HintedUser(user, hubUrl), full ? flags : QueueItem::FLAG_CLIENT_VIEW | QueueItem::FLAG_PARTIAL_LIST, dir);
1865 }
1866 }
1867 catch (const Exception&)
1868 {
1869 }
1870 }
1871 }
1872
grantSlot_client(string cid,string hubUrl)1873 void Search::grantSlot_client(string cid, string hubUrl)
1874 {
1875 if (!cid.empty())
1876 {
1877 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1878 if (user)
1879 {
1880 UploadManager::getInstance()->reserveSlot(HintedUser(user, hubUrl));
1881 }
1882 }
1883 }
1884
addFavUser_client(string cid)1885 void Search::addFavUser_client(string cid)
1886 {
1887 if (!cid.empty())
1888 {
1889 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1890 if (user)
1891 FavoriteManager::getInstance()->addFavoriteUser(user);
1892 }
1893 }
1894
removeSource_client(string cid)1895 void Search::removeSource_client(string cid)
1896 {
1897 if (!cid.empty())
1898 {
1899 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
1900 if (user)
1901 QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
1902 }
1903 }
1904
on(ClientManagerListener::ClientConnected,Client * client)1905 void Search::on(ClientManagerListener::ClientConnected, Client *client) noexcept
1906 {
1907 if (client)
1908 {
1909 typedef Func2<Search, string, string> F2;
1910 F2 *func = new F2(this, &Search::addHub_gui, client->getHubName(), client->getHubUrl());
1911 WulforManager::get()->dispatchGuiFunc(func);
1912 }
1913 }
1914
on(ClientManagerListener::ClientUpdated,Client * client)1915 void Search::on(ClientManagerListener::ClientUpdated, Client *client) noexcept
1916 {
1917 if (client)
1918 {
1919 typedef Func2<Search, string, string> F2;
1920 F2 *func = new F2(this, &Search::modifyHub_gui, client->getHubName(), client->getHubUrl());
1921 WulforManager::get()->dispatchGuiFunc(func);
1922 }
1923 }
1924
on(ClientManagerListener::ClientDisconnected,Client * client)1925 void Search::on(ClientManagerListener::ClientDisconnected, Client *client) noexcept
1926 {
1927 if (client)
1928 {
1929 typedef Func1<Search, string> F1;
1930 F1 *func = new F1(this, &Search::removeHub_gui, client->getHubUrl());
1931 WulforManager::get()->dispatchGuiFunc(func);
1932 }
1933 }
1934
on(TimerManagerListener::Second,uint64_t aTick)1935 void Search::on(TimerManagerListener::Second, uint64_t aTick) noexcept {
1936 if (waitingResults)
1937 {
1938 // use rounding below to workaround bug in gtk_progress_bar_set_fraction()
1939 uint fract = (1000 * (aTick - searchStartTime)) / (searchEndTime - searchStartTime);
1940 float fraction = 1.0f * fract / 1000;
1941 if (fraction >= 1.0)
1942 {
1943 fraction = 1.0;
1944 waitingResults = false;
1945 }
1946 setProgress_gui("progressbar1", _("Searching for ") + target + string(" ..."), fraction);
1947 }
1948 else
1949 {
1950 setProgress_gui("progressbar1", "", 0.0);
1951 gtk_widget_set_sensitive(getWidget("comboboxentrySearch"), TRUE);
1952 }
1953 }
1954
on(SearchManagerListener::SR,const SearchResultPtr & result)1955 void Search::on(SearchManagerListener::SR, const SearchResultPtr& result) noexcept
1956 {
1957 if (searchlist.empty() || !result || stop)
1958 return;
1959
1960 typedef Func2<Search, string, string> F2;
1961
1962 if (isHash)
1963 {
1964 if (result->getType() != SearchResult::TYPE_FILE || TTHValue(searchlist[0]) != result->getTTH())
1965 {
1966 ++droppedResult;
1967 F2 *func = new F2(this, &Search::setStatus_gui, "statusbar3", _("Filtered: ") + Util::toString(droppedResult));
1968 WulforManager::get()->dispatchGuiFunc(func);
1969 return;
1970 }
1971 }
1972 else
1973 {
1974 for (TStringIter i = searchlist.begin(); i != searchlist.end(); ++i)
1975 {
1976 if ((*i->begin() != '-' && Util::findSubString(result->getFile(), *i) == (string::size_type)-1) ||
1977 (*i->begin() == '-' && i->size() != 1 && Util::findSubString(result->getFile(), i->substr(1)) != (string::size_type)-1))
1978 {
1979 ++droppedResult;
1980 F2 *func = new F2(this, &Search::setStatus_gui, "statusbar3", _("Dropped: ") + Util::toString(droppedResult));
1981 WulforManager::get()->dispatchGuiFunc(func);
1982 return;
1983 }
1984 }
1985 }
1986
1987 typedef Func1<Search, SearchResultPtr> F1;
1988 F1 *func = new F1(this, &Search::addResult_gui, result);
1989 WulforManager::get()->dispatchGuiFunc(func);
1990 }
1991
1992 // Filtering causes Gtk-CRITICAL assertion failure, when last item is removed
1993 // see. http://bugzilla.gnome.org/show_bug.cgi?id=464173
searchFilterFunc_gui(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1994 gboolean Search::searchFilterFunc_gui(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1995 {
1996 Search *s = (Search *)data;
1997 if (!s) return FALSE;
1998 dcassert(model == GTK_TREE_MODEL(s->resultStore));
1999
2000 // Enabler filtering only if search within local results is checked
2001 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonFilter"))))
2002 return TRUE;
2003
2004 // Grouping shouldn't be enabled while filtering, but just in case...
2005 if (gtk_tree_model_iter_has_child(model, iter))
2006 return TRUE;
2007
2008 string hub = s->resultView.getString(iter, "Hub URL", model);
2009 GtkTreeIter hubIter;
2010 bool valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(s->hubStore), &hubIter);
2011 while (valid)
2012 {
2013 if (hub == s->hubView.getString(&hubIter, "Url"))
2014 {
2015 if (!s->hubView.getValue<gboolean>(&hubIter, _("Search")))
2016 return FALSE;
2017 else
2018 break;
2019 }
2020 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(s->hubStore), &hubIter);
2021 }
2022
2023 // Filter based on free slots.
2024 gint freeSlots = s->resultView.getValue<gint>(iter, "Free Slots", model);
2025 if (s->onlyFree && freeSlots < 1)
2026 return FALSE;
2027
2028 // Hide results already in share
2029 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s->getWidget("checkbuttonShared"))) &&
2030 s->resultView.getValue<gboolean>(iter, "Shared", model) == TRUE)
2031 return FALSE;
2032
2033 // Filter based on search terms.
2034 string filter = Text::toLower(gtk_entry_get_text(GTK_ENTRY(s->searchEntry)));
2035 TStringList filterList = StringTokenizer<tstring>(filter, ' ').getTokens();
2036 string filename = Text::toLower(s->resultView.getString(iter, _("Filename"), model));
2037 string path = Text::toLower(s->resultView.getString(iter, _("Path"), model));
2038 for (auto term = filterList.begin(); term != filterList.end(); ++term)
2039 {
2040 if ((*term)[0] == '-')
2041 {
2042 if (filename.find((*term).substr(1)) != string::npos)
2043 return FALSE;
2044 else if (path.find((*term).substr(1)) != string::npos)
2045 return FALSE;
2046 }
2047 else if (filename.find(*term) == string::npos && path.find(*term) == string::npos)
2048 return FALSE;
2049 }
2050
2051 // Filter based on file size.
2052 double filterSize = Util::toDouble(gtk_entry_get_text(GTK_ENTRY(s->getWidget("entrySize"))));
2053 if (filterSize > 0)
2054 {
2055 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(s->getWidget("comboboxUnit"))))
2056 {
2057 case 1:
2058 filterSize *= 1024.0;
2059 break;
2060 case 2:
2061 filterSize *= 1024.0 * 1024.0;
2062 break;
2063 case 3:
2064 filterSize *= 1024.0 * 1024.0 * 1024.0;
2065 break;
2066 }
2067
2068 int64_t size = s->resultView.getValue<int64_t>(iter, "Real Size", model);
2069
2070 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(s->getWidget("comboboxSize"))))
2071 {
2072 case 0:
2073 if (size != filterSize)
2074 return FALSE;
2075 break;
2076 case 1:
2077 if (size < filterSize)
2078 return FALSE;
2079 break;
2080 case 2:
2081 if (size > filterSize)
2082 return FALSE;
2083 }
2084 }
2085
2086 int type = gtk_combo_box_get_active(GTK_COMBO_BOX(s->getWidget("comboboxFile")));
2087 if (type != SearchManager::TYPE_ANY && type != ShareManager::getInstance()->getType(filename))
2088 return FALSE;
2089
2090 return TRUE;
2091 }
2092
onSidePanelToggled_gui(GtkWidget * widget,gpointer data)2093 void Search::onSidePanelToggled_gui(GtkWidget *widget, gpointer data)
2094 {
2095 Search *s = (Search *)data;
2096 if (!s) return;
2097 GtkWidget *sidepanel = (GtkWidget *)s->getWidget("sidePanel");
2098
2099 if (gtk_widget_get_visible(sidepanel))
2100 gtk_widget_hide(sidepanel);
2101 else
2102 gtk_widget_show_all(sidepanel);
2103 }
2104
onClearButtonClicked_gui(GtkWidget * widget,gpointer data)2105 void Search::onClearButtonClicked_gui(GtkWidget *widget, gpointer data)
2106 {
2107 Search *s = (Search *)data;
2108 if (!s) return;
2109 gtk_tree_store_clear(s->resultStore);
2110 s->results.clear();
2111 gtk_entry_set_text(GTK_ENTRY(s->searchEntry), "");
2112 gtk_entry_set_text(GTK_ENTRY(s->getWidget("entrySize")), "");
2113 }
2114