comparison gtk/gtkft.c @ 14191:009db0b357b5

This is a hand-crafted commit to migrate across subversion revisions 16854:16861, due to some vagaries of the way the original renames were done. Witness that monotone can do in one revision what svn had to spread across several.
author Ethan Blanton <elb@pidgin.im>
date Sat, 16 Dec 2006 04:59:55 +0000
parents
children 2620e491c8f1
comparison
equal deleted inserted replaced
14190:366be2ce35a7 14191:009db0b357b5
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_detailed_info(GaimGtkXferDialog *dialog, GaimXfer *xfer)
187 {
188 GaimGtkXferUiData *data;
189 char *kbsec, *time_elapsed, *time_remaining;
190 char *status, *utf8;
191
192 if (dialog == NULL || xfer == NULL)
193 return;
194
195 data = GAIM_GTKXFER(xfer);
196
197 get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining);
198
199 status = g_strdup_printf("%ld%% (%ld of %ld bytes)",
200 (unsigned long)(gaim_xfer_get_progress(xfer)*100),
201 (unsigned long)gaim_xfer_get_bytes_sent(xfer),
202 (unsigned long)gaim_xfer_get_size(xfer));
203
204 if (gaim_xfer_is_completed(xfer)) {
205
206 GdkPixbuf *pixbuf = NULL;
207
208 pixbuf = gtk_widget_render_icon(xfer_dialog->window,
209 GAIM_STOCK_FILE_DONE,
210 GTK_ICON_SIZE_MENU, NULL);
211
212 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter,
213 COLUMN_STATUS, pixbuf,
214 -1);
215
216 g_object_unref(pixbuf);
217 }
218
219 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
220 gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label),
221 _("<b>Receiving As:</b>"));
222 gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label),
223 _("<b>Receiving From:</b>"));
224 }
225 else {
226 gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label),
227 _("<b>Sending To:</b>"));
228 gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label),
229 _("<b>Sending As:</b>"));
230 }
231
232 gtk_label_set_text(GTK_LABEL(dialog->local_user_label),
233 gaim_account_get_username(xfer->account));
234 gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), xfer->who);
235 gtk_label_set_text(GTK_LABEL(dialog->protocol_label),
236 gaim_account_get_protocol_name(xfer->account));
237
238 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
239 gtk_label_set_text(GTK_LABEL(dialog->filename_label),
240 gaim_xfer_get_filename(xfer));
241 } else {
242 char *tmp;
243
244 tmp = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
245 utf8 = g_filename_to_utf8(tmp, -1, NULL, NULL, NULL);
246 g_free(tmp);
247
248 gtk_label_set_text(GTK_LABEL(dialog->filename_label), utf8);
249 g_free(utf8);
250 }
251
252 utf8 = g_filename_to_utf8((gaim_xfer_get_local_filename(xfer)), -1, NULL, NULL, NULL);
253 gtk_label_set_text(GTK_LABEL(dialog->localfile_label), utf8);
254 g_free(utf8);
255
256 gtk_label_set_text(GTK_LABEL(dialog->status_label), status);
257
258 gtk_label_set_text(GTK_LABEL(dialog->speed_label), kbsec);
259 gtk_label_set_text(GTK_LABEL(dialog->time_elapsed_label), time_elapsed);
260 gtk_label_set_text(GTK_LABEL(dialog->time_remaining_label),
261 time_remaining);
262
263 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress),
264 gaim_xfer_get_progress(xfer));
265
266 g_free(kbsec);
267 g_free(time_elapsed);
268 g_free(time_remaining);
269 g_free(status);
270 }
271
272 static void
273 update_buttons(GaimGtkXferDialog *dialog, GaimXfer *xfer)
274 {
275 if (dialog->selected_xfer == NULL) {
276 gtk_widget_set_sensitive(dialog->expander, FALSE);
277 gtk_widget_set_sensitive(dialog->open_button, FALSE);
278 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
279 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
280 gtk_widget_set_sensitive(dialog->stop_button, FALSE);
281
282 gtk_widget_show(dialog->stop_button);
283 gtk_widget_hide(dialog->remove_button);
284
285 return;
286 }
287
288 if (dialog->selected_xfer != xfer)
289 return;
290
291 if (gaim_xfer_is_completed(xfer)) {
292 gtk_widget_hide(dialog->stop_button);
293 gtk_widget_show(dialog->remove_button);
294
295 #ifdef _WIN32
296 /* If using Win32... */
297 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
298 gtk_widget_set_sensitive(dialog->open_button, TRUE);
299 } else {
300 gtk_widget_set_sensitive(dialog->open_button, FALSE);
301 }
302 #else
303 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
304 gtk_widget_set_sensitive(dialog->open_button, TRUE);
305 } else {
306 gtk_widget_set_sensitive (dialog->open_button, FALSE);
307 }
308 #endif
309 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
310 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
311
312 gtk_widget_set_sensitive(dialog->remove_button, TRUE);
313 } else if (gaim_xfer_is_canceled(xfer)) {
314 gtk_widget_hide(dialog->stop_button);
315 gtk_widget_show(dialog->remove_button);
316
317 gtk_widget_set_sensitive(dialog->open_button, FALSE);
318 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
319 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
320
321 gtk_widget_set_sensitive(dialog->remove_button, TRUE);
322 } else {
323 gtk_widget_show(dialog->stop_button);
324 gtk_widget_hide(dialog->remove_button);
325
326 gtk_widget_set_sensitive(dialog->open_button, FALSE);
327
328 /* TODO: If the transfer can pause, blah blah */
329 gtk_widget_set_sensitive(dialog->pause_button, FALSE);
330 gtk_widget_set_sensitive(dialog->resume_button, FALSE);
331 gtk_widget_set_sensitive(dialog->stop_button, TRUE);
332 }
333 }
334
335 static void
336 ensure_row_selected(GaimGtkXferDialog *dialog)
337 {
338 GtkTreeIter iter;
339 GtkTreeSelection *selection;
340
341 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
342
343 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
344 return;
345
346 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter))
347 gtk_tree_selection_select_iter(selection, &iter);
348 }
349
350 /**************************************************************************
351 * Callbacks
352 **************************************************************************/
353 static gint
354 delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
355 {
356 GaimGtkXferDialog *dialog;
357
358 dialog = (GaimGtkXferDialog *)d;
359
360 gaim_gtkxfer_dialog_hide(dialog);
361
362 return TRUE;
363 }
364
365 static void
366 toggle_keep_open_cb(GtkWidget *w, GaimGtkXferDialog *dialog)
367 {
368 dialog->keep_open = !dialog->keep_open;
369 gaim_prefs_set_bool("/gaim/gtk/filetransfer/keep_open",
370 dialog->keep_open);
371 }
372
373 static void
374 toggle_clear_finished_cb(GtkWidget *w, GaimGtkXferDialog *dialog)
375 {
376 dialog->auto_clear = !dialog->auto_clear;
377 gaim_prefs_set_bool("/gaim/gtk/filetransfer/clear_finished",
378 dialog->auto_clear);
379 }
380
381 static void
382 selection_changed_cb(GtkTreeSelection *selection, GaimGtkXferDialog *dialog)
383 {
384 GtkTreeIter iter;
385 GaimXfer *xfer = NULL;
386
387 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
388 GValue val;
389
390 gtk_widget_set_sensitive(dialog->expander, TRUE);
391
392 val.g_type = 0;
393 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
394 &iter, COLUMN_DATA, &val);
395
396 xfer = g_value_get_pointer(&val);
397
398 update_detailed_info(dialog, xfer);
399
400 dialog->selected_xfer = xfer;
401 }
402 else {
403 gtk_expander_set_expanded(GTK_EXPANDER(dialog->expander),
404 FALSE);
405
406 gtk_widget_set_sensitive(dialog->expander, FALSE);
407
408 dialog->selected_xfer = NULL;
409 }
410
411 update_buttons(dialog, xfer);
412 }
413
414 static void
415 open_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
416 {
417 #ifdef _WIN32
418 /* If using Win32... */
419 int code;
420 if (G_WIN32_HAVE_WIDECHAR_API ()) {
421 wchar_t *wc_filename = g_utf8_to_utf16(
422 gaim_xfer_get_local_filename(
423 dialog->selected_xfer),
424 -1, NULL, NULL, NULL);
425
426 code = (int) ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
427 SW_SHOW);
428
429 g_free(wc_filename);
430 } else {
431 char *l_filename = g_locale_from_utf8(
432 gaim_xfer_get_local_filename(
433 dialog->selected_xfer),
434 -1, NULL, NULL, NULL);
435
436 code = (int) ShellExecuteA(NULL, NULL, l_filename, NULL, NULL,
437 SW_SHOW);
438
439 g_free(l_filename);
440 }
441
442 if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
443 {
444 gaim_notify_error(dialog, NULL,
445 _("There is no application configured to open this type of file."), NULL);
446 }
447 else if (code < 32)
448 {
449 gaim_notify_error(dialog, NULL,
450 _("An error occurred while opening the file."), NULL);
451 gaim_debug_warning("ft", "filename: %s; code: %d\n",
452 gaim_xfer_get_local_filename(dialog->selected_xfer), code);
453 }
454 #else
455 const char *filename = gaim_xfer_get_local_filename(dialog->selected_xfer);
456 char *command = NULL;
457 char *tmp = NULL;
458 GError *error = NULL;
459
460 if (gaim_running_gnome())
461 {
462 char *escaped = g_shell_quote(filename);
463 command = g_strdup_printf("gnome-open %s", escaped);
464 g_free(escaped);
465 }
466 else if (gaim_running_kde())
467 {
468 char *escaped = g_shell_quote(filename);
469
470 if (gaim_str_has_suffix(filename, ".desktop"))
471 command = g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped);
472 else
473 command = g_strdup_printf("kfmclient openURL %s", escaped);
474 g_free(escaped);
475 }
476 else
477 {
478 gaim_notify_uri(NULL, filename);
479 return;
480 }
481
482 if (gaim_program_is_valid(command))
483 {
484 gint exit_status;
485 if (!g_spawn_command_line_sync(command, NULL, NULL, &exit_status, &error))
486 {
487 tmp = g_strdup_printf(_("Error launching %s: %s"),
488 gaim_xfer_get_local_filename(dialog->selected_xfer),
489 error->message);
490 gaim_notify_error(dialog, NULL, _("Unable to open file."), tmp);
491 g_free(tmp);
492 g_error_free(error);
493 }
494 if (exit_status != 0)
495 {
496 char *primary = g_strdup_printf(_("Error running %s"), command);
497 char *secondary = g_strdup_printf(_("Process returned error code %d"),
498 exit_status);
499 gaim_notify_error(dialog, NULL, primary, secondary);
500 g_free(tmp);
501 }
502 }
503 #endif
504 }
505
506 static void
507 pause_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
508 {
509 }
510
511 static void
512 resume_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
513 {
514 }
515
516 static void
517 remove_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
518 {
519 gaim_gtkxfer_dialog_remove_xfer(dialog, dialog->selected_xfer);
520 }
521
522 static void
523 stop_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
524 {
525 gaim_xfer_cancel_local(dialog->selected_xfer);
526 }
527
528 static void
529 close_button_cb(GtkButton *button, GaimGtkXferDialog *dialog)
530 {
531 gaim_gtkxfer_dialog_hide(dialog);
532 }
533
534 /**************************************************************************
535 * Dialog Building Functions
536 **************************************************************************/
537 static GtkWidget *
538 setup_tree(GaimGtkXferDialog *dialog)
539 {
540 GtkWidget *sw;
541 GtkWidget *tree;
542 GtkListStore *model;
543 GtkCellRenderer *renderer;
544 GtkTreeViewColumn *column;
545 GtkTreeSelection *selection;
546
547 /* Create the scrolled window. */
548 sw = gtk_scrolled_window_new(0, 0);
549 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
550 GTK_SHADOW_IN);
551 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
552 GTK_POLICY_AUTOMATIC,
553 GTK_POLICY_AUTOMATIC);
554 gtk_widget_show(sw);
555
556 /* Build the tree model */
557 /* Transfer type, Progress Bar, Filename, Size, Remaining */
558 model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE,
559 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
560 G_TYPE_POINTER);
561 dialog->model = model;
562
563 /* Create the treeview */
564 dialog->tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
565 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
566 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
567 /* gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); */
568
569 gtk_widget_show(tree);
570
571 g_signal_connect(G_OBJECT(selection), "changed",
572 G_CALLBACK(selection_changed_cb), dialog);
573
574 g_object_unref(G_OBJECT(model));
575
576
577 /* Columns */
578
579 /* Transfer Type column */
580 renderer = gtk_cell_renderer_pixbuf_new();
581 column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
582 "pixbuf", COLUMN_STATUS, NULL);
583 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
584 GTK_TREE_VIEW_COLUMN_FIXED);
585 gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 25);
586 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
587 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
588
589 /* Progress bar column */
590 renderer = gaim_gtk_cell_renderer_progress_new();
591 column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer,
592 "percentage", COLUMN_PROGRESS, NULL);
593 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
594 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
595
596 /* Filename column */
597 renderer = gtk_cell_renderer_text_new();
598 column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer,
599 "text", COLUMN_FILENAME, NULL);
600 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
601 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
602
603 /* File Size column */
604 renderer = gtk_cell_renderer_text_new();
605 column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer,
606 "text", COLUMN_SIZE, NULL);
607 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
608 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
609
610 /* Bytes Remaining column */
611 renderer = gtk_cell_renderer_text_new();
612 column = gtk_tree_view_column_new_with_attributes(_("Remaining"),
613 renderer, "text", COLUMN_REMAINING, NULL);
614 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
615 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
616
617 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree));
618
619 gtk_container_add(GTK_CONTAINER(sw), tree);
620 gtk_widget_show(tree);
621
622 return sw;
623 }
624
625 static GtkWidget *
626 make_info_table(GaimGtkXferDialog *dialog)
627 {
628 GtkWidget *table;
629 GtkWidget *label;
630 int i;
631
632 struct
633 {
634 GtkWidget **desc_label;
635 GtkWidget **val_label;
636 const char *desc;
637
638 } labels[] =
639 {
640 { &dialog->local_user_desc_label, &dialog->local_user_label, NULL },
641 { &dialog->remote_user_desc_label, &dialog->remote_user_label, NULL },
642 { &label, &dialog->protocol_label, _("Protocol:") },
643 { &label, &dialog->filename_label, _("Filename:") },
644 { &label, &dialog->localfile_label, _("Local File:") },
645 { &label, &dialog->status_label, _("Status:") },
646 { &label, &dialog->speed_label, _("Speed:") },
647 { &label, &dialog->time_elapsed_label, _("Time Elapsed:") },
648 { &label, &dialog->time_remaining_label, _("Time Remaining:") }
649 };
650
651 /* Setup the initial table */
652 dialog->table = table = gtk_table_new(9, 2, FALSE);
653 gtk_table_set_row_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE);
654 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE);
655
656 /* Setup the labels */
657 for (i = 0; i < sizeof(labels) / sizeof(*labels); i++) {
658 GtkWidget *label;
659 char buf[256];
660
661 g_snprintf(buf, sizeof(buf), "<b>%s</b>",
662 labels[i].desc != NULL ? labels[i].desc : "");
663
664 *labels[i].desc_label = label = gtk_label_new(NULL);
665 gtk_label_set_markup(GTK_LABEL(label), buf);
666 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
667 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
668 gtk_table_attach(GTK_TABLE(table), label, 0, 1, i, i + 1,
669 GTK_FILL, 0, 0, 0);
670 gtk_widget_show(label);
671
672 *labels[i].val_label = label = gtk_label_new(NULL);
673 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
674 gtk_table_attach(GTK_TABLE(table), label, 1, 2, i, i + 1,
675 GTK_FILL | GTK_EXPAND, 0, 0, 0);
676 gtk_widget_show(label);
677 }
678
679 /* Setup the progress bar */
680 dialog->progress = gtk_progress_bar_new();
681 gtk_table_attach(GTK_TABLE(table), dialog->progress, 0, 2, 8, 9,
682 GTK_FILL, GTK_FILL, 0, 0);
683 gtk_widget_show(dialog->progress);
684
685 return table;
686 }
687
688 GaimGtkXferDialog *
689 gaim_gtkxfer_dialog_new(void)
690 {
691 GaimGtkXferDialog *dialog;
692 GtkWidget *window;
693 GtkWidget *vbox1, *vbox2;
694 GtkWidget *bbox;
695 GtkWidget *sw;
696 GtkWidget *button;
697 GtkWidget *expander;
698 GtkWidget *table;
699 GtkWidget *checkbox;
700
701 dialog = g_new0(GaimGtkXferDialog, 1);
702 dialog->keep_open =
703 gaim_prefs_get_bool("/gaim/gtk/filetransfer/keep_open");
704 dialog->auto_clear =
705 gaim_prefs_get_bool("/gaim/gtk/filetransfer/clear_finished");
706
707 /* Create the window. */
708 dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
709 gtk_window_set_role(GTK_WINDOW(window), "file transfer");
710 gtk_window_set_title(GTK_WINDOW(window), _("File Transfers"));
711 gtk_container_set_border_width(GTK_CONTAINER(window), GAIM_HIG_BORDER);
712
713 g_signal_connect(G_OBJECT(window), "delete_event",
714 G_CALLBACK(delete_win_cb), dialog);
715
716 /* Create the parent vbox for everything. */
717 vbox1 = gtk_vbox_new(FALSE, GAIM_HIG_BORDER);
718 gtk_container_add(GTK_CONTAINER(window), vbox1);
719 gtk_widget_show(vbox1);
720
721 /* Create the main vbox for top half of the window. */
722 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
723 gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0);
724 gtk_widget_show(vbox2);
725
726 /* Setup the listbox */
727 sw = setup_tree(dialog);
728 gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0);
729 gtk_widget_set_size_request(sw,-1, 140);
730
731 /* "Close this window when all transfers finish" */
732 checkbox = gtk_check_button_new_with_mnemonic(
733 _("Close this window when all transfers _finish"));
734 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
735 !dialog->keep_open);
736 g_signal_connect(G_OBJECT(checkbox), "toggled",
737 G_CALLBACK(toggle_keep_open_cb), dialog);
738 gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
739 gtk_widget_show(checkbox);
740
741 /* "Clear finished transfers" */
742 checkbox = gtk_check_button_new_with_mnemonic(
743 _("C_lear finished transfers"));
744 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
745 dialog->auto_clear);
746 g_signal_connect(G_OBJECT(checkbox), "toggled",
747 G_CALLBACK(toggle_clear_finished_cb), dialog);
748 gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0);
749 gtk_widget_show(checkbox);
750
751 /* "Download Details" arrow */
752 expander = gtk_expander_new_with_mnemonic(_("File transfer _details"));
753 dialog->expander = expander;
754 gtk_box_pack_start(GTK_BOX(vbox2), expander, FALSE, FALSE, 0);
755 gtk_widget_show(expander);
756
757 gtk_widget_set_sensitive(expander, FALSE);
758
759 /* The table of information. */
760 table = make_info_table(dialog);
761 gtk_container_add(GTK_CONTAINER(expander), table);
762 gtk_widget_show(table);
763
764 /* Now the button box for the buttons */
765 bbox = gtk_hbutton_box_new();
766 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE);
767 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
768 gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0);
769 gtk_widget_show(bbox);
770
771 /* Open button */
772 button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
773 gtk_widget_set_sensitive(button, FALSE);
774 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
775 gtk_widget_show(button);
776 dialog->open_button = button;
777
778 g_signal_connect(G_OBJECT(button), "clicked",
779 G_CALLBACK(open_button_cb), dialog);
780
781 /* Pause button */
782 button = gtk_button_new_with_mnemonic(_("_Pause"));
783 gtk_widget_set_sensitive(button, FALSE);
784 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
785 gtk_widget_show(button);
786 dialog->pause_button = button;
787
788 g_signal_connect(G_OBJECT(button), "clicked",
789 G_CALLBACK(pause_button_cb), dialog);
790
791 /* Resume button */
792 button = gtk_button_new_with_mnemonic(_("_Resume"));
793 gtk_widget_set_sensitive(button, FALSE);
794 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
795 gtk_widget_show(button);
796 dialog->resume_button = button;
797
798 g_signal_connect(G_OBJECT(button), "clicked",
799 G_CALLBACK(resume_button_cb), dialog);
800
801 /* Remove button */
802 button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
803 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
804 gtk_widget_hide(button);
805 dialog->remove_button = button;
806
807 g_signal_connect(G_OBJECT(button), "clicked",
808 G_CALLBACK(remove_button_cb), dialog);
809
810 /* Stop button */
811 button = gtk_button_new_from_stock(GTK_STOCK_STOP);
812 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
813 gtk_widget_show(button);
814 gtk_widget_set_sensitive(button, FALSE);
815 dialog->stop_button = button;
816
817 g_signal_connect(G_OBJECT(button), "clicked",
818 G_CALLBACK(stop_button_cb), dialog);
819
820 /* Close button */
821 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
822 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
823 gtk_widget_show(button);
824 dialog->close_button = button;
825
826 g_signal_connect(G_OBJECT(button), "clicked",
827 G_CALLBACK(close_button_cb), dialog);
828
829 return dialog;
830 }
831
832 void
833 gaim_gtkxfer_dialog_destroy(GaimGtkXferDialog *dialog)
834 {
835 g_return_if_fail(dialog != NULL);
836
837 gaim_notify_close_with_handle(dialog);
838
839 gtk_widget_destroy(dialog->window);
840
841 g_free(dialog);
842 }
843
844 void
845 gaim_gtkxfer_dialog_show(GaimGtkXferDialog *dialog)
846 {
847 g_return_if_fail(dialog != NULL);
848
849 gtk_widget_show(dialog->window);
850 }
851
852 void
853 gaim_gtkxfer_dialog_hide(GaimGtkXferDialog *dialog)
854 {
855 g_return_if_fail(dialog != NULL);
856
857 gaim_notify_close_with_handle(dialog);
858
859 gtk_widget_hide(dialog->window);
860 }
861
862 void
863 gaim_show_xfer_dialog()
864 {
865 GaimGtkXferDialog *dialog;
866
867 dialog = gaim_get_gtkxfer_dialog();
868
869 if (dialog == NULL) {
870 dialog = gaim_gtkxfer_dialog_new();
871
872 gaim_set_gtkxfer_dialog(dialog);
873 }
874
875 gaim_gtkxfer_dialog_show(dialog);
876 }
877
878 void
879 gaim_gtkxfer_dialog_add_xfer(GaimGtkXferDialog *dialog, GaimXfer *xfer)
880 {
881 GaimGtkXferUiData *data;
882 GaimXferType type;
883 GdkPixbuf *pixbuf;
884 char *size_str, *remaining_str;
885 char *lfilename, *utf8;
886
887 g_return_if_fail(dialog != NULL);
888 g_return_if_fail(xfer != NULL);
889
890 gaim_xfer_ref(xfer);
891
892 data = GAIM_GTKXFER(xfer);
893 data->in_list = TRUE;
894
895 gaim_gtkxfer_dialog_show(dialog);
896
897 data->last_updated_time = 0;
898
899 type = gaim_xfer_get_type(xfer);
900
901 size_str = gaim_str_size_to_units(gaim_xfer_get_size(xfer));
902 remaining_str = gaim_str_size_to_units(gaim_xfer_get_bytes_remaining(xfer));
903
904 pixbuf = gtk_widget_render_icon(dialog->window,
905 (type == GAIM_XFER_RECEIVE
906 ? GAIM_STOCK_DOWNLOAD
907 : GAIM_STOCK_UPLOAD),
908 GTK_ICON_SIZE_MENU, NULL);
909
910 gtk_list_store_append(dialog->model, &data->iter);
911 lfilename = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
912 utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL);
913 g_free(lfilename);
914 lfilename = utf8;
915 gtk_list_store_set(dialog->model, &data->iter,
916 COLUMN_STATUS, pixbuf,
917 COLUMN_PROGRESS, 0.0,
918 COLUMN_FILENAME, (type == GAIM_XFER_RECEIVE)
919 ? gaim_xfer_get_filename(xfer)
920 : lfilename,
921 COLUMN_SIZE, size_str,
922 COLUMN_REMAINING, _("Waiting for transfer to begin"),
923 COLUMN_DATA, xfer,
924 -1);
925 g_free(lfilename);
926
927 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog->tree));
928
929 g_object_unref(pixbuf);
930
931 g_free(size_str);
932 g_free(remaining_str);
933
934 dialog->num_transfers++;
935
936 ensure_row_selected(dialog);
937 }
938
939 void
940 gaim_gtkxfer_dialog_remove_xfer(GaimGtkXferDialog *dialog,
941 GaimXfer *xfer)
942 {
943 GaimGtkXferUiData *data;
944
945 g_return_if_fail(dialog != NULL);
946 g_return_if_fail(xfer != NULL);
947
948 data = GAIM_GTKXFER(xfer);
949
950 if (data == NULL)
951 return;
952
953 if (!data->in_list)
954 return;
955
956 data->in_list = FALSE;
957
958 gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter);
959
960 dialog->num_transfers--;
961
962 if (dialog->num_transfers == 0 && !dialog->keep_open)
963 gaim_gtkxfer_dialog_hide(dialog);
964 else
965 ensure_row_selected(dialog);
966
967 gaim_xfer_unref(xfer);
968 }
969
970 void
971 gaim_gtkxfer_dialog_cancel_xfer(GaimGtkXferDialog *dialog,
972 GaimXfer *xfer)
973 {
974 GaimGtkXferUiData *data;
975 GdkPixbuf *pixbuf;
976 const gchar *status;
977
978 g_return_if_fail(dialog != NULL);
979 g_return_if_fail(xfer != NULL);
980
981 data = GAIM_GTKXFER(xfer);
982
983 if (data == NULL)
984 return;
985
986 if (!data->in_list)
987 return;
988
989 if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) {
990 gaim_gtkxfer_dialog_remove_xfer(dialog, xfer);
991 return;
992 }
993
994 data = GAIM_GTKXFER(xfer);
995
996 update_detailed_info(dialog, xfer);
997
998 pixbuf = gtk_widget_render_icon(dialog->window,
999 GAIM_STOCK_FILE_CANCELED,
1000 GTK_ICON_SIZE_MENU, NULL);
1001
1002 if (gaim_xfer_is_canceled(xfer))
1003 status = _("Canceled");
1004 else
1005 status = _("Failed");
1006
1007 gtk_list_store_set(dialog->model, &data->iter,
1008 COLUMN_STATUS, pixbuf,
1009 COLUMN_REMAINING, status,
1010 -1);
1011
1012 g_object_unref(pixbuf);
1013
1014 update_buttons(dialog, xfer);
1015 }
1016
1017 void
1018 gaim_gtkxfer_dialog_update_xfer(GaimGtkXferDialog *dialog,
1019 GaimXfer *xfer)
1020 {
1021 GaimGtkXferUiData *data;
1022 char *size_str, *remaining_str;
1023 GtkTreeSelection *selection;
1024 time_t current_time;
1025
1026 g_return_if_fail(dialog != NULL);
1027 g_return_if_fail(xfer != NULL);
1028
1029 if ((data = GAIM_GTKXFER(xfer)) == NULL)
1030 return;
1031
1032 if (data->in_list == FALSE)
1033 return;
1034
1035 current_time = time(NULL);
1036 if (((current_time - data->last_updated_time) == 0) &&
1037 (!gaim_xfer_is_completed(xfer)))
1038 {
1039 /* Don't update the window more than once per second */
1040 return;
1041 }
1042 data->last_updated_time = current_time;
1043
1044 size_str = gaim_str_size_to_units(gaim_xfer_get_size(xfer));
1045 remaining_str = gaim_str_size_to_units(gaim_xfer_get_bytes_remaining(xfer));
1046
1047 gtk_list_store_set(xfer_dialog->model, &data->iter,
1048 COLUMN_PROGRESS, gaim_xfer_get_progress(xfer),
1049 COLUMN_SIZE, size_str,
1050 COLUMN_REMAINING, remaining_str,
1051 -1);
1052
1053 if (gaim_xfer_is_completed(xfer))
1054 {
1055 GdkPixbuf *pixbuf;
1056
1057 pixbuf = gtk_widget_render_icon(dialog->window,
1058 GAIM_STOCK_FILE_DONE,
1059 GTK_ICON_SIZE_MENU, NULL);
1060
1061 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter,
1062 COLUMN_STATUS, pixbuf,
1063 COLUMN_REMAINING, _("Finished"),
1064 -1);
1065
1066 g_object_unref(pixbuf);
1067 }
1068
1069 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(xfer_dialog->tree));
1070
1071 if (xfer == dialog->selected_xfer)
1072 update_detailed_info(xfer_dialog, xfer);
1073
1074 if (gaim_xfer_is_completed(xfer) && dialog->auto_clear)
1075 gaim_gtkxfer_dialog_remove_xfer(dialog, xfer);
1076 else
1077 update_buttons(dialog, xfer);
1078 }
1079
1080 /**************************************************************************
1081 * File Transfer UI Ops
1082 **************************************************************************/
1083 static void
1084 gaim_gtkxfer_new_xfer(GaimXfer *xfer)
1085 {
1086 GaimGtkXferUiData *data;
1087
1088 /* This is where we're setting xfer->ui_data for the first time. */
1089 data = g_new0(GaimGtkXferUiData, 1);
1090 xfer->ui_data = data;
1091 }
1092
1093 static void
1094 gaim_gtkxfer_destroy(GaimXfer *xfer)
1095 {
1096 GaimGtkXferUiData *data;
1097
1098 data = GAIM_GTKXFER(xfer);
1099 if (data) {
1100 g_free(data->name);
1101 g_free(data);
1102 xfer->ui_data = NULL;
1103 }
1104 }
1105
1106 static void
1107 gaim_gtkxfer_add_xfer(GaimXfer *xfer)
1108 {
1109 if (xfer_dialog == NULL)
1110 xfer_dialog = gaim_gtkxfer_dialog_new();
1111
1112 gaim_gtkxfer_dialog_add_xfer(xfer_dialog, xfer);
1113 }
1114
1115 static void
1116 gaim_gtkxfer_update_progress(GaimXfer *xfer, double percent)
1117 {
1118 gaim_gtkxfer_dialog_update_xfer(xfer_dialog, xfer);
1119 }
1120
1121 static void
1122 gaim_gtkxfer_cancel_local(GaimXfer *xfer)
1123 {
1124 if (xfer_dialog)
1125 gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer);
1126 }
1127
1128 static void
1129 gaim_gtkxfer_cancel_remote(GaimXfer *xfer)
1130 {
1131 if (xfer_dialog)
1132 gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer);
1133 }
1134
1135 static GaimXferUiOps ops =
1136 {
1137 gaim_gtkxfer_new_xfer,
1138 gaim_gtkxfer_destroy,
1139 gaim_gtkxfer_add_xfer,
1140 gaim_gtkxfer_update_progress,
1141 gaim_gtkxfer_cancel_local,
1142 gaim_gtkxfer_cancel_remote
1143 };
1144
1145 /**************************************************************************
1146 * GTK+ File Transfer API
1147 **************************************************************************/
1148 void
1149 gaim_gtk_xfers_init(void)
1150 {
1151 gaim_prefs_add_none("/gaim/gtk/filetransfer");
1152 gaim_prefs_add_bool("/gaim/gtk/filetransfer/clear_finished", TRUE);
1153 gaim_prefs_add_bool("/gaim/gtk/filetransfer/keep_open", FALSE);
1154 }
1155
1156 void
1157 gaim_gtk_xfers_uninit(void)
1158 {
1159 if (xfer_dialog != NULL)
1160 gaim_gtkxfer_dialog_destroy(xfer_dialog);
1161 }
1162
1163 void
1164 gaim_set_gtkxfer_dialog(GaimGtkXferDialog *dialog)
1165 {
1166 xfer_dialog = dialog;
1167 }
1168
1169 GaimGtkXferDialog *
1170 gaim_get_gtkxfer_dialog(void)
1171 {
1172 return xfer_dialog;
1173 }
1174
1175 GaimXferUiOps *
1176 gaim_gtk_xfers_get_ui_ops(void)
1177 {
1178 return &ops;
1179 }