/* X-Chat * Copyright (C) 1998-2006 Peter Zelezny. * * 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 St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #define WANTSOCKET #define WANTARPA #include "../common/inet.h" #include "fe-gtk.h" #include "../common/hexchat.h" #include "../common/hexchatc.h" #include "../common/fe.h" #include "../common/util.h" #include "../common/network.h" #include "gtkutil.h" #include "palette.h" #include "maingui.h" enum /* DCC SEND/RECV */ { COL_TYPE, COL_STATUS, COL_FILE, COL_SIZE, COL_POS, COL_PERC, COL_SPEED, COL_ETA, COL_NICK, COL_DCC, /* struct DCC * */ COL_COLOR, /* GdkColor */ N_COLUMNS }; enum /* DCC CHAT */ { CCOL_STATUS, CCOL_NICK, CCOL_RECV, CCOL_SENT, CCOL_START, CCOL_DCC, /* struct DCC * */ CCOL_COLOR, /* GdkColor * */ CN_COLUMNS }; struct dccwindow { GtkWidget *window; GtkWidget *list; GtkListStore *store; GtkTreeSelection *sel; GtkWidget *abort_button; GtkWidget *accept_button; GtkWidget *resume_button; GtkWidget *open_button; GtkWidget *clear_button; /* clears aborted and completed requests */ GtkWidget *file_label; GtkWidget *address_label; }; struct my_dcc_send { struct session *sess; char *nick; gint64 maxcps; int passive; }; static struct dccwindow dccfwin = {NULL, }; /* file */ static struct dccwindow dcccwin = {NULL, }; /* chat */ static GdkPixbuf *pix_up = NULL; /* down arrow */ static GdkPixbuf *pix_dn = NULL; /* up arrow */ static int win_width = 600; static int win_height = 256; static short view_mode; /* 1=download 2=upload 3=both */ #define VIEW_DOWNLOAD 1 #define VIEW_UPLOAD 2 #define VIEW_BOTH 3 static void proper_unit (guint64 size, char *buf, size_t buf_len) { gchar *formatted_str; GFormatSizeFlags format_flags = G_FORMAT_SIZE_DEFAULT; #ifndef __APPLE__ /* OS X uses SI */ #ifndef WIN32 /* Windows uses IEC size (with SI format) */ if (prefs.hex_gui_filesize_iec) /* Linux can't decide... */ #endif format_flags = G_FORMAT_SIZE_IEC_UNITS; #endif formatted_str = g_format_size_full (size, format_flags); g_strlcpy (buf, formatted_str, buf_len); g_free (formatted_str); } static void dcc_send_filereq_file (struct my_dcc_send *mdc, char *file) { if (file) dcc_send (mdc->sess, mdc->nick, file, mdc->maxcps, mdc->passive); else { g_free (mdc->nick); g_free (mdc); } } void fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive) { char* tbuf = g_strdup_printf (_("Send file to %s"), nick); struct my_dcc_send *mdc = g_new (struct my_dcc_send, 1); mdc->sess = sess; mdc->nick = g_strdup (nick); mdc->maxcps = maxcps; mdc->passive = passive; gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, prefs.hex_dcc_dir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL); g_free (tbuf); } static void dcc_prepare_row_chat (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter, gboolean update_only) { static char pos[16], size[16]; char *date; date = ctime (&dcc->starttime); date[strlen (date) - 1] = 0; /* remove the \n */ proper_unit (dcc->pos, pos, sizeof (pos)); proper_unit (dcc->size, size, sizeof (size)); gtk_list_store_set (store, iter, CCOL_STATUS, _(dccstat[dcc->dccstat].name), CCOL_NICK, dcc->nick, CCOL_RECV, pos, CCOL_SENT, size, CCOL_START, date, CCOL_DCC, dcc, CCOL_COLOR, dccstat[dcc->dccstat].color == 1 ? NULL : colors + dccstat[dcc->dccstat].color, -1); } static void dcc_prepare_row_send (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter, gboolean update_only) { static char pos[16], size[16], kbs[14], perc[14], eta[14]; int to_go; float per; if (!pix_up) pix_up = gtk_widget_render_icon (dccfwin.window, "gtk-go-up", GTK_ICON_SIZE_MENU, NULL); /* percentage ack'ed */ per = (float) ((dcc->ack * 100.00) / dcc->size); proper_unit (dcc->size, size, sizeof (size)); proper_unit (dcc->pos, pos, sizeof (pos)); g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024); g_snprintf (perc, sizeof (perc), "%.0f%%", per); if (dcc->cps != 0) { to_go = (dcc->size - dcc->ack) / dcc->cps; g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d", to_go / 3600, (to_go / 60) % 60, to_go % 60); } else strcpy (eta, "--:--:--"); if (update_only) gtk_list_store_set (store, iter, COL_STATUS, _(dccstat[dcc->dccstat].name), COL_POS, pos, COL_PERC, perc, COL_SPEED, kbs, COL_ETA, eta, COL_COLOR, dccstat[dcc->dccstat].color == 1 ? NULL : colors + dccstat[dcc->dccstat].color, -1); else gtk_list_store_set (store, iter, COL_TYPE, pix_up, COL_STATUS, _(dccstat[dcc->dccstat].name), COL_FILE, file_part (dcc->file), COL_SIZE, size, COL_POS, pos, COL_PERC, perc, COL_SPEED, kbs, COL_ETA, eta, COL_NICK, dcc->nick, COL_DCC, dcc, COL_COLOR, dccstat[dcc->dccstat].color == 1 ? NULL : colors + dccstat[dcc->dccstat].color, -1); } static void dcc_prepare_row_recv (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter, gboolean update_only) { static char size[16], pos[16], kbs[16], perc[14], eta[16]; float per; int to_go; if (!pix_dn) pix_dn = gtk_widget_render_icon (dccfwin.window, "gtk-go-down", GTK_ICON_SIZE_MENU, NULL); proper_unit (dcc->size, size, sizeof (size)); if (dcc->dccstat == STAT_QUEUED) proper_unit (dcc->resumable, pos, sizeof (pos)); else proper_unit (dcc->pos, pos, sizeof (pos)); g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024); /* percentage recv'ed */ per = (float) ((dcc->pos * 100.00) / dcc->size); g_snprintf (perc, sizeof (perc), "%.0f%%", per); if (dcc->cps != 0) { to_go = (dcc->size - dcc->pos) / dcc->cps; g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d", to_go / 3600, (to_go / 60) % 60, to_go % 60); } else strcpy (eta, "--:--:--"); if (update_only) gtk_list_store_set (store, iter, COL_STATUS, _(dccstat[dcc->dccstat].name), COL_POS, pos, COL_PERC, perc, COL_SPEED, kbs, COL_ETA, eta, COL_COLOR, dccstat[dcc->dccstat].color == 1 ? NULL : colors + dccstat[dcc->dccstat].color, -1); else gtk_list_store_set (store, iter, COL_TYPE, pix_dn, COL_STATUS, _(dccstat[dcc->dccstat].name), COL_FILE, file_part (dcc->file), COL_SIZE, size, COL_POS, pos, COL_PERC, perc, COL_SPEED, kbs, COL_ETA, eta, COL_NICK, dcc->nick, COL_DCC, dcc, COL_COLOR, dccstat[dcc->dccstat].color == 1 ? NULL : colors + dccstat[dcc->dccstat].color, -1); } static gboolean dcc_find_row (struct DCC *find_dcc, GtkTreeModel *model, GtkTreeIter *iter, int col) { struct DCC *dcc; if (gtk_tree_model_get_iter_first (model, iter)) { do { gtk_tree_model_get (model, iter, col, &dcc, -1); if (dcc == find_dcc) return TRUE; } while (gtk_tree_model_iter_next (model, iter)); } return FALSE; } static void dcc_update_recv (struct DCC *dcc) { GtkTreeIter iter; if (!dccfwin.window) return; if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC)) return; dcc_prepare_row_recv (dcc, dccfwin.store, &iter, TRUE); } static void dcc_update_chat (struct DCC *dcc) { GtkTreeIter iter; if (!dcccwin.window) return; if (!dcc_find_row (dcc, GTK_TREE_MODEL (dcccwin.store), &iter, CCOL_DCC)) return; dcc_prepare_row_chat (dcc, dcccwin.store, &iter, TRUE); } static void dcc_update_send (struct DCC *dcc) { GtkTreeIter iter; if (!dccfwin.window) return; if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC)) return; dcc_prepare_row_send (dcc, dccfwin.store, &iter, TRUE); } static void close_dcc_file_window (GtkWindow *win, gpointer data) { dccfwin.window = NULL; } static void dcc_append (struct DCC *dcc, GtkListStore *store, gboolean prepend) { GtkTreeIter iter; if (prepend) gtk_list_store_prepend (store, &iter); else gtk_list_store_append (store, &iter); if (dcc->type == TYPE_RECV) dcc_prepare_row_recv (dcc, store, &iter, FALSE); else dcc_prepare_row_send (dcc, store, &iter, FALSE); } /* Returns aborted and completed transfers. */ static GSList * dcc_get_completed (void) { struct DCC *dcc; GtkTreeIter iter; GtkTreeModel *model; GSList *completed = NULL; model = GTK_TREE_MODEL (dccfwin.store); if (gtk_tree_model_get_iter_first (model, &iter)) { do { gtk_tree_model_get (model, &iter, COL_DCC, &dcc, -1); if (is_dcc_completed (dcc)) completed = g_slist_prepend (completed, dcc); } while (gtk_tree_model_iter_next (model, &iter)); } return completed; } static gboolean dcc_completed_transfer_exists (void) { gboolean exist; GSList *comp_list; comp_list = dcc_get_completed (); exist = comp_list != NULL; g_slist_free (comp_list); return exist; } static void update_clear_button_sensitivity (void) { gboolean sensitive = dcc_completed_transfer_exists (); gtk_widget_set_sensitive (dccfwin.clear_button, sensitive); } static void dcc_fill_window (int flags) { struct DCC *dcc; GSList *list; GtkTreeIter iter; int i = 0; gtk_list_store_clear (GTK_LIST_STORE (dccfwin.store)); if (flags & VIEW_UPLOAD) { list = dcc_list; while (list) { dcc = list->data; if (dcc->type == TYPE_SEND) { dcc_append (dcc, dccfwin.store, FALSE); i++; } list = list->next; } } if (flags & VIEW_DOWNLOAD) { list = dcc_list; while (list) { dcc = list->data; if (dcc->type == TYPE_RECV) { dcc_append (dcc, dccfwin.store, FALSE); i++; } list = list->next; } } /* if only one entry, select it (so Accept button can work) */ if (i == 1) { gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dccfwin.store), &iter); gtk_tree_selection_select_iter (dccfwin.sel, &iter); } update_clear_button_sensitivity (); } /* return list of selected DCCs */ static GSList * treeview_get_selected (GtkTreeModel *model, GtkTreeSelection *sel, int column) { GtkTreeIter iter; GSList *list = NULL; void *ptr; if (gtk_tree_model_get_iter_first (model, &iter)) { do { if (gtk_tree_selection_iter_is_selected (sel, &iter)) { gtk_tree_model_get (model, &iter, column, &ptr, -1); list = g_slist_prepend (list, ptr); } } while (gtk_tree_model_iter_next (model, &iter)); } return g_slist_reverse (list); } static GSList * dcc_get_selected (void) { return treeview_get_selected (GTK_TREE_MODEL (dccfwin.store), dccfwin.sel, COL_DCC); } static void resume_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; char buf[512]; GSList *list; list = dcc_get_selected (); if (!list) return; dcc = list->data; g_slist_free (list); if (dcc->type == TYPE_RECV && !dcc_resume (dcc)) { switch (dcc->resume_error) { case 0: /* unknown error */ fe_message (_("That file is not resumable."), FE_MSG_ERROR); break; case 1: g_snprintf (buf, sizeof (buf), _( "Cannot access file: %s\n" "%s.\n" "Resuming not possible."), dcc->destfile, errorstring (dcc->resume_errno)); fe_message (buf, FE_MSG_ERROR); break; case 2: fe_message (_("File in download directory is larger " "than file offered. Resuming not possible."), FE_MSG_ERROR); break; case 3: fe_message (_("Cannot resume the same file from two people."), FE_MSG_ERROR); } } } static void abort_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_get_selected (); for (; list; list = list->next) { dcc = list->data; dcc_abort (dcc->serv->front_session, dcc); } g_slist_free (start); /* Enable the clear button if it wasn't already enabled */ update_clear_button_sensitivity (); } static void accept_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_get_selected (); for (; list; list = list->next) { dcc = list->data; if (dcc->type != TYPE_SEND) dcc_get (dcc); } g_slist_free (start); } static void clear_completed (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *completed; /* Make a new list of only the completed items and abort each item. * A new list is made because calling dcc_abort removes items from the original list, * making it impossible to iterate over that list directly. */ for (completed = dcc_get_completed (); completed; completed = completed->next) { dcc = completed->data; dcc_abort (dcc->serv->front_session, dcc); } /* The data was freed by dcc_close */ g_slist_free (completed); update_clear_button_sensitivity (); } static void browse_folder (char *dir) { #ifdef WIN32 /* no need for file:// in ShellExecute() */ fe_open_url (dir); #else char buf[512]; g_snprintf (buf, sizeof (buf), "file://%s", dir); fe_open_url (buf); #endif } static void browse_dcc_folder (void) { if (prefs.hex_dcc_completed_dir[0]) browse_folder (prefs.hex_dcc_completed_dir); else browse_folder (prefs.hex_dcc_dir); } static void dcc_details_populate (struct DCC *dcc) { char buf[128]; if (!dcc) { gtk_label_set_text (GTK_LABEL (dccfwin.file_label), NULL); gtk_label_set_text (GTK_LABEL (dccfwin.address_label), NULL); return; } /* full path */ if (dcc->type == TYPE_RECV) gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->destfile); else gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->file); /* address and port */ g_snprintf (buf, sizeof (buf), "%s : %d", net_ip (dcc->addr), dcc->port); gtk_label_set_text (GTK_LABEL (dccfwin.address_label), buf); } static void dcc_row_cb (GtkTreeSelection *sel, gpointer user_data) { struct DCC *dcc; GSList *list; list = dcc_get_selected (); if (!list) { gtk_widget_set_sensitive (dccfwin.accept_button, FALSE); gtk_widget_set_sensitive (dccfwin.resume_button, FALSE); gtk_widget_set_sensitive (dccfwin.abort_button, FALSE); dcc_details_populate (NULL); return; } gtk_widget_set_sensitive (dccfwin.abort_button, TRUE); if (list->next) /* multi selection */ { gtk_widget_set_sensitive (dccfwin.accept_button, TRUE); gtk_widget_set_sensitive (dccfwin.resume_button, TRUE); dcc_details_populate (list->data); } else { /* turn OFF/ON appropriate buttons */ dcc = list->data; if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_RECV) { gtk_widget_set_sensitive (dccfwin.accept_button, TRUE); gtk_widget_set_sensitive (dccfwin.resume_button, TRUE); } else { gtk_widget_set_sensitive (dccfwin.accept_button, FALSE); gtk_widget_set_sensitive (dccfwin.resume_button, FALSE); } dcc_details_populate (dcc); } g_slist_free (list); } static void dcc_dclick_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { struct DCC *dcc; GSList *list; list = dcc_get_selected (); if (!list) return; dcc = list->data; g_slist_free (list); if (dcc->type == TYPE_RECV) { accept_clicked (0, 0); return; } switch (dcc->dccstat) { case STAT_FAILED: case STAT_ABORTED: case STAT_DONE: dcc_abort (dcc->serv->front_session, dcc); } } static void dcc_add_column (GtkWidget *tree, int textcol, int colorcol, char *title, gboolean right_justified) { GtkCellRenderer *renderer; renderer = gtk_cell_renderer_text_new (); if (right_justified) g_object_set (G_OBJECT (renderer), "xalign", (float) 1.0, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title, renderer, "text", textcol, "foreground-gdk", colorcol, NULL); gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1); } static GtkWidget * dcc_detail_label (char *text, GtkWidget *box, int num) { GtkWidget *label; char buf[64]; label = gtk_label_new (NULL); g_snprintf (buf, sizeof (buf), "%s", text); gtk_label_set_markup (GTK_LABEL (label), buf); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_table_attach (GTK_TABLE (box), label, 0, 1, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0); label = gtk_label_new (NULL); gtk_label_set_selectable (GTK_LABEL (label), TRUE); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_table_attach (GTK_TABLE (box), label, 1, 2, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0); return label; } static void dcc_exp_cb (GtkWidget *exp, GtkWidget *box) { if (gtk_widget_get_visible (box)) { gtk_widget_hide (box); } else { gtk_widget_show (box); } } static void dcc_toggle (GtkWidget *item, gpointer data) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item))) { view_mode = GPOINTER_TO_INT (data); dcc_fill_window (GPOINTER_TO_INT (data)); } } static gboolean dcc_configure_cb (GtkWindow *win, GdkEventConfigure *event, gpointer data) { /* remember the window size */ gtk_window_get_size (win, &win_width, &win_height); return FALSE; } int fe_dcc_open_recv_win (int passive) { GtkWidget *radio, *table, *vbox, *bbox, *view, *exp, *detailbox; GtkListStore *store; GSList *group; if (dccfwin.window) { if (!passive) mg_bring_tofront (dccfwin.window); return TRUE; } dccfwin.window = mg_create_generic_tab ("Transfers", _(DISPLAY_NAME": Uploads and Downloads"), FALSE, TRUE, close_dcc_file_window, NULL, win_width, win_height, &vbox, 0); gtkutil_destroy_on_esc (dccfwin.window); gtk_container_set_border_width (GTK_CONTAINER (dccfwin.window), 3); gtk_box_set_spacing (GTK_BOX (vbox), 3); store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR); view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); /* Up/Down Icon column */ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, NULL, gtk_cell_renderer_pixbuf_new (), "pixbuf", COL_TYPE, NULL); dcc_add_column (view, COL_STATUS, COL_COLOR, _("Status"), FALSE); dcc_add_column (view, COL_FILE, COL_COLOR, _("File"), FALSE); dcc_add_column (view, COL_SIZE, COL_COLOR, _("Size"), TRUE); dcc_add_column (view, COL_POS, COL_COLOR, _("Position"), TRUE); dcc_add_column (view, COL_PERC, COL_COLOR, "%", TRUE); dcc_add_column (view, COL_SPEED, COL_COLOR, "KB/s", TRUE); dcc_add_column (view, COL_ETA, COL_COLOR, _("ETA"), FALSE); dcc_add_column (view, COL_NICK, COL_COLOR, _("Nick"), FALSE); gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_FILE), TRUE); gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_NICK), TRUE); dccfwin.list = view; dccfwin.store = store; dccfwin.sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); view_mode = VIEW_BOTH; gtk_tree_selection_set_mode (dccfwin.sel, GTK_SELECTION_MULTIPLE); if (!prefs.hex_gui_tab_utils) g_signal_connect (G_OBJECT (dccfwin.window), "configure_event", G_CALLBACK (dcc_configure_cb), 0); g_signal_connect (G_OBJECT (dccfwin.sel), "changed", G_CALLBACK (dcc_row_cb), NULL); /* double click */ g_signal_connect (G_OBJECT (view), "row-activated", G_CALLBACK (dcc_dclick_cb), NULL); table = gtk_table_new (1, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 16); gtk_box_pack_start (GTK_BOX (vbox), table, 0, 0, 0); radio = gtk_radio_button_new_with_mnemonic (NULL, _("Both")); g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_BOTH)); gtk_table_attach (GTK_TABLE (table), radio, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); radio = gtk_radio_button_new_with_mnemonic (group, _("Uploads")); g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_UPLOAD)); gtk_table_attach (GTK_TABLE (table), radio, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)); radio = gtk_radio_button_new_with_mnemonic (group, _("Downloads")); g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_DOWNLOAD)); gtk_table_attach (GTK_TABLE (table), radio, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); exp = gtk_expander_new (_("Details")); gtk_table_attach (GTK_TABLE (table), exp, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); detailbox = gtk_table_new (3, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (detailbox), 6); gtk_table_set_row_spacings (GTK_TABLE (detailbox), 2); gtk_container_set_border_width (GTK_CONTAINER (detailbox), 6); g_signal_connect (G_OBJECT (exp), "activate", G_CALLBACK (dcc_exp_cb), detailbox); gtk_table_attach (GTK_TABLE (table), detailbox, 0, 4, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); dccfwin.file_label = dcc_detail_label (_("File:"), detailbox, 0); dccfwin.address_label = dcc_detail_label (_("Address:"), detailbox, 1); bbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD); gtk_box_pack_end (GTK_BOX (vbox), bbox, FALSE, FALSE, 2); dccfwin.abort_button = gtkutil_button (bbox, GTK_STOCK_CANCEL, 0, abort_clicked, 0, _("Abort")); dccfwin.accept_button = gtkutil_button (bbox, GTK_STOCK_APPLY, 0, accept_clicked, 0, _("Accept")); dccfwin.resume_button = gtkutil_button (bbox, GTK_STOCK_REFRESH, 0, resume_clicked, 0, _("Resume")); dccfwin.clear_button = gtkutil_button (bbox, GTK_STOCK_CLEAR, 0, clear_completed, 0, _("Clear")); dccfwin.open_button = gtkutil_button (bbox, 0, 0, browse_dcc_folder, 0, _("Open Folder...")); gtk_widget_set_sensitive (dccfwin.accept_button, FALSE); gtk_widget_set_sensitive (dccfwin.resume_button, FALSE); gtk_widget_set_sensitive (dccfwin.abort_button, FALSE); dcc_fill_window (3); gtk_widget_show_all (dccfwin.window); gtk_widget_hide (detailbox); return FALSE; } int fe_dcc_open_send_win (int passive) { /* combined send/recv GUI */ return fe_dcc_open_recv_win (passive); } /* DCC CHAT GUIs BELOW */ static GSList * dcc_chat_get_selected (void) { return treeview_get_selected (GTK_TREE_MODEL (dcccwin.store), dcccwin.sel, CCOL_DCC); } static void accept_chat_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_chat_get_selected (); for (; list; list = list->next) { dcc = list->data; dcc_get (dcc); } g_slist_free (start); } static void abort_chat_clicked (GtkWidget * wid, gpointer none) { struct DCC *dcc; GSList *start, *list; start = list = dcc_chat_get_selected (); for (; list; list = list->next) { dcc = list->data; dcc_abort (dcc->serv->front_session, dcc); } g_slist_free (start); } static void dcc_chat_close_cb (void) { dcccwin.window = NULL; } static void dcc_chat_append (struct DCC *dcc, GtkListStore *store, gboolean prepend) { GtkTreeIter iter; if (prepend) gtk_list_store_prepend (store, &iter); else gtk_list_store_append (store, &iter); dcc_prepare_row_chat (dcc, store, &iter, FALSE); } static void dcc_chat_fill_win (void) { struct DCC *dcc; GSList *list; GtkTreeIter iter; int i = 0; gtk_list_store_clear (GTK_LIST_STORE (dcccwin.store)); list = dcc_list; while (list) { dcc = list->data; if (dcc->type == TYPE_CHATSEND || dcc->type == TYPE_CHATRECV) { dcc_chat_append (dcc, dcccwin.store, FALSE); i++; } list = list->next; } /* if only one entry, select it (so Accept button can work) */ if (i == 1) { gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dcccwin.store), &iter); gtk_tree_selection_select_iter (dcccwin.sel, &iter); } } static void dcc_chat_row_cb (GtkTreeSelection *sel, gpointer user_data) { struct DCC *dcc; GSList *list; list = dcc_chat_get_selected (); if (!list) { gtk_widget_set_sensitive (dcccwin.accept_button, FALSE); gtk_widget_set_sensitive (dcccwin.abort_button, FALSE); return; } gtk_widget_set_sensitive (dcccwin.abort_button, TRUE); if (list->next) /* multi selection */ gtk_widget_set_sensitive (dcccwin.accept_button, TRUE); else { /* turn OFF/ON appropriate buttons */ dcc = list->data; if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_CHATRECV) gtk_widget_set_sensitive (dcccwin.accept_button, TRUE); else gtk_widget_set_sensitive (dcccwin.accept_button, FALSE); } g_slist_free (list); } static void dcc_chat_dclick_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { accept_chat_clicked (0, 0); } int fe_dcc_open_chat_win (int passive) { GtkWidget *view, *vbox, *bbox; GtkListStore *store; if (dcccwin.window) { if (!passive) mg_bring_tofront (dcccwin.window); return TRUE; } dcccwin.window = mg_create_generic_tab ("DCCChat", _(DISPLAY_NAME": DCC Chat List"), FALSE, TRUE, dcc_chat_close_cb, NULL, 550, 180, &vbox, 0); gtkutil_destroy_on_esc (dcccwin.window); gtk_container_set_border_width (GTK_CONTAINER (dcccwin.window), 3); gtk_box_set_spacing (GTK_BOX (vbox), 3); store = gtk_list_store_new (CN_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR); view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1); dcc_add_column (view, CCOL_STATUS, CCOL_COLOR, _("Status"), FALSE); dcc_add_column (view, CCOL_NICK, CCOL_COLOR, _("Nick"), FALSE); dcc_add_column (view, CCOL_RECV, CCOL_COLOR, _("Recv"), TRUE); dcc_add_column (view, CCOL_SENT, CCOL_COLOR, _("Sent"), TRUE); dcc_add_column (view, CCOL_START, CCOL_COLOR, _("Start Time"), FALSE); gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 1), TRUE); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); dcccwin.list = view; dcccwin.store = store; dcccwin.sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); gtk_tree_selection_set_mode (dcccwin.sel, GTK_SELECTION_MULTIPLE); g_signal_connect (G_OBJECT (dcccwin.sel), "changed", G_CALLBACK (dcc_chat_row_cb), NULL); /* double click */ g_signal_connect (G_OBJECT (view), "row-activated", G_CALLBACK (dcc_chat_dclick_cb), NULL); bbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD); gtk_box_pack_end (GTK_BOX (vbox), bbox, FALSE, FALSE, 2); dcccwin.abort_button = gtkutil_button (bbox, GTK_STOCK_CANCEL, 0, abort_chat_clicked, 0, _("Abort")); dcccwin.accept_button = gtkutil_button (bbox, GTK_STOCK_APPLY, 0, accept_chat_clicked, 0, _("Accept")); gtk_widget_set_sensitive (dcccwin.accept_button, FALSE); gtk_widget_set_sensitive (dcccwin.abort_button, FALSE); dcc_chat_fill_win (); gtk_widget_show_all (dcccwin.window); return FALSE; } void fe_dcc_add (struct DCC *dcc) { switch (dcc->type) { case TYPE_RECV: if (dccfwin.window && (view_mode & VIEW_DOWNLOAD)) dcc_append (dcc, dccfwin.store, TRUE); break; case TYPE_SEND: if (dccfwin.window && (view_mode & VIEW_UPLOAD)) dcc_append (dcc, dccfwin.store, TRUE); break; default: /* chat */ if (dcccwin.window) dcc_chat_append (dcc, dcccwin.store, TRUE); } } void fe_dcc_update (struct DCC *dcc) { switch (dcc->type) { case TYPE_SEND: dcc_update_send (dcc); break; case TYPE_RECV: dcc_update_recv (dcc); break; default: dcc_update_chat (dcc); } if (dccfwin.window) update_clear_button_sensitivity(); } void fe_dcc_remove (struct DCC *dcc) { GtkTreeIter iter; switch (dcc->type) { case TYPE_SEND: case TYPE_RECV: if (dccfwin.window) { if (dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC)) gtk_list_store_remove (dccfwin.store, &iter); } break; default: /* chat */ if (dcccwin.window) { if (dcc_find_row (dcc, GTK_TREE_MODEL (dcccwin.store), &iter, CCOL_DCC)) gtk_list_store_remove (dcccwin.store, &iter); } break; } }