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 "DownloadQueue.h"
11 
12 #include <QMap>
13 #include <QTreeView>
14 #include <QAbstractItemModel>
15 #include <QItemSelectionModel>
16 #include <QFileDialog>
17 #include <QClipboard>
18 #include <QHeaderView>
19 #include <QDir>
20 #include <QShortcut>
21 
22 #include "DownloadQueueModel.h"
23 #include "ArenaWidgetFactory.h"
24 #include "SearchFrame.h"
25 #include "HubFrame.h"
26 #include "HubManager.h"
27 #include "WulforUtil.h"
28 #include "WulforSettings.h"
29 #include "Magnet.h"
30 #include "dcpp/ClientManager.h"
31 #include "dcpp/User.h"
32 
33 #define _DEBUG_ 1
34 
35 #if _DEBUG_
36 #include <QtDebug>
37 #endif
38 
39 using namespace dcpp;
40 
41 class DownloadQueuePrivate {
42     typedef QMap<QString, QVariant> VarMap;
43     typedef QMap<QString, QMap<QString, QString> > SourceMap;
44 
45 public:
46     QShortcut *deleteShortcut;
47 
48     DownloadQueueModel *queue_model;
49     DownloadQueueModel *file_model;
50     DownloadQueueDelegate *delegate;
51 
52     DownloadQueue::Menu *menu;
53 
54     SourceMap sources;
55     SourceMap badSources;
56 };
57 
Menu()58 DownloadQueue::Menu::Menu(){
59     menu = new QMenu();
60     QMenu *menu_magnet = new QMenu(tr("Magnet"), DownloadQueue::getInstance());
61 
62     QAction *search_alt  = new QAction(tr("Search for alternates"), menu);
63     QAction *copy_magnet = new QAction(tr("Copy magnet"), menu_magnet);
64     QAction *copy_magnet_web = new QAction(tr("Copy web-magnet"), menu_magnet);
65     QAction *magnet_info = new QAction(tr("Properties of magnet"), menu_magnet);
66     QAction *ren_move    = new QAction(tr("Rename/Move"), menu);
67 
68     QAction *sep1 = new QAction(menu);
69     sep1->setSeparator(true);
70 
71     set_prio = new QMenu(tr("Set priority"), menu);
72     {
73         QAction *paused = new QAction(tr("Paused"), set_prio);
74         paused->setData(static_cast<int>(QueueItem::PAUSED));
75 
76         QAction *lowest = new QAction(tr("Lowest"), set_prio);
77         lowest->setData(static_cast<int>(QueueItem::LOWEST));
78 
79         QAction *low    = new QAction(tr("Low"), set_prio);
80         low->setData(static_cast<int>(QueueItem::LOW));
81 
82         QAction *normal = new QAction(tr("Normal"), set_prio);
83         normal->setData(static_cast<int>(QueueItem::NORMAL));
84 
85         QAction *high   = new QAction(tr("High"), set_prio);
86         high->setData(static_cast<int>(QueueItem::HIGH));
87 
88         QAction *highest= new QAction(tr("Highest"), set_prio);
89         highest->setData(static_cast<int>(QueueItem::HIGHEST));
90 
91         set_prio->addActions(QList<QAction*>() << paused << lowest << low << normal << high << highest);
92     }
93 
94     browse = new QMenu(tr("Browse files"), menu);
95     send_pm = new QMenu(tr("Send private message"), menu);
96 
97     QAction *sep2 = new QAction(menu);
98     sep2->setSeparator(true);
99 
100     rem_src  = new QMenu(tr("Remove source"), menu);
101     rem_usr  = new QMenu(tr("Remove user"), menu);
102 
103     QAction *remove   = new QAction(tr("Remove"), menu);
104 
105     QAction *sep3 = new QAction(menu);
106     sep3->setSeparator(true);
107 
108     map[search_alt] = Alternates;
109     map[copy_magnet] = Magnet;
110     map[copy_magnet_web] = MagnetWeb;
111     map[magnet_info] = MagnetInfo;
112     map[ren_move] = RenameMove;
113     map[remove] = Remove;
114 
115     menu_magnet->addActions(QList<QAction*>()
116             << copy_magnet << copy_magnet_web << sep3 << magnet_info);
117 
118     menu->addAction(search_alt);
119     menu->addMenu(menu_magnet);
120     menu->addAction(ren_move);
121     menu->addAction(sep1);
122     menu->addMenu(set_prio);
123     menu->addMenu(browse);
124     menu->addMenu(send_pm);
125     menu->addAction(sep2);
126     menu->addMenu(rem_src);
127     menu->addMenu(rem_usr);
128     menu->addAction(remove);
129 }
130 
~Menu()131 DownloadQueue::Menu::~Menu(){
132     delete menu;
133 }
134 
clearMenu(QMenu * m)135 void DownloadQueue::Menu::clearMenu(QMenu *m){
136     if (!m)
137         return;
138 
139     QList<QAction*> actions = m->actions();
140 
141    for (const auto &a : actions)
142        m->removeAction(a);
143 
144    qDeleteAll(actions);
145 }
146 
exec(const DownloadQueue::SourceMap & sources,const QString & target,bool multiselect)147 DownloadQueue::Menu::Action DownloadQueue::Menu::exec(const DownloadQueue::SourceMap &sources, const QString &target, bool multiselect){
148     if (target.isEmpty() || sources.isEmpty() || !sources.contains(target))
149         return None;
150 
151     arg = QVariant();
152 
153     clearMenu(browse), clearMenu(send_pm), clearMenu(rem_src), clearMenu(rem_usr);
154 
155     browse->setDisabled(multiselect);
156     send_pm->setDisabled(multiselect);
157     rem_src->setDisabled(multiselect);
158     rem_usr->setDisabled(multiselect);
159 
160     QMap<QString, QString>  users = sources[target];
161     auto it = users.constBegin();
162 
163     for (; it != users.constEnd(); ++it){
164         QAction *act = new QAction(it.key(), menu);
165         act->setStatusTip(it.value());
166 
167         browse->addAction(act);
168         send_pm->addAction(act);
169         rem_src->addAction(act);
170         rem_usr->addAction(act);
171     }
172 
173     QAction *ret = menu->exec(QCursor::pos());
174     DownloadQueue::VarMap rmap;
175 
176     if (!ret)
177         return None;
178     else if (map.contains(ret))
179         return map[ret];
180     else if (set_prio->actions().contains(ret)){
181         arg = ret->data();
182 
183         return SetPriority;
184     }
185 
186     rmap.insert(ret->text(), ret->statusTip());
187     arg = rmap;
188 
189     if (browse->actions().contains(ret))
190         return Browse;
191     else if (send_pm->actions().contains(ret))
192         return SendPM;
193     else if (rem_src->actions().contains(ret))
194         return RemoveSource;
195     else if (rem_usr->actions().contains(ret))
196         return RemoveUser;
197     else
198         arg = QVariant();
199 
200     return None;
201 }
202 
getArg()203 QVariant DownloadQueue::Menu::getArg(){
204     return arg;
205 }
206 
DownloadQueue(QWidget * parent)207 DownloadQueue::DownloadQueue(QWidget *parent):
208         QWidget(parent), d_ptr(new DownloadQueuePrivate())
209 {
210     setupUi(this);
211 
212     init();
213 
214     QueueManager::getInstance()->addListener(this);
215 
216     setUnload(false);
217 }
218 
~DownloadQueue()219 DownloadQueue::~DownloadQueue(){
220     save();
221 
222     QueueManager::getInstance()->removeListener(this);
223     Q_D(DownloadQueue);
224 
225     delete d->menu;
226     delete d_ptr;
227 }
228 
closeEvent(QCloseEvent * e)229 void DownloadQueue::closeEvent(QCloseEvent *e){
230     isUnload()? e->accept() : e->ignore();
231 }
232 
requestDelete()233 void DownloadQueue::requestDelete(){
234     if (!treeView_TARGET->hasFocus())
235         return;
236 
237     QModelIndexList list = treeView_TARGET->selectionModel()->selectedRows(0);
238 
239     if (list.isEmpty())
240         return;
241 
242     QList<DownloadQueueItem*> items;
243 
244     for (const auto &i : list){
245         DownloadQueueItem *item = reinterpret_cast<DownloadQueueItem*>(i.internalPointer());
246 
247         if (!item)
248             continue;
249 
250         if (item->dir)
251             getChilds(item, items);
252         else if (!items.contains(item))
253             items.push_front(item);
254     }
255 
256     QueueManager *QM = QueueManager::getInstance();
257     for (const auto &i : items){
258         QString target = i->data(COLUMN_DOWNLOADQUEUE_PATH).toString() + i->data(COLUMN_DOWNLOADQUEUE_NAME).toString();
259 
260         try {
261             QM->remove(target.toStdString());
262         }
263         catch (const Exception &){}
264     }
265 }
266 
init()267 void DownloadQueue::init(){
268     Q_D(DownloadQueue);
269 
270     d->queue_model = new DownloadQueueModel(this);
271 
272     d->delegate = new DownloadQueueDelegate(d->queue_model);
273 
274     treeView_TARGET->setItemDelegate(d->delegate);
275     treeView_TARGET->setModel(d->queue_model);
276     treeView_TARGET->setItemsExpandable(true);
277     treeView_TARGET->setRootIsDecorated(true);
278     treeView_TARGET->setContextMenuPolicy(Qt::CustomContextMenu);
279     treeView_TARGET->header()->setContextMenuPolicy(Qt::CustomContextMenu);
280 
281     label_STATS->hide();
282 
283     d->deleteShortcut = new QShortcut(QKeySequence(Qt::Key_Delete), this);
284     d->deleteShortcut->setContext(Qt::WidgetWithChildrenShortcut);
285 
286     connect(this, SIGNAL(coreAdded(VarMap)),            this, SLOT(addFile(VarMap)), Qt::QueuedConnection);
287     connect(this, SIGNAL(coreRemoved(VarMap)),          this, SLOT(remFile(VarMap)), Qt::QueuedConnection);
288     connect(this, SIGNAL(coreSourcesUpdated(VarMap)),   this, SLOT(updateFile(VarMap)), Qt::QueuedConnection);
289     connect(this, SIGNAL(coreStatusUpdated(VarMap)),    this, SLOT(updateFile(VarMap)), Qt::QueuedConnection);
290     connect(this, SIGNAL(coreMoved(VarMap)),            this, SLOT(remFile(VarMap)), Qt::QueuedConnection);
291     connect(this, SIGNAL(coreMoved(VarMap)),            this, SLOT(addFile(VarMap)), Qt::QueuedConnection);
292 
293     connect(d->deleteShortcut, SIGNAL(activated()), this, SLOT(requestDelete()));
294     connect(treeView_TARGET, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint)));
295     connect(treeView_TARGET->header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu(QPoint)));
296     connect(d->queue_model, SIGNAL(needExpand(QModelIndex)), treeView_TARGET, SLOT(expand(QModelIndex)));
297     connect(d->queue_model, SIGNAL(rowRemoved(QModelIndex)), this, SLOT(slotCollapseRow(QModelIndex)));
298     connect(d->queue_model, SIGNAL(updateStats(quint64,quint64)), this, SLOT(slotUpdateStats(quint64,quint64)));
299     connect(pushButton_EXPAND,      SIGNAL(clicked()), treeView_TARGET, SLOT(expandAll()));
300     connect(pushButton_COLLAPSE,    SIGNAL(clicked()), treeView_TARGET, SLOT(collapseAll()));
301 
302     d->menu = new Menu();
303 
304     setAttribute(Qt::WA_DeleteOnClose);
305 
306     load();
307 
308     loadList();
309 
310     treeView_TARGET->expandAll();
311 
312     ArenaWidget::setState( ArenaWidget::Flags(ArenaWidget::state() | ArenaWidget::Singleton | ArenaWidget::Hidden) );
313 }
314 
load()315 void DownloadQueue::load(){
316     treeView_TARGET->header()->restoreState(WVGET(WS_DQUEUE_STATE, QByteArray()).toByteArray());
317     treeView_TARGET->setSortingEnabled(true);
318 }
319 
save()320 void DownloadQueue::save(){
321     WVSET(WS_DQUEUE_STATE, treeView_TARGET->header()->saveState());
322 }
323 
getParams(DownloadQueue::VarMap & params,const QueueItem * item)324 void DownloadQueue::getParams(DownloadQueue::VarMap &params, const QueueItem *item){
325     QString nick = "";
326     QMap<QString, QString> source;
327     int online = 0;
328 
329     if (!item)
330         return;
331 
332     params["FNAME"]     = _q(item->getTargetFileName());
333     params["PATH"]      = _q(Util::getFilePath(item->getTarget()));
334     params["TARGET"]    = _q(item->getTarget());
335 
336     params["USERS"] = QString("");
337 
338     QStringList user_list;
339 
340     QueueItem::SourceConstIter it = item->getSources().begin();
341 
342     for (; it != item->getSources().end(); ++it){
343         HintedUser usr = it->getUser();
344         const dcpp::CID &cid = usr.user->getCID();
345 
346         if (usr.user->isOnline())
347             ++online;
348 
349         nick = WulforUtil::getInstance()->getNicks(cid, _q(usr.hint));
350 
351         if (!nick.isEmpty()){
352             source[nick] = _q(cid.toBase32());
353             user_list.push_back(nick);
354         }
355     }
356 
357     if (!user_list.isEmpty())
358         params["USERS"] = user_list.join(", ");
359     else
360         params["USERS"] = tr("No users...");
361 
362     Q_D(DownloadQueue);
363 
364     d->sources[_q(item->getTarget())] = source;
365 
366     if (item->isWaiting())
367         params["STATUS"] = tr("%1 of %2 user(s) online").arg(online).arg(item->getSources().size());
368     else
369         params["STATUS"] = tr("Running...");
370 
371     params["ESIZE"] = (qlonglong)item->getSize();
372     params["DOWN"]  = (qlonglong)item->getDownloadedBytes();
373     params["PRIO"]  = static_cast<int>(item->getPriority());
374 
375     source.clear();
376 
377     params["ERRORS"] = QString("");
378 
379     it = item->getBadSources().begin();
380 
381     for (; it != item->getBadSources().end(); ++it){
382         QString errors = params["ERRORS"].toString();
383         UserPtr usr = it->getUser();
384 
385         nick = WulforUtil::getInstance()->getNicks(usr->getCID());
386         source[nick] = _q(usr->getCID().toBase32());
387 
388         if (!it->isSet(QueueItem::Source::FLAG_REMOVED)){
389             if (!errors.isEmpty())
390                 errors += ", ";
391 
392             errors += nick + " (";
393 
394             if (it->isSet(QueueItem::Source::FLAG_FILE_NOT_AVAILABLE))
395                 errors += tr("File not available");
396             else if (it->isSet(QueueItem::Source::FLAG_PASSIVE))
397                 errors += tr("Passive user");
398             else if (it->isSet(QueueItem::Source::FLAG_CRC_FAILED))
399                 errors += tr("Checksum mismatch");
400             else if (it->isSet(QueueItem::Source::FLAG_BAD_TREE))
401                 errors += tr("Full tree does not match TTH root");
402             else if (it->isSet(QueueItem::Source::FLAG_SLOW_SOURCE))
403                 errors += tr("Source too slow");
404             else if (it->isSet(QueueItem::Source::FLAG_NO_TTHF))
405                 errors += tr("Remote client does not fully support TTH - cannot download");
406 
407             params["ERRORS"] = errors + ")";
408         }
409     }
410 
411     if (params["ERRORS"].toString().isEmpty())
412         params["ERRORS"] = tr("No errors");
413 
414     d->badSources[_q(item->getTarget())] = source;
415 
416     params["ADDED"] = _q(Util::formatTime("%Y-%m-%d %H:%M", item->getAdded()));
417     params["TTH"] = _q(item->getTTH().toBase32());
418 
419 }
420 
getSources()421 QStringList DownloadQueue::getSources(){
422     Q_D(DownloadQueue);
423 
424     auto s_it = d->sources.begin();
425     QStringList ret;
426 
427     for (; s_it != d->sources.end(); ++s_it){
428         QString target = s_it.key();
429         QString users;
430         auto it = s_it.value().begin();
431 
432         for (; it != s_it.value().end(); ++it){
433             users += it.key() + "(" + it.value() + ") ";
434         }
435 
436         ret.push_back(target + "::" + users);
437     }
438 
439     return ret;
440 }
441 
removeTarget(const QString & target)442 void DownloadQueue::removeTarget(const QString &target){
443     QueueManager *QM = QueueManager::getInstance();
444 
445     try {
446         QM->remove(target.toStdString());
447     }
448     catch (const Exception&){}
449 }
450 
removeSource(const QString & cid,const QString & target)451 void DownloadQueue::removeSource(const QString &cid, const QString &target){
452     QueueManager *QM = QueueManager::getInstance();
453     Q_D(DownloadQueue);
454 
455     if (d->sources.contains(target) && !cid.isEmpty()){
456         UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
457 
458         if (user){
459             try {
460                 QM->removeSource(user, QueueItem::Source::FLAG_REMOVED);
461             }
462             catch (const Exception&){}
463         }
464     }
465 }
466 
loadList()467 void DownloadQueue::loadList(){
468     VarMap params;
469 
470     const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
471 
472     for (auto it = ll.begin(); it != ll.end(); ++it){
473         getParams(params, it->second);
474 
475         addFile(params);
476     }
477 
478     QueueManager::getInstance()->unlockQueue();
479 
480     Q_D(DownloadQueue);
481 
482     d->queue_model->sort();
483 }
484 
addFile(const DownloadQueue::VarMap & map)485 void DownloadQueue::addFile(const DownloadQueue::VarMap &map){
486     Q_D(DownloadQueue);
487 
488     d->queue_model->addItem(map);
489 }
490 
remFile(const VarMap & map)491 void DownloadQueue::remFile(const VarMap &map){
492     Q_D(DownloadQueue);
493 
494     if (d->queue_model->remItem(map)){
495         auto it = d->sources.find(map["TARGET"].toString());
496 
497         if (it != d->sources.end())
498             d->sources.erase(it);
499 
500         it = d->badSources.find(map["TARGET"].toString());
501 
502         if (it != d->badSources.end())
503             d->badSources.erase(it);
504     }
505 }
506 
updateFile(const DownloadQueue::VarMap & map)507 void DownloadQueue::updateFile(const DownloadQueue::VarMap &map){
508     Q_D(DownloadQueue);
509 
510     d->queue_model->updItem(map);
511 }
512 
getCID(const VarMap & map)513 QString DownloadQueue::getCID(const VarMap &map){
514     if (map.size() < 1)
515         return "";
516 
517     auto it = map.constBegin();
518 
519     return (it.value()).toString();
520 }
521 
getChilds(DownloadQueueItem * i,QList<DownloadQueueItem * > & list)522 void DownloadQueue::getChilds(DownloadQueueItem *i, QList<DownloadQueueItem *> &list){
523     if (!i)
524         return;
525 
526     if (!i->dir && !list.contains(i)){
527         list.push_back(i);
528 
529         return;
530     }
531 
532     if (i->childCount() < 1)
533         return;
534 
535     for (const auto &ii : i->childItems)
536         getChilds(ii, list);
537 }
538 
getItems(const QModelIndexList & list,QList<DownloadQueueItem * > & items)539 void DownloadQueue::getItems(const QModelIndexList &list, QList<DownloadQueueItem*> &items){
540     items.clear();
541 
542     if (list.isEmpty())
543         return;
544 
545     for (const auto &i : list){
546         DownloadQueueItem *item = reinterpret_cast<DownloadQueueItem*>(i.internalPointer());
547 
548         getChilds(item, items);
549     }
550 }
551 
slotContextMenu(const QPoint &)552 void DownloadQueue::slotContextMenu(const QPoint &){
553     QModelIndexList list = treeView_TARGET->selectionModel()->selectedRows(0);
554     QList<DownloadQueueItem*> items;
555 
556     if (list.isEmpty())
557         return;
558 
559     getItems(list, items);
560 
561     if (items.isEmpty())
562         return;
563 
564     DownloadQueueItem *item = reinterpret_cast<DownloadQueueItem*>(items.at(0));
565 
566     QString target = item->data(COLUMN_DOWNLOADQUEUE_PATH).toString() + item->data(COLUMN_DOWNLOADQUEUE_NAME).toString();
567 
568     if (target.isEmpty())
569         return;
570 
571     Q_D(DownloadQueue);
572 
573     Menu::Action act = d->menu->exec(d->sources, target, items.size() > 1);
574     QueueManager *QM = QueueManager::getInstance();
575     QVariant arg = d->menu->getArg();
576     VarMap rmap;
577 
578     /** Now re-read selected indexes and remove broken items */
579     list = treeView_TARGET->selectionModel()->selectedRows(0);
580 
581     getItems(list, items);
582 
583     if (items.isEmpty())
584         return;
585 
586     switch (act){
587         case Menu::Alternates:
588         {
589             SearchFrame *sf = ArenaWidgetFactory().create<SearchFrame>();
590 
591             for (const auto &i : items)
592                 sf->searchAlternates(i->data(COLUMN_DOWNLOADQUEUE_TTH).toString());
593 
594             break;
595         }
596         case Menu::Magnet:
597         {
598             QString magnet = "";
599 
600             for (const auto &i : items)
601                 magnet += WulforUtil::getInstance()->makeMagnet(
602                         i->data(COLUMN_DOWNLOADQUEUE_NAME).toString(),
603                         i->data(COLUMN_DOWNLOADQUEUE_ESIZE).toLongLong(),
604                         i->data(COLUMN_DOWNLOADQUEUE_TTH).toString()) + "\n";
605 
606             if (!magnet.isEmpty())
607                 qApp->clipboard()->setText(magnet, QClipboard::Clipboard);
608 
609             break;
610         }
611         case Menu::MagnetWeb:
612         {
613             QString magnet = "";
614 
615             for (const auto &i : items){
616                 magnet += "[magnet=\"" +
617                     WulforUtil::getInstance()->makeMagnet(
618                         i->data(COLUMN_DOWNLOADQUEUE_NAME).toString(),
619                         i->data(COLUMN_DOWNLOADQUEUE_ESIZE).toLongLong(),
620                         i->data(COLUMN_DOWNLOADQUEUE_TTH).toString()) +
621                     "\"]"+i->data(COLUMN_DOWNLOADQUEUE_NAME).toString()+"[/magnet]\n";
622             }
623 
624             if (!magnet.isEmpty())
625                 qApp->clipboard()->setText(magnet, QClipboard::Clipboard);
626 
627             break;
628         }
629         case Menu::MagnetInfo:
630         {
631             QString magnet = "";
632 
633             for (const auto &i : items){
634                 magnet = WulforUtil::getInstance()->makeMagnet(
635                     i->data(COLUMN_DOWNLOADQUEUE_NAME).toString(),
636                     i->data(COLUMN_DOWNLOADQUEUE_ESIZE).toLongLong(),
637                     i->data(COLUMN_DOWNLOADQUEUE_TTH).toString()) + "\n";
638 
639                 if (!magnet.isEmpty()){
640                     Magnet m(this);
641                     m.setLink(magnet);
642                     m.exec();
643                 }
644             }
645 
646             break;
647         }
648         case Menu::RenameMove:
649         {
650             for (const auto &i : items){
651                 QString target = i->data(COLUMN_DOWNLOADQUEUE_PATH).toString() +
652                                  i->data(COLUMN_DOWNLOADQUEUE_NAME).toString();
653                 QString new_target = QFileDialog::getSaveFileName(this, tr("Choose filename"), target, tr("All files (*.*)"));
654 
655                 if (!new_target.isEmpty() && new_target != target){
656                     new_target = QDir::toNativeSeparators(new_target);
657                     try {
658                         QM->move(target.toStdString(), new_target.toStdString());
659                     }
660                     catch (const Exception &){}
661                 }
662             }
663 
664             break;
665         }
666         case Menu::SetPriority:
667         {
668             for (const auto &i : items){
669                 QString target = i->data(COLUMN_DOWNLOADQUEUE_PATH).toString() + i->data(COLUMN_DOWNLOADQUEUE_NAME).toString();
670 
671                 try {
672                     QM->setPriority(target.toStdString(), static_cast<QueueItem::Priority>(arg.toInt()));
673                 }
674                 catch (const Exception&) {}
675             }
676 
677             break;
678         }
679         case Menu::Browse:
680         {
681             rmap = arg.toMap();
682             QString cid = getCID(rmap);
683 
684             if (d->sources.contains(target) && !cid.isEmpty()){
685                 UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
686 
687                 if (user){
688                     try {
689                         QM->addList(HintedUser(user, ""), QueueItem::FLAG_CLIENT_VIEW, "");
690                     }
691                     catch (const Exception&){}
692                 }
693             }
694 
695             break;
696         }
697         case Menu::SendPM:
698         {
699             rmap = arg.toMap();
700             auto it = rmap.constBegin();
701             dcpp::CID cid(_tq(getCID(rmap)));
702             QString nick = ((++it).key());
703             QList<QObject*> list = HubManager::getInstance()->getHubs();
704 
705             for (const auto &obj : list){
706                 HubFrame *fr = qobject_cast<HubFrame*>(obj);
707 
708                 if (!fr)
709                     continue;
710 
711                 if (fr->hasCID(cid, nick)){
712                     fr->createPMWindow(cid);
713 
714                     break;
715                 }
716             }
717 
718             break;
719         }
720         case Menu::RemoveSource:
721         {
722             rmap = arg.toMap();
723             QString cid = getCID(rmap);
724 
725             if (d->sources.contains(target) && !cid.isEmpty()){
726                 UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
727 
728                 if (user){
729                     try {
730                         QM->removeSource(target.toStdString(), user, QueueItem::Source::FLAG_REMOVED);
731                     }
732                     catch (const Exception&){}
733                 }
734             }
735 
736             break;
737         }
738         case Menu::RemoveUser:
739         {
740             rmap = arg.toMap();
741             QString cid = getCID(rmap);
742 
743             if (d->sources.contains(target) && !cid.isEmpty()){
744                 UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
745 
746                 if (user){
747                     try {
748                         QM->removeSource(user, QueueItem::Source::FLAG_REMOVED);
749                     }
750                     catch (const Exception&){}
751                 }
752             }
753 
754             break;
755         }
756         case Menu::Remove:
757         {
758             for (const auto &i : items){
759                 QString target = i->data(COLUMN_DOWNLOADQUEUE_PATH).toString() + i->data(COLUMN_DOWNLOADQUEUE_NAME).toString();
760 
761                 try {
762                     QM->remove(target.toStdString());
763                 }
764                 catch (const Exception &){}
765             }
766 
767             break;
768         }
769         default:
770             break;
771     }
772 }
773 
slotCollapseRow(const QModelIndex & row)774 void DownloadQueue::slotCollapseRow(const QModelIndex &row){
775     if (row.isValid())
776         treeView_TARGET->collapse(row);
777 }
778 
slotHeaderMenu(const QPoint &)779 void DownloadQueue::slotHeaderMenu(const QPoint&){
780     WulforUtil::headerMenu(treeView_TARGET);
781 }
782 
slotUpdateStats(quint64 files,quint64 size)783 void DownloadQueue::slotUpdateStats(quint64 files, quint64 size){
784     if (static_cast<qint64>(size) < 0)
785         size = 0;
786 
787     if (files == size && size == 0)
788         label_STATS->hide();
789 
790     if (label_STATS->isHidden())
791         label_STATS->show();
792 
793     label_STATS->setText(tr("Total files: <b>%1</b> Total size: <b>%2</b>").arg(files).arg(WulforUtil::formatBytes(size)));
794 }
795 
slotSettingsChanged(const QString & key,const QString & value)796 void DownloadQueue::slotSettingsChanged(const QString &key, const QString &value){
797     if (key == WS_TRANSLATION_FILE)
798         retranslateUi(this);
799 }
800 
on(QueueManagerListener::Added,QueueItem * item)801 void DownloadQueue::on(QueueManagerListener::Added, QueueItem *item) noexcept{
802     VarMap params;
803     getParams(params, item);
804 
805     emit coreAdded(params);
806     emit added(_q(item->getTargetFileName()));
807 }
808 
on(QueueManagerListener::Moved,QueueItem * item,const std::string & oldTarget)809 void DownloadQueue::on(QueueManagerListener::Moved, QueueItem *item, const std::string &oldTarget) noexcept{
810     VarMap params;
811     getParams(params, item);
812 
813     emit coreMoved(params);
814     emit moved(_q(oldTarget), _q(item->getTargetFileName()));
815 }
816 
on(QueueManagerListener::Removed,QueueItem * item)817 void DownloadQueue::on(QueueManagerListener::Removed, QueueItem *item) noexcept{
818     VarMap params;
819     getParams(params, item);
820 
821     emit coreRemoved(params);
822     emit removed(_q(item->getTargetFileName()));
823 }
824 
on(QueueManagerListener::SourcesUpdated,QueueItem * item)825 void DownloadQueue::on(QueueManagerListener::SourcesUpdated, QueueItem *item) noexcept{
826     VarMap params;
827     getParams(params, item);
828 
829     emit coreSourcesUpdated(params);
830 }
831 
on(QueueManagerListener::StatusUpdated,QueueItem * item)832 void DownloadQueue::on(QueueManagerListener::StatusUpdated, QueueItem *item) noexcept{
833     VarMap params;
834     getParams(params, item);
835 
836     emit coreStatusUpdated(params);
837 }
838