# HG changeset patch # User Yoshiki Yazawa # Date 1199677227 0 # Node ID 9a60b9fab0ea74021d11dd17e9a2ce29cf7adecf # Parent 754a82f1371b34eee12ef8042bc963eca6e708d1# Parent 6f452e8d13a2038ad609ad2a5b313861ea2bc41e propagate from branch 'im.pidgin.pidgin' (head d50f362e369f486aaf9a95a0d79ec0632743f07d) to branch 'im.pidgin.pidgin.yaz' (head 85b37cd20a00067af5a2da2760336b0c1016ef47) diff -r 754a82f1371b -r 9a60b9fab0ea AUTHORS --- a/AUTHORS Mon Dec 17 08:20:42 2007 +0000 +++ b/AUTHORS Mon Jan 07 03:40:27 2008 +0000 @@ -35,6 +35,7 @@ Megan 'Cae' Schneider - support/QA Evan Schoenberg - Developer Kevin 'SimGuy' Stange - Developer & Webmaster +Will 'resiak' Thompson - Developer Stu 'nosnilmot' Tomlinson - Developer Nathan 'faceprint' Walp - Developer @@ -42,8 +43,8 @@ ------------------- Dennis 'EvilDennisR' Ristuccia Peter 'Fmoo' Ruibal +Elliott 'QuLogic' Sales de Andrade Gabriel 'Nix' Schulhof -Will 'resiak' Thompson Retired Developers: ------------------ diff -r 754a82f1371b -r 9a60b9fab0ea COPYRIGHT --- a/COPYRIGHT Mon Dec 17 08:20:42 2007 +0000 +++ b/COPYRIGHT Mon Jan 07 03:40:27 2008 +0000 @@ -151,6 +151,7 @@ Michael Golden Charlie Gordon Ryan C. Gordon +Konrad Gräfe Miah Gregory David Grohmann Christian Hammond @@ -167,6 +168,7 @@ Nick Hebner Mike Heffner Justin Heiner +Moos Heintzen Benjamin Herrenschmidt Fernando Herrera hjheins @@ -298,6 +300,7 @@ Jory A. Pratt Brent Priddy Justin Pryzby +Ignacio Casal Quinteiro Federicco Mena Quintero Yosef Radchenko David Raeman @@ -327,6 +330,7 @@ Andrew Sayman Alceste Scalas Carsten Schaar +Jonathan Schleifer Matteo Settenvini Colin Seymour Luke Schierer @@ -413,6 +417,7 @@ Zsombor Welker Andrew Wellington Adam Wendt +Simon Wenner Dave West Zac West Daniel Westermann-Clark diff -r 754a82f1371b -r 9a60b9fab0ea ChangeLog --- a/ChangeLog Mon Dec 17 08:20:42 2007 +0000 +++ b/ChangeLog Mon Jan 07 03:40:27 2008 +0000 @@ -1,12 +1,23 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.3.2 (??/??/????): +version 2.4.0 (??/??/????): libpurple: * Fixed various problems with loss of status messages when going or returning from idle on MySpaceIM. * Eliminated unmaintained Howl backend implementation for the Bonjour protocol. Avahi (or Apple's Bonjour runtime on win32) is now required to use Bonjour. + * Partial support for viewing ICQ status notes (Collin from + ComBOTS GmbH). + + Pidgin: + * Added the ability to theme conversation name colors (red and blue) + through your GTK+ theme, and exposed those theme settings to the + Pidgin GTK+ Theme Control plugin (Dustin Howett) + * Fixed having multiple alias edit areas in the infopane (Elliott Sales + de Andrade) + * Save the conversation "Enable Logging" option per-contact (Moos + Heintzen) Finch: * Color is used in the buddylist to indicate status, and the conversation @@ -15,6 +26,32 @@ * The default keybinding for dump-screen is now M-D and uses a file request dialog. M-d will properly delete-forward-word, and M-f has been fixed to imitate readline's behavior. + * New bindings alt+tab and alt+shift+tab to help navigating between the + higlighted windows (details on the man-page). + * Recently signed on (or off) buddies blink in the buddy list. + * New action 'Room List' in the action-list can be used to get the list of + available chat rooms for an online account. + +version 2.3.1 (12/7/2007): + http://developer.pidgin.im/query?status=closed&milestone=2.3.1 + NOTE: Due to the way this release was made, it is possible that + bugs marked as fixed in 2.3.1 will not be fixed until the + next release. + + * Fixed a number of MSN bugs introduced in 2.3.0, resolving problems + connecting to MSN and random local display name changes + * Going idle on MySpaceIM will no longer clear your status and message. + * Idle MySpaceIM buddies should now appear online at login. + * Fixed crashes in XMPP when discovering a client's capabilities + * Don't set the current tune title if it's NULL (XMPP/Google Talk) + * Don't allow buddies to be manually added to Bonjour + * Don't advertise IPv6 on Bonjour because we don't support it + * Compile fixes for FreeBSD and Solaris + * Update QQ client version so some accounts can connect again + * Do not allow ISON requests to stack in IRC, preventing flooding IRC + servers when temporary network outages are restored + * Plug several leaks in the perl plugin loader + * Prevent autoaccept plugin overwriting existing files version 2.3.0 (11/24/2007): http://developer.pidgin.im/query?status=closed&milestone=2.3.0 diff -r 754a82f1371b -r 9a60b9fab0ea ChangeLog.API --- a/ChangeLog.API Mon Dec 17 08:20:42 2007 +0000 +++ b/ChangeLog.API Mon Jan 07 03:40:27 2008 +0000 @@ -1,7 +1,50 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.3.2 (??/??/????): +version 2.4.0 (??/??/????): + libpurple: + Added: + * purple_certificate_add_ca_search_path. (Florian Quèze) + * purple_gai_strerror. + * purple_major_version, purple_minor_version, + purple_micro_version variables are exported by version.h, + giving the version of libpurple in use at runtime. + * purple_util_set_current_song, purple_util_format_song_info + * Some accessor functions to the Roomlist API: + * purple_roomlist_get_fields + * purple_roomlist_room_get_type + * purple_roomlist_room_get_name + * purple_roomlist_room_get_parent + * purple_roomlist_room_get_fields + * purple_roomlist_field_get_type + * purple_roomlist_field_get_label + * purple_roomlist_field_get_hidden + + Pidgin: + Added: + * pidgin_create_dialog to create a window that closes on escape. Also + added utility functions pidgin_dialog_get_vbox_with_properties, + pidgin_dialog_get_vbox, pidgin_dialog_get_action_area to access the + contents in the created dialog. (Peter 'fmoo' Ruibal) + * pidgin_dialog_add_button to add buttons to a dialog created by + pidgin_create_dialog. + * GTK_IMHTML_NO_SMILEY for GtkIMHtmlOptions means not to look for + smileys in the text. (Florian 'goutnet' Delizy) + * pidgin_auto_parent_window to make a window transient for a suitable + parent window. + * pidgin_tooltip_setup_for_treeview, pidgin_tooltip_destroy, + pidgin_tooltip_show and pidgin_tooltip_setup_for_widget to simplify + the process of drawing tooltips. + * pidgin_add_widget_to_vbox to simplify adding a labeled widget to a + window. + + Deprecated: + * PIDGIN_DIALOG + Finch: + * finch_roomlist_get_ui_ops and finch_roomlist_show_all + * finch_request_field_get_widget to get the widget for a request + field. + libgnt: * Added gnt_tree_set_row_color to set the color for a row in a tree. * Added gnt_style_get_string_list diff -r 754a82f1371b -r 9a60b9fab0ea ChangeLog.win32 --- a/ChangeLog.win32 Mon Dec 17 08:20:42 2007 +0000 +++ b/ChangeLog.win32 Mon Jan 07 03:40:27 2008 +0000 @@ -1,3 +1,6 @@ +version 2.3.1 (12/7/2007): + * No changes + version 2.3.0 (11/24/2007): * Updated GTK+ to 2.12.1 (This was actually included in 2.2.2, but didn't get into the Changelog.) @@ -135,7 +138,7 @@ version 0.82 (08/26/2004): * Selecting away messages using the system tray icon works - (Thanks Fran?ois Gagn?) + (Thanks François Gagné) * Transparency plugin will save your settings again (Kevin Stange) * Updated gtk-wimp to 0.6.2 * Updated libpng to 1.2.6 (major security update) diff -r 754a82f1371b -r 9a60b9fab0ea Makefile.am --- a/Makefile.am Mon Dec 17 08:20:42 2007 +0000 +++ b/Makefile.am Mon Jan 07 03:40:27 2008 +0000 @@ -30,10 +30,12 @@ distcheck-hook: libpurple/plugins/perl/common/Purple.pm pidgin/plugins/perl/common/Pidgin.pm # cp libpurple/plugins/perl/common/Gaim.pm $(distdir)/libpurple/plugins/perl/common +if ENABLE_GTK appsdir = $(datadir)/applications apps_in_files = pidgin.desktop.in apps_DATA = $(apps_in_files:.desktop.in=.desktop) @INTLTOOL_DESKTOP_RULE@ +endif if ENABLE_GTK GTK_DIR=pidgin diff -r 754a82f1371b -r 9a60b9fab0ea NEWS --- a/NEWS Mon Dec 17 08:20:42 2007 +0000 +++ b/NEWS Mon Jan 07 03:40:27 2008 +0000 @@ -1,5 +1,19 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +2.3.1 (12/7/2007): + Stu: I'm sorry for the MSN problems and the plugin crashes in 2.3.0. + Hopefully this will redeem us. This fixes a number of bugs. I'm a + bit late but I'd like to welcome John to the team. Enjoy! + + Luke: I've done absolutely nothing in the last 2 weeks, except watch + others commit bug and, more, leak fixes. People should be noticing + remarkably fewer memory leaks now than 2 or more releases ago. + + Kevin: I'm not quite sure what happened to our MySpaceIM Summer of + Code student, but I fixed a few MySpace bugs with idle and status. + I will try to fix some of the other more significant bugs, after I + figure out the protocol, especially including grouping issues. + 2.3.0 (11/20/2007): Luke: While this does not have the new MSN code, rest assured that we are working on it and that it is nearing release. This contains diff -r 754a82f1371b -r 9a60b9fab0ea autogen.sh diff -r 754a82f1371b -r 9a60b9fab0ea config.h.mingw --- a/config.h.mingw Mon Dec 17 08:20:42 2007 +0000 +++ b/config.h.mingw Mon Jan 07 03:40:27 2008 +0000 @@ -355,7 +355,7 @@ /* Loads static protocol plugin module initialization functions. */ #ifndef STATIC_PROTO_INIT -#define STATIC_PROTO_INIT static void static_proto_init() { } +#define STATIC_PROTO_INIT static void static_proto_init(void) { } #endif /* Define to 1 if you have the ANSI C header files. */ diff -r 754a82f1371b -r 9a60b9fab0ea configure.ac --- a/configure.ac Mon Dec 17 08:20:42 2007 +0000 +++ b/configure.ac Mon Jan 07 03:40:27 2008 +0000 @@ -43,19 +43,19 @@ # # Make sure to update finch/libgnt/configure.ac with libgnt version changes. # -m4_define([purple_lt_current], [3]) +m4_define([purple_lt_current], [4]) m4_define([purple_major_version], [2]) -m4_define([purple_minor_version], [3]) -m4_define([purple_micro_version], [2]) +m4_define([purple_minor_version], [4]) +m4_define([purple_micro_version], [0]) m4_define([purple_version_suffix], [devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix])) -m4_define([gnt_lt_current], [3]) +m4_define([gnt_lt_current], [4]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [3]) -m4_define([gnt_micro_version], [2]) +m4_define([gnt_minor_version], [4]) +m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) @@ -142,7 +142,7 @@ 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 -o x$MSGFMT$GMSGFMT = x +if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x then AC_ERROR([ @@ -678,7 +678,6 @@ PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [ have_meanwhile="yes" ], [ - AC_MSG_RESULT(no) have_meanwhile="no" ]) AC_SUBST(MEANWHILE_CFLAGS) @@ -697,7 +696,6 @@ avahiincludes="yes" avahilibs="yes" ], [ - AC_MSG_RESULT(no) avahiincludes="no" avahilibs="no" ]) @@ -742,7 +740,6 @@ silcincludes="yes" silcclient="yes" ], [ - AC_MSG_RESULT(no) have_silc="no" ]) if test "x$have_silc" = "xno"; then @@ -751,7 +748,6 @@ silc10includes="yes" silc10client="yes" ], [ - AC_MSG_RESULT(no) have_silc="no" ]) dnl If silcclient.pc wasn't found, check for just silc.pc @@ -761,7 +757,6 @@ silc10includes="yes" silc10client="yes" ], [ - AC_MSG_RESULT(no) have_silc="no" ]) fi @@ -982,7 +977,7 @@ AM_CONDITIONAL(STATIC_YAHOO, test "x$static_yahoo" = "xyes") AM_CONDITIONAL(STATIC_ZEPHYR, test "x$static_zephyr" = "xyes") AC_SUBST(STATIC_LINK_LIBS) -AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init() { $load_proto }, +AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto }, [Loads static protocol plugin module initialization functions.]) AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) @@ -1153,7 +1148,6 @@ AC_SUBST(DBUS_LIBS) enable_dbus=yes ], [ - AC_MSG_RESULT(no) enable_dbus=no ]) diff -r 754a82f1371b -r 9a60b9fab0ea doc/finch.1.in --- a/doc/finch.1.in Mon Dec 17 08:20:42 2007 +0000 +++ b/doc/finch.1.in Mon Jan 07 03:40:27 2008 +0000 @@ -105,6 +105,12 @@ .B Alt \+ 1 2 ... 0 Jump to the 1st, 2nd ... 10th window. .TP +.B Alt \+ Tab +Jump to the next URGENT (highlighted) window. +.TP +.B Alt \+ Shift \+ Tab +Jump to the previous URGENT (highlighted) window. +.TP .B Ctrl \+ o Bring up the menu (if there is one) for a window. .TP @@ -460,6 +466,7 @@ .br # switch-window-n .br +# Other actions: window-next-urgent, window-prev-urgent # For the sample custom window manager .br diff -r 754a82f1371b -r 9a60b9fab0ea doc/pidgin.1.in --- a/doc/pidgin.1.in Mon Dec 17 08:20:42 2007 +0000 +++ b/doc/pidgin.1.in Mon Jan 07 03:40:27 2008 +0000 @@ -590,6 +590,8 @@ .br Kevin 'SimGuy' Stange (developer and webmaster) .br + Will 'resiak' Thompson (developer) +.br Stu 'nosnilmot' Tomlinson (developer) .br Nathan 'faceprint' Walp (developer) @@ -602,9 +604,9 @@ .br Peter 'fmoo' Ruibal .br - Gabriel 'Nix' Schulhof + Elliott 'QuLogic' Sales de Andrade .br - Will 'resiak' Thompson + Gabriel 'Nix' Schulhof .br diff -r 754a82f1371b -r 9a60b9fab0ea finch/Makefile.am --- a/finch/Makefile.am Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/Makefile.am Mon Jan 07 03:40:27 2008 +0000 @@ -30,6 +30,7 @@ gntpounce.c \ gntprefs.c \ gntrequest.c \ + gntroomlist.c \ gntsound.c \ gntstatus.c \ gntui.c @@ -49,6 +50,7 @@ gntpounce.h \ gntprefs.h \ gntrequest.h \ + gntroomlist.h \ gntsound.h \ gntstatus.h \ gntui.h diff -r 754a82f1371b -r 9a60b9fab0ea finch/finch.c --- a/finch/finch.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/finch.c Mon Jan 07 03:40:27 2008 +0000 @@ -49,14 +49,14 @@ #include "config.h" static void -debug_init() +debug_init(void) { finch_debug_init(); purple_debug_set_ui_ops(finch_debug_get_ui_ops()); } static GHashTable *ui_info = NULL; -static GHashTable *finch_ui_get_info() +static GHashTable *finch_ui_get_info(void) { if (ui_info == NULL) { ui_info = g_hash_table_new(g_str_hash, g_str_equal); @@ -91,7 +91,7 @@ }; static PurpleCoreUiOps * -gnt_core_get_ui_ops() +gnt_core_get_ui_ops(void) { return &core_ops; } diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntaccount.c --- a/finch/gntaccount.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntaccount.c Mon Jan 07 03:40:27 2008 +0000 @@ -722,7 +722,7 @@ } static gpointer -finch_accounts_get_handle() +finch_accounts_get_handle(void) { static int handle; diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntblist.c --- a/finch/gntblist.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntblist.c Mon Jan 07 03:40:27 2008 +0000 @@ -83,6 +83,12 @@ GntMenuItem *plugins; } FinchBlist; +typedef struct +{ + gpointer row; /* the row in the GntTree */ + guint signed_timer; /* used when 'recently' signed on/off */ +} FinchBlistNode; + typedef enum { STATUS_PRIMITIVE = 0, @@ -131,6 +137,31 @@ static int color_offline; static int color_idle; +static FinchBlistNode * +create_finch_blist_node(PurpleBlistNode *node, gpointer row) +{ + FinchBlistNode *fnode = node->ui_data; + if (!fnode) { + fnode = g_new0(FinchBlistNode, 1); + fnode->signed_timer = 0; + node->ui_data = fnode; + } + fnode->row = row; + return fnode; +} + +static void +reset_blist_node_ui_data(PurpleBlistNode *node) +{ + FinchBlistNode *fnode = node->ui_data; + if (fnode == NULL) + return; + if (fnode->signed_timer) + purple_timeout_remove(fnode->signed_timer); + g_free(fnode); + node->ui_data = NULL; +} + static int get_display_color(PurpleBlistNode *node) { @@ -157,6 +188,34 @@ return color; } +static GntTextFormatFlags +get_blist_node_flag(PurpleBlistNode *node) +{ + GntTextFormatFlags flag = 0; + FinchBlistNode *fnode = node->ui_data; + + if (ggblist->tagged && g_list_find(ggblist->tagged, node)) + flag |= GNT_TEXT_FLAG_BOLD; + + if (fnode && fnode->signed_timer) + flag |= GNT_TEXT_FLAG_BLINK; + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact *)node); + fnode = node->ui_data; + if (fnode && fnode->signed_timer) + flag |= GNT_TEXT_FLAG_BLINK; + } + + return flag; +} + +static void +blist_update_row_flags(PurpleBlistNode *node) +{ + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, get_blist_node_flag(node)); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node)); +} + static gboolean is_contact_online(PurpleContact *contact) { @@ -217,7 +276,7 @@ return; gnt_tree_remove(GNT_TREE(ggblist->tree), node); - node->ui_data = NULL; + reset_blist_node_ui_data(node); if (ggblist->tagged) ggblist->tagged = g_list_remove(ggblist->tagged, node); @@ -234,7 +293,7 @@ (!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group))) node_remove(list, node->parent); for (node = node->child; node; node = node->next) - node->ui_data = NULL; + reset_blist_node_ui_data(node); } else { for (node = node->child; node; node = node->next) node_remove(list, node); @@ -261,7 +320,7 @@ gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0, get_display_name(node)); gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node)); + blist_update_row_flags(node); } if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { @@ -269,8 +328,6 @@ if (purple_account_is_connected(buddy->account) && (PURPLE_BUDDY_IS_ONLINE(buddy) || purple_prefs_get_bool(PREF_ROOT "/showoffline"))) add_node((PurpleBlistNode*)buddy, list->ui_data); - else - node_remove(purple_get_blist(), node); node_update(list, node->parent); } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { @@ -279,7 +336,7 @@ PurpleContact *contact = (PurpleContact*)node; if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || contact->currentsize < 1) - node_remove(purple_get_blist(), node); + /* nothing */; else { if (node->ui_data == NULL) { /* The core seems to expect the UI to add the buddies. */ @@ -483,7 +540,7 @@ } static void -finch_request_add_group() +finch_request_add_group(void) { purple_request_input(NULL, _("Add Group"), NULL, _("Enter the name of the group"), NULL, FALSE, FALSE, NULL, @@ -511,7 +568,7 @@ }; static gpointer -finch_blist_get_handle() +finch_blist_get_handle(void) { static int handle; @@ -524,8 +581,8 @@ PurpleBlistNode *node = (PurpleBlistNode *)group; if (node->ui_data) return; - node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group, - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL); + create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group, + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node, !purple_blist_node_get_bool(node, "collapsed")); } @@ -539,7 +596,7 @@ if (PURPLE_BLIST_NODE_IS_CONTACT(node)) node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node); /* XXX: this can return NULL?! */ - + if (node == NULL) return NULL; @@ -550,7 +607,7 @@ PurplePresence *presence; PurpleStatus *now; gboolean ascii = gnt_ascii_only(); - + presence = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1); @@ -601,9 +658,9 @@ group = purple_chat_get_group(chat); add_node((PurpleBlistNode*)group, ggblist); - node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat, + create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - group, NULL); + group, NULL)); } static void @@ -623,9 +680,9 @@ group = (PurpleGroup*)node->parent; add_node((PurpleBlistNode*)group, ggblist); - node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact, + create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact, gnt_tree_create_row(GNT_TREE(ggblist->tree), name), - group, NULL); + group, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE); } @@ -635,7 +692,7 @@ { PurpleContact *contact; PurpleBlistNode *node = (PurpleBlistNode *)buddy; - int color = 0; + if (node->ui_data) return; @@ -647,14 +704,13 @@ return; add_node((PurpleBlistNode*)contact, ggblist); - node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, + create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - contact, NULL); - - color = get_display_color((PurpleBlistNode*)buddy); - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); + contact, NULL)); + + blist_update_row_flags((PurpleBlistNode*)buddy); if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); + blist_update_row_flags((PurpleBlistNode*)contact); } #if 0 @@ -1564,9 +1620,7 @@ static void update_node_display(PurpleBlistNode *node, FinchBlist *ggblist) { - GntTextFormatFlags flag = 0; - if (ggblist->tagged && g_list_find(ggblist->tagged, node)) - flag |= GNT_TEXT_FLAG_BOLD; + GntTextFormatFlags flag = get_blist_node_flag(node); gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag); } @@ -1574,33 +1628,18 @@ update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist) { PurpleContact *contact; - GntTextFormatFlags bflag = 0, cflag = 0; - int color = 0; contact = purple_buddy_get_contact(buddy); gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy)); gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact)); - if (ggblist->tagged && g_list_find(ggblist->tagged, buddy)) - bflag |= GNT_TEXT_FLAG_BOLD; - if (ggblist->tagged && g_list_find(ggblist->tagged, contact)) - cflag |= GNT_TEXT_FLAG_BOLD; + blist_update_row_flags((PurpleBlistNode *)buddy); + if (buddy == purple_contact_get_priority_buddy(contact)) + blist_update_row_flags((PurpleBlistNode *)contact); if (ggblist->tnode == (PurpleBlistNode*)buddy) draw_tooltip(ggblist); - - color = get_display_color((PurpleBlistNode*)buddy); - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); - - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); - - if (buddy != purple_contact_get_priority_buddy(contact)) - update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist); } static void @@ -1649,7 +1688,7 @@ node = purple_blist_get_root(); while (node) { - node->ui_data = NULL; + reset_blist_node_ui_data(node); node = purple_blist_node_next(node, TRUE); } @@ -1663,7 +1702,7 @@ } static void -populate_buddylist() +populate_buddylist(void) { PurpleBlistNode *node; PurpleBuddyList *list; @@ -1696,7 +1735,7 @@ } static void -populate_status_dropdown() +populate_status_dropdown(void) { int i; GList *iter; @@ -1762,7 +1801,7 @@ gnt_tree_remove_all(GNT_TREE(ggblist->tree)); node = purple_blist_get_root(); for (; node; node = purple_blist_node_next(node, TRUE)) - node->ui_data = NULL; + reset_blist_node_ui_data(node); populate_buddylist(); gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel); draw_tooltip(ggblist); @@ -2122,8 +2161,53 @@ } } +static gboolean +buddy_recent_signed_on_off(gpointer data) +{ + PurpleBlistNode *node = data; + FinchBlistNode *fnode = node->ui_data; + PurpleBuddy *buddy = (PurpleBuddy*)node; + + purple_timeout_remove(fnode->signed_timer); + fnode->signed_timer = 0; + + if (!purple_account_is_connected(buddy->account) || + (!PURPLE_BUDDY_IS_ONLINE(buddy) && !purple_prefs_get_bool(PREF_ROOT "/showoffline"))) { + node_remove(purple_get_blist(), node); + } else { + update_node_display(node, ggblist); + if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) + update_node_display(node->parent, ggblist); + } + + return FALSE; +} + +static gboolean +buddy_signed_on_off_cb(gpointer data) +{ + PurpleBlistNode *node = data; + FinchBlistNode *fnode = node->ui_data; + if (!ggblist || !fnode) + return FALSE; + + if (fnode->signed_timer) + purple_timeout_remove(fnode->signed_timer); + fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data); + update_node_display(node, ggblist); + if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) + update_node_display(node->parent, ggblist); + return FALSE; +} + static void -reconstruct_plugins_menu() +buddy_signed_on_off(PurpleBuddy* buddy, gpointer null) +{ + g_idle_add(buddy_signed_on_off_cb, buddy); +} + +static void +reconstruct_plugins_menu(void) { GntWidget *sub; GntMenuItem *plg; @@ -2155,7 +2239,7 @@ } static void -reconstruct_accounts_menu() +reconstruct_accounts_menu(void) { GntWidget *sub; GntMenuItem *acc, *item; @@ -2355,7 +2439,7 @@ } static void -create_menu() +create_menu(void) { GntWidget *menu, *sub, *subsub; GntMenuItem *item; @@ -2513,12 +2597,12 @@ purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(), PURPLE_CALLBACK(reconstruct_plugins_menu), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(), + PURPLE_CALLBACK(buddy_signed_on_off), ggblist); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(), + PURPLE_CALLBACK(buddy_signed_on_off), ggblist); + #if 0 - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(), - PURPLE_CALLBACK(buddy_signed_on), ggblist); - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(), - PURPLE_CALLBACK(buddy_signed_off), ggblist); - /* These I plan to use to indicate unread-messages etc. */ purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", finch_blist_get_handle(), PURPLE_CALLBACK(received_im_msg), list); diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntcertmgr.c --- a/finch/gntcertmgr.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntcertmgr.c Mon Jan 07 03:40:27 2008 +0000 @@ -246,7 +246,7 @@ /* populate the list */ static void -populate_cert_list() +populate_cert_list(void) { GList *idlist, *l; diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntconv.c --- a/finch/gntconv.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntconv.c Mon Jan 07 03:40:27 2008 +0000 @@ -350,7 +350,7 @@ } static gpointer -finch_conv_get_handle() +finch_conv_get_handle(void) { static int handle; return &handle; @@ -1119,7 +1119,7 @@ static PurpleCmdRet cmd_show_window(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data) { - void (*callback)() = data; + void (*callback)(void) = data; callback(); return PURPLE_CMD_STATUS_OK; } diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntft.c --- a/finch/gntft.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntft.c Mon Jan 07 03:40:27 2008 +0000 @@ -85,7 +85,7 @@ **************************************************************************/ static void -update_title_progress() +update_title_progress(void) { GList *list; int num_active_xfers = 0; diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntft.h --- a/finch/gntft.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntft.h Mon Jan 07 03:40:27 2008 +0000 @@ -55,7 +55,7 @@ /** * Hides the file transfer dialog. */ -void finch_xfer_dialog_hide(); +void finch_xfer_dialog_hide(void); /** * Adds a file transfer to the dialog. diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntidle.c --- a/finch/gntidle.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntidle.c Mon Jan 07 03:40:27 2008 +0000 @@ -28,7 +28,7 @@ #include "idle.h" static time_t -finch_get_idle_time() +finch_get_idle_time(void) { return gnt_wm_get_idle_time(); } diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntnotify.c --- a/finch/gntnotify.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntnotify.c Mon Jan 07 03:40:27 2008 +0000 @@ -147,14 +147,14 @@ } static void -reset_email_dialog() +reset_email_dialog(void) { emaildialog.window = NULL; emaildialog.tree = NULL; } static void -setup_email_dialog() +setup_email_dialog(void) { GntWidget *box, *tree, *button; if (emaildialog.window) diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntplugin.c --- a/finch/gntplugin.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntplugin.c Mon Jan 07 03:40:27 2008 +0000 @@ -171,7 +171,7 @@ } static void -confwin_init() +confwin_init(void) { confwins = g_hash_table_new(g_direct_hash, g_direct_equal); } diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntplugin.h --- a/finch/gntplugin.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntplugin.h Mon Jan 07 03:40:27 2008 +0000 @@ -40,7 +40,7 @@ **********************************************************************/ /*@{*/ -typedef GntWidget* (*FinchPluginFrame) (); +typedef GntWidget* (*FinchPluginFrame) (void); /* Guess where these came from */ #define FINCH_PLUGIN_TYPE FINCH_UI diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntprefs.c --- a/finch/gntprefs.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntprefs.c Mon Jan 07 03:40:27 2008 +0000 @@ -76,17 +76,17 @@ PurplePrefType type; const char *pref; const char *label; - GList *(*lv)(); /* If the value is to be selected from a number of choices */ + GList *(*lv)(void); /* If the value is to be selected from a number of choices */ } Prefs; static GList * -get_log_options() +get_log_options(void) { return purple_log_logger_get_options(); } static GList * -get_idle_options() +get_idle_options(void) { GList *list = NULL; list = g_list_append(list, (char *)_("Based on keyboard use")); @@ -99,7 +99,7 @@ } static GList * -get_status_titles() +get_status_titles(void) { GList *list = NULL; GList *iter; @@ -212,7 +212,7 @@ }; static void -free_strings() +free_strings(void) { g_list_foreach(pref_request.freestrings, (GFunc)g_free, NULL); g_list_free(pref_request.freestrings); diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntrequest.c --- a/finch/gntrequest.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntrequest.c Mon Jan 07 03:40:27 2008 +0000 @@ -36,6 +36,7 @@ #include "finch.h" #include "gntrequest.h" +#include "debug.h" #include "util.h" typedef struct @@ -44,7 +45,7 @@ GntWidget *dialog; GCallback *cbs; gboolean save; -} PurpleGntFileRequest; +} FinchFileRequest; static GntWidget * setup_request_window(const char *title, const char *primary, @@ -387,6 +388,156 @@ } } +static GntWidget* +create_boolean_field(PurpleRequestField *field) +{ + const char *label = purple_request_field_get_label(field); + GntWidget *check = gnt_check_box_new(label); + gnt_check_box_set_checked(GNT_CHECK_BOX(check), + purple_request_field_bool_get_default_value(field)); + return check; +} + +static GntWidget* +create_string_field(PurpleRequestField *field, GntWidget **screenname) +{ + const char *hint = purple_request_field_get_type_hint(field); + GntWidget *entry = gnt_entry_new( + purple_request_field_string_get_default_value(field)); + gnt_entry_set_masked(GNT_ENTRY(entry), + purple_request_field_string_is_masked(field)); + if (hint && purple_str_has_prefix(hint, "screenname")) { + PurpleBlistNode *node = purple_blist_get_root(); + gboolean offline = purple_str_has_suffix(hint, "all"); + for (; node; node = purple_blist_node_next(node, offline)) { + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + continue; + gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node)); + } + gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE); + if (screenname) + *screenname = entry; + } else if (hint && !strcmp(hint, "group")) { + PurpleBlistNode *node; + for (node = purple_blist_get_root(); node; node = node->next) { + if (PURPLE_BLIST_NODE_IS_GROUP(node)) + gnt_entry_add_suggest(GNT_ENTRY(entry), ((PurpleGroup *)node)->name); + } + } + return entry; +} + +static GntWidget* +create_integer_field(PurpleRequestField *field) +{ + char str[256]; + int val = purple_request_field_int_get_default_value(field); + GntWidget *entry; + + snprintf(str, sizeof(str), "%d", val); + entry = gnt_entry_new(str); + gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT); + return entry; +} + +static GntWidget* +create_choice_field(PurpleRequestField *field) +{ + int id; + GList *list; + GntWidget *combo = gnt_combo_box_new(); + + list = purple_request_field_choice_get_labels(field); + for (id = 1; list; list = list->next, id++) + { + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), + GINT_TO_POINTER(id), list->data); + } + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), + GINT_TO_POINTER(purple_request_field_choice_get_default_value(field))); + return combo; +} + +static GntWidget* +create_list_field(PurpleRequestField *field) +{ + GntWidget *ret = NULL; + GList *list; + gboolean multi = purple_request_field_list_get_multi_select(field); + if (multi) + { + GntWidget *tree = gnt_tree_new(); + + list = purple_request_field_list_get_items(field); + for (; list; list = list->next) + { + const char *text = list->data; + gpointer key = purple_request_field_list_get_data(field, text); + gnt_tree_add_choice(GNT_TREE(tree), key, + gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL); + if (purple_request_field_list_is_selected(field, text)) + gnt_tree_set_choice(GNT_TREE(tree), key, TRUE); + } + ret = tree; + } + else + { + GntWidget *combo = gnt_combo_box_new(); + + list = purple_request_field_list_get_items(field); + for (; list; list = list->next) + { + const char *text = list->data; + gpointer key = purple_request_field_list_get_data(field, text); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text); + if (purple_request_field_list_is_selected(field, text)) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key); + } + ret = combo; + } + return ret; +} + +static GntWidget* +create_account_field(PurpleRequestField *field) +{ + gboolean all; + PurpleAccount *def; + GList *list; + GntWidget *combo = gnt_combo_box_new(); + + all = purple_request_field_account_get_show_all(field); + def = purple_request_field_account_get_value(field); + if (!def) + def = purple_request_field_account_get_default_value(field); + + if (all) + list = purple_accounts_get_all(); + else + list = purple_connections_get_all(); + + for (; list; list = list->next) + { + PurpleAccount *account; + char *text; + + if (all) + account = list->data; + else + account = purple_connection_get_account(list->data); + + text = g_strdup_printf("%s (%s)", + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text); + g_free(text); + if (account == def) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account); + } + gnt_widget_set_size(combo, 20, 3); /* ew */ + return combo; +} + static void * finch_request_fields(const char *title, const char *primary, const char *secondary, PurpleRequestFields *allfields, @@ -424,10 +575,10 @@ PurpleRequestField *field = fields->data; PurpleRequestFieldType type = purple_request_field_get_type(field); const char *label = purple_request_field_get_label(field); - + hbox = gnt_hbox_new(TRUE); /* hrm */ gnt_box_add_widget(GNT_BOX(box), hbox); - + if (type != PURPLE_REQUEST_FIELD_BOOLEAN && label) { GntWidget *l = gnt_label_new(label); @@ -437,154 +588,35 @@ if (type == PURPLE_REQUEST_FIELD_BOOLEAN) { - GntWidget *check = gnt_check_box_new(label); - gnt_check_box_set_checked(GNT_CHECK_BOX(check), - purple_request_field_bool_get_default_value(field)); - gnt_box_add_widget(GNT_BOX(hbox), check); - field->ui_data = check; + field->ui_data = create_boolean_field(field); } else if (type == PURPLE_REQUEST_FIELD_STRING) { - const char *hint = purple_request_field_get_type_hint(field); - GntWidget *entry = gnt_entry_new( - purple_request_field_string_get_default_value(field)); - gnt_entry_set_masked(GNT_ENTRY(entry), - purple_request_field_string_is_masked(field)); - if (hint && purple_str_has_prefix(hint, "screenname")) { - PurpleBlistNode *node = purple_blist_get_root(); - gboolean offline = purple_str_has_suffix(hint, "all"); - for (; node; node = purple_blist_node_next(node, offline)) { - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) - continue; - gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node)); - } - gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE); - screenname = entry; - } else if (hint && !strcmp(hint, "group")) { - PurpleBlistNode *node; - for (node = purple_blist_get_root(); node; node = node->next) { - if (PURPLE_BLIST_NODE_IS_GROUP(node)) - gnt_entry_add_suggest(GNT_ENTRY(entry), ((PurpleGroup *)node)->name); - } - } - gnt_box_add_widget(GNT_BOX(hbox), entry); - field->ui_data = entry; + field->ui_data = create_string_field(field, &screenname); } else if (type == PURPLE_REQUEST_FIELD_INTEGER) { - char str[256]; - int val = purple_request_field_int_get_default_value(field); - GntWidget *entry; - - snprintf(str, sizeof(str), "%d", val); - entry = gnt_entry_new(str); - gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT); - gnt_box_add_widget(GNT_BOX(hbox), entry); - field->ui_data = entry; + field->ui_data = create_integer_field(field); } else if (type == PURPLE_REQUEST_FIELD_CHOICE) { - int id; - GList *list; - GntWidget *combo = gnt_combo_box_new(); - gnt_box_add_widget(GNT_BOX(hbox), combo); - field->ui_data = combo; - - list = purple_request_field_choice_get_labels(field); - for (id = 1; list; list = list->next, id++) - { - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), - GINT_TO_POINTER(id), list->data); - } - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), - GINT_TO_POINTER(purple_request_field_choice_get_default_value(field))); + field->ui_data = create_choice_field(field); } else if (type == PURPLE_REQUEST_FIELD_LIST) { - GList *list; - gboolean multi = purple_request_field_list_get_multi_select(field); - if (multi) - { - GntWidget *tree = gnt_tree_new(); - gnt_box_add_widget(GNT_BOX(hbox), tree); - field->ui_data = tree; - - list = purple_request_field_list_get_items(field); - for (; list; list = list->next) - { - const char *text = list->data; - gpointer key = purple_request_field_list_get_data(field, text); - gnt_tree_add_choice(GNT_TREE(tree), key, - gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL); - if (purple_request_field_list_is_selected(field, text)) - gnt_tree_set_choice(GNT_TREE(tree), key, TRUE); - } - } - else - { - GntWidget *combo = gnt_combo_box_new(); - gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); - gnt_box_add_widget(GNT_BOX(hbox), combo); - field->ui_data = combo; - - list = purple_request_field_list_get_items(field); - for (; list; list = list->next) - { - const char *text = list->data; - gpointer key = purple_request_field_list_get_data(field, text); - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text); - if (purple_request_field_list_is_selected(field, text)) - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key); - } - } + field->ui_data = create_list_field(field); } else if (type == PURPLE_REQUEST_FIELD_ACCOUNT) { - gboolean all; - PurpleAccount *def; - GList *list; - GntWidget *combo = gnt_combo_box_new(); - gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); - gnt_box_add_widget(GNT_BOX(hbox), combo); - field->ui_data = combo; - - all = purple_request_field_account_get_show_all(field); - def = purple_request_field_account_get_value(field); - if (!def) - def = purple_request_field_account_get_default_value(field); - - if (all) - list = purple_accounts_get_all(); - else - list = purple_connections_get_all(); - - for (; list; list = list->next) - { - PurpleAccount *account; - char *text; - - if (all) - account = list->data; - else - account = purple_connection_get_account(list->data); - - text = g_strdup_printf("%s (%s)", - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text); - g_free(text); - if (account == def) - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account); - } - gnt_widget_set_size(combo, 20, 3); /* ew */ - accountlist = combo; + accountlist = field->ui_data = create_account_field(field); } else { - gnt_box_add_widget(GNT_BOX(hbox), - gnt_label_new_with_format(_("Not implemented yet."), - GNT_TEXT_FLAG_BOLD)); + field->ui_data = gnt_label_new_with_format(_("Not implemented yet."), + GNT_TEXT_FLAG_BOLD); } + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(field->ui_data)); } if (grlist->next) gnt_box_add_widget(GNT_BOX(box), gnt_hline_new()); @@ -610,7 +642,7 @@ static void file_cancel_cb(gpointer fq, GntWidget *wid) { - PurpleGntFileRequest *data = fq; + FinchFileRequest *data = fq; if (data->cbs[1] != NULL) ((PurpleRequestFileCb)data->cbs[1])(data->user_data, NULL); @@ -620,7 +652,7 @@ static void file_ok_cb(gpointer fq, GntWidget *widget) { - PurpleGntFileRequest *data = fq; + FinchFileRequest *data = fq; char *file = gnt_file_sel_get_selected_file(GNT_FILE_SEL(data->dialog)); char *dir = g_path_get_dirname(file); if (data->cbs[0] != NULL) @@ -634,38 +666,30 @@ } static void -file_request_destroy(PurpleGntFileRequest *data) +file_request_destroy(FinchFileRequest *data) { g_free(data->cbs); g_free(data); } -static void * -finch_request_file(const char *title, const char *filename, - gboolean savedialog, +static FinchFileRequest * +finch_file_request_window(const char *title, const char *path, GCallback ok_cb, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, void *user_data) { GntWidget *window = gnt_file_sel_new(); GntFileSel *sel = GNT_FILE_SEL(window); - PurpleGntFileRequest *data = g_new0(PurpleGntFileRequest, 1); - const char *path; + FinchFileRequest *data = g_new0(FinchFileRequest, 1); data->user_data = user_data; data->cbs = g_new0(GCallback, 2); data->cbs[0] = ok_cb; data->cbs[1] = cancel_cb; data->dialog = window; - data->save = savedialog; - gnt_box_set_title(GNT_BOX(window), title ? title : (savedialog ? _("Save File...") : _("Open File..."))); + gnt_box_set_title(GNT_BOX(window), title); - path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder"); gnt_file_sel_set_current_location(sel, (path && *path) ? path : purple_home_dir()); - if (savedialog) - gnt_file_sel_set_suggested_filename(sel, filename); - g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(action_performed), window); g_signal_connect(G_OBJECT(sel->select), "activate", @@ -678,9 +702,45 @@ setup_default_callback(window, file_cancel_cb, data); g_object_set_data_full(G_OBJECT(window), "filerequestdata", data, (GDestroyNotify)file_request_destroy); - gnt_widget_show(window); + + return data; +} + +static void * +finch_request_file(const char *title, const char *filename, + gboolean savedialog, + GCallback ok_cb, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data) +{ + FinchFileRequest *data; + const char *path; - return window; + path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder"); + data = finch_file_request_window(title ? title : (savedialog ? _("Save File...") : _("Open File...")), path, + ok_cb, cancel_cb, user_data); + data->save = savedialog; + if (savedialog) + gnt_file_sel_set_suggested_filename(GNT_FILE_SEL(data->dialog), filename); + + gnt_widget_show(data->dialog); + return data->dialog; +} + +static void * +finch_request_folder(const char *title, const char *dirname, GCallback ok_cb, + GCallback cancel_cb, PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data) +{ + FinchFileRequest *data; + + data = finch_file_request_window(title ? title : _("Choose Location..."), dirname, + ok_cb, cancel_cb, user_data); + data->save = TRUE; + gnt_file_sel_set_dirs_only(GNT_FILE_SEL(data->dialog), TRUE); + + gnt_widget_show(data->dialog); + return data->dialog; } static PurpleRequestUiOps uiops = @@ -691,7 +751,7 @@ finch_request_fields, finch_request_file, finch_close_request, - NULL, /* No plans for request_folder */ + finch_request_folder, NULL, NULL, NULL, @@ -765,3 +825,32 @@ } } +GntWidget *finch_request_field_get_widget(PurpleRequestField *field) +{ + GntWidget *ret = NULL; + switch (purple_request_field_get_type(field)) { + case PURPLE_REQUEST_FIELD_BOOLEAN: + ret = create_boolean_field(field); + break; + case PURPLE_REQUEST_FIELD_STRING: + ret = create_string_field(field, NULL); + break; + case PURPLE_REQUEST_FIELD_INTEGER: + ret = create_integer_field(field); + break; + case PURPLE_REQUEST_FIELD_CHOICE: + ret = create_choice_field(field); + break; + case PURPLE_REQUEST_FIELD_LIST: + ret = create_list_field(field); + break; + case PURPLE_REQUEST_FIELD_ACCOUNT: + ret = create_account_field(field); + break; + default: + purple_debug_error("GntRequest", "Unimplemented request-field %d\n", purple_request_field_get_type(field)); + break; + } + return ret; +} + diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntrequest.h --- a/finch/gntrequest.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntrequest.h Mon Jan 07 03:40:27 2008 +0000 @@ -27,6 +27,7 @@ #define _GNT_REQUEST_H #include "request.h" +#include "gnt.h" /********************************************************************** * @name GNT Request API @@ -56,6 +57,15 @@ */ void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *fields); +/** + * Create a widget field for a request-field. + * + * @param field The request field. + * + * @return A GntWidget for the request field. + * @since 2.4.0 + */ +GntWidget *finch_request_field_get_widget(PurpleRequestField *field); /*@}*/ #endif diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntroomlist.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntroomlist.c Mon Jan 07 03:40:27 2008 +0000 @@ -0,0 +1,375 @@ +/** + * @file gntroomlist.c GNT Room List API + * @ingroup finch + */ + +/* finch + * + * Finch 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 + */ + +#include"internal.h" + +#include "gntrequest.h" +#include "gntroomlist.h" + +#include "gntbox.h" +#include "gntbutton.h" +#include "gntcombobox.h" +#include "gnttextview.h" +#include "gnttree.h" +#include "gntwindow.h" + +#include "debug.h" + +/* Yes, just one roomlist at a time. Let's not get greedy. Aight? */ +struct _FinchRoomlist +{ + GntWidget *window; + + GntWidget *accounts; + GntWidget *tree; + GntWidget *details; + + GntWidget *getlist; + GntWidget *add; + GntWidget *join; + GntWidget *stop; + GntWidget *close; + + PurpleAccount *account; + PurpleRoomlist *roomlist; +} froomlist; + +typedef struct _FinchRoomlist FinchRoomlist; + +static void +unset_roomlist(gpointer null) +{ + froomlist.window = NULL; + if (froomlist.roomlist) + purple_roomlist_unref(froomlist.roomlist); +} + +static void +update_roomlist(PurpleRoomlist *list) +{ + if (froomlist.roomlist == list) + return; + + if (froomlist.roomlist) + purple_roomlist_unref(froomlist.roomlist); + + if ((froomlist.roomlist = list) != NULL) + purple_roomlist_ref(list); +} + +static void fl_stop(GntWidget *button, gpointer null) +{ + if (froomlist.roomlist && + purple_roomlist_get_in_progress(froomlist.roomlist)) + purple_roomlist_cancel_get_list(froomlist.roomlist); +} + +static void fl_get_list(GntWidget *button, gpointer null) +{ + PurpleAccount *account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts)); + PurpleConnection *gc = purple_account_get_connection(account); + + if (!gc) + return; + + froomlist.roomlist = purple_roomlist_get_list(gc); + gnt_box_give_focus_to_child(GNT_BOX(froomlist.window), froomlist.tree); +} + +static void fl_add_chat(GntWidget *button, gpointer null) +{ + char *name; + PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(froomlist.tree)); + PurpleConnection *gc = purple_account_get_connection(froomlist.account); + PurplePluginProtocolInfo *prpl_info = NULL; + + if (gc == NULL || room == NULL) + return; + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if(prpl_info != NULL && prpl_info->roomlist_room_serialize) + name = prpl_info->roomlist_room_serialize(room); + else + name = g_strdup(purple_roomlist_room_get_name(room)); + + purple_blist_request_add_chat(froomlist.account, NULL, NULL, name); + + g_free(name); +} + +static void fl_close(GntWidget *button, gpointer null) +{ + gnt_widget_destroy(froomlist.window); +} + +static void +roomlist_activated(GntWidget *widget) +{ + PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(widget)); + if (!room) + return; + + switch (purple_roomlist_room_get_type(room)) { + case PURPLE_ROOMLIST_ROOMTYPE_ROOM: + purple_roomlist_room_join(froomlist.roomlist, room); + break; + case PURPLE_ROOMLIST_ROOMTYPE_CATEGORY: + purple_roomlist_expand_category(froomlist.roomlist, room); + break; + } +} + +static void +roomlist_selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null) +{ + GList *iter, *field; + PurpleRoomlistRoom *room = current; + GntTextView *tv = GNT_TEXT_VIEW(froomlist.details); + gboolean first = TRUE; + + gnt_text_view_clear(tv); + + if (!room) + return; + + for (iter = purple_roomlist_room_get_fields(room), + field = purple_roomlist_get_fields(froomlist.roomlist); + iter && field; + iter = iter->next, field = field->next) { + PurpleRoomlistField *f = field->data; + char *label = NULL; + + if (purple_roomlist_field_get_hidden(f)) { + continue; + } + + if (!first) + gnt_text_view_append_text_with_flags(tv, "\n", GNT_TEXT_FLAG_NORMAL); + + gnt_text_view_append_text_with_flags(tv, + purple_roomlist_field_get_label(f), GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(tv, ": ", GNT_TEXT_FLAG_BOLD); + + switch (purple_roomlist_field_get_type(f)) { + case PURPLE_ROOMLIST_FIELD_BOOL: + label = g_strdup(iter->data ? "True" : "False"); + break; + case PURPLE_ROOMLIST_FIELD_INT: + label = g_strdup_printf("%d", (int)iter->data); + break; + case PURPLE_ROOMLIST_FIELD_STRING: + label = g_strdup(iter->data); + break; + } + gnt_text_view_append_text_with_flags(tv, label, GNT_TEXT_FLAG_NORMAL); + g_free(label); + first = FALSE; + } + + if (purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) { + gnt_text_view_append_text_with_flags(tv, + _("\nHit 'Enter' to find more rooms of this category."), + GNT_TEXT_FLAG_NORMAL); + } +} + +static void +roomlist_account_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null) +{ + if (froomlist.account == current) { + return; + } + + froomlist.account = current; + if (froomlist.roomlist) { + if (purple_roomlist_get_in_progress(froomlist.roomlist)) + purple_roomlist_cancel_get_list(froomlist.roomlist); + update_roomlist(NULL); + } + + gnt_tree_remove_all(GNT_TREE(froomlist.tree)); + gnt_widget_draw(froomlist.tree); +} + +static void +reset_account_list(PurpleAccount *account) +{ + GList *list; + GntComboBox *accounts = GNT_COMBO_BOX(froomlist.accounts); + gnt_combo_box_remove_all(accounts); + for (list = purple_connections_get_all(); list; list = list->next) { + PurplePluginProtocolInfo *prpl_info = NULL; + PurpleConnection *gc = list->data; + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (prpl_info->roomlist_get_list != NULL) { + PurpleAccount *account = purple_connection_get_account(gc); + char *text = g_strdup_printf("%s (%s)", + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + gnt_combo_box_add_data(accounts, account, text); + g_free(text); + } + } +} + +static void +setup_roomlist(PurpleAccount *account) +{ + GntWidget *window, *tree, *hbox, *accounts; + int iter; + struct { + const char *label; + GCallback callback; + GntWidget **widget; + } buttons[] = { + {_("Stop"), G_CALLBACK(fl_stop), &froomlist.stop}, + {_("Get"), G_CALLBACK(fl_get_list), &froomlist.getlist}, + {_("Add"), G_CALLBACK(fl_add_chat), &froomlist.add}, + {_("Close"), G_CALLBACK(fl_close), &froomlist.close}, + {NULL, NULL, NULL} + }; + + if (froomlist.window) + return; + + froomlist.window = window = gnt_window_new(); + g_object_set(G_OBJECT(window), "vertical", TRUE, NULL); + gnt_box_set_pad(GNT_BOX(window), 0); + gnt_box_set_title(GNT_BOX(window), _("Room List")); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + froomlist.accounts = accounts = gnt_combo_box_new(); + reset_account_list(account); + gnt_box_add_widget(GNT_BOX(window), froomlist.accounts); + g_signal_connect(G_OBJECT(froomlist.accounts), "selection-changed", + G_CALLBACK(roomlist_account_changed), NULL); + froomlist.account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts)); + + froomlist.tree = tree = gnt_tree_new_with_columns(2); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(roomlist_activated), NULL); + gnt_tree_set_column_titles(GNT_TREE(tree), _("Name"), ""); + gnt_tree_set_show_separator(GNT_TREE(tree), FALSE); + gnt_tree_set_col_width(GNT_TREE(tree), 1, 1); + gnt_tree_set_column_resizable(GNT_TREE(tree), 1, FALSE); + gnt_tree_set_search_column(GNT_TREE(tree), 0); + + gnt_box_add_widget(GNT_BOX(window), tree); + + froomlist.details = gnt_text_view_new(); + gnt_text_view_set_flag(GNT_TEXT_VIEW(froomlist.details), GNT_TEXT_VIEW_TOP_ALIGN); + gnt_box_add_widget(GNT_BOX(window), froomlist.details); + gnt_widget_set_size(froomlist.details, -1, 10); + + hbox = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), hbox); + + for (iter = 0; buttons[iter].label; iter++) { + GntWidget *button = gnt_button_new(buttons[iter].label); + gnt_box_add_widget(GNT_BOX(hbox), button); + g_signal_connect(G_OBJECT(button), "activate", buttons[iter].callback, NULL); + *buttons[iter].widget = button; + gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(froomlist.details), button); + } + + g_signal_connect(G_OBJECT(tree), "selection-changed", G_CALLBACK(roomlist_selection_changed), NULL); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(unset_roomlist), NULL); +} + +static void +fl_show_with_account(PurpleAccount *account) +{ + setup_roomlist(account); + gnt_window_present(froomlist.window); +} + +static void +fl_create(PurpleRoomlist *list) +{ + list->ui_data = &froomlist; + setup_roomlist(NULL); + update_roomlist(list); +} + +static void +fl_set_fields(PurpleRoomlist *list, GList *fields) +{ +} + +static void +fl_add_room(PurpleRoomlist *roomlist, PurpleRoomlistRoom *room) +{ + if (froomlist.roomlist != roomlist) + return; + + gnt_tree_remove(GNT_TREE(froomlist.tree), room); + gnt_tree_add_row_after(GNT_TREE(froomlist.tree), room, + gnt_tree_create_row(GNT_TREE(froomlist.tree), + purple_roomlist_room_get_name(room), + purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY ? "<" : ""), + purple_roomlist_room_get_parent(room), NULL); +} + +static void +fl_destroy(PurpleRoomlist *list) +{ + if (!froomlist.window) + return; + + if (froomlist.roomlist == list) { + froomlist.roomlist = NULL; + gnt_tree_remove_all(GNT_TREE(froomlist.tree)); + gnt_widget_draw(froomlist.tree); + } +} + +static PurpleRoomlistUiOps ui_ops = +{ + fl_show_with_account, /* void (*show_with_account)(PurpleAccount *account); **< Force the ui to pop up a dialog and get the list */ + fl_create, /* void (*create)(PurpleRoomlist *list); **< A new list was created. */ + fl_set_fields, /* void (*set_fields)(PurpleRoomlist *list, GList *fields); **< Sets the columns. */ + fl_add_room, /* void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room); **< Add a room to the list. */ + NULL, /* void (*in_progress)(PurpleRoomlist *list, gboolean flag); **< Are we fetching stuff still? */ + fl_destroy, /* void (*destroy)(PurpleRoomlist *list); **< We're destroying list. */ + + NULL, /* void (*_purple_reserved1)(void); */ + NULL, /* void (*_purple_reserved2)(void); */ + NULL, /* void (*_purple_reserved3)(void); */ + NULL /* void (*_purple_reserved4)(void); */ +}; +PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void) +{ + return &ui_ops; +} + +void finch_roomlist_show_all(void) +{ + purple_roomlist_show_with_account(NULL); +} + diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntroomlist.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntroomlist.h Mon Jan 07 03:40:27 2008 +0000 @@ -0,0 +1,51 @@ +/** + * @file gntroomlist.h GNT Room List API + * @ingroup finch + */ + +/* finch + * + * Finch 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 _GNT_ROOMLIST_H +#define _GNT_ROOMLIST_H + +#include "roomlist.h" + +/********************************************************************** + * @name GNT Room List API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The PurpleRoomlistUiOps structure populated with the appropriate functions. + */ +PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void); + +/** + * Show the roomlist dialog. + */ +void finch_roomlist_show_all(void); + +/*@}*/ + +#endif + diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntsound.c --- a/finch/gntsound.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntsound.c Mon Jan 07 03:40:27 2008 +0000 @@ -286,7 +286,7 @@ } static void * -finch_sound_get_handle() +finch_sound_get_handle(void) { static int handle; diff -r 754a82f1371b -r 9a60b9fab0ea finch/gntui.c --- a/finch/gntui.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/gntui.c Mon Jan 07 03:40:27 2008 +0000 @@ -35,6 +35,7 @@ #include "gntpounce.h" #include "gntprefs.h" #include "gntrequest.h" +#include "gntroomlist.h" #include "gntstatus.h" #include "gntsound.h" @@ -71,14 +72,20 @@ finch_notify_init(); purple_notify_set_ui_ops(finch_notify_get_ui_ops()); + /* Request */ finch_request_init(); purple_request_set_ui_ops(finch_request_get_ui_ops()); + /* Pounce */ finch_pounces_init(); + /* File transfer */ finch_xfers_init(); purple_xfers_set_ui_ops(finch_xfers_get_ui_ops()); + /* Roomlist */ + purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops()); + gnt_register_action(_("Accounts"), finch_accounts_show_all); gnt_register_action(_("Buddy List"), finch_blist_show); gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show); @@ -86,6 +93,7 @@ gnt_register_action(_("Debug Window"), finch_debug_window_show); gnt_register_action(_("File Transfers"), finch_xfer_dialog_show); gnt_register_action(_("Plugins"), finch_plugins_show_all); + gnt_register_action(_("Room List"), finch_roomlist_show_all); gnt_register_action(_("Sounds"), finch_sounds_show_all); gnt_register_action(_("Preferences"), finch_prefs_show_all); gnt_register_action(_("Statuses"), finch_savedstatus_show_all); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/configure.ac --- a/finch/libgnt/configure.ac Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/configure.ac Mon Jan 07 03:40:27 2008 +0000 @@ -24,10 +24,10 @@ # Make sure to update ../../configure.ac with libgnt version changes. # -m4_define([gnt_lt_current], [3]) +m4_define([gnt_lt_current], [4]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [3]) -m4_define([gnt_micro_version], [2]) +m4_define([gnt_minor_version], [4]) +m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gnt.h --- a/finch/libgnt/gnt.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gnt.h Mon Jan 07 03:40:27 2008 +0000 @@ -141,7 +141,7 @@ * @param label The user-visible label for the action. * @param callback The callback function for the action. */ -void gnt_register_action(const char *label, void (*callback)()); +void gnt_register_action(const char *label, void (*callback)(void)); /** * Show a menu. diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntbindable.c --- a/finch/libgnt/gntbindable.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntbindable.c Mon Jan 07 03:40:27 2008 +0000 @@ -45,7 +45,7 @@ } rebind_info; static void -gnt_bindable_free_rebind_info() +gnt_bindable_free_rebind_info(void) { g_free(rebind_info.name); g_free(rebind_info.keys); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntcolors.c --- a/finch/libgnt/gntcolors.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntcolors.c Mon Jan 07 03:40:27 2008 +0000 @@ -40,7 +40,7 @@ } colors[GNT_TOTAL_COLORS]; static void -backup_colors() +backup_colors(void) { short i; for (i = 0; i < GNT_TOTAL_COLORS; i++) @@ -51,13 +51,13 @@ } static gboolean -can_use_custom_color() +can_use_custom_color(void) { return (gnt_style_get_bool(GNT_STYLE_COLOR, FALSE) && can_change_color()); } static void -restore_colors() +restore_colors(void) { short i; for (i = 0; i < GNT_TOTAL_COLORS; i++) diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntcolors.h --- a/finch/libgnt/gntcolors.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntcolors.h Mon Jan 07 03:40:27 2008 +0000 @@ -93,7 +93,7 @@ * * @return A color * - * @since 2.3.1 (gnt), 2.3.1 (pidgin) + * @since 2.4.0 */ int gnt_colors_get_color(char *key); #endif @@ -119,7 +119,7 @@ * * @return A color pair * - * @since 2.3.1 + * @since 2.4.0 */ int gnt_color_add_pair(int fg, int bg); #endif diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntentry.c --- a/finch/libgnt/gntentry.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntentry.c Mon Jan 07 03:40:27 2008 +0000 @@ -916,7 +916,7 @@ } static GntEntryKillRing * -new_killring() +new_killring(void) { GntEntryKillRing *kr = g_new0(GntEntryKillRing, 1); kr->buffer = g_string_new(NULL); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntmain.c --- a/finch/libgnt/gntmain.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntmain.c Mon Jan 07 03:40:27 2008 +0000 @@ -72,7 +72,7 @@ static void setup_io(void); -static gboolean refresh_screen(); +static gboolean refresh_screen(void); static GntWM *wm; static GntClipboard *clipboard; @@ -326,7 +326,7 @@ } static gboolean -refresh_screen() +refresh_screen(void) { gnt_bindable_perform_action_named(GNT_BINDABLE(wm), "refresh-screen", NULL); return FALSE; @@ -363,7 +363,7 @@ } static void -ask_before_exit() +ask_before_exit(void) { static GntWidget *win = NULL; GntWidget *bbox, *button; @@ -412,7 +412,7 @@ #ifdef SIGWINCH case SIGWINCH: erase(); - g_idle_add(refresh_screen, NULL); + g_idle_add((GSourceFunc)refresh_screen, NULL); if (org_winch_handler) org_winch_handler(sig); signal(SIGWINCH, sighandler); @@ -430,7 +430,7 @@ } static void -init_wm() +init_wm(void) { const char *name = gnt_style_get(GNT_STYLE_WM); gpointer handle; @@ -614,7 +614,7 @@ gnt_wm_update_window(wm, widget); } -void gnt_register_action(const char *label, void (*callback)()) +void gnt_register_action(const char *label, void (*callback)(void)) { GntAction *action = g_new0(GntAction, 1); action->label = g_strdup(label); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntstyle.h Mon Jan 07 03:40:27 2008 +0000 @@ -74,7 +74,7 @@ * * @return NULL terminated string array. The array should be freed with g_strfreev(). * - * @since 2.3.2 + * @since 2.4.0 */ char **gnt_style_get_string_list(const char *group, const char *key, gsize *length); @@ -87,7 +87,7 @@ * * @return The value of the color as an int, or 0 on error. * - * @since 2.3.2 + * @since 2.4.0 */ int gnt_style_get_color(char *group, char *key); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gnttextview.c --- a/finch/libgnt/gnttextview.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gnttextview.c Mon Jan 07 03:40:27 2008 +0000 @@ -830,7 +830,7 @@ static void -cleanup_pageditor() +cleanup_pageditor(void) { unlink(pageditor.file); g_free(pageditor.file); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gnttree.h Mon Jan 07 03:40:27 2008 +0000 @@ -330,6 +330,7 @@ * @param tree The tree * @param key The key for the row * @param color The color + * @since 2.4.0 */ void gnt_tree_set_row_color(GntTree *, void *, int); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntwm.c Mon Jan 07 03:40:27 2008 +0000 @@ -135,7 +135,7 @@ * to expose the entire character, it is not always redrawn. */ static void -work_around_for_ncurses_bug() +work_around_for_ncurses_bug(void) { #ifndef NO_WIDECHAR PANEL *panel = NULL; @@ -183,7 +183,7 @@ } static void -update_act_msg() +update_act_msg(void) { GntWidget *label; GList *iter; @@ -388,10 +388,10 @@ } static void -switch_window(GntWM *wm, int direction) +switch_window(GntWM *wm, int direction, gboolean urgent) { GntWidget *w = NULL, *wid = NULL; - int pos; + int pos, orgpos; if (wm->_list.window || wm->menu) return; @@ -404,15 +404,20 @@ } w = wm->cws->ordered->data; - pos = g_list_index(wm->cws->list, w); - pos += direction; + orgpos = pos = g_list_index(wm->cws->list, w); + + do { + pos += direction; - if (pos < 0) - wid = g_list_last(wm->cws->list)->data; - else if (pos >= g_list_length(wm->cws->list)) - wid = wm->cws->list->data; - else if (pos >= 0) - wid = g_list_nth_data(wm->cws->list, pos); + if (pos < 0) { + wid = g_list_last(wm->cws->list)->data; + pos = g_list_length(wm->cws->list) - 1; + } else if (pos >= g_list_length(wm->cws->list)) { + wid = wm->cws->list->data; + pos = 0; + } else + wid = g_list_nth_data(wm->cws->list, pos); + } while (urgent && !GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT) && pos != orgpos); gnt_wm_raise_window(wm, wid); } @@ -421,7 +426,7 @@ window_next(GntBindable *bindable, GList *null) { GntWM *wm = GNT_WM(bindable); - switch_window(wm, 1); + switch_window(wm, 1, FALSE); return TRUE; } @@ -429,7 +434,7 @@ window_prev(GntBindable *bindable, GList *null) { GntWM *wm = GNT_WM(bindable); - switch_window(wm, -1); + switch_window(wm, -1, FALSE); return TRUE; } @@ -1202,6 +1207,22 @@ return ignore_keys ? !(ignore_keys = FALSE) : FALSE; } +static gboolean +window_next_urgent(GntBindable *bindable, GList *n) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, 1, TRUE); + return TRUE; +} + +static gboolean +window_prev_urgent(GntBindable *bindable, GList *n) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, -1, TRUE); + return TRUE; +} + #ifdef USE_PYTHON static void python_script_selected(GntFileSel *fs, const char *path, const char *f, gpointer n) @@ -1323,6 +1344,7 @@ { int i; GObjectClass *gclass = G_OBJECT_CLASS(klass); + char key[32]; gclass->dispose = gnt_wm_destroy; @@ -1482,10 +1504,15 @@ "\033" "\\", NULL); gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window, "\033" "|", NULL); - gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start, + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start, GNT_KEY_CTRL_G, NULL); - gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end, + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end, "\033" GNT_KEY_CTRL_G, NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next-urgent", window_next_urgent, + "\033" "\t", NULL); + snprintf(key, sizeof(key), "\033%s", GNT_KEY_BACK_TAB); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev-urgent", window_prev_urgent, + key[1] ? key : NULL, NULL); #ifdef USE_PYTHON gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "run-python", run_python, GNT_KEY_F3, NULL); diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/gntwm.h --- a/finch/libgnt/gntwm.h Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/gntwm.h Mon Jan 07 03:40:27 2008 +0000 @@ -73,7 +73,7 @@ typedef struct _GntAction { const char *label; - void (*callback)(); + void (*callback)(void); } GntAction; struct _GntWM diff -r 754a82f1371b -r 9a60b9fab0ea finch/libgnt/pygnt/test.py --- a/finch/libgnt/pygnt/test.py Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/libgnt/pygnt/test.py Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import gobject import gnt diff -r 754a82f1371b -r 9a60b9fab0ea finch/plugins/gntgf.c --- a/finch/plugins/gntgf.c Mon Dec 17 08:20:42 2007 +0000 +++ b/finch/plugins/gntgf.c Mon Jan 07 03:40:27 2008 +0000 @@ -122,7 +122,7 @@ } static void -urgent() +urgent(void) { /* This is from deryni/tuomov's urgent_test.c */ Display *dpy; @@ -322,7 +322,7 @@ } static GntWidget * -config_frame() +config_frame(void) { GntWidget *window, *tree, *check; int i; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/account.c --- a/libpurple/account.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/account.c Mon Jan 07 03:40:27 2008 +0000 @@ -331,6 +331,12 @@ if(err == NULL) return node; + /* It doesn't make sense to have transient errors persist across a + * restart. + */ + if(!purple_connection_error_is_fatal (err->type)) + return node; + child = xmlnode_new_child(node, "type"); snprintf(type_str, sizeof(type_str), "%u", err->type); xmlnode_insert_data(child, type_str, -1); @@ -461,7 +467,7 @@ } static void -schedule_accounts_save() +schedule_accounts_save(void) { if (save_timer == 0) save_timer = purple_timeout_add_seconds(5, save_cb, NULL); @@ -1639,7 +1645,7 @@ if (status == NULL) { purple_debug_error("account", - "Invalid status ID %s for account %s (%s)\n", + "Invalid status ID '%s' for account %s (%s)\n", status_id, purple_account_get_username(account), purple_account_get_protocol_id(account)); return; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/blist.c --- a/libpurple/blist.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/blist.c Mon Jan 07 03:40:27 2008 +0000 @@ -298,7 +298,7 @@ } static xmlnode * -blist_to_xmlnode() +blist_to_xmlnode(void) { xmlnode *node, *child, *grandchild; PurpleBlistNode *gnode; @@ -332,7 +332,7 @@ } static void -purple_blist_sync() +purple_blist_sync(void) { xmlnode *node; char *data; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/blist.h --- a/libpurple/blist.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/blist.h Mon Jan 07 03:40:27 2008 +0000 @@ -680,7 +680,8 @@ * * @param g The group * - * @return A list of purple_accounts + * @return A GSList of accounts (which must be freed), or NULL if the group + * has no accounts. */ GSList *purple_group_get_accounts(PurpleGroup *g); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/buddyicon.c --- a/libpurple/buddyicon.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/buddyicon.c Mon Jan 07 03:40:27 2008 +0000 @@ -98,8 +98,7 @@ { const char *dirname; char *path; - FILE *file = NULL; - + g_return_if_fail(img != NULL); if (!purple_buddy_icons_is_caching()) @@ -120,24 +119,12 @@ } } - if ((file = g_fopen(path, "wb")) != NULL) - { - if (!fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file)) - { - purple_debug_error("buddyicon", "Error writing %s: %s\n", - path, g_strerror(errno)); - } - else - purple_debug_info("buddyicon", "Wrote cache file: %s\n", path); - - fclose(file); - } - else - { + if (!g_file_test(path, G_FILE_TEST_EXISTS)) { + purple_util_write_data_to_file_absolute(path, purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + } else { purple_debug_error("buddyicon", "Unable to create file %s: %s\n", - path, g_strerror(errno)); - g_free(path); - return; + path, "File already exists."); } g_free(path); } @@ -627,6 +614,10 @@ checksum = purple_blist_node_get_string((PurpleBlistNode*)b, "icon_checksum"); purple_buddy_icon_set_data(icon, data, len, checksum); } + else + { + delete_buddy_icon_settings((PurpleBlistNode*)b, "buddy_icon"); + } g_free(path); } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/certificate.c --- a/libpurple/certificate.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/certificate.c Mon Jan 07 03:40:27 2008 +0000 @@ -627,7 +627,7 @@ /** System directory to probe for CA certificates */ /* This is set in the lazy_init function */ -static const gchar *x509_ca_syspath = NULL; +static GList *x509_ca_paths = NULL; /** A list of loaded CAs, populated from the above path whenever the lazy_init happens. Contains pointers to x509_ca_elements */ @@ -674,6 +674,7 @@ GDir *certdir; const gchar *entry; GPatternSpec *pempat; + GList *iter = NULL; if (x509_ca_initialized) return TRUE; @@ -687,54 +688,48 @@ return FALSE; } - /* Attempt to point at the appropriate system path */ - if (NULL == x509_ca_syspath) { -#ifdef _WIN32 - x509_ca_syspath = g_build_filename(DATADIR, - "ca-certs", NULL); -#else - x509_ca_syspath = g_build_filename(DATADIR, - "purple", "ca-certs", NULL); -#endif - } - - /* Populate the certificates pool from the system path */ - certdir = g_dir_open(x509_ca_syspath, 0, NULL); - g_return_val_if_fail(certdir, FALSE); - /* Use a glob to only read .pem files */ pempat = g_pattern_spec_new("*.pem"); - - while ( (entry = g_dir_read_name(certdir)) ) { - gchar *fullpath; - PurpleCertificate *crt; - if ( !g_pattern_match_string(pempat, entry) ) { + /* Populate the certificates pool from the search path(s) */ + for (iter = x509_ca_paths; iter; iter = iter->next) { + certdir = g_dir_open(iter->data, 0, NULL); + if (!certdir) { + purple_debug_error("certificate/x509/ca", "Couldn't open location '%s'\n", iter->data); continue; } - fullpath = g_build_filename(x509_ca_syspath, entry, NULL); - - /* TODO: Respond to a failure in the following? */ - crt = purple_certificate_import(x509, fullpath); + while ( (entry = g_dir_read_name(certdir)) ) { + gchar *fullpath; + PurpleCertificate *crt; + + if ( !g_pattern_match_string(pempat, entry) ) { + continue; + } + + fullpath = g_build_filename(iter->data, entry, NULL); + + /* TODO: Respond to a failure in the following? */ + crt = purple_certificate_import(x509, fullpath); - if (x509_ca_quiet_put_cert(crt)) { - purple_debug_info("certificate/x509/ca", - "Loaded %s\n", - fullpath); - } else { - purple_debug_error("certificate/x509/ca", - "Failed to load %s\n", - fullpath); + if (x509_ca_quiet_put_cert(crt)) { + purple_debug_info("certificate/x509/ca", + "Loaded %s\n", + fullpath); + } else { + purple_debug_error("certificate/x509/ca", + "Failed to load %s\n", + fullpath); + } + + purple_certificate_destroy(crt); + g_free(fullpath); } - - purple_certificate_destroy(crt); - g_free(fullpath); + g_dir_close(certdir); } g_pattern_spec_free(pempat); - g_dir_close(certdir); - + purple_debug_info("certificate/x509/ca", "Lazy init completed.\n"); x509_ca_initialized = TRUE; @@ -744,6 +739,17 @@ static gboolean x509_ca_init(void) { + /* Attempt to point at the appropriate system path */ + if (NULL == x509_ca_paths) { +#ifdef _WIN32 + x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR, + "ca-certs", NULL)); +#else + x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR, + "purple", "ca-certs", NULL)); +#endif + } + /* Attempt to initialize now, but if it doesn't work, that's OK; it will get done later */ if ( ! x509_ca_lazy_init()) { @@ -752,7 +758,7 @@ "dependency is not yet registered. " "It has been deferred to later.\n"); } - + return TRUE; } @@ -768,6 +774,9 @@ g_list_free(x509_ca_certs); x509_ca_certs = NULL; x509_ca_initialized = FALSE; + g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL); + g_list_free(x509_ca_paths); + x509_ca_paths = NULL; } /** Look up a ca_element by dn */ @@ -1219,6 +1228,9 @@ } static void +x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq); + +static void x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq) { /* TODO: Looking this up by name over and over is expensive. @@ -1259,8 +1271,8 @@ } else { purple_debug_info("certificate/x509/tls_cached", "Peer cert did NOT match cached\n"); - /* vrq now becomes the problem of cert_changed */ - x509_tls_cached_peer_cert_changed(vrq); + /* vrq now becomes the problem of the user */ + x509_tls_cached_unknown_peer(vrq); } purple_certificate_destroy(cached_crt); @@ -1271,7 +1283,9 @@ /* For when we've never communicated with this party before */ /* TODO: Need ways to specify possibly multiple problems with a cert, or at least reprioritize them. For example, maybe the signature ought to be - checked BEFORE the hostname checking? */ + checked BEFORE the hostname checking? + Stu thinks we should check the signature before the name, so we do now. + The above TODO still stands. */ static void x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq) { @@ -1283,35 +1297,6 @@ peer_crt = (PurpleCertificate *) chain->data; - /* First, check that the hostname matches */ - if ( ! purple_certificate_check_subject_name(peer_crt, - vrq->subject_name) ) { - gchar *sn = purple_certificate_get_subject_name(peer_crt); - gchar *msg; - - purple_debug_info("certificate/x509/tls_cached", - "Name mismatch: Certificate given for %s " - "has a name of %s\n", - vrq->subject_name, sn); - - /* Prompt the user to authenticate the certificate */ - /* TODO: Provide the user with more guidance about why he is - being prompted */ - /* vrq will be completed by user_auth */ - msg = g_strdup_printf(_("The certificate presented by \"%s\" " - "claims to be from \"%s\" instead. " - "This could mean that you are not " - "connecting to the service you " - "believe you are."), - vrq->subject_name, sn); - - x509_tls_cached_user_auth(vrq,msg); - - g_free(sn); - g_free(msg); - return; - } /* if (name mismatch) */ - /* TODO: Figure out a way to check for a bad signature, as opposed to "not self-signed" */ if ( purple_certificate_signed_by(peer_crt, peer_crt) ) { @@ -1332,7 +1317,7 @@ g_free(msg); return; - } /* if (name mismatch) */ + } /* if (self signed) */ /* Next, check that the certificate chain is valid */ if ( ! purple_certificate_check_signature_chain(chain) ) { @@ -1431,6 +1416,35 @@ return; } /* if (CA signature not good) */ + /* Last, check that the hostname matches */ + if ( ! purple_certificate_check_subject_name(peer_crt, + vrq->subject_name) ) { + gchar *sn = purple_certificate_get_subject_name(peer_crt); + gchar *msg; + + purple_debug_info("certificate/x509/tls_cached", + "Name mismatch: Certificate given for %s " + "has a name of %s\n", + vrq->subject_name, sn); + + /* Prompt the user to authenticate the certificate */ + /* TODO: Provide the user with more guidance about why he is + being prompted */ + /* vrq will be completed by user_auth */ + msg = g_strdup_printf(_("The certificate presented by \"%s\" " + "claims to be from \"%s\" instead. " + "This could mean that you are not " + "connecting to the service you " + "believe you are."), + vrq->subject_name, sn); + + x509_tls_cached_user_auth(vrq,msg); + + g_free(sn); + g_free(msg); + return; + } /* if (name mismatch) */ + /* If we reach this point, the certificate is good. */ /* Look up the local cache and store it there for future use */ tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name, @@ -1901,3 +1915,10 @@ g_byte_array_free(sha_bin, TRUE); } +void purple_certificate_add_ca_search_path(const char *path) +{ + if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp)) + return; + x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path)); +} + diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/certificate.h --- a/libpurple/certificate.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/certificate.h Mon Jan 07 03:40:27 2008 +0000 @@ -786,6 +786,12 @@ void purple_certificate_display_x509(PurpleCertificate *crt); +/** + * Add a search path for certificates. + * + * @param path Path to search for certificates. + */ +void purple_certificate_add_ca_search_path(const char *path); #ifdef __cplusplus } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/cipher.c --- a/libpurple/cipher.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/cipher.c Mon Jan 07 03:40:27 2008 +0000 @@ -64,6 +64,8 @@ /******************************************************************************* * MD5 ******************************************************************************/ +#define MD5_HMAC_BLOCK_SIZE 64 + struct MD5Context { guint32 total[2]; guint32 state[4]; @@ -325,6 +327,13 @@ return TRUE; } +static size_t +md5_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return MD5_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps MD5Ops = { NULL, /* Set option */ NULL, /* Get option */ @@ -340,12 +349,10 @@ NULL, /* get salt size */ NULL, /* set key */ NULL, /* get key size */ - - /* padding */ - NULL, - NULL, - NULL, - NULL + NULL, /* set batch mode */ + NULL, /* get batch mode */ + md5_get_block_size, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -580,6 +587,13 @@ md4_context = NULL; } +static size_t +md4_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return MD4_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps MD4Ops = { NULL, /* Set option */ NULL, /* Get option */ @@ -595,12 +609,202 @@ NULL, /* get salt size */ NULL, /* set key */ NULL, /* get key size */ - - /* padding */ - NULL, - NULL, - NULL, - NULL + NULL, /* set batch mode */ + NULL, /* get batch mode */ + md4_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +/******************************************************************************* + * HMAC + ******************************************************************************/ + +struct HMAC_Context { + PurpleCipherContext *hash; + char *name; + int blocksize; + guchar *opad; +}; + +static void +hmac_init(PurpleCipherContext *context, gpointer extra) +{ + struct HMAC_Context *hctx; + hctx = g_new0(struct HMAC_Context, 1); + purple_cipher_context_set_data(context, hctx); + purple_cipher_context_reset(context, extra); +} + +static void +hmac_reset(PurpleCipherContext *context, gpointer extra) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + g_free(hctx->name); + hctx->name = NULL; + if (hctx->hash) + purple_cipher_context_destroy(hctx->hash); + hctx->hash = NULL; + hctx->blocksize = 0; + g_free(hctx->opad); + hctx->opad = NULL; +} + +static void +hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + if (!strcmp(name, "hash")) { + g_free(hctx->name); + if (hctx->hash) + purple_cipher_context_destroy(hctx->hash); + hctx->name = g_strdup((char*)value); + hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL); + hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash); + } +} + +static void * +hmac_get_opt(PurpleCipherContext *context, const gchar *name) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + if (!strcmp(name, "hash")) { + return hctx->name; + } + + return NULL; +} + +static void +hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + + g_return_if_fail(hctx->hash != NULL); + + purple_cipher_context_append(hctx->hash, data, len); +} + +static gboolean +hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + PurpleCipherContext *hash = hctx->hash; + guchar *inner_hash; + size_t hash_len; + gboolean result; + + g_return_val_if_fail(hash != NULL, FALSE); + + inner_hash = g_malloc(100); /* TODO: Should be enough for now... */ + result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len); + + purple_cipher_context_reset(hash, NULL); + + purple_cipher_context_append(hash, hctx->opad, hctx->blocksize); + purple_cipher_context_append(hash, inner_hash, hash_len); + + g_free(inner_hash); + + result = result && purple_cipher_context_digest(hash, in_len, out, out_len); + + return result; +} + +static void +hmac_uninit(PurpleCipherContext *context) +{ + struct HMAC_Context *hctx; + + purple_cipher_context_reset(context, NULL); + + hctx = purple_cipher_context_get_data(context); + + g_free(hctx); +} + +static void +hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + int blocksize, i; + guchar *ipad; + guchar *full_key; + + g_return_if_fail(hctx->hash != NULL); + + g_free(hctx->opad); + + blocksize = hctx->blocksize; + ipad = g_malloc(blocksize); + hctx->opad = g_malloc(blocksize); + + if (key_len > blocksize) { + purple_cipher_context_reset(hctx->hash, NULL); + purple_cipher_context_append(hctx->hash, key, key_len); + full_key = g_malloc(100); /* TODO: Should be enough for now... */ + purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len); + } else + full_key = g_memdup(key, key_len); + + if (key_len < blocksize) { + full_key = g_realloc(full_key, blocksize); + memset(full_key + key_len, 0, blocksize - key_len); + } + + for(i = 0; i < blocksize; i++) { + ipad[i] = 0x36 ^ full_key[i]; + hctx->opad[i] = 0x5c ^ full_key[i]; + } + + g_free(full_key); + + purple_cipher_context_reset(hctx->hash, NULL); + purple_cipher_context_append(hctx->hash, ipad, blocksize); + g_free(ipad); +} + +static void +hmac_set_key(PurpleCipherContext *context, const guchar * key) +{ + hmac_set_key_with_len(context, key, strlen((char *)key)); +} + +static size_t +hmac_get_block_size(PurpleCipherContext *context) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + + return hctx->blocksize; +} + +static PurpleCipherOps HMACOps = { + hmac_set_opt, /* Set option */ + hmac_get_opt, /* Get option */ + hmac_init, /* init */ + hmac_reset, /* reset */ + hmac_uninit, /* uninit */ + NULL, /* set iv */ + hmac_append, /* append */ + hmac_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + hmac_set_key, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + hmac_get_block_size, /* get block size */ + hmac_set_key_with_len /* set key with len */ }; /****************************************************************************** @@ -986,6 +1190,36 @@ return 0; } +static gint +des_decrypt(PurpleCipherContext *context, const guchar data[], + size_t len, guchar output[], size_t *outlen) { + int offset = 0; + int i = 0; + int tmp; + guint8 buf[8] = {0,0,0,0,0,0,0,0}; + while(offset+8<=len) { + des_ecb_crypt(purple_cipher_context_get_data(context), + data+offset, + output+offset, + 1); + offset+=8; + } + *outlen = len; + if(offsetkey1.encrypt_subkeys); + des_key_schedule (key + 8, ctx->key2.encrypt_subkeys); + des_key_schedule (key + 16, ctx->key3.encrypt_subkeys); + + for (i = 0; i < 32; i += 2) + { + ctx->key1.decrypt_subkeys[i] = ctx->key1.encrypt_subkeys[30-i]; + ctx->key1.decrypt_subkeys[i+1] = ctx->key1.encrypt_subkeys[31-i]; + ctx->key2.decrypt_subkeys[i] = ctx->key2.encrypt_subkeys[30-i]; + ctx->key2.decrypt_subkeys[i+1] = ctx->key2.encrypt_subkeys[31-i]; + ctx->key3.decrypt_subkeys[i] = ctx->key3.encrypt_subkeys[30-i]; + ctx->key3.decrypt_subkeys[i+1] = ctx->key3.encrypt_subkeys[31-i]; + } +} + +static gint +des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + int offset = 0; + int i = 0; + int tmp; + guint8 buf[8] = {0,0,0,0,0,0,0,0}; + while (offset + 8 <= len) { + des_ecb_crypt(&ctx->key1, + data+offset, + output+offset, + 0); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 1); + des_ecb_crypt(&ctx->key3, + buf, + output+offset, + 0); + offset += 8; + } + *outlen = len; + if (offset < len) { + *outlen += len - offset; + tmp = offset; + memset(buf, 0, 8); + while (tmp < len) { + buf[i++] = data[tmp]; + tmp++; + } + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 0); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 1); + des_ecb_crypt(&ctx->key3, + buf, + output+offset, + 0); + } + return 0; +} + +static gint +des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + int offset = 0; + int i = 0; + int tmp; + guint8 buf[8]; + memcpy(buf, ctx->iv, 8); + while (offset + 8 <= len) { + for (i = 0; i < 8; i++) + buf[i] ^= data[offset + i]; + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 0); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 1); + des_ecb_crypt(&ctx->key3, + buf, + output+offset, + 0); + memcpy(buf, output+offset, 8); + offset += 8; + } + *outlen = len; + if (offset < len) { + *outlen += len - offset; + tmp = offset; + i = 0; + while (tmp < len) { + buf[i++] ^= data[tmp]; + tmp++; + } + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 0); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 1); + des_ecb_crypt(&ctx->key3, + buf, + output+offset, + 0); + } + return 0; +} + +static gint +des3_encrypt(PurpleCipherContext *context, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + struct _des3_ctx *ctx = purple_cipher_context_get_data(context); + + if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) { + return des3_ecb_encrypt(ctx, data, len, output, outlen); + } else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) { + return des3_cbc_encrypt(ctx, data, len, output, outlen); + } else { + g_return_val_if_reached(0); + } + + return 0; +} + +static gint +des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + int offset = 0; + int i = 0; + int tmp; + guint8 buf[8] = {0,0,0,0,0,0,0,0}; + while (offset + 8 <= len) { + /* NOTE: Apply key in reverse */ + des_ecb_crypt(&ctx->key3, + data+offset, + output+offset, + 1); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 0); + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 1); + offset+=8; + } + *outlen = len; + if (offset < len) { + *outlen += len - offset; + tmp = offset; + memset(buf, 0, 8); + while (tmp < len) { + buf[i++] = data[tmp]; + tmp++; + } + des_ecb_crypt(&ctx->key3, + buf, + output+offset, + 1); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 0); + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 1); + } + return 0; +} + +static gint +des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + int offset = 0; + int i = 0; + int tmp; + guint8 buf[8] = {0,0,0,0,0,0,0,0}; + guint8 link[8]; + memcpy(link, ctx->iv, 8); + while (offset + 8 <= len) { + des_ecb_crypt(&ctx->key3, + data+offset, + output+offset, + 1); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 0); + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 1); + for (i = 0; i < 8; i++) + output[offset + i] ^= link[i]; + memcpy(link, data + offset, 8); + offset+=8; + } + *outlen = len; + if(offsetkey3, + buf, + output+offset, + 1); + des_ecb_crypt(&ctx->key2, + output+offset, + buf, + 0); + des_ecb_crypt(&ctx->key1, + buf, + output+offset, + 1); + for (i = 0; i < 8; i++) + output[offset + i] ^= link[i]; + } + return 0; +} + +static gint +des3_decrypt(PurpleCipherContext *context, const guchar data[], + size_t len, guchar output[], size_t *outlen) +{ + struct _des3_ctx *ctx = purple_cipher_context_get_data(context); + + if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) { + return des3_ecb_decrypt(ctx, data, len, output, outlen); + } else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) { + return des3_cbc_decrypt(ctx, data, len, output, outlen); + } else { + g_return_val_if_reached(0); + } + + return 0; +} + +static void +des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode) +{ + struct _des3_ctx *ctx = purple_cipher_context_get_data(context); + + ctx->mode = mode; +} + +static PurpleCipherBatchMode +des3_get_batch(PurpleCipherContext *context) +{ + struct _des3_ctx *ctx = purple_cipher_context_get_data(context); + + return ctx->mode; +} + +static void +des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len) +{ + struct _des3_ctx *ctx; + + g_return_if_fail(len == 8); + + ctx = purple_cipher_context_get_data(context); + + memcpy(ctx->iv, iv, len); +} + +static void +des3_init(PurpleCipherContext *context, gpointer extra) +{ + struct _des3_ctx *mctx; + mctx = g_new0(struct _des3_ctx, 1); + purple_cipher_context_set_data(context, mctx); +} + +static void +des3_uninit(PurpleCipherContext *context) +{ + struct _des3_ctx *des3_context; + + des3_context = purple_cipher_context_get_data(context); + memset(des3_context, 0, sizeof(des3_context)); + + g_free(des3_context); + des3_context = NULL; +} + +static PurpleCipherOps DES3Ops = { + NULL, /* Set option */ + NULL, /* Get option */ + des3_init, /* init */ NULL, /* reset */ - des_uninit, /* uninit */ - NULL, /* set iv */ - NULL, /* append */ - NULL, /* digest */ - des_encrypt, /* encrypt */ - NULL, /* decrypt */ - NULL, /* set salt */ - NULL, /* get salt size */ - des_set_key, /* set key */ - NULL, /* get key size */ - - /* padding */ - NULL, - NULL, - NULL, - NULL + des3_uninit, /* uninit */ + des3_set_iv, /* set iv */ + NULL, /* append */ + NULL, /* digest */ + des3_encrypt, /* encrypt */ + des3_decrypt, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + des3_set_key, /* set key */ + NULL, /* get key size */ + des3_set_batch, /* set batch mode */ + des3_get_batch, /* get batch mode */ + NULL, /* get block size */ + NULL /* set key with len */ }; - /******************************************************************************* * SHA-1 ******************************************************************************/ +#define SHA1_HMAC_BLOCK_SIZE 64 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF) struct SHA1Context { @@ -1251,6 +1833,13 @@ return TRUE; } +static size_t +sha1_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return SHA1_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps SHA1Ops = { sha1_set_opt, /* Set Option */ sha1_get_opt, /* Get Option */ @@ -1266,12 +1855,10 @@ NULL, /* get salt size */ NULL, /* set key */ NULL, /* get key size */ - - /* padding */ - NULL, - NULL, - NULL, - NULL + NULL, /* set batch mode */ + NULL, /* get batch mode */ + sha1_get_block_size, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -1435,12 +2022,10 @@ NULL, /* get salt size */ rc4_set_key, /* set key */ rc4_get_key_size, /* get key size */ - - /* padding */ - NULL, - NULL, - NULL, - NULL + NULL, /* set batch mode */ + NULL, /* get batch mode */ + NULL, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -1510,6 +2095,14 @@ caps |= PURPLE_CIPHER_CAPS_SET_KEY; if(ops->get_key_size) caps |= PURPLE_CIPHER_CAPS_GET_KEY_SIZE; + if(ops->set_batch_mode) + caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE; + if(ops->get_batch_mode) + caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE; + if(ops->get_block_size) + caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE; + if(ops->set_key_with_len) + caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN; return caps; } @@ -1636,7 +2229,9 @@ purple_ciphers_register_cipher("md5", &MD5Ops); purple_ciphers_register_cipher("sha1", &SHA1Ops); purple_ciphers_register_cipher("md4", &MD4Ops); + purple_ciphers_register_cipher("hmac", &HMACOps); purple_ciphers_register_cipher("des", &DESOps); + purple_ciphers_register_cipher("des3", &DES3Ops); purple_ciphers_register_cipher("rc4", &RC4Ops); } @@ -1967,6 +2562,80 @@ } void +purple_cipher_context_set_batch_mode(PurpleCipherContext *context, + PurpleCipherBatchMode mode) +{ + PurpleCipher *cipher = NULL; + + g_return_if_fail(context); + + cipher = context->cipher; + g_return_if_fail(cipher); + + if(cipher->ops && cipher->ops->set_batch_mode) + cipher->ops->set_batch_mode(context, mode); + else + purple_debug_info("cipher", "The %s cipher does not support the " + "set_batch_mode operation\n", cipher->name); +} + +PurpleCipherBatchMode +purple_cipher_context_get_batch_mode(PurpleCipherContext *context) +{ + PurpleCipher *cipher = NULL; + + g_return_val_if_fail(context, -1); + + cipher = context->cipher; + g_return_val_if_fail(cipher, -1); + + if(cipher->ops && cipher->ops->get_batch_mode) + return cipher->ops->get_batch_mode(context); + else { + purple_debug_info("cipher", "The %s cipher does not support the " + "get_batch_mode operation\n", cipher->name); + return -1; + } +} + +size_t +purple_cipher_context_get_block_size(PurpleCipherContext *context) +{ + PurpleCipher *cipher = NULL; + + g_return_val_if_fail(context, -1); + + cipher = context->cipher; + g_return_val_if_fail(cipher, -1); + + if(cipher->ops && cipher->ops->get_block_size) + return cipher->ops->get_block_size(context); + else { + purple_debug_info("cipher", "The %s cipher does not support the " + "get_block_size operation\n", cipher->name); + return -1; + } +} + +void +purple_cipher_context_set_key_with_len(PurpleCipherContext *context, + const guchar *key, size_t len) +{ + PurpleCipher *cipher = NULL; + + g_return_if_fail(context); + + cipher = context->cipher; + g_return_if_fail(cipher); + + if(cipher->ops && cipher->ops->set_key_with_len) + cipher->ops->set_key_with_len(context, key, len); + else + purple_debug_info("cipher", "The %s cipher does not support the " + "set_key_with_len operation\n", cipher->name); +} + +void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) { g_return_if_fail(context); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/cipher.h --- a/libpurple/cipher.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/cipher.h Mon Jan 07 03:40:27 2008 +0000 @@ -37,26 +37,37 @@ typedef struct _PurpleCipherOps PurpleCipherOps; /**< Ops for a PurpleCipher */ typedef struct _PurpleCipherContext PurpleCipherContext; /**< A context for a PurpleCipher */ +/** + * Modes for batch encrypters + */ +typedef enum _PurpleCipherBatchMode { + PURPLE_CIPHER_BATCH_MODE_ECB, + PURPLE_CIPHER_BATCH_MODE_CBC +} PurpleCipherBatchMode; /** * The operation flags for a cipher */ typedef enum _PurpleCipherCaps { - PURPLE_CIPHER_CAPS_SET_OPT = 1 << 1, /**< Set option flag */ - PURPLE_CIPHER_CAPS_GET_OPT = 1 << 2, /**< Get option flag */ - PURPLE_CIPHER_CAPS_INIT = 1 << 3, /**< Init flag */ - PURPLE_CIPHER_CAPS_RESET = 1 << 4, /**< Reset flag */ - PURPLE_CIPHER_CAPS_UNINIT = 1 << 5, /**< Uninit flag */ - PURPLE_CIPHER_CAPS_SET_IV = 1 << 6, /**< Set IV flag */ - PURPLE_CIPHER_CAPS_APPEND = 1 << 7, /**< Append flag */ - PURPLE_CIPHER_CAPS_DIGEST = 1 << 8, /**< Digest flag */ - PURPLE_CIPHER_CAPS_ENCRYPT = 1 << 9, /**< Encrypt flag */ - PURPLE_CIPHER_CAPS_DECRYPT = 1 << 10, /**< Decrypt flag */ - PURPLE_CIPHER_CAPS_SET_SALT = 1 << 11, /**< Set salt flag */ - PURPLE_CIPHER_CAPS_GET_SALT_SIZE = 1 << 12, /**< Get salt size flag */ - PURPLE_CIPHER_CAPS_SET_KEY = 1 << 13, /**< Set key flag */ - PURPLE_CIPHER_CAPS_GET_KEY_SIZE = 1 << 14, /**< Get key size flag */ - PURPLE_CIPHER_CAPS_UNKNOWN = 1 << 16 /**< Unknown */ + PURPLE_CIPHER_CAPS_SET_OPT = 1 << 1, /**< Set option flag */ + PURPLE_CIPHER_CAPS_GET_OPT = 1 << 2, /**< Get option flag */ + PURPLE_CIPHER_CAPS_INIT = 1 << 3, /**< Init flag */ + PURPLE_CIPHER_CAPS_RESET = 1 << 4, /**< Reset flag */ + PURPLE_CIPHER_CAPS_UNINIT = 1 << 5, /**< Uninit flag */ + PURPLE_CIPHER_CAPS_SET_IV = 1 << 6, /**< Set IV flag */ + PURPLE_CIPHER_CAPS_APPEND = 1 << 7, /**< Append flag */ + PURPLE_CIPHER_CAPS_DIGEST = 1 << 8, /**< Digest flag */ + PURPLE_CIPHER_CAPS_ENCRYPT = 1 << 9, /**< Encrypt flag */ + PURPLE_CIPHER_CAPS_DECRYPT = 1 << 10, /**< Decrypt flag */ + PURPLE_CIPHER_CAPS_SET_SALT = 1 << 11, /**< Set salt flag */ + PURPLE_CIPHER_CAPS_GET_SALT_SIZE = 1 << 12, /**< Get salt size flag */ + PURPLE_CIPHER_CAPS_SET_KEY = 1 << 13, /**< Set key flag */ + PURPLE_CIPHER_CAPS_GET_KEY_SIZE = 1 << 14, /**< Get key size flag */ + PURPLE_CIPHER_CAPS_SET_BATCH_MODE = 1 << 15, /**< Set batch mode flag */ + PURPLE_CIPHER_CAPS_GET_BATCH_MODE = 1 << 16, /**< Get batch mode flag */ + PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE = 1 << 17, /**< The get block size flag */ + PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN = 1 << 18, /**< The set key with length flag */ + PURPLE_CIPHER_CAPS_UNKNOWN = 1 << 19 /**< Unknown */ } PurpleCipherCaps; /** @@ -105,10 +116,17 @@ /** The get key size function */ size_t (*get_key_size)(PurpleCipherContext *context); - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); + /** The set batch mode function */ + void (*set_batch_mode)(PurpleCipherContext *context, PurpleCipherBatchMode mode); + + /** The get batch mode function */ + PurpleCipherBatchMode (*get_batch_mode)(PurpleCipherContext *context); + + /** The get block size function */ + size_t (*get_block_size)(PurpleCipherContext *context); + + /** The set key with length function */ + void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len); }; #ifdef __cplusplus @@ -345,7 +363,7 @@ /** * Sets the salt on a context * - * @param context The context who's salt to set + * @param context The context whose salt to set * @param salt The salt */ void purple_cipher_context_set_salt(PurpleCipherContext *context, guchar *salt); @@ -353,7 +371,7 @@ /** * Gets the size of the salt if the cipher supports it * - * @param context The context who's salt size to get + * @param context The context whose salt size to get * * @return The size of the salt */ @@ -362,7 +380,7 @@ /** * Sets the key on a context * - * @param context The context who's key to set + * @param context The context whose key to set * @param key The key */ void purple_cipher_context_set_key(PurpleCipherContext *context, const guchar *key); @@ -370,16 +388,53 @@ /** * Gets the key size for a context * - * @param context The context who's key size to get + * @param context The context whose key size to get * * @return The size of the key */ size_t purple_cipher_context_get_key_size(PurpleCipherContext *context); /** + * Sets the batch mode of a context + * + * @param context The context whose batch mode to set + * @param mode The batch mode under which the cipher should operate + * + */ +void purple_cipher_context_set_batch_mode(PurpleCipherContext *context, PurpleCipherBatchMode mode); + +/** + * Gets the batch mode of a context + * + * @param context The context whose batch mode to get + * + * @return The batch mode under which the cipher is operating + */ +PurpleCipherBatchMode purple_cipher_context_get_batch_mode(PurpleCipherContext *context); + +/** + * Gets the block size of a context + * + * @param context The context whose block size to get + * + * @return The block size of the context + */ +size_t purple_cipher_context_get_block_size(PurpleCipherContext *context); + +/** + * Sets the key with a given length on a context + * + * @param context The context whose key to set + * @param key The key + * @param len The length of the key + * + */ +void purple_cipher_context_set_key_with_len(PurpleCipherContext *context, const guchar *key, size_t len); + +/** * Sets the cipher data for a context * - * @param context The context who's cipher data to set + * @param context The context whose cipher data to set * @param data The cipher data to set */ void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data); @@ -387,7 +442,7 @@ /** * Gets the cipher data for a context * - * @param context The context who's cipher data to get + * @param context The context whose cipher data to get * * @return The cipher data */ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/cmds.h --- a/libpurple/cmds.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/cmds.h Mon Jan 07 03:40:27 2008 +0000 @@ -30,25 +30,20 @@ /**************************************************************************/ /*@{*/ -typedef enum _PurpleCmdPriority PurpleCmdPriority; -typedef enum _PurpleCmdFlag PurpleCmdFlag; -typedef enum _PurpleCmdStatus PurpleCmdStatus; -typedef enum _PurpleCmdRet PurpleCmdRet; - -enum _PurpleCmdStatus { +typedef enum _PurpleCmdStatus { PURPLE_CMD_STATUS_OK, PURPLE_CMD_STATUS_FAILED, PURPLE_CMD_STATUS_NOT_FOUND, PURPLE_CMD_STATUS_WRONG_ARGS, PURPLE_CMD_STATUS_WRONG_PRPL, PURPLE_CMD_STATUS_WRONG_TYPE, -}; +} PurpleCmdStatus; -enum _PurpleCmdRet { +typedef enum _PurpleCmdRet { PURPLE_CMD_RET_OK, /**< Everything's okay. Don't look for another command to call. */ PURPLE_CMD_RET_FAILED, /**< The command failed, but stop looking.*/ PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */ -}; +} PurpleCmdRet; #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func) @@ -56,7 +51,7 @@ gchar **args, gchar **error, void *data); typedef guint PurpleCmdId; -enum _PurpleCmdPriority { +typedef enum _PurpleCmdPriority { PURPLE_CMD_P_VERY_LOW = -1000, PURPLE_CMD_P_LOW = 0, PURPLE_CMD_P_DEFAULT = 1000, @@ -65,7 +60,7 @@ PURPLE_CMD_P_ALIAS = 4000, PURPLE_CMD_P_HIGH = 5000, PURPLE_CMD_P_VERY_HIGH = 6000, -}; +} PurpleCmdPriority; /** Flags used to set various properties of commands. Every command should * have at least one of #PURPLE_CMD_FLAG_IM and #PURPLE_CMD_FLAG_CHAT set in @@ -73,7 +68,7 @@ * * @see purple_cmd_register */ -enum _PurpleCmdFlag { +typedef enum _PurpleCmdFlag { /** Command is usable in IMs. */ PURPLE_CMD_FLAG_IM = 0x01, /** Command is usable in multi-user chats. */ @@ -82,7 +77,7 @@ PURPLE_CMD_FLAG_PRPL_ONLY = 0x04, /** Incorrect arguments to this command should be accepted anyway. */ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08, -}; +} PurpleCmdFlag; /*@}*/ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/connection.h --- a/libpurple/connection.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/connection.h Mon Jan 07 03:40:27 2008 +0000 @@ -196,11 +196,11 @@ * available; on Windows, it uses Win32's network change notification * infrastructure. */ - void (*network_connected)(); + void (*network_connected)(void); /** Called when libpurple discovers that the computer's network * connection has gone away. */ - void (*network_disconnected)(); + void (*network_disconnected)(void); /** Called when an error causes a connection to be disconnected. * Called before #disconnected. This op is intended to replace diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/conversation.h --- a/libpurple/conversation.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/conversation.h Mon Jan 07 03:40:27 2008 +0000 @@ -490,7 +490,8 @@ * * @param conv The conversation. * - * @return The conversation's name. + * @return The conversation's name. If the conversation is an IM with a PurpleBuddy, + * then it's the name of the PurpleBuddy. */ const char *purple_conversation_get_name(const PurpleConversation *conv); @@ -718,7 +719,7 @@ * * @param msg A PurpleConvMessage * - * @return The name of the sender of the message + * @return The message flags * * @since 2.2.0 */ @@ -729,7 +730,7 @@ * * @param msg A PurpleConvMessage * - * @return The name of the sender of the message + * @return The timestamp of the message * * @since 2.2.0 */ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/core.h --- a/libpurple/core.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/core.h Mon Jan 07 03:40:27 2008 +0000 @@ -67,13 +67,14 @@ * Calls purple_core_quit(). This can be used as the function * passed to purple_timeout_add() when you want to shutdown Purple * in a specified amount of time. When shutting down Purple - * from a plugin, you must use this with a timeout value of 0: + * from a plugin, you must use this instead of purple_core_quit(); + * for an immediate exit, use a timeout value of 0: * purple_timeout_add(0, purple_core_quitcb, NULL); * This is ensures that code from your plugin is not being - * executed when purple_core_quit() is called. Otherwise you - * would get a core dump after purple_core_quit() executes and - * control returns to your plugin because purple_core_quit() frees - * all plugins. + * executed when purple_core_quit() is called. If the plugin + * called purple_core_quit() directly, you would get a core dump + * after purple_core_quit() executes and control returns to your + * plugin because purple_core_quit() frees all plugins. */ gboolean purple_core_quit_cb(gpointer unused); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/dnsquery.c --- a/libpurple/dnsquery.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/dnsquery.c Mon Jan 07 03:40:27 2008 +0000 @@ -142,7 +142,7 @@ */ #ifdef HAVE_SIGNAL_H G_GNUC_NORETURN static void -trap_gdb_bug() +trap_gdb_bug(int sig) { const char *message = "Purple's DNS child got a SIGTRAP signal.\n" @@ -286,7 +286,7 @@ * Begin the functions for dealing with the DNS child processes. */ static void -cope_with_gdb_brokenness() +cope_with_gdb_brokenness(void) { #ifdef __linux__ static gboolean already_done = FALSE; @@ -460,7 +460,7 @@ static void host_resolved(gpointer data, gint source, PurpleInputCondition cond); static void -handle_next_queued_request() +handle_next_queued_request(void) { PurpleDnsQueryData *query_data; PurpleDnsQueryResolverProcess *resolver; @@ -547,7 +547,7 @@ { #ifdef HAVE_GETADDRINFO g_snprintf(message, sizeof(message), _("Error resolving %s:\n%s"), - query_data->hostname, gai_strerror(err)); + query_data->hostname, purple_gai_strerror(err)); #else g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), query_data->hostname, err); @@ -695,7 +695,7 @@ } freeaddrinfo(tmp); } else { - query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, gai_strerror(rc)); + query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, purple_gai_strerror(rc)); } #else if ((hp = gethostbyname(query_data->hostname))) { diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/example/nullclient.c --- a/libpurple/example/nullclient.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/example/nullclient.c Mon Jan 07 03:40:27 2008 +0000 @@ -167,7 +167,7 @@ }; static void -null_ui_init() +null_ui_init(void) { /** * This should initialize the UI components for all the modules. Here we @@ -191,7 +191,7 @@ }; static void -init_libpurple() +init_libpurple(void) { /* Set a custom user directory (optional) */ purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); @@ -250,14 +250,14 @@ } static void -connect_to_signals_for_demonstration_purposes_only() +connect_to_signals_for_demonstration_purposes_only(void) { static int handle; purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signed_on), NULL); } -int main() +int main(int argc, char *argv[]) { GList *iter; int i, num; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/ft.c --- a/libpurple/ft.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/ft.c Mon Jan 07 03:40:27 2008 +0000 @@ -479,10 +479,11 @@ /* Sending a file */ /* Check the filename. */ #ifdef _WIN32 - if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) { + if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) #else - if (g_strrstr(filename, "../")) { + if (g_strrstr(filename, "../")) #endif + { char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/idle.c --- a/libpurple/idle.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/idle.c Mon Jan 07 03:40:27 2008 +0000 @@ -215,8 +215,8 @@ /* * Check idle and set the timer to fire at the next idle-worth event */ -static gint -check_idleness_timer() +static gboolean +check_idleness_timer(void) { check_idleness(); if (time_until_next_idle_event == 0) @@ -225,7 +225,7 @@ { /* +1 for the boundary, * +1 more for g_timeout_add_seconds rounding. */ - idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, check_idleness_timer, NULL); + idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, (GSourceFunc)check_idleness_timer, NULL); } return FALSE; } @@ -295,7 +295,7 @@ } static void * -purple_idle_get_handle() +purple_idle_get_handle(void) { static int handle; @@ -307,7 +307,7 @@ int idle_poll_minutes = purple_prefs_get_int("/purple/away/mins_before_away"); /* +1 more for g_timeout_add_seconds rounding. */ - idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, check_idleness_timer, NULL); + idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, (GSourceFunc)check_idleness_timer, NULL); purple_idle_touch(); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/log.c --- a/libpurple/log.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/log.c Mon Jan 07 03:40:27 2008 +0000 @@ -348,7 +348,6 @@ void(*get_log_sets)(PurpleLogSetCallback cb, GHashTable *sets), gboolean(*remove)(PurpleLog *log), gboolean(*is_deletable)(PurpleLog *log)) -{ #endif PurpleLogLogger *logger; va_list args; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/network.c --- a/libpurple/network.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/network.c Mon Jan 07 03:40:27 2008 +0000 @@ -289,7 +289,7 @@ errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res); if (errnum != 0) { #ifndef _WIN32 - purple_debug_warning("network", "getaddrinfo: %s\n", gai_strerror(errnum)); + purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum)); if (errnum == EAI_SYSTEM) purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno)); #else diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugin.c --- a/libpurple/plugin.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugin.c Mon Jan 07 03:40:27 2008 +0000 @@ -667,7 +667,10 @@ } else { +#if 0 + /* This isn't necessary. This has already been done when unloading dep_plugin. */ plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l); +#endif } } } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/ciphertest.c --- a/libpurple/plugins/ciphertest.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/ciphertest.c Mon Jan 07 03:40:27 2008 +0000 @@ -61,7 +61,7 @@ }; static void -cipher_test_md5() { +cipher_test_md5(void) { PurpleCipher *cipher; PurpleCipherContext *context; gchar digest[33]; @@ -113,12 +113,12 @@ {"a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"}, {"abc", "a9993e364706816aba3e25717850c26c9cd0d89d"} , {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"} , - {NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"}, + {NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"}, {NULL, NULL} }; static void -cipher_test_sha1() { +cipher_test_sha1(void) { PurpleCipher *cipher; PurpleCipherContext *context; gchar digest[41]; @@ -176,7 +176,7 @@ } static void -cipher_test_digest() +cipher_test_digest(void) { const gchar *nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093"; const gchar *client_nonce = "0a4f113b"; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/log_reader.c --- a/libpurple/plugins/log_reader.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/log_reader.c Mon Jan 07 03:40:27 2008 +0000 @@ -2426,7 +2426,7 @@ } -static void log_reader_init_prefs() { +static void log_reader_init_prefs(void) { char *path; #ifdef _WIN32 char *folder; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/perl/common/Prpl.xs --- a/libpurple/plugins/perl/common/Prpl.xs Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/perl/common/Prpl.xs Mon Jan 07 03:40:27 2008 +0000 @@ -54,3 +54,20 @@ Purple::Account account const char *name time_t login_time + +int +purple_prpl_send_raw(gc, str) + Purple::Connection gc + const char *str +PREINIT: + PurplePluginProtocolInfo *prpl_info; +CODE: + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (prpl_info && prpl_info->send_raw != NULL) { + RETVAL = prpl_info->send_raw(gc, str, strlen(str)); + } else { + RETVAL = 0; + } +OUTPUT: + RETVAL + diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/perl/common/fallback/const-c.inc --- a/libpurple/plugins/perl/common/fallback/const-c.inc Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/perl/common/fallback/const-c.inc Mon Jan 07 03:40:27 2008 +0000 @@ -33,7 +33,7 @@ Regenerate these constant functions by feeding this entire source file to perl -x -#!/usr/bin/perl -w +#!/usr/bin/env perl -w use ExtUtils::Constant qw (constant_types C_constant XS_constant); my $types = {map {($_, 1)} qw(IV)}; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/ssl/ssl-nss.c --- a/libpurple/plugins/ssl/ssl-nss.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/ssl/ssl-nss.c Mon Jan 07 03:40:27 2008 +0000 @@ -109,7 +109,7 @@ } } -static gchar *get_error_text() +static gchar *get_error_text(void) { PRInt32 len = PR_GetErrorTextLength(); gchar *ret = NULL; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/tcl/tcl.c --- a/libpurple/plugins/tcl/tcl.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/tcl/tcl.c Mon Jan 07 03:40:27 2008 +0000 @@ -149,7 +149,7 @@ return 0; } -static Tcl_Interp *tcl_create_interp() +static Tcl_Interp *tcl_create_interp(void) { Tcl_Interp *interp; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/plugins/test.pl --- a/libpurple/plugins/test.pl Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/plugins/test.pl Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl -w use Gaim; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/prefs.c --- a/libpurple/prefs.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/prefs.c Mon Jan 07 03:40:27 2008 +0000 @@ -438,19 +438,6 @@ g_free(filename); prefs_loaded = TRUE; - /* I introduced a bug in 2.0.0beta2. This fixes the broken - * scores on upgrade. This can be removed sometime shortly - * after 2.0.0 final is released. -- rlaager */ - if (purple_prefs_get_int("/purple/status/scores/offline") == -500 && - purple_prefs_get_int("/purple/status/scores/available") == 100 && - purple_prefs_get_int("/purple/status/scores/invisible") == -50 && - purple_prefs_get_int("/purple/status/scores/away") == -100 && - purple_prefs_get_int("/purple/status/scores/extended_away") == -200 && - purple_prefs_get_int("/purple/status/scores/idle") == -400) - { - purple_prefs_set_int("/purple/status/scores/idle", -10); - } - return TRUE; } @@ -914,7 +901,7 @@ if(pref) { if(pref->type != PURPLE_PREF_PATH) { purple_debug_error("prefs", - "purple_prefs_set_path: %s not a string pref\n", name); + "purple_prefs_set_path: %s not a path pref\n", name); return; } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/bonjour/bonjour.c --- a/libpurple/protocols/bonjour/bonjour.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Mon Jan 07 03:40:27 2008 +0000 @@ -617,7 +617,7 @@ #endif static void -initialize_default_account_values() +initialize_default_account_values(void) { #ifndef _WIN32 struct passwd *info; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/bonjour/mdns_win32.c --- a/libpurple/protocols/bonjour/mdns_win32.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Mon Jan 07 03:40:27 2008 +0000 @@ -30,19 +30,21 @@ static GSList *pending_buddies = NULL; +typedef struct _dnssd_service_ref_handler { + DNSServiceRef sdRef; + PurpleAccount *account; + guint input_handler; +} DnsSDServiceRefHandlerData; + /* data used by win32 bonjour implementation */ typedef struct _win32_session_impl_data { - DNSServiceRef presence_svc; - DNSServiceRef browser_svc; + DnsSDServiceRefHandlerData *presence_query; + DnsSDServiceRefHandlerData *browser_query; DNSRecordRef buddy_icon_rec; - - guint presence_handler; - guint browser_handler; } Win32SessionImplData; typedef struct _win32_buddy_service_resolver_data { - DNSServiceRef txt_query; - guint txt_query_handler; + DnsSDServiceRefHandlerData *txt_query; uint32_t if_idx; gchar *name; gchar *type; @@ -53,21 +55,20 @@ typedef struct _win32_buddy_impl_data { GSList *resolvers; - DNSServiceRef null_query; - guint null_query_handler; + DnsSDServiceRefHandlerData *null_query; } Win32BuddyImplData; /* data structure for the resolve callback */ typedef struct _ResolveCallbackArgs { - DNSServiceRef resolver; - guint resolver_handler; + DnsSDServiceRefHandlerData *resolver_query; PurpleAccount *account; BonjourBuddy *bb; Win32SvcResolverData *res_data; gchar *full_service_name; - PurpleDnsQueryData *query; + PurpleDnsQueryData *dns_query; } ResolveCallbackArgs; + static gint _find_resolver_data(gconstpointer a, gconstpointer b) { const Win32SvcResolverData *rd_a = a; @@ -87,8 +88,9 @@ static void _cleanup_resolver_data(Win32SvcResolverData *rd) { if (rd->txt_query != NULL) { - purple_input_remove(rd->txt_query_handler); - DNSServiceRefDeallocate(rd->txt_query); + purple_input_remove(rd->txt_query->input_handler); + DNSServiceRefDeallocate(rd->txt_query->sdRef); + g_free(rd->txt_query); } g_free(rd->name); g_free(rd->type); @@ -98,7 +100,16 @@ static void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) { - DNSServiceProcessResult((DNSServiceRef) data); + DnsSDServiceRefHandlerData *srh = data; + DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef); + if (errorCode != kDNSServiceErr_NoError) { + purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode); + /* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */ + if (errorCode == kDNSServiceErr_Unknown) { + purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Error communicating with local mDNSResponder.")); + } + } } static void @@ -123,7 +134,7 @@ uint32_t ttl, void *context) { - if (kDNSServiceErr_NoError != errorCode) { + if (errorCode != kDNSServiceErr_NoError) { purple_debug_error("bonjour", "record query - callback error.\n"); /* TODO: Probably should remove the buddy when this happens */ } else if (flags & kDNSServiceFlagsAdd) { @@ -142,9 +153,9 @@ bonjour_buddy_got_buddy_icon(bb, rdata, rdlen); /* We've got what we need; stop listening */ - purple_input_remove(idata->null_query_handler); - idata->null_query_handler = 0; - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); idata->null_query = NULL; } } @@ -153,7 +164,7 @@ static void _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message) { - ResolveCallbackArgs* args = (ResolveCallbackArgs*) data; + ResolveCallbackArgs *args = (ResolveCallbackArgs*) data; Win32BuddyImplData *idata = args->bb->mdns_impl_data; gboolean delete_buddy = FALSE; PurpleBuddy *pb; @@ -168,27 +179,31 @@ delete_buddy = TRUE; } else { struct sockaddr_in *addr = g_slist_nth_data(hosts, 1); + DNSServiceErrorType errorCode; + DNSServiceRef txt_query_sr; /* finally, set up the continuous txt record watcher, and add the buddy to purple */ - - if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery, + errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery, kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT, - kDNSServiceClass_IN, _mdns_record_query_callback, args->bb)) { - + kDNSServiceClass_IN, _mdns_record_query_callback, args->bb); + if (errorCode == kDNSServiceErr_NoError) { const char *ip = inet_ntoa(addr->sin_addr); purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj); - args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip)); args->res_data->ip = args->bb->ips->data; - args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query), + args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1); + args->res_data->txt_query->sdRef = txt_query_sr; + args->res_data->txt_query->account = args->account; + + args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr), PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query); bonjour_buddy_add_to_purple(args->bb, NULL); } else { - purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s\n", args->bb->name); + purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s (%d)\n", args->bb->name, errorCode); delete_buddy = TRUE; } @@ -230,21 +245,21 @@ _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) { - ResolveCallbackArgs *args = (ResolveCallbackArgs*)context; + ResolveCallbackArgs *args = (ResolveCallbackArgs*) context; Win32BuddyImplData *idata = args->bb->mdns_impl_data; /* remove the input fd and destroy the service ref */ - purple_input_remove(args->resolver_handler); - args->resolver_handler = 0; - DNSServiceRefDeallocate(args->resolver); - args->resolver = NULL; + purple_input_remove(args->resolver_query->input_handler); + DNSServiceRefDeallocate(args->resolver_query->sdRef); + g_free(args->resolver_query); + args->resolver_query = NULL; - if (kDNSServiceErr_NoError != errorCode) + if (errorCode != kDNSServiceErr_NoError) purple_debug_error("bonjour", "service resolver - callback error.\n"); else { /* set more arguments, and start the host resolver */ - if ((args->query = + if ((args->dns_query = purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) { args->full_service_name = g_strdup(fullname); @@ -286,7 +301,7 @@ const char *name, const char *regtype, const char *domain, void *context) { /* TODO: deal with collision */ - if (kDNSServiceErr_NoError != errorCode) + if (errorCode != kDNSServiceErr_NoError) purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode); else purple_debug_info("bonjour", "service advertisement - callback.\n"); @@ -298,26 +313,28 @@ { PurpleAccount *account = (PurpleAccount*)context; - if (kDNSServiceErr_NoError != errorCode) - purple_debug_error("bonjour", "service browser - callback error\n"); + if (errorCode != kDNSServiceErr_NoError) + purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode); else if (flags & kDNSServiceFlagsAdd) { /* A presence service instance has been discovered... check it isn't us! */ if (purple_utf8_strcasecmp(serviceName, account->username) != 0) { + DNSServiceErrorType resErrorCode; /* OK, lets go ahead and resolve it to add to the buddy list */ ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1); + DNSServiceRef resolver_sr; purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n", serviceName, interfaceIndex, regtype ? regtype : "", replyDomain ? replyDomain : ""); - if (kDNSServiceErr_NoError == DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, - replyDomain, _mdns_service_resolve_callback, args)) { + resErrorCode = DNSServiceResolve(&resolver_sr, 0, 0, serviceName, regtype, + replyDomain, _mdns_service_resolve_callback, args); + if (resErrorCode == kDNSServiceErr_NoError) { GSList *tmp = pending_buddies; PurpleBuddy *pb; BonjourBuddy* bb = NULL; Win32SvcResolverData *rd; Win32BuddyImplData *idata; - gint fd; /* Is there an existing buddy? */ if ((pb = purple_find_buddy(account, serviceName))) @@ -344,7 +361,6 @@ pb->proto_data = bb; } - rd = g_new0(Win32SvcResolverData, 1); rd->if_idx = interfaceIndex; rd->name = g_strdup(serviceName); @@ -358,11 +374,14 @@ args->res_data = rd; args->account = account; + args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1); + args->resolver_query->sdRef = resolver_sr; + args->resolver_query->account = account; /* get a file descriptor for this service ref, and add it to the input list */ - fd = DNSServiceRefSockFD(args->resolver); - args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); + args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr), + PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query); } else { - purple_debug_error("bonjour", "service browser - failed to resolve service.\n"); + purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode); g_free(args); } } @@ -432,7 +451,7 @@ gboolean _mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records) { TXTRecordRef dns_data; gboolean ret = TRUE; - DNSServiceErrorType set_ret = kDNSServiceErr_NoError; + DNSServiceErrorType errorCode = kDNSServiceErr_NoError; Win32SessionImplData *idata = data->mdns_impl_data; g_return_val_if_fail(idata != NULL, FALSE); @@ -441,44 +460,46 @@ while (records) { PurpleKeyValuePair *kvp = records->data; - set_ret = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value); - if (set_ret != kDNSServiceErr_NoError) + errorCode = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value); + if (errorCode != kDNSServiceErr_NoError) break; records = records->next; } - if (set_ret != kDNSServiceErr_NoError) { - purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); + if (errorCode != kDNSServiceErr_NoError) { + purple_debug_error("bonjour", "Unable to allocate memory for text record.(%d)\n", errorCode); ret = FALSE; } else { - DNSServiceErrorType err = kDNSServiceErr_NoError; - /* OK, we're done constructing the text record, (re)publish the service */ + DNSServiceRef presence_sr; switch (type) { case PUBLISH_START: purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj); - err = DNSServiceRegister(&idata->presence_svc, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, + errorCode = DNSServiceRegister(&presence_sr, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), _mdns_service_register_callback, NULL); break; case PUBLISH_UPDATE: purple_debug_info("bonjour", "Updating presence.\n"); - err = DNSServiceUpdateRecord(idata->presence_svc, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); + errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); break; } - if (err != kDNSServiceErr_NoError) { - purple_debug_error("bonjour", "Failed to publish presence service.\n"); + if (errorCode != kDNSServiceErr_NoError) { + purple_debug_error("bonjour", "Failed to publish presence service.(%d)\n", errorCode); ret = FALSE; } else if (type == PUBLISH_START) { /* We need to do this because according to the Apple docs: * "the client is responsible for ensuring that DNSServiceProcessResult() is called * whenever there is a reply from the daemon - the daemon may terminate its connection * with a client that does not process the daemon's responses */ - idata->presence_handler = purple_input_add(DNSServiceRefSockFD(idata->presence_svc), - PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_svc); + idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1); + idata->presence_query->sdRef = presence_sr; + idata->presence_query->account = data->account; + idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr), + PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query); } } @@ -488,17 +509,24 @@ } gboolean _mdns_browse(BonjourDnsSd *data) { + DNSServiceErrorType errorCode; Win32SessionImplData *idata = data->mdns_impl_data; + DNSServiceRef browser_sr; g_return_val_if_fail(idata != NULL, FALSE); - if (DNSServiceBrowse(&idata->browser_svc, 0, 0, ICHAT_SERVICE, NULL, - _mdns_service_browse_callback, data->account) - == kDNSServiceErr_NoError) { - idata->browser_handler = purple_input_add(DNSServiceRefSockFD(idata->browser_svc), - PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_svc); + errorCode = DNSServiceBrowse(&browser_sr, 0, 0, ICHAT_SERVICE, NULL, + _mdns_service_browse_callback, data->account); + if (errorCode == kDNSServiceErr_NoError) { + idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1); + idata->browser_query->sdRef = browser_sr; + idata->browser_query->account = data->account; + idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr), + PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query); return TRUE; - } + } else + purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode); + return FALSE; } @@ -509,14 +537,16 @@ if (idata == NULL) return; - if (idata->presence_svc != NULL) { - purple_input_remove(idata->presence_handler); - DNSServiceRefDeallocate(idata->presence_svc); + if (idata->presence_query != NULL) { + purple_input_remove(idata->presence_query->input_handler); + DNSServiceRefDeallocate(idata->presence_query->sdRef); + g_free(idata->presence_query); } - if (idata->browser_svc != NULL) { - purple_input_remove(idata->browser_handler); - DNSServiceRefDeallocate(idata->browser_svc); + if (idata->browser_query != NULL) { + purple_input_remove(idata->browser_query->input_handler); + DNSServiceRefDeallocate(idata->browser_query->sdRef); + g_free(idata->browser_query); } g_free(idata); @@ -526,28 +556,30 @@ gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) { Win32SessionImplData *idata = data->mdns_impl_data; - DNSServiceErrorType err = kDNSServiceErr_NoError; + DNSServiceErrorType errorCode = kDNSServiceErr_NoError; g_return_val_if_fail(idata != NULL, FALSE); if (avatar_data != NULL && idata->buddy_icon_rec == NULL) { purple_debug_info("bonjour", "Setting new buddy icon.\n"); - err = DNSServiceAddRecord(idata->presence_svc, &idata->buddy_icon_rec, + errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec, 0, kDNSServiceType_NULL, avatar_len, avatar_data, 0); } else if (avatar_data != NULL) { purple_debug_info("bonjour", "Updating existing buddy icon.\n"); - err = DNSServiceUpdateRecord(idata->presence_svc, idata->buddy_icon_rec, + errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0, avatar_len, avatar_data, 0); } else if (idata->buddy_icon_rec != NULL) { purple_debug_info("bonjour", "Removing existing buddy icon.\n"); - DNSServiceRemoveRecord(idata->presence_svc, idata->buddy_icon_rec, 0); + errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0); idata->buddy_icon_rec = NULL; } - if (err != kDNSServiceErr_NoError) - purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", err); + if (errorCode != kDNSServiceErr_NoError) { + purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", errorCode); + return FALSE; + } - return (err == kDNSServiceErr_NoError); + return TRUE; } void _mdns_init_buddy(BonjourBuddy *buddy) { @@ -566,8 +598,9 @@ } if (idata->null_query != NULL) { - purple_input_remove(idata->null_query_handler); - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); } g_free(idata); @@ -583,17 +616,30 @@ /* Cancel any existing query */ if (idata->null_query != NULL) { - purple_input_remove(idata->null_query_handler); - idata->null_query_handler = 0; - DNSServiceRefDeallocate(idata->null_query); + purple_input_remove(idata->null_query->input_handler); + DNSServiceRefDeallocate(idata->null_query->sdRef); + g_free(idata->null_query); idata->null_query = NULL; } - DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local"); - if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, kDNSServiceInterfaceIndexAny, svc_name, - kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy)) { - idata->null_query_handler = purple_input_add(DNSServiceRefSockFD(idata->null_query), - PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); + if (DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local") != 0) + purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name); + else { + DNSServiceRef null_query_sr; + + DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny, + svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy); + + if (errorCode == kDNSServiceErr_NoError) { + idata->null_query = g_new(DnsSDServiceRefHandlerData, 1); + + idata->null_query->sdRef = null_query_sr; + idata->null_query->account = buddy->account; + + idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr), + PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); + } else + purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode); } } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/bonjour/parser.c --- a/libpurple/protocols/bonjour/parser.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/bonjour/parser.c Mon Jan 07 03:40:27 2008 +0000 @@ -39,7 +39,7 @@ for(i=0; i < nb_attributes * 5; i+=5) { if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) { int len = attributes[i+4] - attributes[i+3]; - bconv->buddy_name = g_strndup(attributes[i+3], len); + bconv->buddy_name = g_strndup((char *)attributes[i+3], len); bonjour_jabber_conv_match_by_name(bconv); return (bconv->pb != NULL); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/irc/cmds.c --- a/libpurple/protocols/irc/cmds.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/irc/cmds.c Mon Jan 07 03:40:27 2008 +0000 @@ -367,7 +367,12 @@ if (!end) end = cur + strlen(cur); msg = g_strndup(cur, end - cur); - buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg); + + if(!strcmp(cmd, "msg")) + buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg); + else /* seding a notice if we get here */ + buf = irc_format(irc, "vt:", "NOTICE", args[0], msg); + irc_send(irc, buf); g_free(msg); g_free(buf); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/irc/parse.c Mon Jan 07 03:40:27 2008 +0000 @@ -136,6 +136,7 @@ { "names", "c", irc_cmd_names, N_("names [channel]: List the users currently in a channel.") }, { "nick", "n", irc_cmd_nick, N_("nick <new nickname>: Change your nickname.") }, { "nickserv", ":", irc_cmd_service, N_("nickserv: Send a command to nickserv") }, + { "notice", "t:", irc_cmd_privmsg, N_("notice <target<: Send a notice to a user or channel.") }, { "op", ":", irc_cmd_op, N_("op <nick1> [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this.") }, { "operwall", ":", irc_cmd_wallops, N_("operwall <message>: If you don't know what this is, you probably can't use it.") }, { "operserv", ":", irc_cmd_service, N_("operserv: Send a command to operserv") }, diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/auth.c --- a/libpurple/protocols/jabber/auth.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/auth.c Mon Jan 07 03:40:27 2008 +0000 @@ -330,14 +330,21 @@ disallow_plaintext_auth); g_free(msg); return; - /* Everything else has failed, so fail the - * connection. Should probably have a better - * error here. - */ + } else { - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, - _("Server does not use any supported authentication method")); + /* We have no mechs which can work. + * Try falling back on the old jabber:iq:auth method. We get here if the server supports + * one or more sasl mechs, we are compiled with cyrus-sasl support, but we support or can connect with none of + * the offerred mechs. jabberd 2.0 w/ SASL and Apple's iChat Server 10.5 both handle and expect + * jabber:iq:auth in this situation. iChat Server in particular offers SASL GSSAPI by default, which is often + * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails. + * + * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However, + * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms. + * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers + * which would connect without issue otherwise. -evands + */ + jabber_auth_start_old(js); return; } /* not reached */ @@ -563,6 +570,75 @@ } } +/*! + * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest + * + * This is the crammd5 response. Inspired by cyrus-sasl's _sasl_hmac_md5() + */ +static void +auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest) +{ + PurpleCipher *cipher; + PurpleCipherContext *context; + int i; + /* inner padding - key XORd with ipad */ + unsigned char k_ipad[65]; + /* outer padding - key XORd with opad */ + unsigned char k_opad[65]; + + cipher = purple_ciphers_find_cipher("md5"); + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (strlen(key) > 64) { + guchar keydigest[16]; + + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, (const guchar *)key, strlen(key)); + purple_cipher_context_digest(context, 16, keydigest, NULL); + purple_cipher_context_destroy(context); + + key = (char *)keydigest; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + memset(k_ipad, '\0', sizeof k_ipad); + memset(k_opad, '\0', sizeof k_opad); + memcpy(k_ipad, (void *)key, key_len); + memcpy(k_opad, (void *)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */ + purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */ + purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */ + purple_cipher_context_destroy(context); + + /* perform outer MD5 */ + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */ + purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */ + purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */ + purple_cipher_context_destroy(context); +} + static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data) { JabberIq *iq; @@ -608,6 +684,35 @@ jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); + } else if(js->stream_id && xmlnode_get_child(query, "crammd5")) { + const char *challenge; + guchar digest[16]; + char h[17], *p; + int i; + + challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge"); + auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), digest); + + /* Create the response query */ + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); + query = xmlnode_get_child(iq->node, "query"); + + x = xmlnode_new_child(query, "username"); + xmlnode_insert_data(x, js->user->node, -1); + x = xmlnode_new_child(query, "resource"); + xmlnode_insert_data(x, js->user->resource, -1); + + x = xmlnode_new_child(query, "crammd5"); + + /* Translate the digest to a hexadecimal notation */ + p = h; + for(i=0; i<16; i++, p+=2) + snprintf(p, 3, "%02x", digest[i]); + xmlnode_insert_data(x, h, -1); + + jabber_iq_set_callback(iq, auth_old_result_cb, NULL); + jabber_iq_send(iq); + } else if(xmlnode_get_child(query, "password")) { if(js->gsc == NULL && !purple_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE)) { @@ -972,10 +1077,12 @@ } } /* If we've negotiated a security layer, we need to enable it */ - sasl_getprop(js->sasl, SASL_SSF, &x); - if (*(int *)x > 0) { - sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x); - js->sasl_maxbuf = *(int *)x; + if (js->sasl) { + sasl_getprop(js->sasl, SASL_SSF, &x); + if (*(int *)x > 0) { + sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x); + js->sasl_maxbuf = *(int *)x; + } } #endif diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/buddy.c Mon Jan 07 03:40:27 2008 +0000 @@ -1153,8 +1153,10 @@ void jabber_vcard_fetch_mine(JabberStream *js) { - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "vcard-temp"); - + JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); + + xmlnode *vcard = xmlnode_new_child(iq->node, "vCard"); + xmlnode_set_namespace(vcard, "vcard-temp"); jabber_iq_set_callback(iq, jabber_vcard_save_mine, NULL); jabber_iq_send(iq); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Mon Jan 07 03:40:27 2008 +0000 @@ -389,9 +389,32 @@ g_free(txt); g_free(utf); } +static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) +{ + purple_timeout_remove(GPOINTER_TO_INT(timeout)); + js->keepalive_timeout = -1; +} + +static gboolean jabber_pong_timeout(PurpleConnection *gc) +{ + JabberStream *js = gc->proto_data; + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Ping timeout")); + js->keepalive_timeout = -1; + return FALSE; +} + void jabber_keepalive(PurpleConnection *gc) { - jabber_send_raw(gc->proto_data, "\t", -1); + JabberStream *js = gc->proto_data; + JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); + + xmlnode *ping = xmlnode_new_child(iq->node, "ping"); + xmlnode_set_namespace(ping, "urn:xmpp:ping"); + + js->keepalive_timeout = purple_timeout_add_seconds(20, (GSourceFunc)(jabber_pong_timeout), gc); + jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout)); + jabber_iq_send(iq); } static void @@ -592,7 +615,8 @@ js->user = jabber_id_new(purple_account_get_username(account)); js->next_id = g_random_int(); js->write_buffer = purple_circ_buffer_new(512); - js->old_length = -1; + js->old_length = 0; + js->keepalive_timeout = -1; if(!js->user) { purple_connection_error_reason (gc, @@ -1077,7 +1101,7 @@ g_free, g_free); js->user = jabber_id_new(purple_account_get_username(account)); js->next_id = g_random_int(); - js->old_length = -1; + js->old_length = 0; if(!js->user) { purple_connection_error_reason (gc, @@ -1292,6 +1316,9 @@ g_free(js->old_uri); g_free(js->old_track); + if (js->keepalive_timeout != -1) + purple_timeout_remove(js->keepalive_timeout); + g_free(js); gc->proto_data = NULL; @@ -1497,10 +1524,16 @@ } else purple_notify_user_info_add_pair(user_info, _("Mood"), mood); } - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { + 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); + const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); + const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); + char *playing = purple_util_format_song_info(title, artist, album, NULL); + if (playing) { + purple_notify_user_info_add_pair(user_info, _("Now Listening"), playing); + g_free(playing); + } } } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Mon Jan 07 03:40:27 2008 +0000 @@ -193,6 +193,9 @@ char *old_track; char *host; + + /* A purple timeout tag for the keepalive */ + int keepalive_timeout; }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Mon Jan 07 03:40:27 2008 +0000 @@ -53,7 +53,7 @@ OPT_PROTO_SLASH_COMMANDS_NATIVE, NULL, /* user_splits */ NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ + {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ jabber_list_icon, /* list_icon */ jabber_list_emblem, /* list_emblems */ jabber_status_text, /* status_text */ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/jabber/usertune.c --- a/libpurple/protocols/jabber/usertune.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/jabber/usertune.c Mon Jan 07 03:40:27 2008 +0000 @@ -35,11 +35,12 @@ xmlnode *tuneinfo, *tune; PurpleJabberTuneInfo tuneinfodata; JabberBuddyResource *resource; - + gboolean valid = FALSE; + /* ignore the tune of people not on our buddy list */ if (!buddy || !item) return; - + tuneinfodata.artist = NULL; tuneinfodata.title = NULL; tuneinfodata.album = NULL; @@ -58,36 +59,47 @@ if (!strcmp(tuneinfo->name, "artist")) { if (tuneinfodata.artist == NULL) /* only pick the first one */ tuneinfodata.artist = xmlnode_get_data(tuneinfo); + valid = TRUE; } else if (!strcmp(tuneinfo->name, "length")) { if (tuneinfodata.time == -1) { char *length = xmlnode_get_data(tuneinfo); if (length) tuneinfodata.time = strtol(length, NULL, 10); g_free(length); + if (tuneinfodata.time > 0) + valid = TRUE; } } else if (!strcmp(tuneinfo->name, "source")) { if (tuneinfodata.album == NULL) /* only pick the first one */ tuneinfodata.album = xmlnode_get_data(tuneinfo); + valid = TRUE; } else if (!strcmp(tuneinfo->name, "title")) { if (tuneinfodata.title == NULL) /* only pick the first one */ tuneinfodata.title = xmlnode_get_data(tuneinfo); + valid = TRUE; } else if (!strcmp(tuneinfo->name, "track")) { if (tuneinfodata.track == NULL) /* only pick the first one */ tuneinfodata.track = xmlnode_get_data(tuneinfo); + valid = TRUE; } else if (!strcmp(tuneinfo->name, "uri")) { if (tuneinfodata.url == NULL) /* only pick the first one */ tuneinfodata.url = xmlnode_get_data(tuneinfo); + valid = TRUE; } } } - 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, - PURPLE_TUNE_TRACK, tuneinfodata.track, - PURPLE_TUNE_TIME, tuneinfodata.time, - PURPLE_TUNE_URL, tuneinfodata.url, NULL); + if (valid) { + 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, + PURPLE_TUNE_TRACK, tuneinfodata.track, + PURPLE_TUNE_TIME, tuneinfodata.time, + PURPLE_TUNE_URL, tuneinfodata.url, NULL); + } else { + purple_prpl_got_user_status_deactive(js->gc->account, from, "tune"); + } g_free(tuneinfodata.artist); g_free(tuneinfodata.title); @@ -119,7 +131,7 @@ xmlnode_insert_data(xmlnode_new_child(tunenode, "source"),tuneinfo->album,-1); if(tuneinfo->url && tuneinfo->url[0] != '\0') xmlnode_insert_data(xmlnode_new_child(tunenode, "uri"),tuneinfo->url,-1); - if(tuneinfo->time >= 0) { + if(tuneinfo->time > 0) { char *length = g_strdup_printf("%d", tuneinfo->time); xmlnode_insert_data(xmlnode_new_child(tunenode, "length"),length,-1); g_free(length); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/msn/contact.c --- a/libpurple/protocols/msn/contact.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/msn/contact.c Mon Jan 07 03:40:27 2008 +0000 @@ -1289,16 +1289,16 @@ purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid); msn_group_new(session->userlist, guid, state->new_group_name); - g_free(guid); - if (state->action & MSN_ADD_BUDDY) { msn_userlist_add_buddy(session->userlist, state->who, state->new_group_name); } else if (state->action & MSN_MOVE_BUDDY) { msn_add_contact_to_group(session->contact, state, state->who, guid); + g_free(guid); return; } + g_free(guid); } else { purple_debug_info("MSNCL", "Adding group %s failed\n", state->new_group_name); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Mon Jan 07 03:40:27 2008 +0000 @@ -612,8 +612,8 @@ 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 : ""); + const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); + currentmedia = purple_util_format_song_info(title, artist, album, NULL); /* We could probably just use user->media.title etc. here */ } @@ -662,9 +662,7 @@ } if (currentmedia) { - tmp = g_markup_escape_text(currentmedia, -1); - purple_notify_user_info_add_pair(user_info, _("Current media"), tmp); - g_free(tmp); + purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia); g_free(currentmedia); } } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/msn/soap.c --- a/libpurple/protocols/msn/soap.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/msn/soap.c Mon Jan 07 03:40:27 2008 +0000 @@ -813,10 +813,8 @@ purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n"); } else { purple_debug_info("MSN SOAP", "No requests left to dispatch\n"); +#endif } -#else - } -#endif } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/msn/soap2.c --- a/libpurple/protocols/msn/soap2.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/msn/soap2.c Mon Jan 07 03:40:27 2008 +0000 @@ -170,6 +170,9 @@ { MsnSoapConnection *conn = data; + /* sslconn already frees the connection in case of error */ + conn->ssl = NULL; + g_hash_table_remove(conn->session->soap_table, conn->host); } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/msnp9/slpcall.c --- a/libpurple/protocols/msnp9/slpcall.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/msnp9/slpcall.c Mon Jan 07 03:40:27 2008 +0000 @@ -34,7 +34,7 @@ **************************************************************************/ static char * -rand_guid() +rand_guid(void) { return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", rand() % 0xAAFF + 0x1111, diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Mon Jan 07 03:40:27 2008 +0000 @@ -2912,8 +2912,7 @@ menu = g_list_append(menu, act); #endif - act = purple_plugin_action_new(g_strdup_printf("%s", - _("Add friends from MySpace.com")), msim_import_friends); + act = purple_plugin_action_new(_("Add friends from MySpace.com"), msim_import_friends); menu = g_list_append(menu, act); return menu; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/novell/novell.c --- a/libpurple/protocols/novell/novell.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/novell/novell.c Mon Jan 07 03:40:27 2008 +0000 @@ -1029,7 +1029,7 @@ ******************************************************************************/ static char * -_user_agent_string() +_user_agent_string(void) { #if !defined(_WIN32) diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/family_auth.c --- a/libpurple/protocols/oscar/family_auth.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/family_auth.c Mon Jan 07 03:40:27 2008 +0000 @@ -196,6 +196,10 @@ * unknown= 0x0000008b * serverstore = 0x01 * + * @param truncate_pass Truncate the password to 8 characters. This + * usually happens for AOL accounts. We are told that we + * should truncate it if the 0x0017/0x0007 SNAC contains + * a TLV of type 0x0026 with data 0x0000. */ int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key) @@ -522,8 +526,8 @@ /* * If the truncate_pass TLV exists then we should truncate the - * user's password to 8 characters. This flag is sent when you - * try to log in with an AOL user's screen name. + * user's password to 8 characters. This flag is sent to us + * when logging in with an AOL user's screen name. */ truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/family_icbm.c --- a/libpurple/protocols/oscar/family_icbm.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Mon Jan 07 03:40:27 2008 +0000 @@ -51,10 +51,9 @@ #include "win32dep.h" #endif +#include "util.h" /* yaz */ #include "debug.h" -#include "../../util.h" - /** * Add a standard ICBM header to the given bstream with the given * information. @@ -2378,11 +2377,166 @@ sn = byte_stream_getstr(bs, snlen); reason = byte_stream_get16(bs); - if (channel == 0x0002) { /* File transfer declined */ + if (channel == 0x0002) + { + /* parse status note text */ + + struct aim_icq_info *info = NULL; + struct aim_icq_info *prev_info = NULL; + char *response = NULL; + char *encoding = NULL; + char *stripped_encoding = NULL; + char *status_note_text = NULL; + char *stripped_status_note_text = NULL; + char *status_note = NULL; + + /* + * TODO: Using a while statement here is kind of an ugly hack + * to be able to use 'break'. We might as well be using + * 'goto'. Should probably get rid of this. + */ + while (reason == 0x0003) /* channel-specific */ + { + guint32 length; + guint16 version; + guint32 capability; + guint8 message_type; + guint16 status_code; + guint16 text_length; + guint32 request_length; + guint32 response_length; + guint32 encoding_length; + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + + for (info = od->icq_info; info != NULL; info = info->next) + { + if (memcmp(&info->icbm_cookie, cookie, 8) == 0) + { + if (prev_info == NULL) + od->icq_info = info->next; + else + prev_info->next = info->next; + + break; + } + + prev_info = info; + } + + if (info == NULL) + break; + + if ((length = byte_stream_getle16(bs)) != 27) + { + purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 27, received %u.\n", length); + break; + } + if ((version = byte_stream_getle16(bs)) != 9) + { + purple_debug_misc("oscar", "clientautoresp: incorrect version; expected 9, received %u.\n", version); + break; + } + capability = aim_locate_getcaps(od, bs, 0x10); + if (capability != OSCAR_CAPABILITY_EMPTY) + { + purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n"); + break; + } + byte_stream_advance(bs, 2); /* unknown */ + byte_stream_advance(bs, 4); /* client capabilities flags */ + byte_stream_advance(bs, 1); /* unknown */ + byte_stream_advance(bs, 2); /* downcouner? */ + + if ((length = byte_stream_getle16(bs)) != 14) + { + purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 14, received %u.\n", length); + break; + } + byte_stream_advance(bs, 2); /* downcounter? */ + byte_stream_advance(bs, 12); /* unknown */ + + if ((message_type = byte_stream_get8(bs)) != 0x1a) + { + purple_debug_misc("oscar", "clientautoresp: incorrect message type; expected 0x1a, received 0x%x.\n", message_type); + break; + } + byte_stream_advance(bs, 1); /* message flags */ + if ((status_code = byte_stream_getle16(bs)) != 0) + { + purple_debug_misc("oscar", "clientautoresp: incorrect status code; expected 0, received %u.\n", status_code); + break; + } + byte_stream_advance(bs, 2); /* priority code */ + + text_length = byte_stream_getle16(bs); + byte_stream_advance(bs, text_length); /* text */ + + length = byte_stream_getle16(bs); + byte_stream_advance(bs, 18); /* unknown */ + if (length != 18 + 4 + (request_length = byte_stream_getle32(bs)) + 17) + { + purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 18 + 4 + request_length + 17, length); + break; + } + byte_stream_advance(bs, request_length); /* x request */ + byte_stream_advance(bs, 17); /* unknown */ + + length = byte_stream_getle32(bs); + response_length = byte_stream_getle32(bs); + response = byte_stream_getstr(bs, response_length); + if (length != 4 + response_length + 4 + (encoding_length = byte_stream_getle32(bs))) + { + purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 4 + response_length + 4 + encoding_length, length); + break; + } + encoding = byte_stream_getstr(bs, encoding_length); + + account = purple_connection_get_account(od->gc); + stripped_encoding = oscar_encoding_extract(encoding); + status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length); + stripped_status_note_text = purple_markup_strip_html(status_note_text); + + if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0) + status_note = g_strdup_printf("%s: %s", info->status_note_title, stripped_status_note_text); + else + status_note = g_strdup(info->status_note_title); + + buddy = purple_find_buddy(account, sn); + if (buddy == NULL) + { + purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", sn); + break; + } + + purple_debug_misc("oscar", "clientautoresp: setting status message to \"%s\".\n", status_note); + + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_active_status(presence); + + purple_prpl_got_user_status(account, sn, + purple_status_get_id(status), + "message", status_note, NULL); + + break; + } + + g_free(status_note); + g_free(stripped_status_note_text); + g_free(status_note_text); + g_free(stripped_encoding); + g_free(encoding); + g_free(response); + g_free(info->status_note_title); + g_free(info); + byte_stream_get16(bs); /* Unknown */ byte_stream_get16(bs); /* Unknown */ if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, channel, sn, reason, cookie); + } else if (channel == 0x0004) { /* ICQ message */ switch (reason) { case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/family_icq.c --- a/libpurple/protocols/oscar/family_icq.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Mon Jan 07 03:40:27 2008 +0000 @@ -435,6 +435,65 @@ return 0; } +/* + * getstatusnote may be a misleading name because the response + * contains a lot of different information but currently it's only + * used to get that. + */ +int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len) +{ + FlapConnection *conn; + FlapFrame *frame; + aim_snacid_t snacid; + int bslen; + + purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin); + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0015))) + { + purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n"); + return -EINVAL; + } + + bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin); + + frame = flap_frame_new(od, 0x02, 10 + 4 + bslen); + + snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&frame->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + byte_stream_put16(&frame->data, 0x0001); + byte_stream_put16(&frame->data, bslen); + + byte_stream_putle16(&frame->data, bslen - 2); + byte_stream_putle32(&frame->data, atoi(od->sn)); + byte_stream_putle16(&frame->data, 0x07d0); /* I command thee. */ + byte_stream_putle16(&frame->data, snacid); /* eh. */ + byte_stream_putle16(&frame->data, 0x0fa0); /* shrug. */ + byte_stream_putle16(&frame->data, 58 + strlen(uin)); + + byte_stream_put32(&frame->data, 0x05b90002); /* don't ask */ + byte_stream_put32(&frame->data, 0x80000000); + byte_stream_put32(&frame->data, 0x00000006); + byte_stream_put32(&frame->data, 0x00010002); + byte_stream_put32(&frame->data, 0x00020000); + byte_stream_put32(&frame->data, 0x04e30000); + byte_stream_put32(&frame->data, 0x00020002); + byte_stream_put32(&frame->data, 0x00000001); + + byte_stream_put16(&frame->data, 24 + strlen(uin)); + byte_stream_put32(&frame->data, 0x003c0010); + byte_stream_putraw(&frame->data, note_hash, 16); /* status note hash */ + byte_stream_put16(&frame->data, 0x0032); /* buddy uin */ + byte_stream_put16(&frame->data, strlen(uin)); + byte_stream_putstr(&frame->data, uin); + + flap_connection_send(conn, frame); + + return 0; +} + static void aim_icq_freeinfo(struct aim_icq_info *info) { int i; @@ -467,6 +526,7 @@ g_free(info->workposition); g_free(info->workwebpage); g_free(info->info); + g_free(info->status_note_title); g_free(info); } @@ -641,6 +701,178 @@ info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); /* Then 0x00 02 00 00 00 00 00 */ } break; + + /* status note title and send request for status note text */ + case 0x0fb4: { + GSList *tlvlist; + aim_tlv_t *tlv; + FlapConnection *conn; + char *uin = NULL; + char *status_note_title = NULL; + + conn = flap_connection_findbygroup(od, 0x0004); + if (conn == NULL) + { + purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n"); + break; + } + + byte_stream_advance(&qbs, 0x02); /* length */ + byte_stream_advance(&qbs, 0x2f); /* unknown stuff */ + + tlvlist = aim_tlvlist_read(&qbs); + + tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1); + if (tlv != NULL) + /* Get user number */ + uin = aim_tlv_getvalue_as_string(tlv); + + tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1); + if (tlv != NULL) + /* Get status note title */ + status_note_title = aim_tlv_getvalue_as_string(tlv); + + aim_tlvlist_free(tlvlist); + + if (uin == NULL || status_note_title == NULL) + { + purple_debug_misc("oscar", "icq/0x0fb4: uin or " + "status_note_title was not found\n"); + g_free(uin); + g_free(status_note_title); + break; + } + + if (status_note_title[0] == '\0') + { + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; + + account = purple_connection_get_account(od->gc); + buddy = purple_find_buddy(account, uin); + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_active_status(presence); + + purple_prpl_got_user_status(account, uin, + purple_status_get_id(status), + "message", NULL, NULL); + + g_free(status_note_title); + } + else + { + struct aim_icq_info *info; + guint32 data_len; + FlapFrame *frame; + aim_snacid_t snacid; + guchar cookie[8]; + + info = g_new0(struct aim_icq_info, 1); + + if (info == NULL) + { + g_free(uin); + g_free(status_note_title); + + break; + } + + data_len = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4; + frame = flap_frame_new(od, 0x0002, 10 + 4 + data_len); + snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); + + aim_putsnac(&frame->data, 0x0004, 0x0006, 0x0000, snacid); + + aim_icbm_makecookie(cookie); + + byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */ + byte_stream_put16(&frame->data, 0x0002); /* message channel */ + byte_stream_put8(&frame->data, strlen(uin)); /* uin */ + byte_stream_putstr(&frame->data, uin); + + byte_stream_put16(&frame->data, 0x0005); /* rendez vous data */ + byte_stream_put16(&frame->data, 0x00b2); + byte_stream_put16(&frame->data, 0x0000); /* request */ + byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */ + byte_stream_put32(&frame->data, 0x09461349); /* ICQ server relaying */ + byte_stream_put16(&frame->data, 0x4c7f); + byte_stream_put16(&frame->data, 0x11d1); + byte_stream_put32(&frame->data, 0x82224445); + byte_stream_put32(&frame->data, 0x53540000); + + byte_stream_put16(&frame->data, 0x000a); /* unknown TLV */ + byte_stream_put16(&frame->data, 0x0002); + byte_stream_put16(&frame->data, 0x0001); + + byte_stream_put16(&frame->data, 0x000f); /* unknown TLV */ + byte_stream_put16(&frame->data, 0x0000); + + byte_stream_put16(&frame->data, 0x2711); /* extended data */ + byte_stream_put16(&frame->data, 0x008a); + byte_stream_putle16(&frame->data, 0x001b); /* length */ + byte_stream_putle16(&frame->data, 0x0009); /* version */ + byte_stream_putle32(&frame->data, 0x00000000); /* plugin: none */ + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle16(&frame->data, 0x0000); /* unknown */ + byte_stream_putle32(&frame->data, 0x00000000); /* client capabilities flags */ + byte_stream_put8(&frame->data, 0x00); /* unknown */ + byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */ + byte_stream_putle16(&frame->data, 0x000e); /* length */ + byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */ + byte_stream_putle32(&frame->data, 0x00000000); /* unknown */ + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_putle32(&frame->data, 0x00000000); + byte_stream_put8(&frame->data, 0x1a); /* message type: plugin message descibed by text string */ + byte_stream_put8(&frame->data, 0x00); /* message flags */ + byte_stream_putle16(&frame->data, 0x0000); /* status code */ + byte_stream_putle16(&frame->data, 0x0001); /* priority code */ + byte_stream_putle16(&frame->data, 0x0000); /* text length */ + + byte_stream_put8(&frame->data, 0x3a); /* message dump */ + byte_stream_put32(&frame->data, 0x00811a18); + byte_stream_put32(&frame->data, 0xbc0e6c18); + byte_stream_put32(&frame->data, 0x47a5916f); + byte_stream_put32(&frame->data, 0x18dcc76f); + byte_stream_put32(&frame->data, 0x1a010013); + byte_stream_put32(&frame->data, 0x00000041); + byte_stream_put32(&frame->data, 0x77617920); + byte_stream_put32(&frame->data, 0x53746174); + byte_stream_put32(&frame->data, 0x7573204d); + byte_stream_put32(&frame->data, 0x65737361); + byte_stream_put32(&frame->data, 0x67650100); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x00000015); + byte_stream_put32(&frame->data, 0x00000000); + byte_stream_put32(&frame->data, 0x0000000d); + byte_stream_put32(&frame->data, 0x00000074); + byte_stream_put32(&frame->data, 0x6578742f); + byte_stream_put32(&frame->data, 0x782d616f); + byte_stream_put32(&frame->data, 0x6c727466); + + byte_stream_put16(&frame->data, 0x0003); /* server ACK requested */ + byte_stream_put16(&frame->data, 0x0000); + + info->uin = atoi(uin); + info->status_note_title = status_note_title; + + memcpy(&info->icbm_cookie, cookie, 8); + + info->next = od->icq_info; + od->icq_info = info; + + flap_connection_send(conn, frame); + } + + g_free(uin); + + } break; + } /* End switch statement */ if (!(snac->flags & 0x0001)) { diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/family_locate.c --- a/libpurple/protocols/oscar/family_locate.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Mon Jan 07 03:40:27 2008 +0000 @@ -320,10 +320,10 @@ cur->away_encoding = g_strdup(userinfo->away_encoding); cur->away_len = userinfo->away_len; - } else if (!(userinfo->flags & AIM_FLAG_AWAY)) { + } else { /* - * We don't have an away message specified in this user_info block. - * If the user is not away, clear any cached away message now. + * We don't have an away message specified in this user_info + * block, so clear any cached away message now. */ if (cur->away) { g_free(cur->away); @@ -347,41 +347,6 @@ userfunc(od, conn, NULL, cur); } -void -aim_locate_dorequest(OscarData *od) -{ - struct userinfo_node *cur = od->locate.torequest; - - if (od->locate.waiting_for_response == TRUE) - return; - - od->locate.waiting_for_response = TRUE; - aim_locate_getinfoshort(od, cur->sn, 0x00000003); - - /* Move this node to the "requested" queue */ - od->locate.torequest = cur->next; - cur->next = od->locate.requested; - od->locate.requested = cur; -} - -static gboolean -purple_reqinfo_timeout_cb(void *data) -{ - OscarData *od; - - od = data; - - if (od->locate.torequest == NULL) - { - od->getinfotimer = 0; - return FALSE; - } - - aim_locate_dorequest(od); - - return TRUE; -} - /** * Remove this screen name from our queue. If this info was requested * by our info request queue, then pop the next element off of the queue. @@ -417,19 +382,6 @@ cur = cur->next; } - if (!was_explicit) { - od->locate.waiting_for_response = FALSE; - - /* - * Wait a little while then call aim_locate_dorequest(od). - * This keeps us from hitting the rate limit due to - * requesting away messages and info too quickly. - */ - if (od->getinfotimer == 0) - od->getinfotimer = purple_timeout_add(500, - purple_reqinfo_timeout_cb, od); - } - return was_explicit; } @@ -438,22 +390,18 @@ { struct userinfo_node *cur; - /* Make sure we aren't already requesting info for this buddy */ - cur = od->locate.torequest; - while (cur != NULL) { + /* Make sure we haven't already requested info for this buddy */ + for (cur = od->locate.requested; cur != NULL; cur = cur->next) if (aim_sncmp(sn, cur->sn) == 0) return; - cur = cur->next; - } /* Add a new node to our request queue */ cur = (struct userinfo_node *)g_malloc(sizeof(struct userinfo_node)); cur->sn = g_strdup(sn); - cur->next = od->locate.torequest; - od->locate.torequest = cur; + cur->next = od->locate.requested; + od->locate.requested = cur; - /* Actually request some info up in this piece */ - aim_locate_dorequest(od); + aim_locate_getinfoshort(od, cur->sn, 0x00000003); } aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn) { diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Mon Jan 07 03:40:27 2008 +0000 @@ -1743,8 +1743,7 @@ int type = 0; gboolean buddy_is_away = FALSE; const char *status_id; - gboolean have_status_message = FALSE; - char *message = NULL; + char *itmsurl = NULL; va_list ap; aim_userinfo_t *info; @@ -1792,20 +1791,10 @@ status_id = OSCAR_STATUS_ID_AVAILABLE; } - /* - * Handle the available message. If info->status is NULL then the user - * may or may not have an available message, so don't do anything. If - * info->status is set to the empty string, then the user's client DOES - * support available messages and the user DOES NOT have one set. - * Otherwise info->status contains the available message. - */ - if (info->status != NULL) - { - have_status_message = TRUE; - if (info->status[0] != '\0') - message = oscar_encoding_to_utf8(account, info->status_encoding, - info->status, info->status_len); - } + if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) + /* Grab the iTunes Music Store URL */ + itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding, + info->itmsurl, info->itmsurl_len); if (info->flags & AIM_FLAG_WIRELESS) { @@ -1814,38 +1803,27 @@ purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE); } - if (have_status_message) + if (status_id == OSCAR_STATUS_ID_AVAILABLE) { - if ((!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE)) && (info->itmsurl != NULL)) - { - char *itmsurl; - itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding, - info->itmsurl, info->itmsurl_len); - purple_prpl_got_user_status(account, info->sn, status_id, - "message", message, "itmsurl", itmsurl, NULL); - g_free(itmsurl); - } - else - { - purple_prpl_got_user_status(account, info->sn, status_id, - "message", message, NULL); - } + char *message = NULL; + + if (info->status != NULL && info->status[0] != '\0') + /* Grab the available message */ + message = oscar_encoding_to_utf8(account, info->status_encoding, + info->status, info->status_len); + + purple_prpl_got_user_status(account, info->sn, status_id, + "message", message, "itmsurl", itmsurl, NULL); + g_free(message); } else { - PurpleBuddy *b = purple_find_buddy(account, info->sn); - PurpleStatus *status = NULL; - const char *active_status_id = NULL; - - if (b != NULL) { - status = purple_presence_get_active_status(purple_buddy_get_presence(b)); - active_status_id = purple_status_get_id(status); - } - - if (!active_status_id || strcmp(active_status_id, status_id)) - purple_prpl_got_user_status(account, info->sn, status_id, NULL); - } + purple_prpl_got_user_status(account, info->sn, status_id, + "itmsurl", itmsurl, NULL); + } + + g_free(itmsurl); /* Login time stuff */ if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) @@ -1900,6 +1878,31 @@ g_free(b16); } + /* + * If we didn't receive a status message with the status change, + * or if the message is empty, and we have a note hash, then + * query the ICQ6 status note. + * + * TODO: We should probably always query the status note regardless + * of whether they have a status message set, and we should + * figure out a way to display both the status note and the + * status message at the same time. + */ + if (info->status == NULL || info->status[0] == '\0') + { + struct aim_ssi_item *ssi_item; + aim_tlv_t *note_hash; + + ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, + NULL, info->sn, AIM_SSI_TYPE_BUDDY); + if (ssi_item != NULL) + { + note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); + if (note_hash != NULL) + aim_icq_getstatusnote(od, info->sn, note_hash->value, note_hash->length); + } + } + return 1; } @@ -3026,7 +3029,7 @@ if (!aim_snvalid_icq(userinfo->sn)) { - if (strcmp(purple_buddy_get_name(b), userinfo->sn)) + if (strcmp(purple_buddy_get_name(b), userinfo->sn) != 0) serv_got_alias(gc, purple_buddy_get_name(b), userinfo->sn); else serv_got_alias(gc, purple_buddy_get_name(b), NULL); @@ -3035,23 +3038,19 @@ presence = purple_buddy_get_presence(b); status = purple_presence_get_active_status(presence); - if (!purple_status_is_available(status) && purple_status_is_online(status)) + if (purple_status_is_online(status) && !purple_status_is_available(status) && + userinfo->flags & AIM_FLAG_AWAY && userinfo->away_len > 0 && + userinfo->away != NULL && userinfo->away_encoding != NULL) { - if ((userinfo->flags & AIM_FLAG_AWAY) && - (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) { - gchar *charset = oscar_encoding_extract(userinfo->away_encoding); - message = oscar_encoding_to_utf8(account, charset, - userinfo->away, - userinfo->away_len); - g_free(charset); - purple_status_set_attr_string(status, "message", message); - g_free(message); - } - else - /* Set an empty message so that we know not to show "pending" */ - purple_status_set_attr_string(status, "message", ""); - - purple_blist_update_buddy_status(b, status); + gchar *charset = oscar_encoding_extract(userinfo->away_encoding); + message = oscar_encoding_to_utf8(account, charset, + userinfo->away, + userinfo->away_len); + g_free(charset); + purple_prpl_got_user_status(account, userinfo->sn, + purple_status_get_id(status), + "message", message, NULL); + g_free(message); } return 1; @@ -4547,12 +4546,11 @@ /* This is needed for us to un-set any previous away message. */ away = g_strdup(""); } - else if ((primitive == PURPLE_STATUS_AWAY) || - (primitive == PURPLE_STATUS_EXTENDED_AWAY)) + else { htmlaway = purple_status_get_attr_string(status, "message"); if ((htmlaway == NULL) || (*htmlaway == '\0')) - htmlaway = _("Away"); + htmlaway = purple_status_type_get_name(status_type); away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding); if (awaylen > od->rights.maxawaymsglen) @@ -5126,6 +5124,8 @@ char *gname, *gname_utf8, *alias, *alias_utf8; PurpleBuddy *b; PurpleGroup *g; + struct aim_ssi_item *ssi_item; + aim_tlv_t *note_hash; va_list ap; guint16 snac_subtype, type; const char *name; @@ -5193,6 +5193,21 @@ } + ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, + gname, name, AIM_SSI_TYPE_BUDDY); + if (ssi_item != NULL) + { + note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); + if (note_hash != NULL) + aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length); + } + else + { + purple_debug_error("oscar", "purple_ssi_parseaddmod: " + "Could not find ssi item for oncoming buddy %s, " + "group %s\n", name, gname); + } + g_free(gname_utf8); g_free(alias_utf8); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.h Mon Jan 07 03:40:27 2008 +0000 @@ -3,8 +3,6 @@ * This file is the legal property of its developers. * Please see the AUTHORS file distributed alongside this file. * - * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -311,7 +309,7 @@ } #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 -#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777 +#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000 typedef enum { @@ -468,7 +466,6 @@ gboolean icq; guint getblisttimer; - guint getinfotimer; struct { guint maxwatchers; /* max users who can watch you */ @@ -513,9 +510,7 @@ struct { struct aim_userinfo_s *userinfo; - struct userinfo_node *torequest; struct userinfo_node *requested; - gboolean waiting_for_response; } locate; /* Server-stored information (ssi) */ @@ -1329,6 +1324,10 @@ /* we keep track of these in a linked list because we're 1337 */ struct aim_icq_info *next; + + /* status note info */ + guint8 icbm_cookie[8]; + char *status_note_title; }; int aim_icq_reqofflinemsgs(OscarData *od); @@ -1339,7 +1338,7 @@ int aim_icq_getalias(OscarData *od, const char *uin); int aim_icq_getallinfo(OscarData *od, const char *uin); int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias); - +int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len); /* 0x0017 - family_auth.c */ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/oscar/oscar_data.c --- a/libpurple/protocols/oscar/oscar_data.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Mon Jan 07 03:40:27 2008 +0000 @@ -97,8 +97,6 @@ g_free(od->oldp); if (od->getblisttimer > 0) purple_timeout_remove(od->getblisttimer); - if (od->getinfotimer > 0) - purple_timeout_remove(od->getinfotimer); while (od->oscar_connections != NULL) flap_connection_destroy(od->oscar_connections->data, OSCAR_DISCONNECT_DONE, NULL); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/qq/file_trans.c --- a/libpurple/protocols/qq/file_trans.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/qq/file_trans.c Mon Jan 07 03:40:27 2008 +0000 @@ -59,7 +59,7 @@ return key; } -static guint32 _gen_file_key() +static guint32 _gen_file_key(void) { guint8 seed; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/qq/group_opt.c Mon Jan 07 03:40:27 2008 +0000 @@ -39,37 +39,12 @@ #include "packet_parse.h" #include "utils.h" -/* TODO: can't we use qsort here? */ -/* This implement quick sort algorithm (low->high) */ -static void _quick_sort(gint *numbers, gint left, gint right) +static int _compare_guint32(const void *a, + const void *b) { - gint pivot, l_hold, r_hold; - - l_hold = left; - r_hold = right; - pivot = numbers[left]; - while (left < right) { - while ((numbers[right] >= pivot) && (left < right)) - right--; - if (left != right) { - numbers[left] = numbers[right]; - left++; - } - while ((numbers[left] <= pivot) && (left < right)) - left++; - if (left != right) { - numbers[right] = numbers[left]; - right--; - } - } - numbers[left] = pivot; - pivot = left; - left = l_hold; - right = r_hold; - if (left < pivot) - _quick_sort(numbers, left, pivot - 1); - if (right > pivot) - _quick_sort(numbers, pivot + 1, right); + const guint32 *x = a; + const guint32 *y = b; + return (*x - *y); } static void _sort(guint32 *list) @@ -77,7 +52,7 @@ gint i; for (i = 0; list[i] < 0xffffffff; i++) {; } - _quick_sort((gint *) list, 0, i - 1); + qsort (list, i, sizeof (guint32), _compare_guint32); } static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members) diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/sametime/sametime.c Mon Jan 07 03:40:27 2008 +0000 @@ -3749,7 +3749,7 @@ client = purple_account_get_int(account, MW_KEY_CLIENT, mwLogin_BINARY); major = purple_account_get_int(account, MW_KEY_MAJOR, 0x001e); - minor = purple_account_get_int(account, MW_KEY_MINOR, 0x001d); + minor = purple_account_get_int(account, MW_KEY_MINOR, 0x196f); DEBUG_INFO("client id: 0x%04x\n", client); DEBUG_INFO("client major: 0x%04x\n", major); @@ -3806,7 +3806,7 @@ } -static int mw_rand() { +static int mw_rand(void) { static int seed = 0; /* for diversity, not security. don't touch */ @@ -3818,7 +3818,7 @@ /** generates a random-ish content id string */ -static char *im_mime_content_id() { +static char *im_mime_content_id(void) { return g_strdup_printf("%03x@%05xmeanwhile", mw_rand() & 0xfff, mw_rand() & 0xfffff); } @@ -3826,7 +3826,7 @@ /** generates a multipart/related content type with a random-ish boundary value */ -static char *im_mime_content_type() { +static char *im_mime_content_type(void) { return g_strdup_printf("multipart/related; boundary=related_MW%03x_%04x", mw_rand() & 0xfff, mw_rand() & 0xffff); } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/simple/simple.c --- a/libpurple/protocols/simple/simple.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/simple/simple.c Mon Jan 07 03:40:27 2008 +0000 @@ -45,17 +45,17 @@ #include "dnssrv.h" #include "ntlm.h" -static char *gentag() { +static char *gentag(void) { return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF); } -static char *genbranch() { +static char *genbranch(void) { return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X", rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF); } -static char *gencallid() { +static char *gencallid(void) { return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx", rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/yahoo/util.c --- a/libpurple/protocols/yahoo/util.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/yahoo/util.c Mon Jan 07 03:40:27 2008 +0000 @@ -736,12 +736,15 @@ } else if (((len - i) >= 4) && !strncmp(&src[i], ">", 4)) { g_string_append_c(dest, '>'); i += 3; - } else if (((len - i) >= 5) && !strncmp(&src[i], "&", 4)) { + } else if (((len - i) >= 5) && !strncmp(&src[i], "&", 5)) { g_string_append_c(dest, '&'); i += 4; - } else if (((len - i) >= 6) && !strncmp(&src[i], """, 4)) { + } else if (((len - i) >= 6) && !strncmp(&src[i], """, 6)) { g_string_append_c(dest, '"'); i += 5; + } else if (((len - i) >= 6) && !strncmp(&src[i], "'", 6)) { + g_string_append_c(dest, '\''); + i += 5; } else { g_string_append_c(dest, src[i]); } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/zephyr/ZSendList.c --- a/libpurple/protocols/zephyr/ZSendList.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/zephyr/ZSendList.c Mon Jan 07 03:40:27 2008 +0000 @@ -24,7 +24,7 @@ char *list[]; int nitems; Z_AuthProc cert_routine; - Code_t (*send_routine)(); + Code_t (*send_routine)(void); { Code_t retval; ZNotice_t newnotice; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/zephyr/ZSendNot.c --- a/libpurple/protocols/zephyr/ZSendNot.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/zephyr/ZSendNot.c Mon Jan 07 03:40:27 2008 +0000 @@ -20,7 +20,7 @@ Code_t ZSrvSendNotice(notice, cert_routine, send_routine) ZNotice_t *notice; Z_AuthProc cert_routine; - Code_t (*send_routine)(); + Code_t (*send_routine)(void); { Code_t retval; ZNotice_t newnotice; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/zephyr/Zinternal.c --- a/libpurple/protocols/zephyr/Zinternal.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/zephyr/Zinternal.c Mon Jan 07 03:40:27 2008 +0000 @@ -33,8 +33,6 @@ #include #endif -extern char *inet_ntoa (); - int __Zephyr_fd = -1; int __Zephyr_open; int __Zephyr_port = -1; @@ -144,7 +142,7 @@ /* Return 1 if there is a packet waiting, 0 otherwise */ -static int Z_PacketWaiting() +static int Z_PacketWaiting(void) { struct timeval tv; fd_set read; @@ -158,7 +156,7 @@ /* Wait for a complete notice to become available */ -Code_t Z_WaitForComplete() +Code_t Z_WaitForComplete(void) { Code_t retval; @@ -195,9 +193,7 @@ * notices that haven't been touched in a while */ -static struct _Z_InputQ *Z_SearchQueue(uid, kind) - ZUnique_Id_t *uid; - ZNotice_Kind_t kind; +static struct _Z_InputQ *Z_SearchQueue(ZUnique_Id_t *uid, ZNotice_Kind_t kind) { register struct _Z_InputQ *qptr; struct _Z_InputQ *next; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/zephyr/zephyr.c --- a/libpurple/protocols/zephyr/zephyr.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Mon Jan 07 03:40:27 2008 +0000 @@ -53,7 +53,7 @@ extern Code_t ZGetLocations(ZLocations_t *, int *); extern Code_t ZSetLocation(char *); -extern Code_t ZUnsetLocation(); +extern Code_t ZUnsetLocation(void); extern Code_t ZGetSubscriptions(ZSubscription_t *, int*); extern char __Zephyr_realm[]; typedef struct _zframe zframe; @@ -1386,7 +1386,7 @@ #endif /* WIN32 */ -static char *get_exposure_level() +static char *get_exposure_level(void) { /* XXX add real error reporting */ char *exposure = ZGetVariable("exposure"); @@ -2058,7 +2058,7 @@ static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im, const char *sig, char *opcode) ; -static const char * zephyr_get_signature() +static const char * zephyr_get_signature(void) { /* XXX add zephyr error reporting */ const char * sig =ZGetVariable("zwrite-signature"); @@ -2676,7 +2676,7 @@ return PURPLE_CMD_RET_FAILED; } -static void zephyr_register_slash_commands() +static void zephyr_register_slash_commands(void) { purple_cmd_register("msg","ws", PURPLE_CMD_P_PRPL, diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/protocols/zephyr/zephyr.h --- a/libpurple/protocols/zephyr/zephyr.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/protocols/zephyr/zephyr.h Mon Jan 07 03:40:27 2008 +0000 @@ -163,9 +163,9 @@ Code_t ZReadAscii16 ZP((char *, int, unsigned short *)); Code_t ZSendPacket ZP((char*, int, int)); Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc)); -Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)())); +Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)(void))); Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc)); -Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)())); +Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)(void))); Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc)); Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc)); Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems, diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/purple-remote --- a/libpurple/purple-remote Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/purple-remote Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import dbus import re diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/purple-send --- a/libpurple/purple-send Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/purple-send Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh METHOD_NAME=$1 diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/purple-send-async --- a/libpurple/purple-send-async Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/purple-send-async Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh METHOD_NAME=$1 diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/purple-url-handler --- a/libpurple/purple-url-handler Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/purple-url-handler Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import dbus import re diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/request.c --- a/libpurple/request.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/request.c Mon Jan 07 03:40:27 2008 +0000 @@ -1208,6 +1208,7 @@ g_return_val_if_fail(ok_text != NULL, NULL); g_return_val_if_fail(ok_cb != NULL, NULL); + g_return_val_if_fail(cancel_text != NULL, NULL); ops = purple_request_get_ui_ops(); @@ -1296,6 +1297,7 @@ g_return_val_if_fail(fields != NULL, NULL); g_return_val_if_fail(ok_text != NULL, NULL); g_return_val_if_fail(ok_cb != NULL, NULL); + g_return_val_if_fail(cancel_text != NULL, NULL); ops = purple_request_get_ui_ops(); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/request.h --- a/libpurple/request.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/request.h Mon Jan 07 03:40:27 2008 +0000 @@ -183,39 +183,53 @@ */ typedef struct { + /** @see purple_request_input(). */ void *(*request_input)(const char *title, const char *primary, - const char *secondary, const char *default_value, - gboolean multiline, gboolean masked, gchar *hint, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + const char *secondary, const char *default_value, + gboolean multiline, gboolean masked, gchar *hint, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data); + + /** @see purple_request_choice_varg(). */ void *(*request_choice)(const char *title, const char *primary, - const char *secondary, int default_value, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, va_list choices); + const char *secondary, int default_value, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data, + va_list choices); + + /** @see purple_request_action_varg(). */ void *(*request_action)(const char *title, const char *primary, - const char *secondary, int default_action, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, size_t action_count, - va_list actions); + const char *secondary, int default_action, + PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data, + size_t action_count, va_list actions); + + /** @see purple_request_fields(). */ void *(*request_fields)(const char *title, const char *primary, - const char *secondary, PurpleRequestFields *fields, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + const char *secondary, PurpleRequestFields *fields, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data); + + /** @see purple_request_file(). */ void *(*request_file)(const char *title, const char *filename, - gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + gboolean savedialog, GCallback ok_cb, + GCallback cancel_cb, PurpleAccount *account, + const char *who, PurpleConversation *conv, + void *user_data); + void (*close_request)(PurpleRequestType type, void *ui_handle); + + /** @see purple_request_folder(). */ void *(*request_folder)(const char *title, const char *dirname, - GCallback ok_cb, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + GCallback ok_cb, GCallback cancel_cb, + PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data); void (*_purple_reserved1)(void); void (*_purple_reserved2)(void); @@ -1159,198 +1173,247 @@ * Prompts the user for text input. * * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. The - * handle is used to programmatically close - * the request dialog when it is no longer - * needed. For PRPLs this is often a pointer - * to the PurpleConnection instance. For plugins - * this should be a similar, unique memory - * location. This value is important because - * it allows a request to be closed, say, when - * you sign offline. If the request is NOT - * closed it is VERY likely to cause a crash - * whenever the callback handler functions are - * triggered. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. + * things this is extremely important. The + * handle is used to programmatically close the request + * dialog when it is no longer needed. For PRPLs this + * is often a pointer to the #PurpleConnection + * instance. For plugins this should be a similar, + * unique memory location. This value is important + * because it allows a request to be closed with + * purple_request_close_with_handle() when, for + * example, you sign offline. If the request is + * not closed it is very + * likely to cause a crash whenever the callback + * handler functions are triggered. + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. * @param default_value The default value. - * @param multiline TRUE if the inputted text can span multiple lines. - * @param masked TRUE if the inputted text should be masked in some way. + * @param multiline @c TRUE if the inputted text can span multiple lines. + * @param masked @c TRUE if the inputted text should be masked in some + * way (such as by displaying characters as stars). This + * might be because the input is some kind of password. * @param hint Optionally suggest how the input box should appear. - * Use "html," for example, to allow the user to enter + * Use "html", for example, to allow the user to enter * HTML. - * @param ok_text The text for the @c OK button. - * @param ok_cb The callback for the @c OK button. - * @param cancel_text The text for the @c Cancel button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param ok_text The text for the @c OK button, which may not be @c NULL. + * @param ok_cb The callback for the @c OK button, which may not be @c + * NULL. + * @param cancel_text The text for the @c Cancel button, which may not be @c + * NULL. + * @param cancel_cb The callback for the @c Cancel button, which may be + * @c NULL. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is. + * @param who The username of the buddy associated with this request, + * or @c NULL if none is. + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is. * @param user_data The data to pass to the callback. * * @return A UI-specific handle. */ -void *purple_request_input(void *handle, const char *title, - const char *primary, const char *secondary, - const char *default_value, - gboolean multiline, gboolean masked, gchar *hint, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); +void *purple_request_input(void *handle, const char *title, const char *primary, + const char *secondary, const char *default_value, gboolean multiline, + gboolean masked, gchar *hint, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data); /** * Prompts the user for multiple-choice input. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. - * @param default_value The default value. - * @param ok_text The text for the @c OK button. - * @param ok_cb The callback for the @c OK button. - * @param cancel_text The text for the @c Cancel button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. + * @param default_value The default choice; this should be one of the values + * listed in the varargs. + * @param ok_text The text for the @c OK button, which may not be @c NULL. + * @param ok_cb The callback for the @c OK button, which may not be @c + * NULL. + * @param cancel_text The text for the @c Cancel button, which may not be @c + * NULL. + * @param cancel_cb The callback for the @c Cancel button, or @c NULL to + * do nothing. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is. + * @param who The username of the buddy associated with this request, + * or @c NULL if none is. + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is. * @param user_data The data to pass to the callback. - * @param ... The choices. This argument list should be - * terminated with a NULL parameter. + * @param ... The choices, which should be pairs of char * + * descriptions and int values, terminated with a + * @c NULL parameter. * * @return A UI-specific handle. */ -void *purple_request_choice(void *handle, const char *title, - const char *primary, const char *secondary, - int default_value, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, ...) G_GNUC_NULL_TERMINATED; +void *purple_request_choice(void *handle, const char *title, const char *primary, + const char *secondary, int default_value, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data, ...) G_GNUC_NULL_TERMINATED; /** * Prompts the user for multiple-choice input. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. - * @param default_value The default value. - * @param ok_text The text for the @c OK button. - * @param ok_cb The callback for the @c OK button. - * @param cancel_text The text for the @c Cancel button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. + * @param default_value The default choice; this should be one of the values + * listed in the varargs. + * @param ok_text The text for the @c OK button, which may not be @c NULL. + * @param ok_cb The callback for the @c OK button, which may not be @c + * NULL. + * @param cancel_text The text for the @c Cancel button, which may not be @c + * NULL. + * @param cancel_cb The callback for the @c Cancel button, or @c NULL to do + * nothing. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is + * @param who The username of the buddy associated with this request, + * or @c NULL if none is + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is * @param user_data The data to pass to the callback. - * @param choices The choices. This argument list should be - * terminated with a @c NULL parameter. + * @param choices The choices, which should be pairs of char * + * descriptions and int values, terminated with a + * @c NULL parameter. * * @return A UI-specific handle. */ void *purple_request_choice_varg(void *handle, const char *title, - const char *primary, const char *secondary, - int default_value, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, va_list choices); + const char *primary, const char *secondary, int default_value, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data, va_list choices); /** * Prompts the user for an action. * * This is often represented as a dialog with a button for each action. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. - * @param default_action The default value. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. + * @param default_action The default action, zero-indexed; if the third action + * supplied should be the default, supply 2. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is. + * @param who The username of the buddy associated with this request, + * or @c NULL if none is. + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is. * @param user_data The data to pass to the callback. * @param action_count The number of actions. * @param ... A list of actions. These are pairs of * arguments. The first of each pair is the - * string that appears on the button. It should + * char * that appears on the button. It should * have an underscore before the letter you want * to use as the accelerator key for the button. - * The second of each pair is the callback + * The second of each pair is the GCallback * function to use when the button is clicked. * * @return A UI-specific handle. */ -void *purple_request_action(void *handle, const char *title, - const char *primary, const char *secondary, - int default_action, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, size_t action_count, ...); +void *purple_request_action(void *handle, const char *title, const char *primary, + const char *secondary, int default_action, PurpleAccount *account, + const char *who, PurpleConversation *conv, void *user_data, + size_t action_count, ...); /** * Prompts the user for an action. * * This is often represented as a dialog with a button for each action. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. - * @param default_action The default value. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. + * @param default_action The default action, zero-indexed; if the third action + * supplied should be the default, supply 2. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is. + * @param who The username of the buddy associated with this request, + * or @c NULL if none is. + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is. * @param user_data The data to pass to the callback. * @param action_count The number of actions. - * @param actions A list of actions and callbacks. + * @param actions A list of actions. These are pairs of + * arguments. The first of each pair is the + * char * that appears on the button. It should + * have an underscore before the letter you want + * to use as the accelerator key for the button. + * The second of each pair is the GCallback + * function to use when the button is clicked. * * @return A UI-specific handle. */ void *purple_request_action_varg(void *handle, const char *title, - const char *primary, const char *secondary, - int default_action, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data, size_t action_count, - va_list actions); + const char *primary, const char *secondary, int default_action, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data, size_t action_count, va_list actions); /** * Displays groups of fields for the user to fill in. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title of the message. - * @param primary The main point of the message. - * @param secondary The secondary information. + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. + * @param primary The main point of the message, or @c NULL if you're + * feeling enigmatic. + * @param secondary Secondary information, or @c NULL if there is none. * @param fields The list of fields. - * @param ok_text The text for the @c OK button. - * @param ok_cb The callback for the @c OK button. - * @param cancel_text The text for the @c Cancel button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy associated with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param ok_text The text for the @c OK button, which may not be @c NULL. + * @param ok_cb The callback for the @c OK button, which may not be @c + * NULL. + * @param cancel_text The text for the @c Cancel button, which may not be @c + * NULL. + * @param cancel_cb The callback for the @c Cancel button, which may be + * @c NULL. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is + * @param who The username of the buddy associated with this request, + * or @c NULL if none is + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is * @param user_data The data to pass to the callback. * * @return A UI-specific handle. */ -void *purple_request_fields(void *handle, const char *title, - const char *primary, const char *secondary, - PurpleRequestFields *fields, - const char *ok_text, GCallback ok_cb, - const char *cancel_text, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); +void *purple_request_fields(void *handle, const char *title, const char *primary, + const char *secondary, PurpleRequestFields *fields, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data); /** * Closes a request. @@ -1363,7 +1426,10 @@ /** * Closes all requests registered with the specified handle. * - * @param handle The handle. + * @param handle The handle, as supplied as the @a handle parameter to one of the + * purple_request_* functions. + * + * @see purple_request_input(). */ void purple_request_close_with_handle(void *handle); @@ -1401,50 +1467,57 @@ * Displays a file selector request dialog. Returns the selected filename to * the callback. Can be used for either opening a file or saving a file. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title for the dialog (may be @c NULL) + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. * @param filename The default filename (may be @c NULL) * @param savedialog True if this dialog is being used to save a file. * False if it is being used to open a file. * @param ok_cb The callback for the @c OK button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param cancel_cb The callback for the @c Cancel button, which may be @c NULL. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is + * @param who The username of the buddy associated with this request, + * or @c NULL if none is + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is * @param user_data The data to pass to the callback. * * @return A UI-specific handle. */ void *purple_request_file(void *handle, const char *title, const char *filename, - gboolean savedialog, - GCallback ok_cb, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data); /** * Displays a folder select dialog. Returns the selected filename to * the callback. * - * @param handle The plugin or connection handle. For some - * things this is EXTREMELY important. See - * the comments on purple_request_input. - * @param title The title for the dialog (may be @c NULL) + * @param handle The plugin or connection handle. For some things this + * is extremely important. See the comments on + * purple_request_input(). + * @param title The title of the message, or @c NULL if it should have + * no title. * @param dirname The default directory name (may be @c NULL) * @param ok_cb The callback for the @c OK button. - * @param cancel_cb The callback for the @c Cancel button. - * @param account The PurpleAccount associated with this request, or NULL if none is - * @param who The username of the buddy assocaited with this request, or NULL if none is - * @param conv The PurpleConversation associated with this request, or NULL if none is + * @param cancel_cb The callback for the @c Cancel button, which may be @c NULL. + * @param account The #PurpleAccount associated with this request, or @c + * NULL if none is + * @param who The username of the buddy associated with this request, + * or @c NULL if none is + * @param conv The #PurpleConversation associated with this request, or + * @c NULL if none is * @param user_data The data to pass to the callback. * * @return A UI-specific handle. */ void *purple_request_folder(void *handle, const char *title, const char *dirname, - GCallback ok_cb, GCallback cancel_cb, - PurpleAccount *account, const char *who, PurpleConversation *conv, - void *user_data); + GCallback ok_cb, GCallback cancel_cb, + PurpleAccount *account, const char *who, PurpleConversation *conv, + void *user_data); /*@}*/ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/roomlist.c --- a/libpurple/roomlist.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/roomlist.c Mon Jan 07 03:40:27 2008 +0000 @@ -218,6 +218,11 @@ prpl_info->roomlist_expand_category(list, category); } +GList * purple_roomlist_get_fields(PurpleRoomlist *list) +{ + return list->fields; +} + /*@}*/ /**************************************************************************/ @@ -293,6 +298,26 @@ g_hash_table_destroy(components); } +PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room) +{ + return room->type; +} + +const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room) +{ + return room->name; +} + +PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room) +{ + return room->parent; +} + +GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room) +{ + return room->fields; +} + /*@}*/ /**************************************************************************/ @@ -319,6 +344,21 @@ return f; } +PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field) +{ + return field->type; +} + +const char * purple_roomlist_field_get_label(PurpleRoomlistField *field) +{ + return field->label; +} + +gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field) +{ + return field->hidden; +} + /*@}*/ /**************************************************************************/ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/roomlist.h --- a/libpurple/roomlist.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/roomlist.h Mon Jan 07 03:40:27 2008 +0000 @@ -237,6 +237,15 @@ */ void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category); +/** + * Get the list of fields for a roomlist. + * + * @param roomlist The roomlist, which must not be @c NULL. + * @constreturn A list of fields + * @since 2.4.0 + */ +GList * purple_roomlist_get_fields(PurpleRoomlist *roomlist); + /*@}*/ /**************************************************************************/ @@ -273,6 +282,39 @@ */ void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room); +/** + * Get the type of a room. + * @param room The room, which must not be @c NULL. + * @return The type of the room. + * @since 2.4.0 + */ +PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room); + +/** + * Get the name of a room. + * @param room The room, which must not be @c NULL. + * @return The name of the room. + * @since 2.4.0 + */ +const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room); + +/** + * Get the parent of a room. + * @param room The room, which must not be @c NULL. + * @return The parent of the room, which can be @c NULL. + * @since 2.4.0 + */ +PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room); + +/** + * Get the list of fields for a room. + * + * @param room The room, which must not be @c NULL. + * @constreturn A list of fields + * @since 2.4.0 + */ +GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room); + /*@}*/ /**************************************************************************/ @@ -294,6 +336,36 @@ PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type, const gchar *label, const gchar *name, gboolean hidden); + +/** + * Get the type of a field. + * + * @param field A PurpleRoomlistField, which must not be @c NULL. + * + * @return The type of the field. + * @since 2.4.0 + */ +PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field); + +/** + * Get the label of a field. + * + * @param field A PurpleRoomlistField, which must not be @c NULL. + * + * @return The label of the field. + * @since 2.4.0 + */ +const char * purple_roomlist_field_get_label(PurpleRoomlistField *field); + +/** + * Check whether a roomlist-field is hidden. + * @param field A PurpleRoomlistField, which must not be @c NULL. + * + * @return @c TRUE if the field is hidden, @c FALSE otherwise. + * @since 2.4.0 + */ +gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field); + /*@}*/ /**************************************************************************/ diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/savedstatuses.c --- a/libpurple/savedstatuses.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/savedstatuses.c Mon Jan 07 03:40:27 2008 +0000 @@ -190,7 +190,7 @@ * does the expiration. */ static void -remove_old_transient_statuses() +remove_old_transient_statuses(void) { GList *l, *next; PurpleSavedStatus *saved_status, *current_status; diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/signals.h --- a/libpurple/signals.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/signals.h Mon Jan 07 03:40:27 2008 +0000 @@ -44,11 +44,24 @@ /**************************************************************************/ /*@{*/ -/** - * Signal Connect Priorities +/** The priority of a signal connected using purple_signal_connect(). + * + * @see purple_signal_connect_priority() */ #define PURPLE_SIGNAL_PRIORITY_DEFAULT 0 +/** The largest signal priority; signals with this priority will be called + * last. (This is highest as in numerical value, not as in order of + * importance.) + * + * @see purple_signal_connect_priority(). + */ #define PURPLE_SIGNAL_PRIORITY_HIGHEST 9999 +/** The smallest signal priority; signals with this priority will be called + * first. (This is lowest as in numerical value, not as in order of + * importance.) + * + * @see purple_signal_connect_priority(). + */ #define PURPLE_SIGNAL_PRIORITY_LOWEST -9999 /** @@ -109,19 +122,21 @@ * @param handle The handle of the receiver. * @param func The callback function. * @param data The data to pass to the callback function. - * @param priority The priority with which the handler should be called. Signal handlers are called - * in order from PURPLE_SIGNAL_PRIORITY_LOWEST to PURPLE_SIGNAL_PRIORITY_HIGHEST. + * @param priority The priority with which the handler should be called. Signal + * handlers are called in ascending numerical order of @a + * priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to + * #PURPLE_SIGNAL_PRIORITY_HIGHEST. * * @return The signal handler ID. * * @see purple_signal_disconnect() */ gulong purple_signal_connect_priority(void *instance, const char *signal, - void *handle, PurpleCallback func, void *data, int priority); + void *handle, PurpleCallback func, void *data, int priority); /** * Connects a signal handler to a signal for a particular object. - * (priority defaults to 0) + * (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.) * * Take care not to register a handler function twice. Purple will * not correct any mistakes for you in this area. @@ -137,7 +152,7 @@ * @see purple_signal_disconnect() */ gulong purple_signal_connect(void *instance, const char *signal, - void *handle, PurpleCallback func, void *data); + void *handle, PurpleCallback func, void *data); /** * Connects a signal handler to a signal for a particular object. @@ -153,18 +168,22 @@ * @param handle The handle of the receiver. * @param func The callback function. * @param data The data to pass to the callback function. - * @param priority The order in which the signal should be added to the list + * @param priority The priority with which the handler should be called. Signal + * handlers are called in ascending numerical order of @a + * priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to + * #PURPLE_SIGNAL_PRIORITY_HIGHEST. * * @return The signal handler ID. * * @see purple_signal_disconnect() */ gulong purple_signal_connect_priority_vargs(void *instance, const char *signal, - void *handle, PurpleCallback func, void *data, int priority); + void *handle, PurpleCallback func, void *data, int priority); /** * Connects a signal handler to a signal for a particular object. - * (priority defaults to 0) + * (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.) + * * The signal handler will take a va_args of arguments, instead of * individual arguments. * @@ -182,7 +201,7 @@ * @see purple_signal_disconnect() */ gulong purple_signal_connect_vargs(void *instance, const char *signal, - void *handle, PurpleCallback func, void *data); + void *handle, PurpleCallback func, void *data); /** * Disconnects a signal handler from a signal on an object. diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/status.c --- a/libpurple/status.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/status.c Mon Jan 07 03:40:27 2008 +0000 @@ -607,13 +607,10 @@ if (old_status != NULL) { - tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias, + tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name, purple_status_get_name(old_status), purple_status_get_name(new_status)); - logtmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name, - purple_status_get_name(old_status), - purple_status_get_name(new_status)); - + logtmp = g_markup_escape_text(tmp, -1); } else { @@ -621,18 +618,15 @@ if (purple_status_is_active(new_status)) { - tmp = g_strdup_printf(_("%s is now %s"), buddy_alias, + tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name, purple_status_get_name(new_status)); - logtmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name, - purple_status_get_name(new_status)); - + logtmp = g_markup_escape_text(tmp, -1); } else { - tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias, + tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name, purple_status_get_name(new_status)); - logtmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name, - purple_status_get_name(new_status)); + logtmp = g_markup_escape_text(tmp, -1); } } @@ -1244,12 +1238,15 @@ if (log != NULL) { - char *tmp = g_strdup_printf(_("%s became idle"), + char *tmp, *tmp2; + tmp = g_strdup_printf(_("%s became idle"), purple_buddy_get_alias(buddy)); + tmp2 = g_markup_escape_text(tmp, -1); + g_free(tmp); purple_log_write(log, PURPLE_MESSAGE_SYSTEM, - purple_buddy_get_alias(buddy), current_time, tmp); - g_free(tmp); + purple_buddy_get_alias(buddy), current_time, tmp2); + g_free(tmp2); } } } @@ -1261,12 +1258,15 @@ if (log != NULL) { - char *tmp = g_strdup_printf(_("%s became unidle"), + char *tmp, *tmp2; + tmp = g_strdup_printf(_("%s became unidle"), purple_buddy_get_alias(buddy)); + tmp2 = g_markup_escape_text(tmp, -1); + g_free(tmp); purple_log_write(log, PURPLE_MESSAGE_SYSTEM, - purple_buddy_get_alias(buddy), current_time, tmp); - g_free(tmp); + purple_buddy_get_alias(buddy), current_time, tmp2); + g_free(tmp2); } } } @@ -1321,13 +1321,15 @@ if (log != NULL) { - char *msg; + char *msg, *tmp; if (idle) - msg = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account)); + tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account)); else - msg = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account)); + tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account)); + msg = g_markup_escape_text(tmp, -1); + g_free(tmp); purple_log_write(log, PURPLE_MESSAGE_SYSTEM, purple_account_get_username(account), (idle ? idle_time : current_time), msg); diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/stun.c --- a/libpurple/stun.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/stun.c Mon Jan 07 03:40:27 2008 +0000 @@ -104,7 +104,7 @@ g_free(sc); } -static void do_callbacks() { +static void do_callbacks(void) { while(callbacks) { StunCallback cb = callbacks->data; if(cb) diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/tests/test_cipher.c --- a/libpurple/tests/test_cipher.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/tests/test_cipher.c Mon Jan 07 03:40:27 2008 +0000 @@ -190,6 +190,511 @@ END_TEST /****************************************************************************** + * DES Tests + *****************************************************************************/ +#define DES_TEST(in, keyz, out, len) { \ + PurpleCipher *cipher = NULL; \ + PurpleCipherContext *context = NULL; \ + guchar answer[len+1]; \ + gint ret = 0; \ + guchar decrypt[len+1] = in; \ + guchar key[8+1] = keyz;\ + guchar encrypt[len+1] = out;\ + size_t outlen; \ + \ + cipher = purple_ciphers_find_cipher("des"); \ + context = purple_cipher_context_new(cipher, NULL); \ + purple_cipher_context_set_key(context, key); \ + \ + ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \ + fail_unless(ret == 0, NULL); \ + fail_unless(outlen == (len), NULL); \ + fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \ + \ + ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \ + fail_unless(ret == 0, NULL); \ + fail_unless(outlen == (len), NULL); \ + fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \ + \ + purple_cipher_context_destroy(context); \ +} + +START_TEST(test_des_12345678) { + DES_TEST("12345678", + "\x3b\x38\x98\x37\x15\x20\xf7\x5e", + "\x06\x22\x05\xac\x6a\x0d\x55\xdd", + 8); +} +END_TEST + +START_TEST(test_des_abcdefgh) { + DES_TEST("abcdefgh", + "\x3b\x38\x98\x37\x15\x20\xf7\x5e", + "\x62\xe0\xc6\x8c\x48\xe4\x75\xed", + 8); +} +END_TEST + +/****************************************************************************** + * DES3 Tests + * See http://csrc.nist.gov/groups/ST/toolkit/examples.html + * and some NULL things I made up + *****************************************************************************/ + +#define DES3_TEST(in, key, iv, out, len, mode) { \ + PurpleCipher *cipher = NULL; \ + PurpleCipherContext *context = NULL; \ + guchar answer[len+1]; \ + guchar decrypt[len+1] = in; \ + guchar encrypt[len+1] = out; \ + size_t outlen; \ + gint ret = 0; \ + \ + cipher = purple_ciphers_find_cipher("des3"); \ + context = purple_cipher_context_new(cipher, NULL); \ + purple_cipher_context_set_key(context, (guchar *)key); \ + purple_cipher_context_set_batch_mode(context, (mode)); \ + purple_cipher_context_set_iv(context, (guchar *)iv, 8); \ + \ + ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \ + fail_unless(ret == 0, NULL); \ + fail_unless(outlen == (len), NULL); \ + fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \ + \ + ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \ + fail_unless(ret == 0, NULL); \ + fail_unless(outlen == (len), NULL); \ + fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \ + \ + purple_cipher_context_destroy(context); \ +} + +START_TEST(test_des3_ecb_nist1) { + DES3_TEST( + "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A" + "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x23\x45\x67\x89\xAB\xCD\xEF\x01" + "\x45\x67\x89\xAB\xCD\xEF\x01\x23", + "00000000", /* ignored */ + "\x71\x47\x72\xF3\x39\x84\x1D\x34\x26\x7F\xCC\x4B\xD2\x94\x9C\xC3" + "\xEE\x11\xC2\x2A\x57\x6A\x30\x38\x76\x18\x3F\x99\xC0\xB6\xDE\x87", + 32, + PURPLE_CIPHER_BATCH_MODE_ECB); +} +END_TEST + +START_TEST(test_des3_ecb_nist2) { + DES3_TEST( + "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A" + "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x23\x45\x67\x89\xAB\xCD\xEF\x01" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF", + "00000000", /* ignored */ + "\x06\xED\xE3\xD8\x28\x84\x09\x0A\xFF\x32\x2C\x19\xF0\x51\x84\x86" + "\x73\x05\x76\x97\x2A\x66\x6E\x58\xB6\xC8\x8C\xF1\x07\x34\x0D\x3D", + 32, + PURPLE_CIPHER_BATCH_MODE_ECB); +} +END_TEST + +START_TEST(test_des3_ecb_null_key) { + DES3_TEST( + "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca", + "\x38\x00\x88\x6a\xef\xcb\x00\xad" + "\x5d\xe5\x29\x00\x7d\x98\x64\x4c" + "\x86\x00\x7b\xd3\xc7\x00\x7b\x32", + "00000000", /* ignored */ + "\xc0\x60\x30\xa1\xb7\x25\x42\x44", + 8, + PURPLE_CIPHER_BATCH_MODE_ECB); +} +END_TEST + +START_TEST(test_des3_ecb_null_text) { + DES3_TEST( + "\x65\x73\x34\xc1\x19\x00\x79\x65", + "\x32\x64\xda\x10\x13\x6a\xfe\x1e" + "\x37\x54\xd1\x2c\x41\x04\x10\x40" + "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5", + "00000000", /* ignored */ + "\xe5\x80\xf6\x12\xf8\x4e\xd9\x6c", + 8, + PURPLE_CIPHER_BATCH_MODE_ECB); +} +END_TEST + +START_TEST(test_des3_ecb_null_key_and_text) { + DES3_TEST( + "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2", + "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1" + "\x66\x49\x42\x63\x22\x00\xf0\x99" + "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f", + "00000000", /* ignored */ + "\x73\xd8\x1f\x1f\x50\x01\xe4\x79", + 8, + PURPLE_CIPHER_BATCH_MODE_ECB); +} +END_TEST + +START_TEST(test_des3_cbc_nist1) { + DES3_TEST( + "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A" + "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x23\x45\x67\x89\xAB\xCD\xEF\x01" + "\x45\x67\x89\xAB\xCD\xEF\x01\x23", + "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17", + "\x20\x79\xC3\xD5\x3A\xA7\x63\xE1\x93\xB7\x9E\x25\x69\xAB\x52\x62" + "\x51\x65\x70\x48\x1F\x25\xB5\x0F\x73\xC0\xBD\xA8\x5C\x8E\x0D\xA7", + 32, + PURPLE_CIPHER_BATCH_MODE_CBC); +} +END_TEST + +START_TEST(test_des3_cbc_nist2) { + DES3_TEST( + "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A" + "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + "\x23\x45\x67\x89\xAB\xCD\xEF\x01" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF", + "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17", + "\x74\x01\xCE\x1E\xAB\x6D\x00\x3C\xAF\xF8\x4B\xF4\x7B\x36\xCC\x21" + "\x54\xF0\x23\x8F\x9F\xFE\xCD\x8F\x6A\xCF\x11\x83\x92\xB4\x55\x81", + 32, + PURPLE_CIPHER_BATCH_MODE_CBC); +} +END_TEST + +START_TEST(test_des3_cbc_null_key) { + DES3_TEST( + "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca", + "\x38\x00\x88\x6a\xef\xcb\x00\xad" + "\x5d\xe5\x29\x00\x7d\x98\x64\x4c" + "\x86\x00\x7b\xd3\xc7\x00\x7b\x32", + "\x31\x32\x33\x34\x35\x36\x37\x38", + "\x52\xe7\xde\x96\x39\x87\x87\xdb", + 8, + PURPLE_CIPHER_BATCH_MODE_CBC); +} +END_TEST + +START_TEST(test_des3_cbc_null_text) { + DES3_TEST( + "\x65\x73\x34\xc1\x19\x00\x79\x65", + "\x32\x64\xda\x10\x13\x6a\xfe\x1e" + "\x37\x54\xd1\x2c\x41\x04\x10\x40" + "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5", + "\x7C\xAF\x0D\x57\x1E\x57\x10\xDA", + "\x40\x12\x0e\x00\x85\xff\x6c\xc2", + 8, + PURPLE_CIPHER_BATCH_MODE_CBC); +} +END_TEST + +START_TEST(test_des3_cbc_null_key_and_text) { + DES3_TEST( + "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2", + "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1" + "\x66\x49\x42\x63\x22\x00\xf0\x99" + "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f", + "\x01\x19\x0D\x2c\x40\x67\x89\x67", + "\xa7\xc1\x10\xbe\x9b\xd5\x8a\x67", + 8, + PURPLE_CIPHER_BATCH_MODE_CBC); +} +END_TEST + +/****************************************************************************** + * HMAC Tests + * See RFC2202 and some other NULL tests I made up + *****************************************************************************/ + +#define HMAC_TEST(data, data_len, key, key_len, type, digest) { \ + PurpleCipher *cipher = NULL; \ + PurpleCipherContext *context = NULL; \ + gchar cdigest[41]; \ + gboolean ret = FALSE; \ + \ + cipher = purple_ciphers_find_cipher("hmac"); \ + context = purple_cipher_context_new(cipher, NULL); \ + purple_cipher_context_set_option(context, "hash", type); \ + purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len)); \ + \ + purple_cipher_context_append(context, (guchar *)(data), (data_len)); \ + ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \ + NULL); \ + \ + fail_unless(ret == TRUE, NULL); \ + fail_unless(strcmp((digest), cdigest) == 0, NULL); \ + \ + purple_cipher_context_destroy(context); \ +} + +/* HMAC MD5 */ + +START_TEST(test_hmac_md5_Hi) { + HMAC_TEST("Hi There", + 8, + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + 16, + "md5", + "9294727a3638bb1c13f48ef8158bfc9d"); +} +END_TEST + +START_TEST(test_hmac_md5_what) { + HMAC_TEST("what do ya want for nothing?", + 28, + "Jefe", + 4, + "md5", + "750c783e6ab0b503eaa86e310a5db738"); +} +END_TEST + +START_TEST(test_hmac_md5_dd) { + HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + 50, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 16, + "md5", + "56be34521d144c88dbb8c733f0e8b3f6"); +} +END_TEST + +START_TEST(test_hmac_md5_cd) { + HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + 50, + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", + 25, + "md5", + "697eaf0aca3a3aea3a75164746ffaa79"); +} +END_TEST + +START_TEST(test_hmac_md5_truncation) { + HMAC_TEST("Test With Truncation", + 20, + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + 16, + "md5", + "56461ef2342edc00f9bab995690efd4c"); +} +END_TEST + +START_TEST(test_hmac_md5_large_key) { + HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First", + 54, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "md5", + "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"); +} +END_TEST + +START_TEST(test_hmac_md5_large_key_and_data) { + HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + 73, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "md5", + "6f630fad67cda0ee1fb1f562db3aa53e"); +} +END_TEST + +START_TEST(test_hmac_md5_null_key) { + HMAC_TEST("Hi There", + 8, + "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b" + "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b", + 20, + "md5", + "597bfd644b797a985561eeb03a169e59"); +} +END_TEST + +START_TEST(test_hmac_md5_null_text) { + HMAC_TEST("Hi\x00There", + 8, + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + 20, + "md5", + "70be8e1b7b50dfcc335d6cd7992c564f"); +} +END_TEST + +START_TEST(test_hmac_md5_null_key_and_text) { + HMAC_TEST("Hi\x00Th\x00re", + 8, + "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34" + "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b", + 20, + "md5", + "b31bcbba35a33a067cbba9131cba4889"); +} +END_TEST + +/* HMAC SHA1 */ + +START_TEST(test_hmac_sha1_Hi) { + HMAC_TEST("Hi There", + 8, + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + 20, + "sha1", + "b617318655057264e28bc0b6fb378c8ef146be00"); +} +END_TEST + +START_TEST(test_hmac_sha1_what) { + HMAC_TEST("what do ya want for nothing?", + 28, + "Jefe", + 4, + "sha1", + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"); +} +END_TEST + +START_TEST(test_hmac_sha1_dd) { + HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + 50, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 20, + "sha1", + "125d7342b9ac11cd91a39af48aa17b4f63f175d3"); +} +END_TEST + +START_TEST(test_hmac_sha1_cd) { + HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + 50, + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", + 25, + "sha1", + "4c9007f4026250c6bc8414f9bf50c86c2d7235da"); +} +END_TEST + +START_TEST(test_hmac_sha1_truncation) { + HMAC_TEST("Test With Truncation", + 20, + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + 20, + "sha1", + "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"); +} +END_TEST + +START_TEST(test_hmac_sha1_large_key) { + HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First", + 54, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "sha1", + "aa4ae5e15272d00e95705637ce8a3b55ed402112"); +} +END_TEST + +START_TEST(test_hmac_sha1_large_key_and_data) { + HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + 73, + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "sha1", + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"); +} +END_TEST + +START_TEST(test_hmac_sha1_null_key) { + HMAC_TEST("Hi There", + 8, + "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b" + "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b", + 20, + "sha1", + "eb62a2e0e33d300be669c52aab3f591bc960aac5"); +} +END_TEST + +START_TEST(test_hmac_sha1_null_text) { + HMAC_TEST("Hi\x00There", + 8, + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + 20, + "sha1", + "31ca58d849e971e418e3439de2c6f83144b6abb7"); +} +END_TEST + +START_TEST(test_hmac_sha1_null_key_and_text) { + HMAC_TEST("Hi\x00Th\x00re", + 8, + "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34" + "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b", + 20, + "sha1", + "e6b8e2fede87aa09dcb13e554df1435e056eae36"); +} +END_TEST + +/****************************************************************************** * Suite *****************************************************************************/ Suite * @@ -227,6 +732,53 @@ tcase_add_test(tc, test_sha1_1000_as_1000_times); suite_add_tcase(s, tc); + /* des tests */ + tc = tcase_create("DES"); + tcase_add_test(tc, test_des_12345678); + tcase_add_test(tc, test_des_abcdefgh); + suite_add_tcase(s, tc); + + /* des3 ecb tests */ + tc = tcase_create("DES3 ECB"); + tcase_add_test(tc, test_des3_ecb_nist1); + tcase_add_test(tc, test_des3_ecb_nist2); + tcase_add_test(tc, test_des3_ecb_null_key); + tcase_add_test(tc, test_des3_ecb_null_text); + tcase_add_test(tc, test_des3_ecb_null_key_and_text); + suite_add_tcase(s, tc); + /* des3 cbc tests */ + tc = tcase_create("DES3 CBC"); + tcase_add_test(tc, test_des3_cbc_nist1); + tcase_add_test(tc, test_des3_cbc_nist2); + tcase_add_test(tc, test_des3_cbc_null_key); + tcase_add_test(tc, test_des3_cbc_null_text); + tcase_add_test(tc, test_des3_cbc_null_key_and_text); + suite_add_tcase(s, tc); + + /* hmac tests */ + tc = tcase_create("HMAC"); + tcase_add_test(tc, test_hmac_md5_Hi); + tcase_add_test(tc, test_hmac_md5_what); + tcase_add_test(tc, test_hmac_md5_dd); + tcase_add_test(tc, test_hmac_md5_cd); + tcase_add_test(tc, test_hmac_md5_truncation); + tcase_add_test(tc, test_hmac_md5_large_key); + tcase_add_test(tc, test_hmac_md5_large_key_and_data); + tcase_add_test(tc, test_hmac_md5_null_key); + tcase_add_test(tc, test_hmac_md5_null_text); + tcase_add_test(tc, test_hmac_md5_null_key_and_text); + tcase_add_test(tc, test_hmac_sha1_Hi); + tcase_add_test(tc, test_hmac_sha1_what); + tcase_add_test(tc, test_hmac_sha1_dd); + tcase_add_test(tc, test_hmac_sha1_cd); + tcase_add_test(tc, test_hmac_sha1_truncation); + tcase_add_test(tc, test_hmac_sha1_large_key); + tcase_add_test(tc, test_hmac_sha1_large_key_and_data); + tcase_add_test(tc, test_hmac_sha1_null_key); + tcase_add_test(tc, test_hmac_sha1_null_text); + tcase_add_test(tc, test_hmac_sha1_null_key_and_text); + suite_add_tcase(s, tc); + return s; } diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/upnp.c --- a/libpurple/upnp.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/upnp.c Mon Jan 07 03:40:27 2008 +0000 @@ -777,7 +777,7 @@ /* TODO: This could be exported */ static const gchar * -purple_upnp_get_internal_ip() +purple_upnp_get_internal_ip(void) { if (control_info.status == PURPLE_UPNP_STATUS_DISCOVERED && control_info.internalip diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/util.c --- a/libpurple/util.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/util.c Mon Jan 07 03:40:27 2008 +0000 @@ -4259,6 +4259,53 @@ return g_string_free(workstr, FALSE); } +/* + * This function is copied from g_strerror() but changed to use + * gai_strerror(). + */ +G_CONST_RETURN gchar * +purple_gai_strerror(gint errnum) +{ + static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT; + char *msg; + int saved_errno = errno; + + const char *msg_locale; + + msg_locale = gai_strerror(errnum); + if (g_get_charset(NULL)) + { + /* This string is already UTF-8--great! */ + errno = saved_errno; + return msg_locale; + } + else + { + gchar *msg_utf8 = g_locale_to_utf8(msg_locale, -1, NULL, NULL, NULL); + if (msg_utf8) + { + /* Stick in the quark table so that we can return a static result */ + GQuark msg_quark = g_quark_from_string(msg_utf8); + g_free(msg_utf8); + + msg_utf8 = (gchar *)g_quark_to_string(msg_quark); + errno = saved_errno; + return msg_utf8; + } + } + + msg = g_static_private_get(&msg_private); + if (!msg) + { + msg = g_new(gchar, 64); + g_static_private_set(&msg_private, msg, g_free); + } + + sprintf(msg, "unknown error (%d)", errnum); + + errno = saved_errno; + return msg; +} char * purple_utf8_ncr_encode(const char *str) @@ -4814,3 +4861,57 @@ *newlen = bytes; return utf; } + +void purple_util_set_current_song(const char *title, const char *artist, const char *album) +{ + GList *list = purple_accounts_get_all(); + for (; list; list = list->next) { + PurplePresence *presence; + PurpleStatus *tune; + PurpleAccount *account = list->data; + if (!purple_account_get_enabled(account, purple_core_get_ui())) + continue; + + presence = purple_account_get_presence(account); + tune = purple_presence_get_status(presence, "tune"); + if (!tune) + continue; + if (title) { + purple_status_set_active(tune, TRUE); + purple_status_set_attr_string(tune, PURPLE_TUNE_TITLE, title); + purple_status_set_attr_string(tune, PURPLE_TUNE_ARTIST, artist); + purple_status_set_attr_string(tune, PURPLE_TUNE_ALBUM, album); + } else { + purple_status_set_active(tune, FALSE); + } + } +} + +char * purple_util_format_song_info(const char *title, const char *artist, const char *album, gpointer unused) +{ + GString *string; + char *esc; + + if (!title) + return NULL; + + esc = g_markup_escape_text(title, -1); + string = g_string_new(""); + g_string_append_printf(string, "%s", esc); + g_free(esc); + + if (artist) { + esc = g_markup_escape_text(artist, -1); + g_string_append_printf(string, _(" - %s"), esc); + g_free(esc); + } + + if (album) { + esc = g_markup_escape_text(album, -1); + g_string_append_printf(string, _(" (%s)"), esc); + g_free(esc); + } + + return g_string_free(string, FALSE); +} + diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/util.h --- a/libpurple/util.h Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/util.h Mon Jan 07 03:40:27 2008 +0000 @@ -85,6 +85,31 @@ */ void purple_menu_action_free(PurpleMenuAction *act); +/** + * Set the appropriate presence values for the currently playing song. + * + * @param title The title of the song, @c NULL to unset the value. + * @param artist The artist of the song, can be @c NULL. + * @param album The album of the song, can be @c NULL. + * @since 2.4.0 + */ +void purple_util_set_current_song(const char *title, const char *artist, + const char *album); + +/** + * Format song information. + * + * @param title The title of the song, @c NULL to unset the value. + * @param artist The artist of the song, can be @c NULL. + * @param album The album of the song, can be @c NULL. + * @param unused Currently unused, must be @c NULL. + * + * @return The formatted string. The caller must #g_free the returned string. + * @since 2.4.0 + */ +char * purple_util_format_song_info(const char *title, const char *artist, + const char *album, gpointer unused); + /**************************************************************************/ /** @name Utility Subsystem */ /**************************************************************************/ @@ -1110,6 +1135,18 @@ gchar *purple_utf8_salvage(const char *str); /** + * Return the UTF-8 version of gai_strerror(). It calls gai_strerror() + * then converts the result to UTF-8. This function is analogous to + * g_strerror(). + * + * @param errnum The error code. + * + * @return The UTF-8 error message. + * @since 2.4.0 + */ +G_CONST_RETURN gchar *purple_gai_strerror(gint errnum); + +/** * Compares two UTF-8 strings case-insensitively. This string is * more expensive than a simple g_utf8_collate() comparison because * it calls g_utf8_casefold() on each string, which allocates new diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/version.h.in --- a/libpurple/version.h.in Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/version.h.in Mon Jan 07 03:40:27 2008 +0000 @@ -49,6 +49,34 @@ */ const char *purple_version_check(guint required_major, guint required_minor, guint required_micro); +/** + * The major version of the running libpurple. Contrast with + * #PURPLE_MAJOR_VERSION, which expands at compile time to the major version of + * libpurple being compiled against. + * + * @since 2.4.0 + */ +extern const guint purple_major_version; + +/** + * The minor version of the running libpurple. Contrast with + * #PURPLE_MINOR_VERSION, which expands at compile time to the minor version of + * libpurple being compiled against. + * + * @since 2.4.0 + */ +extern const guint purple_minor_version; + +/** + * + * The micro version of the running libpurple. Contrast with + * #PURPLE_MICRO_VERSION, which expands at compile time to the micro version of + * libpurple being compiled against. + * + * @since 2.4.0 + */ +extern const guint purple_micro_version; + #ifdef __cplusplus } #endif diff -r 754a82f1371b -r 9a60b9fab0ea libpurple/xmlnode.c --- a/libpurple/xmlnode.c Mon Dec 17 08:20:42 2007 +0000 +++ b/libpurple/xmlnode.c Mon Jan 07 03:40:27 2008 +0000 @@ -345,6 +345,7 @@ g_free(node->name); g_free(node->data); g_free(node->xmlns); + g_free(node->prefix); if(node->namespace_map) g_hash_table_destroy(node->namespace_map); @@ -551,6 +552,9 @@ g_strdup_printf("" NEWLINE_S NEWLINE_S "%s", xml); g_free(xml); + if (len) + *len += sizeof("" NEWLINE_S NEWLINE_S) - 1; + return xml_with_declaration; } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/Makefile.am --- a/pidgin/Makefile.am Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/Makefile.am Mon Jan 07 03:40:27 2008 +0000 @@ -119,7 +119,8 @@ gtkthemes.c \ gtkutils.c \ gtkwhiteboard.c \ - minidialog.c + minidialog.c \ + pidgintooltip.c pidgin_headers = \ eggtrayicon.h \ @@ -172,6 +173,7 @@ gtkutils.h \ gtkwhiteboard.h \ minidialog.h \ + pidgintooltip.h \ pidgin.h pidginincludedir=$(includedir)/pidgin diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/Makefile.mingw --- a/pidgin/Makefile.mingw Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/Makefile.mingw Mon Jan 07 03:40:27 2008 +0000 @@ -95,6 +95,7 @@ gtkwhiteboard.c \ minidialog.c \ pidginstock.c \ + pidgintooltip.c \ win32/MinimizeToTray.c \ win32/gtkdocklet-win32.c \ win32/gtkwin32dep.c \ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkaccount.c Mon Jan 07 03:40:27 2008 +0000 @@ -55,7 +55,6 @@ COLUMN_ENABLED, COLUMN_PROTOCOL, COLUMN_DATA, - COLUMN_PULSE_DATA, NUM_COLUMNS }; @@ -139,18 +138,6 @@ } AccountPrefsDialog; -typedef struct -{ - GdkPixbuf *online_pixbuf; - gboolean pulse_to_grey; - float pulse_value; - int timeout; - PurpleAccount *account; - GtkTreeModel *model; - -} PidginPulseData; - - static AccountsWindow *accounts_window = NULL; static GHashTable *account_pref_wins; @@ -171,25 +158,7 @@ add_pref_box(AccountPrefsDialog *dialog, GtkWidget *parent, const char *text, GtkWidget *widget) { - GtkWidget *hbox; - GtkWidget *label; - - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(text); - gtk_size_group_add_widget(dialog->sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget); - gtk_widget_show(label); - - gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER); - gtk_widget_show(widget); - pidgin_set_accessible_label (widget, label); - - return hbox; + return pidgin_add_widget_to_vbox(GTK_BOX(parent), text, dialog->sg, widget, TRUE, NULL); } static void @@ -1121,7 +1090,7 @@ G_CALLBACK(proxy_type_changed_cb), dialog); } -static void +static gboolean account_win_destroy_cb(GtkWidget *w, GdkEvent *event, AccountPrefsDialog *dialog) { @@ -1142,6 +1111,7 @@ purple_signals_disconnect_by_handle(dialog); g_free(dialog); + return FALSE; } static void @@ -1437,7 +1407,6 @@ GtkWidget *win; GtkWidget *main_vbox; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *dbox; GtkWidget *notebook; GtkWidget *button; @@ -1475,16 +1444,14 @@ if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL) dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin); - dialog->window = win = pidgin_create_window((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"), + dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"), PIDGIN_HIG_BORDER, "account", FALSE); g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(account_win_destroy_cb), dialog); /* Setup the vbox */ - main_vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), main_vbox); - gtk_widget_show(main_vbox); + main_vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); notebook = gtk_notebook_new(); gtk_box_pack_start(GTK_BOX(main_vbox), notebook, FALSE, FALSE, 0); @@ -1511,8 +1478,6 @@ if (!dialog->prpl_info || !dialog->prpl_info->register_user) gtk_widget_hide(button); - - /* Setup the page with 'Advanced'. */ dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER); @@ -1524,30 +1489,13 @@ add_protocol_options(dialog, dbox); add_proxy_options(dialog, dbox); - /* Setup the button box */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(main_vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - /* Cancel button */ - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(cancel_account_prefs_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog); /* Save button */ - button = gtk_button_new_from_stock(GTK_STOCK_SAVE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE, G_CALLBACK(ok_account_prefs_cb), dialog); if (dialog->account == NULL) gtk_widget_set_sensitive(button, FALSE); - - gtk_widget_show(button); - dialog->ok_button = button; /* Set up DND */ @@ -1561,9 +1509,6 @@ g_signal_connect(G_OBJECT(dialog->window), "drag_data_received", G_CALLBACK(account_dnd_recv), dialog); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(ok_account_prefs_cb), dialog); - /* Show the window. */ gtk_widget_show(win); } @@ -1575,7 +1520,6 @@ signed_on_off_cb(PurpleConnection *gc, gpointer user_data) { PurpleAccount *account; - PidginPulseData *pulse_data; GtkTreeModel *model; GtkTreeIter iter; GdkPixbuf *pixbuf; @@ -1591,29 +1535,14 @@ if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { - gtk_tree_model_get(GTK_TREE_MODEL(accounts_window->model), &iter, - COLUMN_PULSE_DATA, &pulse_data, -1); - - if (pulse_data != NULL) - { - if (pulse_data->timeout > 0) - g_source_remove(pulse_data->timeout); - - g_object_unref(G_OBJECT(pulse_data->online_pixbuf)); - - g_free(pulse_data); - } - pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); if ((pixbuf != NULL) && purple_account_is_disconnected(account)) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); gtk_list_store_set(accounts_window->model, &iter, COLUMN_ICON, pixbuf, - COLUMN_PULSE_DATA, NULL, -1); - if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); } @@ -1812,16 +1741,14 @@ } } -static gint +static gboolean accedit_win_destroy_cb(GtkWidget *w, GdkEvent *event, AccountsWindow *dialog) { - /* Since this is called as the window is closing, we don't need - * pidgin_accounts_window_hide() to also dispose of the window */ dialog->window = NULL; pidgin_accounts_window_hide(); - return 0; + return FALSE; } static gboolean @@ -2155,24 +2082,24 @@ GtkTreeViewColumn *column; GtkTreeIter iter; PurpleAccount *account; - const gchar *title; dialog = (AccountsWindow *)user_data; + if (event->window != gtk_tree_view_get_bin_window(treeview)) + return FALSE; + /* Figure out which node was clicked */ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dialog->treeview), event->x, event->y, &path, &column, NULL, NULL)) return FALSE; - title = gtk_tree_view_column_get_title(column); - /* The -1 is required because the first two columns of the list - * store are displayed as only one column in the tree view. */ - column = gtk_tree_view_get_column(treeview, COLUMN_ENABLED-1); + if (column == gtk_tree_view_get_column(treeview, 0)) + return FALSE; + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); gtk_tree_path_free(path); gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &account, -1); if ((account != NULL) && (event->button == 1) && - (event->type == GDK_2BUTTON_PRESS) && - (strcmp(gtk_tree_view_column_get_title(column), title))) + (event->type == GDK_2BUTTON_PRESS)) { pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account); return TRUE; @@ -2313,7 +2240,6 @@ AccountsWindow *dialog; GtkWidget *win; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *sw; GtkWidget *button; int width, height; @@ -2328,7 +2254,7 @@ width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width"); height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height"); - dialog->window = win = pidgin_create_window(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE); + dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE); gtk_window_set_default_size(GTK_WINDOW(win), width, height); g_signal_connect(G_OBJECT(win), "delete_event", @@ -2337,57 +2263,28 @@ G_CALLBACK(configure_cb), accounts_window); /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); /* Setup the scrolled window that will contain the list of accounts. */ sw = create_accounts_list(dialog); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); gtk_widget_show(sw); - /* Button box. */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - /* Add button */ - button = gtk_button_new_from_stock(GTK_STOCK_ADD); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(add_account_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(add_account_cb), dialog); /* Modify button */ - button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY); + button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(modify_account_cb), dialog); dialog->modify_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(modify_account_cb), dialog); /* Delete button */ - button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(ask_delete_account_cb), dialog); dialog->delete_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(ask_delete_account_cb), dialog); /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(close_accounts_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog); purple_signal_connect(pidgin_account_get_handle(), "account-modified", accounts_window, diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkblist.c --- a/pidgin/gtkblist.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkblist.c Mon Jan 07 03:40:27 2008 +0000 @@ -59,6 +59,7 @@ #include "gtkscrollbook.h" #include "gtkutils.h" #include "pidgin/minidialog.h" +#include "pidgin/pidgintooltip.h" #include #include @@ -153,6 +154,7 @@ static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full); static const char *item_factory_translate_func (const char *path, gpointer func_data); static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter); +static gboolean buddy_is_displayable(PurpleBuddy *buddy); static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); @@ -175,7 +177,7 @@ } PidginBlistNode; static char dim_grey_string[8] = ""; -static char *dim_grey() +static char *dim_grey(void) { if (!gtkblist) return "dim grey"; @@ -407,6 +409,7 @@ static void gtk_blist_renderer_editing_cancelled_cb(GtkCellRenderer *renderer, PurpleBuddyList *list) { editing_blist = FALSE; + g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL); pidgin_blist_refresh(list); } @@ -532,12 +535,14 @@ if (node_alias && !g_utf8_collate(node_alias, a)) { merges = g_list_append(merges, buddy); i++; + g_free(node_alias); + break; } g_free(node_alias); } } g_free(a); - + if (i > 1) { char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias); @@ -703,12 +708,12 @@ pidgin_blist_update(purple_get_blist(), node); } -static void gtk_blist_show_systemlog_cb() +static void gtk_blist_show_systemlog_cb(void) { pidgin_syslog_show(); } -static void gtk_blist_show_onlinehelp_cb() +static void gtk_blist_show_onlinehelp_cb(void) { purple_notify_uri(NULL, PURPLE_WEBSITE "documentation"); } @@ -838,20 +843,10 @@ for (tmp = list; tmp; tmp = tmp->next) { - GtkWidget *label; - GtkWidget *rowbox; GtkWidget *input; pce = tmp->data; - rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(pce->label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_size_group_add_widget(data->sg, label); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - if (pce->is_int) { GtkObject *adjust; @@ -859,7 +854,7 @@ 1, 10, 10); input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); gtk_widget_set_size_request(input, 50, -1); - gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL); } else { @@ -875,7 +870,7 @@ if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); } - gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL); g_signal_connect(G_OBJECT(input), "changed", G_CALLBACK(joinchat_set_sensitive_if_input_cb), data); } @@ -886,8 +881,6 @@ gtk_widget_grab_focus(input); focus = FALSE; } - gtk_label_set_mnemonic_widget(GTK_LABEL(label), input); - pidgin_set_accessible_label(input, label); g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier); g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int)); g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required)); @@ -983,23 +976,14 @@ gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); - rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_box_pack_start(GTK_BOX(vbox), rowbox, TRUE, TRUE, 0); - data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - label = gtk_label_new_with_mnemonic(_("_Account:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(data->sg, label); - data->account_menu = pidgin_account_option_menu_new(NULL, FALSE, G_CALLBACK(joinchat_select_account_cb), chat_account_filter_func, data); gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), - GTK_WIDGET(data->account_menu)); - pidgin_set_accessible_label (data->account_menu, label); + + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL); data->entries_box = gtk_vbox_new(FALSE, 5); gtk_container_add(GTK_CONTAINER(vbox), data->entries_box); @@ -1112,7 +1096,7 @@ } } -static void pidgin_blist_add_chat_cb() +static void pidgin_blist_add_chat_cb(void) { GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); GtkTreeIter iter; @@ -1132,7 +1116,7 @@ } } -static void pidgin_blist_add_buddy_cb() +static void pidgin_blist_add_buddy_cb(void) { GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); GtkTreeIter iter; @@ -2571,48 +2555,75 @@ int height; }; +static PangoLayout * create_pango_layout(const char *markup, int *width, int *height) +{ + PangoLayout *layout; + int w, h; + + layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); + pango_layout_set_markup(layout, markup, -1); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_width(layout, 300000); + + pango_layout_get_size (layout, &w, &h); + if (width) + *width = PANGO_PIXELS(w); + if (height) + *height = PANGO_PIXELS(h); + return layout; +} + +static struct tooltip_data * create_tip_for_account(PurpleAccount *account) +{ + struct tooltip_data *td = g_new0(struct tooltip_data, 1); + td->status_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + /* Yes, status_icon, not prpl_icon */ + if (purple_account_is_disconnected(account)) + gdk_pixbuf_saturate_and_pixelate(td->status_icon, td->status_icon, 0.0, FALSE); + td->layout = create_pango_layout(purple_account_get_username(account), &td->width, &td->height); + return td; +} + static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean full) { - char *tooltip_text = NULL; struct tooltip_data *td = g_new0(struct tooltip_data, 1); PurpleAccount *account = NULL; - char *tmp, *node_name; - - if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { + char *tmp = NULL, *node_name = NULL, *tooltip_text = NULL; + + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { account = ((PurpleBuddy*)(node))->account; - } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { account = ((PurpleChat*)(node))->account; } td->status_icon = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_LARGE); td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE); - td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + if (account != NULL) { + td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + } tooltip_text = pidgin_get_tooltip_text(node, full); - td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); - td->name_layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); - - if (PURPLE_BLIST_NODE_IS_BUDDY(node)) + if (tooltip_text && *tooltip_text) { + td->layout = create_pango_layout(tooltip_text, &td->width, &td->height); + } + + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { tmp = g_markup_escape_text(purple_buddy_get_name((PurpleBuddy*)node), -1); - else + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { tmp = g_markup_escape_text(purple_chat_get_name((PurpleChat*)node), -1); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + tmp = g_markup_escape_text(purple_group_get_name((PurpleGroup*)node), -1); + } else { + /* I don't believe this can happen currently, I think + * everything that calls this function checks for one of the + * above node types first. */ + tmp = g_strdup(_("Unknown node type")); + } node_name = g_strdup_printf("%s", tmp); g_free(tmp); - pango_layout_set_markup(td->layout, tooltip_text, -1); - pango_layout_set_wrap(td->layout, PANGO_WRAP_WORD); - pango_layout_set_width(td->layout, 300000); - - pango_layout_get_size (td->layout, &td->width, &td->height); - td->width = PANGO_PIXELS(td->width); - td->height = PANGO_PIXELS(td->height); - - pango_layout_set_markup(td->name_layout, node_name, -1); - pango_layout_set_wrap(td->name_layout, PANGO_WRAP_WORD); - pango_layout_set_width(td->name_layout, 300000); - - pango_layout_get_size (td->name_layout, &td->name_width, &td->name_height); - td->name_width = PANGO_PIXELS(td->name_width) + SMALL_SPACE + PRPL_SIZE; - td->name_height = MAX(PANGO_PIXELS(td->name_height), PRPL_SIZE + SMALL_SPACE); + td->name_layout = create_pango_layout(node_name, &td->name_width, &td->name_height); + td->name_width += SMALL_SPACE + PRPL_SIZE; + td->name_height = MAX(td->name_height, PRPL_SIZE + SMALL_SPACE); #if 0 /* PRPL Icon as avatar */ if(!td->avatar && full) { td->avatar = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE); @@ -2630,7 +2641,8 @@ return td; } -static void pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, PurpleBlistNode *node) +static gboolean +pidgin_blist_paint_tip(GtkWidget *widget, gpointer null) { GtkStyle *style; int current_height, max_width; @@ -2638,14 +2650,13 @@ int max_avatar_width; GList *l; int prpl_col = 0; - GtkTextDirection dir = gtk_widget_get_direction(widget); + GtkTextDirection dir = gtk_widget_get_direction(widget); + int status_size = 0; if(gtkblist->tooltipdata == NULL) - return; + return FALSE; style = gtkblist->tipwindow->style; - gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); max_text_width = 0; max_avatar_width = 0; @@ -2657,13 +2668,15 @@ max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); max_avatar_width = MAX(max_avatar_width, td->avatar_width); - } - - max_width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; + if (td->status_icon) + status_size = STATUS_SIZE; + } + + max_width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; if (dir == GTK_TEXT_DIR_RTL) prpl_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE; else - prpl_col = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width - PRPL_SIZE; + prpl_col = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width - PRPL_SIZE; current_height = 12; for(l = gtkblist->tooltipdata; l; l = l->next) @@ -2684,14 +2697,16 @@ } #if GTK_CHECK_VERSION(2,2,0) - if (dir == GTK_TEXT_DIR_RTL) - gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, - 0, 0, max_width - TOOLTIP_BORDER - STATUS_SIZE, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); - else - gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, - 0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); - if(td->avatar) - { + if (td->status_icon) { + if (dir == GTK_TEXT_DIR_RTL) + gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, + 0, 0, max_width - TOOLTIP_BORDER - status_size, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); + else + gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, + 0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); + } + + if(td->avatar) { if (dir == GTK_TEXT_DIR_RTL) gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->avatar, 0, 0, TOOLTIP_BORDER, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); @@ -2701,7 +2716,7 @@ current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); } - if (!td->avatar_is_prpl_icon) + if (!td->avatar_is_prpl_icon && td->prpl_icon) gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->prpl_icon, 0, 0, prpl_col, @@ -2709,41 +2724,49 @@ -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); #else - gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); + if (td->status_icon) { + gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); + } if(td->avatar) gdk_pixbuf_render_to_drawable(td->avatar, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, max_width - (td->avatar_width + TOOLTIP_BORDER), current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); #endif - if (dir == GTK_TEXT_DIR_RTL) { - gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, - NULL, gtkblist->tipwindow, "tooltip", - max_width -(TOOLTIP_BORDER + STATUS_SIZE +SMALL_SPACE) - PANGO_PIXELS(300000), - current_height, td->name_layout); - } else { - gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, - NULL, gtkblist->tipwindow, "tooltip", - TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height, td->name_layout); + if (td->name_layout) { + if (dir == GTK_TEXT_DIR_RTL) { + gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, gtkblist->tipwindow, "tooltip", + max_width -(TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000), + current_height, td->name_layout); + } else { + gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, gtkblist->tipwindow, "tooltip", + TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height, td->name_layout); + } } - if (dir != GTK_TEXT_DIR_RTL) { - gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, - NULL, gtkblist->tipwindow, "tooltip", - TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height + td->name_height, td->layout); - } else { - gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, - NULL, gtkblist->tipwindow, "tooltip", - max_width - (TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE) - PANGO_PIXELS(300000), - current_height + td->name_height, - td->layout); + + if (td->layout) { + if (dir != GTK_TEXT_DIR_RTL) { + gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, gtkblist->tipwindow, "tooltip", + TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height + td->name_height, td->layout); + } else { + gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, gtkblist->tipwindow, "tooltip", + max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000), + current_height + td->name_height, + td->layout); + } } current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER; } -} - - -void pidgin_blist_tooltip_destroy() + return FALSE; +} + +static void +pidgin_blist_destroy_tooltip_data(void) { while(gtkblist->tooltipdata) { struct tooltip_data *td = gtkblist->tooltipdata->data; @@ -2754,17 +2777,94 @@ g_object_unref(td->status_icon); if(td->prpl_icon) g_object_unref(td->prpl_icon); - g_object_unref(td->layout); - g_object_unref(td->name_layout); + if (td->layout) + g_object_unref(td->layout); + if (td->name_layout) + g_object_unref(td->name_layout); g_free(td); gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata); } - - if (gtkblist->tipwindow == NULL) - return; - - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; +} + +void pidgin_blist_tooltip_destroy() +{ + pidgin_blist_destroy_tooltip_data(); + pidgin_tooltip_destroy(); +} + +static gboolean +pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, int *h) +{ + PurpleBlistNode *node = data; + int width, height; + GList *list; + int max_text_width = 0; + int max_avatar_width = 0; + int status_size = 0; + + if (gtkblist->tooltipdata) { + gtkblist->tipwindow = NULL; + pidgin_blist_destroy_tooltip_data(); + } + + gtkblist->tipwindow = widget; + if (PURPLE_BLIST_NODE_IS_CHAT(node) || + PURPLE_BLIST_NODE_IS_BUDDY(node)) { + struct tooltip_data *td = create_tip_for_node(node, TRUE); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleGroup *group = (PurpleGroup*)node; + GSList *accounts; + struct tooltip_data *td = create_tip_for_node(node, TRUE); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + + /* Accounts with buddies in group */ + accounts = purple_group_get_accounts(group); + for (; accounts != NULL; + accounts = g_slist_delete_link(accounts, accounts)) { + PurpleAccount *account = accounts->data; + td = create_tip_for_account(account); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + } + } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBlistNode *child; + PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); + width = height = 0; + + for(child = node->child; child; child = child->next) + { + if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { + struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); + if (b == (PurpleBuddy *)child) { + gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td); + } else { + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + } + } + } + } else { + return FALSE; + } + + height = width = 0; + for (list = gtkblist->tooltipdata; list; list = list->next) { + struct tooltip_data *td = list->data; + max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); + max_avatar_width = MAX(max_avatar_width, td->avatar_width); + height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE, td->avatar_height), + TOOLTIP_BORDER + td->height + td->name_height); + if (td->status_icon) + status_size = MAX(status_size, STATUS_SIZE); + } + height += TOOLTIP_BORDER; + width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; + + if (w) + *w = width; + if (h) + *h = height; + + return TRUE; } static gboolean pidgin_blist_expand_timeout(GtkWidget *tv) @@ -2826,164 +2926,9 @@ purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline"))); } -static gboolean pidgin_blist_tooltip_timeout(GtkWidget *tv) -{ - GtkTreePath *path; - GtkTreeIter iter; - PurpleBlistNode *node; - GValue val; - gboolean editable = FALSE; - - /* If we're editing a cell (e.g. alias editing), don't show the tooltip */ - g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL); - if (editable) - return FALSE; - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2), - &path, NULL, NULL, NULL)) - return FALSE; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - val.g_type = 0; - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - - pidgin_blist_draw_tooltip(node, gtkblist->window); - - gtk_tree_path_free(path); - return FALSE; -} - void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget) { - int scr_w, scr_h, w, h, x, y; -#if GTK_CHECK_VERSION(2,2,0) - int mon_num; - GdkScreen *screen = NULL; -#endif - gboolean tooltip_top = FALSE; - struct _pidgin_blist_node *gtknode; - GdkRectangle mon_size; - int sig; - const char *name; - - if (node == NULL) - return; - - /* - * Attempt to free the previous tooltip. I have a feeling - * this is never needed... but just in case. - */ - pidgin_blist_tooltip_destroy(); - - gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); - - if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { - struct tooltip_data *td = create_tip_for_node(node, TRUE); - gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); - w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + - MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; - h = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height)) - + TOOLTIP_BORDER; - } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleBlistNode *child; - PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); - int max_text_width = 0; - int max_avatar_width = 0; - w = h = 0; - - for(child = node->child; child; child = child->next) - { - if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { - struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); - if (b == (PurpleBuddy *)child) { - gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td); - } else { - gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); - } - max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); - max_avatar_width = MAX(max_avatar_width, td->avatar_width); - h += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height), - TOOLTIP_BORDER + td->height + td->name_height); - } - } - h += TOOLTIP_BORDER; - w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; - } else { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - return; - } - - if (gtkblist->tooltipdata == NULL) { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - return; - } - - gtknode = node->ui_data; - - name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget))); - gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); - gtk_window_set_title(GTK_WINDOW(gtkblist->tipwindow), name ? name : _("Buddy List")); - gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); - gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); - g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", - G_CALLBACK(pidgin_blist_paint_tip), NULL); - gtk_widget_ensure_style (gtkblist->tipwindow); - -#if GTK_CHECK_VERSION(2,2,0) - gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); - mon_num = gdk_screen_get_monitor_at_point(screen, x, y); - gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); - - scr_w = mon_size.width + mon_size.x; - scr_h = mon_size.height + mon_size.y; -#else - scr_w = gdk_screen_width(); - scr_h = gdk_screen_height(); - gdk_window_get_pointer(NULL, &x, &y, NULL); - mon_size.x = 0; - mon_size.y = 0; -#endif - -#if GTK_CHECK_VERSION(2,2,0) - if (w > mon_size.width) - w = mon_size.width - 10; - - if (h > mon_size.height) - h = mon_size.height - 10; -#endif - - x -= ((w >> 1) + 4); - - if ((y + h + 4) > scr_h || tooltip_top) - y = y - h - 5; - else - y = y + 6; - - if (y < mon_size.y) - y = mon_size.y; - - if (y != mon_size.y) { - if ((x + w) > scr_w) - x -= (x + w + 5) - scr_w; - else if (x < mon_size.x) - x = mon_size.x; - } else { - x -= (w / 2 + 10); - if (x < mon_size.x) - x = mon_size.x; - } - - gtk_widget_set_size_request(gtkblist->tipwindow, w, h); - gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); - gtk_widget_show(gtkblist->tipwindow); - - /* Hide the tooltip when the widget is destroyed */ - sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_blist_tooltip_destroy), NULL); - g_signal_connect_swapped(G_OBJECT(gtkblist->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig)); - - return; + pidgin_tooltip_show(widget, node, pidgin_blist_create_tooltip_for_node, pidgin_blist_paint_tip); } static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context, @@ -3033,31 +2978,34 @@ return FALSE; } -static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) -{ - GtkTreePath *path; - int delay; - - delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - - if (delay == 0) +static gboolean +pidgin_blist_create_tooltip(GtkWidget *widget, GtkTreePath *path, + gpointer null, int *w, int *h) +{ + GtkTreeIter iter; + PurpleBlistNode *node; + GValue val; + gboolean editable = FALSE; + + /* If we're editing a cell (e.g. alias editing), don't show the tooltip */ + g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL); + if (editable) return FALSE; - if (gtkblist->timeout) { - if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) - return FALSE; - /* We've left the cell. Remove the timeout and create a new one below */ - pidgin_blist_tooltip_destroy(); - g_source_remove(gtkblist->timeout); - } - - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); - gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); - - if (path) - gtk_tree_path_free(path); - gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv); - + if (gtkblist->tooltipdata) { + gtkblist->tipwindow = NULL; + pidgin_blist_destroy_tooltip_data(); + } + + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + val.g_type = 0; + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + return pidgin_blist_create_tooltip_for_node(widget, node, w, h); +} + +static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) +{ if (gtkblist->mouseover_contact) { if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); @@ -3068,9 +3016,8 @@ return FALSE; } -static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) -{ - +static gboolean pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) +{ if (gtkblist->timeout) { g_source_remove(gtkblist->timeout); gtkblist->timeout = 0; @@ -3081,14 +3028,13 @@ gtkblist->drag_timeout = 0; } - pidgin_blist_tooltip_destroy(); - if (gtkblist->mouseover_contact && !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) && (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) { pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); gtkblist->mouseover_contact = NULL; } + return FALSE; } static void @@ -3179,7 +3125,7 @@ if (g_list_length(purple_connections_get_all()) > 1) { tmp = g_markup_escape_text(chat->account->username, -1); - g_string_append_printf(str, _("\nAccount: %s"), tmp); + g_string_append_printf(str, _("Account: %s"), tmp); g_free(tmp); } @@ -3371,10 +3317,35 @@ g_free(tmp); purple_notify_user_info_destroy(user_info); - } - - purple_signal_emit(pidgin_blist_get_handle(), - "drawing-tooltip", node, str, full); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleGroup *group = (PurpleGroup*)node; + PurpleNotifyUserInfo *user_info; + + user_info = purple_notify_user_info_new(); + + /* Total buddies (from online accounts) in group */ + tmp = g_strdup_printf("%d", + purple_blist_get_group_size(group, FALSE)); + purple_notify_user_info_add_pair(user_info, _("Total Buddies"), + tmp); + g_free(tmp); + + /* Online buddies in group */ + tmp = g_strdup_printf("%d", + purple_blist_get_group_online_count(group)); + purple_notify_user_info_add_pair(user_info, _("Online Buddies"), + tmp); + g_free(tmp); + + tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n"); + g_string_append(str, tmp); + g_free(tmp); + + purple_notify_user_info_destroy(user_info); + } + + purple_signal_emit(pidgin_blist_get_handle(), "drawing-tooltip", + node, str, full); return g_string_free(str, FALSE); } @@ -3795,7 +3766,7 @@ return text; } -static void pidgin_blist_restore_position() +static void pidgin_blist_restore_position(void) { int blist_x, blist_y, blist_width, blist_height; @@ -3942,7 +3913,7 @@ } static void -unseen_conv_menu() +unseen_conv_menu(void) { static GtkWidget *menu = NULL; GList *convs = NULL; @@ -4228,7 +4199,8 @@ pidgin_blist_sort_method_set(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type")); } -static void _prefs_change_redo_list() +static void _prefs_change_redo_list(const char *name, PurplePrefType type, + gconstpointer val, gpointer data) { GtkTreeSelection *sel; GtkTreeIter iter; @@ -4643,6 +4615,13 @@ } static void +clear_elsewhere_errors(PidginMiniDialog *mini_dialog, + gpointer unused) +{ + elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error); +} + +static void ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist) { PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); @@ -4657,6 +4636,12 @@ pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"), reconnect_elsewhere_accounts, NULL); + /* Make dismissing the dialog clear the errors. The "destroy" signal + * does not appear to fire at quit, which is fortunate! + */ + g_signal_connect(G_OBJECT(mini_dialog), "destroy", + (GCallback) clear_elsewhere_errors, NULL); + add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog)); /* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */ @@ -4885,6 +4870,9 @@ #endif gtk_tooltips_force_window (tooltips); +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips"); +#endif gtk_widget_ensure_style (tooltips->tip_window); style = gtk_widget_get_style (tooltips->tip_window); @@ -5069,8 +5057,9 @@ gtk_label_set_line_wrap(GTK_LABEL(gtkblist->headline_label), TRUE); gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_image, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_label, TRUE, TRUE, 0); - g_signal_connect(gtkblist->headline_hbox, - "style-set", + g_signal_connect(gtkblist->headline_label, /* connecting on headline_hbox doesn't work, because + the signal is not emitted when theme is changed */ + "style-set", G_CALLBACK(headline_style_set), NULL); g_signal_connect (gtkblist->headline_hbox, @@ -5152,12 +5141,14 @@ #ifdef _WIN32 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL); #endif - g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); /* Tooltips */ - g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); + pidgin_tooltip_setup_for_treeview(gtkblist->treeview, NULL, + pidgin_blist_create_tooltip, + pidgin_blist_paint_tip); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); @@ -6505,20 +6496,10 @@ for (tmp = list; tmp; tmp = tmp->next) { - GtkWidget *label; - GtkWidget *rowbox; GtkWidget *input; pce = tmp->data; - rowbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(pce->label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_size_group_add_widget(data->sg, label); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - if (pce->is_int) { GtkObject *adjust; @@ -6526,7 +6507,7 @@ 1, 10, 10); input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); gtk_widget_set_size_request(input, 50, -1); - gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL); } else { @@ -6542,7 +6523,7 @@ if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); } - gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL); g_signal_connect(G_OBJECT(input), "changed", G_CALLBACK(addchat_set_sensitive_if_input_cb), data); } @@ -6553,8 +6534,6 @@ gtk_widget_grab_focus(input); focus = FALSE; } - gtk_label_set_mnemonic_widget(GTK_LABEL(label), input); - pidgin_set_accessible_label(input, label); g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier); g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int)); g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required)); @@ -6597,7 +6576,6 @@ GList *l; PurpleConnection *gc; GtkWidget *label; - GtkWidget *rowbox; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *img; @@ -6673,20 +6651,10 @@ gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); - rowbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("_Account:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_size_group_add_widget(data->sg, label); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - data->account_menu = pidgin_account_option_menu_new(account, FALSE, G_CALLBACK(addchat_select_account_cb), chat_account_filter_func, data); - gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu); - pidgin_set_accessible_label (data->account_menu, label); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL); data->entries_box = gtk_vbox_new(FALSE, 5); gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); @@ -6694,36 +6662,17 @@ rebuild_addchat_entries(data); - rowbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("A_lias:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_size_group_add_widget(data->sg, label); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - data->alias_entry = gtk_entry_new(); if (alias != NULL) gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias); - gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry); - pidgin_set_accessible_label (data->alias_entry, label); + + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_lias:"), data->sg, data->alias_entry, TRUE, NULL); if (name != NULL) gtk_widget_grab_focus(data->alias_entry); - rowbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("_Group:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_size_group_add_widget(data->sg, label); - gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); - data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree()); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->group_combo)->child); - pidgin_set_accessible_label (data->group_combo, label); - gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"), data->sg, data->group_combo, TRUE, NULL); data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online.")); data->persistent = gtk_check_button_new_with_mnemonic(_("_Hide chat when the window is closed.")); @@ -6997,7 +6946,8 @@ pidgin_blist_update_sort_methods(); } -void pidgin_blist_sort_method_unreg(const char *id){ +void pidgin_blist_sort_method_unreg(const char *id) +{ GList *l = pidgin_blist_sort_methods; while(l) { @@ -7009,6 +6959,7 @@ g_free(method); break; } + l = l->next; } pidgin_blist_update_sort_methods(); } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkcertmgr.c --- a/pidgin/gtkcertmgr.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkcertmgr.c Mon Jan 07 03:40:27 2008 +0000 @@ -430,7 +430,7 @@ /* Set up the display columns */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( - "Hostname", + _("Hostname"), renderer, "text", TPM_HOSTNAME_COLUMN, NULL); @@ -545,12 +545,13 @@ So if it is set, don't open another one! */ CertMgrDialog *certmgr_dialog = NULL; -static void +static gboolean certmgr_close_cb(GtkWidget *w, CertMgrDialog *dlg) { /* TODO: Ignoring the arguments to this function may not be ideal, but there *should* only be "one dialog to rule them all" at a time*/ pidgin_certmgr_hide(); + return FALSE; } void @@ -559,7 +560,6 @@ CertMgrDialog *dlg; GtkWidget *win; GtkWidget *vbox; - GtkWidget *bbox; /* Enumerate all the certificates on file */ { @@ -599,7 +599,7 @@ dlg = certmgr_dialog = g_new0(CertMgrDialog, 1); win = dlg->window = - pidgin_create_window(_("Certificate Manager"),/* Title */ + pidgin_create_dialog(_("Certificate Manager"),/* Title */ PIDGIN_HIG_BORDER, /*Window border*/ "certmgr", /* Role */ TRUE); /* Allow resizing */ @@ -611,9 +611,7 @@ gtk_window_set_default_size(GTK_WINDOW(win), 400, 400); /* Main vbox */ - vbox = gtk_vbox_new( FALSE, PIDGIN_HIG_BORDER ); - gtk_container_add(GTK_CONTAINER(win), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); /* Notebook of various certificate managers */ dlg->notebook = gtk_notebook_new(); @@ -622,19 +620,9 @@ 0); gtk_widget_show(dlg->notebook); - /* Box for the close button */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - /* Close button */ - dlg->closebutton = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), dlg->closebutton, FALSE, FALSE, 0); - gtk_widget_show(dlg->closebutton); - g_signal_connect(G_OBJECT(dlg->closebutton), "clicked", - G_CALLBACK(certmgr_close_cb), dlg); + dlg->closebutton = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, + G_CALLBACK(certmgr_close_cb), dlg); /* Add the defined certificate managers */ /* TODO: Find a way of determining whether each is shown or not */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkconn.c --- a/pidgin/gtkconn.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkconn.c Mon Jan 07 03:40:27 2008 +0000 @@ -162,11 +162,6 @@ if (info != NULL) g_hash_table_remove(auto_reconns, account); - /* - * TODO: Do we really want to disable the account when it's - * disconnected by wants_to_die? This happens when you sign - * on from somewhere else, or when you enter an invalid password. - */ purple_account_set_enabled(account, PIDGIN_UI, FALSE); } @@ -182,7 +177,7 @@ } } -static void pidgin_connection_network_connected () +static void pidgin_connection_network_connected (void) { GList *list, *l; PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist(); @@ -201,7 +196,7 @@ g_list_free(list); } -static void pidgin_connection_network_disconnected () +static void pidgin_connection_network_disconnected (void) { GList *list, *l; PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist(); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkconv.c --- a/pidgin/gtkconv.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkconv.c Mon Jan 07 03:40:27 2008 +0000 @@ -67,6 +67,7 @@ #include "gtkthemes.h" #include "gtkutils.h" #include "pidginstock.h" +#include "pidgintooltip.h" #include "gtknickcolors.h" @@ -95,9 +96,10 @@ #define PIDGIN_CONV_ALL ((1 << 7) - 1) -#define SEND_COLOR "#204a87" -#define RECV_COLOR "#cc0000" -#define HIGHLIGHT_COLOR "#AF7F00" +#define DEFAULT_SEND_COLOR "#204a87" +#define DEFAULT_RECV_COLOR "#cc0000" +#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00" +#define DEFAULT_ACTION_COLOR "#062585" /* Undef this to turn off "custom-smiley" debug messages */ #define DEBUG_CUSTOM_SMILEY @@ -156,6 +158,7 @@ static void conv_set_unseen(PurpleConversation *gtkconv, PidginUnseenState state); static void gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state); static void update_typing_icon(PidginConversation *gtkconv); +static void update_typing_message(PidginConversation *gtkconv, const char *message); static const char *item_factory_translate_func (const char *path, gpointer func_data); gboolean pidgin_conv_has_focus(PurpleConversation *conv); static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data); @@ -166,8 +169,6 @@ static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win); static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); -static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv); -static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv); static void hide_conv(PidginConversation *gtkconv, gboolean closetimer); static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y, @@ -1404,6 +1405,7 @@ PidginWindow *win = data; PurpleConversation *conv; gboolean logging; + PurpleBlistNode *node; conv = pidgin_conv_window_get_active_conversation(win); @@ -1414,6 +1416,8 @@ if (logging == purple_conversation_is_logging(conv)) return; + + node = get_conversation_blist_node(conv); if (logging) { @@ -1437,6 +1441,27 @@ /* Disable the logging second, so that the above message can be logged. */ purple_conversation_set_logging(conv, FALSE); } + + /* Save the setting IFF it's different than the pref. */ + switch (conv->type) + { + case PURPLE_CONV_TYPE_IM: + if (logging == purple_prefs_get_bool("/purple/logging/log_ims")) + purple_blist_node_remove_setting(node, "enable-logging"); + else + purple_blist_node_set_bool(node, "enable-logging", logging); + break; + + case PURPLE_CONV_TYPE_CHAT: + if (logging == purple_prefs_get_bool("/purple/logging/log_chats")) + purple_blist_node_remove_setting(node, "enable-logging"); + else + purple_blist_node_set_bool(node, "enable-logging", logging); + break; + + default: + break; + } } static void @@ -2474,7 +2499,6 @@ { PidginConversation *gtkconv; PidginWindow *win; - PurpleBuddy *b; GList *l; GdkPixbuf *status = NULL; GdkPixbuf *infopane_status = NULL; @@ -2487,13 +2511,18 @@ if (conv != gtkconv->active_conv) return; - status = pidgin_conv_get_tab_icon(conv, TRUE); infopane_status = pidgin_conv_get_tab_icon(conv, FALSE); - b = purple_find_buddy(conv->account, conv->name); - if (b) - emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + PurpleBuddy *b = purple_find_buddy(conv->account, conv->name); + if (b) + emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b); + } else { + PurpleChat *c = purple_blist_find_chat(conv->account, conv->name); + if (c) + emblem = pidgin_blist_get_emblem((PurpleBlistNode*)c); + } g_return_if_fail(status != NULL); @@ -3444,6 +3473,34 @@ } static void +update_typing_message(PidginConversation *gtkconv, const char *message) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)); + GtkTextMark *stmark, *enmark; + + stmark = gtk_text_buffer_get_mark(buffer, "typing-notification-start"); + enmark = gtk_text_buffer_get_mark(buffer, "typing-notification-end"); + if (stmark && enmark) { + GtkTextIter start, end; + gtk_text_buffer_get_iter_at_mark(buffer, &start, stmark); + gtk_text_buffer_get_iter_at_mark(buffer, &end, enmark); + gtk_text_buffer_delete_mark(buffer, stmark); + gtk_text_buffer_delete_mark(buffer, enmark); + gtk_text_buffer_delete(buffer, &start, &end); + } else if (message && *message == '\n' && !*(message + 1)) + message = NULL; + + if (message) { + GtkTextIter iter; + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_create_mark(buffer, "typing-notification-start", &iter, TRUE); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, message, -1, "TYPING-NOTIFICATION", NULL); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE); + } +} + +static void update_typing_icon(PidginConversation *gtkconv) { PidginWindow *gtkwin; @@ -3451,6 +3508,7 @@ PurpleConversation *conv = gtkconv->active_conv; char *stock_id; const char *tooltip; + char *message = NULL; gtkwin = gtkconv->win; @@ -3469,6 +3527,7 @@ g_source_remove(gtkconv->u.im->typing_timer); gtkconv->u.im->typing_timer = 0; } + update_typing_message(gtkconv, "\n"); return; } @@ -3478,9 +3537,11 @@ } stock_id = PIDGIN_STOCK_ANIMATION_TYPING1; tooltip = _("User is typing..."); + message = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv)); } else { stock_id = PIDGIN_STOCK_ANIMATION_TYPING5; tooltip = _("User has typed something and stopped"); + message = g_strdup_printf(_("\n%s has typed something and stopped"), purple_conversation_get_title(conv)); if (gtkconv->u.im->typing_timer != 0) { g_source_remove(gtkconv->u.im->typing_timer); gtkconv->u.im->typing_timer = 0; @@ -3503,6 +3564,8 @@ } gtk_widget_show(gtkwin->menu.typing_icon); + update_typing_message(gtkconv, message); + g_free(message); } static gboolean @@ -3862,7 +3925,7 @@ if (is_me) { GdkColor send_color; - gdk_color_parse(SEND_COLOR, &send_color); + gdk_color_parse(DEFAULT_SEND_COLOR, &send_color); #if GTK_CHECK_VERSION(2,6,0) gtk_list_store_insert_with_values(ls, &iter, @@ -4420,8 +4483,8 @@ lines = gtk_text_buffer_get_line_count(buffer); - /* Show a maximum of 4 lines, minimum of 2 */ - lines = MIN(MAX(lines, 2), 4); + /* Show a maximum of 4 lines */ + lines = MIN(lines, 4); wrapped_lines = MIN(MAX(wrapped_lines, 2), 4); pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry)); @@ -4483,6 +4546,36 @@ } } +static gboolean +pidgin_conv_userlist_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path, + gpointer userdata, int *w, int *h) +{ + PidginConversation *gtkconv = userdata; + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->u.chat->list)); + PurpleConversation *conv = gtkconv->active_conv; + PurpleBlistNode *node; + PurplePluginProtocolInfo *prpl_info; + PurpleAccount *account = purple_conversation_get_account(conv); + char *who = NULL; + + if (account->gc == NULL) + return FALSE; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) + return FALSE; + + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl); + node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who)); + if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) + pidgin_blist_draw_tooltip(node, gtkconv->infopane); + + g_free(who); + return FALSE; +} + static void setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned) { @@ -4537,14 +4630,13 @@ g_signal_connect(G_OBJECT(list), "button_press_event", G_CALLBACK(right_click_chat_cb), gtkconv); - g_signal_connect(G_OBJECT(list), "motion-notify-event", - G_CALLBACK(pidgin_userlist_motion_cb), gtkconv); - g_signal_connect(G_OBJECT(list), "leave-notify-event", - G_CALLBACK(pidgin_conv_leave_cb), gtkconv); g_signal_connect(G_OBJECT(list), "popup-menu", G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv); g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv); + pidgin_tooltip_setup_for_treeview(list, gtkconv, + pidgin_conv_userlist_create_tooltip, NULL); + rend = gtk_cell_renderer_text_new(); g_object_set(rend, "foreground-set", TRUE, @@ -4580,32 +4672,12 @@ gtk_container_add(GTK_CONTAINER(sw), list); } -/* Stuff used to display tooltips on the infopane */ -static struct { - int timeout; - PidginConversation *gtkconv; /* This is the Pidgin conversation that - triggered the tooltip */ - int userlistx; - int userlisty; -} tooltip; - -static void -reset_tooltip() -{ - if (tooltip.timeout != 0) { - g_source_remove(tooltip.timeout); - tooltip.timeout = 0; - } - tooltip.gtkconv = NULL; -} - static gboolean -pidgin_conv_tooltip_timeout(PidginConversation *gtkconv) +pidgin_conv_create_tooltip(GtkWidget *tipwindow, gpointer userdata, int *w, int *h) { PurpleBlistNode *node = NULL; PurpleConversation *conv; - - g_return_val_if_fail (tooltip.gtkconv == gtkconv, FALSE); + PidginConversation *gtkconv = userdata; conv = gtkconv->active_conv; if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { @@ -4628,102 +4700,6 @@ return FALSE; } -static void -pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv) -{ - pidgin_blist_tooltip_destroy(); - reset_tooltip(); -} - -static gboolean -pidgin_conv_motion_cb (GtkWidget *infopane, GdkEventMotion *event, PidginConversation *gtkconv) -{ - int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - - pidgin_blist_tooltip_destroy(); - if (delay == 0) - return FALSE; - - if (tooltip.timeout != 0) - g_source_remove(tooltip.timeout); - - tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_conv_tooltip_timeout, gtkconv); - tooltip.gtkconv = gtkconv; - return FALSE; -} - -static gboolean -pidgin_userlist_tooltip_timeout(PidginConversation *gtkconv) -{ - PurplePluginProtocolInfo *prpl_info; - PurpleConversation *conv = gtkconv->active_conv; - PidginChatPane *gtkchat; - PurpleBlistNode *node = NULL; - PurpleAccount *account; - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeViewColumn *column; - gchar *who; - int x, y; - - gtkchat = gtkconv->u.chat; - account = purple_conversation_get_account(conv); - - if (account->gc == NULL) - return FALSE; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl); - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list), - tooltip.userlistx, tooltip.userlisty, &path, &column, &x, &y)) - return FALSE; - - gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); - gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); - - node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who)); - if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) - pidgin_blist_draw_tooltip(node, gtkconv->infopane); - - g_free(who); - gtk_tree_path_free(path); - - - return FALSE; -} - -static gboolean -pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv) -{ - PurpleConversation *conv; - PurpleAccount *account; - int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - - pidgin_blist_tooltip_destroy(); - if (delay == 0) - return FALSE; - - if (tooltip.timeout != 0) - g_source_remove(tooltip.timeout); - tooltip.timeout = 0; - - conv = gtkconv->active_conv; - account = purple_conversation_get_account(conv); - - if (account->gc == NULL) - return FALSE; - - tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_userlist_tooltip_timeout, gtkconv); - tooltip.gtkconv = gtkconv; - tooltip.userlistx = event->x; - tooltip.userlisty = event->y; - - return FALSE; -} - static GtkWidget * setup_common_pane(PidginConversation *gtkconv) { @@ -4757,10 +4733,8 @@ g_signal_connect(G_OBJECT(event_box), "button-press-event", G_CALLBACK(infopane_press_cb), gtkconv); - g_signal_connect(G_OBJECT(event_box), "motion-notify-event", - G_CALLBACK(pidgin_conv_motion_cb), gtkconv); - g_signal_connect(G_OBJECT(event_box), "leave-notify-event", - G_CALLBACK(pidgin_conv_leave_cb), gtkconv); + pidgin_tooltip_setup_for_widget(event_box, gtkconv, + pidgin_conv_create_tooltip, NULL); gtkconv->infopane = gtk_cell_view_new(); gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF); @@ -5064,6 +5038,7 @@ GtkWidget *pane = NULL; GtkWidget *tab_cont; PurpleBlistNode *convnode; + PurpleValue *value; if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) { conv->ui_data = gtkconv; @@ -5133,6 +5108,13 @@ g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", G_CALLBACK(conv_dnd_recv), gtkconv); + gtk_text_buffer_create_tag(GTK_IMHTML(gtkconv->imhtml)->text_buffer, "TYPING-NOTIFICATION", + "foreground", "#888888", + "justification", GTK_JUSTIFY_LEFT, /* XXX: RTL'ify */ + "weight", PANGO_WEIGHT_BOLD, + "scale", PANGO_SCALE_SMALL, + NULL); + /* Setup the container for the tab. */ gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv); @@ -5144,6 +5126,13 @@ if (convnode == NULL || !purple_blist_node_get_bool(convnode, "gtk-mute-sound")) gtkconv->make_sound = TRUE; + if (convnode != NULL && + (value = g_hash_table_lookup(convnode->settings, "enable-logging")) && + purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) + { + purple_conversation_set_logging(conv, purple_value_get_boolean(value)); + } + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar")) gtk_widget_show(gtkconv->toolbar); else @@ -5274,9 +5263,6 @@ g_source_remove(gtkconv->attach.timer); } - if (tooltip.gtkconv == gtkconv) - reset_tooltip(); - g_free(gtkconv); } @@ -5706,6 +5692,7 @@ } else { if (purple_message_meify(new_message, -1)) { + GdkColor *col; str = g_malloc(1024); if (flags & PURPLE_MESSAGE_AUTO_RESP) { @@ -5718,9 +5705,20 @@ } if (flags & PURPLE_MESSAGE_NICK) - strcpy(color, HIGHLIGHT_COLOR); + gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL); else - strcpy(color, "#062585"); + gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "action-name-color", &col, NULL); + + if(col) { + g_snprintf(color, sizeof(color), "#%02X%02X%02X", + col->red >> 8, col->green >> 8, col->blue >> 8); + } + else { + if (flags & PURPLE_MESSAGE_NICK) + strcpy(color, DEFAULT_HIGHLIGHT_COLOR); + else + strcpy(color, DEFAULT_ACTION_COLOR); + } } else { str = g_malloc(1024); @@ -5732,19 +5730,46 @@ g_snprintf(str, 1024, "%s:", alias_escaped); tag_end_offset = 1; } - if (flags & PURPLE_MESSAGE_NICK) - strcpy(color, HIGHLIGHT_COLOR); + if (flags & PURPLE_MESSAGE_NICK) { + GdkColor *col; + gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL); + if(col) { + g_snprintf(color, sizeof(color), "#%02X%02X%02X", + col->red >> 8, col->green >> 8, col->blue >> 8); + } + else { + strcpy(color, DEFAULT_HIGHLIGHT_COLOR); + } + } else if (flags & PURPLE_MESSAGE_RECV) { if (type == PURPLE_CONV_TYPE_CHAT) { GdkColor *col = get_nick_color(gtkconv, name); g_snprintf(color, sizeof(color), "#%02X%02X%02X", col->red >> 8, col->green >> 8, col->blue >> 8); - } else - strcpy(color, RECV_COLOR); + } else { + GdkColor *col; + gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "receive-name-color", &col, NULL); + if(col) { + g_snprintf(color, sizeof(color), "#%02X%02X%02X", + col->red >> 8, col->green >> 8, col->blue >> 8); + } + else { + strcpy(color, DEFAULT_RECV_COLOR); + } + } } - else if (flags & PURPLE_MESSAGE_SEND) - strcpy(color, SEND_COLOR); + else if (flags & PURPLE_MESSAGE_SEND) { + GdkColor *col; + gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "send-name-color", &col, NULL); + if(col) { + g_snprintf(color, sizeof(color), "#%02X%02X%02X", + col->red >> 8, col->green >> 8, col->blue >> 8); + } + else { + strcpy(color, DEFAULT_SEND_COLOR); + } + } else { purple_debug_error("gtkconv", "message missing flags\n"); strcpy(color, "#000000"); @@ -5865,6 +5890,7 @@ (type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"), account, name, displaying, conv, flags); g_free(displaying); + update_typing_message(gtkconv, NULL); } static void @@ -6958,10 +6984,8 @@ GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); g_signal_connect(G_OBJECT(event), "button-press-event", G_CALLBACK(icon_menu), gtkconv); - g_signal_connect(G_OBJECT(event), "motion-notify-event", - G_CALLBACK(pidgin_conv_motion_cb), gtkconv); - g_signal_connect(G_OBJECT(event), "leave-notify-event", - G_CALLBACK(pidgin_conv_leave_cb), gtkconv); + + pidgin_tooltip_setup_for_widget(event, gtkconv, pidgin_conv_create_tooltip, NULL); gtk_widget_show(event); gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale); @@ -7972,6 +7996,7 @@ /* Set default tab colors */ GString *str = g_string_new(NULL); GtkSettings *settings = gtk_settings_get_default(); + GtkStyle *parent = gtk_rc_get_style_by_paths(settings, "tab-container.tab-label*", NULL, G_TYPE_NONE), *now; struct { const char *stylename; const char *labelname; @@ -7986,8 +8011,9 @@ }; int iter; for (iter = 0; styles[iter].stylename; iter++) { - if (!gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE)) - /* Apparently both ACTIVE and NORMAL are required */ + now = gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE); + if (parent == now || + (parent && now && parent->rc_style == now->rc_style)) { g_string_append_printf(str, "style \"%s\" {\n" "fg[ACTIVE] = \"%s\"\n" "}\n" @@ -7995,6 +8021,7 @@ styles[iter].stylename, styles[iter].color, styles[iter].labelname, styles[iter].stylename); + } } gtk_rc_parse_string(str->str); g_string_free(str, TRUE); @@ -8417,7 +8444,7 @@ } if (e->button == 3) { - /* Right click was pressed. Popup the Send To menu. */ + /* Right click was pressed. Popup the context menu. */ GtkWidget *menu = gtk_menu_new(), *sub; gboolean populated = populate_menu_with_options(menu, gtkconv, TRUE); sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to)); @@ -8850,7 +8877,7 @@ PurpleConversation *conv = gtkconv->active_conv; const char *text = NULL; - if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) { + if (!GTK_WIDGET_VISIBLE(gtkconv->infopane)) { /* There's already an entry for alias. Let's not create another one. */ return FALSE; } @@ -9395,6 +9422,7 @@ gtkconv->tabby = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); else gtkconv->tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + gtk_widget_set_name(gtkconv->tabby, "tab-container"); /* select the correct ordering for verticle tabs */ if (angle == 90) { @@ -10062,8 +10090,8 @@ GdkColor send_color; time_t breakout_time; - gdk_color_parse(HIGHLIGHT_COLOR, &nick_highlight); - gdk_color_parse(SEND_COLOR, &send_color); + gdk_color_parse(DEFAULT_HIGHLIGHT_COLOR, &nick_highlight); + gdk_color_parse(DEFAULT_SEND_COLOR, &send_color); srand(background.red + background.green + background.blue + 1); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkdebug.c --- a/pidgin/gtkdebug.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkdebug.c Mon Jan 07 03:40:27 2008 +0000 @@ -686,13 +686,11 @@ width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/width"); height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/height"); - PIDGIN_DIALOG(win->window); + win->window = pidgin_create_dialog(_("Debug Window"), 0, "debug", TRUE); purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n", width, height); gtk_window_set_default_size(GTK_WINDOW(win->window), width, height); - gtk_window_set_role(GTK_WINDOW(win->window), "debug"); - gtk_window_set_title(GTK_WINDOW(win->window), _("Debug Window")); g_signal_connect(G_OBJECT(win->window), "delete_event", G_CALLBACK(debug_window_destroy), NULL); @@ -700,7 +698,7 @@ G_CALLBACK(configure_cb), win); handle = pidgin_debug_get_handle(); - + #ifdef HAVE_REGEX_H /* the list store for all the messages */ win->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); @@ -716,8 +714,7 @@ #endif /* HAVE_REGEX_H */ /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(win->window), vbox); + vbox = pidgin_dialog_get_vbox(GTK_DIALOG(win->window)); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) { /* Setup our top button bar thingie. */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkdialogs.c Mon Jan 07 03:40:27 2008 +0000 @@ -89,6 +89,7 @@ {"Megan 'Cae' Schneider", N_("support/QA"), NULL}, {"Evan Schoenberg", N_("developer"), NULL}, {"Kevin 'SimGuy' Stange", N_("developer & webmaster"), NULL}, + {"Will 'resiak' Thompson", N_("developer"), NULL}, {"Stu 'nosnilmot' Tomlinson", N_("developer"), NULL}, {"Nathan 'faceprint' Walp", N_("developer"), NULL}, {NULL, NULL, NULL} @@ -98,8 +99,8 @@ static const struct developer patch_writers[] = { {"Dennis 'EvilDennisR' Ristuccia", N_("Senior Contributor/QA"), NULL}, {"Peter 'Fmoo' Ruibal", NULL, NULL}, + {"Elliott 'QuLogic' Sales de Andrade", NULL, NULL}, {"Gabriel 'Nix' Schulhof", NULL, NULL}, - {"Will 'resiak' Thompson", NULL, NULL}, {NULL, NULL, NULL} }; @@ -287,7 +288,7 @@ } } -static void destroy_about() +static void destroy_about(void) { if (about != NULL) gtk_widget_destroy(about); @@ -336,12 +337,10 @@ void pidgin_dialogs_about() { - GtkWidget *hbox; GtkWidget *vbox; GtkWidget *logo; GtkWidget *frame; GtkWidget *text; - GtkWidget *bbox; GtkWidget *button; GtkTextIter iter; GString *str; @@ -356,20 +355,12 @@ return; } - PIDGIN_DIALOG(about); tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME); - gtk_window_set_title(GTK_WINDOW(about), tmp); + about = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "about", TRUE); g_free(tmp); - gtk_window_set_role(GTK_WINDOW(about), "about"); - gtk_window_set_resizable(GTK_WINDOW(about), TRUE); gtk_window_set_default_size(GTK_WINDOW(about), 340, 450); - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_set_border_width(GTK_CONTAINER(hbox), PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(about), hbox); - - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(hbox), vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(about), FALSE, PIDGIN_HIG_BORDER); /* Generate a logo with a version number */ logo = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -708,15 +699,9 @@ gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), &iter); /* Close Button */ - bbox = gtk_hbutton_box_new(); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + button = pidgin_dialog_add_button(GTK_DIALOG(about), GTK_STOCK_CLOSE, + G_CALLBACK(destroy_about), about); - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect_swapped(G_OBJECT(button), "clicked", - G_CALLBACK(destroy_about), G_OBJECT(about)); g_signal_connect(G_OBJECT(about), "destroy", G_CALLBACK(destroy_about), G_OBJECT(about)); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkdialogs.h --- a/pidgin/gtkdialogs.h Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkdialogs.h Mon Jan 07 03:40:27 2008 +0000 @@ -54,11 +54,10 @@ /* Everything after this should probably be moved elsewhere */ -/** - * Our UI's identifier. - */ +#ifndef PIDGIN_DISABLE_DEPRECATED #define PIDGIN_DIALOG(x) x = gtk_window_new(GTK_WINDOW_TOPLEVEL); \ gtk_window_set_type_hint(GTK_WINDOW(x), GDK_WINDOW_TYPE_HINT_DIALOG) +#endif #define PIDGIN_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED) #endif /* _PIDGINDIALOGS_H_ */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkdocklet-x11.c --- a/pidgin/gtkdocklet-x11.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkdocklet-x11.c Mon Jan 07 03:40:27 2008 +0000 @@ -48,7 +48,7 @@ static void docklet_x11_create(gboolean); static gboolean -docklet_x11_recreate_cb() +docklet_x11_recreate_cb(gpointer data) { docklet_x11_create(TRUE); @@ -79,13 +79,14 @@ g_idle_add(docklet_x11_recreate_cb, NULL); } -static void +static gboolean docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data) { if (event->type != GDK_BUTTON_RELEASE) - return; + return FALSE; pidgin_docklet_clicked(event->button); + return TRUE; } static void @@ -146,7 +147,7 @@ } static void -docklet_x11_blank_icon() +docklet_x11_blank_icon(void) { if (!blank_icon) { GtkIconSize size = GTK_ICON_SIZE_LARGE_TOOLBAR; @@ -204,7 +205,7 @@ #endif static void -docklet_x11_destroy() +docklet_x11_destroy(void) { g_return_if_fail(docklet != NULL); @@ -229,7 +230,7 @@ } static gboolean -docklet_x11_embed_timeout_cb() +docklet_x11_embed_timeout_cb(gpointer data) { /* The docklet was not embedded within the timeout. * Remove it as a visibility manager, but leave the plugin @@ -300,7 +301,7 @@ } static void -docklet_x11_create_ui_op() +docklet_x11_create_ui_op(void) { docklet_x11_create(FALSE); } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkdocklet.c --- a/pidgin/gtkdocklet.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkdocklet.c Mon Jan 07 03:40:27 2008 +0000 @@ -62,7 +62,7 @@ * docklet status and utility functions **************************************************************************/ static gboolean -docklet_blink_icon() +docklet_blink_icon(gpointer data) { static gboolean blinked = FALSE; gboolean ret = FALSE; /* by default, don't keep blinking */ @@ -108,7 +108,7 @@ } static gboolean -docklet_update_status() +docklet_update_status(void) { GList *convs, *l; int count; @@ -219,7 +219,7 @@ } static gboolean -online_account_supports_chat() +online_account_supports_chat(void) { GList *c = NULL; c = purple_connections_get_all(); @@ -523,7 +523,7 @@ } static GtkWidget * -docklet_status_submenu() +docklet_status_submenu(void) { GtkWidget *submenu, *menuitem; GList *popular_statuses, *cur; @@ -667,7 +667,8 @@ } static void -docklet_menu() { +docklet_menu(void) +{ static GtkWidget *menu = NULL; GtkWidget *menuitem; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkft.c --- a/pidgin/gtkft.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkft.c Mon Jan 07 03:40:27 2008 +0000 @@ -156,15 +156,15 @@ } if (time_remaining != NULL) { - if (purple_xfer_get_size(xfer) == 0) { - *time_remaining = g_strdup(_("Unknown")); - } - else if (purple_xfer_is_completed(xfer)) { + if (purple_xfer_is_completed(xfer)) { *time_remaining = g_strdup(_("Finished")); } else if (purple_xfer_is_canceled(xfer)) { *time_remaining = g_strdup(_("Canceled")); } + else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps == 0)) { + *time_remaining = g_strdup(_("Unknown")); + } else if (kb_sent <= 0) { *time_remaining = g_strdup(_("Waiting for transfer to begin")); } @@ -745,7 +745,6 @@ PidginXferDialog *dialog; GtkWidget *window; GtkWidget *vbox1, *vbox2; - GtkWidget *bbox; GtkWidget *sw; GtkWidget *button; GtkWidget *expander; @@ -759,15 +758,13 @@ purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished"); /* Create the window. */ - dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE); + dialog->window = window = pidgin_create_dialog(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_win_cb), dialog); /* Create the parent vbox for everything. */ - vbox1 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(window), vbox1); - gtk_widget_show(vbox1); + vbox1 = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER); /* Create the main vbox for top half of the window. */ vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); @@ -812,71 +809,35 @@ gtk_container_add(GTK_CONTAINER(expander), table); gtk_widget_show(table); - /* Now the button box for the buttons */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - /* Open button */ - button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog); gtk_widget_set_sensitive(button, FALSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); dialog->open_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(open_button_cb), dialog); - /* Pause button */ - button = gtk_button_new_with_mnemonic(_("_Pause")); + button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Pause"), G_CALLBACK(pause_button_cb), dialog); gtk_widget_set_sensitive(button, FALSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); dialog->pause_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pause_button_cb), dialog); - /* Resume button */ - button = gtk_button_new_with_mnemonic(_("_Resume")); + button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Resume"), G_CALLBACK(resume_button_cb), dialog); gtk_widget_set_sensitive(button, FALSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); dialog->resume_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(resume_button_cb), dialog); - /* Remove button */ - button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog); gtk_widget_hide(button); dialog->remove_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(remove_button_cb), dialog); - /* Stop button */ - button = gtk_button_new_from_stock(GTK_STOCK_STOP); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); + button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog); gtk_widget_set_sensitive(button, FALSE); dialog->stop_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(stop_button_cb), dialog); - /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); + button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog); dialog->close_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(close_button_cb), dialog); - #ifdef _WIN32 g_signal_connect(G_OBJECT(dialog->window), "show", G_CALLBACK(winpidgin_ensure_onscreen), dialog->window); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkidle.c --- a/pidgin/gtkidle.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkidle.c Mon Jan 07 03:40:27 2008 +0000 @@ -69,7 +69,7 @@ */ #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT) static time_t -pidgin_get_time_idle() +pidgin_get_time_idle(void) { # ifdef HAVE_IOKIT /* Query the IOKit API */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkimhtml.c Mon Jan 07 03:40:27 2008 +0000 @@ -293,7 +293,7 @@ #endif static GtkSmileyTree* -gtk_smiley_tree_new () +gtk_smiley_tree_new (void) { return g_new0 (GtkSmileyTree, 1); } @@ -820,7 +820,6 @@ static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data) { GtkWidget *menuitem; - GtkWidget *mi, *img; menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text")); gtk_widget_show(menuitem); @@ -1020,7 +1019,7 @@ static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext) { GtkTextIter iter; - GtkIMHtmlOptions flags = plaintext ? 0 : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS); + GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS); if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL)) gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE); @@ -1144,14 +1143,13 @@ #ifdef _WIN32 /* If we're on windows, let's see if we can get data from the HTML Format clipboard before we try to paste from the GTK buffer */ - if (!clipboard_paste_html_win32(imhtml)) { + if (!clipboard_paste_html_win32(imhtml)) #endif + { GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE), paste_received_cb, imhtml); -#ifdef _WIN32 } -#endif g_signal_stop_emission_by_name(imhtml, "paste-clipboard"); } @@ -1405,6 +1403,22 @@ _("Hyperlink prelight color"), _("Color to draw hyperlinks when mouse is over them."), GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("send-name-color", + _("Sent Message Name Color"), + _("Color to draw the name of a message you sent."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("receive-name-color", + _("Received Message Name Color"), + _("Color to draw the name of a message you received."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("highlight-name-color", + _("\"Attention\" Name Color"), + _("Color to draw the name of a message you received containing your name."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color", + _("Action Message Name Color"), + _("Color to draw the name of an action message."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); binding_set = gtk_binding_set_by_class (parent_class); gtk_binding_entry_add_signal (binding_set, GDK_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD); @@ -2990,6 +3004,7 @@ pos += tlen; g_free(tag); /* This was allocated back in VALID_TAG() */ } else if (imhtml->edit.link == NULL && + !(options & GTK_IMHTML_NO_SMILEY) && gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) { GtkIMHtmlFontDetail *fd; gchar *sml = NULL; @@ -3475,6 +3490,9 @@ return; } #endif /* FILECHOOSER */ +#if 0 /* mismatched curly braces */ + } +#endif /* * XXX - We should probably prompt the user to determine if they really @@ -4267,33 +4285,6 @@ g_object_unref(object); } -static void populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer nul) -{ - GtkWidget *mi, *img; - - mi = gtk_menu_item_new(); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_mnemonic(_("_Font")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_mnemonic(_("_Insert")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); - - img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU); - mi = gtk_image_menu_item_new_with_mnemonic(_("S_mile!")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); - gtk_widget_show(mi); - gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); -} - static void imhtml_toggle_bold(GtkIMHtml *imhtml) { GtkTextIter start, end; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkimhtml.h Mon Jan 07 03:40:27 2008 +0000 @@ -225,7 +225,8 @@ GTK_IMHTML_RETURN_LOG = 1 << 7, GTK_IMHTML_USE_POINTSIZE = 1 << 8, GTK_IMHTML_NO_FORMATTING = 1 << 9, - GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10 + GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10, + GTK_IMHTML_NO_SMILEY = 1 << 11, } GtkIMHtmlOptions; enum { diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Mon Jan 07 03:40:27 2008 +0000 @@ -99,7 +99,7 @@ gtk_widget_grab_focus(toolbar->imhtml); } -static void +static gboolean destroy_toolbar_font(GtkWidget *widget, GdkEvent *event, GtkIMHtmlToolbar *toolbar) { @@ -111,6 +111,7 @@ gtk_widget_destroy(toolbar->font_dialog); toolbar->font_dialog = NULL; } + return FALSE; } static void @@ -191,7 +192,7 @@ gtk_widget_grab_focus(toolbar->imhtml); } -static void +static gboolean destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event, GtkIMHtmlToolbar *toolbar) { @@ -203,6 +204,7 @@ gtk_widget_destroy(toolbar->fgcolor_dialog); toolbar->fgcolor_dialog = NULL; } + return FALSE; } static void cancel_toolbar_fgcolor(GtkWidget *widget, @@ -263,7 +265,7 @@ gtk_widget_grab_focus(toolbar->imhtml); } -static void +static gboolean destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event, GtkIMHtmlToolbar *toolbar) { @@ -279,6 +281,7 @@ gtk_widget_destroy(toolbar->bgcolor_dialog); toolbar->bgcolor_dialog = NULL; } + return FALSE; } static void @@ -467,10 +470,11 @@ GtkTextMark *ins; #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - if (response != GTK_RESPONSE_ACCEPT) { + if (response != GTK_RESPONSE_ACCEPT) #else /* FILECHOOSER */ - if (response != GTK_RESPONSE_OK) { + if (response != GTK_RESPONSE_OK) #endif /* FILECHOOSER */ + { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE); return; } @@ -574,11 +578,12 @@ } } -static void +static gboolean close_smiley_dialog(GtkWidget *widget, GdkEvent *event, GtkIMHtmlToolbar *toolbar) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE); + return FALSE; } @@ -704,10 +709,8 @@ smileys = smileys->next; } - PIDGIN_DIALOG(dialog); + dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE); - gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); - gtk_window_set_role(GTK_WINDOW(dialog), "smiley_dialog"); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); if (unique_smileys != NULL) { @@ -764,18 +767,15 @@ } g_signal_connect(G_OBJECT(dialog), "key-press-event", (GCallback)smiley_dialog_input_cb, toolbar); - gtk_container_add(GTK_CONTAINER(dialog), smiley_table); + gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(dialog))), smiley_table); gtk_widget_show(smiley_table); - gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); - /* connect signals */ g_signal_connect(G_OBJECT(dialog), "delete_event", G_CALLBACK(close_smiley_dialog), toolbar); /* show everything */ - gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!")); gtk_widget_show_all(dialog); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar)))); @@ -1096,8 +1096,8 @@ {PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, do_small, &toolbar->smaller_size, _("Decrease Font Size")}, {"", NULL, NULL, NULL}, {PIDGIN_STOCK_TOOLBAR_FONT_FACE, toggle_font, &toolbar->font, _("Font Face")}, - {PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")}, - {PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")}, + {PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")}, + {PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")}, {"", NULL, NULL, NULL}, {PIDGIN_STOCK_CLEAR, clear_formatting_cb, &toolbar->clear, _("Reset Formatting")}, {"", NULL, NULL, NULL}, diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkmain.c --- a/pidgin/gtkmain.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkmain.c Mon Jan 07 03:40:27 2008 +0000 @@ -174,7 +174,7 @@ * be wise to move this code into gtksound.c. */ static void -clean_pid() +clean_pid(void) { int status; pid_t pid; @@ -243,7 +243,7 @@ #endif static int -ui_main() +ui_main(void) { #ifndef _WIN32 GList *icons = NULL; @@ -361,7 +361,7 @@ gtk_main_quit(); } -static GHashTable *pidgin_ui_get_info() +static GHashTable *pidgin_ui_get_info(void) { if(NULL == ui_info) { ui_info = g_hash_table_new(g_str_hash, g_str_equal); @@ -399,6 +399,7 @@ if (terse) { text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, DISPLAY_VERSION, name); } else { +#ifndef WIN32 text = g_strdup_printf(_("%s %s\n" "Usage: %s [OPTION]...\n\n" " -c, --config=DIR use DIR for config files\n" @@ -408,10 +409,20 @@ " -n, --nologin don't automatically login\n" " -l, --login[=NAME] automatically login (optional argument NAME specifies\n" " account(s) to use, separated by commas)\n" -#ifndef WIN32 " --display=DISPLAY X display to use\n" + " -v, --version display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name); +#else + text = g_strdup_printf(_("%s %s\n" + "Usage: %s [OPTION]...\n\n" + " -c, --config=DIR use DIR for config files\n" + " -d, --debug print debugging messages to stdout\n" + " -h, --help display this help and exit\n" + " -m, --multiple do not ensure single instance\n" + " -n, --nologin don't automatically login\n" + " -l, --login[=NAME] automatically login (optional argument NAME specifies\n" + " account(s) to use, separated by commas)\n" + " -v, --version display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name); #endif - " -v, --version display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name); } purple_print_utf8_to_console(stdout, text); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtknotify.c --- a/pidgin/gtknotify.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtknotify.c Mon Jan 07 03:40:27 2008 +0000 @@ -166,16 +166,18 @@ mail_dialog = NULL; } -static void +static gboolean formatted_close_cb(GtkWidget *win, GdkEvent *event, void *user_data) { purple_notify_close(PURPLE_NOTIFY_FORMATTED, win); + return FALSE; } -static void +static gboolean searchresults_close_cb(PidginNotifySearchResultsData *data, GdkEvent *event, gpointer user_data) { purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS, data); + return FALSE; } static void @@ -284,6 +286,8 @@ gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + pidgin_auto_parent_window(dialog); + gtk_widget_show_all(dialog); return dialog; @@ -328,7 +332,7 @@ } static GtkWidget * -pidgin_get_mail_dialog() +pidgin_get_mail_dialog(void) { if (mail_dialog == NULL) { GtkWidget *dialog = NULL; @@ -600,7 +604,7 @@ } static GtkIMHtmlOptions -notify_imhtml_options() +notify_imhtml_options(void) { GtkIMHtmlOptions options = 0; @@ -684,6 +688,8 @@ g_object_set_data(G_OBJECT(window), "info-widget", imhtml); /* Show the window */ + pidgin_auto_parent_window(window); + gtk_widget_show(window); return window; @@ -894,6 +900,8 @@ pidgin_notify_searchresults_new_rows(gc, results, data); /* Show the window */ + pidgin_auto_parent_window(window); + gtk_widget_show(window); return data; } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkplugin.c --- a/pidgin/gtkplugin.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkplugin.c Mon Jan 07 03:40:27 2008 +0000 @@ -31,6 +31,7 @@ #include "debug.h" #include "prefs.h" #include "request.h" +#include "pidgintooltip.h" #include @@ -531,6 +532,58 @@ plugin_dialog_response_cb(dialog, PIDGIN_RESPONSE_CONFIGURE, sel); } +static gboolean +pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, gpointer data) +{ + PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin"); + gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, tipwindow, "tooltip", + 6, 6, layout); + return TRUE; +} + +static gboolean +pidgin_plugins_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path, + gpointer data, int *w, int *h) +{ + GtkTreeIter iter; + GtkTreeView *treeview = GTK_TREE_VIEW(data); + PurplePlugin *plugin = NULL; + GtkTreeModel *model = gtk_tree_view_get_model(treeview); + PangoLayout *layout; + int width, height; + char *markup, *name, *desc, *author; + + if (!gtk_tree_model_get_iter(model, &iter, path)) + return FALSE; + + gtk_tree_model_get(model, &iter, 2, &plugin, -1); + + markup = g_strdup_printf("%s\nDescription: %s\nAuthor: %s", + name = g_markup_escape_text(purple_plugin_get_name(plugin), -1), + desc = g_markup_escape_text(purple_plugin_get_description(plugin), -1), + author = g_markup_escape_text(purple_plugin_get_author(plugin), -1)); + + layout = gtk_widget_create_pango_layout(tipwindow, NULL); + pango_layout_set_markup(layout, markup, -1); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_width(layout, 600000); + pango_layout_get_size(layout, &width, &height); + g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref); + + if (w) + *w = PANGO_PIXELS(width) + 12; + if (h) + *h = PANGO_PIXELS(height) + 12; + + g_free(markup); + g_free(name); + g_free(desc); + g_free(author); + + return TRUE; +} + void pidgin_plugin_dialog_show() { GtkWidget *sw; @@ -613,6 +666,10 @@ gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view), pidgin_tree_view_search_equal_func, NULL, NULL); + pidgin_tooltip_setup_for_treeview(event_view, event_view, + pidgin_plugins_create_tooltip, + pidgin_plugins_paint_tooltip); + expander = gtk_expander_new(_("Plugin Details")); gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE); plugin_details = gtk_label_new(NULL); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkpluginpref.c --- a/pidgin/gtkpluginpref.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkpluginpref.c Mon Jan 07 03:40:27 2008 +0000 @@ -93,22 +93,6 @@ case PURPLE_PLUGIN_PREF_NONE: default: if (format == PURPLE_STRING_FORMAT_TYPE_NONE) - box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - else - box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - - gtk_widget_show(box); - gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0); - - gtk_label = gtk_label_new_with_mnemonic(pref_label); - gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5); - gtk_widget_show(gtk_label); - gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0); - - if(sg) - gtk_size_group_add_widget(sg, gtk_label); - - if (format == PURPLE_STRING_FORMAT_TYPE_NONE) { entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(pref_name)); @@ -123,9 +107,7 @@ g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_cb), (gpointer)pref_name); - gtk_label_set_mnemonic_widget(GTK_LABEL(gtk_label), entry); - gtk_widget_show(entry); - gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(parent), pref_label, sg, entry, TRUE, NULL); } else { @@ -135,6 +117,19 @@ GtkWidget *toolbar; GtkWidget *frame; + box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + + gtk_widget_show(box); + gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0); + + gtk_label = gtk_label_new_with_mnemonic(pref_label); + gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5); + gtk_widget_show(gtk_label); + gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0); + + if(sg) + gtk_size_group_add_widget(sg, gtk_label); + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkpounce.c --- a/pidgin/gtkpounce.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkpounce.c Mon Jan 07 03:40:27 2008 +0000 @@ -1317,7 +1317,6 @@ pidgin_pounces_manager_show(void) { PouncesManager *dialog; - GtkWidget *bbox; GtkWidget *button; GtkWidget *list; GtkWidget *vbox; @@ -1334,7 +1333,7 @@ width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width"); height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height"); - dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE); + dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE); gtk_window_set_default_size(GTK_WINDOW(win), width, height); g_signal_connect(G_OBJECT(win), "delete_event", @@ -1343,61 +1342,33 @@ G_CALLBACK(pounces_manager_configure_cb), dialog); /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); /* List of saved buddy pounces */ list = create_pounces_list(dialog); gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); - /* Button box. */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); + /* Add button */ + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(pounces_manager_add_cb), dialog); + gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL)); - /* Add button */ - button = gtk_button_new_from_stock(GTK_STOCK_ADD); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL)); purple_signal_connect(purple_connections_get_handle(), "signed-on", pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button); purple_signal_connect(purple_connections_get_handle(), "signed-off", pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pounces_manager_add_cb), dialog); /* Modify button */ - button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY); - dialog->modify_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(pounces_manager_modify_cb), dialog); gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pounces_manager_modify_cb), dialog); + dialog->modify_button = button; /* Delete button */ - button = gtk_button_new_from_stock(GTK_STOCK_DELETE); - dialog->delete_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(pounces_manager_delete_cb), dialog); gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pounces_manager_delete_cb), dialog); + dialog->delete_button = button; /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pounces_manager_close_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(pounces_manager_close_cb), dialog); gtk_widget_show(win); } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkprefs.c Mon Jan 07 03:40:27 2008 +0000 @@ -89,23 +89,12 @@ pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg) { - GtkWidget *hbox; - GtkWidget *label; GtkWidget *spin; GtkObject *adjust; int val; val = purple_prefs_get_int(key); - hbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(title); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - adjust = gtk_adjustment_new(val, min, max, 1, 1, 1); spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); g_object_set_data(G_OBJECT(spin), "val", (char *)key); @@ -113,21 +102,11 @@ gtk_widget_set_size_request(spin, 50, -1); else gtk_widget_set_size_request(spin, 60, -1); - gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(adjust), "value-changed", G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); gtk_widget_show(spin); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin); - - if (sg) { - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - } - - pidgin_set_accessible_label (spin, label); - - return hbox; + return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL); } static void @@ -141,37 +120,18 @@ pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, const char *key, GtkSizeGroup *sg) { - GtkWidget *hbox, *label, *entry; + GtkWidget *entry; const gchar *value; value = purple_prefs_get_string(key); - hbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(page), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(title); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), value); - gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_set), (char*)key); gtk_widget_show(entry); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry); - - if(sg) { - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - } - - pidgin_set_accessible_label(entry, label); - - return hbox; + return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL); } static void @@ -205,7 +165,6 @@ { GtkWidget *dropdown, *opt, *menu; GtkWidget *label = NULL; - GtkWidget *hbox; gchar *text; const char *stored_str = NULL; int stored_int = 0; @@ -215,19 +174,6 @@ g_return_val_if_fail(menuitems != NULL, NULL); - if (title != NULL) { - hbox = gtk_hbox_new(FALSE, 5); - /*gtk_container_add (GTK_CONTAINER (box), hbox);*/ - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(title); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - } else { - hbox = box; - } - #if 0 /* GTK_CHECK_VERSION(2,4,0) */ if(type == PURPLE_PREF_INT) model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); @@ -239,11 +185,6 @@ menu = gtk_menu_new(); #endif - if (label != NULL) { - gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown); - pidgin_set_accessible_relations (dropdown, label); - } - if (type == PURPLE_PREF_INT) stored_int = purple_prefs_get_int(key); else if (type == PURPLE_PREF_STRING) @@ -293,8 +234,8 @@ } gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); - gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0); - gtk_widget_show(dropdown); + + pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label); return label; } @@ -412,7 +353,7 @@ gtk_tree_path_free(path); } -static GtkTreeRowReference *theme_refresh_theme_list() +static GtkTreeRowReference *theme_refresh_theme_list(void) { GdkPixbuf *pixbuf; GSList *themes; @@ -676,7 +617,7 @@ } static GtkWidget * -theme_page() +theme_page(void) { GtkWidget *add_button, *remove_button; GtkWidget *hbox_buttons; @@ -877,7 +818,7 @@ } static GtkWidget * -interface_page() +interface_page(void) { GtkWidget *ret; GtkWidget *vbox; @@ -973,7 +914,7 @@ #endif static GtkWidget * -conv_page() +conv_page(void) { GtkWidget *ret; GtkWidget *vbox; @@ -986,7 +927,6 @@ #if GTK_CHECK_VERSION(2,4,0) GtkWidget *hbox; - GtkWidget *label; GtkWidget *font_button; const char *font_name; #endif @@ -1029,19 +969,15 @@ fontpref = pidgin_prefs_checkbox(_("Use document font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox); else fontpref = pidgin_prefs_checkbox(_("Use font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox); - hbox = gtk_hbox_new(FALSE, 3); - label = gtk_label_new_with_mnemonic(_("Conversation _font:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); font_button = gtk_font_button_new_with_font(font_name ? font_name : NULL); gtk_font_button_set_show_style(GTK_FONT_BUTTON(font_button), TRUE); - gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Conversation _font:"), NULL, font_button, FALSE, NULL); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font")) gtk_widget_set_sensitive(hbox, FALSE); g_signal_connect(G_OBJECT(fontpref), "clicked", G_CALLBACK(pidgin_toggle_sensitive), hbox); g_signal_connect(G_OBJECT(font_button), "font-set", G_CALLBACK(pidgin_custom_font_set), NULL); - gtk_widget_show_all(hbox); #endif vbox = pidgin_make_frame(ret, _("Default Formatting")); @@ -1140,7 +1076,7 @@ } static GtkWidget * -network_page() +network_page(void) { GtkWidget *ret; GtkWidget *vbox, *hbox, *entry; @@ -1414,7 +1350,7 @@ return FALSE; } -static GList *get_available_browsers() +static GList *get_available_browsers(void) { struct browser { char *name; @@ -1480,7 +1416,7 @@ } static GtkWidget * -browser_page() +browser_page(void) { GtkWidget *ret; GtkWidget *vbox; @@ -1523,28 +1459,16 @@ browser_changed1_cb, hbox); } - hbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - label = gtk_label_new_with_mnemonic(_("_Manual:\n(%s for URL)")); - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0); - gtk_size_group_add_widget(sg, label); - entry = gtk_entry_new(); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry); - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom")) gtk_widget_set_sensitive(hbox, FALSE); purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser", browser_changed2_cb, hbox); - - gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); - gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command")); g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(manual_browser_set), NULL); - pidgin_set_accessible_label (entry, label); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL); gtk_widget_show_all(ret); g_object_unref(sg); @@ -1553,7 +1477,7 @@ #endif /*_WIN32*/ static GtkWidget * -logging_page() +logging_page(void) { GtkWidget *ret; GtkWidget *vbox; @@ -1781,7 +1705,7 @@ } static GtkWidget * -sound_page() +sound_page(void) { GtkWidget *ret; GtkWidget *vbox, *sw, *button; @@ -1827,33 +1751,20 @@ gtk_size_group_add_widget(sg, dd); gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("Sound c_ommand:\n(%s for filename)")); - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - entry = gtk_entry_new(); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry); - gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command"); if(cmd) gtk_entry_set_text(GTK_ENTRY(entry), cmd); - - gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sound_cmd_yeah), NULL); + hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Sound c_ommand:\n(%s for filename)"), sg, entry, TRUE, NULL); purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method", sound_changed1_cb, hbox); gtk_widget_set_sensitive(hbox, !strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "custom")); - - pidgin_set_accessible_label (entry, label); #endif /* _WIN32 */ vbox = pidgin_make_frame (ret, _("Sound Options")); @@ -1867,13 +1778,6 @@ NULL); #ifdef USE_GSTREAMER - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("Volume:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - sw = gtk_hscale_new_with_range(0.0, 100.0, 5.0); gtk_range_set_increments(GTK_RANGE(sw), 5.0, 25.0); gtk_range_set_value(GTK_RANGE(sw), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume")); @@ -1883,7 +1787,7 @@ g_signal_connect (G_OBJECT (sw), "value-changed", G_CALLBACK (prefs_sound_volume_changed), NULL); - gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0); + hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Volume:"), NULL, sw, TRUE, NULL); purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method", sound_changed3_cb, hbox); @@ -2008,11 +1912,10 @@ } static GtkWidget * -away_page() +away_page(void) { GtkWidget *ret; GtkWidget *vbox; - GtkWidget *hbox; GtkWidget *dd; GtkWidget *label; GtkWidget *button; @@ -2063,22 +1966,13 @@ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), select); - hbox = gtk_hbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - label = gtk_label_new_with_mnemonic(_("Change _status to:")); - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + /* TODO: Show something useful if we don't have any saved statuses. */ + menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Change _status to:"), sg, menu, TRUE, &label); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), menu); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), label); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - /* TODO: Show something useful if we don't have any saved statuses. */ - menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); - gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pidgin_toggle_sensitive), menu); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu); if (!purple_prefs_get_bool("/purple/away/away_when_idle")) { gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE); @@ -2092,22 +1986,13 @@ button = pidgin_prefs_checkbox(_("Use status from last _exit at startup"), "/purple/savedstatus/startup_current_status", vbox); - hbox = gtk_hbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - label = gtk_label_new_with_mnemonic(_("Status to a_pply at startup:")); - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + /* TODO: Show something useful if we don't have any saved statuses. */ + menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus)); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), menu); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Status to a_pply at startup:"), sg, menu, TRUE, &label); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), label); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - - /* TODO: Show something useful if we don't have any saved statuses. */ - menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus)); - gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pidgin_toggle_sensitive), menu); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu); if (purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) { gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE); @@ -2133,7 +2018,7 @@ #endif } -static void prefs_notebook_init() { +static void prefs_notebook_init(void) { prefs_notebook_add_page(_("Interface"), interface_page(), notebook_page++); prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++); prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++); @@ -2153,7 +2038,6 @@ void pidgin_prefs_show(void) { GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *notebook; GtkWidget *button; @@ -2169,31 +2053,20 @@ /* Back to instant-apply! I win! BU-HAHAHA! */ /* Create the window */ - prefs = pidgin_create_window(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE); + prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE); g_signal_connect(G_OBJECT(prefs), "destroy", G_CALLBACK(delete_prefs), NULL); - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(prefs), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(prefs), FALSE, PIDGIN_HIG_BORDER); /* The notebook */ prefsnotebook = notebook = gtk_notebook_new (); gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0); gtk_widget_show(prefsnotebook); - /* The buttons to press! */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); - gtk_widget_show (bbox); - - button = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + button = pidgin_dialog_add_button(GTK_DIALOG(prefs), GTK_STOCK_CLOSE, NULL, NULL); g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_destroy), prefs); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); prefs_notebook_init(); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkprivacy.c --- a/pidgin/gtkprivacy.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkprivacy.c Mon Jan 07 03:40:27 2008 +0000 @@ -45,6 +45,7 @@ GtkWidget *add_button; GtkWidget *remove_button; GtkWidget *clear_button; + GtkWidget *close_button; GtkWidget *button_box; GtkWidget *allow_widget; @@ -259,19 +260,22 @@ gtk_widget_hide(dialog->allow_widget); gtk_widget_hide(dialog->block_widget); - gtk_widget_hide(dialog->button_box); + gtk_widget_hide_all(dialog->button_box); if (new_type == PURPLE_PRIVACY_ALLOW_USERS) { gtk_widget_show(dialog->allow_widget); - gtk_widget_show(dialog->button_box); + gtk_widget_show_all(dialog->button_box); dialog->in_allow_list = TRUE; } else if (new_type == PURPLE_PRIVACY_DENY_USERS) { gtk_widget_show(dialog->block_widget); - gtk_widget_show(dialog->button_box); + gtk_widget_show_all(dialog->button_box); dialog->in_allow_list = FALSE; } + gtk_widget_show_all(dialog->close_button); + gtk_widget_show(dialog->button_box); + purple_blist_schedule_save(); pidgin_blist_refresh(purple_get_blist()); } @@ -355,8 +359,6 @@ privacy_dialog_new(void) { PidginPrivacyDialog *dialog; - GtkWidget *bbox; - GtkWidget *hbox; GtkWidget *vbox; GtkWidget *button; GtkWidget *dropdown; @@ -367,15 +369,13 @@ dialog = g_new0(PidginPrivacyDialog, 1); - dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE); + dialog->win = pidgin_create_dialog(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE); g_signal_connect(G_OBJECT(dialog->win), "delete_event", G_CALLBACK(destroy_cb), dialog); /* Main vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(dialog->win), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog->win), FALSE, PIDGIN_HIG_BORDER); /* Description label */ label = gtk_label_new( @@ -385,22 +385,10 @@ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_widget_show(label); - /* Hbox for the accounts drop-down and label. */ - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - /* "Set privacy for:" label */ - label = gtk_label_new(_("Set privacy for:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - /* Accounts drop-down */ dropdown = pidgin_account_option_menu_new(NULL, FALSE, G_CALLBACK(select_account_cb), NULL, dialog); - gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0); - gtk_widget_show(dropdown); - pidgin_set_accessible_label (dropdown, label); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Set privacy for:"), NULL, dropdown, TRUE, NULL); dialog->account = pidgin_account_option_menu_get_selected(dropdown); /* Add the drop-down list with the allow/block types. */ @@ -433,52 +421,27 @@ gtk_box_pack_start(GTK_BOX(vbox), dialog->block_widget, TRUE, TRUE, 0); /* Add the button box for Add, Remove, Clear */ - dialog->button_box = bbox = gtk_hbutton_box_new(); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD); - gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + dialog->button_box = pidgin_dialog_get_action_area(GTK_DIALOG(dialog->win)); /* Add button */ - button = gtk_button_new_from_stock(GTK_STOCK_ADD); + button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_ADD, G_CALLBACK(add_cb), dialog); dialog->add_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(add_cb), dialog); /* Remove button */ - button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); + button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_REMOVE, G_CALLBACK(remove_cb), dialog); dialog->remove_button = button; gtk_widget_set_sensitive(button, FALSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(remove_cb), dialog); /* Clear button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLEAR); + button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLEAR, G_CALLBACK(clear_cb), dialog); dialog->clear_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(clear_cb), dialog); - - /* Another button box. */ - bbox = gtk_hbutton_box_new(); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); - gtk_widget_show(bbox); /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); + button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog); + dialog->close_button = button; - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(close_cb), dialog); - + type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog); +#if 0 if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) { gtk_widget_show(dialog->allow_widget); gtk_widget_show(dialog->button_box); @@ -489,7 +452,7 @@ gtk_widget_show(dialog->button_box); dialog->in_allow_list = FALSE; } - +#endif return dialog; } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkrequest.c Mon Jan 07 03:40:27 2008 +0000 @@ -251,11 +251,12 @@ purple_request_close(PURPLE_REQUEST_FIELDS, data); } -static void +static gboolean destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event, PidginRequestData *data) { multifield_cancel_cb(NULL, data); + return FALSE; } @@ -439,6 +440,8 @@ pidgin_set_accessible_label (entry, label); data->u.input.entry = entry; + pidgin_auto_parent_window(dialog); + /* Show everything. */ gtk_widget_show(dialog); @@ -546,6 +549,8 @@ g_object_set_data(G_OBJECT(dialog), "radio", radio); /* Show everything. */ + pidgin_auto_parent_window(dialog); + gtk_widget_show_all(dialog); return data; @@ -661,6 +666,8 @@ gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_action); /* Show everything. */ + pidgin_auto_parent_window(dialog); + gtk_widget_show_all(dialog); return data; @@ -1059,7 +1066,6 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *hbox; - GtkWidget *bbox; GtkWidget *frame; GtkWidget *label; GtkWidget *table; @@ -1089,9 +1095,9 @@ #ifdef _WIN32 - data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ; + data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ; #else /* !_WIN32 */ - data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ; + data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ; #endif /* _WIN32 */ g_signal_connect(G_OBJECT(win), "delete_event", @@ -1099,7 +1105,7 @@ /* Setup the main horizontal box */ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), hbox); + gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox); gtk_widget_show(hbox); /* Dialog icon. */ @@ -1382,39 +1388,21 @@ g_object_unref(sg); - /* Button box. */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - /* Cancel button */ - button = gtk_button_new_from_stock(text_to_stock(cancel_text)); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(multifield_cancel_cb), data); - + button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); /* OK button */ - button = gtk_button_new_from_stock(text_to_stock(ok_text)); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - + button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data); data->ok_button = button; - GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_window_set_default(GTK_WINDOW(win), button); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(multifield_ok_cb), data); - if (!purple_request_fields_all_required_filled(fields)) gtk_widget_set_sensitive(button, FALSE); + pidgin_auto_parent_window(win); + gtk_widget_show(win); return data; @@ -1502,7 +1490,9 @@ } #endif /* FILECHOOSER */ - +#if 0 /* mismatched curly braces */ + } +#endif if ((data->u.file.savedialog == TRUE) && (g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) { purple_request_action(data, NULL, _("That file already exists"), @@ -1516,7 +1506,7 @@ } #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ -static void +static gboolean file_cancel_cb(PidginRequestData *data) { generic_response_start(data); @@ -1525,6 +1515,7 @@ ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL); purple_request_close(data->type, data); + return FALSE; } #endif /* FILECHOOSER */ @@ -1622,6 +1613,8 @@ G_CALLBACK(file_ok_check_if_exists_cb), data); #endif /* FILECHOOSER */ + pidgin_auto_parent_window(filesel); + data->dialog = filesel; gtk_widget_show(filesel); @@ -1673,6 +1666,8 @@ #endif data->dialog = dirsel; + pidgin_auto_parent_window(dirsel); + gtk_widget_show(dirsel); return (void *)data; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkroomlist.c --- a/pidgin/gtkroomlist.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkroomlist.c Mon Jan 07 03:40:27 2008 +0000 @@ -28,6 +28,8 @@ #include "pidgin.h" #include "gtkutils.h" #include "pidginstock.h" +#include "pidgintooltip.h" + #include "debug.h" #include "account.h" #include "connection.h" @@ -340,41 +342,20 @@ } } -static void pidgin_roomlist_tooltip_destroy(PidginRoomlist *grl) -{ - if ((grl == NULL) || (grl->tipwindow == NULL)) - return; - - gtk_widget_destroy(grl->tipwindow); - grl->tipwindow = NULL; -} - -static void pidgin_roomlist_tooltip_destroy_cb(GObject *object, PidginRoomlist *grl) -{ - if ((grl == NULL) || (grl->tipwindow == NULL)) - return; - - if (grl->timeout) - g_source_remove(grl->timeout); - grl->timeout = 0; - - pidgin_roomlist_tooltip_destroy(grl); -} - #define SMALL_SPACE 6 #define TOOLTIP_BORDER 12 -static void pidgin_roomlist_paint_tip(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +static gboolean +pidgin_roomlist_paint_tooltip(GtkWidget *widget, gpointer user_data) { - PidginRoomlist *grl = (PidginRoomlist *)user_data; + PurpleRoomlist *list = user_data; + PidginRoomlist *grl = list->ui_data; GtkStyle *style; int current_height, max_width; int max_text_width; GtkTextDirection dir = gtk_widget_get_direction(GTK_WIDGET(grl->tree)); style = grl->tipwindow->style; - gtk_paint_flat_box(style, grl->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, grl->tipwindow, "tooltip", 0, 0, -1, -1); max_text_width = 0; @@ -404,15 +385,13 @@ current_height + grl->tip_name_height, grl->tip_layout); } - + return FALSE; } -static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list) +static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list, GtkTreePath *path) { PidginRoomlist *grl = list->ui_data; - GtkWidget *tv = grl->tree; PurpleRoomlistRoom *room; - GtkTreePath *path; GtkTreeIter iter; GValue val; gchar *name, *tmp, *node_name; @@ -421,10 +400,11 @@ gint j; gboolean first = TRUE; +#if 0 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2), &path, NULL, NULL, NULL)) return FALSE; - +#endif gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); val.g_type = 0; @@ -460,8 +440,6 @@ g_free(label); } - gtk_tree_path_free(path); - grl->tip_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL); grl->tip_name_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL); @@ -492,156 +470,22 @@ return TRUE; } -static void pidgin_roomlist_draw_tooltip(PurpleRoomlist *list, GtkWidget *widget) +static gboolean +pidgin_roomlist_create_tooltip(GtkWidget *widget, GtkTreePath *path, + gpointer data, int *w, int *h) { + PurpleRoomlist *list = data; PidginRoomlist *grl = list->ui_data; - int scr_w, scr_h, w, h, x, y; -#if GTK_CHECK_VERSION(2,2,0) - int mon_num; - GdkScreen *screen = NULL; -#endif - GdkRectangle mon_size; - int sig; - const char *name; - - pidgin_roomlist_tooltip_destroy(grl); - grl->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_ensure_style (grl->tipwindow); - - if (!pidgin_roomlist_create_tip(list)) { - pidgin_roomlist_tooltip_destroy(grl); - return; - } - - name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget))); - gtk_widget_set_app_paintable(grl->tipwindow, TRUE); - gtk_window_set_title(GTK_WINDOW(grl->tipwindow), name ? name : _("Room List")); - gtk_window_set_resizable(GTK_WINDOW(grl->tipwindow), FALSE); - gtk_widget_set_name(grl->tipwindow, "gtk-tooltips"); - g_signal_connect(G_OBJECT(grl->tipwindow), "expose_event", - G_CALLBACK(pidgin_roomlist_paint_tip), grl); - - w = TOOLTIP_BORDER + SMALL_SPACE + - MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER; - h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height - + TOOLTIP_BORDER; - -#if GTK_CHECK_VERSION(2,2,0) - gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); - mon_num = gdk_screen_get_monitor_at_point(screen, x, y); - gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); - - scr_w = mon_size.width + mon_size.x; - scr_h = mon_size.height + mon_size.y; -#else - scr_w = gdk_screen_width(); - scr_h = gdk_screen_height(); - gdk_window_get_pointer(NULL, &x, &y, NULL); - mon_size.x = 0; - mon_size.y = 0; -#endif - -#if GTK_CHECK_VERSION(2,2,0) - if (w > mon_size.width) - w = mon_size.width - 10; - - if (h > mon_size.height) - h = mon_size.height - 10; -#endif - x -= ((w >> 1) + 4); - - if ((y + h + 4) > scr_h) - y = y - h - 5; - else - y = y + 6; - - if (y < mon_size.y) - y = mon_size.y; - - if (y != mon_size.y) { - if ((x + w) > scr_w) - x -= (x + w + 5) - scr_w; - else if (x < mon_size.x) - x = mon_size.x; - } else { - x -= (w / 2 + 10); - if (x < mon_size.x) - x = mon_size.x; - } - - gtk_widget_set_size_request(grl->tipwindow, w, h); - gtk_window_move(GTK_WINDOW(grl->tipwindow), x, y); - gtk_widget_show(grl->tipwindow); - - /* Hide the tooltip when the widget is destroyed */ - sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_roomlist_tooltip_destroy_cb), grl); - g_signal_connect_swapped(G_OBJECT(grl->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig)); -} - -static gboolean pidgin_roomlist_tooltip_timeout(PurpleRoomlist *list) -{ - PidginRoomlist *grl = list->ui_data; - GtkWidget *tv = grl->tree; - GtkTreePath *path; - - pidgin_roomlist_tooltip_destroy(grl); - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2), - &path, NULL, NULL, NULL)) + grl->tipwindow = widget; + if (!pidgin_roomlist_create_tip(data, path)) return FALSE; - - pidgin_roomlist_draw_tooltip(list, GTK_WIDGET(grl->tree)); - - return FALSE; -} - -static gboolean row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer user_data) -{ - PurpleRoomlist *list = user_data; - PidginRoomlist *grl = list->ui_data; - GtkTreePath *path; - int delay; - - /* XXX: should this be using the blist delay pref? */ - delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - - if (delay == 0) - return FALSE; - - if (grl->timeout) { - if ((event->y > grl->tip_rect.y) && ((event->y - grl->tip_rect.height) < grl->tip_rect.y)) - return FALSE; - /* We've left the cell. Remove the timeout and create a new one below */ - pidgin_roomlist_tooltip_destroy(grl); - } - - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); - - if (path == NULL) { - pidgin_roomlist_tooltip_destroy(grl); - return FALSE; - } - - gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &grl->tip_rect); - - if (path) - gtk_tree_path_free(path); - grl->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_roomlist_tooltip_timeout, list); - - return FALSE; -} - -static void row_leave_cb(GtkWidget *tv, GdkEventCrossing *e, gpointer user_data) -{ - PurpleRoomlist *list = user_data; - PidginRoomlist *grl = list->ui_data; - - if (grl->timeout) { - g_source_remove(grl->timeout); - grl->timeout = 0; - } - - pidgin_roomlist_tooltip_destroy(grl); + if (w) + *w = TOOLTIP_BORDER + SMALL_SPACE + + MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER; + if (h) + *h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height + + TOOLTIP_BORDER; + return TRUE; } static gboolean account_filter_func(PurpleAccount *account) @@ -677,48 +521,30 @@ GtkWidget *window; GtkWidget *vbox; GtkWidget *vbox2; - GtkWidget *account_hbox; GtkWidget *bbox; - GtkWidget *label; dialog = g_new0(PidginRoomlistDialog, 1); dialog->account = account; /* Create the window. */ - dialog->window = window = pidgin_create_window(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE); + dialog->window = window = pidgin_create_dialog(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_win_cb), dialog); /* Create the parent vbox for everything. */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(window), vbox); - gtk_widget_show(vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER); vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); gtk_container_add(GTK_CONTAINER(vbox), vbox2); gtk_widget_show(vbox2); /* accounts dropdown list */ - account_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox2), account_hbox, FALSE, FALSE, 0); - gtk_widget_show(account_hbox); - - label = gtk_label_new(NULL); - gtk_box_pack_start(GTK_BOX(account_hbox), label, FALSE, FALSE, 0); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_widget_show(label); - dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE, G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog); - if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */ dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget); - - gtk_box_pack_start(GTK_BOX(account_hbox), dialog->account_widget, TRUE, TRUE, 0); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(dialog->account_widget)); - gtk_widget_show(dialog->account_widget); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL); /* scrolled window */ dialog->sw = gtk_scrolled_window_new(NULL, NULL); @@ -738,19 +564,14 @@ gtk_widget_show(dialog->progress); /* button box */ - bbox = gtk_hbutton_box_new(); + bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window)); gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); /* stop button */ - dialog->stop_button = gtk_button_new_from_stock(GTK_STOCK_STOP); - gtk_box_pack_start(GTK_BOX(bbox), dialog->stop_button, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(dialog->stop_button), "clicked", + dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog); gtk_widget_set_sensitive(dialog->stop_button, FALSE); - gtk_widget_show(dialog->stop_button); /* list button */ dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH, @@ -779,11 +600,8 @@ gtk_widget_show(dialog->join_button); /* close button */ - dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), dialog->close_button, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(dialog->close_button), "clicked", + dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog); - gtk_widget_show(dialog->close_button); /* show the dialog window and return the dialog */ gtk_widget_show(dialog->window); @@ -967,6 +785,9 @@ g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), list); g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), list); #endif + pidgin_tooltip_setup_for_treeview(tree, list, + pidgin_roomlist_create_tooltip, + pidgin_roomlist_paint_tooltip); /* Enable CTRL+F searching */ gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtksavedstatuses.c --- a/pidgin/gtksavedstatuses.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtksavedstatuses.c Mon Jan 07 03:40:27 2008 +0000 @@ -594,7 +594,7 @@ width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/width"); height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/height"); - dialog->window = win = pidgin_create_window(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE); + dialog->window = win = pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE); gtk_window_set_default_size(GTK_WINDOW(win), width, height); g_signal_connect(G_OBJECT(win), "delete_event", @@ -603,18 +603,14 @@ G_CALLBACK(configure_cb), dialog); /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); /* List of saved status states */ list = create_saved_status_list(dialog); gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); /* Button box. */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); + bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win)); /* Use button */ button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE, @@ -627,36 +623,23 @@ G_CALLBACK(status_window_use_cb), dialog); /* Add button */ - button = gtk_button_new_from_stock(GTK_STOCK_ADD); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_add_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, + G_CALLBACK(status_window_add_cb), dialog); /* Modify button */ - button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY); + button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, + G_CALLBACK(status_window_modify_cb), dialog); dialog->modify_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + + /* Delete button */ + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, + G_CALLBACK(status_window_delete_cb), dialog); + dialog->delete_button = button; gtk_widget_set_sensitive(button, FALSE); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_modify_cb), dialog); - - /* Delete button */ - button = gtk_button_new_from_stock(GTK_STOCK_DELETE); - dialog->delete_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_sensitive(button, FALSE); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_delete_cb), dialog); - /* Close button */ - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_close_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, + G_CALLBACK(status_window_close_cb), dialog); purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", status_window, @@ -1109,7 +1092,6 @@ GtkWidget *entry; GtkWidget *frame; GtkWidget *hbox; - GtkWidget *label; GtkWidget *sw; GtkWidget *text; GtkWidget *toolbar; @@ -1147,64 +1129,40 @@ if (edit) dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status)); - dialog->window = win = pidgin_create_window(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE); + dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE); g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(status_editor_destroy_cb), dialog); /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); /* Title */ - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("_Title:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(sg, label); - entry = gtk_entry_new(); dialog->title = GTK_ENTRY(entry); if ((saved_status != NULL) && !purple_savedstatus_is_transient(saved_status) && (purple_savedstatus_get_title(saved_status) != NULL)) gtk_entry_set_text(GTK_ENTRY(entry), purple_savedstatus_get_title(saved_status)); - gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(editor_title_changed_cb), dialog); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Title:"), sg, entry, TRUE, NULL); /* Status type */ - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - label = gtk_label_new_with_mnemonic(_("_Status:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(sg, label); - if (saved_status != NULL) dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status)); else dropdown = create_status_type_menu(PURPLE_STATUS_AWAY); dialog->type = GTK_OPTION_MENU(dropdown); - gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0); + pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL); /* Status message */ - hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); - - label = gtk_label_new_with_mnemonic(_("_Message:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(sg, label); - frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL); dialog->message = GTK_IMHTML(text); - gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); + hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Message:"), sg, frame, TRUE, NULL); + gtk_container_child_set(GTK_CONTAINER(vbox), hbox, "expand", TRUE, "fill", TRUE, NULL); focus_chain = g_list_prepend(focus_chain, dialog->message); gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain); g_list_free(focus_chain); @@ -1257,23 +1215,18 @@ (saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status)); /* Button box */ - bbox = gtk_hbutton_box_new(); + bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win)); gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); /* Cancel button */ - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_editor_cancel_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, + G_CALLBACK(status_editor_cancel_cb), dialog); /* Use button */ button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE, PIDGIN_BUTTON_HORIZONTAL); gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(status_editor_ok_cb), dialog); @@ -1284,19 +1237,15 @@ gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); if (dialog->original_title == NULL) gtk_widget_set_sensitive(button, FALSE); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(status_editor_ok_cb), dialog); /* Save button */ - button = gtk_button_new_from_stock(GTK_STOCK_SAVE); - dialog->save_button = GTK_BUTTON(button); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE, + G_CALLBACK(status_editor_ok_cb), dialog); if (dialog->original_title == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_editor_ok_cb), dialog); + dialog->save_button = GTK_BUTTON(button); gtk_widget_show_all(win); g_object_unref(sg); @@ -1447,8 +1396,6 @@ char *tmp; SubStatusEditor *dialog; GtkSizeGroup *sg; - GtkWidget *bbox; - GtkWidget *button; GtkWidget *combo; GtkWidget *hbox; GtkWidget *frame; @@ -1486,15 +1433,14 @@ dialog->account = account; tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account)); - dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE); + dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE); g_free(tmp); g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(substatus_editor_destroy_cb), dialog); /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(win), vbox); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); @@ -1543,25 +1489,13 @@ dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); - /* Button box */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - /* Cancel button */ - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(substatus_editor_cancel_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, + G_CALLBACK(substatus_editor_cancel_cb), dialog); /* OK button */ - button = gtk_button_new_from_stock(GTK_STOCK_OK); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(substatus_editor_ok_cb), dialog); + pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_OK, + G_CALLBACK(substatus_editor_ok_cb), dialog); /* Seed the input widgets with the current values */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkscrollbook.c --- a/pidgin/gtkscrollbook.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkscrollbook.c Mon Jan 07 03:40:27 2008 +0000 @@ -64,7 +64,7 @@ return scroll_book_type; } -static void +static gboolean scroll_left_cb(PidginScrollBook *scroll_book) { int index; @@ -72,9 +72,10 @@ if (index > 0) gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index - 1); + return TRUE; } -static void +static gboolean scroll_right_cb(PidginScrollBook *scroll_book) { int index, count; @@ -87,6 +88,7 @@ if (index + 1 < count) gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1); + return TRUE; } static void @@ -136,10 +138,11 @@ refresh_scroll_box(scroll_book, index, count); } -static void +static gboolean scroll_close_cb(PidginScrollBook *scroll_book) { gtk_widget_destroy(gtk_notebook_get_nth_page(GTK_NOTEBOOK(scroll_book->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook)))); + return FALSE; } static void diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtksession.c --- a/pidgin/gtksession.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtksession.c Mon Jan 07 03:40:27 2008 +0000 @@ -124,7 +124,7 @@ purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n"); } -static void ice_init() { +static void ice_init(void) { IceIOErrorHandler default_handler; ice_installed_io_error_handler = IceSetIOErrorHandler(NULL); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkstatusbox.c Mon Jan 07 03:40:27 2008 +0000 @@ -119,7 +119,10 @@ DATA_COLUMN, /** - * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored + * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored. + * In the GtkTreeModel for the dropdown, this is the stock-id (gchararray), and for the + * GtkTreeModel for the cell_view (for the account-specific statusbox), this is the prpl-icon + * (GdkPixbuf) of the account. */ EMBLEM_COLUMN, @@ -606,7 +609,7 @@ char aa_color[8]; PurpleSavedStatus *saved_status; char *primary, *secondary, *text; - GdkPixbuf *pixbuf; + GdkPixbuf *pixbuf, *emblem = NULL; GtkTreePath *path; gboolean account_status = FALSE; PurpleAccount *acct = (status_box->token_status_account) ? status_box->token_status_account : status_box->account; @@ -703,6 +706,7 @@ text = g_strdup_printf("%s - %s", purple_account_get_username(status_box->account), aa_color, secondary ? secondary : primary); + emblem = pidgin_create_prpl_icon(status_box->account, PIDGIN_PRPL_ICON_SMALL); } else if (secondary != NULL) { text = g_strdup_printf("%s - %s", primary, aa_color, secondary); @@ -719,10 +723,14 @@ gtk_list_store_set(status_box->store, &(status_box->iter), ICON_COLUMN, pixbuf, TEXT_COLUMN, text, + EMBLEM_COLUMN, emblem, + EMBLEM_VISIBLE_COLUMN, (emblem != NULL), -1); if ((status_box->typing == 0) && (!status_box->connecting)) g_object_unref(pixbuf); g_free(text); + if (emblem) + g_object_unref(emblem); /* Make sure to activate the only row in the tree view */ path = gtk_tree_path_new_from_string("0"); @@ -941,7 +949,7 @@ /* This returns NULL if the active accounts don't have identical * statuses and a token account if they do */ -static PurpleAccount* check_active_accounts_for_identical_statuses() +static PurpleAccount* check_active_accounts_for_identical_statuses(void) { PurpleAccount *acct = NULL, *acct2; GList *tmp, *tmp2, *active_accts = purple_accounts_get_all_active(); @@ -1100,7 +1108,7 @@ return TRUE; } -static int imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box) +static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box) { if (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) { @@ -1690,7 +1698,7 @@ status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); + G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN); status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); @@ -1775,10 +1783,13 @@ status_box->icon_rend = gtk_cell_renderer_pixbuf_new(); status_box->text_rend = gtk_cell_renderer_text_new(); + emblem_rend = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, FALSE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, TRUE); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, "pixbuf", ICON_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, "markup", TEXT_COLUMN, NULL); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, "pixbuf", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL); #if GTK_CHECK_VERSION(2, 6, 0) g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); #endif diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkthemes.c --- a/pidgin/gtkthemes.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkthemes.c Mon Jan 07 03:40:27 2008 +0000 @@ -168,7 +168,7 @@ } static void -pidgin_smiley_themes_remove_non_existing() +pidgin_smiley_themes_remove_non_existing(void) { static struct smiley_theme *theme = NULL; GSList *iter = NULL; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkutils.c --- a/pidgin/gtkutils.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkutils.c Mon Jan 07 03:40:27 2008 +0000 @@ -132,12 +132,9 @@ } } -GtkWidget * -pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable) +static +void pidgin_window_init(GtkWindow *wnd, const char *title, guint border_width, const char *role, gboolean resizable) { - GtkWindow *wnd = NULL; - - wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); if (title) gtk_window_set_title(wnd, title); #ifdef _WIN32 @@ -148,11 +145,63 @@ if (role) gtk_window_set_role(wnd, role); gtk_window_set_resizable(wnd, resizable); +} + +GtkWidget * +pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable) +{ + GtkWindow *wnd = NULL; + + wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + pidgin_window_init(wnd, title, border_width, role, resizable); + + return GTK_WIDGET(wnd); +} + +GtkWidget * +pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable) +{ + GtkWindow *wnd = NULL; + + wnd = GTK_WINDOW(gtk_dialog_new()); + pidgin_window_init(wnd, title, border_width, role, resizable); + g_object_set(G_OBJECT(wnd), "has-separator", FALSE, NULL); return GTK_WIDGET(wnd); } GtkWidget * +pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing) +{ + GtkBox *vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox); + gtk_box_set_homogeneous(vbox, homogeneous); + gtk_box_set_spacing(vbox, spacing); + return GTK_WIDGET(vbox); +} + +GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog) +{ + return GTK_DIALOG(dialog)->vbox; +} + +GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog) +{ + return GTK_DIALOG(dialog)->action_area; +} + +GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label, + GCallback callback, gpointer callbackdata) +{ + GtkWidget *button = gtk_button_new_from_stock(label); + GtkWidget *bbox = pidgin_dialog_get_action_area(dialog); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + if (callback) + g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata); + gtk_widget_show(button); + return button; +} + +GtkWidget * pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret) { GtkWidget *frame; @@ -1399,7 +1448,7 @@ char *str; str = g_strdup_printf(_("The following error has occurred loading %s: %s"), - data->filename, strerror(errno)); + data->filename, g_strerror(errno)); purple_notify_error(NULL, NULL, _("Failed to load image"), str); @@ -1652,23 +1701,23 @@ GdkPixbuf *pixbuf = NULL; if (prim == PURPLE_STATUS_UNAVAILABLE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_AWAY) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_EXTENDED_AWAY) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_INVISIBLE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_OFFLINE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE, - icon_size, "GtkWidget"); - else - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE, - icon_size, "GtkWidget"); + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY, + icon_size, "GtkWidget"); + else if (prim == PURPLE_STATUS_AWAY) + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY, + icon_size, "GtkWidget"); + else if (prim == PURPLE_STATUS_EXTENDED_AWAY) + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA, + icon_size, "GtkWidget"); + else if (prim == PURPLE_STATUS_INVISIBLE) + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE, + icon_size, "GtkWidget"); + else if (prim == PURPLE_STATUS_OFFLINE) + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE, + icon_size, "GtkWidget"); + else + pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE, + icon_size, "GtkWidget"); return pixbuf; } @@ -2306,6 +2355,9 @@ } #endif /* FILECHOOSER */ +#if 0 /* mismatched curly braces */ + } +#endif if (dialog->callback) dialog->callback(filename, dialog->data); gtk_widget_destroy(dialog->icon_filesel); @@ -2895,7 +2947,7 @@ GSList *minidialogs = NULL; static void * -pidgin_utils_get_handle() +pidgin_utils_get_handle(void) { static int handle; @@ -3269,6 +3321,144 @@ gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text)); } +GtkWidget * +pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label) +{ + GtkWidget *hbox; + GtkWidget *label = NULL; + + if (widget_label) { + hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_show(hbox); + gtk_box_pack_start(vbox, hbox, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic(widget_label); + gtk_widget_show(label); + if (sg) { + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(sg, label); + } + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + } else { + hbox = GTK_WIDGET(vbox); + } + + gtk_widget_show(widget); + gtk_box_pack_start(GTK_BOX(hbox), widget, expand, TRUE, 0); + if (label) { + gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget); + pidgin_set_accessible_label (widget, label); + } + + if (p_label) + (*p_label) = label; + return hbox; +} + +gboolean pidgin_auto_parent_window(GtkWidget *widget) +{ +#if 0 + /* This looks at the most recent window that received focus, and makes + * that the parent window. */ +#ifndef _WIN32 + static GdkAtom _WindowTime = GDK_NONE; + static GdkAtom _Cardinal = GDK_NONE; + GList *windows = NULL; + GtkWidget *parent = NULL; + time_t window_time = 0; + + windows = gtk_window_list_toplevels(); + + if (_WindowTime == GDK_NONE) { + _WindowTime = gdk_x11_xatom_to_atom(gdk_x11_get_xatom_by_name("_NET_WM_USER_TIME")); + } + if (_Cardinal == GDK_NONE) { + _Cardinal = gdk_atom_intern("CARDINAL", FALSE); + } + + while (windows) { + GtkWidget *window = windows->data; + guchar *data = NULL; + int al = 0; + time_t value; + + windows = g_list_delete_link(windows, windows); + + if (window == widget || + !GTK_WIDGET_VISIBLE(window)) + continue; + + if (!gdk_property_get(window->window, _WindowTime, _Cardinal, 0, sizeof(time_t), FALSE, + NULL, NULL, &al, &data)) + continue; + value = *(time_t *)data; + if (window_time < value) { + window_time = value; + parent = window; + } + g_free(data); + } + if (windows) + g_list_free(windows); + if (parent) { + if (!gtk_get_current_event() && gtk_window_has_toplevel_focus(GTK_WINDOW(parent))) { + /* The window is in focus, and the new window was not triggered by a keypress/click + * event. So do not set it transient, to avoid focus stealing and all that. + */ + return FALSE; + } + gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent)); + return TRUE; + } + return FALSE; +#endif +#else + /* This finds the currently active window and makes that the parent window. */ + GList *windows = NULL; + GtkWidget *parent = NULL; + GdkEvent *event = gtk_get_current_event(); + GdkWindow *menu = NULL; + + if (event == NULL) + /* The window was not triggered by a user action. */ + return FALSE; + + /* We need to special case events from a popup menu. */ + if (event->type == GDK_BUTTON_RELEASE) { + /* XXX: Neither of the following works: + menu = event->button.window; + menu = gdk_window_get_parent(event->button.window); + menu = gdk_window_get_toplevel(event->button.window); + */ + } else if (event->type == GDK_KEY_PRESS) + menu = event->key.window; + + windows = gtk_window_list_toplevels(); + while (windows) { + GtkWidget *window = windows->data; + windows = g_list_delete_link(windows, windows); + + if (window == widget || + !GTK_WIDGET_VISIBLE(window)) { + continue; + } + + if (gtk_window_has_toplevel_focus(GTK_WINDOW(window)) || + (menu && menu == window->window)) { + parent = window; + break; + } + } + if (windows) + g_list_free(windows); + if (parent) { + gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent)); + return TRUE; + } + return FALSE; +#endif +} + gchar * pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis) { diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/gtkutils.h --- a/pidgin/gtkutils.h Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/gtkutils.h Mon Jan 07 03:40:27 2008 +0000 @@ -121,6 +121,61 @@ GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable); /** + * Creates a new dialog window + * + * @param title The window title, or @c NULL + * @param border_width The window's desired border width + * @param role A string indicating what the window is responsible for doing, or @c NULL + * @param resizable Whether the window should be resizable (@c TRUE) or not (@c FALSE) + * + * @since 2.4.0 + */ +GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable); + +/** + * Retrieves the main content box (vbox) from a pidgin dialog window + * + * @param dialog The dialog window + * @param homogeneous TRUE if all children are to be given equal space allotments. + * @param spacing the number of pixels to place by default between children + * + * @since 2.4.0 + */ +GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing); + +/** + * Retrieves the main content box (vbox) from a pidgin dialog window + * + * @param dialog The dialog window + * + * @since 2.4.0 + */ +GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog); + +/** + * Add a button to a dialog created by #pidgin_create_dialog. + * + * @param dialog The dialog window + * @param label The stock-id or the label for the button + * @param callback The callback function for the button + * @param callbackdata The user data for the callback function + * + * @return The created button. + * @since 2.4.0 + */ +GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label, + GCallback callback, gpointer callbackdata); + +/** + * Retrieves the action area (button box) from a pidgin dialog window + * + * @param dialog The dialog window + * + * @since 2.4.0 + */ +GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog); + +/** * Toggles the sensitivity of a widget. * * @param widget @c NULL. Used for signal handlers. @@ -319,16 +374,20 @@ gboolean pidgin_screenname_autocomplete_default_filter(const PidginBuddyCompletionEntry *completion_entry, gpointer all_accounts); /** + * Add autocompletion of screenames to an entry. + * * @deprecated - * Add autocompletion of screenames to an entry. - * The usage of this function is deprecated. For new code, use the equivalent: - * pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(all)) + * For new code, use the equivalent: + * #pidgin_setup_screenname_autocomplete_with_filter(@a entry, @a optmenu, + * #pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(@a + * all)) * * @param entry The GtkEntry on which to setup autocomplete. - * @param optmenu A menu for accounts, returned by pidgin_account_option_menu_new(). - * If @a optmenu is not @c NULL, it'll be updated when a screenname is chosen - * from the autocomplete list. - * @param all Whether to include screennames from disconnected accounts. + * @param optmenu A menu for accounts, returned by + * pidgin_account_option_menu_new(). If @a optmenu is not @c + * NULL, it'll be updated when a screenname is chosen from the + * autocomplete list. + * @param all Whether to include screennames from disconnected accounts. */ void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, gboolean all); @@ -451,7 +510,7 @@ /** * A valid GtkMenuPositionFunc. This is used to determine where - * to draw context menu's when the menu is activated with the + * to draw context menus when the menu is activated with the * keyboard (shift+F10). If the menu is activated with the mouse, * then you should just use GTK's built-in position function, * because it does a better job of positioning the menu. @@ -725,6 +784,31 @@ */ void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text); +/** + * Automatically make a window transient to a suitable parent window. + * + * @param window The window to make transient. + * + * @return Whether the window was made transient or not. + * @since 2.4.0 + */ +gboolean pidgin_auto_parent_window(GtkWidget *window); + +/** + * Add a labelled widget to a GtkVBox + * + * @param vbox The GtkVBox to add the widget to. + * @param widget_label The label to give the widget. + * @param sg The GtkSizeGroup to add the label to. + * @param widget The GtkWidget to add + * @param expand Whether to expand the widget horizontally. + * @param p_label Place to store a pointer to the GtkLabel, or NULL if you don't care. + * + * @return A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget. + * @since 2.4.0 + */ +GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label); + gchar *pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis); #endif /* _PIDGINUTILS_H_ */ diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/minidialog.c --- a/pidgin/minidialog.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/minidialog.c Mon Jan 07 03:40:27 2008 +0000 @@ -35,7 +35,41 @@ #include "pidgin/pidgin.h" #include "pidgin/pidginstock.h" -G_DEFINE_TYPE (PidginMiniDialog, pidgin_mini_dialog, GTK_TYPE_VBOX) +static void pidgin_mini_dialog_init (PidginMiniDialog *self); +static void pidgin_mini_dialog_class_init (PidginMiniDialogClass *klass); + +static gpointer pidgin_mini_dialog_parent_class = NULL; + +static void +pidgin_mini_dialog_class_intern_init (gpointer klass) +{ + pidgin_mini_dialog_parent_class = g_type_class_peek_parent (klass); + pidgin_mini_dialog_class_init ((PidginMiniDialogClass*) klass); +} + +GType +pidgin_mini_dialog_get_type (void) +{ + static GType g_define_type_id = 0; + if (G_UNLIKELY (g_define_type_id == 0)) + { + static const GTypeInfo g_define_type_info = { + sizeof (PidginMiniDialogClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) pidgin_mini_dialog_class_intern_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (PidginMiniDialog), + 0, /* n_preallocs */ + (GInstanceInitFunc) pidgin_mini_dialog_init, + NULL, + }; + g_define_type_id = g_type_register_static (GTK_TYPE_VBOX, + "PidginMiniDialog", &g_define_type_info, 0); + } + return g_define_type_id; +} enum { diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/pidgincombobox.c --- a/pidgin/pidgincombobox.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/pidgincombobox.c Mon Jan 07 03:40:27 2008 +0000 @@ -2980,7 +2980,7 @@ g_return_if_fail (link != NULL); - combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link); + combo_box->priv->cells = g_slist_delete_link (combo_box->priv->cells, link); combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info, position); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/pidgintooltip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.c Mon Jan 07 03:40:27 2008 +0000 @@ -0,0 +1,363 @@ +/** + * @file pidgintooltip.c Pidgin Tooltip API + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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 + */ + +#include "internal.h" +#include "prefs.h" +#include "pidgin.h" +#include "pidgintooltip.h" +#include "debug.h" + +struct +{ + GtkWidget *widget; + int timeout; + GdkRectangle tip_rect; + GtkWidget *tipwindow; + PidginTooltipPaint paint_tooltip; +} pidgin_tooltip; + +typedef struct +{ + GtkWidget *widget; + gpointer userdata; + PidginTooltipPaint paint_tooltip; + union { + struct { + PidginTooltipCreateForTree create_tooltip; + GtkTreePath *path; + } treeview; + struct { + PidginTooltipCreate create_tooltip; + } widget; + } common; +} PidginTooltipData; + +static void +destroy_tooltip_data(PidginTooltipData *data) +{ + gtk_tree_path_free(data->common.treeview.path); + g_free(data); +} + +void pidgin_tooltip_destroy() +{ + if (pidgin_tooltip.timeout > 0) { + g_source_remove(pidgin_tooltip.timeout); + pidgin_tooltip.timeout = 0; + } + if (pidgin_tooltip.tipwindow) { + gtk_widget_destroy(pidgin_tooltip.tipwindow); + pidgin_tooltip.tipwindow = NULL; + } +} + +static gboolean +pidgin_tooltip_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + if (pidgin_tooltip.paint_tooltip) { + gtk_paint_flat_box(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, widget, "tooltip", 0, 0, -1, -1); + pidgin_tooltip.paint_tooltip(widget, data); + } + return FALSE; +} + +static GtkWidget* +setup_tooltip_window(void) +{ + const char *name; + GtkWidget *tipwindow; + + tipwindow = gtk_window_new(GTK_WINDOW_POPUP); + name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget)); +#if GTK_CHECK_VERSION(2,10,0) + gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP); +#endif + gtk_widget_set_app_paintable(tipwindow, TRUE); + gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip")); + gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE); + gtk_widget_set_name(tipwindow, "gtk-tooltips"); + gtk_widget_ensure_style(tipwindow); + gtk_widget_realize(tipwindow); + return tipwindow; +} + +static void +setup_tooltip_window_position(gpointer data, int w, int h) +{ + int sig; + int scr_w, scr_h, x, y; +#if GTK_CHECK_VERSION(2,2,0) + int mon_num; + GdkScreen *screen = NULL; +#endif + GdkRectangle mon_size; + GtkWidget *tipwindow = pidgin_tooltip.tipwindow; + +#if GTK_CHECK_VERSION(2,2,0) + gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); + mon_num = gdk_screen_get_monitor_at_point(screen, x, y); + gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); + + scr_w = mon_size.width + mon_size.x; + scr_h = mon_size.height + mon_size.y; +#else + scr_w = gdk_screen_width(); + scr_h = gdk_screen_height(); + gdk_window_get_pointer(NULL, &x, &y, NULL); + mon_size.x = 0; + mon_size.y = 0; +#endif + +#if GTK_CHECK_VERSION(2,2,0) + if (w > mon_size.width) + w = mon_size.width - 10; + + if (h > mon_size.height) + h = mon_size.height - 10; +#endif + x -= ((w >> 1) + 4); + + if ((y + h + 4) > scr_h) + y = y - h - 5; + else + y = y + 6; + + if (y < mon_size.y) + y = mon_size.y; + + if (y != mon_size.y) { + if ((x + w) > scr_w) + x -= (x + w + 5) - scr_w; + else if (x < mon_size.x) + x = mon_size.x; + } else { + x -= (w / 2 + 10); + if (x < mon_size.x) + x = mon_size.x; + } + + gtk_widget_set_size_request(tipwindow, w, h); + gtk_window_move(GTK_WINDOW(tipwindow), x, y); + gtk_widget_show(tipwindow); + + g_signal_connect(G_OBJECT(tipwindow), "expose_event", + G_CALLBACK(pidgin_tooltip_expose_event), data); + + /* Hide the tooltip when the widget is destroyed */ + sig = g_signal_connect(G_OBJECT(pidgin_tooltip.widget), "destroy", G_CALLBACK(pidgin_tooltip_destroy), NULL); + g_signal_connect_swapped(G_OBJECT(tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig)); +} + +void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata, + PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip) +{ + GtkWidget *tipwindow; + int w, h; + + pidgin_tooltip_destroy(); + + pidgin_tooltip.widget = gtk_widget_get_toplevel(widget); + pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window(); + pidgin_tooltip.paint_tooltip = paint_tooltip; + + if (!create_tooltip(tipwindow, userdata, &w, &h)) { + pidgin_tooltip_destroy(); + return; + } + setup_tooltip_window_position(userdata, w, h); +} + +static void +reset_data_treepath(PidginTooltipData *data) +{ + gtk_tree_path_free(data->common.treeview.path); + data->common.treeview.path = NULL; +} + +static void +pidgin_tooltip_draw(PidginTooltipData *data) +{ + GtkWidget *tipwindow; + int w, h; + + pidgin_tooltip_destroy(); + + pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget); + pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window(); + pidgin_tooltip.paint_tooltip = data->paint_tooltip; + + if (!data->common.widget.create_tooltip(tipwindow, data->userdata, &w, &h)) { + if (tipwindow == pidgin_tooltip.tipwindow) + pidgin_tooltip_destroy(); + return; + } + + setup_tooltip_window_position(data->userdata, w, h); +} + +static void +pidgin_tooltip_draw_tree(PidginTooltipData *data) +{ + GtkWidget *tipwindow; + GtkTreePath *path = NULL; + int w, h; + + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(data->widget), + pidgin_tooltip.tip_rect.x, + pidgin_tooltip.tip_rect.y + (pidgin_tooltip.tip_rect.height/2), + &path, NULL, NULL, NULL)) { + pidgin_tooltip_destroy(); + return; + } + + if (data->common.treeview.path) { + if (gtk_tree_path_compare(data->common.treeview.path, path) == 0) { + gtk_tree_path_free(path); + return; + } + gtk_tree_path_free(data->common.treeview.path); + data->common.treeview.path = NULL; + } + + pidgin_tooltip_destroy(); + + pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget); + pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window(); + pidgin_tooltip.paint_tooltip = data->paint_tooltip; + + if (!data->common.treeview.create_tooltip(tipwindow, path, data->userdata, &w, &h)) { + if (tipwindow == pidgin_tooltip.tipwindow) + pidgin_tooltip_destroy(); + gtk_tree_path_free(path); + return; + } + + setup_tooltip_window_position(data->userdata, w, h); + + data->common.treeview.path = path; + g_signal_connect_swapped(G_OBJECT(pidgin_tooltip.tipwindow), "destroy", + G_CALLBACK(reset_data_treepath), data); +} + +static gboolean +pidgin_tooltip_timeout(gpointer data) +{ + PidginTooltipData *tdata = data; + pidgin_tooltip.timeout = 0; + if (GTK_IS_TREE_VIEW(tdata->widget)) + pidgin_tooltip_draw_tree(data); + else + pidgin_tooltip_draw(data); + return FALSE; +} + +static gboolean +row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer userdata) +{ + GtkTreePath *path; + int delay; + + if (event->window != gtk_tree_view_get_bin_window(GTK_TREE_VIEW(tv))) + return FALSE; /* The cursor is probably on the TreeView's header. */ + + /* XXX: probably use something more generic? */ + delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); + if (delay == 0) + return FALSE; + + if (pidgin_tooltip.timeout) { + if ((event->y >= pidgin_tooltip.tip_rect.y) && ((event->y - pidgin_tooltip.tip_rect.height) <= pidgin_tooltip.tip_rect.y)) + return FALSE; + /* We've left the cell. Remove the timeout and create a new one below */ + pidgin_tooltip_destroy(); + } + + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); + + if (path == NULL) { + pidgin_tooltip_destroy(); + return FALSE; + } + + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &pidgin_tooltip.tip_rect); + gtk_tree_path_free(path); + + pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, userdata); + + return FALSE; +} + +static gboolean +widget_leave_cb(GtkWidget *tv, GdkEvent *event, gpointer userdata) +{ + pidgin_tooltip_destroy(); + return FALSE; +} + +gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata, + PidginTooltipCreateForTree create_tooltip, PidginTooltipPaint paint_tooltip) +{ + PidginTooltipData *tdata = g_new0(PidginTooltipData, 1); + tdata->widget = tree; + tdata->userdata = userdata; + tdata->common.treeview.create_tooltip = create_tooltip; + tdata->paint_tooltip = paint_tooltip; + + g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata); + g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL); + g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata); + return TRUE; +} + +static gboolean +widget_motion_cb(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); + + pidgin_tooltip_destroy(); + if (delay == 0) + return FALSE; + + pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, data); + return FALSE; +} + +gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata, + PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip) +{ + PidginTooltipData *wdata = g_new0(PidginTooltipData, 1); + wdata->widget = widget; + wdata->userdata = userdata; + wdata->common.widget.create_tooltip = create_tooltip; + wdata->paint_tooltip = paint_tooltip; + + g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata); + g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL); + g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(g_free), wdata); + return TRUE; +} + diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/pidgintooltip.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.h Mon Jan 07 03:40:27 2008 +0000 @@ -0,0 +1,112 @@ +/** + * @file pidgintooltip.h Pidgin Tooltip API + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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 _PIDGIN_TOOLTIP_H_ +#define _PIDGIN_TOOLTIP_H_ + +#include + +/** + * @param tipwindow The window for the tooltip. + * @param path The GtkTreePath representing the row under the cursor. + * @param userdata The userdata set during pidgin_tooltip_setup_for_treeview. + * @param w The value of this should be set to the desired width of the tooltip window. + * @param h The value of this should be set to the desired height of the tooltip window. + * + * @return @c TRUE if the tooltip was created correctly, @c FALSE otherwise. + * @since 2.4.0 + */ +typedef gboolean (*PidginTooltipCreateForTree)(GtkWidget *tipwindow, + GtkTreePath *path, gpointer userdata, int *w, int *h); + +/** + * @param tipwindow The window for the tooltip. + * @param userdata The userdata set during pidgin_tooltip_show. + * @param w The value of this should be set to the desired width of the tooltip window. + * @param h The value of this should be set to the desired height of the tooltip window. + * + * @return @c TRUE if the tooltip was created correctly, @c FALSE otherwise. + * @since 2.4.0 + */ +typedef gboolean (*PidginTooltipCreate)(GtkWidget *tipwindow, + gpointer userdata, int *w, int *h); + +/** + * @param tipwindow The window for the tooltip. + * @param userdata The userdata set during pidgin_tooltip_setup_for_treeview or pidgin_tooltip_show. + * + * @return @c TRUE if the tooltip was painted correctly, @c FALSE otherwise. + * @since 2.4.0 + */ +typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, gpointer userdata); + +/** + * Setup tooltip drawing functions for a treeview. + * + * @param tree The treeview + * @param userdata The userdata to send to the callback functions + * @param create_cb Callback function to create the tooltip for a GtkTreePath + * @param paint_cb Callback function to paint the tooltip + * + * @return @c TRUE if the tooltip callbacks were setup correctly. + * @since 2.4.0 + */ +gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata, + PidginTooltipCreateForTree create_cb, PidginTooltipPaint paint_cb); + +/** + * Setup tooltip drawing functions for any widget. + * + * @param widget The widget + * @param userdata The userdata to send to the callback functions + * @param create_cb Callback function to create the tooltip for the widget + * @param paint_cb Callback function to paint the tooltip + * + * @return @c TRUE if the tooltip callbacks were setup correctly. + * @since 2.4.0 + */ +gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata, + PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip); + +/** + * Destroy the tooltip. + * @since 2.4.0 + */ +void pidgin_tooltip_destroy(void); + +/** + * Create and show a tooltip. + * + * @param widget The widget the tooltip is for + * @param userdata The userdata to send to the callback functions + * @param create_cb Callback function to create the tooltip from the GtkTreePath + * @param paint_cb Callback function to paint the tooltip + * + * @since 2.4.0 + */ +void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata, + PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb); + +#endif diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/cap/cap.c --- a/pidgin/plugins/cap/cap.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/cap/cap.c Mon Jan 07 03:40:27 2008 +0000 @@ -434,26 +434,6 @@ stats->last_seen = time(NULL); } -static void buddy_idle(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) { -} - -#if 0 -static void blist_node_extended_menu(PurpleBlistNode *node, GList **menu) { - PurpleBuddy *buddy; - PurpleMenuAction *menu_action; - purple_debug_info("cap", "got extended blist menu\n"); - purple_debug_info("cap", "is buddy: %d\n", PURPLE_BLIST_NODE_IS_BUDDY(node)); - purple_debug_info("cap", "is contact: %d\n", PURPLE_BLIST_NODE_IS_CONTACT(node)); - purple_debug_info("cap", "is group: %d\n", PURPLE_BLIST_NODE_IS_GROUP(node)); - /* Probably only concerned with buddy/contact types. Contacts = meta-buddies (grouped msn/jabber/etc.) */ - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - buddy = (PurpleBuddy *)node; - menu_action = purple_menu_action_new(_("Display Statistics"), - PURPLE_CALLBACK(display_statistics_action_cb), NULL, NULL); - *menu = g_list_append(*menu, menu_action); -} -#endif - /* drawing-tooltip */ static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full) { if(node->type == PURPLE_BLIST_BUDDY_NODE) { @@ -662,15 +642,6 @@ /* result = dbi_conn_queryf(_conn, "insert into cap_message values(\'%s\', \'%s\', %d, now());", sender, receiver, count); */ } -/* Callbacks */ -void display_statistics_action_cb(PurpleBlistNode *node, gpointer data) { - PurpleBuddy *buddy; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - buddy = (PurpleBuddy *)node; - purple_debug_info("cap", "Statistics for %s requested.\n", buddy->name); -} - /* Purple plugin specific code */ static gboolean plugin_load(PurplePlugin *plugin) { @@ -714,9 +685,6 @@ purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", plugin, PURPLE_CALLBACK(buddy_signed_off), NULL); - /*purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin, - PURPLE_CALLBACK(blist_node_extended_menu), NULL);*/ - purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", plugin, PURPLE_CALLBACK(drawing_tooltip), NULL); @@ -726,9 +694,6 @@ purple_signal_connect(purple_connections_get_handle(), "signed-off", plugin, PURPLE_CALLBACK(signed_off), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", plugin, - PURPLE_CALLBACK(buddy_idle), NULL); - _signals_connected = TRUE; } @@ -765,9 +730,6 @@ purple_signal_disconnect(purple_blist_get_handle(), "buddy-signed-off", plugin, PURPLE_CALLBACK(buddy_signed_off)); - /*purple_signal_disconnect(purple_blist_get_handle(), "blist-node-extended-menu", plugin, - PURPLE_CALLBACK(blist_node_extended_menu));*/ - purple_signal_disconnect(pidgin_blist_get_handle(), "drawing-tooltip", plugin, PURPLE_CALLBACK(drawing_tooltip)); @@ -777,9 +739,6 @@ purple_signal_disconnect(purple_connections_get_handle(), "signed-off", plugin, PURPLE_CALLBACK(signed_off)); - purple_signal_disconnect(purple_blist_get_handle(), "buddy-idle-changed", plugin, - PURPLE_CALLBACK(buddy_idle)); - _signals_connected = FALSE; } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/cap/cap.h --- a/pidgin/plugins/cap/cap.h Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/cap/cap.h Mon Jan 07 03:40:27 2008 +0000 @@ -98,7 +98,6 @@ static void buddy_signed_on(PurpleBuddy *buddy); /* buddy-signed-off */ static void buddy_signed_off(PurpleBuddy *buddy); -static void buddy_idle(PurpleBuddy *buddy, gboolean old_idle, gboolean idle); /* drawing-tooltip */ static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full); /* signed-on */ @@ -107,21 +106,20 @@ static void signed_off(PurpleConnection *gc); static void reset_all_last_message_times(gpointer key, gpointer value, gpointer user_data); static PurpleStatus * get_status_for(PurpleBuddy *buddy); -static void create_tables(); -static gboolean create_database_connection(); -static void destroy_database_connection(); +static void create_tables(void); +static gboolean create_database_connection(void); +static void destroy_database_connection(void); static guint word_count(const gchar *string); static void insert_status_change(CapStatistics *statistics); static void insert_status_change_from_purple_status(CapStatistics *statistics, PurpleStatus *status); static void insert_word_count(const char *sender, const char *receiver, guint count); -void display_statistics_action_cb(PurpleBlistNode *node, gpointer data); static gboolean plugin_load(PurplePlugin *plugin); static void add_plugin_functionality(PurplePlugin *plugin); static void cancel_conversation_timeouts(gpointer key, gpointer value, gpointer user_data); static void remove_plugin_functionality(PurplePlugin *plugin); static void write_stats_on_unload(gpointer key, gpointer value, gpointer user_data); static gboolean plugin_unload(PurplePlugin *plugin); -static CapPrefsUI * create_cap_prefs_ui(); +static CapPrefsUI * create_cap_prefs_ui(void); static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data); static void numeric_spinner_prefs_cb(GtkSpinButton *spinbutton, gpointer user_data); static GtkWidget * get_config_frame(PurplePlugin *plugin); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/crazychat/cc_network.c --- a/pidgin/plugins/crazychat/cc_network.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/crazychat/cc_network.c Mon Jan 07 03:40:27 2008 +0000 @@ -529,7 +529,7 @@ while (total < len) { n = send(s, buf + total, bytesleft, 0); if (n == -1) { - Debug("ERROR: %s\n", strerror(errno)); + Debug("ERROR: %s\n", g_strerror(errno)); return -1; } total += n; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/markerline.c --- a/pidgin/plugins/markerline.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/markerline.c Mon Jan 07 03:40:27 2008 +0000 @@ -190,13 +190,13 @@ } static void -detach_from_all_windows() +detach_from_all_windows(void) { g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)detach_from_pidgin_window, NULL); } static void -attach_to_all_windows() +attach_to_all_windows(void) { g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)attach_to_pidgin_window, NULL); } diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/pidginrc.c --- a/pidgin/plugins/pidginrc.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/pidginrc.c Mon Jan 07 03:40:27 2008 +0000 @@ -30,17 +30,29 @@ static const gchar *color_prefs[] = { "/plugins/gtk/purplerc/color/GtkWidget::cursor-color", "/plugins/gtk/purplerc/color/GtkWidget::secondary-cursor-color", - "/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color" + "/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color", + "/plugins/gtk/purplerc/color/GtkIMHtml::send-name-color", + "/plugins/gtk/purplerc/color/GtkIMHtml::receive-name-color", + "/plugins/gtk/purplerc/color/GtkIMHtml::highlight-name-color", + "/plugins/gtk/purplerc/color/GtkIMHtml::action-name-color" }; static const gchar *color_prefs_set[] = { "/plugins/gtk/purplerc/set/color/GtkWidget::cursor-color", "/plugins/gtk/purplerc/set/color/GtkWidget::secondary-cursor-color", - "/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color" + "/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color", + "/plugins/gtk/purplerc/set/color/GtkIMHtml::send-name-color", + "/plugins/gtk/purplerc/set/color/GtkIMHtml::receive-name-color", + "/plugins/gtk/purplerc/set/color/GtkIMHtml::highlight-name-color", + "/plugins/gtk/purplerc/set/color/GtkIMHtml::action-name-color" }; static const gchar *color_names[] = { N_("Cursor Color"), N_("Secondary Cursor Color"), - N_("Hyperlink Color") + N_("Hyperlink Color"), + N_("Sent Message Name Color"), + N_("Received Message Name Color"), + N_("Highlighted Message Name Color"), + N_("Action Message Name Color") }; static GtkWidget *color_widgets[G_N_ELEMENTS(color_prefs)]; @@ -83,7 +95,7 @@ */ static GString * -make_gtkrc_string() +make_gtkrc_string(void) { gint i; gchar *prefbase = NULL; @@ -173,7 +185,7 @@ } static void -purplerc_make_changes() +purplerc_make_changes(void) { GString *str = make_gtkrc_string(); #if GTK_CHECK_VERSION(2,4,0) diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/spellchk.c --- a/pidgin/plugins/spellchk.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/spellchk.c Mon Jan 07 03:40:27 2008 +0000 @@ -695,7 +695,7 @@ return 1; } -static void load_conf() +static void load_conf(void) { /* Corrections to change "...", "(c)", "(r)", and "(tm)" to their * Unicode character equivalents were not added here even though @@ -1912,7 +1912,7 @@ save_list(); } -static void list_add_new() +static void list_add_new(void) { GtkTreeIter iter; const char *word = gtk_entry_get_text(GTK_ENTRY(bad_entry)); @@ -2015,7 +2015,7 @@ gtk_tree_row_reference_free(row_reference); } -static void list_delete() +static void list_delete(void) { GtkTreeSelection *sel; GSList *list = NULL; @@ -2161,14 +2161,13 @@ get_config_frame(PurplePlugin *plugin) { GtkWidget *ret, *vbox, *win; - GtkWidget *hbox, *label; + GtkWidget *hbox; GtkWidget *button; GtkSizeGroup *sg; GtkSizeGroup *sg2; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *vbox2; - GtkWidget *hbox2; GtkWidget *vbox3; ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); @@ -2275,37 +2274,15 @@ sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - hbox2 = gtk_hbox_new(FALSE, 2); - gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0); - gtk_widget_show(hbox2); - - label = gtk_label_new_with_mnemonic(_("You _type:")); - gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0); - bad_entry = gtk_entry_new(); /* Set a minimum size. Since they're in a size group, the other entry will match up. */ gtk_widget_set_size_request(bad_entry, 350, -1); - gtk_box_pack_start(GTK_BOX(hbox2), bad_entry, TRUE, TRUE, 0); gtk_size_group_add_widget(sg2, bad_entry); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry); - gtk_widget_show(bad_entry); - - hbox2 = gtk_hbox_new(FALSE, 2); - gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0); - gtk_widget_show(hbox2); - - label = gtk_label_new_with_mnemonic(_("You _send:")); - gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); - gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _type:"), sg, bad_entry, FALSE, NULL); good_entry = gtk_entry_new(); - gtk_box_pack_start(GTK_BOX(hbox2), good_entry, TRUE, TRUE, 0); gtk_size_group_add_widget(sg2, good_entry); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry); - gtk_widget_show(good_entry); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _send:"), sg, good_entry, FALSE, NULL); /* Created here so it can be passed to whole_words_button_toggled. */ case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)")); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/ticker/ticker.c --- a/pidgin/plugins/ticker/ticker.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/ticker/ticker.c Mon Jan 07 03:40:27 2008 +0000 @@ -65,7 +65,7 @@ return TRUE; /* don't actually destroy the window */ } -static void buddy_ticker_create_window() { +static void buddy_ticker_create_window(void) { if(tickerwindow) { gtk_widget_show(tickerwindow); return; @@ -215,7 +215,7 @@ buddy_ticker_update_contact(c); } -static void buddy_ticker_show() +static void buddy_ticker_show(void) { PurpleBuddyList *list = purple_get_blist(); PurpleBlistNode *gnode, *cnode, *bnode; diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/plugins/xmppconsole.c --- a/pidgin/plugins/xmppconsole.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/plugins/xmppconsole.c Mon Jan 07 03:40:27 2008 +0000 @@ -727,7 +727,7 @@ } static void -create_console() +create_console(PurplePluginAction *action) { GtkWidget *vbox = gtk_vbox_new(FALSE, 6); GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL); diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Mon Jan 07 03:40:27 2008 +0000 @@ -699,6 +699,7 @@ Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem" Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem" Delete "$INSTDIR\ca-certs\Microsoft_Secure_Server_Authority.pem" + Delete "$INSTDIR\ca-certs\StartCom_Free_SSL_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_Class3_Extended_Validation_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem" @@ -1154,11 +1155,21 @@ !macro RunCheckMacro UN Function ${UN}RunCheck Push $R0 - System::Call 'kernel32::OpenMutex(i 2031617, b 0, t "pidgin_is_running") i .R0' - IntCmp $R0 0 done - MessageBox MB_OK|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDOK + Push $R1 + + IntOp $R1 0 + 0 + retry_runcheck: + ; Close the Handle (needed if we're retrying) + IntCmp $R1 0 +2 + System::Call 'kernel32::CloseHandle(i $R1) i .R1' + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_is_running") i .R1 ?e' + Pop $R0 + IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck Abort + done: + Pop $R1 Pop $R0 FunctionEnd !macroend @@ -1169,10 +1180,16 @@ Push $R0 Push $R1 Push $R2 - System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .r1 ?e' + + IntOp $R1 0 + 0 + retry_runcheck: + ; Close the Handle (needed if we're retrying) + IntCmp $R1 0 +2 + System::Call 'kernel32::CloseHandle(i $R1) i .R1' + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .R1 ?e' Pop $R0 - StrCmp $R0 0 +3 - MessageBox MB_OK|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDOK + IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck Abort Call RunCheck StrCpy $name "Pidgin ${PIDGIN_VERSION}" diff -r 754a82f1371b -r 9a60b9fab0ea pidgin/win32/winpidgin.c --- a/pidgin/win32/winpidgin.c Mon Dec 17 08:20:42 2007 +0000 +++ b/pidgin/win32/winpidgin.c Mon Jan 07 03:40:27 2008 +0000 @@ -445,27 +445,31 @@ #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13) #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14) -static BOOL winpidgin_set_running() { +static BOOL winpidgin_set_running(BOOL fail_if_running) { HANDLE h; if ((h = CreateMutex(NULL, FALSE, "pidgin_is_running"))) { - if (GetLastError() == ERROR_ALREADY_EXISTS) { - HWND msg_win; + DWORD err = GetLastError(); + if (err == ERROR_ALREADY_EXISTS) { + if (fail_if_running) { + HWND msg_win; - printf("An instance of Pidgin is already running.\n"); + printf("An instance of Pidgin is already running.\n"); - if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL))) - if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL)) - return FALSE; + if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL))) + if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL)) + return FALSE; - /* If we get here, the focus request wasn't successful */ + /* If we get here, the focus request wasn't successful */ - MessageBox(NULL, - "An instance of Pidgin is already running", - NULL, MB_OK | MB_TOPMOST); + MessageBox(NULL, + "An instance of Pidgin is already running", + NULL, MB_OK | MB_TOPMOST); - return FALSE; - } + return FALSE; + } + } else if (err != ERROR_SUCCESS) + printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err); } return TRUE; } @@ -628,8 +632,8 @@ winpidgin_set_locale(); /* If help, version or multiple flag used, do not check Mutex */ - if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v") && !strstr(lpszCmdLine, "-m")) - if (!getenv("PIDGIN_MULTI_INST") && !winpidgin_set_running()) + if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v")) + if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && strstr(lpszCmdLine, "-m") == NULL)) return 0; /* Now we are ready for Pidgin .. */ diff -r 754a82f1371b -r 9a60b9fab0ea po/POTFILES.in --- a/po/POTFILES.in Mon Dec 17 08:20:42 2007 +0000 +++ b/po/POTFILES.in Mon Jan 07 03:40:27 2008 +0000 @@ -12,6 +12,7 @@ finch/gntpounce.c finch/gntprefs.c finch/gntrequest.c +finch/gntroomlist.c finch/gntsound.c finch/gntstatus.c finch/gntui.c @@ -179,6 +180,7 @@ libpurple/sslconn.c libpurple/status.c libpurple/util.c +libpurple/win32/libc_interface.c pidgin.desktop.in pidgin/eggtrayicon.c pidgin/gtkaccount.c diff -r 754a82f1371b -r 9a60b9fab0ea po/check_po.pl --- a/po/check_po.pl Mon Dec 17 08:20:42 2007 +0000 +++ b/po/check_po.pl Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl -w # # check_po.pl - check po file translations for likely errors # diff -r 754a82f1371b -r 9a60b9fab0ea po/de.po --- a/po/de.po Mon Dec 17 08:20:42 2007 +0000 +++ b/po/de.po Mon Jan 07 03:40:27 2008 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-12-06 15:10+0100\n" -"PO-Revision-Date: 2007-12-06 15:10+0100\n" +"POT-Creation-Date: 2008-01-05 13:31+0100\n" +"PO-Revision-Date: 2008-01-05 13:31+0100\n" "Last-Translator: Jochen Kemnade \n" "Language-Team: Deutsch \n" "MIME-Version: 1.0\n" @@ -1019,6 +1019,9 @@ msgid "Open File..." msgstr "Datei öffnen..." +msgid "Choose Location..." +msgstr "Wählen Sie einen Ort..." + msgid "Buddy logs in" msgstr "Buddy meldet sich an" @@ -1433,19 +1436,6 @@ msgstr "Ze_rtifikat ansehen..." #. Prompt the user to authenticate the certificate -#. TODO: Provide the user with more guidance about why he is -#. being prompted -#. vrq will be completed by user_auth -#, c-format -msgid "" -"The certificate presented by \"%s\" claims to be from \"%s\" instead. This " -"could mean that you are not connecting to the service you believe you are." -msgstr "" -"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen " -"von „%s“ zu kommen. Das kann bedeuten, dass Sie tatsächlich nicht mit dem " -"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein." - -#. Prompt the user to authenticate the certificate #. vrq will be completed by user_auth #, c-format msgid "" @@ -1495,6 +1485,19 @@ msgid "Invalid certificate authority signature" msgstr "Unbekannte Zertifizierungsstellensignatur" +#. Prompt the user to authenticate the certificate +#. TODO: Provide the user with more guidance about why he is +#. being prompted +#. vrq will be completed by user_auth +#, c-format +msgid "" +"The certificate presented by \"%s\" claims to be from \"%s\" instead. This " +"could mean that you are not connecting to the service you believe you are." +msgstr "" +"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen " +"von „%s“ zu kommen. Das kann bedeuten, dass Sie tatsächlich nicht mit dem " +"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein." + #. Make messages #, c-format msgid "" @@ -3282,6 +3285,10 @@ msgid "nickserv: Send a command to nickserv" msgstr "nickserv: Sendet ein Kommando zum Nickserv" +msgid "notice <target<: Send a notice to a user or channel." +msgstr "" +"notice <Ziel>: Sende eine Notiz an einen Benutzer oder an einen Kanal." + msgid "" "op <nick1> [nick2] ...: Grant channel operator status to someone. You " "must be a channel operator to do this." @@ -3412,12 +3419,12 @@ msgid "Plaintext Authentication" msgstr "Klartext-Authentifizierung" +msgid "Invalid response from server." +msgstr "Ungültige Serverantwort." + msgid "Server does not use any supported authentication method" msgstr "Der Server benutzt keine der unterstützten Authentifizierungsmethoden" -msgid "Invalid response from server." -msgstr "Ungültige Serverantwort." - msgid "" "This server requires plaintext authentication over an unencrypted " "connection. Allow this and continue authentication?" @@ -3830,6 +3837,9 @@ msgid "Write error" msgstr "Schreibfehler" +msgid "Ping timeout" +msgstr "Ping-Zeitüberschreitung" + msgid "Read Error" msgstr "Fehler beim Lesen" @@ -3965,8 +3975,8 @@ msgid "Mood" msgstr "Stimmung" -msgid "Current media" -msgstr "Aktuelles Medium" +msgid "Now Listening" +msgstr "" msgid "Mood Text" msgstr "Stimmungstext" @@ -9513,26 +9523,14 @@ msgstr "Musik hören" #, c-format -msgid "%s changed status from %s to %s" -msgstr "%s hat den Status von %s zu %s geändert" - -#, c-format msgid "%s (%s) changed status from %s to %s" msgstr "%s (%s) hat den Status von %s zu %s geändert" #, c-format -msgid "%s is now %s" -msgstr "%s ist jetzt %s" - -#, c-format msgid "%s (%s) is now %s" msgstr "%s (%s) ist jetzt %s" #, c-format -msgid "%s is no longer %s" -msgstr "%s ist nicht mehr %s" - -#, c-format msgid "%s (%s) is no longer %s" msgstr "%s (%s) ist nicht mehr %s" @@ -9638,6 +9636,36 @@ msgid "Unable to connect to %s: %s" msgstr "Verbindung zu %s nicht möglich: %s" +#, c-format +msgid " - %s" +msgstr " - %s" + +#, c-format +msgid " (%s)" +msgstr " (%s)" + +#. 10053 +#, c-format +msgid "Connection interrupted by other software on your computer." +msgstr "" +"Die Verbindung wurde von einer anderen Software auf ihrem Computer " +"unterbrochen." + +#. 10054 +#, c-format +msgid "Remote host closed connection." +msgstr "Der entfernte Host hat die Verbindung beendet." + +#. 10060 +#, c-format +msgid "Connection timed out." +msgstr "Verbindungsabbruch wegen Zeitüberschreitung." + +#. 10061 +#, c-format +msgid "Connection refused." +msgstr "Verbindung abgelehnt." + msgid "Internet Messenger" msgstr "Internet-Sofortnachrichtendienst" @@ -9892,6 +9920,12 @@ "Sie sind im Moment nicht mit einem Konto angemeldet, welches benutzt werden " "kann, um diesen Buddy hinzuzufügen." +#. I don't believe this can happen currently, I think +#. * everything that calls this function checks for one of the +#. * above node types first. +msgid "Unknown node type" +msgstr "Unbekannter Knotentyp" + #. Buddies menu msgid "/_Buddies" msgstr "/_Buddys" @@ -9993,12 +10027,8 @@ msgstr "/Hilfe/Ü_ber" #, c-format -msgid "" -"\n" -"Account: %s" -msgstr "" -"\n" -"Konto: %s" +msgid "Account: %s" +msgstr "Konto: %s" #, c-format msgid "" @@ -10029,6 +10059,12 @@ msgid "Rockin'" msgstr "Abgefahren" +msgid "Total Buddies" +msgstr "Buddy-Anzahl" + +msgid "Online Buddies" +msgstr "Online-Buddys" + #, c-format msgid "Idle %dd %dh %02dm" msgstr "Untätig %dd %dh %02dm" @@ -10447,6 +10483,14 @@ msgid "User has typed something and stopped" msgstr "Benutzer hat etwas getippt und wartet nun" +#, c-format +msgid "" +"\n" +"%s has typed something and stopped" +msgstr "" +"\n" +"%s hat etwas getippt und wartet nun" + #. Build the Send To menu msgid "S_end To" msgstr "S_enden an" @@ -11084,6 +11128,34 @@ msgstr "" "Farbe zum Darstellen von Hyperlinks, wenn sich die Maus darüber befindet." +msgid "Sent Message Name Color" +msgstr "Farbe des Absendernamens für gesendete Nachrichten" + +msgid "Color to draw the name of a message you sent." +msgstr "" +"Farbe, mit der der Name in einer gesendeten Nachricht dargestellt wird." + +msgid "Received Message Name Color" +msgstr "Farbe des Absendernamens für empfangene Nachrichten" + +msgid "Color to draw the name of a message you received." +msgstr "" +"Farbe, mit der der Name in einer empfangenen Nachricht dargestellt wird." + +msgid "\"Attention\" Name Color" +msgstr "Farbe des Absendernamens für \"Achtung\"-Nachrichten" + +msgid "Color to draw the name of a message you received containing your name." +msgstr "" +"Farbe, mit der der Name in einer Nachricht dargestellt wird, die Ihren Namen " +"enthält." + +msgid "Action Message Name Color" +msgstr "Farbe des Absendernamens für Aktions-Nachrichten" + +msgid "Color to draw the name of an action message." +msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird." + msgid "_Copy E-Mail Address" msgstr "Kopiere _E-Mail-Adresse" @@ -11137,15 +11209,6 @@ msgid "_Save Image..." msgstr "Bild _speichern..." -msgid "_Font" -msgstr "_Schrift" - -msgid "_Insert" -msgstr "_Einfügen" - -msgid "S_mile!" -msgstr "_Lächeln!" - msgid "Select Font" msgstr "Schriftart wählen" @@ -11174,6 +11237,9 @@ msgid "Insert Link" msgstr "Link einfügen" +msgid "_Insert" +msgstr "_Einfügen" + #, c-format msgid "Failed to store image: %s\n" msgstr "Speichern des Bildes fehlgeschlagen: %s\n" @@ -11181,12 +11247,14 @@ msgid "Insert Image" msgstr "Bild einfügen" +msgid "Smile!" +msgstr "Lächeln!" + msgid "This theme has no available smileys." msgstr "Dieses Thema verfügt über keine Smileys." -#. show everything -msgid "Smile!" -msgstr "Lächeln!" +msgid "_Font" +msgstr "_Schrift" msgid "Group Items" msgstr "Elemente gruppieren" @@ -11386,6 +11454,33 @@ #, c-format msgid "" +"%s %s\n" +"Usage: %s [OPTION]...\n" +"\n" +" -c, --config=DIR use DIR for config files\n" +" -d, --debug print debugging messages to stdout\n" +" -h, --help display this help and exit\n" +" -m, --multiple do not ensure single instance\n" +" -n, --nologin don't automatically login\n" +" -l, --login[=NAME] automatically login (optional argument NAME specifies\n" +" account(s) to use, separated by commas)\n" +" -v, --version display the current version and exit\n" +msgstr "" +"%s %s\n" +"Benutzung: %s [OPTION]...\n" +"\n" +" -c, --config=VERZ benutze VERZ als Konfigurationsverzeichnis\n" +" -d, --debug gibt Debugging-Meldungen nach stdout aus\n" +" -h, --help zeigt diese Hilfe und beendet das Programm\n" +" -m, --multiple mehrere Instanzen erlauben\n" +" -n, --nologin nicht automatisch anmelden\n" +" -l, --login[=NAME] automatische Anmeldung (optionales Argument \n" +" NAME bestimmt Konto(n), die benutzt werden\n" +" sollen, getrennt durch Kommata)\n" +" -v, --version zeigt aktuelle Version und beendet das Programm\n" + +#, c-format +msgid "" "%s %s has segfaulted and attempted to dump a core file.\n" "This is a bug in the software and has happened through\n" "no fault of your own.\n" @@ -11694,8 +11789,8 @@ "This is how your outgoing message text will appear when you use protocols " "that support formatting." msgstr "" -"So wird der ausgehende Nachrichtentext aussehen, wenn Sie " -"Protokollebenutzen, die Formatierung unterstützen." +"So wird der ausgehende Nachrichtentext aussehen, wenn Sie Protokolle " +"benutzen, die Formatierung unterstützen." msgid "Cannot start proxy configuration program." msgstr "Kann das Proxy-Konfigurationsprogramm nicht starten." @@ -11952,7 +12047,6 @@ msgid "Changes to privacy settings take effect immediately." msgstr "Einstellungen bzgl. der Privatsphäre werden sofort wirksam." -#. "Set privacy for:" label msgid "Set privacy for:" msgstr "Setze Privatsphäre für:" @@ -12773,6 +12867,9 @@ msgid "Hyperlink Color" msgstr "Hyperlink-Farbe" +msgid "Highlighted Message Name Color" +msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten" + msgid "GtkTreeView Horizontal Separation" msgstr "GtkTreeview horizontaler Abstand" @@ -12780,7 +12877,7 @@ msgstr "Unterhaltungseintrag" msgid "Request Dialog" -msgstr "Dialog anfordern" +msgstr "Anfrage-Dialog" msgid "Notify Dialog" msgstr "Benachrichtigungsdialog" diff -r 754a82f1371b -r 9a60b9fab0ea po/stats.pl --- a/po/stats.pl Mon Dec 17 08:20:42 2007 +0000 +++ b/po/stats.pl Mon Jan 07 03:40:27 2008 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl # Copyright 2003-2005 Nathan Walp # diff -r 754a82f1371b -r 9a60b9fab0ea share/ca-certs/Makefile.am --- a/share/ca-certs/Makefile.am Mon Dec 17 08:20:42 2007 +0000 +++ b/share/ca-certs/Makefile.am Mon Jan 07 03:40:27 2008 +0000 @@ -3,6 +3,7 @@ Equifax_Secure_CA.pem \ GTE_CyberTrust_Global_Root.pem \ Microsoft_Secure_Server_Authority.pem \ + StartCom_Free_SSL_CA.pem \ Verisign_RSA_Secure_Server_CA.pem \ Verisign_Class3_Primary_CA.pem diff -r 754a82f1371b -r 9a60b9fab0ea share/ca-certs/StartCom_Free_SSL_CA.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/StartCom_Free_SSL_CA.pem Mon Jan 07 03:40:27 2008 +0000 @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFjCCBH+gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsDELMAkGA1UEBhMCSUwx +DzANBgNVBAgTBklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0 +Q29tIEx0ZC4xGjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS +YWRtaW5Ac3RhcnRjb20ub3JnMB4XDTA1MDMxNzE3Mzc0OFoXDTM1MDMxMDE3Mzc0 +OFowgbAxCzAJBgNVBAYTAklMMQ8wDQYDVQQIEwZJc3JhZWwxDjAMBgNVBAcTBUVp +bGF0MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMRowGAYDVQQLExFDQSBBdXRob3Jp +dHkgRGVwLjEpMCcGA1UEAxMgRnJlZSBTU0wgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxITAfBgkqhkiG9w0BCQEWEmFkbWluQHN0YXJ0Y29tLm9yZzCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEA7YRgACOeyEpRKSfeOqE5tWmrCbIvNP1h3D3TsM+x +18LEwrHkllbEvqoUDufMOlDIOmKdw6OsWXuO7lUaHEe+o5c5s7XvIywI6Nivcy+5 +yYPo7QAPyHWlLzRMGOh2iCNJitu27Wjaw7ViKUylS7eYtAkUEKD4/mJ2IhULpNYI +LzUCAwEAAaOCAjwwggI4MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgHmMB0G +A1UdDgQWBBQcicOWzL3+MtUNjIExtpidjShkjTCB3QYDVR0jBIHVMIHSgBQcicOW +zL3+MtUNjIExtpidjShkjaGBtqSBszCBsDELMAkGA1UEBhMCSUwxDzANBgNVBAgT +BklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4x +GjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBGcmVlIFNTTCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSYWRtaW5Ac3Rh +cnRjb20ub3JnggEAMB0GA1UdEQQWMBSBEmFkbWluQHN0YXJ0Y29tLm9yZzAdBgNV +HRIEFjAUgRJhZG1pbkBzdGFydGNvbS5vcmcwEQYJYIZIAYb4QgEBBAQDAgAHMC8G +CWCGSAGG+EIBDQQiFiBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAy +BglghkgBhvhCAQQEJRYjaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL2NhLWNybC5j +cmwwKAYJYIZIAYb4QgECBBsWGWh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy8wOQYJ +YIZIAYb4QgEIBCwWKmh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9pbmRleC5waHA/ +YXBwPTExMTANBgkqhkiG9w0BAQQFAAOBgQBscSXhnjSRIe/bbL0BCFaPiNhBOlP1 +ct8nV0t2hPdopP7rPwl+KLhX6h/BquL/lp9JmeaylXOWxkjHXo0Hclb4g4+fd68p +00UOpO6wNnQt8M2YI3s3S9r+UZjEHjQ8iP2ZO1CnwYszx8JSFhKVU2Ui77qLzmLb +cCOxgN8aIDjnfg== +-----END CERTIFICATE-----