# HG changeset patch # User Richard Laager # Date 1195252680 0 # Node ID 789ed158a50a224f142d270912361daeef031489 # Parent b2b16843851b306565b52a031976f68564641de6# Parent 308857bded2520e69c371ed7ebf3cf973769642a explicit merge of '2762c6075c0dc52a96098c5478c5bf68cfd890a3' and '6f090c623ea4e9357e5b4238348a888b4c869ab7' diff -r b2b16843851b -r 789ed158a50a COPYRIGHT --- a/COPYRIGHT Sat Oct 13 21:55:41 2007 +0000 +++ b/COPYRIGHT Fri Nov 16 22:38:00 2007 +0000 @@ -268,6 +268,7 @@ Ruediger Oertel Gudmundur Bjarni Olafsson Bartosz Oler +Stefan Ott Shawn Outman Nathan Owens (pianocomp81) John Oyler diff -r b2b16843851b -r 789ed158a50a ChangeLog --- a/ChangeLog Sat Oct 13 21:55:41 2007 +0000 +++ b/ChangeLog Fri Nov 16 22:38:00 2007 +0000 @@ -1,5 +1,24 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.0: + http://developer.pidgin.im/query?status=closed&milestone=2.3.0 + + libpurple: + * We now honor a PURPLE_DISABLE_DEPRECATED define to allow plugins to + catch deprecated functions earlier rather than later. + + Pidgin: + * If a plugin says it can't be unloaded, we now display an error and + remove the plugin from the list of saved plugins so it won't load + at the next startup. Previously, we were ignoring this case, which + could lead to crashes. + + Finch: + * If a plugin says it can't be unloaded, we now display an error and + remove the plugin from the list of saved plugins so it won't load + at the next startup. Previously, we were ignoring this case, which + could lead to crashes. + version 2.2.2: http://developer.pidgin.im/query?status=closed&milestone=2.2.2 NOTE: Due to 2.2.1 being a security fix release, some bugs diff -r b2b16843851b -r 789ed158a50a ChangeLog.API --- a/ChangeLog.API Sat Oct 13 21:55:41 2007 +0000 +++ b/ChangeLog.API Fri Nov 16 22:38:00 2007 +0000 @@ -1,5 +1,27 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.0 (??/??/????): + libpurple: + Added: + * purple_request_field_blist_nodes_new and its accessory functions. + * a PurpleConversation field in PurpleConvMessage + * account-authorization signals (see account-signals.dox for + details) (Stefan Ott) + * libpurple/purple.h, which includes #define's and #include's + required to compile stand-alone plugins + * purple_plugin_disable(), which is intended to be called when + a purple_plugin_unload()--which was called when a user tried + to unload a plugin--fails. This then prevents the plugin + from being saved in the saved plugins list, so it'll won't + be loaded at the next startup. + + Changed: + * purple_plugin_unload() now honors the return value of a + plugin's unload function and can actually return FALSE now. + * purple_plugin_unload() no longer does its own notifications + when a dependent plugin fails to unload. The UI should do + something appropriate. + version 2.2.2 (??/??/????): libpurple: Changed: diff -r b2b16843851b -r 789ed158a50a configure.ac --- a/configure.ac Sat Oct 13 21:55:41 2007 +0000 +++ b/configure.ac Fri Nov 16 22:38:00 2007 +0000 @@ -43,10 +43,10 @@ # # Make sure to update finch/libgnt/configure.ac with libgnt version changes. # -m4_define([purple_lt_current], [2]) +m4_define([purple_lt_current], [3]) m4_define([purple_major_version], [2]) -m4_define([purple_minor_version], [2]) -m4_define([purple_micro_version], [2]) +m4_define([purple_minor_version], [3]) +m4_define([purple_micro_version], [0]) m4_define([purple_version_suffix], []) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) diff -r b2b16843851b -r 789ed158a50a doc/account-signals.dox --- a/doc/account-signals.dox Sat Oct 13 21:55:41 2007 +0000 +++ b/doc/account-signals.dox Fri Nov 16 22:38:00 2007 +0000 @@ -9,6 +9,9 @@ @signal account-setting-info @signal account-set-info @signal account-status-changed + @signal account-authorization-requested + @signal account-authorization-denied + @signal account-authorization-granted @endsignals @see account.h @@ -102,5 +105,41 @@ @param old The alias before change. @endsignaldef + @signaldef account-authorization-requested + @signalproto +void (*account_authorization_requested)(PurpleAccount *account, const char *user); + @endsignalproto + @signaldesc + Emitted when a user requests authorization. + @param account The account. + @param user The name of the user requesting authorization. + @return Less than zero to deny the request without prompting, greater + than zero if the request should be granted. If zero is returned, + then the user will be prompted with the request. + @since 2.3.0 + @endsignaldef + + @signaldef account-authorization-denied + @signalproto +void (*account_authorization_denied)(PurpleAccount *account, const char *user); + @endsignalproto + @signaldesc + Emitted when the authorization request for a buddy is denied. + @param account The account. + @param user The name of the user requesting authorization. + @since 2.3.0 + @endsignaldef + + @signaldef account-authorization-granted + @signalproto +void (*account_authorization_granted)(PurpleAccount *account, const char *user); + @endsignalproto + @signaldesc + Emitted when the authorization request for a buddy is granted. + @param account The account. + @param user The name of the user requesting authorization. + @since 2.3.0 + @endsignaldef + */ // vim: syntax=c.doxygen tw=75 et diff -r b2b16843851b -r 789ed158a50a finch/gntblist.c --- a/finch/gntblist.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/gntblist.c Fri Nov 16 22:38:00 2007 +0000 @@ -2328,10 +2328,12 @@ gnt_menuitem_set_submenu(item, GNT_MENU(sub)); item = gnt_menuitem_new(_("Send IM...")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "send-im"); gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL); item = gnt_menuitem_new(_("Join Chat...")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "join-chat"); gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), join_chat_select, NULL); @@ -2341,12 +2343,14 @@ gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); item = gnt_menuitem_check_new(_("Empty groups")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-empty-groups"); gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_prefs_get_bool(PREF_ROOT "/emptygroups")); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups"); item = gnt_menuitem_check_new(_("Offline buddies")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-offline-buddies"); gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_prefs_get_bool(PREF_ROOT "/showoffline")); gnt_menu_add_item(GNT_MENU(subsub), item); @@ -2358,14 +2362,17 @@ gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); item = gnt_menuitem_new(_("By Status")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-status"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "status"); item = gnt_menuitem_new(_("Alphabetically")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-alpha"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "text"); item = gnt_menuitem_new(_("By Log Size")); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-log"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log"); @@ -2376,14 +2383,17 @@ gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); item = gnt_menuitem_new("Buddy"); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-buddy"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL); item = gnt_menuitem_new("Chat"); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-chat"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL); item = gnt_menuitem_new("Group"); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-group"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_group_cb, NULL); diff -r b2b16843851b -r 789ed158a50a finch/gntplugin.c --- a/finch/gntplugin.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/gntplugin.c Fri Nov 16 22:38:00 2007 +0000 @@ -83,6 +83,7 @@ if (!purple_plugin_unload(plugin)) { purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL); + purple_plugin_disable(plugin); gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE); } diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntmenu.c --- a/finch/libgnt/gntmenu.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntmenu.c Fri Nov 16 22:38:00 2007 +0000 @@ -459,3 +459,34 @@ menu->list = g_list_append(menu->list, item); } +GntMenuItem *gnt_menu_get_item(GntMenu *menu, const char *id) +{ + GntMenuItem *item = NULL; + GList *iter = menu->list; + + if (!id || !*id) + return NULL; + + for (; iter; iter = iter->next) { + GntMenu *sub; + item = iter->data; + sub = gnt_menuitem_get_submenu(item); + if (sub) { + item = gnt_menu_get_item(sub, id); + if (item) + break; + } else { + const char *itid = gnt_menuitem_get_id(item); + if (itid && strcmp(itid, id) == 0) + break; + /* XXX: Perhaps look at the menu-label as well? */ + } + item = NULL; + } + + if (item) + menuitem_activate(menu, item); + + return item; +} + diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntmenu.h --- a/finch/libgnt/gntmenu.h Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntmenu.h Fri Nov 16 22:38:00 2007 +0000 @@ -86,27 +86,37 @@ G_BEGIN_DECLS /** - * - * - * @return + * @return The GType for GntMenu. */ GType gnt_menu_get_gtype(void); /** - * - * @param type + * Create a new menu. * - * @return + * @param type The type of the menu, whether it's a toplevel menu or a popup menu. + * + * @return The newly created menu. */ GntWidget * gnt_menu_new(GntMenuType type); /** - * - * @param menu - * @param item + * Add an item to the menu. + * + * @param menu The menu. + * @param item The item to add to the menu. */ void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item); +/** + * Get the GntMenuItem with the given ID. + * + * @param menu The menu. + * @param id The ID for an item. + * + * @return The menuitem with the given ID, or @c NULL. + */ +GntMenuItem *gnt_menu_get_item(GntMenu *menu, const char *id); + G_END_DECLS #endif /* GNT_MENU_H */ diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntmenuitem.c --- a/finch/libgnt/gntmenuitem.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntmenuitem.c Fri Nov 16 22:38:00 2007 +0000 @@ -33,6 +33,7 @@ item->text = NULL; if (item->submenu) gnt_widget_destroy(GNT_WIDGET(item->submenu)); + g_free(item->priv.id); parent_class->dispose(obj); } @@ -104,6 +105,11 @@ item->submenu = menu; } +GntMenu *gnt_menuitem_get_submenu(GntMenuItem *item) +{ + return item->submenu; +} + void gnt_menuitem_set_trigger(GntMenuItem *item, char trigger) { item->priv.trigger = trigger; @@ -114,3 +120,14 @@ return item->priv.trigger; } +void gnt_menuitem_set_id(GntMenuItem *item, const char *id) +{ + g_free(item->priv.id); + item->priv.id = g_strdup(id); +} + +const char * gnt_menuitem_get_id(GntMenuItem *item) +{ + return item->priv.id; +} + diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntmenuitem.h --- a/finch/libgnt/gntmenuitem.h Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntmenuitem.h Fri Nov 16 22:38:00 2007 +0000 @@ -53,6 +53,7 @@ int x; int y; char trigger; + char *id; }; typedef void (*GntMenuItemCallback)(GntMenuItem *item, gpointer data); @@ -86,36 +87,46 @@ G_BEGIN_DECLS /** - * - * - * @return + * @return GType for GntMenuItem. */ GType gnt_menuitem_get_gtype(void); /** - * - * @param text + * Create a new menuitem. * - * @return + * @param text Label for the menuitem. + * + * @return The newly created menuitem. */ GntMenuItem * gnt_menuitem_new(const char *text); /** - * - * @param item - * @param callback - * @param data + * Set a callback function for a menuitem. + * + * @param item The menuitem. + * @param callback The callback function. + * @param data Data to send to the callback function. */ void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data); /** - * - * @param item - * @param menu + * Set a submenu for a menuitem. A menuitem with a submenu cannot have a callback. + * + * @param item The menuitem. + * @param menu The submenu. */ void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu); /** + * Get the submenu for a menuitem. + * + * @param item The menuitem. + * + * @return The submenu, or @c NULL. + */ +GntMenu *gnt_menuitem_get_submenu(GntMenuItem *item); + +/** * Set a trigger key for the item. * * @param item The menuitem @@ -134,6 +145,23 @@ */ char gnt_menuitem_get_trigger(GntMenuItem *item); +/** + * Set an ID for the menuitem. + * + * @param item The menuitem. + * @param id The ID for the menuitem. + */ +void gnt_menuitem_set_id(GntMenuItem *item, const char *id); + +/** + * Get the ID of the menuitem. + * + * @param item The menuitem. + * + * @return The ID for the menuitem. + */ +const char * gnt_menuitem_get_id(GntMenuItem *item); + G_END_DECLS #endif /* GNT_MENUITEM_H */ diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntstyle.c --- a/finch/libgnt/gntstyle.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntstyle.c Fri Nov 16 22:38:00 2007 +0000 @@ -227,6 +227,65 @@ #endif } +gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table) +{ +#if GLIB_CHECK_VERSION(2,6,0) + char *kname; + GError *error = NULL; + gboolean ret = FALSE; + + kname = g_strdup_printf("%s::menu", name); + + if (g_key_file_has_group(gkfile, kname)) + { + gsize len = 0; + char **keys; + + keys = g_key_file_get_keys(gkfile, kname, &len, &error); + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + g_free(kname); + return ret; + } + + while (len--) + { + char *key, *menuid; + + key = g_strdup(keys[len]); + menuid = g_key_file_get_string(gkfile, kname, keys[len], &error); + + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + error = NULL; + } + else + { + const char *keycode = parse_key(key); + if (keycode == NULL) { + g_printerr("GntStyle: Invalid key-binding %s\n", key); + } else { + ret = TRUE; + g_hash_table_replace(table, g_strdup(keycode), menuid); + menuid = NULL; + } + } + g_free(key); + g_free(menuid); + } + g_strfreev(keys); + } + + g_free(kname); + return ret; +#endif + return FALSE; +} + void gnt_styles_get_keyremaps(GType type, GHashTable *hash) { #if GLIB_CHECK_VERSION(2,6,0) diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntstyle.h Fri Nov 16 22:38:00 2007 +0000 @@ -93,6 +93,16 @@ */ void gnt_style_read_actions(GType type, GntBindableClass *klass); +/* + * Read menu-accels from ~/.gntrc + * + * @param name The name of the window. + * @param table The hastable to store the accel information. + * + * @return @c TRUE if some accels were read, @c FALSE otherwise. + */ +gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table); + void gnt_style_read_workspaces(GntWM *wm); /** diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntwindow.c --- a/finch/libgnt/gntwindow.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntwindow.c Fri Nov 16 22:38:00 2007 +0000 @@ -25,6 +25,11 @@ #include +struct _GntWindowPriv +{ + GHashTable *accels; /* key => menuitem-id */ +}; + enum { SIG_WORKSPACE_HIDE, @@ -55,6 +60,10 @@ GntWindow *window = GNT_WINDOW(widget); if (window->menu) gnt_widget_destroy(GNT_WIDGET(window->menu)); + if (window->priv) { + g_hash_table_destroy(window->priv->accels); + g_free(window->priv); + } org_destroy(widget); } @@ -98,8 +107,11 @@ gnt_window_init(GTypeInstance *instance, gpointer class) { GntWidget *widget = GNT_WIDGET(instance); + GntWindow *win = GNT_WINDOW(widget); GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + win->priv = g_new0(GntWindowPriv, 1); + win->priv->accels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); GNTDEBUG; } @@ -170,8 +182,23 @@ void gnt_window_set_menu(GntWindow *window, GntMenu *menu) { /* If a menu already existed, then destroy that first. */ + const char *name = gnt_widget_get_name(GNT_WIDGET(window)); if (window->menu) gnt_widget_destroy(GNT_WIDGET(window->menu)); window->menu = menu; + if (name && window->priv) { + if (!gnt_style_read_menu_accels(name, window->priv->accels)) { + g_hash_table_destroy(window->priv->accels); + g_free(window->priv); + window->priv = NULL; + } + } } +const char * gnt_window_get_accel_item(GntWindow *window, const char *key) +{ + if (window->priv) + return g_hash_table_lookup(window->priv->accels, key); + return NULL; +} + diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntwindow.h --- a/finch/libgnt/gntwindow.h Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntwindow.h Fri Nov 16 22:38:00 2007 +0000 @@ -52,6 +52,7 @@ { GntBox parent; GntMenu *menu; + GntWindowPriv *priv; }; struct _GntWindowClass @@ -99,6 +100,8 @@ */ void gnt_window_set_menu(GntWindow *window, GntMenu *menu); +const char * gnt_window_get_accel_item(GntWindow *window, const char *key); + void gnt_window_workspace_hiding(GntWindow *); void gnt_window_workspace_showing(GntWindow *); diff -r b2b16843851b -r 789ed158a50a finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Sat Oct 13 21:55:41 2007 +0000 +++ b/finch/libgnt/gntwm.c Fri Nov 16 22:38:00 2007 +0000 @@ -1852,8 +1852,19 @@ ret = gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys); else if (wm->_list.window) ret = gnt_widget_key_pressed(wm->_list.window, keys); - else if (wm->cws->ordered) - ret = gnt_widget_key_pressed(GNT_WIDGET(wm->cws->ordered->data), keys); + else if (wm->cws->ordered) { + GntWidget *win = wm->cws->ordered->data; + if (GNT_IS_WINDOW(win)) { + GntMenu *menu = GNT_WINDOW(win)->menu; + if (menu) { + const char *id = gnt_window_get_accel_item(GNT_WINDOW(win), keys); + if (id) + ret = (gnt_menu_get_item(menu, id) != NULL); + } + } + if (!ret) + ret = gnt_widget_key_pressed(win, keys); + } return ret; } diff -r b2b16843851b -r 789ed158a50a libpurple/Makefile.am --- a/libpurple/Makefile.am Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/Makefile.am Fri Nov 16 22:38:00 2007 +0000 @@ -114,6 +114,7 @@ privacy.h \ proxy.h \ prpl.h \ + purple.h \ request.h \ roomlist.h \ savedstatuses.h \ diff -r b2b16843851b -r 789ed158a50a libpurple/account.c --- a/libpurple/account.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/account.c Fri Nov 16 22:38:00 2007 +0000 @@ -63,7 +63,10 @@ PurpleAccountRequestType type; PurpleAccount *account; void *ui_handle; - + char *user; + gpointer userdata; + PurpleAccountRequestAuthorizationCb auth_cb; + PurpleAccountRequestAuthorizationCb deny_cb; } PurpleAccountRequestInfo; static PurpleAccountUiOps *account_ui_ops = NULL; @@ -1157,6 +1160,28 @@ } } +static void +request_auth_cb(void *data) +{ + PurpleAccountRequestInfo *info = data; + info->auth_cb(info->userdata); + purple_signal_emit(purple_accounts_get_handle(), + "account-authorization-granted", info->account, info->user); + g_free(info->user); + g_free(info); +} + +static void +request_deny_cb(void *data) +{ + PurpleAccountRequestInfo *info = data; + info->deny_cb(info->userdata); + purple_signal_emit(purple_accounts_get_handle(), + "account-authorization-denied", info->account, info->user); + g_free(info->user); + g_free(info); +} + void * purple_account_request_authorization(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message, gboolean on_list, @@ -1164,18 +1189,35 @@ { PurpleAccountUiOps *ui_ops; PurpleAccountRequestInfo *info; + int plugin_return; g_return_val_if_fail(account != NULL, NULL); g_return_val_if_fail(remote_user != NULL, NULL); ui_ops = purple_accounts_get_ui_ops(); + plugin_return = GPOINTER_TO_INT( + purple_signal_emit_return_1(purple_accounts_get_handle(), + "account-authorization-requested", account, remote_user)); + + if (plugin_return > 0) { + auth_cb(user_data); + return NULL; + } else if (plugin_return < 0) { + deny_cb(user_data); + return NULL; + } + if (ui_ops != NULL && ui_ops->request_authorize != NULL) { info = g_new0(PurpleAccountRequestInfo, 1); info->type = PURPLE_ACCOUNT_REQUEST_AUTHORIZATION; info->account = account; + info->auth_cb = auth_cb; + info->deny_cb = deny_cb; + info->userdata = user_data; + info->user = g_strdup(remote_user); info->ui_handle = ui_ops->request_authorize(account, remote_user, id, alias, message, - on_list, auth_cb, deny_cb, user_data); + on_list, request_auth_cb, request_deny_cb, info); handles = g_list_append(handles, info); return info->ui_handle; @@ -2452,6 +2494,25 @@ PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_STRING)); + purple_signal_register(handle, "account-authorization-requested", + purple_marshal_INT__POINTER_POINTER, + purple_value_new(PURPLE_TYPE_INT), 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_ACCOUNT), + purple_value_new(PURPLE_TYPE_STRING)); + + purple_signal_register(handle, "account-authorization-denied", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_ACCOUNT), + purple_value_new(PURPLE_TYPE_STRING)); + + purple_signal_register(handle, "account-authorization-granted", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_ACCOUNT), + purple_value_new(PURPLE_TYPE_STRING)); + load_accounts(); } diff -r b2b16843851b -r 789ed158a50a libpurple/blist.h --- a/libpurple/blist.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/blist.h Fri Nov 16 22:38:00 2007 +0000 @@ -40,6 +40,8 @@ typedef struct _PurpleContact PurpleContact; typedef struct _PurpleBuddy PurpleBuddy; +typedef gboolean (*PurpleFilterBlistFunc)(PurpleBlistNode *node); + /**************************************************************************/ /* Enumerations */ /**************************************************************************/ @@ -65,9 +67,12 @@ typedef enum { PURPLE_BLIST_NODE_FLAG_NO_SAVE = 1 << 0, /**< node should not be saved with the buddy list */ + PURPLE_BLIST_NODE_HAS_CONVERSATION = 1 << 1, /**< node (buddy or chat) has an open conversation */ } PurpleBlistNodeFlags; +#define PURPLE_BLIST_NODE_SET_FLAG(node, f) (((PurpleBlistNode *)node)->flags |= (f)) +#define PURPLE_BLIST_NODE_UNSET_FLAG(node, f) (((PurpleBlistNode *)node)->flags &= ~(f)) #define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (((PurpleBlistNode*)(b))->flags & (f)) #define PURPLE_BLIST_NODE_SHOULD_SAVE(b) (! PURPLE_BLIST_NODE_HAS_FLAG(b, PURPLE_BLIST_NODE_FLAG_NO_SAVE)) @@ -482,6 +487,7 @@ */ PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact); +#ifndef PURPLE_DISABLE_DEPRECATED /** * Sets the alias for a contact. * @@ -491,6 +497,7 @@ * @deprecated Use purple_blist_alias_contact() instead. */ void purple_contact_set_alias(PurpleContact *contact, const char *alias); +#endif /** * Gets the alias for a contact. diff -r b2b16843851b -r 789ed158a50a libpurple/conversation.c --- a/libpurple/conversation.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/conversation.c Fri Nov 16 22:38:00 2007 +0000 @@ -225,6 +225,7 @@ msg->flags = flags; msg->what = g_strdup(message); msg->when = when; + msg->conv = conv; conv->message_history = g_list_prepend(conv->message_history, msg); } diff -r b2b16843851b -r 789ed158a50a libpurple/conversation.h --- a/libpurple/conversation.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/conversation.h Fri Nov 16 22:38:00 2007 +0000 @@ -294,6 +294,7 @@ char *what; PurpleMessageFlags flags; time_t when; + PurpleConversation *conv; }; /** diff -r b2b16843851b -r 789ed158a50a libpurple/notify.h --- a/libpurple/notify.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/notify.h Fri Nov 16 22:38:00 2007 +0000 @@ -289,7 +289,7 @@ */ void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results, GList *row); - +#ifndef PURPLE_DISABLE_DEPRECATED /** * Returns a number of the rows in the search results object. * @@ -308,7 +308,9 @@ * @return Number of the result rows. */ guint purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Returns a number of the columns in the search results object. * @@ -327,7 +329,9 @@ * @return Number of the columns. */ guint purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Returns a row of the results from the search results object. * @@ -348,7 +352,9 @@ */ GList *purple_notify_searchresults_row_get(PurpleNotifySearchResults *results, unsigned int row_id); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Returns a title of the search results object's column. * @@ -367,6 +373,7 @@ */ char *purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results, unsigned int column_id); +#endif /*@}*/ diff -r b2b16843851b -r 789ed158a50a libpurple/plugin.c --- a/libpurple/plugin.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/plugin.c Fri Nov 16 22:38:00 2007 +0000 @@ -58,13 +58,9 @@ #ifdef PURPLE_PLUGINS static GList *load_queue = NULL; static GList *plugin_loaders = NULL; +static GList *plugins_to_disable = NULL; #endif -/* - * TODO: I think the intention was to allow multiple load and unload - * callback functions. Perhaps using a GList instead of a - * pointer to a single function. - */ static void (*probe_cb)(void *) = NULL; static void *probe_cb_data = NULL; static void (*load_cb)(PurplePlugin *, void *) = NULL; @@ -254,7 +250,6 @@ * plugins being added to the global name space. * * G_MODULE_BIND_LOCAL was added in glib 2.3.3. - * TODO: I guess there's nothing we can do about that? */ #if GLIB_CHECK_VERSION(2,3,3) plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL); @@ -625,7 +620,6 @@ plugin->loaded = TRUE; - /* TODO */ if (load_cb != NULL) load_cb(plugin, load_cb_data); @@ -643,43 +637,37 @@ { #ifdef PURPLE_PLUGINS GList *l; + GList *ll; g_return_val_if_fail(plugin != NULL, FALSE); - - loaded_plugins = g_list_remove(loaded_plugins, plugin); - if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin)) - protocol_plugins = g_list_remove(protocol_plugins, plugin); - g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE); purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name); - /* cancel any pending dialogs the plugin has */ - purple_request_close_with_handle(plugin); - purple_notify_close_with_handle(plugin); - - plugin->loaded = FALSE; - /* Unload all plugins that depend on this plugin. */ - while ((l = plugin->dependent_plugins) != NULL) - { + for (l = plugin->dependent_plugins; l != NULL; l = ll) { const char * dep_name = (const char *)l->data; PurplePlugin *dep_plugin; + /* Store a pointer to the next element in the list. + * This is because we'll be modifying this list in the loop. */ + ll = l->next; + dep_plugin = purple_plugins_find_with_id(dep_name); if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin)) { if (!purple_plugin_unload(dep_plugin)) { - char *tmp; - - tmp = g_strdup_printf(_("The dependent plugin %s failed to unload."), - _(dep_plugin->info->name)); - - purple_notify_error(NULL, NULL, - _("There were errors unloading the plugin."), tmp); - g_free(tmp); + g_free(plugin->error); + plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."), + _(plugin->info->name), + _(dep_plugin->info->name)); + return FALSE; + } + else + { + plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l); } } } @@ -699,8 +687,8 @@ } if (plugin->native_plugin) { - if (plugin->info->unload != NULL) - plugin->info->unload(plugin); + if (plugin->info->unload && !plugin->info->unload(plugin)) + return FALSE; if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) { PurplePluginProtocolInfo *prpl_info; @@ -724,8 +712,7 @@ prpl_info->protocol_options = NULL; } } - } - else { + } else { PurplePlugin *loader; PurplePluginLoaderInfo *loader_info; @@ -736,14 +723,30 @@ loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - if (loader_info->unload != NULL) - loader_info->unload(plugin); + if (loader_info->unload && !loader_info->unload(plugin)) + return FALSE; } + /* cancel any pending dialogs the plugin has */ + purple_request_close_with_handle(plugin); + purple_notify_close_with_handle(plugin); + purple_signals_disconnect_by_handle(plugin); purple_plugin_ipc_unregister_all(plugin); - /* TODO */ + loaded_plugins = g_list_remove(loaded_plugins, plugin); + if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin)) + protocol_plugins = g_list_remove(protocol_plugins, plugin); + plugins_to_disable = g_list_remove(plugins_to_disable, plugin); + plugin->loaded = FALSE; + + /* We wouldn't be anywhere near here if the plugin wasn't loaded, so + * if plugin->error is set at all, it had to be from a previous + * unload failure. It's obviously okay now. + */ + g_free(plugin->error); + plugin->error = NULL; + if (unload_cb != NULL) unload_cb(plugin, unload_cb_data); @@ -757,6 +760,15 @@ #endif /* PURPLE_PLUGINS */ } +void +purple_plugin_disable(PurplePlugin *plugin) +{ + g_return_if_fail(plugin != NULL); + + if (!g_list_find(plugins_to_disable, plugin)) + plugins_to_disable = g_list_prepend(plugins_to_disable, plugin); +} + gboolean purple_plugin_reload(PurplePlugin *plugin) { @@ -1222,14 +1234,14 @@ #ifdef PURPLE_PLUGINS GList *pl; GList *files = NULL; - PurplePlugin *p; for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) { - p = pl->data; + PurplePlugin *plugin = pl->data; - if (p->info->type != PURPLE_PLUGIN_PROTOCOL && - p->info->type != PURPLE_PLUGIN_LOADER) { - files = g_list_append(files, p->path); + if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL && + plugin->info->type != PURPLE_PLUGIN_LOADER && + !g_list_find(plugins_to_disable, plugin)) { + files = g_list_append(files, plugin->path); } } @@ -1391,6 +1403,7 @@ if (probe_cb != NULL) probe_cb(probe_cb_data); + #endif /* PURPLE_PLUGINS */ } @@ -1464,7 +1477,6 @@ void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data) { - /* TODO */ probe_cb = func; probe_cb_data = data; } @@ -1472,7 +1484,6 @@ void purple_plugins_unregister_probe_notify_cb(void (*func)(void *)) { - /* TODO */ probe_cb = NULL; probe_cb_data = NULL; } @@ -1481,7 +1492,6 @@ purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *), void *data) { - /* TODO */ load_cb = func; load_cb_data = data; } @@ -1489,7 +1499,6 @@ void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *)) { - /* TODO */ load_cb = NULL; load_cb_data = NULL; } @@ -1498,7 +1507,6 @@ purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *), void *data) { - /* TODO */ unload_cb = func; unload_cb_data = data; } @@ -1506,7 +1514,6 @@ void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *)) { - /* TODO */ unload_cb = NULL; unload_cb_data = NULL; } diff -r b2b16843851b -r 789ed158a50a libpurple/plugin.h --- a/libpurple/plugin.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/plugin.h Fri Nov 16 22:38:00 2007 +0000 @@ -70,11 +70,6 @@ * * This is used in the version 2.0 API and up. */ -/* TODO We need to figure out exactly what parts of this are required. The - * dependent plugin unloading stuff was causing crashes with perl and tcl - * plugins because they didn't set ids and the dependency code was requiring - * them. Then we need to actually make sure that plugins have all the right - * parts before loading them. */ struct _PurplePluginInfo { unsigned int magic; @@ -296,6 +291,18 @@ gboolean purple_plugin_unload(PurplePlugin *plugin); /** + * Disable a plugin. + * + * This function adds the plugin to a list of plugins to "disable at the next + * startup" by excluding said plugins from the list of plugins to save. The + * UI needs to call purple_plugins_save_loaded() after calling this for it + * to have any effect. + * + * @since 2.3.0 + */ +void purple_plugin_disable(PurplePlugin *plugin); + +/** * Reloads a plugin. * * @param plugin The old plugin handle. @@ -525,53 +532,71 @@ */ gboolean purple_plugins_enabled(void); +#ifndef PURPLE_DISABLE_DEPRECATED /** * Registers a function that will be called when probing is finished. * * @param func The callback function. * @param data Data to pass to the callback. + * @deprecated If you need this, ask for a plugin-probe signal to be added. */ void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Unregisters a function that would be called when probing is finished. * * @param func The callback function. + * @deprecated If you need this, ask for a plugin-probe signal to be added. */ void purple_plugins_unregister_probe_notify_cb(void (*func)(void *)); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Registers a function that will be called when a plugin is loaded. * * @param func The callback function. * @param data Data to pass to the callback. + * @deprecated Use the plugin-load signal instead. */ void purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *), void *data); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Unregisters a function that would be called when a plugin is loaded. * * @param func The callback function. + * @deprecated Use the plugin-load signal instead. */ void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *)); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Registers a function that will be called when a plugin is unloaded. * * @param func The callback function. * @param data Data to pass to the callback. + * @deprecated Use the plugin-unload signal instead. */ void purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *), void *data); +#endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Unregisters a function that would be called when a plugin is unloaded. * * @param func The callback function. + * @deprecated Use the plugin-unload signal instead. */ void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *)); +#endif /** * Finds a plugin with the specified name. diff -r b2b16843851b -r 789ed158a50a libpurple/plugins/signals-test.c --- a/libpurple/plugins/signals-test.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/plugins/signals-test.c Fri Nov 16 22:38:00 2007 +0000 @@ -76,6 +76,28 @@ old, purple_account_get_alias(account)); } +static int +account_authorization_requested_cb(PurpleAccount *account, const char *user, gpointer data) +{ + purple_debug_misc("signals test", "account-authorization-requested (%s, %s)\n", + purple_account_get_username(account), user); + return 0; +} + +static void +account_authorization_granted_cb(PurpleAccount *account, const char *user, gpointer data) +{ + purple_debug_misc("signals test", "account-authorization-granted (%s, %s)\n", + purple_account_get_username(account), user); +} + +static void +account_authorization_denied_cb(PurpleAccount *account, const char *user, gpointer data) +{ + purple_debug_misc("signals test", "account-authorization-denied (%s, %s)\n", + purple_account_get_username(account), user); +} + /************************************************************************** * Buddy Icons signal callbacks **************************************************************************/ @@ -568,6 +590,12 @@ plugin, PURPLE_CALLBACK(account_status_changed), NULL); purple_signal_connect(accounts_handle, "account-alias-changed", plugin, PURPLE_CALLBACK(account_alias_changed), NULL); + purple_signal_connect(accounts_handle, "account-authorization-requested", + plugin, PURPLE_CALLBACK(account_authorization_requested_cb), NULL); + purple_signal_connect(accounts_handle, "account-authorization-denied", + plugin, PURPLE_CALLBACK(account_authorization_denied_cb), NULL); + purple_signal_connect(accounts_handle, "account-authorization-granted", + plugin, PURPLE_CALLBACK(account_authorization_granted_cb), NULL); /* Buddy List subsystem signals */ purple_signal_connect(blist_handle, "buddy-status-changed", diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/google.c --- a/libpurple/protocols/jabber/google.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/google.c Fri Nov 16 22:38:00 2007 +0000 @@ -516,3 +516,22 @@ } return g_string_free(str, FALSE); } + +void jabber_google_presence_incoming(JabberStream *js, const char *user, JabberBuddyResource *jbr) +{ + if (!js->googletalk) + return; + if (jbr->status && !strncmp(jbr->status, "♫ ", strlen("♫ "))) { + purple_prpl_got_user_status(js->gc->account, user, "tune", + PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL); + jbr->status = NULL; + } else { + purple_prpl_got_user_status_deactive(js->gc->account, user, "tune"); + } +} + +char *jabber_google_presence_outgoing(PurpleStatus *tune) +{ + char *ret = g_strdup_printf("♫ %s", purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE)); + return ret; +} diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/google.h --- a/libpurple/protocols/jabber/google.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/google.h Fri Nov 16 22:38:00 2007 +0000 @@ -36,6 +36,10 @@ * if this roster item should continue to be processed */ gboolean jabber_google_roster_incoming(JabberStream *js, xmlnode *item); + +void jabber_google_presence_incoming(JabberStream *js, const char *who, JabberBuddyResource *jbr); +char *jabber_google_presence_outgoing(PurpleStatus *tune); + void jabber_google_roster_add_deny(PurpleConnection *gc, const char *who); void jabber_google_roster_rem_deny(PurpleConnection *gc, const char *who); diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Nov 16 22:38:00 2007 +0000 @@ -1400,10 +1400,11 @@ char *stripped; if(!(stripped = purple_markup_strip_html(jabber_buddy_get_status_msg(jb)))) { - PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(b)); - - if(!purple_status_is_available(status)) - stripped = g_strdup(purple_status_get_name(status)); + PurplePresence *presence = purple_buddy_get_presence(b); + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { + PurpleStatus *status = purple_presence_get_status(presence, "tune"); + stripped = g_strdup(purple_status_get_attr_string(status, PURPLE_TUNE_TITLE)); + } } if(stripped) { @@ -1429,6 +1430,7 @@ if(jb) { JabberBuddyResource *jbr = NULL; + PurplePresence *presence = purple_buddy_get_presence(b); const char *sub; GList *l; const char *mood; @@ -1455,7 +1457,7 @@ purple_notify_user_info_add_pair(user_info, _("Subscription"), sub); - status = purple_presence_get_active_status(purple_buddy_get_presence(b)); + status = purple_presence_get_active_status(presence); value = purple_status_get_attr_value(status, "mood"); if (value && purple_value_get_type(value) == PURPLE_TYPE_STRING && (mood = purple_value_get_string(value))) { @@ -1467,7 +1469,12 @@ g_free(moodplustext); } else purple_notify_user_info_add_pair(user_info, _("Mood"), mood); - } + } + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { + PurpleStatus *tune = purple_presence_get_status(presence, "tune"); + const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); + purple_notify_user_info_add_pair(user_info, _("Current media"), title); + } } for(l=jb->resources; l; l = l->next) { @@ -1532,15 +1539,6 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1555,15 +1553,6 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1578,15 +1567,6 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1601,15 +1581,6 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1624,15 +1595,6 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1650,6 +1612,20 @@ NULL); types = g_list_append(types, type); + type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE, + "tune", NULL, TRUE, TRUE, TRUE, + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + NULL); + types = g_list_append(types, type); + return types; } diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/message.c Fri Nov 16 22:38:00 2007 +0000 @@ -104,6 +104,7 @@ g_snprintf(buf, sizeof(buf), _("%s has left the conversation."), escaped); + g_free(escaped); /* At some point when we restructure PurpleConversation, * this should be able to be implemented by removing the diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Fri Nov 16 22:38:00 2007 +0000 @@ -33,6 +33,7 @@ #include "buddy.h" #include "chat.h" +#include "google.h" #include "presence.h" #include "iq.h" #include "jutil.h" @@ -104,13 +105,14 @@ char *stripped = NULL; JabberBuddyState state; int priority; - const char *artist, *title, *source, *uri, *track; - int length; + const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL; + int length = -1; gboolean allowBuzz; + PurplePresence *p = purple_account_get_presence(account); + PurpleStatus *tune; - if(NULL == status) { - PurplePresence *gpresence = purple_account_get_presence(account); - status = purple_presence_get_active_status(gpresence); + if (NULL == status) { + status = purple_presence_get_active_status(p); } if(!purple_status_is_active(status)) @@ -144,6 +146,12 @@ if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) || js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) { js->allowBuzz = allowBuzz; + + if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { + tune = purple_presence_get_status(p, "tune"); + stripped = jabber_google_presence_outgoing(tune); + } + presence = jabber_presence_create_js(js, state, stripped, priority); if(js->avatar_hash) { @@ -172,12 +180,16 @@ } /* next, check if there are any changes to the tune values */ - artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); - title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); - source = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM); - uri = purple_status_get_attr_string(status, PURPLE_TUNE_URL); - track = purple_status_get_attr_string(status, PURPLE_TUNE_TRACK); - length = (!purple_status_get_attr_value(status, PURPLE_TUNE_TIME))?-1:purple_status_get_attr_int(status, PURPLE_TUNE_TIME); + tune = purple_presence_get_status(p, "tune"); + if (tune && purple_status_is_active(tune)) { + artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); + title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); + source = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); + uri = purple_status_get_attr_string(tune, PURPLE_TUNE_URL); + track = purple_status_get_attr_string(tune, PURPLE_TUNE_TRACK); + length = (!purple_status_get_attr_value(tune, PURPLE_TUNE_TIME)) ? -1 : + purple_status_get_attr_int(tune, PURPLE_TUNE_TIME); + } if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) || CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) { @@ -731,7 +743,8 @@ } if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { - purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL); + jabber_google_presence_incoming(js, buddy_name, found_jbr); + purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); } else { purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL); } diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/jabber/usertune.c --- a/libpurple/protocols/jabber/usertune.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/jabber/usertune.c Fri Nov 16 22:38:00 2007 +0000 @@ -35,7 +35,6 @@ xmlnode *tuneinfo, *tune; PurpleJabberTuneInfo tuneinfodata; JabberBuddyResource *resource; - const char *status_id; /* ignore the tune of people not on our buddy list */ if (!buddy || !item) @@ -81,9 +80,8 @@ } } } - status_id = jabber_buddy_state_get_status_id(resource->state); - purple_prpl_got_user_status(js->gc->account, from, status_id, + purple_prpl_got_user_status(js->gc->account, from, "tune", PURPLE_TUNE_ARTIST, tuneinfodata.artist, PURPLE_TUNE_TITLE, tuneinfodata.title, PURPLE_TUNE_ALBUM, tuneinfodata.album, diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Fri Nov 16 22:38:00 2007 +0000 @@ -539,25 +539,36 @@ /* * Set the User status text - * Add the PSM String Using "Name - PSM String" format */ static char * msn_status_text(PurpleBuddy *buddy) { PurplePresence *presence; PurpleStatus *status; - const char *msg, *cmedia; + const char *msg; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); + /* I think status message should take precedence over media */ msg = purple_status_get_attr_string(status, "message"); - cmedia = purple_status_get_attr_string(status, "currentmedia"); - - if (cmedia) - return g_markup_escape_text(cmedia, -1); - else if (msg) + if (msg && *msg) return g_markup_escape_text(msg, -1); + + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { + const char *title, *artist; + char *media, *esc; + status = purple_presence_get_status(presence, "tune"); + title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); + artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); + + media = g_strdup_printf("%s%s%s", title, artist ? " - " : "", + artist ? artist : ""); + esc = g_markup_escape_text(media, -1); + g_free(media); + return esc; + } + return NULL; } @@ -570,14 +581,21 @@ user = buddy->proto_data; - if (purple_presence_is_online(presence)) { - const char *psm, *currentmedia, *name; + const char *psm, *name; + char *currentmedia = NULL; char *tmp; psm = purple_status_get_attr_string(status, "message"); - currentmedia = purple_status_get_attr_string(status, "currentmedia"); + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { + PurpleStatus *tune = purple_presence_get_status(presence, "tune"); + const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); + const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); + currentmedia = g_strdup_printf("%s%s%s", title, artist ? " - " : "", + artist ? artist : ""); + /* We could probably just use user->media.title etc. here */ + } if (!purple_presence_is_available(presence)) { name = purple_status_get_name(status); @@ -609,6 +627,7 @@ tmp = g_markup_escape_text(currentmedia, -1); purple_notify_user_info_add_pair(user_info, _("Current media"), tmp); g_free(tmp); + g_free(currentmedia); } } @@ -632,40 +651,34 @@ status = purple_status_type_new_with_attrs( PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs( PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs( PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs( PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs( PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs( PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); @@ -681,6 +694,14 @@ "mobile", NULL, FALSE, FALSE, TRUE); types = g_list_append(types, status); + status = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE, + "tune", NULL, TRUE, TRUE, TRUE, + PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING), + NULL); + types = g_list_append(types, status); + return types; } diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Fri Nov 16 22:38:00 2007 +0000 @@ -1616,10 +1616,8 @@ PurpleAccount *account; MsnUser *user; const char *passport; - char *psm_str, *currentmedia_str, *str; - - /*get the payload content*/ -// purple_debug_info("MSNP14","UBX {%s} payload{%s}\n",cmd->params[0], cmd->payload); + char *psm_str, *str; + CurrentMedia media = {NULL, NULL, NULL}; session = cmdproc->session; account = session->account; @@ -1628,16 +1626,17 @@ user = msn_userlist_find_user(session->userlist, passport); psm_str = msn_get_psm(cmd->payload,len); - currentmedia_str = msn_parse_currentmedia( - str = msn_get_currentmedia(cmd->payload, len)); + msn_user_set_statusline(user, psm_str); + g_free(psm_str); + + str = msn_get_currentmedia(cmd->payload, len); + if (msn_parse_currentmedia(str, &media)) + msn_user_set_currentmedia(user, &media); + else + msn_user_set_currentmedia(user, NULL); g_free(str); - msn_user_set_statusline(user, psm_str); - msn_user_set_currentmedia(user, currentmedia_str); msn_user_update(user); - - g_free(psm_str); - g_free(currentmedia_str); } static void diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/state.c Fri Nov 16 22:38:00 2007 +0000 @@ -83,60 +83,56 @@ } /* parse CurrentMedia string */ -char * -msn_parse_currentmedia(const char *cmedia) +gboolean +msn_parse_currentmedia(const char *cmedia, CurrentMedia *media) { char **cmedia_array; - GString *buffer = NULL; int strings; if ((cmedia == NULL) || (*cmedia == '\0')) { purple_debug_info("msn", "No currentmedia string\n"); - return NULL; + return FALSE; } purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia); cmedia_array = g_strsplit(cmedia, "\\0", 0); + /* + * 0: Media Player + * 1: 'Music' + * 2: '1' if enabled, '0' if not + * 3: Format (eg. {0} by {1}) + * 4: Title + * 5: Artist + * 6: Album + * 7: ? + */ strings = 0; - /* Yes, we want to skip the first element here, as it is empty due to - * the cmedia string starting with \0 -- see the examples below. */ while (cmedia_array[++strings] != NULL); - /* The cmedia_array[2] field contains a 1 if enabled. */ - if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) { - char *inptr = cmedia_array[3]; - - buffer = g_string_new(NULL); - - while (*inptr != '\0') { - if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}'))) { - char *tmpptr; - int tmp; - - errno = 0; - tmp = strtol(inptr + 1, &tmpptr, 10); + if (strings < 4) + return FALSE; + if (strcmp(cmedia_array[2], "1")) + return FALSE; - if (errno == 0 && tmpptr != inptr + 1 && - tmp + 4 < strings) { - /* Replace {?} tag with appropriate text only when successful. - * Skip otherwise. */ - buffer = g_string_append(buffer, cmedia_array[tmp + 4]); - } - inptr += 3; /* Skip to the next char after '}' */ - } else { - buffer = g_string_append_c(buffer, *inptr++); - } - } - purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n", - buffer->str); + if (strings == 4) { + media->title = g_strdup(cmedia_array[3]); } else { - purple_debug_info("msn", "Current media marked disabled, not parsing.\n"); + media->title = g_strdup(cmedia_array[4]); } - g_strfreev(cmedia_array); - return buffer ? g_string_free(buffer, FALSE) : NULL; + if (strings > 5) + media->artist = g_strdup(cmedia_array[5]); + else + media->artist = NULL; + + if (strings > 6) + media->album = g_strdup(cmedia_array[6]); + else + media->album = NULL; + + return TRUE; } /* get the CurrentMedia info from the XML string */ @@ -191,6 +187,27 @@ return psm; } +static char * +create_media_string(PurplePresence *presence) +{ + const char *artist, *title, *album; + char *ret; + PurpleStatus *status = purple_presence_get_status(presence, "tune"); + if (!status || !purple_status_is_active(status)) + return g_strdup_printf("WMP\\0Music\\00\\0{0} - {1}\\0\\0\\0\\0\\0"); + + artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); + title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); + album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM); + + ret = g_strdup_printf("WMP\\0Music\\0%c\\0{0} - {1}\\0%s\\0%s\\0%s\\0\\0", + (title && *title) ? '1' : '0', + title ? title : "", + artist ? artist : "", + album ? album : ""); + return ret; +} + /* set the MSN's PSM info,Currently Read from the status Line * Thanks for Cris Code */ @@ -204,7 +221,7 @@ MsnTransaction *trans; char *payload; const char *statusline; - gchar *unescapedstatusline; + gchar *unescapedstatusline, *media = NULL; g_return_if_fail(session != NULL); g_return_if_fail(session->notification != NULL); @@ -219,8 +236,10 @@ status = purple_presence_get_active_status(presence); statusline = purple_status_get_attr_string(status, "message"); unescapedstatusline = purple_unescape_html(statusline); - session->psm = msn_build_psm(unescapedstatusline, NULL, NULL); + media = create_media_string(presence); + session->psm = msn_build_psm(unescapedstatusline, media, NULL); g_free(unescapedstatusline); + g_free(media); payload = session->psm; purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload); diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/state.h --- a/libpurple/protocols/msn/state.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/state.h Fri Nov 16 22:38:00 2007 +0000 @@ -62,7 +62,7 @@ void msn_set_psm(MsnSession *session); /* Parse CurrentMedia string */ -char * msn_parse_currentmedia(const char *cmedia); +gboolean msn_parse_currentmedia(const char *cmedia, CurrentMedia *media); /* Get the CurrentMedia info from the XML string */ char * msn_get_currentmedia(char *xml_str,gsize len); diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/user.c Fri Nov 16 22:38:00 2007 +0000 @@ -80,6 +80,9 @@ g_free(user->phone.home); g_free(user->phone.work); g_free(user->phone.mobile); + g_free(user->media.artist); + g_free(user->media.title); + g_free(user->media.album); g_free(user); } @@ -91,23 +94,24 @@ account = user->userlist->session->account; - if (user->statusline != NULL && user->currentmedia != NULL) { + if (user->status != NULL) { + gboolean offline = (strcmp(user->status, "offline") == 0); purple_prpl_got_user_status(account, user->passport, user->status, - "message", user->statusline, - "currentmedia", user->currentmedia, NULL); - } else if (user->currentmedia != NULL) { - purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia", - user->currentmedia, NULL); - } else if (user->statusline != NULL) { - //char *status = g_strdup_printf("%s - %s", user->status, user->statusline); - purple_prpl_got_user_status(account, user->passport, user->status, - "message", user->statusline, NULL); - } else if (user->status != NULL) { - if (!strcmp(user->status, "offline") && user->mobile) { - purple_prpl_got_user_status(account, user->passport, "offline", NULL); + "message", user->statusline, NULL); + + if (!offline && user->media.title) { + purple_prpl_got_user_status(account, user->passport, "tune", + PURPLE_TUNE_ARTIST, user->media.artist, + PURPLE_TUNE_ALBUM, user->media.album, + PURPLE_TUNE_TITLE, user->media.title, + NULL); + } else { + purple_prpl_got_user_status_deactive(account, user->passport, "tune"); + } + + if (!offline && user->mobile) { purple_prpl_got_user_status(account, user->passport, "mobile", NULL); } else { - purple_prpl_got_user_status(account, user->passport, user->status, NULL); purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); } } @@ -172,12 +176,17 @@ } void -msn_user_set_currentmedia(MsnUser *user, const char *currentmedia) +msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *media) { g_return_if_fail(user != NULL); - g_free(user->currentmedia); - user->currentmedia = g_strdup(currentmedia); + g_free(user->media.title); + g_free(user->media.album); + g_free(user->media.artist); + + user->media.title = media ? g_strdup(media->title) : NULL; + user->media.artist = media ? g_strdup(media->artist) : NULL; + user->media.album = media ? g_strdup(media->album) : NULL; } void diff -r b2b16843851b -r 789ed158a50a libpurple/protocols/msn/user.h --- a/libpurple/protocols/msn/user.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/protocols/msn/user.h Fri Nov 16 22:38:00 2007 +0000 @@ -43,6 +43,16 @@ } MsnUserType; /** + * Current media. + */ +typedef struct _CurrentMedia +{ + char *artist; /**< Artist. */ + char *album; /**< Album. */ + char *title; /**< Title. */ +} CurrentMedia; + +/** * A user. */ struct _MsnUser @@ -60,7 +70,7 @@ const char *status; /**< The state of the user. */ char *statusline; /**< The state of the user. */ - char *currentmedia; /**< The current media of the user. */ + CurrentMedia media; /**< Current media of the user. */ gboolean idle; /**< The idle state of the user. */ @@ -134,10 +144,10 @@ /** * Sets the current media of user. * - * @param user The user. - * @param state The statusline string. + * @param user The user. + * @param cmedia Current media. */ -void msn_user_set_currentmedia(MsnUser *user, const char *currentmedia); +void msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *cmedia); /** * Sets the new state of user. diff -r b2b16843851b -r 789ed158a50a libpurple/purple.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/purple.h Fri Nov 16 22:38:00 2007 +0000 @@ -0,0 +1,92 @@ +/** + * @file purple.h Header files and defines + * This file contains all the necessary preprocessor directives to include + * libpurple's headers and other preprocessor directives required for plugins + * or UIs to build. Inlcuding this file eliminates the need to directly + * include any other libpurple files. It will still be necessary for plugins + * to define @c PURPLE_PLUGINS before including this header. + * + * @ingroup core libpurple + */ + +/* purple + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _PURPLE_H +#define _PURPLE_H + +#ifndef G_GNUC_NULL_TERMINATED +# if __GNUC__ >= 4 +# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +# else +# define G_GNUC_NULL_TERMINATED +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff -r b2b16843851b -r 789ed158a50a libpurple/request.c --- a/libpurple/request.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/request.c Fri Nov 16 22:38:00 2007 +0000 @@ -377,6 +377,13 @@ g_hash_table_destroy(field->u.list.item_data); g_hash_table_destroy(field->u.list.selected_table); } + else if (field->type == PURPLE_REQUEST_FIELD_BLIST) + { + if (field->u.blist.default_nodes) + g_list_free(field->u.blist.default_nodes); + if (field->u.blist.selecteds) + g_list_free(field->u.blist.selecteds); + } g_free(field); } @@ -1133,6 +1140,85 @@ /* -- */ +PurpleRequestField *purple_request_field_blist_nodes_new(const char *id, + const char *text, PurpleRequestBlistFlags flags, GList *selected) +{ + PurpleRequestField *field; + + g_return_val_if_fail(id != NULL, NULL); + g_return_val_if_fail(text != NULL, NULL); + + field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_BLIST); + + field->u.blist.flags = flags; + field->u.blist.default_nodes = selected; + purple_request_field_blist_set_selection_list(field, selected); + + return field; +} + +PurpleFilterBlistFunc +purple_request_field_blist_set_filter(PurpleRequestField *field, PurpleFilterBlistFunc filter) +{ + PurpleFilterBlistFunc old; + g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL); + old = field->u.blist.filter; + field->u.blist.filter = filter; + return old; +} + +PurpleFilterBlistFunc +purple_request_field_blist_get_filter(const PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL); + return field->u.blist.filter; +} + +GList *purple_request_field_blist_get_selection_list(const PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, NULL); + return field->u.blist.selecteds; +} + +gboolean purple_request_field_blist_add(PurpleRequestField *field, PurpleBlistNode *node) +{ + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, FALSE); + if (!g_list_find(field->u.blist.selecteds, node)) { + field->u.blist.selecteds = g_list_append(field->u.blist.selecteds, node); + return TRUE; + } else { + return FALSE; + } +} + +gboolean purple_request_field_blist_remove(PurpleRequestField *field, PurpleBlistNode *node) +{ + GList *search; + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST, FALSE); + if ((search = g_list_find(field->u.blist.selecteds, node)) != NULL) { + field->u.blist.selecteds = g_list_delete_link(field->u.blist.selecteds, search); + return TRUE; + } else { + return FALSE; + } +} + +void purple_request_field_blist_set_selection_list(PurpleRequestField *field, GList *selecteds) +{ + g_return_if_fail(field != NULL); + g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_BLIST); + if (field->u.blist.selecteds) + g_list_free(field->u.blist.selecteds); + field->u.blist.selecteds = selecteds; +} + +/* -- */ + void * purple_request_input(void *handle, const char *title, const char *primary, const char *secondary, const char *default_value, diff -r b2b16843851b -r 789ed158a50a libpurple/request.h --- a/libpurple/request.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/request.h Fri Nov 16 22:38:00 2007 +0000 @@ -61,7 +61,8 @@ PURPLE_REQUEST_FIELD_LIST, PURPLE_REQUEST_FIELD_LABEL, PURPLE_REQUEST_FIELD_IMAGE, - PURPLE_REQUEST_FIELD_ACCOUNT + PURPLE_REQUEST_FIELD_ACCOUNT, + PURPLE_REQUEST_FIELD_BLIST, } PurpleRequestFieldType; @@ -94,6 +95,17 @@ } PurpleRequestFieldGroup; /** + * Flags that can be used for Buddylist Fields. + */ +typedef enum +{ + PURPLE_REQUEST_BLIST_FLAG_BUDDY = 0x01, /**< Include buddies in the list. */ + PURPLE_REQUEST_BLIST_FLAG_CHAT = 0x02, /**< Include chats in the list. */ + PURPLE_REQUEST_BLIST_FLAG_GROUP = 0x04, /**< Include groups in the list. */ + PURPLE_REQUEST_BLIST_FLAG_ALLOW_OFFLINE = 0x08, /**< Include offline buddies in the list. */ +} PurpleRequestBlistFlags; + +/** * A request field. */ typedef struct @@ -172,6 +184,14 @@ gsize size; } image; + struct + { + GList *default_nodes; + PurpleRequestBlistFlags flags; + GList *selecteds; + PurpleFilterBlistFunc filter; + } blist; + } u; void *ui_data; @@ -1151,6 +1171,98 @@ /*@}*/ /**************************************************************************/ +/** @name Buddylist Field API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a buddylist field. + * + * @param id The field ID. + * @param text The label for the field. + * @param flag Flags dictating what kind of blist nodes should be + * included in the request field. + * @param selected A list of PurpleBlistNode's to select by default, or @c NULL. + * + * @return The new field. + * + * @since 2.3.0 + */ +PurpleRequestField *purple_request_field_blist_nodes_new(const char *id, const char *text, + PurpleRequestBlistFlags flag, GList *selected); + +/** + * Set a filter for the request field. + * + * @param field The request field. + * @param filter The filter function. + * + * @return The old filter function, or @c NULL if there was none. + * + * @since 2.3.0 + */ +PurpleFilterBlistFunc purple_request_field_blist_set_filter(PurpleRequestField *field, PurpleFilterBlistFunc filter); + +/** + * Get the filter function for the request field. + * + * @param field The request field. + * + * @return The filter function, or @c NULL if there isn't any. + * + * @since 2.3.0 + */ +PurpleFilterBlistFunc purple_request_field_blist_get_filter(const PurpleRequestField *field); + +/** + * Add a PurpleBlistNode to the selected list. + * + * @param field The request field. + * @param node The buddylist node to add to the list. + * + * @return @c TRUE if the node is added to the list, @c FALSE if it was already in the list. + * + * @since 2.3.0 + */ +gboolean purple_request_field_blist_add(PurpleRequestField *field, PurpleBlistNode *node); + +/** + * Remove a PurpleBlistNode from the selected list. + * + * @param field The request field. + * @param node The buddylist node to remove from the list. + * + * @return @c TRUE if the node is removed from the list, @c FALSE if the node is not in the list. + * + * @since 2.3.0 + */ +gboolean purple_request_field_blist_remove(PurpleRequestField *field, PurpleBlistNode *node); + +/** + * Set the list of selected nodes in the request field. + * + * @param field The request field. + * @param selecteds The list of selected PurpleBlistNode's. Note that the request field + * becomes the owner of the list, and so the caller should not modify it. + * + * @since 2.3.0 + */ +void purple_request_field_blist_set_selection_list(PurpleRequestField *field, GList *selecteds); + +/** + * Get a list of the selected buddylist nodes. + * + * @param field The request field. + * + * @return A GList of PurpleBlistNode's. + * + * @since 2.3.0 + */ +GList *purple_request_field_blist_get_selection_list(const PurpleRequestField *field); + +/*@}*/ + +/**************************************************************************/ /** @name Request API */ /**************************************************************************/ /*@{*/ diff -r b2b16843851b -r 789ed158a50a libpurple/signals.c --- a/libpurple/signals.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/signals.c Fri Nov 16 22:38:00 2007 +0000 @@ -794,6 +794,19 @@ *return_val = GINT_TO_POINTER(ret_val); } +void +purple_marshal_INT__POINTER_POINTER(PurpleCallback cb, va_list args, void *data, + void **return_val) +{ + gint ret_val; + void *arg1 = va_arg(args, void *); + void *arg2 = va_arg(args, void *); + + ret_val = ((gint (*)(void *, void *, void *))cb)(arg1, arg2, data); + + if (return_val != NULL) + *return_val = GINT_TO_POINTER(ret_val); +} void purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER( diff -r b2b16843851b -r 789ed158a50a libpurple/signals.h --- a/libpurple/signals.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/signals.h Fri Nov 16 22:38:00 2007 +0000 @@ -307,6 +307,8 @@ PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_INT__INT_INT( PurpleCallback cb, va_list args, void *data, void **return_val); +void purple_marshal_INT__POINTER_POINTER( + PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER( PurpleCallback cb, va_list args, void *data, void **return_val); diff -r b2b16843851b -r 789ed158a50a libpurple/sslconn.h --- a/libpurple/sslconn.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/sslconn.h Fri Nov 16 22:38:00 2007 +0000 @@ -185,6 +185,7 @@ PurpleSslErrorFunction error_func, void *data); +#ifndef PURPLE_DISABLE_DEPRECATED /** * Makes a SSL connection using an already open file descriptor. * @@ -202,6 +203,7 @@ PurpleSslInputFunction func, PurpleSslErrorFunction error_func, void *data); +#endif /** * Makes a SSL connection using an already open file descriptor. diff -r b2b16843851b -r 789ed158a50a libpurple/status.c --- a/libpurple/status.c Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/status.c Fri Nov 16 22:38:00 2007 +0000 @@ -157,7 +157,8 @@ { PURPLE_STATUS_INVISIBLE, "invisible", N_("Invisible") }, { PURPLE_STATUS_AWAY, "away", N_("Away") }, { PURPLE_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended away") }, - { PURPLE_STATUS_MOBILE, "mobile", N_("Mobile") } + { PURPLE_STATUS_MOBILE, "mobile", N_("Mobile") }, + { PURPLE_STATUS_TUNE, "tune", N_("Listening to music") } }; const char * @@ -903,6 +904,8 @@ } g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING); + /* XXX: Check if the value has actually changed. If it has, and the status + * is active, should this trigger 'status_has_changed'? */ purple_value_set_string(attr_value, value); } diff -r b2b16843851b -r 789ed158a50a libpurple/status.h --- a/libpurple/status.h Sat Oct 13 21:55:41 2007 +0000 +++ b/libpurple/status.h Fri Nov 16 22:38:00 2007 +0000 @@ -94,6 +94,10 @@ /** * A primitive defining the basic structure of a status type. */ +/* + * If you add a value to this enum, make sure you update + * the status_primitive_map array in status.c. + */ typedef enum { PURPLE_STATUS_UNSET = 0, @@ -104,6 +108,7 @@ PURPLE_STATUS_AWAY, PURPLE_STATUS_EXTENDED_AWAY, PURPLE_STATUS_MOBILE, + PURPLE_STATUS_TUNE, PURPLE_STATUS_NUM_PRIMITIVES } PurpleStatusPrimitive; diff -r b2b16843851b -r 789ed158a50a pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtkblist.c Fri Nov 16 22:38:00 2007 +0000 @@ -3384,7 +3384,7 @@ return ret; } - if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) { + if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL); ret = gdk_pixbuf_new_from_file(path, NULL); g_free(path); diff -r b2b16843851b -r 789ed158a50a pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtkconv.c Fri Nov 16 22:38:00 2007 +0000 @@ -7408,6 +7408,17 @@ pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC); } +/* Message history stuff */ + +/* Compare two PurpleConvMessage's, according to time in ascending order. */ +static int +message_compare(gconstpointer p1, gconstpointer p2) +{ + const PurpleConvMessage *m1 = p1, *m2 = p2; + return (m1->when > m2->when); +} + +/* Adds some message history to the gtkconv. This happens in a idle-callback. */ static gboolean add_message_history_to_gtkconv(gpointer data) { @@ -7415,49 +7426,106 @@ int count = 0; int timer = gtkconv->attach.timer; time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time")); + gboolean im = (gtkconv->active_conv->type == PURPLE_CONV_TYPE_IM); gtkconv->attach.timer = 0; while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ PurpleConvMessage *msg = gtkconv->attach.current->data; - if (when && when < msg->when) { + if (!im && when && when < msg->when) { gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "

", 0); g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); } - pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when); - gtkconv->attach.current = gtkconv->attach.current->prev; + pidgin_conv_write_conv(msg->conv, msg->who, msg->who, msg->what, msg->flags, msg->when); + if (im) { + gtkconv->attach.current = g_list_delete_link(gtkconv->attach.current, gtkconv->attach.current); + } else { + gtkconv->attach.current = gtkconv->attach.current->prev; + } count++; } gtkconv->attach.timer = timer; if (gtkconv->attach.current) return TRUE; + g_source_remove(gtkconv->attach.timer); + gtkconv->attach.timer = 0; + if (im) { + /* Print any message that was sent while the old history was being added back. */ + GList *msgs = NULL; + GList *iter = gtkconv->convs; + for (; iter; iter = iter->next) { + PurpleConversation *conv = iter->data; + GList *history = purple_conversation_get_message_history(conv); + for (; history; history = history->next) { + PurpleConvMessage *msg = history->data; + if (msg->when > when) + msgs = g_list_prepend(msgs, msg); + } + } + msgs = g_list_sort(msgs, message_compare); + for (; msgs; msgs = g_list_delete_link(msgs, msgs)) { + PurpleConvMessage *msg = msgs->data; + pidgin_conv_write_conv(msg->conv, msg->who, msg->who, msg->what, msg->flags, msg->when); + } + gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "

", 0); + g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); + } + g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); purple_signal_emit(pidgin_conversations_get_handle(), "conversation-displayed", gtkconv); - g_source_remove(gtkconv->attach.timer); - gtkconv->attach.timer = 0; return FALSE; } +static void +pidgin_conv_attach(PurpleConversation *conv) +{ + int timer; + purple_conversation_set_data(conv, "unseen-count", NULL); + purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops()); + private_gtkconv_new(conv, FALSE); + timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); + if (timer) + purple_timeout_remove(timer); +} + gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv) { GList *list; PidginConversation *gtkconv; - int timer; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) return FALSE; - purple_conversation_set_data(conv, "unseen-count", NULL); - purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops()); - private_gtkconv_new(conv, FALSE); + pidgin_conv_attach(conv); gtkconv = PIDGIN_CONVERSATION(conv); list = purple_conversation_get_message_history(conv); if (list) { + switch (purple_conversation_get_type(conv)) { + case PURPLE_CONV_TYPE_IM: + { + GList *convs; + list = g_list_copy(list); + for (convs = purple_get_ims(); convs; convs = convs->next) + if (convs->data != conv && + pidgin_conv_find_gtkconv(convs->data) == gtkconv) { + pidgin_conv_attach(convs->data); + list = g_list_concat(list, g_list_copy(purple_conversation_get_message_history(convs->data))); + } + list = g_list_sort(list, message_compare); + gtkconv->attach.current = list; + list = g_list_last(list); + break; + } + case PURPLE_CONV_TYPE_CHAT: + gtkconv->attach.current = g_list_last(list); + break; + default: + g_return_val_if_reached(TRUE); + } g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when)); - gtkconv->attach.current = g_list_last(list); gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv); } else { purple_signal_emit(pidgin_conversations_get_handle(), @@ -7469,9 +7537,6 @@ pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE); } - timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); - if (timer) - purple_timeout_remove(timer); return TRUE; } @@ -9802,3 +9867,4 @@ return colors; } + diff -r b2b16843851b -r 789ed158a50a pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtkdialogs.c Fri Nov 16 22:38:00 2007 +0000 @@ -756,6 +756,10 @@ purple_request_field_set_required(field, TRUE); purple_request_field_group_add_field(group, field); + field = purple_request_field_blist_nodes_new("blistnodes", _("Buddy"), + PURPLE_REQUEST_BLIST_FLAG_BUDDY, NULL); + purple_request_field_group_add_field(group, field); + field = purple_request_field_account_new("account", _("_Account"), NULL); purple_request_field_set_type_hint(field, "account"); purple_request_field_set_visible(field, diff -r b2b16843851b -r 789ed158a50a pidgin/gtknotify.c --- a/pidgin/gtknotify.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtknotify.c Fri Nov 16 22:38:00 2007 +0000 @@ -270,8 +270,9 @@ primary_esc = g_markup_escape_text(primary, -1); secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL; g_snprintf(label_text, sizeof(label_text), - "%s\n\n%s", - primary_esc, (secondary ? secondary_esc : "")); + "%s%s%s", + primary_esc, (secondary ? "\n\n" : ""), + (secondary ? secondary_esc : "")); g_free(primary_esc); g_free(secondary_esc); diff -r b2b16843851b -r 789ed158a50a pidgin/gtkplugin.c --- a/pidgin/gtkplugin.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtkplugin.c Fri Nov 16 22:38:00 2007 +0000 @@ -303,7 +303,24 @@ { pidgin_set_cursor(plugin_dialog, GDK_WATCH); - purple_plugin_unload(plug); + if (!purple_plugin_unload(plug)) + { + const char *primary = _("Could not unload plugin"); + const char *reload = _("The plugin could not be unloaded now, but will be disabled at the next startup."); + + if (!plug->error) + { + purple_notify_warning(NULL, NULL, primary, reload); + } + else + { + char *tmp = g_strdup_printf("%s\n\n%s", reload, plug->error); + purple_notify_warning(NULL, NULL, primary, tmp); + g_free(tmp); + } + + purple_plugin_disable(plug); + } pidgin_clear_cursor(plugin_dialog); } diff -r b2b16843851b -r 789ed158a50a pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtkrequest.c Fri Nov 16 22:38:00 2007 +0000 @@ -29,6 +29,7 @@ #include "prefs.h" #include "util.h" +#include "gtkblist.h" #include "gtkimhtml.h" #include "gtkimhtmltoolbar.h" #include "gtkrequest.h" @@ -952,6 +953,92 @@ return widget; } +static GtkWidget * +create_blist_field(PurpleRequestField *field) +{ + GtkTreeStore *model; + GtkWidget *tree, *sw; + PurpleBlistNode *node; + GtkCellRenderer *rend; + GtkTreeViewColumn *column; + GtkTreeIter parent = {0, NULL, NULL, NULL}, iter; + PurpleRequestBlistFlags flags = field->u.blist.flags; + gboolean offline = !!(field->u.blist.flags & PURPLE_REQUEST_BLIST_FLAG_ALLOW_OFFLINE); + + /* Create the treeview. Populate the blistnodes. + * Hook to signed-on, signed-off signals to update the list when account goes online/offline. + * Hook to buddy-signed-on/off, -status-changed signals to update the status pixbuf. + */ + + model = gtk_tree_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF); + node = purple_blist_get_root(); + while (node) { + GdkPixbuf *status = NULL, *prpl = NULL; + const char *name = NULL; + if ((PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node)) + && (flags & PURPLE_REQUEST_BLIST_FLAG_BUDDY)) { + PurpleBuddy *buddy; + GtkTreeIter *p = NULL; + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + buddy = (PurpleBuddy*)node; + p = &parent; + } else { + buddy = purple_contact_get_priority_buddy((PurpleContact*)node); + parent = iter; + } + if (PURPLE_BUDDY_IS_ONLINE(buddy)) { + gtk_tree_store_append(model, &iter, p); + name = purple_buddy_get_name(buddy); + status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL); + } + } else if (PURPLE_BLIST_NODE_IS_CHAT(node) && (flags & PURPLE_REQUEST_BLIST_FLAG_CHAT)) { + gtk_tree_store_append(model, &iter, NULL); + name = purple_chat_get_name((PurpleChat*)node); + status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL); + } + if (name) + gtk_tree_store_set(model, &iter, + 0, status, + 1, name, + 2, prpl, + -1); + if (prpl) + gdk_pixbuf_unref(prpl); + if (status) + gdk_pixbuf_unref(status); + node = purple_blist_node_next(node, offline); + } + + tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), 1); + gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree), pidgin_tree_view_search_equal_func, NULL, NULL); + gtk_widget_show(tree); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", 0, NULL); + + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, TRUE); + gtk_tree_view_column_set_attributes(column, rend, "markup", 1, NULL); + + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", 2, NULL); + + sw = gtk_scrolled_window_new(NULL,NULL); + gtk_widget_show(sw); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_NONE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(sw), tree); + + return sw; +} + static void select_field_list_item(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) @@ -1328,6 +1415,8 @@ widget = create_image_field(field); else if (type == PURPLE_REQUEST_FIELD_ACCOUNT) widget = create_account_field(field); + else if (type == PURPLE_REQUEST_FIELD_BLIST) + widget = create_blist_field(field); else continue; diff -r b2b16843851b -r 789ed158a50a pidgin/gtksavedstatuses.c --- a/pidgin/gtksavedstatuses.c Sat Oct 13 21:55:41 2007 +0000 +++ b/pidgin/gtksavedstatuses.c Fri Nov 16 22:38:00 2007 +0000 @@ -63,7 +63,7 @@ }; /** - * These is used for the GtkTreeView containing the list of accounts + * These are used for the GtkTreeView containing the list of accounts * at the bottom of the window when you're editing a particular * saved status. */ @@ -898,6 +898,12 @@ for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++) { + if (i == PURPLE_STATUS_MOBILE || i == PURPLE_STATUS_TUNE) + /* + * Special-case these. They're intended to be independent + * status types, so don't show them in the list. + */ + continue; item = gtk_menu_item_new_with_label(purple_primitive_get_name_from_type(i)); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); } @@ -1588,8 +1594,12 @@ status_type = list->data; - /* Only allow users to select statuses that are flagged as "user settable" */ - if (!purple_status_type_is_user_settable(status_type)) + /* + * Only allow users to select statuses that are flagged as + * "user settable" and that aren't independent. + */ + if (!purple_status_type_is_user_settable(status_type) || + purple_status_type_is_independent(status_type)) continue; id = purple_status_type_get_id(status_type);