pidgin/pidgin

closing merged branch
port-changes-from-branch-2.x.y-to-default
14 months ago, Gary Kramlich
2f836435c33c
closing merged branch
/*
* Purple buddy notification plugin.
*
* Copyright (C) 2000-2001, Eric Warmenhoven (original code)
* Copyright (C) 2002, Etan Reisner <deryni@eden.rutgers.edu> (rewritten code)
* Copyright (C) 2003, Christian Hammond (update for changed API)
* Copyright (C) 2003, Brian Tarricone <bjt23@cornell.edu> (mostly rewritten)
* Copyright (C) 2003, Mark Doliner (minor cleanup)
* Copyright (C) 2003, Etan Reisner (largely rewritten again)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
/* TODO
* 22:22:17 <seanegan> deryni: speaking of notify.c... you know what else
* might be a neat feature?
* 22:22:30 <seanegan> Changing the window icon.
* 22:23:25 <deryni> seanegan: To what?
* 22:23:42 <seanegan> deryni: I dunno. Flash it between the regular icon and
* blank or something.
* 22:23:53 <deryni> Also I think purple might re-set that sort of frequently,
* but I'd have to look.
* 22:25:16 <seanegan> deryni: I keep my conversations in one workspace and am
* frequently in an another, and the icon flashing in the pager would be a
* neat visual clue.
*/
/*
* From Etan, 2002:
* -Added config dialog
* -Added control over notification method
* -Added control over when to release notification
*
* -Added option to get notification for chats also
* -Cleaned up code
* -Added option to notify on click as it's own option
* rather then as what happens when on focus isn't clicked
* -Added apply button to change the denotification methods for
* open conversation windows
* -Fixed apply to conversations, count now keeps count across applies
* -Fixed(?) memory leak, and in the process fixed some stupidities
* -Hit enter when done editing the title string entry box to save it
*
* Thanks to Carles Pina i Estany <carles@pinux.info>
* for count of new messages option
*
* From Brian, 20 July 2003:
* -Use new xml prefs
* -Better handling of notification states tracking
* -Better pref change handling
* -Fixed a possible memleak and possible crash (rare)
* -Use gtk_window_get_title() rather than gtkwin->title
* -Other random fixes and cleanups
*
* Etan again, 12 August 2003:
* -Better use of the new xml prefs
* -Removed all bitmask stuff
* -Even better pref change handling
* -Removed unnecessary functions
* -Reworking of notification/unnotification stuff
* -Header file include cleanup
* -General code cleanup
*
* Etan yet again, 04 April 2004:
* -Re-added Urgent option
* -Re-added unnotify on focus option (still needs work, as it will only
* react to focus-in events when the entry or history widgets are focused)
*
* Sean, 08 January, 2005:
* -Added Raise option, formally in Purple proper
*/
#include "internal.h"
#include "pidgin.h"
#include "gtkprefs.h"
#include "conversation.h"
#include "prefs.h"
#include "signals.h"
#include "version.h"
#include "debug.h"
#include "gtkplugin.h"
#include "gtkutils.h"
#define NOTIFY_PLUGIN_ID "gtk-x11-notify"
static PurplePlugin *my_plugin = NULL;
#ifdef HAVE_X11
static GdkAtom _Cardinal = GDK_NONE;
static GdkAtom _PurpleUnseenCount = GDK_NONE;
#endif
/* notification set/unset */
static int notify(PurpleConversation *conv, gboolean increment);
static void notify_win(PidginConvWindow *purplewin, PurpleConversation *conv);
static void unnotify(PurpleConversation *conv, gboolean reset);
static int unnotify_cb(GtkWidget *widget, gpointer data,
PurpleConversation *conv);
/* gtk widget callbacks for prefs panel */
static void type_toggle_cb(GtkWidget *widget, gpointer data);
static void method_toggle_cb(GtkWidget *widget, gpointer data);
static void notify_toggle_cb(GtkWidget *widget, gpointer data);
static gboolean options_entry_cb(GtkWidget *widget, GdkEventFocus *event,
gpointer data);
static void apply_method(void);
static void apply_notify(void);
/* string function */
static void handle_string(PidginConvWindow *purplewin);
/* count_title function */
static void handle_count_title(PidginConvWindow *purplewin);
/* count_xprop function */
static void handle_count_xprop(PidginConvWindow *purplewin);
/* urgent function */
static void handle_urgent(PidginConvWindow *purplewin, gboolean set);
/* raise function */
static void handle_raise(PidginConvWindow *purplewin);
/* present function */
static void handle_present(PurpleConversation *conv);
/****************************************/
/* Begin doing stuff below this line... */
/****************************************/
static guint
count_messages(PidginConvWindow *purplewin)
{
guint count = 0;
GList *convs = NULL, *l;
for (convs = purplewin->gtkconvs; convs != NULL; convs = convs->next) {
PidginConversation *conv = convs->data;
for (l = conv->convs; l != NULL; l = l->next) {
count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "notify-message-count"));
}
}
return count;
}
static int
notify(PurpleConversation *conv, gboolean increment)
{
gint count;
gboolean has_focus;
PidginConvWindow *purplewin = NULL;
if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
return 0;
/* We want to remove the notifications, but not reset the counter */
unnotify(conv, FALSE);
purplewin = PIDGIN_CONVERSATION(conv)->win;
/* If we aren't doing notifications for this type of conversation, return */
if ((PURPLE_IS_IM_CONVERSATION(conv) &&
!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) ||
(PURPLE_IS_CHAT_CONVERSATION(conv) &&
!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat")))
return 0;
g_object_get(G_OBJECT(purplewin->window),
"has-toplevel-focus", &has_focus, NULL);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
!has_focus) {
if (increment) {
count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count"));
count++;
g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(count));
}
notify_win(purplewin, conv);
}
return 0;
}
static void
notify_win(PidginConvWindow *purplewin, PurpleConversation *conv)
{
if (count_messages(purplewin) <= 0)
return;
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"))
handle_count_title(purplewin);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"))
handle_count_xprop(purplewin);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"))
handle_string(purplewin);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"))
handle_urgent(purplewin, TRUE);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"))
handle_raise(purplewin);
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"))
handle_present(conv);
}
static void
unnotify(PurpleConversation *conv, gboolean reset)
{
PurpleConversation *active_conv = NULL;
PidginConvWindow *purplewin = NULL;
g_return_if_fail(conv != NULL);
if (PIDGIN_CONVERSATION(conv) == NULL)
return;
purplewin = PIDGIN_CONVERSATION(conv)->win;
active_conv = pidgin_conv_window_get_active_conversation(purplewin);
/* reset the conversation window title */
purple_conversation_autoset_title(active_conv);
if (reset) {
/* Only need to actually remove the urgent hinting here, since
* removing it just to have it readded in re-notify is an
* unnecessary couple extra RTs to the server */
handle_urgent(purplewin, FALSE);
g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
/* Same logic as for the urgent hint, xprops are also a RT.
* This needs to go here so that it gets the updated message
* count. */
handle_count_xprop(purplewin);
}
return;
}
static int
unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
{
if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
unnotify(conv, TRUE);
return 0;
}
static gboolean
message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
{
PurpleMessageFlags flags = purple_message_get_flags(msg);
/* Ignore anything that's not a received message or a system message */
if (!(flags & (PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM)))
return FALSE;
/* Don't highlight for delayed messages */
if ((flags & PURPLE_MESSAGE_RECV) && (flags & PURPLE_MESSAGE_DELAYED))
return FALSE;
/* Check whether to highlight for system message for either chat or IM */
if (flags & PURPLE_MESSAGE_SYSTEM) {
if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"))
return FALSE;
} else if (PURPLE_IS_IM_CONVERSATION(conv)) {
if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"))
return FALSE;
} else {
/* System message not from chat or IM, ignore */
return FALSE;
}
}
/* If it's a chat, check if we should only highlight when nick is mentioned */
if ((PURPLE_IS_CHAT_CONVERSATION(conv) &&
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick") &&
!(flags & PURPLE_MESSAGE_NICK)))
return FALSE;
/* Nothing speaks against notifying, do so */
notify(conv, TRUE);
return FALSE;
}
static void
im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
{
PurpleIMConversation *im = NULL;
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
im = purple_conversations_find_im_with_account(
purple_message_get_recipient(msg), account);
unnotify(PURPLE_CONVERSATION(im), TRUE);
}
}
static void
chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id)
{
PurpleChatConversation *chat = NULL;
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send")) {
chat = purple_conversations_find_chat(purple_account_get_connection(account), id);
unnotify(PURPLE_CONVERSATION(chat), TRUE);
}
}
static int
attach_signals(PurpleConversation *conv)
{
PidginConversation *gtkconv = NULL;
GSList *webview_ids = NULL, *entry_ids = NULL;
guint id;
gtkconv = PIDGIN_CONVERSATION(conv);
if (!gtkconv) {
purple_debug_misc("notify", "Failed to find gtkconv\n");
return 0;
}
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) {
/* TODO should really find a way to make this work no matter
* where the focus is inside the conv window, without having
* to bind to focus-in-event on the g(d|t)kwindow */
/* try setting the signal on the focus-in-event for
* gtkwin->notebook->container? */
id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
G_CALLBACK(unnotify_cb), conv);
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event",
G_CALLBACK(unnotify_cb), conv);
webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
}
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
/* TODO similarly should really find a way to allow for
* clicking in other places of the window */
id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event",
G_CALLBACK(unnotify_cb), conv);
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
id = g_signal_connect(G_OBJECT(gtkconv->webview), "button-press-event",
G_CALLBACK(unnotify_cb), conv);
webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
}
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
G_CALLBACK(unnotify_cb), conv);
entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
}
g_object_set_data(G_OBJECT(conv), "notify-webview-signals", webview_ids);
g_object_set_data(G_OBJECT(conv), "notify-entry-signals", entry_ids);
return 0;
}
static void
detach_signals(PurpleConversation *conv)
{
PidginConversation *gtkconv = NULL;
GSList *ids = NULL, *l;
gtkconv = PIDGIN_CONVERSATION(conv);
if (!gtkconv)
return;
ids = g_object_get_data(G_OBJECT(conv), "notify-webview-signals");
for (l = ids; l != NULL; l = l->next)
g_signal_handler_disconnect(gtkconv->webview, GPOINTER_TO_INT(l->data));
g_slist_free(ids);
ids = g_object_get_data(G_OBJECT(conv), "notify-entry-signals");
for (l = ids; l != NULL; l = l->next)
g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data));
g_slist_free(ids);
g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
g_object_set_data(G_OBJECT(conv), "notify-webview-signals", NULL);
g_object_set_data(G_OBJECT(conv), "notify-entry-signals", NULL);
}
static void
conv_created(PurpleConversation *conv)
{
g_object_set_data(G_OBJECT(conv), "notify-message-count",
GINT_TO_POINTER(0));
/* always attach the signals, notify() will take care of conversation
* type checking */
attach_signals(conv);
}
static void
conv_switched(PurpleConversation *conv)
{
#if 0
PidginConvWindow *purplewin = purple_conversation_get_window(new_conv);
#endif
/*
* If the conversation was switched, then make sure we re-notify
* because Purple will have overwritten our custom window title.
*/
notify(conv, FALSE);
#if 0
printf("conv_switched - %p - %p\n", old_conv, new_conv);
printf("count - %d\n", count_messages(purplewin));
if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"))
unnotify(new_conv, FALSE);
else {
/* if we don't have notification on the window then we don't want to
* re-notify it */
if (count_messages(purplewin))
notify_win(purplewin);
}
#endif
}
static void
deleting_conv(PurpleConversation *conv)
{
PidginConvWindow *purplewin = NULL;
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
if (gtkconv == NULL)
return;
detach_signals(conv);
purplewin = gtkconv->win;
handle_urgent(purplewin, FALSE);
g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
return;
#if 0
/* i think this line crashes */
if (count_messages(purplewin))
notify_win(purplewin);
#endif
}
#if 0
static void
conversation_dragging(PurpleConversation *active_conv,
PidginConvWindow *old_purplewin,
PidginConvWindow *new_purplewin)
{
if (old_purplewin != new_purplewin) {
if (old_purplewin == NULL) {
/*
purple_conversation_autoset_title(active_conv);
handle_urgent(new_purplewin, FALSE);
*/
if (count_messages(new_purplewin))
notify_win(new_purplewin);
} else {
printf("if else count = %d\n", count_messages(new_purplewin));
printf("if else count = %d\n", count_messages(old_purplewin));
/*
PurpleConversation *old_active_conv = NULL;
old_active_conv = purple_conversation_window_get_active_conversation(new_purplewin);
purple_conversation_autoset_title(old_active_conv);
handle_urgent(old_purplewin, FALSE);
if (count_messages(old_purplewin))
notify_win(old_purplewin);
purple_conversation_autoset_title(active_conv);
handle_urgent(new_purplewin, FALSE);
if (count_messages(new_purplewin))
notify_win(new_purplewin);
*/
}
} else {
printf("else count = %d\n", count_messages(new_purplewin));
printf("else count = %d\n", count_messages(old_purplewin));
/*
purple_conversation_autoset_title(active_conv);
handle_urgent(old_purplewin, FALSE);
if (count_messages(old_purplewin))
notify_win(old_purplewin);
*/
}
}
#endif
static void
handle_string(PidginConvWindow *purplewin)
{
GtkWindow *window = NULL;
gchar newtitle[256];
g_return_if_fail(purplewin != NULL);
window = GTK_WINDOW(purplewin->window);
g_return_if_fail(window != NULL);
g_snprintf(newtitle, sizeof(newtitle), "%s%s",
purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"),
gtk_window_get_title(window));
gtk_window_set_title(window, newtitle);
}
static void
handle_count_title(PidginConvWindow *purplewin)
{
GtkWindow *window;
char newtitle[256];
g_return_if_fail(purplewin != NULL);
window = GTK_WINDOW(purplewin->window);
g_return_if_fail(window != NULL);
g_snprintf(newtitle, sizeof(newtitle), "[%d] %s",
count_messages(purplewin), gtk_window_get_title(window));
gtk_window_set_title(window, newtitle);
}
static void
handle_count_xprop(PidginConvWindow *purplewin)
{
#ifdef HAVE_X11
guint count;
GtkWidget *window;
GdkWindow *gdkwin;
window = purplewin->window;
g_return_if_fail(window != NULL);
if (_PurpleUnseenCount == GDK_NONE) {
_PurpleUnseenCount = gdk_atom_intern("_PIDGIN_UNSEEN_COUNT", FALSE);
}
if (_Cardinal == GDK_NONE) {
_Cardinal = gdk_atom_intern("CARDINAL", FALSE);
}
count = count_messages(purplewin);
gdkwin = gtk_widget_get_window(window);
gdk_property_change(gdkwin, _PurpleUnseenCount, _Cardinal, 32,
GDK_PROP_MODE_REPLACE, (guchar *) &count, 1);
#endif
}
static void
handle_urgent(PidginConvWindow *purplewin, gboolean set)
{
g_return_if_fail(purplewin != NULL);
g_return_if_fail(purplewin->window != NULL);
pidgin_set_urgent(GTK_WINDOW(purplewin->window), set);
}
static void
handle_raise(PidginConvWindow *purplewin)
{
pidgin_conv_window_raise(purplewin);
}
static void
handle_present(PurpleConversation *conv)
{
if (pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)))
return;
purple_conversation_present(conv);
}
static void
type_toggle_cb(GtkWidget *widget, gpointer data)
{
gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
gchar pref[256];
g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
(char *)data);
purple_prefs_set_bool(pref, on);
}
static void
method_toggle_cb(GtkWidget *widget, gpointer data)
{
gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
gchar pref[256];
g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
(char *)data);
purple_prefs_set_bool(pref, on);
if (purple_strequal(data, "method_string")) {
GtkWidget *entry = g_object_get_data(G_OBJECT(widget), "title-entry");
gtk_widget_set_sensitive(entry, on);
purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
gtk_entry_get_text(GTK_ENTRY(entry)));
}
apply_method();
}
static void
notify_toggle_cb(GtkWidget *widget, gpointer data)
{
gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
gchar pref[256];
g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
(char *)data);
purple_prefs_set_bool(pref, on);
apply_notify();
}
static gboolean
options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
{
if (data == NULL)
return FALSE;
if (purple_strequal(data, "method_string")) {
purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
gtk_entry_get_text(GTK_ENTRY(widget)));
}
apply_method();
return FALSE;
}
static void
apply_method()
{
GList *convs;
for (convs = purple_conversations_get_all(); convs != NULL;
convs = convs->next) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
/* remove notifications */
unnotify(conv, FALSE);
if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
/* reattach appropriate notifications */
notify(conv, FALSE);
}
}
static void
apply_notify()
{
GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
/* detach signals */
detach_signals(conv);
/* reattach appropriate signals */
attach_signals(conv);
convs = convs->next;
}
}
static GtkWidget *
get_config_frame(PurplePlugin *plugin)
{
GtkWidget *ret = NULL, *frame = NULL;
GtkWidget *vbox = NULL, *hbox = NULL;
GtkWidget *toggle = NULL, *entry = NULL, *ref;
ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
/*---------- "Notify For" ----------*/
frame = pidgin_make_frame(ret, _("Notify For"));
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
toggle = gtk_check_button_new_with_mnemonic(_("_IM windows"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_im");
ref = toggle;
toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_im_sys");
g_object_bind_property(ref, "active", toggle, "sensitive",
G_BINDING_SYNC_CREATE);
toggle = gtk_check_button_new_with_mnemonic(_("C_hat windows"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_chat");
ref = toggle;
toggle = gtk_check_button_new_with_mnemonic(_("\t_Only when someone says your username"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_nick"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_chat_nick");
g_object_bind_property(ref, "active", toggle, "sensitive",
G_BINDING_SYNC_CREATE);
toggle = gtk_check_button_new_with_mnemonic(_("\tS_ystem messages"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat_sys"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_chat_sys");
g_object_bind_property(ref, "active", toggle, "sensitive",
G_BINDING_SYNC_CREATE);
toggle = gtk_check_button_new_with_mnemonic(_("_Focused windows"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(type_toggle_cb), "type_focused");
/*---------- "Notification Methods" ----------*/
frame = pidgin_make_frame(ret, _("Notification Methods"));
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* String method button */
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 18);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_check_button_new_with_mnemonic(_("Prepend _string into window title:"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0);
entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
gtk_entry_set_max_length(GTK_ENTRY(entry), 10);
gtk_widget_set_sensitive(GTK_WIDGET(entry),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_string"));
gtk_entry_set_text(GTK_ENTRY(entry),
purple_prefs_get_string("/plugins/gtk/X11/notify/title_string"));
g_object_set_data(G_OBJECT(toggle), "title-entry", entry);
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_string");
g_signal_connect(G_OBJECT(entry), "focus-out-event",
G_CALLBACK(options_entry_cb), "method_string");
/* Count method button */
toggle = gtk_check_button_new_with_mnemonic(_("Insert c_ount of new messages into window title"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_count");
#ifdef HAVE_X11
/* Count xprop method button */
toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_count_xprop"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_count_xprop");
/* Urgent method button */
toggle = gtk_check_button_new_with_mnemonic(_("Set window manager \"_URGENT\" hint"));
#else
toggle = gtk_check_button_new_with_mnemonic(_("_Flash window"));
#endif
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_urgent"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_urgent");
/* Raise window method button */
toggle = gtk_check_button_new_with_mnemonic(_("R_aise conversation window"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_raise"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_raise");
/* Present conversation method button */
/* Translators: "Present" as used here is a verb. The plugin presents
* the window to the user. */
toggle = gtk_check_button_new_with_mnemonic(_("_Present conversation window"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/method_present"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(method_toggle_cb), "method_present");
/*---------- "Notification Removals" ----------*/
frame = pidgin_make_frame(ret, _("Notification Removal"));
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* Remove on focus button */
toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _gains focus"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus"));
g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(notify_toggle_cb), "notify_focus");
/* Remove on click button */
toggle = gtk_check_button_new_with_mnemonic(_("Remove when conversation window _receives click"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(notify_toggle_cb), "notify_click");
/* Remove on type button */
toggle = gtk_check_button_new_with_mnemonic(_("Remove when _typing in conversation window"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(notify_toggle_cb), "notify_type");
/* Remove on message send button */
toggle = gtk_check_button_new_with_mnemonic(_("Remove when a _message gets sent"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_send"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(notify_toggle_cb), "notify_send");
#if 0
/* Remove on conversation switch button */
toggle = gtk_check_button_new_with_mnemonic(_("Remove on switch to conversation ta_b"));
gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_switch"));
g_signal_connect(G_OBJECT(toggle), "toggled",
G_CALLBACK(notify_toggle_cb), "notify_switch");
#endif
gtk_widget_show_all(ret);
return ret;
}
static PidginPluginInfo *
plugin_query(GError **error)
{
const gchar * const authors[] = {
"Etan Reisner <deryni@eden.rutgers.edu>",
"Brian Tarricone <bjt23@cornell.edu>",
NULL
};
return pidgin_plugin_info_new(
"id", NOTIFY_PLUGIN_ID,
"name", N_("Message Notification"),
"version", DISPLAY_VERSION,
"category", N_("Notification"),
"summary", N_("Provides a variety of ways of notifying "
"you of unread messages."),
"description", N_("Provides a variety of ways of notifying "
"you of unread messages."),
"authors", authors,
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
"gtk-config-frame-cb", get_config_frame,
NULL
);
}
static gboolean
plugin_load(PurplePlugin *plugin, GError **error)
{
GList *convs = purple_conversations_get_all();
void *conv_handle = purple_conversations_get_handle();
void *gtk_conv_handle = pidgin_conversations_get_handle();
my_plugin = plugin;
purple_prefs_add_none("/plugins/gtk");
purple_prefs_add_none("/plugins/gtk/X11");
purple_prefs_add_none("/plugins/gtk/X11/notify");
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im", TRUE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_im_sys", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_nick", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_chat_sys", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/type_focused", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_string", FALSE);
purple_prefs_add_string("/plugins/gtk/X11/notify/title_string", "(*)");
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_urgent", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_count_xprop", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_raise", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/method_present", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_focus", TRUE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_click", FALSE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_type", TRUE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_send", TRUE);
purple_prefs_add_bool("/plugins/gtk/X11/notify/notify_switch", TRUE);
purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
PURPLE_CALLBACK(message_displayed_cb), NULL);
purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
PURPLE_CALLBACK(message_displayed_cb), NULL);
purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin,
PURPLE_CALLBACK(conv_switched), NULL);
purple_signal_connect(conv_handle, "sent-im-msg", plugin,
PURPLE_CALLBACK(im_sent_im), NULL);
purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
PURPLE_CALLBACK(chat_sent_im), NULL);
purple_signal_connect(conv_handle, "conversation-created", plugin,
PURPLE_CALLBACK(conv_created), NULL);
purple_signal_connect(conv_handle, "deleting-conversation", plugin,
PURPLE_CALLBACK(deleting_conv), NULL);
#if 0
purple_signal_connect(gtk_conv_handle, "conversation-dragging", plugin,
PURPLE_CALLBACK(conversation_dragging), NULL);
#endif
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
/* attach signals */
attach_signals(conv);
convs = convs->next;
}
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin, GError **error)
{
GList *convs = purple_conversations_get_all();
while (convs) {
PurpleConversation *conv = (PurpleConversation *)convs->data;
/* kill signals */
detach_signals(conv);
convs = convs->next;
}
return TRUE;
}
PURPLE_PLUGIN_INIT(notify, plugin_query, plugin_load, plugin_unload);