3510
|
1 /* System tray docklet plugin for Gaim
|
|
2 * Copyright (C) 2002 Robert McQueen <robot101@debian.org>
|
|
3 * Inspired by a similar plugin by:
|
|
4 * John (J5) Palmieri <johnp@martianrock.com>
|
|
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 /* todo (in order of importance):
|
|
23 - don't crash when the plugin gets unloaded (it seems to crash after
|
|
24 the plugin has gone, when gtk updates the button in the plugins
|
|
25 dialog. backtrace is always useless. weird)
|
|
26 - handle and update tooltips to show your current accounts
|
|
27 - dernyi's account status menu in the right click
|
|
28 - store icons in gtk2 stock icon thing (needs doing for the whole prog)
|
3554
|
29 - optional pop up notices when GNOME2's system-tray-applet supports it
|
|
30 - support blinking the icon when messages are pending */
|
3510
|
31
|
|
32 /* includes */
|
|
33 #define GAIM_PLUGINS
|
|
34 #include <gtk/gtk.h>
|
|
35 #include "gaim.h"
|
|
36 #include "eggtrayicon.h"
|
|
37
|
|
38 /* types */
|
|
39 enum docklet_status {
|
|
40 online,
|
|
41 away,
|
|
42 away_pending,
|
3517
|
43 unread_pending,
|
3510
|
44 connecting,
|
|
45 offline
|
|
46 };
|
|
47
|
|
48 /* functions */
|
|
49 static void docklet_create();
|
3554
|
50 static void docklet_update_status();
|
3510
|
51
|
|
52 /* globals */
|
3554
|
53 static GtkWidget *configwin;
|
3513
|
54 static EggTrayIcon *docklet = NULL;
|
3510
|
55 static GtkWidget *icon;
|
|
56 static enum docklet_status status;
|
|
57
|
3554
|
58 static void docklet_toggle_mute(GtkWidget *toggle, void *data) {
|
|
59 mute_sounds = GTK_CHECK_MENU_ITEM(toggle)->active;
|
3510
|
60 }
|
|
61
|
3554
|
62 static void docklet_toggle_queue(GtkWidget *widget, void *data) {
|
|
63 away_options ^= OPT_AWAY_QUEUE_UNREAD;
|
|
64 save_prefs();
|
3510
|
65 }
|
3570
|
66
|
|
67 /* static void docklet_toggle_blist_show(GtkWidget *widget, void *data) {
|
|
68 blist_options ^= OPT_BLIST_APP_BUDDY_SHOW;
|
|
69 save_prefs();
|
|
70 } */
|
|
71
|
3554
|
72 static void docklet_flush_queue() {
|
3570
|
73 if (unread_message_queue) {
|
|
74 purge_away_queue(unread_message_queue);
|
|
75 unread_message_queue = NULL;
|
|
76 }
|
3510
|
77 }
|
|
78
|
|
79 static void docklet_menu(GdkEventButton *event) {
|
3513
|
80 static GtkWidget *menu = NULL;
|
3512
|
81 GtkWidget *entry;
|
3510
|
82
|
|
83 if (menu) {
|
|
84 gtk_widget_destroy(menu);
|
|
85 }
|
|
86
|
|
87 menu = gtk_menu_new();
|
|
88
|
|
89 if (status == offline) {
|
|
90 entry = gtk_menu_item_new_with_label(_("Auto-login"));
|
3554
|
91 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(auto_login), NULL);
|
3510
|
92 gtk_menu_append(GTK_MENU(menu), entry);
|
|
93 } else {
|
|
94 if (status == online) {
|
|
95 GtkWidget *docklet_awaymenu;
|
|
96 GSList *awy = NULL;
|
|
97 struct away_message *a = NULL;
|
|
98
|
|
99 docklet_awaymenu = gtk_menu_new();
|
|
100 awy = away_messages;
|
|
101
|
|
102 while (awy) {
|
|
103 a = (struct away_message *)awy->data;
|
|
104
|
|
105 entry = gtk_menu_item_new_with_label(a->name);
|
3554
|
106 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(do_away_message), a);
|
3510
|
107 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
108
|
|
109 awy = g_slist_next(awy);
|
|
110 }
|
|
111
|
|
112 entry = gtk_separator_menu_item_new();
|
|
113 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
114
|
|
115 entry = gtk_menu_item_new_with_label(_("New..."));
|
3554
|
116 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(create_away_mess), NULL);
|
3510
|
117 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
118
|
|
119 entry = gtk_menu_item_new_with_label(_("Away"));
|
3512
|
120 gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry), docklet_awaymenu);
|
3510
|
121 gtk_menu_append(GTK_MENU(menu), entry);
|
|
122 } else {
|
|
123 entry = gtk_menu_item_new_with_label(_("Back"));
|
3554
|
124 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(do_im_back), NULL);
|
3510
|
125 gtk_menu_append(GTK_MENU(menu), entry);
|
|
126 }
|
|
127
|
|
128 entry = gtk_menu_item_new_with_label(_("Signoff"));
|
3554
|
129 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(signoff_all), NULL);
|
3510
|
130 gtk_menu_append(GTK_MENU(menu), entry);
|
|
131 }
|
|
132
|
|
133 entry = gtk_separator_menu_item_new();
|
|
134 gtk_menu_append(GTK_MENU(menu), entry);
|
|
135
|
3517
|
136 entry = gtk_check_menu_item_new_with_label(_("Mute Sounds"));
|
|
137 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), mute_sounds);
|
3554
|
138 g_signal_connect(G_OBJECT(entry), "toggled", G_CALLBACK(docklet_toggle_mute), NULL);
|
3517
|
139 gtk_menu_append(GTK_MENU(menu), entry);
|
|
140
|
3510
|
141 entry = gtk_menu_item_new_with_label(_("Accounts"));
|
3554
|
142 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(account_editor), NULL);
|
3510
|
143 gtk_menu_append(GTK_MENU(menu), entry);
|
|
144
|
|
145 entry = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
|
3554
|
146 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(show_prefs), NULL);
|
3510
|
147 gtk_menu_append(GTK_MENU(menu), entry);
|
|
148
|
|
149 entry = gtk_separator_menu_item_new();
|
|
150 gtk_menu_append(GTK_MENU(menu), entry);
|
|
151
|
|
152 entry = gtk_menu_item_new_with_label(_("About"));
|
3554
|
153 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(show_about), NULL);
|
3510
|
154 gtk_menu_append(GTK_MENU(menu), entry);
|
|
155
|
|
156 entry = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
|
3554
|
157 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(do_quit), NULL);
|
3510
|
158 gtk_menu_append(GTK_MENU(menu), entry);
|
|
159
|
|
160 gtk_widget_show_all(menu);
|
|
161 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
|
|
162 }
|
|
163
|
|
164 static void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data) {
|
|
165 switch (event->button) {
|
|
166 case 1:
|
3517
|
167 if (unread_message_queue) {
|
3570
|
168 docklet_flush_queue();
|
3517
|
169 docklet_update_status();
|
3570
|
170 } else {
|
3517
|
171 docklet_toggle();
|
3554
|
172 }
|
3510
|
173 break;
|
|
174 case 2:
|
|
175 break;
|
|
176 case 3:
|
|
177 docklet_menu(event);
|
|
178 break;
|
|
179 }
|
|
180 }
|
|
181
|
|
182 static void docklet_update_icon() {
|
|
183 gchar *filename;
|
|
184 GdkPixbuf *unscaled;
|
|
185
|
|
186 switch (status) {
|
|
187 case online:
|
|
188 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "online.png", NULL);
|
|
189 break;
|
|
190 case away:
|
|
191 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "away.png", NULL);
|
|
192 break;
|
|
193 case away_pending:
|
|
194 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgpend.png", NULL);
|
|
195 break;
|
3570
|
196 case unread_pending:
|
3517
|
197 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgunread.png", NULL);
|
|
198 break;
|
3510
|
199 case connecting:
|
3517
|
200 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "connect.png", NULL);
|
3510
|
201 break;
|
|
202 case offline:
|
|
203 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "offline.png", NULL);
|
|
204 }
|
|
205
|
|
206 unscaled = gdk_pixbuf_new_from_file(filename, NULL);
|
|
207
|
|
208 if (unscaled) {
|
|
209 GdkPixbuf *scaled;
|
|
210
|
|
211 scaled = gdk_pixbuf_scale_simple(unscaled, 24, 24, GDK_INTERP_BILINEAR);
|
|
212 gtk_image_set_from_pixbuf(GTK_IMAGE(icon), scaled);
|
|
213 g_object_unref(unscaled);
|
|
214 g_object_unref(scaled);
|
|
215
|
|
216 debug_printf("Docklet: updated icon to %s\n",filename);
|
|
217 } else {
|
|
218 debug_printf("Docklet: failed to load icon from %s\n",filename);
|
|
219 }
|
|
220
|
|
221 g_free(filename);
|
|
222 }
|
|
223
|
|
224 static void docklet_update_status() {
|
|
225 enum docklet_status oldstatus;
|
|
226
|
|
227 oldstatus = status;
|
|
228
|
|
229 if (connections) {
|
3517
|
230 if (unread_message_queue) {
|
|
231 status = unread_pending;
|
|
232 } else if (awaymessage) {
|
3510
|
233 if (message_queue) {
|
|
234 status = away_pending;
|
|
235 } else {
|
|
236 status = away;
|
|
237 }
|
3554
|
238 } else if (connecting_count) {
|
|
239 status = connecting;
|
3510
|
240 } else {
|
|
241 status = online;
|
|
242 }
|
|
243 } else {
|
3517
|
244 if (connecting_count) {
|
|
245 status = connecting;
|
|
246 } else {
|
|
247 status = offline;
|
|
248 }
|
3510
|
249 }
|
|
250
|
|
251 if (status != oldstatus) {
|
|
252 docklet_update_icon();
|
|
253 }
|
|
254 }
|
|
255
|
3554
|
256 static void docklet_embedded(GtkWidget *widget, void *data) {
|
3570
|
257 debug_printf("Docklet: embedded\n");
|
|
258 docklet_add();
|
3554
|
259 }
|
|
260
|
|
261 static void docklet_destroyed(GtkWidget *widget, void *data) {
|
3570
|
262 debug_printf("Docklet: destroyed\n");
|
|
263 docklet_flush_queue();
|
|
264 docklet_remove();
|
|
265 docklet_create();
|
3554
|
266 }
|
|
267
|
3510
|
268 static void docklet_create() {
|
|
269 GtkWidget *box;
|
|
270
|
3570
|
271 if (docklet) {
|
|
272 /* if this is being called when a docklet exists, it's because that
|
|
273 docklet is in the process of being destroyed. all we need to do
|
|
274 is tell gobject we're not interested in it any more, and throw
|
|
275 the pointer away. */
|
|
276 g_object_unref(G_OBJECT(docklet));
|
|
277 docklet = NULL;
|
3510
|
278 }
|
|
279
|
|
280 docklet = egg_tray_icon_new("Gaim");
|
|
281 box = gtk_event_box_new();
|
|
282 icon = gtk_image_new();
|
|
283
|
3554
|
284 g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
|
|
285 g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
|
|
286 g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
|
3510
|
287
|
|
288 gtk_container_add(GTK_CONTAINER(box), icon);
|
|
289 gtk_container_add(GTK_CONTAINER(docklet), box);
|
|
290 gtk_widget_show_all(GTK_WIDGET(docklet));
|
|
291
|
3554
|
292 /* ref the docklet before we bandy it about the place */
|
|
293 g_object_ref(G_OBJECT(docklet));
|
3510
|
294 docklet_update_status();
|
|
295 docklet_update_icon();
|
|
296
|
|
297 debug_printf("Docklet: created\n");
|
|
298 }
|
|
299
|
|
300 static void gaim_signon(struct gaim_connection *gc, void *data) {
|
|
301 docklet_update_status();
|
|
302 }
|
|
303
|
|
304 static void gaim_signoff(struct gaim_connection *gc, void *data) {
|
|
305 docklet_update_status();
|
|
306 }
|
|
307
|
|
308 static void gaim_connecting(struct aim_user *user, void *data) {
|
|
309 docklet_update_status();
|
|
310 }
|
|
311
|
|
312 static void gaim_away(struct gaim_connection *gc, char *state, char *message, void *data) {
|
|
313 /* we only support global away. this is the way it is, ok? */
|
|
314 docklet_update_status();
|
|
315 }
|
|
316
|
3570
|
317 static void gaim_im_displayed_recv(struct gaim_connection *gc, char **who, char **what, void *data) {
|
3510
|
318 /* if message queuing while away is enabled, this event could be the first
|
|
319 message so we need to see if the status (and hence icon) needs changing */
|
|
320 docklet_update_status();
|
|
321 }
|
|
322
|
3570
|
323 /* static void gaim_buddy_signon(struct gaim_connection *gc, char *who, void *data) {
|
3510
|
324 }
|
|
325
|
|
326 static void gaim_buddy_signoff(struct gaim_connection *gc, char *who, void *data) {
|
|
327 }
|
|
328
|
|
329 static void gaim_buddy_away(struct gaim_connection *gc, char *who, void *data) {
|
|
330 }
|
|
331
|
|
332 static void gaim_buddy_back(struct gaim_connection *gc, char *who, void *data) {
|
|
333 }
|
|
334
|
|
335 static void gaim_new_conversation(char *who, void *data) {
|
3570
|
336 } */
|
3510
|
337
|
|
338 char *gaim_plugin_init(GModule *handle) {
|
|
339 docklet_create();
|
|
340
|
|
341 gaim_signal_connect(handle, event_signon, gaim_signon, NULL);
|
|
342 gaim_signal_connect(handle, event_signoff, gaim_signoff, NULL);
|
|
343 gaim_signal_connect(handle, event_connecting, gaim_connecting, NULL);
|
|
344 gaim_signal_connect(handle, event_away, gaim_away, NULL);
|
3570
|
345 gaim_signal_connect(handle, event_im_displayed_rcvd, gaim_im_displayed_recv, NULL);
|
|
346 /* gaim_signal_connect(handle, event_buddy_signon, gaim_buddy_signon, NULL);
|
3510
|
347 gaim_signal_connect(handle, event_buddy_signoff, gaim_buddy_signoff, NULL);
|
|
348 gaim_signal_connect(handle, event_buddy_away, gaim_buddy_away, NULL);
|
|
349 gaim_signal_connect(handle, event_buddy_back, gaim_buddy_back, NULL);
|
3570
|
350 gaim_signal_connect(handle, event_new_conversation, gaim_new_conversation, NULL); */
|
3510
|
351
|
|
352 return NULL;
|
|
353 }
|
|
354
|
3554
|
355 void gaim_plugin_remove() {
|
3570
|
356 if (GTK_WIDGET_VISIBLE(docklet)) {
|
|
357 docklet_remove();
|
|
358 }
|
3554
|
359
|
3570
|
360 docklet_flush_queue();
|
3554
|
361
|
3570
|
362 g_object_unref(G_OBJECT(docklet));
|
|
363 g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_destroyed), NULL);
|
|
364 gtk_widget_destroy(GTK_WIDGET(docklet));
|
3554
|
365
|
3570
|
366 debug_printf("Docklet: removed\n");
|
3554
|
367 }
|
|
368
|
3570
|
369 GtkWidget *gaim_plugin_config_gtk() {
|
|
370 GtkWidget *frame;
|
|
371 GtkWidget *vbox, *hbox;
|
|
372 GtkWidget *toggle;
|
3517
|
373
|
3570
|
374 frame = gtk_vbox_new(FALSE, 18);
|
|
375 gtk_container_set_border_width(GTK_CONTAINER(frame), 12);
|
|
376
|
|
377 vbox = make_frame(frame, _("Docklet Configuration"));
|
|
378 hbox = gtk_hbox_new(FALSE, 18);
|
|
379 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
|
3517
|
380
|
3570
|
381 /* toggle = gtk_check_button_new_with_mnemonic(_("_Automatically show buddy list on sign on"));
|
|
382 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), blist_options & OPT_BLIST_APP_BUDDY_SHOW);
|
|
383 g_signal_connect(G_OBJECT(toggle), "clicked", G_CALLBACK(docklet_toggle_blist_show), NULL);
|
|
384 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); */
|
3517
|
385
|
3570
|
386 toggle = gtk_check_button_new_with_mnemonic(_("_Hide new messages until docklet is clicked"));
|
|
387 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), away_options & OPT_AWAY_QUEUE_UNREAD);
|
|
388 g_signal_connect(G_OBJECT(toggle), "clicked", G_CALLBACK(docklet_toggle_queue), NULL);
|
|
389 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
|
|
390
|
|
391 gtk_widget_show_all(frame);
|
|
392 return frame;
|
3517
|
393 }
|
3570
|
394
|
3551
|
395 struct gaim_plugin_description desc;
|
|
396 struct gaim_plugin_description *gaim_plugin_desc() {
|
|
397 desc.api_version = PLUGIN_API_VERSION;
|
3570
|
398 desc.name = g_strdup(_("System Tray Docklet"));
|
3551
|
399 desc.version = g_strdup(VERSION);
|
3570
|
400 desc.description = g_strdup(_("Interacts with a System Tray applet (in GNOME or KDE, for example) to display the current status of Gaim, allow fast access to commonly used functions, and to toggle display of the buddy list or login window."));
|
|
401 desc.authors = g_strdup(_("Robert McQueen <robot101@debian.org>"));
|
3551
|
402 desc.url = g_strdup(WEBSITE);
|
|
403 return &desc;
|
|
404 }
|
3510
|
405
|
3570
|
406 char *name() {
|
3550
|
407 return _("System Tray Docklet");
|
3510
|
408 }
|
|
409
|
3570
|
410 char *description() {
|
3550
|
411 return _("Interacts with a System Tray applet (in GNOME or KDE, for example) to display the current status of Gaim, allow fast access to commonly used functions, and to toggle display of the buddy list or login window.");
|
3510
|
412 }
|