1 /*
2  * Copyright © 2009-2010 freedcpp, http://code.google.com/p/freedcpp
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 "searchspy.hh"
23 #include <dcpp/SearchManager.h>
24 #include <dcpp/TimerManager.h>
25 #include <dcpp/Util.h>
26 #include "settingsmanager.hh"
27 #include "search.hh"
28 #include "wulformanager.hh"
29 #include "WulforUtil.hh"
30 
31 using namespace std;
32 using namespace dcpp;
33 
SearchSpy()34 SearchSpy::SearchSpy():
35     BookEntry(Entry::SEARCH_SPY, _("Search Spy"), "searchspy.ui")
36 {
37 #if !GTK_CHECK_VERSION(3,0,0)
38     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusbar")),FALSE);
39 #endif
40 
41     FrameSize = (SearchType)WGETI("search-spy-frame");
42     Waiting = (guint)WGETI("search-spy-waiting");
43     Top = (guint)WGETI("search-spy-top");
44 
45     // Configure the dialog
46     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(getWidget("ignoreTTHSearchCheckButton")), WGETB("spyframe-ignore-tth-searches"));
47     gtk_window_set_transient_for(GTK_WINDOW(getWidget("TopSearchDialog")), GTK_WINDOW(WulforManager::get()->getMainWindow()->getContainer()));
48     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("frameSpinButton")), (double)FrameSize);
49     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("waitingSpinButton")), (double)Waiting);
50     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("topSpinButton")), (double)Top);
51 
52     // menu
53     g_object_ref_sink(getWidget("menu"));
54 
55     // Initialize search list treeview
56     searchView.setView(GTK_TREE_VIEW(getWidget("searchSpyView")), TRUE, "searchspy");
57     searchView.insertColumn(_("Search String"), G_TYPE_STRING, TreeView::ICON_STRING_TEXT_COLOR, 305, "icon", "color");
58     searchView.insertColumn(_("Count"), G_TYPE_STRING, TreeView::STRING, 70);
59     searchView.insertColumn(_("Time"), G_TYPE_STRING, TreeView::STRING, 90);
60     searchView.insertColumn(_("Status"), G_TYPE_STRING, TreeView::STRING, 90);
61     searchView.insertHiddenColumn("type", G_TYPE_STRING);
62     searchView.insertHiddenColumn("count", G_TYPE_UINT);
63     searchView.insertHiddenColumn("tick", G_TYPE_UINT64);
64     searchView.insertHiddenColumn("icon", G_TYPE_STRING);
65     searchView.insertHiddenColumn("order", G_TYPE_STRING);
66     searchView.insertHiddenColumn("color", G_TYPE_STRING);
67     searchView.finalize();
68 
69     searchStore = gtk_list_store_newv(searchView.getColCount(), searchView.getGTypes());
70     gtk_tree_view_set_model(searchView.get(), GTK_TREE_MODEL(searchStore));
71     g_object_unref(searchStore);
72 
73     searchSelection = gtk_tree_view_get_selection(searchView.get());
74     gtk_tree_selection_set_mode(searchSelection, GTK_SELECTION_MULTIPLE);
75     searchView.setSortColumn_gui(_("Search String"), "count");
76     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(searchStore), searchView.col("count"), GTK_SORT_DESCENDING);
77     gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(searchView.get(), searchView.col(_("Search String"))), TRUE);
78     gtk_tree_view_set_fixed_height_mode(searchView.get(), TRUE);
79 
80     topView.setView(GTK_TREE_VIEW(getWidget("topView")));
81     topView.insertColumn(_("Search String"), G_TYPE_STRING, TreeView::STRING, -1);
82     topView.insertHiddenColumn("type", G_TYPE_STRING);
83     topView.finalize();
84 
85     topStore = gtk_list_store_newv(topView.getColCount(), topView.getGTypes());
86     gtk_tree_view_set_model(topView.get(), GTK_TREE_MODEL(topStore));
87     g_object_unref(topStore);
88 
89     g_signal_connect(getWidget("searchItem"), "activate", G_CALLBACK(onSearchItemClicked_gui), (gpointer)this);
90     g_signal_connect(getWidget("removeItem"), "activate", G_CALLBACK(onRemoveItemClicked_gui), (gpointer)this);
91     g_signal_connect(getWidget("clearFrameButton"), "clicked", G_CALLBACK(onClearFrameClicked_gui), (gpointer)this);
92     g_signal_connect(getWidget("updateFrameButton"), "clicked", G_CALLBACK(onUpdateFrameClicked_gui), (gpointer)this);
93     g_signal_connect(getWidget("showTopButton"), "clicked", G_CALLBACK(onShowTopClicked_gui), (gpointer)this);
94     g_signal_connect(getWidget("clearTopButton"), "clicked", G_CALLBACK(onClearTopClicked_gui), (gpointer)this);
95     g_signal_connect(getWidget("searchTopButton"), "clicked", G_CALLBACK(onSearchTopClicked_gui), (gpointer)this);
96     g_signal_connect(getWidget("removeTopButton"), "clicked", G_CALLBACK(onRemoveTopClicked_gui), (gpointer)this);
97     g_signal_connect(getWidget("ignoreTTHSearchCheckButton"), "toggled", G_CALLBACK(onIgnoreTTHSearchToggled_gui), (gpointer)this);
98     g_signal_connect(searchView.get(), "button-press-event", G_CALLBACK(onButtonPressed_gui), (gpointer)this);
99     g_signal_connect(searchView.get(), "button-release-event", G_CALLBACK(onButtonReleased_gui), (gpointer)this);
100     g_signal_connect(searchView.get(), "key-release-event", G_CALLBACK(onKeyReleased_gui), (gpointer)this);
101     g_signal_connect(getWidget("okButton"), "clicked", G_CALLBACK(onOKButtonClicked_gui), (gpointer)this);
102 
103     aSearchColor = WGETS("search-spy-a-color");
104     tSearchColor = WGETS("search-spy-t-color");
105     qSearchColor = WGETS("search-spy-q-color");
106     cSearchColor = WGETS("search-spy-c-color");
107     rSearchColor = WGETS("search-spy-r-color");
108 }
109 
~SearchSpy()110 SearchSpy::~SearchSpy()
111 {
112     WSET("search-spy-frame", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(getWidget("frameSpinButton"))));
113     WSET("search-spy-waiting", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(getWidget("waitingSpinButton"))));
114     WSET("search-spy-top", (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(getWidget("topSpinButton"))));
115 
116     gtk_widget_destroy(getWidget("TopSearchDialog"));
117     g_object_unref(getWidget("menu"));
118 
119     TimerManager::getInstance()->removeListener(this);
120     ClientManager::getInstance()->removeListener(this);
121 }
122 
show()123 void SearchSpy::show()
124 {
125     ClientManager::getInstance()->addListener(this);
126     TimerManager::getInstance()->addListener(this);
127 }
128 
preferences_gui()129 void SearchSpy::preferences_gui()
130 {
131     FrameSize = (SearchType)WGETI("search-spy-frame");
132     Waiting = (guint)WGETI("search-spy-waiting");
133     Top = (guint)WGETI("search-spy-top");
134 
135     resetFrame();
136 
137     // reset colors
138     aSearchColor = WGETS("search-spy-a-color");
139     tSearchColor = WGETS("search-spy-t-color");
140     qSearchColor = WGETS("search-spy-q-color");
141     cSearchColor = WGETS("search-spy-c-color");
142     rSearchColor = WGETS("search-spy-r-color");
143 
144     string color, order;
145     GtkTreeIter iter;
146 
147     for (auto it = searchIters.begin(); it != searchIters.end(); ++it)
148     {
149         iter = it->second;
150         order = searchView.getString(&iter, "order");
151         guint count = searchView.getValue<guint>(&iter, "count");
152 
153         // reset count
154         if (count > Top)
155         {
156             gtk_list_store_set(searchStore, &iter,
157                 searchView.col(_("Count")), Util::toString(Top).c_str(),
158                 searchView.col("count"), Top,
159                 -1);
160         }
161 
162         switch (order[0])
163         {
164             case 'a': color = aSearchColor; break;
165             case 'c': color = cSearchColor; break;
166             case 'r': color = rSearchColor; break;
167             case 't': color = tSearchColor; break;
168             case 'q': color = qSearchColor; break;
169         }
170         gtk_list_store_set(searchStore, &iter, searchView.col("color"), color.c_str(), -1);
171     }
172 
173     // reset spin button
174     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("frameSpinButton")), (double)FrameSize);
175     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("waitingSpinButton")), (double)Waiting);
176     gtk_spin_button_set_value(GTK_SPIN_BUTTON(getWidget("topSpinButton")), (double)Top);
177 }
178 
resetFrame()179 void SearchSpy::resetFrame()
180 {
181     GtkTreeIter iter;
182 
183     if (FrameSize > 0 && searchIters.size() > FrameSize)
184     {
185         SearchType i = 0;
186         gtk_tree_selection_select_all(searchSelection);
187 
188         for (auto it = searchIters.begin(); it != searchIters.end(); ++it)
189         {
190             if (++i > FrameSize)
191                 break;
192 
193             iter = it->second;
194             gtk_tree_selection_unselect_iter(searchSelection, &iter);
195         }
196         onRemoveItemClicked_gui(NULL, (gpointer)this);
197     }
198 }
199 
findIter_gui(const string & search,GtkTreeIter * iter)200 bool SearchSpy::findIter_gui(const string &search, GtkTreeIter *iter)
201 {
202     auto it = searchIters.find(search);
203 
204     if (it != searchIters.end())
205     {
206         if (iter)
207             *iter = it->second;
208 
209         return TRUE;
210     }
211 
212     return FALSE;
213 }
214 
addTop_gui(const string & search,const string & type)215 void SearchSpy::addTop_gui(const string &search, const string &type)
216 {
217     GtkTreeIter iter;
218     GtkTreeModel *m = GTK_TREE_MODEL(topStore);
219     gboolean valid = gtk_tree_model_get_iter_first(m, &iter);
220 
221     while (valid)
222     {
223         string line = topView.getString(&iter, _("Search String"));
224 
225         if (search == line)
226         {
227             return;
228         }
229         valid = gtk_tree_model_iter_next(m, &iter);
230     }
231 
232     gtk_list_store_append(topStore, &iter);
233     gtk_list_store_set(topStore, &iter,
234         topView.col(_("Search String")), search.c_str(),
235         topView.col("type"), type.c_str(),
236         -1);
237 
238     if (WGETB("bold-search-spy"))
239         setUrgent_gui();
240 }
241 
updateFrameSearch_gui(const string search,const string type)242 void SearchSpy::updateFrameSearch_gui(const string search, const string type)
243 {
244     g_return_if_fail(FrameSize > 0 && FrameSize <= 256);
245 
246     GtkTreeIter iter;
247 
248     if (findIter_gui(search, &iter))
249     {
250         uint64_t tick = GET_TICK();
251 
252         if (searchView.getString(&iter, "order")[0] == 't')
253         {
254             updateFrameStatus_gui(NULL, tick);
255             return;
256         }
257 
258         string time = Util::formatTime("%H:%M:%S", GET_TIME());
259         guint count = searchView.getValue<guint>(&iter, "count");
260         string order = "c";
261 
262         if (count >= Top)
263         {
264             addTop_gui(search, type);
265 
266             count = 0;
267             order = "t";
268         }
269         count++;
270         gtk_list_store_set(searchStore, &iter,
271             searchView.col(_("Count")), Util::toString(count).c_str(),
272             searchView.col(_("Time")), time.c_str(),
273             searchView.col("count"), count,
274             searchView.col("tick"), tick,
275             searchView.col("order"), order.c_str(),
276             -1);
277         updateFrameStatus_gui(NULL, tick);
278     }
279     else
280     {
281         string time = Util::formatTime("%H:%M:%S", GET_TIME());
282         uint64_t tick = GET_TICK();
283 
284         if (searchIters.size() >= FrameSize)
285         {
286             if (updateFrameStatus_gui(&iter, tick))
287             {
288                 string oldstring = searchView.getString(&iter, _("Search String"));
289                 searchIters.erase(oldstring);
290                 searchIters.insert(SearchIters::value_type(search, iter));
291 
292                 tick = GET_TICK();
293 
294                 gtk_list_store_set(searchStore, &iter,
295                     searchView.col(_("Search String")), search.c_str(),
296                     searchView.col(_("Count")), "1",
297                     searchView.col(_("Time")), time.c_str(),
298                     searchView.col(_("Status")), _("waiting..."),
299                     searchView.col("type"), type.c_str(),
300                     searchView.col("count"), 1,
301                     searchView.col("tick"), tick,
302                     searchView.col("icon"), GTK_STOCK_FIND,
303                     searchView.col("order"), "r",
304                     searchView.col("color"), rSearchColor.c_str(),
305                     -1);
306             }
307             return;
308         }
309 
310         gtk_list_store_insert_with_values(searchStore, &iter, searchIters.size(),
311             searchView.col(_("Search String")), search.c_str(),
312             searchView.col(_("Count")), "1",
313             searchView.col(_("Time")), time.c_str(),
314             searchView.col("type"), type.c_str(),
315             searchView.col("count"), 1,
316             searchView.col("tick"), tick,
317             searchView.col("order"), "a",
318             -1);
319 
320         searchIters.insert(SearchIters::value_type(search, iter));
321         updateFrameStatus_gui(NULL, tick);
322     }
323 }
324 
updateFrameStatus_gui(GtkTreeIter * iter,uint64_t tick)325 bool SearchSpy::updateFrameStatus_gui(GtkTreeIter *iter, uint64_t tick)
326 {
327     if(!tick)
328         tick = GET_TICK();
329 
330     uint64_t second = (uint64_t)Waiting * 1000;
331     bool n = FALSE;
332     string status, icon;
333     GtkTreeIter itree;
334     string color;
335 
336     for (auto it = searchIters.begin(); it != searchIters.end(); ++it)
337     {
338         itree = it->second;
339         uint64_t gettick = searchView.getValue<uint64_t>(&itree, "tick");
340         string order = searchView.getString(&itree, "order");
341 
342         dcassert(tick >= gettick);
343 
344         if (tick - gettick > second)
345         {
346             if (iter)
347             {
348                 *iter = itree;
349                 n = TRUE;
350             }
351             status = "?";
352             icon = GTK_STOCK_DIALOG_QUESTION;
353 
354             color = qSearchColor;
355             gtk_list_store_set(searchStore, &itree, searchView.col("order"), "q", -1);
356         }
357         else
358         {
359             if (order[0] == 't')
360             {
361                 status = _("top...");
362                 icon = GTK_STOCK_DIALOG_QUESTION;
363             }
364             else
365             {
366                 status = _("waiting...");
367                 icon = GTK_STOCK_FIND;
368             }
369 
370             switch (order[0])
371             {
372                 case 'a': color = aSearchColor; break;
373                 case 'c': color = cSearchColor; break;
374                 case 'r': color = rSearchColor; break;
375                 case 't': color = tSearchColor; break;
376                 default:  color = qSearchColor; // fix don't know color
377             }
378         }
379         gtk_list_store_set(searchStore, &itree,
380             searchView.col(_("Status")), status.c_str(),
381             searchView.col("icon"), icon.c_str(),
382             searchView.col("color"), color.c_str(),
383             -1);
384     }
385 
386     return n;
387 }
388 
updateFrameStatus_gui()389 void SearchSpy::updateFrameStatus_gui()
390 {
391     updateFrameStatus_gui(NULL, uint64_t(0));
392     setStatus_gui(_("Update frame search"));
393 }
394 
setStatus_gui(const string text)395 void SearchSpy::setStatus_gui(const string text)
396 {
397     if (!text.empty())
398     {
399         GtkWidget *status = getWidget("statusbar");
400         gtk_statusbar_pop(GTK_STATUSBAR(status), 0);
401         gtk_statusbar_push(GTK_STATUSBAR(status), 0, ("[" + Util::getShortTimeString() + "] " + text).c_str());
402     }
403 }
404 
onOKButtonClicked_gui(GtkWidget * widget,gpointer data)405 void SearchSpy::onOKButtonClicked_gui(GtkWidget *widget, gpointer data)
406 {
407     SearchSpy *s =  (SearchSpy *) data;
408 
409     s->FrameSize = (SearchType)gtk_spin_button_get_value(GTK_SPIN_BUTTON(s->getWidget("frameSpinButton")));
410     s->Waiting = (guint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(s->getWidget("waitingSpinButton")));
411     s->Top = (guint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(s->getWidget("topSpinButton")));
412     s->resetFrame();
413     s->resetCount();
414 
415     s->setStatus_gui(_("top/waiting/frame: ") + Util::toString(s->Top) + "/" + Util::toString(s->Waiting) + "/" +
416         Util::toString(s->FrameSize));
417 
418     WSET("search-spy-frame", int(s->FrameSize));
419     WSET("search-spy-waiting", int(s->Waiting));
420     WSET("search-spy-top", int(s->Top));
421 }
422 
resetCount()423 void SearchSpy::resetCount()
424 {
425     GtkTreeIter iter;
426     for (auto it = searchIters.begin(); it != searchIters.end(); ++it)
427     {
428         iter = it->second;
429         guint count = searchView.getValue<guint>(&iter, "count");
430 
431         if (count > Top)
432         {
433             gtk_list_store_set(searchStore, &iter,
434                 searchView.col(_("Count")), Util::toString(Top).c_str(),
435                 searchView.col("count"), Top,
436                 -1);
437         }
438     }
439 }
440 
onShowTopClicked_gui(GtkWidget * widget,gpointer data)441 void SearchSpy::onShowTopClicked_gui(GtkWidget *widget, gpointer data)
442 {
443     SearchSpy *s = (SearchSpy *)data;
444 
445     GtkWidget *dialog = s->getWidget("TopSearchDialog");
446     gint response = gtk_dialog_run(GTK_DIALOG(dialog));
447 
448     // if the dialog gets programmatically destroyed.
449     if (response == GTK_RESPONSE_NONE)
450         return;
451     gtk_widget_hide(dialog);
452 }
453 
onClearTopClicked_gui(GtkWidget * widget,gpointer data)454 void SearchSpy::onClearTopClicked_gui(GtkWidget *widget, gpointer data)
455 {
456     SearchSpy *s = (SearchSpy *)data;
457     gtk_list_store_clear(s->topStore);
458 }
459 
onSearchTopClicked_gui(GtkWidget * widget,gpointer data)460 void SearchSpy::onSearchTopClicked_gui(GtkWidget *widget, gpointer data)
461 {
462     SearchSpy *s = (SearchSpy *)data;
463 
464     GtkTreeIter iter;
465     GtkTreeSelection *selection = gtk_tree_view_get_selection(s->topView.get());
466 
467     if (gtk_tree_selection_get_selected(selection, NULL, &iter))
468     {
469         string type = s->topView.getString(&iter, "type");
470         string search = s->topView.getString(&iter, _("Search String"));
471         Search *ss = WulforManager::get()->getMainWindow()->addSearch_gui();
472 
473         if (type[0] == 't')
474         {
475             ss->putValue_gui(search, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
476         }
477         else if (type[0] == 's')
478         {
479             ss->putValue_gui(search, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_ANY);
480         }
481     }
482 }
483 
onRemoveTopClicked_gui(GtkWidget * widget,gpointer data)484 void SearchSpy::onRemoveTopClicked_gui(GtkWidget *widget, gpointer data)
485 {
486     SearchSpy *s = (SearchSpy *)data;
487 
488     GtkTreeIter iter;
489     GtkTreeSelection *selection = gtk_tree_view_get_selection(s->topView.get());
490 
491     if (gtk_tree_selection_get_selected(selection, NULL, &iter))
492     {
493         gtk_list_store_remove(s->topStore, &iter);
494     }
495 }
496 
onClearFrameClicked_gui(GtkWidget * widget,gpointer data)497 void SearchSpy::onClearFrameClicked_gui(GtkWidget *widget, gpointer data)
498 {
499     SearchSpy *s = (SearchSpy *)data;
500 
501     gtk_list_store_clear(s->searchStore);
502     s->searchIters.clear();
503     s->setStatus_gui(_("Clear frame search"));
504 }
505 
onUpdateFrameClicked_gui(GtkWidget * widget,gpointer data)506 void SearchSpy::onUpdateFrameClicked_gui(GtkWidget *widget, gpointer data)
507 {
508     SearchSpy *s = (SearchSpy *)data;
509 
510     s->updateFrameStatus_gui();
511 }
512 
onIgnoreTTHSearchToggled_gui(GtkWidget * widget,gpointer data)513 void SearchSpy::onIgnoreTTHSearchToggled_gui(GtkWidget *widget, gpointer data)
514 {
515     gboolean toggle = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
516     WSET("spyframe-ignore-tth-searches",toggle);
517 }
518 
onRemoveItemClicked_gui(GtkMenuItem * item,gpointer data)519 void SearchSpy::onRemoveItemClicked_gui(GtkMenuItem *item, gpointer data)
520 {
521     SearchSpy *s = (SearchSpy *)data;
522 
523     if (gtk_tree_selection_count_selected_rows(s->searchSelection) > 0)
524     {
525         StringList search;
526         GtkTreeIter iter;
527         GtkTreePath *path;
528         GList *list = gtk_tree_selection_get_selected_rows(s->searchSelection, NULL);
529 
530         for (GList *i = list; i; i = i->next)
531         {
532             path = (GtkTreePath *) i->data;
533             if (gtk_tree_model_get_iter(GTK_TREE_MODEL(s->searchStore), &iter, path))
534             {
535                 search.push_back(s->searchView.getString(&iter, _("Search String")));
536             }
537             gtk_tree_path_free(path);
538         }
539         g_list_free(list);
540 
541         for (StringIterC it = search.begin(); it != search.end(); ++it)
542         {
543             if (s->findIter_gui(*it, &iter))
544             {
545                 gtk_list_store_remove(s->searchStore, &iter);
546                 s->searchIters.erase(*it);
547             }
548         }
549     }
550 }
551 
onSearchItemClicked_gui(GtkMenuItem * item,gpointer data)552 void SearchSpy::onSearchItemClicked_gui(GtkMenuItem *item, gpointer data)
553 {
554     SearchSpy *s = (SearchSpy *)data;
555 
556     if (gtk_tree_selection_count_selected_rows(s->searchSelection) == 1)
557     {
558         GList *list = gtk_tree_selection_get_selected_rows(s->searchSelection, NULL);
559 
560         if (list)
561         {
562             GtkTreeIter iter;
563             GtkTreePath *path = (GtkTreePath *) list->data;
564 
565             if (gtk_tree_model_get_iter(GTK_TREE_MODEL(s->searchStore), &iter, path))
566             {
567                 string type = s->searchView.getString(&iter, "type");
568                 string search = s->searchView.getString(&iter, _("Search String"));
569                 Search *ss = WulforManager::get()->getMainWindow()->addSearch_gui();
570 
571                 if (type[0] == 't')
572                 {
573                     ss->putValue_gui(search, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
574                 }
575                 else if (type[0] == 's')
576                 {
577                     ss->putValue_gui(search, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_ANY);
578                 }
579             }
580             gtk_tree_path_free(path);
581         }
582         g_list_free(list);
583     }
584 }
585 
onButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)586 gboolean SearchSpy::onButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
587 {
588     SearchSpy *s = (SearchSpy *)data;
589     s->previous = event->type;
590 
591     if (event->button == 3)
592     {
593         GtkTreePath *path;
594 
595         if (gtk_tree_view_get_path_at_pos(s->searchView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
596         {
597             bool selected = gtk_tree_selection_path_is_selected(s->searchSelection, path);
598             gtk_tree_path_free(path);
599 
600             if (selected)
601                 return TRUE;
602         }
603     }
604     return FALSE;
605 }
606 
onButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)607 gboolean SearchSpy::onButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
608 {
609     SearchSpy *s = (SearchSpy *)data;
610 
611     if (gtk_tree_selection_count_selected_rows(s->searchSelection) > 0)
612     {
613         if (event->button == 1 && s->previous == GDK_2BUTTON_PRESS)
614         {
615             // search string/tth
616             s->onSearchItemClicked_gui(NULL, data);
617         }
618         else if (event->button == 3 && event->type == GDK_BUTTON_RELEASE)
619         {
620             // show menu
621             gtk_menu_popup(GTK_MENU(s->getWidget("menu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
622         }
623     }
624 
625     return FALSE;
626 }
627 
onKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)628 gboolean SearchSpy::onKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
629 {
630     SearchSpy *s = (SearchSpy *)data;
631 
632     if (gtk_tree_selection_count_selected_rows(s->searchSelection) > 0)
633     {
634         if (event->keyval == GDK_Delete || event->keyval == GDK_BackSpace)
635         {
636             s->onRemoveItemClicked_gui(NULL, data);
637         }
638         else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
639         {
640             gtk_menu_popup(GTK_MENU(s->getWidget("menu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
641         }
642     }
643 
644     return FALSE;
645 }
646 
on(ClientManagerListener::IncomingSearch,const string & s)647 void SearchSpy::on(ClientManagerListener::IncomingSearch, const string& s) noexcept
648 {
649     if(!WGETB("spyframe-ignore-tth-searches") && s.compare(0, 4, "TTH:"))
650         return;
651 
652     string search, type;
653     if(!s.compare(0, 4, "TTH:"))
654     {
655         type = "t";
656         search = s.substr(4);
657     }
658     else
659     {
660         type = "s";
661         search = s;
662         string::size_type i;
663         while((i = search.find("$")) != string::npos)
664             search[i] = ' ';
665     }
666     typedef Func2<SearchSpy, string, string> F2;
667     F2 *func = new F2(this, &SearchSpy::updateFrameSearch_gui, search, type);
668     WulforManager::get()->dispatchGuiFunc(func);
669 }
670 
on(TimerManagerListener::Minute,uint64_t tick)671 void SearchSpy::on(TimerManagerListener::Minute, uint64_t tick) noexcept
672 {
673     typedef Func0<SearchSpy> F0;
674     F0 *func = new F0(this, &SearchSpy::updateFrameStatus_gui);
675     WulforManager::get()->dispatchGuiFunc(func);
676 }
677