pidgin/purple-plugin-pack
merge of '50af3299b6aaead836d1bfd9a89168a3a410726b'
and '7775e4a7ba416a8e3da2560f8c1769581b430738'
--- a/ChangeLog Wed Apr 23 05:09:46 2008 -0400
+++ b/ChangeLog Wed Apr 23 05:10:02 2008 -0400
@@ -1,6 +1,12 @@
* Merged the Autoprofile plugin into our build system.
* Fixed convbadger's failure to update on conversation switch.
+ * Added Ike Gingerich's colorize plugin + * Added Ike Gingerich's splitter plugin + * Fixed dewysiwygification's debug messages not properly ending lines. + * Added google plugin for "I'm Feeling Lucky" searches. + * Fixed aspell dependency in switchspell (fixes gentoo bug #196693) + * Added nodashi's manualsize plugin * Fixed a typo in irc-more's source that allowed a potential double-free
--- a/Makefile.am Wed Apr 23 05:09:46 2008 -0400
+++ b/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -22,9 +22,13 @@
DIST_SUBDIRS = common doc m4 po $(PP_PURPLE) $(PP_PIDGIN) $(PP_FINCH)
-SUBDIRS = common doc m4 po $(PP_PURPLE_BUILD) $(PP_PIDGIN_BUILD) $(PP_FINCH_BUILD)
+SUBDIRS = common doc m4 $(PO_DIR) $(PP_PURPLE_BUILD) $(PP_PIDGIN_BUILD) $(PP_FINCH_BUILD) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/colorize/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,26 @@
+EXTRA_DIST = .purple-plugin .build Makefile.mingw +colorizedir = $(PURPLE_LIBDIR) +colorize_la_LDFLAGS = -module -avoid-version +colorize_LTLIBRARIES = colorize.la + -DLIBDIR=\"$(PURPLE_LIBDIR)\" \ + -DDATADIR=\"$(PURPLE_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/colorize/Makefile.mingw Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,12 @@
+# Description: Makefile for dice plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/colorize/colorize.c Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,298 @@
+/* Gaim Colorize Plug-in v0.2 + * Colorizes outgoing text to a gradient of specified starting and + * - echo color formatting to local color log + * - fix HTML-mixed messages (currently strips all HTML) + * Copyright (C) 2005, Ike Gingerich <ike_@users.sourceforge.net> + * 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., 59 Temple Place - Suite 330, Boston, MA +#include "../common/pp_internal.h" +#define PLUGIN_ID "core-plugin_pack-colorize" +#define PLUGIN_AUTHOR "Ike Gingerich <ike_@users.sourceforge.net>" +#define PREFS_PREFIX "/plugins/core/" PLUGIN_ID +#define PREFS_I_RED PREFS_PREFIX "/initial_r" +#define PREFS_I_GREEN PREFS_PREFIX "/initial_g" +#define PREFS_I_BLUE PREFS_PREFIX "/initial_b" +#define PREFS_T_RED PREFS_PREFIX "/target_r" +#define PREFS_T_GREEN PREFS_PREFIX "/target_g" +#define PREFS_T_BLUE PREFS_PREFIX "/target_b" +static const guint8 default_initial_rgb[3] = { 0xFF, 0x00, 0x00 }; +static const guint8 default_target_rgb[3] = { 0x00, 0x00, 0x00 }; +/* set up preferences dialog */ +static PurplePluginPrefFrame * +init_pref_frame(PurplePlugin *plugin) { + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; + frame = purple_plugin_pref_frame_new(); + ppref = purple_plugin_pref_new_with_label("Initial Color"); + purple_plugin_pref_frame_add(frame, ppref); + /* initial red intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_I_RED, + "Red intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); + /* initial green intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_I_GREEN, + "Green intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); + /* initial blue intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_I_BLUE, + "Blue intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); + ppref = purple_plugin_pref_new_with_label("Target Color"); + purple_plugin_pref_frame_add(frame, ppref); + /* target red intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_T_RED, + "Red intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); + /* target green intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_T_GREEN, + "Green intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); + /* target blue intensity */ + ppref = purple_plugin_pref_new_with_name_and_label(PREFS_T_BLUE, + "Blue intensity (0-255): "); + purple_plugin_pref_set_bounds(ppref, 0, 255); + purple_plugin_pref_frame_add(frame, ppref); +round_gfloat_to_guint8(gfloat f) { + return ((guchar)(f + 0.5f)); +rgb_equals(guint8 a[3], gfloat b[3]) { + return ( a[0] == round_gfloat_to_guint8(b[0]) && + a[1] == round_gfloat_to_guint8(b[1]) && + a[2] == round_gfloat_to_guint8(b[2]) ); +colorize_message(char **message) { + gfloat d_grad[3], grad[3]; + guint8 initial_rgb[3], target_rgb[3], last_rgb[3]; + gchar *formatted_char, *tmp, *new_msg; + g_return_if_fail(message != NULL); + g_return_if_fail(*message != NULL); + g_return_if_fail(**message != '\0'); + new_msg = g_strdup(""); + len = strlen( *message ); + /* get colors from preferences */ + initial_rgb[0] = (guint8)purple_prefs_get_int(PREFS_I_RED); + initial_rgb[1] = (guint8)purple_prefs_get_int(PREFS_I_GREEN); + initial_rgb[2] = (guint8)purple_prefs_get_int(PREFS_I_BLUE); + target_rgb[0] = (guint8)purple_prefs_get_int(PREFS_T_RED); + target_rgb[1] = (guint8)purple_prefs_get_int(PREFS_T_GREEN); + target_rgb[2] = (guint8)purple_prefs_get_int(PREFS_T_BLUE); + /* initialize current gradient value */ + grad[0] = (gfloat)initial_rgb[0]; + grad[1] = (gfloat)initial_rgb[1]; + grad[2] = (gfloat)initial_rgb[2]; + /* determine the delta gradient value */ + d_grad[0] = (gfloat)(target_rgb[0] - initial_rgb[0]) / (gfloat)len; + d_grad[1] = (gfloat)(target_rgb[1] - initial_rgb[1]) / (gfloat)len; + d_grad[2] = (gfloat)(target_rgb[2] - initial_rgb[2]) / (gfloat)len; + /* open initial font tag and format first character */ + formatted_char = g_strdup_printf("<font color=\"#%02x%02x%02x\">%c", + round_gfloat_to_guint8(grad[0]), + round_gfloat_to_guint8(grad[1]), + round_gfloat_to_guint8(grad[2]), + /* create a new string with the newly formatted char and free the old one */ + tmp = g_strconcat(new_msg, formatted_char, NULL); + g_free(formatted_char); + /* format each character one by one: + * (if it is not a space) AND + * (if it is not the same color as the last character) + last_rgb[0] = round_gfloat_to_guint8(grad[0]); + last_rgb[1] = round_gfloat_to_guint8(grad[1]); + last_rgb[2] = round_gfloat_to_guint8(grad[2]); + /* increment the gradient */ + /* format next character appropriately */ + if( g_ascii_isspace ( *(*message+i) ) || + rgb_equals(last_rgb, grad) ) + formatted_char = g_strdup_printf("%c", *(*message+i)); + formatted_char = g_strdup_printf("</font><font color=\"#%02x%02x%02x\">%c", + round_gfloat_to_guint8(grad[0]), + round_gfloat_to_guint8(grad[1]), + round_gfloat_to_guint8(grad[2]), + /* create a new string with the newly formatted char and free the old one */ + tmp = g_strconcat(new_msg, formatted_char, NULL); + g_free(formatted_char); + /* close final font tag */ + new_msg = g_strconcat(new_msg, "</font>", NULL); +/* respond to a sending-im signal by replacing outgoing text + * with colorized version +sending_im_msg(PurpleAccount *account, gchar *receiver, gchar **message) { + gchar *stripped_message; + /* strip any existing HTML */ + stripped_message = purple_markup_strip_html(*message); + /* colorize the message with HTML font tags */ + *message = stripped_message; + colorize_message(message); + /* todo: additional conversation manipulation is going to be required to + display the colorized version of the message locally */ +/* register sendin-im signal */ +plugin_load(PurplePlugin *plugin) { + purple_signal_connect(purple_conversations_get_handle(), "sending-im-msg", + plugin, PURPLE_CALLBACK(sending_im_msg), NULL); +plugin_unload(PurplePlugin *plugin) { +static PurplePluginUiInfo prefs_info = { +static PurplePluginInfo info = + PURPLE_PLUGIN_STANDARD, + PURPLE_PRIORITY_DEFAULT, +/* initialize default preferences */ +init_plugin(PurplePlugin *plugin) { + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name = _("Colorize"); + info.summary = _("Colorizes outgoing message text."); + info.description = _("Colorizes outgoing message text to a gradient of " + "specified starting and ending RGB values."); + purple_prefs_add_none(PREFS_PREFIX); + purple_prefs_add_int(PREFS_I_RED, default_initial_rgb[0]); + purple_prefs_add_int(PREFS_I_GREEN, default_initial_rgb[1]); + purple_prefs_add_int(PREFS_I_BLUE, default_initial_rgb[2]); + purple_prefs_add_int(PREFS_T_RED, default_target_rgb[0]); + purple_prefs_add_int(PREFS_T_GREEN, default_target_rgb[1]); + purple_prefs_add_int(PREFS_T_BLUE, default_target_rgb[2]); +PURPLE_INIT_PLUGIN(colorize, init_plugin, info) --- a/configure.ac Wed Apr 23 05:09:46 2008 -0400
+++ b/configure.ac Wed Apr 23 05:10:02 2008 -0400
@@ -229,6 +229,8 @@
PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, [], [gtkspell=no])
AC_SUBST(GTKSPELL_CFLAGS)
@@ -243,6 +245,22 @@
+AC_CHECK_HEADER([aspell.h], HAVE_ASPELL_H=yes, AC_MSG_WARN([ +*** libaspell is required to build the switchspell plugin.]) +if test x"$HAVE_ASPELL_H" = x"yes" ; then + AC_CHECK_LIB([aspell], [new_aspell_config], ASPELL_LIBS="-laspell", BUILD_SWITCH_SPELL=no) AM_CONDITIONAL(BUILD_SWITCH_SPELL, test x"$BUILD_SWITCH_SPELL" = x"yes")
@@ -280,6 +298,13 @@
AC_CHECK_HEADERS(regex.h)
dnl #######################################################################
+dnl # Disable installation of translation files +dnl ####################################################################### +AC_ARG_ENABLE(nls, AC_HELP_STRING([--enable-nls], [enable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes) +AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes") +dnl ####################################################################### dnl # Run our plugin checking
dnl #######################################################################
@@ -318,6 +343,7 @@
dewysiwygification/Makefile
@@ -326,8 +352,9 @@
@@ -338,6 +365,8 @@
@@ -349,6 +378,7 @@
--- a/convbadger/convbadger.c Wed Apr 23 05:09:46 2008 -0400
+++ b/convbadger/convbadger.c Wed Apr 23 05:10:02 2008 -0400
@@ -105,7 +105,7 @@
*****************************************************************************/
-convbadger_conv_created_cb(PurpleConversation *conv, gpointer data) {
+conv_badger_conv_created_cb(PurpleConversation *conv, gpointer data) { PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
PidginWindow *win = pidgin_conv_get_window(pconv);
@@ -113,11 +113,11 @@
-convbadger_conv_destroyed_cb(PurpleConversation *conv, gpointer data) {
+conv_badger_conv_destroyed_cb(PurpleConversation *conv, gpointer data) { -convbadger_conv_switched_cb(PurpleConversation *conv, gpointer data) {
+conv_badger_conv_switched_cb(PurpleConversation *conv, gpointer data) { PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
PidginWindow *win = pidgin_conv_get_window(pconv);
@@ -135,13 +135,13 @@
purple_signal_connect(conv_handle, "conversation-created", plugin,
- PURPLE_CALLBACK(convbadger_conv_created_cb), NULL);
+ PURPLE_CALLBACK(conv_badger_conv_created_cb), NULL); purple_signal_connect(conv_handle, "deleting-conversation", plugin,
- PURPLE_CALLBACK(convbadger_conv_destroyed_cb), NULL);
+ PURPLE_CALLBACK(conv_badger_conv_destroyed_cb), NULL); purple_signal_connect(pidgin_conversations_get_handle(),
"conversation-switched", plugin,
- PURPLE_CALLBACK(convbadger_conv_switched_cb), NULL);
+ PURPLE_CALLBACK(conv_badger_conv_switched_cb), NULL); --- a/dewysiwygification/dewysiwygification.c Wed Apr 23 05:09:46 2008 -0400
+++ b/dewysiwygification/dewysiwygification.c Wed Apr 23 05:10:02 2008 -0400
@@ -50,7 +50,7 @@
- purple_debug_misc("dewysiwygification", "it's now: %s", tmp);
+ purple_debug_misc("dewysiwygification", "it's now: %s\n", tmp); @@ -67,7 +67,7 @@
- purple_debug_misc("dewysiwygification", "it's now: %s", tmp);
+ purple_debug_misc("dewysiwygification", "it's now: %s\n", tmp); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/google/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,26 @@
+EXTRA_DIST = .purple-plugin .build Makefile.mingw +googledir = $(PURPLE_LIBDIR) +google_la_LDFLAGS = -module -avoid-version +google_LTLIBRARIES = google.la + -DLIBDIR=\"$(PURPLE_LIBDIR)\" \ + -DDATADIR=\"$(PURPLE_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/google/Makefile.mingw Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,12 @@
+# Description: Makefile for the google plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/google/google.c Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,325 @@
+ * Adds a command to return the first url for a google I'm feeling lucky search + * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.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 02111-1301, USA. +#include "../common/pp_internal.h" +#include <conversation.h> +static PurpleCmdId google_cmd_id = 0; +#define GOOGLE_URL_FORMAT "http://%s/search?q=%s&btnI=I%%27m+Feeling+Lucky" +/****************************************************************************** + *****************************************************************************/ + PurpleConversation *conv; + PurpleProxyConnectData *conn_data; +/****************************************************************************** + * GoogleFetchUrlData API + *****************************************************************************/ +static GoogleFetchUrlData * +google_fetch_url_data_new(const gchar *url) { + GoogleFetchUrlData *gfud = g_new0(GoogleFetchUrlData, 1); + if(!purple_url_parse(url, &gfud->host, &gfud->port, &gfud->path, NULL, + gfud->response = g_string_new(""); +google_fetch_url_data_free(GoogleFetchUrlData *gfud) { + g_string_free(gfud->response, TRUE); + purple_input_remove(gfud->inpa); + purple_proxy_connect_cancel(gfud->conn_data); +/****************************************************************************** + * The final result (hiding in the middle, very sneaky) + *****************************************************************************/ +google_output_url(GoogleFetchUrlData *gfud) { + gchar *str = NULL, *url_s = NULL, *url_e = NULL; + const gchar *needle = "Location: "; + /* if our conv has disappeared from under us, drop out */ + str = gfud->response->str; + len = gfud->response->len; + url_s = g_strstr_len(str, len, needle); + url_s += strlen(needle); + url_e = g_strstr_len(url_s, len, "\r\n"); + if(gfud->conv->type == PURPLE_CONV_TYPE_IM) + purple_conv_im_send(PURPLE_CONV_IM(gfud->conv), url_s); + else if(gfud->conv->type == PURPLE_CONV_TYPE_CHAT) + purple_conv_chat_send(PURPLE_CONV_CHAT(gfud->conv), url_s); +/****************************************************************************** + *****************************************************************************/ +im_feeling_lucky_recv_cb(gpointer data, gint source, PurpleInputCondition c) { + GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data; + while((len = read(source, buff, sizeof(buff))) > 0) + gfud->response = g_string_append_len(gfud->response, buff, len); + google_output_url(gfud); + google_fetch_url_data_free(gfud); +im_feeling_lucky_send_cb(gpointer data, gint source, PurpleInputCondition c) { + GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data; + total_len = strlen(gfud->request); + len = write(gfud->fd, gfud->request + gfud->request_written, + total_len - gfud->request_written); + gfud->request_written += len; + if(gfud->request_written < total_len) + /* done writing the request, now read the response */ + purple_input_remove(gfud->inpa); + gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, + im_feeling_lucky_recv_cb, gfud); +im_feeling_lucky_cb(gpointer data, gint source, const gchar *e) { + GoogleFetchUrlData *gfud = (GoogleFetchUrlData *)data; + gfud->conn_data = NULL; + purple_debug_error("google", "unable to connect to %s: %s\n", + gfud->host, gfud->path); + google_fetch_url_data_free(gfud); + gfud->request = g_strdup_printf( + "User-Agent: Purple/%u.%u.%u\r\n" + "Connection: close\r\n" + purple_major_version, purple_minor_version, purple_micro_version, + gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_WRITE, + im_feeling_lucky_send_cb, gfud); + im_feeling_lucky_send_cb(gfud, gfud->fd, PURPLE_INPUT_WRITE); +/****************************************************************************** + *****************************************************************************/ +im_feeling_lucky(PurpleConversation *conv, const gchar *cmd, gchar **args, + gchar *error, void *data) + GoogleFetchUrlData *gfud = NULL; + PurplePlugin *plugin = (PurplePlugin *)data; + url = g_strdup_printf(GOOGLE_URL_FORMAT, "www.google.com", + purple_url_encode(args[0])); + gfud = google_fetch_url_data_new(url); + return PURPLE_CMD_RET_FAILED; + /* now make the connection */ + purple_proxy_connect(plugin, NULL, gfud->host, gfud->port, + im_feeling_lucky_cb, gfud); + google_fetch_url_data_free(gfud); + return PURPLE_CMD_RET_FAILED; + return PURPLE_CMD_RET_OK; +/****************************************************************************** + *****************************************************************************/ +plugin_load(PurplePlugin *plugin) { + purple_cmd_register("google", "s", PURPLE_CMD_P_PLUGIN, + PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT, + NULL, PURPLE_CMD_FUNC(im_feeling_lucky), + _("Returns the url for a Google I'm feeling lucky " +plugin_unload(PurplePlugin *plugin) { + purple_cmd_unregister(google_cmd_id); +static PurplePluginInfo info = { + PURPLE_PLUGIN_STANDARD, + PURPLE_PRIORITY_DEFAULT, + "core-plugin_pack-google", + "Gary Kramlich <grim@reaperworld.com>", +init_plugin(PurplePlugin *plugin) { + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name = _("Google"); + info.summary = _("Returns the url for a Google I'm feeling lucky search"); + info.description = info.summary; +PURPLE_INIT_PLUGIN(google, init_plugin, info) --- a/irc-more/irc-more.c Wed Apr 23 05:09:46 2008 -0400
+++ b/irc-more/irc-more.c Wed Apr 23 05:10:02 2008 -0400
@@ -51,13 +51,18 @@
/* So you think you can kick me? I'll show you! */
PurpleConversation *conv = data;
- char *command = g_strdup_printf("join %s", purple_conversation_get_name(conv));
- char *markup = g_markup_escape_text(command, -1);
- purple_cmd_do_command(conv, command, markup, &error); /* Do anything with the return value? */
+ char *conv_name = NULL, *command = NULL, *markup = NULL, *error = NULL; + command = g_strdup_printf("join %s", conv_name); + markup = g_markup_escape_text(command, -1); + purple_cmd_do_command(conv, command, markup, &error); /* Do anything with the return value? */ --- a/irssi/textfmt.c Wed Apr 23 05:09:46 2008 -0400
+++ b/irssi/textfmt.c Wed Apr 23 05:10:02 2008 -0400
@@ -60,7 +60,7 @@
- if(!(account)->gc->flags & PURPLE_CONNECTION_HTML) \
+ if(!((account)->gc->flags & PURPLE_CONNECTION_HTML)) \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/manualsize/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,27 @@
+EXTRA_DIST = .pidgin-plugin .build Makefile.mingw +manualsizedir = $(PIDGIN_LIBDIR) +manualsize_la_LDFLAGS = -module -avoid-version +manualsize_LTLIBRARIES = manualsize.la +manualsize_la_SOURCES = \ +manualsize_la_LIBADD = \ + -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \ + -DDATADIR=\"$(PIDGIN_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/manualsize/Makefile.mingw Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,12 @@
+# Description: Makefile for manualsize plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/manualsize/manualsize.c Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,329 @@
+ * Pidgin 2.4 manual entry area height sizing plugin + * License: GPL version 2 or later + * Copyright (C) 2008, Artemy Kapitula <dalt74@gmail.com> +#include "../common/pp_internal.h" +#include <conversation.h> +#define NOTIFY_PLUGIN_ID "pidgin-entry-manual-height" +#define PREF_PREFIX "/plugins/manualsize" +#define PREF_CHAT_ENTRY_HEIGHT PREF_PREFIX "/chat_entry_height" +#define PREF_IM_ENTRY_HEIGHT PREF_PREFIX "/im_entry_height" +static gboolean page_added = FALSE; // The flag of page has been added. + // It's used to track a case when we add a second page and should to do some + // additional work to track a page resize issues +static GList * books_connected = NULL; + // List of notebooks we connected to. When plugin is unloaded, + // we will disconnect our handler for a "page-added" signal + * Find a first "placed" objects (the object that has allocation with a height > 1) + * and it's internal height. + * It's required because when creating a non-first page in the notebook, + * the widget of the added page has allocation->heigth = 1, and we cannot + * use it as a base for evaluating position of separator in a GtkVPaned +find_placed_object(GtkWidget *w, gint *client_height) { + border_width = gtk_container_get_border_width(GTK_CONTAINER(w)); + if((w->allocation.height > 1) || (gtk_widget_get_parent(w)==NULL)) { + *client_height = w->allocation.height; + ret = find_placed_object(gtk_widget_get_parent(w), client_height); + *client_height = *client_height - border_width + 2; + * Find a GtkNotebook in the widget's parents + * It's used to find a GtkNotebook in a conversation window + * to attach a "page-added" signal handler +get_notebook(GtkWidget * w) { + const gchar *name = NULL; + name = G_OBJECT_TYPE_NAME(w); + if (name && strcmp("GtkNotebook", name) == 0) + if(gtk_widget_get_parent(w) == NULL) + return get_notebook(gtk_widget_get_parent(w)); + * Signal handler. Triggers a page_added flag. +on_page_add(GtkNotebook *book, GtkWidget *widget, guint page_num, + * When removing last page, forget this notebook +on_page_remove(GtkNotebook *book, GtkWidget *widget, guint page_num, + if(gtk_notebook_get_n_pages(book) == 0) { + books_connected = g_list_remove(books_connected, book); + * Attach a handlers on a notebook if it is not already attached + * Adds a notebook into a tracked objects list +connect_notebook_handler(GtkNotebook * notebook) { + GList * item = g_list_find(books_connected, notebook); + g_signal_connect_after(notebook, "page-added", + G_CALLBACK(on_page_add), NULL); + g_signal_connect_after(notebook, "page-removed", + G_CALLBACK(on_page_remove), NULL); + books_connected = g_list_append( books_connected, notebook ); + * Rebuild conversation pane. + * Find a conversation pane ("pane") + * Find a parent for a pane ("top") + * Create GtkVPaned ("vpaned") + * Move "pane" from a "top" to the up of "vpaned" + * Move "lower_hbox" of conversation to the bottom "vpaned" + * Insert "vpaned" into a "top" + * Change "vpaned" divider position +rebuild_container(PidginConversation *conv) { + GtkWidget * pane = gtk_widget_get_parent(GTK_WIDGET(conv->lower_hbox)); + GtkWidget * top = gtk_widget_get_parent( pane ); + GtkWidget * vpaned = gtk_vpaned_new(); + GtkNotebook * notebook = GTK_NOTEBOOK(get_notebook(top)); + GtkPositionType tabpos = -1; + gint stored_height = 0; + if(purple_conversation_get_type(conv->active_conv) == PURPLE_CONV_TYPE_CHAT) + stored_height = purple_prefs_get_int(PREF_CHAT_ENTRY_HEIGHT); + stored_height = purple_prefs_get_int(PREF_IM_ENTRY_HEIGHT); + if (stored_height < 0) stored_height = 128; + tabpos = gtk_notebook_get_tab_pos( notebook ); + connect_notebook_handler( notebook ); + g_value_init( &v, G_TYPE_BOOLEAN ); + gtk_widget_show( vpaned ); + g_value_set_boolean( &v, TRUE ); + gtk_widget_reparent( pane, vpaned ); + gtk_container_child_set_property( GTK_CONTAINER(vpaned), pane, "resize", &v ); + g_value_set_boolean( &v, FALSE ); + gtk_widget_reparent( conv->lower_hbox, vpaned ); + gtk_container_child_set_property( GTK_CONTAINER(vpaned), conv->lower_hbox, "resize", &v ); + gtk_container_add( GTK_CONTAINER(top), vpaned ); + gtk_widget_style_get( vpaned, "handle-size", &handle_size, NULL ); + find_placed_object( top, &parent_area ); + border_size = gtk_container_get_border_width(GTK_CONTAINER(top)); + (((page_added==TRUE)&&((tabpos==GTK_POS_TOP)||(tabpos==GTK_POS_BOTTOM)))?24:0); + gtk_paned_set_position( GTK_PANED(vpaned), new_pos ); + gtk_widget_grab_focus( conv->entry ); + * Store input area size depending on a conversation type +store_area_size(PidginConversation *gtkconv) { + GtkWidget *parent = NULL; + const gchar *name = NULL; + parent = gtk_widget_get_parent(GTK_WIDGET(gtkconv->lower_hbox)); + name = G_OBJECT_TYPE_NAME(parent); + if(name && strcmp("GtkVPaned", name) == 0) { + PurpleConversation *conv = gtkconv->active_conv; + if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + purple_prefs_set_int(PREF_CHAT_ENTRY_HEIGHT, + gtkconv->lower_hbox->allocation.height); + purple_prefs_set_int(PREF_IM_ENTRY_HEIGHT, + gtkconv->lower_hbox->allocation.height); + * Signal handler. Called when conversation created, and rebuilds a conversation pane +on_display(gpointer data) { + PidginConversation *gtkconv = (PidginConversation *)data; + rebuild_container(gtkconv); + * Signal handler. Called when conversation destroyed, to store an input area size +on_destroy(void * data) { + PurpleConversation *conv = (PurpleConversation*)data; + PidginConversation * gtkconv = PIDGIN_CONVERSATION(conv); + store_area_size(gtkconv); + * Traverse connected notebooks and remove our signal handler +cleanup_callback(gpointer data, gpointer user_data) { + g_signal_handlers_disconnect_by_func( data, on_page_add, NULL ); + g_signal_handlers_disconnect_by_func( data, on_page_remove, NULL ); +plugin_load(PurplePlugin *plugin) { + void *gtk_conv_handle = pidgin_conversations_get_handle(); + void *conv_handle = purple_conversations_get_handle(); + purple_prefs_add_none(PREF_PREFIX); + purple_prefs_add_int(PREF_CHAT_ENTRY_HEIGHT, 128); + purple_prefs_add_int(PREF_IM_ENTRY_HEIGHT, 128); + purple_signal_connect(gtk_conv_handle, "conversation-displayed", plugin, + PURPLE_CALLBACK(on_display), NULL); + purple_signal_connect(conv_handle, "deleting-conversation", plugin, + PURPLE_CALLBACK(on_destroy), NULL); +plugin_unload(PurplePlugin *plugin) { + g_list_foreach(books_connected, cleanup_callback, NULL); + g_list_free(books_connected); +static PurplePluginInfo info = + PURPLE_PLUGIN_STANDARD, /**< type */ + PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + NOTIFY_PLUGIN_ID, /**< id */ + PP_VERSION, /**< version */ + NULL, /** description */ + "Artemy Kapitula <dalt74@gmail.com>", /**< author */ + PP_WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< extra_info */ +init_plugin(PurplePlugin *plugin) { + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name = _("Entry area manual sizing"); + info.summary = _("Allows you to change entry area height"); + info.description = info.summary; +PURPLE_INIT_PLUGIN(manualsize, init_plugin, info) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/msglen/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,27 @@
+EXTRA_DIST = .pidgin-plugin .incomplete Makefile.mingw +msglendir = $(PIDGIN_LIBDIR) +msglen_la_LDFLAGS = -module -avoid-version +msglen_LTLIBRARIES = msglen.la + -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \ + -DDATADIR=\"$(PIDGIN_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/msglen/Makefile.mingw Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,12 @@
+# Description: Makefile for convbadger plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/msglen/msglen.c Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,217 @@
+ * msglen - Adds the current message's length to the menutray of a conversation + * Copyright (C) 2008 Gary Kramlich <grim@reaperworld.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 02111-1301, USA. +/* If you can't figure out what this line is for, DON'T TOUCH IT. */ +#include "../common/pp_internal.h" +#include <conversation.h> +#include <gtkmenutray.h> +/****************************************************************************** + *****************************************************************************/ + PurpleConversation *conv; +/****************************************************************************** + *****************************************************************************/ +static GHashTable *data = NULL; +/****************************************************************************** + *****************************************************************************/ +msg_len_data_free(MsgLenData *mld) { + if(mld->label && GTK_IS_LABEL(mld->label)) + gtk_widget_destroy(mld->label); +msg_len_data_free_helper(gpointer k, gpointer v, gpointer d) { + MsgLenData *mld = (MsgLenData *)v; + msg_len_data_free(mld); +msg_len_update(PidginWindow *win, PurpleConversation *conv) { + PidginConversation *gtkconv = NULL; + MsgLenData *mld = NULL; + g_return_if_fail(conv); + mld = g_hash_table_lookup(data, conv); + mld = g_new0(MsgLenData, 1); + mld->label = gtk_label_new(""); + pidgin_menu_tray_append(PIDGIN_MENU_TRAY(win->menu.tray), mld->label, + gtk_widget_show(mld->label); + g_signal_connect_swapped(G_OBJECT(mld->label), "destroy", + G_CALLBACK(g_nullify_pointer), &mld->label); + gtkconv = PIDGIN_CONVERSATION(conv); + count = gtk_text_buffer_get_char_count(gtkconv->entry_buffer); + text = g_strdup_printf("%d", count); + gtk_label_set_text(GTK_LABEL(mld->label), text); + g_hash_table_insert(data, win, mld); +/****************************************************************************** + *****************************************************************************/ +msg_len_conv_created_cb(PurpleConversation *conv, gpointer data) { + PidginConversation *pconv = PIDGIN_CONVERSATION(conv); + PidginWindow *win = pidgin_conv_get_window(pconv); + purple_debug_info("msglen", "created\n"); + msg_len_update(win, conv); +msg_len_conv_destroyed_cb(PurpleConversation *conv, gpointer data) { + purple_debug_info("msglen", "destroyed\n"); +msg_len_conv_switched_cb(PurpleConversation *conv, gpointer data) { + PidginConversation *pconv = PIDGIN_CONVERSATION(conv); + PidginWindow *win = pidgin_conv_get_window(pconv); + purple_debug_info("msglen", "switched\n"); + msg_len_update(win, conv); +/****************************************************************************** + *****************************************************************************/ +plugin_load(PurplePlugin *plugin) { + void *conv_handle = purple_conversations_get_handle(); + data = g_hash_table_new_full(g_direct_hash, g_direct_equal, + purple_signal_connect(conv_handle, "conversation-created", plugin, + PURPLE_CALLBACK(msg_len_conv_created_cb), NULL); + purple_signal_connect(conv_handle, "deleting-conversation", plugin, + PURPLE_CALLBACK(msg_len_conv_destroyed_cb), NULL); + purple_signal_connect(pidgin_conversations_get_handle(), + "conversation-switched", plugin, + PURPLE_CALLBACK(msg_len_conv_switched_cb), NULL); +plugin_unload(PurplePlugin *plugin) { + g_hash_table_foreach(data, msg_len_data_free_helper, NULL); + g_hash_table_destroy(data); +static PurplePluginInfo info = { + PURPLE_PLUGIN_STANDARD, + PURPLE_PRIORITY_DEFAULT, + "gtk-plugin_pack-msglen", + "Gary Kramlich <grim@reaperworld.com>", +init_plugin(PurplePlugin *plugin) { + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name = _("Message Length"); + info.summary = _("Shows the length of your current message in the menu " + info.description = info.summary; +PURPLE_INIT_PLUGIN(msg_len, init_plugin, info) --- a/po/POTFILES.in Wed Apr 23 05:09:46 2008 -0400
+++ b/po/POTFILES.in Wed Apr 23 05:10:02 2008 -0400
@@ -1,5 +1,22 @@
+autoprofile/autoprofile.c +autoprofile/comp_countdownup.c +autoprofile/comp_executable.c +autoprofile/comp_logstats.c +autoprofile/comp_logstats_gtk.c +autoprofile/comp_quotation.c +autoprofile/comp_textfile.c +autoprofile/comp_timestamp.c +autoprofile/comp_uptime.c +autoprofile/gtk_actions.c +autoprofile/gtk_away_msgs.c +autoprofile/gtk_widget.c +autoprofile/preferences.c @@ -8,6 +25,7 @@
@@ -19,6 +37,7 @@
@@ -57,6 +76,7 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/splitter/ChangeLog Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,71 @@
+Ike Gingerich <ike_@users.sourceforge.net> +2007-05-04 21:21:15 [v0.95 for 2.0.0] + * trimmed a good amount of unnecessary code and made with the + gaim -> pidgin conversion. +2006-04-15 15:55:10 [v0.95] + * now using Pango to split the message appropriately in different languages +2006-04-13 20:33:07 [v0.93] + * fixed a bug where border characters were sometimes eaten as whitespace + * no longer processes empty slices +2006-04-13 16:47:22 [v0.92] + * replaced html slicing algorithm with a modified version of Gaim's + * corrected a non-static function to be static +2006-04-11 09:52:29 [v0.91] + * fixed a bug with escaped HTML characters counting for more than one + character (thanks xx3nvyxx) +2006-04-11 01:25:06 [v0.9] + * added continuing of HTML formatting between splits + * removed "split around whole words" option and made default behavior +2005-10-04 23:39:59 [v0.8] + * fixed a tiny but lethal and frustrating crash related to word wrapping + (big thanks to itchysoft_ant and mgilb81) + * added additional NULL pointer checking +2005-09-23 18:37:24 [v0.7] +2005-09-23 09:37:24 [v0.6] + * you can now specify delay between message sends +2005-09-12 04:40:16 [v0.5] + * splits on whitespace where possible + * preferences option added to mediate splitting on whitespace +2005-07-27 00:19:21 [v0.4] + * sends messages in the same manner as gaim: + - emits "sent-im-msg" signal + - takes into consideration HTML preferences + * much more robust error checking +2005-07-22 07:44:06 [v0.3] + * big memory allocation bug fixed that could cause a crash in certain situations +2005-07-21 20:29:44 [v0.2] + * alias is displayed in conversation window instead of username +2005-07-21 03:50:14 [v0.1] + * initial release of plugin --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/splitter/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,26 @@
+EXTRA_DIST = .purple-plugin .incomplete Makefile.mingw +splitterdir = $(PURPLE_LIBDIR) +splitter_la_LDFLAGS = -module -avoid-version +splitter_LTLIBRARIES = splitter.la + -DLIBDIR=\"$(PURPLE_LIBDIR)\" \ + -DDATADIR=\"$(PURPLE_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/splitter/Makefile.mingw Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,12 @@
+# Description: Makefile for dice plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/splitter/README Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,91 @@
+Message Splitter Plugin v0.95 +Ike Gingerich <ike_@users.sourceforge.net> +This is a simple plugin to split up outgoing messages that are larger +than a specified threshold in characters. + - The split size and delay between messages are configurable from the + plugin preferences menu. + - Splits yield valid and balanced HTML taking into account tags that may + have been opened or closed outside the split itself. + - Splitting is done using Pango to split the message as appropriate + according to the current locale. + - fix conflicts with plugins like Slashexec that don't expect to be + supplied an empty message during the 'sending-im' signal. + - fix bug with characters disappearing when splitting heavily marked-up + - find any remaining bugs and memory leaks. + If you have build problems make sure you have the + appropriate pidgin-dev and build-essential files + Alternately you can copy spliter.c to + (unzipped-source)/pidgin/plugins and + then copy splitter.so to ~/.purple/plugins/ and launch Pidgin. + I try to keep a pre-built current version of the plugin here: + <http://ikebo.hypermart.net/splitter/splitter-current.dll> and + on the Sourceforge message splitter plugin tracker. + Otherwise you must compile it manually with Cygwin: + copy splitter.c to the plugin directory where the Pidgin + source code is extracted to and type: + $ make -f Makefile.mingw splitter.dll + and copy splitter.dll to your plugin directory + (e.x. C:\Program Files\Pidgin\plugins) +xx3nvyxx - caught disappearing text bug in v0.9 +itchysoft_ant - isolated crash in v0.7 and submitted backtrace logs + suggested delay/chat features for v0.6/v0.7 +mgilb81 - notified of crash in v0.7 and filed report +rageboy04 - a number of other issues/feedback throughout + - notified/fixed alias issues for v0.2 +Feel free to contact me at <ike_@users.sourceforge.net> about problems, +suggestions, or contributions. --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/splitter/splitter.c Wed Apr 23 05:10:02 2008 -0400
@@ -0,0 +1,533 @@
+/* Message Splitter Plugin v0.95 + * Splits a large message into smaller messages and sends them away + * Copyright (C) 2005-2007, Ike Gingerich <ike_@users.sourceforge.net> + * 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., 59 Temple Place - Suite 330, Boston, MA +#define GETTEXT_PACKAGE "gtk20" +#include <glib/gi18n-lib.h> +#define WEBSITE "http://ikebo.hypermart.net/" +#define PLUGIN_ID "core-ike-splitter" +/* plugin constants and structures */ +static const gint MIN_SPLIT_SIZE = 32; +static const gint DEFAULT_SPLIT_SIZE = 786; +static const gint MAX_SPLIT_SIZE = 8192; +static const gint MIN_DELAY_MS = 0; +static const gint DEFAULT_DELAY_MS = 500; +static const gint MAX_DELAY_MS = 3600000; + char *sender_protocol_id; + PurpleConversationType type; + char *receiver; /* IM username */ +/* plugin preference variables */ +static gint current_split_size; +/* initialize preferences dialog */ +static PurplePluginPrefFrame *get_plugin_pref_frame(PurplePlugin *plugin) + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; + frame = purple_plugin_pref_frame_new(); + g_return_val_if_fail(frame != NULL, NULL); + ppref = purple_plugin_pref_new_with_label("Message split size"); + g_return_val_if_fail(ppref != NULL, NULL); + purple_plugin_pref_frame_add(frame, ppref); + ppref = purple_plugin_pref_new_with_name_and_label("/plugins/core/splitter/split_size", + g_return_val_if_fail(ppref != NULL, NULL); + purple_plugin_pref_set_bounds(ppref, MIN_SPLIT_SIZE, MAX_SPLIT_SIZE); + purple_plugin_pref_frame_add(frame, ppref); + ppref = purple_plugin_pref_new_with_label("Delay between messages"); + g_return_val_if_fail(ppref != NULL, NULL); + purple_plugin_pref_frame_add(frame, ppref); + ppref = purple_plugin_pref_new_with_name_and_label("/plugins/core/splitter/delay_ms", + g_return_val_if_fail(ppref != NULL, NULL); + purple_plugin_pref_set_bounds(ppref, MIN_DELAY_MS, MAX_DELAY_MS); + purple_plugin_pref_frame_add(frame, ppref); + * A function to send a chat or im message to the specific conversation + * without emitting "sending-im" or "sending-chat" signal, which would + * cause an infinite loop for this plugin. + * taken from conversation.c with signal emission removed. +static void splitter_common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags) + PurpleConversationType type; + PurpleAccount *account; + char *displayed = NULL, *sent = NULL; + if (strlen(message) == 0) + account = purple_conversation_get_account(conv); + gc = purple_conversation_get_gc(conv); + g_return_if_fail(account != NULL); + g_return_if_fail(gc != NULL); + type = purple_conversation_get_type(conv); + /* Always linkfy the text for display */ + displayed = purple_markup_linkify(message); + if ((conv->features & PURPLE_CONNECTION_HTML) && + !(msgflags & PURPLE_MESSAGE_RAW)) + sent = g_strdup(displayed); + sent = g_strdup(message); + msgflags |= PURPLE_MESSAGE_SEND; + if (type == PURPLE_CONV_TYPE_IM) { + PurpleConvIm *im = PURPLE_CONV_IM(conv); + if (sent != NULL && sent[0] != '\0') { + err = serv_send_im(gc, purple_conversation_get_name(conv), + if ((err > 0) && (displayed != NULL)) + purple_conv_im_write(im, NULL, displayed, msgflags, time(NULL)); + purple_signal_emit(purple_conversations_get_handle(), "sent-im-msg", + purple_conversation_get_name(conv), sent); + if (sent != NULL && sent[0] != '\0') { + err = serv_chat_send(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sent, msgflags); + purple_signal_emit(purple_conversations_get_handle(), "sent-chat-msg", + purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv))); + who = purple_conversation_get_name(conv); + msg = _("Unable to send message: The message is too large."); + if (!purple_conv_present_error(who, account, msg)) { + char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who); + purple_notify_error(gc, NULL, msg2, _("The message is too large.")); + else if (err == -ENOTCONN) { + purple_debug(PURPLE_DEBUG_ERROR, "conversation", + "Not yet connected.\n"); + msg = _("Unable to send message."); + if (!purple_conv_present_error(who, account, msg)) { + char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who); + purple_notify_error(gc, NULL, msg2, NULL); +/* a timer based callback function that sends the next message in the queue */ +static gboolean send_message_timer_cb( message_to_conv *msg_to_conv ) + PurpleAccount *account; + PurpleConversation *conv; + g_return_val_if_fail(msg_to_conv != NULL, FALSE); + g_return_val_if_fail(msg_to_conv->messages != NULL, FALSE); + g_return_val_if_fail(msg_to_conv->sender_username != NULL, FALSE); + g_return_val_if_fail(msg_to_conv->sender_protocol_id != NULL, FALSE); + msg = g_queue_pop_head(msg_to_conv->messages); + /* clean up and terminate timer callback */ + g_queue_free(msg_to_conv->messages); + g_free(msg_to_conv->sender_username); + g_free(msg_to_conv->sender_protocol_id); + if( msg_to_conv->type == PURPLE_CONV_TYPE_IM && + msg_to_conv->receiver != NULL ) + g_free(msg_to_conv->receiver); + /* find account info (it may have changed) and try and create a new + conversation window (it may have been closed) or find the existing + chat, and finally send the message */ + account = purple_accounts_find(msg_to_conv->sender_username, + msg_to_conv->sender_protocol_id); + g_return_val_if_fail(account != NULL, FALSE); + if( msg_to_conv->type == PURPLE_CONV_TYPE_IM && msg_to_conv->receiver != NULL ) + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, msg_to_conv->receiver); + else if( msg_to_conv->type == PURPLE_CONV_TYPE_CHAT ) + conv = purple_find_chat(account->gc, msg_to_conv->id); + g_return_val_if_fail(conv != NULL, FALSE); + splitter_common_send(conv, msg, PURPLE_MESSAGE_SEND); +/* finds the first line-breakable character backwards starting from a[last] */ +static int find_last_break(PangoLogAttr *a, int last) + for(; last > 0; last-- ) + if( a[last].is_line_break == 1) + return ( a[last].is_line_break == 1) ? last-1 : -1; +/* uses Pango to find all possible line break locations in a message and returns + a PangoLogAttr array which maps to each byte of the message of length + one larger than the message. This must be g_free()'d */ +static PangoLogAttr* find_all_breaks(const char *message) + g_return_val_if_fail(message != NULL, NULL); + a = g_new0(PangoLogAttr, n_attr); + context = gdk_pango_context_get(); + g_return_val_if_fail(context != NULL, NULL); + list = pango_itemize(context, message, 0, len, NULL, NULL); + if (list != NULL && list->data != NULL) + pango_break(message, -1, &((PangoItem*)(list->data))->analysis, a, n_attr); +/* return a queue of message slices from a plain text message based on current_split_size using + Pango to determine possible line break locations */ +static GQueue* get_message_slices(const char *message) + int current_break_start, last_break_start, break_pos, len; + a = find_all_breaks(message); + g_return_val_if_fail(a != NULL, NULL); + last_break_start = current_break_start = 0; + while(current_break_start + current_split_size < len) + break_pos = find_last_break(a + last_break_start, current_split_size); + if( break_pos > -1 ) current_break_start += break_pos; + else current_break_start += current_split_size; + slice = g_new0( message_slice, 1 ); + slice->start = MAX( last_break_start, 0 ); + slice->end = MIN( current_break_start, len ); + if( slice->end > slice->start ) + g_queue_push_tail(q, slice); + last_break_start = current_break_start; + slice = g_new0( message_slice, 1 ); + slice->start = last_break_start; + g_queue_push_tail(q, slice); +/* takes a message, splits it up based on whitespace (ignoring HTML formatting), + requests HTMLized slices of the splits, and returns a queue of them. The + messages and the queue must be freed */ +static GQueue* create_message_queue(const char *message) + GQueue *slices, *messages; + char *stripped_message, *msg; + stripped_message = purple_markup_strip_html(message); + stripped_len = strlen(stripped_message); + messages = g_queue_new(); + slices = get_message_slices(stripped_message); + g_return_val_if_fail(slices != NULL, NULL); + while( (slice = g_queue_pop_head(slices)) != NULL ) + msg = purple_markup_slice(message, slice->start, slice->end); + g_queue_push_tail(messages, msg); + g_free(stripped_message); +/* create message queue and prepare timer callbacks */ +static void split_and_send(message_to_conv *msg_to_conv, const char **message) + g_return_if_fail( msg_to_conv != NULL ); + g_return_if_fail( message != NULL ); + g_return_if_fail( *message != NULL ); + /* read and validate preferences */ + current_split_size = purple_prefs_get_int("/plugins/core/splitter/split_size"); + if( current_split_size > MAX_SPLIT_SIZE ) current_split_size = MAX_SPLIT_SIZE; + if( current_split_size < MIN_SPLIT_SIZE ) current_split_size = MIN_SPLIT_SIZE; + message_delay_ms = purple_prefs_get_int("/plugins/core/splitter/delay_ms"); + if( message_delay_ms > MAX_DELAY_MS ) message_delay_ms = MAX_DELAY_MS; + if( message_delay_ms < MIN_DELAY_MS ) message_delay_ms = MIN_DELAY_MS; + /* prepare message queue */ + msg_to_conv->messages = create_message_queue(*message); + g_return_if_fail( msg_to_conv->messages != NULL ); + /* initialize message send timer */ + purple_timeout_add( (g_queue_get_length(msg_to_conv->messages) > 1) ? message_delay_ms : 0, + (GSourceFunc)send_message_timer_cb, + /* free the original message and ensure it does not get sent */ + g_free((char*)*message); +/* initialize a chat message to potentially be split */ +static void sending_chat_msg_cb(PurpleAccount *account, const char **message, int id) + message_to_conv *msg_to_conv; + purple_debug(PURPLE_DEBUG_MISC, "purple-splitter", "splitter plugin invoked\n"); + g_return_if_fail(account != NULL); + g_return_if_fail(message != NULL); + g_return_if_fail(*message != NULL); + msg_to_conv = g_new0(message_to_conv, 1); + g_return_if_fail( msg_to_conv != NULL ); + msg_to_conv->sender_username = g_strdup(account->username); + msg_to_conv->sender_protocol_id = g_strdup(account->protocol_id); + msg_to_conv->type = PURPLE_CONV_TYPE_CHAT; + split_and_send(msg_to_conv, message); +/* initialize an IM message to potentially be split */ +static void sending_im_msg_cb(PurpleAccount *account, const char *receiver, + message_to_conv *msg_to_conv; + purple_debug(PURPLE_DEBUG_MISC, "purple-splitter", "splitter plugin invoked\n"); + g_return_if_fail(account != NULL); + g_return_if_fail(receiver != NULL); + g_return_if_fail(message != NULL); + g_return_if_fail(*message != NULL); + msg_to_conv = g_new0(message_to_conv, 1); + g_return_if_fail( msg_to_conv != NULL ); + msg_to_conv->sender_username = g_strdup(account->username); + msg_to_conv->sender_protocol_id = g_strdup(account->protocol_id); + msg_to_conv->receiver = g_strdup(receiver); + msg_to_conv->type = PURPLE_CONV_TYPE_IM; + split_and_send(msg_to_conv, message); +/* register "sending" message signal callback */ +static gboolean plugin_load(PurplePlugin *plugin) + purple_signal_connect(purple_conversations_get_handle(), + PURPLE_CALLBACK(sending_im_msg_cb), + purple_signal_connect(purple_conversations_get_handle(), + PURPLE_CALLBACK(sending_chat_msg_cb), +static gboolean plugin_unload(PurplePlugin *plugin) +static PurplePluginUiInfo prefs_info = { + get_plugin_pref_frame, 0, NULL, NULL, NULL, NULL, NULL +static PurplePluginInfo info = + PURPLE_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + N_("Message Splitter"), /**< name */ + VERSION, /**< version */ + N_("Splits a large outgoing message into smaller " + "messages of a specified size."), + N_("Splits a large outgoing message into smaller " + "messages of a specified size."), + "Ike Gingerich <ike_@users.sourceforge.net>", /**< author */ + WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< extra_info */ + &prefs_info, /**< prefs_info */ +/* store initial preference values */ +static void init_plugin(PurplePlugin *plugin) + purple_prefs_add_none("/plugins/core/splitter"); + purple_prefs_add_int ("/plugins/core/splitter/split_size", DEFAULT_SPLIT_SIZE); + purple_prefs_add_int ("/plugins/core/splitter/delay_ms", DEFAULT_DELAY_MS); +PURPLE_INIT_PLUGIN(splitter, init_plugin, info) --- a/switchspell/Makefile.am Wed Apr 23 05:09:46 2008 -0400
+++ b/switchspell/Makefile.am Wed Apr 23 05:10:02 2008 -0400
@@ -14,8 +14,9 @@
switchspell_la_LIBADD = \
@@ -23,6 +24,7 @@
-DLIBDIR=\"$(PIDGIN_LIBDIR)\" \
-DDATADIR=\"$(PIDGIN_DATADIR)\" \
-DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \