comparison 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
comparison
equal deleted inserted replaced
21789:cbf778310bdd 21877:6bf73aea6450
57 #include "gtkroomlist.h" 57 #include "gtkroomlist.h"
58 #include "gtkstatusbox.h" 58 #include "gtkstatusbox.h"
59 #include "gtkscrollbook.h" 59 #include "gtkscrollbook.h"
60 #include "gtkutils.h" 60 #include "gtkutils.h"
61 #include "pidgin/minidialog.h" 61 #include "pidgin/minidialog.h"
62 #include "pidgin/pidgintooltip.h"
62 63
63 #include <gdk/gdkkeysyms.h> 64 #include <gdk/gdkkeysyms.h>
64 #include <gtk/gtk.h> 65 #include <gtk/gtk.h>
65 #include <gdk/gdk.h> 66 #include <gdk/gdk.h>
66 67
151 static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node); 152 static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node);
152 static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node); 153 static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node);
153 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full); 154 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full);
154 static const char *item_factory_translate_func (const char *path, gpointer func_data); 155 static const char *item_factory_translate_func (const char *path, gpointer func_data);
155 static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter); 156 static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter);
157 static gboolean buddy_is_displayable(PurpleBuddy *buddy);
156 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); 158 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender);
157 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); 159 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
158 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); 160 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
159 static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node); 161 static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
160 162
2628 g_free(node_name); 2630 g_free(node_name);
2629 g_free(tooltip_text); 2631 g_free(tooltip_text);
2630 return td; 2632 return td;
2631 } 2633 }
2632 2634
2633 static void pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, PurpleBlistNode *node) 2635 static gboolean pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2634 { 2636 {
2635 GtkStyle *style; 2637 GtkStyle *style;
2636 int current_height, max_width; 2638 int current_height, max_width;
2637 int max_text_width; 2639 int max_text_width;
2638 int max_avatar_width; 2640 int max_avatar_width;
2639 GList *l; 2641 GList *l;
2640 int prpl_col = 0; 2642 int prpl_col = 0;
2641 GtkTextDirection dir = gtk_widget_get_direction(widget); 2643 GtkTextDirection dir = gtk_widget_get_direction(widget);
2642 2644
2643 if(gtkblist->tooltipdata == NULL) 2645 if(gtkblist->tooltipdata == NULL)
2644 return; 2646 return FALSE;
2645 2647
2646 style = gtkblist->tipwindow->style; 2648 style = gtkblist->tipwindow->style;
2647 gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, 2649 gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2648 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); 2650 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1);
2649 2651
2738 td->layout); 2740 td->layout);
2739 } 2741 }
2740 2742
2741 current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER; 2743 current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER;
2742 } 2744 }
2743 } 2745 return FALSE;
2744 2746 }
2745 2747
2746 void pidgin_blist_tooltip_destroy() 2748 static void
2749 pidgin_blist_destroy_tooltip_data()
2747 { 2750 {
2748 while(gtkblist->tooltipdata) { 2751 while(gtkblist->tooltipdata) {
2749 struct tooltip_data *td = gtkblist->tooltipdata->data; 2752 struct tooltip_data *td = gtkblist->tooltipdata->data;
2750 2753
2751 if(td->avatar) 2754 if(td->avatar)
2757 g_object_unref(td->layout); 2760 g_object_unref(td->layout);
2758 g_object_unref(td->name_layout); 2761 g_object_unref(td->name_layout);
2759 g_free(td); 2762 g_free(td);
2760 gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata); 2763 gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata);
2761 } 2764 }
2762 2765 }
2763 if (gtkblist->tipwindow == NULL) 2766
2764 return; 2767 void pidgin_blist_tooltip_destroy()
2765 2768 {
2766 gtk_widget_destroy(gtkblist->tipwindow); 2769 pidgin_blist_destroy_tooltip_data();
2767 gtkblist->tipwindow = NULL; 2770 pidgin_tooltip_destroy();
2768 } 2771 }
2769 2772
2770 static gboolean pidgin_blist_expand_timeout(GtkWidget *tv) 2773 static gboolean
2771 { 2774 pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, int *h)
2772 GtkTreePath *path; 2775 {
2773 GtkTreeIter iter; 2776 PurpleBlistNode *node = data;
2774 PurpleBlistNode *node; 2777 int width, height;
2775 GValue val; 2778
2776 struct _pidgin_blist_node *gtknode; 2779 gtkblist->tipwindow = widget;
2777
2778 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),
2779 &path, NULL, NULL, NULL))
2780 return FALSE;
2781 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
2782 val.g_type = 0;
2783 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
2784 node = g_value_get_pointer(&val);
2785
2786 if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) {
2787 gtk_tree_path_free(path);
2788 return FALSE;
2789 }
2790
2791 gtknode = node->ui_data;
2792
2793 if (!gtknode->contact_expanded) {
2794 GtkTreeIter i;
2795
2796 pidgin_blist_expand_contact_cb(NULL, node);
2797
2798 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->contact_rect);
2799 gdk_drawable_get_size(GDK_DRAWABLE(tv->window), &(gtkblist->contact_rect.width), NULL);
2800 gtkblist->mouseover_contact = node;
2801 gtk_tree_path_down (path);
2802 while (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &i, path)) {
2803 GdkRectangle rect;
2804 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect);
2805 gtkblist->contact_rect.height += rect.height;
2806 gtk_tree_path_next(path);
2807 }
2808 }
2809 gtk_tree_path_free(path);
2810 return FALSE;
2811 }
2812
2813 static gboolean buddy_is_displayable(PurpleBuddy *buddy)
2814 {
2815 struct _pidgin_blist_node *gtknode;
2816
2817 if(!buddy)
2818 return FALSE;
2819
2820 gtknode = ((PurpleBlistNode*)buddy)->ui_data;
2821
2822 return (purple_account_is_connected(buddy->account) &&
2823 (purple_presence_is_online(buddy->presence) ||
2824 (gtknode && gtknode->recent_signonoff) ||
2825 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies") ||
2826 purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline")));
2827 }
2828
2829 static gboolean pidgin_blist_tooltip_timeout(GtkWidget *tv)
2830 {
2831 GtkTreePath *path;
2832 GtkTreeIter iter;
2833 PurpleBlistNode *node;
2834 GValue val;
2835 gboolean editable = FALSE;
2836
2837 /* If we're editing a cell (e.g. alias editing), don't show the tooltip */
2838 g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL);
2839 if (editable)
2840 return FALSE;
2841
2842 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),
2843 &path, NULL, NULL, NULL))
2844 return FALSE;
2845 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
2846 val.g_type = 0;
2847 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
2848 node = g_value_get_pointer(&val);
2849
2850 pidgin_blist_draw_tooltip(node, gtkblist->window);
2851
2852 gtk_tree_path_free(path);
2853 return FALSE;
2854 }
2855
2856 void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget)
2857 {
2858 int scr_w, scr_h, w, h, x, y;
2859 #if GTK_CHECK_VERSION(2,2,0)
2860 int mon_num;
2861 GdkScreen *screen = NULL;
2862 #endif
2863 gboolean tooltip_top = FALSE;
2864 struct _pidgin_blist_node *gtknode;
2865 GdkRectangle mon_size;
2866 int sig;
2867 const char *name;
2868
2869 if (node == NULL)
2870 return;
2871
2872 /*
2873 * Attempt to free the previous tooltip. I have a feeling
2874 * this is never needed... but just in case.
2875 */
2876 pidgin_blist_tooltip_destroy();
2877
2878 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
2879
2880 if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { 2780 if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) {
2881 struct tooltip_data *td = create_tip_for_node(node, TRUE); 2781 struct tooltip_data *td = create_tip_for_node(node, TRUE);
2882 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); 2782 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
2883 w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + 2783 width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE +
2884 MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; 2784 MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER;
2885 h = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height)) 2785 height = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height))
2886 + TOOLTIP_BORDER; 2786 + TOOLTIP_BORDER;
2887 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { 2787 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
2888 PurpleBlistNode *child; 2788 PurpleBlistNode *child;
2889 PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); 2789 PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node);
2890 int max_text_width = 0; 2790 int max_text_width = 0;
2891 int max_avatar_width = 0; 2791 int max_avatar_width = 0;
2892 w = h = 0; 2792 width = height = 0;
2893 2793
2894 for(child = node->child; child; child = child->next) 2794 for(child = node->child; child; child = child->next)
2895 { 2795 {
2896 if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { 2796 if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
2897 struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); 2797 struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child));
2900 } else { 2800 } else {
2901 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); 2801 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
2902 } 2802 }
2903 max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); 2803 max_text_width = MAX(max_text_width, MAX(td->width, td->name_width));
2904 max_avatar_width = MAX(max_avatar_width, td->avatar_width); 2804 max_avatar_width = MAX(max_avatar_width, td->avatar_width);
2905 h += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height), 2805 height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height),
2906 TOOLTIP_BORDER + td->height + td->name_height); 2806 TOOLTIP_BORDER + td->height + td->name_height);
2907 } 2807 }
2908 } 2808 }
2909 h += TOOLTIP_BORDER; 2809 height += TOOLTIP_BORDER;
2910 w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; 2810 width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
2911 } else { 2811 } else {
2912 gtk_widget_destroy(gtkblist->tipwindow); 2812 return FALSE;
2913 gtkblist->tipwindow = NULL; 2813 }
2914 return; 2814
2915 } 2815 if (w)
2916 2816 *w = width;
2917 if (gtkblist->tooltipdata == NULL) { 2817 if (h)
2918 gtk_widget_destroy(gtkblist->tipwindow); 2818 *h = height;
2919 gtkblist->tipwindow = NULL; 2819
2920 return; 2820 return TRUE;
2821 }
2822
2823 static gboolean pidgin_blist_expand_timeout(GtkWidget *tv)
2824 {
2825 GtkTreePath *path;
2826 GtkTreeIter iter;
2827 PurpleBlistNode *node;
2828 GValue val;
2829 struct _pidgin_blist_node *gtknode;
2830
2831 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),
2832 &path, NULL, NULL, NULL))
2833 return FALSE;
2834 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
2835 val.g_type = 0;
2836 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
2837 node = g_value_get_pointer(&val);
2838
2839 if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) {
2840 gtk_tree_path_free(path);
2841 return FALSE;
2921 } 2842 }
2922 2843
2923 gtknode = node->ui_data; 2844 gtknode = node->ui_data;
2924 2845
2925 name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget))); 2846 if (!gtknode->contact_expanded) {
2926 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); 2847 GtkTreeIter i;
2927 gtk_window_set_title(GTK_WINDOW(gtkblist->tipwindow), name ? name : _("Buddy List")); 2848
2928 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); 2849 pidgin_blist_expand_contact_cb(NULL, node);
2929 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); 2850
2930 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", 2851 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->contact_rect);
2931 G_CALLBACK(pidgin_blist_paint_tip), NULL); 2852 gdk_drawable_get_size(GDK_DRAWABLE(tv->window), &(gtkblist->contact_rect.width), NULL);
2932 gtk_widget_ensure_style (gtkblist->tipwindow); 2853 gtkblist->mouseover_contact = node;
2933 2854 gtk_tree_path_down (path);
2934 #if GTK_CHECK_VERSION(2,2,0) 2855 while (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &i, path)) {
2935 gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); 2856 GdkRectangle rect;
2936 mon_num = gdk_screen_get_monitor_at_point(screen, x, y); 2857 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect);
2937 gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); 2858 gtkblist->contact_rect.height += rect.height;
2938 2859 gtk_tree_path_next(path);
2939 scr_w = mon_size.width + mon_size.x; 2860 }
2940 scr_h = mon_size.height + mon_size.y; 2861 }
2941 #else 2862 gtk_tree_path_free(path);
2942 scr_w = gdk_screen_width(); 2863 return FALSE;
2943 scr_h = gdk_screen_height(); 2864 }
2944 gdk_window_get_pointer(NULL, &x, &y, NULL); 2865
2945 mon_size.x = 0; 2866 static gboolean buddy_is_displayable(PurpleBuddy *buddy)
2946 mon_size.y = 0; 2867 {
2947 #endif 2868 struct _pidgin_blist_node *gtknode;
2948 2869
2949 #if GTK_CHECK_VERSION(2,2,0) 2870 if(!buddy)
2950 if (w > mon_size.width) 2871 return FALSE;
2951 w = mon_size.width - 10; 2872
2952 2873 gtknode = ((PurpleBlistNode*)buddy)->ui_data;
2953 if (h > mon_size.height) 2874
2954 h = mon_size.height - 10; 2875 return (purple_account_is_connected(buddy->account) &&
2955 #endif 2876 (purple_presence_is_online(buddy->presence) ||
2956 2877 (gtknode && gtknode->recent_signonoff) ||
2957 x -= ((w >> 1) + 4); 2878 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies") ||
2958 2879 purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline")));
2959 if ((y + h + 4) > scr_h || tooltip_top) 2880 }
2960 y = y - h - 5; 2881
2961 else 2882 void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget)
2962 y = y + 6; 2883 {
2963 2884 pidgin_tooltip_show(widget, node, pidgin_blist_create_tooltip_for_node, pidgin_blist_paint_tip);
2964 if (y < mon_size.y)
2965 y = mon_size.y;
2966
2967 if (y != mon_size.y) {
2968 if ((x + w) > scr_w)
2969 x -= (x + w + 5) - scr_w;
2970 else if (x < mon_size.x)
2971 x = mon_size.x;
2972 } else {
2973 x -= (w / 2 + 10);
2974 if (x < mon_size.x)
2975 x = mon_size.x;
2976 }
2977
2978 gtk_widget_set_size_request(gtkblist->tipwindow, w, h);
2979 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
2980 gtk_widget_show(gtkblist->tipwindow);
2981
2982 /* Hide the tooltip when the widget is destroyed */
2983 sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_blist_tooltip_destroy), NULL);
2984 g_signal_connect_swapped(G_OBJECT(gtkblist->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
2985
2986 return;
2987 } 2885 }
2988 2886
2989 static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context, 2887 static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context,
2990 gint x, gint y, guint time, gpointer user_data) 2888 gint x, gint y, guint time, gpointer user_data)
2991 { 2889 {
3031 } 2929 }
3032 2930
3033 return FALSE; 2931 return FALSE;
3034 } 2932 }
3035 2933
2934 static gboolean
2935 pidgin_blist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
2936 gpointer null, int *w, int *h)
2937 {
2938 GtkTreeIter iter;
2939 PurpleBlistNode *node;
2940 GValue val;
2941 gboolean editable = FALSE;
2942
2943 /* If we're editing a cell (e.g. alias editing), don't show the tooltip */
2944 g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL);
2945 if (editable)
2946 return FALSE;
2947
2948 if (gtkblist->tooltipdata) {
2949 gtkblist->tipwindow = NULL;
2950 pidgin_blist_destroy_tooltip_data();
2951 }
2952
2953 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
2954 val.g_type = 0;
2955 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
2956 node = g_value_get_pointer(&val);
2957 return pidgin_blist_create_tooltip_for_node(widget, node, w, h);
2958 }
2959
3036 static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) 2960 static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
3037 { 2961 {
3038 GtkTreePath *path;
3039 int delay;
3040
3041 delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
3042
3043 if (delay == 0)
3044 return FALSE;
3045
3046 if (gtkblist->timeout) {
3047 if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y))
3048 return FALSE;
3049 /* We've left the cell. Remove the timeout and create a new one below */
3050 pidgin_blist_tooltip_destroy();
3051 g_source_remove(gtkblist->timeout);
3052 }
3053
3054 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
3055 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->tip_rect);
3056
3057 if (path)
3058 gtk_tree_path_free(path);
3059 gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv);
3060
3061 if (gtkblist->mouseover_contact) { 2962 if (gtkblist->mouseover_contact) {
3062 if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { 2963 if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) {
3063 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); 2964 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact);
3064 gtkblist->mouseover_contact = NULL; 2965 gtkblist->mouseover_contact = NULL;
3065 } 2966 }
3068 return FALSE; 2969 return FALSE;
3069 } 2970 }
3070 2971
3071 static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) 2972 static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n)
3072 { 2973 {
3073
3074 if (gtkblist->timeout) { 2974 if (gtkblist->timeout) {
3075 g_source_remove(gtkblist->timeout); 2975 g_source_remove(gtkblist->timeout);
3076 gtkblist->timeout = 0; 2976 gtkblist->timeout = 0;
3077 } 2977 }
3078 2978
3079 if (gtkblist->drag_timeout) { 2979 if (gtkblist->drag_timeout) {
3080 g_source_remove(gtkblist->drag_timeout); 2980 g_source_remove(gtkblist->drag_timeout);
3081 gtkblist->drag_timeout = 0; 2981 gtkblist->drag_timeout = 0;
3082 } 2982 }
3083
3084 pidgin_blist_tooltip_destroy();
3085 2983
3086 if (gtkblist->mouseover_contact && 2984 if (gtkblist->mouseover_contact &&
3087 !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) && 2985 !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) &&
3088 (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) { 2986 (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) {
3089 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); 2987 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact);
5150 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(pidgin_blist_drag_data_rcv_cb), NULL); 5048 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(pidgin_blist_drag_data_rcv_cb), NULL);
5151 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(pidgin_blist_drag_data_get_cb), NULL); 5049 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(pidgin_blist_drag_data_get_cb), NULL);
5152 #ifdef _WIN32 5050 #ifdef _WIN32
5153 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL); 5051 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL);
5154 #endif 5052 #endif
5155
5156 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL); 5053 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL);
5157
5158 /* Tooltips */
5159 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); 5054 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL);
5160 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); 5055 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL);
5056
5057 /* Tooltips */
5058 pidgin_tooltip_setup_for_treeview(gtkblist->treeview, NULL,
5059 pidgin_blist_create_tooltip,
5060 pidgin_blist_paint_tip);
5161 5061
5162 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); 5062 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
5163 5063
5164 column = gtk_tree_view_column_new(); 5064 column = gtk_tree_view_column_new();
5165 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); 5065 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);