| |
1 /* |
| |
2 * Gaim buddy notification plugin. |
| |
3 * |
| |
4 * Copyright (C) 2000-2001, Eric Warmenhoven (original code) |
| |
5 * Copyright (C) 2002, Etan Reisner <deryni@eden.rutgers.edu> (rewritten code) |
| |
6 * Copyright (C) 2003, Christian Hammond (update for changed API) |
| |
7 * Copyright (C) 2003, Brian Tarricone <bjt23@cornell.edu> (mostly rewritten) |
| |
8 * Copyright (C) 2003, Mark Doliner (minor cleanup) |
| |
9 * Copyright (C) 2003, Etan Reisner (largely rewritten again) |
| |
10 * |
| |
11 * This program is free software; you can redistribute it and/or modify |
| |
12 * it under the terms of the GNU General Public License as published by |
| |
13 * the Free Software Foundation; either version 2 of the License, or |
| |
14 * (at your option) any later version. |
| |
15 * |
| |
16 * This program is distributed in the hope that it will be useful, |
| |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
19 * GNU General Public License for more details. |
| |
20 * |
| |
21 * You should have received a copy of the GNU General Public License |
| |
22 * along with this program; if not, write to the Free Software |
| |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
24 * |
| |
25 */ |
| |
26 |
| |
27 /* TODO |
| |
28 * 22:22:17 <seanegan> deryni: speaking of notify.c... you know what else |
| |
29 * might be a neat feature? |
| |
30 * 22:22:30 <seanegan> Changing the window icon. |
| |
31 * 22:23:25 <deryni> seanegan: To what? |
| |
32 * 22:23:42 <seanegan> deryni: I dunno. Flash it between the regular icon and |
| |
33 * blank or something. |
| |
34 * 22:23:53 <deryni> Also I think gaim might re-set that sort of frequently, |
| |
35 * but I'd have to look. |
| |
36 * 22:25:16 <seanegan> deryni: I keep my conversations in one workspace and am |
| |
37 * frequently in an another, and the icon flashing in the pager would be a |
| |
38 * neat visual clue. |
| |
39 */ |
| |
40 |
| |
41 /* |
| |
42 * From Etan, 2002: |
| |
43 * -Added config dialog |
| |
44 * -Added control over notification method |
| |
45 * -Added control over when to release notification |
| |
46 * |
| |
47 * -Added option to get notification for chats also |
| |
48 * -Cleaned up code |
| |
49 * -Added option to notify on click as it's own option |
| |
50 * rather then as what happens when on focus isn't clicked |
| |
51 * -Added apply button to change the denotification methods for |
| |
52 * open conversation windows |
| |
53 * -Fixed apply to conversations, count now keeps count across applies |
| |
54 * -Fixed(?) memory leak, and in the process fixed some stupidities |
| |
55 * -Hit enter when done editing the title string entry box to save it |
| |
56 * |
| |
57 * Thanks to Carles Pina i Estany <carles@pinux.info> |
| |
58 * for count of new messages option |
| |
59 * |
| |
60 * From Brian, 20 July 2003: |
| |
61 * -Use new xml prefs |
| |
62 * -Better handling of notification states tracking |
| |
63 * -Better pref change handling |
| |
64 * -Fixed a possible memleak and possible crash (rare) |
| |
65 * -Use gtk_window_get_title() rather than gtkwin->title |
| |
66 * -Other random fixes and cleanups |
| |
67 * |
| |
68 * Etan again, 12 August 2003: |
| |
69 * -Better use of the new xml prefs |
| |
70 * -Removed all bitmask stuff |
| |
71 * -Even better pref change handling |
| |
72 * -Removed unnecessary functions |
| |
73 * -Reworking of notification/unnotification stuff |
| |
74 * -Header file include cleanup |
| |
75 * -General code cleanup |
| |
76 * |
| |
77 * Etan yet again, 04 April 2004: |
| |
78 * -Re-added Urgent option |
| |
79 * -Re-added unnotify on focus option (still needs work, as it will only |
| |
80 * react to focus-in events when the entry or history widgets are focused) |
| |
81 * |
| |
82 * Sean, 08 January, 2005: |
| |
83 * -Added Raise option, formally in Gaim proper |
| |
84 */ |
| |
85 |
| |
86 #include "internal.h" |
| |
87 #include "gtkgaim.h" |
| |
88 #include "gtkprefs.h" |
| |
89 |
| |
90 #include "conversation.h" |
| |
91 #include "prefs.h" |
| |
92 #include "signals.h" |
| |
93 #include "version.h" |
| |
94 #include "debug.h" |
| |
95 |
| |
96 #include "gtkplugin.h" |
| |
97 #include "gtkutils.h" |
| |
98 |
| |
99 #ifndef _WIN32 |
| |
100 #include <X11/Xatom.h> |
| |
101 #include <X11/Xlib.h> |
| |
102 #include <X11/Xutil.h> |
| |
103 #endif |
| |
104 |
| |
105 #define NOTIFY_PLUGIN_ID "gtk-x11-notify" |
| |
106 |
| |
107 static GaimPlugin *my_plugin = NULL; |
| |
108 static GdkAtom _Cardinal = GDK_NONE; |
| |
109 static GdkAtom _GaimUnseenCount = GDK_NONE; |
| |
110 |
| |
111 /* notification set/unset */ |
| |
112 static int notify(GaimConversation *conv, gboolean increment); |
| |
113 static void notify_win(GaimGtkWindow *gaimwin); |
| |
114 static void unnotify(GaimConversation *conv, gboolean reset); |
| |
115 static int unnotify_cb(GtkWidget *widget, gpointer data, |
| |
116 GaimConversation *conv); |
| |
117 |
| |
118 /* gtk widget callbacks for prefs panel */ |
| |
119 static void type_toggle_cb(GtkWidget *widget, gpointer data); |
| |
120 static void method_toggle_cb(GtkWidget *widget, gpointer data); |
| |
121 static void notify_toggle_cb(GtkWidget *widget, gpointer data); |
| |
122 static gboolean options_entry_cb(GtkWidget *widget, GdkEventFocus *event, |
| |
123 gpointer data); |
| |
124 static void apply_method(void); |
| |
125 static void apply_notify(void); |
| |
126 |
| |
127 /* string function */ |
| |
128 static void handle_string(GaimGtkWindow *gaimwin); |
| |
129 |
| |
130 /* count_title function */ |
| |
131 static void handle_count_title(GaimGtkWindow *gaimwin); |
| |
132 |
| |
133 /* count_xprop function */ |
| |
134 static void handle_count_xprop(GaimGtkWindow *gaimwin); |
| |
135 |
| |
136 /* urgent function */ |
| |
137 static void handle_urgent(GaimGtkWindow *gaimwin, gboolean set); |
| |
138 |
| |
139 /* raise function */ |
| |
140 static void handle_raise(GaimGtkWindow *gaimwin); |
| |
141 |
| |
142 /****************************************/ |
| |
143 /* Begin doing stuff below this line... */ |
| |
144 /****************************************/ |
| |
145 static guint |
| |
146 count_messages(GaimGtkWindow *gaimwin) |
| |
147 { |
| |
148 guint count = 0; |
| |
149 GList *convs = NULL, *l; |
| |
150 |
| |
151 for (convs = gaimwin->gtkconvs; convs != NULL; convs = convs->next) { |
| |
152 GaimGtkConversation *conv = convs->data; |
| |
153 for (l = conv->convs; l != NULL; l = l->next) { |
| |
154 count += GPOINTER_TO_INT(gaim_conversation_get_data(l->data, "notify-message-count")); |
| |
155 } |
| |
156 } |
| |
157 |
| |
158 return count; |
| |
159 } |
| |
160 |
| |
161 static int |
| |
162 notify(GaimConversation *conv, gboolean increment) |
| |
163 { |
| |
164 gint count; |
| |
165 gboolean has_focus; |
| |
166 GaimGtkWindow *gaimwin = NULL; |
| |
167 |
| |
168 if (conv == NULL) |
| |
169 return 0; |
| |
170 |
| |
171 /* We want to remove the notifications, but not reset the counter */ |
| |
172 unnotify(conv, FALSE); |
| |
173 |
| |
174 gaimwin = GAIM_GTK_CONVERSATION(conv)->win; |
| |
175 |
| |
176 /* If we aren't doing notifications for this type of conversation, return */ |
| |
177 if (((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) && |
| |
178 !gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) || |
| |
179 ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) && |
| |
180 !gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"))) |
| |
181 return 0; |
| |
182 |
| |
183 g_object_get(G_OBJECT(gaimwin->window), |
| |
184 "has-toplevel-focus", &has_focus, NULL); |
| |
185 |
| |
186 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") || |
| |
187 !has_focus) { |
| |
188 if (increment) { |
| |
189 count = GPOINTER_TO_INT(gaim_conversation_get_data(conv, "notify-message-count")); |
| |
190 count++; |
| |
191 gaim_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count)); |
| |
192 } |
| |
193 |
| |
194 notify_win(gaimwin); |
| |
195 } |
| |
196 |
| |
197 return 0; |
| |
198 } |
| |
199 |
| |
200 static void |
| |
201 notify_win(GaimGtkWindow *gaimwin) |
| |
202 { |
| |
203 if (count_messages(gaimwin) <= 0) |
| |
204 return; |
| |
205 |
| |
206 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_count")) |
| |
207 handle_count_title(gaimwin); |
| |
208 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop")) |
| |
209 handle_count_xprop(gaimwin); |
| |
210 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_string")) |
| |
211 handle_string(gaimwin); |
| |
212 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent")) |
| |
213 handle_urgent(gaimwin, TRUE); |
| |
214 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_raise")) |
| |
215 handle_raise(gaimwin); |
| |
216 } |
| |
217 |
| |
218 static void |
| |
219 unnotify(GaimConversation *conv, gboolean reset) |
| |
220 { |
| |
221 GaimConversation *active_conv = NULL; |
| |
222 GaimGtkWindow *gaimwin = NULL; |
| |
223 |
| |
224 g_return_if_fail(conv != NULL); |
| |
225 |
| |
226 gaimwin = GAIM_GTK_CONVERSATION(conv)->win; |
| |
227 active_conv = gaim_gtk_conv_window_get_active_conversation(gaimwin); |
| |
228 |
| |
229 /* reset the conversation window title */ |
| |
230 gaim_conversation_autoset_title(active_conv); |
| |
231 |
| |
232 if (reset) { |
| |
233 /* Only need to actually remove the urgent hinting here, since |
| |
234 * removing it just to have it readded in re-notify is an |
| |
235 * unnecessary couple extra RTs to the server */ |
| |
236 handle_urgent(gaimwin, FALSE); |
| |
237 gaim_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); |
| |
238 /* Same logic as for the urgent hint, xprops are also a RT. |
| |
239 * This needs to go here so that it gets the updated message |
| |
240 * count. */ |
| |
241 handle_count_xprop(gaimwin); |
| |
242 } |
| |
243 |
| |
244 return; |
| |
245 } |
| |
246 |
| |
247 static int |
| |
248 unnotify_cb(GtkWidget *widget, gpointer data, GaimConversation *conv) |
| |
249 { |
| |
250 if (GPOINTER_TO_INT(gaim_conversation_get_data(conv, "notify-message-count")) != 0) |
| |
251 unnotify(conv, TRUE); |
| |
252 |
| |
253 return 0; |
| |
254 } |
| |
255 |
| |
256 static gboolean |
| |
257 message_displayed_cb(GaimAccount *account, const char *who, char *message, |
| |
258 GaimConversation *conv, GaimMessageFlags flags) |
| |
259 { |
| |
260 if ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT && |
| |
261 gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") && |
| |
262 !(flags & GAIM_MESSAGE_NICK))) |
| |
263 return FALSE; |
| |
264 |
| |
265 if ((flags & GAIM_MESSAGE_RECV) && !(flags & GAIM_MESSAGE_DELAYED)) |
| |
266 notify(conv, TRUE); |
| |
267 |
| |
268 return FALSE; |
| |
269 } |
| |
270 |
| |
271 static void |
| |
272 im_sent_im(GaimAccount *account, const char *receiver, const char *message) |
| |
273 { |
| |
274 GaimConversation *conv = NULL; |
| |
275 |
| |
276 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) { |
| |
277 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, receiver, account); |
| |
278 unnotify(conv, TRUE); |
| |
279 } |
| |
280 } |
| |
281 |
| |
282 static void |
| |
283 chat_sent_im(GaimAccount *account, const char *message, int id) |
| |
284 { |
| |
285 GaimConversation *conv = NULL; |
| |
286 |
| |
287 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) { |
| |
288 conv = gaim_find_chat(gaim_account_get_connection(account), id); |
| |
289 unnotify(conv, TRUE); |
| |
290 } |
| |
291 } |
| |
292 |
| |
293 static int |
| |
294 attach_signals(GaimConversation *conv) |
| |
295 { |
| |
296 GaimGtkConversation *gtkconv = NULL; |
| |
297 GaimGtkWindow *gtkwin = NULL; |
| |
298 GSList *imhtml_ids = NULL, *entry_ids = NULL; |
| |
299 guint id; |
| |
300 |
| |
301 gtkconv = GAIM_GTK_CONVERSATION(conv); |
| |
302 if (!gtkconv) { |
| |
303 gaim_debug_misc("notify", "Failed to find gtkconv\n"); |
| |
304 return 0; |
| |
305 } |
| |
306 |
| |
307 gtkwin = gtkconv->win; |
| |
308 |
| |
309 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) { |
| |
310 /* TODO should really find a way to make this work no matter |
| |
311 * where the focus is inside the conv window, without having |
| |
312 * to bind to focus-in-event on the g(d|t)kwindow */ |
| |
313 /* try setting the signal on the focus-in-event for |
| |
314 * gtkwin->notebook->container? */ |
| |
315 id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event", |
| |
316 G_CALLBACK(unnotify_cb), conv); |
| |
317 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); |
| |
318 |
| |
319 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event", |
| |
320 G_CALLBACK(unnotify_cb), conv); |
| |
321 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id)); |
| |
322 } |
| |
323 |
| |
324 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) { |
| |
325 /* TODO similarly should really find a way to allow for |
| |
326 * clicking in other places of the window */ |
| |
327 id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event", |
| |
328 G_CALLBACK(unnotify_cb), conv); |
| |
329 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); |
| |
330 |
| |
331 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event", |
| |
332 G_CALLBACK(unnotify_cb), conv); |
| |
333 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id)); |
| |
334 } |
| |
335 |
| |
336 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) { |
| |
337 id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event", |
| |
338 G_CALLBACK(unnotify_cb), conv); |
| |
339 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); |
| |
340 } |
| |
341 |
| |
342 gaim_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids); |
| |
343 gaim_conversation_set_data(conv, "notify-entry-signals", entry_ids); |
| |
344 |
| |
345 return 0; |
| |
346 } |
| |
347 |
| |
348 static void |
| |
349 detach_signals(GaimConversation *conv) |
| |
350 { |
| |
351 GaimGtkConversation *gtkconv = NULL; |
| |
352 GaimGtkWindow *gtkwin = NULL; |
| |
353 GSList *ids = NULL, *l; |
| |
354 |
| |
355 gtkconv = GAIM_GTK_CONVERSATION(conv); |
| |
356 if (!gtkconv) |
| |
357 return; |
| |
358 gtkwin = gtkconv->win; |
| |
359 |
| |
360 ids = gaim_conversation_get_data(conv, "notify-imhtml-signals"); |
| |
361 for (l = ids; l != NULL; l = l->next) |
| |
362 g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data)); |
| |
363 g_slist_free(ids); |
| |
364 |
| |
365 ids = gaim_conversation_get_data(conv, "notify-entry-signals"); |
| |
366 for (l = ids; l != NULL; l = l->next) |
| |
367 g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data)); |
| |
368 g_slist_free(ids); |
| |
369 |
| |
370 gaim_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); |
| |
371 |
| |
372 gaim_conversation_set_data(conv, "notify-imhtml-signals", NULL); |
| |
373 gaim_conversation_set_data(conv, "notify-entry-signals", NULL); |
| |
374 } |
| |
375 |
| |
376 static void |
| |
377 conv_created(GaimConversation *conv) |
| |
378 { |
| |
379 gaim_conversation_set_data(conv, "notify-message-count", |
| |
380 GINT_TO_POINTER(0)); |
| |
381 |
| |
382 /* always attach the signals, notify() will take care of conversation |
| |
383 * type checking */ |
| |
384 attach_signals(conv); |
| |
385 } |
| |
386 |
| |
387 static void |
| |
388 conv_switched(GaimConversation *conv) |
| |
389 { |
| |
390 #if 0 |
| |
391 GaimGtkWindow *gaimwin = gaim_conversation_get_window(new_conv); |
| |
392 #endif |
| |
393 |
| |
394 /* |
| |
395 * If the conversation was switched, then make sure we re-notify |
| |
396 * because Gaim will have overwritten our custom window title. |
| |
397 */ |
| |
398 notify(conv, FALSE); |
| |
399 |
| |
400 #if 0 |
| |
401 printf("conv_switched - %p - %p\n", old_conv, new_conv); |
| |
402 printf("count - %d\n", count_messages(gaimwin)); |
| |
403 if (gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch")) |
| |
404 unnotify(new_conv, FALSE); |
| |
405 else { |
| |
406 /* if we don't have notification on the window then we don't want to |
| |
407 * re-notify it */ |
| |
408 if (count_messages(gaimwin)) |
| |
409 notify_win(gaimwin); |
| |
410 } |
| |
411 #endif |
| |
412 } |
| |
413 |
| |
414 static void |
| |
415 deleting_conv(GaimConversation *conv) |
| |
416 { |
| |
417 GaimGtkWindow *gaimwin = NULL; |
| |
418 |
| |
419 detach_signals(conv); |
| |
420 |
| |
421 gaimwin = GAIM_GTK_CONVERSATION(conv)->win; |
| |
422 |
| |
423 handle_urgent(gaimwin, FALSE); |
| |
424 gaim_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); |
| |
425 |
| |
426 return; |
| |
427 |
| |
428 #if 0 |
| |
429 /* i think this line crashes */ |
| |
430 if (count_messages(gaimwin)) |
| |
431 notify_win(gaimwin); |
| |
432 #endif |
| |
433 } |
| |
434 |
| |
435 #if 0 |
| |
436 static void |
| |
437 conversation_dragging(GaimConversation *active_conv, |
| |
438 GaimGtkWindow *old_gaimwin, |
| |
439 GaimGtkWindow *new_gaimwin) |
| |
440 { |
| |
441 if (old_gaimwin != new_gaimwin) { |
| |
442 if (old_gaimwin == NULL) { |
| |
443 /* |
| |
444 gaim_conversation_autoset_title(active_conv); |
| |
445 handle_urgent(new_gaimwin, FALSE); |
| |
446 */ |
| |
447 |
| |
448 if (count_messages(new_gaimwin)) |
| |
449 notify_win(new_gaimwin); |
| |
450 } else { |
| |
451 printf("if else count = %d\n", count_messages(new_gaimwin)); |
| |
452 printf("if else count = %d\n", count_messages(old_gaimwin)); |
| |
453 /* |
| |
454 GaimConversation *old_active_conv = NULL; |
| |
455 old_active_conv = gaim_conv_window_get_active_conversation(new_gaimwin); |
| |
456 |
| |
457 gaim_conversation_autoset_title(old_active_conv); |
| |
458 handle_urgent(old_gaimwin, FALSE); |
| |
459 |
| |
460 if (count_messages(old_gaimwin)) |
| |
461 notify_win(old_gaimwin); |
| |
462 |
| |
463 gaim_conversation_autoset_title(active_conv); |
| |
464 handle_urgent(new_gaimwin, FALSE); |
| |
465 |
| |
466 if (count_messages(new_gaimwin)) |
| |
467 notify_win(new_gaimwin); |
| |
468 */ |
| |
469 } |
| |
470 } else { |
| |
471 printf("else count = %d\n", count_messages(new_gaimwin)); |
| |
472 printf("else count = %d\n", count_messages(old_gaimwin)); |
| |
473 /* |
| |
474 gaim_conversation_autoset_title(active_conv); |
| |
475 handle_urgent(old_gaimwin, FALSE); |
| |
476 |
| |
477 if (count_messages(old_gaimwin)) |
| |
478 notify_win(old_gaimwin); |
| |
479 */ |
| |
480 } |
| |
481 } |
| |
482 #endif |
| |
483 |
| |
484 static void |
| |
485 handle_string(GaimGtkWindow *gaimwin) |
| |
486 { |
| |
487 GtkWindow *window = NULL; |
| |
488 gchar newtitle[256]; |
| |
489 |
| |
490 g_return_if_fail(gaimwin != NULL); |
| |
491 |
| |
492 window = GTK_WINDOW(gaimwin->window); |
| |
493 g_return_if_fail(window != NULL); |
| |
494 |
| |
495 g_snprintf(newtitle, sizeof(newtitle), "%s%s", |
| |
496 gaim_prefs_get_string("/plugins/gtk/X11/notify/title_string"), |
| |
497 gtk_window_get_title(window)); |
| |
498 gtk_window_set_title(window, newtitle); |
| |
499 } |
| |
500 |
| |
501 static void |
| |
502 handle_count_title(GaimGtkWindow *gaimwin) |
| |
503 { |
| |
504 GtkWindow *window; |
| |
505 char newtitle[256]; |
| |
506 |
| |
507 g_return_if_fail(gaimwin != NULL); |
| |
508 |
| |
509 window = GTK_WINDOW(gaimwin->window); |
| |
510 g_return_if_fail(window != NULL); |
| |
511 |
| |
512 g_snprintf(newtitle, sizeof(newtitle), "[%d] %s", |
| |
513 count_messages(gaimwin), gtk_window_get_title(window)); |
| |
514 gtk_window_set_title(window, newtitle); |
| |
515 } |
| |
516 |
| |
517 static void |
| |
518 handle_count_xprop(GaimGtkWindow *gaimwin) |
| |
519 { |
| |
520 #ifndef _WIN32 |
| |
521 guint count; |
| |
522 GtkWidget *window; |
| |
523 GdkWindow *gdkwin; |
| |
524 |
| |
525 window = gaimwin->window; |
| |
526 g_return_if_fail(window != NULL); |
| |
527 |
| |
528 if (_GaimUnseenCount == GDK_NONE) { |
| |
529 _GaimUnseenCount = gdk_atom_intern("_GAIM_UNSEEN_COUNT", FALSE); |
| |
530 } |
| |
531 |
| |
532 if (_Cardinal == GDK_NONE) { |
| |
533 _Cardinal = gdk_atom_intern("CARDINAL", FALSE); |
| |
534 } |
| |
535 |
| |
536 count = count_messages(gaimwin); |
| |
537 gdkwin = window->window; |
| |
538 |
| |
539 gdk_property_change(gdkwin, _GaimUnseenCount, _Cardinal, 32, |
| |
540 GDK_PROP_MODE_REPLACE, (guchar *) &count, 1); |
| |
541 #endif |
| |
542 } |
| |
543 |
| |
544 static void |
| |
545 handle_urgent(GaimGtkWindow *win, gboolean set) |
| |
546 { |
| |
547 #ifndef _WIN32 |
| |
548 gaim_gtk_set_urgent(GTK_WINDOW(win->window), set); |
| |
549 #endif |
| |
550 } |
| |
551 |
| |
552 static void |
| |
553 handle_raise(GaimGtkWindow *gaimwin) |
| |
554 { |
| |
555 gaim_gtk_conv_window_raise(gaimwin); |
| |
556 } |
| |
557 |
| |
558 static void |
| |
559 type_toggle_cb(GtkWidget *widget, gpointer data) |
| |
560 { |
| |
561 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
| |
562 gchar pref[256]; |
| |
563 |
| |
564 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", |
| |
565 (char *)data); |
| |
566 |
| |
567 gaim_prefs_set_bool(pref, on); |
| |
568 } |
| |
569 |
| |
570 static void |
| |
571 method_toggle_cb(GtkWidget *widget, gpointer data) |
| |
572 { |
| |
573 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
| |
574 gchar pref[256]; |
| |
575 |
| |
576 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", |
| |
577 (char *)data); |
| |
578 |
| |
579 gaim_prefs_set_bool(pref, on); |
| |
580 |
| |
581 if (!strcmp(data, "method_string")) { |
| |
582 GtkWidget *entry = g_object_get_data(G_OBJECT(widget), "title-entry"); |
| |
583 gtk_widget_set_sensitive(entry, on); |
| |
584 |
| |
585 gaim_prefs_set_string("/plugins/gtk/X11/notify/title_string", |
| |
586 gtk_entry_get_text(GTK_ENTRY(entry))); |
| |
587 } |
| |
588 |
| |
589 apply_method(); |
| |
590 } |
| |
591 |
| |
592 static void |
| |
593 notify_toggle_cb(GtkWidget *widget, gpointer data) |
| |
594 { |
| |
595 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
| |
596 gchar pref[256]; |
| |
597 |
| |
598 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", |
| |
599 (char *)data); |
| |
600 |
| |
601 gaim_prefs_set_bool(pref, on); |
| |
602 |
| |
603 apply_notify(); |
| |
604 } |
| |
605 |
| |
606 static gboolean |
| |
607 options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data) |
| |
608 { |
| |
609 if (data == NULL) |
| |
610 return FALSE; |
| |
611 |
| |
612 if (!strcmp(data, "method_string")) { |
| |
613 gaim_prefs_set_string("/plugins/gtk/X11/notify/title_string", |
| |
614 gtk_entry_get_text(GTK_ENTRY(widget))); |
| |
615 } |
| |
616 |
| |
617 apply_method(); |
| |
618 |
| |
619 return FALSE; |
| |
620 } |
| |
621 |
| |
622 static void |
| |
623 apply_method() |
| |
624 { |
| |
625 GList *convs; |
| |
626 GaimGtkWindow *gaimwin = NULL; |
| |
627 |
| |
628 for (convs = gaim_get_conversations(); convs != NULL; |
| |
629 convs = convs->next) { |
| |
630 GaimConversation *conv = (GaimConversation *)convs->data; |
| |
631 |
| |
632 /* remove notifications */ |
| |
633 unnotify(conv, FALSE); |
| |
634 |
| |
635 gaimwin = GAIM_GTK_CONVERSATION(conv)->win; |
| |
636 if (GPOINTER_TO_INT(gaim_conversation_get_data(conv, "notify-message-count")) != 0) |
| |
637 /* reattach appropriate notifications */ |
| |
638 notify(conv, FALSE); |
| |
639 } |
| |
640 } |
| |
641 |
| |
642 static void |
| |
643 apply_notify() |
| |
644 { |
| |
645 GList *convs = gaim_get_conversations(); |
| |
646 |
| |
647 while (convs) { |
| |
648 GaimConversation *conv = (GaimConversation *)convs->data; |
| |
649 |
| |
650 /* detach signals */ |
| |
651 detach_signals(conv); |
| |
652 /* reattach appropriate signals */ |
| |
653 attach_signals(conv); |
| |
654 |
| |
655 convs = convs->next; |
| |
656 } |
| |
657 } |
| |
658 |
| |
659 static GtkWidget * |
| |
660 get_config_frame(GaimPlugin *plugin) |
| |
661 { |
| |
662 GtkWidget *ret = NULL, *frame = NULL; |
| |
663 GtkWidget *vbox = NULL, *hbox = NULL; |
| |
664 GtkWidget *toggle = NULL, *entry = NULL, *ref; |
| |
665 |
| |
666 ret = gtk_vbox_new(FALSE, 18); |
| |
667 gtk_container_set_border_width(GTK_CONTAINER (ret), 12); |
| |
668 |
| |
669 /*---------- "Notify For" ----------*/ |
| |
670 frame = gaim_gtk_make_frame(ret, _("Notify For")); |
| |
671 vbox = gtk_vbox_new(FALSE, 5); |
| |
672 gtk_container_add(GTK_CONTAINER(frame), vbox); |
| |
673 |
| |
674 toggle = gtk_check_button_new_with_mnemonic(_("_IM windows")); |
| |
675 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
676 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
677 gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_im")); |
| |
678 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
679 G_CALLBACK(type_toggle_cb), "type_im"); |
| |
680 |
| |
681 toggle = gtk_check_button_new_with_mnemonic(_("C_hat windows")); |
| |
682 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
683 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
684 gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")); |
| |
685 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
686 G_CALLBACK(type_toggle_cb), "type_chat"); |
| |
687 |
| |
688 ref = toggle; |
| |
689 toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your screen name")); |
| |
690 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
691 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
692 gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick")); |
| |
693 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
694 G_CALLBACK(type_toggle_cb), "type_chat_nick"); |
| |
695 gtk_widget_set_sensitive(toggle, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref))); |
| |
696 g_signal_connect(G_OBJECT(ref), "toggled", |
| |
697 G_CALLBACK(gaim_gtk_toggle_sensitive), toggle); |
| |
698 |
| |
699 toggle = gtk_check_button_new_with_mnemonic(_("_Focused windows")); |
| |
700 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
701 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
702 gaim_prefs_get_bool("/plugins/gtk/X11/notify/type_focused")); |
| |
703 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
704 G_CALLBACK(type_toggle_cb), "type_focused"); |
| |
705 |
| |
706 /*---------- "Notification Methods" ----------*/ |
| |
707 frame = gaim_gtk_make_frame(ret, _("Notification Methods")); |
| |
708 vbox = gtk_vbox_new(FALSE, 5); |
| |
709 gtk_container_add(GTK_CONTAINER(frame), vbox); |
| |
710 |
| |
711 /* String method button */ |
| |
712 hbox = gtk_hbox_new(FALSE, 18); |
| |
713 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
714 toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:")); |
| |
715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
716 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_string")); |
| |
717 gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0); |
| |
718 |
| |
719 entry = gtk_entry_new(); |
| |
720 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); |
| |
721 gtk_entry_set_max_length(GTK_ENTRY(entry), 10); |
| |
722 gtk_widget_set_sensitive(GTK_WIDGET(entry), |
| |
723 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_string")); |
| |
724 gtk_entry_set_text(GTK_ENTRY(entry), |
| |
725 gaim_prefs_get_string("/plugins/gtk/X11/notify/title_string")); |
| |
726 g_object_set_data(G_OBJECT(toggle), "title-entry", entry); |
| |
727 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
728 G_CALLBACK(method_toggle_cb), "method_string"); |
| |
729 g_signal_connect(G_OBJECT(entry), "focus-out-event", |
| |
730 G_CALLBACK(options_entry_cb), "method_string"); |
| |
731 |
| |
732 /* Count method button */ |
| |
733 toggle = gtk_check_button_new_with_mnemonic(_("Insert c_ount of new messages into window title")); |
| |
734 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
735 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_count")); |
| |
736 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
737 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
738 G_CALLBACK(method_toggle_cb), "method_count"); |
| |
739 |
| |
740 #ifndef _WIN32 |
| |
741 /* Count xprop method button */ |
| |
742 toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property")); |
| |
743 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
744 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
745 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop")); |
| |
746 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
747 G_CALLBACK(method_toggle_cb), "method_count_xprop"); |
| |
748 |
| |
749 /* Urgent method button */ |
| |
750 toggle = gtk_check_button_new_with_mnemonic(_("Set window manager \"_URGENT\" hint")); |
| |
751 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
752 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
753 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent")); |
| |
754 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
755 G_CALLBACK(method_toggle_cb), "method_urgent"); |
| |
756 #endif |
| |
757 |
| |
758 /* Raise window method button */ |
| |
759 toggle = gtk_check_button_new_with_mnemonic(_("R_aise conversation window")); |
| |
760 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
762 gaim_prefs_get_bool("/plugins/gtk/X11/notify/method_raise")); |
| |
763 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
764 G_CALLBACK(method_toggle_cb), "method_raise"); |
| |
765 |
| |
766 /*---------- "Notification Removals" ----------*/ |
| |
767 frame = gaim_gtk_make_frame(ret, _("Notification Removal")); |
| |
768 vbox = gtk_vbox_new(FALSE, 5); |
| |
769 gtk_container_add(GTK_CONTAINER(frame), vbox); |
| |
770 |
| |
771 /* Remove on focus button */ |
| |
772 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _gains focus")); |
| |
773 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
775 gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")); |
| |
776 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(notify_toggle_cb), "notify_focus"); |
| |
777 |
| |
778 /* Remove on click button */ |
| |
779 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _receives click")); |
| |
780 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
781 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
782 gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")); |
| |
783 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
784 G_CALLBACK(notify_toggle_cb), "notify_click"); |
| |
785 |
| |
786 /* Remove on type button */ |
| |
787 toggle = gtk_check_button_new_with_mnemonic(_("Remove when _typing in conversation window")); |
| |
788 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
789 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
790 gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")); |
| |
791 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
792 G_CALLBACK(notify_toggle_cb), "notify_type"); |
| |
793 |
| |
794 /* Remove on message send button */ |
| |
795 toggle = gtk_check_button_new_with_mnemonic(_("Remove when a _message gets sent")); |
| |
796 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
797 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
798 gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")); |
| |
799 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
800 G_CALLBACK(notify_toggle_cb), "notify_send"); |
| |
801 |
| |
802 #if 0 |
| |
803 /* Remove on conversation switch button */ |
| |
804 toggle = gtk_check_button_new_with_mnemonic(_("Remove on switch to conversation ta_b")); |
| |
805 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); |
| |
806 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), |
| |
807 gaim_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch")); |
| |
808 g_signal_connect(G_OBJECT(toggle), "toggled", |
| |
809 G_CALLBACK(notify_toggle_cb), "notify_switch"); |
| |
810 #endif |
| |
811 |
| |
812 gtk_widget_show_all(ret); |
| |
813 return ret; |
| |
814 } |
| |
815 |
| |
816 static gboolean |
| |
817 plugin_load(GaimPlugin *plugin) |
| |
818 { |
| |
819 GList *convs = gaim_get_conversations(); |
| |
820 void *conv_handle = gaim_conversations_get_handle(); |
| |
821 void *gtk_conv_handle = gaim_gtk_conversations_get_handle(); |
| |
822 |
| |
823 my_plugin = plugin; |
| |
824 |
| |
825 gaim_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin, |
| |
826 GAIM_CALLBACK(message_displayed_cb), NULL); |
| |
827 gaim_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin, |
| |
828 GAIM_CALLBACK(message_displayed_cb), NULL); |
| |
829 gaim_signal_connect(gtk_conv_handle, "conversation-switched", plugin, |
| |
830 GAIM_CALLBACK(conv_switched), NULL); |
| |
831 gaim_signal_connect(conv_handle, "sent-im-msg", plugin, |
| |
832 GAIM_CALLBACK(im_sent_im), NULL); |
| |
833 gaim_signal_connect(conv_handle, "sent-chat-msg", plugin, |
| |
834 GAIM_CALLBACK(chat_sent_im), NULL); |
| |
835 gaim_signal_connect(conv_handle, "conversation-created", plugin, |
| |
836 GAIM_CALLBACK(conv_created), NULL); |
| |
837 gaim_signal_connect(conv_handle, "chat-joined", plugin, |
| |
838 GAIM_CALLBACK(conv_created), NULL); |
| |
839 gaim_signal_connect(conv_handle, "deleting-conversation", plugin, |
| |
840 GAIM_CALLBACK(deleting_conv), NULL); |
| |
841 #if 0 |
| |
842 gaim_signal_connect(gtk_conv_handle, "conversation-dragging", plugin, |
| |
843 GAIM_CALLBACK(conversation_dragging), NULL); |
| |
844 #endif |
| |
845 |
| |
846 while (convs) { |
| |
847 GaimConversation *conv = (GaimConversation *)convs->data; |
| |
848 |
| |
849 /* attach signals */ |
| |
850 attach_signals(conv); |
| |
851 |
| |
852 convs = convs->next; |
| |
853 } |
| |
854 |
| |
855 return TRUE; |
| |
856 } |
| |
857 |
| |
858 static gboolean |
| |
859 plugin_unload(GaimPlugin *plugin) |
| |
860 { |
| |
861 GList *convs = gaim_get_conversations(); |
| |
862 |
| |
863 while (convs) { |
| |
864 GaimConversation *conv = (GaimConversation *)convs->data; |
| |
865 |
| |
866 /* kill signals */ |
| |
867 detach_signals(conv); |
| |
868 |
| |
869 convs = convs->next; |
| |
870 } |
| |
871 |
| |
872 return TRUE; |
| |
873 } |
| |
874 |
| |
875 static GaimGtkPluginUiInfo ui_info = |
| |
876 { |
| |
877 get_config_frame, |
| |
878 0 /* page_num (Reserved) */ |
| |
879 }; |
| |
880 |
| |
881 static GaimPluginInfo info = |
| |
882 { |
| |
883 GAIM_PLUGIN_MAGIC, |
| |
884 GAIM_MAJOR_VERSION, |
| |
885 GAIM_MINOR_VERSION, |
| |
886 GAIM_PLUGIN_STANDARD, /**< type */ |
| |
887 GAIM_GTK_PLUGIN_TYPE, /**< ui_requirement */ |
| |
888 0, /**< flags */ |
| |
889 NULL, /**< dependencies */ |
| |
890 GAIM_PRIORITY_DEFAULT, /**< priority */ |
| |
891 |
| |
892 NOTIFY_PLUGIN_ID, /**< id */ |
| |
893 N_("Message Notification"), /**< name */ |
| |
894 VERSION, /**< version */ |
| |
895 /** summary */ |
| |
896 N_("Provides a variety of ways of notifying you of unread messages."), |
| |
897 /** description */ |
| |
898 N_("Provides a variety of ways of notifying you of unread messages."), |
| |
899 /**< author */ |
| |
900 "Etan Reisner <deryni@eden.rutgers.edu>\n\t\t\tBrian Tarricone <bjt23@cornell.edu>", |
| |
901 GAIM_WEBSITE, /**< homepage */ |
| |
902 |
| |
903 plugin_load, /**< load */ |
| |
904 plugin_unload, /**< unload */ |
| |
905 NULL, /**< destroy */ |
| |
906 |
| |
907 &ui_info, /**< ui_info */ |
| |
908 NULL, /**< extra_info */ |
| |
909 NULL, |
| |
910 NULL |
| |
911 }; |
| |
912 |
| |
913 static void |
| |
914 init_plugin(GaimPlugin *plugin) |
| |
915 { |
| |
916 gaim_prefs_add_none("/plugins/gtk"); |
| |
917 gaim_prefs_add_none("/plugins/gtk/X11"); |
| |
918 gaim_prefs_add_none("/plugins/gtk/X11/notify"); |
| |
919 |
| |
920 gaim_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE); |
| |
921 gaim_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE); |
| |
922 gaim_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE); |
| |
923 gaim_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE); |
| |
924 gaim_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE); |
| |
925 gaim_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)"); |
| |
926 gaim_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE); |
| |
927 gaim_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE); |
| |
928 gaim_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE); |
| |
929 gaim_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE); |
| |
930 gaim_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE); |
| |
931 gaim_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE); |
| |
932 gaim_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE); |
| |
933 gaim_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE); |
| |
934 gaim_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE); |
| |
935 } |
| |
936 |
| |
937 GAIM_INIT_PLUGIN(notify, init_plugin, info) |