Mercurial > pidgin
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 |