1 /***************************************************************************
2 *                                                                         *
3 *   This program is free software; you can redistribute it and/or modify  *
4 *   it under the terms of the GNU General Public License as published by  *
5 *   the Free Software Foundation; either version 3 of the License, or     *
6 *   (at your option) any later version.                                   *
7 *                                                                         *
8 ***************************************************************************/
9 
10 #include <QComboBox>
11 #include <QTreeView>
12 #include <QAction>
13 #include <QTextCodec>
14 #include <QDir>
15 #include <QItemSelectionModel>
16 #include <QFileDialog>
17 #include <QClipboard>
18 #include <QHeaderView>
19 #include <QKeyEvent>
20 #include <QMessageBox>
21 #include <QStringListModel>
22 #include <QKeyEvent>
23 #include <QTimer>
24 #include <QShortcut>
25 #include <QStringListModel>
26 #include <QCompleter>
27 #include <QListWidget>
28 #include <QListWidgetItem>
29 
30 #include "SearchFrame.h"
31 #include "MainWindow.h"
32 #include "HubFrame.h"
33 #include "HubManager.h"
34 #include "SearchModel.h"
35 #include "SearchBlacklist.h"
36 #include "SearchBlacklistDialog.h"
37 #include "WulforUtil.h"
38 #include "Magnet.h"
39 #include "ArenaWidgetManager.h"
40 #include "ArenaWidgetFactory.h"
41 #include "DownloadToHistory.h"
42 #include "GlobalTimer.h"
43 
44 #include "dcpp/CID.h"
45 #include "dcpp/ClientManager.h"
46 #include "dcpp/FavoriteManager.h"
47 #include "dcpp/StringTokenizer.h"
48 #include "dcpp/SettingsManager.h"
49 #include "dcpp/Encoder.h"
50 #include "dcpp/UserCommand.h"
51 
52 #include <QtDebug>
53 
54 using namespace dcpp;
55 
56 class SearchFramePrivate{
57 public:
58     QString arena_title;
59     QString token;
60 
61     TStringList currentSearch;
62 
63     qulonglong dropped;
64     qulonglong results;
65     SearchFrame::AlreadySharedAction filterShared;
66     bool withFreeSlots;
67 
68     QStringList hubs;
69     QStringList searchHistory;
70 
71     QList<dcpp::Client*> client_list;
72 
73     QTimer *timer;
74 
75     QCompleter *completer;
76 
77     QMenu *arena_menu;
78 
79     QShortcut *focusShortcut;
80 
81     bool saveFileType;
82 
83     SearchModel *model;
84     SearchStringListModel *str_model;
85     SearchProxyModel *proxy;
86 
87     bool isHash;
88     bool stop;
89     int left_pane_old_size;
90 
91     QString target;
92     uint64_t searchStartTime;
93     uint64_t searchEndTime;
94     bool waitingResults;
95 };
96 
data(const QModelIndex & index,int role) const97 QVariant SearchStringListModel::data(const QModelIndex &index, int role) const{
98     if (role != Qt::CheckStateRole || !index.isValid())
99         return QStringListModel::data(index, role);
100 
101     return (checked.contains(index.data().toString())? Qt::Checked : Qt::Unchecked);
102 }
103 
setData(const QModelIndex & index,const QVariant & value,int role)104 bool SearchStringListModel::setData(const QModelIndex &index, const QVariant &value, int role){
105     if (role != Qt::CheckStateRole || !index.isValid())
106         return QStringListModel::setData(index, value, role);
107 
108     if (value.toInt() == Qt::Checked)
109         checked.push_back(index.data().toString());
110     else if (checked.contains(index.data().toString()))
111         checked.removeAt(checked.indexOf(index.data().toString()));
112 
113     return true;
114 }
115 
Menu()116 SearchFrame::Menu::Menu(){
117     WulforUtil *WU = WulforUtil::getInstance();
118 
119     menu = new QMenu();
120 
121     magnet_menu = new QMenu(tr("Magnet"));
122 
123     QAction *down       = new QAction(tr("Download"), NULL);
124     down->setIcon(WU->getPixmap(WulforUtil::eiDOWNLOAD));
125 
126     down_to             = new QMenu(tr("Download to..."));
127     down_to->setIcon(WU->getPixmap(WulforUtil::eiDOWNLOAD_AS));
128 
129     QAction *down_wh    = new QAction(tr("Download Whole Directory"), NULL);
130     down_wh->setIcon(WU->getPixmap(WulforUtil::eiDOWNLOAD));
131 
132     down_wh_to          = new QMenu(tr("Download Whole Directory to..."));
133     down_wh_to->setIcon(WU->getPixmap(WulforUtil::eiDOWNLOAD_AS));
134 
135     QAction *sep        = new QAction(menu);
136     sep->setSeparator(true);
137 
138     QAction *find_tth   = new QAction(tr("Search TTH"), NULL);
139     find_tth->setIcon(WU->getPixmap(WulforUtil::eiFILEFIND));
140 
141     QAction *magnet     = new QAction(tr("Copy magnet"), NULL);
142     magnet->setIcon(WU->getPixmap(WulforUtil::eiEDITCOPY));
143 
144     QAction *magnet_web     = new QAction(tr("Copy web-magnet"), NULL);
145     magnet_web->setIcon(WU->getPixmap(WulforUtil::eiEDITCOPY));
146 
147     QAction *magnet_info    = new QAction(tr("Properties of magnet"), NULL);
148     magnet_info->setIcon(WU->getPixmap(WulforUtil::eiDOWNLOAD));
149 
150     QAction *browse     = new QAction(tr("Browse files"), NULL);
151     browse->setIcon(WU->getPixmap(WulforUtil::eiFOLDER_BLUE));
152 
153     QAction *match      = new QAction(tr("Match Queue"), NULL);
154     match->setIcon(WU->getPixmap(WulforUtil::eiDOWN));
155 
156     QAction *send_pm    = new QAction(tr("Send Private Message"), NULL);
157     send_pm->setIcon(WU->getPixmap(WulforUtil::eiMESSAGE));
158 
159     QAction *add_to_fav = new QAction(tr("Add to favorites"), NULL);
160     add_to_fav->setIcon(WU->getPixmap(WulforUtil::eiBOOKMARK_ADD));
161 
162     QAction *grant      = new QAction(tr("Grant extra slot"), NULL);
163     grant->setIcon(WU->getPixmap(WulforUtil::eiEDITADD));
164 
165     QAction *sep1       = new QAction(menu);
166     sep1->setSeparator(true);
167 
168     QAction *sep2       = new QAction(menu);
169     sep2->setSeparator(true);
170 
171     QAction *sep3       = new QAction(menu);
172     sep3->setSeparator(true);
173 
174     QAction *rem_queue  = new QAction(tr("Remove from Queue"), NULL);
175     rem_queue->setIcon(WU->getPixmap(WulforUtil::eiEDITDELETE));
176 
177     QAction *rem        = new QAction(tr("Remove"), NULL);
178     rem->setIcon(WU->getPixmap(WulforUtil::eiEDITDELETE));
179 
180     black_list_menu     = new QMenu(tr("Blacklist..."));
181     black_list_menu->setIcon(WU->getPixmap(WulforUtil::eiFILTER));
182 
183     QAction *blacklist = new QAction(tr("Blacklist"), NULL);
184     blacklist->setIcon(WU->getPixmap(WulforUtil::eiFILTER));
185 
186     QAction *add_to_blacklist = new QAction(tr("Add to Blacklist"), NULL);
187     add_to_blacklist->setIcon(WU->getPixmap(WulforUtil::eiEDITADD));
188 
189     black_list_menu->addActions(QList<QAction*>()
190                     << add_to_blacklist << blacklist);
191 
192     magnet_menu->addActions(QList<QAction*>()
193                     << magnet << magnet_web << sep3 << magnet_info);
194 
195     actions.insert(down, Download);
196     actions.insert(down_wh, DownloadWholeDir);
197     actions.insert(find_tth, SearchTTH);
198     actions.insert(magnet, Magnet);
199     actions.insert(magnet_web, MagnetWeb);
200     actions.insert(magnet_info, MagnetInfo);
201     actions.insert(browse, Browse);
202     actions.insert(match, MatchQueue);
203     actions.insert(send_pm, SendPM);
204     actions.insert(add_to_fav, AddToFav);
205     actions.insert(grant, GrantExtraSlot);
206     actions.insert(rem_queue, RemoveFromQueue);
207     actions.insert(rem, Remove);
208     actions.insert(blacklist, Blacklist);
209     actions.insert(add_to_blacklist, AddToBlacklist);
210 
211     action_list   << down
212                   << down_wh
213                   << sep
214                   << find_tth
215                   << browse
216                   << match
217                   << send_pm
218                   << add_to_fav
219                   << grant
220                   << sep1
221                   << rem_queue
222                   << rem
223                   << sep2;
224 }
225 
~Menu()226 SearchFrame::Menu::~Menu(){
227     qDeleteAll(action_list);
228 
229     magnet_menu->deleteLater();
230     menu->deleteLater();
231     down_to->deleteLater();
232     down_wh_to->deleteLater();
233     black_list_menu->deleteLater();
234 }
235 
exec(QStringList list=QStringList ())236 SearchFrame::Menu::Action SearchFrame::Menu::exec(QStringList list = QStringList()){
237     for (const auto &a : action_list)
238         a->setParent(NULL);
239 
240     qDeleteAll(down_to->actions());
241     qDeleteAll(down_wh_to->actions());
242     down_to->clear();
243     down_wh_to->clear();
244 
245     QString aliases, paths;
246 
247     aliases = QByteArray::fromBase64(WSGET(WS_DOWNLOADTO_ALIASES).toUtf8());
248     paths   = QByteArray::fromBase64(WSGET(WS_DOWNLOADTO_PATHS).toUtf8());
249 
250     QStringList a = aliases.split("\n", QString::SkipEmptyParts);
251     QStringList p = paths.split("\n", QString::SkipEmptyParts);
252 
253     QStringList temp_pathes = DownloadToDirHistory::get();
254 
255     if (!temp_pathes.isEmpty()){
256         for (const auto &t : temp_pathes){
257             QAction *act = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), QDir(t).dirName(), down_to);
258             act->setToolTip(t);
259             act->setData(t);
260 
261             down_to->addAction(act);
262 
263             QAction *act1 = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), QDir(t).dirName(), down_to);
264             act1->setToolTip(t);
265             act1->setData(t);
266 
267             down_wh_to->addAction(act1);
268         }
269 
270         down_to->addSeparator();
271         down_wh_to->addSeparator();
272     }
273 
274     if (a.size() == p.size() && !a.isEmpty()){
275         for (int i = 0; i < a.size(); i++){
276             QAction *act = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), a.at(i), down_to);
277             act->setData(p.at(i));
278 
279             down_to->addAction(act);
280 
281             QAction *act1 = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), a.at(i), down_to);
282             act1->setData(p.at(i));
283 
284             down_wh_to->addAction(act1);
285         }
286 
287         down_to->addSeparator();
288         down_wh_to->addSeparator();
289     }
290 
291     QAction *browse = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), tr("Browse"), down_to);
292     browse->setData("");
293 
294     QAction *browse1 = new QAction(WICON(WulforUtil::eiFOLDER_BLUE), tr("Browse"), down_to);
295     browse->setData("");
296 
297     down_to->addAction(browse);
298     down_wh_to->addAction(browse1);
299 
300     menu->clear();
301     menu->addActions(action_list);
302     menu->insertMenu(action_list.at(1), down_to);
303     menu->insertMenu(action_list.at(2), down_wh_to);
304     menu->insertMenu(action_list.at(5), magnet_menu);
305     menu->insertMenu(action_list.at(12),black_list_menu);
306 
307     QScopedPointer<QMenu> userm(buildUserCmdMenu(list));
308 
309     if (!userm.isNull() && !userm->actions().isEmpty())
310         menu->addMenu(userm.data());
311 
312     QAction *ret = menu->exec(QCursor::pos());
313 
314     if (actions.contains(ret)) {
315         return actions.value(ret);
316     } else if (down_to->actions().contains(ret)) {
317         downToPath = ret->data().toString();
318         return DownloadTo;
319     } else if (down_wh_to->actions().contains(ret)) {
320         downToPath = ret->data().toString();
321         return DownloadWholeDirTo;
322     } else if (ret && ret->data().canConvert(QVariant::Int)) {
323         uc_cmd_id = ret->data().toInt();
324         return UserCommands;
325     } else {
326         return None;
327     }
328 }
329 
buildUserCmdMenu(QList<QString> hub_list)330 QMenu *SearchFrame::Menu::buildUserCmdMenu(QList<QString> hub_list){
331     if (hub_list.empty())
332         return NULL;
333 
334     return WulforUtil::getInstance()->buildUserCmdMenu(hub_list, UserCommand::CONTEXT_SEARCH);
335 }
336 
addTempPath(const QString & path)337 void SearchFrame::Menu::addTempPath(const QString &path){
338     QStringList temp_pathes = DownloadToDirHistory::get();
339     temp_pathes.push_front(path);
340 
341     DownloadToDirHistory::put(temp_pathes);
342 }
343 
SearchFrame(QWidget * parent)344 SearchFrame::SearchFrame(QWidget *parent): QWidget(parent), d_ptr(new SearchFramePrivate())
345 {
346     if (!SearchBlacklist::getInstance())
347         SearchBlacklist::newInstance();
348 
349     Q_D(SearchFrame);
350 
351     d->isHash = false;
352     d->arena_menu = NULL;
353     d->timer = NULL;
354     d->dropped = 0L;
355     d->results = 0L;
356     d->filterShared = SearchFrame::None;
357     d->withFreeSlots = false;
358     d->saveFileType = true;
359     d->proxy = NULL;
360     d->completer = NULL;
361     d->stop = false;
362     d->arena_title = tr("Search");
363     d->searchStartTime = 0;
364     d->searchEndTime = 1;
365     d->waitingResults = false;
366 
367     setupUi(this);
368 
369     init();
370 
371     ClientManager* clientMgr = ClientManager::getInstance();
372 
373 #ifdef DO_NOT_USE_MUTEX
374     clientMgr->lock();
375 #else // DO_NOT_USE_MUTEX
376     auto lock = clientMgr->lock();
377 #endif // DO_NOT_USE_MUTEX
378     clientMgr->addListener(this);
379     Client::List& clients = clientMgr->getClients();
380 
381     for (const auto &client : clients) {
382         if(!client->isConnected())
383             continue;
384 
385         d->hubs.push_back(_q(client->getHubUrl()));
386         d->client_list.push_back(client);
387     }
388 
389 #ifdef DO_NOT_USE_MUTEX
390     clientMgr->unlock();
391 #endif // DO_NOT_USE_MUTEX
392 
393     d->str_model->setStringList(d->hubs);
394 
395 
396     for (int i = 0; i < d->str_model->rowCount(); i++)
397        d-> str_model->setData(d->str_model->index(i, 0), Qt::Checked, Qt::CheckStateRole);
398 
399     SearchManager::getInstance()->addListener(this);
400 }
401 
~SearchFrame()402 SearchFrame::~SearchFrame(){
403     Menu::deleteInstance();
404 
405     Q_D(SearchFrame);
406 
407     treeView_RESULTS->setModel(NULL);
408 
409     if (d->completer)
410         d->completer->deleteLater();
411 
412     if (d->proxy)
413         d->proxy->deleteLater();
414 
415     d->arena_menu->deleteLater();
416 
417     delete d->model;
418     delete d->timer;
419 
420     delete d_ptr;
421 }
422 
closeEvent(QCloseEvent * e)423 void SearchFrame::closeEvent(QCloseEvent *e){
424     SearchManager::getInstance()->removeListener(this);
425     ClientManager::getInstance()->removeListener(this);
426 
427     Q_D(SearchFrame);
428 
429     if (d->timer)
430         d->timer->stop();
431 
432     save();
433 
434     setAttribute(Qt::WA_DeleteOnClose);
435 
436     QWidget::disconnect(this, NULL, this, NULL);
437 
438     e->accept();
439 }
440 
eventFilter(QObject * obj,QEvent * e)441 bool SearchFrame::eventFilter(QObject *obj, QEvent *e){
442     if (e->type() == QEvent::KeyRelease){
443         QKeyEvent *k_e = reinterpret_cast<QKeyEvent*>(e);
444 
445         if (static_cast<LineEdit*>(obj) == lineEdit_FILTER && k_e->key() == Qt::Key_Escape){
446             lineEdit_FILTER->clear();
447 
448             requestFilter();
449 
450             return true;
451         }
452     }
453 
454     return QWidget::eventFilter(obj, e);
455 }
456 
init()457 void SearchFrame::init(){
458     Q_D(SearchFrame);
459 
460     d->model = new SearchModel(NULL);
461     d->str_model = new SearchStringListModel(this);
462 
463     for (int i = 0; i < d->model->columnCount(); i++)
464         comboBox_FILTERCOLUMNS->addItem(d->model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
465 
466     comboBox_FILTERCOLUMNS->setCurrentIndex(COLUMN_SF_FILENAME);
467 
468     frame_FILTER->setVisible(false);
469 
470     pushButton_STOP->hide();
471 
472     toolButton_CLOSEFILTER->setIcon(WICON(WulforUtil::eiEDITDELETE));
473 
474     treeView_RESULTS->setModel(d->model);
475     treeView_RESULTS->setContextMenuPolicy(Qt::CustomContextMenu);
476     treeView_RESULTS->header()->setContextMenuPolicy(Qt::CustomContextMenu);
477 
478     treeView_HUBS->setModel(d->str_model);
479 
480     d->arena_menu = new QMenu(this->windowTitle());
481     QAction *close_wnd = new QAction(WICON(WulforUtil::eiFILECLOSE), tr("Close"), d->arena_menu);
482     d->arena_menu->addAction(close_wnd);
483     const SettingsManager::SearchTypes &searchTypes = SettingsManager::getInstance()->getSearchTypes();
484     QStringList filetypes;
485     // Predefined
486     for (int i = SearchManager::TYPE_ANY; i < SearchManager::TYPE_LAST; i++)
487     {
488             filetypes << _q(SearchManager::getTypeStr(i));
489     }
490 
491     // Customs
492     for (const auto &i : searchTypes)
493     {
494         string type = i.first;
495         if (!(type.size() == 1 && type[0] >= '1' && type[0] <= '7'))
496         {
497                 filetypes << _q(type);
498         }
499     }
500     comboBox_FILETYPES->addItems(filetypes);
501     comboBox_FILETYPES->setCurrentIndex(0);
502 
503     QList<WulforUtil::Icons> icons;
504     icons   << WulforUtil::eiFILETYPE_UNKNOWN  << WulforUtil::eiFILETYPE_MP3         << WulforUtil::eiFILETYPE_ARCHIVE
505             << WulforUtil::eiFILETYPE_DOCUMENT << WulforUtil::eiFILETYPE_APPLICATION << WulforUtil::eiFILETYPE_PICTURE
506             << WulforUtil::eiFILETYPE_VIDEO    << WulforUtil::eiFOLDER_BLUE          << WulforUtil::eiFIND
507             << WulforUtil::eiFILETYPE_ARCHIVE;
508 
509     for (int i = 0; i < icons.size(); i++)
510         comboBox_FILETYPES->setItemIcon(i, WICON(icons.at(i)));
511 
512     QString     raw  = QByteArray::fromBase64(WSGET(WS_SEARCH_HISTORY).toUtf8());
513     d->searchHistory = raw.replace("\r","").split('\n', QString::SkipEmptyParts);
514 
515     QMenu *m = new QMenu();
516 
517     for (const auto &s : d->searchHistory)
518         m->addAction(s);
519 
520     d->focusShortcut = new QShortcut(QKeySequence(Qt::Key_F6), this);
521     d->focusShortcut->setContext(Qt::WidgetWithChildrenShortcut);
522 
523     lineEdit_SEARCHSTR->setMenu(m);
524     lineEdit_SEARCHSTR->setPixmap(WICON(WulforUtil::eiEDITADD).scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
525 
526     lineEdit_FILTER->installEventFilter(this);
527 
528     connect(this, SIGNAL(coreClientConnected(QString)),    this, SLOT(onHubAdded(QString)), Qt::QueuedConnection);
529     connect(this, SIGNAL(coreClientDisconnected(QString)), this, SLOT(onHubRemoved(QString)),Qt::QueuedConnection);
530     connect(this, SIGNAL(coreClientUpdated(QString)),      this, SLOT(onHubChanged(QString)), Qt::QueuedConnection);
531     connect(this, SIGNAL(coreSR(VarMap)),                  this, SLOT(addResult(VarMap)), Qt::QueuedConnection);
532 
533     connect(d->focusShortcut, SIGNAL(activated()), lineEdit_SEARCHSTR, SLOT(setFocus()));
534     connect(d->focusShortcut, SIGNAL(activated()), lineEdit_SEARCHSTR, SLOT(selectAll()));
535     connect(close_wnd, SIGNAL(triggered()), this, SLOT(slotClose()));
536     connect(pushButton_SEARCH, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
537     connect(pushButton_STOP, SIGNAL(clicked()), this, SLOT(slotStopSearch()));
538     connect(pushButton_CLEAR, SIGNAL(clicked()), this, SLOT(slotClear()));
539     connect(treeView_RESULTS, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotResultDoubleClicked(QModelIndex)));
540     connect(treeView_RESULTS, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint)));
541     connect(treeView_RESULTS->header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu(QPoint)));
542     connect(GlobalTimer::getInstance(), SIGNAL(second()), this, SLOT(slotTimer()));
543     connect(pushButton_SIDEPANEL, SIGNAL(clicked()), this, SLOT(slotToggleSidePanel()));
544     connect(lineEdit_SEARCHSTR, SIGNAL(returnPressed()), this, SLOT(slotStartSearch()));
545     connect(lineEdit_SIZE,      SIGNAL(returnPressed()), this, SLOT(slotStartSearch()));
546     connect(comboBox_FILETYPES, SIGNAL(currentIndexChanged(int)), lineEdit_SEARCHSTR, SLOT(setFocus()));
547     connect(comboBox_FILETYPES, SIGNAL(currentIndexChanged(int)), lineEdit_SEARCHSTR, SLOT(selectAll()));
548     connect(toolButton_CLOSEFILTER, SIGNAL(clicked()), this, SLOT(slotFilter()));
549     connect(comboBox_FILTERCOLUMNS, SIGNAL(currentIndexChanged(int)), lineEdit_FILTER, SLOT(selectAll()));
550     connect(comboBox_FILTERCOLUMNS, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeProxyColumn(int)));
551 
552     connect(WulforSettings::getInstance(), SIGNAL(strValueChanged(QString,QString)), this, SLOT(slotSettingsChanged(QString,QString)));
553 
554     load();
555 
556     setAttribute(Qt::WA_DeleteOnClose);
557 
558     QList<int> panes = splitter->sizes();
559     d->left_pane_old_size = panes[0];
560 
561     lineEdit_SEARCHSTR->setFocus();
562 }
563 
load()564 void SearchFrame::load(){
565     Q_D(SearchFrame);
566 
567     treeView_RESULTS->header()->restoreState(QByteArray::fromBase64(WSGET(WS_SEARCH_STATE).toUtf8()));
568     treeView_RESULTS->setSortingEnabled(true);
569 
570     d->filterShared = static_cast<SearchFrame::AlreadySharedAction>(WIGET(WI_SEARCH_SHARED_ACTION));
571 
572     comboBox_SHARED->setCurrentIndex(static_cast<int>(d->filterShared));
573 
574     checkBox_FILTERSLOTS->setChecked(WBGET(WB_SEARCHFILTER_NOFREE));
575     checkBox_HIDEPANEL->setChecked(WBGET(WB_SEARCH_DONTHIDEPANEL));
576 
577     comboBox_FILETYPES->setCurrentIndex(WIGET(WI_SEARCH_LAST_TYPE));
578 
579     treeView_RESULTS->sortByColumn(WIGET(WI_SEARCH_SORT_COLUMN), WulforUtil::getInstance()->intToSortOrder(WIGET(WI_SEARCH_SORT_ORDER)));
580 
581     QString raw = QByteArray::fromBase64(WSGET(WS_SEARCH_HISTORY).toUtf8());
582     QStringList list = raw.replace("\r","").split('\n', QString::SkipEmptyParts);
583 
584     d->completer = new QCompleter(list, lineEdit_SEARCHSTR);
585     d->completer->setCaseSensitivity(Qt::CaseInsensitive);
586     d->completer->setWrapAround(false);
587 
588     lineEdit_SEARCHSTR->setCompleter(d->completer);
589 }
590 
save()591 void SearchFrame::save(){
592     Q_D(SearchFrame);
593 
594     WSSET(WS_SEARCH_STATE, treeView_RESULTS->header()->saveState().toBase64());
595     WISET(WI_SEARCH_SORT_COLUMN, d->model->getSortColumn());
596     WISET(WI_SEARCH_SORT_ORDER, WulforUtil::getInstance()->sortOrderToInt(d->model->getSortOrder()));
597     WISET(WI_SEARCH_SHARED_ACTION, static_cast<int>(d->filterShared));
598 
599     if (d->saveFileType)
600         WISET(WI_SEARCH_LAST_TYPE, comboBox_FILETYPES->currentIndex());
601 
602     WBSET(WB_SEARCHFILTER_NOFREE, checkBox_FILTERSLOTS->isChecked());
603     WBSET(WB_SEARCH_DONTHIDEPANEL, checkBox_HIDEPANEL->isChecked());
604 }
605 
initSecond()606 void SearchFrame::initSecond(){
607     /*Q_D(SearchFrame);
608 
609     if (!d->timer){
610         d->timer = new QTimer(this);
611         d->timer->setInterval(1000);
612         d->timer->setSingleShot(true);
613 
614         connect(d->timer, SIGNAL(timeout()), this, SLOT(timerTick()));
615     }
616 
617     d->timer->start();*/
618 }
619 
getWidget()620 QWidget *SearchFrame::getWidget(){
621     return this;
622 }
623 
getArenaTitle()624 QString SearchFrame::getArenaTitle(){
625     Q_D(SearchFrame);
626 
627     return d->arena_title;
628 }
629 
getArenaShortTitle()630 QString SearchFrame::getArenaShortTitle(){
631     return getArenaTitle();
632 }
633 
getMenu()634 QMenu *SearchFrame::getMenu(){
635     Q_D(SearchFrame);
636 
637     return d->arena_menu;
638 }
639 
getPixmap()640 const QPixmap &SearchFrame::getPixmap(){
641     return WICON(WulforUtil::eiFILEFIND);
642 }
643 
download(const SearchFrame::VarMap & params)644 void SearchFrame::download(const SearchFrame::VarMap &params){
645     string target, cid, filename, hubUrl;
646     int64_t size;
647 
648     target      = params["TARGET"].toString().toStdString();
649     cid         = params["CID"].toString().toStdString();
650     filename    = params["FNAME"].toString().toStdString();
651     hubUrl      = params["HOST"].toString().toStdString();
652     size        = (int64_t)params["ESIZE"].toLongLong();
653 
654     try{
655         UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
656 
657         if (!user)
658             return;
659         // Only files have a TTH
660         if (!params["TTH"].toString().isEmpty()){
661             string subdir = params["FNAME"].toString().split("\\", QString::SkipEmptyParts).last().toStdString();
662             QueueManager::getInstance()->add(target + subdir, size, TTHValue(params["TTH"].toString().toStdString()), HintedUser(user, hubUrl));
663         }
664         else{
665             QueueManager::getInstance()->addDirectory(filename, HintedUser(user, hubUrl), target);
666         }
667     }
668     catch (const Exception&){}
669 }
670 
getFileList(const VarMap & params,bool match)671 void SearchFrame::getFileList(const VarMap &params, bool match){
672     string cid  = params["CID"].toString().toStdString();
673     string dir  = params["FNAME"].toString().toStdString();
674     string host = params["HOST"].toString().toStdString();
675 
676     if (cid.empty())
677         return;
678 
679     try {
680         UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
681 
682         if (user){
683             QueueItem::FileFlags flag = match? QueueItem::FLAG_MATCH_QUEUE : QueueItem::FLAG_CLIENT_VIEW;
684 
685             QueueManager::getInstance()->addList(HintedUser(user, host), flag, dir);
686         }
687     }
688     catch (const Exception&){}
689 
690 }
691 
addToFav(const QString & cid)692 void SearchFrame::addToFav(const QString &cid){
693     if (!cid.isEmpty()){
694         try {
695             UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
696 
697             if (user)
698                 FavoriteManager::getInstance()->addFavoriteUser(user);
699         }
700         catch (const Exception&){}
701     }
702 }
703 
grant(const VarMap & params)704 void SearchFrame::grant(const VarMap &params){
705     string cid  = params["CID"].toString().toStdString();
706     string host = params["HOST"].toString().toStdString();
707 
708     if (cid.empty())
709         return;
710 
711     try {
712         UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
713 
714         if (user)
715             UploadManager::getInstance()->reserveSlot(HintedUser(user, host));
716     }
717     catch (const Exception&){}
718 }
719 
removeSource(const VarMap & params)720 void SearchFrame::removeSource(const VarMap &params){
721     string cid  = params["CID"].toString().toStdString();
722 
723     if (cid.empty())
724         return;
725 
726     try {
727         UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
728 
729         if (user)
730             QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
731     }
732     catch (const Exception&){}
733 
734 }
735 
timerTick()736 void SearchFrame::timerTick() {
737 
738 }
739 
onHubAdded(const QString & info)740 void SearchFrame::onHubAdded(const QString &info){
741     Q_D(SearchFrame);
742 
743     if (d->hubs.contains(info) || info.isEmpty())
744         return;
745 
746     d->hubs.push_back(info);
747     d->client_list.push_back(ClientManager::getInstance()->getClient(_tq(info)));
748 
749     d->str_model->setStringList(d->hubs);
750 }
751 
onHubChanged(const QString & info)752 void SearchFrame::onHubChanged(const QString &info){
753     Q_D(SearchFrame);
754 
755     if (!d->hubs.contains(info) || info.isEmpty())
756         return;
757 
758     Client *cl = ClientManager::getInstance()->getClient(_tq(info));
759     if (!cl || d->client_list.indexOf(cl) < 0)
760         return;
761 
762     d->hubs.removeAt(d->client_list.indexOf(cl));
763     d->client_list.removeAt(d->client_list.indexOf(cl));
764 
765     d->hubs.push_back(info);
766     d->client_list.push_back(cl);
767 
768     d->str_model->setStringList(d->hubs);
769 }
770 
onHubRemoved(const QString & info)771 void SearchFrame::onHubRemoved(const QString &info){
772     Q_D(SearchFrame);
773 
774     if (!d->hubs.contains(info) || info.isEmpty())
775         return;
776 
777     d->client_list.removeAt(d->hubs.indexOf(info));
778     d->hubs.removeAt(d->hubs.indexOf(info));
779 
780     d->str_model->setStringList(d->hubs);
781 }
782 
getParams(SearchFrame::VarMap & map,const dcpp::SearchResultPtr & ptr)783 void SearchFrame::getParams(SearchFrame::VarMap &map, const dcpp::SearchResultPtr &ptr){
784     map.clear();
785 
786     map["SIZE"]    = qulonglong(ptr->getSize());
787 
788     QString fname = _q(ptr->getFile());
789     const QStringList &fname_parts = fname.split('\\', QString::SkipEmptyParts);
790 
791     map["FILE"] = fname_parts.isEmpty()? fname : fname_parts.last();
792 
793     if (ptr->getType() == SearchResult::TYPE_FILE){
794         map["TTH"]  = _q(ptr->getTTH().toBase32());
795 
796         QString path = fname.left(fname.lastIndexOf("\\"));
797 
798         if (!path.endsWith("\\"))
799             path += "\\";
800 
801         map["PATH"] = path;
802         map["ISDIR"]   = false;
803     }
804     else{
805         map["PATH"] = _q(ptr->getFile()).left(_q(ptr->getFile()).lastIndexOf(map["FILE"].toString()));
806         map["TTH"]  = "";
807         map["ISDIR"] = true;
808     }
809 
810     map["NICK"]    = WulforUtil::getInstance()->getNicks(ptr->getUser()->getCID());
811     map["FSLS"]    = ptr->getFreeSlots();
812     map["ASLS"]    = ptr->getSlots();
813     map["IP"]      = _q(ptr->getIP());
814     map["HUB"]     = _q(ptr->getHubName());
815     map["HOST"]    = _q(ptr->getHubURL());
816     map["CID"]     = _q(ptr->getUser()->getCID().toBase32());
817 }
818 
getDownloadParams(SearchFrame::VarMap & params,SearchItem * item)819 bool SearchFrame::getDownloadParams(SearchFrame::VarMap &params, SearchItem *item){
820     if (!item)
821         return false;
822 
823     params.clear();
824 
825     QString fname = item->data(COLUMN_SF_PATH).toString() + item->data(COLUMN_SF_FILENAME).toString();
826 
827     if (item->isDir && !fname.endsWith('\\'))
828         fname += "\\";
829 
830     params["CID"]   = item->cid;
831     params["FNAME"] = fname;
832     params["ESIZE"] = item->data(COLUMN_SF_ESIZE);
833     params["TTH"]   = item->data(COLUMN_SF_TTH);
834     params["HOST"]  = item->data(COLUMN_SF_HOST);
835     params["TARGET"]= _q(SETTING(DOWNLOAD_DIRECTORY));
836 
837     return true;
838 }
839 
getWholeDirParams(SearchFrame::VarMap & params,SearchItem * item)840 bool SearchFrame::getWholeDirParams(SearchFrame::VarMap &params, SearchItem *item){
841     if (!item)
842         return false;
843 
844     params.clear();
845 
846     params["CID"]   = item->cid;
847 
848     if (item->isDir)
849         params["FNAME"] = item->data(COLUMN_SF_PATH).toString() + item->data(COLUMN_SF_FILENAME).toString();
850     else
851         params["FNAME"] = item->data(COLUMN_SF_PATH).toString();//Download directory that containing a file
852 
853     params["ESIZE"] = 0;
854     params["TTH"]   = "";
855     params["HOST"]  = item->data(COLUMN_SF_HOST);
856     params["TARGET"]= _q(SETTING(DOWNLOAD_DIRECTORY));
857 
858     return true;
859 }
860 
addResult(const QMap<QString,QVariant> & map)861 void SearchFrame::addResult(const QMap<QString, QVariant> &map){
862     Q_D(SearchFrame);
863     static SearchBlacklist *SB = SearchBlacklist::getInstance();
864 
865     try {
866         if (SB->ok(map["FILE"].toString(), SearchBlacklist::NAME) && SB->ok(map["TTH"].toString(), SearchBlacklist::TTH)){
867             if (d->model->addResult(map["FILE"].toString(),
868                                     map["SIZE"].toULongLong(),
869                                     map["TTH"].toString(),
870                                     map["PATH"].toString(),
871                                     map["NICK"].toString(),
872                                     map["FSLS"].toULongLong(),
873                                     map["ASLS"].toULongLong(),
874                                     map["IP"].toString(),
875                                     map["HUB"].toString(),
876                                     map["HOST"].toString(),
877                                     map["CID"].toString(),
878                                     map["ISDIR"].toBool()))
879                 d->results++;
880         }
881     }
882     catch (const SearchListException&){}
883 }
884 
searchAlternates(const QString & tth)885 void SearchFrame::searchAlternates(const QString &tth){
886     if (tth.isEmpty())
887         return;
888 
889     Q_D(SearchFrame);
890 
891     lineEdit_SEARCHSTR->setText(tth);
892     comboBox_FILETYPES->setCurrentIndex(SearchManager::TYPE_TTH);
893     lineEdit_SIZE->setText("");
894 
895     slotStartSearch();
896 
897     d->saveFileType = false;
898 }
899 
searchFile(const QString & file)900 void SearchFrame::searchFile(const QString &file){
901     if (file.isEmpty())
902         return;
903 
904     Q_D(SearchFrame);
905 
906     lineEdit_SEARCHSTR->setText(file);
907     comboBox_FILETYPES->setCurrentIndex(SearchManager::TYPE_ANY);
908     lineEdit_SIZE->setText("");
909 
910     d->saveFileType = false;
911 
912     slotStartSearch();
913 }
914 
fastSearch(const QString & text,bool isTTH)915 void SearchFrame::fastSearch(const QString &text, bool isTTH){
916     if (text.isEmpty())
917         return;
918 
919     if (!isTTH)
920         comboBox_FILETYPES->setCurrentIndex(0); // set type "Any"
921     else
922         comboBox_FILETYPES->setCurrentIndex(8); // set type "TTH"
923 
924     lineEdit_SEARCHSTR->setText(text);
925 
926     slotStartSearch();
927 }
928 
slotStartSearch()929 void SearchFrame::slotStartSearch(){
930     if (qobject_cast<QPushButton*>(sender()) != pushButton_SEARCH){
931         pushButton_SEARCH->click(); //Generating clicked() signal that shows pushButton_STOP button.
932                                     //Anybody can suggest something better?
933         return;
934     }
935 
936     Q_D(SearchFrame);
937 
938     d->stop = false;
939 
940     if (lineEdit_SEARCHSTR->text().trimmed().isEmpty())
941         return;
942 
943     MainWindow *MW = MainWindow::getInstance();
944     QString s = lineEdit_SEARCHSTR->text().trimmed();
945     StringList clients;
946 
947     for (int i = 0; i < d->str_model->rowCount(); i++){
948         QModelIndex index = d->str_model->index(i, 0);
949 
950         if (index.data(Qt::CheckStateRole).toInt() == Qt::Checked)
951             clients.push_back(_tq(index.data().toString()));
952     }
953 
954 #ifndef WITH_DHT
955     if (clients.empty())
956         return;
957 #endif
958 
959     QString str_size = lineEdit_SIZE->text();
960     double lsize = Util::toDouble(Text::fromT(str_size.toStdString()));
961 
962     switch (comboBox_SIZE->currentIndex()){
963         case 1:
964             lsize *= 1024.0;
965 
966             break;
967         case 2:
968             lsize *= (1024.0*1024.0);
969 
970             break;
971         case 3:
972             lsize *= (1024.0*1024.0*1024.0);
973 
974             break;
975     }
976 
977     quint64 llsize = (quint64)lsize;
978 
979     if (!d->searchHistory.contains(s)){
980         bool isTTH = WulforUtil::isTTH(s);
981 
982         if ((WBGET("memorize-tth-search-phrases", false) && isTTH) || !isTTH)
983             d->searchHistory.push_front(s);
984 
985         QMenu *m = new QMenu();
986 
987         for (const auto &s : d->searchHistory)
988             m->addAction(s);
989 
990         lineEdit_SEARCHSTR->setMenu(m);
991 
992         uint maxItemsNumber = WIGET("search-history-items-number", 10);
993         while (d->searchHistory.count() > maxItemsNumber)
994                 d->searchHistory.removeLast();
995 
996         QString hist = d->searchHistory.join("\n");
997         WSSET(WS_SEARCH_HISTORY, hist.toUtf8().toBase64());
998     }
999 
1000     {
1001         d->currentSearch = StringTokenizer<tstring>(s.toStdString(), ' ').getTokens();
1002         s = "";
1003 
1004         //strip out terms beginning with -
1005         for (auto si = d->currentSearch.begin(); si != d->currentSearch.end(); ) {
1006             if(si->empty()) {
1007                 si = d->currentSearch.erase(si);
1008                 continue;
1009             }
1010 
1011             if ((*si)[0] != '-')
1012                 s += QString::fromStdString(*si) + ' ';
1013 
1014             ++si;
1015         }
1016 
1017         d->token = _q(Util::toString(Util::rand()));
1018     }
1019 
1020     SearchManager::SizeModes searchMode((SearchManager::SizeModes)comboBox_SIZETYPE->currentIndex());
1021 
1022     if(!llsize || lineEdit_SIZE->text() == "")
1023         searchMode = SearchManager::SIZE_DONTCARE;
1024 
1025     int ftype = comboBox_FILETYPES->currentIndex();
1026 
1027     d->isHash = (ftype == SearchManager::TYPE_TTH);
1028     d->filterShared = static_cast<AlreadySharedAction>(comboBox_SHARED->currentIndex());
1029     d->withFreeSlots = checkBox_FILTERSLOTS->isChecked();
1030 
1031     d->model->setFilterRole(static_cast<int>(d->filterShared));
1032     d->model->clearModel();
1033 
1034     d->dropped = d->results = 0;
1035 
1036     string ftypeStr;
1037     if (ftype > SearchManager::TYPE_ANY && ftype < SearchManager::TYPE_LAST)
1038         ftypeStr = SearchManager::getInstance()->getTypeStr(ftype);
1039     else
1040     {
1041         ftypeStr = _tq(lineEdit_SEARCHSTR->text());
1042         ftype = SearchManager::TYPE_ANY;
1043     }
1044 
1045     StringList exts;
1046     try{
1047         if (ftype == SearchManager::TYPE_ANY){
1048             // Custom searchtype
1049             exts = SettingsManager::getInstance()->getExtensions(ftypeStr);
1050         }
1051         else if ((ftype > SearchManager::TYPE_ANY && ftype < SearchManager::TYPE_DIRECTORY) || ftype == SearchManager::TYPE_CD_IMAGE){
1052             // Predefined searchtype
1053             exts = SettingsManager::getInstance()->getExtensions(string(1, '0' + ftype));
1054         }
1055     }
1056     catch (const SearchTypeException&){
1057         ftype = SearchManager::TYPE_ANY;
1058     }
1059 
1060     d->target = s;
1061     d->searchStartTime = GlobalTimer::getInstance()->getTicks()*1000;
1062 
1063     uint64_t maxDelayBeforeSearch = SearchManager::getInstance()->search(clients, s.toStdString(), llsize, (SearchManager::TypeModes)ftype, searchMode, d->token.toStdString(), exts, (void*)this);
1064     uint64_t waitingResultsTime = 20000; // just assumption that user receives most of results in 20 seconds
1065 
1066     d->searchEndTime = d->searchStartTime + maxDelayBeforeSearch + waitingResultsTime;
1067     d->waitingResults = true;
1068 
1069     if (!checkBox_HIDEPANEL->isChecked()){
1070         QList<int> panes = splitter->sizes();
1071 
1072         panes[1] = panes[0] + panes[1];
1073 
1074         d->left_pane_old_size = panes[0] > 15 ? panes[0] : d->left_pane_old_size;
1075 
1076         panes[0] = 0;
1077 
1078         splitter->setSizes(panes);
1079     }
1080 
1081     d->arena_title = tr("Search - %1").arg(s);
1082 
1083     lineEdit_SEARCHSTR->setEnabled(false);
1084 
1085     MW->redrawToolPanel();
1086 }
1087 
slotClear()1088 void SearchFrame::slotClear(){
1089     Q_D(SearchFrame);
1090 
1091     treeView_RESULTS->clearSelection();
1092     d->model->clearModel();
1093     lineEdit_SEARCHSTR->clear();
1094     lineEdit_SIZE->setText("");
1095 
1096     d->dropped = d->results = 0;
1097 }
1098 
slotResultDoubleClicked(const QModelIndex & index)1099 void SearchFrame::slotResultDoubleClicked(const QModelIndex &index){
1100     if (!index.isValid() || !index.internalPointer())
1101         return;
1102 
1103     Q_D(SearchFrame);
1104 
1105     QModelIndex i = d->proxy? d->proxy->mapToSource(index) : index;
1106 
1107     SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1108     VarMap params;
1109 
1110     if (getDownloadParams(params, item)){
1111         download(params);
1112 
1113         if (item->childCount() > 0 && !SETTING(DONT_DL_ALREADY_QUEUED)){//download all child items
1114             QString fname = params["FNAME"].toString();
1115 
1116             for (const auto &i : item->childItems){
1117                 if (getDownloadParams(params, i)){
1118                     params["FNAME"] = fname;
1119 
1120                     download(params);
1121                 }
1122             }
1123         }
1124     }
1125 }
1126 
slotContextMenu(const QPoint &)1127 void SearchFrame::slotContextMenu(const QPoint &){
1128     QItemSelectionModel *selection_model = treeView_RESULTS->selectionModel();
1129     QModelIndexList list = selection_model->selectedRows(0);
1130     Q_D(SearchFrame);
1131 
1132     if (list.size() < 1)
1133         return;
1134 
1135     if (d->proxy)
1136         std::transform(list.begin(), list.end(), list.begin(), [&d](QModelIndex i) { return d->proxy->mapToSource(i); } );
1137 
1138     if (!Menu::getInstance())
1139         Menu::newInstance();
1140 
1141     QStringList hubs;
1142 
1143     for (const auto &i : list){
1144         SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1145         QString host = item->data(COLUMN_SF_HOST).toString();
1146 
1147         if (!hubs.contains(host))
1148             hubs.push_back(host);
1149     }
1150 
1151     Menu::Action act = Menu::getInstance()->exec(hubs);
1152 
1153     switch (act){
1154         case Menu::None:
1155         {
1156             break;
1157         }
1158         case Menu::Download:
1159         {
1160             for (const auto &i : list){
1161                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1162                 VarMap params;
1163 
1164                 if (getDownloadParams(params, item)){
1165                     download(params);
1166 
1167                     if (item->childCount() > 0 && !SETTING(DONT_DL_ALREADY_QUEUED)){//download all child items
1168                         QString fname = params["FNAME"].toString();
1169 
1170                         for (const auto &i : item->childItems){
1171                             if (getDownloadParams(params, i)){
1172                                 params["FNAME"] = fname;
1173 
1174                                 download(params);
1175                             }
1176                         }
1177                     }
1178                 }
1179             }
1180 
1181             break;
1182         }
1183         case Menu::DownloadTo:
1184         {
1185             static QString old_target = QDir::homePath();
1186             QString target = Menu::getInstance()->getDownloadToPath();
1187 
1188             if (!QDir(target).exists() || target.isEmpty()){
1189                 target = QFileDialog::getExistingDirectory(this, tr("Select directory"), old_target);
1190 
1191                 target = QDir::toNativeSeparators(target);
1192 
1193                 Menu::getInstance()->addTempPath(target);
1194             }
1195 
1196             if (target.isEmpty())
1197                 break;
1198 
1199             if (!target.endsWith(QDir::separator()))
1200                 target += QDir::separator();
1201 
1202             old_target = target;
1203 
1204             for (const auto &i : list){
1205                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1206                 VarMap params;
1207 
1208                 if (getDownloadParams(params, item)){
1209                     params["TARGET"] = target;
1210                     download(params);
1211 
1212                     if (item->childCount() > 0 && !SETTING(DONT_DL_ALREADY_QUEUED)){//download all child items
1213                         QString fname = params["FNAME"].toString();
1214 
1215                         for (const auto  &i : item->childItems){
1216                             if (getDownloadParams(params, i)){
1217                                 params["FNAME"]  = fname;
1218                                 params["TARGET"] = target;
1219 
1220                                 download(params);
1221                             }
1222                         }
1223                     }
1224                 }
1225             }
1226 
1227             break;
1228         }
1229         case Menu::DownloadWholeDir:
1230         {
1231             for (const auto &i : list){
1232                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1233                 VarMap params;
1234 
1235                 if (getWholeDirParams(params, item))
1236                     download(params);
1237             }
1238 
1239             break;
1240         }
1241         case Menu::DownloadWholeDirTo:
1242         {
1243             static QString old_target = QDir::homePath();
1244             QString target = Menu::getInstance()->getDownloadToPath();
1245 
1246             if (!QDir(target).exists() || target.isEmpty()){
1247                 target = QFileDialog::getExistingDirectory(this, tr("Select directory"), old_target);
1248 
1249                 target = QDir::toNativeSeparators(target);
1250 
1251                 Menu::getInstance()->addTempPath(target);
1252             }
1253 
1254             if (target.isEmpty())
1255                 break;
1256 
1257             if (!target.endsWith(QDir::separator()))
1258                 target += QDir::separator();
1259 
1260             old_target = target;
1261 
1262             for (const auto &i : list){
1263                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1264                 VarMap params;
1265 
1266                 if (getWholeDirParams(params, item)){
1267                     params["TARGET"] = target;
1268                     download(params);
1269                 }
1270             }
1271 
1272             break;
1273         }
1274         case Menu::SearchTTH:
1275         {
1276             for (const auto &i : list){
1277                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1278 
1279                 if (!item->isDir){//only one file
1280                     SearchFrame *sf = ArenaWidgetFactory().create<SearchFrame>();
1281 
1282                     sf->searchAlternates(item->data(COLUMN_SF_TTH).toString());
1283 
1284                     break;
1285                 }
1286             }
1287 
1288             break;
1289         }
1290         case Menu::Magnet:
1291         {
1292             QString magnets = "";
1293             WulforUtil *WU = WulforUtil::getInstance();
1294 
1295             for (const auto &i : list){
1296                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1297 
1298                 if (!item->isDir){//only files
1299                     qlonglong size = item->data(COLUMN_SF_ESIZE).toLongLong();
1300                     QString tth = item->data(COLUMN_SF_TTH).toString();
1301                     QString name = item->data(COLUMN_SF_FILENAME).toString();
1302 
1303                     QString magnet = WU->makeMagnet(name, size, tth);
1304 
1305                     if (!magnet.isEmpty())
1306                         magnets += magnet + "\n";
1307                 }
1308             }
1309 
1310             magnets = magnets.trimmed();
1311 
1312             if (!magnets.isEmpty())
1313                 qApp->clipboard()->setText(magnets, QClipboard::Clipboard);
1314 
1315             break;
1316         }
1317         case Menu::MagnetWeb:
1318         {
1319             QString magnets = "";
1320             WulforUtil *WU = WulforUtil::getInstance();
1321 
1322             for (const auto &i : list){
1323                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1324 
1325                 if (!item->isDir){//only files
1326                     qlonglong size = item->data(COLUMN_SF_ESIZE).toLongLong();
1327                     QString tth = item->data(COLUMN_SF_TTH).toString();
1328                     QString name = item->data(COLUMN_SF_FILENAME).toString();
1329 
1330                     QString magnet = "[magnet=\"" + WU->makeMagnet(name, size, tth) + "\"]"+name+"[/magnet]";
1331 
1332                     if (!magnet.isEmpty())
1333                         magnets += magnet + "\n";
1334                 }
1335             }
1336 
1337             magnets = magnets.trimmed();
1338 
1339             if (!magnets.isEmpty())
1340                 qApp->clipboard()->setText(magnets, QClipboard::Clipboard);
1341 
1342             break;
1343         }
1344         case Menu::MagnetInfo:
1345         {
1346             WulforUtil *WU = WulforUtil::getInstance();
1347 
1348             for (const auto &i : list){
1349                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1350 
1351                 if (!item->isDir){//only files
1352                     qlonglong size = item->data(COLUMN_SF_ESIZE).toLongLong();
1353                     QString tth = item->data(COLUMN_SF_TTH).toString();
1354                     QString name = item->data(COLUMN_SF_FILENAME).toString();
1355 
1356                     QString magnet = WU->makeMagnet(name, size, tth);
1357 
1358                     if (!magnet.isEmpty()){
1359                         Magnet m(this);
1360                         m.setLink(magnet + "\n");
1361                         m.exec();
1362                     }
1363                 }
1364             }
1365 
1366             break;
1367         }
1368         case Menu::Browse:
1369         {
1370             for (const auto &i : list){
1371                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1372                 VarMap params;
1373 
1374                 if (getWholeDirParams(params, item))
1375                     getFileList(params, false);
1376             }
1377 
1378             break;
1379         }
1380         case Menu::MatchQueue:
1381         {
1382             for (const auto &i : list){
1383                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1384                 VarMap params;
1385 
1386                 if (getWholeDirParams(params, item)){
1387                     params["FNAME"] = "";
1388                     getFileList(params, true);
1389                 }
1390             }
1391 
1392             break;
1393         }
1394         case Menu::SendPM:
1395         {
1396             HubFrame *fr = NULL;
1397 
1398             for (const auto &i : list){
1399                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1400 
1401                 QString hubUrl = item->data(COLUMN_SF_HOST).toString();
1402                 dcpp::CID cid(_tq(item->cid));
1403 
1404                 fr = qobject_cast<HubFrame*>(HubManager::getInstance()->getHub(hubUrl));
1405 
1406                 if (fr)
1407                     fr->createPMWindow(cid);
1408             }
1409 
1410             break;
1411         }
1412         case Menu::AddToFav:
1413         {
1414             for (const auto &i : list){
1415                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1416                 VarMap params;
1417 
1418                 if (getDownloadParams(params, item))
1419                     addToFav(params["CID"].toString());
1420 
1421             }
1422 
1423             break;
1424         }
1425         case Menu::GrantExtraSlot:
1426         {
1427              for (const auto &i : list){
1428                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1429                 VarMap params;
1430 
1431                 if (getDownloadParams(params, item))
1432                     grant(params);
1433 
1434             }
1435 
1436             break;
1437         }
1438         case Menu::RemoveFromQueue:
1439         {
1440              for (const auto &i : list){
1441                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1442                 VarMap params;
1443 
1444                 if (getDownloadParams(params, item))
1445                     removeSource(params);
1446 
1447              }
1448 
1449              break;
1450         }
1451         case Menu::Remove:
1452         {
1453              selection_model->clearSelection();
1454 
1455              for (const auto &i : list){
1456                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1457 
1458                 d->model->removeItem(item);
1459 
1460                 d->model->repaint();
1461             }
1462 
1463             break;
1464         }
1465         case Menu::UserCommands:
1466         {
1467             for (const auto &i : list){
1468                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1469 
1470                 int id = Menu::getInstance()->getCommandId();
1471 
1472                 UserCommand uc;
1473                 if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
1474                     break;
1475 
1476                 StringMap params;
1477 
1478                 if (WulforUtil::getInstance()->getUserCommandParams(uc, params)){
1479                     UserPtr user = ClientManager::getInstance()->findUser(CID(item->cid.toStdString()));
1480 
1481                     if (user && user->isOnline()){
1482                         params["fileFN"]     = _tq(item->data(COLUMN_SF_PATH).toString() + item->data(COLUMN_SF_FILENAME).toString());
1483                         params["fileSI"]     = _tq(item->data(COLUMN_SF_ESIZE).toString());
1484                         params["fileSIshort"]= _tq(item->data(COLUMN_SF_SIZE).toString());
1485 
1486                         if(!item->isDir)
1487                             params["fileTR"] = _tq(item->data(COLUMN_SF_TTH).toString());
1488 
1489                         // compatibility with 0.674 and earlier
1490                         params["file"] = params["fileFN"];
1491                         params["filesize"] = params["fileSI"];
1492                         params["filesizeshort"] = params["fileSIshort"];
1493                         params["tth"] = params["fileTR"];
1494 
1495                         string hubUrl = _tq(i.data(COLUMN_SF_HOST).toString());
1496 
1497                         ClientManager::getInstance()->userCommand(HintedUser(user, hubUrl), uc, params, true);
1498                     }
1499 
1500                 }
1501             }
1502 
1503             break;
1504         }
1505         case Menu::Blacklist:
1506         {
1507             SearchBlackListDialog dlg(this);
1508 
1509             dlg.exec();
1510 
1511             break;
1512         }
1513         case Menu::AddToBlacklist:
1514         {
1515             QList <QString> new_inst;
1516 
1517             for (const auto &i : list){
1518                 SearchItem *item = reinterpret_cast<SearchItem*>(i.internalPointer());
1519                 VarMap params;
1520 
1521                 if (getDownloadParams(params, item))
1522                     new_inst << item->data(COLUMN_SF_FILENAME).toString();
1523             }
1524             if (!new_inst.isEmpty()){
1525                 static SearchBlacklist *SB = SearchBlacklist::getInstance();
1526                 QList <QString> list = SB->getList(SearchBlacklist::NAME);
1527                 list.append(new_inst);
1528                 SB->setList(SearchBlacklist::NAME, list);
1529             }
1530 
1531             break;
1532         }
1533         default:
1534         {
1535             break;
1536         }
1537     }
1538 }
1539 
slotHeaderMenu(const QPoint &)1540 void SearchFrame::slotHeaderMenu(const QPoint&){
1541     WulforUtil::headerMenu(treeView_RESULTS);
1542 }
1543 
slotTimer()1544 void SearchFrame::slotTimer(){
1545     Q_D(SearchFrame);
1546 
1547     if (d->waitingResults) {
1548         uint64_t now = GlobalTimer::getInstance()->getTicks()*1000;
1549         float fraction  = 100.0f*(now - d->searchStartTime)/(d->searchEndTime - d->searchStartTime);
1550         if (fraction >= 100.0) {
1551             fraction = 100.0;
1552             d->waitingResults = false;
1553         }
1554         QString msg = tr("Searching for %1 ...").arg(d->target);
1555         progressBar->setFormat(msg);
1556         progressBar->setValue(static_cast<unsigned>(fraction));
1557     } else {
1558         QString msg = "";
1559         progressBar->setFormat(msg);
1560         progressBar->setValue(0);
1561         lineEdit_SEARCHSTR->setEnabled(true);
1562     }
1563 
1564     if (d->dropped == d->results && !d->dropped){
1565 
1566         if (d->currentSearch.empty())
1567             frame_PROGRESS->hide();
1568         else {
1569             frame_PROGRESS->show();
1570 
1571             QString text = QString(tr("<b>No results</b>"));
1572 
1573             status->setText(text);
1574         }
1575     }
1576     else {
1577         if (!frame_PROGRESS->isVisible())
1578             frame_PROGRESS->show();
1579 
1580         QString text = QString(tr("Found: <b>%1</b>  Dropped: <b>%2</b>")).arg(d->results).arg(d->dropped);
1581 
1582         status->setText(text);
1583     }
1584 }
1585 
slotToggleSidePanel()1586 void SearchFrame::slotToggleSidePanel(){
1587     QList<int> panes = splitter->sizes();
1588     Q_D(SearchFrame);
1589 
1590     if (panes[0] < 15){//left pane can't have width less than 15px
1591         panes[0] = d->left_pane_old_size;
1592         panes[1] = panes[1] - d->left_pane_old_size;
1593     }
1594     else {
1595         panes[1] = panes[0] + panes[1];
1596         d->left_pane_old_size = panes[0];
1597         panes[0] = 0;
1598     }
1599 
1600     splitter->setSizes(panes);
1601 }
1602 
slotFilter()1603 void SearchFrame::slotFilter(){
1604     Q_D(SearchFrame);
1605 
1606     if (frame_FILTER->isVisible()){
1607         treeView_RESULTS->setModel(d->model);
1608 
1609         disconnect(lineEdit_FILTER, SIGNAL(textChanged(QString)), d->proxy, SLOT(setFilterFixedString(QString)));
1610     }
1611     else {
1612         d->proxy = (d->proxy? d->proxy : (new SearchProxyModel(this)));
1613         d->proxy->setDynamicSortFilter(true);
1614         d->proxy->setFilterFixedString(lineEdit_FILTER->text());
1615         d->proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
1616         d->proxy->setFilterKeyColumn(comboBox_FILTERCOLUMNS->currentIndex());
1617         d->proxy->setSourceModel(d->model);
1618 
1619         treeView_RESULTS->setModel(d->proxy);
1620 
1621         connect(lineEdit_FILTER, SIGNAL(textChanged(QString)), d->proxy, SLOT(setFilterFixedString(QString)));
1622 
1623         if (!lineEdit_SEARCHSTR->selectedText().isEmpty()){
1624             lineEdit_FILTER->setText(lineEdit_SEARCHSTR->selectedText());
1625             lineEdit_FILTER->selectAll();
1626         }
1627 
1628         lineEdit_FILTER->setFocus();
1629 
1630         if (!lineEdit_FILTER->text().isEmpty())
1631             lineEdit_FILTER->selectAll();
1632     }
1633 
1634     frame_FILTER->setVisible(!frame_FILTER->isVisible());
1635 }
1636 
slotChangeProxyColumn(int col)1637 void SearchFrame::slotChangeProxyColumn(int col){
1638     Q_D(SearchFrame);
1639 
1640     if (d->proxy)
1641         d->proxy->setFilterKeyColumn(col);
1642 }
1643 
slotSettingsChanged(const QString & key,const QString & value)1644 void SearchFrame::slotSettingsChanged(const QString &key, const QString &value){
1645     if (key == WS_TRANSLATION_FILE)
1646         retranslateUi(this);
1647 }
1648 
on(SearchManagerListener::SR,const dcpp::SearchResultPtr & aResult)1649 void SearchFrame::on(SearchManagerListener::SR, const dcpp::SearchResultPtr& aResult) noexcept {
1650     Q_D(SearchFrame);
1651 
1652     if (d->currentSearch.empty() || !aResult || d->stop == true)
1653         return;
1654 
1655     if (!aResult->getToken().empty() && d->token != _q(aResult->getToken())){
1656         d->dropped++;
1657 
1658         return;
1659     }
1660 
1661     if(d->isHash) {
1662         if(aResult->getType() != SearchResult::TYPE_FILE || TTHValue(Text::fromT(d->currentSearch[0])) != aResult->getTTH()) {
1663             d->dropped++;
1664 
1665             return;
1666         }
1667     }
1668     else {
1669         for (const auto &j : d->currentSearch) {
1670             if((*j.begin() != ('-') && Util::findSubString(aResult->getFile(), j) == tstring::npos) ||
1671                (*j.begin() == ('-') && j.size() != 1 && Util::findSubString(aResult->getFile(), j.substr(1)) != tstring::npos)
1672               )
1673            {
1674                     d->dropped++;
1675 
1676                     return;
1677            }
1678         }
1679     }
1680 
1681     if (d->filterShared == Filter && aResult->getType() == SearchResult::TYPE_FILE){
1682         const TTHValue& t = aResult->getTTH();
1683 
1684         if (ShareManager::getInstance()->isTTHShared(t)) {
1685             d->dropped++;
1686 
1687             return;
1688         }
1689     }
1690 
1691     if (d->withFreeSlots && !aResult->getFreeSlots()){
1692         d->dropped++;
1693 
1694         return;
1695     }
1696 
1697     QMap<QString, QVariant> map;
1698     getParams(map, aResult);
1699 
1700     emit coreSR(map);
1701 }
1702 
slotClose()1703 void SearchFrame::slotClose() {
1704     ArenaWidgetManager::getInstance()->rem(this);
1705 }
1706 
on(ClientConnected,Client * c)1707 void SearchFrame::on(ClientConnected, Client* c) noexcept{
1708     emit coreClientConnected(_q(c->getHubUrl()));
1709 }
1710 
on(ClientUpdated,Client * c)1711 void SearchFrame::on(ClientUpdated, Client* c) noexcept{
1712     emit coreClientUpdated((_q(c->getHubUrl())));
1713 }
1714 
on(ClientDisconnected,Client * c)1715 void SearchFrame::on(ClientDisconnected, Client* c) noexcept{
1716     emit coreClientDisconnected((_q(c->getHubUrl())));
1717 }
1718 
slotStopSearch()1719 void SearchFrame::slotStopSearch(){
1720     Q_D(SearchFrame);
1721 
1722     d->stop = true;
1723     d->waitingResults = false;
1724 }
1725