comparison pidgin/gtkft.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children d75099d2567e
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /**
2 * @file gtkft.c GTK+ File Transfer UI
3 * @ingroup gtkui
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25 #include "internal.h"
26 #include "gtkgaim.h"
27
28 #include "debug.h"
29 #include "notify.h"
30 #include "ft.h"
31 #include "prpl.h"
32 #include "util.h"
33
34 #include "gtkcellrendererprogress.h"
35 #include "gtkft.h"
36 #include "prefs.h"
37 #include "gtkexpander.h"
38 #include "gaimstock.h"
39 #include "gtkutils.h"
40
41 #define GAIM_GTKXFER(xfer) \
42 (GaimGtkXferUiData *)(xfer)->ui_data
43
44 struct _GaimGtkXferDialog
45 {
46 gboolean keep_open;
47 gboolean auto_clear;
48
49 gint num_transfers;
50
51 GaimXfer *selected_xfer;
52
53 GtkWidget *window;
54 GtkWidget *tree;
55 GtkListStore *model;
56
57 GtkWidget *expander;
58
59 GtkWidget *table;
60
61 GtkWidget *local_user_desc_label;
62 GtkWidget *local_user_label;
63 GtkWidget *remote_user_desc_label;
64 GtkWidget *remote_user_label;
65 GtkWidget *protocol_label;
66 GtkWidget *filename_label;
67 GtkWidget *localfile_label;
68 GtkWidget *status_label;
69 GtkWidget *speed_label;
70 GtkWidget *time_elapsed_label;
71 GtkWidget *time_remaining_label;
72
73 GtkWidget *progress;
74
75 /* Buttons */
76 GtkWidget *open_button;
77 GtkWidget *pause_button;
78 GtkWidget *resume_button;
79 GtkWidget *remove_button;
80 GtkWidget *stop_button;
81 GtkWidget *close_button;
82 };
83
84 typedef struct
85 {
86 GtkTreeIter iter;
87 time_t last_updated_time;
88 gboolean in_list;
89
90 char *name;
91
92 } GaimGtkXferUiData;
93
94 static GaimGtkXferDialog *xfer_dialog = NULL;
95
96 enum
97 {
98 COLUMN_STATUS = 0,
99 COLUMN_PROGRESS,
100 COLUMN_FILENAME,
101 COLUMN_SIZE,
102 COLUMN_REMAINING,
103 COLUMN_DATA,
104 NUM_COLUMNS
105 };
106
107
108 /**************************************************************************
109 * Utility Functions
110 **************************************************************************/
111 static void
112 get_xfer_info_strings(GaimXfer *xfer, char **kbsec, char **time_elapsed,
113 char **time_remaining)
114 {
115 GaimGtkXferUiData *data;
116 double kb_sent, kb_rem;
117 double kbps = 0.0;
118 time_t elapsed, now;
119
120 data = GAIM_GTKXFER(xfer);
121
122 if (xfer->end_time != 0)
123 now = xfer->end_time;
124 else
125 now = time(NULL);
126
127 kb_sent = gaim_xfer_get_bytes_sent(xfer) / 1024.0;
128 kb_rem = gaim_xfer_get_bytes_remaining(xfer) / 1024.0;
129 elapsed = (xfer->start_time > 0 ? now - xfer->start_time : 0);
130 kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0);
131
132 if (kbsec != NULL) {
133 *kbsec = g_strdup_printf(_("%.2f KB/s"), kbps);
134 }
135
136 if (time_elapsed != NULL)
137 {
138 int h, m, s;
139 int secs_elapsed;
140
141 if (xfer->start_time > 0)
142 {
143 secs_elapsed = now - xfer->start_time;
144
145 h = secs_elapsed / 3600;
146 m = (secs_elapsed % 3600) / 60;
147 s = secs_elapsed % 60;
148
149 *time_elapsed = g_strdup_printf("%d:%02d:%02d", h, m, s);
150 }
151 else
152 {
153 *time_elapsed = g_strdup(_("Not started"));
154 }
155 }
156
157 if (time_remaining != NULL) {
158 if (gaim_xfer_get_size(xfer) == 0) {
159 *time_remaining = g_strdup(_("Unknown"));
160 }
161 else if (gaim_xfer_is_completed(xfer)) {
162 *time_remaining = g_strdup(_("Finished"));
163 }
164 else if (gaim_xfer_is_canceled(xfer)) {
165 *time_remaining = g_strdup(_("Canceled"));
166 }
167 else if (kb_sent <= 0) {
168 *time_remaining = g_strdup(_("Waiting for transfer to begin"));
169 }
170 else {
171 int h, m, s;
172 int secs_remaining;
173
174 secs_remaining = (int)(kb_rem / kbps);
175
176 h = secs_remaining / 3600;
177 m = (secs_remaining % 3600) / 60;
178 s = secs_remaining % 60;
179
180 *time_remaining = g_strdup_printf("%d:%02d:%02d", h, m, s);
181 }
182 }
183 }
184
185 static void
186 update_title_progress(GaimGtkXferDialog *dialog)
187 {
188 gboolean valid;
189 GtkTreeIter iter;
190 int num_active_xfers = 0;
191 guint64 total_bytes_xferred = 0;
192 guint64 total_file_size = 0;
193
194 if (dialog->window == NULL)
195 return;
196
197 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter);
198
199 /* Find all active transfers */
200 while (valid) {
201 GValue val;
202 GaimXfer *xfer = NULL;
203
204 val.g_type = 0;
205 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
206 &iter, COLUMN_DATA, &val);
207
208 xfer = g_value_get_pointer(&val);
209 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_STARTED) {
210 num_active_xfers++;
211 total_bytes_xferred += gaim_xfer_get_bytes_sent(xfer);
212 total_file_size += gaim_xfer_get_size(xfer);
213 }
214
215 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->model), &iter);
216 }
217
218 /* Update the title */
219 if (num_active_xfers > 0)
220 {
221 gchar *title;
222 int total_pct = 0;
223
224 if (total_file_size > 0) {
225 total_pct = 100 * total_bytes_xferred / total_file_size;
226 }
227
228 title = g_strdup_printf(_("File Transfers - %d%% of %d files"),
229 total_pct, num_active_xfers);
230 gtk_window_set_title(GTK_WINDOW(dialog->window), title);
231 g_free(title);
232 } else {
233 gtk_window_set_title(GTK_WINDOW(dialog->window), _("File Transfers"));
234 }
235 }
236
237 static void
238 update_detailed_info(GaimGtkXferDialog *dialog, GaimXfer *xfer)
239 {
240 GaimGtkXferUiData *data;
241 char *kbsec, *time_elapsed, *time_remaining;
242 char *status, *utf8;
243
244 if (dialog == NULL || xfer == NULL)
245 return;
246
247 data = GAIM_GTKXFER(xfer);
248
249 get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining);
250
251 status = g_strdup_printf("%ld%% (%ld of %ld bytes)",
252 (unsigned long)(gaim_xfer_get_progress(xfer)*100),
253 (unsigned long)gaim_xfer_get_bytes_sent(xfer),
254 (unsigned long)gaim_xfer_get_size(xfer));
255
256 if (gaim_xfer_is_completed(xfer)) {
257
258 GdkPixbuf *pixbuf = NULL;
259
260 pixbuf = gtk_widget_render_icon(xfer_dialog->window,
261 GAIM_STOCK_FILE_DONE,
262 GTK_ICON_SIZE_MENU, NULL);
263
264 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter,
265 COLUMN_STATUS, pixbuf,
266 -1);
267
268 g_object_unref(pixbuf);
269 }
270
271 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
272 gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label),
273 _("<b>Receiving As:</b>"));
274 gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label),
275 _("<b>Receiving From:</b>"));
276 }
277 else {
278 gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label),
279 _("<b>Sending To:</b>"));
280 gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label),
281 _("<b>Sending As:</b>"));
282 }
283
284 gtk_label_set_text(GTK_LABEL(dialog->local_user_label),
285 gaim_account_get_username(xfer->account));
286 gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), xfer->who);
287 gtk_label_set_text(GTK_LABEL(dialog->protocol_label),
288 gaim_account_get_protocol_name(xfer->account));
289
290 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
291 gtk_label_set_text(GTK_LABEL(dialog->filename_label),
292 gaim_xfer_get_filename(xfer));
293 } else {
294 char *tmp;
295
296 tmp = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
297 utf8 = g_filename_to_utf8(tmp, -1, NULL, NULL, NULL);
298 g_free(tmp);
299
300 gtk_label_set_text(GTK_LABEL(dialog->filename_label), utf8);
301 g_free(utf8);
302 }
303
304 utf8 = g_filename_to_utf8((gaim_xfer_get_local_filename(xfer)), -1, NULL, NULL, NULL);
305 gtk_label_set_text(GTK_LABEL(dialog->localfile_label), utf8);
306 g_free(utf8);
307
308 gtk_label_set_text(GTK_LABEL(dialog->status_label), status);
309
310 gtk_label_set_text(GTK_LABEL(dialog->speed_label), kbsec);
311 gtk_label_set_text(GTK_LABEL(dialog->time_elapsed_label), time_elapsed);
312 gtk_label_set_text(GTK_LABEL(dialog->time_remaining_label),
313 time_remaining);
314
315 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress),
316 gaim_xfer_get_progress(xfer));
317
318 g_free(kbsec);
319 g_free(time_elapsed);
320 g_free(time_remaining);
321 g_free(status);
322 }
323
324 static void
325 update_buttons(GaimGtkXferDialog *dialog, GaimXfer *xfer)
326 {
327 if (dialog->selected_xfer == NULL) {
328 gtk_widget_set_sensitive(dialog->expander, FALSE);
329 gtk_widget_set_sensitive(dialog->open_button, FALSE);
330 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
331 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
332 gtk_widget_set_sensitive(dialog->stop_button, FALSE);
333
334 gtk_widget_show(dialog->stop_button);
335 gtk_widget_hide(dialog->remove_button);
336
337 return;
338 }
339
340 if (dialog->selected_xfer != xfer)
341 return;
342
343 if (gaim_xfer_is_completed(xfer)) {
344 gtk_widget_hide(dialog->stop_button);
345 gtk_widget_show(dialog->remove_button);
346
347 #ifdef _WIN32
348 /* If using Win32... */
349 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
350 gtk_widget_set_sensitive(dialog->open_button, TRUE);
351 } else {
352 gtk_widget_set_sensitive(dialog->open_button, FALSE);
353 }
354 #else
355 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
356 gtk_widget_set_sensitive(dialog->open_button, TRUE);
357 } else {
358 gtk_widget_set_sensitive (dialog->open_button, FALSE);
359 }
360 #endif
361 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
362 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
363
364 gtk_widget_set_sensitive(dialog->remove_button, TRUE);
365 } else if (gaim_xfer_is_canceled(xfer)) {
366 gtk_widget_hide(dialog->stop_button);
367 gtk_widget_show(dialog->remove_button);
368
369 gtk_widget_set_sensitive(dialog->open_button, FALSE);
370 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
371 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
372
373 gtk_widget_set_sensitive(dialog->remove_button, TRUE);
374 } else {
375 gtk_widget_show(dialog->stop_button);
376 gtk_widget_hide(dialog->remove_button);
377
378 gtk_widget_set_sensitive(dialog->open_button, FALSE);
379
380 /* TODO: If the transfer can pause, blah blah */
381 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
382 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
383 gtk_widget_set_sensitive(dialog->stop_button, TRUE);
384 }
385 }
386
387 static void
388 ensure_row_selected(GaimGtkXferDialog *dialog)
389 {
390 GtkTreeIter iter;
391 GtkTreeSelection *selection;
392
393 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
394
395 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
396 return;
397
398 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter))
399 gtk_tree_selection_select_iter(selection, &iter);
400 }
401
402 /**************************************************************************
403 * Callbacks
404 **************************************************************************/
405 static gint
406 delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
407 {
408 GaimGtkXferDialog *dialog;
409
410 dialog = (GaimGtkXferDialog *)d;
411
412 gaim_gtkxfer_dialog_hide(dialog);
413
414 return TRUE;
415 }
416
417 static void
418 toggle_keep_open_cb(GtkWidget *w, GaimGtkXferDialog *dialog)
419 {
420 dialog->keep_open = !dialog->keep_open;
421 gaim_prefs_set_bool("/gaim/gtk/filetransfer/keep_open",
422 dialog->keep_open);
423 }
424
425 static void
426 toggle_clear_finished_cb(GtkWidget *w, GaimGtkXferDialog *dialog)
427 {
428 dialog->auto_clear = !dialog->auto_clear;
429 gaim_prefs_set_bool("/gaim/gtk/filetransfer/clear_finished",
430 dialog->auto_clear);
431 }
432
433 static void
434 selection_changed_cb(GtkTreeSelection *selection, GaimGtkXferDialog *dialog)
435 {
436 GtkTreeIter iter;
437 GaimXfer *xfer = NULL;
438
439 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
440 GValue val;
441
442 gtk_widget_set_sensitive(dialog->expander, TRUE);
443
444 val.g_type = 0;
445 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
446 &iter, COLUMN_DATA, &val);
447
448 xfer = g_value_get_pointer(&val);
449
450 update_detailed_info(dialog, xfer);
451
452 dialog->selected_xfer = xfer;
453 }
454 else {
455 gtk_expander_set_expanded(GTK_EXPANDER(dialog->expander),
456 FALSE);
457
458 gtk_widget_set_sensitive(dialog->expander, FALSE);
459
460 dialog->selected_xfer = NULL;
461 }
462
463 update_buttons(dialog, xfer);
464 }
465
466 static void
467 open_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
468 {
469 #ifdef _WIN32
470 /* If using Win32... */
471 int code;
472 if (G_WIN32_HAVE_WIDECHAR_API ()) {
473 wchar_t *wc_filename = g_utf8_to_utf16(
474 gaim_xfer_get_local_filename(
475 dialog->selected_xfer),
476 -1, NULL, NULL, NULL);
477
478 code = (int) ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
479 SW_SHOW);
480
481 g_free(wc_filename);
482 } else {
483 char *l_filename = g_locale_from_utf8(
484 gaim_xfer_get_local_filename(
485 dialog->selected_xfer),
486 -1, NULL, NULL, NULL);
487
488 code = (int) ShellExecuteA(NULL, NULL, l_filename, NULL, NULL,
489 SW_SHOW);
490
491 g_free(l_filename);
492 }
493
494 if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
495 {
496 gaim_notify_error(dialog, NULL,
497 _("There is no application configured to open this type of file."), NULL);
498 }
499 else if (code < 32)
500 {
501 gaim_notify_error(dialog, NULL,
502 _("An error occurred while opening the file."), NULL);
503 gaim_debug_warning("ft", "filename: %s; code: %d\n",
504 gaim_xfer_get_local_filename(dialog->selected_xfer), code);
505 }
506 #else
507 const char *filename = gaim_xfer_get_local_filename(dialog->selected_xfer);
508 char *command = NULL;
509 char *tmp = NULL;
510 GError *error = NULL;
511
512 if (gaim_running_gnome())
513 {
514 char *escaped = g_shell_quote(filename);
515 command = g_strdup_printf("gnome-open %s", escaped);
516 g_free(escaped);
517 }
518 else if (gaim_running_kde())
519 {
520 char *escaped = g_shell_quote(filename);
521
522 if (gaim_str_has_suffix(filename, ".desktop"))
523 command = g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped);
524 else
525 command = g_strdup_printf("kfmclient openURL %s", escaped);
526 g_free(escaped);
527 }
528 else
529 {
530 gaim_notify_uri(NULL, filename);
531 return;
532 }
533
534 if (gaim_program_is_valid(command))
535 {
536 gint exit_status;
537 if (!g_spawn_command_line_sync(command, NULL, NULL, &exit_status, &error))
538 {
539 tmp = g_strdup_printf(_("Error launching %s: %s"),
540 gaim_xfer_get_local_filename(dialog->selected_xfer),
541 error->message);
542 gaim_notify_error(dialog, NULL, _("Unable to open file."), tmp);
543 g_free(tmp);
544 g_error_free(error);
545 }
546 if (exit_status != 0)
547 {
548 char *primary = g_strdup_printf(_("Error running %s"), command);
549 char *secondary = g_strdup_printf(_("Process returned error code %d"),
550 exit_status);
551 gaim_notify_error(dialog, NULL, primary, secondary);
552 g_free(tmp);
553 }
554 }
555 #endif
556 }
557
558 static void
559 pause_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
560 {
561 }
562
563 static void
564 resume_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
565 {
566 }
567
568 static void
569 remove_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
570 {
571 gaim_gtkxfer_dialog_remove_xfer(dialog, dialog->selected_xfer);
572 }
573
574 static void
575 stop_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
576 {
577 gaim_xfer_cancel_local(dialog->selected_xfer);
578 }
579
580 static void
581 close_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
582 {
583 gaim_gtkxfer_dialog_hide(dialog);
584 }
585
586
587 /**************************************************************************
588 * Dialog Building Functions
589 **************************************************************************/
590 static GtkWidget *
591 setup_tree(GaimGtkXferDialog *dialog)
592 {
593 GtkWidget *sw;
594 GtkWidget *tree;
595 GtkListStore *model;
596 GtkCellRenderer *renderer;
597 GtkTreeViewColumn *column;
598 GtkTreeSelection *selection;
599
600 /* Create the scrolled window. */
601 sw = gtk_scrolled_window_new(0, 0);
602 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
603 GTK_SHADOW_IN);
604 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
605 GTK_POLICY_AUTOMATIC,
606 GTK_POLICY_AUTOMATIC);
607 gtk_widget_show(sw);
608
609 /* Build the tree model */
610 /* Transfer type, Progress Bar, Filename, Size, Remaining */
611 model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE,
612 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
613 G_TYPE_POINTER);
614 dialog->model = model;
615
616 /* Create the treeview */
617 dialog->tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
618 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
619 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
620 /* gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); */
621
622 gtk_widget_show(tree);
623
624 g_signal_connect(G_OBJECT(selection), "changed",
625 G_CALLBACK(selection_changed_cb), dialog);
626
627 g_object_unref(G_OBJECT(model));
628
629
630 /* Columns */
631
632 /* Transfer Type column */
633 renderer = gtk_cell_renderer_pixbuf_new();
634 column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
635 "pixbuf", COLUMN_STATUS, NULL);
636 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
637 GTK_TREE_VIEW_COLUMN_FIXED);
638 gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 25);
639 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
640 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
641
642 /* Progress bar column */
643 renderer = gaim_gtk_cell_renderer_progress_new();
644 column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer,
645 "percentage", COLUMN_PROGRESS, NULL);
646 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
647 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
648
649 /* Filename column */
650 renderer = gtk_cell_renderer_text_new();
651 column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer,
652 "text", COLUMN_FILENAME, NULL);
653 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
654 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
655
656 /* File Size column */
657 renderer = gtk_cell_renderer_text_new();
658 column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer,
659 "text", COLUMN_SIZE, NULL);
660 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
661 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
662
663 /* Bytes Remaining column */
664 renderer = gtk_cell_renderer_text_new();
665 column = gtk_tree_view_column_new_with_attributes(_("Remaining"),
666 renderer, "text", COLUMN_REMAINING, NULL);
667 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
668 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
669
670 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree));
671
672 gtk_container_add(GTK_CONTAINER(sw), tree);
673 gtk_widget_show(tree);
674
675 return sw;
676 }
677
678 static GtkWidget *
679 make_info_table(GaimGtkXferDialog *dialog)
680 {
681 GtkWidget *table;
682 GtkWidget *label;
683 int i;
684
685 struct
686 {
687 GtkWidget **desc_label;
688 GtkWidget **val_label;
689 const char *desc;
690
691 } labels[] =
692 {
693 { &dialog->local_user_desc_label, &dialog->local_user_label, NULL },
694 { &dialog->remote_user_desc_label, &dialog->remote_user_label, NULL },
695 { &label, &dialog->protocol_label, _("Protocol:") },
696 { &label, &dialog->filename_label, _("Filename:") },
697 { &label, &dialog->localfile_label, _("Local File:") },
698 { &label, &dialog->status_label, _("Status:") },
699 { &label, &dialog->speed_label, _("Speed:") },
700 { &label, &dialog->time_elapsed_label, _("Time Elapsed:") },
701 { &label, &dialog->time_remaining_label, _("Time Remaining:") }
702 };
703
704 /* Setup the initial table */
705 dialog->table = table = gtk_table_new(9, 2, FALSE);
706 gtk_table_set_row_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE);
707 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE);
708
709 /* Setup the labels */
710 for (i = 0; i < sizeof(labels) / sizeof(*labels); i++) {
711 GtkWidget *label;
712 char buf[256];
713
714 g_snprintf(buf, sizeof(buf), "<b>%s</b>",
715 labels[i].desc != NULL ? labels[i].desc : "");
716
717 *labels[i].desc_label = label = gtk_label_new(NULL);
718 gtk_label_set_markup(GTK_LABEL(label), buf);
719 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
720 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
721 gtk_table_attach(GTK_TABLE(table), label, 0, 1, i, i + 1,
722 GTK_FILL, 0, 0, 0);
723 gtk_widget_show(label);
724
725 *labels[i].val_label = label = gtk_label_new(NULL);
726 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
727 gtk_table_attach(GTK_TABLE(table), label, 1, 2, i, i + 1,
728 GTK_FILL | GTK_EXPAND, 0, 0, 0);
729 gtk_widget_show(label);
730 }
731
732 /* Setup the progress bar */
733 dialog->progress = gtk_progress_bar_new();
734 gtk_table_attach(GTK_TABLE(table), dialog->progress, 0, 2, 8, 9,
735 GTK_FILL, GTK_FILL, 0, 0);
736 gtk_widget_show(dialog->progress);
737
738 return table;
739 }
740
741 GaimGtkXferDialog *
742 gaim_gtkxfer_dialog_new(void)
743 {
744 GaimGtkXferDialog *dialog;
745 GtkWidget *window;
746 GtkWidget *vbox1, *vbox2;
747 GtkWidget *bbox;
748 GtkWidget *sw;
749 GtkWidget *button;
750 GtkWidget *expander;
751 GtkWidget *table;
752 GtkWidget *checkbox;
753
754 dialog = g_new0(GaimGtkXferDialog, 1);
755 dialog->keep_open =
756 gaim_prefs_get_bool("/gaim/gtk/filetransfer/keep_open");
757 dialog->auto_clear =
758 gaim_prefs_get_bool("/gaim/gtk/filetransfer/clear_finished");
759
760 /* Create the window. */
761 dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
762 gtk_window_set_role(GTK_WINDOW(window), "file transfer");
763 gtk_window_set_title(GTK_WINDOW(window), _("File Transfers"));
764 gtk_container_set_border_width(GTK_CONTAINER(window), GAIM_HIG_BORDER);
765
766 g_signal_connect(G_OBJECT(window), "delete_event",
767 G_CALLBACK(delete_win_cb), dialog);
768
769 /* Create the parent vbox for everything. */
770 vbox1 = gtk_vbox_new(FALSE, GAIM_HIG_BORDER);
771 gtk_container_add(GTK_CONTAINER(window), vbox1);
772 gtk_widget_show(vbox1);
773
774 /* Create the main vbox for top half of the window. */
775 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
776 gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0);
777 gtk_widget_show(vbox2);
778
779 /* Setup the listbox */
780 sw = setup_tree(dialog);
781 gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0);
782 gtk_widget_set_size_request(sw,-1, 140);
783
784 /* "Close this window when all transfers finish" */
785 checkbox = gtk_check_button_new_with_mnemonic(
786 _("Close this window when all transfers _finish"));
787 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
788 !dialog->keep_open);
789 g_signal_connect(G_OBJECT(checkbox), "toggled",
790 G_CALLBACK(toggle_keep_open_cb), dialog);
791 gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
792 gtk_widget_show(checkbox);
793
794 /* "Clear finished transfers" */
795 checkbox = gtk_check_button_new_with_mnemonic(
796 _("C_lear finished transfers"));
797 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
798 dialog->auto_clear);
799 g_signal_connect(G_OBJECT(checkbox), "toggled",
800 G_CALLBACK(toggle_clear_finished_cb), dialog);
801 gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
802 gtk_widget_show(checkbox);
803
804 /* "Download Details" arrow */
805 expander = gtk_expander_new_with_mnemonic(_("File transfer _details"));
806 dialog->expander = expander;
807 gtk_box_pack_start(GTK_BOX(vbox2), expander, FALSE, FALSE, 0);
808 gtk_widget_show(expander);
809
810 gtk_widget_set_sensitive(expander, FALSE);
811
812 /* The table of information. */
813 table = make_info_table(dialog);
814 gtk_container_add(GTK_CONTAINER(expander), table);
815 gtk_widget_show(table);
816
817 /* Now the button box for the buttons */
818 bbox = gtk_hbutton_box_new();
819 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE);
820 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
821 gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0);
822 gtk_widget_show(bbox);
823
824 /* Open button */
825 button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
826 gtk_widget_set_sensitive(button, FALSE);
827 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
828 gtk_widget_show(button);
829 dialog->open_button = button;
830
831 g_signal_connect(G_OBJECT(button), "clicked",
832 G_CALLBACK(open_button_cb), dialog);
833
834 /* Pause button */
835 button = gtk_button_new_with_mnemonic(_("_Pause"));
836 gtk_widget_set_sensitive(button, FALSE);
837 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
838 gtk_widget_show(button);
839 dialog->pause_button = button;
840
841 g_signal_connect(G_OBJECT(button), "clicked",
842 G_CALLBACK(pause_button_cb), dialog);
843
844 /* Resume button */
845 button = gtk_button_new_with_mnemonic(_("_Resume"));
846 gtk_widget_set_sensitive(button, FALSE);
847 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
848 gtk_widget_show(button);
849 dialog->resume_button = button;
850
851 g_signal_connect(G_OBJECT(button), "clicked",
852 G_CALLBACK(resume_button_cb), dialog);
853
854 /* Remove button */
855 button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
856 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
857 gtk_widget_hide(button);
858 dialog->remove_button = button;
859
860 g_signal_connect(G_OBJECT(button), "clicked",
861 G_CALLBACK(remove_button_cb), dialog);
862
863 /* Stop button */
864 button = gtk_button_new_from_stock(GTK_STOCK_STOP);
865 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
866 gtk_widget_show(button);
867 gtk_widget_set_sensitive(button, FALSE);
868 dialog->stop_button = button;
869
870 g_signal_connect(G_OBJECT(button), "clicked",
871 G_CALLBACK(stop_button_cb), dialog);
872
873 /* Close button */
874 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
875 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
876 gtk_widget_show(button);
877 dialog->close_button = button;
878
879 g_signal_connect(G_OBJECT(button), "clicked",
880 G_CALLBACK(close_button_cb), dialog);
881
882 #ifdef _WIN32
883 g_signal_connect(G_OBJECT(dialog->window), "show",
884 G_CALLBACK(gtkwgaim_ensure_onscreen), dialog->window);
885 #endif
886
887 return dialog;
888 }
889
890 void
891 gaim_gtkxfer_dialog_destroy(GaimGtkXferDialog *dialog)
892 {
893 g_return_if_fail(dialog != NULL);
894
895 gaim_notify_close_with_handle(dialog);
896
897 gtk_widget_destroy(dialog->window);
898
899 g_free(dialog);
900 }
901
902 void
903 gaim_gtkxfer_dialog_show(GaimGtkXferDialog *dialog)
904 {
905 GaimGtkXferDialog *tmp;
906
907 if (dialog == NULL) {
908 tmp = gaim_get_gtkxfer_dialog();
909
910 if (tmp == NULL) {
911 tmp = gaim_gtkxfer_dialog_new();
912 gaim_set_gtkxfer_dialog(tmp);
913 }
914
915 gtk_widget_show(tmp->window);
916 } else {
917 gtk_widget_show(dialog->window);
918 }
919 }
920
921 void
922 gaim_gtkxfer_dialog_hide(GaimGtkXferDialog *dialog)
923 {
924 g_return_if_fail(dialog != NULL);
925
926 gaim_notify_close_with_handle(dialog);
927
928 gtk_widget_hide(dialog->window);
929 }
930
931 void
932 gaim_gtkxfer_dialog_add_xfer(GaimGtkXferDialog *dialog, GaimXfer *xfer)
933 {
934 GaimGtkXferUiData *data;
935 GaimXferType type;
936 GdkPixbuf *pixbuf;
937 char *size_str, *remaining_str;
938 char *lfilename, *utf8;
939
940 g_return_if_fail(dialog != NULL);
941 g_return_if_fail(xfer != NULL);
942
943 gaim_xfer_ref(xfer);
944
945 data = GAIM_GTKXFER(xfer);
946 data->in_list = TRUE;
947
948 gaim_gtkxfer_dialog_show(dialog);
949
950 data->last_updated_time = 0;
951
952 type = gaim_xfer_get_type(xfer);
953
954 size_str = gaim_str_size_to_units(gaim_xfer_get_size(xfer));
955 remaining_str = gaim_str_size_to_units(gaim_xfer_get_bytes_remaining(xfer));
956
957 pixbuf = gtk_widget_render_icon(dialog->window,
958 (type == GAIM_XFER_RECEIVE
959 ? GAIM_STOCK_DOWNLOAD
960 : GAIM_STOCK_UPLOAD),
961 GTK_ICON_SIZE_MENU, NULL);
962
963 gtk_list_store_append(dialog->model, &data->iter);
964 lfilename = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
965 utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL);
966 g_free(lfilename);
967 lfilename = utf8;
968 gtk_list_store_set(dialog->model, &data->iter,
969 COLUMN_STATUS, pixbuf,
970 COLUMN_PROGRESS, 0.0,
971 COLUMN_FILENAME, (type == GAIM_XFER_RECEIVE)
972 ? gaim_xfer_get_filename(xfer)
973 : lfilename,
974 COLUMN_SIZE, size_str,
975 COLUMN_REMAINING, _("Waiting for transfer to begin"),
976 COLUMN_DATA, xfer,
977 -1);
978 g_free(lfilename);
979
980 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog->tree));
981
982 g_object_unref(pixbuf);
983
984 g_free(size_str);
985 g_free(remaining_str);
986
987 dialog->num_transfers++;
988
989 ensure_row_selected(dialog);
990 update_title_progress(dialog);
991 }
992
993 void
994 gaim_gtkxfer_dialog_remove_xfer(GaimGtkXferDialog *dialog,
995 GaimXfer *xfer)
996 {
997 GaimGtkXferUiData *data;
998
999 g_return_if_fail(dialog != NULL);
1000 g_return_if_fail(xfer != NULL);
1001
1002 data = GAIM_GTKXFER(xfer);
1003
1004 if (data == NULL)
1005 return;
1006
1007 if (!data->in_list)
1008 return;
1009
1010 data->in_list = FALSE;
1011
1012 gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter);
1013
1014 dialog->num_transfers--;
1015
1016 ensure_row_selected(dialog);
1017
1018 update_title_progress(dialog);
1019 gaim_xfer_unref(xfer);
1020 }
1021
1022 void
1023 gaim_gtkxfer_dialog_cancel_xfer(GaimGtkXferDialog *dialog,
1024 GaimXfer *xfer)
1025 {
1026 GaimGtkXferUiData *data;
1027 GdkPixbuf *pixbuf;
1028 const gchar *status;
1029
1030 g_return_if_fail(dialog != NULL);
1031 g_return_if_fail(xfer != NULL);
1032
1033 data = GAIM_GTKXFER(xfer);
1034
1035 if (data == NULL)
1036 return;
1037
1038 if (!data->in_list)
1039 return;
1040
1041 if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) {
1042 gaim_gtkxfer_dialog_remove_xfer(dialog, xfer);
1043 return;
1044 }
1045
1046 data = GAIM_GTKXFER(xfer);
1047
1048 update_detailed_info(dialog, xfer);
1049 update_title_progress(dialog);
1050
1051 pixbuf = gtk_widget_render_icon(dialog->window,
1052 GAIM_STOCK_FILE_CANCELED,
1053 GTK_ICON_SIZE_MENU, NULL);
1054
1055 if (gaim_xfer_is_canceled(xfer))
1056 status = _("Canceled");
1057 else
1058 status = _("Failed");
1059
1060 gtk_list_store_set(dialog->model, &data->iter,
1061 COLUMN_STATUS, pixbuf,
1062 COLUMN_REMAINING, status,
1063 -1);
1064
1065 g_object_unref(pixbuf);
1066
1067 update_buttons(dialog, xfer);
1068 }
1069
1070 void
1071 gaim_gtkxfer_dialog_update_xfer(GaimGtkXferDialog *dialog,
1072 GaimXfer *xfer)
1073 {
1074 GaimGtkXferUiData *data;
1075 char *size_str, *remaining_str;
1076 GtkTreeSelection *selection;
1077 time_t current_time;
1078 GtkTreeIter iter;
1079 gboolean valid;
1080
1081 g_return_if_fail(dialog != NULL);
1082 g_return_if_fail(xfer != NULL);
1083
1084 if ((data = GAIM_GTKXFER(xfer)) == NULL)
1085 return;
1086
1087 if (data->in_list == FALSE)
1088 return;
1089
1090 current_time = time(NULL);
1091 if (((current_time - data->last_updated_time) == 0) &&
1092 (!gaim_xfer_is_completed(xfer)))
1093 {
1094 /* Don't update the window more than once per second */
1095 return;
1096 }
1097 data->last_updated_time = current_time;
1098
1099 size_str = gaim_str_size_to_units(gaim_xfer_get_size(xfer));
1100 remaining_str = gaim_str_size_to_units(gaim_xfer_get_bytes_remaining(xfer));
1101
1102 gtk_list_store_set(xfer_dialog->model, &data->iter,
1103 COLUMN_PROGRESS, gaim_xfer_get_progress(xfer),
1104 COLUMN_SIZE, size_str,
1105 COLUMN_REMAINING, remaining_str,
1106 -1);
1107
1108 if (gaim_xfer_is_completed(xfer))
1109 {
1110 GdkPixbuf *pixbuf;
1111
1112 pixbuf = gtk_widget_render_icon(dialog->window,
1113 GAIM_STOCK_FILE_DONE,
1114 GTK_ICON_SIZE_MENU, NULL);
1115
1116 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter,
1117 COLUMN_STATUS, pixbuf,
1118 COLUMN_REMAINING, _("Finished"),
1119 -1);
1120
1121 g_object_unref(pixbuf);
1122 }
1123
1124 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(xfer_dialog->tree));
1125
1126 update_title_progress(dialog);
1127 if (xfer == dialog->selected_xfer)
1128 update_detailed_info(xfer_dialog, xfer);
1129
1130 if (gaim_xfer_is_completed(xfer) && dialog->auto_clear)
1131 gaim_gtkxfer_dialog_remove_xfer(dialog, xfer);
1132 else
1133 update_buttons(dialog, xfer);
1134
1135 /*
1136 * If all transfers are finished, and the pref is set, then
1137 * close the dialog. Otherwise just exit this function.
1138 */
1139 if (dialog->keep_open)
1140 return;
1141
1142 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter);
1143 while (valid)
1144 {
1145 GValue val;
1146 GaimXfer *next;
1147
1148 val.g_type = 0;
1149 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
1150 &iter, COLUMN_DATA, &val);
1151
1152 next = g_value_get_pointer(&val);
1153 if (!gaim_xfer_is_completed(next))
1154 return;
1155
1156 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->model), &iter);
1157 }
1158
1159 /* If we got to this point then we know everything is finished */
1160 gaim_gtkxfer_dialog_hide(dialog);
1161 }
1162
1163 /**************************************************************************
1164 * File Transfer UI Ops
1165 **************************************************************************/
1166 static void
1167 gaim_gtkxfer_new_xfer(GaimXfer *xfer)
1168 {
1169 GaimGtkXferUiData *data;
1170
1171 /* This is where we're setting xfer->ui_data for the first time. */
1172 data = g_new0(GaimGtkXferUiData, 1);
1173 xfer->ui_data = data;
1174 }
1175
1176 static void
1177 gaim_gtkxfer_destroy(GaimXfer *xfer)
1178 {
1179 GaimGtkXferUiData *data;
1180
1181 data = GAIM_GTKXFER(xfer);
1182 if (data) {
1183 g_free(data->name);
1184 g_free(data);
1185 xfer->ui_data = NULL;
1186 }
1187 }
1188
1189 static void
1190 gaim_gtkxfer_add_xfer(GaimXfer *xfer)
1191 {
1192 if (xfer_dialog == NULL)
1193 xfer_dialog = gaim_gtkxfer_dialog_new();
1194
1195 gaim_gtkxfer_dialog_add_xfer(xfer_dialog, xfer);
1196 }
1197
1198 static void
1199 gaim_gtkxfer_update_progress(GaimXfer *xfer, double percent)
1200 {
1201 gaim_gtkxfer_dialog_update_xfer(xfer_dialog, xfer);
1202 }
1203
1204 static void
1205 gaim_gtkxfer_cancel_local(GaimXfer *xfer)
1206 {
1207 if (xfer_dialog)
1208 gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer);
1209 }
1210
1211 static void
1212 gaim_gtkxfer_cancel_remote(GaimXfer *xfer)
1213 {
1214 if (xfer_dialog)
1215 gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer);
1216 }
1217
1218 static GaimXferUiOps ops =
1219 {
1220 gaim_gtkxfer_new_xfer,
1221 gaim_gtkxfer_destroy,
1222 gaim_gtkxfer_add_xfer,
1223 gaim_gtkxfer_update_progress,
1224 gaim_gtkxfer_cancel_local,
1225 gaim_gtkxfer_cancel_remote
1226 };
1227
1228 /**************************************************************************
1229 * GTK+ File Transfer API
1230 **************************************************************************/
1231 void
1232 gaim_gtk_xfers_init(void)
1233 {
1234 gaim_prefs_add_none("/gaim/gtk/filetransfer");
1235 gaim_prefs_add_bool("/gaim/gtk/filetransfer/clear_finished", TRUE);
1236 gaim_prefs_add_bool("/gaim/gtk/filetransfer/keep_open", FALSE);
1237 }
1238
1239 void
1240 gaim_gtk_xfers_uninit(void)
1241 {
1242 if (xfer_dialog != NULL)
1243 gaim_gtkxfer_dialog_destroy(xfer_dialog);
1244 }
1245
1246 void
1247 gaim_set_gtkxfer_dialog(GaimGtkXferDialog *dialog)
1248 {
1249 xfer_dialog = dialog;
1250 }
1251
1252 GaimGtkXferDialog *
1253 gaim_get_gtkxfer_dialog(void)
1254 {
1255 return xfer_dialog;
1256 }
1257
1258 GaimXferUiOps *
1259 gaim_gtk_xfers_get_ui_ops(void)
1260 {
1261 return &ops;
1262 }