Change the oscar capabilities variable to be a guint64 everywhere instead
[pidgin-git.git] / pidgin / gtkrequest.c
blob3a164470f8263ebc495fea969831f4754f672226
1 /**
2 * @file gtkrequest.c GTK+ Request API
3 * @ingroup pidgin
4 */
6 /* pidgin
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include "internal.h"
27 #include "pidgin.h"
29 #include "prefs.h"
30 #include "util.h"
32 #include "gtkimhtml.h"
33 #include "gtkimhtmltoolbar.h"
34 #include "gtkrequest.h"
35 #include "gtkutils.h"
36 #include "pidginstock.h"
37 #include "gtkblist.h"
39 #include <gdk/gdkkeysyms.h>
41 static GtkWidget * create_account_field(PurpleRequestField *field);
43 typedef struct
45 PurpleRequestType type;
47 void *user_data;
48 GtkWidget *dialog;
50 GtkWidget *ok_button;
52 size_t cb_count;
53 GCallback *cbs;
55 union
57 struct
59 GtkWidget *entry;
61 gboolean multiline;
62 gchar *hint;
64 } input;
66 struct
68 PurpleRequestFields *fields;
70 } multifield;
72 struct
74 gboolean savedialog;
75 gchar *name;
77 } file;
79 } u;
81 } PidginRequestData;
83 static void
84 pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account)
86 GtkWidget *image;
87 GdkPixbuf *pixbuf;
88 GtkTooltips *tips;
90 if (!account)
91 return;
93 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
94 image = gtk_image_new_from_pixbuf(pixbuf);
95 g_object_unref(G_OBJECT(pixbuf));
97 tips = gtk_tooltips_new();
98 gtk_tooltips_set_tip(tips, image, purple_account_get_username(account), NULL);
100 if (GTK_IS_DIALOG(cont)) {
101 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cont)->action_area), image, FALSE, TRUE, 0);
102 gtk_box_reorder_child(GTK_BOX(GTK_DIALOG(cont)->action_area), image, 0);
103 } else if (GTK_IS_HBOX(cont)) {
104 gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
105 gtk_box_pack_end(GTK_BOX(cont), image, FALSE, TRUE, 0);
107 gtk_widget_show(image);
110 static void
111 generic_response_start(PidginRequestData *data)
113 g_return_if_fail(data != NULL);
115 /* Tell the user we're doing something. */
116 pidgin_set_cursor(GTK_WIDGET(data->dialog), GDK_WATCH);
119 static void
120 input_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
122 const char *value;
123 char *multiline_value = NULL;
125 generic_response_start(data);
127 if (data->u.input.multiline) {
128 GtkTextIter start_iter, end_iter;
129 GtkTextBuffer *buffer =
130 gtk_text_view_get_buffer(GTK_TEXT_VIEW(data->u.input.entry));
132 gtk_text_buffer_get_start_iter(buffer, &start_iter);
133 gtk_text_buffer_get_end_iter(buffer, &end_iter);
135 if ((data->u.input.hint != NULL) && (!strcmp(data->u.input.hint, "html")))
136 multiline_value = gtk_imhtml_get_markup(GTK_IMHTML(data->u.input.entry));
137 else
138 multiline_value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
139 FALSE);
141 value = multiline_value;
143 else
144 value = gtk_entry_get_text(GTK_ENTRY(data->u.input.entry));
146 if (id < data->cb_count && data->cbs[id] != NULL)
147 ((PurpleRequestInputCb)data->cbs[id])(data->user_data, value);
148 else if (data->cbs[1] != NULL)
149 ((PurpleRequestInputCb)data->cbs[1])(data->user_data, value);
151 if (data->u.input.multiline)
152 g_free(multiline_value);
154 purple_request_close(PURPLE_REQUEST_INPUT, data);
157 static void
158 action_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
160 generic_response_start(data);
162 if (id < data->cb_count && data->cbs[id] != NULL)
163 ((PurpleRequestActionCb)data->cbs[id])(data->user_data, id);
165 purple_request_close(PURPLE_REQUEST_INPUT, data);
169 static void
170 choice_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
172 GtkWidget *radio = g_object_get_data(G_OBJECT(dialog), "radio");
173 GSList *group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
175 generic_response_start(data);
177 if (id < data->cb_count && data->cbs[id] != NULL)
178 while (group) {
179 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(group->data))) {
180 ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(group->data), "choice_id")));
181 break;
183 group = group->next;
185 purple_request_close(PURPLE_REQUEST_INPUT, data);
188 static gboolean
189 field_string_focus_out_cb(GtkWidget *entry, GdkEventFocus *event,
190 PurpleRequestField *field)
192 const char *value;
194 if (purple_request_field_string_is_multiline(field))
196 GtkTextBuffer *buffer;
197 GtkTextIter start_iter, end_iter;
199 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
201 gtk_text_buffer_get_start_iter(buffer, &start_iter);
202 gtk_text_buffer_get_end_iter(buffer, &end_iter);
204 value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
206 else
207 value = gtk_entry_get_text(GTK_ENTRY(entry));
209 purple_request_field_string_set_value(field,
210 (*value == '\0' ? NULL : value));
212 return FALSE;
215 static gboolean
216 field_int_focus_out_cb(GtkEntry *entry, GdkEventFocus *event,
217 PurpleRequestField *field)
219 purple_request_field_int_set_value(field,
220 atoi(gtk_entry_get_text(entry)));
222 return FALSE;
225 static void
226 field_bool_cb(GtkToggleButton *button, PurpleRequestField *field)
228 purple_request_field_bool_set_value(field,
229 gtk_toggle_button_get_active(button));
232 static void
233 field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field)
235 purple_request_field_choice_set_value(field,
236 gtk_combo_box_get_active(menu));
239 static void
240 field_choice_option_cb(GtkRadioButton *button, PurpleRequestField *field)
242 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
243 purple_request_field_choice_set_value(field,
244 (g_slist_length(gtk_radio_button_get_group(button)) -
245 g_slist_index(gtk_radio_button_get_group(button), button)) - 1);
248 static void
249 field_account_cb(GObject *w, PurpleAccount *account, PurpleRequestField *field)
251 purple_request_field_account_set_value(field, account);
254 static void
255 multifield_ok_cb(GtkWidget *button, PidginRequestData *data)
257 generic_response_start(data);
259 if (!GTK_WIDGET_HAS_FOCUS(button))
260 gtk_widget_grab_focus(button);
262 if (data->cbs[0] != NULL)
263 ((PurpleRequestFieldsCb)data->cbs[0])(data->user_data,
264 data->u.multifield.fields);
266 purple_request_close(PURPLE_REQUEST_FIELDS, data);
269 static void
270 multifield_cancel_cb(GtkWidget *button, PidginRequestData *data)
272 generic_response_start(data);
274 if (data->cbs[1] != NULL)
275 ((PurpleRequestFieldsCb)data->cbs[1])(data->user_data,
276 data->u.multifield.fields);
278 purple_request_close(PURPLE_REQUEST_FIELDS, data);
281 static gboolean
282 destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event,
283 PidginRequestData *data)
285 multifield_cancel_cb(NULL, data);
286 return FALSE;
290 #define STOCK_ITEMIZE(r, l) \
291 if (!strcmp((r), text)) \
292 return (l);
294 static const char *
295 text_to_stock(const char *text)
297 STOCK_ITEMIZE(_("Yes"), GTK_STOCK_YES);
298 STOCK_ITEMIZE(_("No"), GTK_STOCK_NO);
299 STOCK_ITEMIZE(_("OK"), GTK_STOCK_OK);
300 STOCK_ITEMIZE(_("Cancel"), GTK_STOCK_CANCEL);
301 STOCK_ITEMIZE(_("Apply"), GTK_STOCK_APPLY);
302 STOCK_ITEMIZE(_("Close"), GTK_STOCK_CLOSE);
303 STOCK_ITEMIZE(_("Delete"), GTK_STOCK_DELETE);
304 STOCK_ITEMIZE(_("Add"), GTK_STOCK_ADD);
305 STOCK_ITEMIZE(_("Remove"), GTK_STOCK_REMOVE);
306 STOCK_ITEMIZE(_("Save"), GTK_STOCK_SAVE);
307 STOCK_ITEMIZE(_("Alias"), PIDGIN_STOCK_ALIAS);
309 return text;
312 static void *
313 pidgin_request_input(const char *title, const char *primary,
314 const char *secondary, const char *default_value,
315 gboolean multiline, gboolean masked, gchar *hint,
316 const char *ok_text, GCallback ok_cb,
317 const char *cancel_text, GCallback cancel_cb,
318 PurpleAccount *account, const char *who, PurpleConversation *conv,
319 void *user_data)
321 PidginRequestData *data;
322 GtkWidget *dialog;
323 GtkWidget *vbox;
324 GtkWidget *hbox;
325 GtkWidget *label;
326 GtkWidget *entry;
327 GtkWidget *img;
328 GtkWidget *toolbar;
329 char *label_text;
330 char *primary_esc, *secondary_esc;
332 data = g_new0(PidginRequestData, 1);
333 data->type = PURPLE_REQUEST_INPUT;
334 data->user_data = user_data;
336 data->cb_count = 2;
337 data->cbs = g_new0(GCallback, 2);
339 data->cbs[0] = ok_cb;
340 data->cbs[1] = cancel_cb;
342 /* Create the dialog. */
343 dialog = gtk_dialog_new_with_buttons(title ? title : PIDGIN_ALERT_TITLE,
344 NULL, 0,
345 text_to_stock(cancel_text), 1,
346 text_to_stock(ok_text), 0,
347 NULL);
348 data->dialog = dialog;
350 g_signal_connect(G_OBJECT(dialog), "response",
351 G_CALLBACK(input_response_cb), data);
353 /* Setup the dialog */
354 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
355 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
356 if (!multiline)
357 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
358 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
359 gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
360 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
362 /* Setup the main horizontal box */
363 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
364 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
366 /* Dialog icon. */
367 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
368 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
369 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
370 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
372 /* Vertical box */
373 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
375 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
377 pidgin_widget_decorate_account(hbox, account);
379 /* Descriptive label */
380 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
381 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
382 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
383 "%s</span>%s%s" : "%s%s%s"),
384 (primary ? primary_esc : ""),
385 ((primary && secondary) ? "\n\n" : ""),
386 (secondary ? secondary_esc : ""));
387 g_free(primary_esc);
388 g_free(secondary_esc);
390 label = gtk_label_new(NULL);
392 gtk_label_set_markup(GTK_LABEL(label), label_text);
393 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
394 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
395 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
397 g_free(label_text);
399 /* Entry field. */
400 data->u.input.multiline = multiline;
401 data->u.input.hint = g_strdup(hint);
403 gtk_widget_show_all(hbox);
405 if ((data->u.input.hint != NULL) && (!strcmp(data->u.input.hint, "html"))) {
406 GtkWidget *frame;
408 /* imhtml */
409 frame = pidgin_create_imhtml(TRUE, &entry, &toolbar, NULL);
410 gtk_widget_set_size_request(entry, 320, 130);
411 gtk_widget_set_name(entry, "pidgin_request_imhtml");
412 if (default_value != NULL)
413 gtk_imhtml_append_text(GTK_IMHTML(entry), default_value, GTK_IMHTML_NO_SCROLL);
414 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
415 gtk_widget_show(frame);
417 gtk_imhtml_set_return_inserts_newline(GTK_IMHTML(entry));
419 else {
420 if (multiline) {
421 GtkWidget *sw;
423 sw = gtk_scrolled_window_new(NULL, NULL);
424 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
425 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
426 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
427 GTK_SHADOW_IN);
429 gtk_widget_set_size_request(sw, 320, 130);
431 /* GtkTextView */
432 entry = gtk_text_view_new();
433 gtk_text_view_set_editable(GTK_TEXT_VIEW(entry), TRUE);
435 if (default_value != NULL) {
436 GtkTextBuffer *buffer;
438 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
439 gtk_text_buffer_set_text(buffer, default_value, -1);
442 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(entry), GTK_WRAP_WORD_CHAR);
444 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
446 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
447 pidgin_setup_gtkspell(GTK_TEXT_VIEW(entry));
449 gtk_container_add(GTK_CONTAINER(sw), entry);
451 else {
452 entry = gtk_entry_new();
454 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
456 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
458 if (default_value != NULL)
459 gtk_entry_set_text(GTK_ENTRY(entry), default_value);
461 if (masked)
463 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
464 #if !GTK_CHECK_VERSION(2,16,0)
465 if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
466 gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
467 #endif /* Less than GTK+ 2.16 */
470 gtk_widget_show_all(vbox);
473 pidgin_set_accessible_label (entry, label);
474 data->u.input.entry = entry;
476 pidgin_auto_parent_window(dialog);
478 /* Show everything. */
479 gtk_widget_show(dialog);
481 return data;
484 static void *
485 pidgin_request_choice(const char *title, const char *primary,
486 const char *secondary, int default_value,
487 const char *ok_text, GCallback ok_cb,
488 const char *cancel_text, GCallback cancel_cb,
489 PurpleAccount *account, const char *who, PurpleConversation *conv,
490 void *user_data, va_list args)
492 PidginRequestData *data;
493 GtkWidget *dialog;
494 GtkWidget *vbox, *vbox2;
495 GtkWidget *hbox;
496 GtkWidget *label;
497 GtkWidget *img;
498 GtkWidget *radio = NULL;
499 char *label_text;
500 char *radio_text;
501 char *primary_esc, *secondary_esc;
503 data = g_new0(PidginRequestData, 1);
504 data->type = PURPLE_REQUEST_ACTION;
505 data->user_data = user_data;
507 data->cb_count = 2;
508 data->cbs = g_new0(GCallback, 2);
509 data->cbs[0] = cancel_cb;
510 data->cbs[1] = ok_cb;
512 /* Create the dialog. */
513 data->dialog = dialog = gtk_dialog_new();
515 if (title != NULL)
516 gtk_window_set_title(GTK_WINDOW(dialog), title);
517 #ifdef _WIN32
518 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
519 #endif
521 gtk_dialog_add_button(GTK_DIALOG(dialog),
522 text_to_stock(cancel_text), 0);
524 gtk_dialog_add_button(GTK_DIALOG(dialog),
525 text_to_stock(ok_text), 1);
527 g_signal_connect(G_OBJECT(dialog), "response",
528 G_CALLBACK(choice_response_cb), data);
530 /* Setup the dialog */
531 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
532 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
533 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
534 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
535 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
537 /* Setup the main horizontal box */
538 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
539 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
541 /* Dialog icon. */
542 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
543 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
544 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
545 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
547 pidgin_widget_decorate_account(hbox, account);
549 /* Vertical box */
550 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
551 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
553 /* Descriptive label */
554 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
555 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
556 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
557 "%s</span>%s%s" : "%s%s%s"),
558 (primary ? primary_esc : ""),
559 ((primary && secondary) ? "\n\n" : ""),
560 (secondary ? secondary_esc : ""));
561 g_free(primary_esc);
562 g_free(secondary_esc);
564 label = gtk_label_new(NULL);
566 gtk_label_set_markup(GTK_LABEL(label), label_text);
567 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
568 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
569 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
571 g_free(label_text);
573 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
574 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
575 while ((radio_text = va_arg(args, char*))) {
576 int resp = va_arg(args, int);
577 radio = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio), radio_text);
578 gtk_box_pack_start(GTK_BOX(vbox2), radio, FALSE, FALSE, 0);
579 g_object_set_data(G_OBJECT(radio), "choice_id", GINT_TO_POINTER(resp));
580 if (resp == default_value)
581 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
584 g_object_set_data(G_OBJECT(dialog), "radio", radio);
586 /* Show everything. */
587 pidgin_auto_parent_window(dialog);
589 gtk_widget_show_all(dialog);
591 return data;
594 static void *
595 pidgin_request_action(const char *title, const char *primary,
596 const char *secondary, int default_action,
597 PurpleAccount *account, const char *who, PurpleConversation *conv,
598 void *user_data, size_t action_count, va_list actions)
600 PidginRequestData *data;
601 GtkWidget *dialog;
602 GtkWidget *vbox;
603 GtkWidget *hbox;
604 GtkWidget *label;
605 GtkWidget *img;
606 void **buttons;
607 char *label_text;
608 char *primary_esc, *secondary_esc;
609 int i;
611 data = g_new0(PidginRequestData, 1);
612 data->type = PURPLE_REQUEST_ACTION;
613 data->user_data = user_data;
615 data->cb_count = action_count;
616 data->cbs = g_new0(GCallback, action_count);
618 /* Reverse the buttons */
619 buttons = g_new0(void *, action_count * 2);
621 for (i = 0; i < action_count * 2; i += 2) {
622 buttons[(action_count * 2) - i - 2] = va_arg(actions, char *);
623 buttons[(action_count * 2) - i - 1] = va_arg(actions, GCallback);
626 /* Create the dialog. */
627 data->dialog = dialog = gtk_dialog_new();
629 gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE);
631 if (title != NULL)
632 gtk_window_set_title(GTK_WINDOW(dialog), title);
633 #ifdef _WIN32
634 else
635 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
636 #endif
638 for (i = 0; i < action_count; i++) {
639 gtk_dialog_add_button(GTK_DIALOG(dialog),
640 text_to_stock(buttons[2 * i]), i);
642 data->cbs[i] = buttons[2 * i + 1];
645 g_free(buttons);
647 g_signal_connect(G_OBJECT(dialog), "response",
648 G_CALLBACK(action_response_cb), data);
650 /* Setup the dialog */
651 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
652 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
653 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
654 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
655 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
657 /* Setup the main horizontal box */
658 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
659 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
661 /* Dialog icon. */
662 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
663 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
664 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
665 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
667 /* Vertical box */
668 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
669 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
671 pidgin_widget_decorate_account(hbox, account);
673 /* Descriptive label */
674 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
675 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
676 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
677 "%s</span>%s%s" : "%s%s%s"),
678 (primary ? primary_esc : ""),
679 ((primary && secondary) ? "\n\n" : ""),
680 (secondary ? secondary_esc : ""));
681 g_free(primary_esc);
682 g_free(secondary_esc);
684 label = gtk_label_new(NULL);
686 gtk_label_set_markup(GTK_LABEL(label), label_text);
687 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
688 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
689 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
690 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
692 g_free(label_text);
695 if (default_action == PURPLE_DEFAULT_ACTION_NONE) {
696 GTK_WIDGET_SET_FLAGS(img, GTK_CAN_DEFAULT);
697 GTK_WIDGET_SET_FLAGS(img, GTK_CAN_FOCUS);
698 gtk_widget_grab_focus(img);
699 gtk_widget_grab_default(img);
700 } else
702 * Need to invert the default_action number because the
703 * buttons are added to the dialog in reverse order.
705 gtk_dialog_set_default_response(GTK_DIALOG(dialog), action_count - 1 - default_action);
707 /* Show everything. */
708 pidgin_auto_parent_window(dialog);
710 gtk_widget_show_all(dialog);
712 return data;
715 static void
716 req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
718 PurpleRequestFieldGroup *group;
719 PidginRequestData *req_data;
721 if (purple_request_field_string_is_multiline(field))
723 char *text;
724 GtkTextIter start_iter, end_iter;
726 gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(entry), &start_iter);
727 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(entry), &end_iter);
729 text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(entry), &start_iter, &end_iter, FALSE);
730 purple_request_field_string_set_value(field, (!text || !*text) ? NULL : text);
731 g_free(text);
733 else
735 const char *text = NULL;
736 text = gtk_entry_get_text(GTK_ENTRY(entry));
737 purple_request_field_string_set_value(field, (*text == '\0') ? NULL : text);
740 group = purple_request_field_get_group(field);
741 req_data = (PidginRequestData *)group->fields_list->ui_data;
743 gtk_widget_set_sensitive(req_data->ok_button,
744 purple_request_fields_all_required_filled(group->fields_list));
747 static void
748 setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
750 const char *type_hint;
752 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
754 if (purple_request_field_is_required(field))
756 g_signal_connect(G_OBJECT(entry), "changed",
757 G_CALLBACK(req_entry_field_changed_cb), field);
760 if ((type_hint = purple_request_field_get_type_hint(field)) != NULL)
762 if (purple_str_has_prefix(type_hint, "screenname"))
764 GtkWidget *optmenu = NULL;
765 PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
766 GList *fields = group->fields;
768 /* Ensure the account option menu is created (if the widget hasn't
769 * been initialized already) for username auto-completion. */
770 while (fields)
772 PurpleRequestField *fld = fields->data;
773 fields = fields->next;
775 if (purple_request_field_get_type(fld) == PURPLE_REQUEST_FIELD_ACCOUNT &&
776 purple_request_field_is_visible(fld))
778 const char *type_hint = purple_request_field_get_type_hint(fld);
779 if (type_hint != NULL && strcmp(type_hint, "account") == 0)
781 optmenu = GTK_WIDGET(purple_request_field_get_ui_data(fld));
782 if (optmenu == NULL) {
783 optmenu = GTK_WIDGET(create_account_field(fld));
784 purple_request_field_set_ui_data(fld, optmenu);
786 break;
790 pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(!strcmp(type_hint, "screenname-all")));
795 static GtkWidget *
796 create_string_field(PurpleRequestField *field)
798 const char *value;
799 GtkWidget *widget;
801 value = purple_request_field_string_get_default_value(field);
803 if (purple_request_field_string_is_multiline(field))
805 GtkWidget *textview;
807 widget = gtk_scrolled_window_new(NULL, NULL);
808 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget),
809 GTK_SHADOW_IN);
810 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
811 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
813 textview = gtk_text_view_new();
814 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
815 TRUE);
816 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview),
817 GTK_WRAP_WORD_CHAR);
819 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
820 pidgin_setup_gtkspell(GTK_TEXT_VIEW(textview));
822 gtk_container_add(GTK_CONTAINER(widget), textview);
823 gtk_widget_show(textview);
825 gtk_widget_set_size_request(widget, -1, 75);
827 if (value != NULL)
829 GtkTextBuffer *buffer;
831 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
833 gtk_text_buffer_set_text(buffer, value, -1);
836 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
837 purple_request_field_string_is_editable(field));
839 g_signal_connect(G_OBJECT(textview), "focus-out-event",
840 G_CALLBACK(field_string_focus_out_cb), field);
842 if (purple_request_field_is_required(field))
844 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
845 g_signal_connect(G_OBJECT(buffer), "changed",
846 G_CALLBACK(req_entry_field_changed_cb), field);
849 else
851 widget = gtk_entry_new();
853 setup_entry_field(widget, field);
855 if (value != NULL)
856 gtk_entry_set_text(GTK_ENTRY(widget), value);
858 if (purple_request_field_string_is_masked(field))
860 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
861 #if !GTK_CHECK_VERSION(2,16,0)
862 if (gtk_entry_get_invisible_char(GTK_ENTRY(widget)) == '*')
863 gtk_entry_set_invisible_char(GTK_ENTRY(widget), PIDGIN_INVISIBLE_CHAR);
864 #endif /* Less than GTK+ 2.16 */
867 gtk_editable_set_editable(GTK_EDITABLE(widget),
868 purple_request_field_string_is_editable(field));
870 g_signal_connect(G_OBJECT(widget), "focus-out-event",
871 G_CALLBACK(field_string_focus_out_cb), field);
874 return widget;
877 static GtkWidget *
878 create_int_field(PurpleRequestField *field)
880 int value;
881 GtkWidget *widget;
883 widget = gtk_entry_new();
885 setup_entry_field(widget, field);
887 value = purple_request_field_int_get_default_value(field);
889 if (value != 0)
891 char buf[32];
893 g_snprintf(buf, sizeof(buf), "%d", value);
895 gtk_entry_set_text(GTK_ENTRY(widget), buf);
898 g_signal_connect(G_OBJECT(widget), "focus-out-event",
899 G_CALLBACK(field_int_focus_out_cb), field);
901 return widget;
904 static GtkWidget *
905 create_bool_field(PurpleRequestField *field)
907 GtkWidget *widget;
909 widget = gtk_check_button_new_with_label(
910 purple_request_field_get_label(field));
912 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
913 purple_request_field_bool_get_default_value(field));
915 g_signal_connect(G_OBJECT(widget), "toggled",
916 G_CALLBACK(field_bool_cb), field);
918 return widget;
921 static GtkWidget *
922 create_choice_field(PurpleRequestField *field)
924 GtkWidget *widget;
925 GList *labels = purple_request_field_choice_get_labels(field);
926 int num_labels = g_list_length(labels);
927 GList *l;
929 if (num_labels > 5)
931 widget = gtk_combo_box_new_text();
933 for (l = labels; l != NULL; l = l->next)
935 const char *text = l->data;
936 gtk_combo_box_append_text(GTK_COMBO_BOX(widget), text);
939 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
940 purple_request_field_choice_get_default_value(field));
942 g_signal_connect(G_OBJECT(widget), "changed",
943 G_CALLBACK(field_choice_menu_cb), field);
945 else
947 GtkWidget *box;
948 GtkWidget *first_radio = NULL;
949 GtkWidget *radio;
950 gint i;
952 if (num_labels == 2)
953 box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
954 else
955 box = gtk_vbox_new(FALSE, 0);
957 widget = box;
959 for (l = labels, i = 0; l != NULL; l = l->next, i++)
961 const char *text = l->data;
963 radio = gtk_radio_button_new_with_label_from_widget(
964 GTK_RADIO_BUTTON(first_radio), text);
966 if (first_radio == NULL)
967 first_radio = radio;
969 if (i == purple_request_field_choice_get_default_value(field))
970 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
972 gtk_box_pack_start(GTK_BOX(box), radio, TRUE, TRUE, 0);
973 gtk_widget_show(radio);
975 g_signal_connect(G_OBJECT(radio), "toggled",
976 G_CALLBACK(field_choice_option_cb), field);
980 return widget;
983 static GtkWidget *
984 create_image_field(PurpleRequestField *field)
986 GtkWidget *widget;
987 GdkPixbuf *buf, *scale;
988 GdkPixbufLoader *loader;
990 loader = gdk_pixbuf_loader_new();
991 gdk_pixbuf_loader_write(loader,
992 (const guchar *)purple_request_field_image_get_buffer(field),
993 purple_request_field_image_get_size(field),
994 NULL);
995 gdk_pixbuf_loader_close(loader, NULL);
996 buf = gdk_pixbuf_loader_get_pixbuf(loader);
998 scale = gdk_pixbuf_scale_simple(buf,
999 purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf),
1000 purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf),
1001 GDK_INTERP_BILINEAR);
1002 widget = gtk_image_new_from_pixbuf(scale);
1003 g_object_unref(G_OBJECT(loader));
1004 g_object_unref(G_OBJECT(scale));
1006 return widget;
1009 static GtkWidget *
1010 create_account_field(PurpleRequestField *field)
1012 GtkWidget *widget;
1014 widget = pidgin_account_option_menu_new(
1015 purple_request_field_account_get_default_value(field),
1016 purple_request_field_account_get_show_all(field),
1017 G_CALLBACK(field_account_cb),
1018 purple_request_field_account_get_filter(field),
1019 field);
1021 return widget;
1024 static void
1025 select_field_list_item(GtkTreeModel *model, GtkTreePath *path,
1026 GtkTreeIter *iter, gpointer data)
1028 PurpleRequestField *field = (PurpleRequestField *)data;
1029 char *text;
1031 gtk_tree_model_get(model, iter, 1, &text, -1);
1033 purple_request_field_list_add_selected(field, text);
1034 g_free(text);
1037 static void
1038 list_field_select_changed_cb(GtkTreeSelection *sel, PurpleRequestField *field)
1040 purple_request_field_list_clear_selected(field);
1042 gtk_tree_selection_selected_foreach(sel, select_field_list_item, field);
1045 static GtkWidget *
1046 create_list_field(PurpleRequestField *field)
1048 GtkWidget *sw;
1049 GtkWidget *treeview;
1050 GtkListStore *store;
1051 GtkCellRenderer *renderer;
1052 GtkTreeSelection *sel;
1053 GtkTreeViewColumn *column;
1054 GtkTreeIter iter;
1055 GList *l;
1056 GList *icons = NULL;
1058 icons = purple_request_field_list_get_icons(field);
1060 /* Create the scrolled window */
1061 sw = gtk_scrolled_window_new(NULL, NULL);
1062 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1063 GTK_POLICY_AUTOMATIC,
1064 GTK_POLICY_AUTOMATIC);
1065 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1066 GTK_SHADOW_IN);
1067 gtk_widget_show(sw);
1069 /* Create the list store */
1070 if (icons)
1071 store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF);
1072 else
1073 store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
1075 /* Create the tree view */
1076 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1077 g_object_unref(G_OBJECT(store));
1078 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
1080 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1082 if (purple_request_field_list_get_multi_select(field))
1083 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
1085 column = gtk_tree_view_column_new();
1086 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
1088 renderer = gtk_cell_renderer_text_new();
1089 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1090 gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
1092 if (icons)
1094 renderer = gtk_cell_renderer_pixbuf_new();
1095 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1096 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2);
1098 gtk_widget_set_size_request(treeview, 200, 400);
1101 for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next)
1103 const char *text = (const char *)l->data;
1105 gtk_list_store_append(store, &iter);
1107 if (icons)
1109 const char *icon_path = (const char *)icons->data;
1110 GdkPixbuf* pixbuf = NULL;
1112 if (icon_path)
1113 pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL);
1115 gtk_list_store_set(store, &iter,
1116 0, purple_request_field_list_get_data(field, text),
1117 1, text,
1118 2, pixbuf,
1119 -1);
1120 icons = icons->next;
1122 else
1123 gtk_list_store_set(store, &iter,
1124 0, purple_request_field_list_get_data(field, text),
1125 1, text,
1126 -1);
1128 if (purple_request_field_list_is_selected(field, text))
1129 gtk_tree_selection_select_iter(sel, &iter);
1133 * We only want to catch changes made by the user, so it's important
1134 * that we wait until after the list is created to connect this
1135 * handler. If we connect the handler before the loop above and
1136 * there are multiple items selected, then selecting the first iter
1137 * in the tree causes list_field_select_changed_cb to be triggered
1138 * which clears out the rest of the list of selected items.
1140 g_signal_connect(G_OBJECT(sel), "changed",
1141 G_CALLBACK(list_field_select_changed_cb), field);
1143 gtk_container_add(GTK_CONTAINER(sw), treeview);
1144 gtk_widget_show(treeview);
1146 return sw;
1149 static void *
1150 pidgin_request_fields(const char *title, const char *primary,
1151 const char *secondary, PurpleRequestFields *fields,
1152 const char *ok_text, GCallback ok_cb,
1153 const char *cancel_text, GCallback cancel_cb,
1154 PurpleAccount *account, const char *who, PurpleConversation *conv,
1155 void *user_data)
1157 PidginRequestData *data;
1158 GtkWidget *win;
1159 GtkWidget *vbox;
1160 GtkWidget *vbox2;
1161 GtkWidget *hbox;
1162 GtkWidget *frame;
1163 GtkWidget *label;
1164 GtkWidget *table;
1165 GtkWidget *button;
1166 GtkWidget *img;
1167 GtkWidget *sw;
1168 GtkSizeGroup *sg;
1169 GList *gl, *fl;
1170 PurpleRequestFieldGroup *group;
1171 PurpleRequestField *field;
1172 char *label_text;
1173 char *primary_esc, *secondary_esc;
1174 int total_fields = 0;
1176 data = g_new0(PidginRequestData, 1);
1177 data->type = PURPLE_REQUEST_FIELDS;
1178 data->user_data = user_data;
1179 data->u.multifield.fields = fields;
1181 fields->ui_data = data;
1183 data->cb_count = 2;
1184 data->cbs = g_new0(GCallback, 2);
1186 data->cbs[0] = ok_cb;
1187 data->cbs[1] = cancel_cb;
1190 #ifdef _WIN32
1191 data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
1192 #else /* !_WIN32 */
1193 data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
1194 #endif /* _WIN32 */
1196 g_signal_connect(G_OBJECT(win), "delete_event",
1197 G_CALLBACK(destroy_multifield_cb), data);
1199 /* Setup the main horizontal box */
1200 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
1201 gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
1202 gtk_widget_show(hbox);
1204 /* Dialog icon. */
1205 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
1206 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
1207 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
1208 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
1209 gtk_widget_show(img);
1211 /* Cancel button */
1212 button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
1213 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1215 /* OK button */
1216 button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
1217 data->ok_button = button;
1218 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1219 gtk_window_set_default(GTK_WINDOW(win), button);
1221 pidgin_widget_decorate_account(hbox, account);
1223 /* Setup the vbox */
1224 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1225 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1226 gtk_widget_show(vbox);
1228 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1230 if(primary) {
1231 primary_esc = g_markup_escape_text(primary, -1);
1232 label_text = g_strdup_printf(
1233 "<span weight=\"bold\" size=\"larger\">%s</span>", primary_esc);
1234 g_free(primary_esc);
1235 label = gtk_label_new(NULL);
1237 gtk_label_set_markup(GTK_LABEL(label), label_text);
1238 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1239 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1240 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1241 gtk_widget_show(label);
1242 g_free(label_text);
1245 for (gl = purple_request_fields_get_groups(fields); gl != NULL;
1246 gl = gl->next)
1247 total_fields += g_list_length(purple_request_field_group_get_fields(gl->data));
1249 if(total_fields > 9) {
1250 GtkWidget *hbox_for_spacing, *vbox_for_spacing;
1252 sw = gtk_scrolled_window_new(NULL, NULL);
1253 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1254 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1255 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1256 GTK_SHADOW_NONE);
1257 gtk_widget_set_size_request(sw, -1, 200);
1258 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
1259 gtk_widget_show(sw);
1261 hbox_for_spacing = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
1262 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw),
1263 hbox_for_spacing);
1264 gtk_widget_show(hbox_for_spacing);
1266 vbox_for_spacing = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1267 gtk_box_pack_start(GTK_BOX(hbox_for_spacing),
1268 vbox_for_spacing, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
1269 gtk_widget_show(vbox_for_spacing);
1271 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1272 gtk_box_pack_start(GTK_BOX(vbox_for_spacing),
1273 vbox2, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
1274 gtk_widget_show(vbox2);
1275 } else {
1276 vbox2 = vbox;
1279 if (secondary) {
1280 secondary_esc = g_markup_escape_text(secondary, -1);
1281 label = gtk_label_new(NULL);
1283 gtk_label_set_markup(GTK_LABEL(label), secondary_esc);
1284 g_free(secondary_esc);
1285 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1286 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1287 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
1288 gtk_widget_show(label);
1291 for (gl = purple_request_fields_get_groups(fields);
1292 gl != NULL;
1293 gl = gl->next)
1295 GList *field_list;
1296 size_t field_count = 0;
1297 size_t cols = 1;
1298 size_t rows;
1299 size_t col_num;
1300 size_t row_num = 0;
1302 group = gl->data;
1303 field_list = purple_request_field_group_get_fields(group);
1305 if (purple_request_field_group_get_title(group) != NULL)
1307 frame = pidgin_make_frame(vbox2,
1308 purple_request_field_group_get_title(group));
1310 else
1311 frame = vbox2;
1313 field_count = g_list_length(field_list);
1315 if (field_count > 9)
1317 rows = field_count / 2;
1318 cols++;
1320 else
1322 rows = field_count;
1324 col_num = 0;
1326 for (fl = field_list; fl != NULL; fl = fl->next)
1328 PurpleRequestFieldType type;
1330 field = (PurpleRequestField *)fl->data;
1332 type = purple_request_field_get_type(field);
1334 if (type == PURPLE_REQUEST_FIELD_LABEL)
1336 if (col_num > 0)
1337 rows++;
1339 rows++;
1341 else if ((type == PURPLE_REQUEST_FIELD_LIST) ||
1342 (type == PURPLE_REQUEST_FIELD_STRING &&
1343 purple_request_field_string_is_multiline(field)))
1345 if (col_num > 0)
1346 rows++;
1348 rows += 2;
1351 col_num++;
1353 if (col_num >= cols)
1354 col_num = 0;
1357 table = gtk_table_new(rows, 2 * cols, FALSE);
1358 gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
1359 gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
1361 gtk_container_add(GTK_CONTAINER(frame), table);
1362 gtk_widget_show(table);
1364 for (row_num = 0, fl = field_list;
1365 row_num < rows && fl != NULL;
1366 row_num++)
1368 for (col_num = 0;
1369 col_num < cols && fl != NULL;
1370 col_num++, fl = fl->next)
1372 size_t col_offset = col_num * 2;
1373 PurpleRequestFieldType type;
1374 GtkWidget *widget = NULL;
1375 const char *field_label;
1377 label = NULL;
1378 field = fl->data;
1380 if (!purple_request_field_is_visible(field)) {
1381 col_num--;
1382 continue;
1385 type = purple_request_field_get_type(field);
1386 field_label = purple_request_field_get_label(field);
1388 if (type != PURPLE_REQUEST_FIELD_BOOLEAN && field_label)
1390 char *text = NULL;
1392 if (field_label[strlen(field_label) - 1] != ':')
1393 text = g_strdup_printf("%s:", field_label);
1395 label = gtk_label_new(NULL);
1396 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text ? text : field_label);
1397 g_free(text);
1399 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1401 gtk_size_group_add_widget(sg, label);
1403 if (type == PURPLE_REQUEST_FIELD_LABEL ||
1404 type == PURPLE_REQUEST_FIELD_LIST ||
1405 (type == PURPLE_REQUEST_FIELD_STRING &&
1406 purple_request_field_string_is_multiline(field)))
1408 if(col_num > 0)
1409 row_num++;
1411 gtk_table_attach_defaults(GTK_TABLE(table), label,
1412 0, 2 * cols,
1413 row_num, row_num + 1);
1415 row_num++;
1416 col_num=cols;
1418 else
1420 gtk_table_attach_defaults(GTK_TABLE(table), label,
1421 col_offset, col_offset + 1,
1422 row_num, row_num + 1);
1425 gtk_widget_show(label);
1428 widget = GTK_WIDGET(purple_request_field_get_ui_data(field));
1429 if (widget == NULL)
1431 if (type == PURPLE_REQUEST_FIELD_STRING)
1432 widget = create_string_field(field);
1433 else if (type == PURPLE_REQUEST_FIELD_INTEGER)
1434 widget = create_int_field(field);
1435 else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
1436 widget = create_bool_field(field);
1437 else if (type == PURPLE_REQUEST_FIELD_CHOICE)
1438 widget = create_choice_field(field);
1439 else if (type == PURPLE_REQUEST_FIELD_LIST)
1440 widget = create_list_field(field);
1441 else if (type == PURPLE_REQUEST_FIELD_IMAGE)
1442 widget = create_image_field(field);
1443 else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
1444 widget = create_account_field(field);
1445 else
1446 continue;
1449 if (label)
1450 gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
1452 if (type == PURPLE_REQUEST_FIELD_STRING &&
1453 purple_request_field_string_is_multiline(field))
1455 gtk_table_attach(GTK_TABLE(table), widget,
1456 0, 2 * cols,
1457 row_num, row_num + 1,
1458 GTK_FILL | GTK_EXPAND,
1459 GTK_FILL | GTK_EXPAND,
1460 5, 0);
1462 else if (type == PURPLE_REQUEST_FIELD_LIST)
1464 gtk_table_attach(GTK_TABLE(table), widget,
1465 0, 2 * cols,
1466 row_num, row_num + 1,
1467 GTK_FILL | GTK_EXPAND,
1468 GTK_FILL | GTK_EXPAND,
1469 5, 0);
1471 else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
1473 gtk_table_attach(GTK_TABLE(table), widget,
1474 col_offset, col_offset + 1,
1475 row_num, row_num + 1,
1476 GTK_FILL | GTK_EXPAND,
1477 GTK_FILL | GTK_EXPAND,
1478 5, 0);
1480 else
1482 gtk_table_attach(GTK_TABLE(table), widget,
1483 1, 2 * cols,
1484 row_num, row_num + 1,
1485 GTK_FILL | GTK_EXPAND,
1486 GTK_FILL | GTK_EXPAND,
1487 5, 0);
1490 gtk_widget_show(widget);
1492 purple_request_field_set_ui_data(field, widget);
1497 g_object_unref(sg);
1499 if (!purple_request_fields_all_required_filled(fields))
1500 gtk_widget_set_sensitive(data->ok_button, FALSE);
1502 pidgin_auto_parent_window(win);
1504 gtk_widget_show(win);
1506 return data;
1509 static void
1510 file_yes_no_cb(PidginRequestData *data, gint id)
1512 /* Only call the callback if yes was selected, otherwise the request
1513 * (eg. file transfer) will be cancelled, then when a new filename is chosen
1514 * things go BOOM */
1515 if (id == 1) {
1516 if (data->cbs[1] != NULL)
1517 ((PurpleRequestFileCb)data->cbs[1])(data->user_data, data->u.file.name);
1518 purple_request_close(data->type, data);
1519 } else {
1520 pidgin_clear_cursor(GTK_WIDGET(data->dialog));
1524 static void
1525 file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *data)
1527 gchar *current_folder;
1529 generic_response_start(data);
1531 if (response != GTK_RESPONSE_ACCEPT) {
1532 if (data->cbs[0] != NULL)
1533 ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
1534 purple_request_close(data->type, data);
1535 return;
1538 data->u.file.name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data->dialog));
1539 current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(data->dialog));
1540 if (current_folder != NULL) {
1541 if (data->u.file.savedialog) {
1542 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", current_folder);
1543 } else {
1544 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", current_folder);
1546 g_free(current_folder);
1548 if ((data->u.file.savedialog == TRUE) &&
1549 (g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
1550 purple_request_action(data, NULL, _("That file already exists"),
1551 _("Would you like to overwrite it?"), 0,
1552 NULL, NULL, NULL,
1553 data, 2,
1554 _("Overwrite"), G_CALLBACK(file_yes_no_cb),
1555 _("Choose New Name"), G_CALLBACK(file_yes_no_cb));
1556 } else
1557 file_yes_no_cb(data, 1);
1560 static void *
1561 pidgin_request_file(const char *title, const char *filename,
1562 gboolean savedialog,
1563 GCallback ok_cb, GCallback cancel_cb,
1564 PurpleAccount *account, const char *who, PurpleConversation *conv,
1565 void *user_data)
1567 PidginRequestData *data;
1568 GtkWidget *filesel;
1569 const gchar *current_folder;
1570 gboolean folder_set = FALSE;
1572 data = g_new0(PidginRequestData, 1);
1573 data->type = PURPLE_REQUEST_FILE;
1574 data->user_data = user_data;
1575 data->cb_count = 2;
1576 data->cbs = g_new0(GCallback, 2);
1577 data->cbs[0] = cancel_cb;
1578 data->cbs[1] = ok_cb;
1579 data->u.file.savedialog = savedialog;
1581 filesel = gtk_file_chooser_dialog_new(
1582 title ? title : (savedialog ? _("Save File...")
1583 : _("Open File...")),
1584 NULL,
1585 savedialog ? GTK_FILE_CHOOSER_ACTION_SAVE
1586 : GTK_FILE_CHOOSER_ACTION_OPEN,
1587 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1588 savedialog ? GTK_STOCK_SAVE
1589 : GTK_STOCK_OPEN,
1590 GTK_RESPONSE_ACCEPT,
1591 NULL);
1592 gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT);
1594 if (savedialog) {
1595 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
1596 } else {
1597 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
1600 if ((filename != NULL) && (*filename != '\0')) {
1601 if (savedialog)
1602 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filesel), filename);
1603 else if (g_file_test(filename, G_FILE_TEST_EXISTS))
1604 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filesel), filename);
1606 if ((filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS)) &&
1607 (current_folder != NULL) && (*current_folder != '\0')) {
1608 folder_set = gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), current_folder);
1611 #ifdef _WIN32
1612 if (!folder_set && (filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS))) {
1613 char *my_documents = wpurple_get_special_folder(CSIDL_PERSONAL);
1615 if (my_documents != NULL) {
1616 gtk_file_chooser_set_current_folder(
1617 GTK_FILE_CHOOSER(filesel), my_documents);
1619 g_free(my_documents);
1623 #endif
1624 g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response",
1625 G_CALLBACK(file_ok_check_if_exists_cb), data);
1627 pidgin_auto_parent_window(filesel);
1629 data->dialog = filesel;
1630 gtk_widget_show(filesel);
1632 return (void *)data;
1635 static void *
1636 pidgin_request_folder(const char *title, const char *dirname,
1637 GCallback ok_cb, GCallback cancel_cb,
1638 PurpleAccount *account, const char *who, PurpleConversation *conv,
1639 void *user_data)
1641 PidginRequestData *data;
1642 GtkWidget *dirsel;
1644 data = g_new0(PidginRequestData, 1);
1645 data->type = PURPLE_REQUEST_FOLDER;
1646 data->user_data = user_data;
1647 data->cb_count = 2;
1648 data->cbs = g_new0(GCallback, 2);
1649 data->cbs[0] = cancel_cb;
1650 data->cbs[1] = ok_cb;
1651 data->u.file.savedialog = FALSE;
1653 dirsel = gtk_file_chooser_dialog_new(
1654 title ? title : _("Select Folder..."),
1655 NULL,
1656 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1657 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1658 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1659 NULL);
1660 gtk_dialog_set_default_response(GTK_DIALOG(dirsel), GTK_RESPONSE_ACCEPT);
1662 if ((dirname != NULL) && (*dirname != '\0'))
1663 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirsel), dirname);
1665 g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(dirsel)), "response",
1666 G_CALLBACK(file_ok_check_if_exists_cb), data);
1668 data->dialog = dirsel;
1669 pidgin_auto_parent_window(dirsel);
1671 gtk_widget_show(dirsel);
1673 return (void *)data;
1676 static void
1677 pidgin_close_request(PurpleRequestType type, void *ui_handle)
1679 PidginRequestData *data = (PidginRequestData *)ui_handle;
1681 g_free(data->cbs);
1683 gtk_widget_destroy(data->dialog);
1685 if (type == PURPLE_REQUEST_FIELDS)
1686 purple_request_fields_destroy(data->u.multifield.fields);
1687 else if (type == PURPLE_REQUEST_FILE)
1688 g_free(data->u.file.name);
1690 g_free(data);
1693 static PurpleRequestUiOps ops =
1695 pidgin_request_input,
1696 pidgin_request_choice,
1697 pidgin_request_action,
1698 pidgin_request_fields,
1699 pidgin_request_file,
1700 pidgin_close_request,
1701 pidgin_request_folder,
1702 NULL,
1703 NULL,
1704 NULL,
1705 NULL
1708 PurpleRequestUiOps *
1709 pidgin_request_get_ui_ops(void)
1711 return &ops;