pidgin/gtkrequest.c

changeset 42753
f225f7dfb0bc
parent 42729
b2db8ddd4756
child 42836
000d42c316cb
equal deleted inserted replaced
42752:cc8312cb5017 42753:f225f7dfb0bc
106 "pidgin-window-is-closing", GINT_TO_POINTER(TRUE)); 106 "pidgin-window-is-closing", GINT_TO_POINTER(TRUE));
107 gtk_widget_set_visible(GTK_WIDGET(data->dialog), FALSE); 107 gtk_widget_set_visible(GTK_WIDGET(data->dialog), FALSE);
108 } 108 }
109 109
110 static void 110 static void
111 input_response_cb(G_GNUC_UNUSED GtkDialog *dialog, gint id,
112 PidginRequestData *data)
113 {
114 const char *value;
115 char *multiline_value = NULL;
116
117 generic_response_start(data);
118
119 if(data->u.input.multiline || purple_strequal(data->u.input.hint, "html")) {
120 GtkTextBuffer *buffer = NULL;
121
122 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data->u.input.entry));
123
124 if (purple_strequal(data->u.input.hint, "html")) {
125 multiline_value = pidgin_text_buffer_get_html(buffer);
126 } else {
127 GtkTextIter start_iter, end_iter;
128
129 gtk_text_buffer_get_start_iter(buffer, &start_iter);
130 gtk_text_buffer_get_end_iter(buffer, &end_iter);
131
132 multiline_value = gtk_text_buffer_get_text(buffer, &start_iter,
133 &end_iter, FALSE);
134 }
135
136 value = multiline_value;
137 } else {
138 value = gtk_editable_get_text(GTK_EDITABLE(data->u.input.entry));
139 }
140
141 if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
142 ((PurpleRequestInputCb)data->cbs[id])(data->user_data, value);
143 else if (data->cbs[1] != NULL)
144 ((PurpleRequestInputCb)data->cbs[1])(data->user_data, value);
145
146 if (data->u.input.multiline) {
147 g_free(multiline_value);
148 }
149
150 purple_request_close(PURPLE_REQUEST_INPUT, data);
151 }
152
153 static void
154 action_response_cb(G_GNUC_UNUSED GtkDialog *dialog, gint id,
155 PidginRequestData *data)
156 {
157 generic_response_start(data);
158
159 if (id >= 0 && (gsize)id < data->cb_count && data->cbs[id] != NULL)
160 ((PurpleRequestActionCb)data->cbs[id])(data->user_data, id);
161
162 purple_request_close(PURPLE_REQUEST_INPUT, data);
163 }
164
165
166 static void
167 choice_response_cb(GtkDialog *dialog, gint id, PidginRequestData *data) {
168 GtkDropDown *dropdown = g_object_get_data(G_OBJECT(dialog), "dropdown");
169
170 generic_response_start(data);
171
172 if(0 <= id && (gsize)id < data->cb_count && data->cbs[id] != NULL) {
173 GObject *item = gtk_drop_down_get_selected_item(dropdown);
174 if(G_IS_OBJECT(item)) {
175 gpointer value = g_object_get_data(item, "choice_value");
176 ((PurpleRequestChoiceCb)data->cbs[id])(data->user_data, value);
177 }
178 }
179
180 purple_request_close(PURPLE_REQUEST_INPUT, data);
181 }
182
183 static void
184 field_choice_option_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec, 111 field_choice_option_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
185 gpointer data) 112 gpointer data)
186 { 113 {
187 PurpleRequestField *field = data; 114 PurpleRequestField *field = data;
188 GtkDropDown *dropdown = GTK_DROP_DOWN(obj); 115 GtkDropDown *dropdown = GTK_DROP_DOWN(obj);
228 if(cb != NULL) { 155 if(cb != NULL) {
229 cb(req_data->user_data, req_data->u.multifield.page); 156 cb(req_data->user_data, req_data->u.multifield.page);
230 } 157 }
231 158
232 purple_request_close(PURPLE_REQUEST_FIELDS, data); 159 purple_request_close(PURPLE_REQUEST_FIELDS, data);
233 }
234
235 static gchar *
236 pidgin_request_escape(PurpleRequestCommonParameters *cpar, const gchar *text)
237 {
238 if (text == NULL)
239 return NULL;
240
241 if (purple_request_cpar_is_html(cpar)) {
242 gboolean valid;
243
244 valid = pango_parse_markup(text, -1, 0, NULL, NULL, NULL, NULL);
245
246 if (valid)
247 return g_strdup(text);
248 else {
249 purple_debug_error("pidgin", "Passed label text is not "
250 "a valid markup. Falling back to plain text.");
251 }
252 }
253
254 return g_markup_escape_text(text, -1);
255 } 160 }
256 161
257 static GtkWidget * 162 static GtkWidget *
258 pidgin_request_dialog_icon(PurpleRequestType dialog_type, 163 pidgin_request_dialog_icon(PurpleRequestType dialog_type,
259 PurpleRequestCommonParameters *cpar) 164 PurpleRequestCommonParameters *cpar)
382 g_object_set_data(G_OBJECT(button), "pidgin-help-cb", help_cb); 287 g_object_set_data(G_OBJECT(button), "pidgin-help-cb", help_cb);
383 g_object_set_data(G_OBJECT(button), "pidgin-help-data", help_data); 288 g_object_set_data(G_OBJECT(button), "pidgin-help-data", help_data);
384 289
385 g_signal_connect(G_OBJECT(button), "clicked", 290 g_signal_connect(G_OBJECT(button), "clicked",
386 G_CALLBACK(pidgin_request_help_clicked), NULL); 291 G_CALLBACK(pidgin_request_help_clicked), NULL);
387 }
388
389 static void *
390 pidgin_request_input(const char *title, const char *primary,
391 const char *secondary, const char *default_value,
392 gboolean multiline, gboolean masked, gchar *hint,
393 const char *ok_text, GCallback ok_cb,
394 const char *cancel_text, GCallback cancel_cb,
395 PurpleRequestCommonParameters *cpar,
396 void *user_data)
397 {
398 PidginRequestData *data;
399 GtkWidget *dialog;
400 GtkWidget *vbox;
401 GtkWidget *hbox;
402 GtkLabel *label;
403 GtkWidget *img;
404 GtkWidget *content;
405 char *label_text;
406 char *primary_esc, *secondary_esc;
407
408 data = g_new0(PidginRequestData, 1);
409 data->type = PURPLE_REQUEST_INPUT;
410 data->user_data = user_data;
411
412 data->cb_count = 2;
413 data->cbs = g_new0(GCallback, 2);
414
415 data->cbs[0] = ok_cb;
416 data->cbs[1] = cancel_cb;
417
418 /* Create the dialog. */
419 dialog = gtk_dialog_new_with_buttons(title ? title : PIDGIN_ALERT_TITLE,
420 NULL, 0,
421 cancel_text, 1,
422 ok_text, 0,
423 NULL);
424 data->dialog = dialog;
425
426 g_signal_connect(G_OBJECT(dialog), "response",
427 G_CALLBACK(input_response_cb), data);
428
429 /* Setup the dialog */
430 gtk_widget_set_margin_top(dialog, 6);
431 gtk_widget_set_margin_bottom(dialog, 6);
432 gtk_widget_set_margin_start(dialog, 6);
433 gtk_widget_set_margin_end(dialog, 6);
434
435 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
436 gtk_widget_set_margin_top(content, 6);
437 gtk_widget_set_margin_bottom(content, 6);
438 gtk_widget_set_margin_start(content, 6);
439 gtk_widget_set_margin_end(content, 6);
440
441 if (!multiline)
442 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
443 gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
444 gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
445 12);
446
447 /* Setup the main horizontal box */
448 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
449 gtk_box_append(GTK_BOX(content), hbox);
450
451 /* Dialog icon. */
452 img = pidgin_request_dialog_icon(PURPLE_REQUEST_INPUT, cpar);
453 gtk_widget_set_halign(img, GTK_ALIGN_START);
454 gtk_widget_set_valign(img, GTK_ALIGN_START);
455 gtk_box_append(GTK_BOX(hbox), img);
456
457 pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
458
459 /* Vertical box */
460 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
461 gtk_widget_set_hexpand(vbox, TRUE);
462 gtk_box_append(GTK_BOX(hbox), vbox);
463
464 pidgin_widget_decorate_account(vbox,
465 purple_request_cpar_get_account(cpar));
466
467 /* Descriptive label */
468 primary_esc = pidgin_request_escape(cpar, primary);
469 secondary_esc = pidgin_request_escape(cpar, secondary);
470 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
471 "%s</span>%s%s" : "%s%s%s"),
472 (primary ? primary_esc : ""),
473 ((primary && secondary) ? "\n\n" : ""),
474 (secondary ? secondary_esc : ""));
475 g_free(primary_esc);
476 g_free(secondary_esc);
477
478 label = GTK_LABEL(gtk_label_new(NULL));
479
480 gtk_label_set_markup(label, label_text);
481 gtk_label_set_wrap(label, TRUE);
482 gtk_label_set_xalign(label, 0);
483 gtk_label_set_yalign(label, 0);
484 gtk_box_append(GTK_BOX(vbox), GTK_WIDGET(label));
485
486 g_free(label_text);
487
488 /* Entry field. */
489 data->u.input.multiline = multiline;
490 data->u.input.hint = g_strdup(hint);
491
492 if(multiline || purple_strequal(data->u.input.hint, "html")) {
493 GtkWidget *sw = NULL;
494 GtkWidget *view = NULL;
495 GtkTextBuffer *buffer = NULL;
496
497 sw = gtk_scrolled_window_new();
498 gtk_widget_set_vexpand(sw, TRUE);
499 gtk_box_append(GTK_BOX(vbox), sw);
500
501 view = gtk_text_view_new();
502 gtk_widget_set_size_request(view, 320, 130);
503 gtk_widget_set_name(view, "pidgin_request_input");
504 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
505
506 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
507
508 if(!purple_strempty(default_value)) {
509 if(purple_strequal(data->u.input.hint, "html")) {
510 GtkTextIter start;
511
512 gtk_text_buffer_get_start_iter(buffer, &start);
513 gtk_text_buffer_insert_markup(buffer, &start, default_value,
514 -1);
515 } else {
516 gtk_text_buffer_set_text(buffer, default_value, -1);
517 }
518 }
519
520 data->u.input.entry = view;
521 } else {
522 GtkWidget *entry = NULL;
523
524 if(masked) {
525 entry = gtk_password_entry_new();
526 g_object_set(entry, "activates-default", TRUE,
527 "show-peek-icon", TRUE, NULL);
528 } else {
529 entry = gtk_entry_new();
530 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
531 }
532
533 gtk_box_append(GTK_BOX(vbox), entry);
534
535 if(default_value != NULL) {
536 gtk_editable_set_text(GTK_EDITABLE(entry), default_value);
537 }
538
539 data->u.input.entry = entry;
540 }
541
542 pidgin_set_accessible_label(data->u.input.entry, label);
543
544 pidgin_auto_parent_window(dialog);
545
546 /* Show everything. */
547 gtk_widget_set_visible(dialog, TRUE);
548
549 return data;
550 }
551
552 static void *
553 pidgin_request_choice(const char *title, const char *primary,
554 const char *secondary, gpointer default_value, const char *ok_text,
555 GCallback ok_cb, const char *cancel_text, GCallback cancel_cb,
556 PurpleRequestCommonParameters *cpar, void *user_data, va_list args)
557 {
558 PidginRequestData *data;
559 GtkWidget *dialog;
560 GtkWidget *vbox;
561 GtkWidget *hbox;
562 GtkWidget *label;
563 GtkWidget *img;
564 GtkWidget *dropdown;
565 GListModel *model;
566 GtkWidget *content;
567 char *label_text;
568 const char *radio_text;
569 char *primary_esc, *secondary_esc;
570 guint index, selected;
571
572 data = g_new0(PidginRequestData, 1);
573 data->type = PURPLE_REQUEST_ACTION;
574 data->user_data = user_data;
575
576 data->cb_count = 2;
577 data->cbs = g_new0(GCallback, 2);
578 data->cbs[0] = cancel_cb;
579 data->cbs[1] = ok_cb;
580
581 /* Create the dialog. */
582 data->dialog = dialog = gtk_dialog_new();
583
584 if (title != NULL)
585 gtk_window_set_title(GTK_WINDOW(dialog), title);
586 #ifdef _WIN32
587 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
588 #endif
589
590 gtk_dialog_add_button(GTK_DIALOG(dialog), cancel_text, 0);
591 gtk_dialog_add_button(GTK_DIALOG(dialog), ok_text, 1);
592
593 g_signal_connect(G_OBJECT(dialog), "response",
594 G_CALLBACK(choice_response_cb), data);
595
596 /* Setup the dialog */
597 gtk_widget_set_margin_top(dialog, 6);
598 gtk_widget_set_margin_bottom(dialog, 6);
599 gtk_widget_set_margin_start(dialog, 6);
600 gtk_widget_set_margin_end(dialog, 6);
601 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
602
603 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
604 gtk_widget_set_margin_top(content, 6);
605 gtk_widget_set_margin_bottom(content, 6);
606 gtk_widget_set_margin_start(content, 6);
607 gtk_widget_set_margin_end(content, 6);
608 gtk_box_set_spacing(GTK_BOX(content), 12);
609
610 /* Setup the main horizontal box */
611 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
612 gtk_box_append(GTK_BOX(content), hbox);
613
614 /* Dialog icon. */
615 img = pidgin_request_dialog_icon(PURPLE_REQUEST_CHOICE, cpar);
616 gtk_widget_set_halign(img, GTK_ALIGN_START);
617 gtk_widget_set_valign(img, GTK_ALIGN_START);
618 gtk_box_append(GTK_BOX(hbox), img);
619
620 pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
621
622 /* Vertical box */
623 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
624 gtk_box_append(GTK_BOX(hbox), vbox);
625
626 pidgin_widget_decorate_account(vbox,
627 purple_request_cpar_get_account(cpar));
628
629 /* Descriptive label */
630 primary_esc = pidgin_request_escape(cpar, primary);
631 secondary_esc = pidgin_request_escape(cpar, secondary);
632 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
633 "%s</span>%s%s" : "%s%s%s"),
634 (primary ? primary_esc : ""),
635 ((primary && secondary) ? "\n\n" : ""),
636 (secondary ? secondary_esc : ""));
637 g_free(primary_esc);
638 g_free(secondary_esc);
639
640 label = gtk_label_new(NULL);
641
642 gtk_label_set_markup(GTK_LABEL(label), label_text);
643 gtk_label_set_wrap(GTK_LABEL(label), TRUE);
644 gtk_label_set_xalign(GTK_LABEL(label), 0);
645 gtk_label_set_yalign(GTK_LABEL(label), 0);
646 gtk_widget_set_vexpand(label, TRUE);
647 gtk_box_append(GTK_BOX(vbox), label);
648
649 g_free(label_text);
650
651 dropdown = gtk_drop_down_new_from_strings(NULL);
652 gtk_box_append(GTK_BOX(vbox), dropdown);
653 g_object_set_data(G_OBJECT(dialog), "dropdown", dropdown);
654
655 index = 0;
656 selected = GTK_INVALID_LIST_POSITION;
657 model = gtk_drop_down_get_model(GTK_DROP_DOWN(dropdown));
658 while((radio_text = va_arg(args, const char *))) {
659 GObject *item = NULL;
660 gpointer resp = va_arg(args, gpointer);
661
662 gtk_string_list_append(GTK_STRING_LIST(model), radio_text);
663 item = g_list_model_get_item(model, index);
664 g_object_set_data(item, "choice_value", resp);
665 if (resp == default_value) {
666 selected = index;
667 }
668
669 g_clear_object(&item);
670 index++;
671 }
672
673 if(selected != GTK_INVALID_LIST_POSITION) {
674 gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), selected);
675 }
676
677 /* Show everything. */
678 pidgin_auto_parent_window(dialog);
679
680 gtk_widget_set_visible(dialog, TRUE);
681
682 return data;
683 }
684
685 static void *
686 pidgin_request_action(const char *title, const char *primary,
687 const char *secondary, int default_action,
688 PurpleRequestCommonParameters *cpar, void *user_data,
689 size_t action_count, va_list actions)
690 {
691 PidginRequestData *data;
692 GtkWidget *dialog;
693 GtkWidget *vbox;
694 GtkWidget *hbox;
695 GtkWidget *label;
696 GtkWidget *img = NULL;
697 GtkWidget *content;
698 void **buttons;
699 char *label_text;
700 char *primary_esc, *secondary_esc;
701 gsize i;
702
703 data = g_new0(PidginRequestData, 1);
704 data->type = PURPLE_REQUEST_ACTION;
705 data->user_data = user_data;
706
707 data->cb_count = action_count;
708 data->cbs = g_new0(GCallback, action_count);
709
710 /* Reverse the buttons */
711 buttons = g_new0(void *, action_count * 2);
712
713 for (i = 0; i < action_count * 2; i += 2) {
714 buttons[(action_count * 2) - i - 2] = va_arg(actions, char *);
715 buttons[(action_count * 2) - i - 1] = va_arg(actions, GCallback);
716 }
717
718 /* Create the dialog. */
719 data->dialog = dialog = gtk_dialog_new();
720
721 gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE);
722
723 if (title != NULL)
724 gtk_window_set_title(GTK_WINDOW(dialog), title);
725 #ifdef _WIN32
726 else
727 gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
728 #endif
729
730 for (i = 0; i < action_count; i++) {
731 gtk_dialog_add_button(GTK_DIALOG(dialog), buttons[2 * i], i);
732
733 data->cbs[i] = buttons[2 * i + 1];
734 }
735
736 g_free(buttons);
737
738 g_signal_connect(G_OBJECT(dialog), "response",
739 G_CALLBACK(action_response_cb), data);
740
741 /* Setup the dialog */
742 gtk_widget_set_margin_top(dialog, 6);
743 gtk_widget_set_margin_bottom(dialog, 6);
744 gtk_widget_set_margin_start(dialog, 6);
745 gtk_widget_set_margin_end(dialog, 6);
746 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
747
748 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
749 gtk_widget_set_margin_top(content, 6);
750 gtk_widget_set_margin_bottom(content, 6);
751 gtk_widget_set_margin_start(content, 6);
752 gtk_widget_set_margin_end(content, 6);
753 gtk_box_set_spacing(GTK_BOX(content), 12);
754
755 /* Setup the main horizontal box */
756 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
757 gtk_box_append(GTK_BOX(content), hbox);
758
759 img = pidgin_request_dialog_icon(PURPLE_REQUEST_ACTION, cpar);
760 gtk_widget_set_halign(img, GTK_ALIGN_START);
761 gtk_widget_set_valign(img, GTK_ALIGN_START);
762 gtk_box_append(GTK_BOX(hbox), img);
763
764 /* Vertical box */
765 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
766 gtk_box_append(GTK_BOX(hbox), vbox);
767
768 pidgin_widget_decorate_account(vbox,
769 purple_request_cpar_get_account(cpar));
770
771 pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
772
773 /* Descriptive label */
774 primary_esc = pidgin_request_escape(cpar, primary);
775 secondary_esc = pidgin_request_escape(cpar, secondary);
776 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
777 "%s</span>%s%s" : "%s%s%s"),
778 (primary ? primary_esc : ""),
779 ((primary && secondary) ? "\n\n" : ""),
780 (secondary ? secondary_esc : ""));
781 g_free(primary_esc);
782 g_free(secondary_esc);
783
784 label = gtk_label_new(NULL);
785
786 gtk_label_set_markup(GTK_LABEL(label), label_text);
787 gtk_label_set_wrap(GTK_LABEL(label), TRUE);
788 gtk_label_set_xalign(GTK_LABEL(label), 0);
789 gtk_label_set_yalign(GTK_LABEL(label), 0);
790 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
791 gtk_widget_set_vexpand(label, TRUE);
792 gtk_box_append(GTK_BOX(vbox), label);
793
794 g_free(label_text);
795
796
797 if (default_action != PURPLE_DEFAULT_ACTION_NONE) {
798 /*
799 * Need to invert the default_action number because the
800 * buttons are added to the dialog in reverse order.
801 */
802 gtk_dialog_set_default_response(GTK_DIALOG(dialog), action_count - 1 - default_action);
803 }
804
805 /* Show everything. */
806 pidgin_auto_parent_window(dialog);
807
808 gtk_widget_set_visible(dialog, TRUE);
809
810 return data;
811 }
812
813 static void
814 wait_response_cb(G_GNUC_UNUSED GtkDialog *dialog, G_GNUC_UNUSED gint id,
815 PidginRequestData *data)
816 {
817 generic_response_start(data);
818
819 if (data->cbs[0] != NULL)
820 ((PurpleRequestCancelCb)data->cbs[0])(data->user_data);
821
822 purple_request_close(PURPLE_REQUEST_FIELDS, data);
823 }
824
825 static void *
826 pidgin_request_wait(const char *title, const char *primary,
827 const char *secondary, gboolean with_progress,
828 PurpleRequestCancelCb cancel_cb, PurpleRequestCommonParameters *cpar,
829 void *user_data)
830 {
831 PidginRequestData *data;
832 GtkWidget *dialog, *content;
833 GtkWidget *hbox, *vbox, *img, *label;
834 gchar *primary_esc, *secondary_esc, *label_text;
835
836 data = g_new0(PidginRequestData, 1);
837 data->type = PURPLE_REQUEST_WAIT;
838 data->user_data = user_data;
839
840 data->cb_count = 1;
841 data->cbs = g_new0(GCallback, 1);
842 data->cbs[0] = (GCallback)cancel_cb;
843
844 data->dialog = dialog = gtk_dialog_new();
845
846 g_signal_connect(G_OBJECT(dialog), "response",
847 G_CALLBACK(wait_response_cb), data);
848
849 gtk_window_set_deletable(GTK_WINDOW(data->dialog), cancel_cb != NULL);
850
851 if (title != NULL)
852 gtk_window_set_title(GTK_WINDOW(dialog), title);
853 else
854 gtk_window_set_title(GTK_WINDOW(dialog), _("Please wait"));
855
856 /* Setup the dialog */
857 gtk_widget_set_margin_top(dialog, 6);
858 gtk_widget_set_margin_bottom(dialog, 6);
859 gtk_widget_set_margin_start(dialog, 6);
860 gtk_widget_set_margin_end(dialog, 6);
861 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
862
863 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
864 gtk_widget_set_margin_top(content, 6);
865 gtk_widget_set_margin_bottom(content, 6);
866 gtk_widget_set_margin_start(content, 6);
867 gtk_widget_set_margin_end(content, 6);
868 gtk_box_set_spacing(GTK_BOX(content), 12);
869
870 /* Setup the main horizontal box */
871 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
872 gtk_box_append(GTK_BOX(content), hbox);
873
874 img = pidgin_request_dialog_icon(PURPLE_REQUEST_WAIT, cpar);
875 gtk_widget_set_halign(img, GTK_ALIGN_START);
876 gtk_widget_set_valign(img, GTK_ALIGN_START);
877 gtk_box_append(GTK_BOX(hbox), img);
878
879 /* Cancel button */
880 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL);
881
882 /* Vertical box */
883 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
884 gtk_box_append(GTK_BOX(hbox), vbox);
885
886 pidgin_widget_decorate_account(vbox,
887 purple_request_cpar_get_account(cpar));
888
889 pidgin_request_add_help(GTK_DIALOG(dialog), cpar);
890
891 /* Descriptive label */
892 primary_esc = pidgin_request_escape(cpar, primary);
893 secondary_esc = pidgin_request_escape(cpar, secondary);
894 label_text = g_strdup_printf((primary ? "<span weight=\"bold\" "
895 "size=\"larger\">%s</span>%s%s" : "%s%s%s"),
896 (primary ? primary_esc : ""),
897 ((primary && secondary) ? "\n\n" : ""),
898 (secondary ? secondary_esc : ""));
899 g_free(primary_esc);
900 g_free(secondary_esc);
901
902 label = gtk_label_new(NULL);
903
904 gtk_label_set_markup(GTK_LABEL(label), label_text);
905 gtk_label_set_wrap(GTK_LABEL(label), TRUE);
906 gtk_label_set_xalign(GTK_LABEL(label), 0);
907 gtk_label_set_yalign(GTK_LABEL(label), 0);
908 gtk_label_set_selectable(GTK_LABEL(label), FALSE);
909 gtk_widget_set_vexpand(label, TRUE);
910 gtk_box_append(GTK_BOX(vbox), label);
911
912 g_free(label_text);
913
914 if (with_progress) {
915 GtkProgressBar *bar;
916
917 bar = data->u.wait.progress_bar =
918 GTK_PROGRESS_BAR(gtk_progress_bar_new());
919 gtk_progress_bar_set_fraction(bar, 0);
920 gtk_box_append(GTK_BOX(vbox), GTK_WIDGET(bar));
921 }
922
923 /* Show everything. */
924 pidgin_auto_parent_window(dialog);
925
926 gtk_widget_set_visible(dialog, TRUE);
927
928 return data;
929 }
930
931 static void
932 pidgin_request_wait_update(void *ui_handle, gboolean pulse, gfloat fraction)
933 {
934 GtkProgressBar *bar;
935 PidginRequestData *data = ui_handle;
936
937 g_return_if_fail(data->type == PURPLE_REQUEST_WAIT);
938
939 bar = data->u.wait.progress_bar;
940 if (pulse)
941 gtk_progress_bar_pulse(bar);
942 else
943 gtk_progress_bar_set_fraction(bar, fraction);
944 } 292 }
945 293
946 static GtkWidget * 294 static GtkWidget *
947 create_label_field(void) { 295 create_label_field(void) {
948 GtkWidget *row = NULL; 296 GtkWidget *row = NULL;
1617 gtk_widget_set_visible(win, TRUE); 965 gtk_widget_set_visible(win, TRUE);
1618 966
1619 return data; 967 return data;
1620 } 968 }
1621 969
1622 static void
1623 pidgin_request_file_response_cb(GObject *obj, GAsyncResult *result,
1624 gpointer user_data)
1625 {
1626 PidginRequestData *data = user_data;
1627 GFile *path = NULL;
1628 GFile *parent = NULL;
1629 GError *error = NULL;
1630
1631 if(data->u.file.savedialog) {
1632 path = gtk_file_dialog_save_finish(GTK_FILE_DIALOG(obj), result,
1633 &error);
1634 } else {
1635 path = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(obj), result,
1636 &error);
1637 }
1638 if(path == NULL) {
1639 if(!g_error_matches(error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED)) {
1640 if(data->cbs[0] != NULL) {
1641 ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
1642 }
1643 purple_request_close(data->type, data);
1644 }
1645 g_clear_error(&error);
1646 return;
1647 }
1648
1649 parent = g_file_get_parent(path);
1650 if(parent != NULL) {
1651 char *current_folder = g_file_get_path(parent);
1652 if (data->u.file.savedialog) {
1653 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder",
1654 current_folder);
1655 } else {
1656 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder",
1657 current_folder);
1658 }
1659 g_free(current_folder);
1660 }
1661
1662 if(data->cbs[1] != NULL) {
1663 char *filename = g_file_get_path(path);
1664 ((PurpleRequestFileCb)data->cbs[1])(data->user_data, filename);
1665 g_free(filename);
1666 }
1667
1668 g_clear_object(&parent);
1669 g_clear_object(&path);
1670 g_clear_object(&data->cancellable);
1671 purple_request_close(data->type, data);
1672 }
1673
1674 static void
1675 pidgin_request_folder_response_cb(GObject *obj, GAsyncResult *result,
1676 gpointer user_data)
1677 {
1678 PidginRequestData *data = user_data;
1679 GFile *path = NULL;
1680 char *folder = NULL;
1681 GError *error = NULL;
1682
1683 path = gtk_file_dialog_select_folder_finish(GTK_FILE_DIALOG(obj), result,
1684 &error);
1685 if(path == NULL) {
1686 if(!g_error_matches(error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED)) {
1687 if(data->cbs[0] != NULL) {
1688 ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
1689 }
1690 purple_request_close(data->type, data);
1691 }
1692 g_clear_error(&error);
1693 return;
1694 }
1695
1696 folder = g_file_get_path(path);
1697 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder",
1698 folder);
1699
1700 if(data->cbs[1] != NULL) {
1701 ((PurpleRequestFileCb)data->cbs[1])(data->user_data, folder);
1702 }
1703
1704 g_free(folder);
1705 g_clear_object(&path);
1706 g_clear_object(&data->cancellable);
1707 purple_request_close(data->type, data);
1708 }
1709
1710 static void *
1711 pidgin_request_file(const char *title, const char *filename,
1712 gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
1713 G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
1714 gpointer user_data)
1715 {
1716 PidginRequestData *data;
1717 GtkFileDialog *dialog = NULL;
1718 #ifdef _WIN32
1719 const gchar *current_folder;
1720 gboolean folder_set = FALSE;
1721 #endif
1722
1723 data = g_new0(PidginRequestData, 1);
1724 data->type = PURPLE_REQUEST_FILE;
1725 data->user_data = user_data;
1726 data->cb_count = 2;
1727 data->cbs = g_new0(GCallback, 2);
1728 data->cbs[0] = cancel_cb;
1729 data->cbs[1] = ok_cb;
1730 data->u.file.savedialog = savedialog;
1731
1732 data->dialog = dialog = gtk_file_dialog_new();
1733 gtk_file_dialog_set_title(dialog,
1734 title ? title
1735 : (savedialog ? _("Save File...")
1736 : _("Open File...")));
1737
1738 if(!purple_strempty(filename)) {
1739 GFile *path = g_file_new_for_path(filename);
1740
1741 if(savedialog || g_file_test(filename, G_FILE_TEST_EXISTS)) {
1742 gtk_file_dialog_set_initial_file(dialog, path);
1743 }
1744
1745 g_object_unref(path);
1746 }
1747
1748 #ifdef _WIN32
1749 if (savedialog) {
1750 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
1751 } else {
1752 current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
1753 }
1754
1755 if((purple_strempty(filename) || !g_file_test(filename, G_FILE_TEST_EXISTS)) &&
1756 !purple_strempty(current_folder))
1757 {
1758 GFile *file = g_file_new_for_path(current_folder);
1759 gtk_file_dialog_set_initial_folder(dialog, file);
1760 folder_set = TRUE;
1761 g_clear_object(&file);
1762 }
1763
1764 if(!folder_set &&
1765 (purple_strempty(filename) || !g_file_test(filename, G_FILE_TEST_EXISTS)))
1766 {
1767 const char *my_documents = NULL;
1768
1769 my_documents = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS);
1770 if (my_documents != NULL) {
1771 GFile *file = g_file_new_for_path(my_documents);
1772
1773 gtk_file_dialog_set_initial_folder(dialog, file);
1774
1775 g_clear_object(&file);
1776 }
1777 }
1778 #endif
1779
1780 data->cancellable = g_cancellable_new();
1781 if(savedialog) {
1782 gtk_file_dialog_save(dialog, NULL, data->cancellable,
1783 pidgin_request_file_response_cb, data);
1784 } else {
1785 gtk_file_dialog_open(dialog, NULL, data->cancellable,
1786 pidgin_request_file_response_cb, data);
1787 }
1788
1789 return (void *)data;
1790 }
1791
1792 static void *
1793 pidgin_request_folder(const char *title, const char *dirname, GCallback ok_cb,
1794 GCallback cancel_cb,
1795 G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
1796 gpointer user_data)
1797 {
1798 PidginRequestData *data;
1799 GtkFileDialog *dialog = NULL;
1800
1801 data = g_new0(PidginRequestData, 1);
1802 data->type = PURPLE_REQUEST_FOLDER;
1803 data->user_data = user_data;
1804 data->cb_count = 2;
1805 data->cbs = g_new0(GCallback, 2);
1806 data->cbs[0] = cancel_cb;
1807 data->cbs[1] = ok_cb;
1808 data->u.file.savedialog = FALSE;
1809
1810 data->cancellable = g_cancellable_new();
1811 data->dialog = dialog = gtk_file_dialog_new();
1812 gtk_file_dialog_set_title(dialog, title ? title : _("Select Folder..."));
1813
1814 if(!purple_strempty(dirname)) {
1815 GFile *path = g_file_new_for_path(dirname);
1816 gtk_file_dialog_set_initial_folder(dialog, path);
1817 g_object_unref(path);
1818 }
1819
1820 gtk_file_dialog_select_folder(dialog, NULL, data->cancellable,
1821 pidgin_request_folder_response_cb, data);
1822
1823 return (void *)data;
1824 }
1825
1826 /* if request callback issues another request, it should be attached to the
1827 * primary request parent */
1828 static void
1829 pidgin_window_detach_children(GtkWindow* win)
1830 {
1831 GList *it;
1832 GtkWindow *par;
1833
1834 g_return_if_fail(win != NULL);
1835
1836 par = gtk_window_get_transient_for(win);
1837 it = gtk_window_list_toplevels();
1838 for (it = g_list_first(it); it != NULL; it = g_list_delete_link(it, it)) {
1839 GtkWindow *child = GTK_WINDOW(it->data);
1840 if (gtk_window_get_transient_for(child) != win)
1841 continue;
1842 if (gtk_window_get_destroy_with_parent(child)) {
1843 #ifdef _WIN32
1844 /* XXX test/verify it: Win32 gtk ignores
1845 * gtk_window_set_destroy_with_parent(..., FALSE). */
1846 gtk_window_set_transient_for(child, NULL);
1847 #endif
1848 continue;
1849 }
1850 gtk_window_set_transient_for(child, par);
1851 }
1852 }
1853
1854 static void
1855 pidgin_close_request(PurpleRequestType type, void *ui_handle)
1856 {
1857 PidginRequestData *data = (PidginRequestData *)ui_handle;
1858
1859 if(data->cancellable != NULL) {
1860 g_cancellable_cancel(data->cancellable);
1861 }
1862
1863 if (type == PURPLE_REQUEST_FILE || type == PURPLE_REQUEST_FOLDER) {
1864 /* Will be a GtkFileDialog, not GtkDialog. */
1865 g_clear_object(&data->dialog);
1866 } else {
1867 pidgin_window_detach_children(GTK_WINDOW(data->dialog));
1868
1869 gtk_window_destroy(GTK_WINDOW(data->dialog));
1870 }
1871
1872 if(type == PURPLE_REQUEST_FIELDS) {
1873 g_clear_object(&data->u.multifield.page);
1874 }
1875
1876 g_clear_object(&data->cancellable);
1877 g_free(data->cbs);
1878 g_free(data);
1879 }
1880
1881 GtkWindow * 970 GtkWindow *
1882 pidgin_request_get_dialog_window(void *ui_handle) 971 pidgin_request_get_dialog_window(void *ui_handle)
1883 { 972 {
1884 PidginRequestData *data = ui_handle; 973 PidginRequestData *data = ui_handle;
1885 974
1895 984
1896 return GTK_WINDOW(data->dialog); 985 return GTK_WINDOW(data->dialog);
1897 } 986 }
1898 987
1899 static PurpleRequestUiOps ops = { 988 static PurpleRequestUiOps ops = {
1900 .features = PURPLE_REQUEST_FEATURE_HTML,
1901 .request_input = pidgin_request_input,
1902 .request_choice = pidgin_request_choice,
1903 .request_action = pidgin_request_action,
1904 .request_wait = pidgin_request_wait,
1905 .request_wait_update = pidgin_request_wait_update,
1906 .request_fields = pidgin_request_fields, 989 .request_fields = pidgin_request_fields,
1907 .request_file = pidgin_request_file,
1908 .request_folder = pidgin_request_folder,
1909 .close_request = pidgin_close_request,
1910 }; 990 };
1911 991
1912 PurpleRequestUiOps * 992 PurpleRequestUiOps *
1913 pidgin_request_get_ui_ops(void) 993 pidgin_request_get_ui_ops(void)
1914 { 994 {

mercurial