src/gtkft.c

changeset 4514
40e3588a280f
child 4517
998c580f7f56
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkft.c	Tue Feb 04 06:57:35 2003 +0000
@@ -0,0 +1,610 @@
+/**
+ * @file gtkft.c The GTK+ file transfer UI
+ *
+ * gaim
+ *
+ * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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 Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "gaim.h"
+#include "prpl.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include "gtkcellrendererprogress.h"
+
+struct gaim_gtkxfer_dialog
+{
+	GtkWidget *window;
+	GtkWidget *tree;
+	GtkListStore *model;
+};
+
+struct gaim_gtkxfer_ui_data
+{
+	GtkWidget *filesel;
+	GtkTreeIter iter;
+	time_t start_time;
+
+	char *name;
+};
+
+static struct gaim_gtkxfer_dialog *xfer_dialog = NULL;
+
+enum
+{
+	COLUMN_STATUS = 0,
+	COLUMN_USER,
+	COLUMN_FILENAME,
+	COLUMN_PROGRESS,
+	COLUMN_SIZE,
+	COLUMN_REMAINING,
+	COLUMN_ESTIMATE,
+	COLUMN_SPEED,
+	NUM_COLUMNS
+};
+
+static gint
+delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
+{
+	gaim_gtkxfer_dialog_hide();
+
+	return TRUE;
+}
+
+static void
+close_win_cb(GtkButton *button, gpointer user_data)
+{
+	gaim_gtkxfer_dialog_hide();
+}
+
+static struct gaim_gtkxfer_dialog *
+build_xfer_dialog(void)
+{
+	struct gaim_gtkxfer_dialog *dialog;
+	GtkWidget *window;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	GtkWidget *frame;
+	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);
+
+	/* Create the scrolled window. */
+	sw = gtk_scrolled_window_new(0, 0);
+	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);
+
+	/* 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);
+	gtk_widget_show(tree);
+
+	g_object_unref(G_OBJECT(model));
+
+
+	/* Columns */
+
+	/* 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);
+
+	/* User column */
+	renderer = gtk_cell_renderer_text_new();
+	column = gtk_tree_view_column_new_with_attributes(_("User"), renderer,
+				"text", COLUMN_USER, NULL);
+	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	/* Filename 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);
+
+	/* 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,
+				"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));
+
+	/* 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);
+
+	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+
+	/* Separator */
+	sep = gtk_hseparator_new();
+	gtk_box_pack_start(GTK_BOX(vbox), 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);
+
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
+
+	/* Close button */
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	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);
+	gtk_widget_show(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);
+	gtk_widget_show(button);
+#endif
+
+	return dialog;
+}
+
+static void
+gaim_gtkxfer_destroy(struct gaim_xfer *xfer)
+{
+	struct gaim_gtkxfer_ui_data *data;
+
+	data = xfer->ui_data;
+
+	if (data == NULL)
+		return;
+}
+
+static gboolean
+choose_file_close_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+	gaim_xfer_request_denied((struct gaim_xfer *)user_data);
+
+	return FALSE;
+}
+
+static void
+choose_file_cancel_cb(GtkButton *button, gpointer user_data)
+{
+	struct gaim_xfer *xfer = (struct gaim_xfer *)user_data;
+	struct gaim_gtkxfer_ui_data *data;
+
+	data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data;
+
+	gaim_xfer_request_denied(xfer);
+
+	gtk_widget_destroy(data->filesel);
+	data->filesel = NULL;
+}
+
+static int
+do_overwrite_cb(struct gaim_xfer *xfer)
+{
+	struct gaim_gtkxfer_ui_data *data;
+	
+	data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data;
+
+	gaim_xfer_request_accepted(xfer, data->name);
+
+	/*
+	 * No, we don't want to free data->name. gaim_xfer_request_accepted
+	 * will deal with it.
+	 */
+	data->name = NULL;
+
+	return 0;
+}
+
+static int
+dont_overwrite_cb(struct gaim_xfer *xfer)
+{
+	struct gaim_gtkxfer_ui_data *data;
+	
+	data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data;
+
+	g_free(data->name);
+	data->name = NULL;
+
+	gaim_xfer_request_denied(xfer);
+
+	return 0;
+}
+
+static void
+choose_file_ok_cb(GtkButton *button, gpointer user_data)
+{
+	struct gaim_xfer *xfer;
+	struct gaim_gtkxfer_ui_data *data;
+	struct stat st;
+	const char *name;
+
+	xfer = (struct gaim_xfer *)user_data;
+	data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data;
+
+	name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(data->filesel));
+
+	if (stat(name, &st) == 0) {
+		if (S_ISDIR(st.st_mode)) {
+			/* XXX */
+			gaim_xfer_request_denied(xfer);
+		}
+		else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
+			data->name = g_strdup(name);
+
+			do_ask_dialog(_("That file already exists. "
+							"Would you like to overwrite it?"),
+						  NULL, xfer,
+						  _("Yes"), do_overwrite_cb,
+						  _("No"), dont_overwrite_cb,
+						  NULL, FALSE);
+		}
+		else {
+			gaim_xfer_request_accepted(xfer, g_strdup(name));
+		}
+	}
+	else {
+		/* File not found. */
+		if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
+			gaim_xfer_request_accepted(xfer, g_strdup(name));
+		}
+		else {
+			do_error_dialog(_("That file does not exist."),
+							NULL, GAIM_ERROR);
+
+			gaim_xfer_request_denied(xfer);
+		}
+	}
+
+	gtk_widget_destroy(data->filesel);
+	data->filesel = NULL;
+}
+
+static int
+choose_file(struct gaim_xfer *xfer)
+{
+	char *cur_dir, *init_str;
+	struct gaim_gtkxfer_ui_data *data;
+
+	cur_dir = g_get_current_dir();
+
+	/* This is where we're setting xfer->ui_data for the first time. */
+	data = g_malloc0(sizeof(struct gaim_gtkxfer_ui_data));
+	xfer->ui_data = data;
+
+	if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
+		data->filesel = gtk_file_selection_new(_("Gaim - Open..."));
+	else
+		data->filesel = gtk_file_selection_new(_("Gaim - Save As..."));
+
+	if (gaim_xfer_get_filename(xfer) == NULL)
+		init_str = g_strdup(cur_dir);
+	else
+		init_str = g_build_filename(cur_dir, gaim_xfer_get_filename(xfer),
+									NULL);
+
+	g_free(cur_dir);
+
+	gtk_file_selection_set_filename(GTK_FILE_SELECTION(data->filesel),
+									init_str);
+
+	g_free(init_str);
+
+	g_signal_connect(G_OBJECT(data->filesel), "delete_event",
+					 G_CALLBACK(choose_file_close_cb), xfer);
+	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(data->filesel)->cancel_button),
+					 "clicked",
+					 G_CALLBACK(choose_file_cancel_cb), xfer);
+	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(data->filesel)->ok_button),
+					 "clicked",
+					 G_CALLBACK(choose_file_ok_cb), xfer);
+
+	gtk_widget_show(data->filesel);
+
+	return 0;
+}
+
+static int
+cancel_recv_cb(struct gaim_xfer *xfer)
+{
+	gaim_xfer_request_denied(xfer);
+
+	return 0;
+}
+
+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;
+	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]);
+
+	buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
+						  xfer->who, gaim_xfer_get_filename(xfer), size_buf);
+
+	g_free(size_buf);
+
+	do_ask_dialog(buf, NULL, xfer,
+				  _("Accept"), choose_file,
+				  _("Cancel"), cancel_recv_cb,
+				  NULL, FALSE);
+
+	g_free(buf);
+}
+
+static void
+gaim_gtkxfer_request_file(struct gaim_xfer *xfer)
+{
+	if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE)
+	{
+		gaim_gtkxfer_ask_recv(xfer);
+	}
+	else
+	{
+		choose_file(xfer);
+	}
+}
+
+static void
+gaim_gtkxfer_ask_cancel(struct gaim_xfer *xfer)
+{
+}
+
+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);
+
+	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);
+}
+
+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_rem  = gaim_xfer_get_bytes_remaining(xfer) / 1024.0;
+	kb_sent = gaim_xfer_get_bytes_sent(xfer) / 1024.0;
+	elapsed = (now - data->start_time);
+	kbps    = (elapsed > 0 ? (kb_sent / elapsed) : 0);
+
+	secs_remaining = (int)(kb_rem / kbps);
+
+	if (secs_remaining <= 0) {
+		GdkPixbuf *pixbuf = NULL;
+
+		*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);
+		g_snprintf(speed_buf, sizeof(speed_buf),
+				   _("%.2f KB/s"), kbps);
+	}
+
+	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,
+					   -1);
+}
+
+static void
+gaim_gtkxfer_cancel(struct gaim_xfer *xfer)
+{
+	struct gaim_gtkxfer_ui_data *data;
+	GdkPixbuf *pixbuf;
+
+	data = (struct gaim_gtkxfer_ui_data *)xfer->ui_data;
+
+	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);
+}
+
+struct gaim_xfer_ui_ops ops =
+{
+	gaim_gtkxfer_destroy,
+	gaim_gtkxfer_request_file,
+	gaim_gtkxfer_ask_cancel,
+	gaim_gtkxfer_add_xfer,
+	gaim_gtkxfer_update_progress,
+	gaim_gtkxfer_cancel
+};
+
+void
+gaim_gtkxfer_dialog_show(void)
+{
+	if (xfer_dialog == NULL)
+		xfer_dialog = build_xfer_dialog();
+
+	gtk_widget_show(xfer_dialog->window);
+}
+
+void
+gaim_gtkxfer_dialog_hide(void)
+{
+	gtk_widget_hide(xfer_dialog->window);
+}
+
+struct gaim_xfer_ui_ops *
+gaim_get_gtk_xfer_ui_ops(void)
+{
+	return &ops;
+}

mercurial