]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkdialog.c
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 "gtkbbox.h"
33 #include "gtklabel.h"
34 #include "gtkmarshalers.h"
35 #include "gtkbox.h"
36 #include "gtkboxprivate.h"
37 #include "gtkmain.h"
38 #include "gtkintl.h"
39 #include "gtkbindings.h"
40 #include "gtkprivate.h"
41 #include "gtkbuildable.h"
42 #include "gtksettings.h"
43
44 /**
45  * SECTION:gtkdialog
46  * @Short_description: Create popup windows
47  * @Title: GtkDialog
48  * @See_also: #GtkVBox, #GtkWindow, #GtkButton
49  *
50  * Dialog boxes are a convenient way to prompt the user for a small amount
51  * of input, e.g. to display a message, ask a question, or anything else
52  * that does not require extensive effort on the user's part.
53  *
54  * GTK+ treats a dialog as a window split vertically. The top section is a
55  * #GtkVBox, and is where widgets such as a #GtkLabel or a #GtkEntry should
56  * be packed. The bottom area is known as the
57  * <structfield>action_area</structfield>. This is generally used for
58  * packing buttons into the dialog which may perform functions such as
59  * cancel, ok, or apply.
60  *
61  * #GtkDialog boxes are created with a call to gtk_dialog_new() or
62  * gtk_dialog_new_with_buttons(). gtk_dialog_new_with_buttons() is
63  * recommended; it allows you to set the dialog title, some convenient flags,
64  * and add simple buttons.
65  *
66  * If 'dialog' is a newly created dialog, the two primary areas of the
67  * window can be accessed through gtk_dialog_get_content_area() and
68  * gtk_dialog_get_action_area(), as can be seen from the example below.
69  *
70  * A 'modal' dialog (that is, one which freezes the rest of the application
71  * from user input), can be created by calling gtk_window_set_modal() on the
72  * dialog. Use the GTK_WINDOW() macro to cast the widget returned from
73  * gtk_dialog_new() into a #GtkWindow. When using gtk_dialog_new_with_buttons()
74  * you can also pass the #GTK_DIALOG_MODAL flag to make a dialog modal.
75  *
76  * If you add buttons to #GtkDialog using gtk_dialog_new_with_buttons(),
77  * gtk_dialog_add_button(), gtk_dialog_add_buttons(), or
78  * gtk_dialog_add_action_widget(), clicking the button will emit a signal
79  * called #GtkDialog::response with a response ID that you specified. GTK+
80  * will never assign a meaning to positive response IDs; these are entirely
81  * user-defined. But for convenience, you can use the response IDs in the
82  * #GtkResponseType enumeration (these all have values less than zero). If
83  * a dialog receives a delete event, the #GtkDialog::response signal will
84  * be emitted with a response ID of #GTK_RESPONSE_DELETE_EVENT.
85  *
86  * If you want to block waiting for a dialog to return before returning
87  * control flow to your code, you can call gtk_dialog_run(). This function
88  * enters a recursive main loop and waits for the user to respond to the
89  * dialog, returning the response ID corresponding to the button the user
90  * clicked.
91  *
92  * For the simple dialog in the following example, in reality you'd probably
93  * use #GtkMessageDialog to save yourself some effort. But you'd need to
94  * create the dialog contents manually if you had more than a simple message
95  * in the dialog.
96  * <example>
97  * <title>Simple GtkDialog usage</title>
98  * <programlisting>
99  * /&ast; Function to open a dialog box displaying the message provided. &ast;/
100  * void
101  * quick_message (gchar *message)
102  * {
103  *    GtkWidget *dialog, *label, *content_area;
104  *
105  *    /&ast; Create the widgets &ast;/
106  *    dialog = gtk_dialog_new_with_buttons ("Message",
107  *                                          main_application_window,
108  *                                          GTK_DIALOG_DESTROY_WITH_PARENT,
109  *                                          GTK_STOCK_OK,
110  *                                          GTK_RESPONSE_NONE,
111  *                                          NULL);
112  *    content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
113  *    label = gtk_label_new (message);
114  *
115  *    /&ast; Ensure that the dialog box is destroyed when the user responds &ast;/
116  *    g_signal_connect_swapped (dialog,
117  *                              "response",
118  *                              G_CALLBACK (gtk_widget_destroy),
119  *                              dialog);
120  *
121  *    /&ast; Add the label, and show everything we've added to the dialog &ast;/
122  *
123  *    gtk_container_add (GTK_CONTAINER (content_area), label);
124  *    gtk_widget_show_all (dialog);
125  * }
126  * </programlisting>
127  * </example>
128  *
129  * <refsect2 id="GtkDialog-BUILDER-UI"><title>GtkDialog as GtkBuildable</title>
130  * <para>
131  * The GtkDialog implementation of the #GtkBuildable interface exposes the
132  * @vbox and @action_area as internal children with the names "vbox" and
133  * "action_area".
134  * </para>
135  * <para>
136  * GtkDialog supports a custom &lt;action-widgets&gt; element, which
137  * can contain multiple &lt;action-widget&gt; elements. The "response"
138  * attribute specifies a numeric response, and the content of the element
139  * is the id of widget (which should be a child of the dialogs @action_area).
140  * </para>
141  * <example>
142  * <title>A <structname>GtkDialog</structname> UI definition fragment.</title>
143  * <programlisting><![CDATA[
144  * <object class="GtkDialog" id="dialog1">
145  *   <child internal-child="vbox">"
146  *     <object class="GtkVBox" id="vbox">
147  *       <child internal-child="action_area">
148  *         <object class="GtkHButtonBox" id="button_box">
149  *           <child>
150  *             <object class="GtkButton" id="button_cancel"/>
151  *           </child>
152  *           <child>
153  *             <object class="GtkButton" id="button_ok"/>
154  *           </child>
155  *         </object>
156  *       </child>
157  *     </object>
158  *   </child>
159  *   <action-widgets>
160  *     <action-widget response="3">button_ok</action-widget>
161  *     <action-widget response="-5">button_cancel</action-widget>
162  *   </action-widgets>
163  * </object>
164  * ]]></programlisting>
165  * </example>
166  * </refsect2>
167  */
168
169 struct _GtkDialogPrivate
170 {
171   GtkWidget *vbox;
172   GtkWidget *action_area;
173 };
174
175 typedef struct _ResponseData ResponseData;
176
177 struct _ResponseData
178 {
179   gint response_id;
180 };
181
182 static void      gtk_dialog_add_buttons_valist   (GtkDialog    *dialog,
183                                                   const gchar  *first_button_text,
184                                                   va_list       args);
185
186 static gboolean  gtk_dialog_delete_event_handler (GtkWidget    *widget,
187                                                   GdkEventAny  *event,
188                                                   gpointer      user_data);
189 static void      gtk_dialog_style_updated        (GtkWidget    *widget);
190 static void      gtk_dialog_map                  (GtkWidget    *widget);
191
192 static void      gtk_dialog_close                (GtkDialog    *dialog);
193
194 static ResponseData * get_response_data          (GtkWidget    *widget,
195                                                   gboolean      create);
196
197 static void      gtk_dialog_buildable_interface_init     (GtkBuildableIface *iface);
198 static GObject * gtk_dialog_buildable_get_internal_child (GtkBuildable  *buildable,
199                                                           GtkBuilder    *builder,
200                                                           const gchar   *childname);
201 static gboolean  gtk_dialog_buildable_custom_tag_start   (GtkBuildable  *buildable,
202                                                           GtkBuilder    *builder,
203                                                           GObject       *child,
204                                                           const gchar   *tagname,
205                                                           GMarkupParser *parser,
206                                                           gpointer      *data);
207 static void      gtk_dialog_buildable_custom_finished    (GtkBuildable  *buildable,
208                                                           GtkBuilder    *builder,
209                                                           GObject       *child,
210                                                           const gchar   *tagname,
211                                                           gpointer       user_data);
212
213
214 enum {
215   PROP_0,
216   PROP_HAS_SEPARATOR
217 };
218
219 enum {
220   RESPONSE,
221   CLOSE,
222   LAST_SIGNAL
223 };
224
225 static guint dialog_signals[LAST_SIGNAL];
226
227 G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW,
228                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
229                                                 gtk_dialog_buildable_interface_init))
230
231 static void
232 gtk_dialog_class_init (GtkDialogClass *class)
233 {
234   GObjectClass *gobject_class;
235   GtkWidgetClass *widget_class;
236   GtkBindingSet *binding_set;
237
238   gobject_class = G_OBJECT_CLASS (class);
239   widget_class = GTK_WIDGET_CLASS (class);
240
241   widget_class->map = gtk_dialog_map;
242   widget_class->style_updated = gtk_dialog_style_updated;
243
244   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_DIALOG);
245
246   class->close = gtk_dialog_close;
247
248   g_type_class_add_private (gobject_class, sizeof (GtkDialogPrivate));
249
250   /**
251    * GtkDialog::response:
252    * @dialog: the object on which the signal is emitted
253    * @response_id: the response ID
254    *
255    * Emitted when an action widget is clicked, the dialog receives a
256    * delete event, or the application programmer calls gtk_dialog_response().
257    * On a delete event, the response ID is #GTK_RESPONSE_DELETE_EVENT.
258    * Otherwise, it depends on which action widget was clicked.
259    */
260   dialog_signals[RESPONSE] =
261     g_signal_new (I_("response"),
262                   G_OBJECT_CLASS_TYPE (class),
263                   G_SIGNAL_RUN_LAST,
264                   G_STRUCT_OFFSET (GtkDialogClass, response),
265                   NULL, NULL,
266                   _gtk_marshal_VOID__INT,
267                   G_TYPE_NONE, 1,
268                   G_TYPE_INT);
269
270   /**
271    * GtkDialog::close:
272    *
273    * The ::close signal is a
274    * <link linkend="keybinding-signals">keybinding signal</link>
275    * which gets emitted when the user uses a keybinding to close
276    * the dialog.
277    *
278    * The default binding for this signal is the Escape key.
279    */
280   dialog_signals[CLOSE] =
281     g_signal_new (I_("close"),
282                   G_OBJECT_CLASS_TYPE (class),
283                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
284                   G_STRUCT_OFFSET (GtkDialogClass, close),
285                   NULL, NULL,
286                   _gtk_marshal_VOID__VOID,
287                   G_TYPE_NONE, 0);
288
289   gtk_widget_class_install_style_property (widget_class,
290                                            g_param_spec_int ("content-area-border",
291                                                              P_("Content area border"),
292                                                              P_("Width of border around the main dialog area"),
293                                                              0,
294                                                              G_MAXINT,
295                                                              2,
296                                                              GTK_PARAM_READABLE));
297   /**
298    * GtkDialog:content-area-spacing:
299    *
300    * The default spacing used between elements of the
301    * content area of the dialog, as returned by
302    * gtk_dialog_get_content_area(), unless gtk_box_set_spacing()
303    * was called on that widget directly.
304    *
305    * Since: 2.16
306    */
307   gtk_widget_class_install_style_property (widget_class,
308                                            g_param_spec_int ("content-area-spacing",
309                                                              P_("Content area spacing"),
310                                                              P_("Spacing between elements of the main dialog area"),
311                                                              0,
312                                                              G_MAXINT,
313                                                              0,
314                                                              GTK_PARAM_READABLE));
315   gtk_widget_class_install_style_property (widget_class,
316                                            g_param_spec_int ("button-spacing",
317                                                              P_("Button spacing"),
318                                                              P_("Spacing between buttons"),
319                                                              0,
320                                                              G_MAXINT,
321                                                              6,
322                                                              GTK_PARAM_READABLE));
323
324   gtk_widget_class_install_style_property (widget_class,
325                                            g_param_spec_int ("action-area-border",
326                                                              P_("Action area border"),
327                                                              P_("Width of border around the button area at the bottom of the dialog"),
328                                                              0,
329                                                              G_MAXINT,
330                                                              5,
331                                                              GTK_PARAM_READABLE));
332
333   binding_set = gtk_binding_set_by_class (class);
334
335   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
336 }
337
338 static void
339 update_spacings (GtkDialog *dialog)
340 {
341   GtkDialogPrivate *priv = dialog->priv;
342   gint content_area_border;
343   gint content_area_spacing;
344   gint button_spacing;
345   gint action_area_border;
346
347   gtk_widget_style_get (GTK_WIDGET (dialog),
348                         "content-area-border", &content_area_border,
349                         "content-area-spacing", &content_area_spacing,
350                         "button-spacing", &button_spacing,
351                         "action-area-border", &action_area_border,
352                         NULL);
353
354   
355   gtk_container_set_border_width (GTK_CONTAINER (priv->vbox),
356                                   content_area_border);
357   if (!_gtk_box_get_spacing_set (GTK_BOX (priv->vbox)))
358     {
359       gtk_box_set_spacing (GTK_BOX (priv->vbox), content_area_spacing);
360       _gtk_box_set_spacing_set (GTK_BOX (priv->vbox), FALSE);
361     }
362   gtk_box_set_spacing (GTK_BOX (priv->action_area),
363                        button_spacing);
364   gtk_container_set_border_width (GTK_CONTAINER (priv->action_area),
365                                   action_area_border);
366 }
367
368 static void
369 gtk_dialog_init (GtkDialog *dialog)
370 {
371   GtkDialogPrivate *priv;
372
373   dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
374                                               GTK_TYPE_DIALOG,
375                                               GtkDialogPrivate);
376   priv = dialog->priv;
377
378   /* To avoid breaking old code that prevents destroy on delete event
379    * by connecting a handler, we have to have the FIRST signal
380    * connection on the dialog.
381    */
382   g_signal_connect (dialog,
383                     "delete-event",
384                     G_CALLBACK (gtk_dialog_delete_event_handler),
385                     NULL);
386
387   priv->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
388   gtk_container_add (GTK_CONTAINER (dialog), priv->vbox);
389   gtk_widget_show (priv->vbox);
390
391   priv->action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
392
393   gtk_button_box_set_layout (GTK_BUTTON_BOX (priv->action_area),
394                              GTK_BUTTONBOX_END);
395
396   gtk_box_pack_end (GTK_BOX (priv->vbox), priv->action_area,
397                     FALSE, TRUE, 0);
398   gtk_widget_show (priv->action_area);
399
400   gtk_window_set_type_hint (GTK_WINDOW (dialog),
401                             GDK_WINDOW_TYPE_HINT_DIALOG);
402   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
403
404   update_spacings (dialog);
405 }
406
407 static GtkBuildableIface *parent_buildable_iface;
408
409 static void
410 gtk_dialog_buildable_interface_init (GtkBuildableIface *iface)
411 {
412   parent_buildable_iface = g_type_interface_peek_parent (iface);
413   iface->get_internal_child = gtk_dialog_buildable_get_internal_child;
414   iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start;
415   iface->custom_finished = gtk_dialog_buildable_custom_finished;
416 }
417
418 static GObject *
419 gtk_dialog_buildable_get_internal_child (GtkBuildable *buildable,
420                                          GtkBuilder   *builder,
421                                          const gchar  *childname)
422 {
423   GtkDialogPrivate *priv = GTK_DIALOG (buildable)->priv;
424
425   if (strcmp (childname, "vbox") == 0)
426     return G_OBJECT (priv->vbox);
427   else if (strcmp (childname, "action_area") == 0)
428     return G_OBJECT (priv->action_area);
429
430   return parent_buildable_iface->get_internal_child (buildable,
431                                                      builder,
432                                                      childname);
433 }
434
435 static gboolean
436 gtk_dialog_delete_event_handler (GtkWidget   *widget,
437                                  GdkEventAny *event,
438                                  gpointer     user_data)
439 {
440   /* emit response signal */
441   gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
442
443   /* Do the destroy by default */
444   return FALSE;
445 }
446
447 /* A far too tricky heuristic for getting the right initial
448  * focus widget if none was set. What we do is we focus the first
449  * widget in the tab chain, but if this results in the focus
450  * ending up on one of the response widgets _other_ than the
451  * default response, we focus the default response instead.
452  *
453  * Additionally, skip selectable labels when looking for the
454  * right initial focus widget.
455  */
456 static void
457 gtk_dialog_map (GtkWidget *widget)
458 {
459   GtkWidget *default_widget, *focus;
460   GtkWindow *window = GTK_WINDOW (widget);
461   GtkDialog *dialog = GTK_DIALOG (widget);
462   GtkDialogPrivate *priv = dialog->priv;
463
464   GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
465
466   focus = gtk_window_get_focus (window);
467   if (!focus)
468     {
469       GList *children, *tmp_list;
470       GtkWidget *first_focus = NULL;
471
472       do
473         {
474           g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
475
476           focus = gtk_window_get_focus (window);
477           if (GTK_IS_LABEL (focus) &&
478               !gtk_label_get_current_uri (GTK_LABEL (focus)))
479             gtk_label_select_region (GTK_LABEL (focus), 0, 0);
480
481           if (first_focus == NULL)
482             first_focus = focus;
483           else if (first_focus == focus)
484             break;
485
486           if (!GTK_IS_LABEL (focus))
487             break;
488         }
489       while (TRUE);
490
491       tmp_list = children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
492
493       while (tmp_list)
494         {
495           GtkWidget *child = tmp_list->data;
496
497           default_widget = gtk_window_get_default_widget (window);
498           if ((focus == NULL || child == focus) &&
499               child != default_widget &&
500               default_widget)
501             {
502               gtk_widget_grab_focus (default_widget);
503               break;
504             }
505
506           tmp_list = tmp_list->next;
507         }
508
509       g_list_free (children);
510     }
511 }
512
513 static void
514 gtk_dialog_style_updated (GtkWidget *widget)
515 {
516   GTK_WIDGET_CLASS (gtk_dialog_parent_class)->style_updated (widget);
517
518   update_spacings (GTK_DIALOG (widget));
519 }
520
521 static GtkWidget *
522 dialog_find_button (GtkDialog *dialog,
523                     gint       response_id)
524 {
525   GtkDialogPrivate *priv = dialog->priv;
526   GtkWidget *child = NULL;
527   GList *children, *tmp_list;
528
529   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
530
531   for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
532     {
533       ResponseData *rd = get_response_data (tmp_list->data, FALSE);
534
535       if (rd && rd->response_id == response_id)
536         {
537           child = tmp_list->data;
538           break;
539         }
540     }
541
542   g_list_free (children);
543
544   return child;
545 }
546
547 static void
548 gtk_dialog_close (GtkDialog *dialog)
549 {
550   /* Synthesize delete_event to close dialog. */
551
552   GtkWidget *widget = GTK_WIDGET (dialog);
553   GdkEvent *event;
554
555   event = gdk_event_new (GDK_DELETE);
556
557   event->any.window = g_object_ref (gtk_widget_get_window (widget));
558   event->any.send_event = TRUE;
559
560   gtk_main_do_event (event);
561   gdk_event_free (event);
562 }
563
564 /**
565  * gtk_dialog_new:
566  *
567  * Creates a new dialog box.
568  *
569  * Widgets should not be packed into this #GtkWindow
570  * directly, but into the @vbox and @action_area, as described above.
571  *
572  * Returns: the new dialog as a #GtkWidget
573  */
574 GtkWidget*
575 gtk_dialog_new (void)
576 {
577   return g_object_new (GTK_TYPE_DIALOG, NULL);
578 }
579
580 static GtkWidget*
581 gtk_dialog_new_empty (const gchar     *title,
582                       GtkWindow       *parent,
583                       GtkDialogFlags   flags)
584 {
585   GtkDialog *dialog;
586
587   dialog = g_object_new (GTK_TYPE_DIALOG, NULL);
588
589   if (title)
590     gtk_window_set_title (GTK_WINDOW (dialog), title);
591
592   if (parent)
593     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
594
595   if (flags & GTK_DIALOG_MODAL)
596     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
597
598   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
599     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
600
601   return GTK_WIDGET (dialog);
602 }
603
604 /**
605  * gtk_dialog_new_with_buttons:
606  * @title: (allow-none): Title of the dialog, or %NULL
607  * @parent: (allow-none): Transient parent of the dialog, or %NULL
608  * @flags: from #GtkDialogFlags
609  * @first_button_text: (allow-none): stock ID or text to go in first button, or %NULL
610  * @...: response ID for first button, then additional buttons, ending with %NULL
611  *
612  * Creates a new #GtkDialog with title @title (or %NULL for the default
613  * title; see gtk_window_set_title()) and transient parent @parent (or
614  * %NULL for none; see gtk_window_set_transient_for()). The @flags
615  * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
616  * and/or to have it destroyed along with its transient parent
617  * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
618  * text/response ID pairs should be listed, with a %NULL pointer ending
619  * the list. Button text can be either a stock ID such as
620  * #GTK_STOCK_OK, or some arbitrary text. A response ID can be
621  * any positive number, or one of the values in the #GtkResponseType
622  * enumeration. If the user clicks one of these dialog buttons,
623  * #GtkDialog will emit the #GtkDialog::response signal with the corresponding
624  * response ID. If a #GtkDialog receives the #GtkWidget::delete-event signal,
625  * it will emit ::response with a response ID of #GTK_RESPONSE_DELETE_EVENT.
626  * However, destroying a dialog does not emit the ::response signal;
627  * so be careful relying on ::response when using the
628  * #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
629  * so the first button in the list will be the leftmost button in the dialog.
630  *
631  * Here's a simple example:
632  * |[
633  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
634  *                                                   main_app_window,
635  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
636  *                                                   GTK_STOCK_OK,
637  *                                                   GTK_RESPONSE_ACCEPT,
638  *                                                   GTK_STOCK_CANCEL,
639  *                                                   GTK_RESPONSE_REJECT,
640  *                                                   NULL);
641  * ]|
642  *
643  * Return value: a new #GtkDialog
644  */
645 GtkWidget*
646 gtk_dialog_new_with_buttons (const gchar    *title,
647                              GtkWindow      *parent,
648                              GtkDialogFlags  flags,
649                              const gchar    *first_button_text,
650                              ...)
651 {
652   GtkDialog *dialog;
653   va_list args;
654
655   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
656
657   va_start (args, first_button_text);
658
659   gtk_dialog_add_buttons_valist (dialog,
660                                  first_button_text,
661                                  args);
662
663   va_end (args);
664
665   return GTK_WIDGET (dialog);
666 }
667
668 static void
669 response_data_free (gpointer data)
670 {
671   g_slice_free (ResponseData, data);
672 }
673
674 static ResponseData*
675 get_response_data (GtkWidget *widget,
676                    gboolean   create)
677 {
678   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
679                                         "gtk-dialog-response-data");
680
681   if (ad == NULL && create)
682     {
683       ad = g_slice_new (ResponseData);
684
685       g_object_set_data_full (G_OBJECT (widget),
686                               I_("gtk-dialog-response-data"),
687                               ad,
688                               response_data_free);
689     }
690
691   return ad;
692 }
693
694 static void
695 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
696 {
697   gint response_id;
698
699   response_id = gtk_dialog_get_response_for_widget (dialog, widget);
700
701   gtk_dialog_response (dialog, response_id);
702 }
703
704 /**
705  * gtk_dialog_add_action_widget:
706  * @dialog: a #GtkDialog
707  * @child: an activatable widget
708  * @response_id: response ID for @child
709  *
710  * Adds an activatable widget to the action area of a #GtkDialog,
711  * connecting a signal handler that will emit the #GtkDialog::response
712  * signal on the dialog when the widget is activated. The widget is
713  * appended to the end of the dialog's action area. If you want to add a
714  * non-activatable widget, simply pack it into the @action_area field
715  * of the #GtkDialog struct.
716  **/
717 void
718 gtk_dialog_add_action_widget (GtkDialog *dialog,
719                               GtkWidget *child,
720                               gint       response_id)
721 {
722   GtkDialogPrivate *priv;
723   ResponseData *ad;
724   guint signal_id;
725
726   g_return_if_fail (GTK_IS_DIALOG (dialog));
727   g_return_if_fail (GTK_IS_WIDGET (child));
728
729   priv = dialog->priv;
730
731   ad = get_response_data (child, TRUE);
732
733   ad->response_id = response_id;
734
735   if (GTK_IS_BUTTON (child))
736     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
737   else
738     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
739
740   if (signal_id)
741     {
742       GClosure *closure;
743
744       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
745                                        G_OBJECT (dialog));
746       g_signal_connect_closure_by_id (child,
747                                       signal_id,
748                                       0,
749                                       closure,
750                                       FALSE);
751     }
752   else
753     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
754
755   gtk_box_pack_end (GTK_BOX (priv->action_area),
756                     child,
757                     FALSE, TRUE, 0);
758
759   if (response_id == GTK_RESPONSE_HELP)
760     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area), child, TRUE);
761 }
762
763 /**
764  * gtk_dialog_add_button:
765  * @dialog: a #GtkDialog
766  * @button_text: text of button, or stock ID
767  * @response_id: response ID for the button
768  *
769  * Adds a button with the given text (or a stock button, if @button_text is a
770  * stock ID) and sets things up so that clicking the button will emit the
771  * #GtkDialog::response signal with the given @response_id. The button is
772  * appended to the end of the dialog's action area. The button widget is
773  * returned, but usually you don't need it.
774  *
775  * Return value: (transfer none): the #GtkButton widget that was added
776  **/
777 GtkWidget*
778 gtk_dialog_add_button (GtkDialog   *dialog,
779                        const gchar *button_text,
780                        gint         response_id)
781 {
782   GtkWidget *button;
783
784   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
785   g_return_val_if_fail (button_text != NULL, NULL);
786
787   button = gtk_button_new_from_stock (button_text);
788
789   gtk_widget_set_can_default (button, TRUE);
790
791   gtk_widget_show (button);
792
793   gtk_dialog_add_action_widget (dialog,
794                                 button,
795                                 response_id);
796
797   return button;
798 }
799
800 static void
801 gtk_dialog_add_buttons_valist (GtkDialog      *dialog,
802                                const gchar    *first_button_text,
803                                va_list         args)
804 {
805   const gchar* text;
806   gint response_id;
807
808   g_return_if_fail (GTK_IS_DIALOG (dialog));
809
810   if (first_button_text == NULL)
811     return;
812
813   text = first_button_text;
814   response_id = va_arg (args, gint);
815
816   while (text != NULL)
817     {
818       gtk_dialog_add_button (dialog, text, response_id);
819
820       text = va_arg (args, gchar*);
821       if (text == NULL)
822         break;
823       response_id = va_arg (args, int);
824     }
825 }
826
827 /**
828  * gtk_dialog_add_buttons:
829  * @dialog: a #GtkDialog
830  * @first_button_text: button text or stock ID
831  * @...: response ID for first button, then more text-response_id pairs
832  *
833  * Adds more buttons, same as calling gtk_dialog_add_button()
834  * repeatedly.  The variable argument list should be %NULL-terminated
835  * as with gtk_dialog_new_with_buttons(). Each button must have both
836  * text and response ID.
837  */
838 void
839 gtk_dialog_add_buttons (GtkDialog   *dialog,
840                         const gchar *first_button_text,
841                         ...)
842 {
843   va_list args;
844
845   va_start (args, first_button_text);
846
847   gtk_dialog_add_buttons_valist (dialog,
848                                  first_button_text,
849                                  args);
850
851   va_end (args);
852 }
853
854 /**
855  * gtk_dialog_set_response_sensitive:
856  * @dialog: a #GtkDialog
857  * @response_id: a response ID
858  * @setting: %TRUE for sensitive
859  *
860  * Calls <literal>gtk_widget_set_sensitive (widget, @setting)</literal>
861  * for each widget in the dialog's action area with the given @response_id.
862  * A convenient way to sensitize/desensitize dialog buttons.
863  **/
864 void
865 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
866                                    gint       response_id,
867                                    gboolean   setting)
868 {
869   GtkDialogPrivate *priv;
870   GList *children;
871   GList *tmp_list;
872
873   g_return_if_fail (GTK_IS_DIALOG (dialog));
874
875   priv = dialog->priv;
876
877   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
878
879   tmp_list = children;
880   while (tmp_list != NULL)
881     {
882       GtkWidget *widget = tmp_list->data;
883       ResponseData *rd = get_response_data (widget, FALSE);
884
885       if (rd && rd->response_id == response_id)
886         gtk_widget_set_sensitive (widget, setting);
887
888       tmp_list = g_list_next (tmp_list);
889     }
890
891   g_list_free (children);
892 }
893
894 /**
895  * gtk_dialog_set_default_response:
896  * @dialog: a #GtkDialog
897  * @response_id: a response ID
898  *
899  * Sets the last widget in the dialog's action area with the given @response_id
900  * as the default widget for the dialog. Pressing "Enter" normally activates
901  * the default widget.
902  **/
903 void
904 gtk_dialog_set_default_response (GtkDialog *dialog,
905                                  gint       response_id)
906 {
907   GtkDialogPrivate *priv;
908   GList *children;
909   GList *tmp_list;
910
911   g_return_if_fail (GTK_IS_DIALOG (dialog));
912
913   priv = dialog->priv;
914
915   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
916
917   tmp_list = children;
918   while (tmp_list != NULL)
919     {
920       GtkWidget *widget = tmp_list->data;
921       ResponseData *rd = get_response_data (widget, FALSE);
922
923       if (rd && rd->response_id == response_id)
924         gtk_widget_grab_default (widget);
925
926       tmp_list = g_list_next (tmp_list);
927     }
928
929   g_list_free (children);
930 }
931
932 /**
933  * gtk_dialog_response:
934  * @dialog: a #GtkDialog
935  * @response_id: response ID
936  *
937  * Emits the #GtkDialog::response signal with the given response ID.
938  * Used to indicate that the user has responded to the dialog in some way;
939  * typically either you or gtk_dialog_run() will be monitoring the
940  * ::response signal and take appropriate action.
941  **/
942 void
943 gtk_dialog_response (GtkDialog *dialog,
944                      gint       response_id)
945 {
946   g_return_if_fail (GTK_IS_DIALOG (dialog));
947
948   g_signal_emit (dialog,
949                  dialog_signals[RESPONSE],
950                  0,
951                  response_id);
952 }
953
954 typedef struct
955 {
956   GtkDialog *dialog;
957   gint response_id;
958   GMainLoop *loop;
959   gboolean destroyed;
960 } RunInfo;
961
962 static void
963 shutdown_loop (RunInfo *ri)
964 {
965   if (g_main_loop_is_running (ri->loop))
966     g_main_loop_quit (ri->loop);
967 }
968
969 static void
970 run_unmap_handler (GtkDialog *dialog, gpointer data)
971 {
972   RunInfo *ri = data;
973
974   shutdown_loop (ri);
975 }
976
977 static void
978 run_response_handler (GtkDialog *dialog,
979                       gint response_id,
980                       gpointer data)
981 {
982   RunInfo *ri;
983
984   ri = data;
985
986   ri->response_id = response_id;
987
988   shutdown_loop (ri);
989 }
990
991 static gint
992 run_delete_handler (GtkDialog *dialog,
993                     GdkEventAny *event,
994                     gpointer data)
995 {
996   RunInfo *ri = data;
997
998   shutdown_loop (ri);
999
1000   return TRUE; /* Do not destroy */
1001 }
1002
1003 static void
1004 run_destroy_handler (GtkDialog *dialog, gpointer data)
1005 {
1006   RunInfo *ri = data;
1007
1008   /* shutdown_loop will be called by run_unmap_handler */
1009
1010   ri->destroyed = TRUE;
1011 }
1012
1013 /**
1014  * gtk_dialog_run:
1015  * @dialog: a #GtkDialog
1016  *
1017  * Blocks in a recursive main loop until the @dialog either emits the
1018  * #GtkDialog::response signal, or is destroyed. If the dialog is
1019  * destroyed during the call to gtk_dialog_run(), gtk_dialog_run() returns
1020  * #GTK_RESPONSE_NONE. Otherwise, it returns the response ID from the
1021  * ::response signal emission.
1022  *
1023  * Before entering the recursive main loop, gtk_dialog_run() calls
1024  * gtk_widget_show() on the dialog for you. Note that you still
1025  * need to show any children of the dialog yourself.
1026  *
1027  * During gtk_dialog_run(), the default behavior of #GtkWidget::delete-event
1028  * is disabled; if the dialog receives ::delete_event, it will not be
1029  * destroyed as windows usually are, and gtk_dialog_run() will return
1030  * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog
1031  * will be modal. You can force gtk_dialog_run() to return at any time by
1032  * calling gtk_dialog_response() to emit the ::response signal. Destroying
1033  * the dialog during gtk_dialog_run() is a very bad idea, because your
1034  * post-run code won't know whether the dialog was destroyed or not.
1035  *
1036  * After gtk_dialog_run() returns, you are responsible for hiding or
1037  * destroying the dialog if you wish to do so.
1038  *
1039  * Typical usage of this function might be:
1040  * |[
1041  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
1042  *   switch (result)
1043  *     {
1044  *       case GTK_RESPONSE_ACCEPT:
1045  *          do_application_specific_something ();
1046  *          break;
1047  *       default:
1048  *          do_nothing_since_dialog_was_cancelled ();
1049  *          break;
1050  *     }
1051  *   gtk_widget_destroy (dialog);
1052  * ]|
1053  *
1054  * Note that even though the recursive main loop gives the effect of a
1055  * modal dialog (it prevents the user from interacting with other
1056  * windows in the same window group while the dialog is run), callbacks
1057  * such as timeouts, IO channel watches, DND drops, etc, <emphasis>will</emphasis>
1058  * be triggered during a gtk_dialog_run() call.
1059  *
1060  * Return value: response ID
1061  **/
1062 gint
1063 gtk_dialog_run (GtkDialog *dialog)
1064 {
1065   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
1066   gboolean was_modal;
1067   gulong response_handler;
1068   gulong unmap_handler;
1069   gulong destroy_handler;
1070   gulong delete_handler;
1071
1072   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
1073
1074   g_object_ref (dialog);
1075
1076   was_modal = gtk_window_get_modal (GTK_WINDOW (dialog));
1077   if (!was_modal)
1078     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1079
1080   if (!gtk_widget_get_visible (GTK_WIDGET (dialog)))
1081     gtk_widget_show (GTK_WIDGET (dialog));
1082
1083   response_handler =
1084     g_signal_connect (dialog,
1085                       "response",
1086                       G_CALLBACK (run_response_handler),
1087                       &ri);
1088
1089   unmap_handler =
1090     g_signal_connect (dialog,
1091                       "unmap",
1092                       G_CALLBACK (run_unmap_handler),
1093                       &ri);
1094
1095   delete_handler =
1096     g_signal_connect (dialog,
1097                       "delete-event",
1098                       G_CALLBACK (run_delete_handler),
1099                       &ri);
1100
1101   destroy_handler =
1102     g_signal_connect (dialog,
1103                       "destroy",
1104                       G_CALLBACK (run_destroy_handler),
1105                       &ri);
1106
1107   ri.loop = g_main_loop_new (NULL, FALSE);
1108
1109   gdk_threads_leave ();
1110   g_main_loop_run (ri.loop);
1111   gdk_threads_enter ();
1112
1113   g_main_loop_unref (ri.loop);
1114
1115   ri.loop = NULL;
1116
1117   if (!ri.destroyed)
1118     {
1119       if (!was_modal)
1120         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1121
1122       g_signal_handler_disconnect (dialog, response_handler);
1123       g_signal_handler_disconnect (dialog, unmap_handler);
1124       g_signal_handler_disconnect (dialog, delete_handler);
1125       g_signal_handler_disconnect (dialog, destroy_handler);
1126     }
1127
1128   g_object_unref (dialog);
1129
1130   return ri.response_id;
1131 }
1132
1133 /**
1134  * gtk_dialog_get_widget_for_response:
1135  * @dialog: a #GtkDialog
1136  * @response_id: the response ID used by the @dialog widget
1137  *
1138  * Gets the widget button that uses the given response ID in the action area
1139  * of a dialog.
1140  *
1141  * Returns: (transfer none): the @widget button that uses the given
1142  *     @response_id, or %NULL.
1143  *
1144  * Since: 2.20
1145  */
1146 GtkWidget*
1147 gtk_dialog_get_widget_for_response (GtkDialog *dialog,
1148                                     gint       response_id)
1149 {
1150   GtkDialogPrivate *priv;
1151   GList *children;
1152   GList *tmp_list;
1153
1154   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1155
1156   priv = dialog->priv;
1157
1158   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
1159
1160   tmp_list = children;
1161   while (tmp_list != NULL)
1162     {
1163       GtkWidget *widget = tmp_list->data;
1164       ResponseData *rd = get_response_data (widget, FALSE);
1165
1166       if (rd && rd->response_id == response_id)
1167         {
1168           g_list_free (children);
1169           return widget;
1170         }
1171
1172       tmp_list = g_list_next (tmp_list);
1173     }
1174
1175   g_list_free (children);
1176
1177   return NULL;
1178 }
1179
1180 /**
1181  * gtk_dialog_get_response_for_widget:
1182  * @dialog: a #GtkDialog
1183  * @widget: a widget in the action area of @dialog
1184  *
1185  * Gets the response id of a widget in the action area
1186  * of a dialog.
1187  *
1188  * Returns: the response id of @widget, or %GTK_RESPONSE_NONE
1189  *  if @widget doesn't have a response id set.
1190  *
1191  * Since: 2.8
1192  */
1193 gint
1194 gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1195                                     GtkWidget *widget)
1196 {
1197   ResponseData *rd;
1198
1199   rd = get_response_data (widget, FALSE);
1200   if (!rd)
1201     return GTK_RESPONSE_NONE;
1202   else
1203     return rd->response_id;
1204 }
1205
1206 /**
1207  * gtk_alternative_dialog_button_order:
1208  * @screen: (allow-none): a #GdkScreen, or %NULL to use the default screen
1209  *
1210  * Returns %TRUE if dialogs are expected to use an alternative
1211  * button order on the screen @screen. See
1212  * gtk_dialog_set_alternative_button_order() for more details
1213  * about alternative button order.
1214  *
1215  * If you need to use this function, you should probably connect
1216  * to the ::notify:gtk-alternative-button-order signal on the
1217  * #GtkSettings object associated to @screen, in order to be
1218  * notified if the button order setting changes.
1219  *
1220  * Returns: Whether the alternative button order should be used
1221  *
1222  * Since: 2.6
1223  */
1224 gboolean
1225 gtk_alternative_dialog_button_order (GdkScreen *screen)
1226 {
1227   GtkSettings *settings;
1228   gboolean result;
1229
1230   if (screen)
1231     settings = gtk_settings_get_for_screen (screen);
1232   else
1233     settings = gtk_settings_get_default ();
1234
1235   g_object_get (settings,
1236                 "gtk-alternative-button-order", &result, NULL);
1237
1238   return result;
1239 }
1240
1241 static void
1242 gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
1243                                                 gint       first_response_id,
1244                                                 va_list    args)
1245 {
1246   GtkDialogPrivate *priv = dialog->priv;
1247   GtkWidget *child;
1248   gint response_id;
1249   gint position;
1250
1251   response_id = first_response_id;
1252   position = 0;
1253   while (response_id != -1)
1254     {
1255       /* reorder child with response_id to position */
1256       child = dialog_find_button (dialog, response_id);
1257       gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1258
1259       response_id = va_arg (args, gint);
1260       position++;
1261     }
1262 }
1263
1264 /**
1265  * gtk_dialog_set_alternative_button_order:
1266  * @dialog: a #GtkDialog
1267  * @first_response_id: a response id used by one @dialog's buttons
1268  * @...: a list of more response ids of @dialog's buttons, terminated by -1
1269  *
1270  * Sets an alternative button order. If the
1271  * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE,
1272  * the dialog buttons are reordered according to the order of the
1273  * response ids passed to this function.
1274  *
1275  * By default, GTK+ dialogs use the button order advocated by the Gnome
1276  * <ulink url="http://library.gnome.org/devel/hig-book/stable/">Human
1277  * Interface Guidelines</ulink> with the affirmative button at the far
1278  * right, and the cancel button left of it. But the builtin GTK+ dialogs
1279  * and #GtkMessageDialog<!-- -->s do provide an alternative button order,
1280  * which is more suitable on some platforms, e.g. Windows.
1281  *
1282  * Use this function after adding all the buttons to your dialog, as the
1283  * following example shows:
1284  * |[
1285  * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1286  *                                        GTK_STOCK_CANCEL,
1287  *                                        GTK_RESPONSE_CANCEL);
1288  *
1289  * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1290  *                                    GTK_STOCK_OK,
1291  *                                    GTK_RESPONSE_OK);
1292  *
1293  * gtk_widget_grab_default (ok_button);
1294  *
1295  * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1296  *                                      GTK_STOCK_HELP,
1297  *                                      GTK_RESPONSE_HELP);
1298  *
1299  * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1300  *                                          GTK_RESPONSE_OK,
1301  *                                          GTK_RESPONSE_CANCEL,
1302  *                                          GTK_RESPONSE_HELP,
1303  *                                          -1);
1304  * ]|
1305  *
1306  * Since: 2.6
1307  */
1308 void
1309 gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
1310                                          gint       first_response_id,
1311                                          ...)
1312 {
1313   GdkScreen *screen;
1314   va_list args;
1315
1316   g_return_if_fail (GTK_IS_DIALOG (dialog));
1317
1318   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1319   if (!gtk_alternative_dialog_button_order (screen))
1320       return;
1321
1322   va_start (args, first_response_id);
1323
1324   gtk_dialog_set_alternative_button_order_valist (dialog,
1325                                                   first_response_id,
1326                                                   args);
1327   va_end (args);
1328 }
1329 /**
1330  * gtk_dialog_set_alternative_button_order_from_array:
1331  * @dialog: a #GtkDialog
1332  * @n_params: the number of response ids in @new_order
1333  * @new_order: (array length=n_params): an array of response ids of
1334  *     @dialog's buttons
1335  *
1336  * Sets an alternative button order. If the
1337  * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE,
1338  * the dialog buttons are reordered according to the order of the
1339  * response ids in @new_order.
1340  *
1341  * See gtk_dialog_set_alternative_button_order() for more information.
1342  *
1343  * This function is for use by language bindings.
1344  *
1345  * Since: 2.6
1346  */
1347 void
1348 gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
1349                                                     gint       n_params,
1350                                                     gint      *new_order)
1351 {
1352   GtkDialogPrivate *priv = dialog->priv;
1353   GdkScreen *screen;
1354   GtkWidget *child;
1355   gint position;
1356
1357   g_return_if_fail (GTK_IS_DIALOG (dialog));
1358   g_return_if_fail (new_order != NULL);
1359
1360   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1361   if (!gtk_alternative_dialog_button_order (screen))
1362       return;
1363
1364   for (position = 0; position < n_params; position++)
1365   {
1366       /* reorder child with response_id to position */
1367       child = dialog_find_button (dialog, new_order[position]);
1368       gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1369     }
1370 }
1371
1372 typedef struct {
1373   gchar *widget_name;
1374   gchar *response_id;
1375 } ActionWidgetInfo;
1376
1377 typedef struct {
1378   GtkDialog *dialog;
1379   GtkBuilder *builder;
1380   GSList *items;
1381   gchar *response;
1382 } ActionWidgetsSubParserData;
1383
1384 static void
1385 attributes_start_element (GMarkupParseContext *context,
1386                           const gchar         *element_name,
1387                           const gchar        **names,
1388                           const gchar        **values,
1389                           gpointer             user_data,
1390                           GError             **error)
1391 {
1392   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
1393   guint i;
1394
1395   if (strcmp (element_name, "action-widget") == 0)
1396     {
1397       for (i = 0; names[i]; i++)
1398         if (strcmp (names[i], "response") == 0)
1399           parser_data->response = g_strdup (values[i]);
1400     }
1401   else if (strcmp (element_name, "action-widgets") == 0)
1402     return;
1403   else
1404     g_warning ("Unsupported tag for GtkDialog: %s\n", element_name);
1405 }
1406
1407 static void
1408 attributes_text_element (GMarkupParseContext *context,
1409                          const gchar         *text,
1410                          gsize                text_len,
1411                          gpointer             user_data,
1412                          GError             **error)
1413 {
1414   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
1415   ActionWidgetInfo *item;
1416
1417   if (!parser_data->response)
1418     return;
1419
1420   item = g_new (ActionWidgetInfo, 1);
1421   item->widget_name = g_strndup (text, text_len);
1422   item->response_id = parser_data->response;
1423   parser_data->items = g_slist_prepend (parser_data->items, item);
1424   parser_data->response = NULL;
1425 }
1426
1427 static const GMarkupParser attributes_parser =
1428   {
1429     attributes_start_element,
1430     NULL,
1431     attributes_text_element,
1432   };
1433
1434 static gboolean
1435 gtk_dialog_buildable_custom_tag_start (GtkBuildable  *buildable,
1436                                        GtkBuilder    *builder,
1437                                        GObject       *child,
1438                                        const gchar   *tagname,
1439                                        GMarkupParser *parser,
1440                                        gpointer      *data)
1441 {
1442   ActionWidgetsSubParserData *parser_data;
1443
1444   if (child)
1445     return FALSE;
1446
1447   if (strcmp (tagname, "action-widgets") == 0)
1448     {
1449       parser_data = g_slice_new0 (ActionWidgetsSubParserData);
1450       parser_data->dialog = GTK_DIALOG (buildable);
1451       parser_data->items = NULL;
1452
1453       *parser = attributes_parser;
1454       *data = parser_data;
1455       return TRUE;
1456     }
1457
1458   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
1459                                                    tagname, parser, data);
1460 }
1461
1462 static void
1463 gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
1464                                       GtkBuilder   *builder,
1465                                       GObject      *child,
1466                                       const gchar  *tagname,
1467                                       gpointer      user_data)
1468 {
1469   GtkDialog *dialog = GTK_DIALOG (buildable);
1470   GtkDialogPrivate *priv = dialog->priv;
1471   GSList *l;
1472   ActionWidgetsSubParserData *parser_data;
1473   GObject *object;
1474   ResponseData *ad;
1475   guint signal_id;
1476
1477   if (strcmp (tagname, "action-widgets"))
1478     {
1479     parent_buildable_iface->custom_finished (buildable, builder, child,
1480                                              tagname, user_data);
1481     return;
1482     }
1483
1484   parser_data = (ActionWidgetsSubParserData*)user_data;
1485   parser_data->items = g_slist_reverse (parser_data->items);
1486
1487   for (l = parser_data->items; l; l = l->next)
1488     {
1489       ActionWidgetInfo *item = l->data;
1490
1491       object = gtk_builder_get_object (builder, item->widget_name);
1492       if (!object)
1493         {
1494           g_warning ("Unknown object %s specified in action-widgets of %s",
1495                      item->widget_name,
1496                      gtk_buildable_get_name (GTK_BUILDABLE (buildable)));
1497           continue;
1498         }
1499
1500       ad = get_response_data (GTK_WIDGET (object), TRUE);
1501       ad->response_id = g_ascii_strtoll (item->response_id, NULL, 10);
1502
1503       if (GTK_IS_BUTTON (object))
1504         signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1505       else
1506         signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1507
1508       if (signal_id)
1509         {
1510           GClosure *closure;
1511
1512           closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1513                                            G_OBJECT (dialog));
1514           g_signal_connect_closure_by_id (object,
1515                                           signal_id,
1516                                           0,
1517                                           closure,
1518                                           FALSE);
1519         }
1520
1521       if (ad->response_id == GTK_RESPONSE_HELP)
1522         gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area),
1523                                             GTK_WIDGET (object), TRUE);
1524
1525       g_free (item->widget_name);
1526       g_free (item->response_id);
1527       g_free (item);
1528     }
1529   g_slist_free (parser_data->items);
1530   g_slice_free (ActionWidgetsSubParserData, parser_data);
1531 }
1532
1533 /**
1534  * gtk_dialog_get_action_area:
1535  * @dialog: a #GtkDialog
1536  *
1537  * Returns the action area of @dialog.
1538  *
1539  * Returns: (transfer none): the action area.
1540  *
1541  * Since: 2.14
1542  **/
1543 GtkWidget *
1544 gtk_dialog_get_action_area (GtkDialog *dialog)
1545 {
1546   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1547
1548   return dialog->priv->action_area;
1549 }
1550
1551 /**
1552  * gtk_dialog_get_content_area:
1553  * @dialog: a #GtkDialog
1554  *
1555  * Returns the content area of @dialog.
1556  *
1557  * Returns: (transfer none): the content area #GtkBox.
1558  *
1559  * Since: 2.14
1560  **/
1561 GtkWidget *
1562 gtk_dialog_get_content_area (GtkDialog *dialog)
1563 {
1564   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1565
1566   return dialog->priv->vbox;
1567 }