Use g_strcmp0() for code simplification
[pidgin-git.git] / pidgin / gtkrequest.c
blobf9d93c44e7eefe96c3130e9f4f3ce2bf903e1120
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 "debug.h"
30 #include "prefs.h"
31 #include "util.h"
33 #include "gtkimhtml.h"
34 #include "gtkimhtmltoolbar.h"
35 #include "gtkrequest.h"
36 #include "gtkutils.h"
37 #include "pidginstock.h"
38 #include "gtkblist.h"
40 #include <gdk/gdkkeysyms.h>
42 static GtkWidget * create_account_field(PurpleRequestField *field);
44 typedef struct
46 PurpleRequestType type;
48 void *user_data;
49 GtkWidget *dialog;
51 GtkWidget *ok_button;
53 size_t cb_count;
54 GCallback *cbs;
56 union
58 struct
60 GtkWidget *entry;
62 gboolean multiline;
63 gchar *hint;
65 } input;
67 struct
69 PurpleRequestFields *fields;
71 } multifield;
73 struct
75 gboolean savedialog;
76 gchar *name;
78 } file;
80 } u;
82 } PidginRequestData;
84 static void
85 pidgin_widget_decorate_account(GtkWidget *cont, PurpleAccount *account)
87 GtkWidget *image;
88 GdkPixbuf *pixbuf;
89 GtkTooltips *tips;
91 if (!account)
92 return;
94 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
95 image = gtk_image_new_from_pixbuf(pixbuf);
96 g_object_unref(G_OBJECT(pixbuf));
98 tips = gtk_tooltips_new();
99 gtk_tooltips_set_tip(tips, image, purple_account_get_username(account), NULL);
101 if (GTK_IS_DIALOG(cont)) {
102 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cont)->action_area), image, FALSE, TRUE, 0);
103 gtk_box_reorder_child(GTK_BOX(GTK_DIALOG(cont)->action_area), image, 0);
104 } else if (GTK_IS_HBOX(cont)) {
105 gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
106 gtk_box_pack_end(GTK_BOX(cont), image, FALSE, TRUE, 0);
108 gtk_widget_show(image);
111 static void
112 generic_response_start(PidginRequestData *data)
114 g_return_if_fail(data != NULL);
116 /* Tell the user we're doing something. */
117 pidgin_set_cursor(GTK_WIDGET(data->dialog), GDK_WATCH);
120 static void
121 input_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
123 const char *value;
124 char *multiline_value = NULL;
126 generic_response_start(data);
128 if (data->u.input.multiline) {
129 GtkTextIter start_iter, end_iter;
130 GtkTextBuffer *buffer =
131 gtk_text_view_get_buffer(GTK_TEXT_VIEW(data->u.input.entry));
133 gtk_text_buffer_get_start_iter(buffer, &start_iter);
134 gtk_text_buffer_get_end_iter(buffer, &end_iter);
136 if ((data->u.input.hint != NULL) && (purple_strequal(data->u.input.hint, "html")))
137 multiline_value = gtk_imhtml_get_markup(GTK_IMHTML(data->u.input.entry));
138 else
139 multiline_value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
140 FALSE);
142 value = multiline_value;
144 else
145 value = gtk_entry_get_text(GTK_ENTRY(data->u.input.entry));
147 if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
148 ((PurpleRequestInputCb)data->cbs[id])(data->user_data, value);
149 else if (data->cbs[1] != NULL)
150 ((PurpleRequestInputCb)data->cbs[1])(data->user_data, value);
152 if (data->u.input.multiline)
153 g_free(multiline_value);
155 purple_request_close(PURPLE_REQUEST_INPUT, data);
158 static void
159 action_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
161 generic_response_start(data);
163 if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
164 ((PurpleRequestActionCb)data->cbs[id])(data->user_data, id);
166 purple_request_close(PURPLE_REQUEST_INPUT, data);
170 static void
171 choice_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data)
173 GtkWidget *radio = g_object_get_data(G_OBJECT(dialog), "radio");
174 GSList *group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
176 generic_response_start(data);
178 if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
179 while (group) {
180 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(group->data))) {
181 ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(group->data), "choice_id")));
182 break;
184 group = group->next;
186 purple_request_close(PURPLE_REQUEST_INPUT, data);
189 static gboolean
190 field_string_focus_out_cb(GtkWidget *entry, GdkEventFocus *event,
191 PurpleRequestField *field)
193 const char *value;
195 if (purple_request_field_string_is_multiline(field))
197 GtkTextBuffer *buffer;
198 GtkTextIter start_iter, end_iter;
200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
202 gtk_text_buffer_get_start_iter(buffer, &start_iter);
203 gtk_text_buffer_get_end_iter(buffer, &end_iter);
205 value = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
207 else
208 value = gtk_entry_get_text(GTK_ENTRY(entry));
210 purple_request_field_string_set_value(field,
211 (*value == '\0' ? NULL : value));
213 return FALSE;
216 static gboolean
217 field_int_focus_out_cb(GtkEntry *entry, GdkEventFocus *event,
218 PurpleRequestField *field)
220 purple_request_field_int_set_value(field,
221 atoi(gtk_entry_get_text(entry)));
223 return FALSE;
226 static void
227 field_bool_cb(GtkToggleButton *button, PurpleRequestField *field)
229 purple_request_field_bool_set_value(field,
230 gtk_toggle_button_get_active(button));
233 static void
234 field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field)
236 purple_request_field_choice_set_value(field,
237 gtk_combo_box_get_active(menu));
240 static void
241 field_choice_option_cb(GtkRadioButton *button, PurpleRequestField *field)
243 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
244 purple_request_field_choice_set_value(field,
245 (g_slist_length(gtk_radio_button_get_group(button)) -
246 g_slist_index(gtk_radio_button_get_group(button), button)) - 1);
249 static void
250 field_account_cb(GObject *w, PurpleAccount *account, PurpleRequestField *field)
252 purple_request_field_account_set_value(field, account);
255 static void
256 multifield_ok_cb(GtkWidget *button, PidginRequestData *data)
258 generic_response_start(data);
260 if (!GTK_WIDGET_HAS_FOCUS(button))
261 gtk_widget_grab_focus(button);
263 if (data->cbs[0] != NULL)
264 ((PurpleRequestFieldsCb)data->cbs[0])(data->user_data,
265 data->u.multifield.fields);
267 purple_request_close(PURPLE_REQUEST_FIELDS, data);
270 static void
271 multifield_cancel_cb(GtkWidget *button, PidginRequestData *data)
273 generic_response_start(data);
275 if (data->cbs[1] != NULL)
276 ((PurpleRequestFieldsCb)data->cbs[1])(data->user_data,
277 data->u.multifield.fields);
279 purple_request_close(PURPLE_REQUEST_FIELDS, data);
282 static gboolean
283 destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event,
284 PidginRequestData *data)
286 multifield_cancel_cb(NULL, data);
287 return FALSE;
291 #define STOCK_ITEMIZE(r, l) \
292 if (purple_strequal((r), text)) \
293 return (l);
295 static const char *
296 text_to_stock(const char *text)
298 STOCK_ITEMIZE(_("Yes"), GTK_STOCK_YES);
299 STOCK_ITEMIZE(_("No"), GTK_STOCK_NO);
300 STOCK_ITEMIZE(_("OK"), GTK_STOCK_OK);
301 STOCK_ITEMIZE(_("Cancel"), GTK_STOCK_CANCEL);
302 STOCK_ITEMIZE(_("Apply"), GTK_STOCK_APPLY);
303 STOCK_ITEMIZE(_("Close"), GTK_STOCK_CLOSE);
304 STOCK_ITEMIZE(_("Delete"), GTK_STOCK_DELETE);
305 STOCK_ITEMIZE(_("Add"), GTK_STOCK_ADD);
306 STOCK_ITEMIZE(_("Remove"), GTK_STOCK_REMOVE);
307 STOCK_ITEMIZE(_("Save"), GTK_STOCK_SAVE);
308 STOCK_ITEMIZE(_("Alias"), PIDGIN_STOCK_ALIAS);
310 return text;
313 static void *
314 pidgin_request_input(const char *title, const char *primary,
315 const char *secondary, const char *default_value,
316 gboolean multiline, gboolean masked, gchar *hint,
317 const char *ok_text, GCallback ok_cb,
318 const char *cancel_text, GCallback cancel_cb,
319 PurpleAccount *account, const char *who, PurpleConversation *conv,
320 void *user_data)
322 PidginRequestData *data;
323 GtkWidget *dialog;
324 GtkWidget *vbox;
325 GtkWidget *hbox;
326 GtkWidget *label;
327 GtkWidget *entry;
328 GtkWidget *img;
329 GtkWidget *toolbar;
330 char *label_text;
331 char *primary_esc, *secondary_esc;
333 data = g_new0(PidginRequestData, 1);
334 data->type = PURPLE_REQUEST_INPUT;
335 data->user_data = user_data;
337 data->cb_count = 2;
338 data->cbs = g_new0(GCallback, 2);
340 data->cbs[0] = ok_cb;
341 data->cbs[1] = cancel_cb;
343 /* Create the dialog. */
344 dialog = gtk_dialog_new_with_buttons(title ? title : PIDGIN_ALERT_TITLE,
345 NULL, 0,
346 text_to_stock(cancel_text), 1,
347 text_to_stock(ok_text), 0,
348 NULL);
349 data->dialog = dialog;
351 g_signal_connect(G_OBJECT(dialog), "response",
352 G_CALLBACK(input_response_cb), data);
354 /* Setup the dialog */
355 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
356 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
357 if (!multiline)
358 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
359 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
360 gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
361 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
363 /* Setup the main horizontal box */
364 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
365 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
367 /* Dialog icon. */
368 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
369 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
370 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
371 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
373 /* Vertical box */
374 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
376 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
378 pidgin_widget_decorate_account(hbox, account);
380 /* Descriptive label */
381 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
382 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
383 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
384 "%s</span>%s%s" : "%s%s%s"),
385 (primary ? primary_esc : ""),
386 ((primary && secondary) ? "\n\n" : ""),
387 (secondary ? secondary_esc : ""));
388 g_free(primary_esc);
389 g_free(secondary_esc);
391 label = gtk_label_new(NULL);
393 gtk_label_set_markup(GTK_LABEL(label), label_text);
394 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
395 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
396 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
398 g_free(label_text);
400 /* Entry field. */
401 data->u.input.multiline = multiline;
402 data->u.input.hint = g_strdup(hint);
404 gtk_widget_show_all(hbox);
406 if ((data->u.input.hint != NULL) && (purple_strequal(data->u.input.hint, "html"))) {
407 GtkWidget *frame;
409 /* imhtml */
410 frame = pidgin_create_imhtml(TRUE, &entry, &toolbar, NULL);
411 gtk_widget_set_size_request(entry, 320, 130);
412 gtk_widget_set_name(entry, "pidgin_request_imhtml");
413 if (default_value != NULL)
414 gtk_imhtml_append_text(GTK_IMHTML(entry), default_value, GTK_IMHTML_NO_SCROLL);
415 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
416 gtk_widget_show(frame);
418 gtk_imhtml_set_return_inserts_newline(GTK_IMHTML(entry));
420 else {
421 if (multiline) {
422 /* GtkTextView */
423 entry = gtk_text_view_new();
424 gtk_text_view_set_editable(GTK_TEXT_VIEW(entry), TRUE);
426 if (default_value != NULL) {
427 GtkTextBuffer *buffer;
429 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
430 gtk_text_buffer_set_text(buffer, default_value, -1);
433 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(entry), GTK_WRAP_WORD_CHAR);
435 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
436 pidgin_setup_gtkspell(GTK_TEXT_VIEW(entry));
438 gtk_box_pack_start(GTK_BOX(vbox),
439 pidgin_make_scrollable(entry, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 320, 130),
440 TRUE, TRUE, 0);
442 else {
443 entry = gtk_entry_new();
445 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
447 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
449 if (default_value != NULL)
450 gtk_entry_set_text(GTK_ENTRY(entry), default_value);
452 if (masked)
454 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
455 #if !GTK_CHECK_VERSION(2,16,0)
456 if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
457 gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
458 #endif /* Less than GTK+ 2.16 */
461 gtk_widget_show_all(vbox);
464 pidgin_set_accessible_label (entry, label);
465 data->u.input.entry = entry;
467 pidgin_auto_parent_window(dialog);
469 /* Show everything. */
470 gtk_widget_show(dialog);
472 return data;
475 static void *
476 pidgin_request_choice(const char *title, const char *primary,
477 const char *secondary, int default_value,
478 const char *ok_text, GCallback ok_cb,
479 const char *cancel_text, GCallback cancel_cb,
480 PurpleAccount *account, const char *who, PurpleConversation *conv,
481 void *user_data, va_list args)
483 PidginRequestData *data;
484 GtkWidget *dialog;
485 GtkWidget *vbox, *vbox2;
486 GtkWidget *hbox;
487 GtkWidget *label;
488 GtkWidget *img;
489 GtkWidget *radio = NULL;
490 char *label_text;
491 char *radio_text;
492 char *primary_esc, *secondary_esc;
494 data = g_new0(PidginRequestData, 1);
495 data->type = PURPLE_REQUEST_ACTION;
496 data->user_data = user_data;
498 data->cb_count = 2;
499 data->cbs = g_new0(GCallback, 2);
500 data->cbs[0] = cancel_cb;
501 data->cbs[1] = ok_cb;
503 /* Create the dialog. */
504 data->dialog = dialog = gtk_dialog_new();
506 if (title != NULL)
507 gtk_window_set_title(GTK_WINDOW(dialog), title);
508 #ifdef _WIN32
509 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
510 #endif
512 gtk_dialog_add_button(GTK_DIALOG(dialog),
513 text_to_stock(cancel_text), 0);
515 gtk_dialog_add_button(GTK_DIALOG(dialog),
516 text_to_stock(ok_text), 1);
518 g_signal_connect(G_OBJECT(dialog), "response",
519 G_CALLBACK(choice_response_cb), data);
521 /* Setup the dialog */
522 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
523 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
524 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
525 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
526 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
528 /* Setup the main horizontal box */
529 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
530 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
532 /* Dialog icon. */
533 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
534 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
535 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
536 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
538 pidgin_widget_decorate_account(hbox, account);
540 /* Vertical box */
541 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
542 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
544 /* Descriptive label */
545 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
546 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
547 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
548 "%s</span>%s%s" : "%s%s%s"),
549 (primary ? primary_esc : ""),
550 ((primary && secondary) ? "\n\n" : ""),
551 (secondary ? secondary_esc : ""));
552 g_free(primary_esc);
553 g_free(secondary_esc);
555 label = gtk_label_new(NULL);
557 gtk_label_set_markup(GTK_LABEL(label), label_text);
558 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
559 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
560 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
562 g_free(label_text);
564 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
565 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
566 while ((radio_text = va_arg(args, char*))) {
567 int resp = va_arg(args, int);
568 radio = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio), radio_text);
569 gtk_box_pack_start(GTK_BOX(vbox2), radio, FALSE, FALSE, 0);
570 g_object_set_data(G_OBJECT(radio), "choice_id", GINT_TO_POINTER(resp));
571 if (resp == default_value)
572 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
575 g_object_set_data(G_OBJECT(dialog), "radio", radio);
577 /* Show everything. */
578 pidgin_auto_parent_window(dialog);
580 gtk_widget_show_all(dialog);
582 return data;
585 static void *
586 pidgin_request_action_with_icon(const char *title, const char *primary,
587 const char *secondary, int default_action,
588 PurpleAccount *account, const char *who,
589 PurpleConversation *conv, gconstpointer icon_data,
590 gsize icon_size,
591 void *user_data, size_t action_count, va_list actions)
593 PidginRequestData *data;
594 GtkWidget *dialog;
595 GtkWidget *vbox;
596 GtkWidget *hbox;
597 GtkWidget *label;
598 GtkWidget *img = NULL;
599 void **buttons;
600 char *label_text;
601 char *primary_esc, *secondary_esc;
602 gsize i;
604 data = g_new0(PidginRequestData, 1);
605 data->type = PURPLE_REQUEST_ACTION;
606 data->user_data = user_data;
608 data->cb_count = action_count;
609 data->cbs = g_new0(GCallback, action_count);
611 /* Reverse the buttons */
612 buttons = g_new0(void *, action_count * 2);
614 for (i = 0; i < action_count * 2; i += 2) {
615 buttons[(action_count * 2) - i - 2] = va_arg(actions, char *);
616 buttons[(action_count * 2) - i - 1] = va_arg(actions, GCallback);
619 /* Create the dialog. */
620 data->dialog = dialog = gtk_dialog_new();
622 gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE);
624 if (title != NULL)
625 gtk_window_set_title(GTK_WINDOW(dialog), title);
626 #ifdef _WIN32
627 else
628 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
629 #endif
631 for (i = 0; i < action_count; i++) {
632 gtk_dialog_add_button(GTK_DIALOG(dialog),
633 text_to_stock(buttons[2 * i]), i);
635 data->cbs[i] = buttons[2 * i + 1];
638 g_free(buttons);
640 g_signal_connect(G_OBJECT(dialog), "response",
641 G_CALLBACK(action_response_cb), data);
643 /* Setup the dialog */
644 gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
645 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
646 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
647 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
648 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
650 /* Setup the main horizontal box */
651 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
652 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
654 /* Dialog icon. */
655 if (icon_data) {
656 GdkPixbuf *pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size);
657 if (pixbuf) {
658 /* scale the image if it is too large */
659 int width = gdk_pixbuf_get_width(pixbuf);
660 int height = gdk_pixbuf_get_height(pixbuf);
661 if (width > 128 || height > 128) {
662 int scaled_width = width > height ? 128 : (128 * width) / height;
663 int scaled_height = height > width ? 128 : (128 * height) / width;
664 GdkPixbuf *scaled =
665 gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
666 GDK_INTERP_BILINEAR);
668 purple_debug_info("pidgin",
669 "dialog icon was too large, scaled it down\n");
670 if (scaled) {
671 g_object_unref(pixbuf);
672 pixbuf = scaled;
675 img = gtk_image_new_from_pixbuf(pixbuf);
676 g_object_unref(pixbuf);
677 } else {
678 purple_debug_info("pidgin", "failed to parse dialog icon\n");
682 if (!img) {
683 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
684 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
686 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
687 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
689 /* Vertical box */
690 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
691 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
693 pidgin_widget_decorate_account(hbox, account);
695 /* Descriptive label */
696 primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
697 secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
698 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
699 "%s</span>%s%s" : "%s%s%s"),
700 (primary ? primary_esc : ""),
701 ((primary && secondary) ? "\n\n" : ""),
702 (secondary ? secondary_esc : ""));
703 g_free(primary_esc);
704 g_free(secondary_esc);
706 label = gtk_label_new(NULL);
708 gtk_label_set_markup(GTK_LABEL(label), label_text);
709 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
710 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
711 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
712 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
714 g_free(label_text);
717 if (default_action == PURPLE_DEFAULT_ACTION_NONE) {
718 GTK_WIDGET_SET_FLAGS(img, GTK_CAN_DEFAULT);
719 GTK_WIDGET_SET_FLAGS(img, GTK_CAN_FOCUS);
720 gtk_widget_grab_focus(img);
721 gtk_widget_grab_default(img);
722 } else
724 * Need to invert the default_action number because the
725 * buttons are added to the dialog in reverse order.
727 gtk_dialog_set_default_response(GTK_DIALOG(dialog), action_count - 1 - default_action);
729 /* Show everything. */
730 pidgin_auto_parent_window(dialog);
732 gtk_widget_show_all(dialog);
734 return data;
737 static void *
738 pidgin_request_action(const char *title, const char *primary,
739 const char *secondary, int default_action,
740 PurpleAccount *account, const char *who, PurpleConversation *conv,
741 void *user_data, size_t action_count, va_list actions)
743 return pidgin_request_action_with_icon(title, primary, secondary,
744 default_action, account, who, conv, NULL, 0, user_data, action_count,
745 actions);
748 static void
749 req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
751 PurpleRequestFieldGroup *group;
752 PidginRequestData *req_data;
754 if (purple_request_field_string_is_multiline(field))
756 char *text;
757 GtkTextIter start_iter, end_iter;
759 gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(entry), &start_iter);
760 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(entry), &end_iter);
762 text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(entry), &start_iter, &end_iter, FALSE);
763 purple_request_field_string_set_value(field, (!text || !*text) ? NULL : text);
764 g_free(text);
766 else
768 const char *text = NULL;
769 text = gtk_entry_get_text(GTK_ENTRY(entry));
770 purple_request_field_string_set_value(field, (*text == '\0') ? NULL : text);
773 group = purple_request_field_get_group(field);
774 req_data = (PidginRequestData *)group->fields_list->ui_data;
776 gtk_widget_set_sensitive(req_data->ok_button,
777 purple_request_fields_all_required_filled(group->fields_list));
780 static void
781 setup_entry_field(GtkWidget *entry, PurpleRequestField *field)
783 const char *type_hint;
785 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
787 if (purple_request_field_is_required(field))
789 g_signal_connect(G_OBJECT(entry), "changed",
790 G_CALLBACK(req_entry_field_changed_cb), field);
793 if ((type_hint = purple_request_field_get_type_hint(field)) != NULL)
795 if (purple_str_has_prefix(type_hint, "screenname"))
797 GtkWidget *optmenu = NULL;
798 PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
799 GList *fields = group->fields;
801 /* Ensure the account option menu is created (if the widget hasn't
802 * been initialized already) for username auto-completion. */
803 while (fields)
805 PurpleRequestField *fld = fields->data;
806 fields = fields->next;
808 if (purple_request_field_get_type(fld) == PURPLE_REQUEST_FIELD_ACCOUNT &&
809 purple_request_field_is_visible(fld))
811 const char *type_hint = purple_request_field_get_type_hint(fld);
812 if (purple_strequal(type_hint, "account"))
814 optmenu = GTK_WIDGET(purple_request_field_get_ui_data(fld));
815 if (optmenu == NULL) {
816 optmenu = GTK_WIDGET(create_account_field(fld));
817 purple_request_field_set_ui_data(fld, optmenu);
819 break;
823 pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(purple_strequal(type_hint, "screenname-all")));
828 static GtkWidget *
829 create_string_field(PurpleRequestField *field)
831 const char *value;
832 GtkWidget *widget;
834 value = purple_request_field_string_get_default_value(field);
836 if (purple_request_field_string_is_multiline(field))
838 GtkWidget *textview;
840 textview = gtk_text_view_new();
841 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
842 TRUE);
843 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview),
844 GTK_WRAP_WORD_CHAR);
846 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
847 pidgin_setup_gtkspell(GTK_TEXT_VIEW(textview));
849 gtk_widget_show(textview);
851 if (value != NULL)
853 GtkTextBuffer *buffer;
855 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
857 gtk_text_buffer_set_text(buffer, value, -1);
860 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview),
861 purple_request_field_string_is_editable(field));
863 g_signal_connect(G_OBJECT(textview), "focus-out-event",
864 G_CALLBACK(field_string_focus_out_cb), field);
866 if (purple_request_field_is_required(field))
868 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
869 g_signal_connect(G_OBJECT(buffer), "changed",
870 G_CALLBACK(req_entry_field_changed_cb), field);
873 widget = pidgin_make_scrollable(textview, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, 75);
875 else
877 widget = gtk_entry_new();
879 setup_entry_field(widget, field);
881 if (value != NULL)
882 gtk_entry_set_text(GTK_ENTRY(widget), value);
884 if (purple_request_field_string_is_masked(field))
886 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
887 #if !GTK_CHECK_VERSION(2,16,0)
888 if (gtk_entry_get_invisible_char(GTK_ENTRY(widget)) == '*')
889 gtk_entry_set_invisible_char(GTK_ENTRY(widget), PIDGIN_INVISIBLE_CHAR);
890 #endif /* Less than GTK+ 2.16 */
893 gtk_editable_set_editable(GTK_EDITABLE(widget),
894 purple_request_field_string_is_editable(field));
896 g_signal_connect(G_OBJECT(widget), "focus-out-event",
897 G_CALLBACK(field_string_focus_out_cb), field);
900 return widget;
903 static GtkWidget *
904 create_int_field(PurpleRequestField *field)
906 int value;
907 GtkWidget *widget;
909 widget = gtk_entry_new();
911 setup_entry_field(widget, field);
913 value = purple_request_field_int_get_default_value(field);
915 if (value != 0)
917 char buf[32];
919 g_snprintf(buf, sizeof(buf), "%d", value);
921 gtk_entry_set_text(GTK_ENTRY(widget), buf);
924 g_signal_connect(G_OBJECT(widget), "focus-out-event",
925 G_CALLBACK(field_int_focus_out_cb), field);
927 return widget;
930 static GtkWidget *
931 create_bool_field(PurpleRequestField *field)
933 GtkWidget *widget;
935 widget = gtk_check_button_new_with_label(
936 purple_request_field_get_label(field));
938 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
939 purple_request_field_bool_get_default_value(field));
941 g_signal_connect(G_OBJECT(widget), "toggled",
942 G_CALLBACK(field_bool_cb), field);
944 return widget;
947 static GtkWidget *
948 create_choice_field(PurpleRequestField *field)
950 GtkWidget *widget;
951 GList *labels = purple_request_field_choice_get_labels(field);
952 int num_labels = g_list_length(labels);
953 GList *l;
955 if (num_labels > 5)
957 widget = gtk_combo_box_new_text();
959 for (l = labels; l != NULL; l = l->next)
961 const char *text = l->data;
962 gtk_combo_box_append_text(GTK_COMBO_BOX(widget), text);
965 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
966 purple_request_field_choice_get_default_value(field));
968 g_signal_connect(G_OBJECT(widget), "changed",
969 G_CALLBACK(field_choice_menu_cb), field);
971 else
973 GtkWidget *box;
974 GtkWidget *first_radio = NULL;
975 GtkWidget *radio;
976 gint i;
978 if (num_labels == 2)
979 box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
980 else
981 box = gtk_vbox_new(FALSE, 0);
983 widget = box;
985 for (l = labels, i = 0; l != NULL; l = l->next, i++)
987 const char *text = l->data;
989 radio = gtk_radio_button_new_with_label_from_widget(
990 GTK_RADIO_BUTTON(first_radio), text);
992 if (first_radio == NULL)
993 first_radio = radio;
995 if (i == purple_request_field_choice_get_default_value(field))
996 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
998 gtk_box_pack_start(GTK_BOX(box), radio, TRUE, TRUE, 0);
999 gtk_widget_show(radio);
1001 g_signal_connect(G_OBJECT(radio), "toggled",
1002 G_CALLBACK(field_choice_option_cb), field);
1006 return widget;
1009 static GtkWidget *
1010 create_image_field(PurpleRequestField *field)
1012 GtkWidget *widget;
1013 GdkPixbuf *buf, *scale;
1015 buf = pidgin_pixbuf_from_data(
1016 (const guchar *)purple_request_field_image_get_buffer(field),
1017 purple_request_field_image_get_size(field));
1019 scale = gdk_pixbuf_scale_simple(buf,
1020 purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf),
1021 purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf),
1022 GDK_INTERP_BILINEAR);
1023 widget = gtk_image_new_from_pixbuf(scale);
1024 g_object_unref(G_OBJECT(buf));
1025 g_object_unref(G_OBJECT(scale));
1027 return widget;
1030 static GtkWidget *
1031 create_account_field(PurpleRequestField *field)
1033 GtkWidget *widget;
1035 widget = pidgin_account_option_menu_new(
1036 purple_request_field_account_get_default_value(field),
1037 purple_request_field_account_get_show_all(field),
1038 G_CALLBACK(field_account_cb),
1039 purple_request_field_account_get_filter(field),
1040 field);
1042 return widget;
1045 static void
1046 select_field_list_item(GtkTreeModel *model, GtkTreePath *path,
1047 GtkTreeIter *iter, gpointer data)
1049 PurpleRequestField *field = (PurpleRequestField *)data;
1050 char *text;
1052 gtk_tree_model_get(model, iter, 1, &text, -1);
1054 purple_request_field_list_add_selected(field, text);
1055 g_free(text);
1058 static void
1059 list_field_select_changed_cb(GtkTreeSelection *sel, PurpleRequestField *field)
1061 purple_request_field_list_clear_selected(field);
1063 gtk_tree_selection_selected_foreach(sel, select_field_list_item, field);
1066 static GtkWidget *
1067 create_list_field(PurpleRequestField *field)
1069 GtkWidget *treeview;
1070 GtkListStore *store;
1071 GtkCellRenderer *renderer;
1072 GtkTreeSelection *sel;
1073 GtkTreeViewColumn *column;
1074 GtkTreeIter iter;
1075 GList *l;
1076 GList *icons = NULL;
1078 icons = purple_request_field_list_get_icons(field);
1081 /* Create the list store */
1082 if (icons)
1083 store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF);
1084 else
1085 store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
1087 /* Create the tree view */
1088 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1089 g_object_unref(G_OBJECT(store));
1090 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
1092 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1094 if (purple_request_field_list_get_multi_select(field))
1095 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
1097 column = gtk_tree_view_column_new();
1098 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
1100 renderer = gtk_cell_renderer_text_new();
1101 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1102 gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
1104 if (icons)
1106 renderer = gtk_cell_renderer_pixbuf_new();
1107 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1108 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2);
1110 gtk_widget_set_size_request(treeview, 200, 400);
1113 for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next)
1115 const char *text = (const char *)l->data;
1117 gtk_list_store_append(store, &iter);
1119 if (icons)
1121 const char *icon_path = (const char *)icons->data;
1122 GdkPixbuf* pixbuf = NULL;
1124 if (icon_path)
1125 pixbuf = pidgin_pixbuf_new_from_file(icon_path);
1127 gtk_list_store_set(store, &iter,
1128 0, purple_request_field_list_get_data(field, text),
1129 1, text,
1130 2, pixbuf,
1131 -1);
1132 icons = icons->next;
1134 else
1135 gtk_list_store_set(store, &iter,
1136 0, purple_request_field_list_get_data(field, text),
1137 1, text,
1138 -1);
1140 if (purple_request_field_list_is_selected(field, text))
1141 gtk_tree_selection_select_iter(sel, &iter);
1145 * We only want to catch changes made by the user, so it's important
1146 * that we wait until after the list is created to connect this
1147 * handler. If we connect the handler before the loop above and
1148 * there are multiple items selected, then selecting the first iter
1149 * in the tree causes list_field_select_changed_cb to be triggered
1150 * which clears out the rest of the list of selected items.
1152 g_signal_connect(G_OBJECT(sel), "changed",
1153 G_CALLBACK(list_field_select_changed_cb), field);
1155 gtk_widget_show(treeview);
1157 return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
1160 static void *
1161 pidgin_request_fields(const char *title, const char *primary,
1162 const char *secondary, PurpleRequestFields *fields,
1163 const char *ok_text, GCallback ok_cb,
1164 const char *cancel_text, GCallback cancel_cb,
1165 PurpleAccount *account, const char *who, PurpleConversation *conv,
1166 void *user_data)
1168 PidginRequestData *data;
1169 GtkWidget *win;
1170 GtkWidget *vbox;
1171 GtkWidget *vbox2;
1172 GtkWidget *hbox;
1173 GtkWidget *frame;
1174 GtkWidget *label;
1175 GtkWidget *table;
1176 GtkWidget *button;
1177 GtkWidget *img;
1178 GtkSizeGroup *sg;
1179 GList *gl, *fl;
1180 PurpleRequestFieldGroup *group;
1181 PurpleRequestField *field;
1182 char *label_text;
1183 char *primary_esc, *secondary_esc;
1184 int total_fields = 0;
1186 data = g_new0(PidginRequestData, 1);
1187 data->type = PURPLE_REQUEST_FIELDS;
1188 data->user_data = user_data;
1189 data->u.multifield.fields = fields;
1191 fields->ui_data = data;
1193 data->cb_count = 2;
1194 data->cbs = g_new0(GCallback, 2);
1196 data->cbs[0] = ok_cb;
1197 data->cbs[1] = cancel_cb;
1200 #ifdef _WIN32
1201 data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
1202 #else /* !_WIN32 */
1203 data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
1204 #endif /* _WIN32 */
1206 g_signal_connect(G_OBJECT(win), "delete_event",
1207 G_CALLBACK(destroy_multifield_cb), data);
1209 /* Setup the main horizontal box */
1210 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
1211 gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
1212 gtk_widget_show(hbox);
1214 /* Dialog icon. */
1215 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
1216 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
1217 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
1218 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
1219 gtk_widget_show(img);
1221 /* Cancel button */
1222 button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
1223 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1225 /* OK button */
1226 button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
1227 data->ok_button = button;
1228 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1229 gtk_window_set_default(GTK_WINDOW(win), button);
1231 pidgin_widget_decorate_account(hbox, account);
1233 /* Setup the vbox */
1234 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1235 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1236 gtk_widget_show(vbox);
1238 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1240 if(primary) {
1241 primary_esc = g_markup_escape_text(primary, -1);
1242 label_text = g_strdup_printf(
1243 "<span weight=\"bold\" size=\"larger\">%s</span>", primary_esc);
1244 g_free(primary_esc);
1245 label = gtk_label_new(NULL);
1247 gtk_label_set_markup(GTK_LABEL(label), label_text);
1248 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1249 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1250 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1251 gtk_widget_show(label);
1252 g_free(label_text);
1255 for (gl = purple_request_fields_get_groups(fields); gl != NULL;
1256 gl = gl->next)
1257 total_fields += g_list_length(purple_request_field_group_get_fields(gl->data));
1259 if(total_fields > 9) {
1260 GtkWidget *hbox_for_spacing, *vbox_for_spacing;
1262 hbox_for_spacing = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
1263 gtk_box_pack_start(GTK_BOX(vbox),
1264 pidgin_make_scrollable(hbox_for_spacing, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, 200),
1265 TRUE, TRUE, 0);
1266 gtk_widget_show(hbox_for_spacing);
1268 vbox_for_spacing = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1269 gtk_box_pack_start(GTK_BOX(hbox_for_spacing),
1270 vbox_for_spacing, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
1271 gtk_widget_show(vbox_for_spacing);
1273 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
1274 gtk_box_pack_start(GTK_BOX(vbox_for_spacing),
1275 vbox2, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
1276 gtk_widget_show(vbox2);
1277 } else {
1278 vbox2 = vbox;
1281 if (secondary) {
1282 secondary_esc = g_markup_escape_text(secondary, -1);
1283 label = gtk_label_new(NULL);
1285 gtk_label_set_markup(GTK_LABEL(label), secondary_esc);
1286 g_free(secondary_esc);
1287 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1288 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1289 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
1290 gtk_widget_show(label);
1293 for (gl = purple_request_fields_get_groups(fields);
1294 gl != NULL;
1295 gl = gl->next)
1297 GList *field_list;
1298 size_t field_count = 0;
1299 size_t cols = 1;
1300 size_t rows;
1301 #if 0
1302 size_t col_num;
1303 #endif
1304 size_t row_num = 0;
1306 group = gl->data;
1307 field_list = purple_request_field_group_get_fields(group);
1309 if (purple_request_field_group_get_title(group) != NULL)
1311 frame = pidgin_make_frame(vbox2,
1312 purple_request_field_group_get_title(group));
1314 else
1315 frame = vbox2;
1317 field_count = g_list_length(field_list);
1318 #if 0
1319 if (field_count > 9)
1321 rows = field_count / 2;
1322 cols++;
1324 else
1325 #endif
1326 rows = field_count;
1328 #if 0
1329 col_num = 0;
1330 #endif
1332 for (fl = field_list; fl != NULL; fl = fl->next)
1334 PurpleRequestFieldType type;
1336 field = (PurpleRequestField *)fl->data;
1338 type = purple_request_field_get_type(field);
1340 if (type == PURPLE_REQUEST_FIELD_LABEL)
1342 #if 0
1343 if (col_num > 0)
1344 rows++;
1345 #endif
1347 rows++;
1349 else if ((type == PURPLE_REQUEST_FIELD_LIST) ||
1350 (type == PURPLE_REQUEST_FIELD_STRING &&
1351 purple_request_field_string_is_multiline(field)))
1353 #if 0
1354 if (col_num > 0)
1355 rows++;
1356 #endif
1358 rows += 2;
1361 #if 0
1362 col_num++;
1364 if (col_num >= cols)
1365 col_num = 0;
1366 #endif
1369 table = gtk_table_new(rows, 2 * cols, FALSE);
1370 gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
1371 gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE);
1373 gtk_container_add(GTK_CONTAINER(frame), table);
1374 gtk_widget_show(table);
1376 for (row_num = 0, fl = field_list;
1377 row_num < rows && fl != NULL;
1378 row_num++)
1380 #if 0
1381 for (col_num = 0;
1382 col_num < cols && fl != NULL;
1383 col_num++, fl = fl->next)
1384 #else
1385 gboolean dummy_counter = TRUE;
1386 /* it's the same as loop above */
1387 for (; dummy_counter && fl != NULL; dummy_counter = FALSE, fl = fl->next)
1388 #endif
1390 #if 0
1391 size_t col_offset = col_num * 2;
1392 #else
1393 size_t col_offset = 0;
1394 #endif
1395 PurpleRequestFieldType type;
1396 GtkWidget *widget = NULL;
1397 const char *field_label;
1399 label = NULL;
1400 field = fl->data;
1402 if (!purple_request_field_is_visible(field)) {
1403 #if 0
1404 col_num--;
1405 #endif
1406 continue;
1409 type = purple_request_field_get_type(field);
1410 field_label = purple_request_field_get_label(field);
1412 if (type != PURPLE_REQUEST_FIELD_BOOLEAN && field_label)
1414 char *text = NULL;
1416 if (field_label[strlen(field_label) - 1] != ':')
1417 text = g_strdup_printf("%s:", field_label);
1419 label = gtk_label_new(NULL);
1420 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text ? text : field_label);
1421 g_free(text);
1423 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1425 gtk_size_group_add_widget(sg, label);
1427 if (type == PURPLE_REQUEST_FIELD_LABEL ||
1428 type == PURPLE_REQUEST_FIELD_LIST ||
1429 (type == PURPLE_REQUEST_FIELD_STRING &&
1430 purple_request_field_string_is_multiline(field)))
1432 #if 0
1433 if(col_num > 0)
1434 row_num++;
1435 #endif
1437 gtk_table_attach_defaults(GTK_TABLE(table), label,
1438 0, 2 * cols,
1439 row_num, row_num + 1);
1441 row_num++;
1442 #if 0
1443 col_num=cols;
1444 #endif
1446 else
1448 gtk_table_attach_defaults(GTK_TABLE(table), label,
1449 col_offset, col_offset + 1,
1450 row_num, row_num + 1);
1453 gtk_widget_show(label);
1456 widget = GTK_WIDGET(purple_request_field_get_ui_data(field));
1457 if (widget == NULL)
1459 if (type == PURPLE_REQUEST_FIELD_STRING)
1460 widget = create_string_field(field);
1461 else if (type == PURPLE_REQUEST_FIELD_INTEGER)
1462 widget = create_int_field(field);
1463 else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
1464 widget = create_bool_field(field);
1465 else if (type == PURPLE_REQUEST_FIELD_CHOICE)
1466 widget = create_choice_field(field);
1467 else if (type == PURPLE_REQUEST_FIELD_LIST)
1468 widget = create_list_field(field);
1469 else if (type == PURPLE_REQUEST_FIELD_IMAGE)
1470 widget = create_image_field(field);
1471 else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
1472 widget = create_account_field(field);
1473 else
1474 continue;
1477 if (label)
1478 gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
1480 if (type == PURPLE_REQUEST_FIELD_STRING &&
1481 purple_request_field_string_is_multiline(field))
1483 gtk_table_attach(GTK_TABLE(table), widget,
1484 0, 2 * cols,
1485 row_num, row_num + 1,
1486 GTK_FILL | GTK_EXPAND,
1487 GTK_FILL | GTK_EXPAND,
1488 5, 0);
1490 else if (type == PURPLE_REQUEST_FIELD_LIST)
1492 gtk_table_attach(GTK_TABLE(table), widget,
1493 0, 2 * cols,
1494 row_num, row_num + 1,
1495 GTK_FILL | GTK_EXPAND,
1496 GTK_FILL | GTK_EXPAND,
1497 5, 0);
1499 else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
1501 gtk_table_attach(GTK_TABLE(table), widget,
1502 col_offset, col_offset + 1,
1503 row_num, row_num + 1,
1504 GTK_FILL | GTK_EXPAND,
1505 GTK_FILL | GTK_EXPAND,
1506 5, 0);
1508 else
1510 gtk_table_attach(GTK_TABLE(table), widget,
1511 1, 2 * cols,
1512 row_num, row_num + 1,
1513 GTK_FILL | GTK_EXPAND,
1514 GTK_FILL | GTK_EXPAND,
1515 5, 0);
1518 gtk_widget_show(widget);
1520 purple_request_field_set_ui_data(field, widget);
1525 g_object_unref(sg);
1527 if (!purple_request_fields_all_required_filled(fields))
1528 gtk_widget_set_sensitive(data->ok_button, FALSE);
1530 pidgin_auto_parent_window(win);
1532 gtk_widget_show(win);
1534 return data;
1537 static void
1538 file_yes_no_cb(PidginRequestData *data, gint id)
1540 /* Only call the callback if yes was selected, otherwise the request
1541 * (eg. file transfer) will be cancelled, then when a new filename is chosen
1542 * things go BOOM */
1543 if (id == 1) {
1544 if (data->cbs[1] != NULL)
1545 ((PurpleRequestFileCb)data->cbs[1])(data->user_data, data->u.file.name);
1546 purple_request_close(data->type, data);
1547 } else {
1548 pidgin_clear_cursor(GTK_WIDGET(data->dialog));
1552 static void
1553 file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *data)
1555 gchar *current_folder;
1557 generic_response_start(data);
1559 if (response != GTK_RESPONSE_ACCEPT) {
1560 if (data->cbs[0] != NULL)
1561 ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
1562 purple_request_close(data->type, data);
1563 return;
1566 data->u.file.name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data->dialog));
1567 current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(data->dialog));
1568 if (current_folder != NULL) {
1569 if (data->u.file.savedialog) {
1570 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", current_folder);
1571 } else {
1572 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", current_folder);
1574 g_free(current_folder);
1576 if ((data->u.file.savedialog == TRUE) &&
1577 (g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
1578 purple_request_action(data, NULL, _("That file already exists"),
1579 _("Would you like to overwrite it?"), 0,
1580 NULL, NULL, NULL,
1581 data, 2,
1582 _("Overwrite"), G_CALLBACK(file_yes_no_cb),
1583 _("Choose New Name"), G_CALLBACK(file_yes_no_cb));
1584 } else
1585 file_yes_no_cb(data, 1);
1588 static void *
1589 pidgin_request_file(const char *title, const char *filename,
1590 gboolean savedialog,
1591 GCallback ok_cb, GCallback cancel_cb,
1592 PurpleAccount *account, const char *who, PurpleConversation *conv,
1593 void *user_data)
1595 PidginRequestData *data;
1596 GtkWidget *filesel;
1597 const gchar *current_folder;
1598 gboolean folder_set = FALSE;
1600 data = g_new0(PidginRequestData, 1);
1601 data->type = PURPLE_REQUEST_FILE;
1602 data->user_data = user_data;
1603 data->cb_count = 2;
1604 data->cbs = g_new0(GCallback, 2);
1605 data->cbs[0] = cancel_cb;
1606 data->cbs[1] = ok_cb;
1607 data->u.file.savedialog = savedialog;
1609 filesel = gtk_file_chooser_dialog_new(
1610 title ? title : (savedialog ? _("Save File...")
1611 : _("Open File...")),
1612 NULL,
1613 savedialog ? GTK_FILE_CHOOSER_ACTION_SAVE
1614 : GTK_FILE_CHOOSER_ACTION_OPEN,
1615 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1616 savedialog ? GTK_STOCK_SAVE
1617 : GTK_STOCK_OPEN,
1618 GTK_RESPONSE_ACCEPT,
1619 NULL);
1620 gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT);
1622 if (savedialog) {
1623 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
1624 } else {
1625 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
1628 if ((filename != NULL) && (*filename != '\0')) {
1629 if (savedialog)
1630 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filesel), filename);
1631 else if (g_file_test(filename, G_FILE_TEST_EXISTS))
1632 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filesel), filename);
1634 if ((filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS)) &&
1635 (current_folder != NULL) && (*current_folder != '\0')) {
1636 folder_set = gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), current_folder);
1639 #ifdef _WIN32
1640 if (!folder_set && (filename == NULL || *filename == '\0' || !g_file_test(filename, G_FILE_TEST_EXISTS))) {
1641 char *my_documents = wpurple_get_special_folder(CSIDL_PERSONAL);
1643 if (my_documents != NULL) {
1644 gtk_file_chooser_set_current_folder(
1645 GTK_FILE_CHOOSER(filesel), my_documents);
1647 g_free(my_documents);
1650 #else
1651 (void)folder_set;
1652 #endif
1654 g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response",
1655 G_CALLBACK(file_ok_check_if_exists_cb), data);
1657 pidgin_auto_parent_window(filesel);
1659 data->dialog = filesel;
1660 gtk_widget_show(filesel);
1662 return (void *)data;
1665 static void *
1666 pidgin_request_folder(const char *title, const char *dirname,
1667 GCallback ok_cb, GCallback cancel_cb,
1668 PurpleAccount *account, const char *who, PurpleConversation *conv,
1669 void *user_data)
1671 PidginRequestData *data;
1672 GtkWidget *dirsel;
1674 data = g_new0(PidginRequestData, 1);
1675 data->type = PURPLE_REQUEST_FOLDER;
1676 data->user_data = user_data;
1677 data->cb_count = 2;
1678 data->cbs = g_new0(GCallback, 2);
1679 data->cbs[0] = cancel_cb;
1680 data->cbs[1] = ok_cb;
1681 data->u.file.savedialog = FALSE;
1683 dirsel = gtk_file_chooser_dialog_new(
1684 title ? title : _("Select Folder..."),
1685 NULL,
1686 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1687 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1688 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1689 NULL);
1690 gtk_dialog_set_default_response(GTK_DIALOG(dirsel), GTK_RESPONSE_ACCEPT);
1692 if ((dirname != NULL) && (*dirname != '\0'))
1693 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirsel), dirname);
1695 g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(dirsel)), "response",
1696 G_CALLBACK(file_ok_check_if_exists_cb), data);
1698 data->dialog = dirsel;
1699 pidgin_auto_parent_window(dirsel);
1701 gtk_widget_show(dirsel);
1703 return (void *)data;
1706 static void
1707 pidgin_close_request(PurpleRequestType type, void *ui_handle)
1709 PidginRequestData *data = (PidginRequestData *)ui_handle;
1711 g_free(data->cbs);
1713 gtk_widget_destroy(data->dialog);
1715 if (type == PURPLE_REQUEST_FIELDS)
1716 purple_request_fields_destroy(data->u.multifield.fields);
1717 else if (type == PURPLE_REQUEST_FILE)
1718 g_free(data->u.file.name);
1720 g_free(data);
1723 static PurpleRequestUiOps ops =
1725 pidgin_request_input,
1726 pidgin_request_choice,
1727 pidgin_request_action,
1728 pidgin_request_fields,
1729 pidgin_request_file,
1730 pidgin_close_request,
1731 pidgin_request_folder,
1732 pidgin_request_action_with_icon,
1733 NULL,
1734 NULL,
1735 NULL
1738 PurpleRequestUiOps *
1739 pidgin_request_get_ui_ops(void)
1741 return &ops;