pidgin/plugins/notify/notify.c

changeset 41665
149e5ba1e807
parent 41531
2b86501d13fe
child 41696
35f8ce475b21
equal deleted inserted replaced
41664:a9b4409be267 41665:149e5ba1e807
1 /*
2 * Purple 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 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 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.
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 Purple proper
84 */
85
86 #include <glib/gi18n-lib.h>
87
88 #include <purple.h>
89
90 #include <pidgin.h>
91
92 #define NOTIFY_PLUGIN_ID "gtk-x11-notify"
93
94 static PurplePlugin *my_plugin = NULL;
95 #ifdef HAVE_X11
96 static GdkAtom _Cardinal = GDK_NONE;
97 static GdkAtom _PurpleUnseenCount = GDK_NONE;
98 #endif
99
100 /* notification set/unset */
101 static int notify(PurpleConversation *conv, gboolean increment);
102 static void notify_win(PidginConvWindow *purplewin, PurpleConversation *conv);
103 static void unnotify(PurpleConversation *conv, gboolean reset);
104 static int unnotify_cb(GtkWidget *widget, gpointer data,
105 PurpleConversation *conv);
106
107 /* gtk widget callbacks for prefs panel */
108 static void type_toggle_cb(GtkWidget *widget, gpointer data);
109 static void method_toggle_cb(GtkWidget *widget, gpointer data);
110 static void notify_toggle_cb(GtkWidget *widget, gpointer data);
111 static gboolean options_entry_cb(GtkWidget *widget, GdkEventFocus *event,
112 gpointer data);
113 static void apply_method(void);
114 static void apply_notify(void);
115
116 /* string function */
117 static void handle_string(PidginConvWindow *purplewin);
118
119 /* count_title function */
120 static void handle_count_title(PidginConvWindow *purplewin);
121
122 /* count_xprop function */
123 static void handle_count_xprop(PidginConvWindow *purplewin);
124
125 /* urgent function */
126 static void handle_urgent(PidginConvWindow *purplewin, gboolean set);
127
128 /* present function */
129 static void handle_present(PurpleConversation *conv);
130
131 /****************************************/
132 /* Begin doing stuff below this line... */
133 /****************************************/
134 static guint
135 count_messages(PidginConvWindow *purplewin)
136 {
137 guint count = 0;
138 GList *convs = NULL, *l;
139
140 for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) {
141 PidginConversation *conv = convs->data;
142 for (l = conv->convs; l != NULL; l = l->next) {
143 count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "notify-message-count"));
144 }
145 }
146
147 return count;
148 }
149
150 static int
151 notify(PurpleConversation *conv, gboolean increment)
152 {
153 gint count;
154 gboolean has_focus;
155 PidginConvWindow *purplewin = NULL;
156
157 if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
158 return 0;
159
160 /* We want to remove the notifications, but not reset the counter */
161 unnotify(conv, FALSE);
162
163 purplewin = PIDGIN_CONVERSATION(conv)->win;
164
165 /* If we aren't doing notifications for this type of conversation, return */
166 if ((PURPLE_IS_IM_CONVERSATION(conv) &&
167 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) ||
168 (PURPLE_IS_CHAT_CONVERSATION(conv) &&
169 !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")))
170 return 0;
171
172 g_object_get(G_OBJECT(purplewin->window),
173 "has-toplevel-focus", &has_focus, NULL);
174
175 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
176 !has_focus) {
177 if (increment) {
178 count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count"));
179 count++;
180 g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(count));
181 }
182
183 notify_win(purplewin, conv);
184 }
185
186 return 0;
187 }
188
189 static void
190 notify_win(PidginConvWindow *purplewin, PurpleConversation *conv)
191 {
192 if (count_messages(purplewin) <= 0)
193 return;
194
195 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"))
196 handle_count_title(purplewin);
197 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"))
198 handle_count_xprop(purplewin);
199 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"))
200 handle_string(purplewin);
201 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"))
202 handle_urgent(purplewin, TRUE);
203 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"))
204 handle_present(conv);
205 }
206
207 static void
208 unnotify(PurpleConversation *conv, gboolean reset)
209 {
210 PurpleConversation *active_conv = NULL;
211 PidginConversationWindow *purplewin = NULL;
212 GtkWidget *win;
213
214 g_return_if_fail(conv != NULL);
215 if (PIDGIN_CONVERSATION(conv) == NULL)
216 return;
217
218 win = gtk_widget_get_toplevel(PIDGIN_CONVERSATION(conv)->tab_cont);
219 purplewin = PIDGIN_CONVERSATION_WINDOW(win);
220
221 activate_conv = pidgin_conversation_window_get_selected(purplewin);
222
223 /* reset the conversation window title */
224 purple_conversation_autoset_title(active_conv);
225
226 if (reset) {
227 /* Only need to actually remove the urgent hinting here, since
228 * removing it just to have it re-added in re-notify is an
229 * unnecessary couple extra RTs to the server */
230 handle_urgent(purplewin, FALSE);
231 g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
232 /* Same logic as for the urgent hint, xprops are also a RT.
233 * This needs to go here so that it gets the updated message
234 * count. */
235 handle_count_xprop(purplewin);
236 }
237
238 return;
239 }
240
241 static int
242 unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
243 {
244 if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
245 unnotify(conv, TRUE);
246
247 return 0;
248 }
249
250 static gboolean
251 message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
252 {
253 PurpleMessageFlags flags = purple_message_get_flags(msg);
254
255 /* Ignore anything that's not a received message or a system message */
256 if (!(flags & (PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM)))
257 return FALSE;
258 /* Don't highlight for delayed messages */
259 if ((flags & PURPLE_MESSAGE_RECV) && (flags & PURPLE_MESSAGE_DELAYED))
260 return FALSE;
261 /* Check whether to highlight for system message for either chat or IM */
262 if (flags & PURPLE_MESSAGE_SYSTEM) {
263 if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
264 if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"))
265 return FALSE;
266 } else if (PURPLE_IS_IM_CONVERSATION(conv)) {
267 if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"))
268 return FALSE;
269 } else {
270 /* System message not from chat or IM, ignore */
271 return FALSE;
272 }
273 }
274
275 /* If it's a chat, check if we should only highlight when nick is mentioned */
276 if ((PURPLE_IS_CHAT_CONVERSATION(conv) &&
277 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") &&
278 !(flags & PURPLE_MESSAGE_NICK)))
279 return FALSE;
280
281 /* Nothing speaks against notifying, do so */
282 notify(conv, TRUE);
283
284 return FALSE;
285 }
286
287 static void
288 im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
289 {
290 PurpleIMConversation *im = NULL;
291
292 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
293 im = purple_conversations_find_im_with_account(
294 purple_message_get_recipient(msg), account);
295 unnotify(PURPLE_CONVERSATION(im), TRUE);
296 }
297 }
298
299 static void
300 chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id)
301 {
302 PurpleChatConversation *chat = NULL;
303
304 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
305 chat = purple_conversations_find_chat(purple_account_get_connection(account), id);
306 unnotify(PURPLE_CONVERSATION(chat), TRUE);
307 }
308 }
309
310 static int
311 attach_signals(PurpleConversation *conv)
312 {
313 PidginConversation *gtkconv = NULL;
314 GSList *webview_ids = NULL, *entry_ids = NULL;
315 guint id;
316
317 gtkconv = PIDGIN_CONVERSATION(conv);
318 if (!gtkconv) {
319 purple_debug_misc("notify", "Failed to find gtkconv\n");
320 return 0;
321 }
322
323 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) {
324 /* TODO should really find a way to make this work no matter
325 * where the focus is inside the conv window, without having
326 * to bind to focus-in-event on the g(d|t)kwindow */
327 /* try setting the signal on the focus-in-event for
328 * gtkwin->notebook->container? */
329 id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
330 G_CALLBACK(unnotify_cb), conv);
331 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
332
333 id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event",
334 G_CALLBACK(unnotify_cb), conv);
335 webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
336 }
337
338 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
339 /* TODO similarly should really find a way to allow for
340 * clicking in other places of the window */
341 id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event",
342 G_CALLBACK(unnotify_cb), conv);
343 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
344
345 id = g_signal_connect(G_OBJECT(gtkconv->webview), "button-press-event",
346 G_CALLBACK(unnotify_cb), conv);
347 webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
348 }
349
350 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
351 id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
352 G_CALLBACK(unnotify_cb), conv);
353 entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
354 }
355
356 g_object_set_data(G_OBJECT(conv), "notify-webview-signals", webview_ids);
357 g_object_set_data(G_OBJECT(conv), "notify-entry-signals", entry_ids);
358
359 return 0;
360 }
361
362 static void
363 detach_signals(PurpleConversation *conv)
364 {
365 PidginConversation *gtkconv = NULL;
366 GSList *ids = NULL, *l;
367
368 gtkconv = PIDGIN_CONVERSATION(conv);
369 if (!gtkconv)
370 return;
371
372 ids = g_object_get_data(G_OBJECT(conv), "notify-webview-signals");
373 for (l = ids; l != NULL; l = l->next)
374 g_signal_handler_disconnect(gtkconv->webview, GPOINTER_TO_INT(l->data));
375 g_slist_free(ids);
376
377 ids = g_object_get_data(G_OBJECT(conv), "notify-entry-signals");
378 for (l = ids; l != NULL; l = l->next)
379 g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data));
380 g_slist_free(ids);
381
382 g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
383
384 g_object_set_data(G_OBJECT(conv), "notify-webview-signals", NULL);
385 g_object_set_data(G_OBJECT(conv), "notify-entry-signals", NULL);
386 }
387
388 static void
389 conv_created(PurpleConversation *conv)
390 {
391 g_object_set_data(G_OBJECT(conv), "notify-message-count",
392 GINT_TO_POINTER(0));
393
394 /* always attach the signals, notify() will take care of conversation
395 * type checking */
396 attach_signals(conv);
397 }
398
399 static void
400 conv_switched(PurpleConversation *conv)
401 {
402 #if 0
403 PidginConvWindow *purplewin = purple_conversation_get_window(new_conv);
404 #endif
405
406 /*
407 * If the conversation was switched, then make sure we re-notify
408 * because Purple will have overwritten our custom window title.
409 */
410 notify(conv, FALSE);
411
412 #if 0
413 printf("conv_switched - %p - %p\n", old_conv, new_conv);
414 printf("count - %d\n", count_messages(purplewin));
415 if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"))
416 unnotify(new_conv, FALSE);
417 else {
418 /* if we don't have notification on the window then we don't want to
419 * re-notify it */
420 if (count_messages(purplewin))
421 notify_win(purplewin);
422 }
423 #endif
424 }
425
426 static void
427 deleting_conv(PurpleConversation *conv)
428 {
429 PidginConvWindow *purplewin = NULL;
430 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
431
432 if (gtkconv == NULL)
433 return;
434
435 detach_signals(conv);
436
437 purplewin = gtkconv->win;
438
439 handle_urgent(purplewin, FALSE);
440 g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
441
442 return;
443
444 #if 0
445 /* i think this line crashes */
446 if (count_messages(purplewin))
447 notify_win(purplewin);
448 #endif
449 }
450
451 static void
452 handle_string(PidginConvWindow *purplewin)
453 {
454 GtkWindow *window = NULL;
455 gchar newtitle[256];
456
457 g_return_if_fail(purplewin != NULL);
458
459 window = GTK_WINDOW(purplewin->window);
460 g_return_if_fail(window != NULL);
461
462 g_snprintf(newtitle, sizeof(newtitle), "%s%s",
463 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"),
464 gtk_window_get_title(window));
465 gtk_window_set_title(window, newtitle);
466 }
467
468 static void
469 handle_count_title(PidginConvWindow *purplewin)
470 {
471 GtkWindow *window;
472 char newtitle[256];
473
474 g_return_if_fail(purplewin != NULL);
475
476 window = GTK_WINDOW(purplewin->window);
477 g_return_if_fail(window != NULL);
478
479 g_snprintf(newtitle, sizeof(newtitle), "[%d] %s",
480 count_messages(purplewin), gtk_window_get_title(window));
481 gtk_window_set_title(window, newtitle);
482 }
483
484 static void
485 handle_count_xprop(PidginConvWindow *purplewin)
486 {
487 #ifdef HAVE_X11
488 guint count;
489 GtkWidget *window;
490 GdkWindow *gdkwin;
491
492 window = purplewin->window;
493 g_return_if_fail(window != NULL);
494
495 if (_PurpleUnseenCount == GDK_NONE) {
496 _PurpleUnseenCount = gdk_atom_intern("_PIDGIN_UNSEEN_COUNT", FALSE);
497 }
498
499 if (_Cardinal == GDK_NONE) {
500 _Cardinal = gdk_atom_intern("CARDINAL", FALSE);
501 }
502
503 count = count_messages(purplewin);
504 gdkwin = gtk_widget_get_window(window);
505
506 gdk_property_change(gdkwin, _PurpleUnseenCount, _Cardinal, 32,
507 GDK_PROP_MODE_REPLACE, (guchar *) &count, 1);
508 #endif
509 }
510
511 static void
512 handle_urgent(PidginConvWindow *purplewin, gboolean set)
513 {
514 g_return_if_fail(purplewin != NULL);
515 g_return_if_fail(purplewin->window != NULL);
516
517 gtk_window_set_urgency_hint(GTK_WINDOW(purplewin->window), set);
518 }
519
520 static void
521 handle_present(PurpleConversation *conv)
522 {
523 if (pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)))
524 return;
525
526 purple_conversation_present(conv);
527 }
528
529 static void
530 type_toggle_cb(GtkWidget *widget, gpointer data)
531 {
532 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
533 gchar pref[256];
534
535 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
536 (char *)data);
537
538 purple_prefs_set_bool(pref, on);
539 }
540
541 static void
542 method_toggle_cb(GtkWidget *widget, gpointer data)
543 {
544 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
545 gchar pref[256];
546
547 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
548 (char *)data);
549
550 purple_prefs_set_bool(pref, on);
551
552 if (purple_strequal(data, "method_string")) {
553 GtkWidget *entry = g_object_get_data(G_OBJECT(widget), "title-entry");
554 gtk_widget_set_sensitive(entry, on);
555
556 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
557 gtk_editable_get_text(GTK_EDITABLE(entry)));
558 }
559
560 apply_method();
561 }
562
563 static void
564 notify_toggle_cb(GtkWidget *widget, gpointer data)
565 {
566 gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
567 gchar pref[256];
568
569 g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
570 (char *)data);
571
572 purple_prefs_set_bool(pref, on);
573
574 apply_notify();
575 }
576
577 static gboolean
578 options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
579 {
580 if (data == NULL)
581 return FALSE;
582
583 if (purple_strequal(data, "method_string")) {
584 purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
585 gtk_editable_get_text(GTK_EDITABLE(widget)));
586 }
587
588 apply_method();
589
590 return FALSE;
591 }
592
593 static void
594 apply_method()
595 {
596 GList *convs;
597
598 for (convs = purple_conversations_get_all(); convs != NULL;
599 convs = convs->next) {
600 PurpleConversation *conv = (PurpleConversation *)convs->data;
601
602 /* remove notifications */
603 unnotify(conv, FALSE);
604
605 if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
606 /* reattach appropriate notifications */
607 notify(conv, FALSE);
608 }
609 }
610
611 static void
612 apply_notify()
613 {
614 GList *convs = purple_conversations_get_all();
615
616 while (convs) {
617 PurpleConversation *conv = (PurpleConversation *)convs->data;
618
619 /* detach signals */
620 detach_signals(conv);
621 /* reattach appropriate signals */
622 attach_signals(conv);
623
624 convs = convs->next;
625 }
626 }
627
628 static GtkWidget *
629 get_config_frame(PurplePlugin *plugin)
630 {
631 GtkWidget *ret = NULL, *frame = NULL;
632 GtkWidget *vbox = NULL, *hbox = NULL;
633 GtkWidget *toggle = NULL, *entry = NULL, *ref;
634
635 ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
636 gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
637
638 /*---------- "Notify For" ----------*/
639 frame = pidgin_make_frame(ret, _("Notify For"));
640 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
641 gtk_container_add(GTK_CONTAINER(frame), vbox);
642
643 toggle = gtk_check_button_new_with_mnemonic(_("_IM windows"));
644 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
645 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
646 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im"));
647 g_signal_connect(G_OBJECT(toggle), "toggled",
648 G_CALLBACK(type_toggle_cb), "type_im");
649
650 ref = toggle;
651 toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
652 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
653 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
654 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"));
655 g_signal_connect(G_OBJECT(toggle), "toggled",
656 G_CALLBACK(type_toggle_cb), "type_im_sys");
657 g_object_bind_property(ref, "active", toggle, "sensitive",
658 G_BINDING_SYNC_CREATE);
659
660 toggle = gtk_check_button_new_with_mnemonic(_("C_hat windows"));
661 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
662 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
663 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"));
664 g_signal_connect(G_OBJECT(toggle), "toggled",
665 G_CALLBACK(type_toggle_cb), "type_chat");
666
667 ref = toggle;
668 toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your username"));
669 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
670 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
671 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick"));
672 g_signal_connect(G_OBJECT(toggle), "toggled",
673 G_CALLBACK(type_toggle_cb), "type_chat_nick");
674 g_object_bind_property(ref, "active", toggle, "sensitive",
675 G_BINDING_SYNC_CREATE);
676
677 toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
678 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
679 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
680 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"));
681 g_signal_connect(G_OBJECT(toggle), "toggled",
682 G_CALLBACK(type_toggle_cb), "type_chat_sys");
683 g_object_bind_property(ref, "active", toggle, "sensitive",
684 G_BINDING_SYNC_CREATE);
685
686 toggle = gtk_check_button_new_with_mnemonic(_("_Focused windows"));
687 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
688 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
689 purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused"));
690 g_signal_connect(G_OBJECT(toggle), "toggled",
691 G_CALLBACK(type_toggle_cb), "type_focused");
692
693 /*---------- "Notification Methods" ----------*/
694 frame = pidgin_make_frame(ret, _("Notification Methods"));
695 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
696 gtk_container_add(GTK_CONTAINER(frame), vbox);
697
698 /* String method button */
699 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 18);
700 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
701 toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:"));
702 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
703 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
704 gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0);
705
706 entry = gtk_entry_new();
707 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
708 gtk_entry_set_max_length(GTK_ENTRY(entry), 10);
709 gtk_widget_set_sensitive(GTK_WIDGET(entry),
710 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
711 gtk_editable_set_text(GTK_EDITABLE(entry),
712 purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"));
713 g_object_set_data(G_OBJECT(toggle), "title-entry", entry);
714 g_signal_connect(G_OBJECT(toggle), "toggled",
715 G_CALLBACK(method_toggle_cb), "method_string");
716 g_signal_connect(G_OBJECT(entry), "focus-out-event",
717 G_CALLBACK(options_entry_cb), "method_string");
718
719 /* Count method button */
720 toggle = gtk_check_button_new_with_mnemonic(_("Insert c_ount of new messages into window title"));
721 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
722 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"));
723 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
724 g_signal_connect(G_OBJECT(toggle), "toggled",
725 G_CALLBACK(method_toggle_cb), "method_count");
726
727 #ifdef HAVE_X11
728 /* Count xprop method button */
729 toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property"));
730 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
731 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
732 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"));
733 g_signal_connect(G_OBJECT(toggle), "toggled",
734 G_CALLBACK(method_toggle_cb), "method_count_xprop");
735
736 /* Urgent method button */
737 toggle = gtk_check_button_new_with_mnemonic(_("Set window manager \"_URGENT\" hint"));
738 #else
739 toggle = gtk_check_button_new_with_mnemonic(_("_Flash window"));
740 #endif
741 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
742 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
743 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"));
744 g_signal_connect(G_OBJECT(toggle), "toggled",
745 G_CALLBACK(method_toggle_cb), "method_urgent");
746
747 /* Raise window method button */
748 toggle = gtk_check_button_new_with_mnemonic(_("R_aise conversation window"));
749 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
750 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
751 purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"));
752 g_signal_connect(G_OBJECT(toggle), "toggled",
753 G_CALLBACK(method_toggle_cb), "method_raise");
754
755 /* Present conversation method button */
756 /* Translators: "Present" as used here is a verb. The plugin presents
757 * the window to the user. */
758 toggle = gtk_check_button_new_with_mnemonic(_("_Present conversation window"));
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_present"));
762 g_signal_connect(G_OBJECT(toggle), "toggled",
763 G_CALLBACK(method_toggle_cb), "method_present");
764
765 /*---------- "Notification Removals" ----------*/
766 frame = pidgin_make_frame(ret, _("Notification Removal"));
767 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
768 gtk_container_add(GTK_CONTAINER(frame), vbox);
769
770 /* Remove on focus button */
771 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _gains focus"));
772 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
773 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
774 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus"));
775 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(notify_toggle_cb), "notify_focus");
776
777 /* Remove on click button */
778 toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _receives click"));
779 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
780 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
781 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click"));
782 g_signal_connect(G_OBJECT(toggle), "toggled",
783 G_CALLBACK(notify_toggle_cb), "notify_click");
784
785 /* Remove on type button */
786 toggle = gtk_check_button_new_with_mnemonic(_("Remove when _typing in conversation window"));
787 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
788 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
789 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type"));
790 g_signal_connect(G_OBJECT(toggle), "toggled",
791 G_CALLBACK(notify_toggle_cb), "notify_type");
792
793 /* Remove on message send button */
794 toggle = gtk_check_button_new_with_mnemonic(_("Remove when a _message gets sent"));
795 gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
796 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
797 purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send"));
798 g_signal_connect(G_OBJECT(toggle), "toggled",
799 G_CALLBACK(notify_toggle_cb), "notify_send");
800
801 #if 0
802 /* Remove on conversation switch button */
803 toggle = gtk_check_button_new_with_mnemonic(_("Remove on switch to conversation ta_b"));
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_switch"));
807 g_signal_connect(G_OBJECT(toggle), "toggled",
808 G_CALLBACK(notify_toggle_cb), "notify_switch");
809 #endif
810
811 gtk_widget_show_all(ret);
812 return ret;
813 }
814
815 static GPluginPluginInfo *
816 notify_query(GError **error)
817 {
818 const gchar * const authors[] = {
819 "Etan Reisner <deryni@eden.rutgers.edu>",
820 "Brian Tarricone <bjt23@cornell.edu>",
821 NULL
822 };
823
824 return pidgin_plugin_info_new(
825 "id", NOTIFY_PLUGIN_ID,
826 "name", N_("Message Notification"),
827 "version", DISPLAY_VERSION,
828 "category", N_("Notification"),
829 "summary", N_("Provides a variety of ways of notifying "
830 "you of unread messages."),
831 "description", N_("Provides a variety of ways of notifying "
832 "you of unread messages."),
833 "authors", authors,
834 "website", PURPLE_WEBSITE,
835 "abi-version", PURPLE_ABI_VERSION,
836 "gtk-config-frame-cb", get_config_frame,
837 NULL
838 );
839 }
840
841 static gboolean
842 notify_load(GPluginPlugin *plugin, GError **error)
843 {
844 GList *convs = purple_conversations_get_all();
845 void *conv_handle = purple_conversations_get_handle();
846 void *gtk_conv_handle = pidgin_conversations_get_handle();
847
848 my_plugin = plugin;
849
850 purple_prefs_add_none("/plugins/gtk");
851 purple_prefs_add_none("/plugins/gtk/X11");
852 purple_prefs_add_none("/plugins/gtk/X11/notify");
853
854 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE);
855 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im_sys", FALSE);
856 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE);
857 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE);
858 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_sys", FALSE);
859 purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE);
860 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE);
861 purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)");
862 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE);
863 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE);
864 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE);
865 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE);
866 purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE);
867 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE);
868 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE);
869 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE);
870 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE);
871 purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE);
872
873 purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
874 G_CALLBACK(message_displayed_cb), NULL);
875 purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
876 G_CALLBACK(message_displayed_cb), NULL);
877 purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin,
878 G_CALLBACK(conv_switched), NULL);
879 purple_signal_connect(conv_handle, "sent-im-msg", plugin,
880 G_CALLBACK(im_sent_im), NULL);
881 purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
882 G_CALLBACK(chat_sent_im), NULL);
883 purple_signal_connect(conv_handle, "conversation-created", plugin,
884 G_CALLBACK(conv_created), NULL);
885 purple_signal_connect(conv_handle, "deleting-conversation", plugin,
886 G_CALLBACK(deleting_conv), NULL);
887
888 while (convs) {
889 PurpleConversation *conv = (PurpleConversation *)convs->data;
890
891 /* attach signals */
892 attach_signals(conv);
893
894 convs = convs->next;
895 }
896
897 return TRUE;
898 }
899
900 static gboolean
901 notify_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
902 {
903 GList *convs = purple_conversations_get_all();
904
905 while (convs) {
906 PurpleConversation *conv = (PurpleConversation *)convs->data;
907
908 /* kill signals */
909 detach_signals(conv);
910
911 convs = convs->next;
912 }
913
914 return TRUE;
915 }
916
917 GPLUGIN_NATIVE_PLUGIN_DECLARE(notify)

mercurial