Mercurial > pidgin
changeset 7620:4f41c4aa9913
[gaim-migrate @ 8244]
That was close.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Mon, 24 Nov 2003 02:45:53 +0000 |
parents | 994b2d782711 |
children | bfe3a796b2c2 |
files | src/gtkblist.c |
diffstat | 1 files changed, 2752 insertions(+), 891 deletions(-) [+] |
line wrap: on
line diff
--- a/src/gtkblist.c Mon Nov 24 02:35:27 2003 +0000 +++ b/src/gtkblist.c Mon Nov 24 02:45:53 2003 +0000 @@ -18,69 +18,411 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif -#ifdef GAIM_PLUGINS -#ifndef _WIN32 -#include <dlfcn.h> -#endif -#endif /* GAIM_PLUGINS */ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <math.h> -#include <time.h> - -#ifdef _WIN32 -#include <gdk/gdkwin32.h> -#else -#include <unistd.h> -#include <gdk/gdkx.h> -#endif +#include "gtkinternal.h" + +#include "account.h" +#include "core.h" +#include "debug.h" +#include "multi.h" +#include "notify.h" +#include "prpl.h" +#include "prefs.h" +#include "request.h" +#include "signals.h" +#include "sound.h" +#include "stock.h" +#include "util.h" + +#include "gtkaccount.h" +#include "gtkblist.h" +#include "gtkconv.h" +#include "gtkdebug.h" +#include "gtkft.h" +#include "gtklog.h" +#include "gtkpounce.h" +#include "gtkprefs.h" +#include "gtkprivacy.h" +#include "gtkutils.h" + +#include "ui.h" + +#include "gaim.h" #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> -#include "prpl.h" -#include "sound.h" -#include "gaim.h" -#include "gtkblist.h" -#include "gtkpounce.h" -#include "gtkft.h" -#include "gtkdebug.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif +#include <gdk/gdk.h> + +typedef struct +{ + GaimAccount *account; + + GtkWidget *window; + GtkWidget *combo; + GtkWidget *entry; + GtkWidget *entry_for_alias; + GtkWidget *account_box; + +} GaimGtkAddBuddyData; + +typedef struct +{ + GaimAccount *account; + + GtkWidget *window; + GtkWidget *account_menu; + GtkWidget *alias_entry; + GtkWidget *group_combo; + GtkWidget *entries_box; + GtkSizeGroup *sg; + + GList *entries; + +} GaimGtkAddChatData; + + +static GtkWidget *protomenu = NULL; GSList *gaim_gtk_blist_sort_methods = NULL; static struct gaim_gtk_blist_sort_method *current_sort_method = NULL; -static GtkTreeIter sort_method_none(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); -static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); -static GtkTreeIter sort_method_status(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); -static GtkTreeIter sort_method_log(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); -static struct gaim_gtk_buddy_list *gtkblist = NULL; +static GtkTreeIter sort_method_none(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); + +/* The functions we use for sorting aren't available in gtk 2.0.x, and + * segfault in 2.2.0. 2.2.1 is known to work, so I'll require that */ +#if GTK_CHECK_VERSION(2,2,1) +static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); +static GtkTreeIter sort_method_status(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); +static GtkTreeIter sort_method_log(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); +#endif +static GaimGtkBuddyList *gtkblist = NULL; /* part of the best damn Docklet code this side of Tahiti */ static gboolean gaim_gtk_blist_obscured = FALSE; static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); -static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); +static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node); static char *gaim_get_tooltip_text(GaimBlistNode *node); static char *item_factory_translate_func (const char *path, gpointer func_data); static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter); - -char sort_method[64]; +static void redo_buddy_list(GaimBuddyList *list, gboolean remove); +static void gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node); + +static void show_rename_group(GtkWidget *unused, GaimGroup *g); struct _gaim_gtk_blist_node { GtkTreeRowReference *row; + gboolean contact_expanded; }; +#if GTK_CHECK_VERSION(2,2,0) +/**************************** Weird drop shadow stuff *******************/ +/* This is based on a patch for drop shadows in GTK menus available at http://www.xfce.org/gtkmenu-shadow/ */ + +enum side { + EAST_SIDE, + SOUTH_SIDE +}; + +const double shadow_strip_l[5] = { + .937, .831, .670, .478, .180 +}; + +const double bottom_left_corner[25] = { + 1.00, .682, .423, .333, .258, + 1.00, .898, .800, .682, .584, + 1.00, .937, .874, .800, .737, + 1.00, .968, .937, .898, .866, + 1.00, .988, .976, .960, .945 +}; + +const double bottom_right_corner[25] = { + .258, .584, .737, .866, .945, + .584, .682, .800, .898, .960, + .737, .800, .874, .937, .976, + .866, .898, .937, .968, .988, + .945, .960, .976, .988, .996 +}; + +const double top_right_corner[25] = { + 1.00, 1.00, 1.00, 1.00, 1.00, + .686, .898, .937, .968, .988, + .423, .803, .874, .937, .976, + .333, .686, .800, .898, .960, + .258, .584, .737, .866, .945 +}; + +const double top_left_corner[25] = { + .988, .968, .937, .898, .498, + .976, .937, .874, .803, .423, + .960, .898, .800, .686, .333, + .945, .866, .737, .584, .258, + .941, .847, .698, .521, .215 +}; + + +static GdkPixbuf * +get_pixbuf (GtkWidget *menu, + int x, + int y, + int width, + int height) +{ + GdkPixbuf *dest, *src; + GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(menu)); + GdkWindow *root = gdk_screen_get_root_window (screen); + gint screen_height = gdk_screen_get_height (screen); + gint screen_width = gdk_screen_get_width (screen); + gint original_width = width; + gint original_height = height; + +#ifdef _WIN32 + /* In Win32, GDK gets the workarea that isn't occupied by toolbars + (including the taskbar) and uses that region as the screen size. + GTK returns positions based on a screen size that ignores these + toolbars. Since we want a pixmap with real X,Y coordinates, we + need to find out the offset from GTK's screen to GDK's screen, + and adjust the pixmaps we grab accordingly. GDK will not deal + with toolbar position updates, so we're stuck restarting Gaim + if that happens. */ + RECT *workarea = g_malloc(sizeof(RECT)); + SystemParametersInfo(SPI_GETWORKAREA, 0, (void *)workarea, 0); + x += (workarea->left); + y += (workarea->top); + g_free(workarea); +#endif + + if (x < 0) + { + width += x; + x = 0; + } + + if (y < 0) + { + height += y; + y = 0; + } + + if (x + width > screen_width) + { + width = screen_width - x; + } + + if (y + height > screen_height) + { + height = screen_height - y; + } + + if (width <= 0 || height <= 0) + return NULL; + + dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + original_width, original_height); + src = gdk_pixbuf_get_from_drawable (NULL, root, NULL, x, y, 0, 0, + width, height); + gdk_pixbuf_copy_area (src, 0, 0, width, height, dest, 0, 0); + + g_object_unref (G_OBJECT (src)); + + return dest; +} + +static void +shadow_paint(GaimGtkBuddyList *blist, GdkRectangle *area, enum side shadow) +{ + gint width, height; + GdkGC *gc = gtkblist->tipwindow->style->black_gc; + + switch (shadow) + { + case EAST_SIDE: + if (gtkblist->east != NULL) + { + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + width = gdk_pixbuf_get_width (gtkblist->east); + height = gdk_pixbuf_get_height (gtkblist->east); + +#if GTK_CHECK_VERSION(2,2,0) + gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->east_shadow), gc, + gtkblist->east, 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, + 0, 0); +#else + gdk_pixbuf_render_to_drawable(gtkblist->east, + GDK_DRAWABLE(gtkblist->east_shadow), gc, 0, 0, 0, 0, + width, height, GDK_RGB_DITHER_NONE, 0, 0); +#endif + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + } + break; + case SOUTH_SIDE: + if (blist->south != NULL) + { + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + width = gdk_pixbuf_get_width (gtkblist->south); + height = gdk_pixbuf_get_height (gtkblist->south); +#if GTK_CHECK_VERSION(2,2,0) + gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->south_shadow), gc, gtkblist->south, + 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0); +#else + gdk_pixbuf_render_to_drawable(gtkblist->south, GDK_DRAWABLE(gtkblist->south_shadow), gc, + 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0); +#endif + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + } + break; + default: + break; + } +} + +static void +pixbuf_add_shadow (GdkPixbuf *pb, + enum side shadow) +{ + gint width, rowstride, height; + gint i; + guchar *pixels, *p; + + width = gdk_pixbuf_get_width (pb); + height = gdk_pixbuf_get_height (pb); + rowstride = gdk_pixbuf_get_rowstride (pb); + pixels = gdk_pixbuf_get_pixels (pb); + + switch (shadow) + { + case EAST_SIDE: + if (height > 5) + { + for (i = 0; i < width; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * width; j += 3, k++) + { + p[j] = (guchar) (p[j] * top_right_corner [i * width + k]); + p[j + 1] = (guchar) (p[j + 1] * top_right_corner [i * width + k]); + p[j + 2] = (guchar) (p[j + 2] * top_right_corner [i * width + k]); + } + } + + i = 5; + } + else + { + i = 0; + } + + for (;i < height; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * width; j += 3, k++) + { + p[j] = (guchar) (p[j] * shadow_strip_l[width - 1 - k]); + p[j + 1] = (guchar) (p[j + 1] * shadow_strip_l[width - 1 - k]); + p[j + 2] = (guchar) (p[j + 2] * shadow_strip_l[width - 1 - k]); + } + } + break; + + case SOUTH_SIDE: + for (i = 0; i < height; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * height; j += 3, k++) + { + p[j] = (guchar) (p[j] * bottom_left_corner[i * height + k]); + p[j + 1] = (guchar) (p[j + 1] * bottom_left_corner[i * height + k]); + p[j + 2] = (guchar) (p[j + 2] * bottom_left_corner[i * height + k]); + } + + p = pixels + (i * rowstride) + 3 * height; + for (j = 0, k = 0; j < (width * 3) - (6 * height); j += 3, k++) + { + p[j] = (guchar) (p[j] * bottom_right_corner [i * height]); + p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner [i * height]); + p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner [i * height]); + } + + p = pixels + (i * rowstride) + ((width * 3) - (3 * height)); + for (j = 0, k = 0; j < 3 * height; j += 3, k++) + { + p[j] = (guchar) (p[j] * bottom_right_corner[i * height + k]); + p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner[i * height + k]); + p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner[i * height + k]); + } + } + break; + + default: + break; + } +} + +static gboolean +map_shadow_windows (gpointer data) +{ + GaimGtkBuddyList *blist = (GaimGtkBuddyList*)data; + GtkWidget *widget = blist->tipwindow; + GdkPixbuf *pixbuf; + int x, y; + + gtk_window_get_position(GTK_WINDOW(widget), &x, &y); + pixbuf = get_pixbuf (widget, + x + widget->allocation.width, y, + 5, widget->allocation.height + 5); + if (pixbuf != NULL) + { + pixbuf_add_shadow (pixbuf, EAST_SIDE); + if (blist->east != NULL) + { + g_object_unref (G_OBJECT (blist->east)); + } + blist->east = pixbuf; + } + + pixbuf = get_pixbuf (widget, + x, y + widget->allocation.height, + widget->allocation.width + 5, 5); + if (pixbuf != NULL) + { + pixbuf_add_shadow (pixbuf, SOUTH_SIDE); + if (blist->south != NULL) + { + g_object_unref (G_OBJECT (blist->south)); + } + blist->south = pixbuf; + } + + gdk_window_move_resize (blist->east_shadow, + x + widget->allocation.width, y, + 5, widget->allocation.height); + + gdk_window_move_resize (blist->south_shadow, + x, y + widget->allocation.height, + widget->allocation.width + 5, 5); + gdk_window_show (blist->east_shadow); + gdk_window_show (blist->south_shadow); + shadow_paint(blist, NULL, EAST_SIDE); + shadow_paint(blist, NULL, SOUTH_SIDE); + + return FALSE; +} + +/**************** END WEIRD DROP SHADOW STUFF ***********************************/ +#endif +static GSList *blist_prefs_callbacks = NULL; + /*************************************************** * Callbacks * ***************************************************/ @@ -90,49 +432,52 @@ if (docklet_count) gaim_blist_set_visible(FALSE); else - do_quit(); + gaim_core_quit(); /* we handle everything, event should not propogate further */ return TRUE; } -static gboolean gtk_blist_save_prefs_cb(gpointer data) -{ - save_prefs(); - - /* only run once */ - return FALSE; -} - static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) { /* unfortunately GdkEventConfigure ignores the window gravity, but * * the only way we have of setting the position doesn't. we have to * - * call get_position and get_size because they do pay attention to * - * the gravity. this is inefficient and I agree it sucks, but it's * - * more likely to work correctly. - Robot101 */ + * call get_position because it does pay attention to the gravity. * + * this is inefficient and I agree it sucks, but it's more likely * + * to work correctly. - Robot101 */ gint x, y; /* check for visibility because when we aren't visible, this will * * give us bogus (0,0) coordinates. - xOr */ - if (GTK_WIDGET_VISIBLE(w)) { + if (GTK_WIDGET_VISIBLE(w)) gtk_window_get_position(GTK_WINDOW(w), &x, &y); - - if (x != blist_pos.x || - y != blist_pos.y || - event->width != blist_pos.width || - event->height != blist_pos.height) { - blist_pos.x = x; - blist_pos.y = y; - blist_pos.width = event->width; - blist_pos.height = event->height; - - if (!g_main_context_find_source_by_user_data(NULL, >k_blist_save_prefs_cb)) { - g_timeout_add(5000, gtk_blist_save_prefs_cb, >k_blist_save_prefs_cb); - } - } + else + return FALSE; /* carry on normally */ + + /* don't save if nothing changed */ + if (x == gaim_prefs_get_int("/gaim/gtk/blist/x") && + y == gaim_prefs_get_int("/gaim/gtk/blist/y") && + event->width == gaim_prefs_get_int("/gaim/gtk/blist/width") && + event->height == gaim_prefs_get_int("/gaim/gtk/blist/height")) { + + return FALSE; /* carry on normally */ } + /* don't save off-screen positioning */ + if (x + event->width < 0 || + y + event->height < 0 || + x > gdk_screen_width() || + y > gdk_screen_height()) { + + return FALSE; /* carry on normally */ + } + + /* store the position */ + gaim_prefs_set_int("/gaim/gtk/blist/x", x); + gaim_prefs_set_int("/gaim/gtk/blist/y", y); + gaim_prefs_set_int("/gaim/gtk/blist/width", event->width); + gaim_prefs_set_int("/gaim/gtk/blist/height", event->height); + /* continue to handle event normally */ return FALSE; } @@ -148,17 +493,40 @@ return FALSE; } -static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b) +static void gtk_blist_menu_info_cb(GtkWidget *w, GaimBuddy *b) { serv_get_info(b->account->gc, b->name); } -static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b) +static void gtk_blist_menu_im_cb(GtkWidget *w, GaimBuddy *b) { - gaim_conversation_new(GAIM_CONV_IM, b->account, b->name); + GaimConversation *conv = gaim_conversation_new(GAIM_CONV_IM, b->account, + b->name); + + if(conv) { + GaimConvWindow *win = gaim_conversation_get_window(conv); + + gaim_conv_window_raise(win); + gaim_conv_window_switch_conversation( + gaim_conversation_get_window(conv), + gaim_conversation_get_index(conv)); + + if (GAIM_IS_GTK_WINDOW(win)) + gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window)); + } } -static void gtk_blist_menu_join_cb(GtkWidget *w, struct chat *chat) +static void gtk_blist_menu_autojoin_cb(GtkWidget *w, GaimChat *chat) +{ + if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) + gaim_chat_set_setting(chat, "gtk-autojoin", "true"); + else + gaim_chat_set_setting(chat, "gtk-autojoin", NULL); + + gaim_blist_save(); +} + +static void gtk_blist_menu_join_cb(GtkWidget *w, GaimChat *chat) { serv_join_chat(chat->account->gc, chat->components); } @@ -166,29 +534,36 @@ static void gtk_blist_menu_alias_cb(GtkWidget *w, GaimBlistNode *node) { if(GAIM_BLIST_NODE_IS_BUDDY(node)) - alias_dialog_bud((struct buddy*)node); + alias_dialog_bud((GaimBuddy*)node); + else if(GAIM_BLIST_NODE_IS_CONTACT(node)) + alias_dialog_contact((GaimContact*)node); else if(GAIM_BLIST_NODE_IS_CHAT(node)) - alias_dialog_chat((struct chat*)node); + alias_dialog_blist_chat((GaimChat*)node); } -static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b) +static void gtk_blist_menu_bp_cb(GtkWidget *w, GaimBuddy *b) { - gaim_gtkpounce_dialog_show(b, NULL); + gaim_gtkpounce_dialog_show(b->account, b->name, NULL); } -static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b) +static void gtk_blist_menu_showlog_cb(GtkWidget *w, GaimBuddy *b) { - show_log(b->name); + gaim_gtk_log_show(b->name, b->account); +} + +static void gtk_blist_menu_send_file_cb(GtkWidget *w, GaimBuddy *b) +{ + gaim_prpl_ask_send_file (b->account->gc, b->name); } static void gtk_blist_show_systemlog_cb() { - show_log(NULL); + /* LOG show_log(NULL); */ } static void gtk_blist_show_onlinehelp_cb() { - open_url(NULL, WEBSITE "documentation.php"); + gaim_notify_uri(NULL, GAIM_WEBSITE "documentation.php"); } static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv) @@ -202,7 +577,12 @@ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); + gaim_conversation_new(GAIM_CONV_IM, ((GaimBuddy*)node)->account, ((GaimBuddy*)node)->name); + return; + } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimBuddy *buddy = + gaim_contact_get_priority_buddy((GaimContact*)node); + gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); return; } } @@ -220,7 +600,11 @@ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); + serv_get_info(((GaimBuddy*)node)->account->gc, ((GaimBuddy*)node)->name); + return; + } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + serv_get_info(buddy->account->gc, buddy->name); return; } } @@ -238,7 +622,7 @@ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); if (GAIM_BLIST_NODE_IS_CHAT(node)) { - serv_join_chat(((struct chat *)node)->account->gc, ((struct chat*)node)->components); + serv_join_chat(((GaimChat *)node)->account->gc, ((GaimChat *)node)->components); return; } } @@ -259,7 +643,7 @@ node = g_value_get_pointer(&val); if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_group_set_setting((struct group *)node, "collapsed", NULL); + gaim_group_set_setting((GaimGroup *)node, "collapsed", NULL); gaim_blist_save(); } } @@ -273,8 +657,10 @@ node = g_value_get_pointer(&val); if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_group_set_setting((struct group *)node, "collapsed", "true"); + gaim_group_set_setting((GaimGroup *)node, "collapsed", "true"); gaim_blist_save(); + } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + gaim_gtk_blist_collapse_contact_cb(NULL, node); } } @@ -288,15 +674,22 @@ gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); node = g_value_get_pointer(&val); - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct gaim_conversation *conv = - gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); + if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *buddy; + GaimConversation *conv; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) + buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + else + buddy = (GaimBuddy*)node; + + conv = gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); if(conv) { - struct gaim_window *win = gaim_conversation_get_window(conv); - - gaim_window_raise(win); - gaim_window_switch_conversation( + GaimConvWindow *win = gaim_conversation_get_window(conv); + + gaim_conv_window_raise(win); + gaim_conv_window_switch_conversation( gaim_conversation_get_window(conv), gaim_conversation_get_index(conv)); @@ -304,7 +697,7 @@ gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window)); } } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { - serv_join_chat(((struct chat *)node)->account->gc, ((struct chat*)node)->components); + serv_join_chat(((GaimChat *)node)->account->gc, ((GaimChat *)node)->components); } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { if (gtk_tree_view_row_expanded(tv, path)) gtk_tree_view_collapse_row(tv, path); @@ -321,13 +714,15 @@ if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) - show_add_chat(NULL, (struct group*)node->parent); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_blist_request_add_chat(NULL, (GaimGroup*)node->parent->parent); + if (GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) + gaim_blist_request_add_chat(NULL, (GaimGroup*)node->parent); else if (GAIM_BLIST_NODE_IS_GROUP(node)) - show_add_chat(NULL, (struct group*)node); + gaim_blist_request_add_chat(NULL, (GaimGroup*)node); } else { - show_add_chat(NULL, NULL); + gaim_blist_request_add_chat(NULL, NULL); } } @@ -339,38 +734,158 @@ if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) - show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL); - else if (GAIM_BLIST_NODE_IS_GROUP(node)) - show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node->parent->parent)->name, + NULL); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) + || GAIM_BLIST_NODE_IS_CHAT(node)) { + gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node->parent)->name, NULL); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node)->name, NULL); + } } else { - show_add_buddy(NULL, NULL, NULL, NULL); + gaim_blist_request_add_buddy(NULL, NULL, NULL, NULL); + } +} + +static void +gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) +{ + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + show_confirm_del((GaimBuddy*)node); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + show_confirm_del_blist_chat((GaimChat*)node); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + show_confirm_del_group((GaimGroup*)node); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + show_confirm_del_contact((GaimContact*)node); } } static void -gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) +gaim_gtk_blist_expand_contact_cb(GtkWidget *w, GaimBlistNode *node) { - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct buddy *b = (struct buddy*)node; - show_confirm_del(b->account->gc, b->name); - } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { - struct chat *chat = (struct chat*)node; - show_confirm_del_chat(chat); - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - struct group *g = (struct group*)node; - show_confirm_del_group(g); + struct _gaim_gtk_blist_node *gtknode; + GaimBlistNode *bnode; + + if(!GAIM_BLIST_NODE_IS_CONTACT(node)) + return; + + gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + + gtknode->contact_expanded = TRUE; + + for(bnode = node->child; bnode; bnode = bnode->next) { + gaim_gtk_blist_update(NULL, bnode); + } + gaim_gtk_blist_update(NULL, node); +} + +static void +gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node) +{ + GaimBlistNode *bnode; + struct _gaim_gtk_blist_node *gtknode; + + if(!GAIM_BLIST_NODE_IS_CONTACT(node)) + return; + + gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + + gtknode->contact_expanded = FALSE; + + for(bnode = node->child; bnode; bnode = bnode->next) { + gaim_gtk_blist_update(NULL, bnode); } } -static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b) +static void gaim_proto_menu_cb(GtkMenuItem *item, GaimBuddy *b) { struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback"); if (pbm->callback) pbm->callback(pbm->gc, b->name); } +static void make_buddy_menu(GtkWidget *menu, GaimPluginProtocolInfo *prpl_info, GaimBuddy *b) +{ + GList *list; + GtkWidget *menuitem; + + if (prpl_info && prpl_info->get_info) { + gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, + G_CALLBACK(gtk_blist_menu_info_cb), b, 0, 0, NULL); + } + gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, + G_CALLBACK(gtk_blist_menu_im_cb), b, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, + G_CALLBACK(gtk_blist_menu_bp_cb), b, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("View _Log"), NULL, + G_CALLBACK(gtk_blist_menu_showlog_cb), b, 0, 0, NULL); + + if (prpl_info && prpl_info->buddy_menu) { + list = prpl_info->buddy_menu(b->account->gc, b->name); + while (list) { + struct proto_buddy_menu *pbm = list->data; + menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); + g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(gaim_proto_menu_cb), b); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + list = list->next; + } + } + + if (gaim_prpl_has_send_file (b->account->gc, b->name)) + gaim_new_item_from_stock(menu, _("Send _File"), NULL, + G_CALLBACK(gtk_blist_menu_send_file_cb), b, 0, 0, NULL); + + + gaim_signal_emit(GAIM_GTK_BLIST(gaim_get_blist()), + "drawing-menu", menu, b); + + gaim_separator(menu); + gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, + G_CALLBACK(gtk_blist_menu_alias_cb), b, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), b, + 0, 0, NULL); +} + +static gboolean gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, + gpointer null) +{ + GaimBlistNode *node; + GValue val = { 0, }; + GtkTreeIter iter; + GtkTreeSelection *sel; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); + if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) + return FALSE; + + gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, + NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + + if(event->state & GDK_CONTROL_MASK && + (event->keyval == 'o' || event->keyval == 'O')) { + GaimBuddy *buddy; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + buddy = (GaimBuddy*)node; + } else { + return FALSE; + } + if(buddy) + serv_get_info(buddy->account->gc, buddy->name); + } + + return FALSE; +} + static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) { GtkTreePath *path; @@ -379,12 +894,10 @@ GtkTreeIter iter; GtkWidget *menu, *menuitem; GtkTreeSelection *sel; - GList *list; GaimPlugin *prpl = NULL; GaimPluginProtocolInfo *prpl_info = NULL; - - if (event->button != 3) - return FALSE; + struct _gaim_gtk_blist_node *gtknode; + gboolean handled = FALSE; /* Here we figure out which node was clicked */ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) @@ -392,71 +905,163 @@ gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); node = g_value_get_pointer(&val); - menu = gtk_menu_new(); - - if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL); - } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { - gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + + if (GAIM_BLIST_NODE_IS_GROUP(node) && + event->button == 3 && event->type == GDK_BUTTON_PRESS) { + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, + G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, + G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Rename"), NULL, + G_CALLBACK(show_rename_group), node, 0, 0, NULL); + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); + + handled = TRUE; + } else if (GAIM_BLIST_NODE_IS_CHAT(node) && + event->button == 3 && event->type == GDK_BUTTON_PRESS) { + GaimChat *chat = (GaimChat *)node; + const char *autojoin = gaim_chat_get_setting(chat, "gtk-autojoin"); + + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, + G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); + gaim_new_check_item(menu, _("Auto-Join"), + G_CALLBACK(gtk_blist_menu_autojoin_cb), node, + (autojoin && !strcmp(autojoin, "true"))); + gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, + G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); + + handled = TRUE; + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && + event->state & GDK_CONTROL_MASK && event->button == 2 && + event->type == GDK_BUTTON_PRESS) { + if(gtknode->contact_expanded) + gaim_gtk_blist_collapse_contact_cb(NULL, node); + else + gaim_gtk_blist_expand_contact_cb(NULL, node); + handled = TRUE; + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded + && event->button == 3 && event->type == GDK_BUTTON_PRESS) { + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, + G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, + G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), + node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); + handled = TRUE; + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || + GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *b; + if(GAIM_BLIST_NODE_IS_CONTACT(node)) + b = gaim_contact_get_priority_buddy((GaimContact*)node); + else + b = (GaimBuddy *)node; + /* Protocol specific options */ - prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol); + prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); if (prpl != NULL) prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl && prpl_info->get_info) - gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL); - - gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); - - if (prpl && prpl_info->buddy_menu) { - list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); - while (list) { - struct proto_buddy_menu *pbm = list->data; - menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); - g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - list = list->next; + if(event->button == 2 && event->type == GDK_2BUTTON_PRESS) { + if (prpl && prpl_info->get_info) + serv_get_info(b->account->gc, b->name); + handled = TRUE; + } else if(event->button == 3 && event->type == GDK_BUTTON_PRESS) { + gboolean show_offline = gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies"); + menu = gtk_menu_new(); + make_buddy_menu(menu, prpl_info, b); + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + gaim_separator(menu); + + if(gtknode->contact_expanded) { + gaim_new_item_from_stock(menu, _("_Collapse"), + GTK_STOCK_ZOOM_OUT, + G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), + node, 0, 0, NULL); + } else { + gaim_new_item_from_stock(menu, _("_Expand"), + GTK_STOCK_ZOOM_IN, + G_CALLBACK(gaim_gtk_blist_expand_contact_cb), node, + 0, 0, NULL); + } + if(node->child->next) { + GaimBlistNode *bnode; + + for(bnode = node->child; bnode; bnode = bnode->next) { + GaimBuddy *buddy = (GaimBuddy*)bnode; + GtkWidget *submenu; + GtkWidget *image; + + if(buddy == b) + continue; + if(!buddy->account->gc) + continue; + if(!show_offline && !GAIM_BUDDY_IS_ONLINE(buddy)) + continue; + + + menuitem = gtk_image_menu_item_new_with_label(buddy->name); + image = gtk_image_new_from_pixbuf( + gaim_gtk_blist_get_status_icon(bnode, + GAIM_STATUS_ICON_SMALL)); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), + image); + gtk_widget_show(image); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + gtk_widget_show(submenu); + + prpl = gaim_find_prpl(gaim_account_get_protocol(buddy->account)); + prpl_info = prpl ? GAIM_PLUGIN_PROTOCOL_INFO(prpl) : NULL; + + make_buddy_menu(submenu, prpl_info, buddy); + } + } } + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, + event->time); + + handled = TRUE; } - - gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name); - - gaim_separator(menu); - gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); #if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal * the event propoagates down and somehow gets interpreted as the start of a drag event. */ - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); - gtk_tree_selection_select_path(sel, path); - gtk_tree_path_free(path); - return TRUE; + if(handled) { + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); + gtk_tree_selection_select_path(sel, path); + gtk_tree_path_free(path); + return TRUE; + } #endif + return FALSE; } static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) { - if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) - blist_options &= ~OPT_BLIST_NO_MT_GRP; - else - blist_options |= OPT_BLIST_NO_MT_GRP; - save_prefs(); - gaim_gtk_blist_refresh(gaim_get_blist()); + gaim_prefs_set_bool("/gaim/gtk/blist/show_empty_groups", + gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); } static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, @@ -469,19 +1074,14 @@ gdk_cursor_unref(cursor); } - if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))) - blist_options |= OPT_BLIST_SHOW_OFFLINE; - else - blist_options &= ~OPT_BLIST_SHOW_OFFLINE; - save_prefs(); + gaim_prefs_set_bool("/gaim/gtk/blist/show_offline_buddies", + gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))); if(gtkblist->window->window) { GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); gdk_window_set_cursor(gtkblist->window->window, cursor); gdk_cursor_unref(cursor); } - - gaim_gtk_blist_refresh(gaim_get_blist()); } static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, @@ -513,9 +1113,120 @@ } +enum {DRAG_BUDDY, DRAG_ROW, DRAG_URI_LIST}; + +struct send_file_data +{ + GaimBuddy *buddy; + + char **uris; +}; + + + +static void send_file_accept (struct send_file_data *data) +{ + GaimBuddy *buddy = data->buddy; + char **s, **uris = data->uris; + + s = uris; + + do { + if (g_str_has_prefix(*s, "file://")) { + char *file = g_strstrip(*s + strlen ("file://")); + + serv_send_file(buddy->account->gc, buddy->name, file); + } + + } while (*(++s)); + + g_free(data); + g_strfreev(uris); +} + + static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, GtkSelectionData *sd, guint info, guint t) -{ +{ + if (info == DRAG_URI_LIST && sd->data) { + GaimBuddy *buddy; + GaimBlistNode *node; + GValue val = {0}; + GtkTreeIter iter; + GtkTreePath *path = NULL; + GtkTreeViewDropPosition position; + + if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { + char **uris; + char **s; + int n = 0; + struct send_file_data *data; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), + &iter, path); + + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), + &iter, NODE_COLUMN, &val); + + node = g_value_get_pointer(&val); + + /* Get the buddy to who we are sending */ + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + buddy = (GaimBuddy*) node; + else if (GAIM_BLIST_NODE_IS_CONTACT(node)) + buddy = gaim_contact_get_priority_buddy ((GaimContact *) node); + else { + gtk_tree_path_free(path); + gtk_drag_finish(dc, TRUE, FALSE, t); + return; + } + + /* Check is the user can accept sends */ + if (!gaim_prpl_has_send_file (buddy->account->gc, buddy->name)) { + gaim_request_action(buddy->account->gc, _("Error"), + _("Error"), _("This user can't accept files"), + 0, NULL, 1, _("OK"), NULL); + return; + } + + uris = s = g_strsplit (sd->data, "\n", 0); + + /* Count how many files the user is trying to send */ + do { + if (g_str_has_prefix (*s, "file://")) + n++; + } while (*(++s)); + + /* Some one is trying to drop something != file:/// */ + if (n == 0 && *uris != NULL) { + gaim_request_action(buddy->account->gc, _("Error"), + _("Error"), _("Gaim just support file:// URIS currently"), + 0, NULL, 1, _("OK"), NULL); + return; + } + + data = g_new (struct send_file_data, 1); + + /* Prepare our data array */ + data->buddy = buddy; + data->uris = uris; + + /* Some one wants to drop lots of files */ + if (n > 2) { + gaim_request_accept_cancel(buddy->account->gc, "?", _("You are trying to send a lot of files"), _("Do you really want to send them ?"), + 0, data, send_file_accept, NULL); + return; + } + + /* If there's nothing unusual just send the file[s] */ + send_file_accept (data); + } + + gtk_tree_path_free(path); + gtk_drag_finish(dc, TRUE, FALSE, t); + return; + } + if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { GaimBlistNode *n = NULL; GtkTreePath *path = NULL; @@ -526,46 +1237,121 @@ GtkTreeIter iter; GaimBlistNode *node; GValue val = {0}; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + struct _gaim_gtk_blist_node *gtknode; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), + &iter, path); + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), + &iter, NODE_COLUMN, &val); node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_BUDDY(n)) { - struct buddy *b = (struct buddy*)n; - if (GAIM_BLIST_NODE_IS_BUDDY(node) || + gtknode = node->ui_data; + + if (GAIM_BLIST_NODE_IS_CONTACT(n)) { + GaimContact *c = (GaimContact*)n; + if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded) { + gaim_blist_merge_contact(c, node); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { switch(position) { case GTK_TREE_VIEW_DROP_AFTER: case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - gaim_blist_add_buddy(b, (struct group*)node->parent, node); + gaim_blist_add_contact(c, (GaimGroup*)node->parent, + node); + break; + case GTK_TREE_VIEW_DROP_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + gaim_blist_add_contact(c, (GaimGroup*)node->parent, + node->prev); + break; + } + } else if(GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_blist_add_contact(c, (GaimGroup*)node, NULL); + } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_blist_merge_contact(c, node); + } + } else if (GAIM_BLIST_NODE_IS_BUDDY(n)) { + GaimBuddy *b = (GaimBuddy*)n; + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + switch(position) { + case GTK_TREE_VIEW_DROP_AFTER: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + gaim_blist_add_buddy(b, (GaimContact*)node->parent, + (GaimGroup*)node->parent->parent, node); break; case GTK_TREE_VIEW_DROP_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev); + gaim_blist_add_buddy(b, (GaimContact*)node->parent, + (GaimGroup*)node->parent->parent, + node->prev); break; } + } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { + gaim_blist_add_buddy(b, NULL, (GaimGroup*)node->parent, + NULL); } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_blist_add_buddy(b, (struct group*)node, NULL); + gaim_blist_add_buddy(b, NULL, (GaimGroup*)node, NULL); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + if(gtknode->contact_expanded) { + switch(position) { + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + case GTK_TREE_VIEW_DROP_AFTER: + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + gaim_blist_add_buddy(b, (GaimContact*)node, + (GaimGroup*)node->parent, NULL); + break; + case GTK_TREE_VIEW_DROP_BEFORE: + gaim_blist_add_buddy(b, NULL, + (GaimGroup*)node->parent, node->prev); + break; + } + } else { + switch(position) { + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + case GTK_TREE_VIEW_DROP_AFTER: + gaim_blist_add_buddy(b, NULL, + (GaimGroup*)node->parent, NULL); + break; + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + case GTK_TREE_VIEW_DROP_BEFORE: + gaim_blist_add_buddy(b, NULL, + (GaimGroup*)node->parent, node->prev); + break; + } + } } } else if (GAIM_BLIST_NODE_IS_CHAT(n)) { - struct chat *chat = (struct chat*)n; - if (GAIM_BLIST_NODE_IS_BUDDY(node) || + GaimChat *chat = (GaimChat *)n; + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + switch(position) { + case GTK_TREE_VIEW_DROP_AFTER: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + gaim_blist_add_chat(chat, + (GaimGroup*)node->parent->parent, node); + break; + case GTK_TREE_VIEW_DROP_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + gaim_blist_add_chat(chat, + (GaimGroup*)node->parent->parent, + node->prev); + break; + } + } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { switch(position) { case GTK_TREE_VIEW_DROP_AFTER: case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - gaim_blist_add_chat(chat, (struct group*)node->parent, node); + gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node); break; case GTK_TREE_VIEW_DROP_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - gaim_blist_add_chat(chat, (struct group*)node->parent, node->prev); + gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node->prev); break; } } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_blist_add_chat(chat, (struct group*)node, NULL); + gaim_blist_add_chat(chat, (GaimGroup*)node, NULL); } } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { - struct group *g = (struct group*)n; + GaimGroup *g = (GaimGroup*)n; if (GAIM_BLIST_NODE_IS_GROUP(node)) { switch (position) { case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: @@ -577,15 +1363,17 @@ gaim_blist_add_group(g, node->prev); break; } - - } else if(GAIM_BLIST_NODE_IS_BUDDY(node) || + } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_blist_add_group(g, node->parent->parent); + } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { gaim_blist_add_group(g, node->parent); } - } gtk_tree_path_free(path); + gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); + gaim_blist_save(); } } @@ -598,6 +1386,9 @@ PangoLayout *layout; char *tooltiptext = gaim_get_tooltip_text(node); + if(!tooltiptext) + return; + layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); pango_layout_set_wrap(layout, PANGO_WRAP_WORD); @@ -620,6 +1411,12 @@ g_object_unref (pixbuf); g_object_unref (layout); g_free(tooltiptext); + +#if GTK_CHECK_VERSION(2,2,0) + shadow_paint(gtkblist, NULL, EAST_SIDE); + shadow_paint(gtkblist, NULL, SOUTH_SIDE); +#endif + return; } @@ -632,25 +1429,60 @@ int scr_w,scr_h, w, h, x, y; PangoLayout *layout; char *tooltiptext = NULL; +#if GTK_CHECK_VERSION(2,2,0) + GdkWindowAttr attr; +#endif if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL)) return FALSE; gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); node = g_value_get_pointer(&val); - if(!GAIM_BLIST_NODE_IS_BUDDY(node) && !GAIM_BLIST_NODE_IS_CHAT(node)) + gtk_tree_path_free(path); + + if(!GAIM_BLIST_NODE_IS_CONTACT(node) && !GAIM_BLIST_NODE_IS_BUDDY(node) + && !GAIM_BLIST_NODE_IS_CHAT(node)) return FALSE; tooltiptext = gaim_get_tooltip_text(node); + + if(!tooltiptext) + return FALSE; + gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); - gtkblist->tipwindow->parent = tv; gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", G_CALLBACK(gaim_gtk_blist_paint_tip), node); gtk_widget_ensure_style (gtkblist->tipwindow); - + +#if GTK_CHECK_VERSION(2,2,0) + attr.window_type = GDK_WINDOW_TEMP; + attr.override_redirect = TRUE; + attr.x = gtkblist->tipwindow->allocation.x; + attr.y = gtkblist->tipwindow->allocation.y; + attr.width = gtkblist->tipwindow->allocation.width; + attr.height = gtkblist->tipwindow->allocation.height; + attr.wclass = GDK_INPUT_OUTPUT; + attr.visual = gtk_widget_get_visual (gtkblist->window); + attr.colormap = gtk_widget_get_colormap (gtkblist->window); + + attr.event_mask = gtk_widget_get_events (gtkblist->tipwindow); + + attr.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); + gtkblist->east_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, + GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); + gdk_window_set_user_data (gtkblist->east_shadow, gtkblist->tipwindow); + gdk_window_set_back_pixmap (gtkblist->east_shadow, NULL, FALSE); + + gtkblist->south_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, + GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); + gdk_window_set_user_data (gtkblist->south_shadow, gtkblist->tipwindow); + gdk_window_set_back_pixmap (gtkblist->south_shadow, NULL, FALSE); +#endif + layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); pango_layout_set_wrap(layout, PANGO_WRAP_WORD); pango_layout_set_width(layout, 300000); @@ -673,21 +1505,25 @@ x -= ((w >> 1) + 4); if ((x + w) > scr_w) - x -= (x + w) - scr_w; + x -= (x + w + 5) - scr_w; else if (x < 0) x = 0; if ((y + h + 4) > scr_h) - y = y - h; + y = y - h - 5; else y = y + 6; + g_object_unref (layout); g_free(tooltiptext); gtk_widget_set_size_request(gtkblist->tipwindow, w, h); gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); gtk_widget_show(gtkblist->tipwindow); - gtk_tree_path_free(path); +#if GTK_CHECK_VERSION(2,2,0) + map_shadow_windows(gtkblist); +#endif + return FALSE; } @@ -700,9 +1536,18 @@ /* We've left the cell. Remove the timeout and create a new one below */ if (gtkblist->tipwindow) { gtk_widget_destroy(gtkblist->tipwindow); +#if GTK_CHECK_VERSION(2,2,0) + gdk_window_set_user_data (gtkblist->east_shadow, NULL); + gdk_window_destroy (gtkblist->east_shadow); + gtkblist->east_shadow = NULL; + + gdk_window_set_user_data (gtkblist->south_shadow, NULL); + gdk_window_destroy (gtkblist->south_shadow); + gtkblist->south_shadow = NULL; +#endif gtkblist->tipwindow = NULL; } - + g_source_remove(gtkblist->timeout); } @@ -722,6 +1567,15 @@ } if (gtkblist->tipwindow) { gtk_widget_destroy(gtkblist->tipwindow); +#if GTK_CHECK_VERSION(2,2,0) + gdk_window_set_user_data (gtkblist->east_shadow, NULL); + gdk_window_destroy (gtkblist->east_shadow); + gtkblist->east_shadow = NULL; + + gdk_window_set_user_data (gtkblist->south_shadow, NULL); + gdk_window_destroy (gtkblist->south_shadow); + gtkblist->south_shadow = NULL; +#endif gtkblist->tipwindow = NULL; } } @@ -729,14 +1583,8 @@ static void toggle_debug(void) { - misc_options ^= OPT_MISC_DEBUG; - - if ((misc_options & OPT_MISC_DEBUG)) - gaim_gtk_debug_window_show(); - else - gaim_gtk_debug_window_hide(); - - save_prefs(); + gaim_prefs_set_bool("/gaim/gtk/debug/enabled", + !gaim_prefs_get_bool("/gaim/gtk/debug/enabled")); } @@ -755,10 +1603,10 @@ { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"}, { N_("/Buddies/_Add a Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, { N_("/Buddies/Add a C_hat..."), NULL, gaim_gtk_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD }, - { N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL}, + { N_("/Buddies/Add a _Group..."), NULL, gaim_blist_request_add_group, 0, NULL}, { "/Buddies/sep2", NULL, NULL, 0, "<Separator>" }, - { N_("/Buddies/_Signoff"), "<CTL>D", signoff_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF }, - { N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, + { N_("/Buddies/_Signoff"), "<CTL>D", gaim_connections_disconnect_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF }, + { N_("/Buddies/_Quit"), "<CTL>Q", gaim_core_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, /* Tools */ { N_("/_Tools"), NULL, NULL, 0, "<Branch>" }, @@ -766,50 +1614,62 @@ { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" }, { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" }, { "/Tools/sep1", NULL, NULL, 0, "<Separator>" }, - { N_("/Tools/A_ccounts..."), "<CTL>A", account_editor, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, + { N_("/Tools/A_ccounts"), "<CTL>A", gaim_gtk_accounts_window_show, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, { N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, - { N_("/Tools/Preferences..."), "<CTL>P", show_prefs, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, - { N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "<StockItem>", GAIM_STOCK_PRIVACY }, + { N_("/Tools/Preferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, + { N_("/Tools/Pr_ivacy"), NULL, gaim_gtk_privacy_dialog_show, 0, "<StockItem>", GAIM_STOCK_PRIVACY }, { "/Tools/sep2", NULL, NULL, 0, "<Separator>" }, - { N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, + { N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, /* Help */ { N_("/_Help"), NULL, NULL, 0, "<Branch>" }, { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, - { N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL }, - { N_("/Help/_About..."), "<CTL>F1", show_about, 0, "<StockItem>", GAIM_STOCK_ABOUT }, + { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, NULL }, + { N_("/Help/_About"), NULL, show_about, 0, "<StockItem>", GAIM_STOCK_ABOUT }, }; /********************************************************* * Private Utility functions * *********************************************************/ +static void +rename_group_cb(GaimGroup *g, const char *new_name) +{ + gaim_blist_rename_group(g, new_name); + gaim_blist_save(); +} + +static void +show_rename_group(GtkWidget *unused, GaimGroup *g) +{ + gaim_request_input(NULL, _("Rename Group"), _("New group name"), + _("Please enter a new name for the selected group."), + g->name, FALSE, FALSE, + _("OK"), G_CALLBACK(rename_group_cb), + _("Cancel"), NULL, g); +} static char *gaim_get_tooltip_text(GaimBlistNode *node) { GaimPlugin *prpl; GaimPluginProtocolInfo *prpl_info = NULL; char *text = NULL; + struct _gaim_gtk_blist_node *gtknode = node->ui_data; if(GAIM_BLIST_NODE_IS_CHAT(node)) { - struct chat *chat = (struct chat *)node; + GaimChat *chat = (GaimChat *)node; char *name = NULL; struct proto_chat_entry *pce; GList *parts, *tmp; GString *parts_text = g_string_new(""); - prpl = gaim_find_prpl(chat->account->protocol); + prpl = gaim_find_prpl(gaim_account_get_protocol(chat->account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); parts = prpl_info->chat_info(chat->account->gc); - if(chat->alias) { - name = g_markup_escape_text(chat->alias, -1); - } else { - pce = parts->data; - name = g_markup_escape_text(g_hash_table_lookup(chat->components, - pce->identifier), -1); - } - if(g_slist_length(connections) > 1) { + name = g_markup_escape_text(gaim_chat_get_name(chat), -1); + + if(g_list_length(gaim_connections_get_all()) > 1) { char *account = g_markup_escape_text(chat->account->username, -1); g_string_append_printf(parts_text, _("\n<b>Account:</b> %s"), account); @@ -819,6 +1679,9 @@ char *label, *value; pce = tmp->data; + if(pce->secret) + continue; + label = g_markup_escape_text(pce->label, -1); value = g_markup_escape_text(g_hash_table_lookup(chat->components, @@ -835,17 +1698,30 @@ name, parts_text->str); g_string_free(parts_text, TRUE); g_free(name); - } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct buddy *b = (struct buddy *)node; + } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || + GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *b; char *statustext = NULL; + char *contactaliastext = NULL; char *aliastext = NULL, *nicktext = NULL; char *warning = NULL, *idletime = NULL; char *accounttext = NULL; - prpl = gaim_find_prpl(b->account->protocol); + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimContact *contact = (GaimContact*)node; + if(gtknode->contact_expanded) + return NULL; + b = gaim_contact_get_priority_buddy(contact); + if(contact->alias) + contactaliastext = g_markup_escape_text(contact->alias, -1); + } else { + b = (GaimBuddy *)node; + } + + prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info->tooltip_text) { + if (prpl_info && prpl_info->tooltip_text) { const char *end; statustext = prpl_info->tooltip_text(b); @@ -861,7 +1737,7 @@ statustext = g_strdup(_("<b>Status:</b> Offline")); if (b->idle > 0) - idletime = sec_to_text(time(NULL) - b->idle); + idletime = gaim_str_seconds_to_string(time(NULL) - b->idle); if(b->alias && b->alias[0]) aliastext = g_markup_escape_text(b->alias, -1); @@ -872,11 +1748,12 @@ if (b->evil > 0) warning = g_strdup_printf(_("%d%%"), b->evil); - if(g_slist_length(connections) > 1) + if(g_list_length(gaim_connections_get_all()) > 1) accounttext = g_markup_escape_text(b->account->username, -1); text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>" "%s %s" /* Account */ + "%s %s" /* Contact Alias */ "%s %s" /* Alias */ "%s %s" /* Nickname */ "%s %s" /* Idle */ @@ -885,13 +1762,15 @@ "%s", b->name, accounttext ? _("\n<b>Account:</b>") : "", accounttext ? accounttext : "", + contactaliastext ? _("\n<b>Contact Alias:</b>") : "", contactaliastext ? contactaliastext : "", aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "", nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "", idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "", b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "", statustext ? "\n" : "", statustext ? statustext : "", !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : - !g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") : ""); + !g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") : + !g_ascii_strcasecmp(b->name, "chipx86") ? _("\n<b>Status</b>: Rockin'") : ""); if(warning) g_free(warning); @@ -910,196 +1789,152 @@ return text; } +struct _emblem_data { + char *filename; + int x; + int y; +}; + GdkPixbuf *gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size) { - GdkPixbuf *status = NULL; - GdkPixbuf *scale = NULL; - GdkPixbuf *emblem = NULL; - gchar *filename = NULL; + GdkPixbuf *scale, *status = NULL; + int i, scalesize = 30; + char *filename; const char *protoname = NULL; - - char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL; - - int scalesize = 30; - - GaimPlugin *prpl = NULL; - GaimPluginProtocolInfo *prpl_info = NULL; - - if(GAIM_BLIST_NODE_IS_BUDDY(node)) - prpl = gaim_find_prpl(((struct buddy *)node)->account->protocol); - else if(GAIM_BLIST_NODE_IS_CHAT(node)) - prpl = gaim_find_prpl(((struct chat *)node)->account->protocol); - - if (!prpl) + struct _gaim_gtk_blist_node *gtknode = node->ui_data; + struct _emblem_data emblems[4] = {{NULL, 15, 15}, {NULL, 0, 15}, + {NULL, 0, 0}, {NULL, 15, 0}}; + + GaimBuddy *buddy = NULL; + GaimChat *chat = NULL; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + if(!gtknode->contact_expanded) + buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + buddy = (GaimBuddy*)node; + } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { + chat = (GaimChat*)node; + } else { return NULL; - - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->list_icon) { - if(GAIM_BLIST_NODE_IS_BUDDY(node)) - protoname = prpl_info->list_icon(((struct buddy*)node)->account, - (struct buddy *)node); - else if(GAIM_BLIST_NODE_IS_CHAT(node)) - protoname = prpl_info->list_icon(((struct chat*)node)->account, NULL); } - if (GAIM_BLIST_NODE_IS_BUDDY(node) && - ((struct buddy *)node)->present != GAIM_BUDDY_SIGNING_OFF && - prpl_info->list_emblems) { - prpl_info->list_emblems((struct buddy*)node, &se, &sw, &nw, &ne); + if(buddy || chat) { + GaimAccount *account; + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info; + + if(buddy) + account = buddy->account; + else + account = chat->account; + + prpl = gaim_find_prpl(gaim_account_get_protocol(account)); + if(!prpl) + return NULL; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if(prpl_info && prpl_info->list_icon) { + protoname = prpl_info->list_icon(account, buddy); + } + if(prpl_info && prpl_info->list_emblems && buddy) { + if(buddy->present != GAIM_BUDDY_SIGNING_OFF) + prpl_info->list_emblems(buddy, &emblems[0].filename, + &emblems[1].filename, &emblems[2].filename, + &emblems[3].filename); + } } - if (size == GAIM_STATUS_ICON_SMALL) { + if(size == GAIM_STATUS_ICON_SMALL) { scalesize = 15; - sw = nw = ne = NULL; /* So that only the se icon will composite */ + /* So that only the se icon will composite */ + emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL; } - - if (GAIM_BLIST_NODE_IS_BUDDY(node) && - ((struct buddy*)node)->present == GAIM_BUDDY_SIGNING_ON) { - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && - ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - - /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and - then it will look up protoname from the theme */ - } else { + if(buddy && buddy->present == GAIM_BUDDY_SIGNING_ON) { + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); + } else if(buddy && buddy->present == GAIM_BUDDY_SIGNING_OFF) { + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); + } else if(buddy || chat) { char *image = g_strdup_printf("%s.png", protoname); filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(image); - g_free(filename); - - } - - if (!status) - return NULL; - - scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR); - - g_object_unref(G_OBJECT(status)); - - /* Emblems */ - - /* Each protocol can specify up to four "emblems" to composite over the base icon. "away", "busy", "mobile user" - * are all examples of states represented by emblems. I'm not even really sure I like this yet. */ - - /* XXX Clean this crap up, yo. */ - if (se) { - char *image = g_strdup_printf("%s.png", se); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - if (size == GAIM_STATUS_ICON_LARGE) - gdk_pixbuf_composite (emblem, - scale, 15, 15, - 15, 15, - 15, 15, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - else - gdk_pixbuf_composite (emblem, - scale, 5, 5, - 10, 10, - 5, 5, - .6, .6, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } + } else { + /* gaim dude */ + filename = g_build_filename(DATADIR, "pixmaps", "gaim.png", NULL); } - if (sw) { - char *image = g_strdup_printf("%s.png", sw); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 0, 15, - 15, 15, - 0, 15, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); + + status = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + + if(!status) + return NULL; + + scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, + GDK_INTERP_BILINEAR); + g_object_unref(status); + + for(i=0; i<4; i++) { + if(emblems[i].filename) { + GdkPixbuf *emblem; + char *image = g_strdup_printf("%s.png", emblems[i].filename); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + g_free(image); + emblem = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + if(emblem) { + if(i == 0 && size == GAIM_STATUS_ICON_SMALL) { + gdk_pixbuf_composite(emblem, + scale, 5, 5, + 10, 10, + 5, 5, + .6, .6, + GDK_INTERP_BILINEAR, + 255); + } else { + gdk_pixbuf_composite(emblem, + scale, emblems[i].x, emblems[i].y, + 15, 15, + emblems[i].x, emblems[i].y, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + } + g_object_unref(emblem); + } } } - if (nw) { - char *image = g_strdup_printf("%s.png", nw); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 0, 0, - 15, 15, - 0, 0, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } + + if(buddy) { + if(buddy->present == GAIM_BUDDY_OFFLINE) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); + else if(buddy->idle && + gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); } - if (ne) { - char *image = g_strdup_printf("%s.png", ne); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 15, 0, - 15, 15, - 15, 0, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } - } - - - /* Idle grey buddies affects the whole row. This converts the status icon to greyscale. */ - if (GAIM_BLIST_NODE_IS_BUDDY(node) && - ((struct buddy *)node)->present == GAIM_BUDDY_OFFLINE) - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); - else if (GAIM_BLIST_NODE_IS_BUDDY(node) && - ((struct buddy *)node)->idle && - blist_options & OPT_BLIST_GREY_IDLERS) - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); + return scale; } -static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b) +static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(GaimBuddy *b) { - /* This just opens a file from ~/.gaim/icons/screenname. This needs to change to be more gooder. */ - char *file; + const char *file; GdkPixbuf *buf, *ret; - if (!(blist_options & OPT_BLIST_SHOW_ICONS)) + if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) return NULL; if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL) return NULL; buf = gdk_pixbuf_new_from_file(file, NULL); - g_free(file); if (buf) { if (!GAIM_BUDDY_IS_ONLINE(b)) gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); - if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) + if (b->idle && gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); @@ -1109,30 +1944,47 @@ return NULL; } -static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected) +static gchar *gaim_gtk_blist_get_name_markup(GaimBuddy *b, gboolean selected) { - char *name = gaim_get_buddy_alias(b); - char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL; + const char *name; + char *esc, *text = NULL; GaimPlugin *prpl; GaimPluginProtocolInfo *prpl_info = NULL; - /* XXX Clean up this crap */ - + GaimContact *contact; + struct _gaim_gtk_blist_node *gtkcontactnode = NULL; int ihrs, imin; char *idletime = NULL, *warning = NULL, *statustext = NULL; time_t t; - - prpl = gaim_find_prpl(b->account->protocol); + /* XXX Clean up this crap */ + + contact = (GaimContact*)((GaimBlistNode*)b)->parent; + if(contact) + gtkcontactnode = ((GaimBlistNode*)contact)->ui_data; + + if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) + name = contact->alias; + else + name = gaim_get_buddy_alias(b); + esc = g_markup_escape_text(name, strlen(name)); + + prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); if (prpl != NULL) prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { - if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) { - text = g_strdup_printf("<span color='dim grey'>%s</span>", - esc); + if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { + if ((b->idle && !selected && + gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) || + !GAIM_BUDDY_IS_ONLINE(b)) { + if (selected) + text = g_strdup(esc); + else + text = g_strdup_printf("<span color='dim grey'>%s</span>", + esc); g_free(esc); return text; - } else { + } + else { return esc; } } @@ -1141,7 +1993,7 @@ ihrs = (t - b->idle) / 3600; imin = ((t - b->idle) / 60) % 60; - if (prpl && prpl_info->status_text) { + if (prpl && prpl_info->status_text && b->account->gc) { char *tmp = prpl_info->status_text(b); const char *end; @@ -1158,6 +2010,7 @@ int length = 0, vis=0; gboolean inside = FALSE; g_strdelimit(tmp, "\n", ' '); + gaim_str_strip_cr(tmp); while(*c && vis < 20) { if(*c == '&') @@ -1166,10 +2019,11 @@ inside = FALSE; if(!inside) vis++; - length++; - c++; /* this is fun */ + c = g_utf8_next_char(c); /* this is fun */ } + length = c - tmp; + if(vis == 20) g_snprintf(buf, sizeof(buf), "%%.%ds...", length); else @@ -1181,34 +2035,39 @@ } } - if (b->idle > 0) { + if (b->idle > 0 && + gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")) { if (ihrs) idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); else idletime = g_strdup_printf(_("Idle (%dm) "), imin); } - if (b->evil > 0) + if (b->evil > 0 && + gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")) warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) - statustext = g_strdup("Offline "); - - if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) { + statustext = g_strdup(_("Offline ")); + + if (b->idle && !selected && + gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) { + text = g_strdup_printf("<span color='dim grey'>%s</span>\n" "<span color='dim grey' size='smaller'>%s%s%s</span>", esc, statustext != NULL ? statustext : "", idletime != NULL ? idletime : "", warning != NULL ? warning : ""); - } else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) { + } else if (statustext == NULL && idletime == NULL && warning == NULL && + GAIM_BUDDY_IS_ONLINE(b)) { text = g_strdup(esc); } else { text = g_strdup_printf("%s\n" "<span %s size='smaller'>%s%s%s</span>", esc, selected ? "" : "color='dim grey'", statustext != NULL ? statustext : "", - idletime != NULL ? idletime : "", + idletime != NULL ? idletime : "", warning != NULL ? warning : ""); } if (idletime) @@ -1225,39 +2084,49 @@ static void gaim_gtk_blist_restore_position() { - /* if the window exists, is hidden, we're saving positions, and the position is sane... */ - if(gtkblist && gtkblist->window && - !GTK_WIDGET_VISIBLE(gtkblist->window) && - blist_pos.width != 0) { + int blist_x, blist_y, blist_width, blist_height; + + blist_width = gaim_prefs_get_int("/gaim/gtk/blist/width"); + + /* if the window exists, is hidden, we're saving positions, and the + * position is sane... */ + if (gtkblist && gtkblist->window && + !GTK_WIDGET_VISIBLE(gtkblist->window) && blist_width != 0) { + + blist_x = gaim_prefs_get_int("/gaim/gtk/blist/x"); + blist_y = gaim_prefs_get_int("/gaim/gtk/blist/y"); + blist_height = gaim_prefs_get_int("/gaim/gtk/blist/height"); + /* ...check position is on screen... */ - if (blist_pos.x >= gdk_screen_width()) - blist_pos.x = gdk_screen_width() - 100; - else if (blist_pos.x < 0) - blist_pos.x = 100; - - if (blist_pos.y >= gdk_screen_height()) - blist_pos.y = gdk_screen_height() - 100; - else if (blist_pos.y < 0) - blist_pos.y = 100; - + if (blist_x >= gdk_screen_width()) + blist_x = gdk_screen_width() - 100; + else if (blist_x + blist_width < 0) + blist_x = 100; + + if (blist_y >= gdk_screen_height()) + blist_y = gdk_screen_height() - 100; + else if (blist_y + blist_height < 0) + blist_y = 100; + /* ...and move it back. */ - gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y); - gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height); + gtk_window_move(GTK_WINDOW(gtkblist->window), blist_x, blist_y); + gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_width, blist_height); } } -static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list) +static gboolean gaim_gtk_blist_refresh_timer(GaimBuddyList *list) { - GaimBlistNode *group, *buddy; - - for(group = list->root; group; group = group->next) { - if(!GAIM_BLIST_NODE_IS_GROUP(group)) + GaimBlistNode *gnode, *cnode; + + for(gnode = list->root; gnode; gnode = gnode->next) { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue; - for(buddy = group->child; buddy; buddy = buddy->next) { - if(!GAIM_BLIST_NODE_IS_BUDDY(buddy)) - continue; - if (((struct buddy *)buddy)->idle) - gaim_gtk_blist_update(list, buddy); + for(cnode = gnode->child; cnode; cnode = cnode->next) { + if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { + GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)cnode); + if(buddy && buddy->idle) + gaim_gtk_blist_update(list, cnode); + } } } @@ -1265,7 +2134,7 @@ return TRUE; } -static void gaim_gtk_blist_hide_node(struct gaim_buddy_list *list, GaimBlistNode *node) +static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node) { struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; GtkTreeIter iter; @@ -1278,7 +2147,8 @@ if (get_iter_from_node(node, &iter)) { gtk_tree_store_remove(gtkblist->treemodel, &iter); - if(GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { + if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node) + || GAIM_BLIST_NODE_IS_CHAT(node)) { gaim_gtk_blist_update(list, node->parent); } } @@ -1286,13 +2156,56 @@ gtknode->row = NULL; } +static void +signed_on_off_cb(GaimConnection *gc, GaimBuddyList *blist) +{ + gaim_gtk_blist_update_protocol_actions(); +} + +/* this is called on all sorts of signals, and we have no reason to pass + * it anything, so it remains without arguments. If you need anything + * more specific, do as below, and create another callback that calls + * this */ +static void +raise_on_events_cb() +{ + if(gtkblist && gtkblist->window && + gaim_prefs_get_bool("/gaim/gtk/blist/raise_on_events")) { + gtk_widget_show(gtkblist->window); + gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); + gdk_window_raise(gtkblist->window->window); + } +} + /********************************************************************************** * Public API Functions * **********************************************************************************/ -static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist) +static void gaim_gtk_blist_new_list(GaimBuddyList *blist) { - blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1); + GaimGtkBuddyList *gtkblist; + + gtkblist = g_new0(GaimGtkBuddyList, 1); + blist->ui_data = gtkblist; + + /* Setup some gaim signal handlers. */ + gaim_signal_connect(gaim_connections_get_handle(), "signing-on", + gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); + gaim_signal_connect(gaim_connections_get_handle(), "signing-off", + gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); + + /* Register some of our own. */ + gaim_signal_register(gtkblist, "drawing-menu", + gaim_marshal_VOID__POINTER_POINTER, NULL, 2, + gaim_value_new(GAIM_TYPE_BOXED, "GtkMenu"), + gaim_value_new(GAIM_TYPE_SUBTYPE, + GAIM_SUBTYPE_BLIST_BUDDY)); + + /* All of these signal handlers are for the "Raise on Events" option */ + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", + gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", + gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); } static void gaim_gtk_blist_new_node(GaimBlistNode *node) @@ -1305,36 +2218,48 @@ if(!gtkblist) return; - if (blist_options & OPT_BLIST_SHOW_ICONS) { + if (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); } else { - gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME); - gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN); + gtk_tree_view_column_set_visible(gtkblist->idle_column, + gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")); + gtk_tree_view_column_set_visible(gtkblist->warning_column, + gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")); gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); } } -enum {DRAG_BUDDY, DRAG_ROW}; static char * item_factory_translate_func (const char *path, gpointer func_data) { - return _(path); + return _((char *)path); } void gaim_gtk_blist_setup_sort_methods() { - gaim_gtk_blist_sort_method_reg(_("None"), sort_method_none); - gaim_gtk_blist_sort_method_reg(_("Alphabetical"), sort_method_alphabetical); - gaim_gtk_blist_sort_method_reg(_("By status"), sort_method_status); - gaim_gtk_blist_sort_method_reg(_("By log size"), sort_method_log); - gaim_gtk_blist_sort_method_set(sort_method[0] ? sort_method : _("None")); -} - - -static void gaim_gtk_blist_show(struct gaim_buddy_list *list) + gaim_gtk_blist_sort_method_reg("none", _("None"), sort_method_none); +#if GTK_CHECK_VERSION(2,2,1) + gaim_gtk_blist_sort_method_reg("alphabetical", _("Alphabetical"), sort_method_alphabetical); + gaim_gtk_blist_sort_method_reg("status", _("By status"), sort_method_status); + gaim_gtk_blist_sort_method_reg("log_size", _("By log size"), sort_method_log); +#endif + gaim_gtk_blist_sort_method_set(gaim_prefs_get_string("/gaim/gtk/blist/sort_type")); +} + +static void _prefs_change_redo_list() { + redo_buddy_list(gaim_get_blist(), TRUE); +} + +static void _prefs_change_sort_method(const char *pref_name, GaimPrefType type, + gpointer val, gpointer data) { + if(!strcmp(pref_name, "/gaim/gtk/blist/sort_type")) + gaim_gtk_blist_sort_method_set(val); +} + +static void gaim_gtk_blist_show(GaimBuddyList *list) { GtkCellRenderer *rend; GtkTreeViewColumn *column; @@ -1344,7 +2269,8 @@ GtkAccelGroup *accel_group; GtkTreeSelection *selection; GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, - {"application/x-im-contact", 0, DRAG_BUDDY}}; + {"application/x-im-contact", 0, DRAG_BUDDY}, + {"text/uri-list", 0, DRAG_URI_LIST}}; if (gtkblist && gtkblist->window) { gtk_widget_show(gtkblist->window); @@ -1377,6 +2303,9 @@ NULL, NULL); gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), blist_menu, NULL); + gaim_gtk_load_accels(); + g_signal_connect(G_OBJECT(accel_group), "accel-changed", + G_CALLBACK(gaim_gtk_save_accels_cb), NULL); gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>"), FALSE, FALSE, 0); awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away")); @@ -1386,15 +2315,15 @@ gaim_gtkpounce_menu_build(gtkblist->bpmenu); protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Protocol Actions")); - do_proto_menu(); - + gaim_gtk_blist_update_protocol_actions(); /****************************** GtkTreeView **********************************/ sw = gtk_scrolled_window_new(NULL,NULL); 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_AUTOMATIC); - gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); + gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, + GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); gtk_widget_set_size_request(gtkblist->treeview, -1, 200); @@ -1408,8 +2337,14 @@ /* Set up dnd */ gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte, 2, GDK_ACTION_COPY); - gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, - GDK_ACTION_COPY | GDK_ACTION_MOVE); + + /* This doesn't pass the info parameter for some reason, maybe a GTK+ bug + gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte_drop, 3, + GDK_ACTION_COPY | GDK_ACTION_MOVE);*/ + + gtk_drag_dest_set(GTK_WIDGET (gtkblist->treeview), 0, gte, + 3, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); @@ -1423,7 +2358,7 @@ rend = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start (column, rend, FALSE); - gtk_tree_view_column_set_attributes (column, rend, + gtk_tree_view_column_set_attributes (column, rend, "pixbuf", STATUS_ICON_COLUMN, "visible", STATUS_ICON_VISIBLE_COLUMN, NULL); @@ -1431,7 +2366,7 @@ rend = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start (column, rend, TRUE); - gtk_tree_view_column_set_attributes (column, rend, + gtk_tree_view_column_set_attributes (column, rend, "markup", NAME_COLUMN, NULL); g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); @@ -1457,6 +2392,7 @@ g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL); /* Enable CTRL+F searching */ gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); @@ -1469,9 +2405,9 @@ * after the treeview or faceprint gets mad. -Robot101 */ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))), - blist_options & OPT_BLIST_SHOW_OFFLINE); + gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))), - !(blist_options & OPT_BLIST_NO_MT_GRP)); + gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups")); /* OK... let's show this bad boy. */ gaim_gtk_blist_refresh(list); @@ -1526,27 +2462,109 @@ gaim_gtk_blist_update_toolbar(); /* start the refresh timer */ - if (blist_options & (OPT_BLIST_SHOW_IDLETIME | OPT_BLIST_SHOW_ICONS)) - gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list); + if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || + gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { + + gtkblist->refresh_timer = g_timeout_add(30000, + (GSourceFunc)gaim_gtk_blist_refresh_timer, list); + } + + /* attach prefs callbacks */ + /* for the toolbar buttons */ + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/button_style", + gaim_gtk_blist_update_toolbar, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_buttons", + gaim_gtk_blist_update_toolbar, NULL))); + + /* things that affect how buddies are displayed */ + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/grey_idle_buddies", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_empty_groups", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_group_count", + _prefs_change_redo_list, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_offline_buddies", + _prefs_change_redo_list, NULL))); + + /* sorting */ + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/sort_type", + _prefs_change_sort_method, NULL))); + + /* things that affect what columns are displayed */ + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", + gaim_gtk_blist_update_columns, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", + gaim_gtk_blist_update_columns, NULL))); + blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, + GINT_TO_POINTER( + gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", + gaim_gtk_blist_update_columns, NULL))); } -static void redo_buddy_list(struct gaim_buddy_list *list, gboolean remove) +/* XXX: does this need fixing? */ +static void redo_buddy_list(GaimBuddyList *list, gboolean remove) { - GaimBlistNode *group, *buddy; - - for(group = list->root; group; group = group->next) { - if(!GAIM_BLIST_NODE_IS_GROUP(group)) + GaimBlistNode *gnode, *cnode, *bnode; + + for(gnode = list->root; gnode; gnode = gnode->next) { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue; - gaim_gtk_blist_update(list, group); - for(buddy = group->child; buddy; buddy = buddy->next) { - if (remove) - gaim_gtk_blist_hide_node(list, buddy); - gaim_gtk_blist_update(list, buddy); + for(cnode = gnode->child; cnode; cnode = cnode->next) { + if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { + if(remove) + gaim_gtk_blist_hide_node(list, cnode); + + for(bnode = cnode->child; bnode; bnode = bnode->next) { + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + if(remove) + gaim_gtk_blist_hide_node(list, bnode); + gaim_gtk_blist_update(list, bnode); + } + + gaim_gtk_blist_update(list, cnode); + } else if(GAIM_BLIST_NODE_IS_CHAT(cnode)) { + if(remove) + gaim_gtk_blist_hide_node(list, cnode); + + gaim_gtk_blist_update(list, cnode); + } } + gaim_gtk_blist_update(list, gnode); } } -void gaim_gtk_blist_refresh(struct gaim_buddy_list *list) +void gaim_gtk_blist_refresh(GaimBuddyList *list) { redo_buddy_list(list, FALSE); } @@ -1554,14 +2572,17 @@ void gaim_gtk_blist_update_refresh_timeout() { - struct gaim_buddy_list *blist; - struct gaim_gtk_buddy_list *gtkblist; + GaimBuddyList *blist; + GaimGtkBuddyList *gtkblist; blist = gaim_get_blist(); gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); - if (blist_options & (OPT_BLIST_SHOW_IDLETIME | OPT_BLIST_SHOW_ICONS)) { - gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, blist); + if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || + gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { + + gtkblist->refresh_timer = g_timeout_add(30000, + (GSourceFunc)gaim_gtk_blist_refresh_timer, blist); } else { g_source_remove(gtkblist->refresh_timer); gtkblist->refresh_timer = 0; @@ -1572,8 +2593,11 @@ struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; GtkTreePath *path; + /* XXX: why do we assume we have a buddy here? */ if (!gtknode) { - gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "buddy %s has no ui_data\n", ((struct buddy *)node)->name); +#if 0 + gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "buddy %s has no ui_data\n", ((GaimBuddy *)node)->name); +#endif return FALSE; } @@ -1595,29 +2619,26 @@ return TRUE; } -/* - * These state assignments suck. I'm sorry. They're for historical reasons. - * Roll on new prefs. -Robot101 - * - * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image - * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text - * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images - * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none - */ - -static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) { +static void +gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) +{ + GaimButtonStyle style = gaim_prefs_get_int("/gaim/gtk/blist/button_style"); + if (GTK_IS_IMAGE(widget)) { - if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM) + if (style == GAIM_BUTTON_IMAGE || style == GAIM_BUTTON_TEXT_IMAGE) gtk_widget_show(widget); else gtk_widget_hide(widget); - } else if (GTK_IS_LABEL(widget)) { - if (blist_options & OPT_BLIST_NO_BUTTON_TEXT) + } + else if (GTK_IS_LABEL(widget)) { + if (style == GAIM_BUTTON_IMAGE) gtk_widget_hide(widget); else gtk_widget_show(widget); - } else if (GTK_IS_CONTAINER(widget)) { - gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL); + } + else if (GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), + gaim_gtk_blist_update_toolbar_icons, NULL); } } @@ -1625,18 +2646,22 @@ if (!gtkblist) return; - if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM)) + if (gaim_prefs_get_int("/gaim/gtk/blist/button_style") == GAIM_BUTTON_NONE) gtk_widget_hide(gtkblist->bbox); else { - gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL); + gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), + gaim_gtk_blist_update_toolbar_icons, NULL); gtk_widget_show(gtkblist->bbox); } } -static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node) +static void gaim_gtk_blist_remove(GaimBuddyList *list, GaimBlistNode *node) { gaim_gtk_blist_hide_node(list, node); + if(node->parent) + gaim_gtk_blist_update(list, node->parent); + /* There's something I don't understand here */ /* g_free(node->ui_data); node->ui_data = NULL; */ @@ -1673,270 +2698,320 @@ g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); } -static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) { - GaimBlistNode *sibling; - GtkTreeIter siblingiter; +static void insert_node(GaimBuddyList *list, GaimBlistNode *node, GtkTreeIter *iter) +{ + GtkTreeIter parent_iter, cur, *curptr = NULL; + struct _gaim_gtk_blist_node *gtknode = node->ui_data; GtkTreePath *newpath; - struct group *group = (struct group *)node; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - char *esc = g_markup_escape_text(group->name, -1); - char *mark; - - if(blist_options & OPT_BLIST_SHOW_GRPNUM) - mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); - else - mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); - - g_free(esc); - - sibling = node->prev; - while (sibling && !get_iter_from_node(sibling, &siblingiter)) { - sibling = sibling->prev; + + if(!gtknode || !iter) + return; + + if(node->parent && !get_iter_from_node(node->parent, &parent_iter)) + return; + + if(get_iter_from_node(node, &cur)) + curptr = &cur; + + if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { + *iter = current_sort_method->func(node, list, parent_iter, curptr); + } else { + *iter = sort_method_none(node, list, parent_iter, curptr); } - gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL, - sibling ? &siblingiter : NULL); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); + gtk_tree_row_reference_free(gtknode->row); + newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), + iter); + gtknode->row = + gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), + newpath); gtk_tree_path_free(newpath); gtk_tree_store_set(gtkblist->treemodel, iter, - STATUS_ICON_COLUMN, NULL, - STATUS_ICON_VISIBLE_COLUMN, FALSE, - NAME_COLUMN, mark, NODE_COLUMN, node, -1); - g_free(mark); + + if(node->parent) { + GtkTreePath *expand = NULL; + struct _gaim_gtk_blist_node *gtkparentnode = node->parent->ui_data; + + if(GAIM_BLIST_NODE_IS_GROUP(node->parent)) { + if(!gaim_group_get_setting((GaimGroup*)node->parent, "collapsed")) + expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); + } else if(GAIM_BLIST_NODE_IS_CONTACT(node->parent) && + gtkparentnode->contact_expanded) { + expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); + } + if(expand) { + gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, + FALSE); + gtk_tree_path_free(expand); + } + } + } -static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node) +static void gaim_gtk_blist_update_group(GaimBuddyList *list, GaimBlistNode *node) +{ + GaimGroup *group; + + g_return_if_fail(GAIM_BLIST_NODE_IS_GROUP(node)); + + group = (GaimGroup*)node; + + if(gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups") || + gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies") || + gaim_blist_get_group_online_count(group) > 0) { + char *mark, *esc; + GtkTreeIter iter; + + insert_node(list, node, &iter); + + esc = g_markup_escape_text(group->name, -1); + if(gaim_prefs_get_bool("/gaim/gtk/blist/show_group_count")) { + mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", + esc, gaim_blist_get_group_online_count(group), + gaim_blist_get_group_size(group, FALSE)); + } else { + mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); + } + g_free(esc); + + gtk_tree_store_set(gtkblist->treemodel, &iter, + STATUS_ICON_COLUMN, NULL, + STATUS_ICON_VISIBLE_COLUMN, FALSE, + NAME_COLUMN, mark, + NODE_COLUMN, node, + -1); + g_free(mark); + } else { + gaim_gtk_blist_hide_node(list, node); + } +} + +static void buddy_node(GaimBuddy *buddy, GtkTreeIter *iter, GaimBlistNode *node) { - GtkTreeIter iter; - GtkTreePath *expand = NULL, *newpath = NULL; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - gboolean new_entry = FALSE; - - if (!gtkblist || !gtknode) - return; - - if (!get_iter_from_node(node, &iter)) { - new_entry = TRUE; - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) { - GtkTreeIter groupiter; - char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); - - if(node->parent && - !get_iter_from_node(node->parent, &groupiter)) { - /* This buddy's group has not yet been added. - * We do that here */ - make_a_group(node->parent, &groupiter); - } - if(!collapsed) - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); - else - g_free(collapsed); - - iter = current_sort_method->func(node, list, groupiter, NULL); - - if (blist_options & OPT_BLIST_POPUP) { - gtk_widget_show(gtkblist->window); - gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); - gdk_window_raise(gtkblist->window->window); - } - - } - } else if (GAIM_BLIST_NODE_IS_CHAT(node) && - ((struct chat *)node)->account->gc) { - GtkTreeIter groupiter; - GaimBlistNode *oldersibling; - GtkTreeIter oldersiblingiter; - char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); - - if(node->parent && - !get_iter_from_node(node->parent, &groupiter)) { - /* This buddy's group has not yet been added. - * We do that here */ - make_a_group(node->parent, &groupiter); - } - if(!collapsed) - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); - else - g_free(collapsed); - - oldersibling = node->prev; - while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { - oldersibling = oldersibling->prev; - } - - gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - - } else if (GAIM_BLIST_NODE_IS_GROUP(node) && - ((blist_options & OPT_BLIST_SHOW_OFFLINE) || - !(blist_options & OPT_BLIST_NO_MT_GRP))) { - make_a_group(node, &iter); - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); + GdkPixbuf *status, *avatar; + char *mark; + char *warning = NULL, *idle = NULL; + + gboolean selected = (gtkblist->selected_node == node); + + status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy, + (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") + ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); + + avatar = gaim_gtk_blist_get_buddy_icon(buddy); + mark = gaim_gtk_blist_get_name_markup(buddy, selected); + + if (buddy->idle > 0) { + time_t t; + int ihrs, imin; + time(&t); + ihrs = (t - buddy->idle) / 3600; + imin = ((t - buddy->idle) / 60) % 60; + if(ihrs > 0) + idle = g_strdup_printf("(%d:%02d)", ihrs, imin); + else + idle = g_strdup_printf("(%d)", imin); + } + + if (buddy->evil > 0) + warning = g_strdup_printf("%d%%", buddy->evil); + + if (gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies") && + buddy->idle) { + + if(warning && !selected) { + char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>", + warning); + g_free(warning); + warning = w2; + } + + if(idle && !selected) { + char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>", + idle); + g_free(idle); + idle = i2; } - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) { - gtk_tree_store_remove(gtkblist->treemodel, &iter); - } else { - struct group *group = (struct group *)node; - char *esc = g_markup_escape_text(group->name, -1); + } + + gtk_tree_store_set(gtkblist->treemodel, iter, + STATUS_ICON_COLUMN, status, + STATUS_ICON_VISIBLE_COLUMN, TRUE, + NAME_COLUMN, mark, + WARNING_COLUMN, warning, + IDLE_COLUMN, idle, + BUDDY_ICON_COLUMN, avatar, + -1); + + g_free(mark); + if(idle) + g_free(idle); + if(warning) + g_free(warning); + if(status) + g_object_unref(status); + if(avatar) + g_object_unref(avatar); +} + +static void gaim_gtk_blist_update_contact(GaimBuddyList *list, GaimBlistNode *node) +{ + GaimContact *contact; + GaimBuddy *buddy; + struct _gaim_gtk_blist_node *gtknode; + + g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); + + /* First things first, update the group */ + gaim_gtk_blist_update_group(list, node->parent); + + gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + contact = (GaimContact*)node; + buddy = gaim_contact_get_priority_buddy(contact); + + if(buddy && (buddy->present != GAIM_BUDDY_OFFLINE || + (gaim_account_is_connected(buddy->account) && + gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { + GtkTreeIter iter; + + insert_node(list, node, &iter); + + if(gtknode->contact_expanded) { + GdkPixbuf *status; char *mark; - if(blist_options & OPT_BLIST_SHOW_GRPNUM) - mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); - else - mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); - - g_free(esc); + status = gaim_gtk_blist_get_status_icon(node, + (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? + GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); + + mark = g_markup_escape_text(gaim_contact_get_alias(contact), -1); + gtk_tree_store_set(gtkblist->treemodel, &iter, + STATUS_ICON_COLUMN, status, + STATUS_ICON_VISIBLE_COLUMN, TRUE, NAME_COLUMN, mark, + WARNING_COLUMN, NULL, + IDLE_COLUMN, NULL, + BUDDY_ICON_COLUMN, NULL, -1); g_free(mark); + if(status) + g_object_unref(status); + } else { + buddy_node(buddy, &iter, node); } + } else { + gaim_gtk_blist_hide_node(list, node); } - - if (GAIM_BLIST_NODE_IS_CHAT(node) && ((struct chat*)node)->account->gc) { +} + +static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node) +{ + GaimContact *contact; + GaimBuddy *buddy; + struct _gaim_gtk_blist_node *gtkparentnode; + + g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); + + buddy = (GaimBuddy*)node; + contact = (GaimContact*)node->parent; + gtkparentnode = (struct _gaim_gtk_blist_node *)node->parent->ui_data; + + /* First things first, update the contact */ + gaim_gtk_blist_update_contact(list, node->parent); + + if(gtkparentnode->contact_expanded && + (buddy->present != GAIM_BUDDY_OFFLINE || + (gaim_account_is_connected(buddy->account) && + gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { + GtkTreeIter iter; + + insert_node(list, node, &iter); + buddy_node(buddy, &iter, node); + + } else { + gaim_gtk_blist_hide_node(list, node); + } + +} + +static void gaim_gtk_blist_update_chat(GaimBuddyList *list, GaimBlistNode *node) +{ + GaimChat *chat; + + g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node)); + + /* First things first, update the group */ + gaim_gtk_blist_update_group(list, node->parent); + + chat = (GaimChat*)node; + + if(gaim_account_is_connected(chat->account)) { + GtkTreeIter iter; GdkPixbuf *status; - struct chat *chat = (struct chat *)node; - char *name; + char *mark; + + insert_node(list, node, &iter); status = gaim_gtk_blist_get_status_icon(node, - blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); - if(chat->alias) { - name = g_markup_escape_text(chat->alias, -1); - } else { - struct proto_chat_entry *pce; - GList *parts, *tmp; - - parts = GAIM_PLUGIN_PROTOCOL_INFO(chat->account->gc->prpl)->chat_info(chat->account->gc); - pce = parts->data; - name = g_markup_escape_text(g_hash_table_lookup(chat->components, - pce->identifier), -1); - for(tmp = parts; tmp; tmp = tmp->next) - g_free(tmp->data); - g_list_free(parts); - } - + (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? + GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); + + mark = g_markup_escape_text(gaim_chat_get_name(chat), -1); gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_COLUMN, status, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - NAME_COLUMN, name, - NODE_COLUMN, node, - -1); - - g_free(name); - if (status != NULL) - g_object_unref(status); - } else if(GAIM_BLIST_NODE_IS_CHAT(node) && !((struct chat *)node)->account->gc) { - gaim_gtk_blist_hide_node(list, node); - } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) { - GdkPixbuf *status, *avatar; - GtkTreeIter groupiter; - char *mark; - char *warning = NULL, *idle = NULL; - - gboolean selected = (gtkblist->selected_node == node); - - status = gaim_gtk_blist_get_status_icon(node, - blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); - avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); - mark = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected); - - if (((struct buddy*)node)->idle > 0) { - time_t t; - int ihrs, imin; - time(&t); - ihrs = (t - ((struct buddy *)node)->idle) / 3600; - imin = ((t - ((struct buddy*)node)->idle) / 60) % 60; - if(ihrs > 0) - idle = g_strdup_printf("(%d:%02d)", ihrs, imin); - else - idle = g_strdup_printf("(%d)", imin); - } - - if (((struct buddy*)node)->evil > 0) - warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil); - - - if((blist_options & OPT_BLIST_GREY_IDLERS) - && ((struct buddy *)node)->idle) { - if(warning && !selected) { - char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>", - warning); - g_free(warning); - warning = w2; - } - - if(idle && !selected) { - char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>", - idle); - g_free(idle); - idle = i2; - } - } - if (!selected) { - get_iter_from_node(node->parent, &groupiter); - iter = current_sort_method->func(node, list, groupiter, &iter); - } - - gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_COLUMN, status, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - NAME_COLUMN, mark, - WARNING_COLUMN, warning, - IDLE_COLUMN, idle, - BUDDY_ICON_COLUMN, avatar, - NODE_COLUMN, node, - -1); - - if (blist_options & OPT_BLIST_POPUP && - ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { - gtk_widget_show(gtkblist->window); - gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); - gdk_window_raise(gtkblist->window->window); - } + STATUS_ICON_COLUMN, status, + STATUS_ICON_VISIBLE_COLUMN, TRUE, + NAME_COLUMN, mark, + -1); g_free(mark); - if (idle) - g_free(idle); - if (warning) - g_free(warning); - - if (status != NULL) + if(status) g_object_unref(status); - - if (avatar != NULL) - g_object_unref(avatar); - - } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) { + } else { gaim_gtk_blist_hide_node(list, node); } +} + +static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node) +{ + if(!gtkblist) + return; + + switch(node->type) { + case GAIM_BLIST_GROUP_NODE: + gaim_gtk_blist_update_group(list, node); + break; + case GAIM_BLIST_CONTACT_NODE: + gaim_gtk_blist_update_contact(list, node); + break; + case GAIM_BLIST_BUDDY_NODE: + gaim_gtk_blist_update_buddy(list, node); + break; + case GAIM_BLIST_CHAT_NODE: + gaim_gtk_blist_update_chat(list, node); + break; + case GAIM_BLIST_OTHER_NODE: + return; + } gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); - - - if(expand) { - gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); - gtk_tree_path_free(expand); - } - - if(GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) - gaim_gtk_blist_update(list, node->parent); } -static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list) + +static void gaim_gtk_blist_destroy(GaimBuddyList *list) { if (!gtkblist) return; gtk_widget_destroy(gtkblist->window); + + if (gtkblist->tipwindow) + gtk_widget_destroy(gtkblist->tipwindow); + gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); if (gtkblist->refresh_timer) @@ -1955,9 +3030,14 @@ protomenu = NULL; awaymenu = NULL; gtkblist = NULL; + + while(blist_prefs_callbacks) { + gaim_prefs_disconnect_callback(GPOINTER_TO_INT(blist_prefs_callbacks->data)); + blist_prefs_callbacks = g_slist_remove(blist_prefs_callbacks, blist_prefs_callbacks->data); + } } -static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show) +static void gaim_gtk_blist_set_visible(GaimBuddyList *list, gboolean show) { if (!(gtkblist && gtkblist->window)) return; @@ -1966,7 +3046,7 @@ gaim_gtk_blist_restore_position(); gtk_window_present(GTK_WINDOW(gtkblist->window)); } else { - if (!connections || docklet_count) { + if (!gaim_connections_get_all() || docklet_count) { #ifdef _WIN32 wgaim_systray_minimize(gtkblist->window); #endif @@ -1977,11 +3057,544 @@ } } +static GList * +groups_tree(void) +{ + GList *tmp = NULL; + char *tmp2; + GaimGroup *g; + GaimBlistNode *gnode; + + if (gaim_get_blist()->root == NULL) + { + tmp2 = g_strdup(_("Buddies")); + tmp = g_list_append(tmp, tmp2); + } + else + { + for (gnode = gaim_get_blist()->root; + gnode != NULL; + gnode = gnode->next) + { + if (GAIM_BLIST_NODE_IS_GROUP(gnode)) + { + g = (GaimGroup *)gnode; + tmp2 = g->name; + tmp = g_list_append(tmp, tmp2); + } + } + } + + return tmp; +} + +static void +add_buddy_select_account_cb(GObject *w, GaimAccount *account, + GaimGtkAddBuddyData *data) +{ + /* Save our account */ + data->account = account; +} + +static void +destroy_add_buddy_dialog_cb(GtkWidget *win, GaimGtkAddBuddyData *data) +{ + g_free(data); +} + +static void +add_buddy_cb(GtkWidget *w, int resp, GaimGtkAddBuddyData *data) +{ + const char *grp, *who, *whoalias; + GaimConversation *c; + GaimBuddy *b; + GaimGroup *g; + + if (resp == GTK_RESPONSE_OK) + { + who = gtk_entry_get_text(GTK_ENTRY(data->entry)); + grp = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry)); + whoalias = gtk_entry_get_text(GTK_ENTRY(data->entry_for_alias)); + + c = gaim_find_conversation_with_account(who, data->account); + + if ((g = gaim_find_group(grp)) == NULL) + { + g = gaim_group_new(grp); + gaim_blist_add_group(g, NULL); + } + + b = gaim_buddy_new(data->account, who, whoalias); + gaim_blist_add_buddy(b, NULL, g, NULL); + serv_add_buddy(gaim_account_get_connection(data->account), who, g); + + if (c != NULL) { + gaim_buddy_icon_update(gaim_conv_im_get_icon(GAIM_CONV_IM(c))); + gaim_conversation_update(c, GAIM_CONV_UPDATE_ADD); + } + + gaim_blist_save(); + } + + gtk_widget_destroy(data->window); +} + +static void +gaim_gtk_blist_request_add_buddy(GaimAccount *account, const char *username, + const char *group, const char *alias) +{ + GtkWidget *table; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *img; + GaimGtkBuddyList *gtkblist; + GaimGtkAddBuddyData *data = g_new0(GaimGtkAddBuddyData, 1); + + data->account = + (account != NULL + ? account + : gaim_connection_get_account(gaim_connections_get_all()->data)); + + img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, + GTK_ICON_SIZE_DIALOG); + + gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); + + data->window = gtk_dialog_new_with_buttons(_("Add Buddy"), + (gtkblist->window ? GTK_WINDOW(gtkblist->window) : NULL), 0, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_ADD, GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); + gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); + gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); + gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); + gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); + gtk_window_set_role(GTK_WINDOW(data->window), "add_buddy"); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); + gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(img), 0, 0); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(hbox), vbox); + + label = gtk_label_new( + _("Please enter the screen name of the person you would like " + "to add to your buddy list. You may optionally enter an alias, " + "or nickname, for the buddy. The alias will be displayed in " + "place of the screen name whenever possible.\n")); + + gtk_widget_set_size_request(GTK_WIDGET(label), 400, -1); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), hbox); + + g_signal_connect(G_OBJECT(data->window), "destroy", + G_CALLBACK(destroy_add_buddy_dialog_cb), data); + + table = gtk_table_new(4, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_container_set_border_width(GTK_CONTAINER(table), 0); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + + label = gtk_label_new(_("Screen Name:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + + data->entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1); + gtk_widget_grab_focus(data->entry); + + if (username != NULL) + gtk_entry_set_text(GTK_ENTRY(data->entry), username); + + gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE); + + label = gtk_label_new(_("Alias:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); + + data->entry_for_alias = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), + data->entry_for_alias, 1, 2, 1, 2); + + if (alias != NULL) + gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias); + + gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE); + + label = gtk_label_new(_("Group:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); + + data->combo = gtk_combo_new(); + gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree()); + gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3); + + /* Set up stuff for the account box */ + label = gtk_label_new(_("Account:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); + + data->account_box = gaim_gtk_account_option_menu_new(account, FALSE, + G_CALLBACK(add_buddy_select_account_cb), NULL, data); + + gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4); + + /* End of account box */ + + g_signal_connect(G_OBJECT(data->window), "response", + G_CALLBACK(add_buddy_cb), data); + + gtk_widget_show_all(data->window); + + if (group != NULL) + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry), group); +} + +static void +add_chat_cb(GtkWidget *w, GaimGtkAddChatData *data) +{ + GHashTable *components; + GList *tmp; + GaimChat *chat; + GaimGroup *group; + const char *group_name; + + components = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + for (tmp = data->entries; tmp; tmp = tmp->next) + { + if (g_object_get_data(tmp->data, "is_spin")) + { + g_hash_table_replace(components, + g_strdup(g_object_get_data(tmp->data, "identifier")), + g_strdup_printf("%d", + gtk_spin_button_get_value_as_int(tmp->data))); + } + else + { + g_hash_table_replace(components, + g_strdup(g_object_get_data(tmp->data, "identifier")), + g_strdup(gtk_entry_get_text(tmp->data))); + } + } + + chat = gaim_chat_new(data->account, + gtk_entry_get_text(GTK_ENTRY(data->alias_entry)), + components); + + group_name = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry)); + + if ((group = gaim_find_group(group_name)) == NULL) + { + group = gaim_group_new(group_name); + gaim_blist_add_group(group, NULL); + } + + if (chat != NULL) + { + gaim_blist_add_chat(chat, group, NULL); + gaim_blist_save(); + } + + gtk_widget_destroy(data->window); + g_list_free(data->entries); + + g_free(data); +} + +static void +add_chat_resp_cb(GtkWidget *w, int resp, GaimGtkAddChatData *data) +{ + if (resp == GTK_RESPONSE_OK) + { + add_chat_cb(NULL, data); + } + else + { + gtk_widget_destroy(data->window); + g_list_free(data->entries); + g_free(data); + } +} + +static void +rebuild_addchat_entries(GaimGtkAddChatData *data) +{ + GaimConnection *gc; + GList *list, *tmp; + struct proto_chat_entry *pce; + gboolean focus = TRUE; + + gc = gaim_account_get_connection(data->account); + + while (GTK_BOX(data->entries_box)->children) + { + gtk_container_remove(GTK_CONTAINER(data->entries_box), + ((GtkBoxChild *)GTK_BOX(data->entries_box)->children->data)->widget); + } + + if (data->entries != NULL) + g_list_free(data->entries); + + data->entries = NULL; + + list = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc); + + for (tmp = list; tmp; tmp = tmp->next) + { + GtkWidget *label; + GtkWidget *rowbox; + + pce = tmp->data; + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); + + label = gtk_label_new(pce->label); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(data->sg, label); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + + if (pce->is_int) + { + GtkObject *adjust; + GtkWidget *spin; + adjust = gtk_adjustment_new(pce->min, pce->min, pce->max, + 1, 10, 10); + spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); + g_object_set_data(G_OBJECT(spin), "is_spin", GINT_TO_POINTER(TRUE)); + g_object_set_data(G_OBJECT(spin), "identifier", pce->identifier); + data->entries = g_list_append(data->entries, spin); + gtk_widget_set_size_request(spin, 50, -1); + gtk_box_pack_end(GTK_BOX(rowbox), spin, FALSE, FALSE, 0); + } + else + { + GtkWidget *entry = gtk_entry_new(); + + g_object_set_data(G_OBJECT(entry), "identifier", pce->identifier); + data->entries = g_list_append(data->entries, entry); + + if (pce->def) + gtk_entry_set_text(GTK_ENTRY(entry), pce->def); + + if (focus) + { + gtk_widget_grab_focus(entry); + focus = FALSE; + } + + if (pce->secret) + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + + gtk_box_pack_end(GTK_BOX(rowbox), entry, TRUE, TRUE, 0); + + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(add_chat_cb), data); + } + + g_free(pce); + } + + g_list_free(list); + + gtk_widget_show_all(data->entries_box); +} + +static void +add_chat_select_account_cb(GObject *w, GaimAccount *account, + GaimGtkAddChatData *data) +{ + if (gaim_account_get_protocol(data->account) == + gaim_account_get_protocol(account)) + { + data->account = account; + } + else + { + data->account = account; + rebuild_addchat_entries(data); + } +} + +static gboolean +add_chat_check_account_func(GaimAccount *account) +{ + GaimConnection *gc = gaim_account_get_connection(account); + + return (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL); +} + +void +gaim_gtk_blist_request_add_chat(GaimAccount *account, GaimGroup *group) +{ + GaimGtkAddChatData *data; + GaimGtkBuddyList *gtkblist; + GList *l; + GaimConnection *gc; + GtkWidget *label; + GtkWidget *rowbox; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *img; + + data = g_new0(GaimGtkAddChatData, 1); + + img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, + GTK_ICON_SIZE_DIALOG); + + gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); + + if (account != NULL) + { + data->account = account; + } + else + { + /* Select an account with chat capabilities */ + for (l = gaim_connections_get_all(); l != NULL; l = l->next) + { + gc = (GaimConnection *)l->data; + + if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat != NULL) + { + data->account = gaim_connection_get_account(gc); + break; + } + } + } + + if (data->account == NULL) + { + gaim_notify_error(NULL, NULL, + _("You are not currently signed on with any " + "protocols that have the ability to chat."), NULL); + return; + } + + data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + data->window = gtk_dialog_new_with_buttons(_("Add Chat"), + GTK_WINDOW(gtkblist->window), 0, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_ADD, GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); + gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); + gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); + gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); + gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); + gtk_window_set_role(GTK_WINDOW(data->window), "add_chat"); + + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); + gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(img), 0, 0); + + vbox = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(hbox), vbox); + + label = gtk_label_new( + _("Please enter an alias, and the appropriate information " + "about the chat you would like to add to your buddy list.\n")); + gtk_widget_set_size_request(label, 400, -1); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Account:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(data->sg, label); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + + data->account_menu = gaim_gtk_account_option_menu_new(account, FALSE, + G_CALLBACK(add_chat_select_account_cb), + add_chat_check_account_func, data); + gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); + + data->entries_box = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); + gtk_box_pack_start(GTK_BOX(vbox), data->entries_box, TRUE, TRUE, 0); + + rebuild_addchat_entries(data); + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Alias:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(data->sg, label); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + + data->alias_entry = gtk_entry_new(); + gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Group:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(data->sg, label); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + + data->group_combo = gtk_combo_new(); + gtk_combo_set_popdown_strings(GTK_COMBO(data->group_combo), groups_tree()); + gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); + + if (group) + { + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry), + group->name); + } + + g_signal_connect(G_OBJECT(data->window), "response", + G_CALLBACK(add_chat_resp_cb), data); + + gtk_widget_show_all(data->window); +} + +static void +add_group_cb(GaimConnection *gc, const char *group_name) +{ + GaimGroup *g; + + g = gaim_group_new(group_name); + gaim_blist_add_group(g, NULL); + gaim_blist_save(); +} + +void +gaim_gtk_blist_request_add_group(void) +{ + gaim_request_input(NULL, _("Add Group"), _("Add a new group"), + _("Please enter the name of the group to be added."), + NULL, FALSE, FALSE, + _("Add"), G_CALLBACK(add_group_cb), + _("Cancel"), NULL, NULL); +} + void gaim_gtk_blist_docklet_toggle() { /* Useful for the docklet plugin and also for the win32 tray icon*/ /* This is called when one of those is clicked--it will show/hide the buddy list/login window--depending on which is active */ - if (connections) { + if (gaim_connections_get_all()) { if (gtkblist && gtkblist->window) { if (GTK_WIDGET_VISIBLE(gtkblist->window)) { gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); @@ -1995,7 +3608,7 @@ /* we're logging in or something... do nothing */ /* or should I make the blist? */ gaim_debug(GAIM_DEBUG_WARNING, "blist", - "docklet_toggle called with connections " + "docklet_toggle called with gaim_connections_get_all() " "but no blist!\n"); } } else if (mainwindow) { @@ -2028,7 +3641,7 @@ { docklet_count--; if (!docklet_count) { - if (connections) + if (gaim_connections_get_all()) gaim_blist_set_visible(TRUE); else if (mainwindow) gtk_window_present(GTK_WINDOW(mainwindow)); @@ -2037,7 +3650,7 @@ } } -static struct gaim_blist_ui_ops blist_ui_ops = +static GaimBlistUiOps blist_ui_ops = { gaim_gtk_blist_new_list, gaim_gtk_blist_new_node, @@ -2045,15 +3658,58 @@ gaim_gtk_blist_update, gaim_gtk_blist_remove, gaim_gtk_blist_destroy, - gaim_gtk_blist_set_visible + gaim_gtk_blist_set_visible, + gaim_gtk_blist_request_add_buddy, + gaim_gtk_blist_request_add_chat, + gaim_gtk_blist_request_add_group }; -struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops() +GaimBlistUiOps * +gaim_gtk_blist_get_ui_ops(void) { return &blist_ui_ops; } +static void account_signon_cb(GaimConnection *gc, gpointer z) +{ + GaimAccount *account = gaim_connection_get_account(gc); + GaimBlistNode *gnode, *cnode; + for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) + { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + for(cnode = gnode->child; cnode; cnode = cnode->next) + { + GaimChat *chat; + const char *autojoin; + + if(!GAIM_BLIST_NODE_IS_CHAT(cnode)) + continue; + + chat = (GaimChat *)cnode; + + if(chat->account != account) + continue; + + autojoin = gaim_chat_get_setting(chat, "gtk-autojoin"); + + if(autojoin && !strcmp(autojoin, "true")) + serv_join_chat(gc, chat->components); + } + } +} + +void gaim_gtk_blist_init(void) +{ + /* XXX */ + static int gtk_blist_handle; + + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", + >k_blist_handle, GAIM_CALLBACK(account_signon_cb), + NULL); +} + /********************************************************************* @@ -2061,7 +3717,7 @@ *********************************************************************/ GdkPixbuf * -create_prpl_icon(struct gaim_account *account) +create_prpl_icon(GaimAccount *account) { GaimPlugin *prpl; GaimPluginProtocolInfo *prpl_info = NULL; @@ -2070,7 +3726,7 @@ const char *protoname = NULL; char buf[256]; - prpl = gaim_find_prpl(account->protocol); + prpl = gaim_find_prpl(gaim_account_get_protocol(account)); if (prpl != NULL) { prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); @@ -2101,31 +3757,45 @@ * Buddy List sorting functions * *********************************************************************/ -void gaim_gtk_blist_sort_method_reg(const char *name, gaim_gtk_blist_sort_function func) +void gaim_gtk_blist_sort_method_reg(const char *id, const char *name, gaim_gtk_blist_sort_function func) { struct gaim_gtk_blist_sort_method *method = g_new0(struct gaim_gtk_blist_sort_method, 1); + method->id = g_strdup(id); method->name = g_strdup(name); - method->func = func; + method->func = func;; gaim_gtk_blist_sort_methods = g_slist_append(gaim_gtk_blist_sort_methods, method); } -void gaim_gtk_blist_sort_method_unreg(const char *name){ - +void gaim_gtk_blist_sort_method_unreg(const char *id){ + GSList *l = gaim_gtk_blist_sort_methods; + + while(l) { + struct gaim_gtk_blist_sort_method *method = l->data; + if(!strcmp(method->id, id)) { + gaim_gtk_blist_sort_methods = g_slist_remove(gaim_gtk_blist_sort_methods, method); + g_free(method->id); + g_free(method->name); + g_free(method); + break; + } + } } -void gaim_gtk_blist_sort_method_set(const char *name){ +void gaim_gtk_blist_sort_method_set(const char *id){ GSList *l = gaim_gtk_blist_sort_methods; - while (l && gaim_utf8_strcasecmp(((struct gaim_gtk_blist_sort_method*)l->data)->name, name)) + + if(!id) + id = "none"; + + while (l && strcmp(((struct gaim_gtk_blist_sort_method*)l->data)->id, id)) l = l->next; - + if (l) { current_sort_method = l->data; - strcpy(sort_method, ((struct gaim_gtk_blist_sort_method*)l->data)->name); } else if (!current_sort_method) { - gaim_gtk_blist_sort_method_set(_("None")); + gaim_gtk_blist_sort_method_set("none"); return; } - save_prefs(); redo_buddy_list(gaim_get_blist(), TRUE); } @@ -2134,184 +3804,375 @@ ** Sort Methods ******************************************/ -/* A sort method takes a core buddy list node, the buddy list it belongs in, the GtkTreeIter of its group and - * the nodes own iter if it has one. It returns the iter the buddy list should use to represent this buddy, be - * it a new iter, or an existing one. If it is a new iter, and cur is defined, the buddy list will probably want - * to remove cur from the buddy list. */ -static GtkTreeIter sort_method_none(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) +static GtkTreeIter sort_method_none(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter parent_iter, GtkTreeIter *cur) { - GtkTreePath *newpath; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - GaimBlistNode *oldersibling = node->prev; - GtkTreeIter iter, oldersiblingiter; - - if (cur) + GtkTreeIter iter; + GaimBlistNode *sibling = node->prev; + GtkTreeIter sibling_iter; + + if(cur) return *cur; - - while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { - oldersibling = oldersibling->prev; + + while (sibling && !get_iter_from_node(sibling, &sibling_iter)) { + sibling = sibling->prev; } - - gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); + + gtk_tree_store_insert_after(gtkblist->treemodel, &iter, + node->parent ? &parent_iter : NULL, + sibling ? &sibling_iter : NULL); + return iter; } -static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) +#if GTK_CHECK_VERSION(2,2,1) + +static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) { GtkTreeIter more_z, iter; GaimBlistNode *n; - GtkTreePath *newpath; GValue val = {0,}; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - - if (cur) - return *cur; - + + const char *my_name; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + my_name = gaim_contact_get_alias((GaimContact*)node); + } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { + my_name = gaim_chat_get_name((GaimChat*)node); + } else { + return sort_method_none(node, blist, groupiter, cur); + } + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); + return iter; + } + + do { + const char *this_name; + int cmp; + + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); + n = g_value_get_pointer(&val); + + if(GAIM_BLIST_NODE_IS_CONTACT(n)) { + this_name = gaim_contact_get_alias((GaimContact*)n); + } else if(GAIM_BLIST_NODE_IS_CHAT(n)) { + this_name = gaim_chat_get_name((GaimChat*)n); + } else { + this_name = NULL; + } + + cmp = gaim_utf8_strcasecmp(my_name, this_name); + + if(this_name && (cmp < 0 || (cmp == 0 && node < n))) { + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); + return *cur; + } else { + gtk_tree_store_insert_before(gtkblist->treemodel, &iter, + &groupiter, &more_z); + return iter; + } + } + g_value_unset(&val); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); + + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); + return *cur; + } else { + gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); + return iter; + } +} + +static GtkTreeIter sort_method_status(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) +{ + GtkTreeIter more_z, iter; + GaimBlistNode *n; + GValue val = {0,}; + + GaimBuddy *my_buddy, *this_buddy; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + my_buddy = gaim_contact_get_priority_buddy((GaimContact*)node); + } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { + if(cur) + return *cur; + + gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); + return iter; + } else { + return sort_method_none(node, blist, groupiter, cur); + } + + + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { + gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); return iter; } do { + int cmp; + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); n = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_BUDDY(n) && gaim_utf8_strcasecmp(gaim_get_buddy_alias((struct buddy*)node), - gaim_get_buddy_alias((struct buddy*)n)) < 0) { - gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; + + if(GAIM_BLIST_NODE_IS_CONTACT(n)) { + this_buddy = gaim_contact_get_priority_buddy((GaimContact*)n); + } else { + this_buddy = NULL; + } + + cmp = gaim_utf8_strcasecmp(my_buddy ? + gaim_contact_get_alias(gaim_buddy_get_contact(my_buddy)) + : NULL, this_buddy ? + gaim_contact_get_alias(gaim_buddy_get_contact(this_buddy)) + : NULL); + + /* Hideous */ + if(!this_buddy || + ((my_buddy->present > this_buddy->present) || + (my_buddy->present == this_buddy->present && + (((my_buddy->uc & UC_UNAVAILABLE) < (this_buddy->uc & UC_UNAVAILABLE)) || + (((my_buddy->uc & UC_UNAVAILABLE) == (this_buddy->uc & UC_UNAVAILABLE)) && + (((my_buddy->idle == 0) && (this_buddy->idle != 0)) || + (this_buddy->idle && (my_buddy->idle > this_buddy->idle)) || + ((my_buddy->idle == this_buddy->idle) && + (cmp < 0 || (cmp == 0 && node < n))))))))) { + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); + return *cur; + } else { + gtk_tree_store_insert_before(gtkblist->treemodel, &iter, + &groupiter, &more_z); + return iter; + } } g_value_unset(&val); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); - - gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; + + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); + return *cur; + } else { + gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); + return iter; + } } -static GtkTreeIter sort_method_status(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) +static GtkTreeIter sort_method_log(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) { GtkTreeIter more_z, iter; - GaimBlistNode *n; - GtkTreePath *newpath, *expand = NULL; + GaimBlistNode *n = NULL, *n2; GValue val = {0,}; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); - if(!collapsed) - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); - else - g_free(collapsed); - - - if (cur) - gaim_gtk_blist_hide_node(blist, node); - + + int log_size = 0, this_log_size = 0; + const char *buddy_name, *this_buddy_name; + + if(cur && (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter) == 1)) + return *cur; + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + for (n = node->child; n; n = n->next) + log_size += gaim_log_get_total_size(((GaimBuddy*)(n))->name, ((GaimBuddy*)(n))->account); + buddy_name = gaim_contact_get_alias((GaimContact*)node); + } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { + /* we don't have a reliable way of getting the log filename + * from the chat info in the blist, yet */ + if(cur) + return *cur; + + gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); + return iter; + } else { + return sort_method_none(node, blist, groupiter, cur); + } + + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - if(expand) { - gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); - gtk_tree_path_free(expand); - } return iter; } do { + int cmp; + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); n = g_value_get_pointer(&val); - - if (n && GAIM_BLIST_NODE_IS_BUDDY(n)) { - struct buddy *new = (struct buddy*)node, *it = (struct buddy*)n; - /* This is the worst if statement ever. */ - if ((it->present < new->present) || - ((it->present == new->present) && (it->uc & UC_UNAVAILABLE) > (new->uc & UC_UNAVAILABLE)) || - ((it->present == new->present) && ((it->uc & UC_UNAVAILABLE) == (new->uc & UC_UNAVAILABLE)) && - ((it->idle && !new->idle) || (it->idle && (it->idle < new->idle)))) || - ((it->present == new->present) && (it->uc & UC_UNAVAILABLE) == (new->uc & UC_UNAVAILABLE) && (it->idle == new->idle) && - (gaim_utf8_strcasecmp(gaim_get_buddy_alias((struct buddy*)node), gaim_get_buddy_alias((struct buddy*)n)) < 0))) - { - gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; - } - g_value_unset(&val); + this_log_size = 0; + + if(GAIM_BLIST_NODE_IS_CONTACT(n)) { + for (n2 = n->child; n2; n2 = n2->next) + this_log_size += gaim_log_get_total_size(((GaimBuddy*)(n2))->name, ((GaimBuddy*)(n2))->account); + this_buddy_name = gaim_contact_get_alias((GaimContact*)n); + } else { + this_buddy_name = NULL; } - } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); - - gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; -} - -static GtkTreeIter sort_method_log(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) -{ - GtkTreeIter more_z, iter; - GaimBlistNode *n; - GtkTreePath *newpath; - GValue val = {0,}; - struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - char *logname = g_strdup_printf("%s.log", normalize(((struct buddy*)node)->name)); - char *filename = g_build_filename(gaim_user_dir(), "logs", logname, NULL); - struct stat st, st2; - - if (cur) - return *cur; - - if (stat(filename, &st)) - st.st_size = 0; - g_free(filename); - g_free(logname); - - if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { - gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; - } - - do { - - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); - n = g_value_get_pointer(&val); - - logname = g_strdup_printf("%s.log", normalize(((struct buddy*)n)->name)); - filename = g_build_filename(gaim_user_dir(), "logs", logname, NULL); - if (stat(filename, &st2)) - st2.st_size = 0; - g_free(filename); - g_free(logname); - if (GAIM_BLIST_NODE_IS_BUDDY(n) && st.st_size > st2.st_size) { - gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; + + cmp = gaim_utf8_strcasecmp(buddy_name, this_buddy_name); + + if (!GAIM_BLIST_NODE_IS_CONTACT(n) || log_size > this_log_size || + ((log_size == this_log_size) && + (cmp < 0 || (cmp == 0 && node < n)))) { + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); + return *cur; + } else { + gtk_tree_store_insert_before(gtkblist->treemodel, &iter, + &groupiter, &more_z); + return iter; + } } g_value_unset(&val); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); - - gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); - gtk_tree_path_free(newpath); - return iter; + + if(cur) { + gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); + return *cur; + } else { + gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); + return iter; + } +} + +#endif + +static void +proto_act(GtkObject *obj, struct proto_actions_menu *pam) +{ + if (pam->callback && pam->gc) + pam->callback(pam->gc); } + +void +gaim_gtk_blist_update_protocol_actions(void) +{ + GtkWidget *menuitem; + GtkWidget *submenu; + GaimPluginProtocolInfo *prpl_info = NULL; + GList *l; + GList *c; + struct proto_actions_menu *pam; + GaimConnection *gc = NULL; + int count = 0; + char buf[256]; + + if (!protomenu) + return; + + for (l = gtk_container_get_children(GTK_CONTAINER(protomenu)); + l != NULL; + l = l->next) { + + menuitem = l->data; + pam = g_object_get_data(G_OBJECT(menuitem), "proto_actions_menu"); + + if (pam) + g_free(pam); + + gtk_container_remove(GTK_CONTAINER(protomenu), GTK_WIDGET(menuitem)); + } + + for (c = gaim_connections_get_all(); c != NULL; c = c->next) { + gc = c->data; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info->actions && gc->login_time) + count++; + } + + if (!count) { + g_snprintf(buf, sizeof(buf), _("No actions available")); + menuitem = gtk_menu_item_new_with_label(buf); + gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); + gtk_widget_show(menuitem); + return; + } + + if (count == 1) { + GList *act; + + for (c = gaim_connections_get_all(); c != NULL; c = c->next) { + gc = c->data; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info->actions && gc->login_time) + break; + } + + for (act = prpl_info->actions(gc); act != NULL; act = act->next) { + if (act->data) { + struct proto_actions_menu *pam = act->data; + menuitem = gtk_menu_item_new_with_label(pam->label); + gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(proto_act), pam); + g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", pam); + gtk_widget_show(menuitem); + } + else + gaim_separator(protomenu); + } + } + else { + for (c = gaim_connections_get_all(); c != NULL; c = c->next) { + GaimAccount *account; + GList *act; + GdkPixbuf *pixbuf, *scale; + GtkWidget *image; + + gc = c->data; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (!prpl_info->actions || !gc->login_time) + continue; + + account = gaim_connection_get_account(gc); + + g_snprintf(buf, sizeof(buf), "%s (%s)", + gaim_account_get_username(account), + gc->prpl->info->name); + + menuitem = gtk_image_menu_item_new_with_label(buf); + + pixbuf = create_prpl_icon(gc->account); + if(pixbuf) { + scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, + GDK_INTERP_BILINEAR); + image = gtk_image_new_from_pixbuf(scale); + g_object_unref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(scale)); + gtk_widget_show(image); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), + image); + } + + gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + gtk_widget_show(submenu); + + for (act = prpl_info->actions(gc); act != NULL; act = act->next) { + if (act->data) { + struct proto_actions_menu *pam = act->data; + menuitem = gtk_menu_item_new_with_label(pam->label); + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(proto_act), pam); + g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", + pam); + gtk_widget_show(menuitem); + } + else + gaim_separator(submenu); + } + } + } +}