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)
|
|
29 - pop up notices when GNOME2's system-tray-applet supports it, with a
|
|
30 prefs dialog to choose what to alert for */
|
|
31
|
|
32 /* includes */
|
|
33 #define GAIM_PLUGINS
|
|
34 #include <gtk/gtk.h>
|
3517
|
35 #include <fcntl.h>
|
3510
|
36 #include "gaim.h"
|
|
37 #include "eggtrayicon.h"
|
|
38
|
|
39 /* types */
|
|
40 enum docklet_status {
|
|
41 online,
|
|
42 away,
|
|
43 away_pending,
|
3517
|
44 unread_pending,
|
3510
|
45 connecting,
|
|
46 offline
|
|
47 };
|
|
48
|
|
49 /* functions */
|
|
50 static void docklet_create();
|
|
51
|
|
52 /* globals */
|
3513
|
53 static EggTrayIcon *docklet = NULL;
|
3510
|
54 static GtkWidget *icon;
|
|
55 static enum docklet_status status;
|
3517
|
56 static GtkWidget *configwin = NULL;
|
3510
|
57
|
|
58 static void docklet_embedded(GtkWidget *widget, void *data) {
|
|
59 debug_printf("Docklet: embedded\n");
|
|
60 docklet_add();
|
|
61 }
|
|
62
|
|
63 static void docklet_destroyed(GtkWidget *widget, void *data) {
|
|
64 debug_printf("Docklet: destroyed\n");
|
|
65 docklet_remove();
|
|
66 docklet_create();
|
|
67 }
|
|
68
|
3517
|
69
|
|
70 static void docklet_mute(GtkWidget *toggle, void *data) {
|
|
71 mute_sounds = GTK_CHECK_MENU_ITEM(toggle)->active;
|
|
72 if (mute_sounds) {
|
|
73 debug_printf("Docklet: sounds muted\n");
|
|
74 } else {
|
|
75 debug_printf("Docklet: sounds unmuted\n");
|
|
76 }
|
3510
|
77 }
|
|
78
|
3517
|
79
|
3510
|
80 static void docklet_menu(GdkEventButton *event) {
|
3513
|
81 static GtkWidget *menu = NULL;
|
3512
|
82 GtkWidget *entry;
|
3510
|
83
|
|
84 if (menu) {
|
|
85 gtk_widget_destroy(menu);
|
|
86 }
|
|
87
|
|
88 menu = gtk_menu_new();
|
|
89
|
|
90 if (status == offline) {
|
|
91 entry = gtk_menu_item_new_with_label(_("Auto-login"));
|
|
92 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(auto_login), NULL);
|
|
93 gtk_menu_append(GTK_MENU(menu), entry);
|
|
94 } else {
|
|
95 if (status == online) {
|
|
96 GtkWidget *docklet_awaymenu;
|
|
97 GSList *awy = NULL;
|
|
98 struct away_message *a = NULL;
|
|
99
|
|
100 docklet_awaymenu = gtk_menu_new();
|
|
101 awy = away_messages;
|
|
102
|
|
103 while (awy) {
|
|
104 a = (struct away_message *)awy->data;
|
|
105
|
|
106 entry = gtk_menu_item_new_with_label(a->name);
|
|
107 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(do_away_message), a);
|
|
108 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
109
|
|
110 awy = g_slist_next(awy);
|
|
111 }
|
|
112
|
|
113 entry = gtk_separator_menu_item_new();
|
|
114 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
115
|
|
116 entry = gtk_menu_item_new_with_label(_("New..."));
|
|
117 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(create_away_mess), NULL);
|
|
118 gtk_menu_append(GTK_MENU(docklet_awaymenu), entry);
|
|
119
|
|
120 entry = gtk_menu_item_new_with_label(_("Away"));
|
3512
|
121 gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry), docklet_awaymenu);
|
3510
|
122 gtk_menu_append(GTK_MENU(menu), entry);
|
|
123 } else {
|
|
124 entry = gtk_menu_item_new_with_label(_("Back"));
|
|
125 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(do_im_back), NULL);
|
|
126 gtk_menu_append(GTK_MENU(menu), entry);
|
|
127 }
|
|
128
|
|
129 entry = gtk_menu_item_new_with_label(_("Signoff"));
|
|
130 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(signoff_all), NULL);
|
|
131 gtk_menu_append(GTK_MENU(menu), entry);
|
|
132 }
|
|
133
|
|
134 entry = gtk_separator_menu_item_new();
|
|
135 gtk_menu_append(GTK_MENU(menu), entry);
|
|
136
|
3517
|
137 entry = gtk_check_menu_item_new_with_label(_("Mute Sounds"));
|
|
138 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), mute_sounds);
|
|
139 g_signal_connect(GTK_WIDGET(entry), "toggled", G_CALLBACK(docklet_mute), NULL);
|
|
140 gtk_menu_append(GTK_MENU(menu), entry);
|
|
141
|
3510
|
142 entry = gtk_menu_item_new_with_label(_("Accounts"));
|
|
143 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(account_editor), NULL);
|
|
144 gtk_menu_append(GTK_MENU(menu), entry);
|
|
145
|
|
146 entry = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
|
|
147 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(show_prefs), NULL);
|
|
148 gtk_menu_append(GTK_MENU(menu), entry);
|
|
149
|
|
150 entry = gtk_menu_item_new_with_label(_("Plugins"));
|
|
151 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(show_plugins), NULL);
|
|
152 gtk_menu_append(GTK_MENU(menu), entry);
|
|
153
|
|
154 entry = gtk_separator_menu_item_new();
|
|
155 gtk_menu_append(GTK_MENU(menu), entry);
|
|
156
|
|
157 entry = gtk_menu_item_new_with_label(_("About"));
|
|
158 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(show_about), NULL);
|
|
159 gtk_menu_append(GTK_MENU(menu), entry);
|
|
160
|
|
161 entry = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
|
|
162 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(do_quit), NULL);
|
|
163 gtk_menu_append(GTK_MENU(menu), entry);
|
|
164
|
|
165 gtk_widget_show_all(menu);
|
|
166 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
|
|
167 }
|
|
168
|
|
169 static void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data) {
|
|
170 switch (event->button) {
|
|
171 case 1:
|
3517
|
172 if (unread_message_queue) {
|
|
173 purge_away_queue(unread_message_queue);
|
|
174 unread_message_queue=NULL;
|
|
175 docklet_update_status();
|
|
176 }
|
|
177 else
|
|
178 docklet_toggle();
|
3510
|
179 break;
|
|
180 case 2:
|
|
181 break;
|
|
182 case 3:
|
|
183 docklet_menu(event);
|
|
184 break;
|
|
185 }
|
|
186 }
|
|
187
|
|
188 static void docklet_update_icon() {
|
|
189 gchar *filename;
|
|
190 GdkPixbuf *unscaled;
|
|
191
|
|
192 switch (status) {
|
|
193 case online:
|
|
194 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "online.png", NULL);
|
|
195 break;
|
|
196 case away:
|
|
197 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "away.png", NULL);
|
|
198 break;
|
|
199 case away_pending:
|
|
200 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgpend.png", NULL);
|
|
201 break;
|
3517
|
202 case unread_pending:
|
|
203 /* XXX MAKE ME BLINK! */
|
|
204 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgunread.png", NULL);
|
|
205 break;
|
3510
|
206 case connecting:
|
3517
|
207 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "connect.png", NULL);
|
3510
|
208 break;
|
|
209 case offline:
|
|
210 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "offline.png", NULL);
|
|
211 }
|
|
212
|
|
213 unscaled = gdk_pixbuf_new_from_file(filename, NULL);
|
|
214
|
|
215 if (unscaled) {
|
|
216 GdkPixbuf *scaled;
|
|
217
|
|
218 scaled = gdk_pixbuf_scale_simple(unscaled, 24, 24, GDK_INTERP_BILINEAR);
|
|
219 gtk_image_set_from_pixbuf(GTK_IMAGE(icon), scaled);
|
|
220 g_object_unref(unscaled);
|
|
221 g_object_unref(scaled);
|
|
222
|
|
223 debug_printf("Docklet: updated icon to %s\n",filename);
|
|
224 } else {
|
|
225 debug_printf("Docklet: failed to load icon from %s\n",filename);
|
|
226 }
|
|
227
|
|
228 g_free(filename);
|
|
229 }
|
|
230
|
|
231 static void docklet_update_status() {
|
|
232 enum docklet_status oldstatus;
|
|
233
|
|
234 oldstatus = status;
|
|
235
|
|
236 if (connections) {
|
3517
|
237 if (unread_message_queue) {
|
|
238 status = unread_pending;
|
|
239 } else if (awaymessage) {
|
3510
|
240 if (message_queue) {
|
|
241 status = away_pending;
|
|
242 } else {
|
|
243 status = away;
|
|
244 }
|
|
245 } else {
|
|
246 status = online;
|
|
247 }
|
|
248 } else {
|
3517
|
249 if (connecting_count) {
|
|
250 status = connecting;
|
|
251 } else {
|
|
252 status = offline;
|
|
253 }
|
3510
|
254 }
|
|
255
|
|
256 if (status != oldstatus) {
|
|
257 docklet_update_icon();
|
|
258 }
|
|
259 }
|
|
260
|
|
261 static void docklet_create() {
|
|
262 GtkWidget *box;
|
|
263
|
|
264 /* is this necessary/wise? */
|
|
265 if (docklet) {
|
|
266 g_signal_handlers_disconnect_by_func(GTK_WIDGET(docklet), G_CALLBACK(docklet_destroyed), NULL);
|
|
267 gtk_widget_destroy(GTK_WIDGET(docklet));
|
|
268 debug_printf("Docklet: freed\n");
|
|
269 }
|
|
270
|
|
271 docklet = egg_tray_icon_new("Gaim");
|
|
272 box = gtk_event_box_new();
|
|
273 icon = gtk_image_new();
|
|
274
|
|
275 g_signal_connect(GTK_WIDGET(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
|
|
276 g_signal_connect(GTK_WIDGET(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
|
|
277 g_signal_connect(box, "button-press-event", G_CALLBACK(docklet_clicked), NULL);
|
|
278
|
|
279 gtk_container_add(GTK_CONTAINER(box), icon);
|
|
280 gtk_container_add(GTK_CONTAINER(docklet), box);
|
|
281 gtk_widget_show_all(GTK_WIDGET(docklet));
|
|
282
|
|
283 docklet_update_status();
|
|
284 docklet_update_icon();
|
|
285
|
|
286 debug_printf("Docklet: created\n");
|
|
287 }
|
|
288
|
|
289 static void gaim_signon(struct gaim_connection *gc, void *data) {
|
|
290 docklet_update_status();
|
|
291 }
|
|
292
|
|
293 static void gaim_signoff(struct gaim_connection *gc, void *data) {
|
|
294 docklet_update_status();
|
|
295 }
|
|
296
|
|
297 static void gaim_connecting(struct aim_user *user, void *data) {
|
|
298 docklet_update_status();
|
|
299 }
|
|
300
|
|
301 static void gaim_away(struct gaim_connection *gc, char *state, char *message, void *data) {
|
|
302 /* we only support global away. this is the way it is, ok? */
|
|
303 docklet_update_status();
|
|
304 }
|
|
305
|
|
306 static void gaim_im_recv(struct gaim_connection *gc, char **who, char **what, void *data) {
|
|
307 /* if message queuing while away is enabled, this event could be the first
|
|
308 message so we need to see if the status (and hence icon) needs changing */
|
|
309 docklet_update_status();
|
|
310 }
|
|
311
|
|
312 static void gaim_buddy_signon(struct gaim_connection *gc, char *who, void *data) {
|
|
313 }
|
|
314
|
|
315 static void gaim_buddy_signoff(struct gaim_connection *gc, char *who, void *data) {
|
|
316 }
|
|
317
|
|
318 static void gaim_buddy_away(struct gaim_connection *gc, char *who, void *data) {
|
|
319 }
|
|
320
|
|
321 static void gaim_buddy_back(struct gaim_connection *gc, char *who, void *data) {
|
|
322 }
|
|
323
|
|
324 static void gaim_new_conversation(char *who, void *data) {
|
|
325 }
|
|
326
|
|
327 char *gaim_plugin_init(GModule *handle) {
|
|
328 docklet_create();
|
|
329
|
|
330 gaim_signal_connect(handle, event_signon, gaim_signon, NULL);
|
|
331 gaim_signal_connect(handle, event_signoff, gaim_signoff, NULL);
|
|
332 gaim_signal_connect(handle, event_connecting, gaim_connecting, NULL);
|
|
333 gaim_signal_connect(handle, event_away, gaim_away, NULL);
|
|
334 gaim_signal_connect(handle, event_im_recv, gaim_im_recv, NULL);
|
|
335 gaim_signal_connect(handle, event_buddy_signon, gaim_buddy_signon, NULL);
|
|
336 gaim_signal_connect(handle, event_buddy_signoff, gaim_buddy_signoff, NULL);
|
|
337 gaim_signal_connect(handle, event_buddy_away, gaim_buddy_away, NULL);
|
|
338 gaim_signal_connect(handle, event_buddy_back, gaim_buddy_back, NULL);
|
|
339 gaim_signal_connect(handle, event_new_conversation, gaim_new_conversation, NULL);
|
|
340
|
|
341 return NULL;
|
|
342 }
|
|
343
|
3517
|
344 static void toggle_queue (GtkWidget *w, void *null) {
|
|
345 away_options ^= OPT_AWAY_QUEUE_UNREAD;
|
|
346 save_prefs();
|
|
347 }
|
|
348
|
|
349 void gaim_plugin_config() {
|
|
350 /* This is the sorriest dialog ever written ever */
|
|
351 /* It's a good thing I plan on rewriting it later tonight */
|
|
352 GtkWidget *button;
|
|
353 GtkWidget *vbox;
|
|
354
|
|
355 if (configwin) return;
|
|
356 GAIM_DIALOG(configwin);
|
|
357
|
|
358 vbox = gtk_vbox_new(0, 6);
|
|
359 gtk_container_add(GTK_CONTAINER(configwin), vbox);
|
|
360 gtk_window_set_title(GTK_WINDOW(configwin), "Docklet Configuration");
|
|
361
|
|
362 button = gtk_check_button_new_with_mnemonic("_Hide new messages until docklet is clicked");
|
|
363 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), away_options & OPT_AWAY_QUEUE_UNREAD);
|
|
364 gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(toggle_queue), NULL);
|
|
365 gtk_box_pack_end(GTK_BOX(vbox), button, 0, 0, 0);
|
|
366
|
|
367 gtk_widget_show_all(configwin);
|
|
368 }
|
|
369
|
|
370
|
3510
|
371 void gaim_plugin_remove() {
|
|
372 if (GTK_WIDGET_VISIBLE(docklet)) {
|
|
373 docklet_remove();
|
|
374 }
|
|
375
|
|
376 g_signal_handlers_disconnect_by_func(GTK_WIDGET(docklet), G_CALLBACK(docklet_destroyed), NULL);
|
|
377 gtk_widget_destroy(GTK_WIDGET(docklet));
|
|
378
|
|
379 debug_printf("Docklet: removed\n");
|
|
380 }
|
|
381
|
|
382 const char *name() {
|
3550
|
383 return _("System Tray Docklet");
|
3510
|
384 }
|
|
385
|
|
386 const char *description() {
|
3550
|
387 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
|
388 }
|