| |
1 /** |
| |
2 * @file gtkft.c GTK+ File Transfer UI |
| |
3 * @ingroup gtkui |
| |
4 * |
| |
5 * pidgin |
| |
6 * |
| |
7 * Pidgin 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 "pidgin.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 "pidginstock.h" |
| |
39 #include "gtkutils.h" |
| |
40 |
| |
41 #define PIDGINXFER(xfer) \ |
| |
42 (PidginXferUiData *)(xfer)->ui_data |
| |
43 |
| |
44 struct _PidginXferDialog |
| |
45 { |
| |
46 gboolean keep_open; |
| |
47 gboolean auto_clear; |
| |
48 |
| |
49 gint num_transfers; |
| |
50 |
| |
51 PurpleXfer *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 } PidginXferUiData; |
| |
93 |
| |
94 static PidginXferDialog *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(PurpleXfer *xfer, char **kbsec, char **time_elapsed, |
| |
113 char **time_remaining) |
| |
114 { |
| |
115 PidginXferUiData *data; |
| |
116 double kb_sent, kb_rem; |
| |
117 double kbps = 0.0; |
| |
118 time_t elapsed, now; |
| |
119 |
| |
120 data = PIDGINXFER(xfer); |
| |
121 |
| |
122 if (xfer->end_time != 0) |
| |
123 now = xfer->end_time; |
| |
124 else |
| |
125 now = time(NULL); |
| |
126 |
| |
127 kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0; |
| |
128 kb_rem = purple_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 (purple_xfer_get_size(xfer) == 0) { |
| |
159 *time_remaining = g_strdup(_("Unknown")); |
| |
160 } |
| |
161 else if (purple_xfer_is_completed(xfer)) { |
| |
162 *time_remaining = g_strdup(_("Finished")); |
| |
163 } |
| |
164 else if (purple_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(PidginXferDialog *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 PurpleXfer *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 (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) { |
| |
210 num_active_xfers++; |
| |
211 total_bytes_xferred += purple_xfer_get_bytes_sent(xfer); |
| |
212 total_file_size += purple_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(PidginXferDialog *dialog, PurpleXfer *xfer) |
| |
239 { |
| |
240 PidginXferUiData *data; |
| |
241 char *kbsec, *time_elapsed, *time_remaining; |
| |
242 char *status, *utf8; |
| |
243 |
| |
244 if (dialog == NULL || xfer == NULL) |
| |
245 return; |
| |
246 |
| |
247 data = PIDGINXFER(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)(purple_xfer_get_progress(xfer)*100), |
| |
253 (unsigned long)purple_xfer_get_bytes_sent(xfer), |
| |
254 (unsigned long)purple_xfer_get_size(xfer)); |
| |
255 |
| |
256 if (purple_xfer_is_completed(xfer)) { |
| |
257 |
| |
258 GdkPixbuf *pixbuf = NULL; |
| |
259 |
| |
260 pixbuf = gtk_widget_render_icon(xfer_dialog->window, |
| |
261 PIDGIN_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 (purple_xfer_get_type(xfer) == PURPLE_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 purple_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 purple_account_get_protocol_name(xfer->account)); |
| |
289 |
| |
290 if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) { |
| |
291 gtk_label_set_text(GTK_LABEL(dialog->filename_label), |
| |
292 purple_xfer_get_filename(xfer)); |
| |
293 } else { |
| |
294 char *tmp; |
| |
295 |
| |
296 tmp = g_path_get_basename(purple_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((purple_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 purple_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(PidginXferDialog *dialog, PurpleXfer *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 (purple_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 (purple_xfer_get_type(xfer) == PURPLE_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 (purple_xfer_get_type(xfer) == PURPLE_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 (purple_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(PidginXferDialog *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 PidginXferDialog *dialog; |
| |
409 |
| |
410 dialog = (PidginXferDialog *)d; |
| |
411 |
| |
412 pidginxfer_dialog_hide(dialog); |
| |
413 |
| |
414 return TRUE; |
| |
415 } |
| |
416 |
| |
417 static void |
| |
418 toggle_keep_open_cb(GtkWidget *w, PidginXferDialog *dialog) |
| |
419 { |
| |
420 dialog->keep_open = !dialog->keep_open; |
| |
421 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open", |
| |
422 dialog->keep_open); |
| |
423 } |
| |
424 |
| |
425 static void |
| |
426 toggle_clear_finished_cb(GtkWidget *w, PidginXferDialog *dialog) |
| |
427 { |
| |
428 dialog->auto_clear = !dialog->auto_clear; |
| |
429 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished", |
| |
430 dialog->auto_clear); |
| |
431 } |
| |
432 |
| |
433 static void |
| |
434 selection_changed_cb(GtkTreeSelection *selection, PidginXferDialog *dialog) |
| |
435 { |
| |
436 GtkTreeIter iter; |
| |
437 PurpleXfer *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, PidginXferDialog *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 purple_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 purple_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 purple_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 purple_notify_error(dialog, NULL, |
| |
502 _("An error occurred while opening the file."), NULL); |
| |
503 purple_debug_warning("ft", "filename: %s; code: %d\n", |
| |
504 purple_xfer_get_local_filename(dialog->selected_xfer), code); |
| |
505 } |
| |
506 #else |
| |
507 const char *filename = purple_xfer_get_local_filename(dialog->selected_xfer); |
| |
508 char *command = NULL; |
| |
509 char *tmp = NULL; |
| |
510 GError *error = NULL; |
| |
511 |
| |
512 if (purple_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 (purple_running_kde()) |
| |
519 { |
| |
520 char *escaped = g_shell_quote(filename); |
| |
521 |
| |
522 if (purple_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 purple_notify_uri(NULL, filename); |
| |
531 return; |
| |
532 } |
| |
533 |
| |
534 if (purple_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 purple_xfer_get_local_filename(dialog->selected_xfer), |
| |
541 error->message); |
| |
542 purple_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 purple_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, PidginXferDialog *dialog) |
| |
560 { |
| |
561 } |
| |
562 |
| |
563 static void |
| |
564 resume_button_cb(GtkButton *button, PidginXferDialog *dialog) |
| |
565 { |
| |
566 } |
| |
567 |
| |
568 static void |
| |
569 remove_button_cb(GtkButton *button, PidginXferDialog *dialog) |
| |
570 { |
| |
571 pidginxfer_dialog_remove_xfer(dialog, dialog->selected_xfer); |
| |
572 } |
| |
573 |
| |
574 static void |
| |
575 stop_button_cb(GtkButton *button, PidginXferDialog *dialog) |
| |
576 { |
| |
577 purple_xfer_cancel_local(dialog->selected_xfer); |
| |
578 } |
| |
579 |
| |
580 static void |
| |
581 close_button_cb(GtkButton *button, PidginXferDialog *dialog) |
| |
582 { |
| |
583 pidginxfer_dialog_hide(dialog); |
| |
584 } |
| |
585 |
| |
586 |
| |
587 /************************************************************************** |
| |
588 * Dialog Building Functions |
| |
589 **************************************************************************/ |
| |
590 static GtkWidget * |
| |
591 setup_tree(PidginXferDialog *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 = pidgin_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(PidginXferDialog *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), PIDGIN_HIG_BOX_SPACE); |
| |
707 gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_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 PidginXferDialog * |
| |
742 pidginxfer_dialog_new(void) |
| |
743 { |
| |
744 PidginXferDialog *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(PidginXferDialog, 1); |
| |
755 dialog->keep_open = |
| |
756 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open"); |
| |
757 dialog->auto_clear = |
| |
758 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/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), PIDGIN_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, PIDGIN_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, PIDGIN_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), PIDGIN_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(winpidgin_ensure_onscreen), dialog->window); |
| |
885 #endif |
| |
886 |
| |
887 return dialog; |
| |
888 } |
| |
889 |
| |
890 void |
| |
891 pidginxfer_dialog_destroy(PidginXferDialog *dialog) |
| |
892 { |
| |
893 g_return_if_fail(dialog != NULL); |
| |
894 |
| |
895 purple_notify_close_with_handle(dialog); |
| |
896 |
| |
897 gtk_widget_destroy(dialog->window); |
| |
898 |
| |
899 g_free(dialog); |
| |
900 } |
| |
901 |
| |
902 void |
| |
903 pidginxfer_dialog_show(PidginXferDialog *dialog) |
| |
904 { |
| |
905 PidginXferDialog *tmp; |
| |
906 |
| |
907 if (dialog == NULL) { |
| |
908 tmp = pidgin_get_xfer_dialog(); |
| |
909 |
| |
910 if (tmp == NULL) { |
| |
911 tmp = pidginxfer_dialog_new(); |
| |
912 pidgin_set_xfer_dialog(tmp); |
| |
913 } |
| |
914 |
| |
915 gtk_widget_show(tmp->window); |
| |
916 } else { |
| |
917 gtk_widget_show(dialog->window); |
| |
918 } |
| |
919 } |
| |
920 |
| |
921 void |
| |
922 pidginxfer_dialog_hide(PidginXferDialog *dialog) |
| |
923 { |
| |
924 g_return_if_fail(dialog != NULL); |
| |
925 |
| |
926 purple_notify_close_with_handle(dialog); |
| |
927 |
| |
928 gtk_widget_hide(dialog->window); |
| |
929 } |
| |
930 |
| |
931 void |
| |
932 pidginxfer_dialog_add_xfer(PidginXferDialog *dialog, PurpleXfer *xfer) |
| |
933 { |
| |
934 PidginXferUiData *data; |
| |
935 PurpleXferType 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 purple_xfer_ref(xfer); |
| |
944 |
| |
945 data = PIDGINXFER(xfer); |
| |
946 data->in_list = TRUE; |
| |
947 |
| |
948 pidginxfer_dialog_show(dialog); |
| |
949 |
| |
950 data->last_updated_time = 0; |
| |
951 |
| |
952 type = purple_xfer_get_type(xfer); |
| |
953 |
| |
954 size_str = purple_str_size_to_units(purple_xfer_get_size(xfer)); |
| |
955 remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer)); |
| |
956 |
| |
957 pixbuf = gtk_widget_render_icon(dialog->window, |
| |
958 (type == PURPLE_XFER_RECEIVE |
| |
959 ? PIDGIN_STOCK_DOWNLOAD |
| |
960 : PIDGIN_STOCK_UPLOAD), |
| |
961 GTK_ICON_SIZE_MENU, NULL); |
| |
962 |
| |
963 gtk_list_store_append(dialog->model, &data->iter); |
| |
964 lfilename = g_path_get_basename(purple_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 == PURPLE_XFER_RECEIVE) |
| |
972 ? purple_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 pidginxfer_dialog_remove_xfer(PidginXferDialog *dialog, |
| |
995 PurpleXfer *xfer) |
| |
996 { |
| |
997 PidginXferUiData *data; |
| |
998 |
| |
999 g_return_if_fail(dialog != NULL); |
| |
1000 g_return_if_fail(xfer != NULL); |
| |
1001 |
| |
1002 data = PIDGINXFER(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 purple_xfer_unref(xfer); |
| |
1020 } |
| |
1021 |
| |
1022 void |
| |
1023 pidginxfer_dialog_cancel_xfer(PidginXferDialog *dialog, |
| |
1024 PurpleXfer *xfer) |
| |
1025 { |
| |
1026 PidginXferUiData *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 = PIDGINXFER(xfer); |
| |
1034 |
| |
1035 if (data == NULL) |
| |
1036 return; |
| |
1037 |
| |
1038 if (!data->in_list) |
| |
1039 return; |
| |
1040 |
| |
1041 if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) { |
| |
1042 pidginxfer_dialog_remove_xfer(dialog, xfer); |
| |
1043 return; |
| |
1044 } |
| |
1045 |
| |
1046 data = PIDGINXFER(xfer); |
| |
1047 |
| |
1048 update_detailed_info(dialog, xfer); |
| |
1049 update_title_progress(dialog); |
| |
1050 |
| |
1051 pixbuf = gtk_widget_render_icon(dialog->window, |
| |
1052 PIDGIN_STOCK_FILE_CANCELED, |
| |
1053 GTK_ICON_SIZE_MENU, NULL); |
| |
1054 |
| |
1055 if (purple_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 pidginxfer_dialog_update_xfer(PidginXferDialog *dialog, |
| |
1072 PurpleXfer *xfer) |
| |
1073 { |
| |
1074 PidginXferUiData *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 = PIDGINXFER(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 (!purple_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 = purple_str_size_to_units(purple_xfer_get_size(xfer)); |
| |
1100 remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer)); |
| |
1101 |
| |
1102 gtk_list_store_set(xfer_dialog->model, &data->iter, |
| |
1103 COLUMN_PROGRESS, purple_xfer_get_progress(xfer), |
| |
1104 COLUMN_SIZE, size_str, |
| |
1105 COLUMN_REMAINING, remaining_str, |
| |
1106 -1); |
| |
1107 |
| |
1108 if (purple_xfer_is_completed(xfer)) |
| |
1109 { |
| |
1110 GdkPixbuf *pixbuf; |
| |
1111 |
| |
1112 pixbuf = gtk_widget_render_icon(dialog->window, |
| |
1113 PIDGIN_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 (purple_xfer_is_completed(xfer) && dialog->auto_clear) |
| |
1131 pidginxfer_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 PurpleXfer *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 (!purple_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 pidginxfer_dialog_hide(dialog); |
| |
1161 } |
| |
1162 |
| |
1163 /************************************************************************** |
| |
1164 * File Transfer UI Ops |
| |
1165 **************************************************************************/ |
| |
1166 static void |
| |
1167 pidginxfer_new_xfer(PurpleXfer *xfer) |
| |
1168 { |
| |
1169 PidginXferUiData *data; |
| |
1170 |
| |
1171 /* This is where we're setting xfer->ui_data for the first time. */ |
| |
1172 data = g_new0(PidginXferUiData, 1); |
| |
1173 xfer->ui_data = data; |
| |
1174 } |
| |
1175 |
| |
1176 static void |
| |
1177 pidginxfer_destroy(PurpleXfer *xfer) |
| |
1178 { |
| |
1179 PidginXferUiData *data; |
| |
1180 |
| |
1181 data = PIDGINXFER(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 pidginxfer_add_xfer(PurpleXfer *xfer) |
| |
1191 { |
| |
1192 if (xfer_dialog == NULL) |
| |
1193 xfer_dialog = pidginxfer_dialog_new(); |
| |
1194 |
| |
1195 pidginxfer_dialog_add_xfer(xfer_dialog, xfer); |
| |
1196 } |
| |
1197 |
| |
1198 static void |
| |
1199 pidginxfer_update_progress(PurpleXfer *xfer, double percent) |
| |
1200 { |
| |
1201 pidginxfer_dialog_update_xfer(xfer_dialog, xfer); |
| |
1202 } |
| |
1203 |
| |
1204 static void |
| |
1205 pidginxfer_cancel_local(PurpleXfer *xfer) |
| |
1206 { |
| |
1207 if (xfer_dialog) |
| |
1208 pidginxfer_dialog_cancel_xfer(xfer_dialog, xfer); |
| |
1209 } |
| |
1210 |
| |
1211 static void |
| |
1212 pidginxfer_cancel_remote(PurpleXfer *xfer) |
| |
1213 { |
| |
1214 if (xfer_dialog) |
| |
1215 pidginxfer_dialog_cancel_xfer(xfer_dialog, xfer); |
| |
1216 } |
| |
1217 |
| |
1218 static PurpleXferUiOps ops = |
| |
1219 { |
| |
1220 pidginxfer_new_xfer, |
| |
1221 pidginxfer_destroy, |
| |
1222 pidginxfer_add_xfer, |
| |
1223 pidginxfer_update_progress, |
| |
1224 pidginxfer_cancel_local, |
| |
1225 pidginxfer_cancel_remote |
| |
1226 }; |
| |
1227 |
| |
1228 /************************************************************************** |
| |
1229 * GTK+ File Transfer API |
| |
1230 **************************************************************************/ |
| |
1231 void |
| |
1232 pidgin_xfers_init(void) |
| |
1233 { |
| |
1234 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/filetransfer"); |
| |
1235 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished", TRUE); |
| |
1236 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open", FALSE); |
| |
1237 } |
| |
1238 |
| |
1239 void |
| |
1240 pidgin_xfers_uninit(void) |
| |
1241 { |
| |
1242 if (xfer_dialog != NULL) |
| |
1243 pidginxfer_dialog_destroy(xfer_dialog); |
| |
1244 } |
| |
1245 |
| |
1246 void |
| |
1247 pidgin_set_xfer_dialog(PidginXferDialog *dialog) |
| |
1248 { |
| |
1249 xfer_dialog = dialog; |
| |
1250 } |
| |
1251 |
| |
1252 PidginXferDialog * |
| |
1253 pidgin_get_xfer_dialog(void) |
| |
1254 { |
| |
1255 return xfer_dialog; |
| |
1256 } |
| |
1257 |
| |
1258 PurpleXferUiOps * |
| |
1259 pidgin_xfers_get_ui_ops(void) |
| |
1260 { |
| |
1261 return &ops; |
| |
1262 } |