Move Pidgin plugins into their own subdirectories
Again, not sure if you'll have to do something special for the moves.
Testing Done:
Compile only.
Reviewed at https://reviews.imfreedom.org/r/1743/
--- a/pidgin/plugins/gtkbuddynote.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
- * GtkBuddyNote - Store notes on particular buddies
- * Copyright (C) 2007 Etan Reisner <deryni@pidgin.im>
- * 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.
-append_to_tooltip(PurpleBlistNode *node, GString *text, gboolean full)
- const gchar *note = purple_blist_node_get_string(node, "notes");
- if ((note != NULL) && (*note != '\0')) {
- purple_markup_html_to_xhtml(note, NULL, &tmp);
- esc = g_markup_escape_text(tmp, -1);
- g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"),
-static GPluginPluginInfo *
-gtk_buddy_note_query(GError **error)
- const gchar * const authors[] = {
- "Etan Reisner <deryni@pidgin.im>",
- const gchar * const dependencies[] = {
- "core-plugin_pack-buddynote",
- return pidgin_plugin_info_new(
- "name", N_("Buddy Note Tooltips"),
- "version", DISPLAY_VERSION,
- "category", N_("User interface"),
- "summary", N_("Shows stored buddy notes on the buddy's tooltip."),
- "description", N_("Shows stored buddy notes on the buddy's tooltip."),
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "dependencies", dependencies,
-gtk_buddy_note_load(GPluginPlugin *plugin, GError **error)
- purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip",
- plugin, G_CALLBACK(append_to_tooltip), NULL);
-gtk_buddy_note_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
-GPLUGIN_NATIVE_PLUGIN_DECLARE(gtk_buddy_note)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/gtkbuddynote/gtkbuddynote.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,86 @@
+ * GtkBuddyNote - Store notes on particular buddies + * Copyright (C) 2007 Etan Reisner <deryni@pidgin.im> + * 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. +append_to_tooltip(PurpleBlistNode *node, GString *text, gboolean full) + const gchar *note = purple_blist_node_get_string(node, "notes"); + if ((note != NULL) && (*note != '\0')) { + purple_markup_html_to_xhtml(note, NULL, &tmp); + esc = g_markup_escape_text(tmp, -1); + g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"), +static GPluginPluginInfo * +gtk_buddy_note_query(GError **error) + const gchar * const authors[] = { + "Etan Reisner <deryni@pidgin.im>", + const gchar * const dependencies[] = { + "core-plugin_pack-buddynote", + return pidgin_plugin_info_new( + "name", N_("Buddy Note Tooltips"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Shows stored buddy notes on the buddy's tooltip."), + "description", N_("Shows stored buddy notes on the buddy's tooltip."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "dependencies", dependencies, +gtk_buddy_note_load(GPluginPlugin *plugin, GError **error) + purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", + plugin, G_CALLBACK(append_to_tooltip), NULL); +gtk_buddy_note_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) +GPLUGIN_NATIVE_PLUGIN_DECLARE(gtk_buddy_note) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/gtkbuddynote/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,7 @@
+gtkbuddynote = library('gtkbuddynote', 'gtkbuddynote.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-BuddyNote"'], + dependencies : [libpurple_dep, libpidgin_dep, glib], + install : true, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- a/pidgin/plugins/iconaway.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
-#include <glib/gi18n-lib.h>
-#define ICONAWAY_PLUGIN_ID "gtk-iconaway"
-iconify_windows(PurpleAccount *account, PurpleStatus *old, PurpleStatus *newstatus)
- GApplication *application = NULL;
- PurplePresence *presence;
- presence = purple_status_get_presence(newstatus);
- if(purple_presence_is_available(presence)) {
- purple_blist_set_visible(FALSE);
- application = g_application_get_default();
- windows = gtk_application_get_windows(GTK_APPLICATION(application));
- g_list_foreach(windows, (GFunc)gtk_window_minimize, NULL);
-static GPluginPluginInfo *
-icon_away_query(GError **error)
- const gchar * const authors[] = {
- "Eric Warmenhoven <eric@warmenhoven.org>",
- return pidgin_plugin_info_new(
- "id", ICONAWAY_PLUGIN_ID,
- "name", N_("Minimize on Away"),
- "version", DISPLAY_VERSION,
- "category", N_("User interface"),
- "summary", N_("Minimizes the buddy list and your conversations "
- "description", N_("Minimizes the buddy list and your conversations "
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
-icon_away_load(GPluginPlugin *plugin, GError **error)
- purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
- plugin, G_CALLBACK(iconify_windows), NULL);
-icon_away_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
-GPLUGIN_NATIVE_PLUGIN_DECLARE(icon_away)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/iconaway/iconaway.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,92 @@
+ * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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 +#include <glib/gi18n-lib.h> +#define ICONAWAY_PLUGIN_ID "gtk-iconaway" +iconify_windows(PurpleAccount *account, PurpleStatus *old, PurpleStatus *newstatus) + GApplication *application = NULL; + PurplePresence *presence; + presence = purple_status_get_presence(newstatus); + if(purple_presence_is_available(presence)) { + purple_blist_set_visible(FALSE); + application = g_application_get_default(); + windows = gtk_application_get_windows(GTK_APPLICATION(application)); + g_list_foreach(windows, (GFunc)gtk_window_minimize, NULL); +static GPluginPluginInfo * +icon_away_query(GError **error) + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + return pidgin_plugin_info_new( + "id", ICONAWAY_PLUGIN_ID, + "name", N_("Minimize on Away"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Minimizes the buddy list and your conversations " + "description", N_("Minimizes the buddy list and your conversations " + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, +icon_away_load(GPluginPlugin *plugin, GError **error) + purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", + plugin, G_CALLBACK(iconify_windows), NULL); +icon_away_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) +GPLUGIN_NATIVE_PLUGIN_DECLARE(icon_away) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/iconaway/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,7 @@
+iconaway = library('iconaway', 'iconaway.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-IconAway"'], + dependencies : [libpurple_dep, libpidgin_dep, glib], + install : true, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- a/pidgin/plugins/meson.build Mon Sep 12 05:06:39 2022 -0500
+++ b/pidgin/plugins/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -1,45 +1,11 @@
-gtkbuddynote = library('gtkbuddynote', 'gtkbuddynote.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-BuddyNote"'],
- dependencies : [libpurple_dep, libpidgin_dep, glib],
- install : true, install_dir : PIDGIN_PLUGINDIR)
-iconaway = library('iconaway', 'iconaway.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-IconAway"'],
- dependencies : [libpurple_dep, libpidgin_dep, glib],
- install : true, install_dir : PIDGIN_PLUGINDIR)
-notify = library('notify', 'notify.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Notify"'],
- dependencies : [libpurple_dep, libpidgin_dep, glib],
- build_by_default: false,
- install : false, install_dir : PIDGIN_PLUGINDIR)
-spellchk = library('spellchk', 'spellchk.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-SpellCheck"'],
- dependencies : [libpurple_dep, libpidgin_dep, glib],
- build_by_default: false,
- install : false, install_dir : PIDGIN_PLUGINDIR)
-transparency = library('transparency', 'transparency.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Transparency"'],
- dependencies : [libpurple_dep, libpidgin_dep, glib],
- install : true, install_dir : PIDGIN_PLUGINDIR)
- unity = library('unity', 'unity.c',
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Unity"'],
- dependencies : [UNITY, libpurple_dep, libpidgin_dep, glib],
- install : true, install_dir : PIDGIN_PLUGINDIR)
-devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir())
--- a/pidgin/plugins/notify.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,917 +0,0 @@
- * 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
- * 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
- * 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
- * -Added control over notification method
- * -Added control over when to release notification
- * -Added option to get notification for chats also
- * -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:
- * -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 <glib/gi18n-lib.h>
-#define NOTIFY_PLUGIN_ID "gtk-x11-notify"
-static PurplePlugin *my_plugin = NULL;
-static GdkAtom _Cardinal = GDK_NONE;
-static GdkAtom _PurpleUnseenCount = GDK_NONE;
-/* 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,
-static void apply_method(void);
-static void apply_notify(void);
-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);
-static void handle_urgent(PidginConvWindow *purplewin, gboolean set);
-static void handle_present(PurpleConversation *conv);
-/****************************************/
-/* Begin doing stuff below this line... */
-/****************************************/
-count_messages(PidginConvWindow *purplewin)
- 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"));
-notify(PurpleConversation *conv, gboolean increment)
- PidginConvWindow *purplewin = NULL;
- if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
- /* We want to remove the notifications, but not reset the counter */
- 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")))
- g_object_get(G_OBJECT(purplewin->window),
- "has-toplevel-focus", &has_focus, NULL);
- if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") ||
- count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count"));
- g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(count));
- notify_win(purplewin, conv);
-notify_win(PidginConvWindow *purplewin, PurpleConversation *conv)
- if (count_messages(purplewin) <= 0)
- 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_present"))
-unnotify(PurpleConversation *conv, gboolean reset)
- PurpleConversation *active_conv = NULL;
- PidginConversationWindow *purplewin = NULL;
- g_return_if_fail(conv != NULL);
- if (PIDGIN_CONVERSATION(conv) == NULL)
- win = gtk_widget_get_toplevel(PIDGIN_CONVERSATION(conv)->tab_cont);
- purplewin = PIDGIN_CONVERSATION_WINDOW(win);
- activate_conv = pidgin_conversation_window_get_selected(purplewin);
- /* reset the conversation window title */
- purple_conversation_autoset_title(active_conv);
- /* Only need to actually remove the urgent hinting here, since
- * removing it just to have it re-added 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
- handle_count_xprop(purplewin);
-unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
- if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
-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)))
- /* Don't highlight for delayed messages */
- if ((flags & PURPLE_MESSAGE_RECV) && (flags & PURPLE_MESSAGE_DELAYED))
- /* 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"))
- } else if (PURPLE_IS_IM_CONVERSATION(conv)) {
- if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys"))
- /* System message not from chat or IM, ignore */
- /* 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)))
- /* Nothing speaks against notifying, do so */
-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);
-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);
-attach_signals(PurpleConversation *conv)
- PidginConversation *gtkconv = NULL;
- GSList *webview_ids = NULL, *entry_ids = NULL;
- gtkconv = PIDGIN_CONVERSATION(conv);
- purple_debug_misc("notify", "Failed to find gtkconv\n");
- 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);
-detach_signals(PurpleConversation *conv)
- PidginConversation *gtkconv = NULL;
- GSList *ids = NULL, *l;
- gtkconv = PIDGIN_CONVERSATION(conv);
- 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));
- 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_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);
-conv_created(PurpleConversation *conv)
- g_object_set_data(G_OBJECT(conv), "notify-message-count",
- /* always attach the signals, notify() will take care of conversation
-conv_switched(PurpleConversation *conv)
- PidginConvWindow *purplewin = purple_conversation_get_window(new_conv);
- * If the conversation was switched, then make sure we re-notify
- * because Purple will have overwritten our custom window title.
- 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);
- /* if we don't have notification on the window then we don't want to
- if (count_messages(purplewin))
-deleting_conv(PurpleConversation *conv)
- PidginConvWindow *purplewin = NULL;
- PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- purplewin = gtkconv->win;
- handle_urgent(purplewin, FALSE);
- g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0));
- /* i think this line crashes */
- if (count_messages(purplewin))
-handle_string(PidginConvWindow *purplewin)
- GtkWindow *window = NULL;
- 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);
-handle_count_title(PidginConvWindow *purplewin)
- 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);
-handle_count_xprop(PidginConvWindow *purplewin)
- 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);
-handle_urgent(PidginConvWindow *purplewin, gboolean set)
- g_return_if_fail(purplewin != NULL);
- g_return_if_fail(purplewin->window != NULL);
- gtk_window_set_urgency_hint(GTK_WINDOW(purplewin->window), set);
-handle_present(PurpleConversation *conv)
- if (pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)))
- purple_conversation_present(conv);
-type_toggle_cb(GtkWidget *widget, gpointer data)
- gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
- g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
- purple_prefs_set_bool(pref, on);
-method_toggle_cb(GtkWidget *widget, gpointer data)
- gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
- g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
- 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_editable_get_text(GTK_EDITABLE(entry)));
-notify_toggle_cb(GtkWidget *widget, gpointer data)
- gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
- g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s",
- purple_prefs_set_bool(pref, on);
-options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
- if (purple_strequal(data, "method_string")) {
- purple_prefs_set_string("/plugins/gtk/X11/notify/title_string",
- gtk_editable_get_text(GTK_EDITABLE(widget)));
- for (convs = purple_conversations_get_all(); convs != NULL;
- PurpleConversation *conv = (PurpleConversation *)convs->data;
- /* remove notifications */
- if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0)
- /* reattach appropriate notifications */
- GList *convs = purple_conversations_get_all();
- PurpleConversation *conv = (PurpleConversation *)convs->data;
- /* reattach appropriate signals */
-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");
- 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");
- 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_editable_set_text(GTK_EDITABLE(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");
- /* 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"));
- toggle = gtk_check_button_new_with_mnemonic(_("_Flash 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_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");
- /* 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");
- gtk_widget_show_all(ret);
-static GPluginPluginInfo *
-notify_query(GError **error)
- const gchar * const authors[] = {
- "Etan Reisner <deryni@eden.rutgers.edu>",
- "Brian Tarricone <bjt23@cornell.edu>",
- 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."),
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "gtk-config-frame-cb", get_config_frame,
-notify_load(GPluginPlugin *plugin, GError **error)
- GList *convs = purple_conversations_get_all();
- void *conv_handle = purple_conversations_get_handle();
- void *gtk_conv_handle = pidgin_conversations_get_handle();
- 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,
- G_CALLBACK(message_displayed_cb), NULL);
- purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
- G_CALLBACK(message_displayed_cb), NULL);
- purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin,
- G_CALLBACK(conv_switched), NULL);
- purple_signal_connect(conv_handle, "sent-im-msg", plugin,
- G_CALLBACK(im_sent_im), NULL);
- purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
- G_CALLBACK(chat_sent_im), NULL);
- purple_signal_connect(conv_handle, "conversation-created", plugin,
- G_CALLBACK(conv_created), NULL);
- purple_signal_connect(conv_handle, "deleting-conversation", plugin,
- G_CALLBACK(deleting_conv), NULL);
- PurpleConversation *conv = (PurpleConversation *)convs->data;
-notify_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
- GList *convs = purple_conversations_get_all();
- PurpleConversation *conv = (PurpleConversation *)convs->data;
-GPLUGIN_NATIVE_PLUGIN_DECLARE(notify)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/notify/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,8 @@
+notify = library('notify', 'notify.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Notify"'], + dependencies : [libpurple_dep, libpidgin_dep, glib], + build_by_default: false, + install : false, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/notify/notify.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,917 @@
+ * 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 + * 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 + * 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 + * -Added control over notification method + * -Added control over when to release notification + * -Added option to get notification for chats also + * -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: + * -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 <glib/gi18n-lib.h> +#define NOTIFY_PLUGIN_ID "gtk-x11-notify" +static PurplePlugin *my_plugin = NULL; +static GdkAtom _Cardinal = GDK_NONE; +static GdkAtom _PurpleUnseenCount = GDK_NONE; +/* 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, +static void apply_method(void); +static void apply_notify(void); +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); +static void handle_urgent(PidginConvWindow *purplewin, gboolean set); +static void handle_present(PurpleConversation *conv); +/****************************************/ +/* Begin doing stuff below this line... */ +/****************************************/ +count_messages(PidginConvWindow *purplewin) + 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")); +notify(PurpleConversation *conv, gboolean increment) + PidginConvWindow *purplewin = NULL; + if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL) + /* We want to remove the notifications, but not reset the counter */ + 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"))) + g_object_get(G_OBJECT(purplewin->window), + "has-toplevel-focus", &has_focus, NULL); + if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") || + count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")); + g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(count)); + notify_win(purplewin, conv); +notify_win(PidginConvWindow *purplewin, PurpleConversation *conv) + if (count_messages(purplewin) <= 0) + 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_present")) +unnotify(PurpleConversation *conv, gboolean reset) + PurpleConversation *active_conv = NULL; + PidginConversationWindow *purplewin = NULL; + g_return_if_fail(conv != NULL); + if (PIDGIN_CONVERSATION(conv) == NULL) + win = gtk_widget_get_toplevel(PIDGIN_CONVERSATION(conv)->tab_cont); + purplewin = PIDGIN_CONVERSATION_WINDOW(win); + activate_conv = pidgin_conversation_window_get_selected(purplewin); + /* reset the conversation window title */ + purple_conversation_autoset_title(active_conv); + /* Only need to actually remove the urgent hinting here, since + * removing it just to have it re-added 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 + handle_count_xprop(purplewin); +unnotify_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv) + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0) +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))) + /* Don't highlight for delayed messages */ + if ((flags & PURPLE_MESSAGE_RECV) && (flags & PURPLE_MESSAGE_DELAYED)) + /* 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")) + } else if (PURPLE_IS_IM_CONVERSATION(conv)) { + if (!purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im_sys")) + /* System message not from chat or IM, ignore */ + /* 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))) + /* Nothing speaks against notifying, do so */ +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); +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); +attach_signals(PurpleConversation *conv) + PidginConversation *gtkconv = NULL; + GSList *webview_ids = NULL, *entry_ids = NULL; + gtkconv = PIDGIN_CONVERSATION(conv); + purple_debug_misc("notify", "Failed to find gtkconv\n"); + 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); +detach_signals(PurpleConversation *conv) + PidginConversation *gtkconv = NULL; + GSList *ids = NULL, *l; + gtkconv = PIDGIN_CONVERSATION(conv); + 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)); + 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_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); +conv_created(PurpleConversation *conv) + g_object_set_data(G_OBJECT(conv), "notify-message-count", + /* always attach the signals, notify() will take care of conversation +conv_switched(PurpleConversation *conv) + PidginConvWindow *purplewin = purple_conversation_get_window(new_conv); + * If the conversation was switched, then make sure we re-notify + * because Purple will have overwritten our custom window title. + 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); + /* if we don't have notification on the window then we don't want to + if (count_messages(purplewin)) +deleting_conv(PurpleConversation *conv) + PidginConvWindow *purplewin = NULL; + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + purplewin = gtkconv->win; + handle_urgent(purplewin, FALSE); + g_object_set_data(G_OBJECT(conv), "notify-message-count", GINT_TO_POINTER(0)); + /* i think this line crashes */ + if (count_messages(purplewin)) +handle_string(PidginConvWindow *purplewin) + GtkWindow *window = NULL; + 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); +handle_count_title(PidginConvWindow *purplewin) + 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); +handle_count_xprop(PidginConvWindow *purplewin) + 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); +handle_urgent(PidginConvWindow *purplewin, gboolean set) + g_return_if_fail(purplewin != NULL); + g_return_if_fail(purplewin->window != NULL); + gtk_window_set_urgency_hint(GTK_WINDOW(purplewin->window), set); +handle_present(PurpleConversation *conv) + if (pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv))) + purple_conversation_present(conv); +type_toggle_cb(GtkWidget *widget, gpointer data) + gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", + purple_prefs_set_bool(pref, on); +method_toggle_cb(GtkWidget *widget, gpointer data) + gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", + 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_editable_get_text(GTK_EDITABLE(entry))); +notify_toggle_cb(GtkWidget *widget, gpointer data) + gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + g_snprintf(pref, sizeof(pref), "/plugins/gtk/X11/notify/%s", + purple_prefs_set_bool(pref, on); +options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data) + if (purple_strequal(data, "method_string")) { + purple_prefs_set_string("/plugins/gtk/X11/notify/title_string", + gtk_editable_get_text(GTK_EDITABLE(widget))); + for (convs = purple_conversations_get_all(); convs != NULL; + PurpleConversation *conv = (PurpleConversation *)convs->data; + /* remove notifications */ + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "notify-message-count")) != 0) + /* reattach appropriate notifications */ + GList *convs = purple_conversations_get_all(); + PurpleConversation *conv = (PurpleConversation *)convs->data; + /* reattach appropriate signals */ +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"); + 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"); + 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_editable_set_text(GTK_EDITABLE(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"); + /* 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")); + toggle = gtk_check_button_new_with_mnemonic(_("_Flash 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_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"); + /* 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"); + gtk_widget_show_all(ret); +static GPluginPluginInfo * +notify_query(GError **error) + const gchar * const authors[] = { + "Etan Reisner <deryni@eden.rutgers.edu>", + "Brian Tarricone <bjt23@cornell.edu>", + 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."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, +notify_load(GPluginPlugin *plugin, GError **error) + GList *convs = purple_conversations_get_all(); + void *conv_handle = purple_conversations_get_handle(); + void *gtk_conv_handle = pidgin_conversations_get_handle(); + 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, + G_CALLBACK(message_displayed_cb), NULL); + purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin, + G_CALLBACK(message_displayed_cb), NULL); + purple_signal_connect(gtk_conv_handle, "conversation-switched", plugin, + G_CALLBACK(conv_switched), NULL); + purple_signal_connect(conv_handle, "sent-im-msg", plugin, + G_CALLBACK(im_sent_im), NULL); + purple_signal_connect(conv_handle, "sent-chat-msg", plugin, + G_CALLBACK(chat_sent_im), NULL); + purple_signal_connect(conv_handle, "conversation-created", plugin, + G_CALLBACK(conv_created), NULL); + purple_signal_connect(conv_handle, "deleting-conversation", plugin, + G_CALLBACK(deleting_conv), NULL); + PurpleConversation *conv = (PurpleConversation *)convs->data; +notify_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) + GList *convs = purple_conversations_get_all(); + PurpleConversation *conv = (PurpleConversation *)convs->data; +GPLUGIN_NATIVE_PLUGIN_DECLARE(notify) \ No newline at end of file
--- a/pidgin/plugins/spellchk.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2341 +0,0 @@
- * Purple - Replace certain misspelled words with their correct form.
- * Signification changes were made by Benjamin Kahn ("xkahn") and
- * Richard Laager ("rlaager") in April 2005--you may want to contact
- * them if you have questions.
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
- * A lot of this code (especially the config code) was taken directly
- * or nearly directly from xchat, version 1.4.2 by Peter Zelezny and others.
-#include <glib/gi18n-lib.h>
-#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck"
-#define SPELLCHK_OBJECT_KEY "spellchk"
- GtkTextMark *mark_insert_start;
- GtkTextMark *mark_insert_end;
- gboolean ignore_correction;
- gboolean ignore_correction_on_send;
-typedef struct _spellchk spellchk;
-static GtkListStore *model;
-is_word_uppercase(const gchar *word)
- for (; word[0] != '\0'; word = g_utf8_find_next_char (word, NULL)) {
- gunichar c = g_utf8_get_char(word);
- if (!(g_unichar_isupper(c) ||
- g_unichar_ispunct(c) ||
-is_word_lowercase(const gchar *word)
- for (; word[0] != '\0'; word = g_utf8_find_next_char(word, NULL)) {
- gunichar c = g_utf8_get_char(word);
- if (!(g_unichar_islower(c) ||
- g_unichar_ispunct(c) ||
-is_word_proper(const gchar *word)
- if (!g_unichar_isupper(g_utf8_get_char_validated(word, -1)))
- return is_word_lowercase(g_utf8_offset_to_pointer(word, 1));
-make_word_proper(const gchar *word)
- gchar *lower = g_utf8_strdown(word, -1);
- bytes = g_unichar_to_utf8(g_unichar_toupper(g_utf8_get_char(word)), buf);
- buf[MIN((gsize)bytes, sizeof(buf) - 1)] = '\0';
- ret = g_strconcat(buf, g_utf8_offset_to_pointer(lower, 1), NULL);
-substitute_simple_buffer(GtkTextBuffer *buffer)
- gtk_text_buffer_get_iter_at_offset(buffer, &start, 0);
- gtk_text_buffer_get_iter_at_offset(buffer, &end, 0);
- gtk_text_iter_forward_to_end(&end);
- text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
- if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &treeiter) && text) {
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, WORD_ONLY_COLUMN, &val1);
- if (g_value_get_boolean(&val1))
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, BAD_COLUMN, &val1);
- bad = g_value_get_string(&val1);
- /* using g_utf8_* to get /character/ offsets instead of byte offsets for buffer */
- if ((cursor = g_strrstr(text, bad)))
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, GOOD_COLUMN, &val2);
- good = g_value_get_string(&val2);
- char_pos = g_utf8_pointer_to_offset(text, cursor);
- gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos);
- gtk_text_buffer_get_iter_at_offset(buffer, &end, char_pos + g_utf8_strlen(bad, -1));
- gtk_text_buffer_delete(buffer, &start, &end);
- gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos);
- gtk_text_buffer_insert(buffer, &start, good, -1);
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &treeiter));
-substitute_word(gchar *word)
- lowerword = g_utf8_strdown(word, -1);
- foldedword = g_utf8_casefold(word, -1);
- if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
- gboolean case_sensitive;
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val1);
- if (!g_value_get_boolean(&val1)) {
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val1);
- case_sensitive = g_value_get_boolean(&val1);
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val1);
- bad = g_value_get_string(&val1);
- if ((case_sensitive && purple_strequal(bad, word)) ||
- (!case_sensitive && (purple_strequal(bad, lowerword) ||
- (!is_word_lowercase(bad) &&
- purple_strequal((tmpbad = g_utf8_casefold(bad, -1)), foldedword)))))
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val2);
- good = g_value_get_string(&val2);
- if (!case_sensitive && is_word_lowercase(bad) && is_word_lowercase(good))
- if (is_word_uppercase(word))
- outword = g_utf8_strup(good, -1);
- else if (is_word_proper(word))
- outword = make_word_proper(good);
- outword = g_strdup(good);
- outword = g_strdup(good);
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
-spellchk_free(spellchk *spell)
- g_return_if_fail(spell != NULL);
- buffer = gtk_text_view_get_buffer(spell->view);
- g_signal_handlers_disconnect_matched(buffer,
-/* Pango doesn't know about the "'" character. Let's fix that. */
-spellchk_inside_word(GtkTextIter *iter)
- gunichar ucs4_char = gtk_text_iter_get_char(iter);
- utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL);
- /* Hack because otherwise typing things like U.S. gets difficult
- * if you have 'u' -> 'you' set as a correction...
- * Part 1 of 2: This marks . as being an inside-word character. */
- /* Avoid problems with \r, for example (SF #1289031). */
- if (gtk_text_iter_inside_word (iter) == TRUE)
- gboolean result = gtk_text_iter_backward_char(iter);
- gboolean output = gtk_text_iter_inside_word(iter);
- * Hack so that "u'll" will correct correctly.
- ucs4_char = gtk_text_iter_get_char(iter);
- utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL);
- if (c == 'u' || c == 'U')
- gtk_text_iter_forward_char(iter);
- gtk_text_iter_forward_char(iter);
-spellchk_backward_word_start(GtkTextIter *iter)
- output = gtk_text_iter_backward_word_start(iter);
- /* It didn't work... */
- while (spellchk_inside_word(iter)) {
- result = gtk_text_iter_backward_char(iter);
- /* We can't go backwards anymore? We're at the beginning of the word. */
- if (!spellchk_inside_word(iter)) {
- gtk_text_iter_forward_char(iter);
- output = gtk_text_iter_backward_word_start(iter);
-check_range(spellchk *spell, GtkTextBuffer *buffer,
- GtkTextIter start, GtkTextIter end, gboolean sending)
- if ((replaced = substitute_simple_buffer(buffer)))
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
- spell->pos = gtk_text_iter_get_offset(&pos);
- gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
- gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
- /* We need to go backwards to find out if we are inside a word or not. */
- gtk_text_iter_backward_char(&end);
- if (spellchk_inside_word(&end))
- gtk_text_iter_forward_char(&end);
- return replaced; /* We only pay attention to whole words. */
- /* We could be in the middle of a whitespace block. Check for that. */
- result = gtk_text_iter_backward_char(&end);
- if (!spellchk_inside_word(&end))
- gtk_text_iter_forward_char(&end);
- gtk_text_iter_forward_char(&end);
- /* Move backwards to the beginning of the word. */
- spellchk_backward_word_start(&start);
- spell->word = gtk_text_iter_get_text(&start, &end);
- /* Hack because otherwise typing things like U.S. gets difficult
- * if you have 'u' -> 'you' set as a correction...
- * Part 2 of 2: This chops periods off the end of the word so
- * the right substitution entry is found. */
- tmp = g_strdup(spell->word);
- if (tmp != NULL && *tmp != '\0') {
- for (c = tmp + strlen(tmp) - 1 ; c != tmp ; c--) {
- if ((word = substitute_word(tmp))) {
- for (i = 1 ; i <= period_count ; i++) {
- tmp2 = g_strconcat(word, ".", NULL);
- gtk_text_buffer_delete(buffer, &start, &end);
- gtk_text_buffer_insert(buffer, &start, word, -1);
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
- spell->pos = gtk_text_iter_get_offset(&pos);
-/* insertion works like this:
- * - before the text is inserted, we mark the position in the buffer.
- * - after the text is inserted, we see where our mark is and use that and
- * the current position to check the entire range of inserted text.
- * this may be overkill for the common case (inserting one character). */
-insert_text_before(GtkTextBuffer *buffer, GtkTextIter *iter,
- gchar *text, gint len, spellchk *spell)
- if (spell->inserting == TRUE)
- spell->inserting = TRUE;
- gtk_text_buffer_move_mark(buffer, spell->mark_insert_start, iter);
-insert_text_after(GtkTextBuffer *buffer, GtkTextIter *iter,
- gchar *text, gint len, spellchk *spell)
- GtkTextIter start, end;
- spell->ignore_correction_on_send = FALSE;
- if (spell->ignore_correction) {
- spell->ignore_correction = FALSE;
- /* we need to check a range of text. */
- gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start);
- check_range(spell, buffer, start, *iter, FALSE);
- /* if check_range modified the buffer, iter has been invalidated */
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
- gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end);
- spell->inserting = FALSE;
-delete_range_after(GtkTextBuffer *buffer,
- GtkTextIter *start, GtkTextIter *end, spellchk *spell)
- GtkTextIter start2, end2;
- spell->ignore_correction_on_send = FALSE;
- if (spell->inserting == TRUE)
- spell->inserting = TRUE;
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
- place = gtk_text_iter_get_offset(&pos);
- if ((place + 1) != spell->pos) {
- gtk_text_buffer_get_iter_at_mark(buffer, &start2, spell->mark_insert_start);
- gtk_text_buffer_get_iter_at_mark(buffer, &end2, spell->mark_insert_end);
- gtk_text_buffer_delete(buffer, &start2, &end2);
- gtk_text_buffer_insert(buffer, &start2, spell->word, -1);
- spell->ignore_correction = TRUE;
- spell->ignore_correction_on_send = TRUE;
- spell->inserting = FALSE;
-message_send_cb(GtkWidget *widget, spellchk *spell)
- GtkTextIter start, end;
- if (spell->ignore_correction_on_send)
- spell->ignore_correction_on_send = FALSE;
- if (!purple_prefs_get_bool("/plugins/gtk/spellchk/last_word_replace"))
- buffer = gtk_text_view_get_buffer(spell->view);
- gtk_text_buffer_get_end_iter(buffer, &start);
- gtk_text_buffer_get_end_iter(buffer, &end);
- spell->inserting = TRUE;
- replaced = check_range(spell, buffer, start, end, TRUE);
- spell->inserting = FALSE;
- /* if check_range modified the buffer, iter has been invalidated */
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
- gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end);
- g_signal_stop_emission_by_name(widget, "message_send");
- spell->ignore_correction_on_send = TRUE;
-spellchk_new_attach(PurpleConversation *conv)
- GtkTextIter start, end;
- PidginConversation *gtkconv;
- gtkconv = PIDGIN_CONVERSATION(conv);
- view = GTK_TEXT_VIEW(gtkconv->entry);
- spell = g_object_get_data(G_OBJECT(view), SPELLCHK_OBJECT_KEY);
- /* attach to the widget */
- spell = g_new0(spellchk, 1);
- g_object_set_data_full(G_OBJECT(view), SPELLCHK_OBJECT_KEY, spell,
- (GDestroyNotify)spellchk_free);
- buffer = gtk_text_view_get_buffer(view);
- /* we create the mark here, but we don't use it until text is
- * inserted, so we don't really care where iter points. */
- gtk_text_buffer_get_bounds(buffer, &start, &end);
- spell->mark_insert_start = gtk_text_buffer_create_mark(buffer,
- "spellchk-insert-start",
- spell->mark_insert_end = gtk_text_buffer_create_mark(buffer,
- g_signal_connect_after(G_OBJECT(buffer),
- G_CALLBACK(delete_range_after), spell);
- g_signal_connect(G_OBJECT(buffer),
- G_CALLBACK(insert_text_before), spell);
- g_signal_connect_after(G_OBJECT(buffer),
- G_CALLBACK(insert_text_after), spell);
- g_signal_connect(G_OBJECT(gtkconv->entry), "message_send",
- G_CALLBACK(message_send_cb), spell);
-static int buf_get_line(char *ibuf, char **buf, gsize *position, gsize len)
- while (!(ibuf[pos] == '\n' ||
- (ibuf[pos] == '\r' && ibuf[pos + 1] != '\n')))
- if (pos != 0 && ibuf[pos] == '\n' && ibuf[pos - 1] == '\r')
-static void load_conf(void)
- /* Corrections to change "...", "(c)", "(r)", and "(tm)" to their
- * Unicode character equivalents were not added here even though
- * they existed in the source list(s). I think these corrections
- * would be more trouble than they're worth.
- const char * const defaultconf =
- "BAD abbout\nGOOD about\n"
- "BAD abotu\nGOOD about\n"
- "BAD abouta\nGOOD about a\n"
- "BAD aboutit\nGOOD about it\n"
- "BAD aboutthe\nGOOD about the\n"
- "BAD abscence\nGOOD absence\n"
- "BAD accesories\nGOOD accessories\n"
- "BAD accidant\nGOOD accident\n"
- "BAD accomodate\nGOOD accommodate\n"
- "BAD accordingto\nGOOD according to\n"
- "BAD accross\nGOOD across\n"
- "BAD acheive\nGOOD achieve\n"
- "BAD acheived\nGOOD achieved\n"
- "BAD acheiving\nGOOD achieving\n"
- "BAD acommodate\nGOOD accommodate\n"
- "BAD acomodate\nGOOD accommodate\n"
- "BAD actualyl\nGOOD actually\n"
- "BAD additinal\nGOOD additional\n"
- "BAD addtional\nGOOD additional\n"
- "BAD adequit\nGOOD adequate\n"
- "BAD adequite\nGOOD adequate\n"
- "BAD advanage\nGOOD advantage\n"
- "BAD affraid\nGOOD afraid\n"
- "BAD afterthe\nGOOD after the\n"
- "COMPLETE 0\nBAD againstt he \nGOOD against the \n"
- "BAD aganist\nGOOD against\n"
- "BAD aggresive\nGOOD aggressive\n"
- "BAD agian\nGOOD again\n"
- "BAD agreemeent\nGOOD agreement\n"
- "BAD agreemeents\nGOOD agreements\n"
- "BAD agreemnet\nGOOD agreement\n"
- "BAD agreemnets\nGOOD agreements\n"
- "BAD agressive\nGOOD aggressive\n"
- "BAD agressiveness\nGOOD aggressiveness\n"
- "BAD ahold\nGOOD a hold\n"
- "BAD ahppen\nGOOD happen\n"
- "BAD ahve\nGOOD have\n"
- "BAD allready\nGOOD already\n"
- "BAD allwasy\nGOOD always\n"
- "BAD allwyas\nGOOD always\n"
- "BAD almots\nGOOD almost\n"
- "BAD almsot\nGOOD almost\n"
- "BAD alomst\nGOOD almost\n"
- "BAD alot\nGOOD a lot\n"
- "BAD alraedy\nGOOD already\n"
- "BAD alreayd\nGOOD already\n"
- "BAD alreday\nGOOD already\n"
- "BAD alwasy\nGOOD always\n"
- "BAD alwats\nGOOD always\n"
- "BAD alway\nGOOD always\n"
- "BAD alwyas\nGOOD always\n"
- "BAD amde\nGOOD made\n"
- "BAD Ameria\nGOOD America\n"
- "BAD amke\nGOOD make\n"
- "BAD amkes\nGOOD makes\n"
- "BAD andone\nGOOD and one\n"
- "BAD andteh\nGOOD and the\n"
- "BAD andthe\nGOOD and the\n"
- "COMPLETE 0\nBAD andt he \nGOOD and the \n"
- "BAD anothe\nGOOD another\n"
- "BAD anual\nGOOD annual\n"
- "BAD any1\nGOOD anyone\n"
- "BAD apparant\nGOOD apparent\n"
- "BAD apparrent\nGOOD apparent\n"
- "BAD appearence\nGOOD appearance\n"
- "BAD appeares\nGOOD appears\n"
- "BAD applicaiton\nGOOD application\n"
- "BAD applicaitons\nGOOD applications\n"
- "BAD applyed\nGOOD applied\n"
- "BAD appointiment\nGOOD appointment\n"
- "BAD approrpiate\nGOOD appropriate\n"
- "BAD approrpriate\nGOOD appropriate\n"
- "BAD aquisition\nGOOD acquisition\n"
- "BAD aquisitions\nGOOD acquisitions\n"
- "BAD arent\nGOOD aren't\n"
- "COMPLETE 0\nBAD aren;t \nGOOD aren't \n"
- "BAD arguement\nGOOD argument\n"
- "BAD arguements\nGOOD arguments\n"
- "COMPLETE 0\nBAD arn't \nGOOD aren't \n"
- "BAD arond\nGOOD around\n"
- "BAD artical\nGOOD article\n"
- "BAD articel\nGOOD article\n"
- "BAD asdvertising\nGOOD advertising\n"
- "COMPLETE 0\nBAD askt he \nGOOD ask the \n"
- "BAD assistent\nGOOD assistant\n"
- "BAD asthe\nGOOD as the\n"
- "BAD atention\nGOOD attention\n"
- "BAD atmospher\nGOOD atmosphere\n"
- "BAD attentioin\nGOOD attention\n"
- "BAD atthe\nGOOD at the\n"
- "BAD audeince\nGOOD audience\n"
- "BAD audiance\nGOOD audience\n"
- "BAD authentification\nGOOD authentication\n"
- "BAD availalbe\nGOOD available\n"
- "BAD awya\nGOOD away\n"
- "BAD aywa\nGOOD away\n"
- "BAD b4\nGOOD before\n"
- "BAD bakc\nGOOD back\n"
- "BAD balence\nGOOD balance\n"
- "BAD ballance\nGOOD balance\n"
- "BAD baout\nGOOD about\n"
- "BAD bcak\nGOOD back\n"
- "BAD bcuz\nGOOD because\n"
- "BAD beacuse\nGOOD because\n"
- "BAD becasue\nGOOD because\n"
- "BAD becaus\nGOOD because\n"
- "BAD becausea\nGOOD because a\n"
- "BAD becauseof\nGOOD because of\n"
- "BAD becausethe\nGOOD because the\n"
- "BAD becauseyou\nGOOD because you\n"
- "COMPLETE 0\nBAD beckon call\nGOOD beck and call\n"
- "BAD becomeing\nGOOD becoming\n"
- "BAD becomming\nGOOD becoming\n"
- "BAD becuase\nGOOD because\n"
- "BAD becuse\nGOOD because\n"
- "BAD befoer\nGOOD before\n"
- "BAD beggining\nGOOD beginning\n"
- "BAD begining\nGOOD beginning\n"
- "BAD beginining\nGOOD beginning\n"
- "BAD beleiev\nGOOD believe\n"
- "BAD beleieve\nGOOD believe\n"
- "BAD beleif\nGOOD belief\n"
- "BAD beleive\nGOOD believe\n"
- "BAD beleived\nGOOD believed\n"
- "BAD beleives\nGOOD believes\n"
- "BAD belive\nGOOD believe\n"
- "BAD belived\nGOOD believed\n"
- "BAD belives\nGOOD believes\n"
- "BAD benifit\nGOOD benefit\n"
- "BAD benifits\nGOOD benefits\n"
- "BAD betwen\nGOOD between\n"
- "BAD beutiful\nGOOD beautiful\n"
- "BAD blase\nGOOD blas\303\251\n"
- "BAD boxs\nGOOD boxes\n"
- "BAD brodcast\nGOOD broadcast\n"
- "BAD butthe\nGOOD but the\n"
- "COMPLETE 0\nBAD byt he \nGOOD by the \n"
- "BAD cafe\nGOOD caf\303\251\n"
- "BAD caharcter\nGOOD character\n"
- "BAD calcullated\nGOOD calculated\n"
- "BAD calulated\nGOOD calculated\n"
- "BAD candidtae\nGOOD candidate\n"
- "BAD candidtaes\nGOOD candidates\n"
- "COMPLETE 0\nBAD case and point\nGOOD case in point\n"
- "BAD cant\nGOOD can't\n"
- "COMPLETE 0\nBAD can;t \nGOOD can't \n"
- "COMPLETE 0\nBAD can't of been\nGOOD can't have been\n"
- "BAD catagory\nGOOD category\n"
- "BAD categiory\nGOOD category\n"
- "BAD certian\nGOOD certain\n"
- "BAD challange\nGOOD challenge\n"
- "BAD challanges\nGOOD challenges\n"
- "BAD chaneg\nGOOD change\n"
- "BAD chanegs\nGOOD changes\n"
- "BAD changable\nGOOD changeable\n"
- "BAD changeing\nGOOD changing\n"
- "BAD changng\nGOOD changing\n"
- "BAD charachter\nGOOD character\n"
- "BAD charachters\nGOOD characters\n"
- "BAD charactor\nGOOD character\n"
- "BAD charecter\nGOOD character\n"
- "BAD charector\nGOOD character\n"
- "BAD cheif\nGOOD chief\n"
- "BAD chekc\nGOOD check\n"
- "BAD chnage\nGOOD change\n"
- "BAD cieling\nGOOD ceiling\n"
- "BAD circut\nGOOD circuit\n"
- "BAD claer\nGOOD clear\n"
- "BAD claered\nGOOD cleared\n"
- "BAD claerly\nGOOD clearly\n"
- "BAD cliant\nGOOD client\n"
- "BAD cliche\nGOOD clich\303\251\n"
- "BAD colection\nGOOD collection\n"
- "BAD comanies\nGOOD companies\n"
- "BAD comany\nGOOD company\n"
- "BAD comapnies\nGOOD companies\n"
- "BAD comapny\nGOOD company\n"
- "BAD combintation\nGOOD combination\n"
- "BAD comited\nGOOD committed\n"
- "BAD comittee\nGOOD committee\n"
- "BAD commadn\nGOOD command\n"
- "BAD comming\nGOOD coming\n"
- "BAD commitee\nGOOD committee\n"
- "BAD committe\nGOOD committee\n"
- "BAD committment\nGOOD commitment\n"
- "BAD committments\nGOOD commitments\n"
- "BAD committy\nGOOD committee\n"
- "BAD comntain\nGOOD contain\n"
- "BAD comntains\nGOOD contains\n"
- "BAD compair\nGOOD compare\n"
- "COMPLETE 0\nBAD company;s \nGOOD company's \n"
- "BAD competetive\nGOOD competitive\n"
- "BAD compleated\nGOOD completed\n"
- "BAD compleatly\nGOOD completely\n"
- "BAD compleatness\nGOOD completeness\n"
- "BAD completly\nGOOD completely\n"
- "BAD completness\nGOOD completeness\n"
- "BAD composate\nGOOD composite\n"
- "BAD comtain\nGOOD contain\n"
- "BAD comtains\nGOOD contains\n"
- "BAD comunicate\nGOOD communicate\n"
- "BAD comunity\nGOOD community\n"
- "BAD condolances\nGOOD condolences\n"
- "BAD conected\nGOOD connected\n"
- "BAD conferance\nGOOD conference\n"
- "BAD confirmmation\nGOOD confirmation\n"
- "BAD congradulations\nGOOD congratulations\n"
- "BAD considerit\nGOOD considerate\n"
- "BAD considerite\nGOOD considerate\n"
- "BAD consonent\nGOOD consonant\n"
- "BAD conspiricy\nGOOD conspiracy\n"
- "BAD consultent\nGOOD consultant\n"
- "BAD convertable\nGOOD convertible\n"
- "BAD cooparate\nGOOD cooperate\n"
- "BAD cooporate\nGOOD cooperate\n"
- "BAD corproation\nGOOD corporation\n"
- "BAD corproations\nGOOD corporations\n"
- "BAD corruptable\nGOOD corruptible\n"
- "BAD cotten\nGOOD cotton\n"
- "BAD coudl\nGOOD could\n"
- "COMPLETE 0\nBAD coudln't \nGOOD couldn't \n"
- "COMPLETE 0\nBAD coudn't \nGOOD couldn't \n"
- "BAD couldnt\nGOOD couldn't\n"
- "COMPLETE 0\nBAD couldn;t \nGOOD couldn't \n"
- "COMPLETE 0\nBAD could of been\nGOOD could have been\n"
- "COMPLETE 0\nBAD could of had\nGOOD could have had\n"
- "BAD couldthe\nGOOD could the\n"
- "BAD couldve\nGOOD could've\n"
- "BAD cpoy\nGOOD copy\n"
- "BAD creme\nGOOD cr\303\250me\n"
- "BAD ctaegory\nGOOD category\n"
- "BAD cu\nGOOD see you\n"
- "BAD cusotmer\nGOOD customer\n"
- "BAD cusotmers\nGOOD customers\n"
- "BAD cutsomer\nGOOD customer\n"
- "BAD cutsomers\nGOOD customer\n"
- "BAD cuz\nGOOD because\n"
- "BAD danceing\nGOOD dancing\n"
- "BAD dcument\nGOOD document\n"
- "BAD deatils\nGOOD details\n"
- "BAD decison\nGOOD decision\n"
- "BAD decisons\nGOOD decisions\n"
- "BAD decor\nGOOD d\303\251cor\n"
- "BAD defendent\nGOOD defendant\n"
- "BAD definately\nGOOD definitely\n"
- "COMPLETE 0\nBAD deja vu\nGOOD d\303\251j\303\240 vu\n"
- "BAD deptartment\nGOOD department\n"
- "BAD desicion\nGOOD decision\n"
- "BAD desicions\nGOOD decisions\n"
- "BAD desision\nGOOD decision\n"
- "BAD desisions\nGOOD decisions\n"
- "BAD detente\nGOOD d\303\251tente\n"
- "BAD develeoprs\nGOOD developers\n"
- "BAD devellop\nGOOD develop\n"
- "BAD develloped\nGOOD developed\n"
- "BAD develloper\nGOOD developer\n"
- "BAD devellopers\nGOOD developers\n"
- "BAD develloping\nGOOD developing\n"
- "BAD devellopment\nGOOD development\n"
- "BAD devellopments\nGOOD developments\n"
- "BAD devellops\nGOOD develop\n"
- "BAD develope\nGOOD develop\n"
- "BAD developement\nGOOD development\n"
- "BAD developements\nGOOD developments\n"
- "BAD developor\nGOOD developer\n"
- "BAD developors\nGOOD developers\n"
- "BAD develpment\nGOOD development\n"
- "BAD diaplay\nGOOD display\n"
- "BAD didint\nGOOD didn't\n"
- "BAD didnot\nGOOD did not\n"
- "BAD didnt\nGOOD didn't\n"
- "COMPLETE 0\nBAD didn;t \nGOOD didn't \n"
- "BAD difefrent\nGOOD different\n"
- "BAD diferences\nGOOD differences\n"
- "BAD differance\nGOOD difference\n"
- "BAD differances\nGOOD differences\n"
- "BAD differant\nGOOD different\n"
- "BAD differemt\nGOOD different\n"
- "BAD differnt\nGOOD different\n"
- "BAD diffrent\nGOOD different\n"
- "BAD directer\nGOOD director\n"
- "BAD directers\nGOOD directors\n"
- "BAD directiosn\nGOOD direction\n"
- "BAD disatisfied\nGOOD dissatisfied\n"
- "BAD discoverd\nGOOD discovered\n"
- "BAD disign\nGOOD design\n"
- "BAD dispaly\nGOOD display\n"
- "BAD dissonent\nGOOD dissonant\n"
- "BAD distribusion\nGOOD distribution\n"
- "BAD divsion\nGOOD division\n"
- "BAD docuement\nGOOD documents\n"
- "BAD docuemnt\nGOOD document\n"
- "BAD documetn\nGOOD document\n"
- "BAD documnet\nGOOD document\n"
- "BAD documnets\nGOOD documents\n"
- "COMPLETE 0\nBAD doens't \nGOOD doesn't \n"
- "BAD doese\nGOOD does\n"
- "COMPLETE 0\nBAD doe snot \nGOOD does not \n"
- "BAD doesnt\nGOOD doesn't\n"
- "COMPLETE 0\nBAD doesn;t \nGOOD doesn't \n"
- "BAD doign\nGOOD doing\n"
- "BAD doimg\nGOOD doing\n"
- "BAD doind\nGOOD doing\n"
- "BAD dollers\nGOOD dollars\n"
- "BAD donig\nGOOD doing\n"
- "BAD donno\nGOOD don't know\n"
- "BAD dont\nGOOD don't\n"
- "COMPLETE 0\nBAD do'nt \nGOOD don't \n"
- "COMPLETE 0\nBAD don;t \nGOOD don't \n"
- "COMPLETE 0\nBAD don't no \nGOOD don't know \n"
- "COMPLETE 0\nBAD dosn't \nGOOD doesn't \n"
- "BAD driveing\nGOOD driving\n"
- "BAD drnik\nGOOD drink\n"
- "BAD dunno\nGOOD don't know\n"
- "BAD eclair\nGOOD \303\251clair\n"
- "BAD efel\nGOOD feel\n"
- "BAD effecient\nGOOD efficient\n"
- "BAD efort\nGOOD effort\n"
- "BAD eforts\nGOOD efforts\n"
- "BAD eligable\nGOOD eligible\n"
- "BAD embarass\nGOOD embarrass\n"
- "BAD emigre\nGOOD \303\251migr\303\251\n"
- "BAD enought\nGOOD enough\n"
- "BAD entree\nGOOD entr\303\251e\n"
- "BAD enuf\nGOOD enough\n"
- "BAD equippment\nGOOD equipment\n"
- "BAD equivalant\nGOOD equivalent\n"
- "BAD esle\nGOOD else\n"
- "BAD especally\nGOOD especially\n"
- "BAD especialyl\nGOOD especially\n"
- "BAD espesially\nGOOD especially\n"
- "BAD essense\nGOOD essence\n"
- "BAD excellance\nGOOD excellence\n"
- "BAD excellant\nGOOD excellent\n"
- "BAD excercise\nGOOD exercise\n"
- "BAD exchagne\nGOOD exchange\n"
- "BAD exchagnes\nGOOD exchanges\n"
- "BAD excitment\nGOOD excitement\n"
- "BAD exhcange\nGOOD exchange\n"
- "BAD exhcanges\nGOOD exchanges\n"
- "BAD experiance\nGOOD experience\n"
- "BAD experienc\nGOOD experience\n"
- "BAD exprience\nGOOD experience\n"
- "BAD exprienced\nGOOD experienced\n"
- "BAD facade\nGOOD fa\303\247ade\n"
- "BAD faeture\nGOOD feature\n"
- "BAD faetures\nGOOD feature\n"
- "BAD familair\nGOOD familiar\n"
- "BAD familar\nGOOD familiar\n"
- "BAD familliar\nGOOD familiar\n"
- "BAD fammiliar\nGOOD familiar\n"
- "BAD feild\nGOOD field\n"
- "BAD feilds\nGOOD fields\n"
- "BAD fianlly\nGOOD finally\n"
- "BAD fidn\nGOOD find\n"
- "BAD finalyl\nGOOD finally\n"
- "BAD firends\nGOOD friends\n"
- "BAD firts\nGOOD first\n"
- "BAD follwo\nGOOD follow\n"
- "BAD follwoing\nGOOD following\n"
- "BAD fora\nGOOD for a\n"
- "COMPLETE 0\nBAD for all intensive purposes\nfor all intents and purposes\n"
- "BAD foriegn\nGOOD foreign\n"
- "BAD forthe\nGOOD for the\n"
- "BAD forwrd\nGOOD forward\n"
- "BAD forwrds\nGOOD forwards\n"
- "BAD foudn\nGOOD found\n"
- "BAD foward\nGOOD forward\n"
- "BAD fowards\nGOOD forwards\n"
- "BAD freind\nGOOD friend\n"
- "BAD freindly\nGOOD friendly\n"
- "BAD freinds\nGOOD friends\n"
- "BAD friday\nGOOD Friday\n"
- "BAD frmo\nGOOD from\n"
- "BAD fromthe\nGOOD from the\n"
- "COMPLETE 0\nBAD fromt he \nGOOD from the \n"
- "BAD furneral\nGOOD funeral\n"
- "BAD garantee\nGOOD guarantee\n"
- "BAD gaurd\nGOOD guard\n"
- "BAD gemeral\nGOOD general\n"
- "BAD gerat\nGOOD great\n"
- "BAD geting\nGOOD getting\n"
- "BAD gettin\nGOOD getting\n"
- "BAD gievn\nGOOD given\n"
- "BAD giveing\nGOOD giving\n"
- "BAD gloabl\nGOOD global\n"
- "BAD goign\nGOOD going\n"
- "BAD gonig\nGOOD going\n"
- "BAD govenment\nGOOD government\n"
- "BAD goverment\nGOOD government\n"
- "BAD gruop\nGOOD group\n"
- "BAD gruops\nGOOD groups\n"
- "BAD grwo\nGOOD grow\n"
- "BAD guidlines\nGOOD guidelines\n"
- "BAD hadbeen\nGOOD had been\n"
- "BAD hadnt\nGOOD hadn't\n"
- "COMPLETE 0\nBAD hadn;t \nGOOD hadn't \n"
- "BAD haev\nGOOD have\n"
- "BAD hapen\nGOOD happen\n"
- "BAD hapened\nGOOD happened\n"
- "BAD hapening\nGOOD happening\n"
- "BAD hapens\nGOOD happens\n"
- "BAD happend\nGOOD happened\n"
- "BAD hasbeen\nGOOD has been\n"
- "BAD hasnt\nGOOD hasn't\n"
- "COMPLETE 0\nBAD hasn;t \nGOOD hasn't \n"
- "BAD havebeen\nGOOD have been\n"
- "BAD haveing\nGOOD having\n"
- "BAD havent\nGOOD haven't\n"
- "COMPLETE 0\nBAD haven;t \nGOOD haven't \n"
- "BAD hearign\nGOOD hearing\n"
- "COMPLETE 0\nBAD he;d \nGOOD he'd \n"
- "BAD heirarchy\nGOOD hierarchy\n"
- "BAD hel\nGOOD he'll\n"
- "COMPLETE 0\nBAD he;ll \nGOOD he'll \n"
- "BAD helpfull\nGOOD helpful\n"
- "BAD herat\nGOOD heart\n"
- "BAD heres\nGOOD here's\n"
- "COMPLETE 0\nBAD here;s \nGOOD here's \n"
- "COMPLETE 0\nBAD he;s \nGOOD he's \n"
- "BAD hesaid\nGOOD he said\n"
- "BAD hewas\nGOOD he was\n"
- "BAD hismelf\nGOOD himself\n"
- "BAD hlep\nGOOD help\n"
- "BAD hows\nGOOD how's\n"
- "BAD htere\nGOOD there\n"
- "BAD htese\nGOOD these\n"
- "BAD htey\nGOOD they\n"
- "BAD hting\nGOOD thing\n"
- "BAD htink\nGOOD think\n"
- "BAD htis\nGOOD this\n"
- "COMPLETE 0\nBAD htp:\nGOOD http:\n"
- "COMPLETE 0\nBAD http:\\\\nGOOD http://\n"
- "BAD httpL\nGOOD http:\n"
- "BAD hvae\nGOOD have\n"
- "BAD hvaing\nGOOD having\n"
- "BAD hwich\nGOOD which\n"
- "COMPLETE 0\nBAD i c \nGOOD I see \n"
- "COMPLETE 0\nBAD i;d \nGOOD I'd \n"
- "COMPLETE 0\nBAD i'd \nGOOD I'd \n"
- "COMPLETE 0\nBAD I;d \nGOOD I'd \n"
- "BAD idae\nGOOD idea\n"
- "BAD idaes\nGOOD ideas\n"
- "BAD identofy\nGOOD identify\n"
- "BAD iits the\nGOOD it's the\n"
- "COMPLETE 0\nBAD i'll \nGOOD I'll \n"
- "COMPLETE 0\nBAD I;ll \nGOOD I'll \n"
- "COMPLETE 0\nBAD i;m \nGOOD I'm \n"
- "COMPLETE 0\nBAD i'm \nGOOD I'm \n"
- "COMPLETE 0\nBAD I\"m \nGOOD I'm \n"
- "BAD imediate\nGOOD immediate\n"
- "BAD imediatly\nGOOD immediately\n"
- "BAD immediatly\nGOOD immediately\n"
- "BAD importent\nGOOD important\n"
- "BAD importnat\nGOOD important\n"
- "BAD impossable\nGOOD impossible\n"
- "BAD improvemnt\nGOOD improvement\n"
- "BAD improvment\nGOOD improvement\n"
- "BAD includ\nGOOD include\n"
- "BAD indecate\nGOOD indicate\n"
- "BAD indenpendence\nGOOD independence\n"
- "BAD indenpendent\nGOOD independent\n"
- "BAD indepedent\nGOOD independent\n"
- "BAD independance\nGOOD independence\n"
- "BAD independant\nGOOD independent\n"
- "BAD influance\nGOOD influence\n"
- "BAD infomation\nGOOD information\n"
- "BAD informatoin\nGOOD information\n"
- "BAD inital\nGOOD initial\n"
- "BAD instaleld\nGOOD installed\n"
- "BAD insted\nGOOD instead\n"
- "BAD insurence\nGOOD insurance\n"
- "BAD inteh\nGOOD in the\n"
- "BAD interum\nGOOD interim\n"
- "BAD inthe\nGOOD in the\n"
- "COMPLETE 0\nBAD int he \nGOOD in the \n"
- "BAD inturn\nGOOD intern\n"
- "BAD inwhich\nGOOD in which\n"
- "COMPLETE 0\nBAD i snot \nGOOD is not \n"
- "BAD isnt\nGOOD isn't\n"
- "COMPLETE 0\nBAD isn;t \nGOOD isn't \n"
- "BAD isthe\nGOOD is the\n"
- "COMPLETE 0\nBAD it;d \nGOOD it'd \n"
- "BAD itis\nGOOD it is\n"
- "BAD ititial\nGOOD initial\n"
- "BAD itll\nGOOD it'll\n"
- "COMPLETE 0\nBAD it;ll \nGOOD it'll \n"
- "BAD itnerest\nGOOD interest\n"
- "BAD itnerested\nGOOD interested\n"
- "BAD itneresting\nGOOD interesting\n"
- "BAD itnerests\nGOOD interests\n"
- "COMPLETE 0\nBAD it;s \nGOOD it's \n"
- "BAD itsa\nGOOD it's a\n"
- "COMPLETE 0\nBAD its a \nGOOD it's a \n"
- "COMPLETE 0\nBAD it snot \nGOOD it's not \n"
- "COMPLETE 0\nBAD it' snot \nGOOD it's not \n"
- "COMPLETE 0\nBAD its the \nGOOD it's the \n"
- "BAD itwas\nGOOD it was\n"
- "COMPLETE 0\nBAD i;ve \nGOOD I've \n"
- "COMPLETE 0\nBAD i've \nGOOD I've \n"
- "BAD iwll\nGOOD will\n"
- "BAD iwth\nGOOD with\n"
- "BAD jsut\nGOOD just\n"
- "BAD jugment\nGOOD judgment\n"
- "BAD knowldge\nGOOD knowledge\n"
- "BAD knowlege\nGOOD knowledge\n"
- "BAD knwo\nGOOD know\n"
- "BAD knwon\nGOOD known\n"
- "BAD knwos\nGOOD knows\n"
- "BAD konw\nGOOD know\n"
- "BAD konwn\nGOOD known\n"
- "BAD konws\nGOOD knows\n"
- "BAD labratory\nGOOD laboratory\n"
- "BAD labtop\nGOOD laptop\n"
- "BAD lastyear\nGOOD last year\n"
- "BAD laterz\nGOOD later\n"
- "BAD learnign\nGOOD learning\n"
- "BAD lenght\nGOOD length\n"
- "COMPLETE 0\nBAD let;s \nGOOD let's \n"
- "COMPLETE 0\nBAD let's him \nGOOD lets him \n"
- "COMPLETE 0\nBAD let's it \nGOOD lets it \n"
- "BAD levle\nGOOD level\n"
- "BAD libary\nGOOD library\n"
- "BAD librarry\nGOOD library\n"
- "BAD librery\nGOOD library\n"
- "BAD liek\nGOOD like\n"
- "BAD liekd\nGOOD liked\n"
- "BAD lieutenent\nGOOD lieutenant\n"
- "BAD liev\nGOOD live\n"
- "BAD likly\nGOOD likely\n"
- "BAD lisense\nGOOD license\n"
- "BAD littel\nGOOD little\n"
- "BAD litttle\nGOOD little\n"
- "BAD liuke\nGOOD like\n"
- "BAD liveing\nGOOD living\n"
- "BAD loev\nGOOD love\n"
- "BAD lonly\nGOOD lonely\n"
- "BAD lookign\nGOOD looking\n"
- "BAD maintainence\nGOOD maintenance\n"
- "BAD maintenence\nGOOD maintenance\n"
- "BAD makeing\nGOOD making\n"
- "BAD managment\nGOOD management\n"
- "BAD mantain\nGOOD maintain\n"
- "BAD marraige\nGOOD marriage\n"
- "COMPLETE 0\nBAD may of been\nGOOD may have been\n"
- "COMPLETE 0\nBAD may of had\nGOOD may have had\n"
- "BAD memeber\nGOOD member\n"
- "BAD merchent\nGOOD merchant\n"
- "BAD mesage\nGOOD message\n"
- "BAD mesages\nGOOD messages\n"
- "COMPLETE 0\nBAD might of been\nGOOD might have been\n"
- "COMPLETE 0\nBAD might of had\nGOOD might have had\n"
- "BAD mispell\nGOOD misspell\n"
- "BAD mispelling\nGOOD misspelling\n"
- "BAD mispellings\nGOOD misspellings\n"
- "BAD mkae\nGOOD make\n"
- "BAD mkaes\nGOOD makes\n"
- "BAD mkaing\nGOOD making\n"
- "BAD moeny\nGOOD money\n"
- "BAD monday\nGOOD Monday\n"
- "BAD morgage\nGOOD mortgage\n"
- "BAD mroe\nGOOD more\n"
- "COMPLETE 0\nBAD must of been\nGOOD must have been\n"
- "COMPLETE 0\nBAD must of had\nGOOD must have had\n"
- "COMPLETE 0\nBAD mute point\nGOOD moot point\n"
- "BAD mysefl\nGOOD myself\n"
- "BAD naive\nGOOD naïve\n"
- "BAD ne1\nGOOD anyone\n"
- "BAD neway\nGOOD anyway\n"
- "BAD neways\nGOOD anyways\n"
- "BAD necassarily\nGOOD necessarily\n"
- "BAD necassary\nGOOD necessary\n"
- "BAD neccessarily\nGOOD necessarily\n"
- "BAD neccessary\nGOOD necessary\n"
- "BAD necesarily\nGOOD necessarily\n"
- "BAD necesary\nGOOD necessary\n"
- "BAD negotiaing\nGOOD negotiating\n"
- "BAD nkow\nGOOD know\n"
- "BAD nothign\nGOOD nothing\n"
- "BAD nver\nGOOD never\n"
- "BAD obediant\nGOOD obedient\n"
- "BAD ocasion\nGOOD occasion\n"
- "BAD occassion\nGOOD occasion\n"
- "BAD occurance\nGOOD occurrence\n"
- "BAD occured\nGOOD occurred\n"
- "BAD occurence\nGOOD occurrence\n"
- "BAD occurrance\nGOOD occurrence\n"
- "BAD oclock\nGOOD o'clock\n"
- "BAD oculd\nGOOD could\n"
- "BAD ocur\nGOOD occur\n"
- "BAD oeprator\nGOOD operator\n"
- "BAD ofits\nGOOD of its\n"
- "BAD ofthe\nGOOD of the\n"
- "BAD oft he\nGOOD of the\n"
- "BAD oging\nGOOD going\n"
- "BAD ohter\nGOOD other\n"
- "BAD omre\nGOOD more\n"
- "BAD oneof\nGOOD one of\n"
- "BAD onepoint\nGOOD one point\n"
- "BAD onthe\nGOOD on the\n"
- "COMPLETE 0\nBAD ont he \nGOOD on the \n"
- "BAD onyl\nGOOD only\n"
- "BAD oppasite\nGOOD opposite\n"
- "BAD opperation\nGOOD operation\n"
- "BAD oppertunity\nGOOD opportunity\n"
- "BAD opposate\nGOOD opposite\n"
- "BAD opposible\nGOOD opposable\n"
- "BAD opposit\nGOOD opposite\n"
- "BAD oppotunities\nGOOD opportunities\n"
- "BAD oppotunity\nGOOD opportunity\n"
- "BAD orginization\nGOOD organization\n"
- "BAD orginized\nGOOD organized\n"
- "BAD otehr\nGOOD other\n"
- "BAD outof\nGOOD out of\n"
- "BAD overthe\nGOOD over the\n"
- "BAD owrk\nGOOD work\n"
- "BAD owuld\nGOOD would\n"
- "BAD oxident\nGOOD oxidant\n"
- "BAD papaer\nGOOD paper\n"
- "BAD passe\nGOOD pass\303\251\n"
- "BAD parliment\nGOOD parliament\n"
- "BAD partof\nGOOD part of\n"
- "BAD paymetn\nGOOD payment\n"
- "BAD paymetns\nGOOD payments\n"
- "BAD pciture\nGOOD picture\n"
- "BAD peice\nGOOD piece\n"
- "BAD peices\nGOOD pieces\n"
- "BAD peolpe\nGOOD people\n"
- "BAD peopel\nGOOD people\n"
- "BAD percentof\nGOOD percent of\n"
- "BAD percentto\nGOOD percent to\n"
- "BAD performence\nGOOD performance\n"
- "BAD perhasp\nGOOD perhaps\n"
- "BAD perhpas\nGOOD perhaps\n"
- "BAD permanant\nGOOD permanent\n"
- "BAD perminent\nGOOD permanent\n"
- "BAD personalyl\nGOOD personally\n"
- "BAD pleasent\nGOOD pleasant\n"
- "BAD pls\nGOOD please\n"
- "BAD plz\nGOOD please\n"
- "BAD poeple\nGOOD people\n"
- "BAD porblem\nGOOD problem\n"
- "BAD porblems\nGOOD problems\n"
- "BAD porvide\nGOOD provide\n"
- "BAD possable\nGOOD possible\n"
- "BAD postition\nGOOD position\n"
- "BAD potatoe\nGOOD potato\n"
- "BAD potatos\nGOOD potatoes\n"
- "BAD potentialy\nGOOD potentially\n"
- "BAD ppl\nGOOD people\n"
- "BAD pregnent\nGOOD pregnant\n"
- "BAD presance\nGOOD presence\n"
- "BAD primative\nGOOD primitive\n"
- "BAD probally\nGOOD probably\n"
- "BAD probelm\nGOOD problem\n"
- "BAD probelms\nGOOD problems\n"
- "BAD probly\nGOOD probably\n"
- "BAD prolly\nGOOD probably\n"
- "BAD proly\nGOOD probably\n"
- "BAD prominant\nGOOD prominent\n"
- "BAD proposterous\nGOOD preposterous\n"
- "BAD protege\nGOOD prot\303\251g\303\251\n"
- "BAD protoge\nGOOD prot\303\251g\303\251\n"
- "BAD psoition\nGOOD position\n"
- "BAD ptogress\nGOOD progress\n"
- "BAD pursuade\nGOOD persuade\n"
- "BAD puting\nGOOD putting\n"
- "BAD pwoer\nGOOD power\n"
- "BAD quater\nGOOD quarter\n"
- "BAD quaters\nGOOD quarters\n"
- "BAD quesion\nGOOD question\n"
- "BAD quesions\nGOOD questions\n"
- "BAD questioms\nGOOD questions\n"
- "BAD questiosn\nGOOD questions\n"
- "BAD questoin\nGOOD question\n"
- "BAD quetion\nGOOD question\n"
- "BAD quetions\nGOOD questions\n"
- "BAD raeson\nGOOD reason\n"
- "BAD realyl\nGOOD really\n"
- "BAD reccomend\nGOOD recommend\n"
- "BAD reccommend\nGOOD recommend\n"
- "BAD receieve\nGOOD receive\n"
- "BAD recieve\nGOOD receive\n"
- "BAD recieved\nGOOD received\n"
- "BAD recieving\nGOOD receiving\n"
- "BAD recomend\nGOOD recommend\n"
- "BAD recomendation\nGOOD recommendation\n"
- "BAD recomendations\nGOOD recommendations\n"
- "BAD recomended\nGOOD recommended\n"
- "BAD reconize\nGOOD recognize\n"
- "BAD recrod\nGOOD record\n"
- "BAD rediculous\nGOOD ridiculous\n"
- "BAD rediculus\nGOOD ridiculous\n"
- "BAD reguard\nGOOD regard\n"
- "BAD religous\nGOOD religious\n"
- "BAD reluctent\nGOOD reluctant\n"
- "BAD remeber\nGOOD remember\n"
- "BAD reommend\nGOOD recommend\n"
- "BAD representativs\nGOOD representatives\n"
- "BAD representives\nGOOD representatives\n"
- "BAD represetned\nGOOD represented\n"
- "BAD represnt\nGOOD represent\n"
- "BAD reserach\nGOOD research\n"
- "BAD resollution\nGOOD resolution\n"
- "BAD resorces\nGOOD resources\n"
- "BAD respomd\nGOOD respond\n"
- "BAD respomse\nGOOD response\n"
- "BAD responce\nGOOD response\n"
- "BAD responsability\nGOOD responsibility\n"
- "BAD responsable\nGOOD responsible\n"
- "BAD responsibile\nGOOD responsible\n"
- "BAD responsiblity\nGOOD responsibility\n"
- "BAD restaraunt\nGOOD restaurant\n"
- "BAD restuarant\nGOOD restaurant\n"
- "BAD reult\nGOOD result\n"
- "BAD reveiw\nGOOD review\n"
- "BAD reveiwing\nGOOD reviewing\n"
- "BAD rumers\nGOOD rumors\n"
- "BAD rwite\nGOOD write\n"
- "BAD rythm\nGOOD rhythm\n"
- "BAD saidhe\nGOOD said he\n"
- "BAD saidit\nGOOD said it\n"
- "BAD saidthat\nGOOD said that\n"
- "BAD saidthe\nGOOD said the\n"
- "COMPLETE 0\nBAD saidt he \nGOOD said the \n"
- "BAD sandwhich\nGOOD sandwich\n"
- "BAD sandwitch\nGOOD sandwich\n"
- "BAD saturday\nGOOD Saturday\n"
- "BAD scedule\nGOOD schedule\n"
- "BAD sceduled\nGOOD scheduled\n"
- "BAD seance\nGOOD s\303\251ance\n"
- "BAD secratary\nGOOD secretary\n"
- "BAD sectino\nGOOD section\n"
- "BAD selectoin\nGOOD selection\n"
- "BAD sence\nGOOD sense\n"
- "BAD sentance\nGOOD sentence\n"
- "BAD separeate\nGOOD separate\n"
- "BAD seperate\nGOOD separate\n"
- "BAD sercumstances\nGOOD circumstances\n"
- "BAD shcool\nGOOD school\n"
- "COMPLETE 0\nBAD she;d \nGOOD she'd \n"
- "COMPLETE 0\nBAD she;ll \nGOOD she'll \n"
- "BAD shes\nGOOD she's\n"
- "COMPLETE 0\nBAD she;s \nGOOD she's \n"
- "BAD shesaid\nGOOD she said\n"
- "BAD shineing\nGOOD shining\n"
- "BAD shiped\nGOOD shipped\n"
- "BAD shoudl\nGOOD should\n"
- "COMPLETE 0\nBAD shoudln't \nGOOD shouldn't \n"
- "BAD shouldent\nGOOD shouldn't\n"
- "BAD shouldnt\nGOOD shouldn't\n"
- "COMPLETE 0\nBAD shouldn;t \nGOOD shouldn't \n"
- "COMPLETE 0\nBAD should of been\nGOOD should have been\n"
- "COMPLETE 0\nBAD should of had\nGOOD should have had\n"
- "BAD shouldve\nGOOD should've\n"
- "BAD showinf\nGOOD showing\n"
- "BAD signifacnt\nGOOD significant\n"
- "BAD simalar\nGOOD similar\n"
- "BAD similiar\nGOOD similar\n"
- "BAD simpyl\nGOOD simply\n"
- "BAD sincerly\nGOOD sincerely\n"
- "BAD sitll\nGOOD still\n"
- "BAD smae\nGOOD same\n"
- "BAD smoe\nGOOD some\n"
- "BAD soem\nGOOD some\n"
- "BAD sohw\nGOOD show\n"
- "BAD soical\nGOOD social\n"
- "BAD some1\nGOOD someone\n"
- "BAD somethign\nGOOD something\n"
- "BAD someting\nGOOD something\n"
- "BAD somewaht\nGOOD somewhat\n"
- "BAD somthing\nGOOD something\n"
- "BAD somtimes\nGOOD sometimes\n"
- "COMPLETE 0\nBAD sot hat \nGOOD so that \n"
- "BAD soudn\nGOOD sound\n"
- "BAD soudns\nGOOD sounds\n"
- "BAD speach\nGOOD speech\n"
- "BAD specificaly\nGOOD specifically\n"
- "BAD specificalyl\nGOOD specifically\n"
- "BAD spelt\nGOOD spelled\n"
- "BAD sry\nGOOD sorry\n"
- "COMPLETE 0\nBAD state of the ark\nGOOD state of the art\n"
- "BAD statment\nGOOD statement\n"
- "BAD statments\nGOOD statements\n"
- "BAD stnad\nGOOD stand\n"
- "BAD stopry\nGOOD story\n"
- "BAD stoyr\nGOOD story\n"
- "BAD stpo\nGOOD stop\n"
- "BAD strentgh\nGOOD strength\n"
- "BAD stroy\nGOOD story\n"
- "BAD struggel\nGOOD struggle\n"
- "BAD strugle\nGOOD struggle\n"
- "BAD studnet\nGOOD student\n"
- "BAD successfull\nGOOD successful\n"
- "BAD successfuly\nGOOD successfully\n"
- "BAD successfulyl\nGOOD successfully\n"
- "BAD sucess\nGOOD success\n"
- "BAD sucessfull\nGOOD successful\n"
- "BAD sufficiant\nGOOD sufficient\n"
- "BAD sum1\nGOOD someone\n"
- "BAD sunday\nGOOD Sunday\n"
- "BAD suposed\nGOOD supposed\n"
- "BAD supposably\nGOOD supposedly\n"
- "BAD suppossed\nGOOD supposed\n"
- "BAD suprise\nGOOD surprise\n"
- "BAD suprised\nGOOD surprised\n"
- "BAD sux\nGOOD sucks\n"
- "BAD swiming\nGOOD swimming\n"
- "BAD tahn\nGOOD than\n"
- "BAD taht\nGOOD that\n"
- "COMPLETE 0\nBAD take it for granite\nGOOD take it for granted\n"
- "COMPLETE 0\nBAD taken for granite\nGOOD taken for granted\n"
- "BAD talekd\nGOOD talked\n"
- "BAD talkign\nGOOD talking\n"
- "BAD tath\nGOOD that\n"
- "BAD tecnical\nGOOD technical\n"
- "BAD tehy\nGOOD they\n"
- "COMPLETE 0\nBAD tellt he \nGOOD tell the \n"
- "BAD termoil\nGOOD turmoil\n"
- "BAD tets\nGOOD test\n"
- "BAD tghis\nGOOD this\n"
- "BAD thansk\nGOOD thanks\n"
- "BAD thanx\nGOOD thanks\n"
- "BAD thats\nGOOD that's\n"
- "BAD thatthe\nGOOD that the\n"
- "COMPLETE 0\nBAD thatt he \nGOOD that the \n"
- "BAD thecompany\nGOOD the company\n"
- "BAD thefirst\nGOOD the first\n"
- "BAD thegovernment\nGOOD the government\n"
- "COMPLETE 0\nBAD their are \nGOOD there are \n"
- "COMPLETE 0\nBAD their aren't \nGOOD there aren't \n"
- "COMPLETE 0\nBAD their is \nGOOD there is \n"
- "BAD themself\nGOOD themselves\n"
- "BAD themselfs\nGOOD themselves\n"
- "BAD thenew\nGOOD the new\n"
- "BAD theres\nGOOD there's\n"
- "COMPLETE 0\nBAD there's is \nGOOD theirs is \n"
- "COMPLETE 0\nBAD there's isn't \nGOOD theirs isn't \n"
- "BAD theri\nGOOD their\n"
- "BAD thesame\nGOOD the same\n"
- "BAD thetwo\nGOOD the two\n"
- "BAD theyd\nGOOD they'd\n"
- "COMPLETE 0\nBAD they;d \nGOOD they'd \n"
- "COMPLETE 0\nBAD they;l \nGOOD they'll \n"
- "BAD theyll\nGOOD they'll\n"
- "COMPLETE 0\nBAD they;ll \nGOOD they'll \n"
- "COMPLETE 0\nBAD they;r \nGOOD they're \n"
- "COMPLETE 0\nBAD theyre \nGOOD they're \n"
- "COMPLETE 0\nBAD they;re \nGOOD they're \n"
- "COMPLETE 0\nBAD they're are \nGOOD there are \n"
- "COMPLETE 0\nBAD they're is \nGOOD there is \n"
- "COMPLETE 0\nBAD they;v \nGOOD they've \n"
- "BAD theyve\nGOOD they've\n"
- "COMPLETE 0\nBAD they;ve \nGOOD they've \n"
- "BAD thgat\nGOOD that\n"
- "BAD thier\nGOOD their \n"
- "BAD thigsn\nGOOD things\n"
- "BAD thisyear\nGOOD this year\n"
- "BAD thme\nGOOD them\n"
- "BAD thna\nGOOD than\n"
- "BAD thne\nGOOD then\n"
- "BAD thnig\nGOOD thing\n"
- "BAD thnigs\nGOOD things\n"
- "BAD tho\nGOOD though\n"
- "BAD threatend\nGOOD threatened\n"
- "BAD thsi\nGOOD this\n"
- "BAD thsoe\nGOOD those\n"
- "BAD thta\nGOOD that\n"
- "BAD thursday\nGOOD Thursday\n"
- "BAD thx\nGOOD thanks\n"
- "BAD tihs\nGOOD this\n"
- "BAD timne\nGOOD time\n"
- "BAD tiogether\nGOOD together\n"
- "BAD tkae\nGOOD take\n"
- "BAD tkaes\nGOOD takes\n"
- "BAD tkaing\nGOOD taking\n"
- "BAD tlaking\nGOOD talking\n"
- "BAD tnx\nGOOD thanks\n"
- "BAD todya\nGOOD today\n"
- "BAD togehter\nGOOD together\n"
- "COMPLETE 0\nBAD toldt he \nGOOD told the \n"
- "BAD tomorow\nGOOD tomorrow\n"
- "BAD tongiht\nGOOD tonight\n"
- "BAD tonihgt\nGOOD tonight\n"
- "BAD tonite\nGOOD tonight\n"
- "BAD totaly\nGOOD totally\n"
- "BAD totalyl\nGOOD totally\n"
- "BAD tothe\nGOOD to the\n"
- "COMPLETE 0\nBAD tot he \nGOOD to the \n"
- "BAD touche\nGOOD touch\303\251\n"
- "BAD towrad\nGOOD toward\n"
- "BAD traditionalyl\nGOOD traditionally\n"
- "BAD transfered\nGOOD transferred\n"
- "BAD truely\nGOOD truly\n"
- "BAD truley\nGOOD truly\n"
- "BAD tryed\nGOOD tried\n"
- "BAD tuesday\nGOOD Tuesday\n"
- "BAD tyhat\nGOOD that\n"
- "BAD udnerstand\nGOOD understand\n"
- "BAD understnad\nGOOD understand\n"
- "COMPLETE 0\nBAD undert he \nGOOD under the \n"
- "BAD unforseen\nGOOD unforeseen\n"
- "BAD UnitedStates\nGOOD United States\n"
- "BAD unliek\nGOOD unlike\n"
- "BAD unpleasently\nGOOD unpleasantly\n"
- "BAD untill\nGOOD until\n"
- "BAD untilll\nGOOD until\n"
- "BAD ur\nGOOD you are\n"
- "BAD useing\nGOOD using\n"
- "BAD usualyl\nGOOD usually\n"
- "BAD veyr\nGOOD very\n"
- "BAD virtualyl\nGOOD virtually\n"
- "BAD visavis\nGOOD vis-a-vis\n"
- "COMPLETE 0\nBAD vis-a-vis\nGOOD vis-\303\240-vis\n"
- "BAD vrey\nGOOD very\n"
- "BAD vulnerible\nGOOD vulnerable\n"
- "BAD waht\nGOOD what\n"
- "BAD warrent\nGOOD warrant\n"
- "COMPLETE 0\nBAD wa snot \nGOOD was not \n"
- "COMPLETE 0\nBAD wasnt \nGOOD wasn't \n"
- "COMPLETE 0\nBAD wasn;t \nGOOD wasn't \n"
- "BAD watn\nGOOD want\n"
- "COMPLETE 0\nBAD we;d \nGOOD we'd \n"
- "BAD wednesday\nGOOD Wednesday\n"
- "BAD wehn\nGOOD when\n"
- "COMPLETE 0\nBAD we'l \nGOOD we'll \n"
- "COMPLETE 0\nBAD we;ll \nGOOD we'll \n"
- "COMPLETE 0\nBAD we;re \nGOOD we're \n"
- "BAD werent\nGOOD weren't\n"
- "COMPLETE 0\nBAD weren;t \nGOOD weren't \n"
- "COMPLETE 0\nBAD wern't \nGOOD weren't \n"
- "BAD werre\nGOOD were\n"
- "BAD weve\nGOOD we've\n"
- "COMPLETE 0\nBAD we;ve \nGOOD we've \n"
- "BAD whats\nGOOD what's\n"
- "COMPLETE 0\nBAD what;s \nGOOD what's \n"
- "BAD whcih\nGOOD which\n"
- "COMPLETE 0\nBAD whent he \nGOOD when the \n"
- "BAD wheres\nGOOD where's\n"
- "COMPLETE 0\nBAD where;s \nGOOD where's \n"
- "BAD wherre\nGOOD where\n"
- "BAD whic\nGOOD which\n"
- "COMPLETE 0\nBAD whicht he \nGOOD which the \n"
- "BAD whihc\nGOOD which\n"
- "BAD wholl\nGOOD who'll\n"
- "BAD whos\nGOOD who's\n"
- "COMPLETE 0\nBAD who;s \nGOOD who's \n"
- "BAD whove\nGOOD who've\n"
- "COMPLETE 0\nBAD who;ve \nGOOD who've \n"
- "BAD whta\nGOOD what\n"
- "BAD whys\nGOOD why's\n"
- "BAD wief\nGOOD wife\n"
- "BAD wierd\nGOOD weird\n"
- "BAD wihch\nGOOD which\n"
- "BAD wiht\nGOOD with\n"
- "BAD willbe\nGOOD will be\n"
- "COMPLETE 0\nBAD will of been\nGOOD will have been\n"
- "COMPLETE 0\nBAD will of had\nGOOD will have had\n"
- "BAD windoes\nGOOD windows\n"
- "BAD witha\nGOOD with a\n"
- "BAD withdrawl\nGOOD withdrawal\n"
- "BAD withe\nGOOD with\n"
- "COMPLETE 0\nBAD withthe \nGOOD with the \n"
- "BAD witht he\nGOOD with the\n"
- "BAD wiull\nGOOD will\n"
- "BAD wnat\nGOOD want\n"
- "BAD wnated\nGOOD wanted\n"
- "BAD wnats\nGOOD wants\n"
- "BAD wohle\nGOOD whole\n"
- "BAD wokr\nGOOD work\n"
- "BAD wont\nGOOD won't\n"
- "COMPLETE 0\nBAD wo'nt \nGOOD won't \n"
- "COMPLETE 0\nBAD won;t \nGOOD won't \n"
- "BAD woudl\nGOOD would\n"
- "COMPLETE 0\nBAD woudln't \nGOOD wouldn't \n"
- "BAD wouldbe\nGOOD would be\n"
- "BAD wouldnt\nGOOD wouldn't\n"
- "COMPLETE 0\nBAD wouldn;t \nGOOD wouldn't \n"
- "COMPLETE 0\nBAD would of been\nGOOD would have been\n"
- "COMPLETE 0\nBAD would of had\nGOOD would have had\n"
- "BAD wouldve\nGOOD would've\n"
- "BAD wriet\nGOOD write\n"
- "BAD writting\nGOOD writing\n"
- "BAD wrod\nGOOD word\n"
- "BAD wroet\nGOOD wrote\n"
- "BAD wroking\nGOOD working\n"
- "BAD wtih\nGOOD with\n"
- "BAD wuould\nGOOD would\n"
- "BAD wud\nGOOD would\n"
- "BAD yera\nGOOD year\n"
- "BAD yeras\nGOOD years\n"
- "BAD yersa\nGOOD years\n"
- "BAD youare\nGOOD you are\n"
- "BAD youd\nGOOD you'd\n"
- "COMPLETE 0\nBAD you;d \nGOOD you'd \n"
- "BAD youll\nGOOD you'll\n"
- "COMPLETE 0\nBAD your a \nGOOD you're a \n"
- "COMPLETE 0\nBAD your an \nGOOD you're an \n"
- "BAD youre\nGOOD you're\n"
- "COMPLETE 0\nBAD you;re \nGOOD you're \n"
- "COMPLETE 0\nBAD you're own \nGOOD your own \n"
- "COMPLETE 0\nBAD your her \nGOOD you're her \n"
- "COMPLETE 0\nBAD your here \nGOOD you're here \n"
- "COMPLETE 0\nBAD your his \nGOOD you're his \n"
- "COMPLETE 0\nBAD your my \nGOOD you're my \n"
- "COMPLETE 0\nBAD your the \nGOOD you're the \n"
- "COMPLETE 0\nBAD your their \nGOOD you're their \n"
- "COMPLETE 0\nBAD your your \nGOOD you're your \n"
- "BAD youve\nGOOD you've\n"
- "COMPLETE 0\nBAD you;ve \nGOOD you've \n"
- "BAD yuor\nGOOD your\n";
- gboolean complete = TRUE;
- gboolean case_sensitive = FALSE;
- buf = g_build_filename(purple_config_dir(), "dict", NULL);
- if (!(g_file_get_contents(buf, &ibuf, &size, NULL) && ibuf)) {
- ibuf = g_strdup(defaultconf);
- size = strlen(defaultconf);
- model = gtk_list_store_new((gint)N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
- hashes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- while (ibuf && buf_get_line(ibuf, &buf, &pnt, size)) {
- if (!g_ascii_strncasecmp(buf, "BAD ", 4))
- strncpy(bad, buf + 4, 81);
- else if(!g_ascii_strncasecmp(buf, "CASE ", 5))
- case_sensitive = *(buf+5) == '0' ? FALSE : TRUE;
- else if(!g_ascii_strncasecmp(buf, "COMPLETE ", 9))
- complete = *(buf+9) == '0' ? FALSE : TRUE;
- else if (!g_ascii_strncasecmp(buf, "GOOD ", 5))
- strncpy(good, buf + 5, 255);
- if (*bad && *good && g_hash_table_lookup(hashes, bad) == NULL) {
- /* We don't actually need to store the good string, since this
- * hash is just being used to eliminate duplicate bad strings.
- * The value has to be non-NULL so the lookup above will work.
- g_hash_table_insert(hashes, g_strdup(bad), GINT_TO_POINTER(1));
- gtk_list_store_append(model, &iter);
- gtk_list_store_set(model, &iter,
- WORD_ONLY_COLUMN, complete,
- CASE_SENSITIVE_COLUMN, case_sensitive,
- case_sensitive = FALSE;
- g_hash_table_destroy(hashes);
- gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
- 0, GTK_SORT_ASCENDING);
-static GtkWidget *bad_entry;
-static GtkWidget *good_entry;
-static GtkWidget *complete_toggle;
-static GtkWidget *case_toggle;
-static void save_list(void);
-static void on_edited(GtkCellRendererText *cellrenderertext,
- gchar *path, gchar *arg2, gpointer data)
- g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GPOINTER_TO_INT(data), &val);
- if (!purple_strequal(arg2, g_value_get_string(&val))) {
- gtk_list_store_set(model, &iter, GPOINTER_TO_INT(data), arg2, -1);
-static void word_only_toggled(GtkCellRendererToggle *cellrenderertoggle,
- gchar *path, gpointer data){
- g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
- gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
- WORD_ONLY_COLUMN, &enabled,
- gtk_list_store_set(GTK_LIST_STORE(model), &iter,
- WORD_ONLY_COLUMN, !enabled,
- /* I want to be sure that the above change has happened to the GtkTreeView first. */
- gtk_list_store_set(GTK_LIST_STORE(model), &iter,
- CASE_SENSITIVE_COLUMN, enabled,
-static void case_sensitive_toggled(GtkCellRendererToggle *cellrenderertoggle,
- gchar *path, gpointer data){
- g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
- /* Prevent the case sensitive column from changing on non-whole word replacements.
- * Ideally, the column would be set insensitive in the word_only_toggled callback. */
- gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
- WORD_ONLY_COLUMN, &enabled,
- gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
- CASE_SENSITIVE_COLUMN, &enabled,
- gtk_list_store_set(GTK_LIST_STORE(model), &iter,
- CASE_SENSITIVE_COLUMN, !enabled,
-static void list_add_new(void)
- const char *word = gtk_editable_get_text(GTK_EDITABLE(bad_entry));
- gboolean case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(case_toggle));
- if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
- char *tmpword = g_utf8_casefold(word, -1);
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &bad_val);
- GValue case_sensitive_val;
- case_sensitive_val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &case_sensitive_val);
- /* If they're both case-sensitive, then compare directly.
- * Otherwise, they overlap. */
- if (g_value_get_boolean(&case_sensitive_val))
- match = purple_strequal(g_value_get_string(&bad_val), word);
- char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1);
- match = purple_strequal(bad, tmpword);
- g_value_unset(&case_sensitive_val);
- char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1);
- match = purple_strequal(bad, tmpword);
- g_value_unset(&bad_val);
- purple_notify_error(NULL, _("Duplicate Correction"),
- _("The specified word already exists in the correction list."),
- gtk_editable_get_text(GTK_EDITABLE(bad_entry)), NULL);
- g_value_unset(&bad_val);
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
- gtk_list_store_append(model, &iter);
- gtk_list_store_set(model, &iter,
- GOOD_COLUMN, gtk_editable_get_text(GTK_EDITABLE(good_entry)),
- WORD_ONLY_COLUMN, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(complete_toggle)),
- CASE_SENSITIVE_COLUMN, case_sensitive,
- gtk_editable_delete_text(GTK_EDITABLE(bad_entry), 0, -1);
- gtk_editable_delete_text(GTK_EDITABLE(good_entry), 0, -1);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE);
- gtk_widget_grab_focus(bad_entry);
-static void add_selected_row_to_list(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gpointer data)
- GtkTreeRowReference *row_reference;
- GSList **list = (GSList **)data;
- row_reference = gtk_tree_row_reference_new(model, path);
- *list = g_slist_prepend(*list, row_reference);
-static void remove_row(gpointer data)
- GtkTreeRowReference *row_reference = (GtkTreeRowReference *)data;
- path = gtk_tree_row_reference_get_path(row_reference);
- if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
- gtk_list_store_remove(model, &iter);
- gtk_tree_path_free(path);
- gtk_tree_row_reference_free(row_reference);
-static void list_delete(void)
- sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
- gtk_tree_selection_selected_foreach(sel, add_selected_row_to_list, &list);
- g_slist_free_full(list, remove_row);
- data = g_string_new("");
- if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val0);
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val1);
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val2);
- gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val3);
- g_string_append_printf(data, "COMPLETE %d\nCASE %d\nBAD %s\nGOOD %s\n\n",
- g_value_get_boolean(&val2),
- g_value_get_boolean(&val3),
- g_value_get_string(&val0),
- g_value_get_string(&val1));
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
- purple_util_write_data_to_config_file("dict", data->str, -1);
- g_string_free(data, TRUE);
-static void on_selection_changed(GtkTreeSelection *sel,
- num_selected = gtk_tree_selection_count_selected_rows(sel);
- gtk_widget_set_sensitive((GtkWidget*)data, (num_selected > 0));
-static gboolean non_empty(const char *s)
- while (*s && g_ascii_isspace(*s))
-static void on_entry_changed(GtkEditable *editable, gpointer data)
- gtk_widget_set_sensitive((GtkWidget*)data,
- non_empty(gtk_editable_get_text(GTK_EDITABLE(bad_entry))) &&
- non_empty(gtk_editable_get_text(GTK_EDITABLE(good_entry))));
-static void whole_words_button_toggled(GtkToggleButton *complete_toggle, GtkToggleButton *case_toggle)
- gboolean enabled = gtk_toggle_button_get_active(complete_toggle);
- gtk_toggle_button_set_active(case_toggle, !enabled);
- gtk_widget_set_sensitive(GTK_WIDGET(case_toggle), enabled);
-get_config_frame(PurplePlugin *plugin)
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
- ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- gtk_container_set_border_width (GTK_CONTAINER(ret), 12);
- vbox = pidgin_make_frame(ret, _("Text Replacements"));
- gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
- tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
- gtk_widget_set_size_request(tree, -1, 200);
- renderer = gtk_cell_renderer_text_new();
- g_object_set(G_OBJECT(renderer),
- g_signal_connect(G_OBJECT(renderer), "edited",
- G_CALLBACK(on_edited), GINT_TO_POINTER(0));
- column = gtk_tree_view_column_new_with_attributes(_("You type"), renderer,
- gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
- gtk_tree_view_column_set_fixed_width(column, 150);
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
- renderer = gtk_cell_renderer_text_new();
- g_object_set(G_OBJECT(renderer),
- g_signal_connect(G_OBJECT(renderer), "edited",
- G_CALLBACK(on_edited), GINT_TO_POINTER(1));
- column = gtk_tree_view_column_new_with_attributes(_("You send"), renderer,
- gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
- gtk_tree_view_column_set_fixed_width(column, 150);
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
- renderer = gtk_cell_renderer_toggle_new();
- g_object_set(G_OBJECT(renderer),
- g_signal_connect(G_OBJECT(renderer), "toggled",
- G_CALLBACK(word_only_toggled), NULL);
- column = gtk_tree_view_column_new_with_attributes(_("Whole words only"), renderer,
- "active", WORD_ONLY_COLUMN,
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
- renderer = gtk_cell_renderer_toggle_new();
- g_object_set(G_OBJECT(renderer),
- g_signal_connect(G_OBJECT(renderer), "toggled",
- G_CALLBACK(case_sensitive_toggled), NULL);
- column = gtk_tree_view_column_new_with_attributes(_("Case sensitive"), renderer,
- "active", CASE_SENSITIVE_COLUMN,
- gtk_tree_view_column_set_resizable(column, TRUE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
- gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)),
- GTK_SELECTION_MULTIPLE);
- sw = gtk_scrolled_window_new();
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER,
- gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), tree);
- gtk_widget_set_vexpand(sw, TRUE);
- gtk_box_append(GTK_BOX(vbox), sw);
- hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
- button = gtk_button_new_with_mnemonic(_("_Delete"));
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(list_delete), NULL);
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- gtk_widget_set_sensitive(button, FALSE);
- g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree))),
- "changed", G_CALLBACK(on_selection_changed), button);
- gtk_widget_show(button);
- vbox = pidgin_make_frame(ret, _("Add a new text replacement"));
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
- vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
- gtk_widget_show(vbox2);
- sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
- bad_entry = gtk_entry_new();
- /* Set a minimum size. Since they're in a size group, the other entry will match up. */
- gtk_widget_set_size_request(bad_entry, 350, -1);
- gtk_size_group_add_widget(sg2, bad_entry);
- pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _type:"), sg, bad_entry, FALSE, NULL);
- good_entry = gtk_entry_new();
- gtk_size_group_add_widget(sg2, good_entry);
- pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _send:"), sg, good_entry, FALSE, NULL);
- /* Created here so it can be passed to whole_words_button_toggled. */
- case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)"));
- complete_toggle = gtk_check_button_new_with_mnemonic(_("Only replace _whole words"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE);
- g_signal_connect(G_OBJECT(complete_toggle), "clicked",
- G_CALLBACK(whole_words_button_toggled), case_toggle);
- gtk_widget_show(complete_toggle);
- gtk_box_pack_start(GTK_BOX(vbox2), complete_toggle, FALSE, FALSE, 0);
- /* The button is created above so it can be passed to whole_words_button_toggled. */
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE);
- gtk_widget_show(case_toggle);
- gtk_box_pack_start(GTK_BOX(vbox2), case_toggle, FALSE, FALSE, 0);
- button = gtk_button_new_with_mnemonic(_("_Add"));
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(list_add_new), NULL);
- vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_box_pack_start(GTK_BOX(hbox), vbox3, TRUE, FALSE, 0);
- gtk_widget_show(vbox3);
- gtk_box_pack_end(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
- g_signal_connect(G_OBJECT(bad_entry), "changed", G_CALLBACK(on_entry_changed), button);
- g_signal_connect(G_OBJECT(good_entry), "changed", G_CALLBACK(on_entry_changed), button);
- gtk_widget_set_sensitive(button, FALSE);
- gtk_widget_show(button);
- vbox = pidgin_make_frame(ret, _("General Text Replacement Options"));
- pidgin_prefs_checkbox(_("Enable replacement of last word on send"),
- "/plugins/gtk/spellchk/last_word_replace", vbox);
- gtk_widget_show_all(ret);
-static GPluginPluginInfo *
-spell_check_query(GError **error)
- const gchar * const authors[] = {
- "Eric Warmenhoven <eric@warmenhoven.org>",
- return pidgin_plugin_info_new(
- "id", SPELLCHECK_PLUGIN_ID,
- "name", N_("Text replacement"),
- "version", DISPLAY_VERSION,
- "category", N_("Utility"),
- "summary", N_("Replaces text in outgoing messages according to user-defined rules."),
- "description", N_("Replaces text in outgoing messages according to user-defined rules."),
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "gtk-config-frame-cb", get_config_frame,
-spell_check_load(GPluginPlugin *plugin, GError **error)
- void *conv_handle = purple_conversations_get_handle();
- purple_prefs_add_none("/plugins");
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none("/plugins/gtk/spellchk");
- purple_prefs_add_bool("/plugins/gtk/spellchk/last_word_replace", TRUE);
- /* Attach to existing conversations */
- for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next)
- spellchk_new_attach((PurpleConversation *)convs->data);
- purple_signal_connect(conv_handle, "conversation-created",
- plugin, G_CALLBACK(spellchk_new_attach), NULL);
-spell_check_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
- /* Detach from existing conversations */
- for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next)
- PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)convs->data);
- spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY);
- g_signal_handlers_disconnect_by_func(gtkconv->entry, message_send_cb, spell);
- g_object_set_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY, NULL);
-GPLUGIN_NATIVE_PLUGIN_DECLARE(spell_check)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/spellchk/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,8 @@
+spellchk = library('spellchk', 'spellchk.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-SpellCheck"'], + dependencies : [libpurple_dep, libpidgin_dep, glib], + build_by_default: false, + install : false, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/spellchk/spellchk.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,2341 @@
+ * Purple - Replace certain misspelled words with their correct form. + * Signification changes were made by Benjamin Kahn ("xkahn") and + * Richard Laager ("rlaager") in April 2005--you may want to contact + * them if you have questions. + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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 + * A lot of this code (especially the config code) was taken directly + * or nearly directly from xchat, version 1.4.2 by Peter Zelezny and others. +#include <glib/gi18n-lib.h> +#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck" +#define SPELLCHK_OBJECT_KEY "spellchk" + GtkTextMark *mark_insert_start; + GtkTextMark *mark_insert_end; + gboolean ignore_correction; + gboolean ignore_correction_on_send; +typedef struct _spellchk spellchk; +static GtkListStore *model; +is_word_uppercase(const gchar *word) + for (; word[0] != '\0'; word = g_utf8_find_next_char (word, NULL)) { + gunichar c = g_utf8_get_char(word); + if (!(g_unichar_isupper(c) || + g_unichar_ispunct(c) || +is_word_lowercase(const gchar *word) + for (; word[0] != '\0'; word = g_utf8_find_next_char(word, NULL)) { + gunichar c = g_utf8_get_char(word); + if (!(g_unichar_islower(c) || + g_unichar_ispunct(c) || +is_word_proper(const gchar *word) + if (!g_unichar_isupper(g_utf8_get_char_validated(word, -1))) + return is_word_lowercase(g_utf8_offset_to_pointer(word, 1)); +make_word_proper(const gchar *word) + gchar *lower = g_utf8_strdown(word, -1); + bytes = g_unichar_to_utf8(g_unichar_toupper(g_utf8_get_char(word)), buf); + buf[MIN((gsize)bytes, sizeof(buf) - 1)] = '\0'; + ret = g_strconcat(buf, g_utf8_offset_to_pointer(lower, 1), NULL); +substitute_simple_buffer(GtkTextBuffer *buffer) + gtk_text_buffer_get_iter_at_offset(buffer, &start, 0); + gtk_text_buffer_get_iter_at_offset(buffer, &end, 0); + gtk_text_iter_forward_to_end(&end); + text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &treeiter) && text) { + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, WORD_ONLY_COLUMN, &val1); + if (g_value_get_boolean(&val1)) + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, BAD_COLUMN, &val1); + bad = g_value_get_string(&val1); + /* using g_utf8_* to get /character/ offsets instead of byte offsets for buffer */ + if ((cursor = g_strrstr(text, bad))) + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, GOOD_COLUMN, &val2); + good = g_value_get_string(&val2); + char_pos = g_utf8_pointer_to_offset(text, cursor); + gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos); + gtk_text_buffer_get_iter_at_offset(buffer, &end, char_pos + g_utf8_strlen(bad, -1)); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos); + gtk_text_buffer_insert(buffer, &start, good, -1); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &treeiter)); +substitute_word(gchar *word) + lowerword = g_utf8_strdown(word, -1); + foldedword = g_utf8_casefold(word, -1); + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) { + gboolean case_sensitive; + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val1); + if (!g_value_get_boolean(&val1)) { + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val1); + case_sensitive = g_value_get_boolean(&val1); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val1); + bad = g_value_get_string(&val1); + if ((case_sensitive && purple_strequal(bad, word)) || + (!case_sensitive && (purple_strequal(bad, lowerword) || + (!is_word_lowercase(bad) && + purple_strequal((tmpbad = g_utf8_casefold(bad, -1)), foldedword))))) + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val2); + good = g_value_get_string(&val2); + if (!case_sensitive && is_word_lowercase(bad) && is_word_lowercase(good)) + if (is_word_uppercase(word)) + outword = g_utf8_strup(good, -1); + else if (is_word_proper(word)) + outword = make_word_proper(good); + outword = g_strdup(good); + outword = g_strdup(good); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter)); +spellchk_free(spellchk *spell) + g_return_if_fail(spell != NULL); + buffer = gtk_text_view_get_buffer(spell->view); + g_signal_handlers_disconnect_matched(buffer, +/* Pango doesn't know about the "'" character. Let's fix that. */ +spellchk_inside_word(GtkTextIter *iter) + gunichar ucs4_char = gtk_text_iter_get_char(iter); + utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL); + /* Hack because otherwise typing things like U.S. gets difficult + * if you have 'u' -> 'you' set as a correction... + * Part 1 of 2: This marks . as being an inside-word character. */ + /* Avoid problems with \r, for example (SF #1289031). */ + if (gtk_text_iter_inside_word (iter) == TRUE) + gboolean result = gtk_text_iter_backward_char(iter); + gboolean output = gtk_text_iter_inside_word(iter); + * Hack so that "u'll" will correct correctly. + ucs4_char = gtk_text_iter_get_char(iter); + utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL); + if (c == 'u' || c == 'U') + gtk_text_iter_forward_char(iter); + gtk_text_iter_forward_char(iter); +spellchk_backward_word_start(GtkTextIter *iter) + output = gtk_text_iter_backward_word_start(iter); + /* It didn't work... */ + while (spellchk_inside_word(iter)) { + result = gtk_text_iter_backward_char(iter); + /* We can't go backwards anymore? We're at the beginning of the word. */ + if (!spellchk_inside_word(iter)) { + gtk_text_iter_forward_char(iter); + output = gtk_text_iter_backward_word_start(iter); +check_range(spellchk *spell, GtkTextBuffer *buffer, + GtkTextIter start, GtkTextIter end, gboolean sending) + if ((replaced = substitute_simple_buffer(buffer))) + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); + spell->pos = gtk_text_iter_get_offset(&pos); + gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); + gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); + /* We need to go backwards to find out if we are inside a word or not. */ + gtk_text_iter_backward_char(&end); + if (spellchk_inside_word(&end)) + gtk_text_iter_forward_char(&end); + return replaced; /* We only pay attention to whole words. */ + /* We could be in the middle of a whitespace block. Check for that. */ + result = gtk_text_iter_backward_char(&end); + if (!spellchk_inside_word(&end)) + gtk_text_iter_forward_char(&end); + gtk_text_iter_forward_char(&end); + /* Move backwards to the beginning of the word. */ + spellchk_backward_word_start(&start); + spell->word = gtk_text_iter_get_text(&start, &end); + /* Hack because otherwise typing things like U.S. gets difficult + * if you have 'u' -> 'you' set as a correction... + * Part 2 of 2: This chops periods off the end of the word so + * the right substitution entry is found. */ + tmp = g_strdup(spell->word); + if (tmp != NULL && *tmp != '\0') { + for (c = tmp + strlen(tmp) - 1 ; c != tmp ; c--) { + if ((word = substitute_word(tmp))) { + for (i = 1 ; i <= period_count ; i++) { + tmp2 = g_strconcat(word, ".", NULL); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_buffer_insert(buffer, &start, word, -1); + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); + spell->pos = gtk_text_iter_get_offset(&pos); +/* insertion works like this: + * - before the text is inserted, we mark the position in the buffer. + * - after the text is inserted, we see where our mark is and use that and + * the current position to check the entire range of inserted text. + * this may be overkill for the common case (inserting one character). */ +insert_text_before(GtkTextBuffer *buffer, GtkTextIter *iter, + gchar *text, gint len, spellchk *spell) + if (spell->inserting == TRUE) + spell->inserting = TRUE; + gtk_text_buffer_move_mark(buffer, spell->mark_insert_start, iter); +insert_text_after(GtkTextBuffer *buffer, GtkTextIter *iter, + gchar *text, gint len, spellchk *spell) + GtkTextIter start, end; + spell->ignore_correction_on_send = FALSE; + if (spell->ignore_correction) { + spell->ignore_correction = FALSE; + /* we need to check a range of text. */ + gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start); + check_range(spell, buffer, start, *iter, FALSE); + /* if check_range modified the buffer, iter has been invalidated */ + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); + gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end); + spell->inserting = FALSE; +delete_range_after(GtkTextBuffer *buffer, + GtkTextIter *start, GtkTextIter *end, spellchk *spell) + GtkTextIter start2, end2; + spell->ignore_correction_on_send = FALSE; + if (spell->inserting == TRUE) + spell->inserting = TRUE; + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); + place = gtk_text_iter_get_offset(&pos); + if ((place + 1) != spell->pos) { + gtk_text_buffer_get_iter_at_mark(buffer, &start2, spell->mark_insert_start); + gtk_text_buffer_get_iter_at_mark(buffer, &end2, spell->mark_insert_end); + gtk_text_buffer_delete(buffer, &start2, &end2); + gtk_text_buffer_insert(buffer, &start2, spell->word, -1); + spell->ignore_correction = TRUE; + spell->ignore_correction_on_send = TRUE; + spell->inserting = FALSE; +message_send_cb(GtkWidget *widget, spellchk *spell) + GtkTextIter start, end; + if (spell->ignore_correction_on_send) + spell->ignore_correction_on_send = FALSE; + if (!purple_prefs_get_bool("/plugins/gtk/spellchk/last_word_replace")) + buffer = gtk_text_view_get_buffer(spell->view); + gtk_text_buffer_get_end_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + spell->inserting = TRUE; + replaced = check_range(spell, buffer, start, end, TRUE); + spell->inserting = FALSE; + /* if check_range modified the buffer, iter has been invalidated */ + mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); + gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end); + g_signal_stop_emission_by_name(widget, "message_send"); + spell->ignore_correction_on_send = TRUE; +spellchk_new_attach(PurpleConversation *conv) + GtkTextIter start, end; + PidginConversation *gtkconv; + gtkconv = PIDGIN_CONVERSATION(conv); + view = GTK_TEXT_VIEW(gtkconv->entry); + spell = g_object_get_data(G_OBJECT(view), SPELLCHK_OBJECT_KEY); + /* attach to the widget */ + spell = g_new0(spellchk, 1); + g_object_set_data_full(G_OBJECT(view), SPELLCHK_OBJECT_KEY, spell, + (GDestroyNotify)spellchk_free); + buffer = gtk_text_view_get_buffer(view); + /* we create the mark here, but we don't use it until text is + * inserted, so we don't really care where iter points. */ + gtk_text_buffer_get_bounds(buffer, &start, &end); + spell->mark_insert_start = gtk_text_buffer_create_mark(buffer, + "spellchk-insert-start", + spell->mark_insert_end = gtk_text_buffer_create_mark(buffer, + g_signal_connect_after(G_OBJECT(buffer), + G_CALLBACK(delete_range_after), spell); + g_signal_connect(G_OBJECT(buffer), + G_CALLBACK(insert_text_before), spell); + g_signal_connect_after(G_OBJECT(buffer), + G_CALLBACK(insert_text_after), spell); + g_signal_connect(G_OBJECT(gtkconv->entry), "message_send", + G_CALLBACK(message_send_cb), spell); +static int buf_get_line(char *ibuf, char **buf, gsize *position, gsize len) + while (!(ibuf[pos] == '\n' || + (ibuf[pos] == '\r' && ibuf[pos + 1] != '\n'))) + if (pos != 0 && ibuf[pos] == '\n' && ibuf[pos - 1] == '\r') +static void load_conf(void) + /* Corrections to change "...", "(c)", "(r)", and "(tm)" to their + * Unicode character equivalents were not added here even though + * they existed in the source list(s). I think these corrections + * would be more trouble than they're worth. + const char * const defaultconf = + "BAD abbout\nGOOD about\n" + "BAD abotu\nGOOD about\n" + "BAD abouta\nGOOD about a\n" + "BAD aboutit\nGOOD about it\n" + "BAD aboutthe\nGOOD about the\n" + "BAD abscence\nGOOD absence\n" + "BAD accesories\nGOOD accessories\n" + "BAD accidant\nGOOD accident\n" + "BAD accomodate\nGOOD accommodate\n" + "BAD accordingto\nGOOD according to\n" + "BAD accross\nGOOD across\n" + "BAD acheive\nGOOD achieve\n" + "BAD acheived\nGOOD achieved\n" + "BAD acheiving\nGOOD achieving\n" + "BAD acommodate\nGOOD accommodate\n" + "BAD acomodate\nGOOD accommodate\n" + "BAD actualyl\nGOOD actually\n" + "BAD additinal\nGOOD additional\n" + "BAD addtional\nGOOD additional\n" + "BAD adequit\nGOOD adequate\n" + "BAD adequite\nGOOD adequate\n" + "BAD advanage\nGOOD advantage\n" + "BAD affraid\nGOOD afraid\n" + "BAD afterthe\nGOOD after the\n" + "COMPLETE 0\nBAD againstt he \nGOOD against the \n" + "BAD aganist\nGOOD against\n" + "BAD aggresive\nGOOD aggressive\n" + "BAD agian\nGOOD again\n" + "BAD agreemeent\nGOOD agreement\n" + "BAD agreemeents\nGOOD agreements\n" + "BAD agreemnet\nGOOD agreement\n" + "BAD agreemnets\nGOOD agreements\n" + "BAD agressive\nGOOD aggressive\n" + "BAD agressiveness\nGOOD aggressiveness\n" + "BAD ahold\nGOOD a hold\n" + "BAD ahppen\nGOOD happen\n" + "BAD ahve\nGOOD have\n" + "BAD allready\nGOOD already\n" + "BAD allwasy\nGOOD always\n" + "BAD allwyas\nGOOD always\n" + "BAD almots\nGOOD almost\n" + "BAD almsot\nGOOD almost\n" + "BAD alomst\nGOOD almost\n" + "BAD alot\nGOOD a lot\n" + "BAD alraedy\nGOOD already\n" + "BAD alreayd\nGOOD already\n" + "BAD alreday\nGOOD already\n" + "BAD alwasy\nGOOD always\n" + "BAD alwats\nGOOD always\n" + "BAD alway\nGOOD always\n" + "BAD alwyas\nGOOD always\n" + "BAD amde\nGOOD made\n" + "BAD Ameria\nGOOD America\n" + "BAD amke\nGOOD make\n" + "BAD amkes\nGOOD makes\n" + "BAD andone\nGOOD and one\n" + "BAD andteh\nGOOD and the\n" + "BAD andthe\nGOOD and the\n" + "COMPLETE 0\nBAD andt he \nGOOD and the \n" + "BAD anothe\nGOOD another\n" + "BAD anual\nGOOD annual\n" + "BAD any1\nGOOD anyone\n" + "BAD apparant\nGOOD apparent\n" + "BAD apparrent\nGOOD apparent\n" + "BAD appearence\nGOOD appearance\n" + "BAD appeares\nGOOD appears\n" + "BAD applicaiton\nGOOD application\n" + "BAD applicaitons\nGOOD applications\n" + "BAD applyed\nGOOD applied\n" + "BAD appointiment\nGOOD appointment\n" + "BAD approrpiate\nGOOD appropriate\n" + "BAD approrpriate\nGOOD appropriate\n" + "BAD aquisition\nGOOD acquisition\n" + "BAD aquisitions\nGOOD acquisitions\n" + "BAD arent\nGOOD aren't\n" + "COMPLETE 0\nBAD aren;t \nGOOD aren't \n" + "BAD arguement\nGOOD argument\n" + "BAD arguements\nGOOD arguments\n" + "COMPLETE 0\nBAD arn't \nGOOD aren't \n" + "BAD arond\nGOOD around\n" + "BAD artical\nGOOD article\n" + "BAD articel\nGOOD article\n" + "BAD asdvertising\nGOOD advertising\n" + "COMPLETE 0\nBAD askt he \nGOOD ask the \n" + "BAD assistent\nGOOD assistant\n" + "BAD asthe\nGOOD as the\n" + "BAD atention\nGOOD attention\n" + "BAD atmospher\nGOOD atmosphere\n" + "BAD attentioin\nGOOD attention\n" + "BAD atthe\nGOOD at the\n" + "BAD audeince\nGOOD audience\n" + "BAD audiance\nGOOD audience\n" + "BAD authentification\nGOOD authentication\n" + "BAD availalbe\nGOOD available\n" + "BAD awya\nGOOD away\n" + "BAD aywa\nGOOD away\n" + "BAD b4\nGOOD before\n" + "BAD bakc\nGOOD back\n" + "BAD balence\nGOOD balance\n" + "BAD ballance\nGOOD balance\n" + "BAD baout\nGOOD about\n" + "BAD bcak\nGOOD back\n" + "BAD bcuz\nGOOD because\n" + "BAD beacuse\nGOOD because\n" + "BAD becasue\nGOOD because\n" + "BAD becaus\nGOOD because\n" + "BAD becausea\nGOOD because a\n" + "BAD becauseof\nGOOD because of\n" + "BAD becausethe\nGOOD because the\n" + "BAD becauseyou\nGOOD because you\n" + "COMPLETE 0\nBAD beckon call\nGOOD beck and call\n" + "BAD becomeing\nGOOD becoming\n" + "BAD becomming\nGOOD becoming\n" + "BAD becuase\nGOOD because\n" + "BAD becuse\nGOOD because\n" + "BAD befoer\nGOOD before\n" + "BAD beggining\nGOOD beginning\n" + "BAD begining\nGOOD beginning\n" + "BAD beginining\nGOOD beginning\n" + "BAD beleiev\nGOOD believe\n" + "BAD beleieve\nGOOD believe\n" + "BAD beleif\nGOOD belief\n" + "BAD beleive\nGOOD believe\n" + "BAD beleived\nGOOD believed\n" + "BAD beleives\nGOOD believes\n" + "BAD belive\nGOOD believe\n" + "BAD belived\nGOOD believed\n" + "BAD belives\nGOOD believes\n" + "BAD benifit\nGOOD benefit\n" + "BAD benifits\nGOOD benefits\n" + "BAD betwen\nGOOD between\n" + "BAD beutiful\nGOOD beautiful\n" + "BAD blase\nGOOD blas\303\251\n" + "BAD boxs\nGOOD boxes\n" + "BAD brodcast\nGOOD broadcast\n" + "BAD butthe\nGOOD but the\n" + "COMPLETE 0\nBAD byt he \nGOOD by the \n" + "BAD cafe\nGOOD caf\303\251\n" + "BAD caharcter\nGOOD character\n" + "BAD calcullated\nGOOD calculated\n" + "BAD calulated\nGOOD calculated\n" + "BAD candidtae\nGOOD candidate\n" + "BAD candidtaes\nGOOD candidates\n" + "COMPLETE 0\nBAD case and point\nGOOD case in point\n" + "BAD cant\nGOOD can't\n" + "COMPLETE 0\nBAD can;t \nGOOD can't \n" + "COMPLETE 0\nBAD can't of been\nGOOD can't have been\n" + "BAD catagory\nGOOD category\n" + "BAD categiory\nGOOD category\n" + "BAD certian\nGOOD certain\n" + "BAD challange\nGOOD challenge\n" + "BAD challanges\nGOOD challenges\n" + "BAD chaneg\nGOOD change\n" + "BAD chanegs\nGOOD changes\n" + "BAD changable\nGOOD changeable\n" + "BAD changeing\nGOOD changing\n" + "BAD changng\nGOOD changing\n" + "BAD charachter\nGOOD character\n" + "BAD charachters\nGOOD characters\n" + "BAD charactor\nGOOD character\n" + "BAD charecter\nGOOD character\n" + "BAD charector\nGOOD character\n" + "BAD cheif\nGOOD chief\n" + "BAD chekc\nGOOD check\n" + "BAD chnage\nGOOD change\n" + "BAD cieling\nGOOD ceiling\n" + "BAD circut\nGOOD circuit\n" + "BAD claer\nGOOD clear\n" + "BAD claered\nGOOD cleared\n" + "BAD claerly\nGOOD clearly\n" + "BAD cliant\nGOOD client\n" + "BAD cliche\nGOOD clich\303\251\n" + "BAD colection\nGOOD collection\n" + "BAD comanies\nGOOD companies\n" + "BAD comany\nGOOD company\n" + "BAD comapnies\nGOOD companies\n" + "BAD comapny\nGOOD company\n" + "BAD combintation\nGOOD combination\n" + "BAD comited\nGOOD committed\n" + "BAD comittee\nGOOD committee\n" + "BAD commadn\nGOOD command\n" + "BAD comming\nGOOD coming\n" + "BAD commitee\nGOOD committee\n" + "BAD committe\nGOOD committee\n" + "BAD committment\nGOOD commitment\n" + "BAD committments\nGOOD commitments\n" + "BAD committy\nGOOD committee\n" + "BAD comntain\nGOOD contain\n" + "BAD comntains\nGOOD contains\n" + "BAD compair\nGOOD compare\n" + "COMPLETE 0\nBAD company;s \nGOOD company's \n" + "BAD competetive\nGOOD competitive\n" + "BAD compleated\nGOOD completed\n" + "BAD compleatly\nGOOD completely\n" + "BAD compleatness\nGOOD completeness\n" + "BAD completly\nGOOD completely\n" + "BAD completness\nGOOD completeness\n" + "BAD composate\nGOOD composite\n" + "BAD comtain\nGOOD contain\n" + "BAD comtains\nGOOD contains\n" + "BAD comunicate\nGOOD communicate\n" + "BAD comunity\nGOOD community\n" + "BAD condolances\nGOOD condolences\n" + "BAD conected\nGOOD connected\n" + "BAD conferance\nGOOD conference\n" + "BAD confirmmation\nGOOD confirmation\n" + "BAD congradulations\nGOOD congratulations\n" + "BAD considerit\nGOOD considerate\n" + "BAD considerite\nGOOD considerate\n" + "BAD consonent\nGOOD consonant\n" + "BAD conspiricy\nGOOD conspiracy\n" + "BAD consultent\nGOOD consultant\n" + "BAD convertable\nGOOD convertible\n" + "BAD cooparate\nGOOD cooperate\n" + "BAD cooporate\nGOOD cooperate\n" + "BAD corproation\nGOOD corporation\n" + "BAD corproations\nGOOD corporations\n" + "BAD corruptable\nGOOD corruptible\n" + "BAD cotten\nGOOD cotton\n" + "BAD coudl\nGOOD could\n" + "COMPLETE 0\nBAD coudln't \nGOOD couldn't \n" + "COMPLETE 0\nBAD coudn't \nGOOD couldn't \n" + "BAD couldnt\nGOOD couldn't\n" + "COMPLETE 0\nBAD couldn;t \nGOOD couldn't \n" + "COMPLETE 0\nBAD could of been\nGOOD could have been\n" + "COMPLETE 0\nBAD could of had\nGOOD could have had\n" + "BAD couldthe\nGOOD could the\n" + "BAD couldve\nGOOD could've\n" + "BAD cpoy\nGOOD copy\n" + "BAD creme\nGOOD cr\303\250me\n" + "BAD ctaegory\nGOOD category\n" + "BAD cu\nGOOD see you\n" + "BAD cusotmer\nGOOD customer\n" + "BAD cusotmers\nGOOD customers\n" + "BAD cutsomer\nGOOD customer\n" + "BAD cutsomers\nGOOD customer\n" + "BAD cuz\nGOOD because\n" + "BAD danceing\nGOOD dancing\n" + "BAD dcument\nGOOD document\n" + "BAD deatils\nGOOD details\n" + "BAD decison\nGOOD decision\n" + "BAD decisons\nGOOD decisions\n" + "BAD decor\nGOOD d\303\251cor\n" + "BAD defendent\nGOOD defendant\n" + "BAD definately\nGOOD definitely\n" + "COMPLETE 0\nBAD deja vu\nGOOD d\303\251j\303\240 vu\n" + "BAD deptartment\nGOOD department\n" + "BAD desicion\nGOOD decision\n" + "BAD desicions\nGOOD decisions\n" + "BAD desision\nGOOD decision\n" + "BAD desisions\nGOOD decisions\n" + "BAD detente\nGOOD d\303\251tente\n" + "BAD develeoprs\nGOOD developers\n" + "BAD devellop\nGOOD develop\n" + "BAD develloped\nGOOD developed\n" + "BAD develloper\nGOOD developer\n" + "BAD devellopers\nGOOD developers\n" + "BAD develloping\nGOOD developing\n" + "BAD devellopment\nGOOD development\n" + "BAD devellopments\nGOOD developments\n" + "BAD devellops\nGOOD develop\n" + "BAD develope\nGOOD develop\n" + "BAD developement\nGOOD development\n" + "BAD developements\nGOOD developments\n" + "BAD developor\nGOOD developer\n" + "BAD developors\nGOOD developers\n" + "BAD develpment\nGOOD development\n" + "BAD diaplay\nGOOD display\n" + "BAD didint\nGOOD didn't\n" + "BAD didnot\nGOOD did not\n" + "BAD didnt\nGOOD didn't\n" + "COMPLETE 0\nBAD didn;t \nGOOD didn't \n" + "BAD difefrent\nGOOD different\n" + "BAD diferences\nGOOD differences\n" + "BAD differance\nGOOD difference\n" + "BAD differances\nGOOD differences\n" + "BAD differant\nGOOD different\n" + "BAD differemt\nGOOD different\n" + "BAD differnt\nGOOD different\n" + "BAD diffrent\nGOOD different\n" + "BAD directer\nGOOD director\n" + "BAD directers\nGOOD directors\n" + "BAD directiosn\nGOOD direction\n" + "BAD disatisfied\nGOOD dissatisfied\n" + "BAD discoverd\nGOOD discovered\n" + "BAD disign\nGOOD design\n" + "BAD dispaly\nGOOD display\n" + "BAD dissonent\nGOOD dissonant\n" + "BAD distribusion\nGOOD distribution\n" + "BAD divsion\nGOOD division\n" + "BAD docuement\nGOOD documents\n" + "BAD docuemnt\nGOOD document\n" + "BAD documetn\nGOOD document\n" + "BAD documnet\nGOOD document\n" + "BAD documnets\nGOOD documents\n" + "COMPLETE 0\nBAD doens't \nGOOD doesn't \n" + "BAD doese\nGOOD does\n" + "COMPLETE 0\nBAD doe snot \nGOOD does not \n" + "BAD doesnt\nGOOD doesn't\n" + "COMPLETE 0\nBAD doesn;t \nGOOD doesn't \n" + "BAD doign\nGOOD doing\n" + "BAD doimg\nGOOD doing\n" + "BAD doind\nGOOD doing\n" + "BAD dollers\nGOOD dollars\n" + "BAD donig\nGOOD doing\n" + "BAD donno\nGOOD don't know\n" + "BAD dont\nGOOD don't\n" + "COMPLETE 0\nBAD do'nt \nGOOD don't \n" + "COMPLETE 0\nBAD don;t \nGOOD don't \n" + "COMPLETE 0\nBAD don't no \nGOOD don't know \n" + "COMPLETE 0\nBAD dosn't \nGOOD doesn't \n" + "BAD driveing\nGOOD driving\n" + "BAD drnik\nGOOD drink\n" + "BAD dunno\nGOOD don't know\n" + "BAD eclair\nGOOD \303\251clair\n" + "BAD efel\nGOOD feel\n" + "BAD effecient\nGOOD efficient\n" + "BAD efort\nGOOD effort\n" + "BAD eforts\nGOOD efforts\n" + "BAD eligable\nGOOD eligible\n" + "BAD embarass\nGOOD embarrass\n" + "BAD emigre\nGOOD \303\251migr\303\251\n" + "BAD enought\nGOOD enough\n" + "BAD entree\nGOOD entr\303\251e\n" + "BAD enuf\nGOOD enough\n" + "BAD equippment\nGOOD equipment\n" + "BAD equivalant\nGOOD equivalent\n" + "BAD esle\nGOOD else\n" + "BAD especally\nGOOD especially\n" + "BAD especialyl\nGOOD especially\n" + "BAD espesially\nGOOD especially\n" + "BAD essense\nGOOD essence\n" + "BAD excellance\nGOOD excellence\n" + "BAD excellant\nGOOD excellent\n" + "BAD excercise\nGOOD exercise\n" + "BAD exchagne\nGOOD exchange\n" + "BAD exchagnes\nGOOD exchanges\n" + "BAD excitment\nGOOD excitement\n" + "BAD exhcange\nGOOD exchange\n" + "BAD exhcanges\nGOOD exchanges\n" + "BAD experiance\nGOOD experience\n" + "BAD experienc\nGOOD experience\n" + "BAD exprience\nGOOD experience\n" + "BAD exprienced\nGOOD experienced\n" + "BAD facade\nGOOD fa\303\247ade\n" + "BAD faeture\nGOOD feature\n" + "BAD faetures\nGOOD feature\n" + "BAD familair\nGOOD familiar\n" + "BAD familar\nGOOD familiar\n" + "BAD familliar\nGOOD familiar\n" + "BAD fammiliar\nGOOD familiar\n" + "BAD feild\nGOOD field\n" + "BAD feilds\nGOOD fields\n" + "BAD fianlly\nGOOD finally\n" + "BAD fidn\nGOOD find\n" + "BAD finalyl\nGOOD finally\n" + "BAD firends\nGOOD friends\n" + "BAD firts\nGOOD first\n" + "BAD follwo\nGOOD follow\n" + "BAD follwoing\nGOOD following\n" + "BAD fora\nGOOD for a\n" + "COMPLETE 0\nBAD for all intensive purposes\nfor all intents and purposes\n" + "BAD foriegn\nGOOD foreign\n" + "BAD forthe\nGOOD for the\n" + "BAD forwrd\nGOOD forward\n" + "BAD forwrds\nGOOD forwards\n" + "BAD foudn\nGOOD found\n" + "BAD foward\nGOOD forward\n" + "BAD fowards\nGOOD forwards\n" + "BAD freind\nGOOD friend\n" + "BAD freindly\nGOOD friendly\n" + "BAD freinds\nGOOD friends\n" + "BAD friday\nGOOD Friday\n" + "BAD frmo\nGOOD from\n" + "BAD fromthe\nGOOD from the\n" + "COMPLETE 0\nBAD fromt he \nGOOD from the \n" + "BAD furneral\nGOOD funeral\n" + "BAD garantee\nGOOD guarantee\n" + "BAD gaurd\nGOOD guard\n" + "BAD gemeral\nGOOD general\n" + "BAD gerat\nGOOD great\n" + "BAD geting\nGOOD getting\n" + "BAD gettin\nGOOD getting\n" + "BAD gievn\nGOOD given\n" + "BAD giveing\nGOOD giving\n" + "BAD gloabl\nGOOD global\n" + "BAD goign\nGOOD going\n" + "BAD gonig\nGOOD going\n" + "BAD govenment\nGOOD government\n" + "BAD goverment\nGOOD government\n" + "BAD gruop\nGOOD group\n" + "BAD gruops\nGOOD groups\n" + "BAD grwo\nGOOD grow\n" + "BAD guidlines\nGOOD guidelines\n" + "BAD hadbeen\nGOOD had been\n" + "BAD hadnt\nGOOD hadn't\n" + "COMPLETE 0\nBAD hadn;t \nGOOD hadn't \n" + "BAD haev\nGOOD have\n" + "BAD hapen\nGOOD happen\n" + "BAD hapened\nGOOD happened\n" + "BAD hapening\nGOOD happening\n" + "BAD hapens\nGOOD happens\n" + "BAD happend\nGOOD happened\n" + "BAD hasbeen\nGOOD has been\n" + "BAD hasnt\nGOOD hasn't\n" + "COMPLETE 0\nBAD hasn;t \nGOOD hasn't \n" + "BAD havebeen\nGOOD have been\n" + "BAD haveing\nGOOD having\n" + "BAD havent\nGOOD haven't\n" + "COMPLETE 0\nBAD haven;t \nGOOD haven't \n" + "BAD hearign\nGOOD hearing\n" + "COMPLETE 0\nBAD he;d \nGOOD he'd \n" + "BAD heirarchy\nGOOD hierarchy\n" + "BAD hel\nGOOD he'll\n" + "COMPLETE 0\nBAD he;ll \nGOOD he'll \n" + "BAD helpfull\nGOOD helpful\n" + "BAD herat\nGOOD heart\n" + "BAD heres\nGOOD here's\n" + "COMPLETE 0\nBAD here;s \nGOOD here's \n" + "COMPLETE 0\nBAD he;s \nGOOD he's \n" + "BAD hesaid\nGOOD he said\n" + "BAD hewas\nGOOD he was\n" + "BAD hismelf\nGOOD himself\n" + "BAD hlep\nGOOD help\n" + "BAD hows\nGOOD how's\n" + "BAD htere\nGOOD there\n" + "BAD htese\nGOOD these\n" + "BAD htey\nGOOD they\n" + "BAD hting\nGOOD thing\n" + "BAD htink\nGOOD think\n" + "BAD htis\nGOOD this\n" + "COMPLETE 0\nBAD htp:\nGOOD http:\n" + "COMPLETE 0\nBAD http:\\\\nGOOD http://\n" + "BAD httpL\nGOOD http:\n" + "BAD hvae\nGOOD have\n" + "BAD hvaing\nGOOD having\n" + "BAD hwich\nGOOD which\n" + "COMPLETE 0\nBAD i c \nGOOD I see \n" + "COMPLETE 0\nBAD i;d \nGOOD I'd \n" + "COMPLETE 0\nBAD i'd \nGOOD I'd \n" + "COMPLETE 0\nBAD I;d \nGOOD I'd \n" + "BAD idae\nGOOD idea\n" + "BAD idaes\nGOOD ideas\n" + "BAD identofy\nGOOD identify\n" + "BAD iits the\nGOOD it's the\n" + "COMPLETE 0\nBAD i'll \nGOOD I'll \n" + "COMPLETE 0\nBAD I;ll \nGOOD I'll \n" + "COMPLETE 0\nBAD i;m \nGOOD I'm \n" + "COMPLETE 0\nBAD i'm \nGOOD I'm \n" + "COMPLETE 0\nBAD I\"m \nGOOD I'm \n" + "BAD imediate\nGOOD immediate\n" + "BAD imediatly\nGOOD immediately\n" + "BAD immediatly\nGOOD immediately\n" + "BAD importent\nGOOD important\n" + "BAD importnat\nGOOD important\n" + "BAD impossable\nGOOD impossible\n" + "BAD improvemnt\nGOOD improvement\n" + "BAD improvment\nGOOD improvement\n" + "BAD includ\nGOOD include\n" + "BAD indecate\nGOOD indicate\n" + "BAD indenpendence\nGOOD independence\n" + "BAD indenpendent\nGOOD independent\n" + "BAD indepedent\nGOOD independent\n" + "BAD independance\nGOOD independence\n" + "BAD independant\nGOOD independent\n" + "BAD influance\nGOOD influence\n" + "BAD infomation\nGOOD information\n" + "BAD informatoin\nGOOD information\n" + "BAD inital\nGOOD initial\n" + "BAD instaleld\nGOOD installed\n" + "BAD insted\nGOOD instead\n" + "BAD insurence\nGOOD insurance\n" + "BAD inteh\nGOOD in the\n" + "BAD interum\nGOOD interim\n" + "BAD inthe\nGOOD in the\n" + "COMPLETE 0\nBAD int he \nGOOD in the \n" + "BAD inturn\nGOOD intern\n" + "BAD inwhich\nGOOD in which\n" + "COMPLETE 0\nBAD i snot \nGOOD is not \n" + "BAD isnt\nGOOD isn't\n" + "COMPLETE 0\nBAD isn;t \nGOOD isn't \n" + "BAD isthe\nGOOD is the\n" + "COMPLETE 0\nBAD it;d \nGOOD it'd \n" + "BAD itis\nGOOD it is\n" + "BAD ititial\nGOOD initial\n" + "BAD itll\nGOOD it'll\n" + "COMPLETE 0\nBAD it;ll \nGOOD it'll \n" + "BAD itnerest\nGOOD interest\n" + "BAD itnerested\nGOOD interested\n" + "BAD itneresting\nGOOD interesting\n" + "BAD itnerests\nGOOD interests\n" + "COMPLETE 0\nBAD it;s \nGOOD it's \n" + "BAD itsa\nGOOD it's a\n" + "COMPLETE 0\nBAD its a \nGOOD it's a \n" + "COMPLETE 0\nBAD it snot \nGOOD it's not \n" + "COMPLETE 0\nBAD it' snot \nGOOD it's not \n" + "COMPLETE 0\nBAD its the \nGOOD it's the \n" + "BAD itwas\nGOOD it was\n" + "COMPLETE 0\nBAD i;ve \nGOOD I've \n" + "COMPLETE 0\nBAD i've \nGOOD I've \n" + "BAD iwll\nGOOD will\n" + "BAD iwth\nGOOD with\n" + "BAD jsut\nGOOD just\n" + "BAD jugment\nGOOD judgment\n" + "BAD knowldge\nGOOD knowledge\n" + "BAD knowlege\nGOOD knowledge\n" + "BAD knwo\nGOOD know\n" + "BAD knwon\nGOOD known\n" + "BAD knwos\nGOOD knows\n" + "BAD konw\nGOOD know\n" + "BAD konwn\nGOOD known\n" + "BAD konws\nGOOD knows\n" + "BAD labratory\nGOOD laboratory\n" + "BAD labtop\nGOOD laptop\n" + "BAD lastyear\nGOOD last year\n" + "BAD laterz\nGOOD later\n" + "BAD learnign\nGOOD learning\n" + "BAD lenght\nGOOD length\n" + "COMPLETE 0\nBAD let;s \nGOOD let's \n" + "COMPLETE 0\nBAD let's him \nGOOD lets him \n" + "COMPLETE 0\nBAD let's it \nGOOD lets it \n" + "BAD levle\nGOOD level\n" + "BAD libary\nGOOD library\n" + "BAD librarry\nGOOD library\n" + "BAD librery\nGOOD library\n" + "BAD liek\nGOOD like\n" + "BAD liekd\nGOOD liked\n" + "BAD lieutenent\nGOOD lieutenant\n" + "BAD liev\nGOOD live\n" + "BAD likly\nGOOD likely\n" + "BAD lisense\nGOOD license\n" + "BAD littel\nGOOD little\n" + "BAD litttle\nGOOD little\n" + "BAD liuke\nGOOD like\n" + "BAD liveing\nGOOD living\n" + "BAD loev\nGOOD love\n" + "BAD lonly\nGOOD lonely\n" + "BAD lookign\nGOOD looking\n" + "BAD maintainence\nGOOD maintenance\n" + "BAD maintenence\nGOOD maintenance\n" + "BAD makeing\nGOOD making\n" + "BAD managment\nGOOD management\n" + "BAD mantain\nGOOD maintain\n" + "BAD marraige\nGOOD marriage\n" + "COMPLETE 0\nBAD may of been\nGOOD may have been\n" + "COMPLETE 0\nBAD may of had\nGOOD may have had\n" + "BAD memeber\nGOOD member\n" + "BAD merchent\nGOOD merchant\n" + "BAD mesage\nGOOD message\n" + "BAD mesages\nGOOD messages\n" + "COMPLETE 0\nBAD might of been\nGOOD might have been\n" + "COMPLETE 0\nBAD might of had\nGOOD might have had\n" + "BAD mispell\nGOOD misspell\n" + "BAD mispelling\nGOOD misspelling\n" + "BAD mispellings\nGOOD misspellings\n" + "BAD mkae\nGOOD make\n" + "BAD mkaes\nGOOD makes\n" + "BAD mkaing\nGOOD making\n" + "BAD moeny\nGOOD money\n" + "BAD monday\nGOOD Monday\n" + "BAD morgage\nGOOD mortgage\n" + "BAD mroe\nGOOD more\n" + "COMPLETE 0\nBAD must of been\nGOOD must have been\n" + "COMPLETE 0\nBAD must of had\nGOOD must have had\n" + "COMPLETE 0\nBAD mute point\nGOOD moot point\n" + "BAD mysefl\nGOOD myself\n" + "BAD naive\nGOOD naïve\n" + "BAD ne1\nGOOD anyone\n" + "BAD neway\nGOOD anyway\n" + "BAD neways\nGOOD anyways\n" + "BAD necassarily\nGOOD necessarily\n" + "BAD necassary\nGOOD necessary\n" + "BAD neccessarily\nGOOD necessarily\n" + "BAD neccessary\nGOOD necessary\n" + "BAD necesarily\nGOOD necessarily\n" + "BAD necesary\nGOOD necessary\n" + "BAD negotiaing\nGOOD negotiating\n" + "BAD nkow\nGOOD know\n" + "BAD nothign\nGOOD nothing\n" + "BAD nver\nGOOD never\n" + "BAD obediant\nGOOD obedient\n" + "BAD ocasion\nGOOD occasion\n" + "BAD occassion\nGOOD occasion\n" + "BAD occurance\nGOOD occurrence\n" + "BAD occured\nGOOD occurred\n" + "BAD occurence\nGOOD occurrence\n" + "BAD occurrance\nGOOD occurrence\n" + "BAD oclock\nGOOD o'clock\n" + "BAD oculd\nGOOD could\n" + "BAD ocur\nGOOD occur\n" + "BAD oeprator\nGOOD operator\n" + "BAD ofits\nGOOD of its\n" + "BAD ofthe\nGOOD of the\n" + "BAD oft he\nGOOD of the\n" + "BAD oging\nGOOD going\n" + "BAD ohter\nGOOD other\n" + "BAD omre\nGOOD more\n" + "BAD oneof\nGOOD one of\n" + "BAD onepoint\nGOOD one point\n" + "BAD onthe\nGOOD on the\n" + "COMPLETE 0\nBAD ont he \nGOOD on the \n" + "BAD onyl\nGOOD only\n" + "BAD oppasite\nGOOD opposite\n" + "BAD opperation\nGOOD operation\n" + "BAD oppertunity\nGOOD opportunity\n" + "BAD opposate\nGOOD opposite\n" + "BAD opposible\nGOOD opposable\n" + "BAD opposit\nGOOD opposite\n" + "BAD oppotunities\nGOOD opportunities\n" + "BAD oppotunity\nGOOD opportunity\n" + "BAD orginization\nGOOD organization\n" + "BAD orginized\nGOOD organized\n" + "BAD otehr\nGOOD other\n" + "BAD outof\nGOOD out of\n" + "BAD overthe\nGOOD over the\n" + "BAD owrk\nGOOD work\n" + "BAD owuld\nGOOD would\n" + "BAD oxident\nGOOD oxidant\n" + "BAD papaer\nGOOD paper\n" + "BAD passe\nGOOD pass\303\251\n" + "BAD parliment\nGOOD parliament\n" + "BAD partof\nGOOD part of\n" + "BAD paymetn\nGOOD payment\n" + "BAD paymetns\nGOOD payments\n" + "BAD pciture\nGOOD picture\n" + "BAD peice\nGOOD piece\n" + "BAD peices\nGOOD pieces\n" + "BAD peolpe\nGOOD people\n" + "BAD peopel\nGOOD people\n" + "BAD percentof\nGOOD percent of\n" + "BAD percentto\nGOOD percent to\n" + "BAD performence\nGOOD performance\n" + "BAD perhasp\nGOOD perhaps\n" + "BAD perhpas\nGOOD perhaps\n" + "BAD permanant\nGOOD permanent\n" + "BAD perminent\nGOOD permanent\n" + "BAD personalyl\nGOOD personally\n" + "BAD pleasent\nGOOD pleasant\n" + "BAD pls\nGOOD please\n" + "BAD plz\nGOOD please\n" + "BAD poeple\nGOOD people\n" + "BAD porblem\nGOOD problem\n" + "BAD porblems\nGOOD problems\n" + "BAD porvide\nGOOD provide\n" + "BAD possable\nGOOD possible\n" + "BAD postition\nGOOD position\n" + "BAD potatoe\nGOOD potato\n" + "BAD potatos\nGOOD potatoes\n" + "BAD potentialy\nGOOD potentially\n" + "BAD ppl\nGOOD people\n" + "BAD pregnent\nGOOD pregnant\n" + "BAD presance\nGOOD presence\n" + "BAD primative\nGOOD primitive\n" + "BAD probally\nGOOD probably\n" + "BAD probelm\nGOOD problem\n" + "BAD probelms\nGOOD problems\n" + "BAD probly\nGOOD probably\n" + "BAD prolly\nGOOD probably\n" + "BAD proly\nGOOD probably\n" + "BAD prominant\nGOOD prominent\n" + "BAD proposterous\nGOOD preposterous\n" + "BAD protege\nGOOD prot\303\251g\303\251\n" + "BAD protoge\nGOOD prot\303\251g\303\251\n" + "BAD psoition\nGOOD position\n" + "BAD ptogress\nGOOD progress\n" + "BAD pursuade\nGOOD persuade\n" + "BAD puting\nGOOD putting\n" + "BAD pwoer\nGOOD power\n" + "BAD quater\nGOOD quarter\n" + "BAD quaters\nGOOD quarters\n" + "BAD quesion\nGOOD question\n" + "BAD quesions\nGOOD questions\n" + "BAD questioms\nGOOD questions\n" + "BAD questiosn\nGOOD questions\n" + "BAD questoin\nGOOD question\n" + "BAD quetion\nGOOD question\n" + "BAD quetions\nGOOD questions\n" + "BAD raeson\nGOOD reason\n" + "BAD realyl\nGOOD really\n" + "BAD reccomend\nGOOD recommend\n" + "BAD reccommend\nGOOD recommend\n" + "BAD receieve\nGOOD receive\n" + "BAD recieve\nGOOD receive\n" + "BAD recieved\nGOOD received\n" + "BAD recieving\nGOOD receiving\n" + "BAD recomend\nGOOD recommend\n" + "BAD recomendation\nGOOD recommendation\n" + "BAD recomendations\nGOOD recommendations\n" + "BAD recomended\nGOOD recommended\n" + "BAD reconize\nGOOD recognize\n" + "BAD recrod\nGOOD record\n" + "BAD rediculous\nGOOD ridiculous\n" + "BAD rediculus\nGOOD ridiculous\n" + "BAD reguard\nGOOD regard\n" + "BAD religous\nGOOD religious\n" + "BAD reluctent\nGOOD reluctant\n" + "BAD remeber\nGOOD remember\n" + "BAD reommend\nGOOD recommend\n" + "BAD representativs\nGOOD representatives\n" + "BAD representives\nGOOD representatives\n" + "BAD represetned\nGOOD represented\n" + "BAD represnt\nGOOD represent\n" + "BAD reserach\nGOOD research\n" + "BAD resollution\nGOOD resolution\n" + "BAD resorces\nGOOD resources\n" + "BAD respomd\nGOOD respond\n" + "BAD respomse\nGOOD response\n" + "BAD responce\nGOOD response\n" + "BAD responsability\nGOOD responsibility\n" + "BAD responsable\nGOOD responsible\n" + "BAD responsibile\nGOOD responsible\n" + "BAD responsiblity\nGOOD responsibility\n" + "BAD restaraunt\nGOOD restaurant\n" + "BAD restuarant\nGOOD restaurant\n" + "BAD reult\nGOOD result\n" + "BAD reveiw\nGOOD review\n" + "BAD reveiwing\nGOOD reviewing\n" + "BAD rumers\nGOOD rumors\n" + "BAD rwite\nGOOD write\n" + "BAD rythm\nGOOD rhythm\n" + "BAD saidhe\nGOOD said he\n" + "BAD saidit\nGOOD said it\n" + "BAD saidthat\nGOOD said that\n" + "BAD saidthe\nGOOD said the\n" + "COMPLETE 0\nBAD saidt he \nGOOD said the \n" + "BAD sandwhich\nGOOD sandwich\n" + "BAD sandwitch\nGOOD sandwich\n" + "BAD saturday\nGOOD Saturday\n" + "BAD scedule\nGOOD schedule\n" + "BAD sceduled\nGOOD scheduled\n" + "BAD seance\nGOOD s\303\251ance\n" + "BAD secratary\nGOOD secretary\n" + "BAD sectino\nGOOD section\n" + "BAD selectoin\nGOOD selection\n" + "BAD sence\nGOOD sense\n" + "BAD sentance\nGOOD sentence\n" + "BAD separeate\nGOOD separate\n" + "BAD seperate\nGOOD separate\n" + "BAD sercumstances\nGOOD circumstances\n" + "BAD shcool\nGOOD school\n" + "COMPLETE 0\nBAD she;d \nGOOD she'd \n" + "COMPLETE 0\nBAD she;ll \nGOOD she'll \n" + "BAD shes\nGOOD she's\n" + "COMPLETE 0\nBAD she;s \nGOOD she's \n" + "BAD shesaid\nGOOD she said\n" + "BAD shineing\nGOOD shining\n" + "BAD shiped\nGOOD shipped\n" + "BAD shoudl\nGOOD should\n" + "COMPLETE 0\nBAD shoudln't \nGOOD shouldn't \n" + "BAD shouldent\nGOOD shouldn't\n" + "BAD shouldnt\nGOOD shouldn't\n" + "COMPLETE 0\nBAD shouldn;t \nGOOD shouldn't \n" + "COMPLETE 0\nBAD should of been\nGOOD should have been\n" + "COMPLETE 0\nBAD should of had\nGOOD should have had\n" + "BAD shouldve\nGOOD should've\n" + "BAD showinf\nGOOD showing\n" + "BAD signifacnt\nGOOD significant\n" + "BAD simalar\nGOOD similar\n" + "BAD similiar\nGOOD similar\n" + "BAD simpyl\nGOOD simply\n" + "BAD sincerly\nGOOD sincerely\n" + "BAD sitll\nGOOD still\n" + "BAD smae\nGOOD same\n" + "BAD smoe\nGOOD some\n" + "BAD soem\nGOOD some\n" + "BAD sohw\nGOOD show\n" + "BAD soical\nGOOD social\n" + "BAD some1\nGOOD someone\n" + "BAD somethign\nGOOD something\n" + "BAD someting\nGOOD something\n" + "BAD somewaht\nGOOD somewhat\n" + "BAD somthing\nGOOD something\n" + "BAD somtimes\nGOOD sometimes\n" + "COMPLETE 0\nBAD sot hat \nGOOD so that \n" + "BAD soudn\nGOOD sound\n" + "BAD soudns\nGOOD sounds\n" + "BAD speach\nGOOD speech\n" + "BAD specificaly\nGOOD specifically\n" + "BAD specificalyl\nGOOD specifically\n" + "BAD spelt\nGOOD spelled\n" + "BAD sry\nGOOD sorry\n" + "COMPLETE 0\nBAD state of the ark\nGOOD state of the art\n" + "BAD statment\nGOOD statement\n" + "BAD statments\nGOOD statements\n" + "BAD stnad\nGOOD stand\n" + "BAD stopry\nGOOD story\n" + "BAD stoyr\nGOOD story\n" + "BAD stpo\nGOOD stop\n" + "BAD strentgh\nGOOD strength\n" + "BAD stroy\nGOOD story\n" + "BAD struggel\nGOOD struggle\n" + "BAD strugle\nGOOD struggle\n" + "BAD studnet\nGOOD student\n" + "BAD successfull\nGOOD successful\n" + "BAD successfuly\nGOOD successfully\n" + "BAD successfulyl\nGOOD successfully\n" + "BAD sucess\nGOOD success\n" + "BAD sucessfull\nGOOD successful\n" + "BAD sufficiant\nGOOD sufficient\n" + "BAD sum1\nGOOD someone\n" + "BAD sunday\nGOOD Sunday\n" + "BAD suposed\nGOOD supposed\n" + "BAD supposably\nGOOD supposedly\n" + "BAD suppossed\nGOOD supposed\n" + "BAD suprise\nGOOD surprise\n" + "BAD suprised\nGOOD surprised\n" + "BAD sux\nGOOD sucks\n" + "BAD swiming\nGOOD swimming\n" + "BAD tahn\nGOOD than\n" + "BAD taht\nGOOD that\n" + "COMPLETE 0\nBAD take it for granite\nGOOD take it for granted\n" + "COMPLETE 0\nBAD taken for granite\nGOOD taken for granted\n" + "BAD talekd\nGOOD talked\n" + "BAD talkign\nGOOD talking\n" + "BAD tath\nGOOD that\n" + "BAD tecnical\nGOOD technical\n" + "BAD tehy\nGOOD they\n" + "COMPLETE 0\nBAD tellt he \nGOOD tell the \n" + "BAD termoil\nGOOD turmoil\n" + "BAD tets\nGOOD test\n" + "BAD tghis\nGOOD this\n" + "BAD thansk\nGOOD thanks\n" + "BAD thanx\nGOOD thanks\n" + "BAD thats\nGOOD that's\n" + "BAD thatthe\nGOOD that the\n" + "COMPLETE 0\nBAD thatt he \nGOOD that the \n" + "BAD thecompany\nGOOD the company\n" + "BAD thefirst\nGOOD the first\n" + "BAD thegovernment\nGOOD the government\n" + "COMPLETE 0\nBAD their are \nGOOD there are \n" + "COMPLETE 0\nBAD their aren't \nGOOD there aren't \n" + "COMPLETE 0\nBAD their is \nGOOD there is \n" + "BAD themself\nGOOD themselves\n" + "BAD themselfs\nGOOD themselves\n" + "BAD thenew\nGOOD the new\n" + "BAD theres\nGOOD there's\n" + "COMPLETE 0\nBAD there's is \nGOOD theirs is \n" + "COMPLETE 0\nBAD there's isn't \nGOOD theirs isn't \n" + "BAD theri\nGOOD their\n" + "BAD thesame\nGOOD the same\n" + "BAD thetwo\nGOOD the two\n" + "BAD theyd\nGOOD they'd\n" + "COMPLETE 0\nBAD they;d \nGOOD they'd \n" + "COMPLETE 0\nBAD they;l \nGOOD they'll \n" + "BAD theyll\nGOOD they'll\n" + "COMPLETE 0\nBAD they;ll \nGOOD they'll \n" + "COMPLETE 0\nBAD they;r \nGOOD they're \n" + "COMPLETE 0\nBAD theyre \nGOOD they're \n" + "COMPLETE 0\nBAD they;re \nGOOD they're \n" + "COMPLETE 0\nBAD they're are \nGOOD there are \n" + "COMPLETE 0\nBAD they're is \nGOOD there is \n" + "COMPLETE 0\nBAD they;v \nGOOD they've \n" + "BAD theyve\nGOOD they've\n" + "COMPLETE 0\nBAD they;ve \nGOOD they've \n" + "BAD thgat\nGOOD that\n" + "BAD thier\nGOOD their \n" + "BAD thigsn\nGOOD things\n" + "BAD thisyear\nGOOD this year\n" + "BAD thme\nGOOD them\n" + "BAD thna\nGOOD than\n" + "BAD thne\nGOOD then\n" + "BAD thnig\nGOOD thing\n" + "BAD thnigs\nGOOD things\n" + "BAD tho\nGOOD though\n" + "BAD threatend\nGOOD threatened\n" + "BAD thsi\nGOOD this\n" + "BAD thsoe\nGOOD those\n" + "BAD thta\nGOOD that\n" + "BAD thursday\nGOOD Thursday\n" + "BAD thx\nGOOD thanks\n" + "BAD tihs\nGOOD this\n" + "BAD timne\nGOOD time\n" + "BAD tiogether\nGOOD together\n" + "BAD tkae\nGOOD take\n" + "BAD tkaes\nGOOD takes\n" + "BAD tkaing\nGOOD taking\n" + "BAD tlaking\nGOOD talking\n" + "BAD tnx\nGOOD thanks\n" + "BAD todya\nGOOD today\n" + "BAD togehter\nGOOD together\n" + "COMPLETE 0\nBAD toldt he \nGOOD told the \n" + "BAD tomorow\nGOOD tomorrow\n" + "BAD tongiht\nGOOD tonight\n" + "BAD tonihgt\nGOOD tonight\n" + "BAD tonite\nGOOD tonight\n" + "BAD totaly\nGOOD totally\n" + "BAD totalyl\nGOOD totally\n" + "BAD tothe\nGOOD to the\n" + "COMPLETE 0\nBAD tot he \nGOOD to the \n" + "BAD touche\nGOOD touch\303\251\n" + "BAD towrad\nGOOD toward\n" + "BAD traditionalyl\nGOOD traditionally\n" + "BAD transfered\nGOOD transferred\n" + "BAD truely\nGOOD truly\n" + "BAD truley\nGOOD truly\n" + "BAD tryed\nGOOD tried\n" + "BAD tuesday\nGOOD Tuesday\n" + "BAD tyhat\nGOOD that\n" + "BAD udnerstand\nGOOD understand\n" + "BAD understnad\nGOOD understand\n" + "COMPLETE 0\nBAD undert he \nGOOD under the \n" + "BAD unforseen\nGOOD unforeseen\n" + "BAD UnitedStates\nGOOD United States\n" + "BAD unliek\nGOOD unlike\n" + "BAD unpleasently\nGOOD unpleasantly\n" + "BAD untill\nGOOD until\n" + "BAD untilll\nGOOD until\n" + "BAD ur\nGOOD you are\n" + "BAD useing\nGOOD using\n" + "BAD usualyl\nGOOD usually\n" + "BAD veyr\nGOOD very\n" + "BAD virtualyl\nGOOD virtually\n" + "BAD visavis\nGOOD vis-a-vis\n" + "COMPLETE 0\nBAD vis-a-vis\nGOOD vis-\303\240-vis\n" + "BAD vrey\nGOOD very\n" + "BAD vulnerible\nGOOD vulnerable\n" + "BAD waht\nGOOD what\n" + "BAD warrent\nGOOD warrant\n" + "COMPLETE 0\nBAD wa snot \nGOOD was not \n" + "COMPLETE 0\nBAD wasnt \nGOOD wasn't \n" + "COMPLETE 0\nBAD wasn;t \nGOOD wasn't \n" + "BAD watn\nGOOD want\n" + "COMPLETE 0\nBAD we;d \nGOOD we'd \n" + "BAD wednesday\nGOOD Wednesday\n" + "BAD wehn\nGOOD when\n" + "COMPLETE 0\nBAD we'l \nGOOD we'll \n" + "COMPLETE 0\nBAD we;ll \nGOOD we'll \n" + "COMPLETE 0\nBAD we;re \nGOOD we're \n" + "BAD werent\nGOOD weren't\n" + "COMPLETE 0\nBAD weren;t \nGOOD weren't \n" + "COMPLETE 0\nBAD wern't \nGOOD weren't \n" + "BAD werre\nGOOD were\n" + "BAD weve\nGOOD we've\n" + "COMPLETE 0\nBAD we;ve \nGOOD we've \n" + "BAD whats\nGOOD what's\n" + "COMPLETE 0\nBAD what;s \nGOOD what's \n" + "BAD whcih\nGOOD which\n" + "COMPLETE 0\nBAD whent he \nGOOD when the \n" + "BAD wheres\nGOOD where's\n" + "COMPLETE 0\nBAD where;s \nGOOD where's \n" + "BAD wherre\nGOOD where\n" + "BAD whic\nGOOD which\n" + "COMPLETE 0\nBAD whicht he \nGOOD which the \n" + "BAD whihc\nGOOD which\n" + "BAD wholl\nGOOD who'll\n" + "BAD whos\nGOOD who's\n" + "COMPLETE 0\nBAD who;s \nGOOD who's \n" + "BAD whove\nGOOD who've\n" + "COMPLETE 0\nBAD who;ve \nGOOD who've \n" + "BAD whta\nGOOD what\n" + "BAD whys\nGOOD why's\n" + "BAD wief\nGOOD wife\n" + "BAD wierd\nGOOD weird\n" + "BAD wihch\nGOOD which\n" + "BAD wiht\nGOOD with\n" + "BAD willbe\nGOOD will be\n" + "COMPLETE 0\nBAD will of been\nGOOD will have been\n" + "COMPLETE 0\nBAD will of had\nGOOD will have had\n" + "BAD windoes\nGOOD windows\n" + "BAD witha\nGOOD with a\n" + "BAD withdrawl\nGOOD withdrawal\n" + "BAD withe\nGOOD with\n" + "COMPLETE 0\nBAD withthe \nGOOD with the \n" + "BAD witht he\nGOOD with the\n" + "BAD wiull\nGOOD will\n" + "BAD wnat\nGOOD want\n" + "BAD wnated\nGOOD wanted\n" + "BAD wnats\nGOOD wants\n" + "BAD wohle\nGOOD whole\n" + "BAD wokr\nGOOD work\n" + "BAD wont\nGOOD won't\n" + "COMPLETE 0\nBAD wo'nt \nGOOD won't \n" + "COMPLETE 0\nBAD won;t \nGOOD won't \n" + "BAD woudl\nGOOD would\n" + "COMPLETE 0\nBAD woudln't \nGOOD wouldn't \n" + "BAD wouldbe\nGOOD would be\n" + "BAD wouldnt\nGOOD wouldn't\n" + "COMPLETE 0\nBAD wouldn;t \nGOOD wouldn't \n" + "COMPLETE 0\nBAD would of been\nGOOD would have been\n" + "COMPLETE 0\nBAD would of had\nGOOD would have had\n" + "BAD wouldve\nGOOD would've\n" + "BAD wriet\nGOOD write\n" + "BAD writting\nGOOD writing\n" + "BAD wrod\nGOOD word\n" + "BAD wroet\nGOOD wrote\n" + "BAD wroking\nGOOD working\n" + "BAD wtih\nGOOD with\n" + "BAD wuould\nGOOD would\n" + "BAD wud\nGOOD would\n" + "BAD yera\nGOOD year\n" + "BAD yeras\nGOOD years\n" + "BAD yersa\nGOOD years\n" + "BAD youare\nGOOD you are\n" + "BAD youd\nGOOD you'd\n" + "COMPLETE 0\nBAD you;d \nGOOD you'd \n" + "BAD youll\nGOOD you'll\n" + "COMPLETE 0\nBAD your a \nGOOD you're a \n" + "COMPLETE 0\nBAD your an \nGOOD you're an \n" + "BAD youre\nGOOD you're\n" + "COMPLETE 0\nBAD you;re \nGOOD you're \n" + "COMPLETE 0\nBAD you're own \nGOOD your own \n" + "COMPLETE 0\nBAD your her \nGOOD you're her \n" + "COMPLETE 0\nBAD your here \nGOOD you're here \n" + "COMPLETE 0\nBAD your his \nGOOD you're his \n" + "COMPLETE 0\nBAD your my \nGOOD you're my \n" + "COMPLETE 0\nBAD your the \nGOOD you're the \n" + "COMPLETE 0\nBAD your their \nGOOD you're their \n" + "COMPLETE 0\nBAD your your \nGOOD you're your \n" + "BAD youve\nGOOD you've\n" + "COMPLETE 0\nBAD you;ve \nGOOD you've \n" + "BAD yuor\nGOOD your\n"; + gboolean complete = TRUE; + gboolean case_sensitive = FALSE; + buf = g_build_filename(purple_config_dir(), "dict", NULL); + if (!(g_file_get_contents(buf, &ibuf, &size, NULL) && ibuf)) { + ibuf = g_strdup(defaultconf); + size = strlen(defaultconf); + model = gtk_list_store_new((gint)N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); + hashes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + while (ibuf && buf_get_line(ibuf, &buf, &pnt, size)) { + if (!g_ascii_strncasecmp(buf, "BAD ", 4)) + strncpy(bad, buf + 4, 81); + else if(!g_ascii_strncasecmp(buf, "CASE ", 5)) + case_sensitive = *(buf+5) == '0' ? FALSE : TRUE; + else if(!g_ascii_strncasecmp(buf, "COMPLETE ", 9)) + complete = *(buf+9) == '0' ? FALSE : TRUE; + else if (!g_ascii_strncasecmp(buf, "GOOD ", 5)) + strncpy(good, buf + 5, 255); + if (*bad && *good && g_hash_table_lookup(hashes, bad) == NULL) { + /* We don't actually need to store the good string, since this + * hash is just being used to eliminate duplicate bad strings. + * The value has to be non-NULL so the lookup above will work. + g_hash_table_insert(hashes, g_strdup(bad), GINT_TO_POINTER(1)); + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, + WORD_ONLY_COLUMN, complete, + CASE_SENSITIVE_COLUMN, case_sensitive, + case_sensitive = FALSE; + g_hash_table_destroy(hashes); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), + 0, GTK_SORT_ASCENDING); +static GtkWidget *bad_entry; +static GtkWidget *good_entry; +static GtkWidget *complete_toggle; +static GtkWidget *case_toggle; +static void save_list(void); +static void on_edited(GtkCellRendererText *cellrenderertext, + gchar *path, gchar *arg2, gpointer data) + g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path)); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GPOINTER_TO_INT(data), &val); + if (!purple_strequal(arg2, g_value_get_string(&val))) { + gtk_list_store_set(model, &iter, GPOINTER_TO_INT(data), arg2, -1); +static void word_only_toggled(GtkCellRendererToggle *cellrenderertoggle, + gchar *path, gpointer data){ + g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path)); + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, + WORD_ONLY_COLUMN, &enabled, + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + WORD_ONLY_COLUMN, !enabled, + /* I want to be sure that the above change has happened to the GtkTreeView first. */ + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + CASE_SENSITIVE_COLUMN, enabled, +static void case_sensitive_toggled(GtkCellRendererToggle *cellrenderertoggle, + gchar *path, gpointer data){ + g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path)); + /* Prevent the case sensitive column from changing on non-whole word replacements. + * Ideally, the column would be set insensitive in the word_only_toggled callback. */ + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, + WORD_ONLY_COLUMN, &enabled, + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, + CASE_SENSITIVE_COLUMN, &enabled, + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + CASE_SENSITIVE_COLUMN, !enabled, +static void list_add_new(void) + const char *word = gtk_editable_get_text(GTK_EDITABLE(bad_entry)); + gboolean case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(case_toggle)); + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) { + char *tmpword = g_utf8_casefold(word, -1); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &bad_val); + GValue case_sensitive_val; + case_sensitive_val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &case_sensitive_val); + /* If they're both case-sensitive, then compare directly. + * Otherwise, they overlap. */ + if (g_value_get_boolean(&case_sensitive_val)) + match = purple_strequal(g_value_get_string(&bad_val), word); + char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1); + match = purple_strequal(bad, tmpword); + g_value_unset(&case_sensitive_val); + char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1); + match = purple_strequal(bad, tmpword); + g_value_unset(&bad_val); + purple_notify_error(NULL, _("Duplicate Correction"), + _("The specified word already exists in the correction list."), + gtk_editable_get_text(GTK_EDITABLE(bad_entry)), NULL); + g_value_unset(&bad_val); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter)); + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, + GOOD_COLUMN, gtk_editable_get_text(GTK_EDITABLE(good_entry)), + WORD_ONLY_COLUMN, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(complete_toggle)), + CASE_SENSITIVE_COLUMN, case_sensitive, + gtk_editable_delete_text(GTK_EDITABLE(bad_entry), 0, -1); + gtk_editable_delete_text(GTK_EDITABLE(good_entry), 0, -1); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE); + gtk_widget_grab_focus(bad_entry); +static void add_selected_row_to_list(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) + GtkTreeRowReference *row_reference; + GSList **list = (GSList **)data; + row_reference = gtk_tree_row_reference_new(model, path); + *list = g_slist_prepend(*list, row_reference); +static void remove_row(gpointer data) + GtkTreeRowReference *row_reference = (GtkTreeRowReference *)data; + path = gtk_tree_row_reference_get_path(row_reference); + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) + gtk_list_store_remove(model, &iter); + gtk_tree_path_free(path); + gtk_tree_row_reference_free(row_reference); +static void list_delete(void) + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); + gtk_tree_selection_selected_foreach(sel, add_selected_row_to_list, &list); + g_slist_free_full(list, remove_row); + data = g_string_new(""); + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) { + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val0); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val1); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val2); + gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val3); + g_string_append_printf(data, "COMPLETE %d\nCASE %d\nBAD %s\nGOOD %s\n\n", + g_value_get_boolean(&val2), + g_value_get_boolean(&val3), + g_value_get_string(&val0), + g_value_get_string(&val1)); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter)); + purple_util_write_data_to_config_file("dict", data->str, -1); + g_string_free(data, TRUE); +static void on_selection_changed(GtkTreeSelection *sel, + num_selected = gtk_tree_selection_count_selected_rows(sel); + gtk_widget_set_sensitive((GtkWidget*)data, (num_selected > 0)); +static gboolean non_empty(const char *s) + while (*s && g_ascii_isspace(*s)) +static void on_entry_changed(GtkEditable *editable, gpointer data) + gtk_widget_set_sensitive((GtkWidget*)data, + non_empty(gtk_editable_get_text(GTK_EDITABLE(bad_entry))) && + non_empty(gtk_editable_get_text(GTK_EDITABLE(good_entry)))); +static void whole_words_button_toggled(GtkToggleButton *complete_toggle, GtkToggleButton *case_toggle) + gboolean enabled = gtk_toggle_button_get_active(complete_toggle); + gtk_toggle_button_set_active(case_toggle, !enabled); + gtk_widget_set_sensitive(GTK_WIDGET(case_toggle), enabled); +get_config_frame(PurplePlugin *plugin) + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); + gtk_container_set_border_width (GTK_CONTAINER(ret), 12); + vbox = pidgin_make_frame(ret, _("Text Replacements")); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); + tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_widget_set_size_request(tree, -1, 200); + renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(renderer), + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(on_edited), GINT_TO_POINTER(0)); + column = gtk_tree_view_column_new_with_attributes(_("You type"), renderer, + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 150); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(renderer), + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(on_edited), GINT_TO_POINTER(1)); + column = gtk_tree_view_column_new_with_attributes(_("You send"), renderer, + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, 150); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_toggle_new(); + g_object_set(G_OBJECT(renderer), + g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(word_only_toggled), NULL); + column = gtk_tree_view_column_new_with_attributes(_("Whole words only"), renderer, + "active", WORD_ONLY_COLUMN, + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + renderer = gtk_cell_renderer_toggle_new(); + g_object_set(G_OBJECT(renderer), + g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(case_sensitive_toggled), NULL); + column = gtk_tree_view_column_new_with_attributes(_("Case sensitive"), renderer, + "active", CASE_SENSITIVE_COLUMN, + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), + GTK_SELECTION_MULTIPLE); + sw = gtk_scrolled_window_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, + gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), tree); + gtk_widget_set_vexpand(sw, TRUE); + gtk_box_append(GTK_BOX(vbox), sw); + hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic(_("_Delete")); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_delete), NULL); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_widget_set_sensitive(button, FALSE); + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree))), + "changed", G_CALLBACK(on_selection_changed), button); + gtk_widget_show(button); + vbox = pidgin_make_frame(ret, _("Add a new text replacement")); + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); + gtk_widget_show(vbox2); + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + bad_entry = gtk_entry_new(); + /* Set a minimum size. Since they're in a size group, the other entry will match up. */ + gtk_widget_set_size_request(bad_entry, 350, -1); + gtk_size_group_add_widget(sg2, bad_entry); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _type:"), sg, bad_entry, FALSE, NULL); + good_entry = gtk_entry_new(); + gtk_size_group_add_widget(sg2, good_entry); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _send:"), sg, good_entry, FALSE, NULL); + /* Created here so it can be passed to whole_words_button_toggled. */ + case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)")); + complete_toggle = gtk_check_button_new_with_mnemonic(_("Only replace _whole words")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE); + g_signal_connect(G_OBJECT(complete_toggle), "clicked", + G_CALLBACK(whole_words_button_toggled), case_toggle); + gtk_widget_show(complete_toggle); + gtk_box_pack_start(GTK_BOX(vbox2), complete_toggle, FALSE, FALSE, 0); + /* The button is created above so it can be passed to whole_words_button_toggled. */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE); + gtk_widget_show(case_toggle); + gtk_box_pack_start(GTK_BOX(vbox2), case_toggle, FALSE, FALSE, 0); + button = gtk_button_new_with_mnemonic(_("_Add")); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_add_new), NULL); + vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start(GTK_BOX(hbox), vbox3, TRUE, FALSE, 0); + gtk_widget_show(vbox3); + gtk_box_pack_end(GTK_BOX(vbox3), button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(bad_entry), "changed", G_CALLBACK(on_entry_changed), button); + g_signal_connect(G_OBJECT(good_entry), "changed", G_CALLBACK(on_entry_changed), button); + gtk_widget_set_sensitive(button, FALSE); + gtk_widget_show(button); + vbox = pidgin_make_frame(ret, _("General Text Replacement Options")); + pidgin_prefs_checkbox(_("Enable replacement of last word on send"), + "/plugins/gtk/spellchk/last_word_replace", vbox); + gtk_widget_show_all(ret); +static GPluginPluginInfo * +spell_check_query(GError **error) + const gchar * const authors[] = { + "Eric Warmenhoven <eric@warmenhoven.org>", + return pidgin_plugin_info_new( + "id", SPELLCHECK_PLUGIN_ID, + "name", N_("Text replacement"), + "version", DISPLAY_VERSION, + "category", N_("Utility"), + "summary", N_("Replaces text in outgoing messages according to user-defined rules."), + "description", N_("Replaces text in outgoing messages according to user-defined rules."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, +spell_check_load(GPluginPlugin *plugin, GError **error) + void *conv_handle = purple_conversations_get_handle(); + purple_prefs_add_none("/plugins"); + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none("/plugins/gtk/spellchk"); + purple_prefs_add_bool("/plugins/gtk/spellchk/last_word_replace", TRUE); + /* Attach to existing conversations */ + for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) + spellchk_new_attach((PurpleConversation *)convs->data); + purple_signal_connect(conv_handle, "conversation-created", + plugin, G_CALLBACK(spellchk_new_attach), NULL); +spell_check_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) + /* Detach from existing conversations */ + for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) + PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)convs->data); + spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY); + g_signal_handlers_disconnect_by_func(gtkconv->entry, message_send_cb, spell); + g_object_set_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY, NULL); +GPLUGIN_NATIVE_PLUGIN_DECLARE(spell_check) --- a/pidgin/plugins/transparency.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,615 +0,0 @@
- * Pidgin - Transparency plugin
- * Copyright (C) 1998-2002, Rob Flynn <rob@marko.net>
- * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
- * Copyright (C) 2005, Daniel Atallah <daniel_atallah@yahoo.com>
- * 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
-#include <glib/gi18n-lib.h>
-#include <gplugin-native.h>
-/* The plugin name is left unchanged from its WinAPI days in order to keep it
- * loading for users who were using it. */
-#define WINTRANS_PLUGIN_ID "gtk-win-trans"
-/* Key used to save GtkEventControllerFocus for this plugin. */
-#define WINTRANS_CONTROLLER_KEY (WINTRANS_PLUGIN_ID "-focus-controller")
-/* Key used to save GtkSlider for this plugin. */
-#define WINTRANS_SLIDER_KEY (WINTRANS_PLUGIN_ID "-slider")
-static const char *OPT_WINTRANS_IM_ENABLED= "/plugins/gtk/transparency/im_enabled";
-static const char *OPT_WINTRANS_IM_ALPHA = "/plugins/gtk/transparency/im_alpha";
-static const char *OPT_WINTRANS_IM_SLIDER = "/plugins/gtk/transparency/im_slider";
-static const char *OPT_WINTRANS_IM_ONFOCUS= "/plugins/gtk/transparency/im_solid_onfocus";
-static const char *OPT_WINTRANS_BL_ENABLED= "/plugins/gtk/transparency/bl_enabled";
-static const char *OPT_WINTRANS_BL_ALPHA = "/plugins/gtk/transparency/bl_alpha";
-static const char *OPT_WINTRANS_BL_ONFOCUS= "/plugins/gtk/transparency/bl_solid_onfocus";
-get_buddy_list_window(void) {
- PurpleBuddyList *purple_blist = NULL;
- purple_blist = purple_blist_get_default();
- if(PIDGIN_IS_BUDDY_LIST(purple_blist)) {
- PidginBuddyList *pidgin_blist = PIDGIN_BUDDY_LIST(purple_blist);
- return pidgin_blist->window;
-/* Set window transparency level */
-set_wintrans(GtkWidget *window, int alpha, gboolean enabled)
- gtk_widget_set_opacity(window, alpha / 255.0);
- gtk_widget_set_opacity(window, 1);
- /* Changing from opaque to partially transparent seems to need some kind of
- * structural refresh. Unfortunately, a simple `gtk_widget_queue_draw` is
- * not sufficient, so we need to do this instead. */
- gtk_widget_queue_resize(window);
-/* When a conv window is focused, if we're only transparent when unfocused,
- * deal with transparency */
-focus_conv_win_cb(GtkEventControllerFocus *self, gpointer data) {
- GtkWidget *window = NULL;
- gboolean enter = GPOINTER_TO_INT(data);
- if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
- if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
- window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
- set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), !enter);
-add_focus_controller_to_conv_win(GtkWidget *window) {
- GtkEventController *focus = NULL;
- focus = gtk_event_controller_focus_new();
- g_signal_connect(focus, "enter", G_CALLBACK(focus_conv_win_cb),
- GINT_TO_POINTER(TRUE));
- g_signal_connect(focus, "leave", G_CALLBACK(focus_conv_win_cb),
- GINT_TO_POINTER(FALSE));
- gtk_widget_add_controller(window, focus);
- g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus);
-remove_focus_controller_from_conv_win(GtkWidget *window) {
- GtkEventController *focus = NULL;
- focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY);
- if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) {
- gtk_widget_remove_controller(window, focus);
- g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, NULL);
-/* When buddy list window is focused,
- * if we're only transparent when unfocused, deal with transparency */
-focus_blist_win_cb(GtkEventControllerFocus *self, gpointer data)
- GtkWidget *window = NULL;
- gboolean enter = GPOINTER_TO_INT(data);
- if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
- if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ONFOCUS)) {
- window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
- set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), !enter);
-static void change_alpha(GtkWidget *w, gpointer data) {
- int alpha = gtk_range_get_value(GTK_RANGE(w));
- purple_prefs_set_int(OPT_WINTRANS_IM_ALPHA, alpha);
- /* If we're in no-transparency on focus mode,
- * don't take effect immediately */
- if (!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
- set_wintrans(GTK_WIDGET(data), alpha, TRUE);
-/* Clean up transparency stuff for the conv window */
-conversation_delete_cb(G_GNUC_UNUSED GtkApplication *application,
- GtkWindow *window, G_GNUC_UNUSED gpointer data)
- if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
- purple_debug_info(WINTRANS_PLUGIN_ID,
- "Conv window destroyed... removing from list");
- g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL);
- /* Remove the focus cbs */
- remove_focus_controller_from_conv_win(GTK_WIDGET(window));
-static void set_blist_trans(GtkWidget *w, const char *pref) {
- GtkWidget *window = get_buddy_list_window();
- gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
- purple_prefs_set_bool(pref, enabled);
- set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
- purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED));
-remove_slider(GtkWidget *slider_frame) {
- purple_prefs_disconnect_by_handle(slider_frame);
- gtk_widget_unparent(slider_frame);
-update_slider(G_GNUC_UNUSED const gchar *name,
- G_GNUC_UNUSED PurplePrefType type,
- gconstpointer val, gpointer data)
- GtkWidget *slider = data;
- gint imalpha = GPOINTER_TO_INT(val);
- gtk_range_set_value(GTK_RANGE(slider), imalpha);
-static void add_slider(GtkWidget *win) {
- GtkWidget *vbox = NULL;
- GtkWidget *slider_frame;
- GtkWidget *label, *slider;
- /* Look up this window to see if it already has a slider */
- if (g_object_get_data(G_OBJECT(win), WINTRANS_SLIDER_KEY) != NULL) {
- vbox = gtk_widget_get_first_child(win);
- while(vbox != NULL && !GTK_IS_BOX(vbox)) {
- vbox = gtk_widget_get_next_sibling(vbox);
- purple_debug_error(WINTRANS_PLUGIN_ID, "no vbox found");
- slider_frame = gtk_frame_new(NULL);
- gtk_widget_set_margin_start(slider_frame, 6);
- gtk_widget_set_margin_end(slider_frame, 6);
- gtk_box_prepend(GTK_BOX(vbox), slider_frame);
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_frame_set_child(GTK_FRAME(slider_frame), hbox);
- label = gtk_label_new(_("Opacity:"));
- gtk_box_append(GTK_BOX(hbox), label);
- slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
- gtk_widget_set_hexpand(slider, TRUE);
- gtk_box_append(GTK_BOX(hbox), slider);
- imalpha = purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA);
- gtk_range_set_value(GTK_RANGE(slider), imalpha);
- /* On slider val change, update window's transparency level */
- g_signal_connect(G_OBJECT(slider), "value-changed",
- G_CALLBACK(change_alpha), win);
- /* Set the initial transparency level */
- set_wintrans(win, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE);
- purple_prefs_connect_callback(slider_frame, OPT_WINTRANS_IM_ALPHA,
- update_slider, slider);
- /* Set window data, to track that it has a slider */
- g_object_set_data_full(G_OBJECT(win), WINTRANS_SLIDER_KEY, slider_frame,
- (GDestroyNotify)remove_slider);
-/* Remove all transparency related aspects from conversation windows */
-static void remove_convs_wintrans(gboolean remove_signal) {
- GApplication *application = NULL;
- application = g_application_get_default();
- wins = gtk_application_get_windows(GTK_APPLICATION(application));
- for(; wins; wins = wins->next) {
- GtkWidget *window = wins->data;
- if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
- if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
- set_wintrans(window, 0, FALSE);
- /* Remove the focus cbs */
- remove_focus_controller_from_conv_win(window);
- g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL);
-set_conv_window_trans(GtkWidget *window) {
- /* check prefs to see if we want trans */
- if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
- set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE);
- if (purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) {
-static void update_convs_wintrans(GtkWidget *toggle_btn, const char *pref) {
- purple_prefs_set_bool(pref, gtk_toggle_button_get_active(
- GTK_TOGGLE_BUTTON(toggle_btn)));
- if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
- GApplication *application = NULL;
- application = g_application_get_default();
- wins = gtk_application_get_windows(GTK_APPLICATION(application));
- for(; wins; wins = wins->next) {
- GtkWidget *win = wins->data;
- if(!PIDGIN_IS_CONVERSATION_WINDOW(win)) {
- set_conv_window_trans(win);
- if (!purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) {
- g_object_set_data(G_OBJECT(win), WINTRANS_SLIDER_KEY, NULL);
- remove_convs_wintrans(FALSE);
-new_conversation_cb(G_GNUC_UNUSED GtkApplication *application,
- GtkWindow *window, G_GNUC_UNUSED gpointer data)
- if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
- set_conv_window_trans(GTK_WIDGET(window));
- add_focus_controller_to_conv_win(GTK_WIDGET(window));
-blist_created_cb(PurpleBuddyList *purple_blist, gpointer data) {
- GtkWidget *window = get_buddy_list_window();
- GtkEventController *focus = NULL;
- if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
- purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
- focus = gtk_event_controller_focus_new();
- g_signal_connect(focus, "enter", G_CALLBACK(focus_blist_win_cb),
- GINT_TO_POINTER(TRUE));
- g_signal_connect(focus, "leave", G_CALLBACK(focus_blist_win_cb),
- GINT_TO_POINTER(FALSE));
- gtk_widget_add_controller(window, focus);
- g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus);
-static void alpha_change(GtkWidget *w, gpointer data) {
- GApplication *application = NULL;
- int imalpha = gtk_range_get_value(GTK_RANGE(w));
- application = g_application_get_default();
- wins = gtk_application_get_windows(GTK_APPLICATION(application));
- for(; wins; wins = wins->next) {
- GtkWidget *window = wins->data;
- if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
- set_wintrans(window, imalpha, TRUE);
-alpha_pref_set_int(GtkEventControllerFocus *self, gpointer data) {
- const char *pref = data;
- GtkWidget *slider = NULL;
- slider = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
- alpha = gtk_range_get_value(GTK_RANGE(slider));
- purple_prefs_set_int(pref, alpha);
-static void bl_alpha_change(GtkWidget *w, gpointer data) {
- GtkWidget *window = get_buddy_list_window();
- change_alpha(w, window);
-update_existing_convs(void) {
- GApplication *application = NULL;
- application = g_application_get_default();
- wins = gtk_application_get_windows(GTK_APPLICATION(application));
- for(; wins; wins = wins->next) {
- GtkWidget *window = wins->data;
- if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
- set_conv_window_trans(window);
- add_focus_controller_to_conv_win(window);
-get_config_frame(PurplePlugin *plugin) {
- GtkWidget *imtransbox, *bltransbox;
- GtkWidget *label, *slider;
- GtkEventController *focus = NULL;
- ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- gtk_widget_set_margin_start(ret, 12);
- gtk_widget_set_margin_end(ret, 12);
- gtk_widget_set_margin_top(ret, 12);
- gtk_widget_set_margin_bottom(ret, 12);
- /* IM Convo trans options */
- imtransbox = pidgin_make_frame(ret, _("IM Conversation Windows"));
- button = pidgin_prefs_checkbox(_("_IM window transparency"),
- OPT_WINTRANS_IM_ENABLED, imtransbox);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(update_convs_wintrans),
- (gpointer) OPT_WINTRANS_IM_ENABLED);
- trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- g_object_bind_property(button, "active", trans_box, "sensitive",
- G_BINDING_SYNC_CREATE);
- button = pidgin_prefs_checkbox(_("_Show slider bar in IM window"),
- OPT_WINTRANS_IM_SLIDER, trans_box);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(update_convs_wintrans),
- (gpointer) OPT_WINTRANS_IM_SLIDER);
- button = pidgin_prefs_checkbox(
- _("Remove IM window transparency on focus"),
- OPT_WINTRANS_IM_ONFOCUS, trans_box);
- gtk_box_append(GTK_BOX(imtransbox), trans_box);
- /* IM transparency slider */
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
- label = gtk_label_new(_("Opacity:"));
- gtk_box_append(GTK_BOX(hbox), label);
- slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
- gtk_range_set_value(GTK_RANGE(slider),
- purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA));
- g_signal_connect(G_OBJECT(slider), "value-changed",
- G_CALLBACK(alpha_change), NULL);
- focus = gtk_event_controller_focus_new();
- g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int),
- (gpointer)OPT_WINTRANS_IM_ALPHA);
- gtk_widget_add_controller(slider, focus);
- gtk_box_append(GTK_BOX(hbox), slider);
- gtk_box_append(GTK_BOX(trans_box), hbox);
- /* Buddy List trans options */
- bltransbox = pidgin_make_frame (ret, _("Buddy List Window"));
- button = pidgin_prefs_checkbox(_("_Buddy List window transparency"),
- OPT_WINTRANS_BL_ENABLED, bltransbox);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(set_blist_trans),
- (gpointer) OPT_WINTRANS_BL_ENABLED);
- trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- g_object_bind_property(button, "active", trans_box, "sensitive",
- G_BINDING_SYNC_CREATE);
- button = pidgin_prefs_checkbox(
- _("Remove Buddy List window transparency on focus"),
- OPT_WINTRANS_BL_ONFOCUS, trans_box);
- gtk_box_append(GTK_BOX(bltransbox), trans_box);
- /* IM transparency slider */
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
- label = gtk_label_new(_("Opacity:"));
- gtk_box_append(GTK_BOX(hbox), label);
- slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
- gtk_range_set_value(GTK_RANGE(slider),
- purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA));
- g_signal_connect(G_OBJECT(slider), "value-changed",
- G_CALLBACK(bl_alpha_change), NULL);
- focus = gtk_event_controller_focus_new();
- g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int),
- (gpointer)OPT_WINTRANS_BL_ALPHA);
- gtk_widget_add_controller(slider, focus);
- gtk_box_append(GTK_BOX(hbox), slider);
- gtk_box_append(GTK_BOX(trans_box), hbox);
-static GPluginPluginInfo *
-transparency_query(GError **error) {
- const gchar * const authors[] = {
- "Pidgin Developers <devel@pidgin.im>",
- return pidgin_plugin_info_new(
- "id", WINTRANS_PLUGIN_ID,
- "name", N_("Transparency"),
- "version", DISPLAY_VERSION,
- "category", N_("User interface"),
- "summary", N_("Variable Transparency for the buddy list and conversations."),
- "description", N_("This plugin enables variable alpha transparency on conversation windows and the buddy list."),
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "gtk-config-frame-cb", get_config_frame,
-transparency_load(GPluginPlugin *plugin, GError **error) {
- GApplication *application = NULL;
- GtkWidget *window = NULL;
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none("/plugins/gtk/transparency");
- purple_prefs_add_bool(OPT_WINTRANS_IM_ENABLED, FALSE);
- purple_prefs_add_int(OPT_WINTRANS_IM_ALPHA, 255);
- purple_prefs_add_bool(OPT_WINTRANS_IM_SLIDER, FALSE);
- purple_prefs_add_bool(OPT_WINTRANS_IM_ONFOCUS, FALSE);
- purple_prefs_add_bool(OPT_WINTRANS_BL_ENABLED, FALSE);
- purple_prefs_add_int(OPT_WINTRANS_BL_ALPHA, 255);
- purple_prefs_add_bool(OPT_WINTRANS_BL_ONFOCUS, FALSE);
- purple_prefs_rename("/plugins/gtk/win32/wintrans", "/plugins/gtk/transparency");
- application = g_application_get_default();
- g_signal_connect(application, "window-added",
- G_CALLBACK(new_conversation_cb), NULL);
- g_signal_connect(application, "window-removed",
- G_CALLBACK(conversation_delete_cb), NULL);
- update_existing_convs();
- window = get_buddy_list_window();
- blist_created_cb(NULL, NULL);
- purple_signal_connect(pidgin_blist_get_handle(),
- "gtkblist-created", plugin,
- G_CALLBACK(blist_created_cb), NULL);
-transparency_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) {
- GtkWidget *window = NULL;
- purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading transparency plugin\n");
- remove_convs_wintrans(TRUE);
- window = get_buddy_list_window();
- GtkEventController *focus = NULL;
- if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
- set_wintrans(window, 0, FALSE);
- /* Remove the focus cbs */
- focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY);
- if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) {
- gtk_widget_remove_controller(window, focus);
-GPLUGIN_NATIVE_PLUGIN_DECLARE(transparency)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/transparency/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,7 @@
+transparency = library('transparency', 'transparency.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Transparency"'], + dependencies : [libpurple_dep, libpidgin_dep, glib], + install : true, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/transparency/transparency.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,615 @@
+ * Pidgin - Transparency plugin + * Copyright (C) 1998-2002, Rob Flynn <rob@marko.net> + * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com> + * Copyright (C) 2005, Daniel Atallah <daniel_atallah@yahoo.com> + * 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 +#include <glib/gi18n-lib.h> +#include <gplugin-native.h> +/* The plugin name is left unchanged from its WinAPI days in order to keep it + * loading for users who were using it. */ +#define WINTRANS_PLUGIN_ID "gtk-win-trans" +/* Key used to save GtkEventControllerFocus for this plugin. */ +#define WINTRANS_CONTROLLER_KEY (WINTRANS_PLUGIN_ID "-focus-controller") +/* Key used to save GtkSlider for this plugin. */ +#define WINTRANS_SLIDER_KEY (WINTRANS_PLUGIN_ID "-slider") +static const char *OPT_WINTRANS_IM_ENABLED= "/plugins/gtk/transparency/im_enabled"; +static const char *OPT_WINTRANS_IM_ALPHA = "/plugins/gtk/transparency/im_alpha"; +static const char *OPT_WINTRANS_IM_SLIDER = "/plugins/gtk/transparency/im_slider"; +static const char *OPT_WINTRANS_IM_ONFOCUS= "/plugins/gtk/transparency/im_solid_onfocus"; +static const char *OPT_WINTRANS_BL_ENABLED= "/plugins/gtk/transparency/bl_enabled"; +static const char *OPT_WINTRANS_BL_ALPHA = "/plugins/gtk/transparency/bl_alpha"; +static const char *OPT_WINTRANS_BL_ONFOCUS= "/plugins/gtk/transparency/bl_solid_onfocus"; +get_buddy_list_window(void) { + PurpleBuddyList *purple_blist = NULL; + purple_blist = purple_blist_get_default(); + if(PIDGIN_IS_BUDDY_LIST(purple_blist)) { + PidginBuddyList *pidgin_blist = PIDGIN_BUDDY_LIST(purple_blist); + return pidgin_blist->window; +/* Set window transparency level */ +set_wintrans(GtkWidget *window, int alpha, gboolean enabled) + gtk_widget_set_opacity(window, alpha / 255.0); + gtk_widget_set_opacity(window, 1); + /* Changing from opaque to partially transparent seems to need some kind of + * structural refresh. Unfortunately, a simple `gtk_widget_queue_draw` is + * not sufficient, so we need to do this instead. */ + gtk_widget_queue_resize(window); +/* When a conv window is focused, if we're only transparent when unfocused, + * deal with transparency */ +focus_conv_win_cb(GtkEventControllerFocus *self, gpointer data) { + GtkWidget *window = NULL; + gboolean enter = GPOINTER_TO_INT(data); + if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) { + if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) { + window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self)); + set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), !enter); +add_focus_controller_to_conv_win(GtkWidget *window) { + GtkEventController *focus = NULL; + focus = gtk_event_controller_focus_new(); + g_signal_connect(focus, "enter", G_CALLBACK(focus_conv_win_cb), + GINT_TO_POINTER(TRUE)); + g_signal_connect(focus, "leave", G_CALLBACK(focus_conv_win_cb), + GINT_TO_POINTER(FALSE)); + gtk_widget_add_controller(window, focus); + g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus); +remove_focus_controller_from_conv_win(GtkWidget *window) { + GtkEventController *focus = NULL; + focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY); + if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) { + gtk_widget_remove_controller(window, focus); + g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, NULL); +/* When buddy list window is focused, + * if we're only transparent when unfocused, deal with transparency */ +focus_blist_win_cb(GtkEventControllerFocus *self, gpointer data) + GtkWidget *window = NULL; + gboolean enter = GPOINTER_TO_INT(data); + if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) { + if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ONFOCUS)) { + window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self)); + set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), !enter); +static void change_alpha(GtkWidget *w, gpointer data) { + int alpha = gtk_range_get_value(GTK_RANGE(w)); + purple_prefs_set_int(OPT_WINTRANS_IM_ALPHA, alpha); + /* If we're in no-transparency on focus mode, + * don't take effect immediately */ + if (!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) { + set_wintrans(GTK_WIDGET(data), alpha, TRUE); +/* Clean up transparency stuff for the conv window */ +conversation_delete_cb(G_GNUC_UNUSED GtkApplication *application, + GtkWindow *window, G_GNUC_UNUSED gpointer data) + if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) { + purple_debug_info(WINTRANS_PLUGIN_ID, + "Conv window destroyed... removing from list"); + g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL); + /* Remove the focus cbs */ + remove_focus_controller_from_conv_win(GTK_WIDGET(window)); +static void set_blist_trans(GtkWidget *w, const char *pref) { + GtkWidget *window = get_buddy_list_window(); + gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + purple_prefs_set_bool(pref, enabled); + set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), + purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)); +remove_slider(GtkWidget *slider_frame) { + purple_prefs_disconnect_by_handle(slider_frame); + gtk_widget_unparent(slider_frame); +update_slider(G_GNUC_UNUSED const gchar *name, + G_GNUC_UNUSED PurplePrefType type, + gconstpointer val, gpointer data) + GtkWidget *slider = data; + gint imalpha = GPOINTER_TO_INT(val); + gtk_range_set_value(GTK_RANGE(slider), imalpha); +static void add_slider(GtkWidget *win) { + GtkWidget *vbox = NULL; + GtkWidget *slider_frame; + GtkWidget *label, *slider; + /* Look up this window to see if it already has a slider */ + if (g_object_get_data(G_OBJECT(win), WINTRANS_SLIDER_KEY) != NULL) { + vbox = gtk_widget_get_first_child(win); + while(vbox != NULL && !GTK_IS_BOX(vbox)) { + vbox = gtk_widget_get_next_sibling(vbox); + purple_debug_error(WINTRANS_PLUGIN_ID, "no vbox found"); + slider_frame = gtk_frame_new(NULL); + gtk_widget_set_margin_start(slider_frame, 6); + gtk_widget_set_margin_end(slider_frame, 6); + gtk_box_prepend(GTK_BOX(vbox), slider_frame); + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_frame_set_child(GTK_FRAME(slider_frame), hbox); + label = gtk_label_new(_("Opacity:")); + gtk_box_append(GTK_BOX(hbox), label); + slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1); + gtk_widget_set_hexpand(slider, TRUE); + gtk_box_append(GTK_BOX(hbox), slider); + imalpha = purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA); + gtk_range_set_value(GTK_RANGE(slider), imalpha); + /* On slider val change, update window's transparency level */ + g_signal_connect(G_OBJECT(slider), "value-changed", + G_CALLBACK(change_alpha), win); + /* Set the initial transparency level */ + set_wintrans(win, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE); + purple_prefs_connect_callback(slider_frame, OPT_WINTRANS_IM_ALPHA, + update_slider, slider); + /* Set window data, to track that it has a slider */ + g_object_set_data_full(G_OBJECT(win), WINTRANS_SLIDER_KEY, slider_frame, + (GDestroyNotify)remove_slider); +/* Remove all transparency related aspects from conversation windows */ +static void remove_convs_wintrans(gboolean remove_signal) { + GApplication *application = NULL; + application = g_application_get_default(); + wins = gtk_application_get_windows(GTK_APPLICATION(application)); + for(; wins; wins = wins->next) { + GtkWidget *window = wins->data; + if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) { + if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) { + set_wintrans(window, 0, FALSE); + /* Remove the focus cbs */ + remove_focus_controller_from_conv_win(window); + g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL); +set_conv_window_trans(GtkWidget *window) { + /* check prefs to see if we want trans */ + if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) { + set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE); + if (purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) { +static void update_convs_wintrans(GtkWidget *toggle_btn, const char *pref) { + purple_prefs_set_bool(pref, gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(toggle_btn))); + if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) { + GApplication *application = NULL; + application = g_application_get_default(); + wins = gtk_application_get_windows(GTK_APPLICATION(application)); + for(; wins; wins = wins->next) { + GtkWidget *win = wins->data; + if(!PIDGIN_IS_CONVERSATION_WINDOW(win)) { + set_conv_window_trans(win); + if (!purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) { + g_object_set_data(G_OBJECT(win), WINTRANS_SLIDER_KEY, NULL); + remove_convs_wintrans(FALSE); +new_conversation_cb(G_GNUC_UNUSED GtkApplication *application, + GtkWindow *window, G_GNUC_UNUSED gpointer data) + if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) { + set_conv_window_trans(GTK_WIDGET(window)); + add_focus_controller_to_conv_win(GTK_WIDGET(window)); +blist_created_cb(PurpleBuddyList *purple_blist, gpointer data) { + GtkWidget *window = get_buddy_list_window(); + GtkEventController *focus = NULL; + if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) { + purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), + focus = gtk_event_controller_focus_new(); + g_signal_connect(focus, "enter", G_CALLBACK(focus_blist_win_cb), + GINT_TO_POINTER(TRUE)); + g_signal_connect(focus, "leave", G_CALLBACK(focus_blist_win_cb), + GINT_TO_POINTER(FALSE)); + gtk_widget_add_controller(window, focus); + g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus); +static void alpha_change(GtkWidget *w, gpointer data) { + GApplication *application = NULL; + int imalpha = gtk_range_get_value(GTK_RANGE(w)); + application = g_application_get_default(); + wins = gtk_application_get_windows(GTK_APPLICATION(application)); + for(; wins; wins = wins->next) { + GtkWidget *window = wins->data; + if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) { + set_wintrans(window, imalpha, TRUE); +alpha_pref_set_int(GtkEventControllerFocus *self, gpointer data) { + const char *pref = data; + GtkWidget *slider = NULL; + slider = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self)); + alpha = gtk_range_get_value(GTK_RANGE(slider)); + purple_prefs_set_int(pref, alpha); +static void bl_alpha_change(GtkWidget *w, gpointer data) { + GtkWidget *window = get_buddy_list_window(); + change_alpha(w, window); +update_existing_convs(void) { + GApplication *application = NULL; + application = g_application_get_default(); + wins = gtk_application_get_windows(GTK_APPLICATION(application)); + for(; wins; wins = wins->next) { + GtkWidget *window = wins->data; + if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) { + set_conv_window_trans(window); + add_focus_controller_to_conv_win(window); +get_config_frame(PurplePlugin *plugin) { + GtkWidget *imtransbox, *bltransbox; + GtkWidget *label, *slider; + GtkEventController *focus = NULL; + ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); + gtk_widget_set_margin_start(ret, 12); + gtk_widget_set_margin_end(ret, 12); + gtk_widget_set_margin_top(ret, 12); + gtk_widget_set_margin_bottom(ret, 12); + /* IM Convo trans options */ + imtransbox = pidgin_make_frame(ret, _("IM Conversation Windows")); + button = pidgin_prefs_checkbox(_("_IM window transparency"), + OPT_WINTRANS_IM_ENABLED, imtransbox); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(update_convs_wintrans), + (gpointer) OPT_WINTRANS_IM_ENABLED); + trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); + g_object_bind_property(button, "active", trans_box, "sensitive", + G_BINDING_SYNC_CREATE); + button = pidgin_prefs_checkbox(_("_Show slider bar in IM window"), + OPT_WINTRANS_IM_SLIDER, trans_box); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(update_convs_wintrans), + (gpointer) OPT_WINTRANS_IM_SLIDER); + button = pidgin_prefs_checkbox( + _("Remove IM window transparency on focus"), + OPT_WINTRANS_IM_ONFOCUS, trans_box); + gtk_box_append(GTK_BOX(imtransbox), trans_box); + /* IM transparency slider */ + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); + label = gtk_label_new(_("Opacity:")); + gtk_box_append(GTK_BOX(hbox), label); + slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1); + gtk_range_set_value(GTK_RANGE(slider), + purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA)); + g_signal_connect(G_OBJECT(slider), "value-changed", + G_CALLBACK(alpha_change), NULL); + focus = gtk_event_controller_focus_new(); + g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int), + (gpointer)OPT_WINTRANS_IM_ALPHA); + gtk_widget_add_controller(slider, focus); + gtk_box_append(GTK_BOX(hbox), slider); + gtk_box_append(GTK_BOX(trans_box), hbox); + /* Buddy List trans options */ + bltransbox = pidgin_make_frame (ret, _("Buddy List Window")); + button = pidgin_prefs_checkbox(_("_Buddy List window transparency"), + OPT_WINTRANS_BL_ENABLED, bltransbox); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(set_blist_trans), + (gpointer) OPT_WINTRANS_BL_ENABLED); + trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); + g_object_bind_property(button, "active", trans_box, "sensitive", + G_BINDING_SYNC_CREATE); + button = pidgin_prefs_checkbox( + _("Remove Buddy List window transparency on focus"), + OPT_WINTRANS_BL_ONFOCUS, trans_box); + gtk_box_append(GTK_BOX(bltransbox), trans_box); + /* IM transparency slider */ + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); + label = gtk_label_new(_("Opacity:")); + gtk_box_append(GTK_BOX(hbox), label); + slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1); + gtk_range_set_value(GTK_RANGE(slider), + purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA)); + g_signal_connect(G_OBJECT(slider), "value-changed", + G_CALLBACK(bl_alpha_change), NULL); + focus = gtk_event_controller_focus_new(); + g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int), + (gpointer)OPT_WINTRANS_BL_ALPHA); + gtk_widget_add_controller(slider, focus); + gtk_box_append(GTK_BOX(hbox), slider); + gtk_box_append(GTK_BOX(trans_box), hbox); +static GPluginPluginInfo * +transparency_query(GError **error) { + const gchar * const authors[] = { + "Pidgin Developers <devel@pidgin.im>", + return pidgin_plugin_info_new( + "id", WINTRANS_PLUGIN_ID, + "name", N_("Transparency"), + "version", DISPLAY_VERSION, + "category", N_("User interface"), + "summary", N_("Variable Transparency for the buddy list and conversations."), + "description", N_("This plugin enables variable alpha transparency on conversation windows and the buddy list."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, +transparency_load(GPluginPlugin *plugin, GError **error) { + GApplication *application = NULL; + GtkWidget *window = NULL; + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none("/plugins/gtk/transparency"); + purple_prefs_add_bool(OPT_WINTRANS_IM_ENABLED, FALSE); + purple_prefs_add_int(OPT_WINTRANS_IM_ALPHA, 255); + purple_prefs_add_bool(OPT_WINTRANS_IM_SLIDER, FALSE); + purple_prefs_add_bool(OPT_WINTRANS_IM_ONFOCUS, FALSE); + purple_prefs_add_bool(OPT_WINTRANS_BL_ENABLED, FALSE); + purple_prefs_add_int(OPT_WINTRANS_BL_ALPHA, 255); + purple_prefs_add_bool(OPT_WINTRANS_BL_ONFOCUS, FALSE); + purple_prefs_rename("/plugins/gtk/win32/wintrans", "/plugins/gtk/transparency"); + application = g_application_get_default(); + g_signal_connect(application, "window-added", + G_CALLBACK(new_conversation_cb), NULL); + g_signal_connect(application, "window-removed", + G_CALLBACK(conversation_delete_cb), NULL); + update_existing_convs(); + window = get_buddy_list_window(); + blist_created_cb(NULL, NULL); + purple_signal_connect(pidgin_blist_get_handle(), + "gtkblist-created", plugin, + G_CALLBACK(blist_created_cb), NULL); +transparency_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) { + GtkWidget *window = NULL; + purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading transparency plugin\n"); + remove_convs_wintrans(TRUE); + window = get_buddy_list_window(); + GtkEventController *focus = NULL; + if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) { + set_wintrans(window, 0, FALSE); + /* Remove the focus cbs */ + focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY); + if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) { + gtk_widget_remove_controller(window, focus); +GPLUGIN_NATIVE_PLUGIN_DECLARE(transparency) --- a/pidgin/plugins/unity.c Mon Sep 12 05:06:39 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,674 +0,0 @@
- * Integration with Unity's messaging menu and launcher
- * Copyright (C) 2013 Ankit Vani <a@nevitus.org>
- * 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
-#include <glib/gi18n-lib.h>
-#include <messaging-menu.h>
-#define UNITY_PLUGIN_ID "gtk-unity-integration"
-static MessagingMenuApp *mmapp = NULL;
-static UnityLauncherEntry *launcher = NULL;
-static guint n_sources = 0;
-static gint launcher_count;
-static gint messaging_menu_text;
-static gboolean alert_chat_nick = TRUE;
- LAUNCHER_COUNT_DISABLE,
- LAUNCHER_COUNT_MESSAGES,
- LAUNCHER_COUNT_SOURCES,
-static int attach_signals(PurpleConversation *conv);
-static void detach_signals(PurpleConversation *conv);
- g_return_if_fail(launcher != NULL);
- if (launcher_count == LAUNCHER_COUNT_DISABLE)
- if (launcher_count == LAUNCHER_COUNT_MESSAGES) {
- PurpleConversationManager *manager = NULL;
- manager = purple_conversation_manager_get_default();
- convs = purple_conversation_manager_get_all(manager);
- PurpleConversation *conv = convs->data;
- count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
- "unity-message-count"));
- convs = g_list_delete_link(convs, convs);
- if (launcher != NULL) {
- unity_launcher_entry_set_count_visible(launcher, TRUE);
- unity_launcher_entry_set_count_visible(launcher, FALSE);
- unity_launcher_entry_set_count(launcher, count);
-conversation_id(PurpleConversation *conv)
- PurpleAccount *account = purple_conversation_get_account(conv);
- return g_strconcat((PURPLE_IS_IM_CONVERSATION(conv) ? "im" :
- PURPLE_IS_CHAT_CONVERSATION(conv) ? "chat" : "misc"), ":",
- purple_conversation_get_name(conv), ":",
- purple_account_get_username(account), ":",
- purple_account_get_protocol_id(account), NULL);
-messaging_menu_add_conversation(PurpleConversation *conv, gint count)
- g_return_if_fail(count > 0);
- id = conversation_id(conv);
- /* GBytesIcon may be useful for messaging menu source icons using buddy
- if (!messaging_menu_app_has_source(mmapp, id))
- messaging_menu_app_append_source(mmapp, id, NULL,
- purple_conversation_get_title(conv));
- if (messaging_menu_text == MESSAGING_MENU_TIME)
- messaging_menu_app_set_source_time(mmapp, id, g_get_real_time());
- else if (messaging_menu_text == MESSAGING_MENU_COUNT)
- messaging_menu_app_set_source_count(mmapp, id, count);
- messaging_menu_app_draw_attention(mmapp, id);
-messaging_menu_remove_conversation(PurpleConversation *conv)
- gchar *id = conversation_id(conv);
- if (messaging_menu_app_has_source(mmapp, id))
- messaging_menu_app_remove_source(mmapp, id);
- PurpleConversationManager *manager = NULL;
- manager = purple_conversation_manager_get_default();
- convs = purple_conversation_manager_get_all(manager);
- PurpleConversation *conv = convs->data;
- messaging_menu_add_conversation(conv,
- GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
- "unity-message-count")));
- convs = g_list_delete_link(convs, convs);
-alert(PurpleConversation *conv)
- PidginConversation *gtkconv = NULL;
- PidginConversationWindow *convwin = NULL;
- if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
- gtkconv = PIDGIN_CONVERSATION(conv);
- root = gtk_widget_get_root(gtkconv->tab_cont);
- win = GTK_WIDGET(root);
- convwin = PIDGIN_CONVERSATION_WINDOW(win);
- if (!gtk_widget_has_focus(win) ||
- !pidgin_conversation_window_conversation_is_selected(convwin, conv))
- count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv),
- "unity-message-count"));
- g_object_set_data(G_OBJECT(conv), "unity-message-count",
- GINT_TO_POINTER(count));
- messaging_menu_add_conversation(conv, count);
-unalert(PurpleConversation *conv)
- if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-message-count")) > 0)
- g_object_set_data(G_OBJECT(conv), "unity-message-count",
- messaging_menu_remove_conversation(conv);
-unalert_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
-message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
- PurpleMessageFlags flags = purple_message_get_flags(msg);
- if ((PURPLE_IS_CHAT_CONVERSATION(conv) && alert_chat_nick &&
- !(flags & PURPLE_MESSAGE_NICK)))
- if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED))
-im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused)
- PurpleConversation *im = NULL;
- PurpleConversationManager *manager = NULL;
- manager = purple_conversation_manager_get_default();
- im = purple_conversation_manager_find_im(manager, account,
- purple_message_get_recipient(msg));
-chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id)
- PurpleConversation *chat = NULL;
- PurpleConversationManager *manager = NULL;
- manager = purple_conversation_manager_get_default();
- chat = purple_conversation_manager_find_chat_by_id(manager, account, id);
-conv_created(PurpleConversation *conv)
- g_object_set_data(G_OBJECT(conv), "unity-message-count",
-deleting_conv(PurpleConversation *conv)
-message_source_activated(MessagingMenuApp *app, const gchar *id,
- gchar **sections = g_strsplit(id, ":", 0);
- PurpleConversation *conv = NULL;
- PurpleConversationManager *conversation_manager = NULL;
- PurpleAccount *account;
- PurpleAccountManager *account_manager = NULL;
- char *type = sections[0];
- char *cname = sections[1];
- char *aname = sections[2];
- char *protocol = sections[3];
- conversation_manager = purple_conversation_manager_get_default();
- account_manager = purple_account_manager_get_default();
- account = purple_account_manager_find(account_manager, aname, protocol);
- if (g_strcmp0(type, "im") == 0) {
- conv = purple_conversation_manager_find_im(conversation_manager,
- } else if (g_strcmp0(type, "chat") == 0) {
- conv = purple_conversation_manager_find_chat(conversation_manager,
- conv = purple_conversation_manager_find(conversation_manager,
- PidginConversationWindow *convwin = NULL;
- root = gtk_widget_get_root(PIDGIN_CONVERSATION(conv)->tab_cont);
- win = GTK_WIDGET(root);
- convwin = PIDGIN_CONVERSATION_WINDOW(win);
- pidgin_conversation_window_select(convwin, conv);
- gtk_root_set_focus(root, PIDGIN_CONVERSATION(conv)->entry);
-static PurpleSavedStatus *
-create_transient_status(PurpleStatusPrimitive primitive,
- PurpleStatusType *status_type)
- PurpleSavedStatus *saved_status = purple_savedstatus_new(NULL, primitive);
- if(status_type != NULL) {
- PurpleAccountManager *manager = NULL;
- GList *active_accts = NULL;
- manager = purple_account_manager_get_default();
- active_accts = purple_account_manager_get_enabled(manager);
- while(active_accts != NULL) {
- PurpleAccount *account = PURPLE_ACCOUNT(active_accts->data);
- purple_savedstatus_set_substatus(saved_status, account,
- active_accts = g_list_delete_link(active_accts, active_accts);
-status_changed_cb(PurpleSavedStatus *saved_status)
- MessagingMenuStatus status = MESSAGING_MENU_STATUS_AVAILABLE;
- switch (purple_savedstatus_get_primitive_type(saved_status)) {
- case PURPLE_STATUS_AVAILABLE:
- case PURPLE_STATUS_MOOD:
- case PURPLE_STATUS_TUNE:
- case PURPLE_STATUS_UNSET:
- status = MESSAGING_MENU_STATUS_AVAILABLE;
- case PURPLE_STATUS_AWAY:
- case PURPLE_STATUS_EXTENDED_AWAY:
- status = MESSAGING_MENU_STATUS_AWAY;
- case PURPLE_STATUS_INVISIBLE:
- status = MESSAGING_MENU_STATUS_INVISIBLE;
- case PURPLE_STATUS_MOBILE:
- case PURPLE_STATUS_OFFLINE:
- status = MESSAGING_MENU_STATUS_OFFLINE;
- case PURPLE_STATUS_UNAVAILABLE:
- status = MESSAGING_MENU_STATUS_BUSY;
- g_assert_not_reached();
- messaging_menu_app_set_status(mmapp, status);
-messaging_menu_status_changed(MessagingMenuApp *mmapp,
- MessagingMenuStatus mm_status, gpointer user_data)
- PurpleSavedStatus *saved_status;
- PurpleStatusPrimitive primitive = PURPLE_STATUS_UNSET;
- case MESSAGING_MENU_STATUS_AVAILABLE:
- primitive = PURPLE_STATUS_AVAILABLE;
- case MESSAGING_MENU_STATUS_AWAY:
- primitive = PURPLE_STATUS_AWAY;
- case MESSAGING_MENU_STATUS_BUSY:
- primitive = PURPLE_STATUS_UNAVAILABLE;
- case MESSAGING_MENU_STATUS_INVISIBLE:
- primitive = PURPLE_STATUS_INVISIBLE;
- case MESSAGING_MENU_STATUS_OFFLINE:
- primitive = PURPLE_STATUS_OFFLINE;
- g_assert_not_reached();
- saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL);
- if (saved_status == NULL)
- saved_status = create_transient_status(primitive, NULL);
- purple_savedstatus_activate(saved_status);
-alert_config_cb(GtkWidget *widget, gpointer data)
- gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
- purple_prefs_set_bool("/plugins/gtk/unity/alert_chat_nick", on);
-launcher_config_cb(GtkWidget *widget, gpointer data)
- gint option = GPOINTER_TO_INT(data);
- if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
- purple_prefs_set_int("/plugins/gtk/unity/launcher_count", option);
- launcher_count = option;
- if (option == LAUNCHER_COUNT_DISABLE)
- unity_launcher_entry_set_count_visible(launcher, FALSE);
-messaging_menu_config_cb(GtkWidget *widget, gpointer data)
- gint option = GPOINTER_TO_INT(data);
- if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
- purple_prefs_set_int("/plugins/gtk/unity/messaging_menu_text", option);
- messaging_menu_text = option;
- refill_messaging_menu();
-attach_signals(PurpleConversation *conv)
- PidginConversation *gtkconv = NULL;
- gtkconv = PIDGIN_CONVERSATION(conv);
- id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
- G_CALLBACK(unalert_cb), conv);
- g_object_set_data(G_OBJECT(conv), "unity-entry-signal", GUINT_TO_POINTER(id));
- id = g_signal_connect(G_OBJECT(gtkconv->history), "focus-in-event",
- G_CALLBACK(unalert_cb), conv);
- g_object_set_data(G_OBJECT(conv), "unity-history-signal", GUINT_TO_POINTER(id));
-detach_signals(PurpleConversation *conv)
- PidginConversation *gtkconv = NULL;
- gtkconv = PIDGIN_CONVERSATION(conv);
- id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-history-signal"));
- g_signal_handler_disconnect(gtkconv->history, id);
- id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-entry-signal"));
- g_signal_handler_disconnect(gtkconv->entry, id);
- g_object_set_data(G_OBJECT(conv), "unity-message-count",
-get_config_frame(PurplePlugin *plugin)
- GtkWidget *ret = NULL, *frame = NULL;
- GtkWidget *vbox = NULL, *toggle = NULL, *group = NULL;
- ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- frame = pidgin_make_frame(ret, _("Chatroom alerts"));
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
- gtk_box_append(GTK_BOX(frame), vbox);
- toggle = gtk_check_button_new_with_mnemonic(_("Chatroom message alerts _only where someone says your username"));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"));
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(alert_config_cb), NULL);
- /* Launcher integration */
- frame = pidgin_make_frame(ret, _("Launcher Icon"));
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
- gtk_box_append(GTK_BOX(frame), vbox);
- toggle = gtk_check_button_new_with_mnemonic(_("_Disable launcher integration"));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_DISABLE);
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE));
- toggle = gtk_check_button_new_with_mnemonic(
- _("Show number of unread _messages on launcher icon"));
- gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle),
- GTK_CHECK_BUTTON(group));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_MESSAGES);
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES));
- toggle = gtk_check_button_new_with_mnemonic(
- _("Show number of unread co_nversations on launcher icon"));
- gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle),
- GTK_CHECK_BUTTON(group));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_SOURCES);
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES));
- /* Messaging menu integration */
- frame = pidgin_make_frame(ret, _("Messaging Menu"));
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
- gtk_box_append(GTK_BOX(frame), vbox);
- toggle = gtk_check_button_new_with_mnemonic(
- _("Show number of _unread messages for conversations in messaging menu"));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_COUNT);
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT));
- toggle = gtk_check_button_new_with_mnemonic(
- _("Show _elapsed time for unread conversations in messaging menu"));
- gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle),
- GTK_CHECK_BUTTON(group));
- gtk_box_append(GTK_BOX(vbox), toggle);
- gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle),
- purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_TIME);
- g_signal_connect(G_OBJECT(toggle), "toggled",
- G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME));
-static GPluginPluginInfo *
-unity_query(GError **error)
- const gchar * const authors[] = {
- "Ankit Vani <a@nevitus.org>",
- return pidgin_plugin_info_new(
- "name", N_("Unity Integration"),
- "version", DISPLAY_VERSION,
- "category", N_("Notification"),
- "summary", N_("Provides integration with Unity."),
- "description", N_("Provides integration with Unity's "
- "messaging menu and launcher."),
- "website", PURPLE_WEBSITE,
- "abi-version", PURPLE_ABI_VERSION,
- "gtk-config-frame-cb", get_config_frame,
-unity_load(GPluginPlugin *plugin, GError **error) {
- PurpleConversationManager *manager = NULL;
- PurpleSavedStatus *saved_status;
- void *conv_handle = purple_conversations_get_handle();
- void *gtk_conv_handle = pidgin_conversations_get_handle();
- void *savedstat_handle = purple_savedstatuses_get_handle();
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none("/plugins/gtk/unity");
- purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES);
- purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT);
- purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE);
- alert_chat_nick = purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick");
- mmapp = messaging_menu_app_new("pidgin.desktop");
- messaging_menu_app_register(mmapp);
- messaging_menu_text = purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text");
- g_signal_connect(mmapp, "activate-source",
- G_CALLBACK(message_source_activated), NULL);
- g_signal_connect(mmapp, "status-changed",
- G_CALLBACK(messaging_menu_status_changed), NULL);
- saved_status = purple_savedstatus_get_current();
- status_changed_cb(saved_status);
- purple_signal_connect(savedstat_handle, "savedstatus-changed", plugin,
- G_CALLBACK(status_changed_cb), NULL);
- launcher = unity_launcher_entry_get_for_desktop_id("pidgin.desktop");
- g_object_ref(launcher);
- launcher_count = purple_prefs_get_int("/plugins/gtk/unity/launcher_count");
- purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
- G_CALLBACK(message_displayed_cb), NULL);
- purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
- G_CALLBACK(message_displayed_cb), NULL);
- purple_signal_connect(conv_handle, "sent-im-msg", plugin,
- G_CALLBACK(im_sent_im), NULL);
- purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
- G_CALLBACK(chat_sent_im), NULL);
- purple_signal_connect(conv_handle, "conversation-created", plugin,
- G_CALLBACK(conv_created), NULL);
- purple_signal_connect(conv_handle, "deleting-conversation", plugin,
- G_CALLBACK(deleting_conv), NULL);
- manager = purple_conversation_manager_get_default();
- convs = purple_conversation_manager_get_all(manager);
- PurpleConversation *conv = PURPLE_CONVERSATION(convs->data);
- convs = g_list_delete_link(convs, convs);
-unity_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) {
- PurpleConversationManager *manager = NULL;
- manager = purple_conversation_manager_get_default();
- convs = purple_conversation_manager_get_all(manager);
- PurpleConversation *conv = PURPLE_CONVERSATION(convs->data);
- convs = g_list_delete_link(convs, convs);
- unity_launcher_entry_set_count_visible(launcher, FALSE);
- messaging_menu_app_unregister(mmapp);
- g_object_unref(launcher);
-GPLUGIN_NATIVE_PLUGIN_DECLARE(unity)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/unity/meson.build Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,7 @@
+unity = library('unity', 'unity.c', + c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Unity"'], + dependencies : [UNITY, libpurple_dep, libpidgin_dep, glib], + install : true, install_dir : PIDGIN_PLUGINDIR) +devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/unity/unity.c Mon Sep 12 05:08:59 2022 -0500
@@ -0,0 +1,674 @@
+ * Integration with Unity's messaging menu and launcher + * Copyright (C) 2013 Ankit Vani <a@nevitus.org> + * 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 +#include <glib/gi18n-lib.h> +#include <messaging-menu.h> +#define UNITY_PLUGIN_ID "gtk-unity-integration" +static MessagingMenuApp *mmapp = NULL; +static UnityLauncherEntry *launcher = NULL; +static guint n_sources = 0; +static gint launcher_count; +static gint messaging_menu_text; +static gboolean alert_chat_nick = TRUE; + LAUNCHER_COUNT_DISABLE, + LAUNCHER_COUNT_MESSAGES, + LAUNCHER_COUNT_SOURCES, +static int attach_signals(PurpleConversation *conv); +static void detach_signals(PurpleConversation *conv); + g_return_if_fail(launcher != NULL); + if (launcher_count == LAUNCHER_COUNT_DISABLE) + if (launcher_count == LAUNCHER_COUNT_MESSAGES) { + PurpleConversationManager *manager = NULL; + manager = purple_conversation_manager_get_default(); + convs = purple_conversation_manager_get_all(manager); + PurpleConversation *conv = convs->data; + count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), + "unity-message-count")); + convs = g_list_delete_link(convs, convs); + if (launcher != NULL) { + unity_launcher_entry_set_count_visible(launcher, TRUE); + unity_launcher_entry_set_count_visible(launcher, FALSE); + unity_launcher_entry_set_count(launcher, count); +conversation_id(PurpleConversation *conv) + PurpleAccount *account = purple_conversation_get_account(conv); + return g_strconcat((PURPLE_IS_IM_CONVERSATION(conv) ? "im" : + PURPLE_IS_CHAT_CONVERSATION(conv) ? "chat" : "misc"), ":", + purple_conversation_get_name(conv), ":", + purple_account_get_username(account), ":", + purple_account_get_protocol_id(account), NULL); +messaging_menu_add_conversation(PurpleConversation *conv, gint count) + g_return_if_fail(count > 0); + id = conversation_id(conv); + /* GBytesIcon may be useful for messaging menu source icons using buddy + if (!messaging_menu_app_has_source(mmapp, id)) + messaging_menu_app_append_source(mmapp, id, NULL, + purple_conversation_get_title(conv)); + if (messaging_menu_text == MESSAGING_MENU_TIME) + messaging_menu_app_set_source_time(mmapp, id, g_get_real_time()); + else if (messaging_menu_text == MESSAGING_MENU_COUNT) + messaging_menu_app_set_source_count(mmapp, id, count); + messaging_menu_app_draw_attention(mmapp, id); +messaging_menu_remove_conversation(PurpleConversation *conv) + gchar *id = conversation_id(conv); + if (messaging_menu_app_has_source(mmapp, id)) + messaging_menu_app_remove_source(mmapp, id); + PurpleConversationManager *manager = NULL; + manager = purple_conversation_manager_get_default(); + convs = purple_conversation_manager_get_all(manager); + PurpleConversation *conv = convs->data; + messaging_menu_add_conversation(conv, + GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), + "unity-message-count"))); + convs = g_list_delete_link(convs, convs); +alert(PurpleConversation *conv) + PidginConversation *gtkconv = NULL; + PidginConversationWindow *convwin = NULL; + if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL) + gtkconv = PIDGIN_CONVERSATION(conv); + root = gtk_widget_get_root(gtkconv->tab_cont); + win = GTK_WIDGET(root); + convwin = PIDGIN_CONVERSATION_WINDOW(win); + if (!gtk_widget_has_focus(win) || + !pidgin_conversation_window_conversation_is_selected(convwin, conv)) + count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), + "unity-message-count")); + g_object_set_data(G_OBJECT(conv), "unity-message-count", + GINT_TO_POINTER(count)); + messaging_menu_add_conversation(conv, count); +unalert(PurpleConversation *conv) + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-message-count")) > 0) + g_object_set_data(G_OBJECT(conv), "unity-message-count", + messaging_menu_remove_conversation(conv); +unalert_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv) +message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused) + PurpleMessageFlags flags = purple_message_get_flags(msg); + if ((PURPLE_IS_CHAT_CONVERSATION(conv) && alert_chat_nick && + !(flags & PURPLE_MESSAGE_NICK))) + if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED)) +im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused) + PurpleConversation *im = NULL; + PurpleConversationManager *manager = NULL; + manager = purple_conversation_manager_get_default(); + im = purple_conversation_manager_find_im(manager, account, + purple_message_get_recipient(msg)); +chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id) + PurpleConversation *chat = NULL; + PurpleConversationManager *manager = NULL; + manager = purple_conversation_manager_get_default(); + chat = purple_conversation_manager_find_chat_by_id(manager, account, id); +conv_created(PurpleConversation *conv) + g_object_set_data(G_OBJECT(conv), "unity-message-count", +deleting_conv(PurpleConversation *conv) +message_source_activated(MessagingMenuApp *app, const gchar *id, + gchar **sections = g_strsplit(id, ":", 0); + PurpleConversation *conv = NULL; + PurpleConversationManager *conversation_manager = NULL; + PurpleAccount *account; + PurpleAccountManager *account_manager = NULL; + char *type = sections[0]; + char *cname = sections[1]; + char *aname = sections[2]; + char *protocol = sections[3]; + conversation_manager = purple_conversation_manager_get_default(); + account_manager = purple_account_manager_get_default(); + account = purple_account_manager_find(account_manager, aname, protocol); + if (g_strcmp0(type, "im") == 0) { + conv = purple_conversation_manager_find_im(conversation_manager, + } else if (g_strcmp0(type, "chat") == 0) { + conv = purple_conversation_manager_find_chat(conversation_manager, + conv = purple_conversation_manager_find(conversation_manager, + PidginConversationWindow *convwin = NULL; + root = gtk_widget_get_root(PIDGIN_CONVERSATION(conv)->tab_cont); + win = GTK_WIDGET(root); + convwin = PIDGIN_CONVERSATION_WINDOW(win); + pidgin_conversation_window_select(convwin, conv); + gtk_root_set_focus(root, PIDGIN_CONVERSATION(conv)->entry); +static PurpleSavedStatus * +create_transient_status(PurpleStatusPrimitive primitive, + PurpleStatusType *status_type) + PurpleSavedStatus *saved_status = purple_savedstatus_new(NULL, primitive); + if(status_type != NULL) { + PurpleAccountManager *manager = NULL; + GList *active_accts = NULL; + manager = purple_account_manager_get_default(); + active_accts = purple_account_manager_get_enabled(manager); + while(active_accts != NULL) { + PurpleAccount *account = PURPLE_ACCOUNT(active_accts->data); + purple_savedstatus_set_substatus(saved_status, account, + active_accts = g_list_delete_link(active_accts, active_accts); +status_changed_cb(PurpleSavedStatus *saved_status) + MessagingMenuStatus status = MESSAGING_MENU_STATUS_AVAILABLE; + switch (purple_savedstatus_get_primitive_type(saved_status)) { + case PURPLE_STATUS_AVAILABLE: + case PURPLE_STATUS_MOOD: + case PURPLE_STATUS_TUNE: + case PURPLE_STATUS_UNSET: + status = MESSAGING_MENU_STATUS_AVAILABLE; + case PURPLE_STATUS_AWAY: + case PURPLE_STATUS_EXTENDED_AWAY: + status = MESSAGING_MENU_STATUS_AWAY; + case PURPLE_STATUS_INVISIBLE: + status = MESSAGING_MENU_STATUS_INVISIBLE; + case PURPLE_STATUS_MOBILE: + case PURPLE_STATUS_OFFLINE: + status = MESSAGING_MENU_STATUS_OFFLINE; + case PURPLE_STATUS_UNAVAILABLE: + status = MESSAGING_MENU_STATUS_BUSY; + g_assert_not_reached(); + messaging_menu_app_set_status(mmapp, status); +messaging_menu_status_changed(MessagingMenuApp *mmapp, + MessagingMenuStatus mm_status, gpointer user_data) + PurpleSavedStatus *saved_status; + PurpleStatusPrimitive primitive = PURPLE_STATUS_UNSET; + case MESSAGING_MENU_STATUS_AVAILABLE: + primitive = PURPLE_STATUS_AVAILABLE; + case MESSAGING_MENU_STATUS_AWAY: + primitive = PURPLE_STATUS_AWAY; + case MESSAGING_MENU_STATUS_BUSY: + primitive = PURPLE_STATUS_UNAVAILABLE; + case MESSAGING_MENU_STATUS_INVISIBLE: + primitive = PURPLE_STATUS_INVISIBLE; + case MESSAGING_MENU_STATUS_OFFLINE: + primitive = PURPLE_STATUS_OFFLINE; + g_assert_not_reached(); + saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL); + if (saved_status == NULL) + saved_status = create_transient_status(primitive, NULL); + purple_savedstatus_activate(saved_status); +alert_config_cb(GtkWidget *widget, gpointer data) + gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + purple_prefs_set_bool("/plugins/gtk/unity/alert_chat_nick", on); +launcher_config_cb(GtkWidget *widget, gpointer data) + gint option = GPOINTER_TO_INT(data); + if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + purple_prefs_set_int("/plugins/gtk/unity/launcher_count", option); + launcher_count = option; + if (option == LAUNCHER_COUNT_DISABLE) + unity_launcher_entry_set_count_visible(launcher, FALSE); +messaging_menu_config_cb(GtkWidget *widget, gpointer data) + gint option = GPOINTER_TO_INT(data); + if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + purple_prefs_set_int("/plugins/gtk/unity/messaging_menu_text", option); + messaging_menu_text = option; + refill_messaging_menu(); +attach_signals(PurpleConversation *conv) + PidginConversation *gtkconv = NULL; + gtkconv = PIDGIN_CONVERSATION(conv); + id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event", + G_CALLBACK(unalert_cb), conv); + g_object_set_data(G_OBJECT(conv), "unity-entry-signal", GUINT_TO_POINTER(id)); + id = g_signal_connect(G_OBJECT(gtkconv->history), "focus-in-event", + G_CALLBACK(unalert_cb), conv); + g_object_set_data(G_OBJECT(conv), "unity-history-signal", GUINT_TO_POINTER(id)); +detach_signals(PurpleConversation *conv) + PidginConversation *gtkconv = NULL; + gtkconv = PIDGIN_CONVERSATION(conv); + id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-history-signal")); + g_signal_handler_disconnect(gtkconv->history, id); + id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-entry-signal")); + g_signal_handler_disconnect(gtkconv->entry, id); + g_object_set_data(G_OBJECT(conv), "unity-message-count", +get_config_frame(PurplePlugin *plugin) + GtkWidget *ret = NULL, *frame = NULL; + GtkWidget *vbox = NULL, *toggle = NULL, *group = NULL; + ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); + frame = pidgin_make_frame(ret, _("Chatroom alerts")); + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_box_append(GTK_BOX(frame), vbox); + toggle = gtk_check_button_new_with_mnemonic(_("Chatroom message alerts _only where someone says your username")); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick")); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(alert_config_cb), NULL); + /* Launcher integration */ + frame = pidgin_make_frame(ret, _("Launcher Icon")); + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_box_append(GTK_BOX(frame), vbox); + toggle = gtk_check_button_new_with_mnemonic(_("_Disable launcher integration")); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_DISABLE); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE)); + toggle = gtk_check_button_new_with_mnemonic( + _("Show number of unread _messages on launcher icon")); + gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle), + GTK_CHECK_BUTTON(group)); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_MESSAGES); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES)); + toggle = gtk_check_button_new_with_mnemonic( + _("Show number of unread co_nversations on launcher icon")); + gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle), + GTK_CHECK_BUTTON(group)); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_SOURCES); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES)); + /* Messaging menu integration */ + frame = pidgin_make_frame(ret, _("Messaging Menu")); + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_box_append(GTK_BOX(frame), vbox); + toggle = gtk_check_button_new_with_mnemonic( + _("Show number of _unread messages for conversations in messaging menu")); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_COUNT); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT)); + toggle = gtk_check_button_new_with_mnemonic( + _("Show _elapsed time for unread conversations in messaging menu")); + gtk_check_button_set_group(GTK_CHECK_BUTTON(toggle), + GTK_CHECK_BUTTON(group)); + gtk_box_append(GTK_BOX(vbox), toggle); + gtk_check_button_set_active(GTK_CHECK_BUTTON(toggle), + purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_TIME); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME)); +static GPluginPluginInfo * +unity_query(GError **error) + const gchar * const authors[] = { + "Ankit Vani <a@nevitus.org>", + return pidgin_plugin_info_new( + "name", N_("Unity Integration"), + "version", DISPLAY_VERSION, + "category", N_("Notification"), + "summary", N_("Provides integration with Unity."), + "description", N_("Provides integration with Unity's " + "messaging menu and launcher."), + "website", PURPLE_WEBSITE, + "abi-version", PURPLE_ABI_VERSION, + "gtk-config-frame-cb", get_config_frame, +unity_load(GPluginPlugin *plugin, GError **error) { + PurpleConversationManager *manager = NULL; + PurpleSavedStatus *saved_status; + void *conv_handle = purple_conversations_get_handle(); + void *gtk_conv_handle = pidgin_conversations_get_handle(); + void *savedstat_handle = purple_savedstatuses_get_handle(); + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none("/plugins/gtk/unity"); + purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES); + purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT); + purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE); + alert_chat_nick = purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"); + mmapp = messaging_menu_app_new("pidgin.desktop"); + messaging_menu_app_register(mmapp); + messaging_menu_text = purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text"); + g_signal_connect(mmapp, "activate-source", + G_CALLBACK(message_source_activated), NULL); + g_signal_connect(mmapp, "status-changed", + G_CALLBACK(messaging_menu_status_changed), NULL); + saved_status = purple_savedstatus_get_current(); + status_changed_cb(saved_status); + purple_signal_connect(savedstat_handle, "savedstatus-changed", plugin, + G_CALLBACK(status_changed_cb), NULL); + launcher = unity_launcher_entry_get_for_desktop_id("pidgin.desktop"); + g_object_ref(launcher); + launcher_count = purple_prefs_get_int("/plugins/gtk/unity/launcher_count"); + purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin, + G_CALLBACK(message_displayed_cb), NULL); + purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin, + G_CALLBACK(message_displayed_cb), NULL); + purple_signal_connect(conv_handle, "sent-im-msg", plugin, + G_CALLBACK(im_sent_im), NULL); + purple_signal_connect(conv_handle, "sent-chat-msg", plugin, + G_CALLBACK(chat_sent_im), NULL); + purple_signal_connect(conv_handle, "conversation-created", plugin, + G_CALLBACK(conv_created), NULL); + purple_signal_connect(conv_handle, "deleting-conversation", plugin, + G_CALLBACK(deleting_conv), NULL); + manager = purple_conversation_manager_get_default(); + convs = purple_conversation_manager_get_all(manager); + PurpleConversation *conv = PURPLE_CONVERSATION(convs->data); + convs = g_list_delete_link(convs, convs); +unity_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) { + PurpleConversationManager *manager = NULL; + manager = purple_conversation_manager_get_default(); + convs = purple_conversation_manager_get_all(manager); + PurpleConversation *conv = PURPLE_CONVERSATION(convs->data); + convs = g_list_delete_link(convs, convs); + unity_launcher_entry_set_count_visible(launcher, FALSE); + messaging_menu_app_unregister(mmapp); + g_object_unref(launcher); +GPLUGIN_NATIVE_PLUGIN_DECLARE(unity)