|
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_separator_menu_item_new();
|
|
|
151 gtk_menu_append(GTK_MENU(menu), entry);
|
|
|
152
|
|
|
153 entry = gtk_menu_item_new_with_label(_("About"));
|
|
|
154 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(show_about), NULL);
|
|
|
155 gtk_menu_append(GTK_MENU(menu), entry);
|
|
|
156
|
|
|
157 entry = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
|
|
|
158 g_signal_connect(GTK_WIDGET(entry), "activate", G_CALLBACK(do_quit), NULL);
|
|
|
159 gtk_menu_append(GTK_MENU(menu), entry);
|
|
|
160
|
|
|
161 gtk_widget_show_all(menu);
|
|
|
162 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
|
|
|
163 }
|
|
|
164
|
|
|
165 static void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data) {
|
|
|
166 switch (event->button) {
|
|
|
167 case 1:
|
|
3517
|
168 if (unread_message_queue) {
|
|
|
169 purge_away_queue(unread_message_queue);
|
|
|
170 unread_message_queue=NULL;
|
|
|
171 docklet_update_status();
|
|
|
172 }
|
|
|
173 else
|
|
|
174 docklet_toggle();
|
|
3510
|
175 break;
|
|
|
176 case 2:
|
|
|
177 break;
|
|
|
178 case 3:
|
|
|
179 docklet_menu(event);
|
|
|
180 break;
|
|
|
181 }
|
|
|
182 }
|
|
|
183
|
|
|
184 static void docklet_update_icon() {
|
|
|
185 gchar *filename;
|
|
|
186 GdkPixbuf *unscaled;
|
|
|
187
|
|
|
188 switch (status) {
|
|
|
189 case online:
|
|
|
190 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "online.png", NULL);
|
|
|
191 break;
|
|
|
192 case away:
|
|
|
193 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "away.png", NULL);
|
|
|
194 break;
|
|
|
195 case away_pending:
|
|
|
196 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgpend.png", NULL);
|
|
|
197 break;
|
|
3517
|
198 case unread_pending:
|
|
|
199 /* XXX MAKE ME BLINK! */
|
|
|
200 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "msgunread.png", NULL);
|
|
|
201 break;
|
|
3510
|
202 case connecting:
|
|
3517
|
203 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "connect.png", NULL);
|
|
3510
|
204 break;
|
|
|
205 case offline:
|
|
|
206 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "offline.png", NULL);
|
|
|
207 }
|
|
|
208
|
|
|
209 unscaled = gdk_pixbuf_new_from_file(filename, NULL);
|
|
|
210
|
|
|
211 if (unscaled) {
|
|
|
212 GdkPixbuf *scaled;
|
|
|
213
|
|
|
214 scaled = gdk_pixbuf_scale_simple(unscaled, 24, 24, GDK_INTERP_BILINEAR);
|
|
|
215 gtk_image_set_from_pixbuf(GTK_IMAGE(icon), scaled);
|
|
|
216 g_object_unref(unscaled);
|
|
|
217 g_object_unref(scaled);
|
|
|
218
|
|
|
219 debug_printf("Docklet: updated icon to %s\n",filename);
|
|
|
220 } else {
|
|
|
221 debug_printf("Docklet: failed to load icon from %s\n",filename);
|
|
|
222 }
|
|
|
223
|
|
|
224 g_free(filename);
|
|
|
225 }
|
|
|
226
|
|
|
227 static void docklet_update_status() {
|
|
|
228 enum docklet_status oldstatus;
|
|
|
229
|
|
|
230 oldstatus = status;
|
|
|
231
|
|
|
232 if (connections) {
|
|
3517
|
233 if (unread_message_queue) {
|
|
|
234 status = unread_pending;
|
|
|
235 } else if (awaymessage) {
|
|
3510
|
236 if (message_queue) {
|
|
|
237 status = away_pending;
|
|
|
238 } else {
|
|
|
239 status = away;
|
|
|
240 }
|
|
|
241 } else {
|
|
|
242 status = online;
|
|
|
243 }
|
|
|
244 } else {
|
|
3517
|
245 if (connecting_count) {
|
|
|
246 status = connecting;
|
|
|
247 } else {
|
|
|
248 status = offline;
|
|
|
249 }
|
|
3510
|
250 }
|
|
|
251
|
|
|
252 if (status != oldstatus) {
|
|
|
253 docklet_update_icon();
|
|
|
254 }
|
|
|
255 }
|
|
|
256
|
|
|
257 static void docklet_create() {
|
|
|
258 GtkWidget *box;
|
|
|
259
|
|
|
260 /* is this necessary/wise? */
|
|
|
261 if (docklet) {
|
|
|
262 g_signal_handlers_disconnect_by_func(GTK_WIDGET(docklet), G_CALLBACK(docklet_destroyed), NULL);
|
|
|
263 gtk_widget_destroy(GTK_WIDGET(docklet));
|
|
|
264 debug_printf("Docklet: freed\n");
|
|
|
265 }
|
|
|
266
|
|
|
267 docklet = egg_tray_icon_new("Gaim");
|
|
|
268 box = gtk_event_box_new();
|
|
|
269 icon = gtk_image_new();
|
|
|
270
|
|
|
271 g_signal_connect(GTK_WIDGET(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
|
|
|
272 g_signal_connect(GTK_WIDGET(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
|
|
|
273 g_signal_connect(box, "button-press-event", G_CALLBACK(docklet_clicked), NULL);
|
|
|
274
|
|
|
275 gtk_container_add(GTK_CONTAINER(box), icon);
|
|
|
276 gtk_container_add(GTK_CONTAINER(docklet), box);
|
|
|
277 gtk_widget_show_all(GTK_WIDGET(docklet));
|
|
|
278
|
|
|
279 docklet_update_status();
|
|
|
280 docklet_update_icon();
|
|
|
281
|
|
|
282 debug_printf("Docklet: created\n");
|
|
|
283 }
|
|
|
284
|
|
|
285 static void gaim_signon(struct gaim_connection *gc, void *data) {
|
|
|
286 docklet_update_status();
|
|
|
287 }
|
|
|
288
|
|
|
289 static void gaim_signoff(struct gaim_connection *gc, void *data) {
|
|
|
290 docklet_update_status();
|
|
|
291 }
|
|
|
292
|
|
|
293 static void gaim_connecting(struct aim_user *user, void *data) {
|
|
|
294 docklet_update_status();
|
|
|
295 }
|
|
|
296
|
|
|
297 static void gaim_away(struct gaim_connection *gc, char *state, char *message, void *data) {
|
|
|
298 /* we only support global away. this is the way it is, ok? */
|
|
|
299 docklet_update_status();
|
|
|
300 }
|
|
|
301
|
|
|
302 static void gaim_im_recv(struct gaim_connection *gc, char **who, char **what, void *data) {
|
|
|
303 /* if message queuing while away is enabled, this event could be the first
|
|
|
304 message so we need to see if the status (and hence icon) needs changing */
|
|
|
305 docklet_update_status();
|
|
|
306 }
|
|
|
307
|
|
|
308 static void gaim_buddy_signon(struct gaim_connection *gc, char *who, void *data) {
|
|
|
309 }
|
|
|
310
|
|
|
311 static void gaim_buddy_signoff(struct gaim_connection *gc, char *who, void *data) {
|
|
|
312 }
|
|
|
313
|
|
|
314 static void gaim_buddy_away(struct gaim_connection *gc, char *who, void *data) {
|
|
|
315 }
|
|
|
316
|
|
|
317 static void gaim_buddy_back(struct gaim_connection *gc, char *who, void *data) {
|
|
|
318 }
|
|
|
319
|
|
|
320 static void gaim_new_conversation(char *who, void *data) {
|
|
|
321 }
|
|
|
322
|
|
|
323 char *gaim_plugin_init(GModule *handle) {
|
|
|
324 docklet_create();
|
|
|
325
|
|
|
326 gaim_signal_connect(handle, event_signon, gaim_signon, NULL);
|
|
|
327 gaim_signal_connect(handle, event_signoff, gaim_signoff, NULL);
|
|
|
328 gaim_signal_connect(handle, event_connecting, gaim_connecting, NULL);
|
|
|
329 gaim_signal_connect(handle, event_away, gaim_away, NULL);
|
|
3551
|
330 gaim_signal_connect(handle, event_im_displayed_rcvd, gaim_im_recv, NULL);
|
|
3510
|
331 gaim_signal_connect(handle, event_im_recv, gaim_im_recv, NULL);
|
|
|
332 gaim_signal_connect(handle, event_buddy_signon, gaim_buddy_signon, NULL);
|
|
|
333 gaim_signal_connect(handle, event_buddy_signoff, gaim_buddy_signoff, NULL);
|
|
|
334 gaim_signal_connect(handle, event_buddy_away, gaim_buddy_away, NULL);
|
|
|
335 gaim_signal_connect(handle, event_buddy_back, gaim_buddy_back, NULL);
|
|
|
336 gaim_signal_connect(handle, event_new_conversation, gaim_new_conversation, NULL);
|
|
|
337
|
|
|
338 return NULL;
|
|
|
339 }
|
|
|
340
|
|
3517
|
341 static void toggle_queue (GtkWidget *w, void *null) {
|
|
|
342 away_options ^= OPT_AWAY_QUEUE_UNREAD;
|
|
|
343 save_prefs();
|
|
|
344 }
|
|
|
345
|
|
|
346 void gaim_plugin_config() {
|
|
|
347 /* This is the sorriest dialog ever written ever */
|
|
|
348 /* It's a good thing I plan on rewriting it later tonight */
|
|
|
349 GtkWidget *button;
|
|
|
350 GtkWidget *vbox;
|
|
|
351
|
|
|
352 if (configwin) return;
|
|
|
353 GAIM_DIALOG(configwin);
|
|
|
354
|
|
|
355 vbox = gtk_vbox_new(0, 6);
|
|
|
356 gtk_container_add(GTK_CONTAINER(configwin), vbox);
|
|
|
357 gtk_window_set_title(GTK_WINDOW(configwin), "Docklet Configuration");
|
|
|
358
|
|
|
359 button = gtk_check_button_new_with_mnemonic("_Hide new messages until docklet is clicked");
|
|
|
360 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), away_options & OPT_AWAY_QUEUE_UNREAD);
|
|
|
361 gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(toggle_queue), NULL);
|
|
|
362 gtk_box_pack_end(GTK_BOX(vbox), button, 0, 0, 0);
|
|
|
363
|
|
|
364 gtk_widget_show_all(configwin);
|
|
|
365 }
|
|
|
366
|
|
|
367
|
|
3510
|
368 void gaim_plugin_remove() {
|
|
|
369 if (GTK_WIDGET_VISIBLE(docklet)) {
|
|
|
370 docklet_remove();
|
|
|
371 }
|
|
|
372
|
|
|
373 g_signal_handlers_disconnect_by_func(GTK_WIDGET(docklet), G_CALLBACK(docklet_destroyed), NULL);
|
|
|
374 gtk_widget_destroy(GTK_WIDGET(docklet));
|
|
|
375
|
|
|
376 debug_printf("Docklet: removed\n");
|
|
|
377 }
|
|
3551
|
378
|
|
|
379 struct gaim_plugin_description desc;
|
|
|
380 struct gaim_plugin_description *gaim_plugin_desc() {
|
|
|
381 desc.api_version = PLUGIN_API_VERSION;
|
|
|
382 desc.name = g_strdup("System Tray Docklet");
|
|
|
383 desc.version = g_strdup(VERSION);
|
|
|
384 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.");
|
|
|
385 desc.authors = g_strdup("Robert McQueen <robot101@debian.org>");
|
|
|
386 desc.url = g_strdup(WEBSITE);
|
|
|
387 return &desc;
|
|
|
388 }
|
|
|
389
|
|
3510
|
390
|
|
|
391 const char *name() {
|
|
3550
|
392 return _("System Tray Docklet");
|
|
3510
|
393 }
|
|
|
394
|
|
|
395 const char *description() {
|
|
3550
|
396 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
|
397 }
|