pidgin/pidgin

Re-design account manager as a GtkListBox

16 months ago, Elliott Sales de Andrade
f3090252de57
Re-design account manager as a GtkListBox

Moves to a nicely spaced `GtkListBox`. The avatar is implemented using `AdwAvatar` solely for the autogenerated colour for accounts without an icon to give them some uniqueness, but it could be moved to `PidginAvatar`.

As a followup, I'd probably move the Account Editors from a separate dialog to stack within the manager, similar to how the Plugin dialog works. Neither the previous manager nor this one prevents you from opening two editors for an account, but this would remove that issue as well.

Testing Done:
Toggled enabled/disabled to check that account status worked and was displayed. Added a few bogus accounts to confirm that errors are shown. Activated a row to show the editor. Hit the delete button and cancelled/approved and confirmed that account was kept/removed.

Reviewed at https://reviews.imfreedom.org/r/2079/
/* 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 "pidgincore.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)
{
PurpleContactInfo *info = NULL;
PurpleProtocol *protocol = NULL;
GtkWidget *image;
const gchar *icon_name = NULL;
if(!PURPLE_IS_ACCOUNT(account)) {
return;
}
info = PURPLE_CONTACT_INFO(account);
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_contact_info_get_username(info));
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(G_GNUC_UNUSED 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(G_GNUC_UNUSED 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(G_GNUC_UNUSED GtkDialog *dialog, G_GNUC_UNUSED gint id, G_GNUC_UNUSED 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
req_field_changed_common(G_GNUC_UNUSED 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
field_bool_cb(GtkCheckButton *button, PurpleRequestField *field)
{
purple_request_field_bool_set_value(field,
gtk_check_button_get_active(button));
req_field_changed_common(GTK_WIDGET(button), field);
}
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(G_GNUC_UNUSED GtkCheckButton *button, G_GNUC_UNUSED 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 *obj, G_GNUC_UNUSED GParamSpec *pspec, gpointer data)
{
PurpleRequestField *field = data;
PidginAccountChooser *chooser = PIDGIN_ACCOUNT_CHOOSER(obj);
purple_request_field_account_set_value(
field, pidgin_account_chooser_get_selected(chooser));
req_field_changed_common(GTK_WIDGET(obj), field);
}
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(G_GNUC_UNUSED 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(G_GNUC_UNUSED 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, G_GNUC_UNUSED 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(G_GNUC_UNUSED 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);
} else 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);
}
req_field_changed_common(entry, field);
}
static void
setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
{
const char *type_hint;
g_object_set(entry, "activates-default", TRUE, NULL);
g_signal_connect(entry, "changed", G_CALLBACK(req_entry_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(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(widget, "toggled", G_CALLBACK(field_bool_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 = NULL;
PurpleAccount *account = NULL;
PurpleFilterAccountFunc account_filter = NULL;
GtkFilter *filter = NULL;
widget = pidgin_account_chooser_new();
account = purple_request_field_account_get_default_value(field);
account_filter = purple_request_field_account_get_filter(field);
if(account_filter != NULL) {
GtkCustomFilter *custom_filter = NULL;
custom_filter = gtk_custom_filter_new(field_custom_account_filter_cb,
account_filter, NULL);
filter = GTK_FILTER(custom_filter);
}
if(!purple_request_field_account_get_show_all(field)) {
GtkEveryFilter *every = NULL;
every = gtk_every_filter_new();
if(GTK_IS_FILTER(filter)) {
gtk_multi_filter_append(GTK_MULTI_FILTER(every), filter);
}
filter = pidgin_account_filter_connected_new();
gtk_multi_filter_append(GTK_MULTI_FILTER(every), filter);
filter = GTK_FILTER(every);
}
pidgin_account_chooser_set_selected(PIDGIN_ACCOUNT_CHOOSER(widget),
account);
if(GTK_IS_FILTER(filter)) {
pidgin_account_chooser_set_filter(PIDGIN_ACCOUNT_CHOOSER(widget), filter);
g_object_unref(filter);
}
g_signal_connect(widget, "notify::account", G_CALLBACK(field_account_cb),
field);
gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
return widget;
}
static void
select_field_list_item(GtkTreeModel *model, G_GNUC_UNUSED 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);
}
#if 0
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)));
}
#endif
static void
datasheet_selection_changed(G_GNUC_UNUSED 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(G_GNUC_UNUSED 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 = gtk_dialog_new();
if(title != NULL) {
gtk_window_set_title(GTK_WINDOW(win), title);
} else {
gtk_window_set_title(GTK_WINDOW(win), PIDGIN_ALERT_TITLE);
}
gtk_window_set_resizable(GTK_WINDOW(win), 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(G_GNUC_UNUSED 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(&current_path);
}
static void *
pidgin_request_file(const char *title, const char *filename,
gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
gpointer 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,
G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
gpointer 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;
}