Minor changelog updates
[pidgin-git.git] / pidgin / plugins / notify.c
blobc9f101f474c4da77aa1261ec627231cee3931caa
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 #ifndef _WIN32
100 #include <X11/Xatom.h>
101 #include <X11/Xlib.h>
102 #include <X11/Xutil.h>
103 #endif
105 #define NOTIFY_PLUGIN_ID "gtk-x11-notify"
107 static PurplePlugin *my_plugin = NULL;
108 #ifndef _WIN32
109 static GdkAtom _Cardinal = GDK_NONE;
110 static GdkAtom _PurpleUnseenCount = GDK_NONE;
111 #endif
113 /* notification set/unset */
114 static int notify(PurpleConversation *conv, gboolean increment);
115 static void notify_win(PidginWindow *purplewin);
116 static void unnotify(PurpleConversation *conv, gboolean reset);
117 static int unnotify_cb(GtkWidget *widget, gpointer data,
118 PurpleConversation *conv);
120 /* gtk widget callbacks for prefs panel */
121 static void type_toggle_cb(GtkWidget *widget, gpointer data);
122 static void method_toggle_cb(GtkWidget *widget, gpointer data);
123 static void notify_toggle_cb(GtkWidget *widget, gpointer data);
124 static gboolean options_entry_cb(GtkWidget *widget, GdkEventFocus *event,
125 gpointer data);
126 static void apply_method(void);
127 static void apply_notify(void);
129 /* string function */
130 static void handle_string(PidginWindow *purplewin);
132 /* count_title function */
133 static void handle_count_title(PidginWindow *purplewin);
135 /* count_xprop function */
136 static void handle_count_xprop(PidginWindow *purplewin);
138 /* urgent function */
139 static void handle_urgent(PidginWindow *purplewin, gboolean set);
141 /* raise function */
142 static void handle_raise(PidginWindow *purplewin);
144 /****************************************/
145 /* Begin doing stuff below this line... */
146 /****************************************/
147 static guint
148 count_messages(PidginWindow *purplewin)
150 guint count = 0;
151 GList *convs = NULL, *l;
153 for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) {
154 PidginConversation *conv = convs->data;
155 for (l = conv->convs; l != NULL; l = l->next) {
156 count += GPOINTER_TO_INT(purple_conversation_get_data(l->data, "notify-message-count"));
160 return count;
163 static int
164 notify(PurpleConversation *conv, gboolean increment)
166 gint count;
167 gboolean has_focus;
168 PidginWindow *purplewin = NULL;
170 if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
171 return 0;
173 /* We want to remove the notifications, but not reset the counter */
174 unnotify(conv, FALSE);
176 purplewin = PIDGIN_CONVERSATION(conv)->win;
178 /* If we aren't doing notifications for this type of conversation, return */
179 if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
180 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) ||
181 ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
182 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")))
183 return 0;
185 g_object_get(G_OBJECT(purplewin->window),
186 "has-toplevel-focus", &has_focus, NULL);
188 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
189 !has_focus) {
190 if (increment) {
191 count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count"));
192 count++;
193 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count));
196 notify_win(purplewin);
199 return 0;
202 static void
203 notify_win(PidginWindow *purplewin)
205 if (count_messages(purplewin) <= 0)
206 return;
208 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"))
209 handle_count_title(purplewin);
210 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"))
211 handle_count_xprop(purplewin);
212 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"))
213 handle_string(purplewin);
214 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"))
215 handle_urgent(purplewin, TRUE);
216 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"))
217 handle_raise(purplewin);
220 static void
221 unnotify(PurpleConversation *conv, gboolean reset)
223 PurpleConversation *active_conv = NULL;
224 PidginWindow *purplewin = NULL;
226 g_return_if_fail(conv != NULL);
227 if (PIDGIN_CONVERSATION(conv) == NULL)
228 return;
230 purplewin = PIDGIN_CONVERSATION(conv)->win;
231 active_conv = pidgin_conv_window_get_active_conversation(purplewin);
233 /* reset the conversation window title */
234 purple_conversation_autoset_title(active_conv);
236 if (reset) {
237 /* Only need to actually remove the urgent hinting here, since
238 * removing it just to have it readded in re-notify is an
239 * unnecessary couple extra RTs to the server */
240 handle_urgent(purplewin, FALSE);
241 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
242 /* Same logic as for the urgent hint, xprops are also a RT.
243 * This needs to go here so that it gets the updated message
244 * count. */
245 handle_count_xprop(purplewin);
248 return;
251 static int
252 unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
254 if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
255 unnotify(conv, TRUE);
257 return 0;
260 static gboolean
261 message_displayed_cb(PurpleAccount *account, const char *who, char *message,
262 PurpleConversation *conv, PurpleMessageFlags flags)
264 if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
265 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") &&
266 !(flags & PURPLE_MESSAGE_NICK)))
267 return FALSE;
269 if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED))
270 notify(conv, TRUE);
272 return FALSE;
275 static void
276 im_sent_im(PurpleAccount *account, const char *receiver, const char *message)
278 PurpleConversation *conv = NULL;
280 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
281 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver, account);
282 unnotify(conv, TRUE);
286 static void
287 chat_sent_im(PurpleAccount *account, const char *message, int id)
289 PurpleConversation *conv = NULL;
291 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
292 conv = purple_find_chat(purple_account_get_connection(account), id);
293 unnotify(conv, TRUE);
297 static int
298 attach_signals(PurpleConversation *conv)
300 PidginConversation *gtkconv = NULL;
301 PidginWindow *gtkwin = NULL;
302 GSList *imhtml_ids = NULL, *entry_ids = NULL;
303 guint id;
305 gtkconv = PIDGIN_CONVERSATION(conv);
306 if (!gtkconv) {
307 purple_debug_misc("notify", "Failed to find gtkconv\n");
308 return 0;
311 gtkwin = gtkconv->win;
313 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) {
314 /* TODO should really find a way to make this work no matter
315 * where the focus is inside the conv window, without having
316 * to bind to focus-in-event on the g(d|t)kwindow */
317 /* try setting the signal on the focus-in-event for
318 * gtkwin->notebook->container? */
319 id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
320 G_CALLBACK(unnotify_cb), conv);
321 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
323 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
324 G_CALLBACK(unnotify_cb), conv);
325 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
328 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
329 /* TODO similarly should really find a way to allow for
330 * clicking in other places of the window */
331 id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event",
332 G_CALLBACK(unnotify_cb), conv);
333 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
335 id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event",
336 G_CALLBACK(unnotify_cb), conv);
337 imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
340 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
341 id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
342 G_CALLBACK(unnotify_cb), conv);
343 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
346 purple_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids);
347 purple_conversation_set_data(conv, "notify-entry-signals", entry_ids);
349 return 0;
352 static void
353 detach_signals(PurpleConversation *conv)
355 PidginConversation *gtkconv = NULL;
356 PidginWindow *gtkwin = NULL;
357 GSList *ids = NULL, *l;
359 gtkconv = PIDGIN_CONVERSATION(conv);
360 if (!gtkconv)
361 return;
362 gtkwin = gtkconv->win;
364 ids = purple_conversation_get_data(conv, "notify-imhtml-signals");
365 for (l = ids; l != NULL; l = l->next)
366 g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data));
367 g_slist_free(ids);
369 ids = purple_conversation_get_data(conv, "notify-entry-signals");
370 for (l = ids; l != NULL; l = l->next)
371 g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data));
372 g_slist_free(ids);
374 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
376 purple_conversation_set_data(conv, "notify-imhtml-signals", NULL);
377 purple_conversation_set_data(conv, "notify-entry-signals", NULL);
380 static void
381 conv_created(PurpleConversation *conv)
383 purple_conversation_set_data(conv, "notify-message-count",
384 GINT_TO_POINTER(0));
386 /* always attach the signals, notify() will take care of conversation
387 * type checking */
388 attach_signals(conv);
391 static void
392 conv_switched(PurpleConversation *conv)
394 #if 0
395 PidginWindow *purplewin = purple_conversation_get_window(new_conv);
396 #endif
399 * If the conversation was switched, then make sure we re-notify
400 * because Purple will have overwritten our custom window title.
402 notify(conv, FALSE);
404 #if 0
405 printf("conv_switched - %p - %p\n", old_conv, new_conv);
406 printf("count - %d\n", count_messages(purplewin));
407 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"))
408 unnotify(new_conv, FALSE);
409 else {
410 /* if we don't have notification on the window then we don't want to
411 * re-notify it */
412 if (count_messages(purplewin))
413 notify_win(purplewin);
415 #endif
418 static void
419 deleting_conv(PurpleConversation *conv)
421 PidginWindow *purplewin = NULL;
422 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
424 if (gtkconv == NULL)
425 return;
427 detach_signals(conv);
429 purplewin = gtkconv->win;
431 handle_urgent(purplewin, FALSE);
432 purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
434 return;
436 #if 0
437 /* i think this line crashes */
438 if (count_messages(purplewin))
439 notify_win(purplewin);
440 #endif
443 #if 0
444 static void
445 conversation_dragging(PurpleConversation *active_conv,
446 PidginWindow *old_purplewin,
447 PidginWindow *new_purplewin)
449 if (old_purplewin != new_purplewin) {
450 if (old_purplewin == NULL) {
452 purple_conversation_autoset_title(active_conv);
453 handle_urgent(new_purplewin, FALSE);
456 if (count_messages(new_purplewin))
457 notify_win(new_purplewin);
458 } else {
459 printf("if else count = %d\n", count_messages(new_purplewin));
460 printf("if else count = %d\n", count_messages(old_purplewin));
462 PurpleConversation *old_active_conv = NULL;
463 old_active_conv = purple_conv_window_get_active_conversation(new_purplewin);
465 purple_conversation_autoset_title(old_active_conv);
466 handle_urgent(old_purplewin, FALSE);
468 if (count_messages(old_purplewin))
469 notify_win(old_purplewin);
471 purple_conversation_autoset_title(active_conv);
472 handle_urgent(new_purplewin, FALSE);
474 if (count_messages(new_purplewin))
475 notify_win(new_purplewin);
478 } else {
479 printf("else count = %d\n", count_messages(new_purplewin));
480 printf("else count = %d\n", count_messages(old_purplewin));
482 purple_conversation_autoset_title(active_conv);
483 handle_urgent(old_purplewin, FALSE);
485 if (count_messages(old_purplewin))
486 notify_win(old_purplewin);
490 #endif
492 static void
493 handle_string(PidginWindow *purplewin)
495 GtkWindow *window = NULL;
496 gchar newtitle[256];
498 g_return_if_fail(purplewin != NULL);
500 window = GTK_WINDOW(purplewin->window);
501 g_return_if_fail(window != NULL);
503 g_snprintf(newtitle, sizeof(newtitle), "%s%s",
504 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"),
505 gtk_window_get_title(window));
506 gtk_window_set_title(window, newtitle);
509 static void
510 handle_count_title(PidginWindow *purplewin)
512 GtkWindow *window;
513 char newtitle[256];
515 g_return_if_fail(purplewin != NULL);
517 window = GTK_WINDOW(purplewin->window);
518 g_return_if_fail(window != NULL);
520 g_snprintf(newtitle, sizeof(newtitle), "[%d] %s",
521 count_messages(purplewin), gtk_window_get_title(window));
522 gtk_window_set_title(window, newtitle);
525 static void
526 handle_count_xprop(PidginWindow *purplewin)
528 #ifndef _WIN32
529 guint count;
530 GtkWidget *window;
531 GdkWindow *gdkwin;
533 window = purplewin->window;
534 g_return_if_fail(window != NULL);
536 if (_PurpleUnseenCount == GDK_NONE) {
537 _PurpleUnseenCount = gdk_atom_intern("_PIDGIN_UNSEEN_COUNT", FALSE);
540 if (_Cardinal == GDK_NONE) {
541 _Cardinal = gdk_atom_intern("CARDINAL", FALSE);
544 count = count_messages(purplewin);
545 gdkwin = window->window;
547 gdk_property_change(gdkwin, _PurpleUnseenCount, _Cardinal, 32,
548 GDK_PROP_MODE_REPLACE, (guchar *) &count, 1);
549 #endif
552 static void
553 handle_urgent(PidginWindow *win, gboolean set)
555 #ifndef _WIN32
556 pidgin_set_urgent(GTK_WINDOW(win->window), set);
557 #endif
560 static void
561 handle_raise(PidginWindow *purplewin)
563 pidgin_conv_window_raise(purplewin);
566 static void
567 type_toggle_cb(GtkWidget *widget, gpointer data)
569 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
570 gchar pref[256];
572 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
573 (char *)data);
575 purple_prefs_set_bool(pref, on);
578 static void
579 method_toggle_cb(GtkWidget *widget, gpointer data)
581 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
582 gchar pref[256];
584 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
585 (char *)data);
587 purple_prefs_set_bool(pref, on);
589 if (!strcmp(data, "method_string")) {
590 GtkWidget *entry = g_object_get_data(G_OBJECT(widget), "title-entry");
591 gtk_widget_set_sensitive(entry, on);
593 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
594 gtk_entry_get_text(GTK_ENTRY(entry)));
597 apply_method();
600 static void
601 notify_toggle_cb(GtkWidget *widget, gpointer data)
603 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
604 gchar pref[256];
606 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
607 (char *)data);
609 purple_prefs_set_bool(pref, on);
611 apply_notify();
614 static gboolean
615 options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
617 if (data == NULL)
618 return FALSE;
620 if (!strcmp(data, "method_string")) {
621 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
622 gtk_entry_get_text(GTK_ENTRY(widget)));
625 apply_method();
627 return FALSE;
630 static void
631 apply_method()
633 GList *convs;
634 PidginWindow *purplewin = NULL;
636 for (convs = purple_get_conversations(); convs != NULL;
637 convs = convs->next) {
638 PurpleConversation *conv = (PurpleConversation *)convs->data;
640 /* remove notifications */
641 unnotify(conv, FALSE);
643 purplewin = PIDGIN_CONVERSATION(conv)->win;
644 if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")) != 0)
645 /* reattach appropriate notifications */
646 notify(conv, FALSE);
650 static void
651 apply_notify()
653 GList *convs = purple_get_conversations();
655 while (convs) {
656 PurpleConversation *conv = (PurpleConversation *)convs->data;
658 /* detach signals */
659 detach_signals(conv);
660 /* reattach appropriate signals */
661 attach_signals(conv);
663 convs = convs->next;
667 static GtkWidget *
668 get_config_frame(PurplePlugin *plugin)
670 GtkWidget *ret = NULL, *frame = NULL;
671 GtkWidget *vbox = NULL, *hbox = NULL;
672 GtkWidget *toggle = NULL, *entry = NULL, *ref;
674 ret = gtk_vbox_new(FALSE, 18);
675 gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
677 /*---------- "Notify For" ----------*/
678 frame = pidgin_make_frame(ret, _("Notify For"));
679 vbox = gtk_vbox_new(FALSE, 5);
680 gtk_container_add(GTK_CONTAINER(frame), vbox);
682 toggle = gtk_check_button_new_with_mnemonic(_("_IM windows"));
683 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
684 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
685 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im"));
686 g_signal_connect(G_OBJECT(toggle), "toggled",
687 G_CALLBACK(type_toggle_cb), "type_im");
689 toggle = gtk_check_button_new_with_mnemonic(_("C_hat windows"));
690 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
691 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
692 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"));
693 g_signal_connect(G_OBJECT(toggle), "toggled",
694 G_CALLBACK(type_toggle_cb), "type_chat");
696 ref = toggle;
697 toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your screen name"));
698 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
699 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
700 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick"));
701 g_signal_connect(G_OBJECT(toggle), "toggled",
702 G_CALLBACK(type_toggle_cb), "type_chat_nick");
703 gtk_widget_set_sensitive(toggle, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref)));
704 g_signal_connect(G_OBJECT(ref), "toggled",
705 G_CALLBACK(pidgin_toggle_sensitive), toggle);
707 toggle = gtk_check_button_new_with_mnemonic(_("_Focused windows"));
708 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
709 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
710 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused"));
711 g_signal_connect(G_OBJECT(toggle), "toggled",
712 G_CALLBACK(type_toggle_cb), "type_focused");
714 /*---------- "Notification Methods" ----------*/
715 frame = pidgin_make_frame(ret, _("Notification Methods"));
716 vbox = gtk_vbox_new(FALSE, 5);
717 gtk_container_add(GTK_CONTAINER(frame), vbox);
719 /* String method button */
720 hbox = gtk_hbox_new(FALSE, 18);
721 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
722 toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:"));
723 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
724 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
725 gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0);
727 entry = gtk_entry_new();
728 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
729 gtk_entry_set_max_length(GTK_ENTRY(entry), 10);
730 gtk_widget_set_sensitive(GTK_WIDGET(entry),
731 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
732 gtk_entry_set_text(GTK_ENTRY(entry),
733 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"));
734 g_object_set_data(G_OBJECT(toggle), "title-entry", entry);
735 g_signal_connect(G_OBJECT(toggle), "toggled",
736 G_CALLBACK(method_toggle_cb), "method_string");
737 g_signal_connect(G_OBJECT(entry), "focus-out-event",
738 G_CALLBACK(options_entry_cb), "method_string");
740 /* Count method button */
741 toggle = gtk_check_button_new_with_mnemonic(_("Insert c_ount of new messages into window title"));
742 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
743 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"));
744 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
745 g_signal_connect(G_OBJECT(toggle), "toggled",
746 G_CALLBACK(method_toggle_cb), "method_count");
748 #ifndef _WIN32
749 /* Count xprop method button */
750 toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property"));
751 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
752 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
753 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"));
754 g_signal_connect(G_OBJECT(toggle), "toggled",
755 G_CALLBACK(method_toggle_cb), "method_count_xprop");
757 /* Urgent method button */
758 toggle = gtk_check_button_new_with_mnemonic(_("Set window manager \"_URGENT\" hint"));
759 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
760 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
761 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"));
762 g_signal_connect(G_OBJECT(toggle), "toggled",
763 G_CALLBACK(method_toggle_cb), "method_urgent");
764 #endif
766 /* Raise window method button */
767 toggle = gtk_check_button_new_with_mnemonic(_("R_aise conversation window"));
768 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
769 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
770 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"));
771 g_signal_connect(G_OBJECT(toggle), "toggled",
772 G_CALLBACK(method_toggle_cb), "method_raise");
774 /*---------- "Notification Removals" ----------*/
775 frame = pidgin_make_frame(ret, _("Notification Removal"));
776 vbox = gtk_vbox_new(FALSE, 5);
777 gtk_container_add(GTK_CONTAINER(frame), vbox);
779 /* Remove on focus button */
780 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _gains focus"));
781 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
782 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
783 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus"));
784 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(notify_toggle_cb), "notify_focus");
786 /* Remove on click button */
787 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _receives click"));
788 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
789 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
790 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click"));
791 g_signal_connect(G_OBJECT(toggle), "toggled",
792 G_CALLBACK(notify_toggle_cb), "notify_click");
794 /* Remove on type button */
795 toggle = gtk_check_button_new_with_mnemonic(_("Remove when _typing in conversation window"));
796 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
797 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
798 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type"));
799 g_signal_connect(G_OBJECT(toggle), "toggled",
800 G_CALLBACK(notify_toggle_cb), "notify_type");
802 /* Remove on message send button */
803 toggle = gtk_check_button_new_with_mnemonic(_("Remove when a _message gets sent"));
804 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
805 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
806 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send"));
807 g_signal_connect(G_OBJECT(toggle), "toggled",
808 G_CALLBACK(notify_toggle_cb), "notify_send");
810 #if 0
811 /* Remove on conversation switch button */
812 toggle = gtk_check_button_new_with_mnemonic(_("Remove on switch to conversation ta_b"));
813 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
814 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
815 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"));
816 g_signal_connect(G_OBJECT(toggle), "toggled",
817 G_CALLBACK(notify_toggle_cb), "notify_switch");
818 #endif
820 gtk_widget_show_all(ret);
821 return ret;
824 static gboolean
825 plugin_load(PurplePlugin *plugin)
827 GList *convs = purple_get_conversations();
828 void *conv_handle = purple_conversations_get_handle();
829 void *gtk_conv_handle = pidgin_conversations_get_handle();
831 my_plugin = plugin;
833 purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
834 PURPLE_CALLBACK(message_displayed_cb), NULL);
835 purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
836 PURPLE_CALLBACK(message_displayed_cb), NULL);
837 purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin,
838 PURPLE_CALLBACK(conv_switched), NULL);
839 purple_signal_connect(conv_handle, "sent-im-msg", plugin,
840 PURPLE_CALLBACK(im_sent_im), NULL);
841 purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
842 PURPLE_CALLBACK(chat_sent_im), NULL);
843 purple_signal_connect(conv_handle, "conversation-created", plugin,
844 PURPLE_CALLBACK(conv_created), NULL);
845 purple_signal_connect(conv_handle, "chat-joined", plugin,
846 PURPLE_CALLBACK(conv_created), NULL);
847 purple_signal_connect(conv_handle, "deleting-conversation", plugin,
848 PURPLE_CALLBACK(deleting_conv), NULL);
849 #if 0
850 purple_signal_connect(gtk_conv_handle, "conversation-dragging", plugin,
851 PURPLE_CALLBACK(conversation_dragging), NULL);
852 #endif
854 while (convs) {
855 PurpleConversation *conv = (PurpleConversation *)convs->data;
857 /* attach signals */
858 attach_signals(conv);
860 convs = convs->next;
863 return TRUE;
866 static gboolean
867 plugin_unload(PurplePlugin *plugin)
869 GList *convs = purple_get_conversations();
871 while (convs) {
872 PurpleConversation *conv = (PurpleConversation *)convs->data;
874 /* kill signals */
875 detach_signals(conv);
877 convs = convs->next;
880 return TRUE;
883 static PidginPluginUiInfo ui_info =
885 get_config_frame,
886 0, /* page_num (Reserved) */
888 /* padding */
889 NULL,
890 NULL,
891 NULL,
892 NULL
895 static PurplePluginInfo info =
897 PURPLE_PLUGIN_MAGIC,
898 PURPLE_MAJOR_VERSION,
899 PURPLE_MINOR_VERSION,
900 PURPLE_PLUGIN_STANDARD, /**< type */
901 PIDGIN_PLUGIN_TYPE, /**< ui_requirement */
902 0, /**< flags */
903 NULL, /**< dependencies */
904 PURPLE_PRIORITY_DEFAULT, /**< priority */
906 NOTIFY_PLUGIN_ID, /**< id */
907 N_("Message Notification"), /**< name */
908 VERSION, /**< version */
909 /** summary */
910 N_("Provides a variety of ways of notifying you of unread messages."),
911 /** description */
912 N_("Provides a variety of ways of notifying you of unread messages."),
913 /**< author */
914 "Etan Reisner <deryni@eden.rutgers.edu>\n\t\t\tBrian Tarricone <bjt23@cornell.edu>",
915 PURPLE_WEBSITE, /**< homepage */
917 plugin_load, /**< load */
918 plugin_unload, /**< unload */
919 NULL, /**< destroy */
921 &ui_info, /**< ui_info */
922 NULL, /**< extra_info */
923 NULL,
924 NULL,
926 /* padding */
927 NULL,
928 NULL,
929 NULL,
930 NULL
933 static void
934 init_plugin(PurplePlugin *plugin)
936 purple_prefs_add_none("/plugins/gtk");
937 purple_prefs_add_none("/plugins/gtk/X11");
938 purple_prefs_add_none("/plugins/gtk/X11/notify");
940 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE);
941 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE);
942 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE);
943 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE);
944 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE);
945 purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)");
946 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE);
947 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE);
948 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE);
949 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE);
950 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE);
951 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE);
952 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE);
953 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE);
954 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE);
957 PURPLE_INIT_PLUGIN(notify, init_plugin, info)