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 "gtkheaderbarprivate.h"
35#include "gtklabel.h"
36#include "gtkmarshalers.h"
37#include "gtkbox.h"
38#include "gtkmain.h"
39#include "gtkintl.h"
40#include "gtkprivate.h"
41#include "gtkbuildable.h"
42#include "gtkbuilderprivate.h"
43#include "gtksettings.h"
44#include "gtktypebuiltins.h"
45#include "gtksizegroup.h"
46
47/**
48 * GtkDialog:
49 *
50 * Dialogs are a convenient way to prompt the user for a small amount
51 * of input.
52 *
53 * ![An example GtkDialog](dialog.png)
54 *
55 * Typical uses are to display a message, ask a question, or anything else
56 * that does not require extensive effort on the user’s part.
57 *
58 * The main area of a `GtkDialog` is called the "content area", and is yours
59 * to populate with widgets such a `GtkLabel` or `GtkEntry`, to present
60 * your information, questions, or tasks to the user.
61 *
62 * In addition, dialogs allow you to add "action widgets". Most commonly,
63 * action widgets are buttons. Depending on the platform, action widgets may
64 * be presented in the header bar at the top of the window, or at the bottom
65 * of the window. To add action widgets, create your `GtkDialog` using
66 * [ctor@Gtk.Dialog.new_with_buttons], or use
67 * [method@Gtk.Dialog.add_button], [method@Gtk.Dialog.add_buttons],
68 * or [method@Gtk.Dialog.add_action_widget].
69 *
70 * `GtkDialogs` uses some heuristics to decide whether to add a close
71 * button to the window decorations. If any of the action buttons use
72 * the response ID %GTK_RESPONSE_CLOSE or %GTK_RESPONSE_CANCEL, the
73 * close button is omitted.
74 *
75 * Clicking a button that was added as an action widget will emit the
76 * [signal@Gtk.Dialog::response] signal with a response ID that you specified.
77 * GTK will never assign a meaning to positive response IDs; these are
78 * entirely user-defined. But for convenience, you can use the response
79 * IDs in the [enum@Gtk.ResponseType] enumeration (these all have values
80 * less than zero). If a dialog receives a delete event, the
81 * [signal@Gtk.Dialog::response] signal will be emitted with the
82 * %GTK_RESPONSE_DELETE_EVENT response ID.
83 *
84 * Dialogs are created with a call to [ctor@Gtk.Dialog.new] or
85 * [ctor@Gtk.Dialog.new_with_buttons]. The latter is recommended; it allows
86 * you to set the dialog title, some convenient flags, and add buttons.
87 *
88 * A “modal” dialog (that is, one which freezes the rest of the application
89 * from user input), can be created by calling [method@Gtk.Window.set_modal]
90 * on the dialog. When using [ctor@Gtk.Dialog.new_with_buttons], you can also
91 * pass the %GTK_DIALOG_MODAL flag to make a dialog modal.
92 *
93 * For the simple dialog in the following example, a [class@Gtk.MessageDialog]
94 * would save some effort. But you’d need to create the dialog contents manually
95 * if you had more than a simple message in the dialog.
96 *
97 * An example for simple `GtkDialog` usage:
98 *
99 * ```c
100 * // Function to open a dialog box with a message
101 * void
102 * quick_message (GtkWindow *parent, char *message)
103 * {
104 * GtkWidget *dialog, *label, *content_area;
105 * GtkDialogFlags flags;
106 *
107 * // Create the widgets
108 * flags = GTK_DIALOG_DESTROY_WITH_PARENT;
109 * dialog = gtk_dialog_new_with_buttons ("Message",
110 * parent,
111 * flags,
112 * _("_OK"),
113 * GTK_RESPONSE_NONE,
114 * NULL);
115 * content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
116 * label = gtk_label_new (message);
117 *
118 * // Ensure that the dialog box is destroyed when the user responds
119 *
120 * g_signal_connect_swapped (dialog,
121 * "response",
122 * G_CALLBACK (gtk_window_destroy),
123 * dialog);
124 *
125 * // Add the label, and show everything we’ve added
126 *
127 * gtk_box_append (GTK_BOX (content_area), label);
128 * gtk_widget_show (dialog);
129 * }
130 * ```
131 *
132 * # GtkDialog as GtkBuildable
133 *
134 * The `GtkDialog` implementation of the `GtkBuildable` interface exposes the
135 * @content_area as an internal child with the name “content_area”.
136 *
137 * `GtkDialog` supports a custom `<action-widgets>` element, which can contain
138 * multiple `<action-widget>` elements. The “response” attribute specifies a
139 * numeric response, and the content of the element is the id of widget
140 * (which should be a child of the dialogs @action_area). To mark a response
141 * as default, set the “default” attribute of the `<action-widget>` element
142 * to true.
143 *
144 * `GtkDialog` supports adding action widgets by specifying “action” as
145 * the “type” attribute of a `<child>` element. The widget will be added
146 * either to the action area or the headerbar of the dialog, depending
147 * on the “use-header-bar” property. The response id has to be associated
148 * with the action widget using the `<action-widgets>` element.
149 *
150 * An example of a `GtkDialog` UI definition fragment:
151 *
152 * ```xml
153 * <object class="GtkDialog" id="dialog1">
154 * <child type="action">
155 * <object class="GtkButton" id="button_cancel"/>
156 * </child>
157 * <child type="action">
158 * <object class="GtkButton" id="button_ok">
159 * </object>
160 * </child>
161 * <action-widgets>
162 * <action-widget response="cancel">button_cancel</action-widget>
163 * <action-widget response="ok" default="true">button_ok</action-widget>
164 * </action-widgets>
165 * </object>
166 * ```
167 *
168 * # Accessibility
169 *
170 * `GtkDialog` uses the %GTK_ACCESSIBLE_ROLE_DIALOG role.
171 */
172
173typedef struct _ResponseData ResponseData;
174
175typedef struct
176{
177 GtkWidget *headerbar;
178 GtkWidget *action_area;
179 GtkWidget *content_area;
180 GtkWidget *action_box;
181 GtkSizeGroup *size_group;
182
183 int use_header_bar;
184 gboolean constructed;
185 ResponseData *action_widgets;
186} GtkDialogPrivate;
187
188struct _ResponseData
189{
190 ResponseData *next;
191 GtkDialog *dialog;
192 GtkWidget *widget;
193 int response_id;
194};
195
196static void gtk_dialog_add_buttons_valist (GtkDialog *dialog,
197 const char *first_button_text,
198 va_list args);
199
200static gboolean gtk_dialog_close_request (GtkWindow *window);
201static void gtk_dialog_map (GtkWidget *widget);
202
203static void gtk_dialog_close (GtkDialog *dialog);
204
205static ResponseData * get_response_data (GtkDialog *dialog,
206 GtkWidget *widget,
207 gboolean create);
208
209static void gtk_dialog_buildable_interface_init (GtkBuildableIface *iface);
210static gboolean gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable,
211 GtkBuilder *builder,
212 GObject *child,
213 const char *tagname,
214 GtkBuildableParser *parser,
215 gpointer *data);
216static void gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
217 GtkBuilder *builder,
218 GObject *child,
219 const char *tagname,
220 gpointer user_data);
221static void gtk_dialog_buildable_add_child (GtkBuildable *buildable,
222 GtkBuilder *builder,
223 GObject *child,
224 const char *type);
225
226
227enum {
228 PROP_0,
229 PROP_USE_HEADER_BAR
230};
231
232enum {
233 RESPONSE,
234 CLOSE,
235 LAST_SIGNAL
236};
237
238static guint dialog_signals[LAST_SIGNAL];
239
240G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW,
241 G_ADD_PRIVATE (GtkDialog)
242 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
243 gtk_dialog_buildable_interface_init))
244
245static void
246set_use_header_bar (GtkDialog *dialog,
247 int use_header_bar)
248{
249 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
250
251 if (use_header_bar == -1)
252 return;
253
254 priv->use_header_bar = use_header_bar;
255}
256
257/* A convenience helper for built-in dialogs */
258void
259gtk_dialog_set_use_header_bar_from_setting (GtkDialog *dialog)
260{
261 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
262
263 g_assert (!priv->constructed);
264
265 g_object_get (object: gtk_widget_get_settings (GTK_WIDGET (dialog)),
266 first_property_name: "gtk-dialogs-use-header", &priv->use_header_bar,
267 NULL);
268}
269
270static void
271gtk_dialog_set_property (GObject *object,
272 guint prop_id,
273 const GValue *value,
274 GParamSpec *pspec)
275{
276 GtkDialog *dialog = GTK_DIALOG (object);
277
278 switch (prop_id)
279 {
280 case PROP_USE_HEADER_BAR:
281 set_use_header_bar (dialog, use_header_bar: g_value_get_int (value));
282 break;
283
284 default:
285 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286 break;
287 }
288}
289
290static void
291gtk_dialog_get_property (GObject *object,
292 guint prop_id,
293 GValue *value,
294 GParamSpec *pspec)
295{
296 GtkDialog *dialog = GTK_DIALOG (object);
297 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
298
299 switch (prop_id)
300 {
301 case PROP_USE_HEADER_BAR:
302 g_value_set_int (value, v_int: priv->use_header_bar);
303 break;
304
305 default:
306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307 break;
308 }
309}
310
311static void
312action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
313{
314 int response_id;
315
316 response_id = gtk_dialog_get_response_for_widget (dialog, widget);
317
318 gtk_dialog_response (dialog, response_id);
319}
320
321static void
322add_response_data (GtkDialog *dialog,
323 GtkWidget *child,
324 int response_id)
325{
326 ResponseData *ad;
327 guint signal_id;
328
329 ad = get_response_data (dialog, widget: child, TRUE);
330 ad->response_id = response_id;
331
332 if (GTK_IS_BUTTON (child))
333 signal_id = g_signal_lookup (name: "clicked", GTK_TYPE_BUTTON);
334 else
335 signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (child));
336
337 if (signal_id)
338 {
339 GClosure *closure;
340
341 closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
342 G_OBJECT (dialog));
343 g_signal_connect_closure_by_id (instance: child, signal_id, detail: 0, closure, FALSE);
344 }
345 else
346 g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
347}
348
349static void
350add_to_header_bar (GtkDialog *dialog,
351 GtkWidget *child,
352 int response_id)
353{
354 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
355
356 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_CENTER);
357
358 if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_HELP)
359 gtk_header_bar_pack_start (GTK_HEADER_BAR (priv->headerbar), child);
360 else
361 gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->headerbar), child);
362
363 gtk_size_group_add_widget (size_group: priv->size_group, widget: child);
364
365 if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_CLOSE)
366 gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (priv->headerbar), FALSE);
367}
368
369static void
370apply_response_for_action_area (GtkDialog *dialog,
371 GtkWidget *child,
372 int response_id)
373{
374 GtkDialogPrivate *priv G_GNUC_UNUSED = gtk_dialog_get_instance_private (self: dialog);
375
376 g_assert (gtk_widget_get_parent (child) == priv->action_area);
377}
378
379static void
380add_to_action_area (GtkDialog *dialog,
381 GtkWidget *child,
382 int response_id)
383{
384 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
385
386 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_BASELINE);
387 gtk_box_append (GTK_BOX (priv->action_area), child);
388 apply_response_for_action_area (dialog, child, response_id);
389}
390
391static void
392update_suggested_action (GtkDialog *dialog,
393 GtkWidget *child)
394{
395 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
396
397 if (priv->use_header_bar)
398 {
399 if (gtk_widget_has_css_class (widget: child, css_class: "default"))
400 gtk_widget_add_css_class (widget: child, css_class: "suggested-action");
401 else
402 gtk_widget_remove_css_class (widget: child, css_class: "suggested-action");
403 }
404}
405
406static void
407gtk_dialog_constructed (GObject *object)
408{
409 GtkDialog *dialog = GTK_DIALOG (object);
410 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
411
412 G_OBJECT_CLASS (gtk_dialog_parent_class)->constructed (object);
413
414 priv->constructed = TRUE;
415 if (priv->use_header_bar == -1)
416 priv->use_header_bar = FALSE;
417
418 if (priv->use_header_bar)
419 {
420 GList *children, *l;
421 GtkWidget *child;
422
423 children = NULL;
424 for (child = gtk_widget_get_first_child (widget: priv->action_area);
425 child != NULL;
426 child = gtk_widget_get_next_sibling (widget: child))
427 children = g_list_append (list: children, data: child);
428
429 for (l = children; l != NULL; l = l->next)
430 {
431 gboolean has_default;
432 ResponseData *rd;
433 int response_id;
434
435 child = l->data;
436
437 has_default = gtk_widget_has_default (widget: child);
438 rd = get_response_data (dialog, widget: child, FALSE);
439 response_id = rd ? rd->response_id : GTK_RESPONSE_NONE;
440
441 g_object_ref (child);
442 gtk_box_remove (GTK_BOX (priv->action_area), child);
443 add_to_header_bar (dialog, child, response_id);
444 g_object_unref (object: child);
445
446 if (has_default)
447 {
448 gtk_window_set_default_widget (GTK_WINDOW (dialog), default_widget: child);
449 update_suggested_action (dialog, child);
450 }
451 }
452 g_list_free (list: children);
453
454 _gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
455 }
456 else
457 {
458 gtk_window_set_titlebar (GTK_WINDOW (dialog), NULL);
459 priv->headerbar = NULL;
460 }
461
462 gtk_widget_set_visible (widget: priv->action_box, visible: !priv->use_header_bar);
463}
464
465static void
466gtk_dialog_finalize (GObject *obj)
467{
468 GtkDialog *dialog = GTK_DIALOG (obj);
469 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
470
471 while (priv->action_widgets)
472 g_object_set_data (G_OBJECT (priv->action_widgets->widget),
473 key: "gtk-dialog-response-data", NULL);
474
475 g_object_unref (object: priv->size_group);
476
477 G_OBJECT_CLASS (gtk_dialog_parent_class)->finalize (obj);
478}
479
480static void
481gtk_dialog_class_init (GtkDialogClass *class)
482{
483 GObjectClass *gobject_class;
484 GtkWidgetClass *widget_class;
485 GtkWindowClass *window_class;
486
487 gobject_class = G_OBJECT_CLASS (class);
488 widget_class = GTK_WIDGET_CLASS (class);
489 window_class = GTK_WINDOW_CLASS (class);
490
491 gobject_class->constructed = gtk_dialog_constructed;
492 gobject_class->set_property = gtk_dialog_set_property;
493 gobject_class->get_property = gtk_dialog_get_property;
494 gobject_class->finalize = gtk_dialog_finalize;
495
496 widget_class->map = gtk_dialog_map;
497
498 window_class->close_request = gtk_dialog_close_request;
499
500 class->close = gtk_dialog_close;
501
502 /**
503 * GtkDialog::response:
504 * @dialog: the object on which the signal is emitted
505 * @response_id: the response ID
506 *
507 * Emitted when an action widget is clicked.
508 *
509 * The signal is also emitted when the dialog receives a
510 * delete event, and when [method@Gtk.Dialog.response] is called.
511 * On a delete event, the response ID is %GTK_RESPONSE_DELETE_EVENT.
512 * Otherwise, it depends on which action widget was clicked.
513 */
514 dialog_signals[RESPONSE] =
515 g_signal_new (I_("response"),
516 G_OBJECT_CLASS_TYPE (class),
517 signal_flags: G_SIGNAL_RUN_LAST,
518 G_STRUCT_OFFSET (GtkDialogClass, response),
519 NULL, NULL,
520 NULL,
521 G_TYPE_NONE, n_params: 1,
522 G_TYPE_INT);
523
524 /**
525 * GtkDialog::close:
526 *
527 * Emitted when the user uses a keybinding to close the dialog.
528 *
529 * This is a [keybinding signal](class.SignalAction.html).
530 *
531 * The default binding for this signal is the Escape key.
532 */
533 dialog_signals[CLOSE] =
534 g_signal_new (I_("close"),
535 G_OBJECT_CLASS_TYPE (class),
536 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
537 G_STRUCT_OFFSET (GtkDialogClass, close),
538 NULL, NULL,
539 NULL,
540 G_TYPE_NONE, n_params: 0);
541
542 /**
543 * GtkDialog:use-header-bar:
544 *
545 * %TRUE if the dialog uses a headerbar for action buttons
546 * instead of the action-area.
547 *
548 * For technical reasons, this property is declared as an integer
549 * property, but you should only set it to %TRUE or %FALSE.
550 *
551 * ## Creating a dialog with headerbar
552 *
553 * Builtin `GtkDialog` subclasses such as [class@Gtk.ColorChooserDialog]
554 * set this property according to platform conventions (using the
555 * [property@Gtk.Settings:gtk-dialogs-use-header] setting).
556 *
557 * Here is how you can achieve the same:
558 *
559 * ```c
560 * g_object_get (settings, "gtk-dialogs-use-header", &header, NULL);
561 * dialog = g_object_new (GTK_TYPE_DIALOG, header, TRUE, NULL);
562 * ```
563 */
564 g_object_class_install_property (oclass: gobject_class,
565 property_id: PROP_USE_HEADER_BAR,
566 pspec: g_param_spec_int (name: "use-header-bar",
567 P_("Use Header Bar"),
568 P_("Use Header Bar for actions."),
569 minimum: -1, maximum: 1, default_value: -1,
570 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
571
572 gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Escape, mods: 0, signal: "close", NULL);
573
574 /* Bind class to template
575 */
576 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/org/gtk/libgtk/ui/gtkdialog.ui");
577 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, headerbar);
578 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, action_area);
579 gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, content_area);
580 gtk_widget_class_bind_template_child_private (widget_class, GtkDialog, action_box);
581
582 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_DIALOG);
583}
584
585static void
586gtk_dialog_init (GtkDialog *dialog)
587{
588 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
589
590 gtk_widget_add_css_class (GTK_WIDGET (dialog), css_class: "dialog");
591
592 priv->use_header_bar = -1;
593 priv->size_group = gtk_size_group_new (mode: GTK_SIZE_GROUP_HORIZONTAL);
594
595 gtk_widget_init_template (GTK_WIDGET (dialog));
596}
597
598static GtkBuildableIface *parent_buildable_iface;
599
600static void
601gtk_dialog_buildable_interface_init (GtkBuildableIface *iface)
602{
603 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
604 iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start;
605 iface->custom_finished = gtk_dialog_buildable_custom_finished;
606 iface->add_child = gtk_dialog_buildable_add_child;
607}
608
609static gboolean
610gtk_dialog_close_request (GtkWindow *window)
611{
612 gtk_dialog_response (GTK_DIALOG (window), response_id: GTK_RESPONSE_DELETE_EVENT);
613
614 return GTK_WINDOW_CLASS (gtk_dialog_parent_class)->close_request (window);
615}
616
617/* A far too tricky heuristic for getting the right initial
618 * focus widget if none was set. What we do is we focus the first
619 * widget in the tab chain, but if this results in the focus
620 * ending up on one of the response widgets _other_ than the
621 * default response, we focus the default response instead.
622 *
623 * Additionally, skip selectable labels when looking for the
624 * right initial focus widget.
625 */
626static void
627gtk_dialog_map (GtkWidget *widget)
628{
629 GtkWidget *default_widget, *focus;
630 GtkWindow *window = GTK_WINDOW (widget);
631 GtkDialog *dialog = GTK_DIALOG (widget);
632 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
633 ResponseData *rd;
634
635 if (gtk_window_get_transient_for (window) == NULL)
636 g_message ("GtkDialog mapped without a transient parent. This is discouraged.");
637
638 GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
639
640 focus = gtk_window_get_focus (window);
641 if (!focus)
642 {
643 GtkWidget *first_focus = NULL;
644
645 do
646 {
647 g_signal_emit_by_name (instance: window, detailed_signal: "move_focus", GTK_DIR_TAB_FORWARD);
648
649 focus = gtk_window_get_focus (window);
650 if (GTK_IS_LABEL (focus) &&
651 !gtk_label_get_current_uri (GTK_LABEL (focus)))
652 gtk_label_select_region (GTK_LABEL (focus), start_offset: 0, end_offset: 0);
653
654 if (first_focus == NULL)
655 first_focus = focus;
656 else if (first_focus == focus)
657 break;
658
659 if (!GTK_IS_LABEL (focus))
660 break;
661 }
662 while (TRUE);
663
664 default_widget = gtk_window_get_default_widget (window);
665 for (rd = priv->action_widgets; rd != NULL; rd = rd->next)
666 {
667 if ((focus == NULL || rd->widget == focus) &&
668 rd->widget != default_widget &&
669 default_widget)
670 {
671 gtk_widget_grab_focus (widget: default_widget);
672 break;
673 }
674 }
675 }
676}
677
678static void
679gtk_dialog_close (GtkDialog *dialog)
680{
681 gtk_window_close (GTK_WINDOW (dialog));
682}
683
684/**
685 * gtk_dialog_new:
686 *
687 * Creates a new dialog box.
688 *
689 * Widgets should not be packed into the `GtkWindow`
690 * directly, but into the @content_area and @action_area,
691 * as described above.
692 *
693 * Returns: the new dialog as a `GtkWidget`
694 */
695GtkWidget*
696gtk_dialog_new (void)
697{
698 return g_object_new (GTK_TYPE_DIALOG, NULL);
699}
700
701static GtkWidget*
702gtk_dialog_new_empty (const char *title,
703 GtkWindow *parent,
704 GtkDialogFlags flags)
705{
706 GtkDialog *dialog;
707
708 dialog = g_object_new (GTK_TYPE_DIALOG,
709 first_property_name: "use-header-bar", (flags & GTK_DIALOG_USE_HEADER_BAR) != 0,
710 NULL);
711
712 if (title)
713 gtk_window_set_title (GTK_WINDOW (dialog), title);
714
715 if (parent)
716 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
717
718 if (flags & GTK_DIALOG_MODAL)
719 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
720
721 if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
722 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
723
724 return GTK_WIDGET (dialog);
725}
726
727/**
728 * gtk_dialog_new_with_buttons:
729 * @title: (nullable): Title of the dialog
730 * @parent: (nullable): Transient parent of the dialog
731 * @flags: from `GtkDialogFlags`
732 * @first_button_text: (nullable): text to go in first button
733 * @...: response ID for first button, then additional buttons, ending with %NULL
734 *
735 * Creates a new `GtkDialog` with the given title and transient parent.
736 *
737 * The @flags argument can be used to make the dialog modal, have it
738 * destroyed along with its transient parent, or make it use a headerbar.
739 *
740 * Button text/response ID pairs should be listed in pairs, with a %NULL
741 * pointer ending the list. Button text can be arbitrary text. A response
742 * ID can be any positive number, or one of the values in the
743 * [enum@Gtk.ResponseType] enumeration. If the user clicks one of these
744 * buttons, `GtkDialog` will emit the [signal@Gtk.Dialog::response] signal
745 * with the corresponding response ID.
746 *
747 * If a `GtkDialog` receives a delete event, it will emit ::response with a
748 * response ID of %GTK_RESPONSE_DELETE_EVENT.
749 *
750 * However, destroying a dialog does not emit the ::response signal;
751 * so be careful relying on ::response when using the
752 * %GTK_DIALOG_DESTROY_WITH_PARENT flag.
753 *
754 * Here’s a simple example:
755 * ```c
756 * GtkWindow *main_app_window; // Window the dialog should show up on
757 * GtkWidget *dialog;
758 * GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
759 * dialog = gtk_dialog_new_with_buttons ("My dialog",
760 * main_app_window,
761 * flags,
762 * _("_OK"),
763 * GTK_RESPONSE_ACCEPT,
764 * _("_Cancel"),
765 * GTK_RESPONSE_REJECT,
766 * NULL);
767 * ```
768 *
769 * Returns: a new `GtkDialog`
770 */
771GtkWidget*
772gtk_dialog_new_with_buttons (const char *title,
773 GtkWindow *parent,
774 GtkDialogFlags flags,
775 const char *first_button_text,
776 ...)
777{
778 GtkDialog *dialog;
779 va_list args;
780
781 dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
782
783 va_start (args, first_button_text);
784
785 gtk_dialog_add_buttons_valist (dialog,
786 first_button_text,
787 args);
788
789 va_end (args);
790
791 return GTK_WIDGET (dialog);
792}
793
794static void
795response_data_free (gpointer data)
796{
797 ResponseData *ad = data;
798 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: ad->dialog);
799
800 if (priv->action_widgets == ad)
801 {
802 priv->action_widgets = ad->next;
803 }
804 else
805 {
806 ResponseData *prev = priv->action_widgets;
807 while (prev)
808 {
809 if (prev->next == ad)
810 {
811 prev->next = ad->next;
812 break;
813 }
814 prev = prev->next;
815 }
816 }
817 g_slice_free (ResponseData, data);
818}
819
820static ResponseData *
821get_response_data (GtkDialog *dialog,
822 GtkWidget *widget,
823 gboolean create)
824{
825 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
826
827 ResponseData *ad = g_object_get_data (G_OBJECT (widget),
828 key: "gtk-dialog-response-data");
829
830 if (ad == NULL && create)
831 {
832 ad = g_slice_new (ResponseData);
833 ad->dialog = dialog;
834 ad->widget = widget;
835 g_object_set_data_full (G_OBJECT (widget),
836 I_("gtk-dialog-response-data"),
837 data: ad,
838 destroy: response_data_free);
839 ad->next = priv->action_widgets;
840 priv->action_widgets = ad;
841 }
842
843 return ad;
844}
845
846/**
847 * gtk_dialog_add_action_widget:
848 * @dialog: a `GtkDialog`
849 * @child: an activatable widget
850 * @response_id: response ID for @child
851 *
852 * Adds an activatable widget to the action area of a `GtkDialog`.
853 *
854 * GTK connects a signal handler that will emit the
855 * [signal@Gtk.Dialog::response] signal on the dialog when the widget
856 * is activated. The widget is appended to the end of the dialog’s action
857 * area.
858 *
859 * If you want to add a non-activatable widget, simply pack it into
860 * the @action_area field of the `GtkDialog` struct.
861 */
862void
863gtk_dialog_add_action_widget (GtkDialog *dialog,
864 GtkWidget *child,
865 int response_id)
866{
867 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
868
869 g_return_if_fail (GTK_IS_DIALOG (dialog));
870 g_return_if_fail (GTK_IS_WIDGET (child));
871
872 add_response_data (dialog, child, response_id);
873
874 if (priv->constructed && priv->use_header_bar)
875 {
876 add_to_header_bar (dialog, child, response_id);
877
878 if (gtk_widget_has_default (widget: child))
879 {
880 gtk_window_set_default_widget (GTK_WINDOW (dialog), default_widget: child);
881 update_suggested_action (dialog, child);
882 }
883 }
884 else
885 add_to_action_area (dialog, child, response_id);
886}
887
888/**
889 * gtk_dialog_add_button:
890 * @dialog: a `GtkDialog`
891 * @button_text: text of button
892 * @response_id: response ID for the button
893 *
894 * Adds a button with the given text.
895 *
896 * GTK arranges things so that clicking the button will emit the
897 * [signal@Gtk.Dialog::response] signal with the given @response_id.
898 * The button is appended to the end of the dialog’s action area.
899 * The button widget is returned, but usually you don’t need it.
900 *
901 * Returns: (transfer none): the `GtkButton` widget that was added
902 */
903GtkWidget*
904gtk_dialog_add_button (GtkDialog *dialog,
905 const char *button_text,
906 int response_id)
907{
908 GtkWidget *button;
909
910 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
911 g_return_val_if_fail (button_text != NULL, NULL);
912
913 button = gtk_button_new_with_label (label: button_text);
914 gtk_button_set_use_underline (GTK_BUTTON (button), TRUE);
915
916 gtk_dialog_add_action_widget (dialog, child: button, response_id);
917
918 return button;
919}
920
921static void
922gtk_dialog_add_buttons_valist (GtkDialog *dialog,
923 const char *first_button_text,
924 va_list args)
925{
926 const char * text;
927 int response_id;
928
929 g_return_if_fail (GTK_IS_DIALOG (dialog));
930
931 if (first_button_text == NULL)
932 return;
933
934 text = first_button_text;
935 response_id = va_arg (args, int);
936
937 while (text != NULL)
938 {
939 gtk_dialog_add_button (dialog, button_text: text, response_id);
940
941 text = va_arg (args, char *);
942 if (text == NULL)
943 break;
944 response_id = va_arg (args, int);
945 }
946}
947
948/**
949 * gtk_dialog_add_buttons:
950 * @dialog: a `GtkDialog`
951 * @first_button_text: button text
952 * @...: response ID for first button, then more text-response_id pairs
953 *
954 * Adds multiple buttons.
955 *
956 * This is the same as calling [method@Gtk.Dialog.add_button]
957 * repeatedly. The variable argument list should be %NULL-terminated
958 * as with [ctor@Gtk.Dialog.new_with_buttons]. Each button must have both
959 * text and response ID.
960 */
961void
962gtk_dialog_add_buttons (GtkDialog *dialog,
963 const char *first_button_text,
964 ...)
965{
966 va_list args;
967
968 va_start (args, first_button_text);
969
970 gtk_dialog_add_buttons_valist (dialog,
971 first_button_text,
972 args);
973
974 va_end (args);
975}
976
977/**
978 * gtk_dialog_set_response_sensitive:
979 * @dialog: a `GtkDialog`
980 * @response_id: a response ID
981 * @setting: %TRUE for sensitive
982 *
983 * A convenient way to sensitize/desensitize dialog buttons.
984 *
985 * Calls `gtk_widget_set_sensitive (widget, @setting)`
986 * for each widget in the dialog’s action area with the given @response_id.
987 */
988void
989gtk_dialog_set_response_sensitive (GtkDialog *dialog,
990 int response_id,
991 gboolean setting)
992{
993 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
994 ResponseData *rd;
995
996 g_return_if_fail (GTK_IS_DIALOG (dialog));
997
998 for (rd = priv->action_widgets; rd != NULL; rd = rd->next)
999 {
1000 if (rd->response_id == response_id)
1001 gtk_widget_set_sensitive (widget: rd->widget, sensitive: setting);
1002 }
1003}
1004
1005/**
1006 * gtk_dialog_set_default_response:
1007 * @dialog: a `GtkDialog`
1008 * @response_id: a response ID
1009 *
1010 * Sets the default widget for the dialog based on the response ID.
1011 *
1012 * Pressing “Enter” normally activates the default widget.
1013 */
1014void
1015gtk_dialog_set_default_response (GtkDialog *dialog,
1016 int response_id)
1017{
1018 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1019 ResponseData *rd;
1020
1021 g_return_if_fail (GTK_IS_DIALOG (dialog));
1022
1023 for (rd = priv->action_widgets; rd != NULL; rd = rd->next)
1024 {
1025 if (rd->response_id == response_id)
1026 {
1027 gtk_window_set_default_widget (GTK_WINDOW (dialog), default_widget: rd->widget);
1028 update_suggested_action (dialog, child: rd->widget);
1029 }
1030 }
1031}
1032
1033/**
1034 * gtk_dialog_response: (attributes org.gtk.Method.signal=response)
1035 * @dialog: a `GtkDialog`
1036 * @response_id: response ID
1037 *
1038 * Emits the ::response signal with the given response ID.
1039 *
1040 * Used to indicate that the user has responded to the dialog in some way.
1041 */
1042void
1043gtk_dialog_response (GtkDialog *dialog,
1044 int response_id)
1045{
1046 g_return_if_fail (GTK_IS_DIALOG (dialog));
1047
1048 g_signal_emit (instance: dialog,
1049 signal_id: dialog_signals[RESPONSE],
1050 detail: 0,
1051 response_id);
1052}
1053
1054/**
1055 * gtk_dialog_get_widget_for_response:
1056 * @dialog: a `GtkDialog`
1057 * @response_id: the response ID used by the @dialog widget
1058 *
1059 * Gets the widget button that uses the given response ID in the action area
1060 * of a dialog.
1061 *
1062 * Returns: (nullable) (transfer none): the @widget button that uses the given
1063 * @response_id
1064 */
1065GtkWidget*
1066gtk_dialog_get_widget_for_response (GtkDialog *dialog,
1067 int response_id)
1068{
1069 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1070 ResponseData *rd;
1071
1072 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1073
1074 for (rd = priv->action_widgets; rd != NULL; rd = rd->next)
1075 {
1076 if (rd->response_id == response_id)
1077 return rd->widget;
1078 }
1079
1080 return NULL;
1081}
1082
1083/**
1084 * gtk_dialog_get_response_for_widget:
1085 * @dialog: a `GtkDialog`
1086 * @widget: a widget in the action area of @dialog
1087 *
1088 * Gets the response id of a widget in the action area
1089 * of a dialog.
1090 *
1091 * Returns: the response id of @widget, or %GTK_RESPONSE_NONE
1092 * if @widget doesn’t have a response id set.
1093 */
1094int
1095gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1096 GtkWidget *widget)
1097{
1098 ResponseData *rd;
1099
1100 rd = get_response_data (dialog, widget, FALSE);
1101 if (!rd)
1102 return GTK_RESPONSE_NONE;
1103 else
1104 return rd->response_id;
1105}
1106
1107typedef struct {
1108 char *widget_name;
1109 int response_id;
1110 gboolean is_default;
1111 int line;
1112 int col;
1113} ActionWidgetInfo;
1114
1115typedef struct {
1116 GtkDialog *dialog;
1117 GtkBuilder *builder;
1118 GSList *items;
1119 int response_id;
1120 gboolean is_default;
1121 gboolean is_text;
1122 GString *string;
1123 gboolean in_action_widgets;
1124 int line;
1125 int col;
1126} SubParserData;
1127
1128static void
1129free_action_widget_info (gpointer data)
1130{
1131 ActionWidgetInfo *item = data;
1132
1133 g_free (mem: item->widget_name);
1134 g_free (mem: item);
1135}
1136
1137static void
1138parser_start_element (GtkBuildableParseContext *context,
1139 const char *element_name,
1140 const char **names,
1141 const char **values,
1142 gpointer user_data,
1143 GError **error)
1144{
1145 SubParserData *data = (SubParserData*)user_data;
1146
1147 if (strcmp (s1: element_name, s2: "action-widget") == 0)
1148 {
1149 const char *response;
1150 gboolean is_default = FALSE;
1151 GValue gvalue = G_VALUE_INIT;
1152
1153 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "action-widgets", error))
1154 return;
1155
1156 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1157 first_type: G_MARKUP_COLLECT_STRING, first_attr: "response", &response,
1158 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "default", &is_default,
1159 G_MARKUP_COLLECT_INVALID))
1160 {
1161 _gtk_builder_prefix_error (builder: data->builder, context, error);
1162 return;
1163 }
1164
1165 if (!gtk_builder_value_from_string_type (builder: data->builder, type: GTK_TYPE_RESPONSE_TYPE, string: response, value: &gvalue, error))
1166 {
1167 _gtk_builder_prefix_error (builder: data->builder, context, error);
1168 return;
1169 }
1170
1171 data->response_id = g_value_get_enum (value: &gvalue);
1172 data->is_default = is_default;
1173 data->is_text = TRUE;
1174 g_string_set_size (string: data->string, len: 0);
1175 gtk_buildable_parse_context_get_position (context, line_number: &data->line, char_number: &data->col);
1176 }
1177 else if (strcmp (s1: element_name, s2: "action-widgets") == 0)
1178 {
1179 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "object", error))
1180 return;
1181
1182 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1183 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
1184 G_MARKUP_COLLECT_INVALID))
1185 _gtk_builder_prefix_error (builder: data->builder, context, error);
1186
1187 data->in_action_widgets = TRUE;
1188 }
1189 else
1190 {
1191 _gtk_builder_error_unhandled_tag (builder: data->builder, context,
1192 object: "GtkDialog", element_name,
1193 error);
1194 }
1195}
1196
1197static void
1198parser_text_element (GtkBuildableParseContext *context,
1199 const char *text,
1200 gsize text_len,
1201 gpointer user_data,
1202 GError **error)
1203{
1204 SubParserData *data = (SubParserData*)user_data;
1205
1206 if (data->is_text)
1207 g_string_append_len (string: data->string, val: text, len: text_len);
1208}
1209
1210static void
1211parser_end_element (GtkBuildableParseContext *context,
1212 const char *element_name,
1213 gpointer user_data,
1214 GError **error)
1215{
1216 SubParserData *data = (SubParserData*)user_data;
1217
1218 if (data->is_text)
1219 {
1220 ActionWidgetInfo *item;
1221
1222 item = g_new (ActionWidgetInfo, 1);
1223 item->widget_name = g_strdup (str: data->string->str);
1224 item->response_id = data->response_id;
1225 item->is_default = data->is_default;
1226 item->line = data->line;
1227 item->col = data->col;
1228
1229 data->items = g_slist_prepend (list: data->items, data: item);
1230 data->is_default = FALSE;
1231 data->is_text = FALSE;
1232 }
1233}
1234
1235static const GtkBuildableParser sub_parser =
1236 {
1237 parser_start_element,
1238 parser_end_element,
1239 parser_text_element,
1240 };
1241
1242static gboolean
1243gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable,
1244 GtkBuilder *builder,
1245 GObject *child,
1246 const char *tagname,
1247 GtkBuildableParser *parser,
1248 gpointer *parser_data)
1249{
1250 SubParserData *data;
1251
1252 if (child)
1253 return FALSE;
1254
1255 if (strcmp (s1: tagname, s2: "action-widgets") == 0)
1256 {
1257 data = g_slice_new0 (SubParserData);
1258 data->dialog = GTK_DIALOG (buildable);
1259 data->builder = builder;
1260 data->string = g_string_new (init: "");
1261 data->items = NULL;
1262 data->in_action_widgets = FALSE;
1263
1264 *parser = sub_parser;
1265 *parser_data = data;
1266 return TRUE;
1267 }
1268
1269 return parent_buildable_iface->custom_tag_start (buildable, builder, child,
1270 tagname, parser, parser_data);
1271}
1272
1273static void
1274gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
1275 GtkBuilder *builder,
1276 GObject *child,
1277 const char *tagname,
1278 gpointer user_data)
1279{
1280 GtkDialog *dialog = GTK_DIALOG (buildable);
1281 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1282 GSList *l;
1283 SubParserData *data;
1284 GObject *object;
1285 ResponseData *ad;
1286 guint signal_id;
1287
1288 if (strcmp (s1: tagname, s2: "action-widgets") != 0)
1289 {
1290 parent_buildable_iface->custom_finished (buildable, builder, child,
1291 tagname, user_data);
1292 return;
1293 }
1294
1295 data = (SubParserData*)user_data;
1296 data->items = g_slist_reverse (list: data->items);
1297
1298 for (l = data->items; l; l = l->next)
1299 {
1300 ActionWidgetInfo *item = l->data;
1301 gboolean is_action;
1302
1303 object = _gtk_builder_lookup_object (builder, name: item->widget_name, line: item->line, col: item->col);
1304 if (!object)
1305 continue;
1306
1307 /* If the widget already has response data at this point, it
1308 * was either added by gtk_dialog_add_action_widget(), or via
1309 * <child type="action"> or by moving an action area child
1310 * to the header bar. In these cases, apply placement heuristics
1311 * based on the response id.
1312 */
1313 is_action = get_response_data (dialog, GTK_WIDGET (object), FALSE) != NULL;
1314
1315 ad = get_response_data (dialog, GTK_WIDGET (object), TRUE);
1316 ad->response_id = item->response_id;
1317
1318 if (GTK_IS_BUTTON (object))
1319 signal_id = g_signal_lookup (name: "clicked", GTK_TYPE_BUTTON);
1320 else
1321 signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (object));
1322
1323 if (signal_id && !is_action)
1324 {
1325 GClosure *closure;
1326
1327 closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1328 G_OBJECT (dialog));
1329 g_signal_connect_closure_by_id (instance: object, signal_id, detail: 0, closure, FALSE);
1330 }
1331
1332 if (gtk_widget_get_parent (GTK_WIDGET (object)) == priv->action_area)
1333 {
1334 apply_response_for_action_area (dialog, GTK_WIDGET (object), response_id: ad->response_id);
1335 }
1336 else if (gtk_widget_get_ancestor (GTK_WIDGET (object), GTK_TYPE_HEADER_BAR) == priv->headerbar)
1337 {
1338 if (is_action)
1339 {
1340 g_object_ref (object);
1341 gtk_header_bar_remove (GTK_HEADER_BAR (priv->headerbar), GTK_WIDGET (object));
1342 add_to_header_bar (dialog, GTK_WIDGET (object), response_id: ad->response_id);
1343 g_object_unref (object);
1344 }
1345 }
1346
1347 if (item->is_default)
1348 {
1349 gtk_window_set_default_widget (GTK_WINDOW (dialog), GTK_WIDGET (object));
1350 update_suggested_action (dialog, GTK_WIDGET (object));
1351 }
1352 }
1353
1354 g_slist_free_full (list: data->items, free_func: free_action_widget_info);
1355 g_string_free (string: data->string, TRUE);
1356 g_slice_free (SubParserData, data);
1357}
1358
1359static void
1360gtk_dialog_buildable_add_child (GtkBuildable *buildable,
1361 GtkBuilder *builder,
1362 GObject *child,
1363 const char *type)
1364{
1365 GtkDialog *dialog = GTK_DIALOG (buildable);
1366 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1367
1368 if (type == NULL)
1369 parent_buildable_iface->add_child (buildable, builder, child, type);
1370 else if (g_str_equal (v1: type, v2: "titlebar"))
1371 {
1372 priv->headerbar = GTK_WIDGET (child);
1373 _gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
1374 gtk_window_set_titlebar (GTK_WINDOW (buildable), titlebar: priv->headerbar);
1375 }
1376 else if (g_str_equal (v1: type, v2: "action"))
1377 gtk_dialog_add_action_widget (GTK_DIALOG (buildable), GTK_WIDGET (child), response_id: GTK_RESPONSE_NONE);
1378 else
1379 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
1380}
1381
1382GtkWidget *
1383gtk_dialog_get_action_area (GtkDialog *dialog)
1384{
1385 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1386
1387 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1388
1389 return priv->action_area;
1390}
1391
1392/**
1393 * gtk_dialog_get_header_bar:
1394 * @dialog: a `GtkDialog`
1395 *
1396 * Returns the header bar of @dialog.
1397 *
1398 * Note that the headerbar is only used by the dialog if the
1399 * [property@Gtk.Dialog:use-header-bar] property is %TRUE.
1400 *
1401 * Returns: (type Gtk.HeaderBar) (transfer none): the header bar
1402 */
1403GtkWidget *
1404gtk_dialog_get_header_bar (GtkDialog *dialog)
1405{
1406 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1407
1408 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1409
1410 return priv->headerbar;
1411}
1412
1413/**
1414 * gtk_dialog_get_content_area:
1415 * @dialog: a `GtkDialog`
1416 *
1417 * Returns the content area of @dialog.
1418 *
1419 * Returns: (type Gtk.Box) (transfer none): the content area `GtkBox`.
1420 */
1421GtkWidget *
1422gtk_dialog_get_content_area (GtkDialog *dialog)
1423{
1424 GtkDialogPrivate *priv = gtk_dialog_get_instance_private (self: dialog);
1425
1426 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1427
1428 return priv->content_area;
1429}
1430

source code of gtk/gtk/gtkdialog.c