Sat, 29 Oct 2022 01:14:13 -0500
Convert PidginProxyPrefs to Adwaita 1.2
Testing Done:
Set all the values via the ui and the config file and tested bad values in the port in both as well. Bad values in the ui will store whatever atoi returned, but bad values in the config file will fallback to the default.
Reviewed at https://reviews.imfreedom.org/r/1996/
/* 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 <glib/gi18n-lib.h> #include <talkatu.h> #include <purple.h> #include "gtkrequest.h" #include "gtkblist.h" #include "gtkutils.h" #include "pidginaccountchooser.h" #include "pidginaccountfilterconnected.h" #include "pidginaccountstore.h" #include "pidgincore.h" #include "pidgindialog.h" #include <gdk/gdkkeysyms.h> typedef struct { PurpleRequestType type; void *user_data; /* May be GtkWidget or GtkNativeDialog */ gpointer dialog; GtkWidget *ok_button; size_t cb_count; GCallback *cbs; union { struct { GtkProgressBar *progress_bar; } wait; struct { GtkWidget *entry; gboolean multiline; gchar *hint; } input; struct { PurpleRequestFields *fields; } multifield; struct { gboolean savedialog; gchar *name; } file; } u; } PidginRequestData; static GHashTable *datasheet_stock = NULL; static GtkWidget * create_account_field(PurpleRequestField *field); static void pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account) { PurpleProtocol *protocol = NULL; GtkWidget *image; const gchar *icon_name = NULL; if(!PURPLE_IS_ACCOUNT(account)) { return; } protocol = purple_account_get_protocol(account); icon_name = purple_protocol_get_icon_name(protocol); image = gtk_image_new_from_icon_name(icon_name); gtk_widget_set_tooltip_text(image, purple_account_get_username(account)); if (GTK_IS_BOX(cont)) { gtk_widget_set_halign(image, GTK_ALIGN_START); gtk_widget_set_valign(image, GTK_ALIGN_START); gtk_box_append(GTK_BOX(cont), image); } } static void generic_response_start(PidginRequestData *data) { g_return_if_fail(data != NULL); g_object_set_data(G_OBJECT(data->dialog), "pidgin-window-is-closing", GINT_TO_POINTER(TRUE)); gtk_widget_set_visible(GTK_WIDGET(data->dialog), FALSE); } static void input_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data) { const char *value; char *multiline_value = NULL; generic_response_start(data); if (data->u.input.multiline || purple_strequal(data->u.input.hint, "html")) { GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data->u.input.entry)); if (purple_strequal(data->u.input.hint, "html")) { multiline_value = talkatu_markup_get_html(buffer, NULL); } else { GtkTextIter start_iter, end_iter; gtk_text_buffer_get_start_iter(buffer, &start_iter); gtk_text_buffer_get_end_iter(buffer, &end_iter); multiline_value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE); } value = multiline_value; } else { value = gtk_editable_get_text(GTK_EDITABLE(data->u.input.entry)); } if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL) ((PurpleRequestInputCb)data->cbs[id])(data->user_data, value); else if (data->cbs[1] != NULL) ((PurpleRequestInputCb)data->cbs[1])(data->user_data, value); if (data->u.input.multiline) { g_free(multiline_value); } purple_request_close(PURPLE_REQUEST_INPUT, data); } static void action_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data) { generic_response_start(data); if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL) ((PurpleRequestActionCb)data->cbs[id])(data->user_data, id); purple_request_close(PURPLE_REQUEST_INPUT, data); } static void choice_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data) { #warning please rewrite me #if 0 GtkWidget *radio = g_object_get_data(G_OBJECT(dialog), "radio"); GSList *group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); generic_response_start(data); if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL) while (group) { if (gtk_check_button_get_active(GTK_CHECK_BUTTON(group->data))) { ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, g_object_get_data(G_OBJECT(group->data), "choice_value")); break; } group = group->next; } purple_request_close(PURPLE_REQUEST_INPUT, data); #endif } static gboolean field_string_focus_out_cb(GtkEventControllerFocus *controller, PurpleRequestField *field) { GtkWidget *entry = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)); const char *value; if (purple_request_field_string_is_multiline(field)) { GtkTextBuffer *buffer; GtkTextIter start_iter, end_iter; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)); gtk_text_buffer_get_start_iter(buffer, &start_iter); gtk_text_buffer_get_end_iter(buffer, &end_iter); value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE); } else value = gtk_editable_get_text(GTK_EDITABLE(entry)); purple_request_field_string_set_value(field, (*value == '\0' ? NULL : value)); return FALSE; } static void field_bool_cb(GtkCheckButton *button, PurpleRequestField *field) { purple_request_field_bool_set_value(field, gtk_check_button_get_active(button)); } static void field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field) { int active = gtk_combo_box_get_active(menu); gpointer *values = g_object_get_data(G_OBJECT(menu), "values"); g_return_if_fail(values != NULL); g_return_if_fail(active >= 0); purple_request_field_choice_set_value(field, values[active]); } static void field_choice_option_cb(GtkCheckButton *button, PurpleRequestField *field) { #warning please rewrite me #if 0 int active; gpointer *values = g_object_get_data(G_OBJECT(g_object_get_data( G_OBJECT(button), "box")), "values"); if (!gtk_check_button_get_active(GTK_CHECK_BUTTON(button))) { return; } active = (g_slist_length(gtk_radio_button_get_group(button)) - g_slist_index(gtk_radio_button_get_group(button), button)) - 1; g_return_if_fail(values != NULL); g_return_if_fail(active >= 0); purple_request_field_choice_set_value(field, values[active]); #endif } static void field_account_cb(GObject *w, PurpleRequestField *field) { PidginAccountChooser *chooser = PIDGIN_ACCOUNT_CHOOSER(w); purple_request_field_account_set_value( field, pidgin_account_chooser_get_selected(chooser)); } static void multifield_ok_cb(GtkWidget *button, PidginRequestData *data) { generic_response_start(data); if (!gtk_widget_has_focus(button)) gtk_widget_grab_focus(button); if (data->cbs[0] != NULL) ((PurpleRequestFieldsCb)data->cbs[0])(data->user_data, data->u.multifield.fields); purple_request_close(PURPLE_REQUEST_FIELDS, data); } static void multifield_cancel_cb(GtkWidget *button, PidginRequestData *data) { generic_response_start(data); if (data->cbs[1] != NULL) ((PurpleRequestFieldsCb)data->cbs[1])(data->user_data, data->u.multifield.fields); purple_request_close(PURPLE_REQUEST_FIELDS, data); } static void multifield_extra_cb(GtkWidget *button, PidginRequestData *data) { PurpleRequestFieldsCb cb; generic_response_start(data); cb = g_object_get_data(G_OBJECT(button), "extra-cb"); if (cb != NULL) cb(data->user_data, data->u.multifield.fields); purple_request_close(PURPLE_REQUEST_FIELDS, data); } static gboolean destroy_multifield_cb(GtkWidget *self, PidginRequestData *data) { multifield_cancel_cb(NULL, data); return FALSE; } static gchar * pidgin_request_escape(PurpleRequestCommonParameters *cpar, const gchar *text) { if (text == NULL) return NULL; if (purple_request_cpar_is_html(cpar)) { gboolean valid; valid = pango_parse_markup(text, -1, 0, NULL, NULL, NULL, NULL); if (valid) return g_strdup(text); else { purple_debug_error("pidgin", "Passed label text is not " "a valid markup. Falling back to plain text."); } } return g_markup_escape_text(text, -1); } static GtkWidget * pidgin_request_dialog_icon(PurpleRequestType dialog_type, PurpleRequestCommonParameters *cpar) { GtkWidget *img = NULL; PurpleRequestIconType icon_type; gconstpointer icon_data; gsize icon_size; const gchar *icon_name = "dialog-question"; /* Dialog icon. */ icon_data = purple_request_cpar_get_custom_icon(cpar, &icon_size); if (icon_data) { GdkPixbuf *pixbuf; pixbuf = purple_gdk_pixbuf_from_data(icon_data, icon_size); if (pixbuf) { /* scale the image if it is too large */ int width = gdk_pixbuf_get_width(pixbuf); int height = gdk_pixbuf_get_height(pixbuf); if (width > 128 || height > 128) { int scaled_width = width > height ? 128 : (128 * width) / height; int scaled_height = height > width ? 128 : (128 * height) / width; GdkPixbuf *scaled; purple_debug_info("pidgin", "dialog icon was " "too large, scaling it down"); scaled = gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR); if (scaled) { g_object_unref(pixbuf); pixbuf = scaled; } } img = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); } else { purple_debug_info("pidgin", "failed to parse dialog icon"); } } if (img) return img; icon_type = purple_request_cpar_get_icon(cpar); switch (icon_type) { case PURPLE_REQUEST_ICON_DEFAULT: icon_name = NULL; break; case PURPLE_REQUEST_ICON_REQUEST: icon_name = "dialog-question"; break; case PURPLE_REQUEST_ICON_DIALOG: case PURPLE_REQUEST_ICON_INFO: case PURPLE_REQUEST_ICON_WAIT: /* TODO: we need another icon */ icon_name = "dialog-information"; break; case PURPLE_REQUEST_ICON_WARNING: icon_name = "dialog-warning"; break; case PURPLE_REQUEST_ICON_ERROR: icon_name = "dialog-error"; break; /* intentionally no default value */ } if (icon_name == NULL) { switch (dialog_type) { case PURPLE_REQUEST_INPUT: case PURPLE_REQUEST_CHOICE: case PURPLE_REQUEST_ACTION: case PURPLE_REQUEST_FIELDS: case PURPLE_REQUEST_FILE: case PURPLE_REQUEST_FOLDER: icon_name = "dialog-question"; break; case PURPLE_REQUEST_WAIT: icon_name = "dialog-information"; break; /* intentionally no default value */ } } if(icon_name == NULL) { icon_name = "dialog-question"; } img = gtk_image_new_from_icon_name(icon_name); gtk_image_set_icon_size(GTK_IMAGE(img), GTK_ICON_SIZE_LARGE); return img; } static void pidgin_request_help_clicked(GtkButton *button, gpointer _unused) { PurpleRequestHelpCb cb; gpointer data; cb = g_object_get_data(G_OBJECT(button), "pidgin-help-cb"); data = g_object_get_data(G_OBJECT(button), "pidgin-help-data"); g_return_if_fail(cb != NULL); cb(data); } static void pidgin_request_add_help(GtkDialog *dialog, PurpleRequestCommonParameters *cpar) { GtkWidget *button; PurpleRequestHelpCb help_cb; gpointer help_data; help_cb = purple_request_cpar_get_help_cb(cpar, &help_data); if (help_cb == NULL) return; button = gtk_dialog_add_button(dialog, _("_Help"), GTK_RESPONSE_HELP); g_object_set_data(G_OBJECT(button), "pidgin-help-cb", help_cb); g_object_set_data(G_OBJECT(button), "pidgin-help-data", help_data); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_request_help_clicked), NULL); } static void * pidgin_request_input(const char *title, const char *primary, const char *secondary, const char *default_value, gboolean multiline, gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data) { PidginRequestData *data; GtkWidget *dialog; GtkWidget *vbox; GtkWidget *hbox; GtkLabel *label; GtkWidget *img; GtkWidget *content; char *label_text; char *primary_esc, *secondary_esc; data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_INPUT; data->user_data = user_data; data->cb_count = 2; data->cbs = g_new0(GCallback, 2); data->cbs[0] = ok_cb; data->cbs[1] = cancel_cb; /* Create the dialog. */ dialog = gtk_dialog_new_with_buttons(title ? title : PIDGIN_ALERT_TITLE, NULL, 0, cancel_text, 1, ok_text, 0, NULL); data->dialog = dialog; g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(input_response_cb), data); /* Setup the dialog */ gtk_widget_set_margin_top(dialog, 6); gtk_widget_set_margin_bottom(dialog, 6); gtk_widget_set_margin_start(dialog, 6); gtk_widget_set_margin_end(dialog, 6); content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_widget_set_margin_top(content, 6); gtk_widget_set_margin_bottom(content, 6); gtk_widget_set_margin_start(content, 6); gtk_widget_set_margin_end(content, 6); if (!multiline) gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0); gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), 12); /* Setup the main horizontal box */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_append(GTK_BOX(content), hbox); /* Dialog icon. */ img = pidgin_request_dialog_icon(PURPLE_REQUEST_INPUT, cpar); gtk_widget_set_halign(img, GTK_ALIGN_START); gtk_widget_set_valign(img, GTK_ALIGN_START); gtk_box_append(GTK_BOX(hbox), img); pidgin_request_add_help(GTK_DIALOG(dialog), cpar); /* Vertical box */ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_widget_set_hexpand(vbox, TRUE); gtk_box_append(GTK_BOX(hbox), vbox); pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar)); /* Descriptive label */ primary_esc = pidgin_request_escape(cpar, primary); secondary_esc = pidgin_request_escape(cpar, secondary); label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">" "%s</span>%s%s" : "%s%s%s"), (primary ? primary_esc : ""), ((primary && secondary) ? "\n\n" : ""), (secondary ? secondary_esc : "")); g_free(primary_esc); g_free(secondary_esc); label = GTK_LABEL(gtk_label_new(NULL)); gtk_label_set_markup(label, label_text); gtk_label_set_wrap(label, TRUE); gtk_label_set_xalign(label, 0); gtk_label_set_yalign(label, 0); gtk_box_append(GTK_BOX(vbox), GTK_WIDGET(label)); g_free(label_text); /* Entry field. */ data->u.input.multiline = multiline; data->u.input.hint = g_strdup(hint); if(multiline || purple_strequal(data->u.input.hint, "html")) { GtkWidget *editor = talkatu_editor_new(); GtkWidget *input = talkatu_editor_get_input(TALKATU_EDITOR(editor)); GtkTextBuffer *buffer = NULL; gtk_widget_set_size_request(input, 320, 130); gtk_widget_set_name(input, "pidgin_request_input"); gtk_widget_set_vexpand(editor, TRUE); gtk_box_append(GTK_BOX(vbox), editor); if (purple_strequal(data->u.input.hint, "html")) { buffer = talkatu_html_buffer_new(); if(default_value != NULL) { talkatu_markup_set_html(TALKATU_BUFFER(buffer), default_value, -1); } } else { buffer = gtk_text_buffer_new(NULL); if(default_value != NULL) { gtk_text_buffer_set_text(buffer, default_value, -1); } } gtk_text_view_set_buffer(GTK_TEXT_VIEW(input), buffer); data->u.input.entry = input; } else { GtkWidget *entry = NULL; if(masked) { entry = gtk_password_entry_new(); g_object_set(entry, "activates-default", TRUE, "show-peek-icon", TRUE, NULL); } else { entry = gtk_entry_new(); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); } gtk_box_append(GTK_BOX(vbox), entry); if(default_value != NULL) { gtk_editable_set_text(GTK_EDITABLE(entry), default_value); } data->u.input.entry = entry; } pidgin_set_accessible_label(data->u.input.entry, label); pidgin_auto_parent_window(dialog); /* Show everything. */ gtk_widget_show(dialog); return data; } static void * pidgin_request_choice(const char *title, const char *primary, const char *secondary, gpointer default_value, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data, va_list args) { PidginRequestData *data; GtkWidget *dialog; GtkWidget *vbox, *vbox2; GtkWidget *hbox; GtkWidget *label; GtkWidget *img; GtkWidget *first_radio = NULL; GtkWidget *content; char *label_text; char *radio_text; char *primary_esc, *secondary_esc; data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_ACTION; data->user_data = user_data; data->cb_count = 2; data->cbs = g_new0(GCallback, 2); data->cbs[0] = cancel_cb; data->cbs[1] = ok_cb; /* Create the dialog. */ data->dialog = dialog = gtk_dialog_new(); if (title != NULL) gtk_window_set_title(GTK_WINDOW(dialog), title); #ifdef _WIN32 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE); #endif gtk_dialog_add_button(GTK_DIALOG(dialog), cancel_text, 0); gtk_dialog_add_button(GTK_DIALOG(dialog), ok_text, 1); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(choice_response_cb), data); /* Setup the dialog */ gtk_widget_set_margin_top(dialog, 6); gtk_widget_set_margin_bottom(dialog, 6); gtk_widget_set_margin_start(dialog, 6); gtk_widget_set_margin_end(dialog, 6); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_widget_set_margin_top(content, 6); gtk_widget_set_margin_bottom(content, 6); gtk_widget_set_margin_start(content, 6); gtk_widget_set_margin_end(content, 6); gtk_box_set_spacing(GTK_BOX(content), 12); /* Setup the main horizontal box */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_append(GTK_BOX(content), hbox); /* Dialog icon. */ img = pidgin_request_dialog_icon(PURPLE_REQUEST_CHOICE, cpar); gtk_widget_set_halign(img, GTK_ALIGN_START); gtk_widget_set_valign(img, GTK_ALIGN_START); gtk_box_append(GTK_BOX(hbox), img); pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar)); pidgin_request_add_help(GTK_DIALOG(dialog), cpar); /* Vertical box */ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_box_append(GTK_BOX(hbox), vbox); /* Descriptive label */ primary_esc = pidgin_request_escape(cpar, primary); secondary_esc = pidgin_request_escape(cpar, secondary); label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">" "%s</span>%s%s" : "%s%s%s"), (primary ? primary_esc : ""), ((primary && secondary) ? "\n\n" : ""), (secondary ? secondary_esc : "")); g_free(primary_esc); g_free(secondary_esc); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), label_text); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_widget_set_vexpand(label, TRUE); gtk_box_append(GTK_BOX(vbox), label); g_free(label_text); vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_box_append(GTK_BOX(vbox), vbox2); while ((radio_text = va_arg(args, char*))) { GtkWidget *radio = NULL; gpointer resp = va_arg(args, gpointer); radio = gtk_check_button_new_with_label(radio_text); if(first_radio == NULL) { first_radio = radio; } else { gtk_check_button_set_group(GTK_CHECK_BUTTON(radio), GTK_CHECK_BUTTON(first_radio)); } gtk_box_append(GTK_BOX(vbox2), radio); g_object_set_data(G_OBJECT(radio), "choice_value", resp); if (resp == default_value) { gtk_check_button_set_active(GTK_CHECK_BUTTON(radio), TRUE); } } g_object_set_data(G_OBJECT(dialog), "radio", first_radio); /* Show everything. */ pidgin_auto_parent_window(dialog); gtk_widget_show(dialog); return data; } static void * pidgin_request_action(const char *title, const char *primary, const char *secondary, int default_action, PurpleRequestCommonParameters *cpar, void *user_data, size_t action_count, va_list actions) { PidginRequestData *data; GtkWidget *dialog; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *img = NULL; GtkWidget *content; void **buttons; char *label_text; char *primary_esc, *secondary_esc; gsize i; data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_ACTION; data->user_data = user_data; data->cb_count = action_count; data->cbs = g_new0(GCallback, action_count); /* Reverse the buttons */ buttons = g_new0(void *, action_count * 2); for (i = 0; i < action_count * 2; i += 2) { buttons[(action_count * 2) - i - 2] = va_arg(actions, char *); buttons[(action_count * 2) - i - 1] = va_arg(actions, GCallback); } /* Create the dialog. */ data->dialog = dialog = gtk_dialog_new(); gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE); if (title != NULL) gtk_window_set_title(GTK_WINDOW(dialog), title); #ifdef _WIN32 else gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE); #endif for (i = 0; i < action_count; i++) { gtk_dialog_add_button(GTK_DIALOG(dialog), buttons[2 * i], i); data->cbs[i] = buttons[2 * i + 1]; } g_free(buttons); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(action_response_cb), data); /* Setup the dialog */ gtk_widget_set_margin_top(dialog, 6); gtk_widget_set_margin_bottom(dialog, 6); gtk_widget_set_margin_start(dialog, 6); gtk_widget_set_margin_end(dialog, 6); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_widget_set_margin_top(content, 6); gtk_widget_set_margin_bottom(content, 6); gtk_widget_set_margin_start(content, 6); gtk_widget_set_margin_end(content, 6); gtk_box_set_spacing(GTK_BOX(content), 12); /* Setup the main horizontal box */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_append(GTK_BOX(content), hbox); img = pidgin_request_dialog_icon(PURPLE_REQUEST_ACTION, cpar); gtk_widget_set_halign(img, GTK_ALIGN_START); gtk_widget_set_valign(img, GTK_ALIGN_START); gtk_box_append(GTK_BOX(hbox), img); /* Vertical box */ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_box_append(GTK_BOX(hbox), vbox); pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar)); pidgin_request_add_help(GTK_DIALOG(dialog), cpar); /* Descriptive label */ primary_esc = pidgin_request_escape(cpar, primary); secondary_esc = pidgin_request_escape(cpar, secondary); label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">" "%s</span>%s%s" : "%s%s%s"), (primary ? primary_esc : ""), ((primary && secondary) ? "\n\n" : ""), (secondary ? secondary_esc : "")); g_free(primary_esc); g_free(secondary_esc); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), label_text); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_widget_set_vexpand(label, TRUE); gtk_box_append(GTK_BOX(vbox), label); g_free(label_text); if (default_action != PURPLE_DEFAULT_ACTION_NONE) { /* * Need to invert the default_action number because the * buttons are added to the dialog in reverse order. */ gtk_dialog_set_default_response(GTK_DIALOG(dialog), action_count - 1 - default_action); } /* Show everything. */ pidgin_auto_parent_window(dialog); gtk_widget_show(dialog); return data; } static void wait_cancel_cb(GtkWidget *button, PidginRequestData *data) { generic_response_start(data); if (data->cbs[0] != NULL) ((PurpleRequestCancelCb)data->cbs[0])(data->user_data); purple_request_close(PURPLE_REQUEST_FIELDS, data); } static void * pidgin_request_wait(const char *title, const char *primary, const char *secondary, gboolean with_progress, PurpleRequestCancelCb cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data) { PidginRequestData *data; GtkWidget *dialog, *content; GtkWidget *hbox, *vbox, *img, *label, *button; gchar *primary_esc, *secondary_esc, *label_text; data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_WAIT; data->user_data = user_data; data->cb_count = 1; data->cbs = g_new0(GCallback, 1); data->cbs[0] = (GCallback)cancel_cb; data->dialog = dialog = gtk_dialog_new(); gtk_window_set_deletable(GTK_WINDOW(data->dialog), cancel_cb != NULL); if (title != NULL) gtk_window_set_title(GTK_WINDOW(dialog), title); else gtk_window_set_title(GTK_WINDOW(dialog), _("Please wait")); /* Setup the dialog */ gtk_widget_set_margin_top(dialog, 6); gtk_widget_set_margin_bottom(dialog, 6); gtk_widget_set_margin_start(dialog, 6); gtk_widget_set_margin_end(dialog, 6); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_widget_set_margin_top(content, 6); gtk_widget_set_margin_bottom(content, 6); gtk_widget_set_margin_start(content, 6); gtk_widget_set_margin_end(content, 6); gtk_box_set_spacing(GTK_BOX(content), 12); /* Setup the main horizontal box */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_append(GTK_BOX(content), hbox); img = pidgin_request_dialog_icon(PURPLE_REQUEST_WAIT, cpar); gtk_widget_set_halign(img, GTK_ALIGN_START); gtk_widget_set_valign(img, GTK_ALIGN_START); gtk_box_append(GTK_BOX(hbox), img); /* Cancel button */ button = gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(wait_cancel_cb), data); /* Vertical box */ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_box_append(GTK_BOX(hbox), vbox); pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar)); pidgin_request_add_help(GTK_DIALOG(dialog), cpar); /* Descriptive label */ primary_esc = pidgin_request_escape(cpar, primary); secondary_esc = pidgin_request_escape(cpar, secondary); label_text = g_strdup_printf((primary ? "<span weight=\"bold\" " "size=\"larger\">%s</span>%s%s" : "%s%s%s"), (primary ? primary_esc : ""), ((primary && secondary) ? "\n\n" : ""), (secondary ? secondary_esc : "")); g_free(primary_esc); g_free(secondary_esc); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), label_text); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_label_set_selectable(GTK_LABEL(label), FALSE); gtk_widget_set_vexpand(label, TRUE); gtk_box_append(GTK_BOX(vbox), label); g_free(label_text); if (with_progress) { GtkProgressBar *bar; bar = data->u.wait.progress_bar = GTK_PROGRESS_BAR(gtk_progress_bar_new()); gtk_progress_bar_set_fraction(bar, 0); gtk_box_append(GTK_BOX(vbox), GTK_WIDGET(bar)); } /* Show everything. */ pidgin_auto_parent_window(dialog); gtk_widget_show(dialog); return data; } static void pidgin_request_wait_update(void *ui_handle, gboolean pulse, gfloat fraction) { GtkProgressBar *bar; PidginRequestData *data = ui_handle; g_return_if_fail(data->type == PURPLE_REQUEST_WAIT); bar = data->u.wait.progress_bar; if (pulse) gtk_progress_bar_pulse(bar); else gtk_progress_bar_set_fraction(bar, fraction); } static void req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field) { if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER) { int value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(entry)); purple_request_field_int_set_value(field, value); return; } if (purple_request_field_string_is_multiline(field)) { char *text; GtkTextIter start_iter, end_iter; gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(entry), &start_iter); gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(entry), &end_iter); text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(entry), &start_iter, &end_iter, FALSE); purple_request_field_string_set_value(field, (!text || !*text) ? NULL : text); g_free(text); } else { const char *text = NULL; text = gtk_editable_get_text(GTK_EDITABLE(entry)); purple_request_field_string_set_value(field, (*text == '\0') ? NULL : text); } } static void req_field_changed_cb(GtkWidget *widget, PurpleRequestField *field) { PurpleRequestFieldGroup *group; PurpleRequestFields *fields; PidginRequestData *req_data; const GList *it; group = purple_request_field_get_group(field); fields = purple_request_field_group_get_fields_list(group); req_data = purple_request_fields_get_ui_data(fields); gtk_widget_set_sensitive(req_data->ok_button, purple_request_fields_all_required_filled(fields) && purple_request_fields_all_valid(fields)); it = purple_request_fields_get_autosensitive(fields); for (; it != NULL; it = g_list_next(it)) { PurpleRequestField *field = it->data; GtkWidget *widget = purple_request_field_get_ui_data(field); gboolean sensitive; sensitive = purple_request_field_is_sensitive(field); gtk_widget_set_sensitive(widget, sensitive); /* XXX: and what about multiline? */ if (GTK_IS_EDITABLE(widget)) gtk_editable_set_editable(GTK_EDITABLE(widget), sensitive); } } static void setup_entry_field(GtkWidget *entry, PurpleRequestField *field) { const char *type_hint; g_object_set(entry, "activates-default", TRUE, NULL); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(req_entry_field_changed_cb), field); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(req_field_changed_cb), field); if ((type_hint = purple_request_field_get_field_type_hint(field)) != NULL) { if (g_str_has_prefix(type_hint, "screenname")) { GtkWidget *optmenu = NULL; PurpleRequestFieldGroup *group = purple_request_field_get_group(field); GList *fields = purple_request_field_group_get_fields(group); /* Ensure the account option menu is created (if the widget hasn't * been initialized already) for username auto-completion. */ while (fields) { PurpleRequestField *fld = fields->data; fields = fields->next; if (purple_request_field_get_field_type(fld) == PURPLE_REQUEST_FIELD_ACCOUNT && purple_request_field_is_visible(fld)) { const char *type_hint = purple_request_field_get_field_type_hint(fld); if (purple_strequal(type_hint, "account")) { optmenu = GTK_WIDGET(purple_request_field_get_ui_data(fld)); if (optmenu == NULL) { optmenu = GTK_WIDGET(create_account_field(fld)); purple_request_field_set_ui_data(fld, optmenu); } break; } } } pidgin_setup_screenname_autocomplete(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(purple_strequal(type_hint, "screenname-all"))); } } } static GtkWidget * create_string_field(PurpleRequestField *field) { const char *value; GtkWidget *widget; gboolean is_editable; value = purple_request_field_string_get_default_value(field); is_editable = purple_request_field_is_sensitive(field); if (purple_request_field_string_is_multiline(field)) { GtkWidget *textview; GtkEventController *controller; textview = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), TRUE); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); if (value != NULL) { GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); gtk_text_buffer_set_text(buffer, value, -1); } gtk_widget_set_tooltip_text(textview, purple_request_field_get_tooltip(field)); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), is_editable); controller = gtk_event_controller_focus_new(); gtk_widget_add_controller(textview, controller); g_signal_connect(controller, "leave", G_CALLBACK(field_string_focus_out_cb), field); if (purple_request_field_is_required(field)) { GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(req_entry_field_changed_cb), field); } widget = gtk_scrolled_window_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_widget_set_size_request(widget, -1, 75); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(widget), textview); } else { GtkEventController *controller = NULL; if (purple_request_field_string_is_masked(field)) { widget = gtk_password_entry_new(); gtk_password_entry_set_show_peek_icon(GTK_PASSWORD_ENTRY(widget), TRUE); } else { widget = gtk_entry_new(); } setup_entry_field(widget, field); if(value != NULL) { gtk_editable_set_text(GTK_EDITABLE(widget), value); } gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); gtk_editable_set_editable(GTK_EDITABLE(widget), is_editable); controller = gtk_event_controller_focus_new(); gtk_widget_add_controller(widget, controller); g_signal_connect(controller, "leave", G_CALLBACK(field_string_focus_out_cb), field); } return widget; } static GtkWidget * create_int_field(PurpleRequestField *field) { int value; GtkWidget *widget; widget = gtk_spin_button_new_with_range( purple_request_field_int_get_lower_bound(field), purple_request_field_int_get_upper_bound(field), 1); setup_entry_field(widget, field); value = purple_request_field_int_get_default_value(field); gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), value); gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); return widget; } static GtkWidget * create_bool_field(PurpleRequestField *field, PurpleRequestCommonParameters *cpar) { GtkWidget *widget; gchar *label; label = pidgin_request_escape(cpar, purple_request_field_get_label(field)); widget = gtk_check_button_new_with_label(label); g_free(label); gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); gtk_check_button_set_active(GTK_CHECK_BUTTON(widget), purple_request_field_bool_get_default_value(field)); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(field_bool_cb), field); g_signal_connect(widget, "toggled", G_CALLBACK(req_field_changed_cb), field); return widget; } static GtkWidget * create_choice_field(PurpleRequestField *field, PurpleRequestCommonParameters *cpar) { GtkWidget *widget; GList *elements = purple_request_field_choice_get_elements(field); guint num_labels = g_list_length(elements); gpointer *values = g_new(gpointer, num_labels); gpointer default_value; gboolean default_found = FALSE; int i; default_value = purple_request_field_choice_get_value(field); if (num_labels > 5 || purple_request_cpar_is_compact(cpar)) { int default_index = 0; widget = gtk_combo_box_text_new(); i = 0; for (GList *l = elements; l != NULL; l = g_list_next(l)) { PurpleKeyValuePair *choice = l->data; gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), choice->key); if (choice->value == default_value) { default_index = i; default_found = TRUE; } values[i++] = choice->value; } gtk_combo_box_set_active(GTK_COMBO_BOX(widget), default_index); gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(field_choice_menu_cb), field); } else { GtkWidget *box; GtkWidget *first_radio = NULL; GtkWidget *radio; GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL; if(num_labels == 2) { orientation = GTK_ORIENTATION_HORIZONTAL; box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); } else { orientation = GTK_ORIENTATION_VERTICAL; box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); } widget = box; gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); i = 0; for (GList *l = elements; l != NULL; l = g_list_next(l)) { PurpleKeyValuePair *choice = l->data; radio = gtk_check_button_new_with_label(choice->key); g_object_set_data(G_OBJECT(radio), "box", box); if(first_radio == NULL) { first_radio = radio; } else { gtk_check_button_set_group(GTK_CHECK_BUTTON(radio), GTK_CHECK_BUTTON(first_radio)); } if (choice->value == default_value) { gtk_check_button_set_active(GTK_CHECK_BUTTON(radio), TRUE); default_found = TRUE; } values[i++] = choice->value; if(orientation == GTK_ORIENTATION_VERTICAL) { gtk_widget_set_vexpand(radio, TRUE); } else if(orientation == GTK_ORIENTATION_HORIZONTAL) { gtk_widget_set_hexpand(radio, TRUE); } gtk_box_append(GTK_BOX(box), radio); g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(field_choice_option_cb), field); } } if (!default_found && i > 0) purple_request_field_choice_set_value(field, values[0]); g_object_set_data_full(G_OBJECT(widget), "values", values, g_free); return widget; return NULL; } static GtkWidget * create_image_field(PurpleRequestField *field) { GtkWidget *widget; GdkPixbuf *buf, *scale; buf = purple_gdk_pixbuf_from_data( (const guchar *)purple_request_field_image_get_buffer(field), purple_request_field_image_get_size(field)); scale = gdk_pixbuf_scale_simple(buf, purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf), purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf), GDK_INTERP_BILINEAR); widget = gtk_image_new_from_pixbuf(scale); g_object_unref(G_OBJECT(buf)); g_object_unref(G_OBJECT(scale)); gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); return widget; } static gboolean field_custom_account_filter_cb(gpointer item, G_GNUC_UNUSED gpointer data) { PurpleFilterAccountFunc func = data; gboolean ret = FALSE; if(PURPLE_IS_ACCOUNT(item)) { ret = func(PURPLE_ACCOUNT(item)); } return ret; } static GtkWidget * create_account_field(PurpleRequestField *field) { GtkWidget *widget; PurpleAccount *account; GtkCustomFilter *custom_filter = NULL; widget = pidgin_account_chooser_new(); account = purple_request_field_account_get_default_value(field); if(purple_request_field_account_get_show_all(field)) { GtkListStore *store = pidgin_account_store_new(); gtk_combo_box_set_model(GTK_COMBO_BOX(widget), GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); } else { GtkListStore *store = NULL; GtkTreeModel *filter = NULL; store = pidgin_account_store_new(); filter = pidgin_account_filter_connected_new(GTK_TREE_MODEL(store), NULL); g_object_unref(G_OBJECT(store)); gtk_combo_box_set_model(GTK_COMBO_BOX(widget), GTK_TREE_MODEL(filter)); g_object_unref(G_OBJECT(filter)); } pidgin_account_chooser_set_selected(PIDGIN_ACCOUNT_CHOOSER(widget), account); custom_filter = gtk_custom_filter_new( field_custom_account_filter_cb, purple_request_field_account_get_filter(field), NULL); pidgin_account_chooser_set_filter( PIDGIN_ACCOUNT_CHOOSER(widget), GTK_FILTER(custom_filter)); g_object_unref(custom_filter); g_signal_connect(widget, "changed", G_CALLBACK(field_account_cb), field); gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field)); g_signal_connect(widget, "changed", G_CALLBACK(req_field_changed_cb), field); return widget; } static void select_field_list_item(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { PurpleRequestField *field = (PurpleRequestField *)data; char *text; gtk_tree_model_get(model, iter, 1, &text, -1); purple_request_field_list_add_selected(field, text); g_free(text); } static void list_field_select_changed_cb(GtkTreeSelection *sel, PurpleRequestField *field) { purple_request_field_list_clear_selected(field); gtk_tree_selection_selected_foreach(sel, select_field_list_item, field); } static GtkWidget * create_list_field(PurpleRequestField *field) { GtkWidget *sw; GtkWidget *treeview; GtkListStore *store; GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; GtkTreeIter iter; GList *l; gboolean has_icons; has_icons = purple_request_field_list_has_icons(field); /* Create the list store */ if (has_icons) { store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF); } else { store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); } /* Create the tree view */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); if (purple_request_field_list_get_multi_select(field)) gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); column = gtk_tree_view_column_new(); gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", 1); if (has_icons) { renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2); gtk_widget_set_size_request(treeview, 200, 400); } for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next) { PurpleKeyValuePair *item = l->data; const char *text = (const char *)item->key; gtk_list_store_append(store, &iter); if (has_icons) { const char *icon_path = (const char *)item->value; GdkPixbuf* pixbuf = NULL; if (icon_path) pixbuf = purple_gdk_pixbuf_new_from_file(icon_path); gtk_list_store_set(store, &iter, 0, purple_request_field_list_get_data(field, text), 1, text, 2, pixbuf, -1); } else { gtk_list_store_set(store, &iter, 0, purple_request_field_list_get_data(field, text), 1, text, -1); } if (purple_request_field_list_is_selected(field, text)) { gtk_tree_selection_select_iter(sel, &iter); } } /* * We only want to catch changes made by the user, so it's important * that we wait until after the list is created to connect this * handler. If we connect the handler before the loop above and * there are multiple items selected, then selecting the first iter * in the tree causes list_field_select_changed_cb to be triggered * which clears out the rest of the list of selected items. */ g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(list_field_select_changed_cb), field); sw = gtk_scrolled_window_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), treeview); return sw; } static GdkPixbuf* _pidgin_datasheet_stock_icon_get(const gchar *stock_name) { GdkPixbuf *image = NULL; if (stock_name == NULL) return NULL; /* core is quitting */ if (datasheet_stock == NULL) return NULL; if (g_hash_table_lookup_extended(datasheet_stock, stock_name, NULL, (gpointer*)&image)) { return image; } purple_debug_error("gtkrequest", "Unknown icon: %s", stock_name); return NULL; } static PurpleRequestDatasheetRecord* datasheet_get_selected_row(GtkWidget *sheet_widget) { PurpleRequestDatasheet *sheet; GtkTreeView *view; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GList *sel_list; gpointer key; g_return_val_if_fail(sheet_widget != NULL, NULL); view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(sheet_widget), "view")); sheet = g_object_get_data(G_OBJECT(sheet_widget), "sheet"); g_return_val_if_fail(view != NULL, NULL); g_return_val_if_fail(sheet != NULL, NULL); selection = gtk_tree_view_get_selection(view); if (gtk_tree_selection_count_selected_rows(selection) != 1) return NULL; sel_list = gtk_tree_selection_get_selected_rows(selection, &model); gtk_tree_model_get_iter(model, &iter, sel_list->data); g_list_free_full(sel_list, (GDestroyNotify)gtk_tree_path_free); gtk_tree_model_get(model, &iter, 0, &key, -1); return purple_request_datasheet_record_find(sheet, key); } static void datasheet_button_check_sens(GtkWidget *button, gpointer _sheet_widget) { PurpleRequestDatasheetAction *act; GtkWidget *sheet_widget = GTK_WIDGET(_sheet_widget); g_return_if_fail(sheet_widget != NULL); act = g_object_get_data(G_OBJECT(button), "action"); g_return_if_fail(act != NULL); gtk_widget_set_sensitive(button, purple_request_datasheet_action_is_sensitive(act, datasheet_get_selected_row(sheet_widget))); } static void datasheet_selection_changed(GtkWidget *sheet_widget) { #if 0 gpointer buttons_box; g_return_if_fail(sheet_widget != NULL); buttons_box = g_object_get_data(G_OBJECT(sheet_widget), "buttons"); gtk_container_foreach(GTK_CONTAINER(buttons_box), datasheet_button_check_sens, sheet_widget); #endif } static void datasheet_update_rec(PurpleRequestDatasheetRecord *rec, GtkListStore *model, GtkTreeIter *iter) { guint i, col_count; PurpleRequestDatasheet *sheet; g_return_if_fail(rec != NULL); g_return_if_fail(model != NULL); g_return_if_fail(iter != NULL); sheet = purple_request_datasheet_record_get_datasheet(rec); g_return_if_fail(sheet != NULL); col_count = purple_request_datasheet_get_column_count(sheet); for (i = 0; i < col_count; i++) { PurpleRequestDatasheetColumnType type; type = purple_request_datasheet_get_column_type( sheet, i); if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING) { GValue val; val.g_type = 0; g_value_init(&val, G_TYPE_STRING); g_value_set_string(&val, purple_request_datasheet_record_get_string_data( rec, i)); gtk_list_store_set_value(model, iter, i + 1, &val); } else if (type == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE) { GdkPixbuf *pixbuf; pixbuf = _pidgin_datasheet_stock_icon_get( purple_request_datasheet_record_get_image_data( rec, i)); gtk_list_store_set(model, iter, i + 1, pixbuf, -1); } else g_warn_if_reached(); } } static void datasheet_fill(PurpleRequestDatasheet *sheet, GtkListStore *model) { const GList *it; gtk_list_store_clear(model); it = purple_request_datasheet_get_records(sheet); for (; it != NULL; it = g_list_next(it)) { PurpleRequestDatasheetRecord *rec = it->data; GtkTreeIter iter; gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, 0, purple_request_datasheet_record_get_key(rec), -1); datasheet_update_rec(rec, model, &iter); } datasheet_selection_changed(GTK_WIDGET(g_object_get_data( G_OBJECT(model), "sheet-widget"))); } static void datasheet_update(PurpleRequestDatasheet *sheet, gpointer key, GtkListStore *model) { PurpleRequestDatasheetRecord *rec; GtkTreeIter iter; GtkTreeModel *tmodel = GTK_TREE_MODEL(model); gboolean found = FALSE; g_return_if_fail(tmodel != NULL); if (key == NULL) { datasheet_fill(sheet, model); return; } rec = purple_request_datasheet_record_find(sheet, key); if (gtk_tree_model_get_iter_first(tmodel, &iter)) { do { gpointer ikey; gtk_tree_model_get(tmodel, &iter, 0, &ikey, -1); if (key == ikey) { found = TRUE; break; } } while (gtk_tree_model_iter_next(tmodel, &iter)); } if (rec == NULL && !found) return; if (rec == NULL) { gtk_list_store_remove(model, &iter); return; } if (!found) { gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, 0, key, -1); } datasheet_update_rec(rec, model, &iter); datasheet_selection_changed(GTK_WIDGET(g_object_get_data( G_OBJECT(model), "sheet-widget"))); } static void datasheet_selection_changed_cb(GtkTreeSelection *sel, gpointer sheet_widget) { datasheet_selection_changed(GTK_WIDGET(sheet_widget)); } static void datasheet_action_clicked(GtkButton *btn, PurpleRequestDatasheetAction *act) { GtkWidget *sheet_widget; sheet_widget = g_object_get_data(G_OBJECT(btn), "sheet-widget"); g_return_if_fail(sheet_widget != NULL); purple_request_datasheet_action_call(act, datasheet_get_selected_row( sheet_widget)); } static GtkWidget * create_datasheet_field(PurpleRequestField *field, GtkSizeGroup *buttons_sg) { PurpleRequestDatasheet *sheet; guint i, col_count; GType *col_types; GtkListStore *model; GtkTreeView *view; GtkTreeSelection *sel; GtkWidget *scrollable; GtkCellRenderer *renderer_image = NULL, *renderer_text = NULL; GtkTreeViewColumn *id_column; GtkWidget *main_box; GtkWidget *buttons_box; const GList *it; sheet = purple_request_field_datasheet_get_sheet(field); main_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); col_count = purple_request_datasheet_get_column_count(sheet); col_types = g_new0(GType, col_count + 1); col_types[0] = G_TYPE_POINTER; for (i = 0; i < col_count; i++) { PurpleRequestDatasheetColumnType type; type = purple_request_datasheet_get_column_type(sheet, i); if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING) col_types[i + 1] = G_TYPE_STRING; else if (type == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE) col_types[i + 1] = GDK_TYPE_PIXBUF; else g_warn_if_reached(); } model = gtk_list_store_newv(col_count + 1, col_types); g_free(col_types); view = GTK_TREE_VIEW(gtk_tree_view_new_with_model( GTK_TREE_MODEL(model))); g_object_set_data(G_OBJECT(model), "sheet-widget", main_box); g_object_unref(G_OBJECT(model)); id_column = gtk_tree_view_column_new(); gtk_tree_view_column_set_visible(id_column, FALSE); gtk_tree_view_append_column(view, id_column); for (i = 0; i < col_count; i++) { PurpleRequestDatasheetColumnType type; const gchar *title; GtkCellRenderer *renderer = NULL; const gchar *type_str = ""; type = purple_request_datasheet_get_column_type(sheet, i); title = purple_request_datasheet_get_column_title(sheet, i); if (type == PURPLE_REQUEST_DATASHEET_COLUMN_STRING) { type_str = "text"; if (!renderer_text) renderer_text = gtk_cell_renderer_text_new(); renderer = renderer_text; } else if (type == PURPLE_REQUEST_DATASHEET_COLUMN_IMAGE) { type_str = "pixbuf"; if (!renderer_image) renderer_image = gtk_cell_renderer_pixbuf_new(); renderer = renderer_image; } else g_warn_if_reached(); if (title == NULL) title = ""; gtk_tree_view_insert_column_with_attributes( view, -1, title, renderer, type_str, i + 1, NULL); } gtk_widget_set_size_request(GTK_WIDGET(view), 400, 250); scrollable = gtk_scrolled_window_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollable), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollable), GTK_WIDGET(view)); gtk_widget_set_hexpand(scrollable, TRUE); gtk_box_append(GTK_BOX(main_box), scrollable); buttons_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_size_group_add_widget(buttons_sg, buttons_box); gtk_box_append(GTK_BOX(main_box), buttons_box); it = purple_request_datasheet_get_actions(sheet); for (; it != NULL; it = g_list_next(it)) { PurpleRequestDatasheetAction *act = it->data; GtkButton *btn; const gchar *label; label = purple_request_datasheet_action_get_label(act); btn = GTK_BUTTON(gtk_button_new_with_label(label ? label : "")); g_object_set_data(G_OBJECT(btn), "action", act); g_object_set_data(G_OBJECT(btn), "sheet-widget", main_box); g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(datasheet_action_clicked), act); gtk_box_append(GTK_BOX(buttons_box), GTK_WIDGET(btn)); } g_object_set_data(G_OBJECT(main_box), "view", view); g_object_set_data(G_OBJECT(main_box), "buttons", buttons_box); g_object_set_data(G_OBJECT(main_box), "sheet", sheet); datasheet_fill(sheet, model); purple_signal_connect(sheet, "record-changed", pidgin_request_get_handle(), G_CALLBACK(datasheet_update), model); sel = gtk_tree_view_get_selection(view); g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(datasheet_selection_changed_cb), main_box); return main_box; } static void * pidgin_request_fields(const char *title, const char *primary, const char *secondary, PurpleRequestFields *fields, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data) { PidginRequestData *data; GtkWidget *win; GtkWidget *hbox, *vbox; GtkWidget *frame; GtkWidget *label; GtkWidget *grid; GtkWidget *button; GtkWidget *img; GtkWidget *content; GtkSizeGroup *sg, *datasheet_buttons_sg; GList *gl, *fl; PurpleRequestFieldGroup *group; PurpleRequestField *field; char *label_text; char *primary_esc, *secondary_esc; const gboolean compact = purple_request_cpar_is_compact(cpar); GSList *extra_actions; size_t i; gboolean ok_btn = (ok_text != NULL); data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_FIELDS; data->user_data = user_data; data->u.multifield.fields = fields; purple_request_fields_set_ui_data(fields, data); extra_actions = purple_request_cpar_get_extra_actions(cpar); data->cb_count = 2; data->cbs = g_new0(GCallback, 2); data->cbs[0] = ok_cb; data->cbs[1] = cancel_cb; data->dialog = win = pidgin_dialog_new(title, 12, "multifield", TRUE) ; g_signal_connect(G_OBJECT(win), "close-request", G_CALLBACK(destroy_multifield_cb), data); /* Setup the main horizontal box */ content = gtk_dialog_get_content_area(GTK_DIALOG(win)); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_append(GTK_BOX(content), hbox); /* Dialog icon. */ img = pidgin_request_dialog_icon(PURPLE_REQUEST_FIELDS, cpar); gtk_widget_set_halign(img, GTK_ALIGN_START); gtk_widget_set_valign(img, GTK_ALIGN_START); gtk_box_append(GTK_BOX(hbox), img); pidgin_request_add_help(GTK_DIALOG(win), cpar); i = 0; for (GSList *it = extra_actions; it != NULL; it = it->next) { PurpleKeyValuePair *extra_action = it->data; button = gtk_dialog_add_button(GTK_DIALOG(win), extra_action->key, i++); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(multifield_extra_cb), data); g_object_set_data(G_OBJECT(button), "extra-cb", extra_action->value); } /* Cancel button */ button = gtk_dialog_add_button(GTK_DIALOG(win), cancel_text, GTK_RESPONSE_CANCEL); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(multifield_cancel_cb), data); /* OK button */ if (!ok_btn) { gtk_window_set_default_widget(GTK_WINDOW(win), button); } else { button = gtk_dialog_add_button(GTK_DIALOG(win), ok_text, GTK_RESPONSE_OK); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(multifield_ok_cb), data); data->ok_button = button; gtk_window_set_default_widget(GTK_WINDOW(win), button); } pidgin_widget_decorate_account(hbox, purple_request_cpar_get_account(cpar)); /* Setup the vbox */ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); gtk_widget_set_hexpand(vbox, TRUE); gtk_box_append(GTK_BOX(hbox), vbox); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); datasheet_buttons_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); if(primary) { primary_esc = pidgin_request_escape(cpar, primary); label_text = g_strdup_printf( "<span weight=\"bold\" size=\"larger\">%s</span>", primary_esc); g_free(primary_esc); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), label_text); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_box_append(GTK_BOX(vbox), label); g_free(label_text); } if (secondary) { secondary_esc = pidgin_request_escape(cpar, secondary); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), secondary_esc); g_free(secondary_esc); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_box_append(GTK_BOX(vbox), label); } for (gl = purple_request_fields_get_groups(fields); gl != NULL; gl = gl->next) { GList *field_list; size_t field_count = 0; size_t cols = 1; size_t rows; size_t row_num = 0; gboolean contains_resizable = FALSE; group = gl->data; field_list = purple_request_field_group_get_fields(group); if(purple_request_field_group_get_title(group) != NULL) { frame = pidgin_make_frame(vbox, purple_request_field_group_get_title(group)); } else { frame = vbox; } field_count = g_list_length(field_list); rows = field_count; for (fl = field_list; fl != NULL; fl = fl->next) { PurpleRequestFieldType type; field = (PurpleRequestField *)fl->data; type = purple_request_field_get_field_type(field); if (type == PURPLE_REQUEST_FIELD_DATASHEET) contains_resizable = TRUE; if (type == PURPLE_REQUEST_FIELD_LABEL) { rows++; } else if ((type == PURPLE_REQUEST_FIELD_LIST) || (type == PURPLE_REQUEST_FIELD_STRING && purple_request_field_string_is_multiline(field))) { rows += 2; } else if (compact && type != PURPLE_REQUEST_FIELD_BOOLEAN) rows++; } grid = gtk_grid_new(); gtk_grid_set_row_spacing(GTK_GRID(grid), 6); gtk_grid_set_column_spacing(GTK_GRID(grid), 6); /* This box could be setup in a number of ways, so just set all of the * fill and expand properties instead of only setting the * minimums. */ g_object_set(G_OBJECT(grid), "hexpand", contains_resizable, "vexpand", contains_resizable, NULL); gtk_box_append(GTK_BOX(frame), grid); for (row_num = 0, fl = field_list; row_num < rows && fl != NULL; row_num++) { gboolean dummy_counter = TRUE; /* it's the same as loop above */ for (; dummy_counter && fl != NULL; dummy_counter = FALSE, fl = fl->next) { size_t col_offset = 0; PurpleRequestFieldType type; GtkWidget *widget = NULL; gchar *field_label; label = NULL; field = fl->data; if (!purple_request_field_is_visible(field)) { continue; } type = purple_request_field_get_field_type(field); field_label = pidgin_request_escape(cpar, purple_request_field_get_label(field)); if (type != PURPLE_REQUEST_FIELD_BOOLEAN && field_label) { char *text = NULL; if (field_label[strlen(field_label) - 1] != ':' && field_label[strlen(field_label) - 1] != '?' && type != PURPLE_REQUEST_FIELD_LABEL) { text = g_strdup_printf("%s:", field_label); } label = gtk_label_new(NULL); gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text ? text : field_label); g_free(text); gtk_widget_set_hexpand(label, TRUE); gtk_widget_set_vexpand(label, TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_size_group_add_widget(sg, label); if (type == PURPLE_REQUEST_FIELD_LABEL || type == PURPLE_REQUEST_FIELD_LIST || (type == PURPLE_REQUEST_FIELD_STRING && purple_request_field_string_is_multiline(field))) { gtk_grid_attach(GTK_GRID(grid), label, 0, row_num, 2 * cols, 1); row_num++; } else { gtk_grid_attach(GTK_GRID(grid), label, col_offset, row_num, 1, 1); } g_free(field_label); } widget = GTK_WIDGET(purple_request_field_get_ui_data(field)); if (widget == NULL) { if (type == PURPLE_REQUEST_FIELD_STRING) widget = create_string_field(field); else if (type == PURPLE_REQUEST_FIELD_INTEGER) widget = create_int_field(field); else if (type == PURPLE_REQUEST_FIELD_BOOLEAN) widget = create_bool_field(field, cpar); else if (type == PURPLE_REQUEST_FIELD_CHOICE) widget = create_choice_field(field, cpar); else if (type == PURPLE_REQUEST_FIELD_LIST) widget = create_list_field(field); else if (type == PURPLE_REQUEST_FIELD_IMAGE) widget = create_image_field(field); else if (type == PURPLE_REQUEST_FIELD_ACCOUNT) widget = create_account_field(field); else if (type == PURPLE_REQUEST_FIELD_DATASHEET) widget = create_datasheet_field(field, datasheet_buttons_sg); else continue; } gtk_widget_set_sensitive(widget, purple_request_field_is_sensitive(field)); if (label) gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget); gtk_widget_set_hexpand(widget, TRUE); gtk_widget_set_vexpand(widget, TRUE); gtk_widget_set_margin_start(widget, 5); gtk_widget_set_margin_end(widget, 5); if (type == PURPLE_REQUEST_FIELD_STRING && purple_request_field_string_is_multiline(field)) { gtk_grid_attach(GTK_GRID(grid), widget, 0, row_num, 2 * cols, 1); } else if (type == PURPLE_REQUEST_FIELD_LIST) { gtk_grid_attach(GTK_GRID(grid), widget, 0, row_num, 2 * cols, 1); } else if (type == PURPLE_REQUEST_FIELD_BOOLEAN) { gtk_grid_attach(GTK_GRID(grid), widget, col_offset, row_num, 1, 1); } else if (compact) { row_num++; gtk_grid_attach(GTK_GRID(grid), widget, 0, row_num, 2 * cols, 1); } else { gtk_grid_attach(GTK_GRID(grid), widget, 1, row_num, 2 * cols - 1, 1); } purple_request_field_set_ui_data(field, widget); } } } g_object_unref(sg); g_object_unref(datasheet_buttons_sg); if (!purple_request_fields_all_required_filled(fields)) gtk_widget_set_sensitive(data->ok_button, FALSE); if (!purple_request_fields_all_valid(fields)) gtk_widget_set_sensitive(data->ok_button, FALSE); pidgin_auto_parent_window(win); gtk_widget_show(win); return data; } static void file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *data) { GFile *current_path; if (response != GTK_RESPONSE_ACCEPT) { if (data->cbs[0] != NULL) ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL); purple_request_close(data->type, data); return; } data->u.file.name = gtk_file_chooser_get_current_name(GTK_FILE_CHOOSER(data->dialog)); current_path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(data->dialog)); if (current_path != NULL) { gchar *current_folder = g_file_get_path(current_path); if (data->u.file.savedialog) { purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", current_folder); } else { purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", current_folder); } g_free(current_folder); } if (data->cbs[1] != NULL) { ((PurpleRequestFileCb)data->cbs[1])(data->user_data, data->u.file.name); } purple_request_close(data->type, data); g_clear_object(¤t_path); } static void * pidgin_request_file(const char *title, const char *filename, gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data) { PidginRequestData *data; GtkFileChooserNative *filesel; #ifdef _WIN32 GFile *file = NULL; const gchar *current_folder; gboolean folder_set = FALSE; #endif data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_FILE; data->user_data = user_data; data->cb_count = 2; data->cbs = g_new0(GCallback, 2); data->cbs[0] = cancel_cb; data->cbs[1] = ok_cb; data->u.file.savedialog = savedialog; filesel = gtk_file_chooser_native_new( title ? title : (savedialog ? _("Save File...") : _("Open File...")), NULL, savedialog ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, savedialog ? _("_Save") : _("_Open"), _("_Cancel")); if ((filename != NULL) && (*filename != '\0')) { GFile *path = g_file_new_for_path(filename); if(savedialog) { gtk_file_chooser_set_file(GTK_FILE_CHOOSER(filesel), path, NULL); } else if (g_file_test(filename, G_FILE_TEST_EXISTS)) { gtk_file_chooser_set_file(GTK_FILE_CHOOSER(filesel), path, NULL); } g_object_unref(path); } #ifdef _WIN32 if (savedialog) { current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder"); } else { current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder"); } if ((filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS)) && (current_folder != NULL) && (*current_folder != '\0')) { file = g_file_new_for_path(current_folder); folder_set = gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), file, NULL); } if (!folder_set && (filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS))) { char *my_documents = wpurple_get_special_folder(CSIDL_PERSONAL); g_clear_object(&file); if (my_documents != NULL) { file = g_file_new_for_path(my_documents); gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filesel), file, NULL); g_free(my_documents); } } g_clear_object(&file); #endif g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response", G_CALLBACK(file_ok_check_if_exists_cb), data); #if 0 /* FIXME: Not implemented for native dialogs. */ pidgin_auto_parent_window(filesel); #endif data->dialog = filesel; gtk_native_dialog_show(GTK_NATIVE_DIALOG(filesel)); return (void *)data; } static void * pidgin_request_folder(const char *title, const char *dirname, GCallback ok_cb, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, void *user_data) { PidginRequestData *data; GtkFileChooserNative *dirsel; data = g_new0(PidginRequestData, 1); data->type = PURPLE_REQUEST_FOLDER; data->user_data = user_data; data->cb_count = 2; data->cbs = g_new0(GCallback, 2); data->cbs[0] = cancel_cb; data->cbs[1] = ok_cb; data->u.file.savedialog = FALSE; dirsel = gtk_file_chooser_native_new( title ? title : _("Select Folder..."), NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, _("_OK"), _("_Cancel")); if ((dirname != NULL) && (*dirname != '\0')) { GFile *path = g_file_new_for_path(dirname); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirsel), path, NULL); g_object_unref(path); } g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(dirsel)), "response", G_CALLBACK(file_ok_check_if_exists_cb), data); data->dialog = dirsel; #if 0 /* FIXME: Not implemented for native dialogs. */ pidgin_auto_parent_window(dirsel); #endif gtk_native_dialog_show(GTK_NATIVE_DIALOG(dirsel)); return (void *)data; } /* if request callback issues another request, it should be attached to the * primary request parent */ static void pidgin_window_detach_children(GtkWindow* win) { GList *it; GtkWindow *par; g_return_if_fail(win != NULL); par = gtk_window_get_transient_for(win); it = gtk_window_list_toplevels(); for (it = g_list_first(it); it != NULL; it = g_list_delete_link(it, it)) { GtkWindow *child = GTK_WINDOW(it->data); if (gtk_window_get_transient_for(child) != win) continue; if (gtk_window_get_destroy_with_parent(child)) { #ifdef _WIN32 /* XXX test/verify it: Win32 gtk ignores * gtk_window_set_destroy_with_parent(..., FALSE). */ gtk_window_set_transient_for(child, NULL); #endif continue; } gtk_window_set_transient_for(child, par); } } static void pidgin_close_request(PurpleRequestType type, void *ui_handle) { PidginRequestData *data = (PidginRequestData *)ui_handle; g_free(data->cbs); if (type == PURPLE_REQUEST_FILE || type == PURPLE_REQUEST_FOLDER) { /* Will be a GtkNativeDialog, not GtkDialog. */ g_object_unref(data->dialog); } else { pidgin_window_detach_children(GTK_WINDOW(data->dialog)); gtk_window_destroy(GTK_WINDOW(data->dialog)); } if (type == PURPLE_REQUEST_FIELDS) purple_request_fields_destroy(data->u.multifield.fields); else if (type == PURPLE_REQUEST_FILE) g_free(data->u.file.name); g_free(data); } GtkWindow * pidgin_request_get_dialog_window(void *ui_handle) { PidginRequestData *data = ui_handle; g_return_val_if_fail( purple_request_is_valid_ui_handle(data, NULL), NULL); if (data->type == PURPLE_REQUEST_FILE || data->type == PURPLE_REQUEST_FOLDER) { /* Not a GtkWidget, but a GtkFileChooserNative. Eventually this function * should not be needed, once we don't need to auto-parent. */ return NULL; } return GTK_WINDOW(data->dialog); } static PurpleRequestUiOps ops = { .features = PURPLE_REQUEST_FEATURE_HTML, .request_input = pidgin_request_input, .request_choice = pidgin_request_choice, .request_action = pidgin_request_action, .request_wait = pidgin_request_wait, .request_wait_update = pidgin_request_wait_update, .request_fields = pidgin_request_fields, .request_file = pidgin_request_file, .request_folder = pidgin_request_folder, .close_request = pidgin_close_request, }; PurpleRequestUiOps * pidgin_request_get_ui_ops(void) { return &ops; } void * pidgin_request_get_handle(void) { static int handle; return &handle; } static void pidgin_request_datasheet_stock_remove(gpointer obj) { if (obj == NULL) return; g_object_unref(obj); } void pidgin_request_init(void) { datasheet_stock = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, pidgin_request_datasheet_stock_remove); } void pidgin_request_uninit(void) { purple_signals_disconnect_by_handle(pidgin_request_get_handle()); g_hash_table_destroy(datasheet_stock); datasheet_stock = NULL; }