# HG changeset patch # User Christian Hammond # Date 1044651320 0 # Node ID d03fcb3f4be2e95316b0228a95564ad0948ddfd2 # Parent a2f2a717fdf279b1957ee087a58b51604130d68e [gaim-migrate @ 4833] We have a brand new file transfer dialog! committer: Tailor Script diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/Makefile.am --- a/src/Makefile.am Fri Feb 07 20:54:14 2003 +0000 +++ b/src/Makefile.am Fri Feb 07 20:55:20 2003 +0000 @@ -22,6 +22,8 @@ ft.h \ gaim.h \ gaim-socket.h \ + gaim-disclosure.c \ + gaim-disclosure.h \ gaimrc.c \ gtkcellrendererprogress.c \ gtkcellrendererprogress.h \ diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/buddy.c --- a/src/buddy.c Fri Feb 07 20:54:14 2003 +0000 +++ b/src/buddy.c Fri Feb 07 20:55:20 2003 +0000 @@ -2442,6 +2442,22 @@ } } +static void +show_xfer_dialog(GtkMenuItem *item, gpointer user_data) +{ + struct gaim_gtkxfer_dialog *dialog; + + dialog = gaim_get_gtkxfer_dialog(); + + if (dialog == NULL) { + dialog = gaim_gtkxfer_dialog_new(); + + gaim_set_gtkxfer_dialog(dialog); + } + + gaim_gtkxfer_dialog_show(dialog); +} + void make_buddy_list() { @@ -2535,7 +2551,7 @@ G_CALLBACK(show_prefs), NULL, 'p', GDK_CONTROL_MASK, "Ctl+P"); gaim_new_item_from_stock(menu, _("_File Transfers..."), GTK_STOCK_REVERT_TO_SAVED, - G_CALLBACK(gaim_gtkxfer_dialog_show), NULL, 0, 0, NULL); + G_CALLBACK(show_xfer_dialog), NULL, 0, 0, NULL); gaim_separator(menu); diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/ft.c --- a/src/ft.c Fri Feb 07 20:54:14 2003 +0000 +++ b/src/ft.c Fri Feb 07 20:55:20 2003 +0000 @@ -506,7 +506,7 @@ if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); - if (xfer->completed) + if (gaim_xfer_is_completed(xfer)) gaim_xfer_end(xfer); } @@ -618,9 +618,6 @@ fclose(xfer->dest_fp); xfer->dest_fp = NULL; } - - /* Delete the transfer. */ - gaim_xfer_destroy(xfer); } void @@ -653,9 +650,6 @@ ui_ops->cancel(xfer); xfer->bytes_remaining = 0; - - /* Delete the transfer. */ - gaim_xfer_destroy(xfer); } void diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/gaim-disclosure.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim-disclosure.c Fri Feb 07 20:55:20 2003 +0000 @@ -0,0 +1,303 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Iain Holmes + * + * Copyright 2002 Iain Holmes + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "gaim-disclosure.h" + +#ifdef ENABLE_NLS +# include +# define _(x) gettext(x) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define N_(String) (String) +# define _(x) (x) +#endif + +static GtkCheckButtonClass *parent_class = NULL; + +struct _GaimDisclosurePrivate { + GtkWidget *container; + char *shown; + char *hidden; + + guint32 expand_id; + GtkExpanderStyle style; + + int expander_size; + int direction; +}; + +static void +finalize (GObject *object) +{ + GaimDisclosure *disclosure; + + disclosure = GAIM_DISCLOSURE (object); + if (disclosure->priv == NULL) { + return; + } + + g_free (disclosure->priv->hidden); + g_free (disclosure->priv->shown); + + if (disclosure->priv->container != NULL) { + g_object_unref (G_OBJECT (disclosure->priv->container)); + } + + g_free (disclosure->priv); + disclosure->priv = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +get_x_y (GaimDisclosure *disclosure, + int *x, + int *y, + GtkStateType *state_type) +{ + GtkCheckButton *check_button; + int indicator_size = 0; + int focus_width; + int focus_pad; + gboolean interior_focus; + GtkWidget *widget = GTK_WIDGET (disclosure); + GtkBin *bin = GTK_BIN (disclosure); + int width; + + if (GTK_WIDGET_VISIBLE (disclosure) && + GTK_WIDGET_MAPPED (disclosure)) { + check_button = GTK_CHECK_BUTTON (disclosure); + + gtk_widget_style_get (widget, + "interior_focus", &interior_focus, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + *state_type = GTK_WIDGET_STATE (widget); + if ((*state_type != GTK_STATE_NORMAL) && + (*state_type != GTK_STATE_PRELIGHT)) { + *state_type = GTK_STATE_NORMAL; + } + + if (bin->child) { + width = bin->child->allocation.x - widget->allocation.x - (2 * GTK_CONTAINER (widget)->border_width); + } else { + width = widget->allocation.width; + } + + *x = widget->allocation.x + (width) / 2; + *y = widget->allocation.y + widget->allocation.height / 2; + + if (interior_focus == FALSE) { + *x += focus_width + focus_pad; + } + + *state_type = GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE ? GTK_STATE_NORMAL : GTK_WIDGET_STATE (widget); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { + *x = widget->allocation.x + widget->allocation.width - (indicator_size + *x - widget->allocation.x); + } + } else { + *x = 0; + *y = 0; + *state_type = GTK_STATE_NORMAL; + } +} + +static gboolean +expand_collapse_timeout (gpointer data) +{ + GtkWidget *widget = data; + GaimDisclosure *disclosure = data; + GtkStateType state_type; + int x, y; + + gdk_window_invalidate_rect (widget->window, &widget->allocation, TRUE); + get_x_y (disclosure, &x, &y, &state_type); + + gtk_paint_expander (widget->style, + widget->window, + state_type, + &widget->allocation, + widget, + "disclosure", + x, y, + disclosure->priv->style); + + disclosure->priv->style += disclosure->priv->direction; + if ((int) disclosure->priv->style > (int) GTK_EXPANDER_EXPANDED) { + disclosure->priv->style = GTK_EXPANDER_EXPANDED; + + if (disclosure->priv->container != NULL) { + gtk_widget_show (disclosure->priv->container); + } + + g_object_set (G_OBJECT (disclosure), + "label", disclosure->priv->hidden, + NULL); + return FALSE; + } else if ((int) disclosure->priv->style < (int) GTK_EXPANDER_COLLAPSED) { + disclosure->priv->style = GTK_EXPANDER_COLLAPSED; + + if (disclosure->priv->container != NULL) { + gtk_widget_hide (disclosure->priv->container); + } + + g_object_set (G_OBJECT (disclosure), + "label", disclosure->priv->shown, + NULL); + + return FALSE; + } else { + return TRUE; + } +} + +static void +do_animation (GaimDisclosure *disclosure, + gboolean opening) +{ + if (disclosure->priv->expand_id > 0) { + g_source_remove(disclosure->priv->expand_id); + } + + disclosure->priv->direction = opening ? 1 : -1; + disclosure->priv->expand_id = g_timeout_add (50, expand_collapse_timeout, disclosure); +} + +static void +toggled (GtkToggleButton *tb) +{ + GaimDisclosure *disclosure; + + disclosure = GAIM_DISCLOSURE (tb); + do_animation (disclosure, gtk_toggle_button_get_active (tb)); + + if (disclosure->priv->container == NULL) { + return; + } +} + +static void +draw_indicator (GtkCheckButton *check, + GdkRectangle *area) +{ + GtkWidget *widget = GTK_WIDGET (check); + GaimDisclosure *disclosure = GAIM_DISCLOSURE (check); + GtkStateType state_type; + int x, y; + + get_x_y (disclosure, &x, &y, &state_type); + gtk_paint_expander (widget->style, + widget->window, + state_type, + area, + widget, + "treeview", + x, y, + disclosure->priv->style); +} + +static void +class_init (GaimDisclosureClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkCheckButtonClass *button_class; + GtkToggleButtonClass *toggle_class; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + button_class = GTK_CHECK_BUTTON_CLASS (klass); + toggle_class = GTK_TOGGLE_BUTTON_CLASS (klass); + + toggle_class->toggled = toggled; + button_class->draw_indicator = draw_indicator; + + object_class->finalize = finalize; + + parent_class = g_type_class_peek_parent (klass); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("expander_size", + _("Expander Size"), + _("Size of the expander arrow"), + 0, G_MAXINT, + 10, G_PARAM_READABLE)); +} + +static void +init (GaimDisclosure *disclosure) +{ + disclosure->priv = g_new0 (GaimDisclosurePrivate, 1); + disclosure->priv->expander_size = 10; +} + +GType +gaim_disclosure_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + GTypeInfo info = { + sizeof (GaimDisclosureClass), + NULL, NULL, (GClassInitFunc) class_init, NULL, NULL, + sizeof (GaimDisclosure), 0, (GInstanceInitFunc) init + }; + + type = g_type_register_static (GTK_TYPE_CHECK_BUTTON, "GaimDisclosure", &info, 0); + } + + return type; +} + +GtkWidget * +gaim_disclosure_new (const char *shown, + const char *hidden) +{ + GaimDisclosure *disclosure; + + disclosure = g_object_new (gaim_disclosure_get_type (), "label", shown, NULL); + + disclosure->priv->shown = g_strdup (shown); + disclosure->priv->hidden = g_strdup (hidden); + return GTK_WIDGET (disclosure); +} + +void +gaim_disclosure_set_container (GaimDisclosure *disclosure, + GtkWidget *container) +{ + g_object_ref (G_OBJECT (container)); + disclosure->priv->container = container; +} diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/gaim-disclosure.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim-disclosure.h Fri Feb 07 20:55:20 2003 +0000 @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Iain Holmes + * + * Copyright 2002 Iain Holmes + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GAIM_DISCLOSURE_H__ +#define __GAIM_DISCLOSURE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif + +#define GAIM_DISCLOSURE_TYPE (gaim_disclosure_get_type ()) +#define GAIM_DISCLOSURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIM_DISCLOSURE_TYPE, GaimDisclosure)) +#define GAIM_DISCLOSURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIM_DISCLOSURE_TYPE, GaimDisclosureClass)) +#define IS_GAIM_DISCLOSURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIM_DISCLOSURE_TYPE)) +#define IS_GAIM_DISCLOSURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIM_DISCLOSURE_TYPE)) + +typedef struct _GaimDisclosure GaimDisclosure; +typedef struct _GaimDisclosureClass GaimDisclosureClass; +typedef struct _GaimDisclosurePrivate GaimDisclosurePrivate; + +struct _GaimDisclosure { + GtkCheckButton parent; + + GaimDisclosurePrivate *priv; +}; + +struct _GaimDisclosureClass { + GtkCheckButtonClass parent_class; +}; + +GType gaim_disclosure_get_type (void); +GtkWidget *gaim_disclosure_new (const char *shown, const char *hidden); +void gaim_disclosure_set_container (GaimDisclosure *disclosure, + GtkWidget *container); + +#ifdef __cplusplus +} +#endif + +#endif diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/gtkft.c --- a/src/gtkft.c Fri Feb 07 20:54:14 2003 +0000 +++ b/src/gtkft.c Fri Feb 07 20:55:20 2003 +0000 @@ -27,12 +27,44 @@ #include #include #include "gtkcellrendererprogress.h" +#include "gaim-disclosure.h" + +#define GAIM_GTKXFER_UI_DATA(xfer) \ + (struct gaim_gtkxfer_ui_data *)(xfer)->ui_data struct gaim_gtkxfer_dialog { + gboolean keep_open; + gboolean auto_clear; + + gint num_transfers; + + struct gaim_xfer *selected_xfer; + GtkWidget *window; GtkWidget *tree; GtkListStore *model; + + GtkWidget *disclosure; + + GtkWidget *table; + + GtkWidget *user_desc_label; + GtkWidget *user_label; + GtkWidget *filename_label; + GtkWidget *status_label; + GtkWidget *speed_label; + GtkWidget *time_elapsed_label; + GtkWidget *time_remaining_label; + + GtkWidget *progress; + + /* Buttons */ + GtkWidget *open_button; + GtkWidget *pause_button; + GtkWidget *resume_button; + GtkWidget *remove_button; + GtkWidget *stop_button; }; struct gaim_gtkxfer_ui_data @@ -49,89 +81,350 @@ enum { COLUMN_STATUS = 0, - COLUMN_USER, + COLUMN_PROGRESS, COLUMN_FILENAME, - COLUMN_PROGRESS, COLUMN_SIZE, COLUMN_REMAINING, - COLUMN_ESTIMATE, - COLUMN_SPEED, + COLUMN_DATA, NUM_COLUMNS }; +/************************************************************************** + * Utility Functions + **************************************************************************/ +static char * +get_size_string(size_t size) +{ + static const char *size_str[4] = { "bytes", "KB", "MB", "GB" }; + float size_mag; + int size_index = 0; + + if (size == -1) { + return g_strdup(_("Calculating...")); + } + else if (size == 0) { + return g_strdup(_("Unknown.")); + } + else { + size_mag = (float)size; + + while ((size_index < 4) && (size_mag > 1024)) { + size_mag /= 1024; + size_index++; + } + + return g_strdup_printf("%.3g %s", size_mag, size_str[size_index]); + } +} + +static void +get_xfer_info_strings(struct gaim_xfer *xfer, + char **kbsec, char **time_elapsed, + char **time_remaining) +{ + struct gaim_gtkxfer_ui_data *data; + double kb_sent, kb_rem; + double kbps = 0.0; + time_t elapsed, now; + + data = GAIM_GTKXFER_UI_DATA(xfer); + + now = time(NULL); + + kb_sent = gaim_xfer_get_bytes_sent(xfer) / 1024.0; + kb_rem = gaim_xfer_get_bytes_remaining(xfer) / 1024.0; + elapsed = (now - data->start_time); + kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0); + + if (kbsec != NULL) { + if (gaim_xfer_is_completed(xfer)) + *kbsec = g_strdup(""); + else + *kbsec = g_strdup_printf(_("%.2f KB/s"), kbps); + } + + if (time_elapsed != NULL) { + int h, m, s; + int secs_elapsed; + + secs_elapsed = now - data->start_time; + + h = secs_elapsed / 3600; + m = (secs_elapsed % 3600) / 60; + s = secs_elapsed % 60; + + *time_elapsed = g_strdup_printf("%d:%02d:%02d", h, m, s); + } + + if (time_remaining != NULL) { + if (gaim_xfer_get_size(xfer) == 0) { + *time_remaining = g_strdup("Unknown"); + } + else if (gaim_xfer_is_completed(xfer)) { + *time_remaining = g_strdup("Done."); + } + else { + int h, m, s; + int secs_remaining; + + secs_remaining = (int)(kb_rem / kbps); + + h = secs_remaining / 3600; + m = (secs_remaining % 3600) / 60; + s = secs_remaining % 60; + + *time_remaining = g_strdup_printf("%d:%02d:%02d", h, m, s); + } + } +} + +static void +update_detailed_info(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer) +{ + struct gaim_gtkxfer_ui_data *data; + char *kbsec, *time_elapsed, *time_remaining; + char *status; + + if (dialog == NULL || xfer == NULL) + return; + + data = GAIM_GTKXFER_UI_DATA(xfer); + + get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining); + + status = g_strdup_printf("%ld of %ld", + (unsigned long)gaim_xfer_get_bytes_sent(xfer), + (unsigned long)gaim_xfer_get_size(xfer)); + + if (gaim_xfer_get_size(xfer) >= 0 && + gaim_xfer_is_completed(xfer)) { + + GdkPixbuf *pixbuf = NULL; + + pixbuf = gtk_widget_render_icon(xfer_dialog->window, + GAIM_STOCK_FILE_DONE, + GTK_ICON_SIZE_MENU, NULL); + + gtk_list_store_set(GTK_LIST_STORE(xfer_dialog->model), &data->iter, + COLUMN_STATUS, pixbuf, + -1); + + g_object_unref(pixbuf); + } + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) + gtk_label_set_markup(GTK_LABEL(dialog->user_desc_label), + _("Receiving From:")); + else + gtk_label_set_markup(GTK_LABEL(dialog->user_desc_label), + _("Sending To:")); + + gtk_label_set_text(GTK_LABEL(dialog->user_label), xfer->who); + + gtk_label_set_text(GTK_LABEL(dialog->filename_label), + gaim_xfer_get_filename(xfer)); + + 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), + time_remaining); + + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), + gaim_xfer_get_progress(xfer)); + + g_free(kbsec); + g_free(time_elapsed); + g_free(time_remaining); + g_free(status); +} + +static void +update_buttons(struct gaim_gtkxfer_dialog *dialog, struct gaim_xfer *xfer) +{ + if (dialog->selected_xfer == NULL) { + gtk_widget_set_sensitive(dialog->disclosure, FALSE); + gtk_widget_set_sensitive(dialog->open_button, FALSE); + gtk_widget_set_sensitive(dialog->pause_button, FALSE); + gtk_widget_set_sensitive(dialog->resume_button, FALSE); + gtk_widget_set_sensitive(dialog->stop_button, FALSE); + + gtk_widget_show(dialog->stop_button); + gtk_widget_hide(dialog->remove_button); + + return; + } + + if (dialog->selected_xfer != xfer) + return; + + if (gaim_xfer_is_completed(xfer)) { + gtk_widget_hide(dialog->stop_button); + gtk_widget_show(dialog->remove_button); + + /* TODO: gtk_widget_set_sensitive(dialog->open_button, TRUE); */ + gtk_widget_set_sensitive(dialog->pause_button, FALSE); + gtk_widget_set_sensitive(dialog->resume_button, FALSE); + + gtk_widget_set_sensitive(dialog->remove_button, TRUE); + } + else { + gtk_widget_show(dialog->stop_button); + gtk_widget_hide(dialog->remove_button); + + gtk_widget_set_sensitive(dialog->open_button, FALSE); + + /* TODO: If the transfer can pause, blah blah */ + gtk_widget_set_sensitive(dialog->pause_button, FALSE); + gtk_widget_set_sensitive(dialog->resume_button, FALSE); + gtk_widget_set_sensitive(dialog->stop_button, TRUE); + } +} + +static void +ensure_row_selected(struct gaim_gtkxfer_dialog *dialog) +{ + GtkTreeIter iter; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree)); + + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter)) + gtk_tree_selection_select_iter(selection, &iter); +} + +/************************************************************************** + * Callbacks + **************************************************************************/ static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) { - gaim_gtkxfer_dialog_hide(); + struct gaim_gtkxfer_dialog *dialog; + + dialog = (struct gaim_gtkxfer_dialog *)d; + + gaim_gtkxfer_dialog_hide(dialog); return TRUE; } static void -close_win_cb(GtkButton *button, gpointer user_data) +toggle_keep_open_cb(GtkWidget *w, struct gaim_gtkxfer_dialog *dialog) { - gaim_gtkxfer_dialog_hide(); + dialog->keep_open = !dialog->keep_open; +} + +static void +toggle_clear_finished_cb(GtkWidget *w, struct gaim_gtkxfer_dialog *dialog) +{ + dialog->auto_clear = !dialog->auto_clear; } -static struct gaim_gtkxfer_dialog * -build_xfer_dialog(void) +static void +selection_changed_cb(GtkTreeSelection *selection, + struct gaim_gtkxfer_dialog *dialog) { - struct gaim_gtkxfer_dialog *dialog; - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *frame; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { + GValue val = {0, }; + struct gaim_xfer *xfer; + + gtk_widget_set_sensitive(dialog->disclosure, 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; + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->disclosure), + FALSE); + + gtk_widget_set_sensitive(dialog->disclosure, FALSE); + + dialog->selected_xfer = NULL; + } + + update_buttons(dialog, xfer); +} + +static void +open_button_cb(GtkButton *button, struct gaim_gtkxfer_dialog *dialog) +{ +} + +static void +pause_button_cb(GtkButton *button, struct gaim_gtkxfer_dialog *dialog) +{ +} + +static void +resume_button_cb(GtkButton *button, struct gaim_gtkxfer_dialog *dialog) +{ +} + +static void +remove_button_cb(GtkButton *button, struct gaim_gtkxfer_dialog *dialog) +{ + gaim_gtkxfer_dialog_remove_xfer(dialog, dialog->selected_xfer); +} + +static void +stop_button_cb(GtkButton *button, struct gaim_gtkxfer_dialog *dialog) +{ + gaim_xfer_cancel(dialog->selected_xfer); +} + +/************************************************************************** + * Dialog Building Functions + **************************************************************************/ +static GtkWidget * +setup_tree(struct gaim_gtkxfer_dialog *dialog) +{ GtkWidget *sw; - GtkWidget *sep; - GtkWidget *button; GtkWidget *tree; - GtkTreeViewColumn *column; GtkListStore *model; GtkCellRenderer *renderer; - GtkSizeGroup *sg; - - dialog = g_malloc0(sizeof(struct gaim_gtkxfer_dialog)); - - /* Create the window. */ - dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_role(GTK_WINDOW(window), "file transfer"); - gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); - gtk_window_set_default_size(GTK_WINDOW(window), 550, 250); - gtk_container_border_width(GTK_CONTAINER(window), 0); - gtk_widget_realize(window); - - g_signal_connect(G_OBJECT(window), "delete_event", - G_CALLBACK(delete_win_cb), dialog); - - /* Create the parent vbox for everything. */ - vbox = gtk_vbox_new(FALSE, 6); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); - gtk_container_add(GTK_CONTAINER(window), vbox); - gtk_widget_show(vbox); + GtkTreeViewColumn *column; + GtkTreeSelection *selection; /* Create the scrolled window. */ sw = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_show(sw); /* Build the tree model */ - /* Transfer type, User, Filename, Progress Bar, Size, Remaining */ - dialog->model = model = gtk_list_store_new(NUM_COLUMNS, - GDK_TYPE_PIXBUF, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_LONG, G_TYPE_LONG, - G_TYPE_STRING, G_TYPE_STRING); + /* Transfer type, Progress Bar, Filename, Size, Remaining */ + model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER); + dialog->model = model; /* 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); - gtk_tree_selection_set_mode( - gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), - GTK_SELECTION_MULTIPLE); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); + /* gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); */ + gtk_widget_show(tree); + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(selection_changed_cb), dialog); + g_object_unref(G_OBJECT(model)); @@ -147,10 +440,10 @@ gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); - /* User column */ - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("User"), renderer, - "text", COLUMN_USER, NULL); + /* Progress bar column */ + renderer = gtk_cell_renderer_progress_new(); + column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer, + "percentage", 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); @@ -161,13 +454,6 @@ 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, - "percentage", 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); - /* File Size column */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer, @@ -184,74 +470,421 @@ gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree)); - /* ETA column */ - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("ETA"), - renderer, "text", COLUMN_ESTIMATE, NULL); - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); - - /* Speed column */ - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Speed"), - renderer, "text", COLUMN_SPEED, 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)); - gtk_container_add(GTK_CONTAINER(sw), tree); gtk_widget_show(tree); - /* Create the outer frame for the scrolled window. */ - frame = gtk_frame_new(NULL), - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(frame), sw); - gtk_widget_show(frame); + return sw; +} + +static GtkWidget * +make_info_table(struct gaim_gtkxfer_dialog *dialog) +{ + GtkWidget *table; + GtkWidget *label; + GtkWidget *sep; + int i; + + struct + { + GtkWidget **desc_label; + GtkWidget **val_label; + const char *desc; + + } labels[] = + { + { &dialog->user_desc_label, &dialog->user_label, NULL }, + { &label, &dialog->filename_label, _("Filename:") }, + { &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(8, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 6); + gtk_table_set_col_spacings(GTK_TABLE(table), 6); + + /* Setup the labels */ + for (i = 0; i < sizeof(labels) / sizeof(*labels); i++) { + GtkWidget *label; + char buf[256]; + + printf("Adding %s\n", labels[i].desc); + g_snprintf(buf, sizeof(buf), "%s", 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, + GTK_FILL, 0, 0, 0); + gtk_widget_show(label); + + *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); + gtk_widget_show(label); + } + + /* Setup the progress bar */ + dialog->progress = gtk_progress_bar_new(); + gtk_table_attach(GTK_TABLE(table), dialog->progress, 0, 2, 6, 7, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(dialog->progress); + + sep = gtk_hseparator_new(); + gtk_table_attach(GTK_TABLE(table), sep, 0, 2, 7, 8, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show(sep); + + return table; +} - gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); +struct gaim_gtkxfer_dialog * +gaim_gtkxfer_dialog_new(void) +{ + struct gaim_gtkxfer_dialog *dialog; + GtkWidget *window; + GtkWidget *vbox1, *vbox2; + GtkWidget *bbox; + GtkWidget *sw; + GtkWidget *sep; + GtkWidget *button; + GtkWidget *disclosure; + GtkWidget *table; + GtkWidget *checkbox; + + dialog = g_new0(struct gaim_gtkxfer_dialog, 1); + dialog->keep_open = FALSE; + dialog->auto_clear = TRUE; + + /* Create the window. */ + dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(window), "file transfer"); +// gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE); +// gtk_window_set_default_size(GTK_WINDOW(window), 390, 400); +// gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_container_border_width(GTK_CONTAINER(window), 12); + gtk_widget_realize(window); + + 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, 12); + gtk_container_add(GTK_CONTAINER(window), vbox1); + gtk_widget_show(vbox1); + + /* Create the main vbox for top half of the window. */ + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0); + gtk_widget_show(vbox2); + + /* Setup the listbox */ + sw = setup_tree(dialog); + gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0); + + /* "Keep the dialog open" */ + checkbox = gtk_check_button_new_with_mnemonic( + _("_Keep the dialog open")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), + dialog->keep_open); + 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( + _("_Clear finished transfers")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), + dialog->auto_clear); + 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 */ + disclosure = gaim_disclosure_new(_("Show download details"), + _("Hide download details")); + dialog->disclosure = disclosure; + gtk_box_pack_start(GTK_BOX(vbox2), disclosure, FALSE, FALSE, 0); + gtk_widget_show(disclosure); + + gtk_widget_set_sensitive(disclosure, FALSE); + +#if 0 + g_signal_connect(G_OBJECT(disclosure), "toggled", + G_CALLBACK(toggle_details_cb), dialog); +#endif /* Separator */ sep = gtk_hseparator_new(); - gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), sep, FALSE, FALSE, 0); gtk_widget_show(sep); - /* Now the hbox for the buttons */ - hbox = gtk_hbox_new(FALSE, 6); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 6); - gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); + /* The table of information. */ + table = make_info_table(dialog); + gtk_box_pack_start(GTK_BOX(vbox2), table, TRUE, TRUE, 0); + + /* Setup the disclosure for the table. */ + gaim_disclosure_set_container(GAIM_DISCLOSURE(disclosure), table); - sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); + /* Now the button box for the buttons */ + bbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(bbox), 8); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), + GTK_BUTTONBOX_END); + gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0); + gtk_widget_show(bbox); - /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + /* Open button */ + button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + gtk_widget_set_sensitive(button, FALSE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + dialog->open_button = button; + g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(close_win_cb), NULL); - gtk_size_group_add_widget(sg, button); - gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + G_CALLBACK(open_button_cb), dialog); + + /* Pause button */ + button = gtk_button_new_with_mnemonic(_("_Pause")); + gtk_widget_set_sensitive(button, FALSE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); gtk_widget_show(button); + dialog->pause_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pause_button_cb), dialog); + + /* Resume button */ + button = gtk_button_new_with_mnemonic(_("_Resume")); + gtk_widget_set_sensitive(button, FALSE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + dialog->resume_button = button; -#if 0 - /* Cancel button */ - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_size_group_add_widget(sg, button); - gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(resume_button_cb), dialog); + + /* Remove button */ + button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_hide(button); + dialog->remove_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(remove_button_cb), dialog); + + /* Stop button */ + button = gtk_button_new_from_stock(GTK_STOCK_STOP); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); gtk_widget_show(button); -#endif + gtk_widget_set_sensitive(button, FALSE); + dialog->stop_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(stop_button_cb), dialog); return dialog; } -static void -gaim_gtkxfer_destroy(struct gaim_xfer *xfer) +void +gaim_gtkxfer_dialog_destroy(struct gaim_gtkxfer_dialog *dialog) +{ + if (dialog == NULL) + return; + + gtk_widget_destroy(dialog->window); + + g_free(dialog); +} + +void +gaim_gtkxfer_dialog_show(struct gaim_gtkxfer_dialog *dialog) +{ + if (dialog == NULL) + return; + + gtk_widget_show(dialog->window); +} + +void +gaim_gtkxfer_dialog_hide(struct gaim_gtkxfer_dialog *dialog) +{ + if (dialog == NULL) + return; + + gtk_widget_hide(dialog->window); +} + +void +gaim_gtkxfer_dialog_add_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer) +{ + struct gaim_gtkxfer_ui_data *data; + GaimXferType type; + GdkPixbuf *pixbuf; + char *size_str, *remaining_str; + + if (dialog == NULL || xfer == NULL) + return; + + data = GAIM_GTKXFER_UI_DATA(xfer); + + gaim_gtkxfer_dialog_show(dialog); + + data->start_time = time(NULL); + + type = gaim_xfer_get_type(xfer); + + size_str = get_size_string(gaim_xfer_get_size(xfer)); + remaining_str = get_size_string(gaim_xfer_get_bytes_remaining(xfer)); + + pixbuf = gtk_widget_render_icon(dialog->window, + (type == GAIM_XFER_RECEIVE + ? GAIM_STOCK_DOWNLOAD + : GAIM_STOCK_UPLOAD), + GTK_ICON_SIZE_MENU, NULL); + + gtk_list_store_append(dialog->model, &data->iter); + gtk_list_store_set(dialog->model, &data->iter, + COLUMN_STATUS, pixbuf, + COLUMN_PROGRESS, 0.0, + COLUMN_FILENAME, gaim_xfer_get_filename(xfer), + COLUMN_SIZE, size_str, + COLUMN_REMAINING, remaining_str, + COLUMN_DATA, xfer, + -1); + + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog->tree)); + + g_object_unref(pixbuf); + + g_free(size_str); + g_free(remaining_str); + + dialog->num_transfers++; + + ensure_row_selected(dialog); +} + +void +gaim_gtkxfer_dialog_remove_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer) { struct gaim_gtkxfer_ui_data *data; - data = xfer->ui_data; + if (dialog == NULL || xfer == NULL) + return; + + data = GAIM_GTKXFER_UI_DATA(xfer); + + if (data == NULL) + return; + + gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); + + g_free(data->name); + g_free(data); + + xfer->ui_data = NULL; + + dialog->num_transfers--; + + if (dialog->num_transfers == 0 && !dialog->keep_open) + gaim_gtkxfer_dialog_hide(dialog); + else + ensure_row_selected(dialog); +} + +void +gaim_gtkxfer_dialog_cancel_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer) +{ + struct gaim_gtkxfer_ui_data *data; +#if 0 + GdkPixbuf *pixbuf; +#endif + + if (dialog == NULL || xfer == NULL) + return; + + data = GAIM_GTKXFER_UI_DATA(xfer); if (data == NULL) return; + + gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); + + g_free(data->name); + g_free(data); + + xfer->ui_data = NULL; + + dialog->num_transfers--; + + if (dialog->num_transfers == 0 && !dialog->keep_open) + gaim_gtkxfer_dialog_hide(dialog); + +#if 0 + data = GAIM_GTKXFER_UI_DATA(xfer); + + pixbuf = gtk_widget_render_icon(dialog->window, + GAIM_STOCK_FILE_CANCELED, + GTK_ICON_SIZE_MENU, NULL); + + gtk_list_store_set(dialog->model, &data->iter, + COLUMN_STATUS, pixbuf, + -1); + + g_object_unref(pixbuf); +#endif +} + +void +gaim_gtkxfer_dialog_update_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer) +{ + struct gaim_gtkxfer_ui_data *data; + char *size_str, *remaining_str; + GtkTreeSelection *selection; + + if (dialog == NULL || xfer == NULL) + return; + + data = GAIM_GTKXFER_UI_DATA(xfer); + + size_str = get_size_string(gaim_xfer_get_size(xfer)); + remaining_str = get_size_string(gaim_xfer_get_bytes_remaining(xfer)); + + gtk_list_store_set(xfer_dialog->model, &data->iter, + COLUMN_PROGRESS, gaim_xfer_get_progress(xfer), + COLUMN_SIZE, size_str, + COLUMN_REMAINING, remaining_str, + -1); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(xfer_dialog->tree)); + + if (xfer == dialog->selected_xfer) + update_detailed_info(xfer_dialog, xfer); + + if (gaim_xfer_is_completed(xfer) && dialog->auto_clear) + gaim_gtkxfer_dialog_remove_xfer(dialog, xfer); + else + update_buttons(dialog, xfer); +} + +/************************************************************************** + * File Transfer UI Ops + **************************************************************************/ +static void +gaim_gtkxfer_destroy(struct gaim_xfer *xfer) +{ + gaim_gtkxfer_dialog_remove_xfer(xfer_dialog, xfer); } static gboolean @@ -268,7 +901,7 @@ struct gaim_xfer *xfer = (struct gaim_xfer *)user_data; struct gaim_gtkxfer_ui_data *data; - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; + data = GAIM_GTKXFER_UI_DATA(xfer); gaim_xfer_request_denied(xfer); @@ -281,7 +914,7 @@ { struct gaim_gtkxfer_ui_data *data; - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; + data = GAIM_GTKXFER_UI_DATA(xfer); gaim_xfer_request_accepted(xfer, data->name); @@ -299,7 +932,7 @@ { struct gaim_gtkxfer_ui_data *data; - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; + data = GAIM_GTKXFER_UI_DATA(xfer); g_free(data->name); data->name = NULL; @@ -318,7 +951,7 @@ const char *name; xfer = (struct gaim_xfer *)user_data; - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; + data = GAIM_GTKXFER_UI_DATA(xfer); name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(data->filesel)); @@ -413,25 +1046,12 @@ static void gaim_gtkxfer_ask_recv(struct gaim_xfer *xfer) { - static const char *size_str[4] = { "bytes", "KB", "MB", "GB" }; - char *buf; - char *size_buf; - float size_mag; + char *buf, *size_buf; size_t size; - int size_index = 0; size = gaim_xfer_get_size(xfer); - size_mag = (float)size; - while ((size_index < 4) && (size_mag > 1024)) { - size_mag /= 1024; - size_index++; - } - - if (size == -1) - size_buf = g_strdup_printf(_("Unknown size")); - else - size_buf = g_strdup_printf("%.3g %s", size_mag, size_str[size_index]); + size_buf = get_size_string(size); buf = g_strdup_printf(_("%s wants to send you %s (%s)"), xfer->who, gaim_xfer_get_filename(xfer), size_buf); @@ -467,125 +1087,30 @@ static void gaim_gtkxfer_add_xfer(struct gaim_xfer *xfer) { - struct gaim_gtkxfer_ui_data *data; - GaimXferType type; - GdkPixbuf *pixbuf; - - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; - - gaim_gtkxfer_dialog_show(); - - data->start_time = time(NULL); - - type = gaim_xfer_get_type(xfer); + if (xfer_dialog == NULL) + xfer_dialog = gaim_gtkxfer_dialog_new(); - pixbuf = gtk_widget_render_icon(xfer_dialog->window, - (type == GAIM_XFER_RECEIVE - ? GAIM_STOCK_DOWNLOAD - : GAIM_STOCK_UPLOAD), - GTK_ICON_SIZE_MENU, NULL); - - gtk_list_store_append(xfer_dialog->model, &data->iter); - gtk_list_store_set(xfer_dialog->model, &data->iter, - COLUMN_STATUS, pixbuf, - COLUMN_USER, xfer->who, - COLUMN_FILENAME, gaim_xfer_get_filename(xfer), - COLUMN_PROGRESS, 0.0, - COLUMN_SIZE, gaim_xfer_get_size(xfer), - COLUMN_REMAINING, gaim_xfer_get_bytes_remaining(xfer), - COLUMN_ESTIMATE, NULL, - COLUMN_SPEED, NULL, - -1); - - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(xfer_dialog->tree)); - - g_object_unref(pixbuf); + gaim_gtkxfer_dialog_add_xfer(xfer_dialog, xfer); } static void gaim_gtkxfer_update_progress(struct gaim_xfer *xfer, double percent) { - struct gaim_gtkxfer_ui_data *data; - time_t now; - char speed_buf[256]; - char estimate_buf[256]; - double kb_sent, kb_rem; - double kbps = 0.0; - time_t elapsed; - int secs_remaining; - - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; - - now = time(NULL); - kb_sent = gaim_xfer_get_bytes_sent(xfer) / 1024.0; - elapsed = (now - data->start_time); - kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0); - - g_snprintf(speed_buf, sizeof(speed_buf), - _("%.2f KB/s"), kbps); - - if (gaim_xfer_get_size(xfer) == 0) { - strncpy(estimate_buf, _("Unknown"), sizeof(estimate_buf)); - } - else { - kb_rem = gaim_xfer_get_bytes_remaining(xfer) / 1024.0; - secs_remaining = (int)(kb_rem / kbps); - - if (secs_remaining <= 0) { - GdkPixbuf *pixbuf = NULL; + gaim_gtkxfer_dialog_update_xfer(xfer_dialog, xfer); - *speed_buf = '\0'; - strncpy(estimate_buf, _("Done."), sizeof(estimate_buf)); - - pixbuf = gtk_widget_render_icon(xfer_dialog->window, - GAIM_STOCK_FILE_DONE, - GTK_ICON_SIZE_MENU, NULL); - - gtk_list_store_set(xfer_dialog->model, &data->iter, - COLUMN_STATUS, pixbuf, - -1); - - g_object_unref(pixbuf); - } - else { - int h = secs_remaining / 3600; - int m = (secs_remaining % 3600) / 60; - int s = secs_remaining % 60; - - g_snprintf(estimate_buf, sizeof(estimate_buf), - _("%d:%02d:%02d"), h, m, s); - } - } - - gtk_list_store_set(xfer_dialog->model, &data->iter, - COLUMN_REMAINING, gaim_xfer_get_bytes_remaining(xfer), - COLUMN_PROGRESS, percent, - COLUMN_ESTIMATE, estimate_buf, - COLUMN_SPEED, speed_buf, - COLUMN_SIZE, gaim_xfer_get_size(xfer), - -1); + /* See if it's removed. */ + if (xfer->ui_data == NULL) + gaim_xfer_destroy(xfer); } static void gaim_gtkxfer_cancel(struct gaim_xfer *xfer) { - struct gaim_gtkxfer_ui_data *data; - GdkPixbuf *pixbuf; - - if (xfer_dialog == NULL) - return; - - data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data; + gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer); - pixbuf = gtk_widget_render_icon(xfer_dialog->window, - GAIM_STOCK_FILE_CANCELED, - GTK_ICON_SIZE_MENU, NULL); - - gtk_list_store_set(xfer_dialog->model, &data->iter, - COLUMN_STATUS, pixbuf, - -1); - - g_object_unref(pixbuf); + /* See if it's removed. */ + if (xfer->ui_data == NULL) + gaim_xfer_destroy(xfer); } struct gaim_xfer_ui_ops ops = @@ -598,19 +1123,19 @@ gaim_gtkxfer_cancel }; +/************************************************************************** + * GTK+ File Transfer API + **************************************************************************/ void -gaim_gtkxfer_dialog_show(void) +gaim_set_gtkxfer_dialog(struct gaim_gtkxfer_dialog *dialog) { - if (xfer_dialog == NULL) - xfer_dialog = build_xfer_dialog(); - - gtk_widget_show(xfer_dialog->window); + xfer_dialog = dialog; } -void -gaim_gtkxfer_dialog_hide(void) +struct gaim_gtkxfer_dialog * +gaim_get_gtkxfer_dialog(void) { - gtk_widget_hide(xfer_dialog->window); + return xfer_dialog; } struct gaim_xfer_ui_ops * diff -r a2f2a717fdf2 -r d03fcb3f4be2 src/gtkft.h --- a/src/gtkft.h Fri Feb 07 20:54:14 2003 +0000 +++ b/src/gtkft.h Fri Feb 07 20:55:20 2003 +0000 @@ -23,20 +23,105 @@ #ifndef _GAIM_GTK_FT_H_ #define _GAIM_GTK_FT_H_ +#include "ft.h" + +/** + * A file transfer dialog. + * + * The structure is opaque, as nobody should be touching anything inside of + * it. + */ +struct gaim_gtkxfer_dialog; + +/**************************************************************************/ +/** @name GTK+ File Transfer Dialog API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new file transfer dialog. + * + * @return The new dialog. + */ +struct gaim_gtkxfer_dialog *gaim_gtkxfer_dialog_new(void); + +/** + * Destroys a file transfer dialog. + * + * @param dialog The file transfer dialog. + */ +void gaim_gtkxfer_dialog_destroy(struct gaim_gtkxfer_dialog *dialog); + +/** + * Displays the file transfer dialog. + * + * @param dialog The file transfer dialog to show. + */ +void gaim_gtkxfer_dialog_show(struct gaim_gtkxfer_dialog *dialog); + +/** + * Hides the file transfer dialog. + * + * @param dialog The file transfer dialog to hide. + */ +void gaim_gtkxfer_dialog_hide(struct gaim_gtkxfer_dialog *dialog); + +/** + * Adds a file transfer to the dialog. + * + * @param dialog The file transfer dialog. + * @param xfer The file transfer. + */ +void gaim_gtkxfer_dialog_add_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer); + +/** + * Removes a file transfer from the dialog. + * + * @param dialog The file transfer dialog. + * @param xfer The file transfer. + */ +void gaim_gtkxfer_dialog_remove_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer); + +/** + * Indicate in a file transfer dialog that a transfer was canceled. + * + * @param dialog The file transfer dialog. + * @param xfer The file transfer that was canceled. + */ +void gaim_gtkxfer_dialog_cancel_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer); + +/** + * Updates the information for a transfer in the dialog. + * + * @param dialog The file transfer dialog. + * @param xfer The file transfer. + */ +void gaim_gtkxfer_dialog_update_xfer(struct gaim_gtkxfer_dialog *dialog, + struct gaim_xfer *xfer); + +/*@}*/ + /**************************************************************************/ /** @name GTK+ File Transfer API */ /**************************************************************************/ /*@{*/ /** - * Displays the file transfer dialog. + * Sets gaim's main file transfer dialog. + * + * @param dialog The main dialog. */ -void gaim_gtkxfer_dialog_show(void); +void gaim_set_gtkxfer_dialog(struct gaim_gtkxfer_dialog *dialog); /** - * Hides the file transfer dialog. + * Returns gaim's main file transfer dialog. + * + * @return The main dialog. */ -void gaim_gtkxfer_dialog_hide(void); +struct gaim_gtkxfer_dialog *gaim_get_gtkxfer_dialog(void); /** * Returns the UI operations structure for the GTK+ file transfer UI.