# HG changeset patch # User Richard Laager # Date 1192159937 0 # Node ID d604027959a5022c9ff43c911a16b464c4c923aa # Parent 3301b2f813f0426335b4079939332a2c5d0d6878# Parent 6bbd66f67fa8bdebaf0bc21279c6cfea61fb9d15 merge of '87d5331b62c3e62aaacddc0fe0a8c3f0787cda2b' and 'd06c9d5a05c154d236601f6380dbfd9bb299c30a' diff -r 3301b2f813f0 -r d604027959a5 COPYRIGHT --- a/COPYRIGHT Wed Oct 10 04:04:52 2007 +0000 +++ b/COPYRIGHT Fri Oct 12 03:32:17 2007 +0000 @@ -267,6 +267,7 @@ Ruediger Oertel Gudmundur Bjarni Olafsson Bartosz Oler +Stefan Ott Shawn Outman Nathan Owens (pianocomp81) John Oyler diff -r 3301b2f813f0 -r d604027959a5 ChangeLog.API --- a/ChangeLog.API Wed Oct 10 04:04:52 2007 +0000 +++ b/ChangeLog.API Fri Oct 12 03:32:17 2007 +0000 @@ -3,6 +3,16 @@ version 2.3.0 (??/??/????): libpurple: Added: + * purple_request_field_blist_nodes_new and its accessory functions. + * Added a PurpleConversation field in PurpleConvMessage + * Added account-authorization signals. (see account-signals.dox for + details) (Stefan Ott) + * Added libpurple/purple.h, which includes #define's and #include's + required to compile stand-alone plugins. + +version 2.3.0 (??/??/????): + libpurple: + Added: * 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 @@ -112,9 +122,6 @@ * purple_timeout_add_seconds Callers should prefer this to purple_timeout_add for timers longer than 1 second away. Be aware of the rounding, though. - * purple_timeout_add_seconds - Callers should prefer this to purple_timeout_add for timers - longer than 1 second away. Be aware of the rounding, though. * purple_xfer_get_remote_user * purple_pounces_get_all_for_ui * purple_prefs_get_children_names diff -r 3301b2f813f0 -r d604027959a5 configure.ac --- a/configure.ac Wed Oct 10 04:04:52 2007 +0000 +++ b/configure.ac Fri Oct 12 03:32:17 2007 +0000 @@ -142,13 +142,21 @@ dnl If we don't have msgfmt, then po/ is going to fail -- ensure that dnl AM_GLIB_GNU_GETTEXT found it. -if test x$MSGFMT = xno +if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT = x then AC_ERROR([ The msgfmt command is required to build libpurple. If it is installed on your system, ensure that it is in your path. If it is not, install GNU gettext to continue. + +If you have msgfmt installed, but for some reason this error message +is still displayed, you have encountered what appears to be a bug in +third-party configure macros. Try setting the MSGFMT environment +variable to the absolute path to your msgfmt binary and trying +configure again, like this: + +MSGFMT=/path/to/msgfmt ./configure ... ]) fi diff -r 3301b2f813f0 -r d604027959a5 doc/account-signals.dox --- a/doc/account-signals.dox Wed Oct 10 04:04:52 2007 +0000 +++ b/doc/account-signals.dox Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 doc/conversation-signals.dox --- a/doc/conversation-signals.dox Wed Oct 10 04:04:52 2007 +0000 +++ b/doc/conversation-signals.dox Fri Oct 12 03:32:17 2007 +0000 @@ -429,6 +429,7 @@ conversation. @param conv The conversation. @param list A pointer to the list of actions. + @since 2.1.0 @endsignaldef */ // vim: syntax=c.doxygen tw=75 et diff -r 3301b2f813f0 -r d604027959a5 doc/gtkconv-signals.dox --- a/doc/gtkconv-signals.dox Wed Oct 10 04:04:52 2007 +0000 +++ b/doc/gtkconv-signals.dox Fri Oct 12 03:32:17 2007 +0000 @@ -127,6 +127,7 @@ @signaldesc Emitted immediately before an existing conversation is hidden. @param gtkconv The PidginConversation + @since 2.2.0 @endsignaldef @signaldef conversation-displayed @@ -136,6 +137,7 @@ @signaldesc Emitted right after the Pidgin UI is attached to a new or a hidden conversation. @param gtkconv The PidginConversation + @since 2.2.0 @endsignaldef */ diff -r 3301b2f813f0 -r d604027959a5 doc/notify-signals.dox --- a/doc/notify-signals.dox Wed Oct 10 04:04:52 2007 +0000 +++ b/doc/notify-signals.dox Fri Oct 12 03:32:17 2007 +0000 @@ -35,6 +35,7 @@ @param from Who the email is from. @param to Who the email is to. @param url A url to view the email. + @since 2.1.0 @endsignaldef @signaldef displaying-emails-notification @@ -52,6 +53,7 @@ @param tos Who the emails are to. @param urls The urls to view the emails. @param count Number of emails being notified of. + @since 2.1.0 @endsignaldef */ diff -r 3301b2f813f0 -r d604027959a5 finch/gntaccount.h --- a/finch/gntaccount.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/gntaccount.h Fri Oct 12 03:32:17 2007 +0000 @@ -59,6 +59,8 @@ * Show the edit dialog for an account. * * @param account The account to edit, or @c NULL to create a new account. + * + * @since 2.2.0 */ void finch_account_dialog_show(PurpleAccount *account); diff -r 3301b2f813f0 -r d604027959a5 finch/gntblist.c --- a/finch/gntblist.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/gntblist.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/gntblist.h --- a/finch/gntblist.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/gntblist.h Fri Oct 12 03:32:17 2007 +0000 @@ -98,6 +98,8 @@ * @param name The user to get information about. * * @return Returns the ui-handle for the userinfo notification. + * + * @since 2.1.0 */ gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name); diff -r 3301b2f813f0 -r d604027959a5 finch/gntsound.h --- a/finch/gntsound.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/gntsound.h Fri Oct 12 03:32:17 2007 +0000 @@ -37,6 +37,8 @@ * Get the name of the active sound profile. * * @return The name of the profile + * + * @since 2.1.0 */ const char *finch_sound_get_active_profile(void); @@ -44,6 +46,8 @@ * Set the active profile. If the profile doesn't exist, nothing is changed. * * @param name The name of the profile + * + * @since 2.1.0 */ void finch_sound_set_active_profile(const char *name); @@ -52,6 +56,8 @@ * * @return A list of strings denoting sound profile names. * Caller must free the list (but not the data). + * + * @since 2.1.0 */ GList *finch_sound_get_profiles(void); @@ -60,6 +66,8 @@ * * @return Returns FALSE if preference is set to 'No sound', or if volume is * set to zero. + * + * @since 2.2.0 */ gboolean finch_sound_is_enabled(void); @@ -67,11 +75,15 @@ * Gets GNT sound UI ops. * * @return The UI operations structure. + * + * @since 2.1.0 */ PurpleSoundUiOps *finch_sound_get_ui_ops(void); /** * Show the sound settings dialog. + * + * @since 2.1.0 */ void finch_sounds_show_all(void); diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gnt.h --- a/finch/libgnt/gnt.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gnt.h Fri Oct 12 03:32:17 2007 +0000 @@ -62,6 +62,14 @@ */ gboolean gnt_ascii_only(void); +/** + * Present a window. If the event was triggered because of user interaction, + * the window is moved to the foreground. Otherwise, the Urgent hint is set. + * + * @param window The window the present. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_window_present(GntWidget *window); /** * diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gntmenu.c --- a/finch/libgnt/gntmenu.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntmenu.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntmenu.h --- a/finch/libgnt/gntmenu.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntmenu.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntmenuitem.c --- a/finch/libgnt/gntmenuitem.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntmenuitem.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntmenuitem.h --- a/finch/libgnt/gntmenuitem.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntmenuitem.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntslider.h --- a/finch/libgnt/gntslider.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntslider.h Fri Oct 12 03:32:17 2007 +0000 @@ -75,6 +75,8 @@ /** * @return The GType for GntSlider + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ GType gnt_slider_get_gtype(void); @@ -89,6 +91,8 @@ * @param min The minimum value for the slider * * @return The newly created slider + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ GntWidget * gnt_slider_new(gboolean orient, int max, int min); @@ -98,6 +102,8 @@ * @param slider The slider * @param max The maximum value * @param min The minimum value + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_slider_set_range(GntSlider *slider, int max, int min); @@ -106,6 +112,8 @@ * * @param slider The slider * @param step The amount for each step + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_slider_set_step(GntSlider *slider, int step); @@ -114,6 +122,8 @@ * * @param slider The slider * @param step The amount for a small step (for the slider) + * + * @since 2.2.0 */ void gnt_slider_set_small_step(GntSlider *slider, int step); @@ -122,6 +132,8 @@ * * @param slider The slider * @param step The amount for a large step (for the slider) + * + * @since 2.2.0 */ void gnt_slider_set_large_step(GntSlider *slider, int step); @@ -133,6 +145,8 @@ * forward, negative to change backward * * @return The value of the slider after the change + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ int gnt_slider_advance_step(GntSlider *slider, int steps); @@ -141,6 +155,8 @@ * * @param slider The slider * @param value The current value + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_slider_set_value(GntSlider *slider, int value); @@ -149,6 +165,8 @@ * * @param slider The slider * + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ int gnt_slider_get_value(GntSlider *slider); @@ -157,6 +175,8 @@ * * @param slider The slider * @param label The label to update + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_slider_reflect_label(GntSlider *slider, GntLabel *label); diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gntstyle.c --- a/finch/libgnt/gntstyle.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntstyle.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntstyle.h Fri Oct 12 03:32:17 2007 +0000 @@ -53,6 +53,8 @@ * @param key The key * * @return The value of the setting as a string, or @c NULL + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ char *gnt_style_get_from_name(const char *group, const char *key); @@ -62,6 +64,8 @@ * * @param value The value of the boolean setting as a string * @return The boolean value + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ gboolean gnt_style_parse_bool(const char *value); @@ -89,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 3301b2f813f0 -r d604027959a5 finch/libgnt/gnttextview.h --- a/finch/libgnt/gnttextview.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gnttextview.h Fri Oct 12 03:32:17 2007 +0000 @@ -204,6 +204,8 @@ * * @param view The textview widget * @param flag The flag to set + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag); diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gnttree.h Fri Oct 12 03:32:17 2007 +0000 @@ -383,6 +383,8 @@ * * @see gnt_tree_set_column_titles * @see gnt_tree_set_show_title + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_column_title(GntTree *tree, int index, const char *title); @@ -486,6 +488,8 @@ * * @see gnt_tree_set_col_width * @see gnt_tree_set_column_width_ratio + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res); @@ -505,6 +509,8 @@ * @param tree The tree * @param col The index of the column * @param right @c TRUE if the text in the column should be right aligned + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right); @@ -519,6 +525,8 @@ * * @see gnt_tree_set_col_width * @see gnt_tree_set_column_resizable + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]); @@ -527,6 +535,8 @@ * * @param tree The tree * @param col The index of the column + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_search_column(GntTree *tree, int col); @@ -535,6 +545,8 @@ * * @param tree The tree * @return @c TRUE if the user is searching, @c FALSE otherwise. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ gboolean gnt_tree_is_searching(GntTree *tree); @@ -547,6 +559,8 @@ * string and the content of row in the search column. * If the function returns @c TRUE, the row is dislayed, * otherwise it's not. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_tree_set_search_function(GntTree *tree, gboolean (*func)(GntTree *tree, gpointer key, const char *search, const char *current)); diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gntutils.h --- a/finch/libgnt/gntutils.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntutils.h Fri Oct 12 03:32:17 2007 +0000 @@ -139,6 +139,8 @@ * @param string The XHTML string * @param tv The GntTextView * @return @c TRUE if the string was added to the textview properly, @c FALSE otherwise. + * + * @since 2.2.0 */ gboolean gnt_util_parse_xhtml_to_textview(const char *string, GntTextView *tv); @@ -148,6 +150,8 @@ * @param widget The widget * @param key The key to trigger the button * @param button The button to trigger + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) */ void gnt_util_set_trigger_widget(GntWidget *wid, const char *text, GntWidget *button); diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/gntwindow.c --- a/finch/libgnt/gntwindow.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntwindow.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntwindow.h --- a/finch/libgnt/gntwindow.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntwindow.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntwm.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 finch/libgnt/gntws.h --- a/finch/libgnt/gntws.h Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/gntws.h Fri Oct 12 03:32:17 2007 +0000 @@ -69,18 +69,112 @@ G_BEGIN_DECLS +/** + * @return The GType for GntWS. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ GType gnt_ws_get_gtype(void); +/** + * Create a new workspace with the specified name. + * + * @param name The desired name of the workspace, or @c NULL. + * + * @return The newly created workspace. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ GntWS *gnt_ws_new(const char *name); + +/** + * Set the name of a workspace. + * + * @param ws The workspace to rename. + * @param name The new name of the workspace. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_set_name(GntWS *ws, const gchar *name); + +/** + * Add a widget to a workspace. + * + * @param ws The workspace. + * @param widget The widget to add. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_add_widget(GntWS *ws, GntWidget *widget); + +/** + * Remove a widget from a workspace. + * + * @param ws The workspace + * @param widget The widget to remove from the workspace. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_remove_widget(GntWS *ws, GntWidget *widget); + +/** + * Hide a widget in a workspace. + * + * @param widget The widget to hide. + * @param nodes A hashtable containing information about the widgets. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes); + +/** + * Show a widget in a workspace. + * + * @param widget The widget to show. + * @param nodes A hashtable containing information about the widgets. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes); + +/** + * Draw the taskbar in a workspace. + * + * @param ws The workspace. + * @param reposition Whether the workspace should reposition the taskbar. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_draw_taskbar(GntWS *ws, gboolean reposition); + +/** + * Hide a workspace. + * + * @param ws The workspace to hide. + * @param table A hashtable containing information about the widgets. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_hide(GntWS *ws, GHashTable *table); + +/** + * Show a workspace. + * + * @param ws The workspace to hide. + * @param table A hashtable containing information about the widgets. + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ void gnt_ws_show(GntWS *ws, GHashTable *table); +/** + * Get the name of a workspace. + * + * @param ws The workspace. + * @return The name of the workspace (can be @c NULL). + * + * @since 2.0.0 (gnt), 2.1.0 (pidgin) + */ const char * gnt_ws_get_name(GntWS *ws); #endif diff -r 3301b2f813f0 -r d604027959a5 finch/libgnt/test/Makefile --- a/finch/libgnt/test/Makefile Wed Oct 10 04:04:52 2007 +0000 +++ b/finch/libgnt/test/Makefile Fri Oct 12 03:32:17 2007 +0000 @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE +CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE -I/usr/inclue/ncursesw/ LDFLAGS=`pkg-config --libs gobject-2.0 gmodule-2.0 gnt` -pg EXAMPLES=combo focus tv multiwin keys menu parse diff -r 3301b2f813f0 -r d604027959a5 libpurple/Makefile.am --- a/libpurple/Makefile.am Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/Makefile.am Fri Oct 12 03:32:17 2007 +0000 @@ -114,6 +114,7 @@ privacy.h \ proxy.h \ prpl.h \ + purple.h \ request.h \ roomlist.h \ savedstatuses.h \ diff -r 3301b2f813f0 -r d604027959a5 libpurple/account.c --- a/libpurple/account.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/account.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/blist.h --- a/libpurple/blist.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/blist.h Fri Oct 12 03:32:17 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)) diff -r 3301b2f813f0 -r d604027959a5 libpurple/certificate.h --- a/libpurple/certificate.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/certificate.h Fri Oct 12 03:32:17 2007 +0000 @@ -2,6 +2,7 @@ * @file certificate.h Public-Key Certificate API * @ingroup core * @see @ref certificate-signals + * @since 2.2.0 */ /* diff -r 3301b2f813f0 -r d604027959a5 libpurple/conversation.c --- a/libpurple/conversation.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/conversation.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/conversation.h --- a/libpurple/conversation.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/conversation.h Fri Oct 12 03:32:17 2007 +0000 @@ -285,6 +285,8 @@ /** * Description of a conversation message + * + * @since 2.2.0 */ struct _PurpleConvMessage { @@ -292,6 +294,7 @@ char *what; PurpleMessageFlags flags; time_t when; + PurpleConversation *conv; }; /** @@ -670,6 +673,8 @@ * @return A GList of PurpleConvMessage's. The must not modify the list or the data within. * The list contains the newest message at the beginning, and the oldest message at * the end. + * + * @since 2.2.0 */ GList *purple_conversation_get_message_history(PurpleConversation *conv); @@ -677,6 +682,8 @@ * Clear the message history of a conversation. * * @param conv The conversation + * + * @since 2.2.0 */ void purple_conversation_clear_message_history(PurpleConversation *conv); @@ -686,6 +693,8 @@ * @param msg A PurpleConvMessage * * @return The name of the sender of the message + * + * @since 2.2.0 */ const char *purple_conversation_message_get_sender(PurpleConvMessage *msg); @@ -695,6 +704,8 @@ * @param msg A PurpleConvMessage * * @return The name of the sender of the message + * + * @since 2.2.0 */ const char *purple_conversation_message_get_message(PurpleConvMessage *msg); @@ -704,6 +715,8 @@ * @param msg A PurpleConvMessage * * @return The name of the sender of the message + * + * @since 2.2.0 */ PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg); @@ -713,6 +726,8 @@ * @param msg A PurpleConvMessage * * @return The name of the sender of the message + * + * @since 2.2.0 */ time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg); @@ -1318,6 +1333,8 @@ * @return A list of PurpleMenuAction items, harvested by the * chat-extended-menu signal. The list and the menuaction * items should be freed by the caller. + * + * @since 2.1.0 */ GList * purple_conversation_get_extended_menu(PurpleConversation *conv); @@ -1331,6 +1348,8 @@ * message, if not @c NULL. It must be freed by the caller with g_free(). * * @return @c TRUE if the command was executed successfully, @c FALSE otherwise. + * + * @since 2.1.0 */ gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error); diff -r 3301b2f813f0 -r d604027959a5 libpurple/eventloop.h --- a/libpurple/eventloop.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/eventloop.h Fri Oct 12 03:32:17 2007 +0000 @@ -138,6 +138,8 @@ * @param data data to pass to @a function. * @return A handle to the timer which can be passed to * purple_timeout_remove to remove the timer. + * + * @since 2.1.0 */ guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data); diff -r 3301b2f813f0 -r d604027959a5 libpurple/ft.h --- a/libpurple/ft.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/ft.h Fri Oct 12 03:32:17 2007 +0000 @@ -242,6 +242,8 @@ * @param xfer The file transfer. * * @return The name of the remote user. + * + * @since 2.1.0 */ const char *purple_xfer_get_remote_user(const PurpleXfer *xfer); diff -r 3301b2f813f0 -r d604027959a5 libpurple/pluginpref.h --- a/libpurple/pluginpref.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/pluginpref.h Fri Oct 12 03:32:17 2007 +0000 @@ -35,16 +35,16 @@ */ typedef enum { - PURPLE_STRING_FORMAT_TYPE_NONE = 0, - PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0, - PURPLE_STRING_FORMAT_TYPE_HTML = 1 << 1 + PURPLE_STRING_FORMAT_TYPE_NONE = 0, /**< The string is plain text. */ + PURPLE_STRING_FORMAT_TYPE_MULTILINE = 1 << 0, /**< The string can have newlines. */ + PURPLE_STRING_FORMAT_TYPE_HTML = 1 << 1 /**< The string can be in HTML. */ } PurpleStringFormatType; typedef enum { PURPLE_PLUGIN_PREF_NONE, PURPLE_PLUGIN_PREF_CHOICE, - PURPLE_PLUGIN_PREF_INFO, /**< no-value label */ - PURPLE_PLUGIN_PREF_STRING_FORMAT + PURPLE_PLUGIN_PREF_INFO, /**< no-value label */ + PURPLE_PLUGIN_PREF_STRING_FORMAT /**< The preference has a string value. */ } PurplePluginPrefType; #include diff -r 3301b2f813f0 -r d604027959a5 libpurple/plugins/signals-test.c --- a/libpurple/plugins/signals-test.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/plugins/signals-test.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/prefs.h --- a/libpurple/prefs.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/prefs.h Fri Oct 12 03:32:17 2007 +0000 @@ -291,6 +291,8 @@ * @return A list of newly allocated strings denoting the names of the children. * Returns @c NULL if there are no children or if pref doesn't exist. * The caller must free all the strings and the list. + * + * @since 2.1.0 */ GList *purple_prefs_get_children_names(const char *name); diff -r 3301b2f813f0 -r d604027959a5 libpurple/protocols/bonjour/Makefile.mingw --- a/libpurple/protocols/bonjour/Makefile.mingw Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.mingw Fri Oct 12 03:32:17 2007 +0000 @@ -36,7 +36,7 @@ -I$(PIDGIN_TREE_TOP) LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(BONJOUR_TOP)/lib \ + -L$(BONJOUR_TOP)/lib/win32 \ -L$(LIBXML2_TOP)/lib \ -L$(PURPLE_TOP) diff -r 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/google.c --- a/libpurple/protocols/jabber/google.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/google.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/google.h --- a/libpurple/protocols/jabber/google.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/google.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/message.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/jabber/usertune.c --- a/libpurple/protocols/jabber/usertune.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/jabber/usertune.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/state.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/state.h --- a/libpurple/protocols/msn/state.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/state.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/user.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/msn/user.h --- a/libpurple/protocols/msn/user.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/msn/user.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/protocols/qq/buddy_info.c --- a/libpurple/protocols/qq/buddy_info.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Fri Oct 12 03:32:17 2007 +0000 @@ -306,12 +306,10 @@ g_free(mid); } -static gchar *parse_field(GList **list, gboolean choice) +static gchar *parse_field(PurpleRequestField *field, gboolean choice) { gchar *value; - PurpleRequestField *field; - field = (PurpleRequestField *) (*list)->data; if (choice) { value = g_strdup_printf("%d", purple_request_field_choice_get_value(field)); } else { @@ -321,7 +319,6 @@ else value = utf8_to_qq(value, QQ_CHARSET_DEFAULT); } - *list = g_list_remove_link(*list, *list); return value; } @@ -331,7 +328,7 @@ { PurpleConnection *gc; qq_data *qd; - GList *list, *groups; + GList *groups; contact_info *info; gc = mid->gc; @@ -341,34 +338,76 @@ info = mid->info; groups = purple_request_fields_get_groups(fields); - list = purple_request_field_group_get_fields(groups->data); - info->uid = parse_field(&list, FALSE); - info->nick = parse_field(&list, FALSE); - info->name = parse_field(&list, FALSE); - info->age = parse_field(&list, FALSE); - info->gender = parse_field(&list, TRUE); - info->country = parse_field(&list, FALSE); - info->province = parse_field(&list, FALSE); - info->city = parse_field(&list, FALSE); - groups = g_list_remove_link(groups, groups); - list = purple_request_field_group_get_fields(groups->data); - info->horoscope = parse_field(&list, TRUE); - info->occupation = parse_field(&list, FALSE); - info->zodiac = parse_field(&list, TRUE); - info->blood = parse_field(&list, TRUE); - info->college = parse_field(&list, FALSE); - info->email = parse_field(&list, FALSE); - info->address = parse_field(&list, FALSE); - info->zipcode = parse_field(&list, FALSE); - info->hp_num = parse_field(&list, FALSE); - info->tel = parse_field(&list, FALSE); - info->homepage = parse_field(&list, FALSE); - groups = g_list_remove_link(groups, groups); - list = purple_request_field_group_get_fields(groups->data); - info->intro = parse_field(&list, FALSE); + while (groups != NULL) { + PurpleRequestFieldGroup *group = groups->data; + const char *g_name = purple_request_field_group_get_title(group); + GList *fields = purple_request_field_group_get_fields(group); + + if (g_name == NULL) + continue; + + while (fields != NULL) { + PurpleRequestField *field = fields->data; + const char *f_id = purple_request_field_get_id(field); + + if (!strcmp(QQ_PRIMARY_INFORMATION, g_name)) { + + if (!strcmp(f_id, "uid")) + info->uid = parse_field(field, FALSE); + else if (!strcmp(f_id, "nick")) + info->nick = parse_field(field, FALSE); + else if (!strcmp(f_id, "name")) + info->name = parse_field(field, FALSE); + else if (!strcmp(f_id, "age")) + info->age = parse_field(field, FALSE); + else if (!strcmp(f_id, "gender")) + info->gender = parse_field(field, TRUE); + else if (!strcmp(f_id, "country")) + info->country = parse_field(field, FALSE); + else if (!strcmp(f_id, "province")) + info->province = parse_field(field, FALSE); + else if (!strcmp(f_id, "city")) + info->city = parse_field(field, FALSE); + + } else if (!strcmp(QQ_ADDITIONAL_INFORMATION, g_name)) { - g_list_free(groups); + if (!strcmp(f_id, "horoscope")) + info->horoscope = parse_field(field, TRUE); + else if (!strcmp(f_id, "occupation")) + info->occupation = parse_field(field, FALSE); + else if (!strcmp(f_id, "zodiac")) + info->zodiac = parse_field(field, TRUE); + else if (!strcmp(f_id, "blood")) + info->blood = parse_field(field, TRUE); + else if (!strcmp(f_id, "college")) + info->college = parse_field(field, FALSE); + else if (!strcmp(f_id, "email")) + info->email = parse_field(field, FALSE); + else if (!strcmp(f_id, "address")) + info->address = parse_field(field, FALSE); + else if (!strcmp(f_id, "zipcode")) + info->zipcode = parse_field(field, FALSE); + else if (!strcmp(f_id, "hp_num")) + info->hp_num = parse_field(field, FALSE); + else if (!strcmp(f_id, "tel")) + info->tel = parse_field(field, FALSE); + else if (!strcmp(f_id, "homepage")) + info->homepage = parse_field(field, FALSE); + } else if (!strcmp(QQ_INTRO, g_name)) { + + if (!strcmp(f_id, "intro")) + info->intro = parse_field(field, FALSE); + + } + + fields = fields->next; + } + + groups = groups->next; + } + + /* This casting looks like a horrible idea to me -DAA */ qq_send_packet_modify_info(gc, (gchar **) info); g_strfreev((gchar **) mid->info); @@ -438,6 +477,7 @@ add_string_field_to_group(group, "country", QQ_COUNTRY, info->country); add_string_field_to_group(group, "province", QQ_PROVINCE, info->province); add_string_field_to_group(group, "city", QQ_CITY, info->city); + group = setup_field_group(fields, QQ_ADDITIONAL_INFORMATION); add_choice_field_to_group(group, "horoscope", QQ_HOROSCOPE, info->horoscope, horoscope_names, QQ_HOROSCOPE_SIZE); add_string_field_to_group(group, "occupation", QQ_OCCUPATION, info->occupation); diff -r 3301b2f813f0 -r d604027959a5 libpurple/purple.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/purple.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/request.c --- a/libpurple/request.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/request.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/request.h --- a/libpurple/request.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/request.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/signals.c --- a/libpurple/signals.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/signals.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/signals.h --- a/libpurple/signals.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/signals.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/sslconn.h --- a/libpurple/sslconn.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/sslconn.h Fri Oct 12 03:32:17 2007 +0000 @@ -206,17 +206,19 @@ #endif /** - * Makes a SSL connection using an already open file descriptor. - * - * @param account The account making the connection. - * @param fd The file descriptor. - * @param func The SSL input handler function. - * @param error_func The SSL error handler function. - * @param host The hostname of the other peer (to verify the CN) - * @param data User-defined data. - * - * @return The SSL connection handle. - */ + * Makes a SSL connection using an already open file descriptor. + * + * @param account The account making the connection. + * @param fd The file descriptor. + * @param func The SSL input handler function. + * @param error_func The SSL error handler function. + * @param host The hostname of the other peer (to verify the CN) + * @param data User-defined data. + * + * @return The SSL connection handle. + * + * @since 2.2.0 + */ PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd, PurpleSslInputFunction func, PurpleSslErrorFunction error_func, @@ -270,6 +272,8 @@ * * @return The peer certificate chain, in the order of certificate, issuer, * issuer's issuer, etc. @a NULL if no certificates have been provided, + * + * @since 2.2.0 */ GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc); diff -r 3301b2f813f0 -r d604027959a5 libpurple/status.c --- a/libpurple/status.c Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/status.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 libpurple/status.h --- a/libpurple/status.h Wed Oct 10 04:04:52 2007 +0000 +++ b/libpurple/status.h Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkblist.c Fri Oct 12 03:32:17 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); @@ -5300,7 +5300,7 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) { PurplePresence *presence; - GdkPixbuf *status, *avatar, *emblem; + GdkPixbuf *status, *avatar, *emblem, *prpl_icon; char *mark; char *idle = NULL; gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded; @@ -5310,7 +5310,7 @@ if (editing_blist) return; - + status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy, PIDGIN_STATUS_ICON_SMALL); @@ -5357,6 +5357,8 @@ } } + prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL); + gtk_tree_store_set(gtkblist->treemodel, iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, @@ -5367,7 +5369,7 @@ BUDDY_ICON_VISIBLE_COLUMN, biglist, EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, (emblem != NULL), - PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL), + PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), BGCOLOR_COLUMN, NULL, CONTACT_EXPANDER_COLUMN, NULL, @@ -5383,6 +5385,8 @@ g_object_unref(status); if(avatar) g_object_unref(avatar); + if(prpl_icon) + g_object_unref(prpl_icon); } /* This is a variation on the original gtk_blist_update_contact. Here we @@ -5503,9 +5507,7 @@ if(purple_account_is_connected(chat->account)) { GtkTreeIter iter; - GdkPixbuf *status; - GdkPixbuf *avatar; - GdkPixbuf *emblem; + GdkPixbuf *status, *avatar, *emblem, *prpl_icon; char *mark; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); PidginBlistNode *ui; @@ -5536,6 +5538,8 @@ mark = bold; } + prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL); + gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, @@ -5543,7 +5547,7 @@ BUDDY_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"), EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, emblem != NULL, - PROTOCOL_ICON_COLUMN, pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL), + PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), NAME_COLUMN, mark, GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -5556,6 +5560,8 @@ g_object_unref(status); if(avatar) g_object_unref(avatar); + if(prpl_icon) + g_object_unref(prpl_icon); } else { pidgin_blist_hide_node(list, node, TRUE); } diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkblist.h --- a/pidgin/gtkblist.h Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkblist.h Fri Oct 12 03:32:17 2007 +0000 @@ -371,6 +371,8 @@ * @param selected Whether this buddy is selected. If TRUE, the markup will not change the color. * @param aliased TRUE to return the appropriate alias of this buddy, FALSE to return its screenname and status information * @return The markup for this buddy + * + * @since 2.1.0 */ gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased); @@ -382,11 +384,15 @@ * * @param node The buddy list node to show a tooltip for * @param widget The widget to draw the tooltip on + * + * @since 2.1.0 */ void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget); /** * Destroys the current (if any) Buddy List tooltip + * + * @since 2.1.0 */ void pidgin_blist_tooltip_destroy(void); diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkconv.c Fri Oct 12 03:32:17 2007 +0000 @@ -7397,6 +7397,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) { @@ -7404,49 +7415,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(), @@ -7458,9 +7526,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; } @@ -9791,3 +9856,4 @@ return colors; } + diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkconv.h --- a/pidgin/gtkconv.h Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkconv.h Fri Oct 12 03:32:17 2007 +0000 @@ -253,6 +253,8 @@ * @param conv The conversation. * * @return Wheter Pidgin UI was successfully attached. + * + * @since 2.2.0 */ gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv); diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkdialogs.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 pidgin/gtkdocklet.c --- a/pidgin/gtkdocklet.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkdocklet.c Fri Oct 12 03:32:17 2007 +0000 @@ -473,6 +473,87 @@ return menuitem; } + +static void +plugin_act(GtkObject *obj, PurplePluginAction *pam) +{ + if (pam && pam->callback) + pam->callback(pam); +} + +static void +build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin, + gpointer context) +{ + GtkWidget *menuitem; + PurplePluginAction *action = NULL; + GList *actions, *l; + + actions = PURPLE_PLUGIN_ACTIONS(plugin, context); + + for (l = actions; l != NULL; l = l->next) + { + if (l->data) + { + action = (PurplePluginAction *) l->data; + action->plugin = plugin; + action->context = context; + + menuitem = gtk_menu_item_new_with_label(action->label); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(plugin_act), action); + g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", + action, + (GDestroyNotify)purple_plugin_action_free); + gtk_widget_show(menuitem); + } + else + pidgin_separator(menu); + } + + g_list_free(actions); +} + + +static void +docklet_plugin_actions(GtkWidget *menu) +{ + GtkWidget *menuitem, *submenu; + PurplePlugin *plugin = NULL; + GList *l; + int c = 0; + + g_return_if_fail(menu != NULL); + + /* Add a submenu for each plugin with custom actions */ + for (l = purple_plugins_get_loaded(); l; l = l->next) { + plugin = (PurplePlugin *) l->data; + + if (PURPLE_IS_PROTOCOL_PLUGIN(plugin)) + continue; + + if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin)) + continue; + + menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + gtk_widget_show(submenu); + + build_plugin_actions(submenu, plugin, NULL); + + c++; + } + if(c>0) + pidgin_separator(menu); +} + + static void docklet_menu() { static GtkWidget *menu = NULL; @@ -539,6 +620,9 @@ pidgin_separator(menu); + /* add plugin actions */ + docklet_plugin_actions(menu); + pidgin_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(purple_core_quit), NULL, 0, 0, NULL); #ifdef _WIN32 diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkimhtml.h Fri Oct 12 03:32:17 2007 +0000 @@ -441,6 +441,8 @@ * @param id The id to associate with the image. * * @return A new IM/HTML Scalable object with an image. + * + * @since 2.1.0 */ /* * TODO: All this animation code could be combined much better with @@ -837,6 +839,8 @@ * * @param imhtml The GTK+ IM/HTML. * @param flags The connection flag which describes the allowed types of formatting. + * + * @since 2.1.0 */ void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags); diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtklog.c --- a/pidgin/gtklog.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtklog.c Fri Oct 12 03:32:17 2007 +0000 @@ -680,6 +680,7 @@ PidginLogViewer *lv = NULL; const char *name = screenname; char *title; + GdkPixbuf *prpl_icon; g_return_if_fail(account != NULL); g_return_if_fail(screenname != NULL); @@ -717,9 +718,14 @@ title = g_strdup_printf(_("Conversations with %s"), name); } + prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); + display_log_viewer(ht, purple_log_get_logs(type, screenname, account), - title, gtk_image_new_from_pixbuf(pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM)), + title, gtk_image_new_from_pixbuf(prpl_icon), purple_log_get_total_size(type, screenname, account)); + + if (prpl_icon) + g_object_unref(prpl_icon); g_free(title); } diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkrequest.c Fri Oct 12 03:32:17 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 3301b2f813f0 -r d604027959a5 pidgin/gtksavedstatuses.c --- a/pidgin/gtksavedstatuses.c Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtksavedstatuses.c Fri Oct 12 03:32:17 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); diff -r 3301b2f813f0 -r d604027959a5 pidgin/gtkutils.h --- a/pidgin/gtkutils.h Wed Oct 10 04:04:52 2007 +0000 +++ b/pidgin/gtkutils.h Fri Oct 12 03:32:17 2007 +0000 @@ -376,6 +376,8 @@ * * @param conn The connection to get information from. * @param name The user to get information about. + * + * @since 2.1.0 */ void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name); @@ -385,6 +387,8 @@ * @param conn The connection to get information from. * @param name The user to get information about. * @param chatid The chat id. + * + * @since 2.1.0 */ void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chatid); @@ -423,6 +427,8 @@ * * @param w The widget that we want to label. * @param l A GtkLabel that we want to use as the label for the widget. + * + * @since 2.2.0 */ void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l); @@ -437,6 +443,8 @@ * where the menu shall be drawn. This is an output parameter. * @param push_in This is an output parameter? * @param data Not used by this particular position function. + * + * @since 2.1.0 */ void pidgin_menu_position_func_helper(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data); @@ -671,6 +679,8 @@ * * @return A newly created text GtkComboBox containing a GtkEntry * child. + * + * @since 2.2.0 */ GtkWidget *pidgin_text_combo_box_entry_new(const char *default_item, GList *items); @@ -680,6 +690,8 @@ * @param widget The simple text GtkComboBoxEntry equivalent widget * * @return The text in the widget's entry. It must not be freed + * + * @since 2.2.0 */ const char *pidgin_text_combo_box_entry_get_text(GtkWidget *widget); @@ -688,6 +700,8 @@ * * @param widget The simple text GtkComboBoxEntry equivalent widget * @param text The text to set + * + * @since 2.2.0 */ void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);