Mercurial > pidgin.yaz
comparison pidgin/pidgintooltip.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 | |
children | b93c099dcfe4 |
comparison
equal
deleted
inserted
replaced
21789:cbf778310bdd | 21877:6bf73aea6450 |
---|---|
1 /** | |
2 * @file pidgintooltip.c Pidgin Tooltip API | |
3 * @ingroup pidgin | |
4 */ | |
5 | |
6 /* pidgin | |
7 * | |
8 * Pidgin is the legal property of its developers, whose names are too numerous | |
9 * to list here. Please refer to the COPYRIGHT file distributed with this | |
10 * source distribution. | |
11 * | |
12 * This program is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU General Public License as published by | |
14 * the Free Software Foundation; either version 2 of the License, or | |
15 * (at your option) any later version. | |
16 * | |
17 * This program is distributed in the hope that it will be useful, | |
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 * GNU General Public License for more details. | |
21 * | |
22 * You should have received a copy of the GNU General Public License | |
23 * along with this program; if not, write to the Free Software | |
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA | |
25 */ | |
26 | |
27 #include "internal.h" | |
28 #include "prefs.h" | |
29 #include "pidgin.h" | |
30 #include "pidgintooltip.h" | |
31 | |
32 struct | |
33 { | |
34 GtkWidget *widget; | |
35 int timeout; | |
36 GdkRectangle tip_rect; | |
37 GtkWidget *tipwindow; | |
38 PidginTooltipPaint paint_tooltip; | |
39 } pidgin_tooltip; | |
40 | |
41 typedef struct | |
42 { | |
43 GtkWidget *widget; | |
44 gpointer userdata; | |
45 PidginTooltipCreateForTree create_tooltip; | |
46 PidginTooltipPaint paint_tooltip; | |
47 GtkTreePath *path; | |
48 } PidginTooltipData; | |
49 | |
50 static void | |
51 destroy_tooltip_data(PidginTooltipData *data) | |
52 { | |
53 gtk_tree_path_free(data->path); | |
54 g_free(data); | |
55 } | |
56 | |
57 void pidgin_tooltip_destroy() | |
58 { | |
59 if (pidgin_tooltip.timeout > 0) { | |
60 g_source_remove(pidgin_tooltip.timeout); | |
61 pidgin_tooltip.timeout = 0; | |
62 } | |
63 if (pidgin_tooltip.tipwindow) { | |
64 gtk_widget_destroy(pidgin_tooltip.tipwindow); | |
65 pidgin_tooltip.tipwindow = NULL; | |
66 } | |
67 } | |
68 | |
69 static void | |
70 pidgin_tooltip_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) | |
71 { | |
72 if (pidgin_tooltip.paint_tooltip) | |
73 pidgin_tooltip.paint_tooltip(widget, event, data); | |
74 } | |
75 | |
76 static void | |
77 setup_tooltip_window(gpointer data, int w, int h) | |
78 { | |
79 const char *name; | |
80 int sig; | |
81 int scr_w, scr_h, x, y; | |
82 #if GTK_CHECK_VERSION(2,2,0) | |
83 int mon_num; | |
84 GdkScreen *screen = NULL; | |
85 #endif | |
86 GdkRectangle mon_size; | |
87 GtkWidget *tipwindow = pidgin_tooltip.tipwindow; | |
88 | |
89 name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget)); | |
90 gtk_widget_set_app_paintable(tipwindow, TRUE); | |
91 gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip")); | |
92 gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE); | |
93 gtk_widget_set_name(tipwindow, "gtk-tooltips"); | |
94 g_signal_connect(G_OBJECT(tipwindow), "expose_event", | |
95 G_CALLBACK(pidgin_tooltip_expose_event), data); | |
96 | |
97 #if GTK_CHECK_VERSION(2,2,0) | |
98 gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); | |
99 mon_num = gdk_screen_get_monitor_at_point(screen, x, y); | |
100 gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); | |
101 | |
102 scr_w = mon_size.width + mon_size.x; | |
103 scr_h = mon_size.height + mon_size.y; | |
104 #else | |
105 scr_w = gdk_screen_width(); | |
106 scr_h = gdk_screen_height(); | |
107 gdk_window_get_pointer(NULL, &x, &y, NULL); | |
108 mon_size.x = 0; | |
109 mon_size.y = 0; | |
110 #endif | |
111 | |
112 #if GTK_CHECK_VERSION(2,2,0) | |
113 if (w > mon_size.width) | |
114 w = mon_size.width - 10; | |
115 | |
116 if (h > mon_size.height) | |
117 h = mon_size.height - 10; | |
118 #endif | |
119 x -= ((w >> 1) + 4); | |
120 | |
121 if ((y + h + 4) > scr_h) | |
122 y = y - h - 5; | |
123 else | |
124 y = y + 6; | |
125 | |
126 if (y < mon_size.y) | |
127 y = mon_size.y; | |
128 | |
129 if (y != mon_size.y) { | |
130 if ((x + w) > scr_w) | |
131 x -= (x + w + 5) - scr_w; | |
132 else if (x < mon_size.x) | |
133 x = mon_size.x; | |
134 } else { | |
135 x -= (w / 2 + 10); | |
136 if (x < mon_size.x) | |
137 x = mon_size.x; | |
138 } | |
139 | |
140 gtk_widget_set_size_request(tipwindow, w, h); | |
141 gtk_window_move(GTK_WINDOW(tipwindow), x, y); | |
142 gtk_widget_show(tipwindow); | |
143 | |
144 /* Hide the tooltip when the widget is destroyed */ | |
145 sig = g_signal_connect(G_OBJECT(pidgin_tooltip.widget), "destroy", G_CALLBACK(pidgin_tooltip_destroy), NULL); | |
146 g_signal_connect_swapped(G_OBJECT(tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig)); | |
147 } | |
148 | |
149 void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata, | |
150 PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip) | |
151 { | |
152 GtkWidget *tipwindow; | |
153 int w, h; | |
154 | |
155 pidgin_tooltip_destroy(); | |
156 pidgin_tooltip.tipwindow = tipwindow = gtk_window_new(GTK_WINDOW_POPUP); | |
157 pidgin_tooltip.widget = gtk_widget_get_toplevel(widget); | |
158 pidgin_tooltip.paint_tooltip = paint_tooltip; | |
159 gtk_widget_ensure_style(tipwindow); | |
160 | |
161 if (!create_tooltip(tipwindow, userdata, &w, &h)) { | |
162 pidgin_tooltip_destroy(); | |
163 return; | |
164 } | |
165 setup_tooltip_window(userdata, w, h); | |
166 } | |
167 | |
168 static void | |
169 pidgin_tooltip_draw(PidginTooltipData *data) | |
170 { | |
171 GtkWidget *tipwindow; | |
172 GtkTreePath *path = NULL; | |
173 int w, h; | |
174 | |
175 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(data->widget), | |
176 pidgin_tooltip.tip_rect.x, | |
177 pidgin_tooltip.tip_rect.y + (pidgin_tooltip.tip_rect.height/2), | |
178 &path, NULL, NULL, NULL)) { | |
179 pidgin_tooltip_destroy(); | |
180 return; | |
181 } | |
182 | |
183 if (data->path) { | |
184 if (gtk_tree_path_compare(data->path, path) == 0) | |
185 return; | |
186 gtk_tree_path_free(data->path); | |
187 data->path = NULL; | |
188 } | |
189 | |
190 pidgin_tooltip.tipwindow = tipwindow = gtk_window_new(GTK_WINDOW_POPUP); | |
191 pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget); | |
192 pidgin_tooltip.paint_tooltip = data->paint_tooltip; | |
193 gtk_widget_ensure_style(tipwindow); | |
194 | |
195 if (!data->create_tooltip(tipwindow, path, data->userdata, &w, &h)) { | |
196 pidgin_tooltip_destroy(); | |
197 gtk_tree_path_free(path); | |
198 return; | |
199 } | |
200 | |
201 data->path = path; | |
202 setup_tooltip_window(data->userdata, w, h); | |
203 } | |
204 | |
205 static gboolean | |
206 pidgin_tooltip_timeout(gpointer data) | |
207 { | |
208 pidgin_tooltip.timeout = 0; | |
209 pidgin_tooltip_draw(data); | |
210 return FALSE; | |
211 } | |
212 | |
213 static gboolean | |
214 row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer userdata) | |
215 { | |
216 GtkTreePath *path; | |
217 int delay; | |
218 | |
219 /* XXX: probably use something more generic? */ | |
220 delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); | |
221 if (delay == 0) | |
222 return FALSE; | |
223 | |
224 if (pidgin_tooltip.timeout) { | |
225 if ((event->y >= pidgin_tooltip.tip_rect.y) && ((event->y - pidgin_tooltip.tip_rect.height) <= pidgin_tooltip.tip_rect.y)) | |
226 return FALSE; | |
227 /* We've left the cell. Remove the timeout and create a new one below */ | |
228 pidgin_tooltip_destroy(); | |
229 } | |
230 | |
231 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); | |
232 | |
233 if (path == NULL) { | |
234 pidgin_tooltip_destroy(); | |
235 return FALSE; | |
236 } | |
237 | |
238 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &pidgin_tooltip.tip_rect); | |
239 | |
240 if (path) | |
241 gtk_tree_path_free(path); | |
242 pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, userdata); | |
243 | |
244 return FALSE; | |
245 } | |
246 | |
247 static gboolean | |
248 row_leave_cb(GtkWidget *tv, GdkEvent *event, gpointer userdata) | |
249 { | |
250 pidgin_tooltip_destroy(); | |
251 return FALSE; | |
252 } | |
253 | |
254 gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata, | |
255 PidginTooltipCreateForTree create_tooltip, PidginTooltipPaint paint_tooltip) | |
256 { | |
257 PidginTooltipData *tdata = g_new0(PidginTooltipData, 1); | |
258 tdata->widget = tree; | |
259 tdata->userdata = userdata; | |
260 tdata->create_tooltip = create_tooltip; | |
261 tdata->paint_tooltip = paint_tooltip; | |
262 | |
263 g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata); | |
264 g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), NULL); | |
265 g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata); | |
266 return TRUE; | |
267 } | |
268 |