1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "gtkbutton.h"
31 #include "gtkdialog.h"
32 #include "gtkdialogprivate.h"
33 #include "gtkheaderbar.h"
34 #include "gtkbbox.h"
35 #include "gtklabel.h"
36 #include "gtkmarshalers.h"
37 #include "gtkbox.h"
38 #include "gtkboxprivate.h"
39 #include "gtkcontainerprivate.h"
40 #include "gtkmain.h"
41 #include "gtkintl.h"
42 #include "gtkbindings.h"
43 #include "gtkprivate.h"
44 #include "gtkbuildable.h"
45 #include "gtkbuilderprivate.h"
46 #include "gtksettings.h"
47 #include "gtktypebuiltins.h"
48 #include "deprecated/gtkstock.h"
49 #include "gtksizegroup.h"
50
51 /**
52 * SECTION:gtkdialog
53 * @Short_description: Create popup windows
54 * @Title: GtkDialog
55 * @See_also: #GtkVBox, #GtkWindow, #GtkButton
56 *
57 * Dialog boxes are a convenient way to prompt the user for a small amount
58 * of input, e.g. to display a message, ask a question, or anything else
59 * that does not require extensive effort on the user’s part.
60 *
61 * GTK+ treats a dialog as a window split vertically. The top section is a
62 * #GtkVBox, and is where widgets such as a #GtkLabel or a #GtkEntry should
63 * be packed. The bottom area is known as the
64 * “action area”. This is generally used for
65 * packing buttons into the dialog which may perform functions such as
66 * cancel, ok, or apply.
67 *
68 * #GtkDialog boxes are created with a call to gtk_dialog_new() or
69 * gtk_dialog_new_with_buttons(). gtk_dialog_new_with_buttons() is
70 * recommended; it allows you to set the dialog title, some convenient
71 * flags, and add simple buttons.
72 *
73 * If “dialog” is a newly created dialog, the two primary areas of the
74 * window can be accessed through gtk_dialog_get_content_area() and
75 * gtk_dialog_get_action_area(), as can be seen from the example below.
76 *
77 * A “modal” dialog (that is, one which freezes the rest of the application
78 * from user input), can be created by calling gtk_window_set_modal() on the
79 * dialog. Use the GTK_WINDOW() macro to cast the widget returned from
80 * gtk_dialog_new() into a #GtkWindow. When using gtk_dialog_new_with_buttons()
81 * you can also pass the #GTK_DIALOG_MODAL flag to make a dialog modal.
82 *
83 * If you add buttons to #GtkDialog using gtk_dialog_new_with_buttons(),
84 * gtk_dialog_add_button(), gtk_dialog_add_buttons(), or
85 * gtk_dialog_add_action_widget(), clicking the button will emit a signal
86 * called #GtkDialog::response with a response ID that you specified. GTK+
87 * will never assign a meaning to positive response IDs; these are entirely
88 * user-defined. But for convenience, you can use the response IDs in the
89 * #GtkResponseType enumeration (these all have values less than zero). If
90 * a dialog receives a delete event, the #GtkDialog::response signal will
91 * be emitted with a response ID of #GTK_RESPONSE_DELETE_EVENT.
92 *
93 * If you want to block waiting for a dialog to return before returning
94 * control flow to your code, you can call gtk_dialog_run(). This function
95 * enters a recursive main loop and waits for the user to respond to the
96 * dialog, returning the response ID corresponding to the button the user
97 * clicked.
98 *
99 * For the simple dialog in the following example, in reality you’d probably
100 * use #GtkMessageDialog to save yourself some effort. But you’d need to
101 * create the dialog contents manually if you had more than a simple message
102 * in the dialog.
103 *
104 * An example for simple GtkDialog usage:
105 * |[<!-- language="C" -->
106 * // Function to open a dialog box with a message
107 * void
108 * quick_message (GtkWindow *parent, gchar *message)
109 * {
110 * GtkWidget *dialog, *label, *content_area;
111 * GtkDialogFlags flags;
112 *
113 * // Create the widgets
114 * flags = GTK_DIALOG_DESTROY_WITH_PARENT;
115 * dialog = gtk_dialog_new_with_buttons ("Message",
116 * parent,
117 * flags,
118 * _("_OK"),
119 * GTK_RESPONSE_NONE,
120 * NULL);
121 * content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
122 * label = gtk_label_new (message);
123 *
124 * // Ensure that the dialog box is destroyed when the user responds
125 *
126 * g_signal_connect_swapped (dialog,
127 * "response",
128 * G_CALLBACK (gtk_widget_destroy),
129 * dialog);
130 *
131 * // Add the label, and show everything we’ve added
132 *
133 * gtk_container_add (GTK_CONTAINER (content_area), label);
134 * gtk_widget_show_all (dialog);
135 * }
136 * ]|
137 *
138 * # GtkDialog as GtkBuildable
139 *
140 * The GtkDialog implementation of the #GtkBuildable interface exposes the
141 * @vbox and @action_area as internal children with the names “vbox” and
142 * “action_area”.
143 *
144 * GtkDialog supports a custom `<action-widgets>` element, which can contain
145 * multiple `<action-widget>` elements. The “response” attribute specifies a
146 * numeric response, and the content of the element is the id of widget
147 * (which should be a child of the dialogs @action_area). To mark a response
148 * as default, set the “default“ attribute of the `<action-widget>` element
149 * to true.
150 *
151 * GtkDialog supports adding action widgets by specifying “action“ as
152 * the “type“ attribute of a `<child>` element. The widget will be added
153 * either to the action area or the headerbar of the dialog, depending
154 * on the “use-header-bar“ property. The response id has to be associated
155 * with the action widget using the `<action-widgets>` element.
156 *
157 * An example of a #GtkDialog UI definition fragment:
158 *
159 * |[<!-- language="xml" -->
160 * <object class="GtkDialog" id="dialog1">
161 * <child type="action">
162 * <object class="GtkButton" id="button_cancel"/>
163 * </child>
164 * <child type="action">
165 * <object class="GtkButton" id="button_ok">
166 * <property name="can-default">True</property>
167 * </object>
168 * </child>
169 * <action-widgets>
170 * <action-widget response="cancel">button_cancel</action-widget>
171 * <action-widget response="ok" default="true">button_ok</action-widget>
172 * </action-widgets>
173 * </object>
174 * ]|
175 */
176
177 struct _GtkDialogPrivate
178 {
179 GtkWidget *vbox;
180 GtkWidget *headerbar;
181 GtkWidget *action_area;
182 GtkWidget *action_box;
183 GtkSizeGroup *size_group;
184
185 gint use_header_bar;
186 gboolean constructed;
187 };
188
189 typedef struct _ResponseData ResponseData;
190
191 struct _ResponseData
192 {
193 gint response_id;
194 };
195
196 static void gtk_dialog_add_buttons_valist (GtkDialog *dialog,
197 const gchar *first_button_text,
198 va_list args);
199
200 static gboolean gtk_dialog_delete_event_handler (GtkWidget *widget,
201 GdkEventAny *event,
202 gpointer user_data);
203 static void gtk_dialog_style_updated (GtkWidget *widget);
204 static void gtk_dialog_map (GtkWidget *widget);
205
206 static void gtk_dialog_close (GtkDialog *dialog);
207
208 static ResponseData * get_response_data (GtkWidget *widget,
209 gboolean create);
210
211 static void gtk_dialog_buildable_interface_init (GtkBuildableIface *iface);
212 static gboolean gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable,
213 GtkBuilder *builder,
214 GObject *child,
215 const gchar *tagname,
216 GMarkupParser *parser,
217 gpointer *data);
218 static void gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
219 GtkBuilder *builder,
220 GObject *child,
221 const gchar *tagname,
222 gpointer user_data);
223 static void gtk_dialog_buildable_add_child (GtkBuildable *buildable,
224 GtkBuilder *builder,
225 GObject *child,
226 const gchar *type);
227
228
229 enum {
230 PROP_0,
231 PROP_USE_HEADER_BAR
232 };
233
234 enum {
235 RESPONSE,
236 CLOSE,
237 LAST_SIGNAL
238 };
239
240 static guint dialog_signals[LAST_SIGNAL];
241
G_DEFINE_TYPE_WITH_CODE(GtkDialog,gtk_dialog,GTK_TYPE_WINDOW,G_ADD_PRIVATE (GtkDialog)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_dialog_buildable_interface_init))242 G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW,
243 G_ADD_PRIVATE (GtkDialog)
244 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
245 gtk_dialog_buildable_interface_init))
246
247 static void
248 set_use_header_bar (GtkDialog *dialog,
249 gint use_header_bar)
250 {
251 GtkDialogPrivate *priv = dialog->priv;
252
253 if (use_header_bar == -1)
254 return;
255
256 priv->use_header_bar = use_header_bar;
257 }
258
259 /* A convenience helper for built-in dialogs */
260 void
gtk_dialog_set_use_header_bar_from_setting(GtkDialog * dialog)261 gtk_dialog_set_use_header_bar_from_setting (GtkDialog *dialog)
262 {
263 GtkDialogPrivate *priv = dialog->priv;
264
265 g_assert (!priv->constructed);
266
267 g_object_get (gtk_widget_get_settings (GTK_WIDGET (dialog)),
268 "gtk-dialogs-use-header", &priv->use_header_bar,
269 NULL);
270 }
271
272 static void
gtk_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)273 gtk_dialog_set_property (GObject *object,
274 guint prop_id,
275 const GValue *value,
276 GParamSpec *pspec)
277 {
278 GtkDialog *dialog = GTK_DIALOG (object);
279
280 switch (prop_id)
281 {
282 case PROP_USE_HEADER_BAR:
283 set_use_header_bar (dialog, g_value_get_int (value));
284 break;
285
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 break;
289 }
290 }
291
292 static void
gtk_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)293 gtk_dialog_get_property (GObject *object,
294 guint prop_id,
295 GValue *value,
296 GParamSpec *pspec)
297 {
298 GtkDialog *dialog = GTK_DIALOG (object);
299 GtkDialogPrivate *priv = dialog->priv;
300
301 switch (prop_id)
302 {
303 case PROP_USE_HEADER_BAR:
304 g_value_set_int (value, priv->use_header_bar);
305 break;
306
307 default:
308 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
309 break;
310 }
311 }
312
313 static void
action_widget_activated(GtkWidget * widget,GtkDialog * dialog)314 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
315 {
316 gint response_id;
317
318 response_id = gtk_dialog_get_response_for_widget (dialog, widget);
319
320 gtk_dialog_response (dialog, response_id);
321 }
322
323 typedef struct {
324 GtkWidget *child;
325 gint response_id;
326 } ActionWidgetData;
327
328 static void
add_response_data(GtkDialog * dialog,GtkWidget * child,gint response_id)329 add_response_data (GtkDialog *dialog,
330 GtkWidget *child,
331 gint response_id)
332 {
333 ResponseData *ad;
334 guint signal_id;
335
336 ad = get_response_data (child, TRUE);
337 ad->response_id = response_id;
338
339 if (GTK_IS_BUTTON (child))
340 signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
341 else
342 signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
343
344 if (signal_id)
345 {
346 GClosure *closure;
347
348 closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
349 G_OBJECT (dialog));
350 g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE);
351 }
352 else
353 g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
354 }
355
356 static void
apply_response_for_header_bar(GtkDialog * dialog,GtkWidget * child,gint response_id)357 apply_response_for_header_bar (GtkDialog *dialog,
358 GtkWidget *child,
359 gint response_id)
360 {
361 GtkDialogPrivate *priv = dialog->priv;
362 GtkPackType pack;
363
364 g_assert (gtk_widget_get_parent (child) == priv->headerbar);
365
366 if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_HELP)
367 pack = GTK_PACK_START;
368 else
369 pack = GTK_PACK_END;
370
371 gtk_container_child_set (GTK_CONTAINER (priv->headerbar), child,
372 "pack-type", pack,
373 NULL);
374
375 if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_CLOSE)
376 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (priv->headerbar), FALSE);
377 }
378
379 static void
add_to_header_bar(GtkDialog * dialog,GtkWidget * child,gint response_id)380 add_to_header_bar (GtkDialog *dialog,
381 GtkWidget *child,
382 gint response_id)
383 {
384 GtkDialogPrivate *priv = dialog->priv;
385
386 gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
387 gtk_container_add (GTK_CONTAINER (priv->headerbar), child);
388 gtk_size_group_add_widget (priv->size_group, child);
389 apply_response_for_header_bar (dialog, child, response_id);
390
391 }
392
393 static void
apply_response_for_action_area(GtkDialog * dialog,GtkWidget * child,gint response_id)394 apply_response_for_action_area (GtkDialog *dialog,
395 GtkWidget *child,
396 gint response_id)
397 {
398 GtkDialogPrivate *priv = dialog->priv;
399
400 g_assert (gtk_widget_get_parent (child) == priv->action_area);
401
402 if (response_id == GTK_RESPONSE_HELP)
403 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area), child, TRUE);
404 }
405
406 static void
add_to_action_area(GtkDialog * dialog,GtkWidget * child,gint response_id)407 add_to_action_area (GtkDialog *dialog,
408 GtkWidget *child,
409 gint response_id)
410 {
411 GtkDialogPrivate *priv = dialog->priv;
412
413 gtk_widget_set_valign (child, GTK_ALIGN_BASELINE);
414 gtk_container_add (GTK_CONTAINER (priv->action_area), child);
415 apply_response_for_action_area (dialog, child, response_id);
416 }
417
418 static void
update_suggested_action(GtkDialog * dialog)419 update_suggested_action (GtkDialog *dialog)
420 {
421 GtkDialogPrivate *priv = dialog->priv;
422
423 if (priv->use_header_bar)
424 {
425 GList *children, *l;
426
427 children = gtk_container_get_children (GTK_CONTAINER (priv->headerbar));
428 for (l = children; l != NULL; l = l->next)
429 {
430 GtkWidget *child = l->data;
431 GtkStyleContext *context = gtk_widget_get_style_context (child);
432
433 if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_DEFAULT))
434 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SUGGESTED_ACTION);
435 else
436 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_SUGGESTED_ACTION);
437 }
438 g_list_free (children);
439 }
440 }
441
442 static void
add_cb(GtkContainer * container,GtkWidget * widget,GtkDialog * dialog)443 add_cb (GtkContainer *container,
444 GtkWidget *widget,
445 GtkDialog *dialog)
446 {
447 GtkDialogPrivate *priv = dialog->priv;
448
449 if (priv->use_header_bar)
450 g_warning ("Content added to the action area of a dialog using header bars");
451
452 gtk_widget_set_visible (priv->action_box, TRUE);
453 gtk_widget_set_no_show_all (priv->action_box, FALSE);
454 }
455
456 static void
gtk_dialog_constructed(GObject * object)457 gtk_dialog_constructed (GObject *object)
458 {
459 GtkDialog *dialog = GTK_DIALOG (object);
460 GtkDialogPrivate *priv = dialog->priv;
461
462 G_OBJECT_CLASS (gtk_dialog_parent_class)->constructed (object);
463
464 priv->constructed = TRUE;
465 if (priv->use_header_bar == -1)
466 priv->use_header_bar = FALSE;
467
468 if (priv->use_header_bar)
469 {
470 GList *children, *l;
471
472 children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
473 for (l = children; l != NULL; l = l->next)
474 {
475 GtkWidget *child = l->data;
476 gboolean has_default;
477 ResponseData *rd;
478 gint response_id;
479
480 has_default = gtk_widget_has_default (child);
481 rd = get_response_data (child, FALSE);
482 response_id = rd ? rd->response_id : GTK_RESPONSE_NONE;
483
484 g_object_ref (child);
485 gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
486 add_to_header_bar (dialog, child, response_id);
487 g_object_unref (child);
488
489 if (has_default)
490 gtk_widget_grab_default (child);
491 }
492 g_list_free (children);
493
494 update_suggested_action (dialog);
495
496 g_signal_connect (priv->action_area, "add", G_CALLBACK (add_cb), dialog);
497 }
498 else
499 {
500 gtk_window_set_titlebar (GTK_WINDOW (dialog), NULL);
501 priv->headerbar = NULL;
502 }
503
504 gtk_widget_set_visible (priv->action_box, !priv->use_header_bar);
505 gtk_widget_set_no_show_all (priv->action_box, priv->use_header_bar);
506 }
507
508 static void
gtk_dialog_finalize(GObject * obj)509 gtk_dialog_finalize (GObject *obj)
510 {
511 GtkDialog *dialog = GTK_DIALOG (obj);
512
513 g_object_unref (dialog->priv->size_group);
514
515 G_OBJECT_CLASS (gtk_dialog_parent_class)->finalize (obj);
516 }
517
518 static void
gtk_dialog_class_init(GtkDialogClass * class)519 gtk_dialog_class_init (GtkDialogClass *class)
520 {
521 GObjectClass *gobject_class;
522 GtkWidgetClass *widget_class;
523 GtkBindingSet *binding_set;
524
525 gobject_class = G_OBJECT_CLASS (class);
526 widget_class = GTK_WIDGET_CLASS (class);
527
528 gobject_class->constructed = gtk_dialog_constructed;
529 gobject_class->set_property = gtk_dialog_set_property;
530 gobject_class->get_property = gtk_dialog_get_property;
531 gobject_class->finalize = gtk_dialog_finalize;
532
533 widget_class->map = gtk_dialog_map;
534 widget_class->style_updated = gtk_dialog_style_updated;
535
536 gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_DIALOG);
537
538 class->close = gtk_dialog_close;
539
540 /**
541 * GtkDialog::response:
542 * @dialog: the object on which the signal is emitted
543 * @response_id: the response ID
544 *
545 * Emitted when an action widget is clicked, the dialog receives a
546 * delete event, or the application programmer calls gtk_dialog_response().
547 * On a delete event, the response ID is #GTK_RESPONSE_DELETE_EVENT.
548 * Otherwise, it depends on which action widget was clicked.
549 */
550 dialog_signals[RESPONSE] =
551 g_signal_new (I_("response"),
552 G_OBJECT_CLASS_TYPE (class),
553 G_SIGNAL_RUN_LAST,
554 G_STRUCT_OFFSET (GtkDialogClass, response),
555 NULL, NULL,
556 NULL,
557 G_TYPE_NONE, 1,
558 G_TYPE_INT);
559
560 /**
561 * GtkDialog::close:
562 *
563 * The ::close signal is a
564 * [keybinding signal][GtkBindingSignal]
565 * which gets emitted when the user uses a keybinding to close
566 * the dialog.
567 *
568 * The default binding for this signal is the Escape key.
569 */
570 dialog_signals[CLOSE] =
571 g_signal_new (I_("close"),
572 G_OBJECT_CLASS_TYPE (class),
573 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
574 G_STRUCT_OFFSET (GtkDialogClass, close),
575 NULL, NULL,
576 NULL,
577 G_TYPE_NONE, 0);
578
579 /**
580 * GtkDialog:content-area-border:
581 *
582 * The default border width used around the
583 * content area of the dialog, as returned by
584 * gtk_dialog_get_content_area(), unless gtk_container_set_border_width()
585 * was called on that widget directly.
586 */
587 gtk_widget_class_install_style_property (widget_class,
588 g_param_spec_int ("content-area-border",
589 P_("Content area border"),
590 P_("Width of border around the main dialog area"),
591 0,
592 G_MAXINT,
593 2,
594 GTK_PARAM_READABLE));
595 /**
596 * GtkDialog:content-area-spacing:
597 *
598 * The default spacing used between elements of the
599 * content area of the dialog, as returned by
600 * gtk_dialog_get_content_area(), unless gtk_box_set_spacing()
601 * was called on that widget directly.
602 *
603 * Since: 2.16
604 */
605 gtk_widget_class_install_style_property (widget_class,
606 g_param_spec_int ("content-area-spacing",
607 P_("Content area spacing"),
608 P_("Spacing between elements of the main dialog area"),
609 0,
610 G_MAXINT,
611 0,
612 GTK_PARAM_READABLE));
613 gtk_widget_class_install_style_property (widget_class,
614 g_param_spec_int ("button-spacing",
615 P_("Button spacing"),
616 P_("Spacing between buttons"),
617 0,
618 G_MAXINT,
619 6,
620 GTK_PARAM_READABLE));
621
622 /**
623 * GtkDialog:action-area-border:
624 *
625 * The default border width used around the
626 * action area of the dialog, as returned by
627 * gtk_dialog_get_action_area(), unless gtk_container_set_border_width()
628 * was called on that widget directly.
629 */
630 gtk_widget_class_install_style_property (widget_class,
631 g_param_spec_int ("action-area-border",
632 P_("Action area border"),
633 P_("Width of border around the button area at the bottom of the dialog"),
634 0,
635 G_MAXINT,
636 5,
637 GTK_PARAM_READABLE));
638
639 /**
640 * GtkDialog:use-header-bar:
641 *
642 * %TRUE if the dialog uses a #GtkHeaderBar for action buttons
643 * instead of the action-area.
644 *
645 * For technical reasons, this property is declared as an integer
646 * property, but you should only set it to %TRUE or %FALSE.
647 *
648 * Since: 3.12
649 */
650 g_object_class_install_property (gobject_class,
651 PROP_USE_HEADER_BAR,
652 g_param_spec_int ("use-header-bar",
653 P_("Use Header Bar"),
654 P_("Use Header Bar for actions."),
655 -1, 1, -1,
656 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
657
658 binding_set = gtk_binding_set_by_class (class);
659 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
660
661 /* Bind class to template
662 */
663 gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdialog.ui");
664 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, vbox);
665 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, headerbar);
666 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, action_area);
667 gtk_widget_class_bind_template_child_private (widget_class, GtkDialog, action_box);
668 gtk_widget_class_bind_template_callback (widget_class, gtk_dialog_delete_event_handler);
669
670 gtk_widget_class_set_css_name (widget_class, "dialog");
671 }
672
673 static void
update_spacings(GtkDialog * dialog)674 update_spacings (GtkDialog *dialog)
675 {
676 GtkDialogPrivate *priv = dialog->priv;
677 gint content_area_border;
678 gint content_area_spacing;
679 gint button_spacing;
680 gint action_area_border;
681
682 gtk_widget_style_get (GTK_WIDGET (dialog),
683 "content-area-border", &content_area_border,
684 "content-area-spacing", &content_area_spacing,
685 "button-spacing", &button_spacing,
686 "action-area-border", &action_area_border,
687 NULL);
688
689 if (!_gtk_container_get_border_width_set (GTK_CONTAINER (priv->vbox)))
690 {
691 gtk_container_set_border_width (GTK_CONTAINER (priv->vbox),
692 content_area_border);
693 _gtk_container_set_border_width_set (GTK_CONTAINER (priv->vbox), FALSE);
694 }
695
696 if (!_gtk_box_get_spacing_set (GTK_BOX (priv->vbox)))
697 {
698 gtk_box_set_spacing (GTK_BOX (priv->vbox), content_area_spacing);
699 _gtk_box_set_spacing_set (GTK_BOX (priv->vbox), FALSE);
700 }
701
702 /* don't set spacing when buttons are linked */
703 if (gtk_button_box_get_layout (GTK_BUTTON_BOX (priv->action_area)) != GTK_BUTTONBOX_EXPAND)
704 gtk_box_set_spacing (GTK_BOX (priv->action_area), button_spacing);
705
706 if (!_gtk_container_get_border_width_set (GTK_CONTAINER (priv->action_area)))
707 {
708 gtk_container_set_border_width (GTK_CONTAINER (priv->action_area),
709 action_area_border);
710 _gtk_container_set_border_width_set (GTK_CONTAINER (priv->action_area), FALSE);
711 }
712 }
713
714 static void
gtk_dialog_init(GtkDialog * dialog)715 gtk_dialog_init (GtkDialog *dialog)
716 {
717 dialog->priv = gtk_dialog_get_instance_private (dialog);
718
719 dialog->priv->use_header_bar = -1;
720 dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
721
722 gtk_widget_init_template (GTK_WIDGET (dialog));
723
724 update_spacings (dialog);
725 }
726
727 static GtkBuildableIface *parent_buildable_iface;
728
729 static void
gtk_dialog_buildable_interface_init(GtkBuildableIface * iface)730 gtk_dialog_buildable_interface_init (GtkBuildableIface *iface)
731 {
732 parent_buildable_iface = g_type_interface_peek_parent (iface);
733 iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start;
734 iface->custom_finished = gtk_dialog_buildable_custom_finished;
735 iface->add_child = gtk_dialog_buildable_add_child;
736 }
737
738 static gboolean
gtk_dialog_delete_event_handler(GtkWidget * widget,GdkEventAny * event,gpointer user_data)739 gtk_dialog_delete_event_handler (GtkWidget *widget,
740 GdkEventAny *event,
741 gpointer user_data)
742 {
743 /* emit response signal */
744 gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
745
746 /* Do the destroy by default */
747 return FALSE;
748 }
749
750 static GList *
get_action_children(GtkDialog * dialog)751 get_action_children (GtkDialog *dialog)
752 {
753 GtkDialogPrivate *priv = dialog->priv;
754 GList *children;
755
756 if (priv->constructed && priv->use_header_bar)
757 children = gtk_container_get_children (GTK_CONTAINER (priv->headerbar));
758 else
759 children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
760
761 return children;
762 }
763
764 /* A far too tricky heuristic for getting the right initial
765 * focus widget if none was set. What we do is we focus the first
766 * widget in the tab chain, but if this results in the focus
767 * ending up on one of the response widgets _other_ than the
768 * default response, we focus the default response instead.
769 *
770 * Additionally, skip selectable labels when looking for the
771 * right initial focus widget.
772 */
773 static void
gtk_dialog_map(GtkWidget * widget)774 gtk_dialog_map (GtkWidget *widget)
775 {
776 GtkWidget *default_widget, *focus;
777 GtkWindow *window = GTK_WINDOW (widget);
778 GtkDialog *dialog = GTK_DIALOG (widget);
779
780 GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
781
782 focus = gtk_window_get_focus (window);
783 if (!focus)
784 {
785 GList *children, *tmp_list;
786 GtkWidget *first_focus = NULL;
787
788 do
789 {
790 g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
791
792 focus = gtk_window_get_focus (window);
793 if (GTK_IS_LABEL (focus) &&
794 !gtk_label_get_current_uri (GTK_LABEL (focus)))
795 gtk_label_select_region (GTK_LABEL (focus), 0, 0);
796
797 if (first_focus == NULL)
798 first_focus = focus;
799 else if (first_focus == focus)
800 break;
801
802 if (!GTK_IS_LABEL (focus))
803 break;
804 }
805 while (TRUE);
806
807 tmp_list = children = get_action_children (dialog);
808
809 while (tmp_list)
810 {
811 GtkWidget *child = tmp_list->data;
812
813 default_widget = gtk_window_get_default_widget (window);
814 if ((focus == NULL || child == focus) &&
815 child != default_widget &&
816 default_widget)
817 {
818 gtk_widget_grab_focus (default_widget);
819 break;
820 }
821
822 tmp_list = tmp_list->next;
823 }
824
825 g_list_free (children);
826 }
827 }
828
829 static void
gtk_dialog_style_updated(GtkWidget * widget)830 gtk_dialog_style_updated (GtkWidget *widget)
831 {
832 GTK_WIDGET_CLASS (gtk_dialog_parent_class)->style_updated (widget);
833
834 update_spacings (GTK_DIALOG (widget));
835 }
836
837 static GtkWidget *
dialog_find_button(GtkDialog * dialog,gint response_id)838 dialog_find_button (GtkDialog *dialog,
839 gint response_id)
840 {
841 GtkWidget *child = NULL;
842 GList *children, *tmp_list;
843
844 children = get_action_children (dialog);
845
846 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
847 {
848 ResponseData *rd = get_response_data (tmp_list->data, FALSE);
849
850 if (rd && rd->response_id == response_id)
851 {
852 child = tmp_list->data;
853 break;
854 }
855 }
856
857 g_list_free (children);
858
859 return child;
860 }
861
862 static void
gtk_dialog_close(GtkDialog * dialog)863 gtk_dialog_close (GtkDialog *dialog)
864 {
865 gtk_window_close (GTK_WINDOW (dialog));
866 }
867
868 /**
869 * gtk_dialog_new:
870 *
871 * Creates a new dialog box.
872 *
873 * Widgets should not be packed into this #GtkWindow
874 * directly, but into the @vbox and @action_area, as described above.
875 *
876 * Returns: the new dialog as a #GtkWidget
877 */
878 GtkWidget*
gtk_dialog_new(void)879 gtk_dialog_new (void)
880 {
881 return g_object_new (GTK_TYPE_DIALOG, NULL);
882 }
883
884 static GtkWidget*
gtk_dialog_new_empty(const gchar * title,GtkWindow * parent,GtkDialogFlags flags)885 gtk_dialog_new_empty (const gchar *title,
886 GtkWindow *parent,
887 GtkDialogFlags flags)
888 {
889 GtkDialog *dialog;
890
891 dialog = g_object_new (GTK_TYPE_DIALOG,
892 "use-header-bar", (flags & GTK_DIALOG_USE_HEADER_BAR) != 0,
893 NULL);
894
895 if (title)
896 gtk_window_set_title (GTK_WINDOW (dialog), title);
897
898 if (parent)
899 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
900
901 if (flags & GTK_DIALOG_MODAL)
902 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
903
904 if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
905 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
906
907 return GTK_WIDGET (dialog);
908 }
909
910 /**
911 * gtk_dialog_new_with_buttons:
912 * @title: (allow-none): Title of the dialog, or %NULL
913 * @parent: (allow-none): Transient parent of the dialog, or %NULL
914 * @flags: from #GtkDialogFlags
915 * @first_button_text: (allow-none): text to go in first button, or %NULL
916 * @...: response ID for first button, then additional buttons, ending with %NULL
917 *
918 * Creates a new #GtkDialog with title @title (or %NULL for the default
919 * title; see gtk_window_set_title()) and transient parent @parent (or
920 * %NULL for none; see gtk_window_set_transient_for()). The @flags
921 * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
922 * and/or to have it destroyed along with its transient parent
923 * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
924 * text/response ID pairs should be listed, with a %NULL pointer ending
925 * the list. Button text can be arbitrary text. A response ID can be
926 * any positive number, or one of the values in the #GtkResponseType
927 * enumeration. If the user clicks one of these dialog buttons,
928 * #GtkDialog will emit the #GtkDialog::response signal with the corresponding
929 * response ID. If a #GtkDialog receives the #GtkWidget::delete-event signal,
930 * it will emit ::response with a response ID of #GTK_RESPONSE_DELETE_EVENT.
931 * However, destroying a dialog does not emit the ::response signal;
932 * so be careful relying on ::response when using the
933 * #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
934 * so the first button in the list will be the leftmost button in the dialog.
935 *
936 * Here’s a simple example:
937 * |[<!-- language="C" -->
938 * GtkWidget *main_app_window; // Window the dialog should show up on
939 * GtkWidget *dialog;
940 * GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
941 * dialog = gtk_dialog_new_with_buttons ("My dialog",
942 * main_app_window,
943 * flags,
944 * _("_OK"),
945 * GTK_RESPONSE_ACCEPT,
946 * _("_Cancel"),
947 * GTK_RESPONSE_REJECT,
948 * NULL);
949 * ]|
950 *
951 * Returns: a new #GtkDialog
952 */
953 GtkWidget*
gtk_dialog_new_with_buttons(const gchar * title,GtkWindow * parent,GtkDialogFlags flags,const gchar * first_button_text,...)954 gtk_dialog_new_with_buttons (const gchar *title,
955 GtkWindow *parent,
956 GtkDialogFlags flags,
957 const gchar *first_button_text,
958 ...)
959 {
960 GtkDialog *dialog;
961 va_list args;
962
963 dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
964
965 va_start (args, first_button_text);
966
967 gtk_dialog_add_buttons_valist (dialog,
968 first_button_text,
969 args);
970
971 va_end (args);
972
973 return GTK_WIDGET (dialog);
974 }
975
976 static void
response_data_free(gpointer data)977 response_data_free (gpointer data)
978 {
979 g_slice_free (ResponseData, data);
980 }
981
982 static ResponseData *
get_response_data(GtkWidget * widget,gboolean create)983 get_response_data (GtkWidget *widget,
984 gboolean create)
985 {
986 ResponseData *ad = g_object_get_data (G_OBJECT (widget),
987 "gtk-dialog-response-data");
988
989 if (ad == NULL && create)
990 {
991 ad = g_slice_new (ResponseData);
992
993 g_object_set_data_full (G_OBJECT (widget),
994 I_("gtk-dialog-response-data"),
995 ad,
996 response_data_free);
997 }
998
999 return ad;
1000 }
1001
1002 /**
1003 * gtk_dialog_add_action_widget:
1004 * @dialog: a #GtkDialog
1005 * @child: an activatable widget
1006 * @response_id: response ID for @child
1007 *
1008 * Adds an activatable widget to the action area of a #GtkDialog,
1009 * connecting a signal handler that will emit the #GtkDialog::response
1010 * signal on the dialog when the widget is activated. The widget is
1011 * appended to the end of the dialog’s action area. If you want to add a
1012 * non-activatable widget, simply pack it into the @action_area field
1013 * of the #GtkDialog struct.
1014 **/
1015 void
gtk_dialog_add_action_widget(GtkDialog * dialog,GtkWidget * child,gint response_id)1016 gtk_dialog_add_action_widget (GtkDialog *dialog,
1017 GtkWidget *child,
1018 gint response_id)
1019 {
1020 GtkDialogPrivate *priv = dialog->priv;
1021
1022 g_return_if_fail (GTK_IS_DIALOG (dialog));
1023 g_return_if_fail (GTK_IS_WIDGET (child));
1024
1025 add_response_data (dialog, child, response_id);
1026
1027 if (priv->constructed && priv->use_header_bar)
1028 {
1029 add_to_header_bar (dialog, child, response_id);
1030
1031 if (gtk_widget_has_default (child))
1032 {
1033 gtk_widget_grab_default (child);
1034 update_suggested_action (dialog);
1035 }
1036 }
1037 else
1038 add_to_action_area (dialog, child, response_id);
1039 }
1040
1041 /**
1042 * gtk_dialog_add_button:
1043 * @dialog: a #GtkDialog
1044 * @button_text: text of button
1045 * @response_id: response ID for the button
1046 *
1047 * Adds a button with the given text and sets things up so that
1048 * clicking the button will emit the #GtkDialog::response signal with
1049 * the given @response_id. The button is appended to the end of the
1050 * dialog’s action area. The button widget is returned, but usually
1051 * you don’t need it.
1052 *
1053 * Returns: (transfer none): the #GtkButton widget that was added
1054 **/
1055 GtkWidget*
gtk_dialog_add_button(GtkDialog * dialog,const gchar * button_text,gint response_id)1056 gtk_dialog_add_button (GtkDialog *dialog,
1057 const gchar *button_text,
1058 gint response_id)
1059 {
1060 GtkWidget *button;
1061
1062 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1063 g_return_val_if_fail (button_text != NULL, NULL);
1064
1065 button = gtk_button_new_with_label (button_text);
1066 gtk_button_set_use_underline (GTK_BUTTON (button), TRUE);
1067
1068 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1069
1070 if (button_text)
1071 {
1072 GtkStockItem item;
1073 if (gtk_stock_lookup (button_text, &item))
1074 g_object_set (button, "use-stock", TRUE, NULL);
1075 }
1076
1077 G_GNUC_END_IGNORE_DEPRECATIONS;
1078
1079 gtk_style_context_add_class (gtk_widget_get_style_context (button), "text-button");
1080 gtk_widget_set_can_default (button, TRUE);
1081
1082 gtk_widget_show (button);
1083
1084 gtk_dialog_add_action_widget (dialog, button, response_id);
1085
1086 return button;
1087 }
1088
1089 static void
gtk_dialog_add_buttons_valist(GtkDialog * dialog,const gchar * first_button_text,va_list args)1090 gtk_dialog_add_buttons_valist (GtkDialog *dialog,
1091 const gchar *first_button_text,
1092 va_list args)
1093 {
1094 const gchar* text;
1095 gint response_id;
1096
1097 g_return_if_fail (GTK_IS_DIALOG (dialog));
1098
1099 if (first_button_text == NULL)
1100 return;
1101
1102 text = first_button_text;
1103 response_id = va_arg (args, gint);
1104
1105 while (text != NULL)
1106 {
1107 gtk_dialog_add_button (dialog, text, response_id);
1108
1109 text = va_arg (args, gchar*);
1110 if (text == NULL)
1111 break;
1112 response_id = va_arg (args, int);
1113 }
1114 }
1115
1116 /**
1117 * gtk_dialog_add_buttons:
1118 * @dialog: a #GtkDialog
1119 * @first_button_text: button text
1120 * @...: response ID for first button, then more text-response_id pairs
1121 *
1122 * Adds more buttons, same as calling gtk_dialog_add_button()
1123 * repeatedly. The variable argument list should be %NULL-terminated
1124 * as with gtk_dialog_new_with_buttons(). Each button must have both
1125 * text and response ID.
1126 */
1127 void
gtk_dialog_add_buttons(GtkDialog * dialog,const gchar * first_button_text,...)1128 gtk_dialog_add_buttons (GtkDialog *dialog,
1129 const gchar *first_button_text,
1130 ...)
1131 {
1132 va_list args;
1133
1134 va_start (args, first_button_text);
1135
1136 gtk_dialog_add_buttons_valist (dialog,
1137 first_button_text,
1138 args);
1139
1140 va_end (args);
1141 }
1142
1143 /**
1144 * gtk_dialog_set_response_sensitive:
1145 * @dialog: a #GtkDialog
1146 * @response_id: a response ID
1147 * @setting: %TRUE for sensitive
1148 *
1149 * Calls `gtk_widget_set_sensitive (widget, @setting)`
1150 * for each widget in the dialog’s action area with the given @response_id.
1151 * A convenient way to sensitize/desensitize dialog buttons.
1152 **/
1153 void
gtk_dialog_set_response_sensitive(GtkDialog * dialog,gint response_id,gboolean setting)1154 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
1155 gint response_id,
1156 gboolean setting)
1157 {
1158 GList *children;
1159 GList *tmp_list;
1160
1161 g_return_if_fail (GTK_IS_DIALOG (dialog));
1162
1163 children = get_action_children (dialog);
1164
1165 tmp_list = children;
1166 while (tmp_list != NULL)
1167 {
1168 GtkWidget *widget = tmp_list->data;
1169 ResponseData *rd = get_response_data (widget, FALSE);
1170
1171 if (rd && rd->response_id == response_id)
1172 gtk_widget_set_sensitive (widget, setting);
1173
1174 tmp_list = tmp_list->next;
1175 }
1176
1177 g_list_free (children);
1178 }
1179
1180 /**
1181 * gtk_dialog_set_default_response:
1182 * @dialog: a #GtkDialog
1183 * @response_id: a response ID
1184 *
1185 * Sets the last widget in the dialog’s action area with the given @response_id
1186 * as the default widget for the dialog. Pressing “Enter” normally activates
1187 * the default widget.
1188 **/
1189 void
gtk_dialog_set_default_response(GtkDialog * dialog,gint response_id)1190 gtk_dialog_set_default_response (GtkDialog *dialog,
1191 gint response_id)
1192 {
1193 GList *children;
1194 GList *tmp_list;
1195
1196 g_return_if_fail (GTK_IS_DIALOG (dialog));
1197
1198 children = get_action_children (dialog);
1199
1200 tmp_list = children;
1201 while (tmp_list != NULL)
1202 {
1203 GtkWidget *widget = tmp_list->data;
1204 ResponseData *rd = get_response_data (widget, FALSE);
1205
1206 if (rd && rd->response_id == response_id)
1207 gtk_widget_grab_default (widget);
1208
1209 tmp_list = tmp_list->next;
1210 }
1211
1212 g_list_free (children);
1213
1214 if (dialog->priv->use_header_bar)
1215 update_suggested_action (dialog);
1216 }
1217
1218 /**
1219 * gtk_dialog_response:
1220 * @dialog: a #GtkDialog
1221 * @response_id: response ID
1222 *
1223 * Emits the #GtkDialog::response signal with the given response ID.
1224 * Used to indicate that the user has responded to the dialog in some way;
1225 * typically either you or gtk_dialog_run() will be monitoring the
1226 * ::response signal and take appropriate action.
1227 **/
1228 void
gtk_dialog_response(GtkDialog * dialog,gint response_id)1229 gtk_dialog_response (GtkDialog *dialog,
1230 gint response_id)
1231 {
1232 g_return_if_fail (GTK_IS_DIALOG (dialog));
1233
1234 g_signal_emit (dialog,
1235 dialog_signals[RESPONSE],
1236 0,
1237 response_id);
1238 }
1239
1240 typedef struct
1241 {
1242 GtkDialog *dialog;
1243 gint response_id;
1244 GMainLoop *loop;
1245 gboolean destroyed;
1246 } RunInfo;
1247
1248 static void
shutdown_loop(RunInfo * ri)1249 shutdown_loop (RunInfo *ri)
1250 {
1251 if (g_main_loop_is_running (ri->loop))
1252 g_main_loop_quit (ri->loop);
1253 }
1254
1255 static void
run_unmap_handler(GtkDialog * dialog,gpointer data)1256 run_unmap_handler (GtkDialog *dialog, gpointer data)
1257 {
1258 RunInfo *ri = data;
1259
1260 shutdown_loop (ri);
1261 }
1262
1263 static void
run_response_handler(GtkDialog * dialog,gint response_id,gpointer data)1264 run_response_handler (GtkDialog *dialog,
1265 gint response_id,
1266 gpointer data)
1267 {
1268 RunInfo *ri;
1269
1270 ri = data;
1271
1272 ri->response_id = response_id;
1273
1274 shutdown_loop (ri);
1275 }
1276
1277 static gint
run_delete_handler(GtkDialog * dialog,GdkEventAny * event,gpointer data)1278 run_delete_handler (GtkDialog *dialog,
1279 GdkEventAny *event,
1280 gpointer data)
1281 {
1282 RunInfo *ri = data;
1283
1284 shutdown_loop (ri);
1285
1286 return TRUE; /* Do not destroy */
1287 }
1288
1289 static void
run_destroy_handler(GtkDialog * dialog,gpointer data)1290 run_destroy_handler (GtkDialog *dialog, gpointer data)
1291 {
1292 RunInfo *ri = data;
1293
1294 /* shutdown_loop will be called by run_unmap_handler */
1295
1296 ri->destroyed = TRUE;
1297 }
1298
1299 /**
1300 * gtk_dialog_run:
1301 * @dialog: a #GtkDialog
1302 *
1303 * Blocks in a recursive main loop until the @dialog either emits the
1304 * #GtkDialog::response signal, or is destroyed. If the dialog is
1305 * destroyed during the call to gtk_dialog_run(), gtk_dialog_run() returns
1306 * #GTK_RESPONSE_NONE. Otherwise, it returns the response ID from the
1307 * ::response signal emission.
1308 *
1309 * Before entering the recursive main loop, gtk_dialog_run() calls
1310 * gtk_widget_show() on the dialog for you. Note that you still
1311 * need to show any children of the dialog yourself.
1312 *
1313 * During gtk_dialog_run(), the default behavior of #GtkWidget::delete-event
1314 * is disabled; if the dialog receives ::delete_event, it will not be
1315 * destroyed as windows usually are, and gtk_dialog_run() will return
1316 * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog
1317 * will be modal. You can force gtk_dialog_run() to return at any time by
1318 * calling gtk_dialog_response() to emit the ::response signal. Destroying
1319 * the dialog during gtk_dialog_run() is a very bad idea, because your
1320 * post-run code won’t know whether the dialog was destroyed or not.
1321 *
1322 * After gtk_dialog_run() returns, you are responsible for hiding or
1323 * destroying the dialog if you wish to do so.
1324 *
1325 * Typical usage of this function might be:
1326 * |[<!-- language="C" -->
1327 * GtkWidget *dialog = gtk_dialog_new ();
1328 * // Set up dialog...
1329 *
1330 * int result = gtk_dialog_run (GTK_DIALOG (dialog));
1331 * switch (result)
1332 * {
1333 * case GTK_RESPONSE_ACCEPT:
1334 * // do_application_specific_something ();
1335 * break;
1336 * default:
1337 * // do_nothing_since_dialog_was_cancelled ();
1338 * break;
1339 * }
1340 * gtk_widget_destroy (dialog);
1341 * ]|
1342 *
1343 * Note that even though the recursive main loop gives the effect of a
1344 * modal dialog (it prevents the user from interacting with other
1345 * windows in the same window group while the dialog is run), callbacks
1346 * such as timeouts, IO channel watches, DND drops, etc, will
1347 * be triggered during a gtk_dialog_run() call.
1348 *
1349 * Returns: response ID
1350 **/
1351 gint
gtk_dialog_run(GtkDialog * dialog)1352 gtk_dialog_run (GtkDialog *dialog)
1353 {
1354 RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
1355 gboolean was_modal;
1356 gulong response_handler;
1357 gulong unmap_handler;
1358 gulong destroy_handler;
1359 gulong delete_handler;
1360
1361 g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
1362
1363 g_object_ref (dialog);
1364
1365 was_modal = gtk_window_get_modal (GTK_WINDOW (dialog));
1366 if (!was_modal)
1367 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1368
1369 if (!gtk_widget_get_visible (GTK_WIDGET (dialog)))
1370 gtk_widget_show (GTK_WIDGET (dialog));
1371
1372 response_handler =
1373 g_signal_connect (dialog,
1374 "response",
1375 G_CALLBACK (run_response_handler),
1376 &ri);
1377
1378 unmap_handler =
1379 g_signal_connect (dialog,
1380 "unmap",
1381 G_CALLBACK (run_unmap_handler),
1382 &ri);
1383
1384 delete_handler =
1385 g_signal_connect (dialog,
1386 "delete-event",
1387 G_CALLBACK (run_delete_handler),
1388 &ri);
1389
1390 destroy_handler =
1391 g_signal_connect (dialog,
1392 "destroy",
1393 G_CALLBACK (run_destroy_handler),
1394 &ri);
1395
1396 ri.loop = g_main_loop_new (NULL, FALSE);
1397
1398 gdk_threads_leave ();
1399 g_main_loop_run (ri.loop);
1400 gdk_threads_enter ();
1401
1402 g_main_loop_unref (ri.loop);
1403
1404 ri.loop = NULL;
1405
1406 if (!ri.destroyed)
1407 {
1408 if (!was_modal)
1409 gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1410
1411 g_signal_handler_disconnect (dialog, response_handler);
1412 g_signal_handler_disconnect (dialog, unmap_handler);
1413 g_signal_handler_disconnect (dialog, delete_handler);
1414 g_signal_handler_disconnect (dialog, destroy_handler);
1415 }
1416
1417 g_object_unref (dialog);
1418
1419 return ri.response_id;
1420 }
1421
1422 /**
1423 * gtk_dialog_get_widget_for_response:
1424 * @dialog: a #GtkDialog
1425 * @response_id: the response ID used by the @dialog widget
1426 *
1427 * Gets the widget button that uses the given response ID in the action area
1428 * of a dialog.
1429 *
1430 * Returns: (nullable) (transfer none): the @widget button that uses the given
1431 * @response_id, or %NULL.
1432 *
1433 * Since: 2.20
1434 */
1435 GtkWidget*
gtk_dialog_get_widget_for_response(GtkDialog * dialog,gint response_id)1436 gtk_dialog_get_widget_for_response (GtkDialog *dialog,
1437 gint response_id)
1438 {
1439 GList *children;
1440 GList *tmp_list;
1441
1442 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1443
1444 children = get_action_children (dialog);
1445
1446 tmp_list = children;
1447 while (tmp_list != NULL)
1448 {
1449 GtkWidget *widget = tmp_list->data;
1450 ResponseData *rd = get_response_data (widget, FALSE);
1451
1452 if (rd && rd->response_id == response_id)
1453 {
1454 g_list_free (children);
1455 return widget;
1456 }
1457
1458 tmp_list = tmp_list->next;
1459 }
1460
1461 g_list_free (children);
1462
1463 return NULL;
1464 }
1465
1466 /**
1467 * gtk_dialog_get_response_for_widget:
1468 * @dialog: a #GtkDialog
1469 * @widget: a widget in the action area of @dialog
1470 *
1471 * Gets the response id of a widget in the action area
1472 * of a dialog.
1473 *
1474 * Returns: the response id of @widget, or %GTK_RESPONSE_NONE
1475 * if @widget doesn’t have a response id set.
1476 *
1477 * Since: 2.8
1478 */
1479 gint
gtk_dialog_get_response_for_widget(GtkDialog * dialog,GtkWidget * widget)1480 gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1481 GtkWidget *widget)
1482 {
1483 ResponseData *rd;
1484
1485 rd = get_response_data (widget, FALSE);
1486 if (!rd)
1487 return GTK_RESPONSE_NONE;
1488 else
1489 return rd->response_id;
1490 }
1491
1492 static gboolean
gtk_alt_dialog_button_order(void)1493 gtk_alt_dialog_button_order (void)
1494 {
1495 gboolean result;
1496 g_object_get (gtk_settings_get_default (),
1497 "gtk-alternative-button-order", &result, NULL);
1498 return result;
1499 }
1500
1501 /**
1502 * gtk_alternative_dialog_button_order:
1503 * @screen: (allow-none): a #GdkScreen, or %NULL to use the default screen
1504 *
1505 * Returns %TRUE if dialogs are expected to use an alternative
1506 * button order on the screen @screen. See
1507 * gtk_dialog_set_alternative_button_order() for more details
1508 * about alternative button order.
1509 *
1510 * If you need to use this function, you should probably connect
1511 * to the ::notify:gtk-alternative-button-order signal on the
1512 * #GtkSettings object associated to @screen, in order to be
1513 * notified if the button order setting changes.
1514 *
1515 * Returns: Whether the alternative button order should be used
1516 *
1517 * Since: 2.6
1518 * Deprecated: 3.10: Deprecated
1519 */
1520 gboolean
gtk_alternative_dialog_button_order(GdkScreen * screen)1521 gtk_alternative_dialog_button_order (GdkScreen *screen)
1522 {
1523 return gtk_alt_dialog_button_order ();
1524 }
1525
1526 static void
gtk_dialog_set_alternative_button_order_valist(GtkDialog * dialog,gint first_response_id,va_list args)1527 gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
1528 gint first_response_id,
1529 va_list args)
1530 {
1531 GtkDialogPrivate *priv = dialog->priv;
1532 GtkWidget *child;
1533 gint response_id;
1534 gint position;
1535
1536 response_id = first_response_id;
1537 position = 0;
1538 while (response_id != -1)
1539 {
1540 /* reorder child with response_id to position */
1541 child = dialog_find_button (dialog, response_id);
1542 if (child != NULL)
1543 gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1544 else
1545 g_warning ("%s : no child button with response id %d.", G_STRFUNC,
1546 response_id);
1547
1548 response_id = va_arg (args, gint);
1549 position++;
1550 }
1551 }
1552
1553 /**
1554 * gtk_dialog_set_alternative_button_order:
1555 * @dialog: a #GtkDialog
1556 * @first_response_id: a response id used by one @dialog’s buttons
1557 * @...: a list of more response ids of @dialog’s buttons, terminated by -1
1558 *
1559 * Sets an alternative button order. If the
1560 * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE,
1561 * the dialog buttons are reordered according to the order of the
1562 * response ids passed to this function.
1563 *
1564 * By default, GTK+ dialogs use the button order advocated by the
1565 * [GNOME Human Interface Guidelines](http://library.gnome.org/devel/hig-book/stable/)
1566 * with the affirmative button at the far
1567 * right, and the cancel button left of it. But the builtin GTK+ dialogs
1568 * and #GtkMessageDialogs do provide an alternative button order,
1569 * which is more suitable on some platforms, e.g. Windows.
1570 *
1571 * Use this function after adding all the buttons to your dialog, as the
1572 * following example shows:
1573 *
1574 * |[<!-- language="C" -->
1575 * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1576 * _("_Cancel"),
1577 * GTK_RESPONSE_CANCEL);
1578 *
1579 * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1580 * _("_OK"),
1581 * GTK_RESPONSE_OK);
1582 *
1583 * gtk_widget_grab_default (ok_button);
1584 *
1585 * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1586 * _("_Help"),
1587 * GTK_RESPONSE_HELP);
1588 *
1589 * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1590 * GTK_RESPONSE_OK,
1591 * GTK_RESPONSE_CANCEL,
1592 * GTK_RESPONSE_HELP,
1593 * -1);
1594 * ]|
1595 *
1596 * Since: 2.6
1597 * Deprecated: 3.10: Deprecated
1598 */
1599 void
gtk_dialog_set_alternative_button_order(GtkDialog * dialog,gint first_response_id,...)1600 gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
1601 gint first_response_id,
1602 ...)
1603 {
1604 GtkDialogPrivate *priv = dialog->priv;
1605 va_list args;
1606
1607 g_return_if_fail (GTK_IS_DIALOG (dialog));
1608
1609 if (priv->constructed && priv->use_header_bar)
1610 return;
1611
1612 if (!gtk_alt_dialog_button_order ())
1613 return;
1614
1615 va_start (args, first_response_id);
1616
1617 gtk_dialog_set_alternative_button_order_valist (dialog,
1618 first_response_id,
1619 args);
1620 va_end (args);
1621 }
1622 /**
1623 * gtk_dialog_set_alternative_button_order_from_array:
1624 * @dialog: a #GtkDialog
1625 * @n_params: the number of response ids in @new_order
1626 * @new_order: (array length=n_params): an array of response ids of
1627 * @dialog’s buttons
1628 *
1629 * Sets an alternative button order. If the
1630 * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE,
1631 * the dialog buttons are reordered according to the order of the
1632 * response ids in @new_order.
1633 *
1634 * See gtk_dialog_set_alternative_button_order() for more information.
1635 *
1636 * This function is for use by language bindings.
1637 *
1638 * Since: 2.6
1639 * Deprecated: 3.10: Deprecated
1640 */
1641 void
gtk_dialog_set_alternative_button_order_from_array(GtkDialog * dialog,gint n_params,gint * new_order)1642 gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
1643 gint n_params,
1644 gint *new_order)
1645 {
1646 GtkDialogPrivate *priv = dialog->priv;
1647 GtkWidget *child;
1648 gint position;
1649
1650 g_return_if_fail (GTK_IS_DIALOG (dialog));
1651 g_return_if_fail (new_order != NULL);
1652
1653 if (dialog->priv->use_header_bar)
1654 return;
1655
1656 if (!gtk_alt_dialog_button_order ())
1657 return;
1658
1659 for (position = 0; position < n_params; position++)
1660 {
1661 /* reorder child with response_id to position */
1662 child = dialog_find_button (dialog, new_order[position]);
1663 if (child != NULL)
1664 gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1665 else
1666 g_warning ("%s : no child button with response id %d.", G_STRFUNC,
1667 new_order[position]);
1668 }
1669 }
1670
1671 typedef struct {
1672 gchar *widget_name;
1673 gint response_id;
1674 gboolean is_default;
1675 gint line;
1676 gint col;
1677 } ActionWidgetInfo;
1678
1679 typedef struct {
1680 GtkDialog *dialog;
1681 GtkBuilder *builder;
1682 GSList *items;
1683 gint response_id;
1684 gboolean is_default;
1685 gboolean is_text;
1686 GString *string;
1687 gboolean in_action_widgets;
1688 gint line;
1689 gint col;
1690 } SubParserData;
1691
1692 static void
free_action_widget_info(gpointer data)1693 free_action_widget_info (gpointer data)
1694 {
1695 ActionWidgetInfo *item = data;
1696
1697 g_free (item->widget_name);
1698 g_free (item);
1699 }
1700
1701 static void
parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)1702 parser_start_element (GMarkupParseContext *context,
1703 const gchar *element_name,
1704 const gchar **names,
1705 const gchar **values,
1706 gpointer user_data,
1707 GError **error)
1708 {
1709 SubParserData *data = (SubParserData*)user_data;
1710
1711 if (strcmp (element_name, "action-widget") == 0)
1712 {
1713 const gchar *response;
1714 gboolean is_default = FALSE;
1715 GValue gvalue = G_VALUE_INIT;
1716
1717 if (!_gtk_builder_check_parent (data->builder, context, "action-widgets", error))
1718 return;
1719
1720 if (!g_markup_collect_attributes (element_name, names, values, error,
1721 G_MARKUP_COLLECT_STRING, "response", &response,
1722 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "default", &is_default,
1723 G_MARKUP_COLLECT_INVALID))
1724 {
1725 _gtk_builder_prefix_error (data->builder, context, error);
1726 return;
1727 }
1728
1729 if (!gtk_builder_value_from_string_type (data->builder, GTK_TYPE_RESPONSE_TYPE, response, &gvalue, error))
1730 {
1731 _gtk_builder_prefix_error (data->builder, context, error);
1732 return;
1733 }
1734
1735 data->response_id = g_value_get_enum (&gvalue);
1736 data->is_default = is_default;
1737 data->is_text = TRUE;
1738 g_string_set_size (data->string, 0);
1739 g_markup_parse_context_get_position (context, &data->line, &data->col);
1740 }
1741 else if (strcmp (element_name, "action-widgets") == 0)
1742 {
1743 if (!_gtk_builder_check_parent (data->builder, context, "object", error))
1744 return;
1745
1746 if (!g_markup_collect_attributes (element_name, names, values, error,
1747 G_MARKUP_COLLECT_INVALID, NULL, NULL,
1748 G_MARKUP_COLLECT_INVALID))
1749 _gtk_builder_prefix_error (data->builder, context, error);
1750
1751 data->in_action_widgets = TRUE;
1752 }
1753 else
1754 {
1755 _gtk_builder_error_unhandled_tag (data->builder, context,
1756 "GtkDialog", element_name,
1757 error);
1758 }
1759 }
1760
1761 static void
parser_text_element(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)1762 parser_text_element (GMarkupParseContext *context,
1763 const gchar *text,
1764 gsize text_len,
1765 gpointer user_data,
1766 GError **error)
1767 {
1768 SubParserData *data = (SubParserData*)user_data;
1769
1770 if (data->is_text)
1771 g_string_append_len (data->string, text, text_len);
1772 }
1773
1774 static void
parser_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)1775 parser_end_element (GMarkupParseContext *context,
1776 const gchar *element_name,
1777 gpointer user_data,
1778 GError **error)
1779 {
1780 SubParserData *data = (SubParserData*)user_data;
1781
1782 if (data->is_text)
1783 {
1784 ActionWidgetInfo *item;
1785
1786 item = g_new (ActionWidgetInfo, 1);
1787 item->widget_name = g_strdup (data->string->str);
1788 item->response_id = data->response_id;
1789 item->is_default = data->is_default;
1790 item->line = data->line;
1791 item->col = data->col;
1792
1793 data->items = g_slist_prepend (data->items, item);
1794 data->is_default = FALSE;
1795 data->is_text = FALSE;
1796 }
1797 }
1798
1799 static const GMarkupParser sub_parser =
1800 {
1801 parser_start_element,
1802 parser_end_element,
1803 parser_text_element,
1804 };
1805
1806 static gboolean
gtk_dialog_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * parser_data)1807 gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable,
1808 GtkBuilder *builder,
1809 GObject *child,
1810 const gchar *tagname,
1811 GMarkupParser *parser,
1812 gpointer *parser_data)
1813 {
1814 SubParserData *data;
1815
1816 if (child)
1817 return FALSE;
1818
1819 if (strcmp (tagname, "action-widgets") == 0)
1820 {
1821 data = g_slice_new0 (SubParserData);
1822 data->dialog = GTK_DIALOG (buildable);
1823 data->builder = builder;
1824 data->string = g_string_new ("");
1825 data->items = NULL;
1826 data->in_action_widgets = FALSE;
1827
1828 *parser = sub_parser;
1829 *parser_data = data;
1830 return TRUE;
1831 }
1832
1833 return parent_buildable_iface->custom_tag_start (buildable, builder, child,
1834 tagname, parser, parser_data);
1835 }
1836
1837 static void
gtk_dialog_buildable_custom_finished(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer user_data)1838 gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
1839 GtkBuilder *builder,
1840 GObject *child,
1841 const gchar *tagname,
1842 gpointer user_data)
1843 {
1844 GtkDialog *dialog = GTK_DIALOG (buildable);
1845 GtkDialogPrivate *priv = dialog->priv;
1846 GSList *l;
1847 SubParserData *data;
1848 GObject *object;
1849 ResponseData *ad;
1850 guint signal_id;
1851
1852 if (strcmp (tagname, "action-widgets") != 0)
1853 {
1854 parent_buildable_iface->custom_finished (buildable, builder, child,
1855 tagname, user_data);
1856 return;
1857 }
1858
1859 data = (SubParserData*)user_data;
1860 data->items = g_slist_reverse (data->items);
1861
1862 for (l = data->items; l; l = l->next)
1863 {
1864 ActionWidgetInfo *item = l->data;
1865 gboolean is_action;
1866
1867 object = _gtk_builder_lookup_object (builder, item->widget_name, item->line, item->col);
1868 if (!object)
1869 continue;
1870
1871 /* If the widget already has reponse data at this point, it
1872 * was either added by gtk_dialog_add_action_widget(), or via
1873 * <child type="action"> or by moving an action area child
1874 * to the header bar. In these cases, apply placement heuristics
1875 * based on the response id.
1876 */
1877 is_action = get_response_data (GTK_WIDGET (object), FALSE) != NULL;
1878
1879 ad = get_response_data (GTK_WIDGET (object), TRUE);
1880 ad->response_id = item->response_id;
1881
1882 if (GTK_IS_BUTTON (object))
1883 signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1884 else
1885 signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1886
1887 if (signal_id && !is_action)
1888 {
1889 GClosure *closure;
1890
1891 closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1892 G_OBJECT (dialog));
1893 g_signal_connect_closure_by_id (object, signal_id, 0, closure, FALSE);
1894 }
1895
1896 if (gtk_widget_get_parent (GTK_WIDGET (object)) == priv->action_area)
1897 {
1898 apply_response_for_action_area (dialog, GTK_WIDGET (object), ad->response_id);
1899 }
1900 else if (gtk_widget_get_parent (GTK_WIDGET (object)) == priv->headerbar)
1901 {
1902 if (is_action)
1903 apply_response_for_header_bar (dialog, GTK_WIDGET (object), ad->response_id);
1904 }
1905
1906 if (item->is_default)
1907 gtk_widget_grab_default (GTK_WIDGET (object));
1908 }
1909
1910 g_slist_free_full (data->items, free_action_widget_info);
1911 g_string_free (data->string, TRUE);
1912 g_slice_free (SubParserData, data);
1913
1914 update_suggested_action (dialog);
1915 }
1916
1917 static void
gtk_dialog_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)1918 gtk_dialog_buildable_add_child (GtkBuildable *buildable,
1919 GtkBuilder *builder,
1920 GObject *child,
1921 const gchar *type)
1922 {
1923 GtkDialog *dialog = GTK_DIALOG (buildable);
1924 GtkDialogPrivate *priv = dialog->priv;
1925
1926 if (type == NULL)
1927 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
1928 else if (g_str_equal (type, "titlebar"))
1929 {
1930 priv->headerbar = GTK_WIDGET (child);
1931 gtk_window_set_titlebar (GTK_WINDOW (buildable), priv->headerbar);
1932 }
1933 else if (g_str_equal (type, "action"))
1934 gtk_dialog_add_action_widget (GTK_DIALOG (buildable), GTK_WIDGET (child), GTK_RESPONSE_NONE);
1935 else
1936 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
1937 }
1938
1939 /**
1940 * gtk_dialog_get_action_area:
1941 * @dialog: a #GtkDialog
1942 *
1943 * Returns the action area of @dialog.
1944 *
1945 * Returns: (type Gtk.Box) (transfer none): the action area
1946 *
1947 * Since: 2.14
1948 *
1949 * Deprecated:3.12: Direct access to the action area
1950 * is discouraged; use gtk_dialog_add_button(), etc.
1951 */
1952 GtkWidget *
gtk_dialog_get_action_area(GtkDialog * dialog)1953 gtk_dialog_get_action_area (GtkDialog *dialog)
1954 {
1955 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1956
1957 return dialog->priv->action_area;
1958 }
1959
1960 /**
1961 * gtk_dialog_get_header_bar:
1962 * @dialog: a #GtkDialog
1963 *
1964 * Returns the header bar of @dialog. Note that the
1965 * headerbar is only used by the dialog if the
1966 * #GtkDialog:use-header-bar property is %TRUE.
1967 *
1968 * Returns: (type Gtk.HeaderBar) (transfer none): the header bar
1969 *
1970 * Since: 3.12
1971 */
1972 GtkWidget *
gtk_dialog_get_header_bar(GtkDialog * dialog)1973 gtk_dialog_get_header_bar (GtkDialog *dialog)
1974 {
1975 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1976
1977 return dialog->priv->headerbar;
1978 }
1979
1980 /**
1981 * gtk_dialog_get_content_area:
1982 * @dialog: a #GtkDialog
1983 *
1984 * Returns the content area of @dialog.
1985 *
1986 * Returns: (type Gtk.Box) (transfer none): the content area #GtkBox.
1987 *
1988 * Since: 2.14
1989 **/
1990 GtkWidget *
gtk_dialog_get_content_area(GtkDialog * dialog)1991 gtk_dialog_get_content_area (GtkDialog *dialog)
1992 {
1993 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1994
1995 return dialog->priv->vbox;
1996 }
1997