3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "pidgintooltip.h"
28 static gboolean enable_tooltips
;
29 static int tooltip_delay
= -1;
35 GdkRectangle tip_rect
;
37 PidginTooltipPaint paint_tooltip
;
44 PidginTooltipPaint paint_tooltip
;
47 PidginTooltipCreateForTree create_tooltip
;
51 PidginTooltipCreate create_tooltip
;
57 initialize_tooltip_delay()
59 GtkSettings
*settings
;
61 if (tooltip_delay
!= -1)
64 settings
= gtk_settings_get_default();
66 g_object_get(settings
, "gtk-enable-tooltips", &enable_tooltips
, NULL
);
67 g_object_get(settings
, "gtk-tooltip-timeout", &tooltip_delay
, NULL
);
71 destroy_tooltip_data(PidginTooltipData
*data
)
73 if (data
->common
.treeview
.path
)
74 gtk_tree_path_free(data
->common
.treeview
.path
);
75 pidgin_tooltip_destroy();
79 void pidgin_tooltip_destroy()
81 if (pidgin_tooltip
.timeout
> 0) {
82 g_source_remove(pidgin_tooltip
.timeout
);
83 pidgin_tooltip
.timeout
= 0;
85 if (pidgin_tooltip
.tipwindow
) {
86 gtk_widget_destroy(pidgin_tooltip
.tipwindow
);
87 pidgin_tooltip
.tipwindow
= NULL
;
92 pidgin_tooltip_draw_cb(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
94 GtkAllocation allocation
;
96 gtk_widget_get_allocation(widget
, &allocation
);
98 if (pidgin_tooltip
.paint_tooltip
) {
99 GtkStyleContext
*context
= gtk_widget_get_style_context(widget
);
100 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_TOOLTIP
);
101 gtk_render_background(context
, cr
,
102 0, 0, allocation
.width
, allocation
.height
);
103 pidgin_tooltip
.paint_tooltip(widget
, cr
, data
);
109 setup_tooltip_window(void)
112 GtkWidget
*tipwindow
;
114 tipwindow
= gtk_window_new(GTK_WINDOW_POPUP
);
115 name
= gtk_window_get_title(GTK_WINDOW(pidgin_tooltip
.widget
));
116 gtk_window_set_type_hint(GTK_WINDOW(tipwindow
), GDK_WINDOW_TYPE_HINT_TOOLTIP
);
117 gtk_widget_set_app_paintable(tipwindow
, TRUE
);
118 gtk_window_set_title(GTK_WINDOW(tipwindow
), name
? name
: _("Pidgin Tooltip"));
119 gtk_window_set_resizable(GTK_WINDOW(tipwindow
), FALSE
);
120 gtk_widget_set_name(tipwindow
, "gtk-tooltips");
121 gtk_widget_ensure_style(tipwindow
);
122 gtk_widget_realize(tipwindow
);
127 setup_tooltip_window_position(gpointer data
, int w
, int h
)
129 int scr_w
, scr_h
, x
, y
, dy
;
130 int preserved_x
, preserved_y
;
132 GdkScreen
*screen
= NULL
;
133 GdkRectangle mon_size
;
134 GtkWidget
*tipwindow
= pidgin_tooltip
.tipwindow
;
139 seat
= gdk_display_get_default_seat(gdk_display_get_default());
140 dev
= gdk_seat_get_pointer(seat
);
141 gdk_device_get_position(dev
, &screen
, &x
, &y
);
143 mon_num
= gdk_screen_get_monitor_at_point(screen
, x
, y
);
144 gdk_screen_get_monitor_geometry(screen
, mon_num
, &mon_size
);
146 scr_w
= mon_size
.width
+ mon_size
.x
;
147 scr_h
= mon_size
.height
+ mon_size
.y
;
149 dy
= gdk_display_get_default_cursor_size(gdk_display_get_default()) / 2;
151 if (w
> mon_size
.width
)
152 w
= mon_size
.width
- 10;
154 if (h
> mon_size
.height
)
155 h
= mon_size
.height
- 10;
162 if ((y
+ h
+ 4) > scr_h
)
170 if (y
!= mon_size
.y
) {
172 x
-= (x
+ w
+ 5) - scr_w
;
173 else if (x
< mon_size
.x
)
181 /* If the mouse covered by the tipwindow, move the tipwindow
182 * to the righ side of the it */
183 if ((preserved_x
>= x
) && (preserved_x
<= (x
+ w
))
184 && (preserved_y
>= y
) && (preserved_y
<= (y
+ h
)))
185 x
= preserved_x
+ dy
;
187 gtk_widget_set_size_request(tipwindow
, w
, h
);
188 gtk_window_move(GTK_WINDOW(tipwindow
), x
, y
);
189 gtk_widget_show(tipwindow
);
191 g_signal_connect(G_OBJECT(tipwindow
), "draw",
192 G_CALLBACK(pidgin_tooltip_draw_cb
), data
);
194 /* Hide the tooltip when the widget is destroyed */
195 g_signal_connect_object(G_OBJECT(pidgin_tooltip
.widget
),
196 "destroy", G_CALLBACK(pidgin_tooltip_destroy
),
197 G_OBJECT(tipwindow
), 0);
200 void pidgin_tooltip_show(GtkWidget
*widget
, gpointer userdata
,
201 PidginTooltipCreate create_tooltip
, PidginTooltipPaint paint_tooltip
)
203 GtkWidget
*tipwindow
;
206 pidgin_tooltip_destroy();
208 pidgin_tooltip
.widget
= gtk_widget_get_toplevel(widget
);
209 pidgin_tooltip
.tipwindow
= tipwindow
= setup_tooltip_window();
210 pidgin_tooltip
.paint_tooltip
= paint_tooltip
;
212 if (!create_tooltip(tipwindow
, userdata
, &w
, &h
)) {
213 pidgin_tooltip_destroy();
216 setup_tooltip_window_position(userdata
, w
, h
);
220 reset_data_treepath(PidginTooltipData
*data
)
222 gtk_tree_path_free(data
->common
.treeview
.path
);
223 data
->common
.treeview
.path
= NULL
;
227 pidgin_tooltip_draw(PidginTooltipData
*data
)
229 GtkWidget
*tipwindow
;
232 pidgin_tooltip_destroy();
234 pidgin_tooltip
.widget
= gtk_widget_get_toplevel(data
->widget
);
235 pidgin_tooltip
.tipwindow
= tipwindow
= setup_tooltip_window();
236 pidgin_tooltip
.paint_tooltip
= data
->paint_tooltip
;
238 if (!data
->common
.widget
.create_tooltip(tipwindow
, data
->userdata
, &w
, &h
)) {
239 if (tipwindow
== pidgin_tooltip
.tipwindow
)
240 pidgin_tooltip_destroy();
244 setup_tooltip_window_position(data
->userdata
, w
, h
);
248 pidgin_tooltip_draw_tree(PidginTooltipData
*data
)
250 GtkWidget
*tipwindow
;
251 GtkTreePath
*path
= NULL
;
254 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(data
->widget
),
255 pidgin_tooltip
.tip_rect
.x
,
256 pidgin_tooltip
.tip_rect
.y
+ (pidgin_tooltip
.tip_rect
.height
/2),
257 &path
, NULL
, NULL
, NULL
)) {
258 pidgin_tooltip_destroy();
262 if (data
->common
.treeview
.path
) {
263 if (gtk_tree_path_compare(data
->common
.treeview
.path
, path
) == 0) {
264 gtk_tree_path_free(path
);
267 gtk_tree_path_free(data
->common
.treeview
.path
);
268 data
->common
.treeview
.path
= NULL
;
271 pidgin_tooltip_destroy();
273 pidgin_tooltip
.widget
= gtk_widget_get_toplevel(data
->widget
);
274 pidgin_tooltip
.tipwindow
= tipwindow
= setup_tooltip_window();
275 pidgin_tooltip
.paint_tooltip
= data
->paint_tooltip
;
277 if (!data
->common
.treeview
.create_tooltip(tipwindow
, path
, data
->userdata
, &w
, &h
)) {
278 if (tipwindow
== pidgin_tooltip
.tipwindow
)
279 pidgin_tooltip_destroy();
280 gtk_tree_path_free(path
);
284 setup_tooltip_window_position(data
->userdata
, w
, h
);
286 data
->common
.treeview
.path
= path
;
287 g_signal_connect_swapped(G_OBJECT(pidgin_tooltip
.tipwindow
), "destroy",
288 G_CALLBACK(reset_data_treepath
), data
);
292 pidgin_tooltip_timeout(gpointer data
)
294 PidginTooltipData
*tdata
= data
;
295 pidgin_tooltip
.timeout
= 0;
296 if (GTK_IS_TREE_VIEW(tdata
->widget
))
297 pidgin_tooltip_draw_tree(data
);
299 pidgin_tooltip_draw(data
);
304 row_motion_cb(GtkWidget
*tv
, GdkEventMotion
*event
, gpointer userdata
)
308 if (event
->window
!= gtk_tree_view_get_bin_window(GTK_TREE_VIEW(tv
)))
309 return FALSE
; /* The cursor is probably on the TreeView's header. */
311 initialize_tooltip_delay();
312 if (!enable_tooltips
)
315 if (pidgin_tooltip
.timeout
) {
316 if ((event
->y
>= pidgin_tooltip
.tip_rect
.y
) && ((event
->y
- pidgin_tooltip
.tip_rect
.height
) <= pidgin_tooltip
.tip_rect
.y
))
318 /* We've left the cell. Remove the timeout and create a new one below */
319 pidgin_tooltip_destroy();
322 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
325 pidgin_tooltip_destroy();
329 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv
), path
, NULL
, &pidgin_tooltip
.tip_rect
);
330 gtk_tree_path_free(path
);
332 pidgin_tooltip
.timeout
= g_timeout_add(tooltip_delay
, (GSourceFunc
)pidgin_tooltip_timeout
, userdata
);
338 widget_leave_cb(GtkWidget
*tv
, GdkEvent
*event
, gpointer userdata
)
340 pidgin_tooltip_destroy();
344 gboolean
pidgin_tooltip_setup_for_treeview(GtkWidget
*tree
, gpointer userdata
,
345 PidginTooltipCreateForTree create_tooltip
, PidginTooltipPaint paint_tooltip
)
347 PidginTooltipData
*tdata
= g_new0(PidginTooltipData
, 1);
348 tdata
->widget
= tree
;
349 tdata
->userdata
= userdata
;
350 tdata
->common
.treeview
.create_tooltip
= create_tooltip
;
351 tdata
->paint_tooltip
= paint_tooltip
;
353 g_signal_connect(G_OBJECT(tree
), "motion-notify-event", G_CALLBACK(row_motion_cb
), tdata
);
354 g_signal_connect(G_OBJECT(tree
), "leave-notify-event", G_CALLBACK(widget_leave_cb
), NULL
);
355 g_signal_connect(G_OBJECT(tree
), "scroll-event", G_CALLBACK(widget_leave_cb
), NULL
);
356 g_signal_connect_swapped(G_OBJECT(tree
), "destroy", G_CALLBACK(destroy_tooltip_data
), tdata
);
361 widget_motion_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
363 initialize_tooltip_delay();
365 pidgin_tooltip_destroy();
366 if (!enable_tooltips
)
369 pidgin_tooltip
.timeout
= g_timeout_add(tooltip_delay
, (GSourceFunc
)pidgin_tooltip_timeout
, data
);
373 gboolean
pidgin_tooltip_setup_for_widget(GtkWidget
*widget
, gpointer userdata
,
374 PidginTooltipCreate create_tooltip
, PidginTooltipPaint paint_tooltip
)
376 PidginTooltipData
*wdata
= g_new0(PidginTooltipData
, 1);
377 wdata
->widget
= widget
;
378 wdata
->userdata
= userdata
;
379 wdata
->common
.widget
.create_tooltip
= create_tooltip
;
380 wdata
->paint_tooltip
= paint_tooltip
;
382 g_signal_connect(G_OBJECT(widget
), "motion-notify-event", G_CALLBACK(widget_motion_cb
), wdata
);
383 g_signal_connect(G_OBJECT(widget
), "leave-notify-event", G_CALLBACK(widget_leave_cb
), NULL
);
384 g_signal_connect(G_OBJECT(widget
), "scroll-event", G_CALLBACK(widget_leave_cb
), NULL
);
385 g_signal_connect_swapped(G_OBJECT(widget
), "destroy", G_CALLBACK(destroy_tooltip_data
), wdata
);