Replace strcmp() with purple_strequal()
[pidgin-git.git] / pidgin / plugins / notify.c
blobcd8554c23a6c2ededac5591c1178c85cabb88ea4
1 /*
2 * Purple buddy notification plugin.
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)
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
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 purple 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.
42 * From Etan, 2002:
43 * -Added config dialog
44 * -Added control over notification method
45 * -Added control over when to release notification
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
57 * Thanks to Carles Pina i Estany <carles@pinux.info>
58 * for count of new messages option
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
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
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)
82 * Sean, 08 January, 2005:
83 * -Added Raise option, formally in Purple proper
86 #include "internal.h"
87 #include "pidgin.h"
88 #include "gtkprefs.h"
90 #include "conversation.h"
91 #include "prefs.h"
92 #include "signals.h"
93 #include "version.h"
94 #include "debug.h"
96 #include "gtkplugin.h"
97 #include "gtkutils.h"
99 #define NOTIFY_PLUGIN_ID "gtk-x11-notify"
101 static PurplePlugin *my_plugin = NULL;
102 #ifdef HAVE_X11
103 static GdkAtom _Cardinal = GDK_NONE;
104 static GdkAtom _PurpleUnseenCount = GDK_NONE;
105 #endif
107 /* notification set/unset */
108 static int notify(PurpleConversation *conv, gboolean increment);
109 static void notify_win(PidginWindow *purplewin, PurpleConversation *conv);
110 static void unnotify(PurpleConversation *conv, gboolean reset);
111 static int unnotify_cb(GtkWidget *widget, gpointer data,
112 PurpleConversation *conv);
114 /* gtk widget callbacks for prefs panel */
115 static void type_toggle_cb(GtkWidget *widget, gpointer data);
116 static void method_toggle_cb(GtkWidget *widget, gpointer data);
117 static void notify_toggle_cb(GtkWidget *widget, gpointer data);
118 static gboolean options_entry_cb(GtkWidget *widget, GdkEventFocus *event,
119 gpointer data);
120 static void apply_method(void);
121 static void apply_notify(void);
123 /* string function */
124 static void handle_string(PidginWindow *purplewin);
126 /* count_title function */
127 static void handle_count_title(PidginWindow *purplewin);
129 /* count_xprop function */
130 static void handle_count_xprop(PidginWindow *purplewin);
132 /* urgent function */
133 static void handle_urgent(PidginWindow *purplewin, gboolean set);
135 /* raise function */
136 static void handle_raise(PidginWindow *purplewin);
138 /* present function */
139 static void handle_present(PurpleConversation *conv);
141 /****************************************/
142 /* Begin doing stuff below this line... */
143 /****************************************/
144 static guint
145 count_messages(PidginWindow *purplewin)
147 guint count = 0;
148 GList *convs = NULL, *l;
150 for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) {
151 PidginConversation *conv = convs->data;
152 for (l = conv->convs; l != NULL; l = l->next) {
153 count += GPOINTER_TO_INT(purple_conversation_get_data(l->data, "notify-message-count"));
157 return count;
160 static int
161 notify(PurpleConversation *conv, gboolean increment)
163 gint count;
164 gboolean has_focus;
165 PidginWindow *purplewin = NULL;
167 if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
168 return 0;
170 /* We want to remove the notifications, but not reset the counter */
171 unnotify(conv, FALSE);
173 purplewin = PIDGIN_CONVERSATION(conv)->win;
175 /* If we aren't doing notifications for this type of conversation, return */
176 if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
177 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) ||
178 ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
179 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")))
180 return 0;
182 g_object_get(G_OBJECT(purplewin->window),
183 "has-toplevel-focus", &has_focus, NULL);
185 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
186 !has_focus) {
187 if (increment) {
188 count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count"));
189 count++;
190 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count));
193 notify_win(purplewin, conv);
196 return 0;
199 static void
200 notify_win(PidginWindow *purplewin, PurpleConversation *conv)
202 if (count_messages(purplewin) <= 0)
203 return;
205 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"))
206 handle_count_title(purplewin);
207 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"))
208 handle_count_xprop(purplewin);
209 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"))
210 handle_string(purplewin);
211 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"))
212 handle_urgent(purplewin, TRUE);
213 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"))
214 handle_raise(purplewin);
215 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"))
216 handle_present(conv);
219 static void
220 unnotify(PurpleConversation *conv, gboolean reset)
222 PurpleConversation *active_conv = NULL;
223 PidginWindow *purplewin = NULL;
225 g_return_if_fail(conv != NULL);
226 if (PIDGIN_CONVERSATION(conv) == NULL)
227 return;
229 purplewin = PIDGIN_CONVERSATION(conv)->win;
230 active_conv = pidgin_conv_window_get_active_conversation(purplewin);
232 /* reset the conversation window title */
233 purple_conversation_autoset_title(active_conv);
235 if (reset) {
236 /* Only need to actually remove the urgent hinting here, since
237 * removing it just to have it readded in re-notify is an
238 * unnecessary couple extra RTs to the server */
239 handle_urgent(purplewin, FALSE);
240 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
241 /* Same logic as for the urgent hint, xprops are also a RT.
242 * This needs to go here so that it gets the updated message
243 * count. */
244 handle_count_xprop(purplewin);
247 return;
250 static int
251 unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
253 if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
254 unnotify(conv, TRUE);
256 return 0;
259 static gboolean
260 message_displayed_cb(PurpleAccount *account, const char *who, char *message,
261 PurpleConversation *conv, PurpleMessageFlags flags)
263 PurpleConversationType ct = purple_conversation_get_type(conv);
265 /* Ignore anything that's not a received message or a system message */
266 if (!(flags & (PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM)))
267 return FALSE;
268 /* Don't highlight for delayed messages */
269 if ((flags & PURPLE_MESSAGE_RECV) && (flags & PURPLE_MESSAGE_DELAYED))
270 return FALSE;
271 /* Check whether to highlight for system message for either chat or IM */
272 if (flags & PURPLE_MESSAGE_SYSTEM) {
273 switch (ct) {
274 case PURPLE_CONV_TYPE_CHAT:
275 if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"))
276 return FALSE;
277 break;
278 case PURPLE_CONV_TYPE_IM:
279 if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"))
280 return FALSE;
281 break;
282 default:
283 /* System message not from chat or IM, ignore */
284 return FALSE;
287 /* If it's a chat, check if we should only highlight when nick is mentioned */
288 if (ct == PURPLE_CONV_TYPE_CHAT &&
289 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") &&
290 !(flags & PURPLE_MESSAGE_NICK))
291 return FALSE;
293 /* Nothing speaks against notifying, do so */
294 notify(conv, TRUE);
296 return FALSE;
299 static void
300 im_sent_im(PurpleAccount *account, const char *receiver, const char *message)
302 PurpleConversation *conv = NULL;
304 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
305 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver, account);
306 unnotify(conv, TRUE);
310 static void
311 chat_sent_im(PurpleAccount *account, const char *message, int id)
313 PurpleConversation *conv = NULL;
315 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
316 conv = purple_find_chat(purple_account_get_connection(account), id);
317 unnotify(conv, TRUE);
321 static int
322 attach_signals(PurpleConversation *conv)
324 PidginConversation *gtkconv = NULL;
325 GSList *imhtml_ids = NULL, *entry_ids = NULL;
326 guint id;
328 gtkconv = PIDGIN_CONVERSATION(conv);
329 if (!gtkconv) {
330 purple_debug_misc("notify", "Failed to find gtkconv\n");
331 return 0;
334 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) {
335 /* TODO should really find a way to make this work no matter
336 * where the focus is inside the conv window, without having
337 * to bind to focus-in-event on the g(d|t)kwindow */
338 /* try setting the signal on the focus-in-event for
339 * gtkwin->notebook->container? */
340 id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
341 G_CALLBACK(unnotify_cb), conv);
342 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
344 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
345 G_CALLBACK(unnotify_cb), conv);
346 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
349 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
350 /* TODO similarly should really find a way to allow for
351 * clicking in other places of the window */
352 id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event",
353 G_CALLBACK(unnotify_cb), conv);
354 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
356 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event",
357 G_CALLBACK(unnotify_cb), conv);
358 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
361 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
362 id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
363 G_CALLBACK(unnotify_cb), conv);
364 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
367 purple_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids);
368 purple_conversation_set_data(conv, "notify-entry-signals", entry_ids);
370 return 0;
373 static void
374 detach_signals(PurpleConversation *conv)
376 PidginConversation *gtkconv = NULL;
377 GSList *ids = NULL, *l;
379 gtkconv = PIDGIN_CONVERSATION(conv);
380 if (!gtkconv)
381 return;
383 ids = purple_conversation_get_data(conv, "notify-imhtml-signals");
384 for (l = ids; l != NULL; l = l->next)
385 g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data));
386 g_slist_free(ids);
388 ids = purple_conversation_get_data(conv, "notify-entry-signals");
389 for (l = ids; l != NULL; l = l->next)
390 g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data));
391 g_slist_free(ids);
393 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
395 purple_conversation_set_data(conv, "notify-imhtml-signals", NULL);
396 purple_conversation_set_data(conv, "notify-entry-signals", NULL);
399 static void
400 conv_created(PurpleConversation *conv)
402 purple_conversation_set_data(conv, "notify-message-count",
403 GINT_TO_POINTER(0));
405 /* always attach the signals, notify() will take care of conversation
406 * type checking */
407 attach_signals(conv);
410 static void
411 conv_switched(PurpleConversation *conv)
413 #if 0
414 PidginWindow *purplewin = purple_conversation_get_window(new_conv);
415 #endif
418 * If the conversation was switched, then make sure we re-notify
419 * because Purple will have overwritten our custom window title.
421 notify(conv, FALSE);
423 #if 0
424 printf("conv_switched - %p - %p\n", old_conv, new_conv);
425 printf("count - %d\n", count_messages(purplewin));
426 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"))
427 unnotify(new_conv, FALSE);
428 else {
429 /* if we don't have notification on the window then we don't want to
430 * re-notify it */
431 if (count_messages(purplewin))
432 notify_win(purplewin);
434 #endif
437 static void
438 deleting_conv(PurpleConversation *conv)
440 PidginWindow *purplewin = NULL;
441 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
443 if (gtkconv == NULL)
444 return;
446 detach_signals(conv);
448 purplewin = gtkconv->win;
450 handle_urgent(purplewin, FALSE);
451 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
453 return;
455 #if 0
456 /* i think this line crashes */
457 if (count_messages(purplewin))
458 notify_win(purplewin);
459 #endif
462 #if 0
463 static void
464 conversation_dragging(PurpleConversation *active_conv,
465 PidginWindow *old_purplewin,
466 PidginWindow *new_purplewin)
468 if (old_purplewin != new_purplewin) {
469 if (old_purplewin == NULL) {
471 purple_conversation_autoset_title(active_conv);
472 handle_urgent(new_purplewin, FALSE);
475 if (count_messages(new_purplewin))
476 notify_win(new_purplewin);
477 } else {
478 printf("if else count = %d\n", count_messages(new_purplewin));
479 printf("if else count = %d\n", count_messages(old_purplewin));
481 PurpleConversation *old_active_conv = NULL;
482 old_active_conv = purple_conv_window_get_active_conversation(new_purplewin);
484 purple_conversation_autoset_title(old_active_conv);
485 handle_urgent(old_purplewin, FALSE);
487 if (count_messages(old_purplewin))
488 notify_win(old_purplewin);
490 purple_conversation_autoset_title(active_conv);
491 handle_urgent(new_purplewin, FALSE);
493 if (count_messages(new_purplewin))
494 notify_win(new_purplewin);
497 } else {
498 printf("else count = %d\n", count_messages(new_purplewin));
499 printf("else count = %d\n", count_messages(old_purplewin));
501 purple_conversation_autoset_title(active_conv);
502 handle_urgent(old_purplewin, FALSE);
504 if (count_messages(old_purplewin))
505 notify_win(old_purplewin);
509 #endif
511 static void
512 handle_string(PidginWindow *purplewin)
514 GtkWindow *window = NULL;
515 gchar newtitle[256];
517 g_return_if_fail(purplewin != NULL);
519 window = GTK_WINDOW(purplewin->window);
520 g_return_if_fail(window != NULL);
522 g_snprintf(newtitle, sizeof(newtitle), "%s%s",
523 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"),
524 gtk_window_get_title(window));
525 gtk_window_set_title(window, newtitle);
528 static void
529 handle_count_title(PidginWindow *purplewin)
531 GtkWindow *window;
532 char newtitle[256];
534 g_return_if_fail(purplewin != NULL);
536 window = GTK_WINDOW(purplewin->window);
537 g_return_if_fail(window != NULL);
539 g_snprintf(newtitle, sizeof(newtitle), "[%d] %s",
540 count_messages(purplewin), gtk_window_get_title(window));
541 gtk_window_set_title(window, newtitle);
544 static void
545 handle_count_xprop(PidginWindow *purplewin)
547 #ifdef HAVE_X11
548 guint count;
549 GtkWidget *window;
550 GdkWindow *gdkwin;
552 window = purplewin->window;
553 g_return_if_fail(window != NULL);
555 if (_PurpleUnseenCount == GDK_NONE) {
556 _PurpleUnseenCount = gdk_atom_intern("_PIDGIN_UNSEEN_COUNT", FALSE);
559 if (_Cardinal == GDK_NONE) {
560 _Cardinal = gdk_atom_intern("CARDINAL", FALSE);
563 count = count_messages(purplewin);
564 #if GTK_CHECK_VERSION(2,14,0)
565 gdkwin = gtk_widget_get_window(window);
566 #else
567 gdkwin = window->window;
568 #endif
570 gdk_property_change(gdkwin, _PurpleUnseenCount, _Cardinal, 32,
571 GDK_PROP_MODE_REPLACE, (guchar *) &count, 1);
572 #endif
575 static void
576 handle_urgent(PidginWindow *purplewin, gboolean set)
578 g_return_if_fail(purplewin != NULL);
579 g_return_if_fail(purplewin->window != NULL);
581 pidgin_set_urgent(GTK_WINDOW(purplewin->window), set);
584 static void
585 handle_raise(PidginWindow *purplewin)
587 pidgin_conv_window_raise(purplewin);
590 static void
591 handle_present(PurpleConversation *conv)
593 if (pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)))
594 return;
596 purple_conversation_present(conv);
599 static void
600 type_toggle_cb(GtkWidget *widget, gpointer data)
602 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
603 gchar pref[256];
605 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
606 (char *)data);
608 purple_prefs_set_bool(pref, on);
611 static void
612 method_toggle_cb(GtkWidget *widget, gpointer data)
614 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
615 gchar pref[256];
617 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
618 (char *)data);
620 purple_prefs_set_bool(pref, on);
622 if (purple_strequal(data, "method_string")) {
623 GtkWidget *entry = g_object_get_data(G_OBJECT(widget), "title-entry");
624 gtk_widget_set_sensitive(entry, on);
626 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
627 gtk_entry_get_text(GTK_ENTRY(entry)));
630 apply_method();
633 static void
634 notify_toggle_cb(GtkWidget *widget, gpointer data)
636 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
637 gchar pref[256];
639 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
640 (char *)data);
642 purple_prefs_set_bool(pref, on);
644 apply_notify();
647 static gboolean
648 options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
650 if (data == NULL)
651 return FALSE;
653 if (purple_strequal(data, "method_string")) {
654 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
655 gtk_entry_get_text(GTK_ENTRY(widget)));
658 apply_method();
660 return FALSE;
663 static void
664 apply_method()
666 GList *convs;
668 for (convs = purple_get_conversations(); convs != NULL;
669 convs = convs->next) {
670 PurpleConversation *conv = (PurpleConversation *)convs->data;
672 /* remove notifications */
673 unnotify(conv, FALSE);
675 if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
676 /* reattach appropriate notifications */
677 notify(conv, FALSE);
681 static void
682 apply_notify()
684 GList *convs = purple_get_conversations();
686 while (convs) {
687 PurpleConversation *conv = (PurpleConversation *)convs->data;
689 /* detach signals */
690 detach_signals(conv);
691 /* reattach appropriate signals */
692 attach_signals(conv);
694 convs = convs->next;
698 static GtkWidget *
699 get_config_frame(PurplePlugin *plugin)
701 GtkWidget *ret = NULL, *frame = NULL;
702 GtkWidget *vbox = NULL, *hbox = NULL;
703 GtkWidget *toggle = NULL, *entry = NULL, *ref;
705 ret = gtk_vbox_new(FALSE, 18);
706 gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
708 /*---------- "Notify For" ----------*/
709 frame = pidgin_make_frame(ret, _("Notify For"));
710 vbox = gtk_vbox_new(FALSE, 5);
711 gtk_container_add(GTK_CONTAINER(frame), vbox);
713 toggle = gtk_check_button_new_with_mnemonic(_("_IM windows"));
714 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
716 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im"));
717 g_signal_connect(G_OBJECT(toggle), "toggled",
718 G_CALLBACK(type_toggle_cb), "type_im");
720 ref = toggle;
721 toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
722 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
723 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
724 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"));
725 g_signal_connect(G_OBJECT(toggle), "toggled",
726 G_CALLBACK(type_toggle_cb), "type_im_sys");
727 gtk_widget_set_sensitive(toggle, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref)));
728 g_signal_connect(G_OBJECT(ref), "toggled",
729 G_CALLBACK(pidgin_toggle_sensitive), toggle);
731 toggle = gtk_check_button_new_with_mnemonic(_("C_hat windows"));
732 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
733 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
734 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"));
735 g_signal_connect(G_OBJECT(toggle), "toggled",
736 G_CALLBACK(type_toggle_cb), "type_chat");
738 ref = toggle;
739 toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your username"));
740 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
741 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
742 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick"));
743 g_signal_connect(G_OBJECT(toggle), "toggled",
744 G_CALLBACK(type_toggle_cb), "type_chat_nick");
745 gtk_widget_set_sensitive(toggle, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref)));
746 g_signal_connect(G_OBJECT(ref), "toggled",
747 G_CALLBACK(pidgin_toggle_sensitive), toggle);
749 toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
750 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
751 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
752 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"));
753 g_signal_connect(G_OBJECT(toggle), "toggled",
754 G_CALLBACK(type_toggle_cb), "type_chat_sys");
755 gtk_widget_set_sensitive(toggle, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref)));
756 g_signal_connect(G_OBJECT(ref), "toggled",
757 G_CALLBACK(pidgin_toggle_sensitive), toggle);
759 toggle = gtk_check_button_new_with_mnemonic(_("_Focused windows"));
760 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
762 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused"));
763 g_signal_connect(G_OBJECT(toggle), "toggled",
764 G_CALLBACK(type_toggle_cb), "type_focused");
766 /*---------- "Notification Methods" ----------*/
767 frame = pidgin_make_frame(ret, _("Notification Methods"));
768 vbox = gtk_vbox_new(FALSE, 5);
769 gtk_container_add(GTK_CONTAINER(frame), vbox);
771 /* String method button */
772 hbox = gtk_hbox_new(FALSE, 18);
773 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
774 toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:"));
775 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
776 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
777 gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0);
779 entry = gtk_entry_new();
780 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
781 gtk_entry_set_max_length(GTK_ENTRY(entry), 10);
782 gtk_widget_set_sensitive(GTK_WIDGET(entry),
783 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
784 gtk_entry_set_text(GTK_ENTRY(entry),
785 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"));
786 g_object_set_data(G_OBJECT(toggle), "title-entry", entry);
787 g_signal_connect(G_OBJECT(toggle), "toggled",
788 G_CALLBACK(method_toggle_cb), "method_string");
789 g_signal_connect(G_OBJECT(entry), "focus-out-event",
790 G_CALLBACK(options_entry_cb), "method_string");
792 /* Count method button */
793 toggle = gtk_check_button_new_with_mnemonic(_("Insert c_ount of new messages into window title"));
794 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
795 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"));
796 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
797 g_signal_connect(G_OBJECT(toggle), "toggled",
798 G_CALLBACK(method_toggle_cb), "method_count");
800 #ifdef HAVE_X11
801 /* Count xprop method button */
802 toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property"));
803 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
804 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
805 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"));
806 g_signal_connect(G_OBJECT(toggle), "toggled",
807 G_CALLBACK(method_toggle_cb), "method_count_xprop");
809 /* Urgent method button */
810 toggle = gtk_check_button_new_with_mnemonic(_("Set window manager \"_URGENT\" hint"));
811 #else
812 toggle = gtk_check_button_new_with_mnemonic(_("_Flash window"));
813 #endif
814 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
815 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
816 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"));
817 g_signal_connect(G_OBJECT(toggle), "toggled",
818 G_CALLBACK(method_toggle_cb), "method_urgent");
820 /* Raise window method button */
821 toggle = gtk_check_button_new_with_mnemonic(_("R_aise conversation window"));
822 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
823 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
824 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"));
825 g_signal_connect(G_OBJECT(toggle), "toggled",
826 G_CALLBACK(method_toggle_cb), "method_raise");
828 /* Present conversation method button */
829 /* Translators: "Present" as used here is a verb. The plugin presents
830 * the window to the user. */
831 toggle = gtk_check_button_new_with_mnemonic(_("_Present conversation window"));
832 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
833 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
834 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"));
835 g_signal_connect(G_OBJECT(toggle), "toggled",
836 G_CALLBACK(method_toggle_cb), "method_present");
838 /*---------- "Notification Removals" ----------*/
839 frame = pidgin_make_frame(ret, _("Notification Removal"));
840 vbox = gtk_vbox_new(FALSE, 5);
841 gtk_container_add(GTK_CONTAINER(frame), vbox);
843 /* Remove on focus button */
844 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _gains focus"));
845 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
846 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
847 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus"));
848 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(notify_toggle_cb), "notify_focus");
850 /* Remove on click button */
851 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _receives click"));
852 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
854 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click"));
855 g_signal_connect(G_OBJECT(toggle), "toggled",
856 G_CALLBACK(notify_toggle_cb), "notify_click");
858 /* Remove on type button */
859 toggle = gtk_check_button_new_with_mnemonic(_("Remove when _typing in conversation window"));
860 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
862 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type"));
863 g_signal_connect(G_OBJECT(toggle), "toggled",
864 G_CALLBACK(notify_toggle_cb), "notify_type");
866 /* Remove on message send button */
867 toggle = gtk_check_button_new_with_mnemonic(_("Remove when a _message gets sent"));
868 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
869 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
870 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send"));
871 g_signal_connect(G_OBJECT(toggle), "toggled",
872 G_CALLBACK(notify_toggle_cb), "notify_send");
874 #if 0
875 /* Remove on conversation switch button */
876 toggle = gtk_check_button_new_with_mnemonic(_("Remove on switch to conversation ta_b"));
877 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
878 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
879 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"));
880 g_signal_connect(G_OBJECT(toggle), "toggled",
881 G_CALLBACK(notify_toggle_cb), "notify_switch");
882 #endif
884 gtk_widget_show_all(ret);
885 return ret;
888 static gboolean
889 plugin_load(PurplePlugin *plugin)
891 GList *convs = purple_get_conversations();
892 void *conv_handle = purple_conversations_get_handle();
893 void *gtk_conv_handle = pidgin_conversations_get_handle();
895 my_plugin = plugin;
897 purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
898 PURPLE_CALLBACK(message_displayed_cb), NULL);
899 purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
900 PURPLE_CALLBACK(message_displayed_cb), NULL);
901 purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin,
902 PURPLE_CALLBACK(conv_switched), NULL);
903 purple_signal_connect(conv_handle, "sent-im-msg", plugin,
904 PURPLE_CALLBACK(im_sent_im), NULL);
905 purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
906 PURPLE_CALLBACK(chat_sent_im), NULL);
907 purple_signal_connect(conv_handle, "conversation-created", plugin,
908 PURPLE_CALLBACK(conv_created), NULL);
909 purple_signal_connect(conv_handle, "deleting-conversation", plugin,
910 PURPLE_CALLBACK(deleting_conv), NULL);
911 #if 0
912 purple_signal_connect(gtk_conv_handle, "conversation-dragging", plugin,
913 PURPLE_CALLBACK(conversation_dragging), NULL);
914 #endif
916 while (convs) {
917 PurpleConversation *conv = (PurpleConversation *)convs->data;
919 /* attach signals */
920 attach_signals(conv);
922 convs = convs->next;
925 return TRUE;
928 static gboolean
929 plugin_unload(PurplePlugin *plugin)
931 GList *convs = purple_get_conversations();
933 while (convs) {
934 PurpleConversation *conv = (PurpleConversation *)convs->data;
936 /* kill signals */
937 detach_signals(conv);
939 convs = convs->next;
942 return TRUE;
945 static PidginPluginUiInfo ui_info =
947 get_config_frame,
948 0, /* page_num (Reserved) */
950 /* padding */
951 NULL,
952 NULL,
953 NULL,
954 NULL
957 static PurplePluginInfo info =
959 PURPLE_PLUGIN_MAGIC,
960 PURPLE_MAJOR_VERSION,
961 PURPLE_MINOR_VERSION,
962 PURPLE_PLUGIN_STANDARD, /**< type */
963 PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
964 0, /**< flags */
965 NULL, /**< dependencies */
966 PURPLE_PRIORITY_DEFAULT, /**< priority */
968 NOTIFY_PLUGIN_ID, /**< id */
969 N_("Message Notification"), /**< name */
970 DISPLAY_VERSION, /**< version */
971 /** summary */
972 N_("Provides a variety of ways of notifying you of unread messages."),
973 /** description */
974 N_("Provides a variety of ways of notifying you of unread messages."),
975 /**< author */
976 "Etan Reisner <deryni@eden.rutgers.edu>,\nBrian Tarricone <bjt23@cornell.edu>",
977 PURPLE_WEBSITE, /**< homepage */
979 plugin_load, /**< load */
980 plugin_unload, /**< unload */
981 NULL, /**< destroy */
983 &ui_info, /**< ui_info */
984 NULL, /**< extra_info */
985 NULL,
986 NULL,
988 /* padding */
989 NULL,
990 NULL,
991 NULL,
992 NULL
995 static void
996 init_plugin(PurplePlugin *plugin)
998 purple_prefs_add_none("/plugins/gtk");
999 purple_prefs_add_none("/plugins/gtk/X11");
1000 purple_prefs_add_none("/plugins/gtk/X11/notify");
1002 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE);
1003 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im_sys", FALSE);
1004 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE);
1005 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE);
1006 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_sys", FALSE);
1007 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE);
1008 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE);
1009 purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)");
1010 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE);
1011 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE);
1012 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE);
1013 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE);
1014 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE);
1015 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE);
1016 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE);
1017 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE);
1018 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE);
1019 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE);
1022 PURPLE_INIT_PLUGIN(notify, init_plugin, info)