comparison pidgin/plugins/notify.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children bb3bf2f88406
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
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)