Mercurial > pidgin.yaz
comparison plugins/docklet/docklet.c @ 6077:b2c8e08508af
[gaim-migrate @ 6536]
Integrated Win Gaim systray to the docklet plugin
committer: Tailor Script <tailor@pidgin.im>
author | Herman Bloggs <hermanator12002@yahoo.com> |
---|---|
date | Thu, 10 Jul 2003 23:13:07 +0000 |
parents | 5239a3b4ab33 |
children | 1894de5f7fbd |
comparison
equal
deleted
inserted
replaced
6076:8d6aa792e0f6 | 6077:b2c8e08508af |
---|---|
1 /* System tray icon (aka docklet) plugin for Gaim | 1 /* System tray icon (aka docklet) plugin for Gaim |
2 * Copyright (C) 2002 Robert McQueen <robot101@debian.org> | 2 * Copyright (C) 2002 Robert McQueen <robot101@debian.org> |
3 * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com> | |
3 * Inspired by a similar plugin by: | 4 * Inspired by a similar plugin by: |
4 * John (J5) Palmieri <johnp@martianrock.com> | 5 * John (J5) Palmieri <johnp@martianrock.com> |
5 * | 6 * |
6 * This program is free software; you can redistribute it and/or | 7 * This program is free software; you can redistribute it and/or |
7 * modify it under the terms of the GNU General Public License as | 8 * modify it under the terms of the GNU General Public License as |
39 #include "gtkplugin.h" | 40 #include "gtkplugin.h" |
40 #include "gtkprefs.h" | 41 #include "gtkprefs.h" |
41 #include "gtksound.h" | 42 #include "gtksound.h" |
42 #include "gtkutils.h" | 43 #include "gtkutils.h" |
43 #include "stock.h" | 44 #include "stock.h" |
44 | 45 #include "docklet.h" |
45 #include "eggtrayicon.h" | |
46 | 46 |
47 #include "gaim.h" | 47 #include "gaim.h" |
48 #include "ui.h" | 48 #include "ui.h" |
49 | 49 |
50 #define DOCKLET_PLUGIN_ID "gtk-docklet" | 50 #define DOCKLET_PLUGIN_ID "gtk-docklet" |
51 | 51 |
52 /* types */ | 52 /* globals */ |
53 enum docklet_status { | 53 static struct gaim_tray_ops *tray_ops = NULL; |
54 offline, | 54 static enum docklet_status status=offline; |
55 offline_connecting, | 55 static enum docklet_status icon=offline; |
56 online, | 56 #ifdef _WIN32 |
57 online_connecting, | 57 __declspec(dllimport) GSList *unread_message_queue; |
58 online_pending, | 58 __declspec(dllimport) GSList *away_messages; |
59 away, | 59 __declspec(dllimport) struct away_message *awaymessage; |
60 away_pending | 60 __declspec(dllimport) GSList *message_queue; |
61 }; | 61 #endif |
62 | 62 |
63 /* functions */ | 63 /* functions */ |
64 static gboolean docklet_create(); | 64 extern void trayicon_init(); |
65 static gboolean docklet_update_status(); | 65 static gboolean docklet_update_status(); |
66 static gboolean plugin_unload(GaimPlugin *plugin); | 66 static gboolean plugin_unload(GaimPlugin *plugin); |
67 | 67 |
68 /* globals */ | |
69 static EggTrayIcon *docklet = NULL; | |
70 static GtkWidget *image = NULL; | |
71 static enum docklet_status status; | |
72 static enum docklet_status icon; | |
73 | |
74 static void docklet_toggle_mute(GtkWidget *toggle, void *data) { | 68 static void docklet_toggle_mute(GtkWidget *toggle, void *data) { |
75 gaim_gtk_sound_set_mute(GTK_CHECK_MENU_ITEM(toggle)->active); | 69 gaim_gtk_sound_set_mute(GTK_CHECK_MENU_ITEM(toggle)->active); |
76 } | 70 } |
77 | 71 |
78 static void docklet_set_bool(GtkWidget *widget, const char *key) { | 72 static void docklet_set_bool(GtkWidget *widget, const char *key) { |
81 | 75 |
82 static void docklet_auto_login() { | 76 static void docklet_auto_login() { |
83 gaim_accounts_auto_login(GAIM_GTK_UI); | 77 gaim_accounts_auto_login(GAIM_GTK_UI); |
84 } | 78 } |
85 | 79 |
86 static void docklet_flush_queue() { | 80 #ifdef _WIN32 |
87 if (unread_message_queue) { | 81 /* This is a workaround for a bug in windows GTK+.. Clicking outside of the |
88 purge_away_queue(&unread_message_queue); | 82 menu does not get rid of it, so instead we get rid of it as soon as the |
89 } | 83 pointer leaves the menu. */ |
90 } | 84 static gboolean menu_leave(GtkWidget *menu, |
91 | 85 GdkEventCrossing *event, |
92 static void docklet_menu(GdkEventButton *event) { | 86 gpointer user_data) { |
87 if(event->detail == GDK_NOTIFY_ANCESTOR) { | |
88 gaim_debug(GAIM_DEBUG_INFO, "docklet", "leave-notify-event\n"); | |
89 gtk_menu_popdown(GTK_MENU(menu)); | |
90 } | |
91 return FALSE; | |
92 } | |
93 #endif | |
94 | |
95 static void docklet_menu() { | |
93 static GtkWidget *menu = NULL; | 96 static GtkWidget *menu = NULL; |
94 GtkWidget *entry; | 97 GtkWidget *entry; |
95 | 98 |
96 if (menu) { | 99 if (menu) { |
97 gtk_widget_destroy(menu); | 100 gtk_widget_destroy(menu); |
98 } | 101 } |
99 | 102 |
100 menu = gtk_menu_new(); | 103 menu = gtk_menu_new(); |
101 | 104 #ifdef _WIN32 |
105 g_signal_connect(menu, "leave-notify-event", G_CALLBACK(menu_leave), NULL); | |
106 #endif | |
102 switch (status) { | 107 switch (status) { |
103 case offline: | 108 case offline: |
104 case offline_connecting: | 109 case offline_connecting: |
105 gaim_new_item_from_stock(menu, _("Auto-login"), GAIM_STOCK_SIGN_ON, G_CALLBACK(docklet_auto_login), NULL, 0, 0, NULL); | 110 gaim_new_item_from_stock(menu, _("Auto-login"), GAIM_STOCK_SIGN_ON, G_CALLBACK(docklet_auto_login), NULL, 0, 0, NULL); |
106 break; | 111 break; |
176 } | 181 } |
177 | 182 |
178 gaim_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(do_quit), NULL, 0, 0, 0); | 183 gaim_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(do_quit), NULL, 0, 0, 0); |
179 | 184 |
180 gtk_widget_show_all(menu); | 185 gtk_widget_show_all(menu); |
181 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time); | 186 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); |
182 } | |
183 | |
184 static void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data) { | |
185 if (event->type != GDK_BUTTON_PRESS) | |
186 return; | |
187 | |
188 switch (event->button) { | |
189 case 1: | |
190 if (unread_message_queue) { | |
191 docklet_flush_queue(); | |
192 docklet_update_status(); | |
193 } else { | |
194 gaim_gtk_blist_docklet_toggle(); | |
195 } | |
196 break; | |
197 case 2: | |
198 switch (status) { | |
199 case offline: | |
200 case offline_connecting: | |
201 docklet_auto_login(); | |
202 break; | |
203 default: | |
204 break; | |
205 } | |
206 break; | |
207 case 3: | |
208 docklet_menu(event); | |
209 break; | |
210 } | |
211 } | |
212 | |
213 static void docklet_update_icon() { | |
214 const gchar *icon_name = NULL; | |
215 | |
216 switch (icon) { | |
217 case offline: | |
218 icon_name = GAIM_STOCK_ICON_OFFLINE; | |
219 break; | |
220 case offline_connecting: | |
221 case online_connecting: | |
222 icon_name = GAIM_STOCK_ICON_CONNECT; | |
223 break; | |
224 case online: | |
225 icon_name = GAIM_STOCK_ICON_ONLINE; | |
226 break; | |
227 case online_pending: | |
228 icon_name = GAIM_STOCK_ICON_ONLINE_MSG; | |
229 break; | |
230 case away: | |
231 icon_name = GAIM_STOCK_ICON_AWAY; | |
232 break; | |
233 case away_pending: | |
234 icon_name = GAIM_STOCK_ICON_AWAY_MSG; | |
235 break; | |
236 } | |
237 | |
238 gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); | |
239 } | 187 } |
240 | 188 |
241 static gboolean docklet_blink_icon() { | 189 static gboolean docklet_blink_icon() { |
242 if (status == online_pending) { | 190 if (status == online_pending) { |
243 if (status == icon) { | 191 if (status == icon) { |
258 } else { | 206 } else { |
259 /* no messages, stop blinking */ | 207 /* no messages, stop blinking */ |
260 return FALSE; | 208 return FALSE; |
261 } | 209 } |
262 | 210 |
263 docklet_update_icon(); | 211 if(tray_ops->update_icon) |
212 tray_ops->update_icon(icon); | |
264 | 213 |
265 return TRUE; /* keep blinking */ | 214 return TRUE; /* keep blinking */ |
266 } | 215 } |
267 | 216 |
268 static gboolean docklet_update_status() { | 217 static gboolean docklet_update_status() { |
293 } | 242 } |
294 | 243 |
295 /* update the icon if we changed status */ | 244 /* update the icon if we changed status */ |
296 if (status != oldstatus) { | 245 if (status != oldstatus) { |
297 icon = status; | 246 icon = status; |
298 docklet_update_icon(); | 247 if(tray_ops->update_icon) |
248 tray_ops->update_icon(icon); | |
299 | 249 |
300 /* and schedule the blinker function if messages are pending */ | 250 /* and schedule the blinker function if messages are pending */ |
301 if (status == online_pending || status == away_pending) { | 251 if (status == online_pending || status == away_pending) { |
302 g_timeout_add(500, docklet_blink_icon, &docklet); | 252 g_timeout_add(500, docklet_blink_icon, NULL); |
303 } | 253 } |
304 } | 254 } |
305 | |
306 return FALSE; /* for when we're called by the glib idle handler */ | |
307 } | |
308 | |
309 static void docklet_embedded(GtkWidget *widget, void *data) { | |
310 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: embedded\n"); | |
311 gaim_gtk_blist_docklet_add(); | |
312 } | |
313 | |
314 static void docklet_remove_callbacks() { | |
315 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: removing callbacks"); | |
316 | |
317 while (g_source_remove_by_user_data(&docklet)) { | |
318 gaim_debug(GAIM_DEBUG_INFO, NULL, "."); | |
319 } | |
320 | |
321 gaim_debug(GAIM_DEBUG_INFO, NULL, "\n"); | |
322 } | |
323 | |
324 static void docklet_destroyed(GtkWidget *widget, void *data) { | |
325 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: destroyed\n"); | |
326 | |
327 gaim_gtk_blist_docklet_remove(); | |
328 | |
329 docklet_flush_queue(); | |
330 | |
331 docklet_remove_callbacks(); | |
332 | |
333 g_object_unref(G_OBJECT(docklet)); | |
334 docklet = NULL; | |
335 | |
336 g_idle_add(docklet_create, &docklet); | |
337 } | |
338 | |
339 static gboolean docklet_create() { | |
340 GtkWidget *box; | |
341 | |
342 if (docklet) { | |
343 /* if this is being called when a tray icon exists, it's because | |
344 something messed up. try destroying it before we proceed, | |
345 although docklet_refcount may be all hosed. hopefully won't happen. */ | |
346 gaim_debug(GAIM_DEBUG_WARNING, "docklet", | |
347 "Tray Icon: trying to create icon but it already exists?\n"); | |
348 plugin_unload(NULL); | |
349 } | |
350 | |
351 docklet = egg_tray_icon_new("Gaim"); | |
352 box = gtk_event_box_new(); | |
353 image = gtk_image_new(); | |
354 | |
355 g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL); | |
356 g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL); | |
357 g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL); | |
358 | |
359 gtk_container_add(GTK_CONTAINER(box), image); | |
360 gtk_container_add(GTK_CONTAINER(docklet), box); | |
361 gtk_widget_show_all(GTK_WIDGET(docklet)); | |
362 | |
363 /* ref the docklet before we bandy it about the place */ | |
364 g_object_ref(G_OBJECT(docklet)); | |
365 docklet_update_status(); | |
366 docklet_update_icon(); | |
367 | |
368 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: created\n"); | |
369 | |
370 return FALSE; /* for when we're called by the glib idle handler */ | 255 return FALSE; /* for when we're called by the glib idle handler */ |
371 } | 256 } |
372 | 257 |
373 static void gaim_signon(GaimConnection *gc, void *data) { | 258 static void gaim_signon(GaimConnection *gc, void *data) { |
374 docklet_update_status(); | 259 docklet_update_status(); |
396 static void gaim_im_recv(GaimConnection *gc, char **who, char **what, void *data) { | 281 static void gaim_im_recv(GaimConnection *gc, char **who, char **what, void *data) { |
397 /* if message queuing while away is enabled, this event could be the first | 282 /* if message queuing while away is enabled, this event could be the first |
398 message so we need to see if the status (and hence icon) needs changing. | 283 message so we need to see if the status (and hence icon) needs changing. |
399 do this when idle so that all message processing is completed, queuing | 284 do this when idle so that all message processing is completed, queuing |
400 etc, before we run. */ | 285 etc, before we run. */ |
401 g_idle_add(docklet_update_status, &docklet); | 286 g_idle_add(docklet_update_status, NULL); |
402 } | 287 } |
403 | 288 |
404 /* static void gaim_buddy_signon(GaimConnection *gc, char *who, void *data) { | 289 /* static void gaim_buddy_signon(GaimConnection *gc, char *who, void *data) { |
405 } | 290 } |
406 | 291 |
414 } | 299 } |
415 | 300 |
416 static void gaim_new_conversation(char *who, void *data) { | 301 static void gaim_new_conversation(char *who, void *data) { |
417 } */ | 302 } */ |
418 | 303 |
304 /* | |
305 * Public Code | |
306 */ | |
307 void docklet_clicked(int button_type) { | |
308 switch (button_type) { | |
309 case 1: | |
310 if (unread_message_queue) { | |
311 docklet_flush_queue(); | |
312 docklet_update_status(); | |
313 } else { | |
314 gaim_gtk_blist_docklet_toggle(); | |
315 } | |
316 break; | |
317 case 2: | |
318 switch (status) { | |
319 case offline: | |
320 case offline_connecting: | |
321 docklet_auto_login(); | |
322 break; | |
323 default: | |
324 break; | |
325 } | |
326 break; | |
327 case 3: | |
328 docklet_menu(); | |
329 break; | |
330 } | |
331 } | |
332 | |
333 void docklet_embedded() { | |
334 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: embedded\n"); | |
335 gaim_gtk_blist_docklet_add(); | |
336 } | |
337 | |
338 void docklet_flush_queue() { | |
339 if (unread_message_queue) { | |
340 purge_away_queue(&unread_message_queue); | |
341 } | |
342 } | |
343 | |
344 | |
345 /* Set Platform Dependent Code */ | |
346 void docklet_set_tray_ops(struct gaim_tray_ops *ops) { | |
347 tray_ops = ops; | |
348 } | |
349 | |
350 | |
351 /* | |
352 * PLUGIN CODE | |
353 */ | |
354 | |
419 static gboolean | 355 static gboolean |
420 plugin_load(GaimPlugin *plugin) | 356 plugin_load(GaimPlugin *plugin) |
421 { | 357 { |
358 trayicon_init(); | |
359 if(tray_ops->create) | |
360 tray_ops->create(); | |
361 | |
422 gaim_prefs_add_none("/plugins/gtk/docklet"); | 362 gaim_prefs_add_none("/plugins/gtk/docklet"); |
423 gaim_prefs_add_bool("/plugins/gtk/docklet/queue_messages", FALSE); | 363 gaim_prefs_add_bool("/plugins/gtk/docklet/queue_messages", FALSE); |
424 | 364 |
425 docklet_create(NULL); | 365 docklet_update_status(); |
366 if(tray_ops->update_icon) | |
367 tray_ops->update_icon(icon); | |
426 | 368 |
427 gaim_signal_connect(plugin, event_signon, gaim_signon, NULL); | 369 gaim_signal_connect(plugin, event_signon, gaim_signon, NULL); |
428 gaim_signal_connect(plugin, event_signoff, gaim_signoff, NULL); | 370 gaim_signal_connect(plugin, event_signoff, gaim_signoff, NULL); |
429 gaim_signal_connect(plugin, event_connecting, gaim_connecting, NULL); | 371 gaim_signal_connect(plugin, event_connecting, gaim_connecting, NULL); |
430 gaim_signal_connect(plugin, event_away, gaim_away, NULL); | 372 gaim_signal_connect(plugin, event_away, gaim_away, NULL); |
433 gaim_signal_connect(plugin, event_buddy_signoff, gaim_buddy_signoff, NULL); | 375 gaim_signal_connect(plugin, event_buddy_signoff, gaim_buddy_signoff, NULL); |
434 gaim_signal_connect(plugin, event_buddy_away, gaim_buddy_away, NULL); | 376 gaim_signal_connect(plugin, event_buddy_away, gaim_buddy_away, NULL); |
435 gaim_signal_connect(plugin, event_buddy_back, gaim_buddy_back, NULL); | 377 gaim_signal_connect(plugin, event_buddy_back, gaim_buddy_back, NULL); |
436 gaim_signal_connect(plugin, event_new_conversation, gaim_new_conversation, NULL); */ | 378 gaim_signal_connect(plugin, event_new_conversation, gaim_new_conversation, NULL); */ |
437 | 379 |
380 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Plugin loaded\n"); | |
381 | |
438 return TRUE; | 382 return TRUE; |
439 } | 383 } |
440 | 384 |
441 static gboolean | 385 static gboolean |
442 plugin_unload(GaimPlugin *plugin) | 386 plugin_unload(GaimPlugin *plugin) |
443 { | 387 { |
444 if (GTK_WIDGET_VISIBLE(docklet)) { | 388 gaim_gtk_blist_docklet_remove(); |
445 gaim_gtk_blist_docklet_remove(); | |
446 } | |
447 | |
448 docklet_flush_queue(); | 389 docklet_flush_queue(); |
449 | |
450 docklet_remove_callbacks(); | |
451 | |
452 g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_destroyed), NULL); | |
453 gtk_widget_destroy(GTK_WIDGET(docklet)); | |
454 | |
455 g_object_unref(G_OBJECT(docklet)); | |
456 docklet = NULL; | |
457 | |
458 /* XXX: do this while gaim has no other way to toggle the global mute */ | 390 /* XXX: do this while gaim has no other way to toggle the global mute */ |
459 gaim_gtk_sound_set_mute(FALSE); | 391 gaim_gtk_sound_set_mute(FALSE); |
460 | 392 |
461 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Tray Icon: removed\n"); | 393 if(tray_ops->destroy) |
394 tray_ops->destroy(); | |
395 | |
396 gaim_debug(GAIM_DEBUG_INFO, "docklet", "Plugin unloaded\n"); | |
462 | 397 |
463 return TRUE; | 398 return TRUE; |
464 } | 399 } |
465 | 400 |
466 static GtkWidget * | 401 static GtkWidget * |
505 N_("System Tray Icon"), /**< name */ | 440 N_("System Tray Icon"), /**< name */ |
506 VERSION, /**< version */ | 441 VERSION, /**< version */ |
507 /** summary */ | 442 /** summary */ |
508 N_("Displays an icon for Gaim in the system tray."), | 443 N_("Displays an icon for Gaim in the system tray."), |
509 /** description */ | 444 /** description */ |
510 N_("Interacts with a Notification Area applet (in GNOME or KDE, " | 445 N_("Interacts with a Notification Area applet (in GNOME, KDE or " |
511 "for example) to display the current status of Gaim, allow fast " | 446 "Windows for example) to display the current status of Gaim, allow " |
512 "access to commonly used functions, and to toggle display of the " | 447 "fast access to commonly used functions, and to toggle display of " |
513 "buddy list or login window. Also allows messages to be queued " | 448 "the buddy list or login window. Also allows messages to be queued " |
514 "until the icon is clicked, similar to ICQ."), | 449 "until the icon is clicked, similar to ICQ."), |
515 "Robert McQueen <robot101@debian.org>", /**< author */ | 450 "Robert McQueen <robot101@debian.org>", /**< author */ |
516 WEBSITE, /**< homepage */ | 451 WEBSITE, /**< homepage */ |
517 | 452 |
518 plugin_load, /**< load */ | 453 plugin_load, /**< load */ |