pidgin/purple-plugin-pack
Clone
merge of '473803c9bd5b7821a3b34914b6785b338a65cdef'
and 'b459dd956b446010f04ffcdd1e7e452404efb727'
--- a/AUTHORS Sun Dec 23 07:18:21 2007 -0500
+++ b/AUTHORS Mon Mar 24 15:08:23 2008 -0400
@@ -10,6 +10,9 @@
Sadrul H Chowdhury <sadrul@users.sourceforge.net>
Richard Laager <rlaager@guifications.org>
Martijn van Oosterhout <kleptog@svana.org>
+Matt Perry <guy@fscked.org> +Andrew Pangborn <gaim@andrewpangborn.com> @@ -40,13 +43,13 @@
-qwert - realphabetize or replace with real name before release!
--- a/ChangeLog Sun Dec 23 07:18:21 2007 -0500
+++ b/ChangeLog Mon Mar 24 15:08:23 2008 -0400
@@ -1,4 +1,8 @@
+ * Merged the Autoprofile plugin into our build system. + * Fixed convbadger's failure to update on conversation switch. * Fixed a typo in irc-more's source that allowed a potential double-free
* Fixed unregistering commands when unloading gRIM and Magic 8 ball plugin
* Fixed napster plugin. It builds cleanly and loads properly now.
@@ -10,8 +14,18 @@
(http://en.wikipedia.org/wiki/Dice_notation), but it's not perfect yet.
* Dice plugin now calls the /me command with its output.
+ * Irc-more plugin adds notice support only when built against libpurple * Napster plugin now builds by default.
* Removed the broadcast plugin.
+ * Fixed the --with-plugins configure argument. It now correctly handles + all, default, and a comma separated list of plugins. + * Fixed a crash in the xchat-chats plugin which occurs due to the changes + to the conversation window in 2.4.0. + * Fixed a missing header include in the timelog plugin which caused a + plugin load failure under some circumstances. + * Finally added some content to README * Added 'menuconfig' script to make it easier to select what plugins to
--- a/README Sun Dec 23 07:18:21 2007 -0500
+++ b/README Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,9 @@
+The Purple Plugin Pack was originally created by Gary Kramlich and Stu +Tomlinson as a way to distribute their ever growing lists of simple Pidgin +plugins. It has since grown from its origins of about 6 plugins to nearly 50. +Also, many more developers have continued to add to it, including John Bailey, +Peter Lawler, Sadrul Habib Chowdhury, and most recently Richard Laager. +More information on the Plugin Pack can be found at +http://plugins.guifications.org/trac/wiki/PluginPack --- a/VERSION Sun Dec 23 07:18:21 2007 -0500
+++ b/VERSION Mon Mar 24 15:08:23 2008 -0400
@@ -1,1 +1,1 @@
--- a/album/album-ui.c Sun Dec 23 07:18:21 2007 -0500
+++ b/album/album-ui.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,8 +1,8 @@
* Album (Buddy Icon Archiver)
- * Copyright (C) 2005-2006, Sadrul Habib Chowdhury <imadil@gmail.com>
- * Copyright (C) 2005-2006, Richard Laager <rlaager@pidgin.im>
+ * Copyright (C) 2005-2008, Sadrul Habib Chowdhury <imadil@gmail.com> + * Copyright (C) 2005-2008, Richard Laager <rlaager@pidgin.im> * Copyright (C) 2006, Jérôme Poulin (TiCPU) <jeromepoulin@gmail.com>
* This program is free software; you can redistribute it and/or
@@ -126,6 +126,13 @@
static void update_icon_view(icon_viewer_key *key);
static void show_buddy_icon_window(icon_viewer_key *key, const char *name);
+void icon_viewer_key_free(void *data) + icon_viewer_key *key = (icon_viewer_key *)data; + g_free(key->screenname); guint icon_viewer_hash(gconstpointer data)
const icon_viewer_key *key = data;
@@ -932,6 +939,7 @@
/* Return if a window is already opened for the buddy. */
if ((bw = g_hash_table_lookup(buddy_windows, key)) != NULL)
+ icon_viewer_key_free(key); gtk_window_present(GTK_WINDOW(bw->window));
@@ -942,6 +950,7 @@
if (key->contact == NULL &&
(bw = g_hash_table_find(buddy_windows, (GHRFunc)compare_buddy_keys, key)) != NULL)
+ icon_viewer_key_free(key); gtk_window_present(GTK_WINDOW(bw->window));
--- a/album/album-ui.h Sun Dec 23 07:18:21 2007 -0500
+++ b/album/album-ui.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,8 +1,8 @@
* Album (Buddy Icon Archiver)
- * Copyright (C) 2005-2006, Sadrul Habib Chowdhury <imadil@gmail.com>
- * Copyright (C) 2005-2006, Richard Laager <rlaager@pidgin.im>
+ * Copyright (C) 2005-2008, Sadrul Habib Chowdhury <imadil@gmail.com> + * Copyright (C) 2005-2008, Richard Laager <rlaager@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
@@ -36,6 +36,8 @@
gboolean icon_viewer_equal(gconstpointer y, gconstpointer z);
+void icon_viewer_key_free(void *key); GList *album_get_plugin_actions(PurplePlugin *plugin, gpointer data);
void album_blist_node_menu_cb(PurpleBlistNode *node, GList **menu);
--- a/album/album.c Sun Dec 23 07:18:21 2007 -0500
+++ b/album/album.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,8 +1,8 @@
* Album (Buddy Icon Archiver)
- * Copyright (C) 2005-2006, Sadrul Habib Chowdhury <imadil@gmail.com>
- * Copyright (C) 2005-2006, Richard Laager <rlaager@pidgin.im>
+ * Copyright (C) 2005-2008, Sadrul Habib Chowdhury <imadil@gmail.com> + * Copyright (C) 2005-2008, Richard Laager <rlaager@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
@@ -253,7 +253,7 @@
cache_existing_icons(NULL);
- buddy_windows = g_hash_table_new_full(icon_viewer_hash, icon_viewer_equal, g_free, g_free);
+ buddy_windows = g_hash_table_new_full(icon_viewer_hash, icon_viewer_equal, icon_viewer_key_free, g_free); --- a/album/album.h Sun Dec 23 07:18:21 2007 -0500
+++ b/album/album.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,8 +1,8 @@
* Album (Buddy Icon Archiver)
- * Copyright (C) 2005-2006, Sadrul Habib Chowdhury <imadil@gmail.com>
- * Copyright (C) 2005-2006, Richard Laager <rlaager@pidgin.im>
+ * Copyright (C) 2005-2008, Sadrul Habib Chowdhury <imadil@gmail.com> + * Copyright (C) 2005-2008, Richard Laager <rlaager@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
--- a/autogen.sh Sun Dec 23 07:18:21 2007 -0500
+++ b/autogen.sh Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
# Guifications - The end-all, be-all notification framework
-# Copyright (C) 2003-2007 Gary Kramlich <grim@reaperworld.com>
+# Copyright (C) 2003-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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/Makefile.am Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,60 @@
+autoprofiledir = $(PURPLE_LIBDIR) +autoprofile_la_LDFLAGS = -module -avoid-version +autoprofile_LTLIBRARIES = autoprofile.la +autoprofile_la_SOURCES = \ +autoprofile_la_LIBADD = \ + -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \ + -DDATADIR=\"$(PIDGIN_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/Makefile.mingw Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,37 @@
+# Description: Makefile for autoprofile plugin. +include $(PP_TOP)/win_pp.mak --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/autoaway.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,145 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "conversation.h" +#define AP_IDLE_CHECK_INTERVAL 5 +static guint check_timeout = 0; +static guint pref_cb = 0; +static time_t last_active_time = 0; +static gboolean is_idle () + PurpleIdleUiOps *ui_ops; + const gchar *idle_reporting; + ui_ops = purple_idle_get_ui_ops (); + idle_reporting = purple_prefs_get_string ("/core/away/idle_reporting"); + if (!strcmp (idle_reporting, "system") && + (ui_ops != NULL) && (ui_ops->get_time_idle != NULL)) { + time_idle = time (NULL) - last_active_time; + } else if (!strcmp (idle_reporting, "gaim")) { + time_idle = time (NULL) - last_active_time; + (60 * purple_prefs_get_int("/core/away/mins_before_away"))); +static gboolean ap_check_idleness (gpointer data) + // 0 0 0 don't do anything + // 0 0 1 ap_use_idleaway () + // 1 0 x don't do anything, we're already away + // 1 1 0 ap_dont_use_idleaway () + // 1 1 1 don't do anything + if (ap_is_currently_away () && !ap_autoaway_in_use ()) return TRUE; + auto_away = purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/away_when_idle"); + if (auto_away && !ap_is_currently_away () && !ap_autoaway_in_use ()) { + if (ap_is_currently_away () && ap_autoaway_in_use ()) { + ap_autoaway_disable (); +void ap_autoaway_touch () + time (&last_active_time); +static gboolean writing_im_msg_cb (PurpleAccount *account, const char *who, + char **message, PurpleConversation *conv, PurpleMessageFlags flags) + ap_check_idleness (NULL); +static void auto_pref_cb ( + const char *name, PurplePrefType type, gconstpointer val, gpointer data) + if (!purple_prefs_get_bool ("/core/away/away_when_idle")) return; + purple_notify_error (NULL, NULL, + N_("This preference is disabled"), + N_("This preference currently has no effect because AutoProfile is in " + "use. To modify this behavior, use the AutoProfile configuration " + purple_prefs_set_bool ("/core/away/away_when_idle", FALSE); +/*--------------------------------------------------------------------------* + * Global functions to start it all * + *--------------------------------------------------------------------------*/ +void ap_autoaway_start () + purple_prefs_set_bool ("/core/away/away_when_idle", FALSE); + check_timeout = purple_timeout_add (AP_IDLE_CHECK_INTERVAL * 1000, + ap_check_idleness, NULL); + purple_signal_connect (purple_conversations_get_handle (), "writing-im-msg", + ap_get_plugin_handle (), PURPLE_CALLBACK(writing_im_msg_cb), NULL); + pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (), + "/core/away/away_when_idle", auto_pref_cb, NULL); +void ap_autoaway_finish () + // Assumes signals are disconnected globally + purple_prefs_disconnect_callback (pref_cb); + if (check_timeout > 0) purple_timeout_remove (check_timeout); + purple_prefs_set_bool ("/core/away/away_when_idle", + purple_prefs_get_bool ("/plugins/gtk/autoprofile/away_when_idle")); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/autoprofile.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,861 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "savedstatuses.h" +static void ap_status_changed ( + const char *, PurplePrefType, gconstpointer, gpointer); +static void ap_account_connected (PurpleConnection *); +static void ap_delete_legacy_prefs (); +static void ap_update_queue_start (); +static void ap_update_queue_finish (); +/*-------------------------------------------------------------------------- + *------------------------------------------------------------------------*/ +static PurplePlugin *plugin_handle = NULL; +static PurpleSavedStatus *current_ap_status = NULL; +static GStaticMutex update_timeout_mutex = G_STATIC_MUTEX_INIT; +static GHashTable *update_timeouts = NULL; +static gboolean using_idleaway = FALSE; +static GStaticMutex update_queue_mutex = G_STATIC_MUTEX_INIT; +static GList *queued_profiles = NULL; +static guint update_queue_timeout = 0; +/* Functions related to general variables */ +PurplePlugin *ap_get_plugin_handle () { return plugin_handle; } +gboolean ap_is_currently_away () { + return current_ap_status != NULL && + purple_savedstatus_get_type (current_ap_status) == PURPLE_STATUS_AWAY; +/*-------------------------------------------------------------------------- + * REQUIRED GAIM FUNCTIONS- INFO, INITIALIZATION, UNLOADING + *------------------------------------------------------------------------*/ +/* What to do when plugin is loaded */ +static gboolean plugin_load (PurplePlugin *plugin) + ap_debug ("general", "AutoProfile is being loaded"); + plugin_handle = plugin; + current_ap_status = purple_savedstatus_new (NULL, PURPLE_STATUS_UNSET); + update_timeouts = g_hash_table_new (NULL, NULL); + ap_delete_legacy_prefs (); + /* The core autoprofile tracking system */ + purple_prefs_connect_callback (plugin_handle, "/core/savedstatus/current", + ap_status_changed, NULL); + purple_signal_connect (purple_connections_get_handle (), + "signed-on", plugin_handle, + PURPLE_CALLBACK (ap_account_connected), NULL); + accounts_pref = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/profile_accounts"); + ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (accounts_pref != NULL)); + free_string_list (accounts_pref); + ap_update_after_delay (AP_UPDATE_STATUS); + ap_update_after_delay (AP_UPDATE_PROFILE); + ap_update_queue_start (); +/* What to do when plugin is unloaded */ +static gboolean plugin_unload (PurplePlugin *plugin) + ap_update_queue_finish (); + ap_autoreply_finish (); + using_idleaway = FALSE; + ap_update_stop (AP_UPDATE_STATUS); + ap_update_stop (AP_UPDATE_PROFILE); + /* Disconnect tracking system */ + purple_signals_disconnect_by_handle (plugin); + ap_component_finish (); + g_hash_table_destroy (update_timeouts); +/* General information */ +static PurplePluginInfo info = + PURPLE_PLUGIN_STANDARD, /* type */ + PIDGIN_PLUGIN_TYPE, /* ui_requirement */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + N_("gtk-kluge-autoprofile"), /* id */ + N_("AutoProfile"), /* name */ + PP_VERSION, /* version */ + N_("User profile and status message content generator"),/* summary */ + N_("Allows user to place dynamic text into profiles\n" + "and status messages, with the text automatically\n" + "updated whenever content changes"), + N_("Casey Ho <casey at hkn-berkeley-edu>" + "\n\t\t\taim:caseyho"), + N_("http://autoprofile.sourceforge.net/"), /* homepage */ + plugin_load, /* load */ + plugin_unload, /* unload */ + &ui_info, /* ui_info */ +/*-------------------------------------------------------------------------- + *------------------------------------------------------------------------*/ +static gint get_max_size_status ( + const PurpleAccount *account, const PurpleStatusPrimitive type) { + case PURPLE_STATUS_AVAILABLE: return AP_SIZE_AVAILABLE_MAX; + case PURPLE_STATUS_AWAY: return AP_SIZE_AWAY_MAX; + default: return AP_SIZE_MAXIMUM; + id = purple_account_get_protocol_id (account); + case PURPLE_STATUS_AVAILABLE: + if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AVAILABLE_AIM; + else if (!strcmp (id, "prpl-msn")) return AP_SIZE_AVAILABLE_MSN; + else if (!strcmp (id, "prpl-yahoo")) return AP_SIZE_AVAILABLE_YAHOO; + else return AP_SIZE_AVAILABLE_MAX; + case PURPLE_STATUS_AWAY: + if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AWAY_AIM; + else return AP_SIZE_AWAY_MAX; + return AP_SIZE_MAXIMUM; +static const char *ap_savedstatus_get_message ( + const PurpleSavedStatus *status, const PurpleAccount *account) + const PurpleSavedStatusSub *substatus; + substatus = purple_savedstatus_get_substatus(status, account); + if (substatus != NULL) { + return purple_savedstatus_substatus_get_message (substatus); + return purple_savedstatus_get_message (status); +static PurpleStatusPrimitive ap_savedstatus_get_type ( + const PurpleSavedStatus *status, const PurpleAccount *account) + const PurpleSavedStatusSub *substatus; + substatus = purple_savedstatus_get_substatus(status, account); + if (substatus != NULL) { + return purple_status_type_get_primitive ( + purple_savedstatus_substatus_get_type (substatus)); + return purple_savedstatus_get_type (status); +gchar *ap_get_sample_status_message (PurpleAccount *account) + const PurpleSavedStatus *s; + PurpleStatusPrimitive type; + s = (using_idleaway? purple_savedstatus_get_idleaway () : + purple_savedstatus_get_current ()); + message = ap_savedstatus_get_message (s, account); + type = ap_savedstatus_get_type (s, account); + if (!message) return NULL; + return ap_generate (message, get_max_size_status (account, type)); +static gchar *ap_process_replacement (const gchar *f) { + w = ap_widget_find (f); + result = w->component->generate (w); + g_string_printf (s, "[%s]", f); + g_string_free (s, FALSE); +/* The workhorse generation function! */ +gchar *ap_generate (const gchar *f, gint max_length) { + gchar *format, *format_start, *percent_start; + output = g_string_new (""); + format_start = format = purple_utf8_salvage (f); + /* When a % has been read (and searching for next %), state is 1 + g_string_append_unichar (output, g_utf8_get_char ("[")); + g_string_append (output, percent_start); + percent_start = format = format+1; + } else if (*format == ']') { + replacement = ap_process_replacement (percent_start); + g_string_append (output, replacement); + format = g_utf8_next_char (format); + g_string_append (output, "<br>"); + } else if (*format == '[') { + percent_start = format+1; + g_string_append_unichar (output, g_utf8_get_char (format)); + format = g_utf8_next_char (format); + /* Deal with case where final ] not found */ + g_string_append_unichar (output, g_utf8_get_char ("[")); + g_string_append (output, percent_start); + g_string_truncate (output, max_length); + result = purple_utf8_salvage(output->str); + g_string_free (output, TRUE); +void ap_account_enable_profile (const PurpleAccount *account, gboolean enable) { + gboolean original_status; + gchar *username, *protocol_id; + original_status = ap_account_has_profile_enabled (account); + if (original_status == enable) { + ap_debug_warn ("profile", "New status identical to original, skipping"); + original = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/profile_accounts"); + username = strdup (purple_account_get_username (account)); + protocol_id = strdup (purple_account_get_protocol_id (account)); + /* Remove from the list */ + ap_debug ("profile", "Disabling profile updates for account"); + if (!strcmp (original->data, username) && + !strcmp (original->next->data, protocol_id)) { + original = original->next->next; + original = original->next->next; + node->next->next = ret; + GList *ret_start, *ret_end; + ap_debug ("profile", "enabling profile updates for account"); + ret_start = (GList *) malloc (sizeof (GList)); + ret_end = (GList *) malloc (sizeof (GList)); + ret_start->data = username; + ret_start->next = ret_end; + ret_end->data = protocol_id; + ret_end->next = original; + purple_prefs_set_string_list ( + "/plugins/gtk/autoprofile/profile_accounts", new); + ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (new != NULL)); + free_string_list (new); +gboolean ap_account_has_profile_enabled (const PurpleAccount *account) { + GList *accounts_list, *start_list; + accounts_list = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/profile_accounts"); + start_list = accounts_list; + /* Search through list of values */ + while (accounts_list) { + // Make sure these things come in pairs + if (accounts_list->next == NULL) { + ap_debug_error ("is_account_profile_enabled", "invalid account string"); + free_string_list (start_list); + if (!strcmp ((char *) accounts_list->data, account->username)) { + if (!strcmp ((char *) accounts_list->next->data, account->protocol_id)) + free_string_list (start_list); + accounts_list = accounts_list->next->next; + /* Not found, hence it wasn't enabled */ + free_string_list (start_list); +/* Profiles: Update every so often */ +static gboolean ap_update_profile () { + PurpleAccount *account; + const GList *purple_accounts; + gboolean account_updated; + char *generated_profile; + /* Generate the profile text */ + format = purple_prefs_get_string ("/plugins/gtk/autoprofile/profile"); + ap_debug_error ("general", "profile is null"); + generated_profile = ap_generate (format, AP_SIZE_PROFILE_MAX); + // If string is blank, nothing would happen + if (*generated_profile == '\0') { + free (generated_profile); + ap_debug_misc ("general", "empty profile set"); + generated_profile = strdup (" "); + /* Get all accounts and search through each */ + account_updated = FALSE; + for (purple_accounts = purple_accounts_get_all (); + purple_accounts != NULL; + purple_accounts = purple_accounts->next) { + account = (PurpleAccount *)purple_accounts->data; + old_info = purple_account_get_user_info (account); + /* Check to see if update option set on account */ + if (ap_account_has_profile_enabled (account) && + (old_info == NULL || strcmp (old_info, generated_profile))) { + purple_account_set_user_info (account, generated_profile); + account_updated = TRUE; + if (purple_account_is_connected (account)) { + g_static_mutex_lock (&update_queue_mutex); + if (g_list_find (queued_profiles, account) == NULL) { + queued_profiles = g_list_append (queued_profiles, account); + g_static_mutex_unlock (&update_queue_mutex); + ap_debug_misc ("general", "account not online, not setting profile"); + ap_gtk_add_message (AP_UPDATE_PROFILE, AP_MESSAGE_TYPE_PROFILE, + free (generated_profile); + return account_updated; +static gboolean ap_update_status () + const PurpleSavedStatus *template_status; + GHashTable *substatus_messages; + gchar *new_message, *new_substatus_message; + const gchar *sample_message, *old_message; + PurpleStatusPrimitive old_type, new_type; + const PurpleStatusType *substatus_type; + PurpleAccount *account; + PurpleSavedStatusSub *substatus; + template_status = (using_idleaway? purple_savedstatus_get_idleaway () : + purple_savedstatus_get_current ()); + /* If there are substatuses */ + if (purple_savedstatus_has_substatuses (template_status)) { + substatus_messages = g_hash_table_new (NULL, NULL); + for (accounts = purple_accounts_get_all (); + accounts = accounts->next) + account = (PurpleAccount *) accounts->data; + substatus = purple_savedstatus_get_substatus (template_status, account); + new_type = purple_status_type_get_primitive ( + purple_savedstatus_substatus_get_type (substatus)); + purple_savedstatus_substatus_get_message (substatus); + new_substatus_message = ap_generate (sample_message, + get_max_size_status (account, new_type)); + new_substatus_message = NULL; + g_hash_table_insert (substatus_messages, account, + new_substatus_message); + old_type = ap_savedstatus_get_type (current_ap_status, account); + ap_savedstatus_get_message (current_ap_status, account); + if ((old_type != new_type) || + ((old_message == NULL || new_substatus_message == NULL) && + (old_message != new_substatus_message)) || + (old_message != NULL && new_substatus_message != NULL && + strcmp (old_message, new_substatus_message))) + substatus_messages = NULL; + /* And then the generic main message */ + sample_message = purple_savedstatus_get_message (template_status); + new_message = ap_generate (sample_message, get_max_size_status (NULL, + purple_savedstatus_get_type (template_status))); + new_type = purple_savedstatus_get_type (template_status); + old_type = purple_savedstatus_get_type (current_ap_status); + old_message = purple_savedstatus_get_message (current_ap_status); + if ((old_type != new_type) || + ((old_message == NULL || new_message == NULL) && + (old_message != new_message)) || + (old_message != NULL && new_message != NULL && + strcmp (old_message, new_message))) + PurpleSavedStatus *new_status; + new_status = purple_savedstatus_new (NULL, new_type); + purple_savedstatus_set_message (new_status, new_message); + for (accounts = purple_accounts_get_all (); + accounts = accounts->next) { + account = (PurpleAccount *) accounts->data; + substatus = purple_savedstatus_get_substatus (template_status, account); + if (substatus != NULL) { + substatus_type = purple_savedstatus_substatus_get_type (substatus); + new_substatus_message = (gchar *) + g_hash_table_lookup (substatus_messages, account); + purple_savedstatus_set_substatus ( + new_status, account, substatus_type, new_substatus_message); + free (new_substatus_message); + purple_savedstatus_activate_for_account (new_status, account); + current_ap_status = new_status; + if (new_type == PURPLE_STATUS_AVAILABLE) type = AP_MESSAGE_TYPE_AVAILABLE; + else if (new_type == PURPLE_STATUS_AWAY) type = AP_MESSAGE_TYPE_AWAY; + else type = AP_MESSAGE_TYPE_STATUS; + ap_gtk_add_message (AP_UPDATE_STATUS, type, new_message); + if (new_message) free (new_message); + if (substatus_messages) { + g_hash_table_destroy (substatus_messages); +static gboolean ap_update_cb (gpointer data) { + g_static_mutex_lock (&update_timeout_mutex); + /* Start by removing timeout to self no matter what */ + timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts, data)); + if (timeout) purple_timeout_remove (timeout); + /* In future, check here if widget content has changed? */ + switch (GPOINTER_TO_INT (data)) { + result = ap_update_status (); + case AP_UPDATE_PROFILE: + result = ap_update_profile (); + ap_debug ("general", "Content hasn't changed, updating later"); + delay = AP_SCHEDULE_UPDATE_DELAY; + ap_debug ("general", "Content updated"); + purple_prefs_get_int ("/plugins/gtk/autoprofile/delay_update") * 1000; + timeout = purple_timeout_add (delay, ap_update_cb, data); + g_hash_table_insert (update_timeouts, data, GINT_TO_POINTER (timeout)); + g_static_mutex_unlock (&update_timeout_mutex); +void ap_update (APUpdateType type) + ap_update_cb (GINT_TO_POINTER (type)); +void ap_update_after_delay (APUpdateType type) + g_static_mutex_lock (&update_timeout_mutex); + timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts, + GINT_TO_POINTER (type))); + if (timeout) purple_timeout_remove (timeout); + timeout = purple_timeout_add (AP_SCHEDULE_UPDATE_DELAY, ap_update_cb, + GINT_TO_POINTER (type)); + g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type), + GINT_TO_POINTER (timeout)); + g_static_mutex_unlock (&update_timeout_mutex); +void ap_update_stop (APUpdateType type) + g_static_mutex_lock (&update_timeout_mutex); + timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts, + GINT_TO_POINTER (type))); + if (timeout) purple_timeout_remove (timeout); + g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type), 0); + g_static_mutex_unlock (&update_timeout_mutex); +static void ap_account_connected (PurpleConnection *gc) { + ap_debug ("general", "Account connection detected"); + ap_update_after_delay (AP_UPDATE_PROFILE); + ap_update_after_delay (AP_UPDATE_STATUS); +void ap_update_queueing () { + if (ap_is_currently_away ()) { + if (purple_prefs_get_bool( + "/plugins/gtk/autoprofile/queue_messages_when_away")) { + purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away"); + purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never"); +/* Called whenever current status is changed by Purple's status menu +static void ap_status_changed ( + const char *name, PurplePrefType type, gconstpointer val, gpointer data) { + ap_debug ("general", "Status change detected"); + using_idleaway = FALSE; + ap_update (AP_UPDATE_STATUS); +void ap_autoaway_enable () { + ap_debug ("idle", "Using idleaway"); + ap_update (AP_UPDATE_STATUS); +void ap_autoaway_disable () { + ap_debug ("idle", "Disabling idleaway"); + using_idleaway = FALSE; + ap_update (AP_UPDATE_STATUS); +gboolean ap_autoaway_in_use () { +static gboolean ap_update_queue (gpointer data) + PurpleAccount *account = NULL; + PurpleConnection *gc = NULL; + g_static_mutex_lock (&update_queue_mutex); + if (queued_profiles != NULL) { + account = (PurpleAccount *) queued_profiles->data; + queued_profiles = queued_profiles->next; + g_static_mutex_unlock (&update_queue_mutex); + gc = purple_account_get_connection (account); + serv_set_info (gc, purple_account_get_user_info (account)); +static void ap_update_queue_start () + update_queue_timeout = purple_timeout_add (2000, ap_update_queue, NULL); +static void ap_update_queue_finish () + purple_timeout_remove (update_queue_timeout); + update_queue_timeout = 0; +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +static void ap_delete_legacy_prefs () { + if (purple_prefs_exists ("/plugins/gtk/autoprofile/tab_number")) { + ap_debug ("general", "Deleting legacy preferences"); + purple_prefs_remove ("/plugins/gtk/autoprofile/components"); + purple_prefs_remove ("/plugins/gtk/autoprofile/tab_number"); + purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_away"); + purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_profile"); + purple_prefs_remove ("/plugins/gtk/autoprofile/accounts"); + purple_prefs_remove ("/plugins/gtk/autoprofile/message_titles"); + purple_prefs_remove ("/plugins/gtk/autoprofile/message_texts"); + purple_prefs_remove ("/plugins/gtk/autoprofile/default_profile"); + purple_prefs_remove ("/plugins/gtk/autoprofile/default_away"); + purple_prefs_remove ("/plugins/gtk/autoprofile/current_away"); + purple_prefs_remove ("/plugins/gtk/autoprofile/added_text"); + purple_prefs_remove ("/plugins/gtk/autoprofile/delay_profile"); + purple_prefs_remove ("/plugins/gtk/autoprofile/delay_away"); + purple_prefs_rename ("/plugins/gtk/autoprofile/text_respond", + "/plugins/gtk/autoprofile/autorespond/text"); + purple_prefs_rename ("/plugins/gtk/autoprofile/text_trigger", + "/plugins/gtk/autoprofile/autorespond/trigger"); + purple_prefs_rename ("/plugins/gtk/autoprofile/delay_respond", + "/plugins/gtk/autoprofile/autorespond/delay"); + purple_prefs_rename ("/plugins/gtk/autoprofile/use_trigger", + "/plugins/gtk/autoprofile/autorespond/enable"); +static void ap_init_preferences () { + ap_debug ("general", "Initializing preference defaults if necessary"); + /* Adding the folders */ + purple_prefs_add_none ("/plugins/gtk"); + purple_prefs_add_none ("/plugins/gtk/autoprofile"); + purple_prefs_add_none ("/plugins/gtk/autoprofile/widgets"); + purple_prefs_add_none ("/plugins/gtk/autoprofile/autorespond"); + /* Behavior-settings */ + purple_prefs_add_int ("/plugins/gtk/autoprofile/delay_update", 30); + purple_prefs_add_string ("/plugins/gtk/autoprofile/show_summary", "always"); + purple_prefs_add_bool ("/plugins/gtk/autoprofile/queue_messages_when_away", + purple_prefs_add_bool ("/plugins/gtk/autoprofile/away_when_idle", + purple_prefs_get_bool ("/core/away/away_when_idle")); + /* Auto-response settings */ + purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/auto_reply", + purple_prefs_get_string ("/core/away/auto_reply")); + purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/text", + _("Say the magic word if you want me to talk more!")); + purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/trigger", + purple_prefs_add_int ("/plugins/gtk/autoprofile/autorespond/delay", 2); + purple_prefs_add_bool ("/plugins/gtk/autoprofile/autorespond/enable", TRUE); + purple_prefs_add_string_list( + "/plugins/gtk/autoprofile/profile_accounts", NULL); + purple_prefs_add_string ("/plugins/gtk/autoprofile/profile", + _("Get AutoProfile for Purple at <a href=\"" + "http://autoprofile.sourceforge.net/\">" + "autoprofile.sourceforge.net</a><br><br>[Timestamp]")); +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +static void init_plugin (PurplePlugin *plugin) + ap_debug ("general", "Initializing AutoProfile"); + ap_init_preferences (); +PURPLE_INIT_PLUGIN (autoprofile, init_plugin, info) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/autoprofile.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,110 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +#define AP_SCHEDULE_UPDATE_DELAY 3000 +#define AP_GTK_MAX_MESSAGES 50 + AP_MESSAGE_TYPE_OTHER = -1, + AP_MESSAGE_TYPE_PROFILE, + AP_MESSAGE_TYPE_AVAILABLE, +/* Variable access functions */ +PurplePlugin *ap_get_plugin_handle (); +gboolean ap_is_currently_away (); +void ap_account_enable_profile (const PurpleAccount *, gboolean); +gboolean ap_account_has_profile_enabled (const PurpleAccount *); +/* Core behavior functions */ +gchar *ap_generate (const char *, gint); +gchar *ap_get_sample_status_message (PurpleAccount *account); +void ap_update (APUpdateType); +void ap_update_after_delay (APUpdateType); +void ap_update_stop (APUpdateType); +/* Queueing functions */ +void ap_update_queueing (); +/* Auto-away functions */ +void ap_autoaway_start (); +void ap_autoaway_finish (); +void ap_autoaway_touch (); +void ap_autoaway_enable (); +void ap_autoaway_disable (); +gboolean ap_autoaway_in_use (); +/* Auto-reply functions */ +void ap_autoreply_start (); +void ap_autoreply_finish (); +void ap_gtk_make_visible (); +void ap_gtk_add_message (APUpdateType, APMessageType, const gchar *); +void ap_gtk_set_progress_visible (APUpdateType, gboolean); +GList *actions (PurplePlugin *, gpointer); +void ap_actions_finish (); +PidginPluginUiInfo ui_info; +void ap_preferences_display (); +void ap_gtk_prefs_add_summary_option (GtkWidget *); +GtkWidget *get_account_page (); +#endif /* #ifndef AUTOPROFILE_H */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/autoreply.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,324 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "conversation.h" +#define SECS_BEFORE_RESENDING_AUTORESPONSE 600 +#define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married" +#define MILLISECS_BEFORE_PROCESSING_MSG 100 +static GSList *last_auto_responses = NULL; +struct last_auto_response { +static time_t response_timeout = 0; +/*--------------------------------------------------------------------------* + * Auto-response utility functions * + *--------------------------------------------------------------------------*/ +expire_last_auto_responses(gpointer data) + struct last_auto_response *lar; + tmp = last_auto_responses; + lar = (struct last_auto_response *)cur->data; + if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) { + last_auto_responses = g_slist_remove(last_auto_responses, lar); + return FALSE; /* do not run again */ +static struct last_auto_response * +get_last_auto_response(PurpleConnection *gc, const char *name) + struct last_auto_response *lar; + /* because we're modifying or creating a lar, schedule the + * function to expire them as the pref dictates */ + purple_timeout_add((SECS_BEFORE_RESENDING_AUTORESPONSE + 5) * 1000, + expire_last_auto_responses, NULL); + tmp = last_auto_responses; + lar = (struct last_auto_response *)tmp->data; + if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name))) + lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1); + g_snprintf(lar->name, sizeof(lar->name), "%s", name); + last_auto_responses = g_slist_append(last_auto_responses, lar); +/*--------------------------------------------------------------------------* + * Message send/receive general functionality * + *--------------------------------------------------------------------------*/ +/* Detecting sent message stuff */ +static void sent_im_msg_cb (PurpleAccount *account, const char *receiver, + PurplePresence *presence; + const gchar *auto_reply_pref; + gc = purple_account_get_connection (account); + presence = purple_account_get_presence (account); + * FIXME - If "only auto-reply when away & idle" is set, then shouldn't + * this only reset lar->sent if we're away AND idle? + purple_prefs_get_string ("/plugins/gtk/autoprofile/autorespond/auto_reply"); + if ((gc->flags & PURPLE_CONNECTION_AUTO_RESP) && + !purple_presence_is_available(presence) && + strcmp(auto_reply_pref, "never")) + struct last_auto_response *lar; + lar = get_last_auto_response(gc, receiver); + lar->sent = time(NULL); +/* Detecting received message stuff */ +struct received_im_msg { + PurpleAccount *account; +static gint process_received_im_msg (gpointer data) + struct received_im_msg *received_im; + PurpleAccount *account; + PurpleConversation *conv; + received_im = (struct received_im_msg *) data; + account = received_im->account; + sender = received_im->sender; + message = received_im->message; + gc = purple_account_get_connection (account); + /* search for conversation again in case it was created by other handlers */ + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + * Don't autorespond if: + * - it's not supported on this connection + * - or we're not idle and the 'only auto respond if idle' pref + if (gc->flags & PURPLE_CONNECTION_AUTO_RESP) + PurplePresence *presence; + PurpleStatusType *status_type; + PurpleStatusPrimitive primitive; + const gchar *auto_reply_pref; + auto_reply_pref = purple_prefs_get_string( + "/plugins/gtk/autoprofile/autorespond/auto_reply"); + presence = purple_account_get_presence(account); + status = purple_presence_get_active_status(presence); + status_type = purple_status_get_type(status); + primitive = purple_status_type_get_primitive(status_type); + if ((primitive == PURPLE_STATUS_AVAILABLE) || + (primitive == PURPLE_STATUS_INVISIBLE) || + (primitive == PURPLE_STATUS_MOBILE) || + !strcmp(auto_reply_pref, "never") || + (!purple_presence_is_idle(presence) && + !strcmp(auto_reply_pref, "awayidle"))) + away_msg = ap_get_sample_status_message (account); + if ((away_msg != NULL) && (*away_msg != '\0')) { + struct last_auto_response *lar; + gboolean autorespond_enable; + time_t now = time(NULL); + autorespond_enable = purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/autorespond/enable"); + * This used to be based on the conversation window. But um, if + * you went away, and someone sent you a message and got your + * auto-response, and then you closed the window, and then they + * sent you another one, they'd get the auto-response back too + * soon. Besides that, we need to keep track of this even if we've + * got a queue. So the rest of this block is just the auto-response, + lar = get_last_auto_response(gc, sender); + if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE) { + // Send basic autoresponse + serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP); + purple_conv_im_write (PURPLE_CONV_IM(conv), NULL, away_msg, + PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP, + // Send additional hint if enabled + if (autorespond_enable) { + const gchar *query = purple_prefs_get_string ( + "/plugins/gtk/autoprofile/autorespond/text"); + serv_send_im (gc, sender, query, PURPLE_MESSAGE_AUTO_RESP); + purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, query, + PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP, + } else if (autorespond_enable && + difftime (time(NULL), response_timeout) > + purple_prefs_get_int ("/plugins/gtk/autoprofile/autorespond/delay")) { + gchar *text = purple_markup_strip_html (message); + if (match_start (text, purple_prefs_get_string ( + "/plugins/gtk/autoprofile/autorespond/trigger")) == 1) { + serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP); + purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, away_msg, + PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP, + response_timeout = time (NULL); + ap_debug ("autorespond", "string matched, responding"); +static void received_im_msg_cb (PurpleAccount *account, char *sender, + char *message, PurpleConversation *conv, PurpleMessageFlags flags) + struct received_im_msg *received_im; + (struct received_im_msg *) malloc (sizeof (struct received_im_msg)); + received_im->account = account; + received_im->sender = strdup (sender); + received_im->message = strdup (message); + purple_timeout_add (MILLISECS_BEFORE_PROCESSING_MSG, process_received_im_msg, +static void auto_pref_cb ( + const char *name, PurplePrefType type, gconstpointer val, gpointer data) + if (!strcmp (purple_prefs_get_string ("/core/away/auto_reply"), "never")) + purple_notify_error (NULL, NULL, + N_("This preference is disabled"), + N_("This preference currently has no effect because AutoProfile is in " + "use. To modify this behavior, use the AutoProfile configuration " + purple_prefs_set_string ("/core/away/auto_reply", "never"); +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +void ap_autoreply_start () + purple_prefs_set_string ("/core/away/auto_reply", "never"); + purple_signal_connect (purple_conversations_get_handle (), "sent-im-msg", + ap_get_plugin_handle (), PURPLE_CALLBACK(sent_im_msg_cb), NULL); + purple_signal_connect (purple_conversations_get_handle (), "received-im-msg", + ap_get_plugin_handle (), PURPLE_CALLBACK(received_im_msg_cb), NULL); + pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (), + "/core/away/auto_reply", auto_pref_cb, NULL); +void ap_autoreply_finish () + // Assumes signals are disconnected globally + purple_prefs_disconnect_callback (pref_cb); + purple_prefs_set_string ("/core/away/auto_reply", purple_prefs_get_string ( + "/plugins/gtk/autoprofile/autorespond/auto_reply")); + while (last_auto_responses) { + tmp = last_auto_responses->next; + g_free (last_auto_responses->data); + g_slist_free_1 (last_auto_responses); + last_auto_responses = tmp; --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_countdownup.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,438 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +static GtkWidget *spin_secs; +static GtkWidget *spin_mins; +static GtkWidget *spin_hour; +static GtkWidget *spin_day; +static GtkWidget *spin_month; +static GtkWidget *spin_year; +/* Generate the time! */ +char *count_generate (struct widget *w) + double d_secs, d_mins, d_hours, d_days; + char *s_secs, *s_mins, *s_hours, *s_days; + ref_time = (struct tm *) malloc (sizeof (struct tm)); + ref_time->tm_sec = ap_prefs_get_int (w, "secs"); + ref_time->tm_min = ap_prefs_get_int (w, "mins"); + ref_time->tm_hour = ap_prefs_get_int (w, "hour"); + ref_time->tm_mday = ap_prefs_get_int (w, "day"); + ref_time->tm_mon = ap_prefs_get_int (w, "month") - 1; + ref_time->tm_year = ap_prefs_get_int (w, "year") - 1900; + ref_time->tm_isdst = -1; + if (ap_prefs_get_int (w, "down") == 1) + difference = difftime (mktime (ref_time), time(NULL)); + difference = difftime (time(NULL), mktime (ref_time)); + d_mins = floor (difference / 60); + d_secs = difference - (d_mins * 60); + d_hours = floor (d_mins / 60); + d_mins = d_mins - (d_hours * 60); + d_days = floor (d_hours / 24); + d_hours = d_hours - (d_days * 24); + result = (char *)malloc(sizeof (char) * AP_SIZE_MAXIMUM); + l = ap_prefs_get_int (w, "large"); + s = ap_prefs_get_int (w, "small"); + g_snprintf(result, AP_SIZE_MAXIMUM, + "%.0f days, %.0f hours, %.0f minutes, %.0f seconds", + d_days, d_hours, d_mins, d_secs); + d_hours = d_hours + (d_days * 24); + d_mins = d_mins + (d_hours * 60); + d_secs = d_secs + (d_mins * 60); + s_days = g_strdup ("day"); + s_days = g_strdup ("days"); + s_hours = g_strdup ("hour"); + s_hours = g_strdup ("hours"); + s_mins = g_strdup ("minute"); + s_mins = g_strdup ("minutes"); + s_secs = g_strdup ("second"); + s_secs = g_strdup ("seconds"); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s", + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s", + d_days, s_days, d_hours, s_hours); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s", + d_days, s_days, d_hours, s_hours, d_mins, s_mins); + g_snprintf (result, AP_SIZE_MAXIMUM, + "%.0f %s, %.0f %s, %.0f %s, %.0f %s", + d_days, s_days, d_hours, s_hours, d_mins, s_mins, d_secs, s_secs); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s", + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s", + d_hours, s_hours, d_mins, s_mins); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s", + d_hours, s_hours, d_mins, s_mins, d_secs, s_secs); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s", + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s", + d_mins, s_mins, d_secs, s_secs); + g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s", +static void update_year (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "year", value); +static void update_month (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "month", value); +static void update_day (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "day", value); +static void update_hour (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "hour", value); +static void update_mins (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "mins", value); +static void update_secs (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "secs", value); +static void set_to_current_time (GtkButton *button, struct widget *w) + ref_time = ap_localtime(&the_time); + ap_prefs_set_int (w, "year", ref_time->tm_year + 1900); + ap_prefs_set_int (w, "month", ref_time->tm_mon + 1); + ap_prefs_set_int (w, "day", ref_time->tm_mday); + ap_prefs_set_int (w, "hour", ref_time->tm_hour); + ap_prefs_set_int (w, "mins", ref_time->tm_min); + ap_prefs_set_int (w, "secs", ref_time->tm_sec); + if (spin_secs != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_secs), + ap_prefs_get_int (w, "secs")); + if (spin_mins != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_mins), + ap_prefs_get_int (w, "mins")); + if (spin_hour != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_hour), + ap_prefs_get_int (w, "hour")); + if (spin_day != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_day), + ap_prefs_get_int (w, "day")); + if (spin_month != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_month), + ap_prefs_get_int (w, "month")); + if (spin_year != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_year), + ap_prefs_get_int (w, "year")); +GtkWidget *count_menu (struct widget *w) + GtkWidget *vbox, *hbox, *big_hbox, *frame; + GtkWidget *label, *spinner, *dropbox, *button; + big_hbox = gtk_hbox_new (FALSE, 6); + frame = pidgin_make_frame (big_hbox, _("Start/end time")); + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (frame), vbox); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Year: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (1970, 2035, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "year")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_year), w); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Month: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (1, 12, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "month")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_month), w); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Day: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (1, 31, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "day")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_day), w); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Hour: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (0, 23, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "hour")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_hour), w); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Minutes: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (0, 59, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "mins")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_mins), w); + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Seconds: ")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0); + spinner = gtk_spin_button_new_with_range (0, 59, 1); + gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "secs")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_secs), w); + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Set to current time"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (set_to_current_time), w); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + frame = pidgin_make_frame (big_hbox, _("Which way")); + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (frame), vbox); + options = g_list_append (NULL, (char *) _("Count down to stop date")); + options = g_list_append (options, GINT_TO_POINTER(1)); + options = g_list_append (options, (char *) + _("Count time since start date")); + options = g_list_append (options, GINT_TO_POINTER(0)); + dropbox = ap_prefs_dropdown_from_list (w, vbox, NULL, + PURPLE_PREF_INT, "down", options); + options = g_list_append (NULL, (char *) _("Days")); + options = g_list_append (options, GINT_TO_POINTER(3)); + options = g_list_append (options, (char *) _("Hours")); + options = g_list_append (options, GINT_TO_POINTER(2)); + options = g_list_append (options, (char *) _("Minutes")); + options = g_list_append (options, GINT_TO_POINTER(1)); + options = g_list_append (options, (char *) _("Seconds")); + options = g_list_append (options, GINT_TO_POINTER(0)); + dropbox = ap_prefs_dropdown_from_list (w, vbox, + _("Largest units displayed"), PURPLE_PREF_INT, "large", options); + dropbox = ap_prefs_dropdown_from_list (w, vbox, + _("Smallest units displayed"), PURPLE_PREF_INT, "small", options); +void count_init (struct widget *w) { + ref_time = ap_localtime(&the_time); + ap_prefs_add_int (w, "down", 1); + ap_prefs_add_int (w, "small", 0); + ap_prefs_add_int (w, "large", 3); + ap_prefs_add_int (w, "year", + ref_time->tm_year + 1900); + ap_prefs_add_int (w, "month", + ap_prefs_add_int (w, "day", + ap_prefs_add_int (w, "hour", + ap_prefs_add_int (w, "mins", + ap_prefs_add_int (w, "secs", +struct component count = + N_("Given a date, shows amount of time until it (or since it)"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_executable.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,169 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +/*---------- EXECUTABLE: STDOUT from a program ----------*/ +static GtkWidget *file_selector; +static GtkWidget *file_entry; +/* Read file into string and return */ +char *executable_generate (struct widget *w) + char *text, *text_start; + max = ap_prefs_get_int (w, "max_size"); + exec = g_spawn_command_line_sync (ap_prefs_get_string (w, "command"), + &text_start, NULL, NULL, &return_error); + ap_debug ("executable", "command failed to execute"); + return g_strdup (_("[ERROR: command failed to execute]")); + if (strlen (text_start) < max) + text = text_start + strlen(text_start); + text = text_start + max; + /* Should back off only if the last item is newline */ + /* Gets rid of the extra <BR> in output */ +void executable_filename (GtkWidget *widget, gpointer user_data) { + const gchar *selected_filename; + selected_filename = gtk_file_selection_get_filename ( + GTK_FILE_SELECTION (file_selector)); + ap_prefs_set_string ((struct widget *) user_data, "command", + gtk_entry_set_text (GTK_ENTRY (file_entry), selected_filename); +/* Creates and pops up file selection dialog for fortune file */ +void executable_selection (GtkWidget *widget, struct widget *w) { + /* Create the selector */ + file_selector = gtk_file_selection_new ( + "Select the location of the program"); + cur_file = ap_prefs_get_string (w, "command"); + if (strlen (cur_file) > 1) { + gtk_file_selection_set_filename ( + GTK_FILE_SELECTION (file_selector), cur_file); + g_signal_connect (GTK_OBJECT( + GTK_FILE_SELECTION(file_selector)->ok_button), + "clicked", G_CALLBACK (executable_filename), w); + /* Destroy dialog box when the user clicks button. */ + g_signal_connect_swapped (GTK_OBJECT( + GTK_FILE_SELECTION(file_selector)->ok_button), + "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); + g_signal_connect_swapped (GTK_OBJECT ( + GTK_FILE_SELECTION (file_selector)->cancel_button), + "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); + gtk_widget_show (file_selector); +static gboolean executable_update (GtkWidget *widget, GdkEventFocus *evt, + ap_prefs_set_string ((struct widget *) data, "command", + gtk_entry_get_text (GTK_ENTRY (file_entry))); +GtkWidget *executable_menu (struct widget *w) + GtkWidget *ret = gtk_vbox_new (FALSE, 5); + GtkWidget *hbox, *label, *button; + label = gtk_label_new ( + _("Specify the command line you wish to execute")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + /* Text entry to type in program name */ + file_entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), file_entry, FALSE, FALSE, 0); + gtk_entry_set_text (GTK_ENTRY (file_entry), + ap_prefs_get_string (w, "command")); + g_signal_connect (G_OBJECT (file_entry), "focus-out-event", + G_CALLBACK (executable_update), w); + /* Button to bring up file select dialog */ + button = gtk_button_new_with_label ("Browse for program"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (executable_selection), w); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + ap_prefs_labeled_spin_button (w, ret, + _("Max characters to read from output: "), "max_size", + 1, AP_SIZE_MAXIMUM, NULL); +void executable_init (struct widget *w) { + ap_prefs_add_string (w, "command", "date"); + ap_prefs_add_int (w, "max_size", 1000); +struct component executable = + N_("Reproduces standard output of running a program on the command line"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_http.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,204 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +static GHashTable *refresh_timeouts = NULL; +/*---------- HTTP: HTTP requested Data ----------*/ +static void http_response (PurpleUtilFetchUrlData *reuqest_data, gpointer data, const char *c, gsize len, const gchar *error_message) + w = (struct widget *) data; + ap_prefs_set_string (w, "http_data", + _("[AutoProfile error: Invalid URL or no internet connection]")); + w = (struct widget *) data; + ap_prefs_set_string (w, "http_data", c); +static char* http_generate (struct widget *w) + const char *result, *url; + url = ap_prefs_get_string (w, "http_url"); + if (!url || url[0] == '\0') { + return g_strdup (_("[AutoProfile error: No URL specified]")); + result = ap_prefs_get_string (w, "http_data"); + if (result == NULL) return g_strdup (""); + return g_strdup (result); +static gboolean http_refresh_update (gpointer user_data) + w = (struct widget *) user_data; + http_url = g_strdup (ap_prefs_get_string (w, "http_url")); + if( http_url && (http_url[0] != '\0') ) { + purple_util_fetch_url(http_url, TRUE, NULL, FALSE, http_response, w); + ap_prefs_set_string (w, "http_data", ""); +static void http_load (struct widget *w) + gpointer http_refresh_timeout; + if (refresh_timeouts == NULL) { + refresh_timeouts = g_hash_table_new (NULL, NULL); + http_refresh_update (w); + http_refresh_timeout = GINT_TO_POINTER (g_timeout_add ( + ap_prefs_get_int (w, "http_refresh_mins") * 60 * 1000, + http_refresh_update, w)); + g_hash_table_insert (refresh_timeouts, w, http_refresh_timeout); +static void http_unload (struct widget *w) + gpointer http_refresh_timeout; + http_refresh_timeout = g_hash_table_lookup (refresh_timeouts, w); + g_source_remove (GPOINTER_TO_INT (http_refresh_timeout)); + g_hash_table_remove (refresh_timeouts, w); +static void http_init (struct widget *w) + ap_prefs_add_string (w, "http_url", ""); + ap_prefs_add_string (w, "http_data", ""); + ap_prefs_add_int (w, "http_refresh_mins", 1); +static gboolean http_url_update (GtkWidget *widget, GdkEventFocus *evt, + struct widget *w = (struct widget *) data; + ap_prefs_set_string (w, "http_url", + gtk_entry_get_text (GTK_ENTRY (widget))); +static gboolean http_refresh_mins_update (GtkWidget *widget, gpointer data) + minutes = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + w = (struct widget *) data; + ap_prefs_set_int (w, "http_refresh_mins", minutes); + // Kill the current timer and run a new one + timeout = g_hash_table_lookup (refresh_timeouts, w); + g_source_remove (GPOINTER_TO_INT(timeout)); + timeout = GINT_TO_POINTER (g_timeout_add (minutes * 60 * 1000, + http_refresh_update, w)); + g_hash_table_replace (refresh_timeouts, w, timeout); +static void http_data_update (GtkWidget *w, gpointer data) { + http_refresh_update (data); +static GtkWidget *http_menu (struct widget *w) + GtkWidget *ret = gtk_vbox_new (FALSE, 5); + GtkWidget *label, *hbox, *button, *spinner; + GtkWidget *http_url_entry; + label = gtk_label_new (_("Select URL with source content")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + http_url_entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), http_url_entry, TRUE, TRUE, 0); + gtk_entry_set_text (GTK_ENTRY (http_url_entry), + ap_prefs_get_string (w, "http_url")); + g_signal_connect (G_OBJECT (http_url_entry), "focus-out-event", + G_CALLBACK (http_url_update), w); + button = gtk_button_new_with_label (_("Fetch page now!")); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (http_data_update), w); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Delay")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + spinner = gtk_spin_button_new_with_range (1, 60, 1); + gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "http_refresh_mins")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (http_refresh_mins_update), w); + label = gtk_label_new (_("minutes between page fetches")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + N_("Data fetched from an internet URL using HTTP"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_logstats.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,1042 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "conversation.h" +#include "comp_logstats.h" +struct conversation_time { +/* Represents data about a particular 24 hour period in the logs */ + int month; // The month + int received_msgs; // # msgs received + int received_words; // # words received + int sent_msgs; // # msgs sent + int sent_words; // # words sent + GSList *conversation_times; // List of conversation_time pointers +/* List of struct log_dates + This is SORTED by most recent first */ +static GSList *dates = NULL; +/* Hashtable of log_dates */ +static GHashTable *dates_table = NULL; +/* Is the current line part of a message sent or received? */ +static gboolean receiving = FALSE; +static char *cur_receiver = NULL; +static char *cur_sender = NULL; +/* Implements GCompareFunc */ +static gint conversation_time_compare (gconstpointer x, gconstpointer y) { + const struct conversation_time *a = x; + const struct conversation_time *b = y; + if (difftime (*(a->start_time), *(b->start_time)) == 0.0) { + if (!strcmp (a->name, b->name)) +/* Implements GCompareFunc */ +static gint log_date_compare (gconstpointer x, gconstpointer y) + const struct log_date *a = y; + const struct log_date *b = x; + if (a->year == b->year) { + if (a->month == b->month) { + return a->day - b->day; + return a->month - b->month; + return a->year - b->year; +/* Implements GHashFunc */ +static guint log_date_hash (gconstpointer key) + const struct log_date *d = key; + return ((d->year * 365) + (d->month * 12) + (d->day)); +/* Implements GEqualFunc */ +static gboolean log_date_equal (gconstpointer x, gconstpointer y) + const struct log_date *a = y; + const struct log_date *b = x; + if (a->year == b->year && + a->month == b->month && +/* Returns the struct log_date associated with a particular date. + Will MODIFY list of dates and insert sorted if not yet created */ +static struct log_date *get_date (int year, int month, int day) + struct log_date *cur_date; + cur_date = (struct log_date *)malloc(sizeof(struct log_date)); + cur_date->month = month; + if ((node = g_hash_table_lookup (dates_table, cur_date))) { + return (struct log_date *)node; + g_hash_table_insert (dates_table, cur_date, cur_date); + cur_date->received_msgs = 0; + cur_date->received_words = 0; + cur_date->sent_msgs = 0; + cur_date->sent_words = 0; + cur_date->conversation_times = NULL; +/* Like get_date, except specific to the current date */ +static struct log_date *get_today () + cur_time = localtime (&the_time); + return get_date (cur_time->tm_year, cur_time->tm_mon, cur_time->tm_mday); +static int string_word_count (const char *line) + /* If state is 1, currently processing a word */ +/* Figure out if a person is yourself or someone else */ +static gboolean is_self (PurpleAccount *a, const char *name) { + GList *accounts, *aliases, *aliases_start; + PurpleAccount *account; + const char *normalized_alias; + if (cur_sender && !strcmp (cur_sender, name)) { + if (cur_receiver && !strcmp (cur_receiver, name)) { + normalized = strdup (purple_normalize (a, name)); + accounts = purple_accounts_get_all (); + aliases_start = aliases = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases"); + normalized_alias = purple_normalize (a, (char *)aliases->data); + if (!strcmp (normalized, normalized_alias)) { + free_string_list (aliases_start); + cur_sender = strdup (name); + aliases = aliases->next; + free_string_list (aliases_start); + account = (PurpleAccount *)accounts->data; + if (!strcmp (normalized, purple_account_get_username (account))) { + cur_sender = strdup (name); + accounts = accounts->next; + cur_receiver = strdup (name); +/* Parses a line of a conversation */ +static void parse_line (PurpleLog *cur_log, char *l, struct log_date *d) + char *cur_line, *cur_line_start; + if (strlen (line) > 14 && *line == ' ') + if (strlen (line) > 13 && + isdigit (*(line + 1)) && + isdigit (*(line + 2)) && + isdigit (*(line + 4)) && + isdigit (*(line + 5)) && + isdigit (*(line + 7)) && + isdigit (*(line + 8)) && + isspace (*(line + 10))) { + cur_line_start = cur_line = line + 11; + if (*cur_line == ':') { + receiving = !is_self (cur_log->account, name); + d->received_words += string_word_count (message); + d->sent_words += string_word_count (message); + d->received_words += string_word_count (line); + d->sent_words += string_word_count (line); +/* Parses a conversation if hasn't been handled yet */ +static void parse_log (PurpleLog *cur_log) + struct log_date *the_date; + struct conversation_time *conv_time; + PurpleLogReadFlags flags; + char *content, *cur_content, *cur_content_start, *temp; + the_time = localtime (&(cur_log->time)); + the_date = get_date (the_time->tm_year, the_time->tm_mon, the_time->tm_mday); + /* Check for old log and if no conflicts, add to list */ + conv_time = (struct conversation_time *)malloc ( + sizeof (struct conversation_time)); + conv_time->start_time = (time_t *)malloc (sizeof(time_t)); + *(conv_time->start_time) = cur_log->time; + conv_time->name = strdup (cur_log->name); + if (g_slist_find_custom (the_date->conversation_times, conv_time, + conversation_time_compare)) { + /* We already processed this! Halt! */ + free (conv_time->start_time); + free (conv_time->name); + the_date->conversation_times = g_slist_prepend (the_date->conversation_times, + /* Start rolling the counters! */ + temp = purple_log_read (cur_log, &flags); + if (!strcmp ("html", cur_log->logger->id)) { + content = purple_markup_strip_html (temp); + cur_content_start = cur_content = content; + /* Splits the conversation into lines (each line may not necessarily + be a seperate message */ + if (*cur_content == '\n') { + parse_line (cur_log, cur_content_start, the_date); + cur_content_start = cur_content + 1; + parse_line (cur_log, cur_content_start, the_date); +/* Get names of users in logs */ +static GList *logstats_get_names (PurpleLogType type, PurpleAccount *account) + if (type == PURPLE_LOG_CHAT) + me = g_strdup_printf ("%s.chat", purple_normalize(account, + purple_account_get_username(account))); + me = g_strdup (purple_normalize(account, + purple_account_get_username(account))); + /* Get the old logger names */ + path = g_build_filename(purple_user_dir(), "logs", NULL); + if (!(dir = g_dir_open(path, 0, NULL))) { + while ((filename = g_dir_read_name (dir))) { + if (purple_str_has_suffix (filename, ".log")) { + tmp = strdup (filename); + *(tmp + strlen (filename) - 4) = '\0'; + if (!string_list_find (ret, tmp)) + ret = g_list_prepend (ret, strdup (tmp)); + /* Get the account-specific names */ + prpl = PURPLE_PLUGIN_PROTOCOL_INFO + (purple_find_prpl (purple_account_get_protocol_id(account)))->list_icon( + path = g_build_filename(purple_user_dir(), "logs", prpl, me, NULL); + if (!(dir = g_dir_open(path, 0, NULL))) { + while ((filename = g_dir_read_name (dir))) { + if (!string_list_find (ret, filename)) + ret = g_list_prepend (ret, strdup (filename)); +/* On load, reads in all logs and initializes stats database */ +static void logstats_read_logs () + GList *accounts, *logs, *logs_start, *names, *names_start; + accounts = purple_accounts_get_all(); + ap_debug ("logstats", "parsing log files"); + names_start = names = logstats_get_names (PURPLE_LOG_IM, + (PurpleAccount *)accounts->data); + logs_start = purple_log_get_logs (PURPLE_LOG_IM, (char *)names->data, + (PurpleAccount *)accounts->data); + cur_log = (PurpleLog *)logs->data; + purple_log_free (cur_log); + g_list_free (logs_start); + free_string_list (names_start); + accounts = accounts->next; + ap_debug ("logstats", "finished parsing log files"); +static void add_element (gpointer key, gpointer value, gpointer data) + dates = g_slist_insert_sorted (dates, value, log_date_compare); +/* Updates GList against hashtable */ +static void logstats_update_dates () + g_hash_table_foreach (dates_table, add_element, NULL); +/*--------------------- Total calculations -------------------*/ +static int get_total (const char *field) + d = (struct log_date *)cur_day->data; + if (!strcmp (field, "received_msgs")) { + count += d->received_msgs; + } else if (!strcmp (field, "received_words")) { + count += d->received_words; + } else if (!strcmp (field, "sent_msgs")) { + } else if (!strcmp (field, "sent_words")) { + count += d->sent_words; + } else if (!strcmp (field, "num_convos")) { + count += g_slist_length (d->conversation_times); + cur_day = cur_day->next; +static int get_recent_total (const char *field, int hours) + d = (struct log_date *)cur_day->data; + cur_day_time = purple_time_build (d->year + 1900, d->month + 1, d->day, + if (difftime (time (NULL), cur_day_time) > (double) hours * 60.0 * 60.0) + if (!strcmp (field, "received_msgs")) { + count += d->received_msgs; + } else if (!strcmp (field, "sent_msgs")) { + } else if (!strcmp (field, "num_convos")) { + count += g_slist_length (d->conversation_times); + cur_day = cur_day->next; +static int num_days_since_start () + first_day = g_slist_last (dates); + d = (struct log_date *)first_day->data; + difference = difftime ( + time (NULL), purple_time_build (d->year + 1900, d->month + 1, d->day, + return (int) difference / (60.0 * 60.0 * 24.0); +static struct log_date *get_max_date (const char *field) + struct log_date *max_date, *cur_date; + int max_so_far, cur_max; + cur_date = (struct log_date *)cur_day->data; + if (!strcmp (field, "conversations")) { + cur_max = g_slist_length (cur_date->conversation_times); + } else if (!strcmp (field, "received")) { + cur_max = cur_date->received_msgs; + } else if (!strcmp (field, "sent")) { + cur_max = cur_date->sent_msgs; + } else if (!strcmp (field, "total")) { + cur_max = cur_date->sent_msgs + cur_date->received_msgs; + if (cur_max >= max_so_far) { + cur_day = cur_day->next; +static char *date_string (const char *field) + last_day = g_slist_last (dates); + if (!strcmp (field, "first")) { + d = (struct log_date *) last_day->data; + d = get_max_date (field); + output = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM); + t_struct = (struct tm *)malloc(sizeof(struct tm)); + t_struct->tm_year = d->year; + t_struct->tm_mon = d->month; + t_struct->tm_mday = d->day; + t_struct = localtime (&t); + strftime (output, AP_SIZE_MAXIMUM - 1, "%a %b %d, %Y", t_struct); +static int get_max (const char *field) + struct log_date *max_date = get_max_date (field); + if (!strcmp (field, "conversations")) { + return g_slist_length (max_date->conversation_times); + } else if (!strcmp (field, "received")) { + return max_date->received_msgs; + } else if (!strcmp (field, "sent")) { + return max_date->sent_msgs; + } else if (!strcmp (field, "total")) { + return max_date->sent_msgs + max_date->received_msgs; + ap_debug ("logstats", "get-max: invalid paramater"); +/*--------------------- Signal handlers ----------------------*/ +static void logstats_received_im (PurpleAccount *account, char *sender, + char *message, int flags) + struct log_date *the_date; + the_date = get_today (); + the_date->received_msgs++; + the_date->received_words += string_word_count (message); +static void logstats_sent_im (PurpleAccount *account, const char *receiver, + struct log_date *the_date; + the_date = get_today (); + the_date->sent_words += string_word_count (message); +static void logstats_conv_created (PurpleConversation *conv) + struct log_date *the_date; + struct conversation_time *the_time; + if (conv->type == PURPLE_CONV_TYPE_IM) { + the_time = malloc (sizeof(struct conversation_time)); + the_time->name = strdup (conv->name); + the_time->start_time = malloc (sizeof(time_t)); + time (the_time->start_time); + the_date = get_today (); + the_date->conversation_times = g_slist_prepend ( + the_date->conversation_times, the_time); + logstats_update_dates (); +/*--------------------------- Main functions -------------------------*/ +void logstats_load (struct widget *w) + if (!purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")) { + /* Initialize database */ + dates_table = g_hash_table_new (log_date_hash, log_date_equal); + logstats_update_dates (); + msg = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM); + count = get_total ("received_msgs"); + g_snprintf (msg, AP_SIZE_MAXIMUM, "received msg total is %d", count); + ap_debug ("logstats", msg); + count = get_total ("sent_msgs"); + g_snprintf (msg, AP_SIZE_MAXIMUM, "sent msg total is %d", count); + ap_debug ("logstats", msg); + count = get_total ("received_words"); + g_snprintf (msg, AP_SIZE_MAXIMUM, "received word total is %d", count); + ap_debug ("logstats", msg); + count = get_total ("sent_words"); + g_snprintf (msg, AP_SIZE_MAXIMUM, "sent word total is %d", count); + ap_debug ("logstats", msg); + count = get_total ("num_convos"); + g_snprintf (msg, AP_SIZE_MAXIMUM, "num conversations is %d", count); + ap_debug ("logstats", msg); + count = g_slist_length (dates); + g_snprintf (msg, AP_SIZE_MAXIMUM, "num days with conversations is %d", count); + ap_debug ("logstats", msg); + purple_signal_connect (purple_conversations_get_handle (), + "received-im-msg", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_received_im), NULL); + purple_signal_connect (purple_conversations_get_handle (), + "sent-im-msg", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_sent_im), NULL); + purple_signal_connect (purple_conversations_get_handle (), + "conversation-created", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_conv_created), NULL); +void logstats_unload (struct widget *w) + struct log_date *cur_date; + struct conversation_time *cur_time; + if (!purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")) { + /* Disconnect signals */ + purple_signal_disconnect (purple_conversations_get_handle (), + "received-im-msg", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_received_im)); + purple_signal_disconnect (purple_conversations_get_handle (), + "sent-im-msg", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_sent_im)); + purple_signal_disconnect (purple_conversations_get_handle (), + "conversation-created", ap_get_plugin_handle (), + PURPLE_CALLBACK (logstats_conv_created)); + logstats_update_dates (); + /* Free all the memory */ + cur_date = (struct log_date *)dates->data; + while (cur_date->conversation_times) { + temp = cur_date->conversation_times; + cur_time = (struct conversation_time *)temp->data; + cur_date->conversation_times = temp->next; + free (cur_time->start_time); + g_hash_table_destroy (dates_table); +/* Generate the output */ +static char *logstats_generate (struct widget *w) + char *buf, *output, *date; + if (!purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")) { + format = purple_prefs_get_string ( + "/plugins/gtk/autoprofile/components/logstat/format"); + output = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM); + buf = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_msgs")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_words")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_msgs")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_words")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + get_total ("sent_msgs") + get_total ("received_msgs")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + get_total ("sent_words") + get_total ("received_words")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + num_days_since_start ()); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("num_convos")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("num_convos") / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("conversations")); + date = date_string ("conversations"); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("sent")); + date = date_string ("sent"); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("received")); + date = date_string ("received"); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("total")); + date = date_string ("total"); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date); + date = date_string ("first"); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("received_words") / (double) get_total ("received_msgs")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("sent_words") / (double) get_total ("sent_msgs")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) (get_total ("received_words") + get_total ("sent_words")) / (double) (get_total("received_msgs") + get_total ("sent_msgs"))); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("received_msgs") / (double) get_total ("num_convos")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("sent_msgs") / (double) get_total ("num_convos")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) get_total("num_convos")); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("received_words") / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("sent_words") / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + ((double) get_total ("received_words") + (double) get_total ("sent_words")) / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("received_msgs") / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) get_total ("sent_msgs") / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output, + (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) g_slist_length (dates)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.1f", output, + 100.0 * (double) g_slist_length (dates) / (double) num_days_since_start ()); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + ((struct log_date *) dates->data)->received_msgs); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + ((struct log_date *) dates->data)->sent_msgs); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + g_slist_length (((struct log_date *) dates->data)->conversation_times)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + ((struct log_date *) dates->data)->sent_msgs + ((struct log_date *) dates->data)->received_msgs); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("received_msgs", 24 * 7)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("sent_msgs", 24 * 7)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("num_convos", 24 * 7)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, + get_recent_total ("received_msgs", 24 * 7) + get_recent_total ("received_msgs", 24 * 7)); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format); + g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format); +/* Initialize preferences */ +static void logstats_init (struct widget *w) + purple_prefs_add_none ("/plugins/gtk/autoprofile/components/logstat"); + purple_prefs_add_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE); + purple_prefs_add_string ( + "/plugins/gtk/autoprofile/components/logstat/format", ""); + purple_prefs_add_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases", NULL); +/* The heart of the component */ +static char *identifiers [7] = { +struct component logstats = + N_("Purple log statistics"), + N_("Display various statistics about your message and system logs"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_logstats.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,28 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +void logstats_load (struct widget *w); +void logstats_unload (struct widget *w); +GtkWidget *logstats_prefs (struct widget *w); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_logstats_gtk.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,355 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "comp_logstats.h" +GtkWidget *checkbox = NULL; +GtkListStore *alias_list = NULL; +GtkWidget *alias_view = NULL; +/* General callbacks from main preferences */ +static void logstats_response_cb (GtkDialog *dialog, gint id, + purple_prefs_set_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled", TRUE); + gtk_widget_set_sensitive (widget, TRUE); + gtk_widget_destroy (GTK_WIDGET(dialog)); +static void toggle_enable (GtkButton *button, gpointer data) + GtkWidget *popup, *vbox, *label; + if (purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")) { + logstats_unload (NULL); + purple_prefs_set_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE); + gtk_widget_set_sensitive (vbox, FALSE); + popup = gtk_dialog_new_with_buttons ( + "Enable stats for logs", NULL, 0, + GTK_STOCK_OK, 42, NULL); + g_signal_connect (G_OBJECT(popup), "response", + G_CALLBACK(logstats_response_cb), vbox); + label = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(label), + "\nEnabling this component will have some minor side effects. Doing so " + "will cause Purple to take slightly longer to start up because it must " + "parse a large amount of data to gather statistics. On average, this " + "can take slightly over a second for every 100,000 messages in your " + "logs.\n\nThe time from when you press the OK button to the time " + "when this dialog vanishes is a good approximation of how much extra " + "time will elapse before the login screen is shown.\n" + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->vbox), label, + gtk_widget_show_all (popup); +static gboolean logstat_format (GtkWidget *widget, GdkEventFocus *event, + purple_prefs_set_string ( + "/plugins/gtk/autoprofile/components/logstat/format", + gtk_entry_get_text (GTK_ENTRY (widget))); +static void new_alias (gpointer data, PurpleRequestFields *fields) + alias = purple_request_fields_get_string (fields, "alias"); + aliases = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases"); + aliases = g_list_append (aliases, strdup (alias)); + purple_prefs_set_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases", aliases); + free_string_list (aliases); + gtk_list_store_insert (alias_list, &iter, 0); + gtk_list_store_set (alias_list, &iter, +static void alias_add (GtkButton *button, gpointer data) + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + field = purple_request_field_string_new("alias", _("Alias"), + purple_request_field_set_required(field, TRUE); + purple_request_field_set_type_hint(field, "alias"); + purple_request_field_group_add_field(group, field); + purple_request_fields(purple_get_blist(), _("Add Alias"), + _("Type in the alias that you use"), + _("OK"), G_CALLBACK(new_alias), + NULL, NULL, NULL, NULL); +static void alias_delete (GtkButton *button, gpointer data) + GtkTreeSelection *selection; + GList *aliases, *aliases_start, *new_aliases; + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (alias_view)); + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + gtk_tree_model_get (GTK_TREE_MODEL (alias_list), &iter, + gtk_list_store_remove (alias_list, &iter); + aliases = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases"); + aliases_start = aliases; + if (strcmp ((char *)aliases->data, alias)) { + new_aliases = g_list_append (new_aliases, aliases->data); + aliases = aliases->next; + purple_prefs_set_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases", new_aliases); + free_string_list (aliases_start); + g_list_free (new_aliases); +static void alias_what (GtkButton *button, gpointer data) + purple_notify_formatted (NULL, _("Aliases"), _("What this list is for"), NULL, + _("Logs in Purple are stored verbatim with what you see on the screen. " + "The names of the people in the conversation (both yourself and your " + "buddy) are shown with their given aliases as opposed to actual screen " + "names. If you have given yourself an alias in a conversation, list " + "it using this dialog. If you do not, messages written by you will " + "be incorrectly identified as received instead of sent.<br><br>Correct " + "capitalization and whitespace are not required for detection to " + "work.<br><br>You must disable/re-enable log stats to refresh the " + "database after an alias change."), +GtkWidget *logstats_prefs (struct widget *w) + GtkWidget *ret, *vbox, *hbox; + GtkWidget *label, *button, *entry, *sw; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTreeViewColumn *col; + GList *aliases, *aliases_start; + ret = gtk_vbox_new (FALSE, 6); + /* Checkbox for enabling/disabling */ + checkbox = gtk_check_button_new_with_mnemonic ( + "Enable statistics for logs"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbox), + purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")); + gtk_box_pack_start (GTK_BOX(ret), checkbox, FALSE, FALSE, 0); + vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(ret), vbox, TRUE, TRUE, 0); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), "<b>Format string for output</b>"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); + entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX(vbox), entry, FALSE, FALSE, 0); + gtk_entry_set_max_length (GTK_ENTRY(entry), 1000); + gtk_entry_set_text (GTK_ENTRY(entry), purple_prefs_get_string ( + "/plugins/gtk/autoprofile/components/logstat/format")); + g_signal_connect (G_OBJECT (entry), "focus-out-event", + G_CALLBACK (logstat_format), NULL); + label = gtk_label_new (_( + "%R\tTotal messages received\n" + "%r\tTotal words received\n" + "%S\tTotal messages sent\n" + "%s\tTotal words sent\n" + "%T\tTotal messages sent/received\n" + "%t\tTotal words sent/received\n" + "%D\tNumber of days since first logged conversation\n" + "%d\tNumber of days with logged conversations\n" + "%N\tNumber of logged conversations\n" + "%n\tAverage number of conversations per day with logs\n" + "%i\tMost conversations in a single day\n" + "%I\tDate with most conversations\n" + "%j\tMost messages sent in a single day\n" + "%J\tDate with most messages sent\n" + "%k\tMost messages received in a single day\n" + "%K\tDate with most messages received\n" + "%l\tMost total messages sent/received in a single day\n" + "%L\tDate with most total messages sent/received\n" + "%f\tDate of first logged conversation\n" + "%u\tAverage words per message received\n" + "%v\tAverage words per message sent\n" + "%w\tAverage words per message sent/received\n" + "%U\tAverage messages received per conversation\n" + "%V\tAverage messages sent per conversation\n" + "%W\tAverage messages sent/received per conversation\n" + "%x\tAverage words received per day with logs\n" + "%y\tAverage words sent per day with logs\n" + "%z\tAverage words sent/received per day with logs\n" + "%X\tAverage messages received per day with logs\n" + "%Y\tAverage messages sent per day with logs\n" + "%Z\tAverage messages sent/received per day with logs\n" + "%p\tPercentage of days with logs\n" + "%a\tNumber of messages received today\n" + "%b\tNumber of messages sent today\n" + "%c\tNumber of conversations started today\n" + "%e\tNumber of messages sent/received today\n" + "%A\tNumber of messages received in last week\n" + "%B\tNumber of messages sent in last week\n" + "%C\tNumber of conversations started in last week\n" + "%E\tNumber of messages sent/received in last week\n" + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + sw = gtk_scrolled_window_new (NULL,NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE , 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), "<b>Personal aliases</b>"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), + "You need this if you have an alias for your own screen name,\n" + "else IM's you sent will be incorrectly counted as received"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 3); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label (_("Add alias")); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK (alias_add), NULL); + gtk_box_pack_start (GTK_BOX(hbox), button, TRUE, TRUE, 0); + button = gtk_button_new_with_label (_("Delete alias")); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK (alias_delete), NULL); + gtk_box_pack_start (GTK_BOX(hbox), button, TRUE, TRUE, 0); + button = gtk_button_new_with_label (_("?")); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK (alias_what), NULL); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + sw = gtk_scrolled_window_new (0, 0); + gtk_box_pack_start (GTK_BOX(vbox), sw, FALSE, FALSE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + alias_list = gtk_list_store_new (1, G_TYPE_STRING); + alias_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (alias_list)); + gtk_container_add (GTK_CONTAINER(sw), alias_view); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(alias_view), FALSE); + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (alias_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + col = gtk_tree_view_column_new_with_attributes ( + _("Alias"), renderer, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(alias_view), col); + aliases = purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/components/logstat/aliases"); + aliases_start = aliases; + gtk_list_store_append (alias_list, &iter); + gtk_list_store_set (alias_list, &iter, + 0, (char *)aliases->data, -1); + aliases = aliases->next; + free_string_list (aliases_start); + /* Finish up the checkbox stuff */ + g_signal_connect (G_OBJECT(checkbox), "clicked", + G_CALLBACK(toggle_enable), vbox); + if (!purple_prefs_get_bool ( + "/plugins/gtk/autoprofile/components/logstat/enabled")) { + gtk_widget_set_sensitive (vbox, FALSE); + gtk_widget_set_sensitive (vbox, TRUE); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_quotation.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,602 @@
+/*----------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *----------------------------------------------------------------------------*/ +#include "autoprofile.h" + QUOTATION_LIST_STORE = 1, + QUOTATION_FILE_SELECTOR, +/*--------------------------------------------------------------------------* + * Menu related things * + *--------------------------------------------------------------------------*/ +static void append_quote (struct widget *w, GtkListStore *ls, gchar *quote) + GtkTreeSelection *selection; + gtk_list_store_append (ls, &iter); + quote_tmp = purple_markup_strip_html (quote); + g_string_printf (s, "%ld bytes", g_utf8_strlen (quote, -1)); + gtk_list_store_set (ls, &iter, + g_string_free (s, TRUE); + treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW); + if (treeview == NULL) return; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview)); + gtk_tree_selection_select_iter (selection, &iter); +static void file_dialog_cb (GtkWidget *dialog, int response, struct widget *w) + GList *quotes, *quotes_start, *new_quotes; + case GTK_RESPONSE_ACCEPT: + ls = ap_widget_get_data (w, QUOTATION_LIST_STORE); + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); + checkbox = gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER(dialog)); + g_object_get (checkbox, "active", &include_html, NULL); + quotes = ap_prefs_get_string_list (w, "quotes"); + new_quotes = read_fortune_file (filename, !include_html); + quotes = g_list_concat (quotes, new_quotes); + ap_prefs_set_string_list (w, "quotes", quotes); + for (quotes = new_quotes; quotes != NULL; quotes = quotes->next) { + append_quote (w, ls, quotes->data); + free_string_list (quotes_start); + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + ap_widget_set_data (w, QUOTATION_FILE_SELECTOR, NULL); + gtk_widget_destroy (dialog); +static void quotation_explain_fortune_file (GtkMenuItem *item, gpointer data) + purple_notify_formatted (NULL, _("Fortune files"), + _("A quick definition of a fortune file"), NULL, + _("A fortune file is a simple text file with a number of quotes. " + "The following is an example:<br><br>" + "<b>\"Glory is fleeing, but obscurity is forver.\"<br>" + "- Napoleon Bonaparte (1769-1821)<br>" + "Yet another quote<br>" + "Quotes can have any sort of text within them. They end when there " + "is a newline followed by a percent sign \"%\" on the next line.<br>" + "<br>Fortune files with pre-selected quotes can be found on the" +static void quotation_select_import_file (GtkMenuItem *item, struct widget *w) + dialog = gtk_file_chooser_dialog_new ( + _("Select fortune file to import quotes from"), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK (file_dialog_cb), + ap_widget_set_data (w, QUOTATION_FILE_SELECTOR, dialog); + checkbox = gtk_check_button_new_with_label ( + _("Interpret bracketed text (such as \"<br>\") as HTML tags")); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), checkbox); + gtk_widget_show_all (dialog); +static void quotation_edit_dialog_cb (struct widget *w, const char *quote) + GtkTreeSelection *selection; + treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW); + if (treeview == NULL) return; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *quote_tmp, *old_quote; + gtk_tree_model_get (model, &iter, 1, &old_quote, -1); + start = ap_prefs_get_string_list (w, "quotes"); + /* FIXME: this could grab the wrong quote, if two quotes are identical */ + for (node = start; node != NULL; node = node->next) { + if (!strcmp ((char *) node->data, old_quote)) { + /* Update saved prefs */ + node->data = strdup (quote); + ap_prefs_set_string_list (w, "quotes", start); + free_string_list (start); + /* Update list store */ + quote_tmp = purple_markup_strip_html (quote); + g_string_printf (s, "%ld bytes", g_utf8_strlen (quote, -1)); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + g_string_free (s, TRUE); + free_string_list (start); + purple_notify_error (NULL, NULL, + N_("Unable to edit quote"), + N_("No quote is currently selected")); +static void quotation_edit_dialog (struct widget *w, const gchar *quote) + purple_request_input (ap_get_plugin_handle (), NULL, + _("Save"), G_CALLBACK(quotation_edit_dialog_cb), + _("Cancel"), NULL, NULL, NULL, NULL, +static void quotation_edit (GtkWidget *button, struct widget *w) + GtkTreeSelection *selection; + treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW); + if (treeview == NULL) return; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, 1, "e, -1); + quotation_edit_dialog (w, quote); + purple_notify_error (NULL, NULL, + N_("Unable to edit quote"), + N_("No quote is currently selected")); +static void quotation_create (GtkWidget *button, struct widget *w) + ls = ap_widget_get_data (w, QUOTATION_LIST_STORE); + if (ls == NULL) return; + append_quote (w, ls, ""); + quotes = ap_prefs_get_string_list (w, "quotes"); + quotes = g_list_append (quotes, strdup ("")); + ap_prefs_set_string_list (w, "quotes", quotes); + free_string_list (quotes); + quotation_edit_dialog (w, ""); +static void quotation_delete (GtkWidget *button, struct widget *w) + GtkTreeSelection *selection; + treeview = (GtkWidget *) ap_widget_get_data (w, QUOTATION_TREE_VIEW); + if (treeview == NULL) return; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, 1, "e, -1); + start = ap_prefs_get_string_list (w, "quotes"); + /* FIXME: this could grab the wrong quote, if two quotes are identical */ + for (node = start; node != NULL; node = node->next) { + if (!strcmp ((char *) node->data, quote)) { + start = g_list_remove_link (start, node); + ap_prefs_set_string_list (w, "quotes", start); + free_string_list (start); + gtk_list_store_remove (GTK_LIST_STORE(model), &iter); + free_string_list (start); + purple_notify_error (NULL, NULL, + N_("Unable to delete quote"), + N_("No quote is currently selected")); +static void quotation_delete_all_cb (struct widget *w) + ls = ap_widget_get_data (w, QUOTATION_LIST_STORE); + if (ls == NULL) return; + gtk_list_store_clear (ls); + ap_prefs_set_string_list (w, "quotes", NULL); +static void quotation_delete_all (GtkMenuItem *item, struct widget *w) + purple_request_ok_cancel (ap_get_plugin_handle (), + NULL, _("Delete all quotes?"), NULL, 0, NULL, NULL, + NULL, w, G_CALLBACK(quotation_delete_all_cb), NULL); +static void quotation_more_menu (GtkWidget *button, struct widget *w) + menu = gtk_menu_new (); + menu_item = gtk_menu_item_new_with_label (_("Delete all quotes")); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item); + g_signal_connect (G_OBJECT(menu_item), "activate", + G_CALLBACK(quotation_delete_all), w); + menu_item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item); + menu_item = gtk_menu_item_new_with_label ( + _("Import quotes from from fortune file")); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item); + g_signal_connect (G_OBJECT(menu_item), "activate", + G_CALLBACK(quotation_select_import_file), w); + menu_item = gtk_menu_item_new_with_label ( + _("What is a fortune file?")); + gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item); + g_signal_connect (G_OBJECT(menu_item), "activate", + G_CALLBACK(quotation_explain_fortune_file), NULL); + gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, w, 0, + gtk_get_current_event_time ()); + gtk_widget_show_all (menu); +static void quotation_rate_changed (GtkSpinButton *spinner, struct widget *w) + int value = gtk_spin_button_get_value_as_int (spinner); + ap_prefs_set_int (w, "update_rate", value); +static void quotation_force_change (GtkButton *button, struct widget *w) + ap_prefs_set_int (w, "current_index", + ap_prefs_get_int (w, "current_index") + 1); +search_func(GtkTreeModel *model, gint column, const gchar *key, + GtkTreeIter *iter, gpointer search_data) + gtk_tree_model_get (model, iter, 1, &haystack, -1); + result = (purple_strcasestr(haystack, key) == NULL); +static void menu_destroy_cb (GtkWidget *widget, struct widget *w) + GtkWidget *file_selector; + ap_widget_set_data (w, QUOTATION_LIST_STORE, NULL); + ap_widget_set_data (w, QUOTATION_TREE_VIEW, NULL); + file_selector = (GtkWidget *) ap_widget_get_data (w, QUOTATION_FILE_SELECTOR); + if (file_selector != NULL) { + file_dialog_cb (file_selector, GTK_RESPONSE_DELETE_EVENT, w); +static GtkWidget *quotation_menu (struct widget *w) + GtkWidget *button, *label, *spinner; + GList *quotes, *quotes_start; + GtkTreeViewColumn *col; + ret = gtk_vbox_new (FALSE, 6); + g_signal_connect (G_OBJECT(ret), "destroy", G_CALLBACK (menu_destroy_cb), w); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_box_pack_start (GTK_BOX(ret), sw, TRUE, TRUE, 0); + ls = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + ap_widget_set_data (w, QUOTATION_LIST_STORE, ls); + treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls)); + ap_widget_set_data (w, QUOTATION_TREE_VIEW, treeview); + rend = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("Size"), + rend, "text", 2, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col); + g_object_set (G_OBJECT(rend), + "cell-background-set", TRUE, + "cell-background", "gray", + rend = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("Quotes"), + rend, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), col); + /* Enable CTRL+F searching */ + gtk_tree_view_set_search_column (GTK_TREE_VIEW(treeview), 0); + gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(treeview), + search_func, NULL, NULL); + gtk_container_add (GTK_CONTAINER(sw), treeview); + /* Add in the original quotes */ + quotes_start = ap_prefs_get_string_list (w, "quotes"); + for (quotes = quotes_start; quotes != NULL; quotes = quotes->next) { + append_quote (w, ls, quotes->data); + free_string_list (quotes_start); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(treeview), TRUE); + hbox = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX(hbox), + gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label (_("New quote")); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(quotation_create), w); + button = gtk_button_new_with_label (_("Edit")); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(quotation_edit), w); + button = gtk_button_new_with_label (_("Delete")); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(quotation_delete), w); + button = gtk_button_new_with_label (_("More...")); + gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(quotation_more_menu), w); + gtk_box_pack_start (GTK_BOX(ret), gtk_hseparator_new (), FALSE, FALSE, 0); + /* Behavior selection */ + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Change quote every ")); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0); + spinner = gtk_spin_button_new_with_range (0, G_MAXINT, 1); + gtk_box_pack_start (GTK_BOX(hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(spinner), + ap_prefs_get_int (w, "update_rate")); + g_signal_connect (G_OBJECT(spinner), "value-changed", + G_CALLBACK(quotation_rate_changed), w); + label = gtk_label_new (_("hours (0: always show a new quote)")); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0); + button = gtk_button_new_with_label (_("Change quote now")); + gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (quotation_force_change), w); +/*--------------------------------------------------------------------------* + * Core quotation things * + *--------------------------------------------------------------------------*/ +static gchar *quotation_generate (struct widget *w) + time_t cur_time, old_time; + index = ap_prefs_get_int (w, "current_index"); + quotes = ap_prefs_get_string_list (w, "quotes"); + /* Sanity check the quotes */ + num_quotes = g_list_length (quotes); + return strdup (_("[ERROR: no quotes available]")); + /* Increment index if time has elapsed */ + old_time = purple_str_to_time (ap_prefs_get_string (w, "last_update"), TRUE, + cur_time = time (NULL); + if (difftime (cur_time, old_time) > + 60.0 * 60.0 * (double) ap_prefs_get_int (w, "update_rate")) + ap_debug ("quote", "time interval elapsed, moving to new quote"); + time_string = (char *)malloc(1000); + t = ap_gmtime (&cur_time); + strftime (time_string, 999, "%Y-%m-%dT%H:%M:%S+00:00", t); + ap_prefs_set_string (w, "last_update", time_string); + ap_prefs_set_int (w, "current_index", index); + /* Wrap around when last quote is reached */ + if (index >= num_quotes) { + ap_prefs_set_int (w, "current_index", 0); + /* Choose and output the quote */ + ret = strdup((gchar *) g_list_nth_data (quotes, index)); + free_string_list (quotes); +static void quotation_init (struct widget *w) + time_string = (char *)malloc(1000); + strftime (time_string, 999, "%Y-%m-%dT%H:%M:%S+00:00", gmtime (&the_time)); + ap_prefs_add_string_list (w, "quotes", NULL); + ap_prefs_add_int (w, "current_index", 0); + ap_prefs_add_int (w, "update_rate", 0); + ap_prefs_add_string (w, "last_update", time_string); +struct component quotation = + N_("Displays a quotation from a provided selection"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_rss.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,477 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +static GtkWidget *entry_username = NULL; +static GtkWidget *entry_url = NULL; +GHashTable *rss_entries = NULL; +static GHashTable *rss_timeouts = NULL; +GStaticMutex rss_mutex = G_STATIC_MUTEX_INIT; +static char *get_rss_data (struct widget *w, const char *field, int index, + const struct rss_entry *e; + g_static_mutex_lock (&rss_mutex); + tmp = (GList *) g_hash_table_lookup (rss_entries, w); + g_static_mutex_unlock (&rss_mutex); + return strdup (_("[ERROR: Invalid entry number]")); + g_static_mutex_unlock (&rss_mutex); + return strdup (_("[ERROR: No data, invalid URL/account?]")); + g_static_mutex_unlock (&rss_mutex); + return strdup (_("[ERROR: Insufficient number of entries]")); + e = (struct rss_entry *) tmp->data; + if (!strcmp (field, "link")) { + } else if (!strcmp (field, "title")) { + ret = strdup (e->title); + } else if (!strcmp (field, "entry")) { + max = ap_prefs_get_int (w, "entry_limit"); + ret = strdup (e->entry); + if (max < g_utf8_strlen (ret, -1)) { + gchar *tmp = g_utf8_offset_to_pointer (ret, max); + } else if (!strcmp (field, "time")) { + g_static_mutex_unlock (&rss_mutex); +static char *rss_generate (struct widget *w) + format = ap_prefs_get_string (w, "format"); + output = g_string_new (""); + time_tmp = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM); + if (isdigit (*format)) { + count = (count * 10) + (int) *format - 48; + tmp = get_rss_data (w, "time", count, &time); + strftime (time_tmp, AP_SIZE_MAXIMUM, fmt_char, time); + g_string_append_printf (output, "%s", time_tmp); + tmp = get_rss_data (w, "link", count, NULL); + g_string_append_printf (output, "%s", tmp); + tmp = get_rss_data (w, "title", count, NULL); + g_string_append_printf (output, "%s", tmp); + tmp = get_rss_data (w, "entry", count, NULL); + g_string_append_printf (output, "%s", tmp); + g_string_append_printf (output, "%c", *format); + g_string_append_unichar (output, g_utf8_get_char (format)); + format = g_utf8_next_char (format); + g_string_append_unichar (output, g_utf8_get_char (format)); + format = g_utf8_next_char (format); + g_string_free (output, FALSE); +static gboolean rss_update (gpointer data) + parse_rss ((struct widget *) data); +static void rss_load (struct widget *w) + g_static_mutex_lock (&rss_mutex); + rss_entries = g_hash_table_new (NULL, NULL); + rss_timeouts = g_hash_table_new (NULL, NULL); + rss_timeout = GINT_TO_POINTER (g_timeout_add ( + ap_prefs_get_int (w, "update_rate") * 60 * 1000, + g_hash_table_insert (rss_timeouts, w, rss_timeout); + g_static_mutex_unlock (&rss_mutex); +static void rss_unload (struct widget *w) + g_static_mutex_lock (&rss_mutex); + rss_timeout = g_hash_table_lookup (rss_timeouts, w); + g_source_remove (GPOINTER_TO_INT (rss_timeout)); + g_hash_table_remove (rss_timeouts, w); + g_static_mutex_unlock (&rss_mutex); +static void rss_init (struct widget *w) + ap_prefs_add_int (w, "type", RSS_XANGA); + ap_prefs_add_string (w, "location", ""); + ap_prefs_add_string (w, "username", ""); + ap_prefs_add_string (w, "format", + "My <a href=\"%l\">blog</a> was most recently updated on " + "%1B %1d at %I:%M %p"); + ap_prefs_add_int (w, "update_rate", 5); + ap_prefs_add_int (w, "entry_limit", 1000); +static gboolean update_refresh_rate (GtkWidget *widget, GdkEventFocus *evt, + minutes = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + ap_prefs_set_int (w, "update_rate", minutes); + // Kill the current timer and run a new one + g_static_mutex_lock (&rss_mutex); + timeout = g_hash_table_lookup (rss_timeouts, w); + g_source_remove (GPOINTER_TO_INT(timeout)); + timeout = GINT_TO_POINTER (g_timeout_add (minutes * 60 * 1000, + g_hash_table_replace (rss_timeouts, w, timeout); + g_static_mutex_unlock (&rss_mutex); +static void rss_data_update (GtkWidget *widget, struct widget *w) +static void sensitivity_cb (const char *name, PurplePrefType type, + gconstpointer val, gpointer data) + int real_val = GPOINTER_TO_INT (val); + if (real_val == RSS_XANGA || real_val == RSS_LIVEJOURNAL) { + gtk_widget_set_sensitive (entry_username, TRUE); + gtk_widget_set_sensitive (entry_url, FALSE); + gtk_widget_set_sensitive (entry_username, FALSE); + gtk_widget_set_sensitive (entry_url, TRUE); +static GtkWidget *entry; +static void event_cb (GtkWidget *widget, struct widget *w) + ap_prefs_set_string (w, "format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static void formatting_toggle_cb (GtkIMHtml *imhtml, + GtkIMHtmlButtons buttons, struct widget *w) + ap_prefs_set_string (w, "format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static void formatting_clear_cb (GtkIMHtml *imhtml, + ap_prefs_set_string (w, "format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static GtkWidget *rss_menu (struct widget *w) + GtkWidget *label, *hbox, *button, *spinner, *sw; + GtkWidget *entry_window, *toolbar; + GtkTextBuffer *text_buffer; + ret = gtk_vbox_new (FALSE, 5); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), "<b>Format string for output</b>"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX(ret), hbox, TRUE, TRUE, 0); + entry_window = pidgin_create_imhtml (TRUE, &entry, &toolbar, &sw); + gtk_box_pack_start (GTK_BOX (hbox), entry_window, TRUE, TRUE, 0); + gtk_imhtml_append_text_with_images (GTK_IMHTML(entry), + ap_prefs_get_string (w, "format"), + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry)); + g_signal_connect (G_OBJECT (text_buffer), "changed", + G_CALLBACK (event_cb), w); + g_signal_connect_after(G_OBJECT(entry), "format_function_toggle", + G_CALLBACK(formatting_toggle_cb), w); + g_signal_connect_after(G_OBJECT(entry), "format_function_clear", + G_CALLBACK(formatting_clear_cb), w); + label = gtk_label_new (_( + "The following options can be specified with a numerical modifier\n" + "(i.e. \"%e\" can also be written \"%1e\" or \"%2e\"). The optional\n" + "number specifies which entry to get the data for. \"1\" refers to the\n" + "most recent entry, \"2\" refers to the second-most recent entry, and so\n" + "forth. \"1\" is used if no number is specified.\n\n" + "%e\tStarting text of the entry.\n" + "%l\tLink to the specific entry.\n" + "%t\tTitle of entry (Xanga incompatible)\n" + "%H\thour of entry(24-hour clock)\n" + "%I\thour (12-hour clock)\n" + "%a\tabbreviated weekday name\n" + "%A\tfull weekday name\n" + "%b\tabbreviated month name\n" + "%B\tfull month name\n" + "%m\tmonth (numerical)\n" + "%d\tday of the month\n" + "%j\tday of the year\n" + "%W\tweek number of the year\n" + "%w\tweekday (numerical)\n" + "%y\tyear without century\n" + "%Y\tyear with century\n" + "%z\ttime zone name, if any\n" + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + sw = gtk_scrolled_window_new (NULL,NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE , 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label); + /* Type/URL/Username selection */ + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), "<b>RSS/Blog location</b>"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX(ret), hbox, FALSE, FALSE, 0); + options = g_list_append (NULL, (char *) _("Xanga")); + options = g_list_append (options, GINT_TO_POINTER(RSS_XANGA)); + options = g_list_append (options, (char *) _("LiveJournal")); + options = g_list_append (options, GINT_TO_POINTER(RSS_LIVEJOURNAL)); + options = g_list_append (options, (char *) _("RSS 2.0")); + options = g_list_append (options, GINT_TO_POINTER(RSS_2)); + ap_prefs_dropdown_from_list (w, hbox, NULL, PURPLE_PREF_INT, "type", options); + pref = ap_prefs_get_pref_name (w, "type"); + purple_prefs_connect_callback (ap_get_plugin_handle (), pref, + /* Username/URL fields */ + entry_username = ap_prefs_labeled_entry (w, hbox, _("Username:"), + entry_url = ap_prefs_labeled_entry (w, hbox, _("URL of feed:"), + value = ap_prefs_get_int (w, "type"); + if (value == RSS_XANGA || value == RSS_LIVEJOURNAL) { + gtk_widget_set_sensitive (entry_username, TRUE); + gtk_widget_set_sensitive (entry_url, FALSE); + gtk_widget_set_sensitive (entry_username, FALSE); + gtk_widget_set_sensitive (entry_url, TRUE); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), "<b>Other options</b>"); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(ret), label, FALSE, FALSE, 0); + /* # of chars to display from description */ + ap_prefs_labeled_spin_button (w, ret, + "Max characters to show in entry (%e)", "entry_limit", 1, + AP_SIZE_MAXIMUM - 1, NULL); + /* Update rate selection */ + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Minutes between checks for updates:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + spinner = gtk_spin_button_new_with_range (1, 60, 1); + gtk_box_pack_start (GTK_BOX(hbox), spinner, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), + ap_prefs_get_int (w, "update_rate")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_refresh_rate), w); + button = gtk_button_new_with_label ("Fetch data now!"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (rss_data_update), w); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + N_("Information taken from an RSS feed (Xanga and LiveJournal capable)"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_rss.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,52 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#define MAX_USERNAME_LENGTH 1000 +extern GHashTable *rss_entries; +extern GStaticMutex rss_mutex; +void parse_rss (struct widget *); +void parse_xanga_rss (struct widget *, gchar *); +GMarkupParser rss_parser; --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_rss_parser.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,350 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +static gboolean parsing_rss = FALSE; +static gboolean parsing_item = FALSE; +static gboolean item_title = FALSE; +static gboolean item_link = FALSE; +static gboolean item_description = FALSE; +static gboolean item_comments = FALSE; +static gboolean item_pubdate = FALSE; +/* Get URL of RSS feed */ +static char *get_url (struct widget *w) + type = ap_prefs_get_int (w, "type"); + username = ap_prefs_get_string (w, "username"); + g_string_append_printf (s, + "http://www.livejournal.com/users/%s/data/rss", username); + username = ap_prefs_get_string (w, "username"); + g_string_append_printf (s, "http://www.xanga.com/%s/rss", username); + g_string_append_printf (s, "%s", ap_prefs_get_string (w, "location")); + g_string_free (s, FALSE); +static void free_entries (struct widget *w) { + entries = (GList *) g_hash_table_lookup (rss_entries, w); + e = (struct rss_entry *) entries->data; + entries = entries->next; + g_hash_table_replace (rss_entries, w, NULL); +/* Date parsing functions */ +static struct tm *parse_date_rfc822 (const char *date_string) + time_t t, gmt_time, local_time; + struct tm *ret, *result; + local_time = time(NULL); + gmt_time = mktime(gmtime(&gmt_time)); + // TODO: Change this to GDate + t = rfc_parse_date_time (date_string); + // if (rfc_parse_was_gmt ()) { + // FIXME: Handle time zones + ret = (struct tm *) malloc (sizeof (struct tm)); + result = localtime(&t); + ret->tm_sec = result->tm_sec; + ret->tm_min = result->tm_min; + ret->tm_hour = result->tm_hour; + ret->tm_mday = result->tm_mday; + ret->tm_mon = result->tm_mon; + ret->tm_year = result->tm_year; +/* XML Parser Callbacks */ +static void start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribuate_names, + const gchar **attribute_values, + gpointer data, GError **error) + struct widget *w = (struct widget *) data; + //printf ("start:%s\n", element_name); + if (!parsing_rss && !strcmp (element_name, "rss")) + else if (parsing_rss && !parsing_item && + !strcmp (element_name, "item")) { + e = (struct rss_entry *) malloc (sizeof(struct rss_entry)); + entries = (GList *) g_hash_table_lookup (rss_entries, w); + entries = g_list_prepend (entries, e); + g_hash_table_replace (rss_entries, w, entries); + else if (parsing_item && !strcmp (element_name, "title")) + else if (parsing_item && !strcmp (element_name, "link")) + else if (parsing_item && !strcmp (element_name, "description")) + item_description = TRUE; + else if (parsing_item && !strcmp (element_name, "comments")) + else if (parsing_item && !strcmp (element_name, "pubDate")) +static void end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer data, GError **error) + struct widget *w = (struct widget *) w; + //printf ("end:%s\n", element_name); + if (!strcmp (element_name, "rss")) + else if (!strcmp (element_name, "item")) + else if (!strcmp (element_name, "title")) + else if (!strcmp (element_name, "link")) + else if (!strcmp (element_name, "description")) + item_description = FALSE; + else if (!strcmp (element_name, "comments")) + else if (!strcmp (element_name, "pubDate")) +static void text_handler (GMarkupParseContext *context, + const gchar *text, gsize text_len, + gpointer data, GError **error) + struct widget *w = (struct widget *) data; + entries = (GList *) g_hash_table_lookup (rss_entries, w); + e = (struct rss_entry *) entries->data; + e->url = g_strdup (text); + else if (item_description) { + e->entry = purple_unescape_html (text); + // If there is a standard format for Xanga titles (there really isn't) + // it will probably be devised from the actual content. Will be placed + // here if there is proven demand. + else if (item_comments) { + e->comments = g_strdup (text); + e->title = g_strdup (text); + else if (item_pubdate) { + e->t = parse_date_rfc822 (text); +/* Final parser variable */ +GMarkupParser rss_parser = +/* Callback for HTTP data fetcher */ +static void url_callback (gpointer data, const char *text, size_t size) + GMarkupParseContext *context; + struct widget *w = (struct widget *) data; + /* Make sure URL exists/connected to Internet */ + ap_debug ("rss", "error, unable to fetch page via internet"); + item_description = FALSE; + g_static_mutex_lock (&rss_mutex); + filtered_text = purple_utf8_salvage (text); + gchar *convert = purple_utf8_try_convert ("<"); + gchar *next = g_utf8_strchr (filtered_text, 10, g_utf8_get_char (convert)); + g_static_mutex_unlock (&rss_mutex); + if (ap_prefs_get_int (w, "type") == RSS_XANGA) { + parse_xanga_rss (w, filtered_text); + entries = (GList *) g_hash_table_lookup (rss_entries, w); + entries = g_list_reverse (entries); + g_hash_table_replace (rss_entries, w, entries); + g_static_mutex_unlock (&rss_mutex); + context = g_markup_parse_context_new (&rss_parser, 0, w, NULL); + if (!g_markup_parse_context_parse (context, next, size, &err)) { + g_markup_parse_context_free (context); + ap_debug ("rss", "error, unable to start parser"); + ap_debug ("rss", err->message); + if (!g_markup_parse_context_end_parse (context, &err)) { + g_markup_parse_context_free (context); + ap_debug ("rss", "error, unable to end parser"); + g_markup_parse_context_free (context); + entries = (GList *) g_hash_table_lookup (rss_entries, w); + entries = g_list_reverse (entries); + g_hash_table_replace (rss_entries, w, entries); + g_static_mutex_unlock (&rss_mutex); +void parse_rss (struct widget *w) + if (strcmp (url, "") != 0) { + purple_util_fetch_url (url, TRUE, NULL, FALSE, url_callback, w); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_rss_xanga.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,117 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +static gchar *convert_char; +static gchar *find_next_tag (gchar *text) { + return g_utf8_strchr (text, -1, g_utf8_get_char (convert_char)); +static gchar *find_end_of_tag (gchar *text) { + return g_utf8_strchr (text, -1, g_utf8_get_char (convert_char)); +static gboolean starts_with (gchar *text, gchar letter) { + *convert_char = letter; + return (g_utf8_strchr (text, 1, g_utf8_get_char (convert_char)) == text); +void parse_xanga_rss (struct widget *w, gchar *text) { + gchar *next_tag, *end_prev_tag; + gchar *tag_first_char, *tag_second_char; + convert_char = (gchar *) malloc (sizeof(gchar) * 2); + *(convert_char+1) = '\0'; + rss_parser.start_element (NULL, "rss", NULL, NULL, w, NULL); + while ((next_tag = find_next_tag (end_prev_tag)) != NULL) { + tag_first_char = g_utf8_next_char (next_tag); + tag_second_char = g_utf8_next_char (tag_first_char); + if (starts_with (tag_first_char, 't')) rss_parser.start_element ( + NULL, "title", NULL, NULL, w, NULL); + else if (starts_with (tag_first_char, 'l')) rss_parser.start_element ( + NULL, "link", NULL, NULL, w, NULL); + else if (starts_with (tag_first_char, 'p')) rss_parser.start_element ( + NULL, "pubDate", NULL, NULL, w, NULL); + else if (starts_with (tag_first_char, 'd')) rss_parser.start_element ( + NULL, "description", NULL, NULL, w, NULL); + else if (starts_with (tag_first_char, 'c')) rss_parser.start_element ( + NULL, "comments", NULL, NULL, w, NULL); + else if (starts_with (tag_first_char, '/')) { + rss_parser.text (NULL, end_prev_tag, -1, w, NULL); + if (starts_with (tag_second_char, 't')) + rss_parser.end_element (NULL, "title", w, NULL); + else if (starts_with (tag_second_char, 'l')) + rss_parser.end_element (NULL, "link", w, NULL); + else if (starts_with (tag_second_char, 'p')) + rss_parser.end_element (NULL, "pubDate", w, NULL); + else if (starts_with (tag_second_char, 'd')) + rss_parser.end_element (NULL, "description", w, NULL); + else if (starts_with (tag_second_char, 'c')) + rss_parser.end_element (NULL, "comments", w, NULL); + else if (starts_with (tag_second_char, 'i')) { + rss_parser.end_element (NULL, "item", w, NULL); + // TODO: WARN USER IN THIS CASE + if (starts_with (tag_first_char, 'i') && + starts_with (tag_second_char, 't')) { + rss_parser.start_element (NULL, "item", NULL, NULL, w, NULL); + if ((next_tag = find_end_of_tag (tag_first_char)) == NULL) { + // TODO: NOTIFY USER THAT WE REACHED END + end_prev_tag = g_utf8_next_char (next_tag); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_textfile.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,268 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +/*---------- TEXT FILE: Text from a file ----------*/ +static GtkWidget *file_entry; +static GtkWidget *file_selector; +/* Read file into string and return */ +char *text_file_generate (struct widget *w) + gchar *text, *salvaged, *converted; + int max = ap_prefs_get_int (w, "text_size"); + filename = ap_prefs_get_string (w, "text_file"); + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + return g_strdup (_("[ERROR: File does not exist]")); + if (!g_file_get_contents (filename, &text, NULL, NULL)) { + return g_strdup (_("[ERROR: Unable to open file]")); + converted = purple_utf8_try_convert (text); + if (converted != NULL) { + if (strlen (text) > max) { + salvaged = purple_utf8_salvage (text); +static gboolean text_file_update (GtkWidget *widget, GdkEventFocus *evt, + ap_prefs_set_string (w, "text_file", + gtk_entry_get_text (GTK_ENTRY (file_entry))); +static void text_file_filename (GtkWidget *widget, gpointer user_data) { + const gchar *selected_filename; + struct widget *w = (struct widget *) user_data; + selected_filename = gtk_file_selection_get_filename ( + GTK_FILE_SELECTION (file_selector)); + ap_prefs_set_string (w, "text_file", selected_filename); + gtk_entry_set_text (GTK_ENTRY (file_entry), selected_filename); +/* Creates and pops up file selection dialog for fortune file */ +static void text_file_selection (GtkWidget *widget, gpointer user_data) { + struct widget *w = (struct widget *) user_data; + /* Create the selector */ + file_selector = gtk_file_selection_new ( + "Select a text file with content"); + cur_file = ap_prefs_get_string (w, "text_file"); + if (cur_file && strlen (cur_file) > 1) { + gtk_file_selection_set_filename ( + GTK_FILE_SELECTION (file_selector), cur_file); + g_signal_connect (GTK_OBJECT( + GTK_FILE_SELECTION(file_selector)->ok_button), + "clicked", G_CALLBACK (text_file_filename), w); + /* Destroy dialog box when the user clicks button. */ + g_signal_connect_swapped (GTK_OBJECT( + GTK_FILE_SELECTION(file_selector)->ok_button), + "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); + g_signal_connect_swapped (GTK_OBJECT ( + GTK_FILE_SELECTION (file_selector)->cancel_button), + "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector); + gtk_widget_show (file_selector); +/* Pop up message with instructions */ +static void text_file_info_button (GtkButton *button, gpointer data) + if (!strcmp ((char *) data, "itunes")) { + purple_notify_formatted (NULL, _("iTunes"), _("Current song in iTunes"), NULL, + _("Get TuneCam from <a href=\"" + "http://www.soft-o-mat.com/productions.shtml\">" + "http://www.soft-o-mat.com/productions.shtml</a> and start it.<br>" + "Create a html file that contains the following text:<br><br><tc" + ">artist</tc> - <tc>title</tc><br><br>and " + "press the \"T\" button. Import the html file as a template for " + "the \"File Track\" and whatever else you see fit. Then select " + "the \"G\" button and choose the location of the output file which " + "will be used in this component"), + } else if (!strcmp ((char *) data, "xmms")) { + purple_notify_formatted (NULL, _("XMMS"), _("Current song in XMMS"), NULL, + _("Included in the misc folder of AutoProfile is a script called \"" + "xmms_currenttrack\". Install this script in your $PATH and give it " + "executable permissions, and specify the program using a pipe.<br><br>" + "Alternatively, in XMMS, go to Options->Preferences->Effects/General " + "Plugins.<br>Configure the \"Song Change\" plugin. In the song change" + " command box, put<br><br>echo \"%s\" > /path/to/output/file<br><br>" + "and be sure to enable the plugin. Select the file location in " + "AutoProfile and you should be done"), + } else if (!strcmp ((char *) data, "wmp")) { + purple_notify_formatted (NULL, _("Windows Media Player"), + _("Current song in Windows Media Player"), NULL, + _("Download NowPlaying, a plugin for WMP from <a href=\"" + "http://www.wmplugins.com/ItemDetail.aspx?ItemID=357\">" + "http://www.wmplugins.com/ItemDetail.aspx?ItemID=357</a> and follow " + "the included installation instructions.<br>Set the output filename " + "to the file you choose in this component"), + } else if (!strcmp ((char *) data, "amip")) { + purple_notify_formatted (NULL, _("iTunes/Winamp/Foobar/Apollo/QCD"), + _("Current song in iTunes/Winamp/Foobar/Apollo/QCD"), NULL, + _("Get the version of AMIP associated with your player from <a href=\"" + "http://amip.tools-for.net/\">" + "http://amip.tools-for.net/</a> and install/" + "Check the box \"Write song info to file\", play with the settings, " + "and set the file in this component to be the file in the AMIP " +GtkWidget *text_file_menu (struct widget *w) + GtkWidget *ret = gtk_vbox_new (FALSE, 5); + GtkWidget *hbox, *label, *button; + label = gtk_label_new (_("Select text file with source content")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + /* Text entry to type in file name */ + file_entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), file_entry, FALSE, FALSE, 0); + gtk_entry_set_text (GTK_ENTRY (file_entry), + ap_prefs_get_string (w, "text_file")); + g_signal_connect (G_OBJECT (file_entry), "focus-out-event", + G_CALLBACK (text_file_update), w); + /* Button to bring up file select dialog */ + button = gtk_button_new_with_label ("Browse ..."); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (text_file_selection), w); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + ap_prefs_labeled_spin_button (w, ret, + _("Max characters to read from file:"), "text_size", + 1, AP_SIZE_MAXIMUM - 1, NULL); + gtk_box_pack_start (GTK_BOX (ret), + gtk_hseparator_new (), 0, 0, 0); + label = gtk_label_new (_("Windows users: Play the current song in:")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("iTunes/Winamp/Foobar/Apollo/QCD"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (text_file_info_button), "amip"); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Windows Media Player"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (text_file_info_button), "wmp"); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("*nix users: Play the current song in:")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("XMMS"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (text_file_info_button), "xmms"); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + label = gtk_label_new (_("OS X users: Play the current song in:")); + gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("iTunes"); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (text_file_info_button), "itunes"); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); +void text_file_init (struct widget *w) { + ap_prefs_add_string (w, "text_file", ""); + ap_prefs_add_int (w, "text_size", AP_SIZE_MAXIMUM - 1); + N_("Text File / Songs"), + N_("Copies text from file that external programs " + "(e.g. XMMS, Winamp, iTunes) can modify on a regular basis"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_timestamp.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,142 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +/*---------- TIMESTAMP: Display time at creation ---------------------------*/ +static char *timestamp_generate (struct widget *w) { + time_t *general_time = (time_t *)malloc(sizeof(time_t)); + cur_time = ap_localtime (general_time); + ret = (char *)malloc (AP_SIZE_MAXIMUM); + strftime (ret, AP_SIZE_MAXIMUM - 1, + ap_prefs_get_string (w, "timestamp_format"), +static void timestamp_init (struct widget *w) { + ap_prefs_add_string (w, "timestamp_format", + "Automatically created at %I:%M %p"); +static GtkWidget *entry; +static void event_cb (GtkWidget *widget, struct widget *w) + ap_prefs_set_string (w, "timestamp_format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static void formatting_toggle_cb (GtkIMHtml *imhtml, + GtkIMHtmlButtons buttons, struct widget *w) + ap_prefs_set_string (w, "timestamp_format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static void formatting_clear_cb (GtkIMHtml *imhtml, + ap_prefs_set_string (w, "timestamp_format", + gtk_imhtml_get_markup (GTK_IMHTML(entry))); +static GtkWidget *timestamp_menu (struct widget *w) + GtkWidget *ret = gtk_vbox_new (FALSE, 5); + GtkWidget *entry_window, *toolbar; + GtkTextBuffer *text_buffer; + entry_window = pidgin_create_imhtml (TRUE, &entry, &toolbar, &sw); + gtk_box_pack_start (GTK_BOX (ret), entry_window, FALSE, FALSE, 0); gtk_imhtml_append_text_with_images (GTK_IMHTML(entry), + ap_prefs_get_string (w, "timestamp_format"), + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry)); + g_signal_connect (G_OBJECT (text_buffer), "changed", + G_CALLBACK (event_cb), w); + g_signal_connect_after(G_OBJECT(entry), "format_function_toggle", + G_CALLBACK(formatting_toggle_cb), w); + g_signal_connect_after(G_OBJECT(entry), "format_function_clear", + G_CALLBACK(formatting_clear_cb), w); + label = gtk_label_new (_( + "Insert the following characters where time is to be displayed:\n\n" + "%H\thour (24-hour clock)\n" + "%I\thour (12-hour clock)\n" + "%a\tabbreviated weekday name\n" + "%A\tfull weekday name\n" + "%b\tabbreviated month name\n" + "%B\tfull month name\n" + "%m\tmonth (numerical)\n" + "%d\tday of the month\n" + "%j\tday of the year\n" + "%W\tweek number of the year\n" + "%w\tweekday (numerical)\n" + "%y\tyear without century\n" + "%Y\tyear with century\n" + "%z\ttime zone name, if any\n" + sw = gtk_scrolled_window_new (NULL,NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_box_pack_start (GTK_BOX (ret), sw, TRUE, TRUE , 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw), label); +struct component timestamp = + N_("Displays custom text showing when message was created"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/comp_uptime.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,100 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +/*---------- UPTIME: Display the computer uptime --*/ +char *uptime_generate (struct widget *w) { + char *out, *line, *working; + char *p_character, *colon_character, *comma_character, *m_character; + exec = g_spawn_command_line_sync (line, + &out, NULL, NULL, &return_error); + /* Buffer length for safety */ + working = (char *)malloc (strlen (out)+7+8+8+1); + strcpy (working, "Uptime:"); + /* Break into minutes, hours, and everything else */ + p_character = strchr (out, 'p'); + m_character = strchr (p_character, 'm'); + /* Uptime format including "pm" */ + if (m_character != NULL && m_character == p_character + 1) { + p_character = strchr (m_character, 'p'); + m_character = strchr (p_character, 'm'); + /* Uptime if < 1 hour */ + if (m_character != NULL && *(m_character+1) == 'i') { + strcat (working, p_character); + strcat (working, "minutes"); + colon_character = strchr (p_character, ':'); + comma_character = strchr (colon_character, ','); + *colon_character++ = '\0'; + *comma_character = '\0'; + /* Yank it all together */ + strcat (working, p_character); + strcat (working, " hours, "); + strcat (working, colon_character); + strcat (working, " minutes"); + ap_debug ("uptime", "command failed to execute"); + return g_strdup(_("[ERROR: failed to execute uptime command]")); +struct component uptime = + N_("Show how long your computer has been running"), --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/component.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,87 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +static GList *components = NULL; +static GList *get_components (); +void ap_component_start () { + if (components) g_list_free (components); + components = get_components (); +void ap_component_finish () { + g_list_free (components); +GList *ap_component_get_components () { +struct component *ap_component_get_component (const gchar *identifier) { + struct component *cur_comp; + for (comps = components; comps != NULL; comps = comps->next) { + cur_comp = (struct component *) comps->data; + if (!strcmp (cur_comp->identifier, identifier)) +static GList *get_components () + /* Add each listed component */ + ret = g_list_append (ret, &logstats); + ret = g_list_append (ret, &text); + ret = g_list_append (ret, "ation); + ret = g_list_append (ret, &rss); + ret = g_list_append (ret, ×tamp); + ret = g_list_append (ret, &http); + ret = g_list_append (ret, &count); + ret = g_list_append (ret, &executable); + ret = g_list_append (ret, &uptime); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/component.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,73 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#ifndef _AP_COMPONENT_H_ +#define _AP_COMPONENT_H_ +/* A component is composed of code that generates some sort of content, + and a widget is a specific _instance_ of a component */ + char *(*generate)(struct widget *); + void (*init_pref)(struct widget *); + void (*load)(struct widget *); + void (*unload)(struct widget *); + gboolean (*has_content_changed)(struct widget *); + GtkWidget *(*pref_menu)(struct widget *); +void ap_component_start (); +void ap_component_finish (); +GList *ap_component_get_components (); +struct component *ap_component_get_component (const gchar *); +extern struct component logstats; +extern struct component count; +extern struct component executable; +extern struct component http; +extern struct component quotation; +extern struct component rss; +extern struct component text; +extern struct component timestamp; +extern struct component uptime; +#endif /* _AP_COMPONENT_H_ */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/gtk_actions.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,341 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "gtkimhtmltoolbar.h" +static GtkWidget *current_profile = NULL; +static GtkWidget *accounts_dialog = NULL; +static GtkWidget *content_win = NULL; +/*--------------------------------------------------------------------------* + * Accounts edit popup window * + *--------------------------------------------------------------------------*/ +static void accounts_response_cb (GtkWidget *d, int response, gpointer data) + gtk_widget_destroy (accounts_dialog); + accounts_dialog = NULL; +static void display_accounts_dialog () { + if (accounts_dialog != NULL) { + gtk_window_present (GTK_WINDOW (accounts_dialog)); + accounts_dialog = gtk_dialog_new_with_buttons (_("Edit Profile Accounts"), + NULL, GTK_DIALOG_NO_SEPARATOR, NULL, NULL); + gtk_dialog_set_has_separator (GTK_DIALOG(accounts_dialog), TRUE); + gtk_dialog_add_button (GTK_DIALOG(accounts_dialog), GTK_STOCK_OK, + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (label), + _("<b>No accounts currently enabled:</b> You have not yet specified\n " + "what accounts AutoProfile should set the profile for. Until you\n " + "check one of the boxes below, AutoProfile will effectively do\n " + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(accounts_dialog)->vbox), label, + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(accounts_dialog)->vbox), + get_account_page (), TRUE, TRUE, 0); + g_signal_connect (G_OBJECT(accounts_dialog), "response", + G_CALLBACK(accounts_response_cb), NULL); + gtk_window_set_default_size (GTK_WINDOW(accounts_dialog), 400, 450); + gtk_widget_show_all (accounts_dialog); +/*--------------------------------------------------------------------------* + * Profile edit window * + *--------------------------------------------------------------------------*/ +/* Callbacks for refreshing profile preview window */ +static void refresh_preview (GtkWidget *preview) { + // TODO: See if a delay timeout is necessary here + if (preview == NULL || current_profile == NULL) return; + gtk_imhtml_clear (GTK_IMHTML(preview)); + input = gtk_imhtml_get_markup ((GtkIMHtml *) current_profile); + output = ap_generate (input, AP_SIZE_PROFILE_MAX); + gtk_imhtml_append_text (GTK_IMHTML(preview), output, +static void refresh_cb (GtkWidget *widget, gpointer data) + refresh_preview ((GtkWidget *) data); +static void event_cb (GtkWidget *widget, gpointer data) + refresh_preview ((GtkWidget *) data); +static void formatting_toggle_cb (GtkIMHtml *imhtml, + GtkIMHtmlButtons buttons, gpointer data) + refresh_preview ((GtkWidget *) data); +static void formatting_clear_cb (GtkIMHtml *imhtml, gpointer data) + refresh_preview ((GtkWidget *) data); +static void revert_cb (GtkWidget *button, GtkWidget *imhtml) + gtk_imhtml_clear (GTK_IMHTML(imhtml)); + gtk_imhtml_append_text_with_images (GTK_IMHTML(imhtml), + purple_prefs_get_string ("/plugins/gtk/autoprofile/profile"), +static void save_cb (GtkWidget *button, GtkWidget *imhtml) + if (imhtml == NULL) return; + new_text = gtk_imhtml_get_markup ((GtkIMHtml *) imhtml); + purple_prefs_set_string ("/plugins/gtk/autoprofile/profile", new_text); + if (NULL == purple_prefs_get_string_list ( + "/plugins/gtk/autoprofile/profile_accounts")) { + // If no accounts set, ask for one! + display_accounts_dialog (); +/* Profile edit window */ +static GtkWidget *get_profile_page () + GtkWidget *hbox, *vbox, *dialog_box, *preview, *edit_window; + GtkWidget *label, *sw, *toolbar, *bbox; + GtkWidget *refresh_button, *revert_button, *save_button; + GtkTextBuffer *text_buffer; + ret = gtk_vbox_new (FALSE, 6); + dialog_box = gtk_vbox_new (FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER(dialog_box), 6); + gtk_box_pack_start (GTK_BOX(ret), dialog_box, TRUE, TRUE, 0); + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX(dialog_box), hbox, FALSE, FALSE, 0); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(label), _("<b>Preview</b>")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0); + refresh_button = gtk_button_new_with_label (_("Refresh")); + gtk_box_pack_end (GTK_BOX(hbox), refresh_button, FALSE, FALSE, 0); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_box_pack_start (GTK_BOX(dialog_box), sw, TRUE, TRUE, 0); + preview = gtk_imhtml_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(sw), preview); + pidgin_setup_imhtml (preview); + gtk_imhtml_append_text (GTK_IMHTML(preview), + purple_prefs_get_string ("/plugins/gtk/autoprofile/profile"), + gtk_box_pack_start (GTK_BOX(ret), gtk_hseparator_new (), FALSE, FALSE, 0); + dialog_box = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER(dialog_box), 6); + gtk_box_pack_start (GTK_BOX(ret), dialog_box, TRUE, TRUE, 0); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(label), + _("<b>Edit</b> (Drag widgets into profile / " + "Use shift+enter to insert a new line)")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX(dialog_box), label, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(dialog_box), hbox, TRUE, TRUE, 0); + vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + get_widget_list (vbox, &sel); + bbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + revert_button = gtk_button_new_with_label (_("Revert")); + gtk_box_pack_start (GTK_BOX(bbox), revert_button, TRUE, TRUE, 0); + save_button = gtk_button_new_with_label (_("Save profile")); + gtk_box_pack_start (GTK_BOX(bbox), save_button, TRUE, TRUE, 0); + edit_window = pidgin_create_imhtml (TRUE, ¤t_profile, &toolbar, + gtk_box_pack_start (GTK_BOX(hbox), edit_window, TRUE, TRUE, 0); + g_signal_connect (G_OBJECT(save_button), "clicked", + G_CALLBACK(save_cb), current_profile); + g_signal_connect (G_OBJECT(revert_button), "clicked", + G_CALLBACK(revert_cb), current_profile); + g_signal_connect (G_OBJECT (refresh_button), "clicked", + G_CALLBACK (refresh_cb), preview); + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (current_profile)); + g_signal_connect (G_OBJECT (text_buffer), "changed", + G_CALLBACK (event_cb), preview); + g_signal_connect_after(G_OBJECT(current_profile), "format_function_toggle", + G_CALLBACK(formatting_toggle_cb), preview); + g_signal_connect_after(G_OBJECT(current_profile), "format_function_clear", + G_CALLBACK(formatting_clear_cb), preview); + revert_cb (revert_button, current_profile); + refresh_cb (refresh_button, preview); +/*--------------------------------------------------------------------------* + * General edit window * + *--------------------------------------------------------------------------*/ +ap_edit_content_destroy (GtkWidget *button, GtkWidget *window) + gtk_widget_destroy (content_win); + done_with_widget_list (); + current_profile = NULL; +static void ap_edit_content_show () + gtk_window_present (GTK_WINDOW(content_win)); + /* Create the window */ + content_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_role (GTK_WINDOW(content_win), "ap_edit_content"); + gtk_window_set_title (GTK_WINDOW(content_win), _("Edit Content")); + gtk_window_set_default_size (GTK_WINDOW(content_win), 700, 550); + gtk_container_set_border_width (GTK_CONTAINER(content_win), 6); + g_signal_connect (G_OBJECT(content_win), "destroy", + G_CALLBACK(ap_edit_content_destroy), NULL); + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER(content_win), vbox); + notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0); + gtk_notebook_append_page (GTK_NOTEBOOK(notebook), + ap_widget_get_config_page (), gtk_label_new (_("Widgets"))); + gtk_notebook_append_page (GTK_NOTEBOOK(notebook), get_profile_page (), + gtk_label_new (_("Info/profile"))); + /* The buttons to press! */ + bbox = gtk_hbutton_box_new (); + gtk_box_set_spacing (GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); + gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + button = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + g_signal_connect (G_OBJECT(button), "clicked", + G_CALLBACK(ap_edit_content_destroy), NULL); + gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show_all (content_win); +/*--------------------------------------------------------------------------* + * The actions themselves * + *--------------------------------------------------------------------------*/ +static void edit_content (PurplePluginAction *action) + ap_edit_content_show (); +static void edit_preferences (PurplePluginAction *action) + ap_preferences_display (); +static void make_visible (PurplePluginAction *action) + ap_gtk_make_visible (); +/* Return the actions */ +GList *actions (PurplePlugin *plugin, gpointer context) + PurplePluginAction *act; + act = purple_plugin_action_new (_("Edit Content"), edit_content); + list = g_list_append (list, act); + act = purple_plugin_action_new (_("Preferences"), edit_preferences); + list = g_list_append (list, act); + act = purple_plugin_action_new (_("Show summary"), make_visible); + list = g_list_append (list, act); +void ap_actions_finish () + if (content_win) ap_edit_content_destroy (NULL, NULL); + if (accounts_dialog) accounts_response_cb (NULL, 0, NULL); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/gtk_away_msgs.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,486 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +/* VARIABLE DEFINITIONS */ +static guint queue_pref_cb = 0; +static guint sound_pref_cb = 0; +static gboolean ap_previously_away = FALSE; +/* The list containing data on generated profiles / status messages */ +static GtkListStore *message_list = NULL; +/* The general window */ +static GtkWidget *dialog = NULL; +typedef struct _ap_progress_bar { +static GHashTable *progress_bars = NULL; +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +static void hide_cb (GtkButton *button, gpointer data) { + gtk_widget_hide_all (dialog); + const char *name, PurplePrefType type, gconstpointer val, gpointer data) + const char *name, PurplePrefType type, gconstpointer val, gpointer data) + button = (GtkWidget *) data; + value = purple_prefs_get_bool ("/core/sound/while_away"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), value); +static void update_summary_visibility () + const gchar *summary_pref; + // Decide whether or not to show window + summary_pref = purple_prefs_get_string ( + "/plugins/gtk/autoprofile/show_summary"); + if (!strcmp (summary_pref, "always")) { + gtk_widget_show_all (dialog); + } else if (!strcmp (summary_pref, "away") && ap_is_currently_away ()) { + gtk_widget_show_all (dialog); + gtk_widget_hide_all (dialog); + ap_previously_away = ap_is_currently_away (); +/*--------------------------------------------------------------------------* + * Displayed message stuff * + *--------------------------------------------------------------------------*/ +static void display_diff_msg (GtkTreeSelection *select, gpointer data) + GtkWidget *imhtml = (GtkWidget *) data; + if (gtk_tree_selection_get_selected (select, &model, &iter)) + gtk_tree_model_get (model, &iter, 3, &string, -1); + gtk_imhtml_clear (GTK_IMHTML(imhtml)); + gtk_imhtml_append_text (GTK_IMHTML(imhtml), string, + gtk_imhtml_append_text (GTK_IMHTML(imhtml), "<BR>", +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +static APProgressBar *progress_create (APUpdateType type, + APProgressBar *progress_bar; + progress_bar = (APProgressBar *) malloc (sizeof (APProgressBar)); + progress_bar->timeout = 0; + progress_bar->type = type; + progress_bar->bar = gtk_progress_bar_new (); + gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR(progress_bar->bar), GTK_PROGRESS_CONTINUOUS); + gtk_box_pack_start (GTK_BOX(container), progress_bar->bar, FALSE, FALSE, 0); + if (type == AP_UPDATE_PROFILE) { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), + _("no updates made to profile")); + } else if (type == AP_UPDATE_STATUS) { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), + _("no updates made to status")); + g_hash_table_insert (progress_bars, GINT_TO_POINTER(type), progress_bar); +static void progress_update_stop (APProgressBar *progress_bar) { + if (progress_bar->timeout) { + purple_timeout_remove (progress_bar->timeout); + progress_bar->timeout = 0; + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar->bar), 1.0); + if (progress_bar->type == AP_UPDATE_PROFILE) { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), + _("waiting for new profile content")); + } else if (progress_bar->type == AP_UPDATE_STATUS) { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), + _("waiting for new status content")); +static gboolean progress_update (gpointer data) { + int total_milliseconds; + double fraction_increment; + APProgressBar *progress_bar = (APProgressBar *) data; + // Update fraction on bar + purple_prefs_get_int ("/plugins/gtk/autoprofile/delay_update") * 1000; + fraction_increment = BAH / ((double) total_milliseconds); + cur_fraction = gtk_progress_bar_get_fraction ( + GTK_PROGRESS_BAR(progress_bar->bar)); + result = cur_fraction + fraction_increment; + progress_update_stop (progress_bar); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar->bar), result); + seconds_remaining = (int) + (((double) total_milliseconds / 1000) - + (cur_fraction * (double) total_milliseconds / 1000)); + text = g_string_new (""); + if (progress_bar->type == AP_UPDATE_PROFILE) { + g_string_printf (text, _("next profile update in %d seconds"), + } else if (progress_bar->type == AP_UPDATE_STATUS) { + g_string_printf (text, _("next status update in %d seconds"), + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress_bar->bar), text->str); + g_string_free (text, TRUE); +static void ap_gtk_timeout_start (APUpdateType type) { + APProgressBar *progress_bar; + progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type)); + if (progress_bar->timeout) { + purple_timeout_remove (progress_bar->timeout); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar->bar), 0); + progress_bar->timeout = + purple_timeout_add (BAH, progress_update, progress_bar); + progress_update (progress_bar); +void ap_gtk_set_progress_visible (APUpdateType type, gboolean visible) + APProgressBar *progress_bar; + progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type)); + if (visible) gtk_widget_show (progress_bar->bar); + else gtk_widget_hide (progress_bar->bar); +/*--------------------------------------------------------------------------* + * Create the main window * + *--------------------------------------------------------------------------*/ +static void create_dialog () { + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkWidget *message_list_view; + GtkWidget *vbox, *vpane, *hbox, *config_vbox; + GtkWidget *sw, *imhtml, *msg_window, *button; + imhtml = gtk_imhtml_new (NULL, NULL); + /* Create main display window */ + gtk_window_set_title (GTK_WINDOW(dialog), _("AutoProfile Summary")); + gtk_widget_realize (dialog); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (dialog), vbox); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + /* Set up progress bar container */ + progress_create (AP_UPDATE_PROFILE, vbox); + progress_create (AP_UPDATE_STATUS, vbox); + /* Set up list of past away messages */ + vpane = gtk_vpaned_new (); + gtk_box_pack_start (GTK_BOX(vbox), vpane, TRUE, TRUE, 0); + message_list = gtk_list_store_new (4, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + message_list_view = gtk_tree_view_new_with_model ( + GTK_TREE_MODEL (message_list)); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ( + _("Time"), renderer, "markup", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column); + gtk_tree_view_column_set_sort_column_id (column, 0); + column = gtk_tree_view_column_new_with_attributes ( + _("Type"), renderer, "markup", 1, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column); + gtk_tree_view_column_set_sort_column_id (column, 1); + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + column = gtk_tree_view_column_new_with_attributes ( + _("Text"), renderer, "markup", 2, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (message_list_view), column); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_container_add (GTK_CONTAINER (sw), message_list_view); + gtk_paned_add1 (GTK_PANED(vpane), sw); + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (message_list_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (display_diff_msg), imhtml); + /* Set up the window to display away message in */ + msg_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(msg_window), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(msg_window), + gtk_paned_add2 (GTK_PANED(vpane), msg_window); + gtk_container_add (GTK_CONTAINER(msg_window), imhtml); + pidgin_setup_imhtml (imhtml); + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + config_vbox = gtk_vbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX(hbox), config_vbox, TRUE, TRUE, 0); + pidgin_prefs_checkbox ( + _("Queue new messages while away"), + "/plugins/gtk/autoprofile/queue_messages_when_away", + button = pidgin_prefs_checkbox ( + _("Play sounds while away"), + "/core/sound/while_away", + sound_pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (), + "/core/sound/while_away", sound_cb, button); + gtk_box_pack_start (GTK_BOX(hbox), gtk_vseparator_new (), FALSE, FALSE, 0); + config_vbox = gtk_vbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX(hbox), config_vbox, TRUE, TRUE, 0); + ap_gtk_prefs_add_summary_option (config_vbox); + button = gtk_button_new_with_label (_("Hide summary now")); + gtk_box_pack_start (GTK_BOX(config_vbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (hide_cb), NULL); + g_signal_connect (G_OBJECT(dialog), "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + gtk_paned_set_position (GTK_PANED(vpane), 250); + gtk_window_set_default_size (GTK_WINDOW(dialog), 430, 430); +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +void ap_gtk_add_message (APUpdateType update_type, APMessageType type, + char *time_string, *simple_text, *s; + // Create the time string + general_time = (time_t *) malloc (sizeof(time_t)); + cur_time = ap_localtime (general_time); + time_string = (char *) malloc (sizeof(char[32])); + strftime (time_string, 31, "<b>%I:%M %p</b>", cur_time); + // Create the type string + type_string = strdup("<b>Status</b>"); + case AP_MESSAGE_TYPE_PROFILE: + type_string = strdup (_("<b>User profile</b>")); + case AP_MESSAGE_TYPE_AWAY: + type_string = strdup (_("<b>Away message</b>")); + case AP_MESSAGE_TYPE_AVAILABLE: + type_string = strdup (_("<b>Available message</b>")); + case AP_MESSAGE_TYPE_STATUS: + type_string = strdup (_("<b>Status message</b>")); + type_string = strdup (_("<b>Other</b>")); + simple_text = strdup (text); + // Only show the first line + s = (gchar *) purple_strcasestr (simple_text, "<br>"); + simple_text = purple_markup_strip_html (simple_text); + gtk_list_store_prepend (message_list, &iter); + gtk_list_store_set (message_list, &iter, + if (simple_text) free (simple_text); + if (gtk_tree_model_iter_nth_child + (GTK_TREE_MODEL(message_list), &iter, NULL, + AP_GTK_MAX_MESSAGES)) { + gtk_list_store_remove (message_list, &iter); + // Move the timeout bar + ap_gtk_timeout_start (update_type); + // Check if it needs to be visible or not + if (type != AP_MESSAGE_TYPE_PROFILE && + ap_is_currently_away () != ap_previously_away) { + update_summary_visibility (); +void ap_gtk_make_visible () + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW(dialog)); + progress_bars = g_hash_table_new (NULL, NULL); + queue_pref_cb = purple_prefs_connect_callback ( + ap_get_plugin_handle (), + "/plugins/gtk/autoprofile/queue_messages_when_away", queue_cb, NULL); + update_summary_visibility (); +static void ap_gtk_finish_progress_bar (APUpdateType type) + APProgressBar *progress_bar; + progress_bar = g_hash_table_lookup (progress_bars, GINT_TO_POINTER(type)); + if (progress_bar->timeout) { + purple_timeout_remove (progress_bar->timeout); + g_hash_table_insert (progress_bars, GINT_TO_POINTER(type), NULL); + // Kill the window and associated variables + gtk_widget_destroy (dialog); + ap_gtk_finish_progress_bar (AP_UPDATE_PROFILE); + ap_gtk_finish_progress_bar (AP_UPDATE_STATUS); + // Disconnect queue message + purple_prefs_disconnect_callback (queue_pref_cb); + purple_prefs_disconnect_callback (sound_pref_cb); + g_hash_table_destroy (progress_bars); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/gtk_widget.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,778 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#define AP_RESPONSE_CHOOSE 98125 +static GtkWidget *dialog_box = NULL; +static GtkWidget *dialog_box_contents = NULL; +static GtkWidget *dialog_box_preview = NULL; +static struct widget *dialog_box_widget = NULL; +static GStaticMutex preview_mutex = G_STATIC_MUTEX_INIT; +static GtkWidget *component_dialog = NULL; +static GtkWidget *choose_button = NULL; +static GtkWidget *widget_dialog = NULL; +static GtkWidget *delete_button = NULL; +static GtkWidget *rename_button = NULL; +static GtkListStore *tree_list = NULL; +static GHashTable *pref_names = NULL; +static void component_dialog_show (); +void ap_widget_prefs_updated (struct widget *w) { + if (dialog_box_preview == NULL) return; + if (w != dialog_box_widget) return; + // TODO: Investigate how laggy this is, possibly add a timeout + output = w->component->generate (w); + g_static_mutex_lock (&preview_mutex); + gtk_imhtml_clear (GTK_IMHTML(dialog_box_preview)); + gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview), output, + g_static_mutex_unlock (&preview_mutex); +static void update_widget_list (GtkListStore *ls) { + GList *widgets, *widgets_start; + gtk_list_store_clear (ls); + widgets_start = widgets = ap_widget_get_widgets (); + for (widgets = widgets_start; widgets != NULL; widgets = widgets->next) { + gtk_list_store_append (ls, &iter); + w = (struct widget *) widgets->data; + g_string_printf (s, "<b>%s</b>", w->alias); + gtk_list_store_set (ls, &iter, + g_list_free (widgets_start); + g_string_free (s, TRUE); +static void refresh_cb (GtkWidget *widget, gpointer data) { + w = (struct widget *) data; + ap_widget_prefs_updated (w); +/* Widget configuration menu */ +static GtkWidget *get_widget_configuration (struct widget *w) { + GtkWidget *config, *hbox, *vbox, *sw; + GtkWidget *label, *button; + config = gtk_vbox_new (FALSE, 0); + /* Title/Description */ + hbox = gtk_hbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER(hbox), 6); + gtk_box_pack_start (GTK_BOX(config), hbox, FALSE, FALSE, 0); + g_string_printf (s, "<b>%s:</b> %s", w->component->name, + w->component->description); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(label), s->str); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + g_string_free (s, TRUE); + gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER(vbox), 6); + gtk_box_pack_start (GTK_BOX(config), vbox, FALSE, FALSE, 0); + hbox = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(label), _("<b>Preview</b>")); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0); + button = gtk_button_new_with_label (_("Refresh")); + gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (refresh_cb), w); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_box_pack_start (GTK_BOX(vbox), sw, TRUE, TRUE, 0); + dialog_box_preview = gtk_imhtml_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(sw), dialog_box_preview); + pidgin_setup_imhtml (dialog_box_preview); + output = w->component->generate (w); + gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview), + output, GTK_IMHTML_NO_SCROLL); + gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), + /* Configuration stuff */ + vbox = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER(vbox), 6); + gtk_box_pack_start (GTK_BOX(config), vbox, TRUE, TRUE, 0); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(label), _("<b>Configuration</b>")); + gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + if (w->component->pref_menu == NULL || + (menu = (w->component->pref_menu) (w)) == NULL) { + label = gtk_label_new (_("No options available for this component")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), menu, TRUE, TRUE, 0); +static GtkWidget *get_info_message () { + page = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (page), 12); + aboutwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(aboutwin), + gtk_box_pack_start (GTK_BOX(page), aboutwin, TRUE, TRUE, 0); + text = gtk_imhtml_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(aboutwin), text); + pidgin_setup_imhtml (text); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<b><u>Basic info</u></b><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("A <b>widget</b> is a little piece/snippet of automatically " + "generated text. There are all sorts of widgets; each type has " + "different content (i.e. a random quote, text from a blog, the " + "song currently playing, etc).<br><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("To use a widget, simply drag it from the list on the left and " + "drop it into a profile or status message. <i>It's that easy!</i>" + "<br><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<b>To edit your profile:</b> " + "Use the \"Info/profile\" tab in this window.<br>"), + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<b>To edit your available/away/status message:</b> " + "Use the regular Purple interface built into the bottom of the buddy " + "list.<br><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<b><u>Advanced Tips</u></b><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("You can insert a widget into a profile or status by typing its name. " + "To do this, just type \"[widget-name]\" wherever you want to " + "place a widget (names of widgets are listed on the left). <br><br>" + "<b>You type:</b> The song I am playing now is [iTunesInfo].<br>" + "<b>AutoProfile result:</b> The song I am playing now is " + "The Beatles - Yellow Submarine.<br><br>"), GTK_IMHTML_NO_SCROLL); +/* Dialog window actions */ +static void widget_popup_rename_cb ( + struct widget *w, const char *new_text) { + struct widget *cur_widget; + gtk_tree_model_get_iter_first (GTK_TREE_MODEL(tree_list), &iter); + gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val); + cur_widget = g_value_get_pointer(&val); + if (cur_widget == w) break; + if (!gtk_tree_model_iter_next (GTK_TREE_MODEL(tree_list), &iter)) { + purple_notify_error (NULL, NULL, + N_("Unable to change name"), + N_("The specified widget no longer exists.")); + if (ap_widget_rename (w, new_text)) { + g_string_printf (s, "<b>%s</b>", w->alias); + gtk_list_store_set (tree_list, &iter, + g_string_free (s, TRUE); + purple_notify_error (NULL, NULL, + N_("Unable to change name"), + N_("The widget name you have specified is already in use.")); +static void delete_cb (GtkWidget *button, GtkTreeSelection *sel) + gtk_tree_selection_get_selected (sel, &model, &iter); + gtk_tree_model_get_value (model, &iter, 1, &val); + w = g_value_get_pointer(&val); + gtk_list_store_remove (GTK_LIST_STORE(model), &iter); +static void rename_cb (GtkWidget *button, GtkTreeSelection *sel) + gtk_tree_selection_get_selected (sel, &model, &iter); + gtk_tree_model_get_value (model, &iter, 1, &val); + w = g_value_get_pointer(&val); + purple_request_input(NULL, + _("Rename Widget"), NULL, + _("Enter a new name for this widget."), w->alias, + _("Rename"), G_CALLBACK(widget_popup_rename_cb), + _("Cancel"), NULL, NULL, NULL, NULL, w); +static void add_cb (GtkWidget *button, GtkTreeSelection *sel) + component_dialog_show (); +void ap_widget_gtk_start () { + pref_names = g_hash_table_new (g_str_hash, g_str_equal); +void ap_widget_gtk_finish () { + done_with_widget_list (); + g_hash_table_destroy (pref_names); +static void widget_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { + gtk_widget_destroy (dialog_box_contents); + if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { + gtk_widget_set_sensitive(rename_button, FALSE); + gtk_widget_set_sensitive(delete_button, FALSE); + dialog_box_contents = get_info_message (); + dialog_box_preview = NULL; + dialog_box_widget = NULL; + gtk_widget_set_sensitive(rename_button, TRUE); + gtk_widget_set_sensitive(delete_button, TRUE); + gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val); + w = g_value_get_pointer(&val); + dialog_box_contents = get_widget_configuration (w); + gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents, + gtk_widget_show_all (dialog_box); +GtkWidget *ap_widget_get_config_page () + /* Arrange main parts of window */ + dialog_box = gtk_hbox_new (FALSE, 0); + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX(dialog_box), vbox, FALSE, FALSE, 0); + get_widget_list (vbox, &sel); + g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (widget_sel_cb), + add_button = gtk_button_new_with_label (_("New Widget")); + g_signal_connect (G_OBJECT(add_button), "clicked", + G_CALLBACK(add_cb), sel); + gtk_box_pack_start (GTK_BOX(vbox), add_button, FALSE, FALSE, 0); + rename_button = gtk_button_new_with_label (_("Rename")); + gtk_widget_set_sensitive(rename_button, FALSE); + g_signal_connect (G_OBJECT(rename_button), "clicked", + G_CALLBACK(rename_cb), sel); + gtk_box_pack_start (GTK_BOX(vbox), rename_button, FALSE, FALSE, 0); + delete_button = gtk_button_new_with_label (_("Delete")); + gtk_widget_set_sensitive(delete_button, FALSE); + g_signal_connect (G_OBJECT(delete_button), "clicked", + G_CALLBACK(delete_cb), sel); + gtk_box_pack_start (GTK_BOX(vbox), delete_button, FALSE, FALSE, 0); + dialog_box_contents = get_info_message (); + gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents, +drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, + GtkSelectionData *data, guint info, guint time, + GtkListStore *ls = (GtkListStore *) user_data; + if (ls == NULL) return; + if (data->target == gdk_atom_intern ("STRING", FALSE)) { + GtkTreeRowReference *ref; + GtkTreePath *source_row; + ref = g_object_get_data (G_OBJECT(ctx), "gtk-tree-view-source-row"); + source_row = gtk_tree_row_reference_get_path (ref); + if (source_row == NULL) return; + gtk_tree_model_get_iter(GTK_TREE_MODEL(ls), &iter, source_row); + gtk_tree_model_get_value(GTK_TREE_MODEL(ls), &iter, + w = g_value_get_pointer (&val); + g_string_printf (s, "[%s]", w->alias); + gtk_selection_data_set (data, gdk_atom_intern ("STRING", FALSE), + 8, s->str, strlen(s->str)+1); + g_string_free (s, TRUE); + gtk_tree_path_free (source_row); +void done_with_widget_list () { + g_object_unref (tree_list); + dialog_box_contents = NULL; + dialog_box_preview = NULL; + dialog_box_widget = NULL; + if (component_dialog != NULL) { + gtk_widget_destroy (component_dialog); + component_dialog = NULL; +GtkWidget *get_widget_list (GtkWidget *box, GtkTreeSelection **sel) + GtkTreeViewColumn *col; + GtkTargetEntry gte [] = {{"STRING", 0, GTK_IMHTML_DRAG_STRING}}; + if (tree_list == NULL) { + tree_list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(tree_list), + 0, GTK_SORT_ASCENDING); + update_widget_list (tree_list); + g_object_ref (G_OBJECT(tree_list)); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_box_pack_start (GTK_BOX(box), sw, TRUE, TRUE, 0); + event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_list)); + *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view)); + rend = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("Widget"), rend, + gtk_tree_view_append_column (GTK_TREE_VIEW (event_view), col); + gtk_tree_view_column_set_sort_column_id (col, 0); + gtk_container_add (GTK_CONTAINER(sw), event_view); + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(event_view), GDK_BUTTON1_MASK, gte, + g_signal_connect(G_OBJECT(event_view), "drag-data-get", + G_CALLBACK(drag_data_get_cb), tree_list); +/********************************************************* + Component selection window +**********************************************************/ +static void add_component (struct component *c) { + w = ap_widget_create (c); + gtk_list_store_append (tree_list, &iter); + g_string_printf (s, "<b>%s</b>", w->alias); + gtk_list_store_set (tree_list, &iter, + g_string_free (s, TRUE); +static void component_row_activate_cb (GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer null) + sel = gtk_tree_view_get_selection (view); + if (gtk_tree_selection_get_selected (sel, &model, &iter)) { + gtk_tree_model_get (model, &iter, 1, &c, -1); + gtk_widget_destroy (component_dialog); + component_dialog = NULL; +static void component_response_cb(GtkWidget *d, int response, + case AP_RESPONSE_CHOOSE: + gtk_tree_selection_get_selected (sel, &model, &iter); + gtk_tree_model_get_value (model, &iter, 1, &val); + c = g_value_get_pointer(&val); + case GTK_RESPONSE_CLOSE: + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + component_dialog = NULL; +static void component_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { + if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { + gtk_widget_set_sensitive(choose_button, FALSE); + gtk_widget_set_sensitive(choose_button, TRUE); +static void update_component_list (GtkListStore *ls) { + gchar *name, *description; + gtk_list_store_clear (ls); + for (components = ap_component_get_components (); + components = components->next) { + gtk_list_store_append (ls, &iter); + c = (struct component *) components->data; + name = g_markup_escape_text (c->name, -1); + description = g_markup_escape_text (c->description, -1); + g_string_printf (s, "<b>%s</b>\n%s", name, description); + gtk_list_store_set (ls, &iter, + g_string_free (s, TRUE); +static void component_dialog_show () + GtkCellRenderer *rendt; + GtkTreeViewColumn *col; + if (component_dialog != NULL) { + gtk_window_present(GTK_WINDOW(component_dialog)); + component_dialog = gtk_dialog_new_with_buttons (_("Select a widget type"), + GTK_DIALOG_NO_SEPARATOR, + choose_button = gtk_dialog_add_button (GTK_DIALOG(component_dialog), + _("Create widget"), AP_RESPONSE_CHOOSE); + gtk_dialog_add_button (GTK_DIALOG(component_dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_widget_set_sensitive (choose_button, FALSE); + sw = gtk_scrolled_window_new (NULL,NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(component_dialog)->vbox), sw, + ls = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(ls), + 0, GTK_SORT_ASCENDING); + update_component_list (ls); + event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls)); + g_signal_connect(G_OBJECT(event_view), "row-activated", + G_CALLBACK(component_row_activate_cb), event_view); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view)); + rendt = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("Widget type"), +#if GTK_CHECK_VERSION(2,6,0) + gtk_tree_view_column_set_expand (col, TRUE); + g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col); + gtk_tree_view_column_set_sort_column_id (col, 0); + g_object_unref (G_OBJECT(ls)); + gtk_container_add (GTK_CONTAINER(sw), event_view); + g_signal_connect (G_OBJECT (sel), "changed", + G_CALLBACK (component_sel_cb), NULL); + g_signal_connect(G_OBJECT(component_dialog), "response", + G_CALLBACK(component_response_cb), sel); + gtk_window_set_default_size(GTK_WINDOW(component_dialog), 550, 430); + gtk_widget_show_all(component_dialog); +static void pref_callback (const char *name, PurplePrefType type, + gconstpointer val, gpointer data) { + struct widget *w = (struct widget *) data; + ap_widget_prefs_updated (w); +static const gchar *get_const_pref (struct widget *w, const char *key) { + // This is here to prevent memory leaks + pref = ap_prefs_get_pref_name (w, key); + if (pref_names == NULL) { + pref_names = g_hash_table_new (g_str_hash, g_str_equal); + result = g_hash_table_lookup (pref_names, pref); + g_hash_table_insert (pref_names, pref, pref); +GtkWidget *ap_prefs_checkbox (struct widget *w, const char *title, + const char *key, GtkWidget *page) + pref = get_const_pref (w, key); + result = pidgin_prefs_checkbox (title, pref, page); + purple_prefs_connect_callback (ap_get_plugin_handle (), pref, +GtkWidget *ap_prefs_dropdown_from_list (struct widget *w, GtkWidget *page, + const gchar *title, PurplePrefType type, const char *key, GList *menuitems) + pref = get_const_pref (w, key); + result = pidgin_prefs_dropdown_from_list ( + page, title, type, pref, menuitems); + purple_prefs_connect_callback (ap_get_plugin_handle (), pref, +GtkWidget *ap_prefs_labeled_entry (struct widget *w, GtkWidget *page, + const gchar *title, const char *key, GtkSizeGroup *sg) { + pref = get_const_pref (w, key); + result = pidgin_prefs_labeled_entry (page, title, pref, sg); + purple_prefs_connect_callback (ap_get_plugin_handle (), pref, +GtkWidget *ap_prefs_labeled_spin_button (struct widget *w, + GtkWidget *page, const gchar *title, const char *key, int min, + int max, GtkSizeGroup *sg) + pref = get_const_pref (w, key); + result = pidgin_prefs_labeled_spin_button (page, title, pref, + purple_prefs_connect_callback (ap_get_plugin_handle (), pref, --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/preferences.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,750 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +#include "gtksavedstatuses.h" +/*--------------------------------------------------------------------------* + *--------------------------------------------------------------------------*/ +static GtkWidget *get_info_page () { + gchar *labeltext, *str; + page = gtk_vbox_new (FALSE, 5); + gtk_container_set_border_width (GTK_CONTAINER (page), 5); + /* AutoProfile title */ + labeltext = g_strdup_printf ( + _("<span weight=\"bold\" size=\"larger\">AutoProfile %s</span>"), + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL(label), labeltext); + gtk_label_set_line_wrap (GTK_LABEL(label), TRUE); + gtk_misc_set_alignment (GTK_MISC(label), 0.5, 0); + gtk_box_pack_start (GTK_BOX(page), label, FALSE, FALSE, 0); + aboutwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(aboutwin), + gtk_box_pack_start (GTK_BOX(page), aboutwin, TRUE, TRUE, 0); + text = gtk_imhtml_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(aboutwin), text); + pidgin_setup_imhtml (text); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("Use the <b>Autoprofile</b> portion of the <b>Tools</b> " + "buddy list</b> to configure the actual content that will go in your " + "status messages and profiles and set options.<br><br>"), + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<u>DOCUMENTATION / HELP</u><br>"), GTK_IMHTML_NO_SCROLL); + gtk_imhtml_append_text (GTK_IMHTML(text), + _("Complete documentation can be found at:<br> <a href=" + "\"http://hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php\">" + "hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php</a><br>"), + gtk_imhtml_append_text (GTK_IMHTML(text), + _("<br><u>ABOUT</u><br>"), GTK_IMHTML_NO_SCROLL); + "<font size=\"3\"><b>", _("Developers"), ":</b></font><br>" + " Casey Ho (Lead Developer)<br>" + " Mitchell Harwell<br>", NULL); + gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); + "<font size=\"3\"><b>", _("Contributors/Patchers"), ":</b></font><br>" + " Michael Milligan<br>" + " Mark Painter<br>", NULL); + gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); + "<font size=\"3\"><b>", _("Website"), ":</b></font><br>" + " <a href=\"http://autoprofile.sourceforge.net\">" + "autoprofile.sourceforge.net<br>", NULL); + gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); +/*---------------------------------------------------------------------------- + *--------------------------------------------------------------------------*/ +/* PRIMARILY RIPPED FROM GAIM GTKACCOUNT.C */ + PurpleAccount *account; +} PidginAccountAddUserData; + GtkTreeViewColumn *screenname_col; +static void add_account_to_liststore(PurpleAccount *, gpointer); +static void set_account(GtkListStore *, GtkTreeIter *, PurpleAccount *); +static gboolean is_profile_settable (PurpleAccount *a) { + const gchar *id = purple_account_get_protocol_id (a); + if (!strcmp (id, "prpl-yahoo") || + !strcmp (id, "prpl-msn") || + !strcmp (id, "prpl-jabber")) { +drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, + GtkSelectionData *data, guint info, guint time, + AccountsWindow *dialog) + if (data->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) { + GtkTreeRowReference *ref; + GtkTreePath *source_row; + PurpleAccount *account = NULL; + ref = g_object_get_data(G_OBJECT(ctx), "gtk-tree-view-source-row"); + source_row = gtk_tree_row_reference_get_path(ref); + if (source_row == NULL) return; + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, source_row); + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, + dialog->drag_iter = iter; + account = g_value_get_pointer(&val); + gtk_selection_data_set(data, gdk_atom_intern("PURPLE_ACCOUNT", FALSE), + 8, (void *)&account, sizeof(account)); + gtk_tree_path_free(source_row); +move_account_after(GtkListStore *store, GtkTreeIter *iter, + PurpleAccount *account; + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1); + gtk_list_store_insert_after(store, &new_iter, position); + set_account(store, &new_iter, account); + gtk_list_store_remove(store, iter); +move_account_before(GtkListStore *store, GtkTreeIter *iter, + PurpleAccount *account; + gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1); + gtk_list_store_insert_before(store, &new_iter, position); + set_account(store, &new_iter, account); + gtk_list_store_remove(store, iter); +drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx, + guint x, guint y, GtkSelectionData *sd, + guint info, guint t, AccountsWindow *dialog) + if (sd->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && sd->data) { + PurpleAccount *a = NULL; + GtkTreePath *path = NULL; + GtkTreeViewDropPosition position; + memcpy(&a, sd->data, sizeof(a)); + if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, + PurpleAccount *account; + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, + account = g_value_get_pointer(&val); + case GTK_TREE_VIEW_DROP_AFTER: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + move_account_after(dialog->model, &dialog->drag_iter, &iter); + dest_index = g_list_index(purple_accounts_get_all(), account) + 1; + case GTK_TREE_VIEW_DROP_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + dest_index = g_list_index(purple_accounts_get_all(), account); + move_account_before(dialog->model, &dialog->drag_iter, &iter); + purple_accounts_reorder(a, dest_index); +enabled_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) + AccountsWindow *dialog = (AccountsWindow *)data; + PurpleAccount *account; + GtkTreeModel *model = GTK_TREE_MODEL(dialog->model); + gtk_tree_model_get_iter_from_string(model, &iter, path_str); + gtk_tree_model_get(model, &iter, + COLUMN_ENABLED, &enabled, + /* Change profile settings */ + ap_account_enable_profile (account, !enabled); + set_account (dialog->model, &iter, account); +add_columns(GtkWidget *treeview, AccountsWindow *dialog) + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + /* Screen Name column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Screen Name")); + gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COLUMN_ICON); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, + "text", COLUMN_SCREENNAME); + dialog->screenname_col = column; + renderer = gtk_cell_renderer_toggle_new(); + g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(enabled_cb), dialog); + gtk_tree_view_column_new_with_attributes(_("AutoProfile sets user info"), + renderer, "active", COLUMN_ENABLED, NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Protocol")); + gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, + "text", COLUMN_PROTOCOL); +set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account) + pixbuf = pidgin_create_prpl_icon(account, 0.5); + scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); + if (purple_account_is_disconnected(account)) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); + gtk_list_store_set(store, iter, + COLUMN_SCREENNAME, purple_account_get_username(account), + COLUMN_ENABLED, ap_account_has_profile_enabled(account), + COLUMN_PROTOCOL, purple_account_get_protocol_name(account), + if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); + if (scale != NULL) g_object_unref(G_OBJECT(scale)); +add_account_to_liststore(PurpleAccount *account, gpointer user_data) + AccountsWindow *dialog = (AccountsWindow *) user_data; + if (dialog == NULL) return; + if (!is_profile_settable (account)) return; + gtk_list_store_append(dialog->model, &iter); + set_account(dialog->model, &iter, account); +populate_accounts_list(AccountsWindow *dialog) + gtk_list_store_clear(dialog->model); + for (l = purple_accounts_get_all(); l != NULL; l = l->next) + add_account_to_liststore((PurpleAccount *)l->data, dialog); +#if !GTK_CHECK_VERSION(2,2,0) +get_selected_helper(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer user_data) + *((gboolean *)user_data) = TRUE; +account_selected_cb(GtkTreeSelection *sel, AccountsWindow *dialog) + gboolean selected = FALSE; +#if GTK_CHECK_VERSION(2,2,0) + selected = (gtk_tree_selection_count_selected_rows(sel) > 0); + gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected); +create_accounts_list(AccountsWindow *dialog) + GtkTargetEntry gte[] = {{"PURPLE_ACCOUNT", GTK_TARGET_SAME_APP, 0}}; + /* Create the scrolled window. */ + sw = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); + /* Create the list model. */ + dialog->model = gtk_list_store_new(NUM_COLUMNS, + GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, + /* And now the actual treeview */ + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); + dialog->treeview = treeview; + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(account_selected_cb), dialog); + gtk_container_add(GTK_CONTAINER(sw), treeview); + gtk_widget_show(treeview); + add_columns(treeview, dialog); + populate_accounts_list(dialog); + /* Setup DND. I wanna be an orc! */ + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte, + gtk_tree_view_enable_model_drag_dest( + GTK_TREE_VIEW(treeview), gte, 1, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(G_OBJECT(treeview), "drag-data-received", + G_CALLBACK(drag_data_received_cb), dialog); + g_signal_connect(G_OBJECT(treeview), "drag-data-get", + G_CALLBACK(drag_data_get_cb), dialog); +static void account_page_delete_cb (GtkObject *object, gpointer data) +GtkWidget *get_account_page () { + AccountsWindow *accounts_window; + page = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (page), 12); + accounts_window = g_new0(AccountsWindow, 1); + /* Setup the scrolled window that will contain the list of accounts. */ + sw = create_accounts_list(accounts_window); + gtk_box_pack_start(GTK_BOX(page), sw, TRUE, TRUE, 0); + label = gtk_label_new ( + _("Accounts that do not support user-specified profiles are not shown")); + gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (page), "destroy", + G_CALLBACK (account_page_delete_cb), accounts_window); +/*---------------------------------------------------------------------------- + *--------------------------------------------------------------------------*/ +void ap_gtk_prefs_add_summary_option (GtkWidget *widget) { + pidgin_prefs_dropdown (widget, + "Show AutoProfile summary window", + "/plugins/gtk/autoprofile/show_summary", + "Always", "always", "When away", "away", "Never", "never", NULL); +set_idle_away(PurpleSavedStatus *status) + purple_prefs_set_int("/core/savedstatus/idleaway", + purple_savedstatus_get_creation_time(status)); +static GtkWidget *get_behavior_page () { + GtkWidget *frame, *vbox, *hbox; + GtkWidget *button, *select, *menu; + page = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (page), 12); + /*---------- Update frequency ----------*/ + frame = pidgin_make_frame (page, _("Update frequency")); + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (frame), vbox); + pidgin_prefs_labeled_spin_button (vbox, + _("Minimum number of seconds between updates"), + "/plugins/gtk/autoprofile/delay_update", + label = gtk_label_new (""); + markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", + _("WARNING: Using values below 60 seconds may increase the frequency\n" + "of rate limiting errors")); + gtk_label_set_markup (GTK_LABEL (label), markup); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + /*----------- Auto-away stuff ------------*/ + frame = pidgin_make_frame(page, _("Auto-away")); + button = pidgin_prefs_checkbox(_("Change status when idle"), + "/plugins/gtk/autoprofile/away_when_idle", frame); + sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + select = pidgin_prefs_labeled_spin_button(frame, + _("Minutes before changing status:"), "/core/away/mins_before_away", + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), select); + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), hbox); + label = gtk_label_new_with_mnemonic(_("Change status to:")); + gtk_size_group_add_widget(sg, label); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + /* TODO: Show something useful if we don't have any saved statuses. */ + menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), + G_CALLBACK(set_idle_away)); + gtk_box_pack_start(GTK_BOX(frame), menu, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), menu); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu); + if (!purple_prefs_get_bool("/plugins/gtk/autoprofile/away_when_idle")) { + gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(select), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE); +/*---------------------------------------------------------------------------- + *--------------------------------------------------------------------------*/ +/* Update string arguments */ +static gboolean update_behavior_string (GtkWidget *widget, GdkEventFocus *evt, + ap_debug ("preferences", "behavior string preference modified"); + if (!strcmp (data, "text_trigger")) { + purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/trigger", + gtk_entry_get_text (GTK_ENTRY (widget))); + } else if (!strcmp (data, "text_respond")) { + purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/text", + gtk_entry_get_text (GTK_ENTRY (widget))); + ap_debug_error ("preferences", "invalid data argument to string update"); +/* Update value returned from spinner for auto-respond delay */ +static gboolean update_delay_respond (GtkWidget *widget, GdkEventFocus *evt, + purple_prefs_set_int ("/plugins/gtk/autoprofile/delay_respond", + gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget))); +static GtkWidget *get_autoreply_page () { + GtkWidget *label, *checkbox, *spinner, *entry; + GtkWidget *frame, *vbox, *large_vbox, *hbox; + page = gtk_vbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (page), 12); + frame = pidgin_make_frame(page, _("General")); + dd = pidgin_prefs_dropdown(frame, _("Auto-reply:"), + PURPLE_PREF_STRING, "/plugins/gtk/autoprofile/autorespond/auto_reply", + _("When away"), "away", + _("When both away and idle"), "awayidle", + sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget(sg, dd); + gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); + /*---------- Auto-responses ----------*/ + frame = pidgin_make_frame (page, _("Dynamic auto-responses")); + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (frame), vbox); + /* Auto-response activated */ + checkbox = pidgin_prefs_checkbox ( + _("Allow users to request more auto-responses"), + "/plugins/gtk/autoprofile/autorespond/enable", vbox); + large_vbox = gtk_vbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (vbox), large_vbox, FALSE, FALSE, 0); + /* Auto-response delay */ + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (large_vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new (_("Delay")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + spinner = gtk_spin_button_new_with_range (1, G_MAXINT, 1); + gtk_box_pack_start (GTK_BOX (hbox), spinner, TRUE, TRUE, 0); + label = gtk_label_new (_("seconds between auto-responses")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), purple_prefs_get_int ( + "/plugins/gtk/autoprofile/autorespond/delay")); + g_signal_connect (G_OBJECT (spinner), "value-changed", + G_CALLBACK (update_delay_respond), NULL); + /* Auto-response message string */ + label = gtk_label_new (_("Message sent with first autoresponse:")); + gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0); + gtk_entry_set_max_length (GTK_ENTRY (entry), 100); + gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string ( + "/plugins/gtk/autoprofile/autorespond/text")); + g_signal_connect (G_OBJECT (entry), "focus-out-event", + G_CALLBACK (update_behavior_string), "text_respond"); + label = gtk_label_new (_("Request trigger message:")); + gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0); + gtk_entry_set_max_length (GTK_ENTRY (entry), 50); + gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string ( + "/plugins/gtk/autoprofile/autorespond/trigger")); + g_signal_connect (G_OBJECT (entry), "focus-out-event", + G_CALLBACK (update_behavior_string), "text_trigger"); + /* Sensitivity signals */ + g_signal_connect(G_OBJECT(checkbox), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), large_vbox); + if (!purple_prefs_get_bool ("/plugins/gtk/autoprofile/autorespond/enable")) { + gtk_widget_set_sensitive (large_vbox, FALSE); + gtk_widget_set_sensitive (large_vbox, TRUE); +/*---------------------------------------------------------------------------- + *--------------------------------------------------------------------------*/ +static GtkWidget *get_config_frame (PurplePlugin *plugin) + GtkWidget *info = get_info_page (); + gtk_widget_set_size_request (info, 350, 400); +static void dialog_cb (GtkDialog *dialog, gint arg1, gpointer user_data) + gtk_widget_destroy ((GtkWidget *)dialog); +void ap_preferences_display () + GtkWidget *dialog, *notebook; + notebook = gtk_notebook_new (); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + get_behavior_page (), gtk_label_new (_("General"))); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + get_account_page (), gtk_label_new (_("User info/profiles"))); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + get_autoreply_page (), gtk_label_new (_("Auto-reply"))); + g_object_set (notebook, "homogeneous", TRUE, NULL); + dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, NULL, + GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), notebook); + gtk_window_set_default_size (GTK_WINDOW(dialog), 400, 400); + gtk_widget_show_all (dialog); + g_signal_connect (G_OBJECT(dialog), "response", + G_CALLBACK(dialog_cb), dialog); +/*--------------- Generate the preference widget once ----------------*/ +PidginPluginUiInfo ui_info = --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/sizes.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,49 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +/* Size definitions (0 indicates not compatible) */ +#define AP_SIZE_MAXIMUM 2048 +#define AP_SIZE_PROFILE_MAX 2048 +#define AP_SIZE_PROFILE_AIM 2048 +#define AP_SIZE_PROFILE_ICQ 0 +#define AP_SIZE_PROFILE_JABBER 0 +#define AP_SIZE_PROFILE_MSN 0 +#define AP_SIZE_PROFILE_YAHOO 0 +/* Away message sizes */ +#define AP_SIZE_AWAY_MAX 2048 +#define AP_SIZE_AWAY_AIM 2048 +/* Available message sizes */ +#define AP_SIZE_AVAILABLE_MAX 512 +#define AP_SIZE_AVAILABLE_AIM 60 +#define AP_SIZE_AVAILABLE_ICQ 0 +#define AP_SIZE_AVAILABLE_MSN 0 +#define AP_SIZE_AVAILABLE_YAHOO 512 +/* End size definitions */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/utility.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,221 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "autoprofile.h" +/* TODO: get rid of this and port to glib time */ +static GStaticMutex time_mutex = G_STATIC_MUTEX_INIT; +static struct tm *ap_tm_copy (const struct tm *t) { + r = (struct tm *) malloc (sizeof (struct tm)); + r->tm_hour = t->tm_hour; + r->tm_mday = t->tm_mday; + r->tm_year = t->tm_year; + r->tm_wday = t->tm_wday; + r->tm_yday = t->tm_yday; + r->tm_isdst = t->tm_isdst; +struct tm *ap_localtime (const time_t *tp) { + g_static_mutex_lock (&time_mutex); + result = ap_tm_copy (localtime (tp)); + g_static_mutex_unlock (&time_mutex); +struct tm *ap_gmtime (const time_t *tp) { + g_static_mutex_lock (&time_mutex); + result = ap_tm_copy (gmtime (tp)); + g_static_mutex_unlock (&time_mutex); +/* Reads from fortune-style file and returns GList of each quote */ +static void fortune_helper (GString *s, gchar *data, gboolean escape_html) { + g_string_append_printf (s, "<br>"); + case '"': g_string_append_printf (s, """); return; + case '&': g_string_append_printf (s, "&"); return; + case '<': g_string_append_printf (s, "<"); return; + case '>': g_string_append_printf (s, ">"); return; + g_string_append_unichar (s, g_utf8_get_char (data)); +GList *read_fortune_file (const char *filename, gboolean escape_html) + gchar *raw_data, *raw_data_start; + gchar *converted, *text; + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + if (!g_file_get_contents (filename, &text, NULL, NULL)) { + converted = purple_utf8_try_convert (text); + if (converted != NULL) { + raw_data_start = raw_data = purple_utf8_salvage (text); + purple_str_strip_char (raw_data, '\r'); + /* Modeling the parser as a finite state machine */ + cur_quote = g_string_new (""); + /* State after newline (potential quote) */ + if (*raw_data == '%') { // Found it + quotes = g_list_append (quotes, strdup (cur_quote->str)); + g_string_truncate (cur_quote, 0); + g_string_append_printf (cur_quote, "<br>"); + fortune_helper (cur_quote, raw_data, escape_html); + /* State after end of a quote */ + if (*raw_data != '\n' && *raw_data != '%') { + fortune_helper (cur_quote, raw_data, escape_html); + if (*raw_data == '\n') { + fortune_helper (cur_quote, raw_data, escape_html); + raw_data = g_utf8_next_char (raw_data); + if (strlen (cur_quote->str) > 0) { + quotes = g_list_append (quotes, strdup (cur_quote->str)); + g_string_free (cur_quote, TRUE); +/* Returns 1 if a pattern is found at the start of a string */ +int match_start (const char *text, const char *pattern) + if (!*text || *pattern++ != *text++) +/* Free's a GList as well as the internal contents */ +void free_string_list (GList *list) +/* Check if string is in GList */ +gboolean string_list_find (GList *lst, const char *data) + if (!strcmp (data, (char *) lst->data)) { +/* Prints out debug messages with repetitive formatting completed */ +static void auto_debug_helper ( + PurpleDebugLevel level, const char *category, const char *message) + g_string_printf (s, "%s: %s\n", category, message); + purple_debug (level, "autoprofile", s->str); + g_string_free (s, TRUE); +void ap_debug (const char *category, const char *message) { + auto_debug_helper (PURPLE_DEBUG_INFO, category, message); +void ap_debug_misc (const char *category, const char *message) { + auto_debug_helper (PURPLE_DEBUG_MISC, category, message); +void ap_debug_warn (const char *category, const char *message) { + auto_debug_helper (PURPLE_DEBUG_WARNING, category, message); +void ap_debug_error (const char *category, const char *message) { + auto_debug_helper (PURPLE_DEBUG_ERROR, category, message); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/utility.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,41 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +struct tm *ap_localtime (const time_t *); +struct tm *ap_gmtime (const time_t *); +GList *read_fortune_file (const char *, gboolean); +int match_start (const char *, const char *); +void free_string_list (GList *); +gboolean string_list_find (GList *, const char *); +void ap_debug (const char *, const char *); +void ap_debug_misc (const char *, const char *); +void ap_debug_warn (const char *, const char *); +void ap_debug_error (const char *, const char *); +time_t rfc_parse_date_time (const char *data); +int rfc_parse_was_gmt (); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/utility_rfc822.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,187 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +static struct tm parsed_datetime; +static int parsed_gmttime = 0; +/* Strip leading whitespace */ +static const char* rfc_parse_whitespace (const char *data) { + while (*data && isspace (*data)) +/* Strip leading digits */ +static const char* rfc_parse_num (const char *data) { + while (*data && isdigit (*data)) +/* Strip leading whitespace and digits */ +static const char* rfc_parse_whitespace_num (const char *data) { + while (*data && (isspace (*data) || isdigit (*data))) +static const char* rfc_parse_date (const char *data) { + sscanf (data, "%d", &day); + data = rfc_parse_whitespace_num (data); + sscanf (data, "%s", month); + if (!strcmp (month, "Jan")) { monthnum = 0; } else + if (!strcmp (month, "Feb")) { monthnum = 1; } else + if (!strcmp (month, "Mar")) { monthnum = 2; } else + if (!strcmp (month, "Apr")) { monthnum = 3; } else + if (!strcmp (month, "May")) { monthnum = 4; } else + if (!strcmp (month, "Jun")) { monthnum = 5; } else + if (!strcmp (month, "Jul")) { monthnum = 6; } else + if (!strcmp (month, "Aug")) { monthnum = 7; } else + if (!strcmp (month, "Sep")) { monthnum = 8; } else + if (!strcmp (month, "Oct")) { monthnum = 9; } else + if (!strcmp (month, "Nov")) { monthnum = 10; } else + if (!strcmp (month, "Dec")) { monthnum = 11; } + sscanf (data, "%d", &year); + data = rfc_parse_whitespace (data); + data = rfc_parse_num (data); + } else if (year > 100) { + parsed_datetime.tm_mday = day; + parsed_datetime.tm_mon = monthnum; + parsed_datetime.tm_year = year; +static const char* rfc_parse_hour (const char *data) { + sscanf (data, "%d", &hour); + data = strchr (data, ':'); + sscanf (++data, "%d", &minutes); + if (strchr (data, ':')) { + data = strchr (data, ':') + 1; + sscanf (data, "%d", &seconds); + data = rfc_parse_whitespace_num (data); + parsed_datetime.tm_hour = hour; + parsed_datetime.tm_min = minutes; + parsed_datetime.tm_sec = seconds; +static const char *rfc_parse_zone (const char *data) { + if (strstr (data, "GMT")) +static const char* rfc_parse_time (const char *data) { + data = rfc_parse_hour (data); + data = rfc_parse_zone (data); +static const char* rfc_parse_day (const char *data) { + return strchr (data, ',') + 1; +int rfc_parse_was_gmt () { +time_t rfc_parse_date_time (const char *data) { + /* Initialize values */ + parsed_datetime.tm_sec = 0; + parsed_datetime.tm_min = 0; + parsed_datetime.tm_hour = 0; + parsed_datetime.tm_mday = 0; + parsed_datetime.tm_mon = 0; + parsed_datetime.tm_year = 0; + parsed_datetime.tm_isdst = -1; + data = rfc_parse_whitespace (data); + data = rfc_parse_day (data); + data = rfc_parse_date (data); + data = rfc_parse_time (data); + result = mktime(&parsed_datetime); + if (rfc_parse_was_gmt ()) + struct tm *x = rfc_parse_date_time ("Mon, 06 Jun 2005 20:24:18 GMT"); + printf ("Sec: %d\n", x->tm_sec); + printf ("Min: %d\n", x->tm_min); + printf ("Hour: %d\n", x->tm_hour); + printf ("Day: %d\n", x->tm_mday); + printf ("Month: %d\n", x->tm_mon); + printf ("Year: %d\n", x->tm_year); + printf ("GMT: %d\n", parsed_gmttime); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/widget.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,607 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +#include "../common/pp_internal.h" +static GStaticMutex widget_mutex = G_STATIC_MUTEX_INIT; +static GList *widgets = NULL; +static GHashTable *identifiers = NULL; +static char *widget_pref = "/plugins/gtk/autoprofile/widgets/widget_ids"; +static void ap_widget_init_default_statuses () + // Make sure we don't keep on readding the default statuses if a user + if (!purple_prefs_exists (widget_pref)) { + purple_prefs_add_none ("/plugins/gtk/autoprofile/widgets/42"); + purple_prefs_add_string ( + "/plugins/gtk/autoprofile/widgets/42/component", "Timestamp"); + purple_prefs_add_string ( + "/plugins/gtk/autoprofile/widgets/42/alias", "Timestamp"); + purple_prefs_add_string ( + "/plugins/gtk/autoprofile/widgets/42/timestamp_format", + "Automatically created at %I:%M %p"); +void ap_widget_init () { + ap_widget_init_default_statuses (); + node = g_list_append (NULL, g_strdup ("42")); + purple_prefs_add_string_list (widget_pref, node); + free_string_list (node); +static gchar *strip_whitespace (const gchar *text) { + gchar *result, *end, *search; + while (isspace (*text)) { + search = result = g_strdup (text); + if (end == NULL && isspace (*search)) { + if (!isspace (*search)) { + if (end != NULL) *end = '\0'; +static void update_widget_ids () { + struct widget *cur_widget; + for (cur_node = widgets; cur_node != NULL; cur_node = cur_node->next) { + cur_widget = (struct widget *) cur_node->data; + ids = g_list_append (ids, cur_widget->wid); + purple_prefs_set_string_list (widget_pref, ids); +// Mutex is ALREADY HELD when this function is called +static struct widget *ap_widget_find_internal (const gchar *search_text) { + struct widget *cur_widget; + alias = strip_whitespace (search_text); + cur_widget = (struct widget *) cur_node->data; + if (!purple_utf8_strcasecmp (alias, cur_widget->alias)) { + cur_node = cur_node->next; +struct widget *ap_widget_find (const gchar *search_text) { + g_static_mutex_lock (&widget_mutex); + w = ap_widget_find_internal (search_text); + g_static_mutex_unlock (&widget_mutex); +struct widget *ap_widget_find_by_identifier (const gchar *search_text) { + g_static_mutex_lock (&widget_mutex); + w = (struct widget *) g_hash_table_lookup (identifiers, search_text); + g_static_mutex_unlock (&widget_mutex); +void ap_widget_start () { + GList *widget_identifiers, *widget_identifiers_start; + const gchar *identifier, *component_identifier; + struct component *comp; + g_static_mutex_lock (&widget_mutex); + identifiers = g_hash_table_new (g_str_hash, g_str_equal); + pref_name = g_string_new (""); + widget_identifiers_start = purple_prefs_get_string_list (widget_pref); + for (widget_identifiers = widget_identifiers_start; + widget_identifiers != NULL; + widget_identifiers = widget_identifiers->next) { + g_string_printf (pref_name, + "/plugins/gtk/autoprofile/widgets/%s/component", + (gchar *) widget_identifiers->data); + component_identifier = purple_prefs_get_string (pref_name->str); + if (component_identifier == NULL) { + ap_debug_error ("widget", "widget does not have component information"); + comp = ap_component_get_component (component_identifier); + ap_debug_error ("widget", "no component matches widget identifier"); + g_string_printf (pref_name, + "/plugins/gtk/autoprofile/widgets/%s/alias", + (gchar *) widget_identifiers->data); + identifier = purple_prefs_get_string (pref_name->str); + if (identifier == NULL) { + ap_debug_error ("widget", "widget does not have alias information"); + w = ap_widget_find_internal (identifier); + ap_debug_error ("widget", "widget alias already in use"); + w = (struct widget *) malloc (sizeof (struct widget)); + w->alias = g_strdup (identifier); + w->wid = g_strdup ((gchar *) widget_identifiers->data); + w->data = g_hash_table_new (NULL, NULL); + widgets = g_list_append (widgets, w); + g_hash_table_insert (identifiers, w->wid, w); + if (w->component->load) { + w->component->load (w); + g_string_printf (pref_name, + "loaded saved widget with alias %s and identifier %s", + ap_debug_misc ("widget", pref_name->str); + free_string_list (widget_identifiers_start); + g_string_free (pref_name, TRUE); + g_static_mutex_unlock (&widget_mutex); + ap_widget_gtk_start (); +void ap_widget_finish () { + g_static_mutex_lock (&widget_mutex); + ap_widget_gtk_finish (); + g_hash_table_destroy (identifiers); + w = (struct widget *) widgets->data; + if (w->component->unload) { + w->component->unload (w); + g_hash_table_destroy (w->data); + g_list_free_1 (widgets); + g_static_mutex_unlock (&widget_mutex); +gboolean ap_widget_has_content_changed () { + gboolean changed = FALSE; + g_static_mutex_lock (&widget_mutex); + for (node = widgets; node != NULL; node = node->next) { + w = (struct widget *) node->data; + if (w->component->has_content_changed == NULL || + w->component->has_content_changed (w)) { + g_static_mutex_unlock (&widget_mutex); +GList *ap_widget_get_widgets () { + g_static_mutex_lock (&widget_mutex); + result = g_list_copy (widgets); + g_static_mutex_unlock (&widget_mutex); +struct widget *ap_widget_create (struct component *comp) + gchar *identifier, *alias; + g_static_mutex_lock (&widget_mutex); + // Sanity check to make sure we dont "delete" old widgets by + if (identifiers == NULL) { + ap_debug_warn ("widget", + "tried to create widget when variables unitialized"); + g_static_mutex_unlock (&widget_mutex); + ap_debug ("widget", "instantiating new widget from component"); + w = ap_widget_find_internal (comp->identifier); + alias = NULL; // Stupid compiler + alias = g_strdup (comp->identifier); + for (i = 1; i < 10000; i++) { + g_string_printf (s, "%s%d", comp->identifier, i); + w = ap_widget_find_internal (s->str); + alias = g_strdup (s->str); + // This would happen....very very rarely... + ap_debug_error ("widget", "ran out of aliases for component"); + g_string_free (s, TRUE); + g_static_mutex_unlock (&widget_mutex); + g_string_printf (s, "%d", i); + w = (struct widget *) node->data; + if (!strcmp (s->str, w->wid)) { + identifier = g_strdup (s->str); + w = (struct widget *) malloc (sizeof (struct widget)); + w->data = g_hash_table_new (NULL, NULL); + widgets = g_list_append (widgets, w); + g_hash_table_insert (identifiers, w->wid, w); + g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s", w->wid); + purple_prefs_add_none (s->str); + g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/component", + purple_prefs_add_string (s->str, w->component->identifier); + g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/alias", w->wid); + purple_prefs_add_string (s->str, w->alias); + if (w->component->init_pref) { + w->component->init_pref (w); + if (w->component->load) { + w->component->load (w); + g_string_printf (s, "Created widget with alias %s and identifier %s", + ap_debug ("widget", s->str); + g_string_free (s, TRUE); + g_static_mutex_unlock (&widget_mutex); +void ap_widget_delete (struct widget *w) { + ap_debug_error ("widget", "attempt to delete NULL widget"); + g_static_mutex_lock (&widget_mutex); + // Sanity check to make sure we dont "delete" old widgets by + if (identifiers == NULL) { + ap_debug_warn ("widget", + "tried to delete widget when variables unitialized"); + g_static_mutex_unlock (&widget_mutex); + g_string_printf (s, "Deleting widget with alias %s and identifier %s", + ap_debug ("widget", s->str); + widgets = g_list_remove (widgets, w); + g_hash_table_remove (identifiers, w->wid); + g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s", w->wid); + purple_prefs_remove (s->str); + g_string_free (s, TRUE); + if (w->component->unload) { + w->component->unload (w); + g_hash_table_destroy (w->data); + g_static_mutex_unlock (&widget_mutex); +// TRUE if rename succeeds, FALSE otherwise +gboolean ap_widget_rename (struct widget *orig, const gchar *new_alias) { + g_static_mutex_lock (&widget_mutex); + w = ap_widget_find_internal (new_alias); + if (w != NULL && w != orig) { + g_static_mutex_unlock (&widget_mutex); + orig_alias = orig->alias; + orig->alias = g_strdup (new_alias); + g_string_printf (s, "/plugins/gtk/autoprofile/widgets/%s/alias", orig->wid); + purple_prefs_set_string (s->str, new_alias); + g_string_printf (s, "Changed alias of widget from %s to %s", + orig_alias, new_alias); + ap_debug ("widget", s->str); + g_string_free (s, TRUE); + g_static_mutex_unlock (&widget_mutex); +/* Widget data galore! */ +void ap_widget_set_data (struct widget *w, int id, gpointer data) { + g_static_mutex_lock (&widget_mutex); + g_hash_table_insert (w->data, GINT_TO_POINTER(id), data); + g_static_mutex_unlock (&widget_mutex); +gpointer ap_widget_get_data (struct widget *w, int id) { + g_static_mutex_lock (&widget_mutex); + result = g_hash_table_lookup (w->data, GINT_TO_POINTER(id)); + g_static_mutex_unlock (&widget_mutex); +/* Widget preferences galore! */ +gchar *ap_prefs_get_pref_name (struct widget *w, const char *name) { + g_string_append (s, "/plugins/gtk/autoprofile/widgets/"); + g_string_append_printf (s, "%s/%s", w->wid, name); + g_string_free (s, FALSE); +void ap_prefs_add_bool (struct widget *w, const char *name, gboolean value) { + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_add_bool (pref, value); +void ap_prefs_add_int (struct widget *w, const char *name, int value) { + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_add_int (pref, value); +void ap_prefs_add_none (struct widget *w, const char *name) { + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_add_none (pref); +void ap_prefs_add_string (struct widget *w, const char *name, + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_add_string (pref, value); +void ap_prefs_add_string_list (struct widget *w, const char *name, + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_add_string_list (pref, value); +gboolean ap_prefs_get_bool (struct widget *w, const char *name) { + gchar *pref = ap_prefs_get_pref_name (w, name); + result = purple_prefs_get_bool (pref); +int ap_prefs_get_int (struct widget *w, const char *name) { + gchar *pref = ap_prefs_get_pref_name (w, name); + result = purple_prefs_get_int (pref); +const char *ap_prefs_get_string (struct widget *w, const char *name) { + gchar *pref = ap_prefs_get_pref_name (w, name); + result = purple_prefs_get_string (pref); +GList *ap_prefs_get_string_list (struct widget *w, const char *name) { + gchar *pref = ap_prefs_get_pref_name (w, name); + result = purple_prefs_get_string_list (pref); +void ap_prefs_set_bool (struct widget *w, const char *name, gboolean value) { + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_set_bool (pref, value); + ap_widget_prefs_updated (w); +void ap_prefs_set_int (struct widget *w, const char *name, int value) { + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_set_int (pref, value); + ap_widget_prefs_updated (w); +void ap_prefs_set_string (struct widget *w, const char *name, + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_set_string (pref, value); + ap_widget_prefs_updated (w); +void ap_prefs_set_string_list (struct widget *w, const char *name, + gchar *pref = ap_prefs_get_pref_name (w, name); + purple_prefs_set_string_list (pref, value); + ap_widget_prefs_updated (w); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autoprofile/widget.h Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,96 @@
+/*--------------------------------------------------------------------------* + * A Purple away message and profile manager that supports dynamic text * + * AutoProfile is the legal property of its developers. Please refer to * + * the COPYRIGHT file distributed with this source distribution. * + * 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 02111-1307 USA * + *--------------------------------------------------------------------------*/ +/* The heart of everything */ + struct component *component; +void ap_widget_start (); +void ap_widget_finish (); +void ap_widget_gtk_start (); +void ap_widget_gtk_finish (); +gboolean ap_widget_has_content_changed (); +GList *ap_widget_get_widgets (); +struct widget *ap_widget_find (const gchar *); +struct widget *ap_widget_find_by_identifier (const gchar *); +struct widget *ap_widget_create (struct component *); +void ap_widget_delete (struct widget *); +// TRUE if rename succeeds, FALSE otherwise +gboolean ap_widget_rename (struct widget *, const gchar *); +GtkWidget *ap_widget_get_config_page (); +void ap_widget_prefs_updated (struct widget *); +GtkWidget *get_widget_list (GtkWidget *, GtkTreeSelection **); +void done_with_widget_list (); +/* Widget data galore! */ +void ap_widget_set_data (struct widget *, int, gpointer); +gpointer ap_widget_get_data (struct widget *, int); +/* Widget preferences galore! */ +gchar *ap_prefs_get_pref_name (struct widget *, const char *); +GtkWidget *ap_prefs_checkbox (struct widget *, const char *, const char *, +GtkWidget *ap_prefs_dropdown_from_list (struct widget *, GtkWidget *, + const gchar *, PurplePrefType, const char *, GList *); +GtkWidget *ap_prefs_labeled_entry (struct widget *, GtkWidget *page, + const gchar *, const char *, GtkSizeGroup *); +GtkWidget *ap_prefs_labeled_spin_button (struct widget *, GtkWidget *, + const gchar *, const char *, int, int, GtkSizeGroup *); +void ap_prefs_add_bool (struct widget *, const char *name, gboolean value); +void ap_prefs_add_int (struct widget *, const char *name, int value); +void ap_prefs_add_none (struct widget *, const char *name); +void ap_prefs_add_string (struct widget *, const char *, const char *); +void ap_prefs_add_string_list (struct widget *, const char *, GList *); +gboolean ap_prefs_get_bool (struct widget *, const char *name); +int ap_prefs_get_int (struct widget *, const char *name); +const char * ap_prefs_get_string (struct widget *, const char *name); +GList * ap_prefs_get_string_list (struct widget *, const char *name); +void ap_prefs_set_bool (struct widget *, const char *name, gboolean value); +void ap_prefs_set_int (struct widget *, const char *name, int value); +void ap_prefs_set_string (struct widget *, const char *name, const char *); +void ap_prefs_set_string_list (struct widget *, const char *, GList *); +#endif /* _AP_WIDGET_H_ */ --- a/autoreply/autoreply.c Sun Dec 23 07:18:21 2007 -0500
+++ b/autoreply/autoreply.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Autoreply - Autoreply feature for all the protocols
+ * Copyright (C) 2005-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/awaynotify/awaynotify.c Sun Dec 23 07:18:21 2007 -0500
+++ b/awaynotify/awaynotify.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* awaynotify - show notices when status changes
- * Copyright (C) 2005-2006 Matt Perry <guy@somewhere.fscked.org>
+ * Copyright (C) 2005-2008 Matt Perry <guy@somewhere.fscked.org> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/bash/bash.c Sun Dec 23 07:18:21 2007 -0500
+++ b/bash/bash.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* bash: provides a /command to display the URL for a random or specified
--- a/bit/bit.c Sun Dec 23 07:18:21 2007 -0500
+++ b/bit/bit.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,5 +1,5 @@
- * Copyright (C) 2005 Peter Lawler <bleeter from users.sf.net>
+ * Copyright (C) 2005-2008 Peter Lawler <bleeter from users.sf.net> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/blistops/blistops.c Sun Dec 23 07:18:21 2007 -0500
+++ b/blistops/blistops.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,7 @@
* Hides the blist on signon (or when it's created)
- * Copyright (C) 2004 Gary Kramlich.
+ * Copyright (C) 2004-2008 Gary Kramlich + * Copyright (C) 2007-2008 Sadrul Habib Chowdhury * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/chronic/chronic.c Sun Dec 23 07:18:21 2007 -0500
+++ b/chronic/chronic.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Chronic - Remote sound play triggering
- * Copyright (C) 2006 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/common/Makefile.am Sun Dec 23 07:18:21 2007 -0500
+++ b/common/Makefile.am Mon Mar 24 15:08:23 2008 -0400
@@ -1,5 +1,5 @@
--- a/common/core_template.c Sun Dec 23 07:18:21 2007 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
- * Plugin Name - Summary
- * 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 "../common/pp_internal.h"
-#define PLUGIN_ID "unnamed plugin"
-#define PLUGIN_STATIC_NAME "unnamed"
-#define PLUGIN_AUTHOR "someone <someone@somewhere.tld>"
-plugin_load(PurplePlugin *plugin) {
-plugin_unload(PurplePlugin *plugin) {
-static PurplePluginInfo info = {
- PURPLE_PLUGIN_MAGIC, /* Magic */
- PURPLE_MAJOR_VERSION, /* Purple Major Version */
- PURPLE_MINOR_VERSION, /* Purple Minor Version */
- PURPLE_PLUGIN_STANDARD, /* plugin type */
- NULL, /* ui requirement */
- NULL, /* dependencies */
- PURPLE_PRIORITY_DEFAULT, /* priority */
- PLUGIN_ID, /* plugin id */
- PP_VERSION, /* version */
- NULL, /* description */
- PLUGIN_AUTHOR, /* author */
- PP_WEBSITE, /* website */
- plugin_load, /* load */
- plugin_unload, /* unload */
-init_plugin(PurplePlugin *plugin) {
- bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
- bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
- info.name = _("unnamed");
- info.summary = _("summary");
- info.description = _("description");
-PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
--- a/common/glib_compat.h Sun Dec 23 07:18:21 2007 -0500
+++ b/common/glib_compat.h Mon Mar 24 15:08:23 2008 -0400
@@ -24,8 +24,7 @@
#if !GLIB_CHECK_VERSION(2,2,0)
-g_str_has_suffix (const gchar *str,
+g_str_has_suffix (const gchar *str, const gchar *suffix) @@ -43,8 +42,7 @@
-g_str_has_prefix (const gchar *str,
+g_str_has_prefix (const gchar *str, const gchar *prefix) @@ -77,9 +75,7 @@
# define N_(String) (String)
-g_strsplit_set (const gchar *string,
- const gchar *delimiters,
+g_strsplit_set (const gchar *string, const gchar *delimiters, gint max_tokens) gboolean delim_table[256];
--- a/common/gtk_template.c Sun Dec 23 07:18:21 2007 -0500
+++ b/common/gtk_template.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
+ * Copyright (C) 2004-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/common/pp_internal.h Sun Dec 23 07:18:21 2007 -0500
+++ b/common/pp_internal.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See AUTHORS for a list of all authors
* This program is free software; you can redistribute it and/or
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/common/purple_template.c Mon Mar 24 15:08:23 2008 -0400
@@ -0,0 +1,84 @@
+ * Plugin Name - Summary + * Copyright (C) 2004-2008 + * 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 "../common/pp_internal.h" +#define PLUGIN_ID "unnamed plugin" +#define PLUGIN_STATIC_NAME "unnamed" +#define PLUGIN_AUTHOR "someone <someone@somewhere.tld>" +plugin_load(PurplePlugin *plugin) { +plugin_unload(PurplePlugin *plugin) { +static PurplePluginInfo info = { + PURPLE_PLUGIN_MAGIC, /* Magic */ + PURPLE_MAJOR_VERSION, /* Purple Major Version */ + PURPLE_MINOR_VERSION, /* Purple Minor Version */ + PURPLE_PLUGIN_STANDARD, /* plugin type */ + NULL, /* ui requirement */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + PLUGIN_ID, /* plugin id */ + PP_VERSION, /* version */ + NULL, /* description */ + PLUGIN_AUTHOR, /* author */ + PP_WEBSITE, /* website */ + plugin_load, /* load */ + plugin_unload, /* unload */ +init_plugin(PurplePlugin *plugin) { + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name = _("unnamed"); + info.summary = _("summary"); + info.description = _("description"); +PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) --- a/configure.ac Sun Dec 23 07:18:21 2007 -0500
+++ b/configure.ac Mon Mar 24 15:08:23 2008 -0400
@@ -1,4 +1,4 @@
-AC_INIT([purple-plugin_pack], [2.3.0mtn], [plugins-devel@lists.guifications.org])
+AC_INIT([purple-plugin_pack], [2.4.0mtn], [plugins-devel@lists.guifications.org]) AM_CONFIG_HEADER(pre_config.h)
@@ -209,6 +209,13 @@
+PKG_CHECK_MODULES(GNT, [gnt], HAVE_GNT="yes", HAVE_GNT="no") dnl #######################################################################
dnl # Plugin dependency checking
dnl #######################################################################
@@ -310,6 +317,7 @@
--- a/convbadger/convbadger.c Sun Dec 23 07:18:21 2007 -0500
+++ b/convbadger/convbadger.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* ConvBadger - Adds the protocol icon to the menu tray of a conversation
- * Copyright (C) 2007 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2007-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
@@ -139,7 +139,8 @@
purple_signal_connect(conv_handle, "deleting-conversation", plugin,
PURPLE_CALLBACK(convbadger_conv_destroyed_cb), NULL);
- purple_signal_connect(conv_handle, "conversation-switched", plugin,
+ purple_signal_connect(pidgin_conversations_get_handle(), + "conversation-switched", plugin, PURPLE_CALLBACK(convbadger_conv_switched_cb), NULL);
@@ -181,6 +182,11 @@
--- a/dewysiwygification/dewysiwygification.c Sun Dec 23 07:18:21 2007 -0500
+++ b/dewysiwygification/dewysiwygification.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* DeWYSIWYGification - Lets you type in HTML without it being escaped to entities.
- * Copyright (C) 2004 Tim Ringenbach <omarvo@hotmail.com>
+ * Copyright (C) 2004-2008 Tim Ringenbach <omarvo@hotmail.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -107,6 +107,11 @@
--- a/dice/dice.c Sun Dec 23 07:18:21 2007 -0500
+++ b/dice/dice.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,8 +1,8 @@
* Adds a command to roll an arbitrary number of dice with an arbitrary
- * Copyright (C) 2005 Gary Kramlich <grim@reaperworld.com>
- * Modified and commented 2007-11 by Lucas <reilithion@gmail.com>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2007 Lucas <reilithion@gmail.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/difftopic/difftopic.c Sun Dec 23 07:18:21 2007 -0500
+++ b/difftopic/difftopic.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* DiffTopic - Show the old topic when the topic in a chat room changes.
+ * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/eight_ball/eight_ball.c Sun Dec 23 07:18:21 2007 -0500
+++ b/eight_ball/eight_ball.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* eight_ball: Provides Magic 8-ball-like functionality
--- a/enhancedhist/enhancedhist.c Sun Dec 23 07:18:21 2007 -0500
+++ b/enhancedhist/enhancedhist.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,5 +1,8 @@
* enhanced_hist.c - Enhanced History Plugin for libpurple
+ * Copyright (C) 2004-2008 Andrew Pangborn <gaim@andrewpangborn.com> + * Copyright (C) 2007-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2007 Ankit Singla * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -49,6 +52,8 @@
#define ENHANCED_HISTORY_ID "gtk-plugin_pack-enhanced_history"
+#define PREF_ROOT_GPPATH "/plugins/gtk" +#define PREF_ROOT_PPATH "/plugins/gtk/plugin_pack" #define PREF_ROOT_PATH "/plugins/gtk/plugin_pack/enhanced_history"
#define PREF_NUMBER_PATH "/plugins/gtk/plugin_pack/enhanced_history/number"
#define PREF_MINS_PATH "/plugins/gtk/plugin_pack/enhanced_history/minutes"
@@ -340,15 +345,11 @@
gboolean dates = FALSE, ims = FALSE, chats = FALSE;
+ purple_prefs_add_none(PREF_ROOT_GPPATH); + purple_prefs_add_none(PREF_ROOT_PPATH); purple_prefs_add_none(PREF_ROOT_PATH);
if(purple_prefs_exists("/plugins/core/enhanced_history/int")) {
- /* Rename these prefs to fit within the Plugin Pack scheme */
- purple_prefs_rename("/plugins/core/enhanced_history/int", PREF_NUMBER_PATH);
- purple_prefs_rename("/plugins/core/enhanced_history/mins", PREF_MINS_PATH);
- purple_prefs_rename("/plugins/core/enhanced_history/hours", PREF_HOURS_PATH);
- purple_prefs_rename("/plugins/core/enhanced_history/days", PREF_DAYS_PATH);
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_date"), "no"))
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_im"), "no"))
@@ -356,14 +357,22 @@
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_chat"), "no"))
+ purple_prefs_add_int(PREF_NUMBER_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/int")); + purple_prefs_add_int(PREF_MINS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/mins")); + purple_prefs_add_int(PREF_HOURS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/hours")); + purple_prefs_add_int(PREF_DAYS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/days")); + purple_prefs_add_bool(PREF_DATES_PATH, dates); + purple_prefs_add_bool(PREF_IM_PATH, ims); + purple_prefs_add_bool(PREF_CHAT_PATH, chats); + purple_prefs_remove("/plugins/core/enhanced_history/int"); + purple_prefs_remove("/plugins/core/enhanced_history/mins"); + purple_prefs_remove("/plugins/core/enhanced_history/hours"); + purple_prefs_remove("/plugins/core/enhanced_history/days"); purple_prefs_remove("/plugins/core/enhanced_history/string_date");
purple_prefs_remove("/plugins/core/enhanced_history/string_im");
purple_prefs_remove("/plugins/core/enhanced_history/string_chat");
purple_prefs_remove("/plugins/core/enhanced_history");
- purple_prefs_add_bool(PREF_DATES_PATH, dates);
- purple_prefs_add_bool(PREF_IM_PATH, ims);
- purple_prefs_add_bool(PREF_CHAT_PATH, chats);
/* Create these prefs with sensible defaults */
purple_prefs_add_int(PREF_NUMBER_PATH, 1);
--- a/findip/findip.c Sun Dec 23 07:18:21 2007 -0500
+++ b/findip/findip.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Find IP - Find the IP of a person in the buddylist
+ * Copyright (C) 2007-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/gRIM/gRIM.c Sun Dec 23 07:18:21 2007 -0500
+++ b/gRIM/gRIM.c Mon Mar 24 15:08:23 2008 -0400
@@ -2,7 +2,11 @@
* A completely stupid plugin, inspired by a dumb conversation in #gaim
* and needing some light relief from 'real' work.
* Also as a tribute to our fearless project leader.
- * Copyright (C) 2005 Peter Lawler <bleeter from users.sf.net>
+ * Copyright (C) 2005-2008 Peter Lawler <bleeter from users.sf.net> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2007 Ankit Singla * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/groupmsg/groupmsg.c Sun Dec 23 07:18:21 2007 -0500
+++ b/groupmsg/groupmsg.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* GroupMsg - Send an IM to a group of buddies
- * Copyright (C) 2004 Stu Tomlinson <stu@nosnilmot.com>
+ * Copyright (C) 2004-2008 Stu Tomlinson <stu@nosnilmot.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/hideconv/hideconv.c Sun Dec 23 07:18:21 2007 -0500
+++ b/hideconv/hideconv.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Hide Conversations - You can hide conversations without having to close them.
+ * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/highlight/highlight.c Sun Dec 23 07:18:21 2007 -0500
+++ b/highlight/highlight.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,7 @@
* highlight.c Highlight on customized words.
- * Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
+ * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/ignore/ignore.c Sun Dec 23 07:18:21 2007 -0500
+++ b/ignore/ignore.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,7 @@
* @file ignore.c Ignore people.
- * Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
+ * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@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
@@ -117,7 +117,7 @@
char *split = strrchr(pref, '/');
+ if (rule && *rule != 'n') { if (last == NULL || g_strcasecmp(last, pref)) {
--- a/infopane/infopane.c Sun Dec 23 07:18:21 2007 -0500
+++ b/infopane/infopane.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Infopane - Use different views for the details information in conversation windows.
+ * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irc-more/irc-more.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irc-more/irc-more.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* @file irc-more.c A couple of additional IRC features.
- * Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
+ * Copyright (C) 2007-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> + * Copyright (C) 2007-2008 John Bailey <rekkanoryo@rekkanoryo.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
@@ -122,6 +123,7 @@
+#if !PURPLE_VERSION_CHECK(2,4,0) notice_cmd_cb(PurpleConversation *conv, const gchar *cmd, gchar **args,
gchar **error, void *data)
@@ -171,12 +173,13 @@
return PURPLE_CMD_RET_OK;
irc_sending_text(PurpleConnection *gc, char **msg, gpointer null)
PurpleAccount *account = purple_connection_get_account(gc);
char *message = strchr(*msg, ':');
@@ -197,8 +200,8 @@
*msg = g_strdup_printf("%s:\001VERSION %s\001\r\n", *msg, CTCP_REPLY);
@@ -218,9 +221,11 @@
/* specify our help string and register our command */
notice_help = _("notice target message: Send a notice to the specified target.");
+#if !PURPLE_VERSION_CHECK(2,4,0) notice_cmd_id = purple_cmd_register("notice", "ws", PURPLE_CMD_P_PLUGIN,
PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
"prpl-irc", notice_cmd_cb, notice_help, NULL);
/* we need this handle for the signed-on signal */
gc_handle = purple_connections_get_handle();
--- a/irchelper/irchelper.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irchelper/irchelper.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,7 @@
* IRC Helper Plugin for libpurple
- * Copyright (C) 2005-2007, Richard Laager <rlaager@pidgin.im>
+ * Copyright (C) 2005-2008, Richard Laager <rlaager@pidgin.im> * Copyright (C) 2004-2005, Mathias Hasselmann <mathias@taschenorakel.de>
* Copyright (C) 2005, Daniel Beardsmore <uilleann@users.sf.net>
* Copyright (C) 2005, Björn Nilsson <BNI on irc.freenode.net>
@@ -199,7 +199,12 @@
conv = g_new0(PurpleConversation, 1);
conv->type = PURPLE_CONV_TYPE_IM;
- purple_conversation_set_account(conv, account);
+ /* If we use this then the conversation updated signal is fired and + * other plugins might start doing things to our conversation, such as + * setting data on it which we would then need to free etc. It's easier + * just to be more hacky by setting account directly. */ + /* purple_conversation_set_account(conv, account); */ + conv->account = account; --- a/irssi/datechange.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/datechange.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/datechange.h Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/datechange.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/irssi.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/irssi.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2006 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2006 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/lastlog.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/lastlog.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/lastlog.h Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/lastlog.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/layout.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/layout.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/layout.h Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/layout.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/textfmt.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/textfmt.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
@@ -103,8 +104,10 @@
/* now let's replce the matches.
* If we don't have any, drop out right away.
- if(regexec(®ex, iter, GROUP_TOTAL, matches, 0) != 0)
+ if(regexec(®ex, iter, GROUP_TOTAL, matches, 0) != 0) { /* create our GString. Heh heh */
@@ -144,6 +147,8 @@
iter += matches[GROUP_ALL].rm_eo;
} while(regexec(®ex, iter, GROUP_TOTAL, matches, REG_NOTBOL) == 0);
/* at this point, iter is either the remains of the text, of a single null
* terminator. So throw it onto the GString.
--- a/irssi/textfmt.h Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/textfmt.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/window.c Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/window.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/irssi/window.h Sun Dec 23 07:18:21 2007 -0500
+++ b/irssi/window.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,8 @@
* irssi - Implements several irssi features for Purple
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
- * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.org>
+ * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org> + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/lastseen/lastseen.c Sun Dec 23 07:18:21 2007 -0500
+++ b/lastseen/lastseen.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Last Seen - Record when a buddy was last seen
- * Copyright (C) 2004 Stu Tomlinson <stu@nosnilmot.com>
+ * Copyright (C) 2004-2008 Stu Tomlinson <stu@nosnilmot.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/listhandler/aim_blt_files.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/aim_blt_files.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/aim_blt_files.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/aim_blt_files.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/alias_xml_files.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/alias_xml_files.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/alias_xml_files.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/alias_xml_files.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/gen_xml_files.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/gen_xml_files.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/gen_xml_files.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/gen_xml_files.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/lh_util.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/lh_util.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/lh_util.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/lh_util.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/listhandler.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/listhandler.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/listhandler.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/listhandler.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/migrate.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/migrate.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/migrate.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/migrate.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/purple_blist_xml.c Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/purple_blist_xml.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/listhandler/purple_blist_xml.h Sun Dec 23 07:18:21 2007 -0500
+++ b/listhandler/purple_blist_xml.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- * Copyright (C) 2003-2005
+ * Copyright (C) 2003-2008 * See ../AUTHORS for a list of all authors
* listhandler: Provides importing, exporting, and copying functions
--- a/m4/pluginpack.m4 Sun Dec 23 07:18:21 2007 -0500
+++ b/m4/pluginpack.m4 Mon Mar 24 15:08:23 2008 -0400
@@ -37,7 +37,7 @@
- if test -f "$d/Makefile.am" -a ! "$d" = "$srcdir/common" -a ! "$d" = "$srcdir/doc" -a ! "$d" = "$srcdir/m4" -a ! -f "$d/.abusive" -a ! -f "$d/.build" -a ! -f "$d/.incomplete" ; then
+ if test -f "$d/Makefile.am" -a ! "$d" = "$srcdir/common" -a ! "$d" = "$srcdir/doc" -a ! "$d" = "$srcdir/m4" -a ! -f "$d/configure" -a ! -f "$d/.abusive" -a ! -f "$d/.build" -a ! -f "$d/.incomplete" ; then *** Plugin Directory $d is misconfigured
@@ -64,7 +64,9 @@
PP_PURPLE="$PP_PURPLE $base"
- elif test -f "$d/.pidgin-plugin" ; then
+ if test -f "$d/.pidgin-plugin" ; then if test -f "$d/.abusive" ; then
PP_PIDGIN_ABUSIVE="$PP_PIDGIN_ABUSIVE $base"
elif test -f "$d/.build" ; then
@@ -76,7 +78,9 @@
PP_PIDGIN="$PP_PIDGIN $base"
- elif test -f "$d/.finch-plugin" ; then
+ if test -f "$d/.finch-plugin" ; then if test -f "$d/.abusive" ; then
PP_FINCH_ABUSIVE="$PP_FINCH_ABUSIVE $base"
elif test -f "$d/.build" ; then
@@ -96,29 +100,64 @@
dnl #######################################################################
AC_HELP_STRING([--with-plugins], [what plugins to build]),
dnl #######################################################################
dnl # Now determine which ones have been selected
dnl #######################################################################
- if test "x$with_plugins" = "xdefault" ; then
- tmp_SUB="$PP_AVAILABLE"
- exp_plugins=`echo "$with_plugins" | sed 's/,/ /g'`
- for p in "$PP_AVAILABLE $PP_ABUSIVE"; do
- for r in $exp_plugins; do
- if test x"$r" = x"$p" ; then
+ case "$with_plugins" in + PP_FINCH_BUILD="$PP_FINCH_ABUSIVE $PP_FINCH_BUILD" + PP_PIDGIN_BUILD="$PP_PIDGIN_ABUSIVE $PP_PIDGIN_BUILD" + PP_PURPLE_BUILD="$PP_PURPLE_ABUSIVE $PP_PURPLE_BUILD" + dnl # we don't do anything if the defaults are selected, they're + dnl # already set up :) + dnl # clear out the build variables + dnl # turn the with plugins variable into a space delimited list + exp_plugins=`echo "$with_plugins" | sed 's/,/ /g'` + dnl # loop through the with plugins list and update the build variables + dnl # as we find the plugins in each type. + PP_FINCH_BUILD="$PP_FINCH_BUILD $p" + PP_PIDGIN_BUILD="$PP_PIDGIN_BUILD $p" + PP_PURPLE_BUILD="$PP_PURPLE_BUILD $p"
- dnl # remove duplicates
- PP_BUILD=`echo $tmp_SUB | awk '{for (i = 1; i <= NF; i++) { print $i } }' | sort | uniq | xargs echo `
- dnl # add the abusive plugins to the dist
- PP_DIST="$PP_AVAILABLE $PP_ABUSIVE"
+ PP_FINCH_BUILD=`echo $PP_FINCH_BUILD | awk '{for (i = 1; i <=NF; i++) { print $i } }' | sort | uniq | xargs echo` + PP_PIDGIN_BUILD=`echo $PP_PIDGIN_BUILD | awk '{for (i = 1; i <=NF; i++) { print $i } } ' | sort | uniq | xargs echo` + PP_PURPLE_BUILD=`echo $PP_PURPLE_BUILD | awk '{for (i = 1; i <=NF; i++) { print $i } } ' | sort | uniq | xargs echo` dnl #######################################################################
dnl # substitue our sub dirs
--- a/mystatusbox/mystatusbox.c Sun Dec 23 07:18:21 2007 -0500
+++ b/mystatusbox/mystatusbox.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* mystatusbox - Show/Hide the peraccount statusboxes
+ * Copyright (C) 2005-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
Binary file napster/16/napster.png has changed
Binary file napster/22/napster.png has changed
Binary file napster/48/napster.png has changed
--- a/napster/Makefile.am Sun Dec 23 07:18:21 2007 -0500
+++ b/napster/Makefile.am Mon Mar 24 15:08:23 2008 -0400
@@ -1,10 +1,10 @@
napsterdir = $(PURPLE_LIBDIR)
@@ -22,19 +22,14 @@
-napsterpix16x16dir=$(PIDGIN_PIXMAPSDIR)/protocols/16
-napsterpix16x16_DATA=napster16x16.png
-napsterpix22x22dir=$(PIDGIN_PIXMAPSDIR)/protocols/22
-napsterpix22x22_DATA=napster22x22.png
+napsterpix16dir=$(PIDGIN_PIXMAPSDIR)/protocols/16 +napsterpix16_DATA=16/napster.png -napsterpix48x48dir=$(PIDGIN_PIXMAPSDIR)/protocols/48
-napsterpix48x48_DATA=napster48x48.png
+napsterpix22dir=$(PIDGIN_PIXMAPSDIR)/protocols/22 +napsterpix22_DATA=22/napster.png
- mv $(PIDGIN_PIXMAPSDIR)/protocols/16/napster16x16.png $(PIDGIN_PIXMAPSDIR)//protocols/16/napster.png
- mv $(PIDGIN_PIXMAPSDIR)/protocols/22/napster22x22.png $(PIDGIN_PIXMAPSDIR)//protocols/22/napster.png
- mv $(PIDGIN_PIXMAPSDIR)/protocols/48/napster48x48.png $(PIDGIN_PIXMAPSDIR)//protocols/48/napster.png
+napsterpix48dir=$(PIDGIN_PIXMAPSDIR)/protocols/48 +napsterpix48_DATA=48/napster.png @@ -46,3 +41,5 @@
--- a/napster/napster.c Sun Dec 23 07:18:21 2007 -0500
+++ b/napster/napster.c Mon Mar 24 15:08:23 2008 -0400
@@ -4,7 +4,8 @@
* Copyright (C) 2000-2001, Rob Flynn <rob@marko.net>
* Assimilated for inclusion in the Plugin Pack:
- * Copyright (C) 2006, Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2006-2008 Gary Kramlich <grim@reaperworld.com> + * Copyright (C) 2007 John Bailey <rekkanoryo@rekkanoryo.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
@@ -692,9 +693,11 @@
NULL, /* offline_message */
NULL, /* whiteboard_prpl_ops */
+ NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ Binary file napster/napster16x16.png has changed
Binary file napster/napster22x22.png has changed
Binary file napster/napster48x48.png has changed
--- a/nicksaid/nicksaid.c Sun Dec 23 07:18:21 2007 -0500
+++ b/nicksaid/nicksaid.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Nicksaid - Record when someone said your nick in a chat.
+ * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@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
--- a/oldlogger/oldlogger.c Sun Dec 23 07:18:21 2007 -0500
+++ b/oldlogger/oldlogger.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Old Logger - Re-implements the legacy, deficient, logging
- * Copyright (C) 2004 Stu Tomlinson <stu@nosnilmot.com>
+ * Copyright (C) 2004-2008 Stu Tomlinson <stu@nosnilmot.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/plonkers/plonkers.c Sun Dec 23 07:18:21 2007 -0500
+++ b/plonkers/plonkers.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
- Purple-Plonkers - Manager the plonkers out in cyberland
- Copyright (C) 2005 Peter Lawler
+ Purple-Plonkers - Manage the plonkers out in cyberland + Copyright (C) 2005-2008 Peter Lawler Very loosely based on gxr, Copyright (C) 2004 Gary Kramlich
--- a/plugin_pack.spec.in Sun Dec 23 07:18:21 2007 -0500
+++ b/plugin_pack.spec.in Mon Mar 24 15:08:23 2008 -0400
@@ -37,6 +37,7 @@
Summary: Plugin Pack for Pidgin
Group: Applications/Internet
Requires: pidgin >= %{pidgin_major_ver}.%{pidgin_build_minor_ver}, pidgin < %{pidgin_next_major_ver}
+Requires: purple-plugin_pack = %{version} All the other plugins for all libpurple derived clients
@@ -75,6 +76,9 @@
+* Sat Mar 01 2008 Stu Tomlinson <stu@nosnilmot.com> +- make the pidgin plugin pack depend on the purple plugin pack * Sat Oct 27 2007 Stu Tomlinson <stu@nosnilmot.com>
- Add --without xmms option to build without xmms plugin
--- a/po/POTFILES.in Sun Dec 23 07:18:21 2007 -0500
+++ b/po/POTFILES.in Mon Mar 24 15:08:23 2008 -0400
@@ -8,9 +8,7 @@
+common/purple_template.c dewysiwygification/dewysiwygification.c
--- a/schedule/pidgin-schedule.c Sun Dec 23 07:18:21 2007 -0500
+++ b/schedule/pidgin-schedule.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Purple-Schedule - Schedule reminders at specified times.
+ * Copyright (C) 2006-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/schedule/schedule.c Sun Dec 23 07:18:21 2007 -0500
+++ b/schedule/schedule.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Purple-Schedule - Schedule reminders/pounces at specified times.
+ * Copyright (C) 2006-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/schedule/schedule.h Sun Dec 23 07:18:21 2007 -0500
+++ b/schedule/schedule.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Purple-Schedule - Schedule reminders/pounces at specified times.
+ * Copyright (C) 2006-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/sepandtab/sepandtab.c Sun Dec 23 07:18:21 2007 -0500
+++ b/sepandtab/sepandtab.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Extra conversation placement options for Purple
- * Copyright (C) 2004 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2004-2008 Gary Kramlich <grim@reaperworld.com> * Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
--- a/showoffline/showoffline.c Sun Dec 23 07:18:21 2007 -0500
+++ b/showoffline/showoffline.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Show Offline - Show specific buddies while offline
- * Copyright (C) 2004 Stu Tomlinson <stu@nosnilmot.com>
+ * Copyright (C) 2004-2008 Stu Tomlinson <stu@nosnilmot.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/simfix/simfix.c Sun Dec 23 07:18:21 2007 -0500
+++ b/simfix/simfix.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,7 @@
* simfix - fix received messages from SIM clients in Purple
- * (C) Copyright 2005 Stu Tomlinson <stu@nosnilmot.com>
+ * (C) Copyright 2005-2008 Stu Tomlinson <stu@nosnilmot.com> * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/slashexec/slashexec.c Sun Dec 23 07:18:21 2007 -0500
+++ b/slashexec/slashexec.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,10 +1,10 @@
* slashexec - A CLI for libpurple clients
- * Copyright (C) 2004-2007 Gary Kramlich
- * Copyright (C) 2005-2007 Peter Lawler
- * Copyright (C) 2005-2007 Daniel Atallah
- * Copyright (C) 2005-2007 John Bailey
- * Copyright (C) 2006-2007 Sadrul Habib Chowdhury
+ * Copyright (C) 2004-2008 Gary Kramlich + * Copyright (C) 2005-2008 Peter Lawler + * Copyright (C) 2005-2008 Daniel Atallah + * Copyright (C) 2005-2008 John Bailey + * Copyright (C) 2006-2008 Sadrul Habib Chowdhury * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- a/smartear/Makefile.am Sun Dec 23 07:18:21 2007 -0500
+++ b/smartear/Makefile.am Mon Mar 24 15:08:23 2008 -0400
@@ -1,36 +1,62 @@
-smarteardir = $(PIDGIN_LIBDIR)
-smartear_la_LDFLAGS = -module -avoid-version
+smarteardir = $(PURPLE_LIBDIR) +smartear_la_LDFLAGS = -module -avoid-version +smartear_la_CPPFLAGS = \ + -DLIBDIR=\"$(PURPLE_LIBDIR)\" \ + -DDATADIR=\"$(PURPLE_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \ smartear_LTLIBRARIES = smartear.la
smartear_la_SOURCES = smartear.c
smartear_la_LIBADD = $(PURPLE_LIBS)
+gtksmarteardir = $(PIDGIN_LIBDIR) +gtksmartear_la_LDFLAGS = -module -avoid-version +gtksmartear_la_CPPFLAGS = \ + -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \ + -DDATADIR=\"$(PIDGIN_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PIDGIN_PIIXMAPSDIR)\" \ gtksmartear_LTLIBRARIES = gtksmartear.la
gtksmartear_la_SOURCES = gtksmartear.c
-gtksmartear_la_LIBADD = $(PIDGIN_LIBS)
+gtksmartear_la_LIBADD = \ +gntsmarteardir = $(FINCH_LIBDIR) +gntsmartear_la_LDFLAGS = -module -avoid-version +gntsmartear_la_CPPFLAGS = \ + -DLIBDIR=\"$(FINCH_LIBDIR)\" \ + -DDATADIR=\"$(FINCH_DATADIR)\" \ + -DPIXMAPSDIR=\"$(FINCH_PIIXMAPSDIR)\" \ gntsmartear_LTLIBRARIES = gntsmartear.la
gntsmartear_la_SOURCES = gntsmartear.c
-gntsmartear_la_LIBADD = $(FINCH_LIBS)
+gntsmartear_la_LIBADD = \
- -DLIBDIR=\"$(PURPLE_LIBDIR)\" \
- -DDATADIR=\"$(PURPLE_DATADIR)\" \
- -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \
--- a/smartear/gtksmartear.c Sun Dec 23 07:18:21 2007 -0500
+++ b/smartear/gtksmartear.c Mon Mar 24 15:08:23 2008 -0400
@@ -25,8 +25,6 @@
#include "../common/pp_internal.h"
-#define PLUGIN_AUTHOR "John Bailey <rekkanoryo@rekkanoryo.org>"
@@ -86,7 +84,7 @@
PP_VERSION, /* version */
- PLUGIN_AUTHOR, /* author */
+ "John Bailey <rekkanoryo@rekkanoryo.org>", PP_WEBSITE, /* website */
--- a/snpp/snpp.c Sun Dec 23 07:18:21 2007 -0500
+++ b/snpp/snpp.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,7 +1,7 @@
* gaim-snpp Protocol Plugin
- * Copyright (C) 2004, Don Seiler <don@seiler.us>
+ * Copyright (C) 2004-2008 Don Seiler <don@seiler.us> * 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
@@ -534,7 +534,16 @@
NULL, /* roomlist_cancel */
NULL, /* roomlist_expand_catagory */
NULL, /* can_receive_file */
+ NULL, /* offline_message */ + NULL, /* whiteboard_prpl_ops */ + NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ --- a/sslinfo/sslinfo.c Sun Dec 23 07:18:21 2007 -0500
+++ b/sslinfo/sslinfo.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* sslinfo - Gets info about your currently loaded ssl plugin
- * Copyright (C) 2004 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2004-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
--- a/stocker/stocker.c Sun Dec 23 07:18:21 2007 -0500
+++ b/stocker/stocker.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Stocker - Adds a stock ticker to the buddy list
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2005-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
--- a/stocker/stocker_prefs.c Sun Dec 23 07:18:21 2007 -0500
+++ b/stocker/stocker_prefs.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Stocker - Adds a stock ticker to the buddy list
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2005-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
--- a/stocker/stocker_prefs.h Sun Dec 23 07:18:21 2007 -0500
+++ b/stocker/stocker_prefs.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Stocker - Adds a stock ticker to the buddy list
- * Copyright (C) 2005-2007 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2005-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
--- a/switchspell/switchspell.c Sun Dec 23 07:18:21 2007 -0500
+++ b/switchspell/switchspell.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Switchspell - Switch spelling language during run time.
+ * Copyright (C) 2007-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -127,6 +127,7 @@
delete_aspell_config(config);
dels = aspell_dict_info_list_elements(dlist);
+ aspell_dict_info_list_empty(dlist); while ((entry = aspell_dict_info_enumeration_next(dels)) != 0) {
GtkWidget *menuitem = gtk_radio_menu_item_new_with_label(group, entry->name);
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
--- a/timelog/log-widget.c Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/log-widget.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,12 +1,11 @@
- * Copyright (c) 2006 Jon Oberheide.
+ * Copyright (C) 2006 Jon Oberheide. + * Copyright (C) 2007-2008 Stu Tomlinson * Based on code from Gaim's gtklog.c
- * $Id: log-widget.c,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
* 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
--- a/timelog/log-widget.h Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/log-widget.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,9 +1,8 @@
- * Copyright (c) 2006 Jon Oberheide.
- * $Id: log-widget.h,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
+ * Copyright (C) 2006 Jon Oberheide. + * Copyright (C) 2007-2008 Stu Tomlinson * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/timelog/range-widget.c Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/range-widget.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,9 +1,8 @@
- * Copyright (c) 2006 Jon Oberheide.
- * $Id: range-widget.c,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
+ * Copyright (C) 2006 Jon Oberheide + * Copyright (C) 2007-2008 Stu Tomlinson * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/timelog/range-widget.h Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/range-widget.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,9 +1,8 @@
- * Copyright (c) 2006 Jon Oberheide.
- * $Id: range-widget.h,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
+ * Copyright (C) 2006 Jon Oberheide. + * Copyright (C) 2007-2008 Stu Tomlinson * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/timelog/timelog.c Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/timelog.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,9 +1,8 @@
- * Copyright (c) 2006 Jon Oberheide.
- * $Id: timelog.c,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
+ * Copyright (C) 2006 Jon Oberheide + * Copyright (C) 2007-2008 Stu Tomlinson * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
--- a/timelog/timelog.h Sun Dec 23 07:18:21 2007 -0500
+++ b/timelog/timelog.h Mon Mar 24 15:08:23 2008 -0400
@@ -1,9 +1,8 @@
- * Copyright (c) 2006 Jon Oberheide.
- * $Id: gaim-timelog.h,v 1.30 2005/12/24 05:14:55 binaryjono Exp $
+ * Copyright (C) 2006 Jon Oberheide + * Copyright (C) 2007-2008 Stu Tomlinson * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -22,7 +21,7 @@
#define TIMELOG_PLUGIN_ID "gtk-binaryjono-timelog"
#define TIMELOG_TITLE _("TimeLog")
--- a/xchat-chats/xchat-chats.c Sun Dec 23 07:18:21 2007 -0500
+++ b/xchat-chats/xchat-chats.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
* Purple-XChat - Use XChat-like chats
+ * Copyright (C) 2005-2008 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -147,7 +147,14 @@
-GtkWidget *get_xtext(PurpleConversation *conv)
+ return (purple_version_check(2, 4, 0) == NULL); +static GtkWidget *get_xtext(PurpleConversation *conv) @@ -258,35 +265,44 @@
+ name = G_OBJECT_TYPE_NAME(x); \ hack_and_get_widget(PidginConversation *gtkconv)
- GtkWidget *tab_cont, *pane, *vbox, *hpaned, *frame;
+ GtkWidget *tab_cont, *vbox, *hpaned, *frame; /* If you think this is ugly, you are right. */
- name = G_OBJECT_TYPE_NAME(x); \
tab_cont = gtkconv->tab_cont;
list = gtk_container_get_children(GTK_CONTAINER(tab_cont));
+ if (!is_2_4_0_or_above()) { + GtkWidget *pane = list->data; + vbox = GTK_PANED(pane)->child1;
- vbox = GTK_PANED(pane)->child1;
list = GTK_BOX(vbox)->children;
+ if (GTK_IS_PANED(((GtkBoxChild*)list->data)->widget)) hpaned = ((GtkBoxChild*)list->data)->widget;
frame = GTK_PANED(hpaned)->child1;
@@ -310,6 +326,7 @@
box = gtk_hbox_new(FALSE, 0);
GTK_PANED(parent)->child1 = NULL;
gtk_paned_pack1(GTK_PANED(parent), box, TRUE, TRUE);
@@ -345,6 +362,17 @@
+workaround_for_hidden_convs(PidginConversation *gtkconv) + PurpleConversation *conv = gtkconv->active_conv; + if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT || + g_hash_table_lookup(xchats, conv)) + purple_conversation_use_xtext(conv); plugin_load(PurplePlugin *plugin)
@@ -370,10 +398,17 @@
list = purple_get_chats();
+ /* TODO: We can get the message history of the conversation and populate + * the next xtext with that data. + * Note: purple_conversation_get_message_history purple_conversation_use_xtext(list->data);
+ purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", + plugin, G_CALLBACK(workaround_for_hidden_convs), NULL); @@ -401,7 +436,7 @@
uiops->write_conv = default_write_conv;
uiops->create_conversation = default_create_conversation;
uiops->destroy_conversation = default_destroy_conversation;
/* Clear up everything */
g_hash_table_foreach(xchats, (GHFunc)remove_xtext, NULL);
g_hash_table_destroy(xchats);
--- a/xmmsremote/xmmsremote.c Sun Dec 23 07:18:21 2007 -0500
+++ b/xmmsremote/xmmsremote.c Mon Mar 24 15:08:23 2008 -0400
@@ -1,6 +1,6 @@
xmms-remote - Control xmms from Pidgin conversations
- Copyright (C) 2004 Gary Kramlich
+ Copyright (C) 2004-2008 Gary Kramlich This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License