pidgin/gtkdocklet-x11.c

branch
cpw.qulogic.msnp16
changeset 30905
f9b17246cead
parent 30904
fb83e7d177c9
parent 30902
a1a3d626b7d4
child 30906
957af130d083
equal deleted inserted replaced
30904:fb83e7d177c9 30905:f9b17246cead
1 /*
2 * System tray icon (aka docklet) plugin for Purple
3 *
4 * Copyright (C) 2002-3 Robert McQueen <robot101@debian.org>
5 * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com>
6 * Inspired by a similar plugin by:
7 * John (J5) Palmieri <johnp@martianrock.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02111-1301, USA.
23 */
24
25 #include "internal.h"
26 #include "pidgin.h"
27 #include "debug.h"
28 #include "prefs.h"
29 #include "pidginstock.h"
30
31 #include "gtkdialogs.h"
32
33 #include "eggtrayicon.h"
34 #include "gtkdocklet.h"
35 #include <gdk/gdkkeysyms.h>
36
37 #if !GTK_CHECK_VERSION(2,10,0)
38
39 #define SHORT_EMBED_TIMEOUT 5000
40 #define LONG_EMBED_TIMEOUT 15000
41
42 /* globals */
43 static EggTrayIcon *docklet = NULL;
44 static GtkWidget *image = NULL;
45 static GtkTooltips *tooltips = NULL;
46 static GdkPixbuf *blank_icon = NULL;
47 static int embed_timeout = 0;
48 static int docklet_height = 0;
49
50 /* protos */
51 static void docklet_x11_create(gboolean);
52
53 static gboolean
54 docklet_x11_recreate_cb(gpointer data)
55 {
56 docklet_x11_create(TRUE);
57
58 return FALSE; /* for when we're called by the glib idle handler */
59 }
60
61 static void
62 docklet_x11_embedded_cb(GtkWidget *widget, void *data)
63 {
64 purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 embedded\n");
65
66 g_source_remove(embed_timeout);
67 embed_timeout = 0;
68 pidgin_docklet_embedded();
69 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE);
70 }
71
72 static void
73 docklet_x11_destroyed_cb(GtkWidget *widget, void *data)
74 {
75 purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 destroyed\n");
76
77 pidgin_docklet_remove();
78
79 g_object_unref(G_OBJECT(docklet));
80 docklet = NULL;
81
82 g_idle_add(docklet_x11_recreate_cb, NULL);
83 }
84
85 static gboolean
86 docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data)
87 {
88 if (event->type != GDK_BUTTON_PRESS)
89 return FALSE;
90
91 pidgin_docklet_clicked(event->button);
92 return TRUE;
93 }
94
95 static gboolean
96 docklet_x11_pressed_cb(GtkWidget *button, GdkEventKey *event)
97 {
98 guint state, keyval;
99
100 state = event->state & gtk_accelerator_get_default_mod_mask();
101 keyval = event->keyval;
102 if (state == 0 &&
103 (keyval == GDK_Return ||
104 keyval == GDK_KP_Enter ||
105 keyval == GDK_ISO_Enter ||
106 keyval == GDK_space ||
107 keyval == GDK_KP_Space))
108 {
109 pidgin_docklet_clicked(1);
110 return TRUE;
111 }
112
113 return FALSE;
114 }
115
116 static void
117 docklet_x11_popup_cb(GtkWidget *button)
118 {
119 pidgin_docklet_clicked(3);
120 }
121
122 static void
123 docklet_x11_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
124 {
125 const gchar *icon_name = NULL;
126
127 g_return_if_fail(image != NULL);
128
129 switch (status) {
130 case PURPLE_STATUS_OFFLINE:
131 icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
132 break;
133 case PURPLE_STATUS_AWAY:
134 icon_name = PIDGIN_STOCK_TRAY_AWAY;
135 break;
136 case PURPLE_STATUS_UNAVAILABLE:
137 icon_name = PIDGIN_STOCK_TRAY_BUSY;
138 break;
139 case PURPLE_STATUS_EXTENDED_AWAY:
140 icon_name = PIDGIN_STOCK_TRAY_XA;
141 break;
142 case PURPLE_STATUS_INVISIBLE:
143 icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
144 break;
145 default:
146 icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
147 break;
148 }
149
150 if (pending)
151 icon_name = PIDGIN_STOCK_TRAY_PENDING;
152 if (connecting)
153 icon_name = PIDGIN_STOCK_TRAY_CONNECT;
154
155 if(icon_name) {
156 int icon_size;
157 if (docklet_height < 22)
158 icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
159 else if (docklet_height < 32)
160 icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
161 else if (docklet_height < 48)
162 icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM);
163 else
164 icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE);
165
166 gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, icon_size);
167 }
168 }
169
170 static void
171 docklet_x11_resize_icon(GtkWidget *widget)
172 {
173 if (docklet_height == MIN(widget->allocation.height, widget->allocation.width))
174 return;
175 docklet_height = MIN(widget->allocation.height, widget->allocation.width);
176 pidgin_docklet_update_icon();
177 }
178
179 static void
180 docklet_x11_blank_icon(void)
181 {
182 if (!blank_icon) {
183 GtkIconSize size = GTK_ICON_SIZE_LARGE_TOOLBAR;
184 gint width, height;
185 g_object_get(G_OBJECT(image), "icon-size", &size, NULL);
186 gtk_icon_size_lookup(size, &width, &height);
187 blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
188 gdk_pixbuf_fill(blank_icon, 0);
189 }
190
191 gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon);
192 }
193
194 static void
195 docklet_x11_set_tooltip(gchar *tooltip)
196 {
197 if (!tooltips)
198 tooltips = gtk_tooltips_new();
199
200 /* image->parent is a GtkEventBox */
201 if (tooltip) {
202 gtk_tooltips_enable(tooltips);
203 gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL);
204 } else {
205 gtk_tooltips_set_tip(tooltips, image->parent, "", NULL);
206 gtk_tooltips_disable(tooltips);
207 }
208 }
209
210 static void
211 docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in,
212 gpointer user_data)
213 {
214 GtkWidget *widget = GTK_WIDGET(docklet);
215 GtkRequisition req;
216 gint menu_xpos, menu_ypos;
217
218 gtk_widget_size_request(GTK_WIDGET(menu), &req);
219 gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos);
220
221 menu_xpos += widget->allocation.x;
222 menu_ypos += widget->allocation.y;
223
224 if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2)
225 menu_ypos -= req.height;
226 else
227 menu_ypos += widget->allocation.height;
228
229 *x = menu_xpos;
230 *y = menu_ypos;
231
232 *push_in = TRUE;
233 }
234
235 static void
236 docklet_x11_destroy(void)
237 {
238 g_return_if_fail(docklet != NULL);
239
240 if (embed_timeout)
241 g_source_remove(embed_timeout);
242
243 pidgin_docklet_remove();
244
245 g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL);
246 gtk_widget_destroy(GTK_WIDGET(docklet));
247
248 g_object_unref(G_OBJECT(docklet));
249 docklet = NULL;
250
251 if (blank_icon)
252 g_object_unref(G_OBJECT(blank_icon));
253 blank_icon = NULL;
254
255 image = NULL;
256
257 purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 destroyed\n");
258 }
259
260 static gboolean
261 docklet_x11_embed_timeout_cb(gpointer data)
262 {
263 /* The docklet was not embedded within the timeout.
264 * Remove it as a visibility manager, but leave the plugin
265 * loaded so that it can embed automatically if/when a notification
266 * area becomes available.
267 */
268 purple_debug_info("docklet", "X11 failed to embed within timeout\n");
269 pidgin_docklet_remove();
270
271 return FALSE;
272 }
273
274 static void
275 docklet_x11_create(gboolean recreate)
276 {
277 GtkWidget *box;
278
279 if (docklet) {
280 /* if this is being called when a tray icon exists, it's because
281 something messed up. try destroying it before we proceed,
282 although docklet_refcount may be all hosed. hopefully won't happen. */
283 purple_debug(PURPLE_DEBUG_WARNING, "docklet", "trying to create icon but it already exists?\n");
284 docklet_x11_destroy();
285 }
286
287 docklet = egg_tray_icon_new(PIDGIN_NAME);
288 box = gtk_event_box_new();
289 image = gtk_image_new();
290 GTK_WIDGET_SET_FLAGS (image, GTK_CAN_FOCUS);
291
292 g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL);
293 g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL);
294 g_signal_connect(G_OBJECT(docklet), "size-allocate", G_CALLBACK(docklet_x11_resize_icon), NULL);
295 g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL);
296 g_signal_connect(G_OBJECT(box), "key-press-event", G_CALLBACK(docklet_x11_pressed_cb), NULL);
297 g_signal_connect(G_OBJECT(box), "popup-menu", G_CALLBACK(docklet_x11_popup_cb), NULL);
298 gtk_container_add(GTK_CONTAINER(box), image);
299 gtk_container_add(GTK_CONTAINER(docklet), box);
300
301 if (!gtk_check_version(2,4,0))
302 g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
303
304 gtk_widget_show_all(GTK_WIDGET(docklet));
305
306 /* ref the docklet before we bandy it about the place */
307 g_object_ref(G_OBJECT(docklet));
308
309 /* This is a hack to avoid a race condition between the docklet getting
310 * embedded in the notification area and the gtkblist restoring its
311 * previous visibility state. If the docklet does not get embedded within
312 * the timeout, it will be removed as a visibility manager until it does
313 * get embedded. Ideally, we would only call docklet_embedded() when the
314 * icon was actually embedded. This only happens when the docklet is first
315 * created, not when being recreated.
316 *
317 * The x11 docklet tracks whether it successfully embedded in a pref and
318 * allows for a longer timeout period if it successfully embedded the last
319 * time it was run. This should hopefully solve problems with the buddy
320 * list not properly starting hidden when Pidgin is started on login.
321 */
322 if(!recreate) {
323 pidgin_docklet_embedded();
324 if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
325 embed_timeout = g_timeout_add(LONG_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL);
326 } else {
327 embed_timeout = g_timeout_add(SHORT_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL);
328 }
329 }
330
331 purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 created\n");
332 }
333
334 static void
335 docklet_x11_create_ui_op(void)
336 {
337 docklet_x11_create(FALSE);
338 }
339
340 static struct docklet_ui_ops ui_ops =
341 {
342 docklet_x11_create_ui_op,
343 docklet_x11_destroy,
344 docklet_x11_update_icon,
345 docklet_x11_blank_icon,
346 docklet_x11_set_tooltip,
347 docklet_x11_position_menu
348 };
349
350 void
351 docklet_ui_init()
352 {
353 pidgin_docklet_set_ui_ops(&ui_ops);
354 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/x11");
355 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE);
356 }
357
358 #endif /* !GTK_CHECK_VERSION(2,10,0) */
359

mercurial