im.pidgin.pidgin.sadrul.tooltips: 3a80592a4aafc37dd3c7c423fe26ab92a3486504
sadrul at pidgin.im
sadrul at pidgin.im
Thu Dec 6 02:46:02 EST 2007
-----------------------------------------------------------------
Revision: 3a80592a4aafc37dd3c7c423fe26ab92a3486504
Ancestor: a9bb156e458b3ce77f9a7d20ff81ece2513296dc
Author: sadrul at pidgin.im
Date: 2007-12-06T07:33:53
Branch: im.pidgin.pidgin.sadrul.tooltips
Added files:
pidgin/pidgintooltip.c pidgin/pidgintooltip.h
Modified files:
pidgin/Makefile.am pidgin/gtkblist.c pidgin/gtkroomlist.c
ChangeLog:
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?
-------------- next part --------------
============================================================
--- pidgin/pidgintooltip.c 91204d8855a782415ac8c0acc45792afc0ec474a
+++ pidgin/pidgintooltip.c 91204d8855a782415ac8c0acc45792afc0ec474a
@@ -0,0 +1,268 @@
+/**
+ * @file pidgintooltip.c Pidgin Tooltip API
+ * @ingroup pidgin
+ */
+
+/* pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "internal.h"
+#include "prefs.h"
+#include "pidgin.h"
+#include "pidgintooltip.h"
+
+struct
+{
+ GtkWidget *widget;
+ int timeout;
+ GdkRectangle tip_rect;
+ GtkWidget *tipwindow;
+ PidginTooltipPaint paint_tooltip;
+} pidgin_tooltip;
+
+typedef struct
+{
+ GtkWidget *widget;
+ gpointer userdata;
+ PidginTooltipCreateForTree create_tooltip;
+ PidginTooltipPaint paint_tooltip;
+ GtkTreePath *path;
+} PidginTooltipData;
+
+static void
+destroy_tooltip_data(PidginTooltipData *data)
+{
+ gtk_tree_path_free(data->path);
+ g_free(data);
+}
+
+void pidgin_tooltip_destroy()
+{
+ if (pidgin_tooltip.timeout > 0) {
+ g_source_remove(pidgin_tooltip.timeout);
+ pidgin_tooltip.timeout = 0;
+ }
+ if (pidgin_tooltip.tipwindow) {
+ gtk_widget_destroy(pidgin_tooltip.tipwindow);
+ pidgin_tooltip.tipwindow = NULL;
+ }
+}
+
+static void
+pidgin_tooltip_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ if (pidgin_tooltip.paint_tooltip)
+ pidgin_tooltip.paint_tooltip(widget, event, data);
+}
+
+static void
+setup_tooltip_window(gpointer data, int w, int h)
+{
+ const char *name;
+ int sig;
+ int scr_w, scr_h, x, y;
+#if GTK_CHECK_VERSION(2,2,0)
+ int mon_num;
+ GdkScreen *screen = NULL;
+#endif
+ GdkRectangle mon_size;
+ GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
+
+ name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget));
+ gtk_widget_set_app_paintable(tipwindow, TRUE);
+ gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip"));
+ gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE);
+ gtk_widget_set_name(tipwindow, "gtk-tooltips");
+ g_signal_connect(G_OBJECT(tipwindow), "expose_event",
+ G_CALLBACK(pidgin_tooltip_expose_event), data);
+
+#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)
+ 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(tipwindow, w, h);
+ gtk_window_move(GTK_WINDOW(tipwindow), x, y);
+ gtk_widget_show(tipwindow);
+
+ /* Hide the tooltip when the widget is destroyed */
+ sig = g_signal_connect(G_OBJECT(pidgin_tooltip.widget), "destroy", G_CALLBACK(pidgin_tooltip_destroy), NULL);
+ g_signal_connect_swapped(G_OBJECT(tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
+}
+
+void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
+ PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip)
+{
+ GtkWidget *tipwindow;
+ int w, h;
+
+ pidgin_tooltip_destroy();
+ pidgin_tooltip.tipwindow = tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
+ pidgin_tooltip.widget = gtk_widget_get_toplevel(widget);
+ pidgin_tooltip.paint_tooltip = paint_tooltip;
+ gtk_widget_ensure_style(tipwindow);
+
+ if (!create_tooltip(tipwindow, userdata, &w, &h)) {
+ pidgin_tooltip_destroy();
+ return;
+ }
+ setup_tooltip_window(userdata, w, h);
+}
+
+static void
+pidgin_tooltip_draw(PidginTooltipData *data)
+{
+ GtkWidget *tipwindow;
+ GtkTreePath *path = NULL;
+ int w, h;
+
+ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(data->widget),
+ pidgin_tooltip.tip_rect.x,
+ pidgin_tooltip.tip_rect.y + (pidgin_tooltip.tip_rect.height/2),
+ &path, NULL, NULL, NULL)) {
+ pidgin_tooltip_destroy();
+ return;
+ }
+
+ if (data->path) {
+ if (gtk_tree_path_compare(data->path, path) == 0)
+ return;
+ gtk_tree_path_free(data->path);
+ data->path = NULL;
+ }
+
+ pidgin_tooltip.tipwindow = tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
+ pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget);
+ pidgin_tooltip.paint_tooltip = data->paint_tooltip;
+ gtk_widget_ensure_style(tipwindow);
+
+ if (!data->create_tooltip(tipwindow, path, data->userdata, &w, &h)) {
+ pidgin_tooltip_destroy();
+ gtk_tree_path_free(path);
+ return;
+ }
+
+ data->path = path;
+ setup_tooltip_window(data->userdata, w, h);
+}
+
+static gboolean
+pidgin_tooltip_timeout(gpointer data)
+{
+ pidgin_tooltip.timeout = 0;
+ pidgin_tooltip_draw(data);
+ return FALSE;
+}
+
+static gboolean
+row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer userdata)
+{
+ GtkTreePath *path;
+ int delay;
+
+ /* XXX: probably use something more generic? */
+ delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
+ if (delay == 0)
+ return FALSE;
+
+ if (pidgin_tooltip.timeout) {
+ if ((event->y >= pidgin_tooltip.tip_rect.y) && ((event->y - pidgin_tooltip.tip_rect.height) <= pidgin_tooltip.tip_rect.y))
+ return FALSE;
+ /* We've left the cell. Remove the timeout and create a new one below */
+ pidgin_tooltip_destroy();
+ }
+
+ gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
+
+ if (path == NULL) {
+ pidgin_tooltip_destroy();
+ return FALSE;
+ }
+
+ gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &pidgin_tooltip.tip_rect);
+
+ if (path)
+ gtk_tree_path_free(path);
+ pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, userdata);
+
+ return FALSE;
+}
+
+static gboolean
+row_leave_cb(GtkWidget *tv, GdkEvent *event, gpointer userdata)
+{
+ pidgin_tooltip_destroy();
+ return FALSE;
+}
+
+gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
+ PidginTooltipCreateForTree create_tooltip, PidginTooltipPaint paint_tooltip)
+{
+ PidginTooltipData *tdata = g_new0(PidginTooltipData, 1);
+ tdata->widget = tree;
+ tdata->userdata = userdata;
+ tdata->create_tooltip = create_tooltip;
+ tdata->paint_tooltip = paint_tooltip;
+
+ g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata);
+ g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), NULL);
+ g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata);
+ return TRUE;
+}
+
============================================================
--- pidgin/pidgintooltip.h c063d0d4afcad7a2391065c772251e5cf51cbde5
+++ pidgin/pidgintooltip.h c063d0d4afcad7a2391065c772251e5cf51cbde5
@@ -0,0 +1,66 @@
+/**
+ * @file pidgintooltip.h Pidgin Tooltip API
+ * @ingroup pidgin
+ */
+
+/* pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#ifndef _PIDGIN_TOOLTIP_H_
+#define _PIDGIN_TOOLTIP_H_
+
+#include <gtk/gtk.h>
+
+typedef gboolean (*PidginTooltipCreateForTree)(GtkWidget *window, GtkTreePath *path, gpointer userdata, int *w, int *h);
+typedef gboolean (*PidginTooltipCreate)(GtkWidget *window, gpointer userdata, int *w, int *h);
+typedef gboolean (*PidginTooltipPaint)(GtkWidget *widget, GdkEventExpose *event, gpointer data);
+
+/**
+ * Setup tooltip drawing functions for a treeview.
+ *
+ * @param tree The treeview
+ * @param userdata The userdata to send to the callback functions
+ * @param create_cb Callback function to create the tooltip from the GtkTreePath
+ * @param paint_cb Callback function to paint the tooltip
+ *
+ * @return @c TRUE if the tooltip callbacks were setup correctly.
+ * @since
+ */
+gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
+ PidginTooltipCreateForTree create_cb, PidginTooltipPaint paint_cb);
+
+/**
+ * Destroy the tooltip.
+ */
+void pidgin_tooltip_destroy(void);
+
+/**
+ * Create and show a tooltip.
+ *
+ * @param widget The widget the tooltip is for
+ * @param userdata The userdata to send to the callback functions
+ * @param create_cb Callback function to create the tooltip from the GtkTreePath
+ * @param paint_cb Callback function to paint the tooltip
+ *
+ * @since
+ */
+void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
+ PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
+#endif
============================================================
--- pidgin/Makefile.am 0668d0a91a43144b49c0fcc7424d8764f5f65ac7
+++ pidgin/Makefile.am 510e3537b50a6f6f3da3cb4cc110d44d3e183beb
@@ -119,7 +119,8 @@ pidgin_SOURCES = \
gtkthemes.c \
gtkutils.c \
gtkwhiteboard.c \
- minidialog.c
+ minidialog.c \
+ pidgintooltip.c
pidgin_headers = \
eggtrayicon.h \
@@ -172,6 +173,7 @@ pidgin_headers = \
gtkutils.h \
gtkwhiteboard.h \
minidialog.h \
+ pidgintooltip.h \
pidgin.h
pidginincludedir=$(includedir)/pidgin
============================================================
--- pidgin/gtkblist.c 9dd63f2810bc42c1b2dbdbf2af73731616e9161f
+++ pidgin/gtkblist.c 830190eabd127478fc8a6632639f37c5d1c77ca2
@@ -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 gboolean get_iter_from_node(Purpl
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 @@ static struct tooltip_data * create_tip_
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 @@ static void pidgin_blist_paint_tip(GtkWi
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 @@ static void pidgin_blist_paint_tip(GtkWi
current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER;
}
+ return FALSE;
}
-
-void pidgin_blist_tooltip_destroy()
+static void
+pidgin_blist_destroy_tooltip_data()
{
while(gtkblist->tooltipdata) {
struct tooltip_data *td = gtkblist->tooltipdata->data;
@@ -2759,12 +2762,62 @@ void pidgin_blist_tooltip_destroy()
g_free(td);
gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata);
}
+}
- if (gtkblist->tipwindow == NULL)
- return;
+void pidgin_blist_tooltip_destroy()
+{
+ pidgin_blist_destroy_tooltip_data();
+ pidgin_tooltip_destroy();
+}
- gtk_widget_destroy(gtkblist->tipwindow);
- gtkblist->tipwindow = NULL;
+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 @@ static gboolean buddy_is_displayable(Pur
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 @@ static gboolean pidgin_blist_drag_motion
return FALSE;
}
-static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
+static gboolean
+pidgin_blist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
+ gpointer null, int *w, int *h)
{
- GtkTreePath *path;
- int delay;
+ GtkTreeIter iter;
+ PurpleBlistNode *node;
+ GValue val;
+ gboolean editable = FALSE;
- delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-
- if (delay == 0)
+ /* 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);
+ if (gtkblist->tooltipdata) {
+ gtkblist->tipwindow = NULL;
+ pidgin_blist_destroy_tooltip_data();
}
- 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);
+ 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);
+}
- if (path)
- gtk_tree_path_free(path);
- gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv);
-
+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 (GtkWi
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 @@ static void pidgin_blist_leave_cb (GtkWi
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 @@ static void pidgin_blist_show(PurpleBudd
#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);
============================================================
--- pidgin/gtkroomlist.c b7ad01a0b16840852c4d1391a75df6fedb57848b
+++ pidgin/gtkroomlist.c 406c5ba939479f119b14cf65bedf4adbd7b1be29
@@ -28,6 +28,8 @@
#include "pidgin.h"
#include "gtkutils.h"
#include "pidginstock.h"
+#include "pidgintooltip.h"
+
#include "debug.h"
#include "account.h"
#include "connection.h"
@@ -340,33 +342,14 @@ static void row_expanded_cb(GtkTreeView
}
}
-static void pidgin_roomlist_tooltip_destroy(PidginRoomlist *grl)
-{
- if ((grl == NULL) || (grl->tipwindow == NULL))
- return;
-
- gtk_widget_destroy(grl->tipwindow);
- grl->tipwindow = NULL;
-}
-
-static void pidgin_roomlist_tooltip_destroy_cb(GObject *object, PidginRoomlist *grl)
-{
- if ((grl == NULL) || (grl->tipwindow == NULL))
- return;
-
- if (grl->timeout)
- g_source_remove(grl->timeout);
- grl->timeout = 0;
-
- pidgin_roomlist_tooltip_destroy(grl);
-}
-
#define SMALL_SPACE 6
#define TOOLTIP_BORDER 12
-static void pidgin_roomlist_paint_tip(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+static gboolean
+pidgin_roomlist_paint_tooltip(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
- PidginRoomlist *grl = (PidginRoomlist *)user_data;
+ PurpleRoomlist *list = user_data;
+ PidginRoomlist *grl = list->ui_data;
GtkStyle *style;
int current_height, max_width;
int max_text_width;
@@ -404,15 +387,13 @@ static void pidgin_roomlist_paint_tip(Gt
current_height + grl->tip_name_height,
grl->tip_layout);
}
-
+ return FALSE;
}
-static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list)
+static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list, GtkTreePath *path)
{
PidginRoomlist *grl = list->ui_data;
- GtkWidget *tv = grl->tree;
PurpleRoomlistRoom *room;
- GtkTreePath *path;
GtkTreeIter iter;
GValue val;
gchar *name, *tmp, *node_name;
@@ -421,10 +402,11 @@ static gboolean pidgin_roomlist_create_t
gint j;
gboolean first = TRUE;
+#if 0
if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2),
&path, NULL, NULL, NULL))
return FALSE;
-
+#endif
gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path);
val.g_type = 0;
@@ -460,8 +442,6 @@ static gboolean pidgin_roomlist_create_t
g_free(label);
}
- gtk_tree_path_free(path);
-
grl->tip_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL);
grl->tip_name_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL);
@@ -492,158 +472,24 @@ static gboolean pidgin_roomlist_create_t
return TRUE;
}
-static void pidgin_roomlist_draw_tooltip(PurpleRoomlist *list, GtkWidget *widget)
+static gboolean
+pidgin_roomlist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
+ gpointer data, int *w, int *h)
{
+ PurpleRoomlist *list = data;
PidginRoomlist *grl = list->ui_data;
- int scr_w, scr_h, w, h, x, y;
-#if GTK_CHECK_VERSION(2,2,0)
- int mon_num;
- GdkScreen *screen = NULL;
-#endif
- GdkRectangle mon_size;
- int sig;
- const char *name;
-
- pidgin_roomlist_tooltip_destroy(grl);
- grl->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
- gtk_widget_ensure_style (grl->tipwindow);
-
- if (!pidgin_roomlist_create_tip(list)) {
- pidgin_roomlist_tooltip_destroy(grl);
- return;
- }
-
- name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget)));
- gtk_widget_set_app_paintable(grl->tipwindow, TRUE);
- gtk_window_set_title(GTK_WINDOW(grl->tipwindow), name ? name : _("Room List"));
- gtk_window_set_resizable(GTK_WINDOW(grl->tipwindow), FALSE);
- gtk_widget_set_name(grl->tipwindow, "gtk-tooltips");
- g_signal_connect(G_OBJECT(grl->tipwindow), "expose_event",
- G_CALLBACK(pidgin_roomlist_paint_tip), grl);
-
- w = TOOLTIP_BORDER + SMALL_SPACE +
- MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER;
- h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height
- + TOOLTIP_BORDER;
-
-#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)
- 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(grl->tipwindow, w, h);
- gtk_window_move(GTK_WINDOW(grl->tipwindow), x, y);
- gtk_widget_show(grl->tipwindow);
-
- /* Hide the tooltip when the widget is destroyed */
- sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_roomlist_tooltip_destroy_cb), grl);
- g_signal_connect_swapped(G_OBJECT(grl->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
-}
-
-static gboolean pidgin_roomlist_tooltip_timeout(PurpleRoomlist *list)
-{
- PidginRoomlist *grl = list->ui_data;
- GtkWidget *tv = grl->tree;
- GtkTreePath *path;
-
- pidgin_roomlist_tooltip_destroy(grl);
-
- if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2),
- &path, NULL, NULL, NULL))
+ grl->tipwindow = widget;
+ if (!pidgin_roomlist_create_tip(data, path))
return FALSE;
-
- pidgin_roomlist_draw_tooltip(list, GTK_WIDGET(grl->tree));
-
- return FALSE;
+ if (w)
+ *w = TOOLTIP_BORDER + SMALL_SPACE +
+ MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER;
+ if (h)
+ *h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height
+ + TOOLTIP_BORDER;
+ return TRUE;
}
-static gboolean row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer user_data)
-{
- PurpleRoomlist *list = user_data;
- PidginRoomlist *grl = list->ui_data;
- GtkTreePath *path;
- int delay;
-
- /* XXX: should this be using the blist delay pref? */
- delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-
- if (delay == 0)
- return FALSE;
-
- if (grl->timeout) {
- if ((event->y > grl->tip_rect.y) && ((event->y - grl->tip_rect.height) < grl->tip_rect.y))
- return FALSE;
- /* We've left the cell. Remove the timeout and create a new one below */
- pidgin_roomlist_tooltip_destroy(grl);
- }
-
- gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
-
- if (path == NULL) {
- pidgin_roomlist_tooltip_destroy(grl);
- return FALSE;
- }
-
- gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &grl->tip_rect);
-
- if (path)
- gtk_tree_path_free(path);
- grl->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_roomlist_tooltip_timeout, list);
-
- return FALSE;
-}
-
-static void row_leave_cb(GtkWidget *tv, GdkEventCrossing *e, gpointer user_data)
-{
- PurpleRoomlist *list = user_data;
- PidginRoomlist *grl = list->ui_data;
-
- if (grl->timeout) {
- g_source_remove(grl->timeout);
- grl->timeout = 0;
- }
-
- pidgin_roomlist_tooltip_destroy(grl);
-}
-
static gboolean account_filter_func(PurpleAccount *account)
{
PurpleConnection *gc = purple_account_get_connection(account);
@@ -967,6 +813,9 @@ static void pidgin_roomlist_set_fields(P
g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), list);
g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), list);
#endif
+ pidgin_tooltip_setup_for_treeview(tree, list,
+ pidgin_roomlist_create_tooltip,
+ pidgin_roomlist_paint_tooltip);
/* Enable CTRL+F searching */
gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN);
More information about the Commits
mailing list