pidgin/gtkdocklet-gtk.c

branch
cpw.qulogic.gtk3
changeset 32438
dc8991868906
parent 32437
a1093fbc45d2
child 32925
d4d8afc054dc
equal deleted inserted replaced
32437:a1093fbc45d2 32438:dc8991868906
1 /*
2 * System tray icon (aka docklet) plugin for Purple
3 *
4 * Copyright (C) 2007 Anders Hasselqvist
5 *
6 * 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 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #include "internal.h"
23 #include "pidgin.h"
24 #include "debug.h"
25 #include "prefs.h"
26 #include "pidginstock.h"
27 #include "gtkdocklet.h"
28
29 #define SHORT_EMBED_TIMEOUT 5
30 #define LONG_EMBED_TIMEOUT 15
31
32 /* globals */
33 static GtkStatusIcon *docklet = NULL;
34 static guint embed_timeout = 0;
35
36 /* protos */
37 static void docklet_gtk_status_create(gboolean);
38
39 static gboolean
40 docklet_gtk_recreate_cb(gpointer data)
41 {
42 docklet_gtk_status_create(TRUE);
43
44 return FALSE;
45 }
46
47 static gboolean
48 docklet_gtk_embed_timeout_cb(gpointer data)
49 {
50 #if !GTK_CHECK_VERSION(2,12,0)
51 if (gtk_status_icon_is_embedded(docklet)) {
52 /* Older GTK+ (<2.12) don't implement the embedded signal, but the
53 information is still accessable through the above function. */
54 purple_debug_info("docklet", "embedded\n");
55
56 pidgin_docklet_embedded();
57 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
58 }
59 else
60 #endif
61 {
62 /* The docklet was not embedded within the timeout.
63 * Remove it as a visibility manager, but leave the plugin
64 * loaded so that it can embed automatically if/when a notification
65 * area becomes available.
66 */
67 purple_debug_info("docklet", "failed to embed within timeout\n");
68 pidgin_docklet_remove();
69 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
70 }
71
72 #if GTK_CHECK_VERSION(2,12,0)
73 embed_timeout = 0;
74 return FALSE;
75 #else
76 return TRUE;
77 #endif
78 }
79
80 #if GTK_CHECK_VERSION(2,12,0)
81 static gboolean
82 docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
83 {
84 if (embed_timeout) {
85 purple_timeout_remove(embed_timeout);
86 embed_timeout = 0;
87 }
88
89 if (gtk_status_icon_is_embedded(docklet)) {
90 purple_debug_info("docklet", "embedded\n");
91
92 pidgin_docklet_embedded();
93 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
94 } else {
95 purple_debug_info("docklet", "detached\n");
96
97 pidgin_docklet_remove();
98 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
99 }
100
101 return TRUE;
102 }
103 #endif
104
105 static void
106 docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
107 {
108 purple_debug_info("docklet", "destroyed\n");
109
110 pidgin_docklet_remove();
111
112 g_object_unref(G_OBJECT(docklet));
113 docklet = NULL;
114
115 g_idle_add(docklet_gtk_recreate_cb, NULL);
116 }
117
118 static void
119 docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
120 {
121 pidgin_docklet_clicked(1);
122 }
123
124 static void
125 docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
126 {
127 purple_debug_info("docklet", "The button is %u\n", button);
128 #ifdef GDK_WINDOWING_QUARTZ
129 /* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
130 pidgin_docklet_clicked(3);
131 #else
132 pidgin_docklet_clicked(button);
133 #endif
134 }
135
136 static void
137 docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
138 {
139 const gchar *icon_name = NULL;
140
141 switch (status) {
142 case PURPLE_STATUS_OFFLINE:
143 icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
144 break;
145 case PURPLE_STATUS_AWAY:
146 icon_name = PIDGIN_STOCK_TRAY_AWAY;
147 break;
148 case PURPLE_STATUS_UNAVAILABLE:
149 icon_name = PIDGIN_STOCK_TRAY_BUSY;
150 break;
151 case PURPLE_STATUS_EXTENDED_AWAY:
152 icon_name = PIDGIN_STOCK_TRAY_XA;
153 break;
154 case PURPLE_STATUS_INVISIBLE:
155 icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
156 break;
157 default:
158 icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
159 break;
160 }
161
162 if (pending)
163 icon_name = PIDGIN_STOCK_TRAY_PENDING;
164 if (connecting)
165 icon_name = PIDGIN_STOCK_TRAY_CONNECT;
166
167 if (icon_name) {
168 gtk_status_icon_set_from_icon_name(docklet, icon_name);
169 }
170 }
171
172 static void
173 docklet_gtk_status_set_tooltip(gchar *tooltip)
174 {
175 gtk_status_icon_set_tooltip_text(docklet, tooltip);
176 }
177
178 static void
179 docklet_gtk_status_position_menu(GtkMenu *menu,
180 int *x, int *y, gboolean *push_in,
181 gpointer user_data)
182 {
183 gtk_status_icon_position_menu(menu, x, y, push_in, docklet);
184 }
185
186 static void
187 docklet_gtk_status_destroy(void)
188 {
189 g_return_if_fail(docklet != NULL);
190
191 pidgin_docklet_remove();
192
193 if (embed_timeout) {
194 purple_timeout_remove(embed_timeout);
195 embed_timeout = 0;
196 }
197
198 gtk_status_icon_set_visible(docklet, FALSE);
199 g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
200 g_object_unref(G_OBJECT(docklet));
201 docklet = NULL;
202
203 purple_debug_info("docklet", "GTK+ destroyed\n");
204 }
205
206 static void
207 docklet_gtk_status_create(gboolean recreate)
208 {
209 if (docklet) {
210 /* if this is being called when a tray icon exists, it's because
211 something messed up. try destroying it before we proceed,
212 although docklet_refcount may be all hosed. hopefully won't happen. */
213 purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
214 docklet_gtk_status_destroy();
215 }
216
217 docklet = gtk_status_icon_new();
218 g_return_if_fail(docklet != NULL);
219
220 g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
221 g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
222 #if GTK_CHECK_VERSION(2,12,0)
223 g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
224 #endif
225 g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
226
227 gtk_status_icon_set_visible(docklet, TRUE);
228
229 /* This is a hack to avoid a race condition between the docklet getting
230 * embedded in the notification area and the gtkblist restoring its
231 * previous visibility state. If the docklet does not get embedded within
232 * the timeout, it will be removed as a visibility manager until it does
233 * get embedded. Ideally, we would only call docklet_embedded() when the
234 * icon was actually embedded. This only happens when the docklet is first
235 * created, not when being recreated.
236 *
237 * The gtk docklet tracks whether it successfully embedded in a pref and
238 * allows for a longer timeout period if it successfully embedded the last
239 * time it was run. This should hopefully solve problems with the buddy
240 * list not properly starting hidden when Pidgin is started on login.
241 */
242 if (!recreate) {
243 pidgin_docklet_embedded();
244 #if GTK_CHECK_VERSION(2,12,0)
245 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
246 embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
247 } else {
248 embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
249 }
250 #else
251 embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
252 #endif
253 }
254
255 purple_debug_info("docklet", "GTK+ created\n");
256 }
257
258 static void
259 docklet_gtk_status_create_ui_op(void)
260 {
261 docklet_gtk_status_create(FALSE);
262 }
263
264 static struct docklet_ui_ops ui_ops =
265 {
266 docklet_gtk_status_create_ui_op,
267 docklet_gtk_status_destroy,
268 docklet_gtk_status_update_icon,
269 NULL,
270 docklet_gtk_status_set_tooltip,
271 docklet_gtk_status_position_menu
272 };
273
274 void
275 docklet_ui_init(void)
276 {
277 pidgin_docklet_set_ui_ops(&ui_ops);
278
279 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
280 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
281 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
282 purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
283 } else {
284 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
285 }
286
287 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
288 DATADIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S "pidgin" G_DIR_SEPARATOR_S "tray");
289 }
290

mercurial