Mercurial > pidgin.yaz
changeset 21894:65e23b7a4398
propagate from branch 'im.pidgin.pidgin.next.minor' (head 10ff46ff55aee3830dd003ed89866809b5f290ac)
to branch 'im.pidgin.pidgin' (head 94224c6b7f9215a59d27023874ccec8a0b803093)
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Tue, 18 Dec 2007 21:27:01 +0000 |
parents | ae08b1e3ef63 (diff) 7efb9a7de561 (current diff) |
children | 39b078ac8a81 622a96f78a9c |
files | pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkrequest.c pidgin/gtkutils.c |
diffstat | 27 files changed, 1160 insertions(+), 918 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Tue Dec 18 21:24:28 2007 +0000 +++ b/COPYRIGHT Tue Dec 18 21:27:01 2007 +0000 @@ -327,6 +327,7 @@ Andrew Sayman Alceste Scalas Carsten Schaar +Jonathan Schleifer <js-pidgin@webkeks.org> Matteo Settenvini Colin Seymour Luke Schierer
--- a/ChangeLog.API Tue Dec 18 21:24:28 2007 +0000 +++ b/ChangeLog.API Tue Dec 18 21:27:01 2007 +0000 @@ -1,5 +1,25 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.4.0 (??/??/????): + libpurple: + Added: + * purple_certificate_add_ca_search_path. (Florian Quèze) + + 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 and + pidgin_tooltip_show to simplify the process of drawing tooltips. + version 2.3.2 (??/??/????): Finch: libgnt:
--- a/configure.ac Tue Dec 18 21:24:28 2007 +0000 +++ b/configure.ac Tue Dec 18 21:27:01 2007 +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])
--- a/finch/gntrequest.c Tue Dec 18 21:24:28 2007 +0000 +++ b/finch/gntrequest.c Tue Dec 18 21:27:01 2007 +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,
--- a/finch/libgnt/configure.ac Tue Dec 18 21:24:28 2007 +0000 +++ b/finch/libgnt/configure.ac Tue Dec 18 21:27:01 2007 +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])
--- a/libpurple/certificate.c Tue Dec 18 21:24:28 2007 +0000 +++ b/libpurple/certificate.c Tue Dec 18 21:27:01 2007 +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 */ @@ -1901,3 +1910,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)); +} +
--- a/libpurple/certificate.h Tue Dec 18 21:24:28 2007 +0000 +++ b/libpurple/certificate.h Tue Dec 18 21:27:01 2007 +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 }
--- a/pidgin/Makefile.am Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/Makefile.am Tue Dec 18 21:27:01 2007 +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
--- a/pidgin/Makefile.mingw Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/Makefile.mingw Tue Dec 18 21:27:01 2007 +0000 @@ -95,6 +95,7 @@ gtkwhiteboard.c \ minidialog.c \ pidginstock.c \ + pidgintooltip.c \ win32/MinimizeToTray.c \ win32/gtkdocklet-win32.c \ win32/gtkwin32dep.c \
--- a/pidgin/gtkaccount.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkaccount.c Tue Dec 18 21:27:01 2007 +0000 @@ -1437,7 +1437,6 @@ GtkWidget *win; GtkWidget *main_vbox; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *dbox; GtkWidget *notebook; GtkWidget *button; @@ -1475,16 +1474,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 +1508,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 +1519,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 +1539,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); } @@ -2313,7 +2288,6 @@ AccountsWindow *dialog; GtkWidget *win; GtkWidget *vbox; - GtkWidget *bbox; GtkWidget *sw; GtkWidget *button; int width, height; @@ -2328,7 +2302,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 +2311,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,
--- a/pidgin/gtkblist.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkblist.c Tue Dec 18 21:27:01 2007 +0000 @@ -59,6 +59,7 @@ #include "gtkscrollbook.h" #include "gtkutils.h" #include "pidgin/minidialog.h" +#include "pidgin/pidgintooltip.h" #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> @@ -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); @@ -2630,7 +2632,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,10 +2641,10 @@ 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, @@ -2740,10 +2743,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 +2763,62 @@ 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; + + gtkblist->tipwindow = widget; + if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { + struct tooltip_data *td = create_tip_for_node(node, TRUE); + gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); + 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 +2880,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 +2932,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); @@ -3070,7 +2972,6 @@ static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) { - if (gtkblist->timeout) { g_source_remove(gtkblist->timeout); gtkblist->timeout = 0; @@ -3081,8 +2982,6 @@ 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)))) { @@ -5165,12 +5064,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);
--- a/pidgin/gtkcertmgr.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkcertmgr.c Tue Dec 18 21:27:01 2007 +0000 @@ -559,7 +559,6 @@ CertMgrDialog *dlg; GtkWidget *win; GtkWidget *vbox; - GtkWidget *bbox; /* Enumerate all the certificates on file */ { @@ -599,7 +598,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 +610,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 +619,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 */
--- a/pidgin/gtkconv.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkconv.c Tue Dec 18 21:27:01 2007 +0000 @@ -8357,7 +8357,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));
--- a/pidgin/gtkft.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkft.c Tue Dec 18 21:27:01 2007 +0000 @@ -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);
--- a/pidgin/gtkimhtml.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkimhtml.c Tue Dec 18 21:27:01 2007 +0000 @@ -1012,7 +1012,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 +2997,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;
--- a/pidgin/gtkimhtml.h Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkimhtml.h Tue Dec 18 21:27:01 2007 +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 {
--- a/pidgin/gtknotify.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtknotify.c Tue Dec 18 21:27:01 2007 +0000 @@ -284,6 +284,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 +686,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 +898,8 @@ pidgin_notify_searchresults_new_rows(gc, results, data); /* Show the window */ + pidgin_auto_parent_window(window); + gtk_widget_show(window); return data; }
--- a/pidgin/gtkpounce.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkpounce.c Tue Dec 18 21:27:01 2007 +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); }
--- a/pidgin/gtkprefs.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkprefs.c Tue Dec 18 21:27:01 2007 +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();
--- a/pidgin/gtkprivacy.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkprivacy.c Tue Dec 18 21:27:01 2007 +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; }
--- a/pidgin/gtkrequest.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkrequest.c Tue Dec 18 21:27:01 2007 +0000 @@ -439,6 +439,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 +548,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 +665,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 +1065,6 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *hbox; - GtkWidget *bbox; GtkWidget *frame; GtkWidget *label; GtkWidget *table; @@ -1089,9 +1094,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 +1104,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 +1387,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; @@ -1624,6 +1611,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 +1664,8 @@ #endif data->dialog = dirsel; + pidgin_auto_parent_window(dirsel); + gtk_widget_show(dirsel); return (void *)data;
--- a/pidgin/gtkroomlist.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkroomlist.c Tue Dec 18 21:27:01 2007 +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,33 +342,14 @@ } } -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; @@ -404,15 +387,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 +402,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 +442,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 +472,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 +531,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 +582,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 +618,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 +803,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);
--- a/pidgin/gtksavedstatuses.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtksavedstatuses.c Tue Dec 18 21:27:01 2007 +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 */
--- a/pidgin/gtkutils.c Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkutils.c Tue Dec 18 21:27:01 2007 +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 +} +
--- a/pidgin/gtkutils.h Tue Dec 18 21:24:28 2007 +0000 +++ b/pidgin/gtkutils.h Tue Dec 18 21:27:01 2007 +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. @@ -725,5 +780,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_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.c Tue Dec 18 21:27:01 2007 +0000 @@ -0,0 +1,297 @@ +/** + * @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; + PidginTooltipCreateForTree create_tooltip; + PidginTooltipPaint paint_tooltip; + GtkTreePath *path; +} PidginTooltipData; + +static void +destroy_tooltip_data(PidginTooltipData *data) +{ + gtk_tree_path_free(data->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) + 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)); + gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP); + 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->path); + data->path = NULL; +} + +static void +pidgin_tooltip_draw(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->path) { + if (gtk_tree_path_compare(data->path, path) == 0) { + gtk_tree_path_free(path); + return; + } + gtk_tree_path_free(data->path); + data->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->create_tooltip(tipwindow, path, data->userdata, &w, &h)) { + pidgin_tooltip_destroy(); + gtk_tree_path_free(path); + return; + } + + setup_tooltip_window_position(data->userdata, w, h); + + data->path = path; + g_signal_connect_swapped(G_OBJECT(tipwindow), "destroy", + G_CALLBACK(reset_data_treepath), data); +} + +static gboolean +pidgin_tooltip_timeout(gpointer data) +{ + pidgin_tooltip.timeout = 0; + 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 +row_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->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(row_leave_cb), NULL); + g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata); + return TRUE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgintooltip.h Tue Dec 18 21:27:01 2007 +0000 @@ -0,0 +1,97 @@ +/** + * @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 <gtk/gtk.h> + +/** + * @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); + +/** + * 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