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