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 */