Mercurial > pidgin
diff pidgin/gtkblist.c @ 21877:6bf73aea6450
Some utility functions for showing tooltips. This is used by the buddylist,
the roomlist and the infopane in the conversation window. The userlist in
chats can also use this, I think.
It works OK, but it may be possible to make the code a bit nicer. Any
suggestions?
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Thu, 06 Dec 2007 07:33:53 +0000 |
parents | 6a287408bc54 |
children | f5d961556972 |
line wrap: on
line diff
--- a/pidgin/gtkblist.c Thu Dec 06 01:30:38 2007 +0000 +++ b/pidgin/gtkblist.c Thu Dec 06 07:33:53 2007 +0000 @@ -59,6 +59,7 @@ #include "gtkscrollbook.h" #include "gtkutils.h" #include "pidgin/minidialog.h" +#include "pidgin/pidgintooltip.h" #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> @@ -153,6 +154,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full); static const char *item_factory_translate_func (const char *path, gpointer func_data); static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter); +static gboolean buddy_is_displayable(PurpleBuddy *buddy); static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); @@ -2630,7 +2632,7 @@ return td; } -static void pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, PurpleBlistNode *node) +static gboolean pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkStyle *style; int current_height, max_width; @@ -2638,10 +2640,10 @@ int max_avatar_width; GList *l; int prpl_col = 0; - GtkTextDirection dir = gtk_widget_get_direction(widget); + GtkTextDirection dir = gtk_widget_get_direction(widget); if(gtkblist->tooltipdata == NULL) - return; + return FALSE; style = gtkblist->tipwindow->style; gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, @@ -2740,10 +2742,11 @@ current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER; } -} - - -void pidgin_blist_tooltip_destroy() + return FALSE; +} + +static void +pidgin_blist_destroy_tooltip_data() { while(gtkblist->tooltipdata) { struct tooltip_data *td = gtkblist->tooltipdata->data; @@ -2759,12 +2762,62 @@ g_free(td); gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata); } - - if (gtkblist->tipwindow == NULL) - return; - - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; +} + +void pidgin_blist_tooltip_destroy() +{ + pidgin_blist_destroy_tooltip_data(); + pidgin_tooltip_destroy(); +} + +static gboolean +pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, int *h) +{ + PurpleBlistNode *node = data; + int width, height; + + gtkblist->tipwindow = widget; + if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { + struct tooltip_data *td = create_tip_for_node(node, TRUE); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + + MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; + height = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height)) + + TOOLTIP_BORDER; + } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBlistNode *child; + PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); + int max_text_width = 0; + int max_avatar_width = 0; + width = height = 0; + + for(child = node->child; child; child = child->next) + { + if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { + struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); + if (b == (PurpleBuddy *)child) { + gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td); + } else { + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + } + max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); + max_avatar_width = MAX(max_avatar_width, td->avatar_width); + height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height), + TOOLTIP_BORDER + td->height + td->name_height); + } + } + height += TOOLTIP_BORDER; + width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; + } else { + return FALSE; + } + + if (w) + *w = width; + if (h) + *h = height; + + return TRUE; } static gboolean pidgin_blist_expand_timeout(GtkWidget *tv) @@ -2826,164 +2879,9 @@ purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline"))); } -static gboolean pidgin_blist_tooltip_timeout(GtkWidget *tv) -{ - GtkTreePath *path; - GtkTreeIter iter; - PurpleBlistNode *node; - GValue val; - gboolean editable = FALSE; - - /* If we're editing a cell (e.g. alias editing), don't show the tooltip */ - g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL); - if (editable) - return FALSE; - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2), - &path, NULL, NULL, NULL)) - return FALSE; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - val.g_type = 0; - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - - pidgin_blist_draw_tooltip(node, gtkblist->window); - - gtk_tree_path_free(path); - return FALSE; -} - void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget) { - int scr_w, scr_h, w, h, x, y; -#if GTK_CHECK_VERSION(2,2,0) - int mon_num; - GdkScreen *screen = NULL; -#endif - gboolean tooltip_top = FALSE; - struct _pidgin_blist_node *gtknode; - GdkRectangle mon_size; - int sig; - const char *name; - - if (node == NULL) - return; - - /* - * Attempt to free the previous tooltip. I have a feeling - * this is never needed... but just in case. - */ - pidgin_blist_tooltip_destroy(); - - gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); - - if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { - struct tooltip_data *td = create_tip_for_node(node, TRUE); - gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); - w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + - MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; - h = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height)) - + TOOLTIP_BORDER; - } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleBlistNode *child; - PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); - int max_text_width = 0; - int max_avatar_width = 0; - w = h = 0; - - for(child = node->child; child; child = child->next) - { - if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { - struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); - if (b == (PurpleBuddy *)child) { - gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td); - } else { - gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); - } - max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); - max_avatar_width = MAX(max_avatar_width, td->avatar_width); - h += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height), - TOOLTIP_BORDER + td->height + td->name_height); - } - } - h += TOOLTIP_BORDER; - w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; - } else { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - return; - } - - if (gtkblist->tooltipdata == NULL) { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - return; - } - - gtknode = node->ui_data; - - name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget))); - gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); - gtk_window_set_title(GTK_WINDOW(gtkblist->tipwindow), name ? name : _("Buddy List")); - 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(pidgin_blist_paint_tip), NULL); - gtk_widget_ensure_style (gtkblist->tipwindow); - -#if GTK_CHECK_VERSION(2,2,0) - gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); - mon_num = gdk_screen_get_monitor_at_point(screen, x, y); - gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); - - scr_w = mon_size.width + mon_size.x; - scr_h = mon_size.height + mon_size.y; -#else - scr_w = gdk_screen_width(); - scr_h = gdk_screen_height(); - gdk_window_get_pointer(NULL, &x, &y, NULL); - mon_size.x = 0; - mon_size.y = 0; -#endif - -#if GTK_CHECK_VERSION(2,2,0) - if (w > mon_size.width) - w = mon_size.width - 10; - - if (h > mon_size.height) - h = mon_size.height - 10; -#endif - - x -= ((w >> 1) + 4); - - if ((y + h + 4) > scr_h || tooltip_top) - y = y - h - 5; - else - y = y + 6; - - if (y < mon_size.y) - y = mon_size.y; - - if (y != mon_size.y) { - if ((x + w) > scr_w) - x -= (x + w + 5) - scr_w; - else if (x < mon_size.x) - x = mon_size.x; - } else { - x -= (w / 2 + 10); - if (x < mon_size.x) - x = mon_size.x; - } - - gtk_widget_set_size_request(gtkblist->tipwindow, w, h); - gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); - gtk_widget_show(gtkblist->tipwindow); - - /* Hide the tooltip when the widget is destroyed */ - sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_blist_tooltip_destroy), NULL); - g_signal_connect_swapped(G_OBJECT(gtkblist->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig)); - - return; + pidgin_tooltip_show(widget, node, pidgin_blist_create_tooltip_for_node, pidgin_blist_paint_tip); } static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context, @@ -3033,31 +2931,34 @@ return FALSE; } -static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) -{ - GtkTreePath *path; - int delay; - - delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - - if (delay == 0) +static gboolean +pidgin_blist_create_tooltip(GtkWidget *widget, GtkTreePath *path, + gpointer null, int *w, int *h) +{ + GtkTreeIter iter; + PurpleBlistNode *node; + GValue val; + gboolean editable = FALSE; + + /* If we're editing a cell (e.g. alias editing), don't show the tooltip */ + g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL); + if (editable) return FALSE; - if (gtkblist->timeout) { - if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) - return FALSE; - /* We've left the cell. Remove the timeout and create a new one below */ - pidgin_blist_tooltip_destroy(); - g_source_remove(gtkblist->timeout); - } - - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); - gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); - - if (path) - gtk_tree_path_free(path); - gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv); - + if (gtkblist->tooltipdata) { + gtkblist->tipwindow = NULL; + pidgin_blist_destroy_tooltip_data(); + } + + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + val.g_type = 0; + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + return pidgin_blist_create_tooltip_for_node(widget, node, w, h); +} + +static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) +{ if (gtkblist->mouseover_contact) { if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); @@ -3070,7 +2971,6 @@ static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) { - if (gtkblist->timeout) { g_source_remove(gtkblist->timeout); gtkblist->timeout = 0; @@ -3081,8 +2981,6 @@ gtkblist->drag_timeout = 0; } - pidgin_blist_tooltip_destroy(); - if (gtkblist->mouseover_contact && !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) && (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) { @@ -5152,12 +5050,14 @@ #ifdef _WIN32 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL); #endif - g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); /* Tooltips */ - g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); + pidgin_tooltip_setup_for_treeview(gtkblist->treeview, NULL, + pidgin_blist_create_tooltip, + pidgin_blist_paint_tip); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);