1 /*
2  * Copyright © 2004-2008 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 "finishedtransfers.hh"
23 #include <dcpp/Text.h>
24 #include <dcpp/ClientManager.h>
25 #include "wulformanager.hh"
26 #include "WulforUtil.hh"
27 
28 using namespace std;
29 using namespace dcpp;
30 
createFinishedDownloads()31 FinishedTransfers* FinishedTransfers::createFinishedDownloads()
32 {
33 	return new FinishedTransfers(Entry::FINISHED_DOWNLOADS, _("Finished Downloads"), false);
34 }
35 
createFinishedUploads()36 FinishedTransfers* FinishedTransfers::createFinishedUploads()
37 {
38 	return new FinishedTransfers(Entry::FINISHED_UPLOADS, _("Finished Uploads"), true);
39 }
40 
FinishedTransfers(const EntryType type,const string & title,bool isUpload)41 FinishedTransfers::FinishedTransfers(const EntryType type, const string &title, bool isUpload):
42 	BookEntry(type, title, "finishedtransfers.glade"),
43 	isUpload(isUpload),
44 	totalFiles(0),
45 	totalBytes(0),
46 	totalTime(0)
47 {
48 	// Initialize transfer treeview
49 	fileView.setView(GTK_TREE_VIEW(getWidget("fileView")), true, "finished");
50 	fileView.insertColumn(N_("Time"), G_TYPE_STRING, TreeView::STRING, 150);
51 	fileView.insertColumn(N_("Filename"), G_TYPE_STRING, TreeView::STRING, 130);
52 	fileView.insertColumn(N_("Path"), G_TYPE_STRING, TreeView::STRING, 200);
53 	fileView.insertColumn(N_("Users"), G_TYPE_STRING, TreeView::STRING, 110);
54 	fileView.insertColumn(N_("Size"), G_TYPE_INT64, TreeView::SIZE, 100);
55 	fileView.insertColumn(N_("Average Speed"), G_TYPE_INT64, TreeView::SPEED, 135);
56 	fileView.insertColumn(N_("CRC Checked"), G_TYPE_STRING, TreeView::STRING, 125);
57 	fileView.insertHiddenColumn("Target", G_TYPE_STRING);
58 	fileView.insertHiddenColumn("Elapsed Time", G_TYPE_INT64);
59 	fileView.finalize();
60 	fileStore = gtk_list_store_newv(fileView.getColCount(), fileView.getGTypes());
61 	gtk_tree_view_set_model(fileView.get(), GTK_TREE_MODEL(fileStore));
62 	g_object_unref(fileStore);
63 	fileSelection = gtk_tree_view_get_selection(fileView.get());
64 	gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(fileView.get(), fileView.col("Time")), TRUE);
65 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileStore), fileView.col("Time"), GTK_SORT_ASCENDING);
66 	gtk_tree_view_set_fixed_height_mode(fileView.get(), TRUE);
67 	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(fileView.get()), GTK_SELECTION_MULTIPLE);
68 
69 	// Connect the signals to their callback functions.
70 	g_signal_connect(getWidget("openItem"), "activate", G_CALLBACK(onOpen_gui), (gpointer)this);
71 	g_signal_connect(getWidget("openFolderItem"), "activate", G_CALLBACK(onOpenFolder_gui), (gpointer)this);
72 	g_signal_connect(getWidget("removeItem"), "activate", G_CALLBACK(onRemoveItems_gui), (gpointer)this);
73 	g_signal_connect(getWidget("removeAllItem"), "activate", G_CALLBACK(onRemoveAll_gui), (gpointer)this);
74 	g_signal_connect(fileView.get(), "button-press-event", G_CALLBACK(onButtonPressed_gui), (gpointer)this);
75 	g_signal_connect(fileView.get(), "button-release-event", G_CALLBACK(onButtonReleased_gui), (gpointer)this);
76 	g_signal_connect(fileView.get(), "key-release-event", G_CALLBACK(onKeyReleased_gui), (gpointer)this);
77 
78 }
79 
~FinishedTransfers()80 FinishedTransfers::~FinishedTransfers()
81 {
82 	FinishedManager::getInstance()->removeListener(this);
83 }
84 
show()85 void FinishedTransfers::show()
86 {
87 	initializeList_client();
88 	//Func0<FinishedTransfers> *func = new Func0<FinishedTransfers>(this, &FinishedTransfers::initializeList_client);
89 	//WulforManager::get()->dispatchClientFunc(func);
90 	FinishedManager::getInstance()->addListener(this);
91 }
92 
findFile_gui(GtkTreeIter * iter,const string & item)93 bool FinishedTransfers::findFile_gui(GtkTreeIter* iter, const string& item)
94 {
95 	bool valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileStore), iter);
96 
97 	while (valid)
98 	{
99 		if (fileView.getString(iter, "Target") == item)
100 			return TRUE;
101 
102 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileStore), iter);
103 	}
104 
105 	return FALSE;
106 }
107 
addFile_gui(StringMap params,bool update)108 void FinishedTransfers::addFile_gui(StringMap params, bool update)
109 {
110 	GtkTreeIter iter;
111 	int64_t size = Util::toInt64(params["Size"]);
112 	int64_t speed = Util::toInt64(params["Average Speed"]);
113 	int64_t time = Util::toInt64(params["Elapsed Time"]);
114 
115 	if (!isUpload || !findFile_gui(&iter, params["Target"]))
116 	{
117 		gtk_list_store_append(fileStore, &iter);
118 		totalFiles++;
119 	}
120 
121 	if (!isUpload)
122 	{
123 		totalTime += time;
124 		totalBytes += size;
125 	}
126 	else
127 	{
128 		totalTime += (time - fileView.getValue<int64_t>(&iter, "Elapsed Time"));
129 		totalBytes += (size - fileView.getValue<int64_t>(&iter, "Size"));
130 
131 	}
132 
133 	gtk_list_store_set(fileStore, &iter,
134 		fileView.col("Time"), params["Time"].c_str(),
135 		fileView.col("Filename"), params["Filename"].c_str(),
136 		fileView.col("Path"), params["Path"].c_str(),
137 		fileView.col("Users"), params["Users"].c_str(),
138 		fileView.col("Size"), size,
139 		fileView.col("Average Speed"), speed,
140 		fileView.col("CRC Checked"), params["CRC Checked"].c_str(),
141 		fileView.col("Target"), params["Target"].c_str(),
142 		fileView.col("Elapsed Time"), time,
143 		-1);
144 
145 	if (update)
146 	{
147 		updateStatus_gui();
148 
149 		if ((!isUpload && BOOLSETTING(BOLD_FINISHED_DOWNLOADS)) ||
150 		     (isUpload && BOOLSETTING(BOLD_FINISHED_UPLOADS)))
151 		{
152 			setBold_gui();
153 		}
154 	}
155 }
156 
updateStatus_gui()157 void FinishedTransfers::updateStatus_gui()
158 {
159 	// TRANSLATORS: Displays the total number of files in the status bar.
160 	string status = P_("File: %1%", "Files: %1%", % totalFiles, totalFiles);
161 
162 	string size = Util::formatBytes(totalBytes);
163 	string speed = Util::formatBytes((totalTime > 0) ? totalBytes * ((int64_t)1000) / totalTime : 0) + "/s";
164 
165 	gtk_statusbar_push(GTK_STATUSBAR(getWidget("totalItems")), 0, status.c_str());
166 	gtk_statusbar_push(GTK_STATUSBAR(getWidget("totalSize")), 0, size.c_str());
167 	gtk_statusbar_push(GTK_STATUSBAR(getWidget("averageSpeed")), 0, speed.c_str());
168 }
169 
onButtonPressed_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)170 gboolean FinishedTransfers::onButtonPressed_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
171 {
172 	FinishedTransfers *ft = (FinishedTransfers *)data;
173 
174 	if (event->button == 3)
175 	{
176 		GtkTreePath *path;
177 		if (gtk_tree_view_get_path_at_pos(ft->fileView.get(), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
178 		{
179 			bool selected = gtk_tree_selection_path_is_selected(ft->fileSelection, path);
180 			gtk_tree_path_free(path);
181 
182 			if (selected)
183 				return TRUE;
184 		}
185 	}
186 
187 
188 	return FALSE;
189 }
190 
onButtonReleased_gui(GtkWidget * widget,GdkEventButton * event,gpointer data)191 gboolean FinishedTransfers::onButtonReleased_gui(GtkWidget *widget, GdkEventButton *event, gpointer data)
192 {
193 	FinishedTransfers *ft = (FinishedTransfers *)data;
194 
195 	int count = gtk_tree_selection_count_selected_rows(ft->fileSelection);
196 
197 	if (event->button == 3 && count > 0)
198 	{
199 		gtk_menu_popup(GTK_MENU(ft->getWidget("menu")), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
200 		gtk_widget_show_all(ft->getWidget("menu"));
201 	}
202 
203 	return FALSE;
204 }
205 
onKeyReleased_gui(GtkWidget * widget,GdkEventKey * event,gpointer data)206 gboolean FinishedTransfers::onKeyReleased_gui(GtkWidget *widget, GdkEventKey *event, gpointer data)
207 {
208 	FinishedTransfers *ft = (FinishedTransfers *)data;
209 
210 	int count = gtk_tree_selection_count_selected_rows(ft->fileSelection);
211 
212 	if (count > 0)
213 	{
214 		if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
215 		{
216 			onOpen_gui(NULL, data);
217 		}
218 		else if (event->keyval == GDK_Delete || event->keyval == GDK_BackSpace)
219 		{
220 			onRemoveItems_gui(NULL, data);
221 		}
222 		else if (event->keyval == GDK_Menu || (event->keyval == GDK_F10 && event->state & GDK_SHIFT_MASK))
223 		{
224 			gtk_menu_popup(GTK_MENU(ft->getWidget("menu")), NULL, NULL, NULL, NULL, 1, event->time);
225 			gtk_widget_show_all(ft->getWidget("menu"));
226 		}
227 	}
228 
229 	return FALSE;
230 }
231 
onOpen_gui(GtkMenuItem * item,gpointer data)232 void FinishedTransfers::onOpen_gui(GtkMenuItem *item, gpointer data)
233 {
234 	FinishedTransfers *ft = (FinishedTransfers *)data;
235 	int count = gtk_tree_selection_count_selected_rows(ft->fileSelection);
236 
237 	if (count <= 0)
238 		return;
239 
240 	GtkTreeIter iter;
241 	GtkTreePath *path;
242 	GList *list = gtk_tree_selection_get_selected_rows(ft->fileSelection, NULL);
243 	for (GList *i = list; i; i = i->next)
244 	{
245 		path = (GtkTreePath *)i->data;
246 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ft->fileStore), &iter, path))
247 		{
248 			string target = ft->fileView.getString(&iter, "Target");
249 			if (!target.empty())
250 				WulforUtil::openURI(target);
251 		}
252 		gtk_tree_path_free(path);
253 	}
254 	g_list_free(list);
255 
256 }
257 
onOpenFolder_gui(GtkMenuItem * item,gpointer data)258 void FinishedTransfers::onOpenFolder_gui(GtkMenuItem *item, gpointer data)
259 {
260 	FinishedTransfers *ft = (FinishedTransfers *)data;
261 	int count = gtk_tree_selection_count_selected_rows(ft->fileSelection);
262 
263 	if (count <= 0)
264 		return;
265 
266 	GtkTreeIter iter;
267 	GtkTreePath *path;
268 	GList *list = gtk_tree_selection_get_selected_rows(ft->fileSelection, NULL);
269 
270 	for (GList *i = list; i; i = i->next)
271 	{
272 		path = (GtkTreePath *)i->data;
273 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ft->fileStore), &iter, path))
274 		{
275 			string target = ft->fileView.getString(&iter, "Path");
276 			if (!target.empty())
277 				WulforUtil::openURI(target);
278 		}
279 		gtk_tree_path_free(path);
280 	}
281 	g_list_free(list);
282 }
283 
onRemoveItems_gui(GtkMenuItem * item,gpointer data)284 void FinishedTransfers::onRemoveItems_gui(GtkMenuItem *item, gpointer data)
285 {
286 	FinishedTransfers *ft = (FinishedTransfers *)data;
287 
288 	string target;
289 	GtkTreeIter iter;
290 	GtkTreePath *path;
291 	GList *list = gtk_tree_selection_get_selected_rows(ft->fileSelection, NULL);
292 
293 	typedef Func1<FinishedTransfers, string> F1;
294 	F1 *func;
295 
296 	for (GList *i = list; i; i = i->next)
297 	{
298 		path = (GtkTreePath *)i->data;
299 		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ft->fileStore), &iter, path))
300 		{
301 			target = ft->fileView.getString(&iter, "Target");
302 			func = new F1(ft, &FinishedTransfers::removeFile_client, target);
303 			WulforManager::get()->dispatchClientFunc(func);
304 		}
305 		gtk_tree_path_free(path);
306 	}
307 	g_list_free(list);
308 
309 	ft->updateStatus_gui();		// Why? model won't change until after the _client call.
310 }
311 
removeFile_gui(string target)312 void FinishedTransfers::removeFile_gui(string target)
313 {
314 	GtkTreeIter iter;
315 	GtkTreeModel *m = GTK_TREE_MODEL(fileStore);
316 	bool valid = gtk_tree_model_get_iter_first(m, &iter);
317 
318 	while (valid)
319 	{
320 		if (target == fileView.getString(&iter, "Target"))
321 		{
322 			totalBytes -= fileView.getValue<gint64>(&iter, "Size");
323 			totalTime -= fileView.getValue<gint64>(&iter, "Elapsed Time");
324 			gtk_list_store_remove(fileStore, &iter);
325 			totalFiles--;
326 			updateStatus_gui();
327 			return;
328 		}
329 		valid = gtk_tree_model_iter_next(m, &iter);
330 	}
331 }
332 
onRemoveAll_gui(GtkMenuItem * item,gpointer data)333 void FinishedTransfers::onRemoveAll_gui(GtkMenuItem *item, gpointer data)
334 {
335 	FinishedTransfers *ft = (FinishedTransfers *)data;
336 
337 	gtk_list_store_clear(ft->fileStore);
338 	ft->totalBytes = 0;
339 	ft->totalTime = 0;
340 	ft->totalFiles = 0;
341 	ft->updateStatus_gui();
342 
343 	typedef Func0<FinishedTransfers> F0;
344 	F0 *func = new F0(ft, &FinishedTransfers::removeAll_client);
345 	WulforManager::get()->dispatchClientFunc(func);
346 }
347 
initializeList_client()348 void FinishedTransfers::initializeList_client()
349 {
350 	StringMap params;
351 	typedef Func2<FinishedTransfers, StringMap, bool> F2;
352 	//F2 *func;
353 	FinishedManager::getInstance()->lockLists();
354 	const FinishedManager::MapByFile &list = FinishedManager::getInstance()->getMapByFile(isUpload);
355 
356 	for (FinishedManager::MapByFile::const_iterator it = list.begin(); it != list.end(); ++it)
357 	{
358 		params.clear();
359 		getFinishedParams_client(it->second, it->first, params);
360 		addFile_gui(params, FALSE);
361 		//func = new F2(this, &FinishedTransfers::addItem_gui, params, FALSE);
362 		//WulforManager::get()->dispatchGuiFunc(func);
363 	}
364 
365 	FinishedManager::getInstance()->unLockLists();
366 
367 	updateStatus_gui();
368 	//WulforManager::get()->dispatchGuiFunc(new Func0<FinishedTransfers>(this, &FinishedTransfers::updateStatus_gui));
369 }
370 
getFinishedParams_client(const FinishedFileItemPtr & item,const string & file,StringMap & params)371 void FinishedTransfers::getFinishedParams_client(const FinishedFileItemPtr& item, const string& file, StringMap &params)
372 {
373 	std::string nicks;
374 	params["Filename"] = Util::getFileName(file);
375 	params["Time"] = Util::formatTime("%Y-%m-%d %H:%M:%S", item->getTime());
376 	params["Path"] = Util::getFilePath(file);
377 	for (UserList::const_iterator it = item->getUsers().begin(); it != item->getUsers().end(); ++it)
378 	{
379 		nicks += WulforUtil::getNicks(it->get()->getCID()) + ", ";
380 	}
381 	params["Users"] = nicks.substr(0, nicks.length() - 2);
382 	params["Size"] = Util::toString(item->getTransferred());
383 	params["Average Speed"] = Util::toString(item->getAverageSpeed());
384 	params["CRC Checked"] = item->getCrc32Checked() ? _("Yes") : _("No");
385 	params["Target"] = file;
386 	params["Elapsed Time"] = Util::toString(item->getMilliSeconds());
387 }
388 
removeFile_client(std::string target)389 void FinishedTransfers::removeFile_client(std::string target)
390 {
391 
392 	if (!target.empty())
393 		FinishedManager::getInstance()->remove(isUpload, target);
394 
395 }
396 
removeAll_client()397 void FinishedTransfers::removeAll_client()
398 {
399 	FinishedManager::getInstance()->removeAll(isUpload);
400 }
401 
on(FinishedManagerListener::AddedFile,bool upload,const string & file,const FinishedFileItemPtr & item)402 void FinishedTransfers::on(FinishedManagerListener::AddedFile, bool upload, const string& file, const FinishedFileItemPtr& item) throw()
403 {
404 	// Show partial uploads, but only full downloads
405 	if (isUpload == upload && (upload || item->isFull()))
406 	{
407 		StringMap params;
408 		getFinishedParams_client(item, file, params);
409 
410 		typedef Func2<FinishedTransfers, StringMap, bool> F2;
411 		F2* func = new F2(this, &FinishedTransfers::addFile_gui, params, TRUE);
412 		WulforManager::get()->dispatchGuiFunc(func);
413 	}
414 }
415 
on(FinishedManagerListener::UpdatedFile,bool upload,const string & file,const FinishedFileItemPtr & item)416 void FinishedTransfers::on(FinishedManagerListener::UpdatedFile, bool upload, const string& file, const FinishedFileItemPtr& item) throw()
417 {
418 	// Show partial uploads, but only full downloads
419 	if (isUpload == upload && (upload || item->isFull()))
420 	{
421 		StringMap params;
422 		getFinishedParams_client(item, file, params);
423 
424 		typedef Func2<FinishedTransfers, StringMap, bool> F2;
425 		F2 *func = new F2(this, &FinishedTransfers::addFile_gui, params, TRUE);
426 		WulforManager::get()->dispatchGuiFunc(func);
427 	}
428 }
429 
on(FinishedManagerListener::RemovedFile,bool upload,const string & item)430 void FinishedTransfers::on(FinishedManagerListener::RemovedFile, bool upload, const string& item) throw()
431 {
432 	if (isUpload == upload)
433 	{
434 		typedef Func1<FinishedTransfers, string> F1;
435 		F1 *func = new F1(this, &FinishedTransfers::removeFile_gui, item);
436 		WulforManager::get()->dispatchGuiFunc(func);
437 	}
438 }
439 
440