1 /*
2  * Copyright © 2004-2010 Jens Oknelid, paskharen@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * In addition, as a special exception, compiling, linking, and/or
19  * using OpenSSL with this program is allowed.
20  */
21 
22 #include "downloadqueue.hh"
23 #include <dcpp/ResourceManager.h>
24 #include "search.hh"
25 #include "settingsmanager.hh"
26 #include "wulformanager.hh"
27 #include "WulforUtil.hh"
28 
29 using namespace std;
30 using namespace dcpp;
31 
DownloadQueue()32 DownloadQueue::DownloadQueue():
33     BookEntry(Entry::DOWNLOAD_QUEUE, _("Download Queue"), "downloadqueue.ui"),
34     currentItems(0),
35     totalItems(0),
36     currentSize(0),
37     totalSize(0)
38 {
39 #if !GTK_CHECK_VERSION(3,0,0)
40     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusMain")),FALSE);
41     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusItems")),FALSE);
42     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusFileSize")),FALSE);
43     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusFiles")),FALSE);
44     gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(getWidget("statusTotalSize")),FALSE);
45 #endif
46 
47     // Configure the dialogs
48     File::ensureDirectory(SETTING(DOWNLOAD_DIRECTORY));
49     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(getWidget("dirChooserDialog")), Text::fromUtf8(SETTING(DOWNLOAD_DIRECTORY)).c_str());
50     gtk_dialog_set_alternative_button_order(GTK_DIALOG(getWidget("dirChooserDialog")), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
51 
52     // menu
53     g_object_ref_sink(getWidget("dirMenu"));
54     g_object_ref_sink(getWidget("fileMenu"));
55 
56     // Initialize directory treeview
57     dirView.setView(GTK_TREE_VIEW(getWidget("dirView")));
58     dirView.insertColumn("Dir", G_TYPE_STRING, TreeView::ICON_STRING, -1, "Icon");
59     dirView.insertHiddenColumn("Path", G_TYPE_STRING);
60     dirView.insertHiddenColumn("File Count", G_TYPE_INT);
61     dirView.insertHiddenColumn("Icon", G_TYPE_STRING);
62     dirView.finalize();
63     dirStore = gtk_tree_store_newv(dirView.getColCount(), dirView.getGTypes());
64     gtk_tree_view_set_model(dirView.get(), GTK_TREE_MODEL(dirStore));
65     g_object_unref(dirStore);
66     dirSelection = gtk_tree_view_get_selection(dirView.get());
67     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dirStore), dirView.col("Dir"), GTK_SORT_ASCENDING);
68     gtk_tree_view_set_enable_tree_lines(dirView.get(), TRUE);
69 
70     // Initialize file treeview
71     fileView.setView(GTK_TREE_VIEW(getWidget("fileView")), TRUE, "downloadqueue");
72     fileView.insertColumn(_("Filename"), G_TYPE_STRING, TreeView::ICON_STRING, 200, "Icon");
73     fileView.insertColumn(_("Status"), G_TYPE_STRING, TreeView::STRING, 100);
74     fileView.insertColumn(_("Size"), G_TYPE_STRING, TreeView::STRING, 100);
75     fileView.insertColumn(_("Downloaded"), G_TYPE_STRING, TreeView::STRING, 150);
76     fileView.insertColumn(_("Priority"), G_TYPE_STRING, TreeView::STRING, 75);
77     fileView.insertColumn(_("Users"), G_TYPE_STRING, TreeView::STRING, 200);
78     fileView.insertColumn(_("Path"), G_TYPE_STRING, TreeView::STRING, 200);
79     fileView.insertColumn(_("Exact Size"), G_TYPE_STRING, TreeView::STRING, 100);
80     fileView.insertColumn(_("Errors"), G_TYPE_STRING, TreeView::STRING, 200);
81     fileView.insertColumn(_("Added"), G_TYPE_STRING, TreeView::STRING, 120);
82     fileView.insertColumn(_("TTH"), G_TYPE_STRING, TreeView::STRING, 125);
83     fileView.insertHiddenColumn("Size Sort", G_TYPE_INT64);
84     fileView.insertHiddenColumn("Downloaded Sort", G_TYPE_INT64);
85     fileView.insertHiddenColumn("Target", G_TYPE_STRING);
86     fileView.insertHiddenColumn("Icon", G_TYPE_STRING);
87     fileView.finalize();
88     fileStore = gtk_list_store_newv(fileView.getColCount(), fileView.getGTypes());
89     gtk_tree_view_set_model(fileView.get(), GTK_TREE_MODEL(fileStore));
90     g_object_unref(fileStore);
91     gtk_tree_selection_set_mode(gtk_tree_view_get_selection(fileView.get()), GTK_SELECTION_MULTIPLE);
92     fileSelection = gtk_tree_view_get_selection(fileView.get());
93     fileView.setSortColumn_gui(_("Size"), "Size Sort");
94     fileView.setSortColumn_gui(_("Exact Size"), "Size Sort");
95     fileView.setSortColumn_gui(_("Downloaded"), "Downloaded Sort");
96     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), fileView.col(_("Filename")), GTK_SORT_ASCENDING);
97     gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(fileView.get(), fileView.col(_("Filename"))), TRUE);
98 
99     // Connect the signals to their callback functions.
100     g_signal_connect(getWidget("pausedPriorityItem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
101     g_signal_connect(getWidget("lowestPriorityItem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
102     g_signal_connect(getWidget("lowPrioritytem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
103     g_signal_connect(getWidget("normalPriorityItem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
104     g_signal_connect(getWidget("highPriorityItem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
105     g_signal_connect(getWidget("highestPriorityItem"), "activate", G_CALLBACK(onDirPriorityClicked_gui), (gpointer)this);
106     g_signal_connect(getWidget("moveDirItem"), "activate", G_CALLBACK(onDirMoveClicked_gui), (gpointer)this);
107     g_signal_connect(getWidget("removeDirItem"), "activate", G_CALLBACK(onDirRemoveClicked_gui), (gpointer)this);
108     g_signal_connect(getWidget("searchForAlternatesItem"), "activate", G_CALLBACK(onFileSearchAlternatesClicked_gui), (gpointer)this);
109     g_signal_connect(getWidget("moveFileItem"), "activate", G_CALLBACK(onFileMoveClicked_gui), (gpointer)this);
110     g_signal_connect(getWidget("filePausedItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
111     g_signal_connect(getWidget("fileLowestPriorityItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
112     g_signal_connect(getWidget("fileLowPriorityItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
113     g_signal_connect(getWidget("fileNormalPriorityItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
114     g_signal_connect(getWidget("fileHighPriorityItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
115     g_signal_connect(getWidget("fileHighestPriorityItem"), "activate", G_CALLBACK(onFilePriorityClicked_gui), (gpointer)this);
116     g_signal_connect(getWidget("fileRemoveItem"), "activate", G_CALLBACK(onFileRemoveClicked_gui), (gpointer)this);
117     g_signal_connect(getWidget("copyMagnetItem"), "activate", G_CALLBACK(onCopyMagnetClicked_gui), (gpointer)this);
118     g_signal_connect(dirView.get(), "button-press-event", G_CALLBACK(onDirButtonPressed_gui), (gpointer)this);
119     g_signal_connect(dirView.get(), "button-release-event", G_CALLBACK(onDirButtonReleased_gui), (gpointer)this);
120     g_signal_connect(dirView.get(), "key-release-event", G_CALLBACK(onDirKeyReleased_gui), (gpointer)this);
121     g_signal_connect(fileView.get(), "button-press-event", G_CALLBACK(onFileButtonPressed_gui), (gpointer)this);
122     g_signal_connect(fileView.get(), "button-release-event", G_CALLBACK(onFileButtonReleased_gui), (gpointer)this);
123     g_signal_connect(fileView.get(), "key-release-event", G_CALLBACK(onFileKeyReleased_gui), (gpointer)this);
124 
125     // Set the pane position
126     gtk_paned_set_position(GTK_PANED(getWidget("pane")), WGETI("downloadqueue-pane-position"));
127     int panePosition = WGETI("downloadqueue-pane-position");
128     if (panePosition > 10)
129         gtk_paned_set_position(GTK_PANED(getWidget("pane")), panePosition);
130 }
131 
~DownloadQueue()132 DownloadQueue::~DownloadQueue()
133 {
134     QueueManager::getInstance()->removeListener(this);
135 
136     // Save the pane position
137     int panePosition = gtk_paned_get_position(GTK_PANED(getWidget("pane")));
138     if (panePosition > 10)
139         WSET("downloadqueue-pane-position", panePosition);
140 
141     gtk_widget_destroy(getWidget("dirChooserDialog"));
142     g_object_unref(getWidget("dirMenu"));
143     g_object_unref(getWidget("fileMenu"));
144 }
145 
show()146 void DownloadQueue::show()
147 {
148     buildList_client();
149     QueueManager::getInstance()->addListener(this);
150 }
151 
buildDynamicMenu_gui()152 void DownloadQueue::buildDynamicMenu_gui()
153 {
154     bool showMenus = FALSE;
155     bool showReAddMenu = FALSE;
156     int count = gtk_tree_selection_count_selected_rows(fileSelection);
157 
158     if (count == 1)
159     {
160         GtkTreeIter iter;
161         GList *list = gtk_tree_selection_get_selected_rows(fileSelection, NULL);
162         GtkTreePath *path = (GtkTreePath *)g_list_nth_data(list, 0);
163 
164         gtk_container_foreach(GTK_CONTAINER(getWidget("browseMenu")), (GtkCallback)gtk_widget_destroy, NULL);
165         gtk_container_foreach(GTK_CONTAINER(getWidget("pmMenu")), (GtkCallback)gtk_widget_destroy, NULL);
166         gtk_container_foreach(GTK_CONTAINER(getWidget("reAddMenu")), (GtkCallback)gtk_widget_destroy, NULL);
167         gtk_container_foreach(GTK_CONTAINER(getWidget("removeMenu")), (GtkCallback)gtk_widget_destroy, NULL);
168         gtk_container_foreach(GTK_CONTAINER(getWidget("removeAllMenu")), (GtkCallback)gtk_widget_destroy, NULL);
169 
170         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileStore), &iter, path))
171         {
172             GtkWidget *menuItem;
173             string target = fileView.getString(&iter, "Target");
174 
175             ///@todo: Fix this. sources & badSources should not be accessible from gui thread.
176             for (SourceIter it = sources[target].begin(); it != sources[target].end(); ++it)
177             {
178                 showMenus = TRUE;
179                 menuItem = gtk_menu_item_new_with_label(it->first.c_str());
180                 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("browseMenu")), menuItem);
181                 g_signal_connect(menuItem, "activate", G_CALLBACK(onFileGetListClicked_gui), (gpointer)this);
182 
183                 menuItem = gtk_menu_item_new_with_label(it->first.c_str());
184                 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("pmMenu")), menuItem);
185                 g_signal_connect(menuItem, "activate", G_CALLBACK(onFileSendPMClicked_gui), (gpointer)this);
186 
187                 menuItem = gtk_menu_item_new_with_label(it->first.c_str());
188                 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("removeMenu")), menuItem);
189                 g_signal_connect(menuItem, "activate", G_CALLBACK(onFileRemoveSourceClicked_gui), (gpointer)this);
190 
191                 menuItem = gtk_menu_item_new_with_label(it->first.c_str());
192                 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("removeAllMenu")), menuItem);
193                 g_signal_connect(menuItem, "activate", G_CALLBACK(onFileRemoveUserFromQueueClicked_gui), (gpointer)this);
194             }
195 
196             for (SourceIter it = badSources[target].begin(); it != badSources[target].end(); ++it)
197             {
198                 showReAddMenu = TRUE;
199                 menuItem = gtk_menu_item_new_with_label(it->first.c_str());
200                 gtk_menu_shell_append(GTK_MENU_SHELL(getWidget("reAddMenu")), menuItem);
201                 g_signal_connect(menuItem, "activate", G_CALLBACK(onFileReAddSourceClicked_gui), (gpointer)this);
202             }
203         }
204         gtk_tree_path_free(path);
205         g_list_free(list);
206 
207         gtk_widget_set_sensitive(getWidget("searchForAlternatesItem"), TRUE);
208     }
209     else
210         gtk_widget_set_sensitive(getWidget("searchForAlternatesItem"), FALSE);
211 
212     if (showMenus)
213     {
214         gtk_widget_show_all(getWidget("browseMenu"));
215         gtk_widget_show_all(getWidget("pmMenu"));
216         gtk_widget_show_all(getWidget("removeMenu"));
217         gtk_widget_show_all(getWidget("removeAllMenu"));
218     }
219     if (showReAddMenu)
220         gtk_widget_show_all(getWidget("reAddMenu"));
221 
222     gtk_widget_set_sensitive(getWidget("getFileListItem"), showMenus);
223     gtk_widget_set_sensitive(getWidget("sendPrivateMessageItem"), showMenus);
224     gtk_widget_set_sensitive(getWidget("removeSourceItem"), showMenus);
225     gtk_widget_set_sensitive(getWidget("removeUserFromQueueItem"), showMenus);
226     gtk_widget_set_sensitive(getWidget("reAddSourceItem"), showReAddMenu);
227 }
228 
setStatus_gui(string text,string statusItem)229 void DownloadQueue::setStatus_gui(string text, string statusItem)
230 {
231     if (!text.empty() && !statusItem.empty())
232     {
233         if (statusItem == "statusMain")
234             text = "[" + Util::getShortTimeString() + "] " + text;
235 
236         gtk_statusbar_pop(GTK_STATUSBAR(getWidget(statusItem)), 0);
237         gtk_statusbar_push(GTK_STATUSBAR(getWidget(statusItem)), 0, text.c_str());
238     }
239 }
240 
updateStatus_gui()241 void DownloadQueue::updateStatus_gui()
242 {
243     setStatus_gui(_("Items: ") + Util::toString(currentItems), "statusItems");
244     setStatus_gui(_("Size: ") + Util::formatBytes(currentSize), "statusFileSize");
245     setStatus_gui(_("Files: ") + Util::toString(totalItems), "statusFiles");
246     setStatus_gui(_("Size: ") + Util::formatBytes(totalSize), "statusTotalSize");
247 }
248 
addFiles_gui(vector<StringMap> files,bool firstUpdate)249 void DownloadQueue::addFiles_gui(vector<StringMap> files, bool firstUpdate)
250 {
251     if (!files.empty() && currentDir == files[0]["Path"] &&
252         gtk_tree_selection_get_selected(dirSelection, NULL, NULL))
253     {
254         if (firstUpdate)
255             gtk_list_store_clear(fileStore);
256 
257         gint sortColumn;
258         GtkSortType sortType;
259         gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(fileStore), &sortColumn, &sortType);
260         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, sortType);
261 
262         for (auto it = files.begin(); it != files.end(); ++it)
263             addFile_gui(*it, FALSE);
264 
265         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), sortColumn, sortType);
266         gtk_tree_view_scroll_to_point(fileView.get(), 0, 0);
267     }
268 }
269 
addFile_gui(StringMap params,bool updateDirs)270 void DownloadQueue::addFile_gui(StringMap params, bool updateDirs)
271 {
272     GtkTreeIter iter;
273     int64_t size = Util::toInt64(params["Size Sort"]);
274 
275     if (gtk_tree_selection_get_selected(dirSelection, NULL, NULL) && currentDir == params["Path"])
276     {
277         ++currentItems;
278         currentSize += size;
279 
280         gtk_list_store_append(fileStore, &iter);
281         gtk_list_store_set(fileStore, &iter,
282             fileView.col(_("Filename")), params["Filename"].c_str(),
283             fileView.col(_("Users")), params["Users"].c_str(),
284             fileView.col(_("Status")), params["Status"].c_str(),
285             fileView.col(_("Size")), params["Size"].c_str(),
286             fileView.col(_("Exact Size")), params["Exact Size"].c_str(),
287             fileView.col("Size Sort"), size,
288             fileView.col(_("Downloaded")), params["Downloaded"].c_str(),
289             fileView.col("Downloaded Sort"), Util::toInt64(params["Downloaded Sort"]),
290             fileView.col(_("Priority")), params["Priority"].c_str(),
291             fileView.col(_("Path")), params["Path"].c_str(),
292             fileView.col(_("Errors")), params["Errors"].c_str(),
293             fileView.col(_("Added")), params["Added"].c_str(),
294             fileView.col(_("TTH")), params["TTH"].c_str(),
295             fileView.col("Target"), params["Target"].c_str(),
296             fileView.col("Icon"), "icon-file",
297             -1);
298 
299         if (WGETB("bold-queue"))
300             setBold_gui();
301     }
302 
303     if (updateDirs)
304     {
305         ++totalItems;
306         totalSize += size;
307 
308         // Ensure root node
309         if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dirStore), &iter))
310         {
311             gtk_tree_store_append(dirStore, &iter, NULL);
312             gtk_tree_store_set(dirStore, &iter,
313                 dirView.col("Dir"), "/",
314                 dirView.col("Path"), "/",
315                 dirView.col("File Count"), 0,
316                 dirView.col("Icon"), "icon-directory",
317                 -1);
318         }
319 
320         if (params["Path"].length() > 1)
321             addDir_gui(params["Path"].substr(1), &iter);
322     }
323 
324     updateStatus_gui();
325 }
326 
addDir_gui(const string & path,GtkTreeIter * parent)327 void DownloadQueue::addDir_gui(const string &path, GtkTreeIter *parent)
328 {
329     if (path.empty() || !parent)
330         return;
331 
332     GtkTreeIter iter;
333     string::size_type i = path.find_first_of(PATH_SEPARATOR);
334     const string &dir = path.substr(0, i);
335     const string &fullpath = dirView.getString(parent, "Path") + dir + PATH_SEPARATOR_STR;
336     bool valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(dirStore), &iter, parent);
337 
338     while (valid)
339     {
340         if (dir == dirView.getString(&iter, "Dir"))
341         {
342             addDir_gui(path.substr(i + 1), &iter);
343 
344             if (fullpath == dirView.getString(parent, "Path") + path)
345             {
346                 int count = dirView.getValue<gint>(&iter, "File Count");
347                 gtk_tree_store_set(dirStore, &iter, dirView.col("File Count"), ++count, -1);
348             }
349 
350             return;
351         }
352         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dirStore), &iter);
353     }
354 
355     gtk_tree_store_append(dirStore, &iter, parent);
356     gtk_tree_store_set(dirStore, &iter,
357         dirView.col("Dir"), dir.c_str(),
358         dirView.col("Path"), fullpath.c_str(),
359         dirView.col("File Count"), 0,
360         dirView.col("Icon"), "icon-directory",
361         -1);
362 
363     GtkTreePath *treePath = gtk_tree_model_get_path(GTK_TREE_MODEL(dirStore), parent);
364     gtk_tree_view_expand_row(dirView.get(), treePath, FALSE);
365     gtk_tree_path_free(treePath);
366 
367     if (fullpath == dirView.getString(parent, "Path") + path)
368     {
369         int count = dirView.getValue<gint>(&iter, "File Count");
370         gtk_tree_store_set(dirStore, &iter, dirView.col("File Count"), ++count, -1);
371     }
372 
373     addDir_gui(path.substr(i + 1), &iter);
374 }
375 
updateFile_gui(StringMap params)376 void DownloadQueue::updateFile_gui(StringMap params)
377 {
378     if (gtk_tree_selection_get_selected(dirSelection, NULL, NULL) && currentDir == params["Path"])
379     {
380         GtkTreeIter iter;
381         GtkTreeModel *m = GTK_TREE_MODEL(fileStore);
382         bool valid = gtk_tree_model_get_iter_first(m, &iter);
383 
384         while (valid)
385         {
386             if (fileView.getString(&iter, "Target") == params["Target"])
387             {
388                 gtk_list_store_set(fileStore, &iter,
389                     fileView.col(_("Filename")), params["Filename"].c_str(),
390                     fileView.col(_("Users")), params["Users"].c_str(),
391                     fileView.col(_("Status")), params["Status"].c_str(),
392                     fileView.col(_("Size")), params["Size"].c_str(),
393                     fileView.col(_("Exact Size")), params["Exact Size"].c_str(),
394                     fileView.col("Size Sort"), Util::toInt64(params["Size Sort"]),
395                     fileView.col(_("Downloaded")), params["Downloaded"].c_str(),
396                     fileView.col("Downloaded Sort"), Util::toInt64(params["Downloaded Sort"]),
397                     fileView.col(_("Priority")), params["Priority"].c_str(),
398                     fileView.col(_("Path")), params["Path"].c_str(),
399                     fileView.col(_("Errors")), params["Errors"].c_str(),
400                     fileView.col(_("Added")), params["Added"].c_str(),
401                     fileView.col(_("TTH")), params["TTH"].c_str(),
402                     fileView.col("Target"), params["Target"].c_str(),
403                     fileView.col("Icon"), "icon-file",
404                     -1);
405                 return;
406             }
407             valid = gtk_tree_model_iter_next(m, &iter);
408         }
409     }
410 }
411 
removeFile_gui(string target,int64_t size)412 void DownloadQueue::removeFile_gui(string target, int64_t size)
413 {
414     GtkTreeIter iter;
415     bool valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileStore), &iter);
416     string path = Util::getFilePath(target);
417 
418     while (valid)
419     {
420         if (target == fileView.getString(&iter, "Target"))
421         {
422             --currentItems;
423             currentSize -= size;
424             gtk_list_store_remove(fileStore, &iter);
425             break;
426         }
427         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileStore), &iter);
428     }
429 
430     --totalItems;
431     totalSize -= size;
432 
433     if (path.length() > 1 && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dirStore), &iter))
434         removeDir_gui(path.substr(1), &iter);
435 
436     updateStatus_gui();
437 
438     if (WGETB("bold-queue"))
439         setBold_gui();
440 }
441 
removeDir_gui(const string & path,GtkTreeIter * parent)442 void DownloadQueue::removeDir_gui(const string &path, GtkTreeIter *parent)
443 {
444     if (path.empty() || !parent)
445         return;
446 
447     GtkTreeIter iter;
448     string curPath;
449     int count = 0;
450     string::size_type i = path.find_first_of(PATH_SEPARATOR);
451     string dir = path.substr(0, i);
452     string fullpath = dirView.getString(parent, "Path") + path;
453     bool valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(dirStore), &iter, parent);
454 
455     while (valid)
456     {
457         if (dir == dirView.getString(&iter, "Dir"))
458         {
459             removeDir_gui(path.substr(i + 1), &iter);
460 
461             curPath = dirView.getString(&iter, "Path");
462             count = dirView.getValue<gint>(&iter, "File Count");
463             if (curPath == fullpath)
464                 gtk_tree_store_set(dirStore, &iter, dirView.col("File Count"), --count, -1);
465 
466             // No files in leaf node
467             if (count <= 0 && !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(dirStore), &iter))
468             {
469                 gtk_tree_store_remove(dirStore, &iter);
470                 if (currentDir == curPath)
471                 {
472                     gtk_list_store_clear(fileStore);
473                     currentDir.clear();
474                 }
475             }
476 
477             break;
478         }
479         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dirStore), &iter);
480     }
481 }
482 
updateFileView_gui()483 void DownloadQueue::updateFileView_gui()
484 {
485     GtkTreeIter iter;
486 
487     if (gtk_tree_selection_get_selected(dirSelection, NULL, &iter))
488     {
489         string dir = dirView.getString(&iter, "Path");
490         if (dir != currentDir)
491         {
492             gtk_list_store_clear(fileStore);
493             currentDir = dir;
494             currentItems = 0;
495             currentSize = 0;
496 
497             typedef Func1<DownloadQueue, string> F1;
498             F1 *func = new F1(this, &DownloadQueue::updateFileView_client, currentDir);
499             WulforManager::get()->dispatchClientFunc(func);
500         }
501     }
502 }
503 
sendMessage_gui(string cid)504 void DownloadQueue::sendMessage_gui(string cid)
505 {
506     if (!cid.empty())
507         WulforManager::get()->getMainWindow()->addPrivateMessage_gui(Msg::UNKNOWN, cid);
508 }
509 
onDirButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)510 gboolean DownloadQueue::onDirButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
511 {
512     DownloadQueue *dq = (DownloadQueue *)data;
513     dq->dirPrevious = event->type;
514 
515     return FALSE;
516 }
517 
onDirButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)518 gboolean DownloadQueue::onDirButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
519 {
520     DownloadQueue *dq = (DownloadQueue *)data;
521 
522     if (dq->dirPrevious == GDK_BUTTON_PRESS && gtk_tree_selection_get_selected(dq->dirSelection, NULL, NULL))
523     {
524         if (event->button == 1)
525         {
526             dq->updateFileView_gui();
527         }
528         else if (event->button == 3)
529         {
530             dq->updateFileView_gui();
531             gtk_menu_popup(GTK_MENU(dq->getWidget("dirMenu")), NULL, NULL,
532                 NULL, NULL, 0, gtk_get_current_event_time());
533         }
534     }
535 
536     return FALSE;
537 }
538 
onDirKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)539 gboolean DownloadQueue::onDirKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
540 {
541     DownloadQueue *dq = (DownloadQueue *)data;
542     GtkTreeIter iter;
543 
544     if (gtk_tree_selection_get_selected(dq->dirSelection, NULL, &iter))
545     {
546         if (event->keyval == GDK_Delete || event->keyval == GDK_BackSpace)
547         {
548             dq->onDirRemoveClicked_gui(NULL, data);
549         }
550         else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
551         {
552             gtk_menu_popup(GTK_MENU(dq->getWidget("dirMenu")), NULL, NULL,
553                 NULL, NULL, 0, gtk_get_current_event_time());
554         }
555         else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
556             event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
557         {
558             dq->updateFileView_gui();
559         }
560         else if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
561         {
562             GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(dq->dirStore), &iter);
563             if (gtk_tree_view_row_expanded(dq->dirView.get(), path))
564                 gtk_tree_view_collapse_row(dq->dirView.get(), path);
565             else
566                 gtk_tree_view_expand_row(dq->dirView.get(), path, FALSE);
567             gtk_tree_path_free(path);
568         }
569     }
570 
571     return FALSE;
572 }
573 
onFileButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)574 gboolean DownloadQueue::onFileButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
575 {
576     DownloadQueue *dq = (DownloadQueue *)data;
577 
578     if (event->button == 3)
579     {
580         GtkTreePath *path;
581         if (gtk_tree_view_get_path_at_pos(dq->fileView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
582         {
583             bool selected = gtk_tree_selection_path_is_selected(dq->fileSelection, path);
584             gtk_tree_path_free(path);
585 
586             if (selected)
587                 return TRUE;
588         }
589     }
590     return FALSE;
591 }
592 
onFileButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)593 gboolean DownloadQueue::onFileButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
594 {
595     DownloadQueue *dq = (DownloadQueue *)data;
596 
597     if (event->button == 3 && event->type == GDK_BUTTON_RELEASE)
598     {
599         if (gtk_tree_selection_count_selected_rows(dq->fileSelection) > 0)
600         {
601             dq->buildDynamicMenu_gui();
602             gtk_menu_popup(GTK_MENU(dq->getWidget("fileMenu")), NULL, NULL,
603                 NULL, NULL, 0, gtk_get_current_event_time());
604             return TRUE;
605         }
606     }
607     return FALSE;
608 }
609 
onFileKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)610 gboolean DownloadQueue::onFileKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
611 {
612     DownloadQueue *dq = (DownloadQueue *)data;
613     int count = gtk_tree_selection_count_selected_rows(dq->fileSelection);
614 
615     if (count > 0)
616     {
617         if (event->keyval == GDK_Delete || event->keyval == GDK_BackSpace)
618         {
619             dq->onFileRemoveClicked_gui(NULL, data);
620         }
621         else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
622         {
623             dq->buildDynamicMenu_gui();
624             gtk_menu_popup(GTK_MENU(dq->getWidget("fileMenu")), NULL, NULL,
625                 NULL, NULL, 0, gtk_get_current_event_time());
626         }
627     }
628 
629     return FALSE;
630 }
631 
onDirPriorityClicked_gui(GtkMenuItem * item,gpointer data)632 void DownloadQueue::onDirPriorityClicked_gui(GtkMenuItem *item, gpointer data)
633 {
634     DownloadQueue *dq = (DownloadQueue *)data;
635     GtkTreeIter iter;
636 
637     if (gtk_tree_selection_get_selected(dq->dirSelection, NULL, &iter))
638     {
639         string path = dq->dirView.getString(&iter, "Path");
640         QueueItem::Priority priority;
641 
642         if (item == GTK_MENU_ITEM(dq->getWidget("pausedPriorityItem")))
643             priority = QueueItem::PAUSED;
644         else if (item == GTK_MENU_ITEM(dq->getWidget("lowestPriorityItem")))
645             priority = QueueItem::LOWEST;
646         else if (item == GTK_MENU_ITEM(dq->getWidget("lowPrioritytem")))
647             priority = QueueItem::LOW;
648         else if (item == GTK_MENU_ITEM(dq->getWidget("highPriorityItem")))
649             priority = QueueItem::HIGH;
650         else if (item == GTK_MENU_ITEM(dq->getWidget("highestPriorityItem")))
651             priority = QueueItem::HIGHEST;
652         else
653             priority = QueueItem::NORMAL;
654 
655         typedef Func2<DownloadQueue, string, QueueItem::Priority> F2;
656         F2 *func = new F2(dq, &DownloadQueue::setPriorityDir_client, path, priority);
657         WulforManager::get()->dispatchClientFunc(func);
658     }
659 }
660 
onDirMoveClicked_gui(GtkMenuItem * menuItem,gpointer data)661 void DownloadQueue::onDirMoveClicked_gui(GtkMenuItem *menuItem, gpointer data)
662 {
663     DownloadQueue *dq = (DownloadQueue *)data;
664     GtkTreeIter iter;
665 
666     if (gtk_tree_selection_get_selected(dq->dirSelection, NULL, &iter))
667     {
668         string path = Text::fromUtf8(dq->dirView.getString(&iter, "Path"));
669         GtkWidget *dialog = dq->getWidget("dirChooserDialog");
670         gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
671         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path.c_str());
672         gint response = gtk_dialog_run(GTK_DIALOG(dialog));
673 
674         // Widget failed if the dialog gets programmatically destroyed.
675         if (response == GTK_RESPONSE_NONE)
676             return;
677 
678         gtk_widget_hide(dialog);
679 
680         if (response == GTK_RESPONSE_OK)
681         {
682             gchar *temp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
683             if (temp)
684             {
685                 string target = Text::toUtf8(temp);
686                 g_free(temp);
687 
688                 if (target[target.length() - 1] != PATH_SEPARATOR)
689                     target += PATH_SEPARATOR;
690 
691                 typedef Func2<DownloadQueue, string, string> F2;
692                 F2 *func = new F2(dq, &DownloadQueue::moveDir_client, path, target);
693                 WulforManager::get()->dispatchClientFunc(func);
694             }
695         }
696     }
697 }
698 
onDirRemoveClicked_gui(GtkMenuItem * menuitem,gpointer data)699 void DownloadQueue::onDirRemoveClicked_gui(GtkMenuItem *menuitem, gpointer data)
700 {
701     DownloadQueue *dq = (DownloadQueue *)data;
702     GtkTreeIter iter;
703 
704     if (gtk_tree_selection_get_selected(dq->dirSelection, NULL, &iter))
705     {
706         string path = dq->dirView.getString(&iter, "Path");
707         gtk_list_store_clear(dq->fileStore);
708 
709         typedef Func1<DownloadQueue, string> F1;
710         F1 *func = new F1(dq, &DownloadQueue::removeDir_client, path);
711         WulforManager::get()->dispatchClientFunc(func);
712     }
713 }
714 
onFileSearchAlternatesClicked_gui(GtkMenuItem * item,gpointer data)715 void DownloadQueue::onFileSearchAlternatesClicked_gui(GtkMenuItem *item, gpointer data)
716 {
717     DownloadQueue *dq = (DownloadQueue *)data;
718     string tth;
719     GtkTreePath *path;
720     GtkTreeIter iter;
721     Search *s;
722     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
723 
724     for (GList *i = list; i; i = i->next)
725     {
726         path = (GtkTreePath *)i->data;
727         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
728         {
729             tth = dq->fileView.getString(&iter, _("TTH"));
730             if (!tth.empty())
731             {
732                 s = WulforManager::get()->getMainWindow()->addSearch_gui();
733                 s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
734             }
735         }
736         gtk_tree_path_free(path);
737     }
738     g_list_free(list);
739 }
740 
onCopyMagnetClicked_gui(GtkMenuItem * item,gpointer data)741 void DownloadQueue::onCopyMagnetClicked_gui(GtkMenuItem* item, gpointer data)
742 {
743     DownloadQueue *dq = (DownloadQueue *)data;
744     GtkTreePath *path;
745     GtkTreeIter iter;
746     string magnets, magnet, filename, tth;
747     int64_t size;
748     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
749 
750     for (GList *i = list; i; i = i->next)
751     {
752         path = (GtkTreePath *)i->data;
753         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
754         {
755             filename = dq->fileView.getString(&iter, _("Filename"));
756             size = dq->fileView.getValue<int64_t>(&iter, "Size Sort");
757             tth = dq->fileView.getString(&iter, _("TTH"));
758             magnet = WulforUtil::makeMagnet(filename, size, tth);
759 
760             if (!magnet.empty())
761             {
762                 if (!magnets.empty())
763                     magnets += '\n';
764                 magnets += magnet;
765             }
766         }
767         gtk_tree_path_free(path);
768     }
769     g_list_free(list);
770 
771     if (!magnets.empty())
772         gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), magnets.c_str(), magnets.length());
773 }
774 
onFileMoveClicked_gui(GtkMenuItem * menuItem,gpointer data)775 void DownloadQueue::onFileMoveClicked_gui(GtkMenuItem *menuItem, gpointer data)
776 {
777     DownloadQueue *dq = (DownloadQueue *)data;
778     typedef Func2<DownloadQueue, string, string> F2;
779     F2 *func;
780     string source;
781     GtkTreePath *path;
782     GtkTreeIter iter;
783     int count = gtk_tree_selection_count_selected_rows(dq->fileSelection);
784     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
785     GtkWidget *dialog = dq->getWidget("dirChooserDialog");
786 
787     if (gtk_tree_selection_get_selected(dq->dirSelection, NULL, &iter))
788     {
789         string filepath = Text::fromUtf8(dq->dirView.getString(&iter, "Path"));
790         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), filepath.c_str());
791     }
792 
793     if (count == 1)
794     {
795         path = (GtkTreePath *)list->data;
796         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
797         {
798             string target = Text::fromUtf8(dq->fileView.getString(&iter, _("Filename")));
799             gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), GTK_FILE_CHOOSER_ACTION_SAVE);
800             gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), target.c_str());
801             gint response = gtk_dialog_run(GTK_DIALOG(dialog));
802 
803             // Widget failed if the dialog gets programmatically destroyed.
804             if (response != GTK_RESPONSE_NONE)
805                 gtk_widget_hide(dialog);
806 
807             if (response == GTK_RESPONSE_OK)
808             {
809                 gchar *tmp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
810                 if (tmp)
811                 {
812                     target = Text::toUtf8(tmp);
813                     g_free(tmp);
814                     source = dq->fileView.getString(&iter, "Target");
815                     func = new F2(dq, &DownloadQueue::move_client, source, target);
816                     WulforManager::get()->dispatchClientFunc(func);
817                 }
818             }
819         }
820         gtk_tree_path_free(path);
821     }
822     else if (count > 1)
823     {
824         gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
825         gint response = gtk_dialog_run(GTK_DIALOG(dialog));
826 
827         // Widget failed if the dialog gets programmatically destroyed.
828         if (response != GTK_RESPONSE_NONE)
829             gtk_widget_hide(dialog);
830 
831         if (response == GTK_RESPONSE_OK)
832         {
833             gchar *tmp = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
834             if (tmp)
835             {
836                 string filename;
837                 string target = Text::toUtf8(tmp);
838                 g_free(tmp);
839 
840                 if (target[target.length() - 1] != PATH_SEPARATOR)
841                     target += PATH_SEPARATOR;
842 
843                 for (GList *i = list; i; i = i->next)
844                 {
845                     path = (GtkTreePath *)i->data;
846                     if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
847                     {
848                         source = dq->fileView.getString(&iter, "Target");
849                         filename = dq->fileView.getString(&iter, _("Filename"));
850                         func = new F2(dq, &DownloadQueue::move_client, source, target + filename);
851                         WulforManager::get()->dispatchClientFunc(func);
852                     }
853                     gtk_tree_path_free(path);
854                 }
855             }
856         }
857     }
858     g_list_free(list);
859 }
860 
onFilePriorityClicked_gui(GtkMenuItem * item,gpointer data)861 void DownloadQueue::onFilePriorityClicked_gui(GtkMenuItem *item, gpointer data)
862 {
863     DownloadQueue *dq = (DownloadQueue *)data;
864     typedef Func2<DownloadQueue, string, QueueItem::Priority> F2;
865     F2 *func;
866     string target;
867     GtkTreePath *path;
868     GtkTreeIter iter;
869     QueueItem::Priority priority;
870     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
871 
872     for (GList *i = list; i; i = i->next)
873     {
874         path = (GtkTreePath *)i->data;
875         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
876         {
877             target = dq->fileView.getString(&iter, "Target");
878 
879             if (item == GTK_MENU_ITEM(dq->getWidget("filePausedItem")))
880                 priority = QueueItem::PAUSED;
881             else if (item == GTK_MENU_ITEM(dq->getWidget("fileLowestPriorityItem")))
882                 priority = QueueItem::LOWEST;
883             else if (item == GTK_MENU_ITEM(dq->getWidget("fileLowPriorityItem")))
884                 priority = QueueItem::LOW;
885             else if (item == GTK_MENU_ITEM(dq->getWidget("fileHighPriorityItem")))
886                 priority = QueueItem::HIGH;
887             else if (item == GTK_MENU_ITEM(dq->getWidget("fileHighestPriorityItem")))
888                 priority = QueueItem::HIGHEST;
889             else
890                 priority = QueueItem::NORMAL;
891 
892             func = new F2(dq, &DownloadQueue::setPriority_client, target, priority);
893             WulforManager::get()->dispatchClientFunc(func);
894         }
895         gtk_tree_path_free(path);
896     }
897     g_list_free(list);
898 }
899 
onFileGetListClicked_gui(GtkMenuItem * item,gpointer data)900 void DownloadQueue::onFileGetListClicked_gui(GtkMenuItem *item, gpointer data)
901 {
902     DownloadQueue *dq = (DownloadQueue *)data;
903     typedef Func2<DownloadQueue, string, string> F2;
904     F2 *func;
905     string nick, target;
906     GtkTreePath *path;
907     GtkTreeIter iter;
908     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
909 
910     for (GList *i = list; i; i = i->next)
911     {
912         path = (GtkTreePath *)i->data;
913         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
914         {
915             target = dq->fileView.getString(&iter, "Target");
916             nick = WulforUtil::getTextFromMenu(item);
917 
918             func = new F2(dq, &DownloadQueue::addList_client, target, nick);
919             WulforManager::get()->dispatchClientFunc(func);
920         }
921         gtk_tree_path_free(path);
922     }
923     g_list_free(list);
924 }
925 
onFileSendPMClicked_gui(GtkMenuItem * item,gpointer data)926 void DownloadQueue::onFileSendPMClicked_gui(GtkMenuItem *item, gpointer data)
927 {
928     DownloadQueue *dq = (DownloadQueue *)data;
929     typedef Func2<DownloadQueue, string, string> F2;
930     F2 *func;
931     string nick, target;
932     GtkTreePath *path;
933     GtkTreeIter iter;
934     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
935 
936     for (GList *i = list; i; i = i->next)
937     {
938         path = (GtkTreePath *)i->data;
939         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
940         {
941             target = dq->fileView.getString(&iter, "Target");
942             nick = WulforUtil::getTextFromMenu(item);
943 
944             func = new F2(dq, &DownloadQueue::sendMessage_client, target, nick);
945             WulforManager::get()->dispatchClientFunc(func);
946         }
947         gtk_tree_path_free(path);
948     }
949     g_list_free(list);
950 }
951 
onFileReAddSourceClicked_gui(GtkMenuItem * item,gpointer data)952 void DownloadQueue::onFileReAddSourceClicked_gui(GtkMenuItem *item, gpointer data)
953 {
954     DownloadQueue *dq = (DownloadQueue *)data;
955     typedef Func2<DownloadQueue, string, string> F2;
956     F2 *func;
957     string nick, target;
958     GtkTreePath *path;
959     GtkTreeIter iter;
960     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
961 
962     for (GList *i = list; i; i = i->next)
963     {
964         path = (GtkTreePath *)i->data;
965         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
966         {
967             target = dq->fileView.getString(&iter, "Target");
968             nick = WulforUtil::getTextFromMenu(item);
969 
970             func = new F2(dq, &DownloadQueue::reAddSource_client, target, nick);
971             WulforManager::get()->dispatchClientFunc(func);
972         }
973         gtk_tree_path_free(path);
974     }
975     g_list_free(list);
976 }
977 
onFileRemoveSourceClicked_gui(GtkMenuItem * item,gpointer data)978 void DownloadQueue::onFileRemoveSourceClicked_gui(GtkMenuItem *item, gpointer data)
979 {
980     DownloadQueue *dq = (DownloadQueue *)data;
981     typedef Func2<DownloadQueue, string, string> F2;
982     F2 *func;
983     string nick, target;
984     GtkTreePath *path;
985     GtkTreeIter iter;
986     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
987 
988     for (GList *i = list; i; i = i->next)
989     {
990         path = (GtkTreePath *)i->data;
991         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
992         {
993             target = dq->fileView.getString(&iter, "Target");
994             nick = WulforUtil::getTextFromMenu(item);
995 
996             func = new F2(dq, &DownloadQueue::removeSource_client, target, nick);
997             WulforManager::get()->dispatchClientFunc(func);
998         }
999         gtk_tree_path_free(path);
1000     }
1001     g_list_free(list);
1002 }
1003 
onFileRemoveUserFromQueueClicked_gui(GtkMenuItem * item,gpointer data)1004 void DownloadQueue::onFileRemoveUserFromQueueClicked_gui(GtkMenuItem *item, gpointer data)
1005 {
1006     DownloadQueue *dq = (DownloadQueue *)data;
1007     typedef Func2<DownloadQueue, string, string> F2;
1008     F2 *func;
1009     string nick, target;
1010     GtkTreePath *path;
1011     GtkTreeIter iter;
1012     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
1013 
1014     for (GList *i = list; i; i = i->next)
1015     {
1016         path = (GtkTreePath *)i->data;
1017         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
1018         {
1019             target = dq->fileView.getString(&iter, "Target");
1020             nick = WulforUtil::getTextFromMenu(item);
1021 
1022             func = new F2(dq, &DownloadQueue::removeSources_client, target, nick);
1023             WulforManager::get()->dispatchClientFunc(func);
1024         }
1025         gtk_tree_path_free(path);
1026     }
1027     g_list_free(list);
1028 }
1029 
onFileRemoveClicked_gui(GtkMenuItem * menuitem,gpointer data)1030 void DownloadQueue::onFileRemoveClicked_gui(GtkMenuItem *menuitem, gpointer data)
1031 {
1032     DownloadQueue *dq = (DownloadQueue *)data;
1033     typedef Func1<DownloadQueue, string> F1;
1034     F1 *func;
1035     string target;
1036     GtkTreePath *path;
1037     GtkTreeIter iter;
1038     GList *list = gtk_tree_selection_get_selected_rows(dq->fileSelection, NULL);
1039 
1040     for (GList *i = list; i; i = i->next)
1041     {
1042         path = (GtkTreePath *)i->data;
1043         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dq->fileStore), &iter, path))
1044         {
1045             target = dq->fileView.getString(&iter, "Target");
1046             func = new F1(dq, &DownloadQueue::remove_client, target);
1047             WulforManager::get()->dispatchClientFunc(func);
1048         }
1049         gtk_tree_path_free(path);
1050     }
1051     g_list_free(list);
1052 }
1053 
buildList_client()1054 void DownloadQueue::buildList_client()
1055 {
1056     StringMap params;
1057     const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
1058 
1059     for (auto it = ll.begin(); it != ll.end(); ++it)
1060     {
1061         params["Size Sort"] = Util::toString(it->second->getSize());
1062         params["Path"] = Util::getFilePath(*it->first);
1063 
1064         addFile_gui(params, TRUE);
1065     }
1066 
1067     QueueManager::getInstance()->unlockQueue();
1068 }
1069 
move_client(string source,string target)1070 void DownloadQueue::move_client(string source, string target)
1071 {
1072     if (!source.empty() && !target.empty())
1073         QueueManager::getInstance()->move(source, target);
1074 }
1075 
moveDir_client(string source,string target)1076 void DownloadQueue::moveDir_client(string source, string target)
1077 {
1078     if (!source.empty() && !target.empty() && target[target.length() - 1] == PATH_SEPARATOR)
1079     {
1080         // Can't modify QueueItem::StringMap in the loop, so we have to queue them.
1081         vector<string> targets;
1082         string *file;
1083         const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
1084 
1085         for (auto it = ll.begin(); it != ll.end(); ++it)
1086         {
1087             file = it->first;
1088             if (file->length() >= source.length() && file->substr(0, source.length()) == source)
1089                 targets.push_back(*file);
1090         }
1091         QueueManager::getInstance()->unlockQueue();
1092 
1093         for (auto it = targets.begin(); it != targets.end(); ++it)
1094             QueueManager::getInstance()->move(*it, target + it->substr(source.length()));
1095     }
1096 }
1097 
setPriority_client(string target,QueueItem::Priority p)1098 void DownloadQueue::setPriority_client(string target, QueueItem::Priority p)
1099 {
1100     if (!target.empty())
1101         QueueManager::getInstance()->setPriority(target, p);
1102 }
1103 
setPriorityDir_client(string path,QueueItem::Priority p)1104 void DownloadQueue::setPriorityDir_client(string path, QueueItem::Priority p)
1105 {
1106     if (!path.empty() && path[path.length() - 1] == PATH_SEPARATOR)
1107     {
1108         string *file;
1109         const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
1110 
1111         for (auto it = ll.begin(); it != ll.end(); ++it)
1112         {
1113             file = it->first;
1114             if (file->length() >= path.length() && file->substr(0, path.length()) == path)
1115                 QueueManager::getInstance()->setPriority(*file, p);
1116         }
1117         QueueManager::getInstance()->unlockQueue();
1118     }
1119 }
1120 
addList_client(string target,string nick)1121 void DownloadQueue::addList_client(string target, string nick)
1122 {
1123     try
1124     {
1125         if (!target.empty() && !nick.empty() && sources.find(target) != sources.end())
1126         {
1127             SourceIter it = sources[target].find(nick);
1128             if (it != sources[target].end())
1129             {
1130                 UserPtr user = ClientManager::getInstance()->findUser(CID(it->second));
1131                 if (user)
1132                     QueueManager::getInstance()->addList(HintedUser(user, ""), QueueItem::FLAG_CLIENT_VIEW);
1133             }
1134         }
1135     }
1136     catch (const Exception &e)
1137     {
1138         typedef Func2<DownloadQueue, string, string> F2;
1139         F2 *func = new F2(this, &DownloadQueue::setStatus_gui, e.getError(), "statusMain");
1140         WulforManager::get()->dispatchGuiFunc(func);
1141     }
1142 }
1143 
sendMessage_client(string target,string nick)1144 void DownloadQueue::sendMessage_client(string target, string nick)
1145 {
1146     if (!target.empty() && !nick.empty() && sources.find(target) != sources.end())
1147     {
1148         SourceIter it = sources[target].find(nick);
1149         if (it != sources[target].end())
1150         {
1151             typedef Func1<DownloadQueue, string> F1;
1152             F1 *func = new F1(this, &DownloadQueue::sendMessage_gui, it->second);
1153             WulforManager::get()->dispatchGuiFunc(func);
1154         }
1155     }
1156 }
1157 
reAddSource_client(string target,string nick)1158 void DownloadQueue::reAddSource_client(string target, string nick)
1159 {
1160     try
1161     {
1162         if (!target.empty() && !nick.empty() && badSources.find(target) != sources.end())
1163         {
1164             SourceIter it = badSources[target].find(nick);
1165             if (it != badSources[target].end())
1166             {
1167                 UserPtr user = ClientManager::getInstance()->findUser(CID(it->second));
1168                 if (user)
1169                     QueueManager::getInstance()->readd(target, HintedUser(user, ""));
1170             }
1171         }
1172     }
1173     catch (const Exception &e)
1174     {
1175         typedef Func2<DownloadQueue, string, string> F2;
1176         F2 *func = new F2(this, &DownloadQueue::setStatus_gui, e.getError(), "statusMain");
1177         WulforManager::get()->dispatchGuiFunc(func);
1178     }
1179 }
1180 
removeSource_client(string target,string nick)1181 void DownloadQueue::removeSource_client(string target, string nick)
1182 {
1183     if (!target.empty() && !nick.empty() && sources.find(target) != sources.end())
1184     {
1185         SourceIter it = sources[target].find(nick);
1186         if (it != sources[target].end())
1187         {
1188             UserPtr user = ClientManager::getInstance()->findUser(CID(it->second));
1189             if (user)
1190                 QueueManager::getInstance()->removeSource(target, user, QueueItem::Source::FLAG_REMOVED);
1191         }
1192     }
1193 }
1194 
removeSources_client(string target,string nick)1195 void DownloadQueue::removeSources_client(string target, string nick)
1196 {
1197     if (!target.empty() && !nick.empty() && sources.find(target) != sources.end())
1198     {
1199         SourceIter it = sources[target].find(nick);
1200         if (it != sources[target].end())
1201         {
1202             UserPtr user = ClientManager::getInstance()->findUser(CID(it->second));
1203             if (user)
1204                 QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
1205         }
1206     }
1207 }
1208 
remove_client(string target)1209 void DownloadQueue::remove_client(string target)
1210 {
1211     if (!target.empty())
1212         QueueManager::getInstance()->remove(target);
1213 }
1214 
removeDir_client(string path)1215 void DownloadQueue::removeDir_client(string path)
1216 {
1217     if (!path.empty())
1218     {
1219         string *file;
1220         vector<string> targets;
1221         const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
1222 
1223         for (auto it = ll.begin(); it != ll.end(); ++it)
1224         {
1225             file = it->first;
1226             if (file->length() >= path.length() && file->substr(0, path.length()) == path)
1227                 targets.push_back(*file);
1228         }
1229         QueueManager::getInstance()->unlockQueue();
1230 
1231         for (auto it = targets.begin(); it != targets.end(); ++it)
1232             QueueManager::getInstance()->remove(*it);
1233     }
1234 }
1235 
updateFileView_client(string path)1236 void DownloadQueue::updateFileView_client(string path)
1237 {
1238     if (!path.empty())
1239     {
1240         vector<StringMap> files;
1241         const QueueItem::StringMap &ll = QueueManager::getInstance()->lockQueue();
1242 
1243         for (auto it = ll.begin(); it != ll.end(); ++it)
1244         {
1245             if (it->first->length() >= path.length() && it->first->substr(0, it->first->rfind('/') + 1) == path)
1246             {
1247                 StringMap params;
1248                 getQueueParams_client(it->second, params);
1249                 files.push_back(params);
1250             }
1251         }
1252         QueueManager::getInstance()->unlockQueue();
1253 
1254         // Updating gui is smoother if we do it in large chunks.
1255         typedef Func2<DownloadQueue, vector<StringMap>, bool> F2;
1256         F2 *func = new F2(this, &DownloadQueue::addFiles_gui, files, TRUE);
1257         WulforManager::get()->dispatchGuiFunc(func);
1258     }
1259 }
1260 
getQueueParams_client(QueueItem * item,StringMap & params)1261 void DownloadQueue::getQueueParams_client(QueueItem *item, StringMap &params)
1262 {
1263     string nick;
1264     map<string, string> source;
1265     int online = 0;
1266 
1267     params["Filename"] = item->getTargetFileName();
1268     params["Path"] = Util::getFilePath(item->getTarget());
1269     params["Target"] = item->getTarget();
1270 
1271     params["Users"] = "";
1272     for (QueueItem::SourceConstIter it = item->getSources().begin(); it != item->getSources().end(); ++it)
1273     {
1274         if (it->getUser().user->isOnline())
1275             ++online;
1276 
1277         if(!params["Users"].empty())
1278             params["Users"] += ", ";
1279 
1280         nick = WulforUtil::getNicks(it->getUser());
1281         source[nick] = it->getUser().user->getCID().toBase32();
1282         params["Users"] += nick;
1283     }
1284     if (params["Users"].empty())
1285         params["Users"] = _("No users");
1286     sources[item->getTarget()] = source;
1287 
1288     // Status
1289     if (item->isWaiting())
1290         params["Status"] = Util::toString(online) + _(" of ") + Util::toString(item->getSources().size()) + _(" user(s) online");
1291     else
1292         params["Status"] = _("Running...");
1293 
1294     // Size
1295     params["Size Sort"] = Util::toString(item->getSize());
1296     if (item->getSize() < 0)
1297     {
1298         params["Size"] = _("Unknown");
1299         params["Exact Size"] = _("Unknown");
1300     }
1301     else
1302     {
1303         params["Size"] = Util::formatBytes(item->getSize());
1304         params["Exact Size"] = Util::formatExactSize(item->getSize());
1305     }
1306 
1307     // Downloaded
1308     params["Downloaded Sort"] = Util::toString(item->getDownloadedBytes());
1309     if (item->getSize() > 0)
1310     {
1311         double percent = (double)item->getDownloadedBytes() * 100.0 / (double)item->getSize();
1312         params["Downloaded"] = Util::formatBytes(item->getDownloadedBytes()) + " (" + Util::toString(percent) + "%)";
1313     }
1314     else
1315     {
1316         params["Downloaded"] = _("0 B (0.00%)");
1317     }
1318 
1319     // Priority
1320     switch (item->getPriority())
1321     {
1322         case QueueItem::PAUSED:
1323             params["Priority"] = _("Paused");
1324             break;
1325         case QueueItem::LOWEST:
1326             params["Priority"] = _("Lowest");
1327             break;
1328         case QueueItem::LOW:
1329             params["Priority"] = _("Low");
1330             break;
1331         case QueueItem::HIGH:
1332             params["Priority"] = _("High");
1333             break;
1334         case QueueItem::HIGHEST:
1335             params["Priority"] = _("Highest");
1336             break;
1337         default:
1338             params["Priority"] = _("Normal");
1339     }
1340 
1341     // Error
1342     source.clear();
1343     params["Errors"] = "";
1344     for (QueueItem::SourceConstIter it = item->getBadSources().begin(); it != item->getBadSources().end(); ++it)
1345     {
1346         nick = WulforUtil::getNicks(it->getUser());
1347         source[nick] = it->getUser().user->getCID().toBase32();
1348 
1349         if (!it->isSet(QueueItem::Source::FLAG_REMOVED))
1350         {
1351             if(params["Errors"].empty())
1352                 params["Errors"] += ", ";
1353             params["Errors"] += nick + " (";
1354 
1355             if (it->isSet(QueueItem::Source::FLAG_FILE_NOT_AVAILABLE))
1356                 params["Errors"] += _("File not available");
1357             else if (it->isSet(QueueItem::Source::FLAG_PASSIVE))
1358                 params["Errors"] += _("Passive user");
1359             else if (it->isSet(QueueItem::Source::FLAG_CRC_FAILED))
1360                 params["Errors"] += _("CRC32 inconsistency (SFV-Check)");
1361             else if (it->isSet(QueueItem::Source::FLAG_BAD_TREE))
1362                 params["Errors"] += _("Full tree does not match TTH root");
1363             else if (it->isSet(QueueItem::Source::FLAG_SLOW_SOURCE))
1364                 params["Errors"] += _("Source too slow");
1365             else if (it->isSet(QueueItem::Source::FLAG_NO_TTHF))
1366                 params["Errors"] += _("Remote client does not fully support TTH - cannot download");
1367 
1368             params["Errors"] += ")";
1369         }
1370     }
1371     if (params["Errors"].empty())
1372         params["Errors"] = _("No errors");
1373     badSources[item->getTarget()] = source;
1374 
1375     // Added
1376     params["Added"] = Util::formatTime("%Y-%m-%d %H:%M", item->getAdded());
1377 
1378     // TTH
1379     params["TTH"] = item->getTTH().toBase32();
1380 }
1381 
on(QueueManagerListener::Added,QueueItem * item)1382 void DownloadQueue::on(QueueManagerListener::Added, QueueItem *item) noexcept
1383 {
1384     StringMap params;
1385     getQueueParams_client(item, params);
1386 
1387     typedef Func2<DownloadQueue, StringMap, bool> F2;
1388     F2 *func = new F2(this, &DownloadQueue::addFile_gui, params, TRUE);
1389     WulforManager::get()->dispatchGuiFunc(func);
1390 }
1391 
on(QueueManagerListener::Moved,QueueItem * item,const string & oldTarget)1392 void DownloadQueue::on(QueueManagerListener::Moved, QueueItem *item, const string &oldTarget) noexcept
1393 {
1394     // Remove the old file
1395     typedef Func2<DownloadQueue, string, int64_t> F2a;
1396     F2a *func1 = new F2a(this, &DownloadQueue::removeFile_gui, oldTarget, item->getSize());
1397     WulforManager::get()->dispatchGuiFunc(func1);
1398 
1399     // Add the new file
1400     StringMap params;
1401     getQueueParams_client(item, params);
1402 
1403     typedef Func2<DownloadQueue, StringMap, bool> F2b;
1404     F2b *func2 = new F2b(this, &DownloadQueue::addFile_gui, params, TRUE);
1405     WulforManager::get()->dispatchGuiFunc(func2);
1406 }
1407 
on(QueueManagerListener::Removed,QueueItem * item)1408 void DownloadQueue::on(QueueManagerListener::Removed, QueueItem *item) noexcept
1409 {
1410     typedef Func2<DownloadQueue, string, int64_t> F2;
1411     F2 *func = new F2(this, &DownloadQueue::removeFile_gui, item->getTarget(), item->getSize());
1412     WulforManager::get()->dispatchGuiFunc(func);
1413 }
1414 
on(QueueManagerListener::SourcesUpdated,QueueItem * item)1415 void DownloadQueue::on(QueueManagerListener::SourcesUpdated, QueueItem *item) noexcept
1416 {
1417     StringMap params;
1418     getQueueParams_client(item, params);
1419 
1420     typedef Func1<DownloadQueue, StringMap> F1;
1421     F1 *func = new F1(this, &DownloadQueue::updateFile_gui, params);
1422     WulforManager::get()->dispatchGuiFunc(func);
1423 }
1424 
on(QueueManagerListener::StatusUpdated,QueueItem * item)1425 void DownloadQueue::on(QueueManagerListener::StatusUpdated, QueueItem *item) noexcept
1426 {
1427     StringMap params;
1428     getQueueParams_client(item, params);
1429 
1430     typedef Func1<DownloadQueue, StringMap> F1;
1431     F1 *func = new F1(this, &DownloadQueue::updateFile_gui, params);
1432     WulforManager::get()->dispatchGuiFunc(func);
1433 }
1434