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