2 * @file gtkrequest.c GTK+ Request API
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
33 #include "gtkimhtml.h"
34 #include "gtkimhtmltoolbar.h"
35 #include "gtkrequest.h"
37 #include "pidginstock.h"
40 #include <gdk/gdkkeysyms.h>
42 static GtkWidget
* create_account_field(PurpleRequestField
*field
);
46 PurpleRequestType type
;
69 PurpleRequestFields
*fields
;
85 pidgin_widget_decorate_account(GtkWidget
*cont
, PurpleAccount
*account
)
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
);
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
);
121 input_response_cb(GtkDialog
*dialog
, gint id
, PidginRequestData
*data
)
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
));
139 multiline_value
= gtk_text_buffer_get_text(buffer
, &start_iter
, &end_iter
,
142 value
= multiline_value
;
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
);
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
);
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
)
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")));
186 purple_request_close(PURPLE_REQUEST_INPUT
, data
);
190 field_string_focus_out_cb(GtkWidget
*entry
, GdkEventFocus
*event
,
191 PurpleRequestField
*field
)
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
);
208 value
= gtk_entry_get_text(GTK_ENTRY(entry
));
210 purple_request_field_string_set_value(field
,
211 (*value
== '\0' ? NULL
: value
));
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
)));
227 field_bool_cb(GtkToggleButton
*button
, PurpleRequestField
*field
)
229 purple_request_field_bool_set_value(field
,
230 gtk_toggle_button_get_active(button
));
234 field_choice_menu_cb(GtkComboBox
*menu
, PurpleRequestField
*field
)
236 purple_request_field_choice_set_value(field
,
237 gtk_combo_box_get_active(menu
));
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);
250 field_account_cb(GObject
*w
, PurpleAccount
*account
, PurpleRequestField
*field
)
252 purple_request_field_account_set_value(field
, account
);
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
);
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
);
283 destroy_multifield_cb(GtkWidget
*dialog
, GdkEvent
*event
,
284 PidginRequestData
*data
)
286 multifield_cancel_cb(NULL
, data
);
291 #define STOCK_ITEMIZE(r, l) \
292 if (purple_strequal((r), text)) \
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
);
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
,
322 PidginRequestData
*data
;
331 char *primary_esc
, *secondary_esc
;
333 data
= g_new0(PidginRequestData
, 1);
334 data
->type
= PURPLE_REQUEST_INPUT
;
335 data
->user_data
= user_data
;
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
,
346 text_to_stock(cancel_text
), 1,
347 text_to_stock(ok_text
), 0,
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);
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
);
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);
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
: ""));
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);
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"))) {
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
));
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),
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
);
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
);
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
;
485 GtkWidget
*vbox
, *vbox2
;
489 GtkWidget
*radio
= NULL
;
492 char *primary_esc
, *secondary_esc
;
494 data
= g_new0(PidginRequestData
, 1);
495 data
->type
= PURPLE_REQUEST_ACTION
;
496 data
->user_data
= user_data
;
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();
507 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
509 gtk_window_set_title(GTK_WINDOW(dialog
), PIDGIN_ALERT_TITLE
);
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
);
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
);
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
: ""));
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);
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
);
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
,
591 void *user_data
, size_t action_count
, va_list actions
)
593 PidginRequestData
*data
;
598 GtkWidget
*img
= NULL
;
601 char *primary_esc
, *secondary_esc
;
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
);
625 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
628 gtk_window_set_title(GTK_WINDOW(dialog
), PIDGIN_ALERT_TITLE
);
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];
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
);
656 GdkPixbuf
*pixbuf
= pidgin_pixbuf_from_data(icon_data
, icon_size
);
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
;
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");
671 g_object_unref(pixbuf
);
675 img
= gtk_image_new_from_pixbuf(pixbuf
);
676 g_object_unref(pixbuf
);
678 purple_debug_info("pidgin", "failed to parse dialog icon\n");
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);
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
: ""));
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);
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
);
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
);
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
,
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
))
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
);
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
));
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. */
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
);
823 pidgin_setup_screenname_autocomplete_with_filter(entry
, optmenu
, pidgin_screenname_autocomplete_default_filter
, GINT_TO_POINTER(purple_strequal(type_hint
, "screenname-all")));
829 create_string_field(PurpleRequestField
*field
)
834 value
= purple_request_field_string_get_default_value(field
);
836 if (purple_request_field_string_is_multiline(field
))
840 textview
= gtk_text_view_new();
841 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview
),
843 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview
),
846 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/conversations/spellcheck"))
847 pidgin_setup_gtkspell(GTK_TEXT_VIEW(textview
));
849 gtk_widget_show(textview
);
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);
877 widget
= gtk_entry_new();
879 setup_entry_field(widget
, field
);
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
);
904 create_int_field(PurpleRequestField
*field
)
909 widget
= gtk_entry_new();
911 setup_entry_field(widget
, field
);
913 value
= purple_request_field_int_get_default_value(field
);
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
);
931 create_bool_field(PurpleRequestField
*field
)
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
);
948 create_choice_field(PurpleRequestField
*field
)
951 GList
*labels
= purple_request_field_choice_get_labels(field
);
952 int num_labels
= g_list_length(labels
);
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
);
974 GtkWidget
*first_radio
= NULL
;
979 box
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
981 box
= gtk_vbox_new(FALSE
, 0);
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
)
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
);
1010 create_image_field(PurpleRequestField
*field
)
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
));
1031 create_account_field(PurpleRequestField
*field
)
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
),
1046 select_field_list_item(GtkTreeModel
*model
, GtkTreePath
*path
,
1047 GtkTreeIter
*iter
, gpointer data
)
1049 PurpleRequestField
*field
= (PurpleRequestField
*)data
;
1052 gtk_tree_model_get(model
, iter
, 1, &text
, -1);
1054 purple_request_field_list_add_selected(field
, text
);
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
);
1067 create_list_field(PurpleRequestField
*field
)
1069 GtkWidget
*treeview
;
1070 GtkListStore
*store
;
1071 GtkCellRenderer
*renderer
;
1072 GtkTreeSelection
*sel
;
1073 GtkTreeViewColumn
*column
;
1076 GList
*icons
= NULL
;
1078 icons
= purple_request_field_list_get_icons(field
);
1081 /* Create the list store */
1083 store
= gtk_list_store_new(3, G_TYPE_POINTER
, G_TYPE_STRING
, GDK_TYPE_PIXBUF
);
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);
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
);
1121 const char *icon_path
= (const char *)icons
->data
;
1122 GdkPixbuf
* pixbuf
= NULL
;
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
),
1132 icons
= icons
->next
;
1135 gtk_list_store_set(store
, &iter
,
1136 0, purple_request_field_list_get_data(field
, text
),
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);
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
,
1168 PidginRequestData
*data
;
1180 PurpleRequestFieldGroup
*group
;
1181 PurpleRequestField
*field
;
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
;
1194 data
->cbs
= g_new0(GCallback
, 2);
1196 data
->cbs
[0] = ok_cb
;
1197 data
->cbs
[1] = cancel_cb
;
1201 data
->dialog
= win
= pidgin_create_dialog(PIDGIN_ALERT_TITLE
, PIDGIN_HIG_BORDER
, "multifield", TRUE
) ;
1203 data
->dialog
= win
= pidgin_create_dialog(title
, PIDGIN_HIG_BORDER
, "multifield", TRUE
) ;
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
);
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
);
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
);
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
);
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
);
1255 for (gl
= purple_request_fields_get_groups(fields
); gl
!= NULL
;
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),
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
);
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
);
1298 size_t field_count
= 0;
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
));
1317 field_count
= g_list_length(field_list
);
1319 if (field_count
> 9)
1321 rows
= field_count
/ 2;
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
)
1349 else if ((type
== PURPLE_REQUEST_FIELD_LIST
) ||
1350 (type
== PURPLE_REQUEST_FIELD_STRING
&&
1351 purple_request_field_string_is_multiline(field
)))
1364 if (col_num
>= cols
)
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
;
1382 col_num
< cols
&& fl
!= NULL
;
1383 col_num
++, fl
= fl
->next
)
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
)
1391 size_t col_offset
= col_num
* 2;
1393 size_t col_offset
= 0;
1395 PurpleRequestFieldType type
;
1396 GtkWidget
*widget
= NULL
;
1397 const char *field_label
;
1402 if (!purple_request_field_is_visible(field
)) {
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
)
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
);
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
)))
1437 gtk_table_attach_defaults(GTK_TABLE(table
), label
,
1439 row_num
, row_num
+ 1);
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
));
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
);
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
,
1485 row_num
, row_num
+ 1,
1486 GTK_FILL
| GTK_EXPAND
,
1487 GTK_FILL
| GTK_EXPAND
,
1490 else if (type
== PURPLE_REQUEST_FIELD_LIST
)
1492 gtk_table_attach(GTK_TABLE(table
), widget
,
1494 row_num
, row_num
+ 1,
1495 GTK_FILL
| GTK_EXPAND
,
1496 GTK_FILL
| GTK_EXPAND
,
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
,
1510 gtk_table_attach(GTK_TABLE(table
), widget
,
1512 row_num
, row_num
+ 1,
1513 GTK_FILL
| GTK_EXPAND
,
1514 GTK_FILL
| GTK_EXPAND
,
1518 gtk_widget_show(widget
);
1520 purple_request_field_set_ui_data(field
, widget
);
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
);
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
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
);
1548 pidgin_clear_cursor(GTK_WIDGET(data
->dialog
));
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
);
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
);
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,
1582 _("Overwrite"), G_CALLBACK(file_yes_no_cb
),
1583 _("Choose New Name"), G_CALLBACK(file_yes_no_cb
));
1585 file_yes_no_cb(data
, 1);
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
,
1595 PidginRequestData
*data
;
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
;
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...")),
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
1618 GTK_RESPONSE_ACCEPT
,
1620 gtk_dialog_set_default_response(GTK_DIALOG(filesel
), GTK_RESPONSE_ACCEPT
);
1623 current_folder
= purple_prefs_get_path(PIDGIN_PREFS_ROOT
"/filelocations/last_save_folder");
1625 current_folder
= purple_prefs_get_path(PIDGIN_PREFS_ROOT
"/filelocations/last_open_folder");
1628 if ((filename
!= NULL
) && (*filename
!= '\0')) {
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
);
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
);
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
;
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
,
1671 PidginRequestData
*data
;
1674 data
= g_new0(PidginRequestData
, 1);
1675 data
->type
= PURPLE_REQUEST_FOLDER
;
1676 data
->user_data
= user_data
;
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..."),
1686 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
,
1687 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1688 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
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
;
1707 pidgin_close_request(PurpleRequestType type
, void *ui_handle
)
1709 PidginRequestData
*data
= (PidginRequestData
*)ui_handle
;
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
);
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
,
1738 PurpleRequestUiOps
*
1739 pidgin_request_get_ui_ops(void)