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