# HG changeset patch # User Richard Laager # Date 1199312106 0 # Node ID c7c5e2ff2b040445ee13c5012274ac75da3e8551 # Parent 2ca7a3d281864a34c6cf099c3770797dd6063314# Parent ecdb9f9b75e44d8f0a502219bd8932be01681131 merge of '46216b10d02d4e5cae2343e469f0e405088537d3' and '795d7e0fc010741ba22123931ad0cca10f456bc8' diff -r 2ca7a3d28186 -r c7c5e2ff2b04 AUTHORS --- a/AUTHORS Tue Dec 18 18:18:31 2007 +0000 +++ b/AUTHORS Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 COPYRIGHT --- a/COPYRIGHT Tue Dec 18 18:18:31 2007 +0000 +++ b/COPYRIGHT Wed Jan 02 22:15:06 2008 +0000 @@ -298,6 +298,7 @@ Jory A. Pratt Brent Priddy Justin Pryzby +Ignacio Casal Quinteiro Federicco Mena Quintero Yosef Radchenko David Raeman @@ -327,6 +328,7 @@ Andrew Sayman Alceste Scalas Carsten Schaar +Jonathan Schleifer Matteo Settenvini Colin Seymour Luke Schierer @@ -413,6 +415,7 @@ Zsombor Welker Andrew Wellington Adam Wendt +Simon Wenner Dave West Zac West Daniel Westermann-Clark diff -r 2ca7a3d28186 -r c7c5e2ff2b04 ChangeLog --- a/ChangeLog Tue Dec 18 18:18:31 2007 +0000 +++ b/ChangeLog Wed Jan 02 22:15:06 2008 +0000 @@ -1,6 +1,6 @@ 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. @@ -12,6 +12,8 @@ * 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) Finch: * Color is used in the buddylist to indicate status, and the conversation diff -r 2ca7a3d28186 -r c7c5e2ff2b04 ChangeLog.API --- a/ChangeLog.API Tue Dec 18 18:18:31 2007 +0000 +++ b/ChangeLog.API Wed Jan 02 22:15:06 2008 +0000 @@ -1,6 +1,33 @@ 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. + + 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. + + Deprecated: + * PIDGIN_DIALOG + Finch: libgnt: * Added gnt_tree_set_row_color to set the color for a row in a tree. diff -r 2ca7a3d28186 -r c7c5e2ff2b04 ChangeLog.win32 --- a/ChangeLog.win32 Tue Dec 18 18:18:31 2007 +0000 +++ b/ChangeLog.win32 Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 configure.ac --- a/configure.ac Tue Dec 18 18:18:31 2007 +0000 +++ b/configure.ac Wed Jan 02 22:15:06 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]) @@ -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 @@ -1154,7 +1149,6 @@ AC_SUBST(DBUS_LIBS) enable_dbus=yes ], [ - AC_MSG_RESULT(no) enable_dbus=no ]) diff -r 2ca7a3d28186 -r c7c5e2ff2b04 doc/pidgin.1.in --- a/doc/pidgin.1.in Tue Dec 18 18:18:31 2007 +0000 +++ b/doc/pidgin.1.in Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 finch/gntrequest.c --- a/finch/gntrequest.c Tue Dec 18 18:18:31 2007 +0000 +++ b/finch/gntrequest.c Wed Jan 02 22:15:06 2008 +0000 @@ -44,7 +44,7 @@ GntWidget *dialog; GCallback *cbs; gboolean save; -} PurpleGntFileRequest; +} FinchFileRequest; static GntWidget * setup_request_window(const char *title, const char *primary, @@ -387,6 +387,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 +574,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 +587,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 +641,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 +651,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 +665,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 +701,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 +750,7 @@ finch_request_fields, finch_request_file, finch_close_request, - NULL, /* No plans for request_folder */ + finch_request_folder, NULL, NULL, NULL, diff -r 2ca7a3d28186 -r c7c5e2ff2b04 finch/libgnt/configure.ac --- a/finch/libgnt/configure.ac Tue Dec 18 18:18:31 2007 +0000 +++ b/finch/libgnt/configure.ac Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 finch/libgnt/gntcolors.h --- a/finch/libgnt/gntcolors.h Tue Dec 18 18:18:31 2007 +0000 +++ b/finch/libgnt/gntcolors.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Tue Dec 18 18:18:31 2007 +0000 +++ b/finch/libgnt/gntstyle.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Tue Dec 18 18:18:31 2007 +0000 +++ b/finch/libgnt/gnttree.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/account.c --- a/libpurple/account.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/account.c Wed Jan 02 22:15:06 2008 +0000 @@ -1645,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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/blist.h --- a/libpurple/blist.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/blist.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/certificate.c --- a/libpurple/certificate.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/certificate.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/certificate.h --- a/libpurple/certificate.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/certificate.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/cipher.c --- a/libpurple/cipher.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/cipher.c Wed Jan 02 22:15:06 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(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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/cipher.h --- a/libpurple/cipher.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/cipher.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/cmds.h --- a/libpurple/cmds.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/cmds.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/connection.h --- a/libpurple/connection.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/connection.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/conversation.h --- a/libpurple/conversation.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/conversation.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/plugin.c --- a/libpurple/plugin.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/plugin.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/prefs.c --- a/libpurple/prefs.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/prefs.c Wed Jan 02 22:15:06 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; } diff -r 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/irc/cmds.c --- a/libpurple/protocols/irc/cmds.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/irc/cmds.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/irc/parse.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/jabber/auth.c --- a/libpurple/protocols/jabber/auth.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/jabber/auth.c Wed Jan 02 22:15:06 2008 +0000 @@ -690,6 +690,10 @@ 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"); @@ -699,8 +703,6 @@ xmlnode_insert_data(x, js->user->resource, -1); x = xmlnode_new_child(query, "crammd5"); - challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge"); - auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), &digest); /* Translate the digest to a hexadecimal notation */ p = h; @@ -1075,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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Wed Jan 02 22:15:06 2008 +0000 @@ -390,26 +390,29 @@ static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) { - g_source_remove(GPOINTER_TO_INT(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) { - JabberIq *iq = jabber_iq_new(gc->proto_data, JABBER_IQ_GET); - guint timeout; + 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"); + xmlnode *ping = xmlnode_new_child(iq->node, "ping"); + xmlnode_set_namespace(ping, "urn:xmpp:ping"); - timeout = purple_timeout_add_seconds(20, (GSourceFunc)(jabber_pong_timeout), gc); - jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(timeout)); + 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); } @@ -612,6 +615,7 @@ js->next_id = g_random_int(); js->write_buffer = purple_circ_buffer_new(512); js->old_length = -1; + js->keepalive_timeout = -1; if(!js->user) { purple_connection_error_reason (gc, @@ -1311,6 +1315,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; diff -r 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/msn/soap2.c --- a/libpurple/protocols/msn/soap2.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/msn/soap2.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/protocols/qq/group_opt.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/request.c --- a/libpurple/request.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/request.c Wed Jan 02 22:15:06 2008 +0000 @@ -1209,7 +1209,6 @@ 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); - g_return_val_if_fail(cancel_cb != NULL, NULL); ops = purple_request_get_ui_ops(); @@ -1299,7 +1298,6 @@ 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); - g_return_val_if_fail(cancel_cb != NULL, NULL); ops = purple_request_get_ui_ops(); diff -r 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/request.h --- a/libpurple/request.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/request.h Wed Jan 02 22:15:06 2008 +0000 @@ -1203,11 +1203,11 @@ * 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 not be + * @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 assonciated with this request, + * @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. @@ -1241,11 +1241,11 @@ * 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 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 assonciated with this request, + * @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. @@ -1281,11 +1281,11 @@ * 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 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 assonciated with this request, + * @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 @@ -1320,7 +1320,7 @@ * 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 assonciated with this request, + * @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. @@ -1358,7 +1358,7 @@ * 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 assonciated with this request, + * @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. @@ -1396,11 +1396,11 @@ * 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 not be + * @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 assonciated with this request, + * @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 @@ -1476,10 +1476,10 @@ * @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 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 assonciated with this request, + * @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 @@ -1503,10 +1503,10 @@ * 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 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 assonciated with this request, + * @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 diff -r 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/signals.h --- a/libpurple/signals.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/signals.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/tests/test_cipher.c --- a/libpurple/tests/test_cipher.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/tests/test_cipher.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/util.h --- a/libpurple/util.h Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/util.h Wed Jan 02 22:15:06 2008 +0000 @@ -1117,6 +1117,7 @@ * @param errnum The error code. * * @return The UTF-8 error message. + * @since 2.4.0 */ G_CONST_RETURN gchar *purple_gai_strerror(gint errnum); diff -r 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/version.h.in --- a/libpurple/version.h.in Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/version.h.in Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Tue Dec 18 18:18:31 2007 +0000 +++ b/libpurple/xmlnode.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/Makefile.am --- a/pidgin/Makefile.am Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/Makefile.am Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/Makefile.mingw --- a/pidgin/Makefile.mingw Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/Makefile.mingw Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkaccount.c Wed Jan 02 22:15:06 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; @@ -1121,7 +1108,7 @@ G_CALLBACK(proxy_type_changed_cb), dialog); } -static void +static gboolean account_win_destroy_cb(GtkWidget *w, GdkEvent *event, AccountPrefsDialog *dialog) { @@ -1142,6 +1129,7 @@ purple_signals_disconnect_by_handle(dialog); g_free(dialog); + return FALSE; } static void @@ -1437,7 +1425,6 @@ GtkWidget *win; GtkWidget *main_vbox; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *dbox; GtkWidget *notebook; GtkWidget *button; @@ -1475,16 +1462,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 +1496,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 +1507,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 +1527,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 +1538,6 @@ signed_on_off_cb(PurpleConnection *gc, gpointer user_data) { PurpleAccount *account; - PidginPulseData *pulse_data; GtkTreeModel *model; GtkTreeIter iter; GdkPixbuf *pixbuf; @@ -1591,29 +1553,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 +1759,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 +2100,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 +2258,6 @@ AccountsWindow *dialog; GtkWidget *win; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *sw; GtkWidget *button; int width, height; @@ -2328,7 +2272,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 +2281,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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkblist.c Wed Jan 02 22:15:06 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); @@ -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); @@ -2573,28 +2578,37 @@ 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 (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); @@ -2630,7 +2644,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 +2653,12 @@ int max_avatar_width; GList *l; int prpl_col = 0; - GtkTextDirection dir = gtk_widget_get_direction(widget); + GtkTextDirection dir = gtk_widget_get_direction(widget); 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; @@ -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,7 +2724,9 @@ -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, @@ -2740,10 +2757,11 @@ 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() { while(gtkblist->tooltipdata) { struct tooltip_data *td = gtkblist->tooltipdata->data; @@ -2759,12 +2777,69 @@ 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; + + 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) || + PURPLE_BLIST_NODE_IS_GROUP(node)) { + struct tooltip_data *td = create_tip_for_node(node, TRUE); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + + MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; + height = 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; + 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); + } + 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); + } + } + height += TOOLTIP_BORDER; + width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; + } else { + return FALSE; + } + + if (w) + *w = width; + if (h) + *h = height; + + return TRUE; } static gboolean pidgin_blist_expand_timeout(GtkWidget *tv) @@ -2826,164 +2901,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 +2953,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 +2991,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 +3003,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 @@ -3371,10 +3292,44 @@ 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)) { + GSList *accounts; + 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); + + /* 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; + purple_notify_user_info_add_pair(user_info, _("Account"), purple_account_get_username(account)); + } + + 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); } @@ -4898,6 +4853,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); @@ -5082,8 +5040,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, @@ -5165,12 +5124,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); diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkcertmgr.c --- a/pidgin/gtkcertmgr.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkcertmgr.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkconn.c --- a/pidgin/gtkconn.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkconn.c Wed Jan 02 22:15:06 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); } diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkconv.c Wed Jan 02 22:15:06 2008 +0000 @@ -67,6 +67,7 @@ #include "gtkthemes.h" #include "gtkutils.h" #include "pidginstock.h" +#include "pidgintooltip.h" #include "gtknickcolors.h" @@ -153,6 +154,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); @@ -163,8 +165,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, @@ -3377,6 +3377,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; @@ -3384,6 +3412,7 @@ PurpleConversation *conv = gtkconv->active_conv; char *stock_id; const char *tooltip; + char *message = NULL; gtkwin = gtkconv->win; @@ -3402,6 +3431,7 @@ g_source_remove(gtkconv->u.im->typing_timer); gtkconv->u.im->typing_timer = 0; } + update_typing_message(gtkconv, "\n"); return; } @@ -3411,9 +3441,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; @@ -3436,6 +3468,8 @@ } gtk_widget_show(gtkwin->menu.typing_icon); + update_typing_message(gtkconv, message); + g_free(message); } static gboolean @@ -4399,6 +4433,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) { @@ -4453,14 +4517,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, @@ -4496,32 +4559,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) { @@ -4544,102 +4587,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) { @@ -4669,10 +4616,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); @@ -5031,6 +4976,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); @@ -5171,9 +5123,6 @@ g_source_remove(gtkconv->attach.timer); } - if (tooltip.gtkconv == gtkconv) - reset_tooltip(); - g_free(gtkconv); } @@ -5801,6 +5750,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 @@ -6881,10 +6831,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); @@ -7873,6 +7821,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; @@ -7887,8 +7836,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" @@ -7896,6 +7846,7 @@ styles[iter].stylename, styles[iter].color, styles[iter].labelname, styles[iter].stylename); + } } gtk_rc_parse_string(str->str); g_string_free(str, TRUE); @@ -8318,7 +8269,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)); @@ -8751,7 +8702,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; } @@ -9296,6 +9247,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) { diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkdebug.c --- a/pidgin/gtkdebug.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkdebug.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkdialogs.c Wed Jan 02 22:15:06 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} }; @@ -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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkdialogs.h --- a/pidgin/gtkdialogs.h Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkdialogs.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkdocklet-x11.c --- a/pidgin/gtkdocklet-x11.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkdocklet-x11.c Wed Jan 02 22:15:06 2008 +0000 @@ -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 diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkft.c --- a/pidgin/gtkft.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkft.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkimhtml.c Wed Jan 02 22:15:06 2008 +0000 @@ -812,7 +812,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); @@ -1012,7 +1011,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); @@ -2997,6 +2996,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; @@ -4277,33 +4277,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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkimhtml.h --- a/pidgin/gtkimhtml.h Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkimhtml.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Wed Jan 02 22:15:06 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 @@ -575,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; } @@ -705,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) { @@ -765,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)))); diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtknotify.c --- a/pidgin/gtknotify.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtknotify.c Wed Jan 02 22:15:06 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; @@ -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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkplugin.c --- a/pidgin/gtkplugin.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkplugin.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkpounce.c --- a/pidgin/gtkpounce.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkpounce.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkprefs.c Wed Jan 02 22:15:06 2008 +0000 @@ -2150,7 +2150,6 @@ void pidgin_prefs_show(void) { GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *notebook; GtkWidget *button; @@ -2166,31 +2165,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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkprivacy.c --- a/pidgin/gtkprivacy.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkprivacy.c Wed Jan 02 22:15:06 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,7 +359,6 @@ privacy_dialog_new(void) { PidginPrivacyDialog *dialog; - GtkWidget *bbox; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *button; @@ -367,15 +370,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( @@ -433,52 +434,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 +465,7 @@ gtk_widget_show(dialog->button_box); dialog->in_allow_list = FALSE; } - +#endif return dialog; } diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkrequest.c Wed Jan 02 22:15:06 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; @@ -1518,7 +1506,7 @@ } #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ -static void +static gboolean file_cancel_cb(PidginRequestData *data) { generic_response_start(data); @@ -1527,6 +1515,7 @@ ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL); purple_request_close(data->type, data); + return FALSE; } #endif /* FILECHOOSER */ @@ -1624,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); @@ -1675,6 +1666,8 @@ #endif data->dialog = dirsel; + pidgin_auto_parent_window(dirsel); + gtk_widget_show(dirsel); return (void *)data; diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkroomlist.c --- a/pidgin/gtkroomlist.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkroomlist.c Wed Jan 02 22:15:06 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) @@ -685,15 +529,13 @@ 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); @@ -738,19 +580,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 +616,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 +801,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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtksavedstatuses.c --- a/pidgin/gtksavedstatuses.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtksavedstatuses.c Wed Jan 02 22:15:06 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, @@ -1147,14 +1130,13 @@ 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); @@ -1257,23 +1239,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 +1261,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 +1420,6 @@ char *tmp; SubStatusEditor *dialog; GtkSizeGroup *sg; - GtkWidget *bbox; - GtkWidget *button; GtkWidget *combo; GtkWidget *hbox; GtkWidget *frame; @@ -1486,15 +1457,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 +1513,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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkscrollbook.c --- a/pidgin/gtkscrollbook.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkscrollbook.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkstatusbox.c Wed Jan 02 22:15:06 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"); @@ -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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkutils.c Wed Jan 02 22:15:06 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; @@ -3272,3 +3321,107 @@ gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text)); } +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 +} + diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/gtkutils.h --- a/pidgin/gtkutils.h Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/gtkutils.h Wed Jan 02 22:15:06 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,5 +784,15 @@ */ 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); + #endif /* _PIDGINUTILS_H_ */ diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/minidialog.c --- a/pidgin/minidialog.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/minidialog.c Wed Jan 02 22:15:06 2008 +0000 @@ -63,6 +63,7 @@ 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); diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/pidgincombobox.c --- a/pidgin/pidgincombobox.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/pidgincombobox.c Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/pidgintooltip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.c Wed Jan 02 22:15:06 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() +{ + 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/pidgintooltip.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.h Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jan 02 22:15:06 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" diff -r 2ca7a3d28186 -r c7c5e2ff2b04 pidgin/win32/winpidgin.c --- a/pidgin/win32/winpidgin.c Tue Dec 18 18:18:31 2007 +0000 +++ b/pidgin/win32/winpidgin.c Wed Jan 02 22:15:06 2008 +0000 @@ -467,7 +467,7 @@ return FALSE; } else - printf("Error (%d) accessing \"pidgin_is_running\" mutex.\n", err); + printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err); } return TRUE; } diff -r 2ca7a3d28186 -r c7c5e2ff2b04 po/de.po --- a/po/de.po Tue Dec 18 18:18:31 2007 +0000 +++ b/po/de.po Wed Jan 02 22:15:06 2008 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-12-18 16:51+0100\n" -"PO-Revision-Date: 2007-12-18 16:50+0100\n" +"POT-Creation-Date: 2007-12-19 10:17+0100\n" +"PO-Revision-Date: 2007-12-19 10:17+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" @@ -9630,7 +9633,7 @@ msgstr "Verbindung zu %s nicht möglich: %s" #. 10053 -#, fuzzy, c-format +#, c-format msgid "Connection interrupted by other software on your computer." msgstr "" "Die Verbindung wurde von einer anderen Software auf ihrem Computer " @@ -10460,6 +10463,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" @@ -11425,6 +11436,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" @@ -13203,12 +13241,3 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "" "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients." - -#~ msgid "%s changed status from %s to %s" -#~ msgstr "%s hat den Status von %s zu %s geändert" - -#~ msgid "%s is now %s" -#~ msgstr "%s ist jetzt %s" - -#~ msgid "%s is no longer %s" -#~ msgstr "%s ist nicht mehr %s" diff -r 2ca7a3d28186 -r c7c5e2ff2b04 share/ca-certs/Makefile.am --- a/share/ca-certs/Makefile.am Tue Dec 18 18:18:31 2007 +0000 +++ b/share/ca-certs/Makefile.am Wed Jan 02 22:15:06 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 2ca7a3d28186 -r c7c5e2ff2b04 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 Wed Jan 02 22:15:06 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-----