3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
32 #include "pidginstock.h"
35 #include "gtk3compat.h"
38 # include <shellapi.h>
41 /* the maximum size of files we will try to make a thumbnail for */
42 #define PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL 10 * 1024 * 1024
44 struct _PidginXferDialog
49 GtkWidget
*auto_clear
;
53 PurpleXfer
*selected_xfer
;
60 GtkWidget
*local_user_desc_label
;
61 GtkWidget
*local_user_label
;
62 GtkWidget
*remote_user_desc_label
;
63 GtkWidget
*remote_user_label
;
64 GtkWidget
*protocol_label
;
65 GtkWidget
*filename_label
;
66 GtkWidget
*localfile_label
;
67 GtkWidget
*status_label
;
68 GtkWidget
*speed_label
;
69 GtkWidget
*time_elapsed_label
;
70 GtkWidget
*time_remaining_label
;
75 GtkWidget
*open_button
;
76 GtkWidget
*remove_button
;
77 GtkWidget
*stop_button
;
78 GtkWidget
*close_button
;
81 G_DEFINE_TYPE(PidginXferDialog
, pidgin_xfer_dialog
, GTK_TYPE_DIALOG
);
86 gint64 last_updated_time
;
93 static PidginXferDialog
*xfer_dialog
= NULL
;
106 /**************************************************************************
108 **************************************************************************/
110 get_xfer_info_strings(PurpleXfer
*xfer
, char **kbsec
, char **time_elapsed
,
111 char **time_remaining
)
113 double kb_sent
, kb_rem
;
118 elapsed
= purple_xfer_get_start_time(xfer
);
120 now
= purple_xfer_get_end_time(xfer
);
122 now
= g_get_monotonic_time();
124 elapsed
= now
- elapsed
;
127 kb_sent
= purple_xfer_get_bytes_sent(xfer
) / 1000.0;
128 kb_rem
= purple_xfer_get_bytes_remaining(xfer
) / 1000.0;
129 kbps
= (elapsed
> 0 ? (kb_sent
* G_USEC_PER_SEC
) / elapsed
: 0);
132 *kbsec
= g_strdup_printf(_("%.2f KB/s"), kbps
);
135 if (time_elapsed
!= NULL
)
140 if (purple_xfer_get_start_time(xfer
) > 0) {
141 secs_elapsed
= elapsed
/ G_USEC_PER_SEC
;
143 h
= secs_elapsed
/ 3600;
144 m
= (secs_elapsed
% 3600) / 60;
145 s
= secs_elapsed
% 60;
147 *time_elapsed
= g_strdup_printf("%d:%02d:%02d", h
, m
, s
);
149 *time_elapsed
= g_strdup(_("Not started"));
153 if (time_remaining
!= NULL
) {
154 if (purple_xfer_is_completed(xfer
)) {
155 *time_remaining
= g_strdup(_("Finished"));
157 else if (purple_xfer_is_cancelled(xfer
)) {
158 *time_remaining
= g_strdup(_("Cancelled"));
160 else if (purple_xfer_get_size(xfer
) == 0 || (kb_sent
> 0 && kbps
< 0.001)) {
161 *time_remaining
= g_strdup(_("Unknown"));
163 else if (kb_sent
<= 0) {
164 *time_remaining
= g_strdup(_("Waiting for transfer to begin"));
170 secs_remaining
= (int)(kb_rem
/ kbps
);
172 h
= secs_remaining
/ 3600;
173 m
= (secs_remaining
% 3600) / 60;
174 s
= secs_remaining
% 60;
176 *time_remaining
= g_strdup_printf("%d:%02d:%02d", h
, m
, s
);
182 update_title_progress(PidginXferDialog
*dialog
)
186 int num_active_xfers
= 0;
187 guint64 total_bytes_xferred
= 0;
188 guint64 total_file_size
= 0;
190 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog
->model
), &iter
);
192 /* Find all active transfers */
195 PurpleXfer
*xfer
= NULL
;
198 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog
->model
), &iter
,
200 xfer
= g_value_get_object(&val
);
202 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_STARTED
) {
204 total_bytes_xferred
+= purple_xfer_get_bytes_sent(xfer
);
205 total_file_size
+= purple_xfer_get_size(xfer
);
208 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog
->model
), &iter
);
211 /* Update the title */
212 if (num_active_xfers
> 0)
217 if (total_file_size
> 0) {
218 total_pct
= 100 * total_bytes_xferred
/ total_file_size
;
221 title
= g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
222 "File Transfers - %d%% of %d files",
224 total_pct
, num_active_xfers
);
225 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
228 gtk_window_set_title(GTK_WINDOW(dialog
), _("File Transfers"));
233 update_detailed_info(PidginXferDialog
*dialog
, PurpleXfer
*xfer
)
235 PidginXferUiData
*data
;
236 char *kbsec
, *time_elapsed
, *time_remaining
;
239 if (dialog
== NULL
|| xfer
== NULL
)
242 data
= purple_xfer_get_ui_data(xfer
);
244 get_xfer_info_strings(xfer
, &kbsec
, &time_elapsed
, &time_remaining
);
246 status
= g_strdup_printf("%d%% (%" G_GOFFSET_FORMAT
" of %" G_GOFFSET_FORMAT
" bytes)",
247 (int)(purple_xfer_get_progress(xfer
)*100),
248 purple_xfer_get_bytes_sent(xfer
),
249 purple_xfer_get_size(xfer
));
251 if (purple_xfer_is_completed(xfer
)) {
252 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog
->model
), &data
->iter
,
257 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
) {
258 gtk_label_set_markup(GTK_LABEL(dialog
->local_user_desc_label
),
259 _("<b>Receiving As:</b>"));
260 gtk_label_set_markup(GTK_LABEL(dialog
->remote_user_desc_label
),
261 _("<b>Receiving From:</b>"));
264 gtk_label_set_markup(GTK_LABEL(dialog
->remote_user_desc_label
),
265 _("<b>Sending To:</b>"));
266 gtk_label_set_markup(GTK_LABEL(dialog
->local_user_desc_label
),
267 _("<b>Sending As:</b>"));
270 gtk_label_set_text(GTK_LABEL(dialog
->local_user_label
),
271 purple_account_get_username(purple_xfer_get_account(xfer
)));
272 gtk_label_set_text(GTK_LABEL(dialog
->remote_user_label
), purple_xfer_get_remote_user(xfer
));
273 gtk_label_set_text(GTK_LABEL(dialog
->protocol_label
),
274 purple_account_get_protocol_name(purple_xfer_get_account(xfer
)));
276 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
) {
277 gtk_label_set_text(GTK_LABEL(dialog
->filename_label
),
278 purple_xfer_get_filename(xfer
));
282 tmp
= g_path_get_basename(purple_xfer_get_local_filename(xfer
));
283 utf8
= g_filename_to_utf8(tmp
, -1, NULL
, NULL
, NULL
);
286 gtk_label_set_text(GTK_LABEL(dialog
->filename_label
), utf8
);
290 utf8
= g_filename_to_utf8((purple_xfer_get_local_filename(xfer
)), -1, NULL
, NULL
, NULL
);
291 gtk_label_set_text(GTK_LABEL(dialog
->localfile_label
), utf8
);
294 gtk_label_set_text(GTK_LABEL(dialog
->status_label
), status
);
296 gtk_label_set_text(GTK_LABEL(dialog
->speed_label
), kbsec
);
297 gtk_label_set_text(GTK_LABEL(dialog
->time_elapsed_label
), time_elapsed
);
298 gtk_label_set_text(GTK_LABEL(dialog
->time_remaining_label
),
301 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog
->progress
),
302 purple_xfer_get_progress(xfer
));
305 g_free(time_elapsed
);
306 g_free(time_remaining
);
311 update_buttons(PidginXferDialog
*dialog
, PurpleXfer
*xfer
)
313 if (dialog
->selected_xfer
== NULL
) {
314 gtk_widget_set_sensitive(dialog
->expander
, FALSE
);
315 gtk_widget_set_sensitive(dialog
->open_button
, FALSE
);
316 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
318 gtk_widget_show(dialog
->stop_button
);
319 gtk_widget_hide(dialog
->remove_button
);
324 if (dialog
->selected_xfer
!= xfer
)
327 if (purple_xfer_is_completed(xfer
)) {
328 gtk_widget_hide(dialog
->stop_button
);
329 gtk_widget_show(dialog
->remove_button
);
332 /* If using Win32... */
333 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
) {
334 gtk_widget_set_sensitive(dialog
->open_button
, TRUE
);
336 gtk_widget_set_sensitive(dialog
->open_button
, FALSE
);
339 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
) {
340 gtk_widget_set_sensitive(dialog
->open_button
, TRUE
);
342 gtk_widget_set_sensitive (dialog
->open_button
, FALSE
);
346 gtk_widget_set_sensitive(dialog
->remove_button
, TRUE
);
347 } else if (purple_xfer_is_cancelled(xfer
)) {
348 gtk_widget_hide(dialog
->stop_button
);
349 gtk_widget_show(dialog
->remove_button
);
351 gtk_widget_set_sensitive(dialog
->open_button
, FALSE
);
353 gtk_widget_set_sensitive(dialog
->remove_button
, TRUE
);
355 gtk_widget_show(dialog
->stop_button
);
356 gtk_widget_hide(dialog
->remove_button
);
358 gtk_widget_set_sensitive(dialog
->open_button
, FALSE
);
359 gtk_widget_set_sensitive(dialog
->stop_button
, TRUE
);
364 ensure_row_selected(PidginXferDialog
*dialog
)
367 GtkTreeSelection
*selection
;
369 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->tree
));
371 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
374 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog
->model
), &iter
))
375 gtk_tree_selection_select_iter(selection
, &iter
);
378 /**************************************************************************
380 **************************************************************************/
382 delete_win_cb(GtkWidget
*w
, GdkEventAny
*e
, gpointer d
)
384 PidginXferDialog
*dialog
;
386 dialog
= (PidginXferDialog
*)d
;
388 pidgin_xfer_dialog_hide(dialog
);
394 toggle_keep_open_cb(GtkWidget
*w
, G_GNUC_UNUSED gpointer data
)
396 purple_prefs_set_bool(
397 PIDGIN_PREFS_ROOT
"/filetransfer/keep_open",
398 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w
)));
402 toggle_clear_finished_cb(GtkWidget
*w
, G_GNUC_UNUSED gpointer data
)
404 purple_prefs_set_bool(
405 PIDGIN_PREFS_ROOT
"/filetransfer/clear_finished",
406 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w
)));
410 selection_changed_cb(GtkTreeSelection
*selection
, PidginXferDialog
*dialog
)
413 PurpleXfer
*xfer
= NULL
;
415 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) {
418 gtk_widget_set_sensitive(dialog
->expander
, TRUE
);
421 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog
->model
), &iter
,
423 xfer
= g_value_get_object(&val
);
425 update_detailed_info(dialog
, xfer
);
427 dialog
->selected_xfer
= xfer
;
430 gtk_expander_set_expanded(GTK_EXPANDER(dialog
->expander
),
433 gtk_widget_set_sensitive(dialog
->expander
, FALSE
);
435 dialog
->selected_xfer
= NULL
;
438 update_buttons(dialog
, xfer
);
442 open_button_cb(GtkButton
*button
, PidginXferDialog
*dialog
)
445 /* If using Win32... */
447 wchar_t *wc_filename
= g_utf8_to_utf16(
448 purple_xfer_get_local_filename(
449 dialog
->selected_xfer
),
450 -1, NULL
, NULL
, NULL
);
452 code
= (int) ShellExecuteW(NULL
, NULL
, wc_filename
, NULL
, NULL
,
457 if (code
== SE_ERR_ASSOCINCOMPLETE
|| code
== SE_ERR_NOASSOC
)
459 purple_notify_error(dialog
, NULL
,
460 _("There is no application configured to open this type of file."),
465 purple_notify_error(dialog
, NULL
,
466 _("An error occurred while opening the file."), NULL
, NULL
);
467 purple_debug_warning("xfer", "filename: %s; code: %d\n",
468 purple_xfer_get_local_filename(dialog
->selected_xfer
), code
);
471 const char *filename
= purple_xfer_get_local_filename(dialog
->selected_xfer
);
472 char *command
= NULL
;
473 GError
*error
= NULL
;
475 if (purple_running_gnome())
477 char *escaped
= g_shell_quote(filename
);
478 command
= g_strdup_printf("gnome-open %s", escaped
);
481 else if (purple_running_kde())
483 char *escaped
= g_shell_quote(filename
);
485 if (purple_str_has_suffix(filename
, ".desktop"))
486 command
= g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped
);
488 command
= g_strdup_printf("kfmclient openURL %s", escaped
);
493 gchar
*uri
= g_strdup_printf("file://%s", filename
);
494 purple_notify_uri(NULL
, uri
);
499 if (purple_program_is_valid(command
))
502 if (!g_spawn_command_line_sync(command
, NULL
, NULL
, &exit_status
, &error
))
504 char *tmp
= g_strdup_printf(_("Error launching %s: %s"),
505 purple_xfer_get_local_filename(dialog
->selected_xfer
),
507 purple_notify_error(dialog
, NULL
, _("Unable to open file."), tmp
, NULL
);
511 if (exit_status
!= 0)
513 char *primary
= g_strdup_printf(_("Error running %s"), command
);
514 char *secondary
= g_strdup_printf(_("Process returned error code %d"),
516 purple_notify_error(dialog
, NULL
, primary
, secondary
, NULL
);
525 remove_button_cb(GtkButton
*button
, PidginXferDialog
*dialog
)
527 pidgin_xfer_dialog_remove_xfer(dialog
, dialog
->selected_xfer
);
531 stop_button_cb(GtkButton
*button
, PidginXferDialog
*dialog
)
533 purple_xfer_cancel_local(dialog
->selected_xfer
);
537 close_button_cb(GtkButton
*button
, PidginXferDialog
*dialog
)
539 pidgin_xfer_dialog_hide(dialog
);
543 /**************************************************************************
544 * Dialog Building Functions
545 **************************************************************************/
547 pidgin_xfer_dialog_new(void)
549 return PIDGIN_XFER_DIALOG(g_object_new(PIDGIN_TYPE_XFER_DIALOG
, NULL
));
553 pidgin_xfer_dialog_class_init(PidginXferDialogClass
*klass
)
555 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
557 gtk_widget_class_set_template_from_resource(
558 widget_class
, "/im/pidgin/Pidgin/Xfer/xfer.ui");
560 gtk_widget_class_bind_template_callback(widget_class
, delete_win_cb
);
562 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
564 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
566 gtk_widget_class_bind_template_callback(widget_class
,
567 selection_changed_cb
);
569 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
571 gtk_widget_class_bind_template_callback(widget_class
,
572 toggle_keep_open_cb
);
574 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
576 gtk_widget_class_bind_template_callback(widget_class
,
577 toggle_clear_finished_cb
);
579 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
582 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
583 local_user_desc_label
);
584 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
586 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
587 remote_user_desc_label
);
588 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
590 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
592 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
594 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
596 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
598 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
600 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
602 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
603 time_remaining_label
);
605 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
608 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
610 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
612 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
614 gtk_widget_class_bind_template_child(widget_class
, PidginXferDialog
,
617 gtk_widget_class_bind_template_callback(widget_class
, open_button_cb
);
618 gtk_widget_class_bind_template_callback(widget_class
, remove_button_cb
);
619 gtk_widget_class_bind_template_callback(widget_class
, stop_button_cb
);
620 gtk_widget_class_bind_template_callback(widget_class
, close_button_cb
);
624 pidgin_xfer_dialog_init(PidginXferDialog
*dialog
)
626 gtk_widget_init_template(GTK_WIDGET(dialog
));
628 /* "Close this window when all transfers finish" */
629 gtk_toggle_button_set_active(
630 GTK_TOGGLE_BUTTON(dialog
->keep_open
),
631 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT
632 "/filetransfer/keep_open"));
634 /* "Clear finished transfers" */
635 gtk_toggle_button_set_active(
636 GTK_TOGGLE_BUTTON(dialog
->auto_clear
),
637 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
638 "/filetransfer/clear_finished"));
641 g_signal_connect(G_OBJECT(dialog
), "show",
642 G_CALLBACK(winpidgin_ensure_onscreen
), NULL
);
647 pidgin_xfer_dialog_destroy(PidginXferDialog
*dialog
)
649 g_return_if_fail(dialog
!= NULL
);
651 purple_notify_close_with_handle(dialog
);
653 gtk_widget_destroy(GTK_WIDGET(dialog
));
657 pidgin_xfer_dialog_show(PidginXferDialog
*dialog
)
659 PidginXferDialog
*tmp
;
661 if (dialog
== NULL
) {
662 tmp
= pidgin_get_xfer_dialog();
665 tmp
= pidgin_xfer_dialog_new();
666 pidgin_set_xfer_dialog(tmp
);
669 gtk_widget_show(GTK_WIDGET(tmp
));
671 gtk_window_present(GTK_WINDOW(dialog
));
676 pidgin_xfer_dialog_hide(PidginXferDialog
*dialog
)
678 g_return_if_fail(dialog
!= NULL
);
680 purple_notify_close_with_handle(dialog
);
682 gtk_widget_hide(GTK_WIDGET(dialog
));
686 pidgin_xfer_dialog_add_xfer(PidginXferDialog
*dialog
, PurpleXfer
*xfer
)
688 PidginXferUiData
*data
;
690 const gchar
*icon_name
;
691 char *size_str
, *remaining_str
;
692 char *lfilename
, *utf8
;
694 g_return_if_fail(dialog
!= NULL
);
695 g_return_if_fail(xfer
!= NULL
);
699 data
= purple_xfer_get_ui_data(xfer
);
700 data
->in_list
= TRUE
;
702 pidgin_xfer_dialog_show(dialog
);
704 data
->last_updated_time
= 0;
706 type
= purple_xfer_get_xfer_type(xfer
);
708 size_str
= g_format_size(purple_xfer_get_size(xfer
));
709 remaining_str
= g_format_size(purple_xfer_get_bytes_remaining(xfer
));
711 icon_name
= (type
== PURPLE_XFER_TYPE_RECEIVE
? "go-down" : "go-up");
713 gtk_list_store_append(dialog
->model
, &data
->iter
);
714 lfilename
= g_path_get_basename(purple_xfer_get_local_filename(xfer
));
715 utf8
= g_filename_to_utf8(lfilename
, -1, NULL
, NULL
, NULL
);
718 gtk_list_store_set(dialog
->model
, &data
->iter
, COLUMN_STATUS
, icon_name
,
719 COLUMN_PROGRESS
, 0, COLUMN_FILENAME
,
720 (type
== PURPLE_XFER_TYPE_RECEIVE
)
721 ? purple_xfer_get_filename(xfer
)
723 COLUMN_SIZE
, size_str
, COLUMN_REMAINING
,
724 _("Waiting for transfer to begin"), COLUMN_XFER
,
728 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog
->tree
));
731 g_free(remaining_str
);
733 dialog
->num_transfers
++;
735 ensure_row_selected(dialog
);
736 update_title_progress(dialog
);
740 pidgin_xfer_dialog_remove_xfer(PidginXferDialog
*dialog
,
743 PidginXferUiData
*data
;
745 g_return_if_fail(dialog
!= NULL
);
746 g_return_if_fail(xfer
!= NULL
);
748 data
= purple_xfer_get_ui_data(xfer
);
756 data
->in_list
= FALSE
;
758 gtk_list_store_remove(GTK_LIST_STORE(dialog
->model
), &data
->iter
);
760 dialog
->num_transfers
--;
762 ensure_row_selected(dialog
);
764 update_title_progress(dialog
);
765 g_object_unref(xfer
);
769 pidgin_xfer_dialog_cancel_xfer(PidginXferDialog
*dialog
,
772 PidginXferUiData
*data
;
775 g_return_if_fail(dialog
!= NULL
);
776 g_return_if_fail(xfer
!= NULL
);
778 data
= purple_xfer_get_ui_data(xfer
);
786 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
&&
787 gtk_toggle_button_get_active(
788 GTK_TOGGLE_BUTTON(dialog
->auto_clear
))) {
789 pidgin_xfer_dialog_remove_xfer(dialog
, xfer
);
793 data
= purple_xfer_get_ui_data(xfer
);
795 update_detailed_info(dialog
, xfer
);
796 update_title_progress(dialog
);
798 if (purple_xfer_is_cancelled(xfer
))
799 status
= _("Cancelled");
801 status
= _("Failed");
803 gtk_list_store_set(dialog
->model
, &data
->iter
,
804 COLUMN_STATUS
, "dialog-error",
805 COLUMN_REMAINING
, status
,
808 update_buttons(dialog
, xfer
);
812 pidgin_xfer_dialog_update_xfer(PidginXferDialog
*dialog
,
815 PidginXferUiData
*data
;
816 char *size_str
, *remaining_str
;
821 g_return_if_fail(dialog
!= NULL
);
822 g_return_if_fail(xfer
!= NULL
);
824 if ((data
= purple_xfer_get_ui_data(xfer
)) == NULL
)
827 if (data
->in_list
== FALSE
)
830 current_time
= g_get_monotonic_time();
831 if (((current_time
- data
->last_updated_time
) < G_USEC_PER_SEC
) &&
832 (!purple_xfer_is_completed(xfer
)))
834 /* Don't update the window more than once per second */
837 data
->last_updated_time
= current_time
;
839 size_str
= g_format_size(purple_xfer_get_size(xfer
));
840 remaining_str
= g_format_size(purple_xfer_get_bytes_remaining(xfer
));
842 gtk_list_store_set(xfer_dialog
->model
, &data
->iter
,
843 COLUMN_PROGRESS
, (gint
)(purple_xfer_get_progress(xfer
) * 100),
844 COLUMN_SIZE
, size_str
,
845 COLUMN_REMAINING
, remaining_str
,
849 g_free(remaining_str
);
851 if (purple_xfer_is_completed(xfer
))
853 gtk_list_store_set(GTK_LIST_STORE(xfer_dialog
->model
), &data
->iter
,
855 COLUMN_REMAINING
, _("Finished"),
859 update_title_progress(dialog
);
860 if (xfer
== dialog
->selected_xfer
)
861 update_detailed_info(xfer_dialog
, xfer
);
863 if (purple_xfer_is_completed(xfer
) &&
864 gtk_toggle_button_get_active(
865 GTK_TOGGLE_BUTTON(dialog
->auto_clear
))) {
866 pidgin_xfer_dialog_remove_xfer(dialog
, xfer
);
868 update_buttons(dialog
, xfer
);
872 * If all transfers are finished, and the pref is set, then
873 * close the dialog. Otherwise just exit this function.
875 if (!gtk_toggle_button_get_active(
876 GTK_TOGGLE_BUTTON(dialog
->keep_open
))) {
880 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog
->model
), &iter
);
887 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog
->model
), &iter
,
889 next
= g_value_get_object(&val
);
891 if (!purple_xfer_is_completed(next
))
894 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog
->model
), &iter
);
897 /* If we got to this point then we know everything is finished */
898 pidgin_xfer_dialog_hide(dialog
);
901 /**************************************************************************
902 * File Transfer UI Ops
903 **************************************************************************/
905 pidgin_xfer_new_xfer(PurpleXfer
*xfer
)
907 PidginXferUiData
*data
;
909 /* This is where we're setting xfer's "ui_data" for the first time. */
910 data
= g_new0(PidginXferUiData
, 1);
911 purple_xfer_set_ui_data(xfer
, data
);
915 pidgin_xfer_destroy(PurpleXfer
*xfer
)
917 PidginXferUiData
*data
;
919 data
= purple_xfer_get_ui_data(xfer
);
923 purple_xfer_set_ui_data(xfer
, NULL
);
928 pidgin_xfer_add_xfer(PurpleXfer
*xfer
)
930 if (xfer_dialog
== NULL
)
931 xfer_dialog
= pidgin_xfer_dialog_new();
933 pidgin_xfer_dialog_add_xfer(xfer_dialog
, xfer
);
937 pidgin_xfer_update_progress(PurpleXfer
*xfer
, double percent
)
939 pidgin_xfer_dialog_update_xfer(xfer_dialog
, xfer
);
943 pidgin_xfer_cancel_local(PurpleXfer
*xfer
)
946 pidgin_xfer_dialog_cancel_xfer(xfer_dialog
, xfer
);
950 pidgin_xfer_cancel_remote(PurpleXfer
*xfer
)
953 pidgin_xfer_dialog_cancel_xfer(xfer_dialog
, xfer
);
957 pidgin_xfer_add_thumbnail(PurpleXfer
*xfer
, const gchar
*formats
)
959 purple_debug_info("xfer", "creating thumbnail for transfer\n");
961 if (purple_xfer_get_size(xfer
) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL
) {
962 GdkPixbuf
*thumbnail
=
963 pidgin_pixbuf_new_from_file_at_size(
964 purple_xfer_get_local_filename(xfer
), 128, 128);
967 gchar
**formats_split
= g_strsplit(formats
, ",", 0);
968 gchar
*buffer
= NULL
;
970 char *option_keys
[2] = {NULL
, NULL
};
971 char *option_values
[2] = {NULL
, NULL
};
973 gchar
*format
= NULL
;
975 for (i
= 0; formats_split
[i
]; i
++) {
976 if (purple_strequal(formats_split
[i
], "jpeg")) {
977 purple_debug_info("xfer", "creating JPEG thumbnail\n");
978 option_keys
[0] = "quality";
979 option_values
[0] = "90";
982 } else if (purple_strequal(formats_split
[i
], "png")) {
983 purple_debug_info("xfer", "creating PNG thumbnail\n");
984 option_keys
[0] = "compression";
985 option_values
[0] = "9";
991 /* Try the first format given by the protocol without options */
992 if (format
== NULL
) {
993 purple_debug_info("xfer",
994 "creating thumbnail of format %s as demanded by protocol\n",
996 format
= formats_split
[0];
999 gdk_pixbuf_save_to_bufferv(thumbnail
, &buffer
, &size
, format
,
1000 option_keys
, option_values
, NULL
);
1003 gchar
*mimetype
= g_strdup_printf("image/%s", format
);
1004 purple_debug_info("xfer",
1005 "created thumbnail of %" G_GSIZE_FORMAT
" bytes\n",
1007 purple_xfer_set_thumbnail(xfer
, buffer
, size
, mimetype
);
1011 g_object_unref(thumbnail
);
1012 g_strfreev(formats_split
);
1017 static PurpleXferUiOps ops
=
1019 pidgin_xfer_new_xfer
,
1020 pidgin_xfer_destroy
,
1021 pidgin_xfer_add_xfer
,
1022 pidgin_xfer_update_progress
,
1023 pidgin_xfer_cancel_local
,
1024 pidgin_xfer_cancel_remote
,
1028 pidgin_xfer_add_thumbnail
1031 /**************************************************************************
1032 * GTK+ File Transfer API
1033 **************************************************************************/
1035 pidgin_xfers_init(void)
1037 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/filetransfer");
1038 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/filetransfer/clear_finished", TRUE
);
1039 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/filetransfer/keep_open", FALSE
);
1043 pidgin_xfers_uninit(void)
1045 if (xfer_dialog
!= NULL
)
1046 pidgin_xfer_dialog_destroy(xfer_dialog
);
1050 pidgin_set_xfer_dialog(PidginXferDialog
*dialog
)
1052 xfer_dialog
= dialog
;
1056 pidgin_get_xfer_dialog(void)
1062 pidgin_xfers_get_ui_ops(void)