comparison pidgin/gtkdocklet.c @ 32672:3828a61c44da

A boring and large patch so I can merge heads.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Fri, 23 Dec 2011 08:21:58 +0000
parents ab33826dc179
children
comparison
equal deleted inserted replaced
32671:0e69949b3e61 32672:3828a61c44da
28 #include "conversation.h" 28 #include "conversation.h"
29 #include "debug.h" 29 #include "debug.h"
30 #include "prefs.h" 30 #include "prefs.h"
31 #include "signals.h" 31 #include "signals.h"
32 #include "sound.h" 32 #include "sound.h"
33 #include "status.h"
33 34
34 #include "gtkaccount.h" 35 #include "gtkaccount.h"
35 #include "gtkblist.h" 36 #include "gtkblist.h"
36 #include "gtkconv.h" 37 #include "gtkconv.h"
37 #include "gtkplugin.h" 38 #include "gtkplugin.h"
46 47
47 #ifndef DOCKLET_TOOLTIP_LINE_LIMIT 48 #ifndef DOCKLET_TOOLTIP_LINE_LIMIT
48 #define DOCKLET_TOOLTIP_LINE_LIMIT 5 49 #define DOCKLET_TOOLTIP_LINE_LIMIT 5
49 #endif 50 #endif
50 51
52 #define SHORT_EMBED_TIMEOUT 5
53 #define LONG_EMBED_TIMEOUT 15
54
51 /* globals */ 55 /* globals */
52 static struct docklet_ui_ops *ui_ops = NULL; 56 static GtkStatusIcon *docklet = NULL;
57 static guint embed_timeout = 0;
53 static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE; 58 static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE;
54 static gboolean pending = FALSE; 59 static gboolean pending = FALSE;
55 static gboolean connecting = FALSE; 60 static gboolean connecting = FALSE;
56 static gboolean enable_join_chat = FALSE; 61 static gboolean enable_join_chat = FALSE;
57 static guint docklet_blinking_timer = 0; 62 static guint docklet_blinking_timer = 0;
58 static gboolean visible = FALSE; 63 static gboolean visible = FALSE;
59 static gboolean visibility_manager = FALSE; 64 static gboolean visibility_manager = FALSE;
60 65
66 /* protos */
67 static void docklet_gtk_status_create(gboolean);
68 static void docklet_gtk_status_destroy(void);
69
61 /************************************************************************** 70 /**************************************************************************
62 * docklet status and utility functions 71 * docklet status and utility functions
63 **************************************************************************/ 72 **************************************************************************/
73 static void
74 docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
75 {
76 const gchar *icon_name = NULL;
77
78 switch (status) {
79 case PURPLE_STATUS_OFFLINE:
80 icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
81 break;
82 case PURPLE_STATUS_AWAY:
83 icon_name = PIDGIN_STOCK_TRAY_AWAY;
84 break;
85 case PURPLE_STATUS_UNAVAILABLE:
86 icon_name = PIDGIN_STOCK_TRAY_BUSY;
87 break;
88 case PURPLE_STATUS_EXTENDED_AWAY:
89 icon_name = PIDGIN_STOCK_TRAY_XA;
90 break;
91 case PURPLE_STATUS_INVISIBLE:
92 icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
93 break;
94 default:
95 icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
96 break;
97 }
98
99 if (pending)
100 icon_name = PIDGIN_STOCK_TRAY_PENDING;
101 if (connecting)
102 icon_name = PIDGIN_STOCK_TRAY_CONNECT;
103
104 if (icon_name) {
105 gtk_status_icon_set_from_icon_name(docklet, icon_name);
106 }
107
108 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
109 gtk_status_icon_set_blinking(docklet, (pending && !connecting));
110 } else if (gtk_status_icon_get_blinking(docklet)) {
111 gtk_status_icon_set_blinking(docklet, FALSE);
112 }
113 }
114
64 static gboolean 115 static gboolean
65 docklet_blink_icon(gpointer data) 116 docklet_blink_icon(gpointer data)
66 { 117 {
67 static gboolean blinked = FALSE; 118 static gboolean blinked = FALSE;
68 gboolean ret = FALSE; /* by default, don't keep blinking */ 119 gboolean ret = FALSE; /* by default, don't keep blinking */
69 120
70 blinked = !blinked; 121 blinked = !blinked;
71 122
72 if(pending && !connecting) { 123 if(pending && !connecting) {
73 if (blinked) { 124 if (!blinked) {
74 if (ui_ops && ui_ops->blank_icon) 125 docklet_gtk_status_update_icon(status, connecting, pending);
75 ui_ops->blank_icon();
76 } else {
77 pidgin_docklet_update_icon();
78 } 126 }
79 ret = TRUE; /* keep blinking */ 127 ret = TRUE; /* keep blinking */
80 } else { 128 } else {
81 docklet_blinking_timer = 0; 129 docklet_blinking_timer = 0;
82 blinked = FALSE; 130 blinked = FALSE;
124 172
125 /* determine if any ims have unseen messages */ 173 /* determine if any ims have unseen messages */
126 convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT); 174 convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT);
127 175
128 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) { 176 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
129 if (convs && ui_ops->create && !visible) { 177 if (convs && !visible) {
130 g_list_free(convs); 178 g_list_free(convs);
131 ui_ops->create(); 179 docklet_gtk_status_create(FALSE);
132 return FALSE; 180 return FALSE;
133 } else if (!convs && ui_ops->destroy && visible) { 181 } else if (!convs && visible) {
134 ui_ops->destroy(); 182 docklet_gtk_status_destroy();
135 return FALSE; 183 return FALSE;
136 } 184 }
137 } 185 }
138 186
139 if (!visible) { 187 if (!visible) {
140 g_list_free(convs); 188 g_list_free(convs);
141 return FALSE; 189 return FALSE;
142 } 190 }
143 191
144 if (convs != NULL) { 192 if (convs != NULL) {
193 /* set tooltip if messages are pending */
194 GString *tooltip_text = g_string_new("");
145 newpending = TRUE; 195 newpending = TRUE;
146 196
147 /* set tooltip if messages are pending */ 197 for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
148 if (ui_ops->set_tooltip) { 198 PurpleConversation *conv = (PurpleConversation *)l->data;
149 GString *tooltip_text = g_string_new(""); 199 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
150 for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) { 200
151 PurpleConversation *conv = (PurpleConversation *)l->data; 201 if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
152 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); 202 g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
153 203 } else if(gtkconv) {
154 if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) { 204 g_string_append_printf(tooltip_text,
155 g_string_append(tooltip_text, _("Right-click for more unread messages...\n")); 205 ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
156 } else if(gtkconv) { 206 gtkconv->unseen_count,
157 g_string_append_printf(tooltip_text, 207 purple_conversation_get_title(conv));
158 ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), 208 } else {
159 gtkconv->unseen_count, 209 g_string_append_printf(tooltip_text,
160 purple_conversation_get_title(conv)); 210 ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
161 } else { 211 GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
162 g_string_append_printf(tooltip_text, 212 GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
163 ngettext("%d unread message from %s\n", "%d unread messages from %s\n", 213 purple_conversation_get_title(conv));
164 GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
165 GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
166 purple_conversation_get_title(conv));
167 }
168 } 214 }
169 215 }
170 /* get rid of the last newline */ 216
171 if (tooltip_text->len > 0) 217 /* get rid of the last newline */
172 tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1); 218 if (tooltip_text->len > 0)
173 219 tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1);
174 ui_ops->set_tooltip(tooltip_text->str); 220
175 221 gtk_status_icon_set_tooltip(docklet, tooltip_text->str);
176 g_string_free(tooltip_text, TRUE); 222
177 } 223 g_string_free(tooltip_text, TRUE);
178
179 g_list_free(convs); 224 g_list_free(convs);
180 225
181 } else if (ui_ops->set_tooltip) { 226 } else {
182 char *tooltip_text = g_strconcat(PIDGIN_NAME, " - ", 227 char *tooltip_text = g_strconcat(PIDGIN_NAME, " - ",
183 purple_savedstatus_get_title(saved_status), NULL); 228 purple_savedstatus_get_title(saved_status), NULL);
184 ui_ops->set_tooltip(tooltip_text); 229 gtk_status_icon_set_tooltip(docklet, tooltip_text);
185 g_free(tooltip_text); 230 g_free(tooltip_text);
186 } 231 }
187 232
188 for(l = purple_accounts_get_all(); l != NULL; l = l->next) { 233 for(l = purple_accounts_get_all(); l != NULL; l = l->next) {
189 234
190 PurpleAccount *account = (PurpleAccount*)l->data; 235 PurpleAccount *account = (PurpleAccount*)l->data;
191 PurpleStatus *account_status;
192 236
193 if (!purple_account_get_enabled(account, PIDGIN_UI)) 237 if (!purple_account_get_enabled(account, PIDGIN_UI))
194 continue; 238 continue;
195 239
196 if (purple_account_is_disconnected(account)) 240 if (purple_account_is_disconnected(account))
197 continue; 241 continue;
198 242
199 account_status = purple_account_get_active_status(account);
200 if (purple_account_is_connecting(account)) 243 if (purple_account_is_connecting(account))
201 newconnecting = TRUE; 244 newconnecting = TRUE;
202 } 245 }
203 246
204 newstatus = purple_savedstatus_get_type(saved_status); 247 newstatus = purple_savedstatus_get_type(saved_status);
207 if (status != newstatus || pending!=newpending || connecting!=newconnecting) { 250 if (status != newstatus || pending!=newpending || connecting!=newconnecting) {
208 status = newstatus; 251 status = newstatus;
209 pending = newpending; 252 pending = newpending;
210 connecting = newconnecting; 253 connecting = newconnecting;
211 254
212 pidgin_docklet_update_icon(); 255 docklet_gtk_status_update_icon(status, connecting, pending);
256
257 /* and schedule the blinker function if messages are pending */
258 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")
259 && pending && !connecting && docklet_blinking_timer == 0) {
260 docklet_blinking_timer = g_timeout_add(500, docklet_blink_icon, NULL);
261 }
213 } 262 }
214 263
215 return FALSE; /* for when we're called by the glib idle handler */ 264 return FALSE; /* for when we're called by the glib idle handler */
216 } 265 }
217 266
221 GList *c = NULL; 270 GList *c = NULL;
222 c = purple_connections_get_all(); 271 c = purple_connections_get_all();
223 272
224 while(c != NULL) { 273 while(c != NULL) {
225 PurpleConnection *gc = c->data; 274 PurpleConnection *gc = c->data;
226 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); 275 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
227 if (prpl_info != NULL && prpl_info->chat_info != NULL) 276 if (prpl_info != NULL && prpl_info->chat_info != NULL)
228 return TRUE; 277 return TRUE;
229 c = c->next; 278 c = c->next;
230 } 279 }
231 280
258 307
259 static void 308 static void
260 docklet_signed_on_cb(PurpleConnection *gc) 309 docklet_signed_on_cb(PurpleConnection *gc)
261 { 310 {
262 if (!enable_join_chat) { 311 if (!enable_join_chat) {
263 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) 312 if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL)
264 enable_join_chat = TRUE; 313 enable_join_chat = TRUE;
265 } 314 }
266 docklet_update_status(); 315 docklet_update_status();
267 } 316 }
268 317
269 static void 318 static void
270 docklet_signed_off_cb(PurpleConnection *gc) 319 docklet_signed_off_cb(PurpleConnection *gc)
271 { 320 {
272 if (enable_join_chat) { 321 if (enable_join_chat) {
273 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) 322 if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info != NULL)
274 enable_join_chat = online_account_supports_chat(); 323 enable_join_chat = online_account_supports_chat();
275 } 324 }
276 docklet_update_status(); 325 docklet_update_status();
277 } 326 }
278 327
280 docklet_show_pref_changed_cb(const char *name, PurplePrefType type, 329 docklet_show_pref_changed_cb(const char *name, PurplePrefType type,
281 gconstpointer value, gpointer data) 330 gconstpointer value, gpointer data)
282 { 331 {
283 const char *val = value; 332 const char *val = value;
284 if (!strcmp(val, "always")) { 333 if (!strcmp(val, "always")) {
285 if (ui_ops->create) { 334 if (!visible)
286 if (!visible) 335 docklet_gtk_status_create(FALSE);
287 ui_ops->create(); 336 else if (!visibility_manager) {
288 else if (!visibility_manager) { 337 pidgin_blist_visibility_manager_add();
289 pidgin_blist_visibility_manager_add(); 338 visibility_manager = TRUE;
290 visibility_manager = TRUE;
291 }
292 } 339 }
293 } else if (!strcmp(val, "never")) { 340 } else if (!strcmp(val, "never")) {
294 if (visible && ui_ops->destroy) 341 if (visible)
295 ui_ops->destroy(); 342 docklet_gtk_status_destroy();
296 } else { 343 } else {
297 if (visibility_manager) { 344 if (visibility_manager) {
298 pidgin_blist_visibility_manager_remove(); 345 pidgin_blist_visibility_manager_remove();
299 visibility_manager = FALSE; 346 visibility_manager = FALSE;
300 } 347 }
307 * docklet pop-up menu 354 * docklet pop-up menu
308 **************************************************************************/ 355 **************************************************************************/
309 static void 356 static void
310 docklet_toggle_mute(GtkWidget *toggle, void *data) 357 docklet_toggle_mute(GtkWidget *toggle, void *data)
311 { 358 {
312 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", 359 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", GTK_CHECK_MENU_ITEM(toggle)->active);
313 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle))); 360 }
361
362 static void
363 docklet_toggle_blink(GtkWidget *toggle, void *data)
364 {
365 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/blink", GTK_CHECK_MENU_ITEM(toggle)->active);
314 } 366 }
315 367
316 static void 368 static void
317 docklet_toggle_blist(GtkWidget *toggle, void *data) 369 docklet_toggle_blist(GtkWidget *toggle, void *data)
318 { 370 {
319 purple_blist_set_visible(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle))); 371 purple_blist_set_visible(GTK_CHECK_MENU_ITEM(toggle)->active);
320 } 372 }
321 373
322 #ifdef _WIN32 374 #ifdef _WIN32
323 /* This is a workaround for a bug in windows GTK+. Clicking outside of the 375 /* This is a workaround for a bug in windows GTK+. Clicking outside of the
324 menu does not get rid of it, so instead we get rid of it as soon as the 376 menu does not get rid of it, so instead we get rid of it as soon as the
580 } 632 }
581 633
582 634
583 635
584 static void 636 static void
585 plugin_act(GtkWidget *widget, PurplePluginAction *pam) 637 plugin_act(GtkObject *obj, PurplePluginAction *pam)
586 { 638 {
587 if (pam && pam->callback) 639 if (pam && pam->callback)
588 pam->callback(pam); 640 pam->callback(pam);
589 } 641 }
590 642
661 static void 713 static void
662 docklet_menu(void) 714 docklet_menu(void)
663 { 715 {
664 static GtkWidget *menu = NULL; 716 static GtkWidget *menu = NULL;
665 GtkWidget *menuitem; 717 GtkWidget *menuitem;
718 GtkMenuPositionFunc pos_func = gtk_status_icon_position_menu;
666 719
667 if (menu) { 720 if (menu) {
668 gtk_widget_destroy(menu); 721 gtk_widget_destroy(menu);
669 } 722 }
670 723
721 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none")) 774 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"))
722 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE); 775 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
723 g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL); 776 g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL);
724 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); 777 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
725 778
779 menuitem = gtk_check_menu_item_new_with_mnemonic(_("_Blink on New Message"));
780 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink"));
781 g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blink), NULL);
782 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
783
726 pidgin_separator(menu); 784 pidgin_separator(menu);
727 785
728 /* add plugin actions */ 786 /* add plugin actions */
729 docklet_plugin_actions(menu); 787 docklet_plugin_actions(menu);
730 788
731 pidgin_new_item_from_stock(menu, _("_Quit"), GTK_STOCK_QUIT, G_CALLBACK(purple_core_quit), NULL, 0, 0, NULL); 789 pidgin_new_item_from_stock(menu, _("_Quit"), GTK_STOCK_QUIT, G_CALLBACK(purple_core_quit), NULL, 0, 0, NULL);
732 790
733 #ifdef _WIN32 791 #ifdef _WIN32
734 g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); 792 g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
735 g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); 793 g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
794 pos_func = NULL;
736 #endif 795 #endif
737 gtk_widget_show_all(menu); 796 gtk_widget_show_all(menu);
738 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, 797 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
739 ui_ops->position_menu, 798 pos_func,
740 NULL, 0, gtk_get_current_event_time()); 799 docklet, 0, gtk_get_current_event_time());
741 } 800 }
742 801
743 /************************************************************************** 802 static void
744 * public api for ui_ops
745 **************************************************************************/
746 void
747 pidgin_docklet_update_icon()
748 {
749 if (ui_ops && ui_ops->update_icon)
750 ui_ops->update_icon(status, connecting, pending);
751 }
752
753 void
754 pidgin_docklet_clicked(int button_type) 803 pidgin_docklet_clicked(int button_type)
755 { 804 {
756 switch (button_type) { 805 switch (button_type) {
757 case 1: 806 case 1:
758 if (pending) { 807 if (pending) {
769 docklet_menu(); 818 docklet_menu();
770 break; 819 break;
771 } 820 }
772 } 821 }
773 822
774 void 823 static void
775 pidgin_docklet_embedded() 824 pidgin_docklet_embedded(void)
776 { 825 {
777 if (!visibility_manager 826 if (!visibility_manager
778 && strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) { 827 && strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
779 pidgin_blist_visibility_manager_add(); 828 pidgin_blist_visibility_manager_add();
780 visibility_manager = TRUE; 829 visibility_manager = TRUE;
781 } 830 }
782 visible = TRUE; 831 visible = TRUE;
783 docklet_update_status(); 832 docklet_update_status();
784 pidgin_docklet_update_icon(); 833 docklet_gtk_status_update_icon(status, connecting, pending);
785 } 834 }
786 835
787 void 836 static void
788 pidgin_docklet_remove() 837 pidgin_docklet_remove(void)
789 { 838 {
790 if (visible) { 839 if (visible) {
791 if (visibility_manager) { 840 if (visibility_manager) {
792 pidgin_blist_visibility_manager_remove(); 841 pidgin_blist_visibility_manager_remove();
793 visibility_manager = FALSE; 842 visibility_manager = FALSE;
799 visible = FALSE; 848 visible = FALSE;
800 status = PURPLE_STATUS_OFFLINE; 849 status = PURPLE_STATUS_OFFLINE;
801 } 850 }
802 } 851 }
803 852
804 void 853 static gboolean
805 pidgin_docklet_set_ui_ops(struct docklet_ui_ops *ops) 854 docklet_gtk_recreate_cb(gpointer data)
806 { 855 {
807 ui_ops = ops; 856 docklet_gtk_status_create(TRUE);
808 } 857
809 858 return FALSE;
859 }
860
861 #ifndef _WIN32
862 static gboolean
863 docklet_gtk_embed_timeout_cb(gpointer data)
864 {
865 #if !GTK_CHECK_VERSION(2,12,0)
866 if (gtk_status_icon_is_embedded(docklet)) {
867 /* Older GTK+ (<2.12) don't implement the embedded signal, but the
868 information is still accessible through the above function. */
869 purple_debug_info("docklet", "embedded\n");
870
871 pidgin_docklet_embedded();
872 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
873 }
874 else
875 #endif
876 {
877 /* The docklet was not embedded within the timeout.
878 * Remove it as a visibility manager, but leave the plugin
879 * loaded so that it can embed automatically if/when a notification
880 * area becomes available.
881 */
882 purple_debug_info("docklet", "failed to embed within timeout\n");
883 pidgin_docklet_remove();
884 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
885 }
886
887 #if GTK_CHECK_VERSION(2,12,0)
888 embed_timeout = 0;
889 return FALSE;
890 #else
891 return TRUE;
892 #endif
893 }
894 #endif
895
896 #if GTK_CHECK_VERSION(2,12,0)
897 static gboolean
898 docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
899 {
900 if (embed_timeout) {
901 purple_timeout_remove(embed_timeout);
902 embed_timeout = 0;
903 }
904
905 if (gtk_status_icon_is_embedded(docklet)) {
906 purple_debug_info("docklet", "embedded\n");
907
908 pidgin_docklet_embedded();
909 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
910 } else {
911 purple_debug_info("docklet", "detached\n");
912
913 pidgin_docklet_remove();
914 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
915 }
916
917 return TRUE;
918 }
919 #endif
920
921 static void
922 docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
923 {
924 purple_debug_info("docklet", "destroyed\n");
925
926 pidgin_docklet_remove();
927
928 g_object_unref(G_OBJECT(docklet));
929 docklet = NULL;
930
931 g_idle_add(docklet_gtk_recreate_cb, NULL);
932 }
933
934 static void
935 docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
936 {
937 pidgin_docklet_clicked(1);
938 }
939
940 static void
941 docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
942 {
943 purple_debug_info("docklet", "The button is %u\n", button);
944 #ifdef GDK_WINDOWING_QUARTZ
945 /* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
946 pidgin_docklet_clicked(3);
947 #else
948 pidgin_docklet_clicked(button);
949 #endif
950 }
951
952 static void
953 docklet_gtk_status_destroy(void)
954 {
955 g_return_if_fail(docklet != NULL);
956
957 pidgin_docklet_remove();
958
959 if (embed_timeout) {
960 purple_timeout_remove(embed_timeout);
961 embed_timeout = 0;
962 }
963
964 gtk_status_icon_set_visible(docklet, FALSE);
965 g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
966 g_object_unref(G_OBJECT(docklet));
967 docklet = NULL;
968
969 purple_debug_info("docklet", "GTK+ destroyed\n");
970 }
971
972 static void
973 docklet_gtk_status_create(gboolean recreate)
974 {
975 if (docklet) {
976 /* if this is being called when a tray icon exists, it's because
977 something messed up. try destroying it before we proceed,
978 although docklet_refcount may be all hosed. hopefully won't happen. */
979 purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
980 docklet_gtk_status_destroy();
981 }
982
983 docklet = gtk_status_icon_new();
984 g_return_if_fail(docklet != NULL);
985
986 g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
987 g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
988 #if GTK_CHECK_VERSION(2,12,0)
989 g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
990 #endif
991 g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
992
993 gtk_status_icon_set_visible(docklet, TRUE);
994
995 /* This is a hack to avoid a race condition between the docklet getting
996 * embedded in the notification area and the gtkblist restoring its
997 * previous visibility state. If the docklet does not get embedded within
998 * the timeout, it will be removed as a visibility manager until it does
999 * get embedded. Ideally, we would only call docklet_embedded() when the
1000 * icon was actually embedded. This only happens when the docklet is first
1001 * created, not when being recreated.
1002 *
1003 * The gtk docklet tracks whether it successfully embedded in a pref and
1004 * allows for a longer timeout period if it successfully embedded the last
1005 * time it was run. This should hopefully solve problems with the buddy
1006 * list not properly starting hidden when Pidgin is started on login.
1007 */
1008 if (!recreate) {
1009 pidgin_docklet_embedded();
1010 #ifndef _WIN32
1011 #if GTK_CHECK_VERSION(2,12,0)
1012 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
1013 embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
1014 } else {
1015 embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
1016 }
1017 #else
1018 embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
1019 #endif
1020 #endif
1021 }
1022
1023 purple_debug_info("docklet", "GTK+ created\n");
1024 }
1025
1026 /**************************************************************************
1027 * public api
1028 **************************************************************************/
1029
810 void* 1030 void*
811 pidgin_docklet_get_handle() 1031 pidgin_docklet_get_handle()
812 { 1032 {
813 static int i; 1033 static int i;
814 return &i; 1034 return &i;
820 void *conn_handle = purple_connections_get_handle(); 1040 void *conn_handle = purple_connections_get_handle();
821 void *conv_handle = purple_conversations_get_handle(); 1041 void *conv_handle = purple_conversations_get_handle();
822 void *accounts_handle = purple_accounts_get_handle(); 1042 void *accounts_handle = purple_accounts_get_handle();
823 void *status_handle = purple_savedstatuses_get_handle(); 1043 void *status_handle = purple_savedstatuses_get_handle();
824 void *docklet_handle = pidgin_docklet_get_handle(); 1044 void *docklet_handle = pidgin_docklet_get_handle();
1045 gchar *tmp;
825 1046
826 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet"); 1047 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet");
827 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE); 1048 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE);
828 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always"); 1049 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always");
829 purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show", 1050 purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show",
830 docklet_show_pref_changed_cb, NULL); 1051 docklet_show_pref_changed_cb, NULL);
831 1052
832 docklet_ui_init(); 1053 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
833 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always") && ui_ops && ui_ops->create) 1054 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
834 ui_ops->create(); 1055 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
1056 purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
1057 } else {
1058 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
1059 }
1060
1061 tmp = g_build_path(G_DIR_SEPARATOR_S, DATADIR, "pixmaps", "pidgin", "tray", NULL);
1062 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), tmp);
1063 g_free(tmp);
1064
1065 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always"))
1066 docklet_gtk_status_create(FALSE);
835 1067
836 purple_signal_connect(conn_handle, "signed-on", 1068 purple_signal_connect(conn_handle, "signed-on",
837 docklet_handle, PURPLE_CALLBACK(docklet_signed_on_cb), NULL); 1069 docklet_handle, PURPLE_CALLBACK(docklet_signed_on_cb), NULL);
838 purple_signal_connect(conn_handle, "signed-off", 1070 purple_signal_connect(conn_handle, "signed-off",
839 docklet_handle, PURPLE_CALLBACK(docklet_signed_off_cb), NULL); 1071 docklet_handle, PURPLE_CALLBACK(docklet_signed_off_cb), NULL);
858 } 1090 }
859 1091
860 void 1092 void
861 pidgin_docklet_uninit() 1093 pidgin_docklet_uninit()
862 { 1094 {
863 if (visible && ui_ops && ui_ops->destroy) 1095 if (visible)
864 ui_ops->destroy(); 1096 docklet_gtk_status_destroy();
865 } 1097 }
1098