Add deleted HTTP digest functions to ChangeLog.API.
[pidgin-git.git] / pidgin / pidgintooltip.c
blob9f6d8729a52d9b46d5b28c2de649549733c91f21
1 /* pidgin
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
5 * source distribution.
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
22 #include "internal.h"
23 #include "prefs.h"
24 #include "pidgin.h"
25 #include "pidgintooltip.h"
26 #include "debug.h"
28 static gboolean enable_tooltips;
29 static int tooltip_delay = -1;
31 struct
33 GtkWidget *widget;
34 int timeout;
35 GdkRectangle tip_rect;
36 GtkWidget *tipwindow;
37 PidginTooltipPaint paint_tooltip;
38 } pidgin_tooltip;
40 typedef struct
42 GtkWidget *widget;
43 gpointer userdata;
44 PidginTooltipPaint paint_tooltip;
45 union {
46 struct {
47 PidginTooltipCreateForTree create_tooltip;
48 GtkTreePath *path;
49 } treeview;
50 struct {
51 PidginTooltipCreate create_tooltip;
52 } widget;
53 } common;
54 } PidginTooltipData;
56 static void
57 initialize_tooltip_delay()
59 GtkSettings *settings;
61 if (tooltip_delay != -1)
62 return;
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);
70 static void
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();
76 g_free(data);
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;
91 static gboolean
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);
105 return FALSE;
108 static GtkWidget*
109 setup_tooltip_window(void)
111 const char *name;
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);
123 return tipwindow;
126 static void
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;
131 int mon_num;
132 GdkScreen *screen = NULL;
133 GdkRectangle mon_size;
134 GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
136 GdkSeat *seat;
137 GdkDevice *dev;
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;
157 preserved_x = x;
158 preserved_y = y;
160 x -= ((w >> 1) + 4);
162 if ((y + h + 4) > scr_h)
163 y = y - h - dy - 5;
164 else
165 y = y + dy + 6;
167 if (y < mon_size.y)
168 y = mon_size.y;
170 if (y != mon_size.y) {
171 if ((x + w) > scr_w)
172 x -= (x + w + 5) - scr_w;
173 else if (x < mon_size.x)
174 x = mon_size.x;
175 } else {
176 x -= (w / 2 + 10);
177 if (x < mon_size.x)
178 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;
204 int w, h;
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();
214 return;
216 setup_tooltip_window_position(userdata, w, h);
219 static void
220 reset_data_treepath(PidginTooltipData *data)
222 gtk_tree_path_free(data->common.treeview.path);
223 data->common.treeview.path = NULL;
226 static void
227 pidgin_tooltip_draw(PidginTooltipData *data)
229 GtkWidget *tipwindow;
230 int w, h;
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();
241 return;
244 setup_tooltip_window_position(data->userdata, w, h);
247 static void
248 pidgin_tooltip_draw_tree(PidginTooltipData *data)
250 GtkWidget *tipwindow;
251 GtkTreePath *path = NULL;
252 int w, h;
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();
259 return;
262 if (data->common.treeview.path) {
263 if (gtk_tree_path_compare(data->common.treeview.path, path) == 0) {
264 gtk_tree_path_free(path);
265 return;
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);
281 return;
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);
291 static gboolean
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);
298 else
299 pidgin_tooltip_draw(data);
300 return FALSE;
303 static gboolean
304 row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer userdata)
306 GtkTreePath *path;
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)
313 return FALSE;
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))
317 return FALSE;
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);
324 if (path == NULL) {
325 pidgin_tooltip_destroy();
326 return FALSE;
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);
334 return FALSE;
337 static gboolean
338 widget_leave_cb(GtkWidget *tv, GdkEvent *event, gpointer userdata)
340 pidgin_tooltip_destroy();
341 return FALSE;
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);
357 return TRUE;
360 static gboolean
361 widget_motion_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
363 initialize_tooltip_delay();
365 pidgin_tooltip_destroy();
366 if (!enable_tooltips)
367 return FALSE;
369 pidgin_tooltip.timeout = g_timeout_add(tooltip_delay, (GSourceFunc)pidgin_tooltip_timeout, data);
370 return FALSE;
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);
386 return TRUE;