* @file gtkft.c GTK+ File Transfer UI * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #define PIDGINXFER(xfer) \ (PidginXferUiData *)(xfer)->ui_data /* the maximum size of files we will try to make a thumbnail for */ #define PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL 10 * 1024 * 1024 PurpleXfer *selected_xfer; GtkWidget *local_user_desc_label; GtkWidget *local_user_label; GtkWidget *remote_user_desc_label; GtkWidget *remote_user_label; GtkWidget *protocol_label; GtkWidget *filename_label; GtkWidget *localfile_label; GtkWidget *time_elapsed_label; GtkWidget *time_remaining_label; GtkWidget *remove_button; time_t last_updated_time; static PidginXferDialog *xfer_dialog = NULL; /************************************************************************** **************************************************************************/ get_xfer_info_strings(PurpleXfer *xfer, char **kbsec, char **time_elapsed, kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0; kb_rem = purple_xfer_get_bytes_remaining(xfer) / 1024.0; elapsed = (xfer->start_time > 0 ? now - xfer->start_time : 0); kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0); *kbsec = g_strdup_printf(_("%.2f KiB/s"), kbps); if (time_elapsed != NULL) if (xfer->start_time > 0) secs_elapsed = now - xfer->start_time; m = (secs_elapsed % 3600) / 60; *time_elapsed = g_strdup_printf("%d:%02d:%02d", h, m, s); *time_elapsed = g_strdup(_("Not started")); if (time_remaining != NULL) { if (purple_xfer_is_completed(xfer)) { *time_remaining = g_strdup(_("Finished")); else if (purple_xfer_is_canceled(xfer)) { *time_remaining = g_strdup(_("Cancelled")); else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps == 0)) { *time_remaining = g_strdup(_("Unknown")); *time_remaining = g_strdup(_("Waiting for transfer to begin")); secs_remaining = (int)(kb_rem / kbps); h = secs_remaining / 3600; m = (secs_remaining % 3600) / 60; *time_remaining = g_strdup_printf("%d:%02d:%02d", h, m, s); update_title_progress(PidginXferDialog *dialog) int num_active_xfers = 0; guint64 total_bytes_xferred = 0; guint64 total_file_size = 0; if (dialog->window == NULL) valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter); /* Find all active transfers */ gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &val); xfer = g_value_get_pointer(&val); if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) { total_bytes_xferred += purple_xfer_get_bytes_sent(xfer); total_file_size += purple_xfer_get_size(xfer); valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->model), &iter); if (num_active_xfers > 0) if (total_file_size > 0) { total_pct = 100 * total_bytes_xferred / total_file_size; title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file", "File Transfers - %d%% of %d files", total_pct, num_active_xfers); gtk_window_set_title(GTK_WINDOW(dialog->window), title); gtk_window_set_title(GTK_WINDOW(dialog->window), _("File Transfers")); update_detailed_info(PidginXferDialog *dialog, PurpleXfer *xfer) char *kbsec, *time_elapsed, *time_remaining; if (dialog == NULL || xfer == NULL) get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining); status = g_strdup_printf("%d%% (%" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT " bytes)", (int)(purple_xfer_get_progress(xfer)*100), purple_xfer_get_bytes_sent(xfer), purple_xfer_get_size(xfer)); if (purple_xfer_is_completed(xfer)) { GdkPixbuf *pixbuf = NULL; pixbuf = gtk_widget_render_icon(xfer_dialog->window, GTK_ICON_SIZE_MENU, NULL); gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter, if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) { gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label), _("<b>Receiving As:</b>")); gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label), _("<b>Receiving From:</b>")); gtk_label_set_markup(GTK_LABEL(dialog->remote_user_desc_label), _("<b>Sending To:</b>")); gtk_label_set_markup(GTK_LABEL(dialog->local_user_desc_label), _("<b>Sending As:</b>")); gtk_label_set_text(GTK_LABEL(dialog->local_user_label), purple_account_get_username(xfer->account)); gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), xfer->who); gtk_label_set_text(GTK_LABEL(dialog->protocol_label), purple_account_get_protocol_name(xfer->account)); if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) { gtk_label_set_text(GTK_LABEL(dialog->filename_label), purple_xfer_get_filename(xfer)); tmp = g_path_get_basename(purple_xfer_get_local_filename(xfer)); utf8 = g_filename_to_utf8(tmp, -1, NULL, NULL, NULL); gtk_label_set_text(GTK_LABEL(dialog->filename_label), utf8); utf8 = g_filename_to_utf8((purple_xfer_get_local_filename(xfer)), -1, NULL, NULL, NULL); gtk_label_set_text(GTK_LABEL(dialog->localfile_label), utf8); gtk_label_set_text(GTK_LABEL(dialog->status_label), status); gtk_label_set_text(GTK_LABEL(dialog->speed_label), kbsec); gtk_label_set_text(GTK_LABEL(dialog->time_elapsed_label), time_elapsed); gtk_label_set_text(GTK_LABEL(dialog->time_remaining_label), gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), purple_xfer_get_progress(xfer)); update_buttons(PidginXferDialog *dialog, PurpleXfer *xfer) if (dialog->selected_xfer == NULL) { gtk_widget_set_sensitive(dialog->expander, FALSE); gtk_widget_set_sensitive(dialog->open_button, FALSE); gtk_widget_set_sensitive(dialog->stop_button, FALSE); gtk_widget_show(dialog->stop_button); gtk_widget_hide(dialog->remove_button); if (dialog->selected_xfer != xfer) if (purple_xfer_is_completed(xfer)) { gtk_widget_hide(dialog->stop_button); gtk_widget_show(dialog->remove_button); if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) { gtk_widget_set_sensitive(dialog->open_button, TRUE); gtk_widget_set_sensitive(dialog->open_button, FALSE); if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) { gtk_widget_set_sensitive(dialog->open_button, TRUE); gtk_widget_set_sensitive (dialog->open_button, FALSE); gtk_widget_set_sensitive(dialog->remove_button, TRUE); } else if (purple_xfer_is_canceled(xfer)) { gtk_widget_hide(dialog->stop_button); gtk_widget_show(dialog->remove_button); gtk_widget_set_sensitive(dialog->open_button, FALSE); gtk_widget_set_sensitive(dialog->remove_button, TRUE); gtk_widget_show(dialog->stop_button); gtk_widget_hide(dialog->remove_button); gtk_widget_set_sensitive(dialog->open_button, FALSE); gtk_widget_set_sensitive(dialog->stop_button, TRUE); ensure_row_selected(PidginXferDialog *dialog) GtkTreeSelection *selection; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree)); if (gtk_tree_selection_get_selected(selection, NULL, &iter)) if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter)) gtk_tree_selection_select_iter(selection, &iter); /************************************************************************** **************************************************************************/ delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) PidginXferDialog *dialog; dialog = (PidginXferDialog *)d; pidgin_xfer_dialog_hide(dialog); toggle_keep_open_cb(GtkWidget *w, PidginXferDialog *dialog) dialog->keep_open = !dialog->keep_open; purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open", toggle_clear_finished_cb(GtkWidget *w, PidginXferDialog *dialog) dialog->auto_clear = !dialog->auto_clear; purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished", selection_changed_cb(GtkTreeSelection *selection, PidginXferDialog *dialog) if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { gtk_widget_set_sensitive(dialog->expander, TRUE); gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &val); xfer = g_value_get_pointer(&val); update_detailed_info(dialog, xfer); dialog->selected_xfer = xfer; gtk_expander_set_expanded(GTK_EXPANDER(dialog->expander), gtk_widget_set_sensitive(dialog->expander, FALSE); dialog->selected_xfer = NULL; update_buttons(dialog, xfer); open_button_cb(GtkButton *button, PidginXferDialog *dialog) wchar_t *wc_filename = g_utf8_to_utf16( purple_xfer_get_local_filename( code = (int) ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL, if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC) purple_notify_error(dialog, NULL, _("There is no application configured to open this type of file."), NULL); purple_notify_error(dialog, NULL, _("An error occurred while opening the file."), NULL); purple_debug_warning("ft", "filename: %s; code: %d\n", purple_xfer_get_local_filename(dialog->selected_xfer), code); const char *filename = purple_xfer_get_local_filename(dialog->selected_xfer); if (purple_running_gnome()) char *escaped = g_shell_quote(filename); command = g_strdup_printf("gnome-open %s", escaped); else if (purple_running_kde()) char *escaped = g_shell_quote(filename); if (purple_str_has_suffix(filename, ".desktop")) command = g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped); command = g_strdup_printf("kfmclient openURL %s", escaped); purple_notify_uri(NULL, filename); if (purple_program_is_valid(command)) if (!g_spawn_command_line_sync(command, NULL, NULL, &exit_status, &error)) tmp = g_strdup_printf(_("Error launching %s: %s"), purple_xfer_get_local_filename(dialog->selected_xfer), purple_notify_error(dialog, NULL, _("Unable to open file."), tmp); char *primary = g_strdup_printf(_("Error running %s"), command); char *secondary = g_strdup_printf(_("Process returned error code %d"), purple_notify_error(dialog, NULL, primary, secondary); remove_button_cb(GtkButton *button, PidginXferDialog *dialog) pidgin_xfer_dialog_remove_xfer(dialog, dialog->selected_xfer); stop_button_cb(GtkButton *button, PidginXferDialog *dialog) purple_xfer_cancel_local(dialog->selected_xfer); close_button_cb(GtkButton *button, PidginXferDialog *dialog) pidgin_xfer_dialog_hide(dialog); /************************************************************************** * Dialog Building Functions **************************************************************************/ setup_tree(PidginXferDialog *dialog) GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; /* Build the tree model */ /* Transfer type, Progress Bar, Filename, Size, Remaining */ model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, /* Create the treeview */ dialog->tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); /* gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); */ g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(selection_changed_cb), dialog); g_object_unref(G_OBJECT(model)); /* Transfer Type column */ renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", COLUMN_STATUS, NULL); gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 25); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); /* Progress bar column */ renderer = gtk_cell_renderer_progress_new(); column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer, "value", COLUMN_PROGRESS, NULL); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer, "text", COLUMN_FILENAME, NULL); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer, "text", COLUMN_SIZE, NULL); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); /* Bytes Remaining column */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Remaining"), renderer, "text", COLUMN_REMAINING, NULL); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree)); make_info_table(PidginXferDialog *dialog) { &dialog->local_user_desc_label, &dialog->local_user_label, NULL }, { &dialog->remote_user_desc_label, &dialog->remote_user_label, NULL }, { &label, &dialog->protocol_label, _("Protocol:") }, { &label, &dialog->filename_label, _("Filename:") }, { &label, &dialog->localfile_label, _("Local File:") }, { &label, &dialog->status_label, _("Status:") }, { &label, &dialog->speed_label, _("Speed:") }, { &label, &dialog->time_elapsed_label, _("Time Elapsed:") }, { &label, &dialog->time_remaining_label, _("Time Remaining:") } /* Setup the initial table */ dialog->table = table = gtk_table_new(G_N_ELEMENTS(labels) + 1, 2, FALSE); gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE); gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE); for (i = 0; i < G_N_ELEMENTS(labels); i++) { g_snprintf(buf, sizeof(buf), "<b>%s</b>", labels[i].desc != NULL ? labels[i].desc : ""); *labels[i].desc_label = label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), buf); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_table_attach(GTK_TABLE(table), label, 0, 1, i, i + 1, *labels[i].val_label = label = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_table_attach(GTK_TABLE(table), label, 1, 2, i, i + 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Setup the progress bar */ dialog->progress = gtk_progress_bar_new(); gtk_table_attach(GTK_TABLE(table), dialog->progress, G_N_ELEMENTS(labels), G_N_ELEMENTS(labels) + 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show(dialog->progress); pidgin_xfer_dialog_new(void) PidginXferDialog *dialog; GtkWidget *vbox1, *vbox2; dialog = g_new0(PidginXferDialog, 1); purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open"); purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished"); dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE); gtk_window_set_default_size(GTK_WINDOW(window), 450, 250); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_win_cb), dialog); /* Create the parent vbox for everything. */ vbox1 = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox1); /* Create the main vbox for top half of the window. */ vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140), /* "Close this window when all transfers finish" */ checkbox = gtk_check_button_new_with_mnemonic( _("Close this window when all transfers _finish")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), g_signal_connect(G_OBJECT(checkbox), "toggled", G_CALLBACK(toggle_keep_open_cb), dialog); gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0); gtk_widget_show(checkbox); /* "Clear finished transfers" */ checkbox = gtk_check_button_new_with_mnemonic( _("C_lear finished transfers")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), g_signal_connect(G_OBJECT(checkbox), "toggled", G_CALLBACK(toggle_clear_finished_cb), dialog); gtk_box_pack_start(GTK_BOX(vbox2), checkbox, FALSE, FALSE, 0); gtk_widget_show(checkbox); /* "Download Details" arrow */ expander = gtk_expander_new_with_mnemonic(_("File transfer _details")); dialog->expander = expander; gtk_box_pack_start(GTK_BOX(vbox2), expander, FALSE, FALSE, 0); gtk_widget_show(expander); gtk_widget_set_sensitive(expander, FALSE); /* Small indent make table fall under GtkExpander's label */ alignment = gtk_alignment_new(1, 0, 1, 1); gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 20, 0); gtk_container_add(GTK_CONTAINER(expander), alignment); gtk_widget_show(alignment); /* The table of information. */ table = make_info_table(dialog); gtk_container_add(GTK_CONTAINER(alignment), table); bbox = gtk_hbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0); #define ADD_BUTTON(b, label, callback, callbackdata) do { \ GtkWidget *button = gtk_button_new_from_stock(label); \ gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); \ g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata); \ gtk_widget_show(button); \ ADD_BUTTON(dialog->open_button, GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog); gtk_widget_set_sensitive(dialog->open_button, FALSE); ADD_BUTTON(dialog->remove_button, GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog); gtk_widget_hide(dialog->remove_button); ADD_BUTTON(dialog->stop_button, GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog); gtk_widget_set_sensitive(dialog->stop_button, FALSE); ADD_BUTTON(dialog->close_button, GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog); g_signal_connect(G_OBJECT(dialog->window), "show", G_CALLBACK(winpidgin_ensure_onscreen), dialog->window); pidgin_xfer_dialog_destroy(PidginXferDialog *dialog) g_return_if_fail(dialog != NULL); purple_notify_close_with_handle(dialog); gtk_widget_destroy(dialog->window); pidgin_xfer_dialog_show(PidginXferDialog *dialog) tmp = pidgin_get_xfer_dialog(); tmp = pidgin_xfer_dialog_new(); pidgin_set_xfer_dialog(tmp); gtk_widget_show(tmp->window); gtk_window_present(GTK_WINDOW(dialog->window)); pidgin_xfer_dialog_hide(PidginXferDialog *dialog) g_return_if_fail(dialog != NULL); purple_notify_close_with_handle(dialog); gtk_widget_hide(dialog->window); pidgin_xfer_dialog_add_xfer(PidginXferDialog *dialog, PurpleXfer *xfer) char *size_str, *remaining_str; g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); pidgin_xfer_dialog_show(dialog); data->last_updated_time = 0; type = purple_xfer_get_type(xfer); size_str = purple_str_size_to_units(purple_xfer_get_size(xfer)); remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer)); pixbuf = gtk_widget_render_icon(dialog->window, (type == PURPLE_XFER_RECEIVE GTK_ICON_SIZE_MENU, NULL); gtk_list_store_append(dialog->model, &data->iter); lfilename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL); gtk_list_store_set(dialog->model, &data->iter, COLUMN_FILENAME, (type == PURPLE_XFER_RECEIVE) ? purple_xfer_get_filename(xfer) COLUMN_REMAINING, _("Waiting for transfer to begin"), gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog->tree)); ensure_row_selected(dialog); update_title_progress(dialog); pidgin_xfer_dialog_remove_xfer(PidginXferDialog *dialog, g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); ensure_row_selected(dialog); update_title_progress(dialog); pidgin_xfer_dialog_cancel_xfer(PidginXferDialog *dialog, g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) { pidgin_xfer_dialog_remove_xfer(dialog, xfer); update_detailed_info(dialog, xfer); update_title_progress(dialog); pixbuf = gtk_widget_render_icon(dialog->window, PIDGIN_STOCK_FILE_CANCELED, GTK_ICON_SIZE_MENU, NULL); if (purple_xfer_is_canceled(xfer)) gtk_list_store_set(dialog->model, &data->iter, COLUMN_REMAINING, status, update_buttons(dialog, xfer); pidgin_xfer_dialog_update_xfer(PidginXferDialog *dialog, char *size_str, *remaining_str; g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); if ((data = PIDGINXFER(xfer)) == NULL) if (data->in_list == FALSE) current_time = time(NULL); if (((current_time - data->last_updated_time) == 0) && (!purple_xfer_is_completed(xfer))) /* Don't update the window more than once per second */ data->last_updated_time = current_time; size_str = purple_str_size_to_units(purple_xfer_get_size(xfer)); remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer)); gtk_list_store_set(xfer_dialog->model, &data->iter, COLUMN_PROGRESS, (gint)(purple_xfer_get_progress(xfer) * 100), COLUMN_REMAINING, remaining_str, if (purple_xfer_is_completed(xfer)) pixbuf = gtk_widget_render_icon(dialog->window, GTK_ICON_SIZE_MENU, NULL); gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter, COLUMN_REMAINING, _("Finished"), update_title_progress(dialog); if (xfer == dialog->selected_xfer) update_detailed_info(xfer_dialog, xfer); if (purple_xfer_is_completed(xfer) && dialog->auto_clear) pidgin_xfer_dialog_remove_xfer(dialog, xfer); update_buttons(dialog, xfer); * If all transfers are finished, and the pref is set, then * close the dialog. Otherwise just exit this function. valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter); gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &val); next = g_value_get_pointer(&val); if (!purple_xfer_is_completed(next)) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(dialog->model), &iter); /* If we got to this point then we know everything is finished */ pidgin_xfer_dialog_hide(dialog); /************************************************************************** **************************************************************************/ pidgin_xfer_new_xfer(PurpleXfer *xfer) /* This is where we're setting xfer->ui_data for the first time. */ data = g_new0(PidginXferUiData, 1); pidgin_xfer_destroy(PurpleXfer *xfer) pidgin_xfer_add_xfer(PurpleXfer *xfer) xfer_dialog = pidgin_xfer_dialog_new(); pidgin_xfer_dialog_add_xfer(xfer_dialog, xfer); pidgin_xfer_update_progress(PurpleXfer *xfer, double percent) pidgin_xfer_dialog_update_xfer(xfer_dialog, xfer); pidgin_xfer_cancel_local(PurpleXfer *xfer) pidgin_xfer_dialog_cancel_xfer(xfer_dialog, xfer); pidgin_xfer_cancel_remote(PurpleXfer *xfer) pidgin_xfer_dialog_cancel_xfer(xfer_dialog, xfer); pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats) purple_debug_info("ft", "creating thumbnail for transfer\n"); if (purple_xfer_get_size(xfer) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL) { pidgin_pixbuf_new_from_file_at_size( purple_xfer_get_local_filename(xfer), 128, 128); gchar **formats_split = g_strsplit(formats, ",", 0); char *option_keys[2] = {NULL, NULL}; char *option_values[2] = {NULL, NULL}; for (i = 0; formats_split[i]; i++) { if (purple_strequal(formats_split[i], "jpeg")) { purple_debug_info("ft", "creating JPEG thumbnail\n"); option_keys[0] = "quality"; } else if (purple_strequal(formats_split[i], "png")) { purple_debug_info("ft", "creating PNG thumbnail\n"); option_keys[0] = "compression"; /* Try the first format given by the PRPL without options */ "creating thumbnail of format %s as demanded by PRPL\n", format = formats_split[0]; gdk_pixbuf_save_to_bufferv(thumbnail, &buffer, &size, format, option_keys, option_values, NULL); gchar *mimetype = g_strdup_printf("image/%s", format); "created thumbnail of %" G_GSIZE_FORMAT " bytes\n", purple_xfer_set_thumbnail(xfer, buffer, size, mimetype); g_object_unref(thumbnail); g_strfreev(formats_split); static PurpleXferUiOps ops = pidgin_xfer_update_progress, pidgin_xfer_cancel_local, pidgin_xfer_cancel_remote, pidgin_xfer_add_thumbnail /************************************************************************** **************************************************************************/ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/filetransfer"); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished", TRUE); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open", FALSE); pidgin_xfers_uninit(void) pidgin_xfer_dialog_destroy(xfer_dialog); pidgin_set_xfer_dialog(PidginXferDialog *dialog) pidgin_get_xfer_dialog(void) pidgin_xfers_get_ui_ops(void)