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