]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
gtk/gtkdialog.c: use accessor functions to access GtkWidget
[~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   GtkWindow *window = GTK_WINDOW (widget);
411   GtkDialog *dialog = GTK_DIALOG (widget);
412   GtkDialogPriv *priv = dialog->priv;
413   
414   GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
415
416   if (!window->focus_widget)
417     {
418       GList *children, *tmp_list;
419       GtkWidget *first_focus = NULL;
420       
421       do 
422         {
423           g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
424
425           if (first_focus == NULL)
426             first_focus = window->focus_widget;
427           else if (first_focus == window->focus_widget)
428             break;
429           if (!GTK_IS_LABEL (window->focus_widget))
430             break;
431           if (!gtk_label_get_current_uri (GTK_LABEL (window->focus_widget)))
432             gtk_label_select_region (GTK_LABEL (window->focus_widget), 0, 0);
433         }
434       while (TRUE);
435
436       tmp_list = children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
437       
438       while (tmp_list)
439         {
440           GtkWidget *child = tmp_list->data;
441           
442           if ((window->focus_widget == NULL || 
443                child == window->focus_widget) && 
444               child != window->default_widget &&
445               window->default_widget)
446             {
447               gtk_widget_grab_focus (window->default_widget);
448               break;
449             }
450           
451           tmp_list = tmp_list->next;
452         }
453       
454       g_list_free (children);
455     }
456 }
457
458 static void
459 gtk_dialog_style_set (GtkWidget *widget,
460                       GtkStyle  *prev_style)
461 {
462   update_spacings (GTK_DIALOG (widget));
463 }
464
465 static GtkWidget *
466 dialog_find_button (GtkDialog *dialog,
467                     gint       response_id)
468 {
469   GtkDialogPriv *priv = dialog->priv;
470   GtkWidget *child = NULL;
471   GList *children, *tmp_list;
472       
473   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
474
475   for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
476     {
477       ResponseData *rd = get_response_data (tmp_list->data, FALSE);
478       
479       if (rd && rd->response_id == response_id)
480         {
481           child = tmp_list->data;
482           break;
483         }
484     }
485
486   g_list_free (children);
487
488   return child;
489 }
490
491 static void
492 gtk_dialog_close (GtkDialog *dialog)
493 {
494   /* Synthesize delete_event to close dialog. */
495   
496   GtkWidget *widget = GTK_WIDGET (dialog);
497   GdkEvent *event;
498
499   event = gdk_event_new (GDK_DELETE);
500
501   event->any.window = g_object_ref (gtk_widget_get_window (widget));
502   event->any.send_event = TRUE;
503   
504   gtk_main_do_event (event);
505   gdk_event_free (event);
506 }
507
508 GtkWidget*
509 gtk_dialog_new (void)
510 {
511   return g_object_new (GTK_TYPE_DIALOG, NULL);
512 }
513
514 static GtkWidget*
515 gtk_dialog_new_empty (const gchar     *title,
516                       GtkWindow       *parent,
517                       GtkDialogFlags   flags)
518 {
519   GtkDialog *dialog;
520
521   dialog = g_object_new (GTK_TYPE_DIALOG, NULL);
522
523   if (title)
524     gtk_window_set_title (GTK_WINDOW (dialog), title);
525
526   if (parent)
527     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
528
529   if (flags & GTK_DIALOG_MODAL)
530     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
531   
532   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
533     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
534
535   if (flags & GTK_DIALOG_NO_SEPARATOR)
536     gtk_dialog_set_has_separator (dialog, FALSE);
537   
538   return GTK_WIDGET (dialog);
539 }
540
541 /**
542  * gtk_dialog_new_with_buttons:
543  * @title: (allow-none): Title of the dialog, or %NULL
544  * @parent: (allow-none): Transient parent of the dialog, or %NULL
545  * @flags: from #GtkDialogFlags
546  * @first_button_text: (allow-none): stock ID or text to go in first button, or %NULL
547  * @Varargs: response ID for first button, then additional buttons, ending with %NULL
548  *
549  * Creates a new #GtkDialog with title @title (or %NULL for the default
550  * title; see gtk_window_set_title()) and transient parent @parent (or
551  * %NULL for none; see gtk_window_set_transient_for()). The @flags
552  * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
553  * and/or to have it destroyed along with its transient parent
554  * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
555  * text/response ID pairs should be listed, with a %NULL pointer ending
556  * the list. Button text can be either a stock ID such as
557  * #GTK_STOCK_OK, or some arbitrary text. A response ID can be
558  * any positive number, or one of the values in the #GtkResponseType
559  * enumeration. If the user clicks one of these dialog buttons,
560  * #GtkDialog will emit the #GtkDialog::response signal with the corresponding
561  * response ID. If a #GtkDialog receives the #GtkWidget::delete-event signal, 
562  * it will emit ::response with a response ID of #GTK_RESPONSE_DELETE_EVENT.
563  * However, destroying a dialog does not emit the ::response signal;
564  * so be careful relying on ::response when using the 
565  * #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
566  * so the first button in the list will be the leftmost button in the dialog.
567  *
568  * Here's a simple example:
569  * |[
570  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
571  *                                                   main_app_window,
572  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
573  *                                                   GTK_STOCK_OK,
574  *                                                   GTK_RESPONSE_ACCEPT,
575  *                                                   GTK_STOCK_CANCEL,
576  *                                                   GTK_RESPONSE_REJECT,
577  *                                                   NULL);
578  * ]|
579  * 
580  * Return value: a new #GtkDialog
581  **/
582 GtkWidget*
583 gtk_dialog_new_with_buttons (const gchar    *title,
584                              GtkWindow      *parent,
585                              GtkDialogFlags  flags,
586                              const gchar    *first_button_text,
587                              ...)
588 {
589   GtkDialog *dialog;
590   va_list args;
591   
592   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
593
594   va_start (args, first_button_text);
595
596   gtk_dialog_add_buttons_valist (dialog,
597                                  first_button_text,
598                                  args);
599   
600   va_end (args);
601
602   return GTK_WIDGET (dialog);
603 }
604
605 static void 
606 response_data_free (gpointer data)
607 {
608   g_slice_free (ResponseData, data);
609 }
610
611 static ResponseData*
612 get_response_data (GtkWidget *widget,
613                    gboolean   create)
614 {
615   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
616                                         "gtk-dialog-response-data");
617
618   if (ad == NULL && create)
619     {
620       ad = g_slice_new (ResponseData);
621       
622       g_object_set_data_full (G_OBJECT (widget),
623                               I_("gtk-dialog-response-data"),
624                               ad,
625                               response_data_free);
626     }
627
628   return ad;
629 }
630
631 static void
632 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
633 {
634   gint response_id;
635   
636   response_id = gtk_dialog_get_response_for_widget (dialog, widget);
637
638   gtk_dialog_response (dialog, response_id);
639 }
640
641 /**
642  * gtk_dialog_add_action_widget:
643  * @dialog: a #GtkDialog
644  * @child: an activatable widget
645  * @response_id: response ID for @child
646  * 
647  * Adds an activatable widget to the action area of a #GtkDialog,
648  * connecting a signal handler that will emit the #GtkDialog::response 
649  * signal on the dialog when the widget is activated. The widget is 
650  * appended to the end of the dialog's action area. If you want to add a
651  * non-activatable widget, simply pack it into the @action_area field 
652  * of the #GtkDialog struct.
653  **/
654 void
655 gtk_dialog_add_action_widget (GtkDialog *dialog,
656                               GtkWidget *child,
657                               gint       response_id)
658 {
659   GtkDialogPriv *priv;
660   ResponseData *ad;
661   guint signal_id;
662   
663   g_return_if_fail (GTK_IS_DIALOG (dialog));
664   g_return_if_fail (GTK_IS_WIDGET (child));
665
666   priv = dialog->priv;
667
668   ad = get_response_data (child, TRUE);
669
670   ad->response_id = response_id;
671
672   if (GTK_IS_BUTTON (child))
673     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
674   else
675     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
676
677   if (signal_id)
678     {
679       GClosure *closure;
680
681       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
682                                        G_OBJECT (dialog));
683       g_signal_connect_closure_by_id (child,
684                                       signal_id,
685                                       0,
686                                       closure,
687                                       FALSE);
688     }
689   else
690     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
691
692   gtk_box_pack_end (GTK_BOX (priv->action_area),
693                     child,
694                     FALSE, TRUE, 0);
695   
696   if (response_id == GTK_RESPONSE_HELP)
697     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area), child, TRUE);
698 }
699
700 /**
701  * gtk_dialog_add_button:
702  * @dialog: a #GtkDialog
703  * @button_text: text of button, or stock ID
704  * @response_id: response ID for the button
705  * 
706  * Adds a button with the given text (or a stock button, if @button_text is a
707  * stock ID) and sets things up so that clicking the button will emit the
708  * #GtkDialog::response signal with the given @response_id. The button is 
709  * appended to the end of the dialog's action area. The button widget is 
710  * returned, but usually you don't need it.
711  *
712  * Return value: the button widget that was added
713  **/
714 GtkWidget*
715 gtk_dialog_add_button (GtkDialog   *dialog,
716                        const gchar *button_text,
717                        gint         response_id)
718 {
719   GtkWidget *button;
720   
721   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
722   g_return_val_if_fail (button_text != NULL, NULL);
723
724   button = gtk_button_new_from_stock (button_text);
725
726   gtk_widget_set_can_default (button, TRUE);
727   
728   gtk_widget_show (button);
729   
730   gtk_dialog_add_action_widget (dialog,
731                                 button,
732                                 response_id);
733
734   return button;
735 }
736
737 static void
738 gtk_dialog_add_buttons_valist (GtkDialog      *dialog,
739                                const gchar    *first_button_text,
740                                va_list         args)
741 {
742   const gchar* text;
743   gint response_id;
744
745   g_return_if_fail (GTK_IS_DIALOG (dialog));
746   
747   if (first_button_text == NULL)
748     return;
749   
750   text = first_button_text;
751   response_id = va_arg (args, gint);
752
753   while (text != NULL)
754     {
755       gtk_dialog_add_button (dialog, text, response_id);
756
757       text = va_arg (args, gchar*);
758       if (text == NULL)
759         break;
760       response_id = va_arg (args, int);
761     }
762 }
763
764 /**
765  * gtk_dialog_add_buttons:
766  * @dialog: a #GtkDialog
767  * @first_button_text: button text or stock ID
768  * @Varargs: response ID for first button, then more text-response_id pairs
769  * 
770  * Adds more buttons, same as calling gtk_dialog_add_button()
771  * repeatedly.  The variable argument list should be %NULL-terminated
772  * as with gtk_dialog_new_with_buttons(). Each button must have both
773  * text and response ID.
774  **/
775 void
776 gtk_dialog_add_buttons (GtkDialog   *dialog,
777                         const gchar *first_button_text,
778                         ...)
779 {  
780   va_list args;
781
782   va_start (args, first_button_text);
783
784   gtk_dialog_add_buttons_valist (dialog,
785                                  first_button_text,
786                                  args);
787   
788   va_end (args);
789 }
790
791 /**
792  * gtk_dialog_set_response_sensitive:
793  * @dialog: a #GtkDialog
794  * @response_id: a response ID
795  * @setting: %TRUE for sensitive
796  *
797  * Calls <literal>gtk_widget_set_sensitive (widget, @setting)</literal> 
798  * for each widget in the dialog's action area with the given @response_id.
799  * A convenient way to sensitize/desensitize dialog buttons.
800  **/
801 void
802 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
803                                    gint       response_id,
804                                    gboolean   setting)
805 {
806   GtkDialogPriv *priv;
807   GList *children;
808   GList *tmp_list;
809
810   g_return_if_fail (GTK_IS_DIALOG (dialog));
811
812   priv = dialog->priv;
813
814   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
815
816   tmp_list = children;
817   while (tmp_list != NULL)
818     {
819       GtkWidget *widget = tmp_list->data;
820       ResponseData *rd = get_response_data (widget, FALSE);
821
822       if (rd && rd->response_id == response_id)
823         gtk_widget_set_sensitive (widget, setting);
824
825       tmp_list = g_list_next (tmp_list);
826     }
827
828   g_list_free (children);
829 }
830
831 /**
832  * gtk_dialog_set_default_response:
833  * @dialog: a #GtkDialog
834  * @response_id: a response ID
835  * 
836  * Sets the last widget in the dialog's action area with the given @response_id
837  * as the default widget for the dialog. Pressing "Enter" normally activates
838  * the default widget.
839  **/
840 void
841 gtk_dialog_set_default_response (GtkDialog *dialog,
842                                  gint       response_id)
843 {
844   GtkDialogPriv *priv;
845   GList *children;
846   GList *tmp_list;
847
848   g_return_if_fail (GTK_IS_DIALOG (dialog));
849
850   priv = dialog->priv;
851
852   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
853
854   tmp_list = children;
855   while (tmp_list != NULL)
856     {
857       GtkWidget *widget = tmp_list->data;
858       ResponseData *rd = get_response_data (widget, FALSE);
859
860       if (rd && rd->response_id == response_id)
861         gtk_widget_grab_default (widget);
862             
863       tmp_list = g_list_next (tmp_list);
864     }
865
866   g_list_free (children);
867 }
868
869 /**
870  * gtk_dialog_set_has_separator:
871  * @dialog: a #GtkDialog
872  * @setting: %TRUE to have a separator
873  *
874  * Sets whether the dialog has a separator above the buttons.
875  * %TRUE by default.
876  **/
877 void
878 gtk_dialog_set_has_separator (GtkDialog *dialog,
879                               gboolean   setting)
880 {
881   GtkDialogPriv *priv;
882
883   g_return_if_fail (GTK_IS_DIALOG (dialog));
884
885   priv = dialog->priv;
886
887   /* this might fail if we get called before _init() somehow */
888   g_assert (priv->vbox != NULL);
889
890   if (priv->ignore_separator)
891     {
892       g_warning ("Ignoring the separator setting");
893       return;
894     }
895   
896   if (setting && priv->separator == NULL)
897     {
898       priv->separator = gtk_hseparator_new ();
899       gtk_box_pack_end (GTK_BOX (priv->vbox), priv->separator, FALSE, TRUE, 0);
900
901       /* The app programmer could screw this up, but, their own fault.
902        * Moves the separator just above the action area.
903        */
904       gtk_box_reorder_child (GTK_BOX (priv->vbox), priv->separator, 1);
905       gtk_widget_show (priv->separator);
906     }
907   else if (!setting && priv->separator != NULL)
908     {
909       gtk_widget_destroy (priv->separator);
910       priv->separator = NULL;
911     }
912
913   g_object_notify (G_OBJECT (dialog), "has-separator");
914 }
915
916 /**
917  * gtk_dialog_get_has_separator:
918  * @dialog: a #GtkDialog
919  * 
920  * Accessor for whether the dialog has a separator.
921  * 
922  * Return value: %TRUE if the dialog has a separator
923  **/
924 gboolean
925 gtk_dialog_get_has_separator (GtkDialog *dialog)
926 {
927   g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE);
928
929   return dialog->priv->separator != NULL;
930 }
931
932 /**
933  * gtk_dialog_response:
934  * @dialog: a #GtkDialog
935  * @response_id: response ID 
936  * 
937  * Emits the #GtkDialog::response signal with the given response ID. 
938  * Used to indicate that the user has responded to the dialog in some way;
939  * typically either you or gtk_dialog_run() will be monitoring the
940  * ::response signal and take appropriate action.
941  **/
942 void
943 gtk_dialog_response (GtkDialog *dialog,
944                      gint       response_id)
945 {
946   g_return_if_fail (GTK_IS_DIALOG (dialog));
947
948   g_signal_emit (dialog,
949                  dialog_signals[RESPONSE],
950                  0,
951                  response_id);
952 }
953
954 typedef struct
955 {
956   GtkDialog *dialog;
957   gint response_id;
958   GMainLoop *loop;
959   gboolean destroyed;
960 } RunInfo;
961
962 static void
963 shutdown_loop (RunInfo *ri)
964 {
965   if (g_main_loop_is_running (ri->loop))
966     g_main_loop_quit (ri->loop);
967 }
968
969 static void
970 run_unmap_handler (GtkDialog *dialog, gpointer data)
971 {
972   RunInfo *ri = data;
973
974   shutdown_loop (ri);
975 }
976
977 static void
978 run_response_handler (GtkDialog *dialog,
979                       gint response_id,
980                       gpointer data)
981 {
982   RunInfo *ri;
983
984   ri = data;
985
986   ri->response_id = response_id;
987
988   shutdown_loop (ri);
989 }
990
991 static gint
992 run_delete_handler (GtkDialog *dialog,
993                     GdkEventAny *event,
994                     gpointer data)
995 {
996   RunInfo *ri = data;
997     
998   shutdown_loop (ri);
999   
1000   return TRUE; /* Do not destroy */
1001 }
1002
1003 static void
1004 run_destroy_handler (GtkDialog *dialog, gpointer data)
1005 {
1006   RunInfo *ri = data;
1007
1008   /* shutdown_loop will be called by run_unmap_handler */
1009   
1010   ri->destroyed = TRUE;
1011 }
1012
1013 /**
1014  * gtk_dialog_run:
1015  * @dialog: a #GtkDialog
1016  * 
1017  * Blocks in a recursive main loop until the @dialog either emits the
1018  * #GtkDialog::response signal, or is destroyed. If the dialog is 
1019  * destroyed during the call to gtk_dialog_run(), gtk_dialog_run() returns 
1020  * #GTK_RESPONSE_NONE. Otherwise, it returns the response ID from the 
1021  * ::response signal emission.
1022  *
1023  * Before entering the recursive main loop, gtk_dialog_run() calls
1024  * gtk_widget_show() on the dialog for you. Note that you still
1025  * need to show any children of the dialog yourself.
1026  *
1027  * During gtk_dialog_run(), the default behavior of #GtkWidget::delete-event 
1028  * is disabled; if the dialog receives ::delete_event, it will not be
1029  * destroyed as windows usually are, and gtk_dialog_run() will return
1030  * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog 
1031  * will be modal. You can force gtk_dialog_run() to return at any time by
1032  * calling gtk_dialog_response() to emit the ::response signal. Destroying 
1033  * the dialog during gtk_dialog_run() is a very bad idea, because your 
1034  * post-run code won't know whether the dialog was destroyed or not.
1035  *
1036  * After gtk_dialog_run() returns, you are responsible for hiding or
1037  * destroying the dialog if you wish to do so.
1038  *
1039  * Typical usage of this function might be:
1040  * |[
1041  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
1042  *   switch (result)
1043  *     {
1044  *       case GTK_RESPONSE_ACCEPT:
1045  *          do_application_specific_something ();
1046  *          break;
1047  *       default:
1048  *          do_nothing_since_dialog_was_cancelled ();
1049  *          break;
1050  *     }
1051  *   gtk_widget_destroy (dialog);
1052  * ]|
1053  * 
1054  * Note that even though the recursive main loop gives the effect of a
1055  * modal dialog (it prevents the user from interacting with other 
1056  * windows in the same window group while the dialog is run), callbacks 
1057  * such as timeouts, IO channel watches, DND drops, etc, <emphasis>will</emphasis> 
1058  * be triggered during a gtk_dialog_run() call.
1059  * 
1060  * Return value: response ID
1061  **/
1062 gint
1063 gtk_dialog_run (GtkDialog *dialog)
1064 {
1065   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
1066   gboolean was_modal;
1067   gulong response_handler;
1068   gulong unmap_handler;
1069   gulong destroy_handler;
1070   gulong delete_handler;
1071   
1072   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
1073
1074   g_object_ref (dialog);
1075
1076   was_modal = GTK_WINDOW (dialog)->modal;
1077   if (!was_modal)
1078     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1079
1080   if (!gtk_widget_get_visible (GTK_WIDGET (dialog)))
1081     gtk_widget_show (GTK_WIDGET (dialog));
1082   
1083   response_handler =
1084     g_signal_connect (dialog,
1085                       "response",
1086                       G_CALLBACK (run_response_handler),
1087                       &ri);
1088   
1089   unmap_handler =
1090     g_signal_connect (dialog,
1091                       "unmap",
1092                       G_CALLBACK (run_unmap_handler),
1093                       &ri);
1094   
1095   delete_handler =
1096     g_signal_connect (dialog,
1097                       "delete-event",
1098                       G_CALLBACK (run_delete_handler),
1099                       &ri);
1100   
1101   destroy_handler =
1102     g_signal_connect (dialog,
1103                       "destroy",
1104                       G_CALLBACK (run_destroy_handler),
1105                       &ri);
1106   
1107   ri.loop = g_main_loop_new (NULL, FALSE);
1108
1109   GDK_THREADS_LEAVE ();  
1110   g_main_loop_run (ri.loop);
1111   GDK_THREADS_ENTER ();  
1112
1113   g_main_loop_unref (ri.loop);
1114
1115   ri.loop = NULL;
1116   
1117   if (!ri.destroyed)
1118     {
1119       if (!was_modal)
1120         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1121       
1122       g_signal_handler_disconnect (dialog, response_handler);
1123       g_signal_handler_disconnect (dialog, unmap_handler);
1124       g_signal_handler_disconnect (dialog, delete_handler);
1125       g_signal_handler_disconnect (dialog, destroy_handler);
1126     }
1127
1128   g_object_unref (dialog);
1129
1130   return ri.response_id;
1131 }
1132
1133 void
1134 _gtk_dialog_set_ignore_separator (GtkDialog *dialog,
1135                                   gboolean   ignore_separator)
1136 {
1137   GtkDialogPriv *priv = dialog->priv;
1138
1139   priv->ignore_separator = ignore_separator;
1140 }
1141
1142 /**
1143  * gtk_dialog_get_widget_for_response:
1144  * @dialog: a #GtkDialog
1145  * @response_id: the response ID used by the @dialog widget
1146  *
1147  * Gets the widget button that uses the given response ID in the action area
1148  * of a dialog.
1149  *
1150  * Returns: the @widget button that uses the given @response_id, or %NULL.
1151  *
1152  * Since: 2.20
1153  */
1154 GtkWidget*
1155 gtk_dialog_get_widget_for_response (GtkDialog *dialog,
1156                                     gint       response_id)
1157 {
1158   GtkDialogPriv *priv;
1159   GList *children;
1160   GList *tmp_list;
1161
1162   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1163
1164   priv = dialog->priv;
1165
1166   children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
1167
1168   tmp_list = children;
1169   while (tmp_list != NULL)
1170     {
1171       GtkWidget *widget = tmp_list->data;
1172       ResponseData *rd = get_response_data (widget, FALSE);
1173
1174       if (rd && rd->response_id == response_id)
1175         {
1176           g_list_free (children);
1177           return widget;
1178         }
1179
1180       tmp_list = g_list_next (tmp_list);
1181     }
1182
1183   g_list_free (children);
1184
1185   return NULL;
1186 }
1187
1188 /**
1189  * gtk_dialog_get_response_for_widget:
1190  * @dialog: a #GtkDialog
1191  * @widget: a widget in the action area of @dialog
1192  *
1193  * Gets the response id of a widget in the action area
1194  * of a dialog.
1195  *
1196  * Returns: the response id of @widget, or %GTK_RESPONSE_NONE
1197  *  if @widget doesn't have a response id set.
1198  *
1199  * Since: 2.8
1200  */
1201 gint
1202 gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1203                                     GtkWidget *widget)
1204 {
1205   ResponseData *rd;
1206
1207   rd = get_response_data (widget, FALSE);
1208   if (!rd)
1209     return GTK_RESPONSE_NONE;
1210   else
1211     return rd->response_id;
1212 }
1213
1214 /**
1215  * gtk_alternative_dialog_button_order:
1216  * @screen: (allow-none): a #GdkScreen, or %NULL to use the default screen
1217  *
1218  * Returns %TRUE if dialogs are expected to use an alternative
1219  * button order on the screen @screen. See
1220  * gtk_dialog_set_alternative_button_order() for more details
1221  * about alternative button order. 
1222  *
1223  * If you need to use this function, you should probably connect
1224  * to the ::notify:gtk-alternative-button-order signal on the
1225  * #GtkSettings object associated to @screen, in order to be 
1226  * notified if the button order setting changes.
1227  *
1228  * Returns: Whether the alternative button order should be used
1229  *
1230  * Since: 2.6
1231  */
1232 gboolean 
1233 gtk_alternative_dialog_button_order (GdkScreen *screen)
1234 {
1235   GtkSettings *settings;
1236   gboolean result;
1237
1238   if (screen)
1239     settings = gtk_settings_get_for_screen (screen);
1240   else
1241     settings = gtk_settings_get_default ();
1242   
1243   g_object_get (settings,
1244                 "gtk-alternative-button-order", &result, NULL);
1245
1246   return result;
1247 }
1248
1249 static void
1250 gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
1251                                                 gint       first_response_id,
1252                                                 va_list    args)
1253 {
1254   GtkDialogPriv *priv = dialog->priv;
1255   GtkWidget *child;
1256   gint response_id;
1257   gint position;
1258
1259   response_id = first_response_id;
1260   position = 0;
1261   while (response_id != -1)
1262     {
1263       /* reorder child with response_id to position */
1264       child = dialog_find_button (dialog, response_id);
1265       gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1266
1267       response_id = va_arg (args, gint);
1268       position++;
1269     }
1270 }
1271
1272 /**
1273  * gtk_dialog_set_alternative_button_order:
1274  * @dialog: a #GtkDialog
1275  * @first_response_id: a response id used by one @dialog's buttons
1276  * @Varargs: a list of more response ids of @dialog's buttons, terminated by -1
1277  *
1278  * Sets an alternative button order. If the 
1279  * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE, 
1280  * the dialog buttons are reordered according to the order of the 
1281  * response ids passed to this function.
1282  *
1283  * By default, GTK+ dialogs use the button order advocated by the Gnome 
1284  * <ulink url="http://developer.gnome.org/projects/gup/hig/2.0/">Human 
1285  * Interface Guidelines</ulink> with the affirmative button at the far 
1286  * right, and the cancel button left of it. But the builtin GTK+ dialogs
1287  * and #GtkMessageDialog<!-- -->s do provide an alternative button order,
1288  * which is more suitable on some platforms, e.g. Windows.
1289  *
1290  * Use this function after adding all the buttons to your dialog, as the 
1291  * following example shows:
1292  * |[
1293  * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1294  *                                        GTK_STOCK_CANCEL,
1295  *                                        GTK_RESPONSE_CANCEL);
1296  *  
1297  * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1298  *                                    GTK_STOCK_OK,
1299  *                                    GTK_RESPONSE_OK);
1300  *   
1301  * gtk_widget_grab_default (ok_button);
1302  *   
1303  * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1304  *                                      GTK_STOCK_HELP,
1305  *                                      GTK_RESPONSE_HELP);
1306  *  
1307  * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1308  *                                          GTK_RESPONSE_OK,
1309  *                                          GTK_RESPONSE_CANCEL,
1310  *                                          GTK_RESPONSE_HELP,
1311  *                                          -1);
1312  * ]|
1313  * 
1314  * Since: 2.6
1315  */
1316 void 
1317 gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
1318                                          gint       first_response_id,
1319                                          ...)
1320 {
1321   GdkScreen *screen;
1322   va_list args;
1323   
1324   g_return_if_fail (GTK_IS_DIALOG (dialog));
1325
1326   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1327   if (!gtk_alternative_dialog_button_order (screen))
1328       return;
1329
1330   va_start (args, first_response_id);
1331
1332   gtk_dialog_set_alternative_button_order_valist (dialog,
1333                                                   first_response_id,
1334                                                   args);
1335   va_end (args);
1336 }
1337 /**
1338  * gtk_dialog_set_alternative_button_order_from_array:
1339  * @dialog: a #GtkDialog
1340  * @n_params: the number of response ids in @new_order
1341  * @new_order: an array of response ids of @dialog's buttons
1342  *
1343  * Sets an alternative button order. If the 
1344  * #GtkSettings:gtk-alternative-button-order setting is set to %TRUE, 
1345  * the dialog buttons are reordered according to the order of the 
1346  * response ids in @new_order.
1347  *
1348  * See gtk_dialog_set_alternative_button_order() for more information.
1349  *
1350  * This function is for use by language bindings.
1351  * 
1352  * Since: 2.6
1353  */
1354 void 
1355 gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
1356                                                     gint       n_params,
1357                                                     gint      *new_order)
1358 {
1359   GtkDialogPriv *priv = dialog->priv;
1360   GdkScreen *screen;
1361   GtkWidget *child;
1362   gint position;
1363
1364   g_return_if_fail (GTK_IS_DIALOG (dialog));
1365   g_return_if_fail (new_order != NULL);
1366
1367   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1368   if (!gtk_alternative_dialog_button_order (screen))
1369       return;
1370
1371   for (position = 0; position < n_params; position++)
1372   {
1373       /* reorder child with response_id to position */
1374       child = dialog_find_button (dialog, new_order[position]);
1375       gtk_box_reorder_child (GTK_BOX (priv->action_area), child, position);
1376     }
1377 }
1378
1379 typedef struct {
1380   gchar *widget_name;
1381   gchar *response_id;
1382 } ActionWidgetInfo;
1383
1384 typedef struct {
1385   GtkDialog *dialog;
1386   GtkBuilder *builder;
1387   GSList *items;
1388   gchar *response;
1389 } ActionWidgetsSubParserData;
1390
1391 static void
1392 attributes_start_element (GMarkupParseContext *context,
1393                           const gchar         *element_name,
1394                           const gchar        **names,
1395                           const gchar        **values,
1396                           gpointer             user_data,
1397                           GError             **error)
1398 {
1399   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
1400   guint i;
1401
1402   if (strcmp (element_name, "action-widget") == 0)
1403     {
1404       for (i = 0; names[i]; i++)
1405         if (strcmp (names[i], "response") == 0)
1406           parser_data->response = g_strdup (values[i]);
1407     }
1408   else if (strcmp (element_name, "action-widgets") == 0)
1409     return;
1410   else
1411     g_warning ("Unsupported tag for GtkDialog: %s\n", element_name);
1412 }
1413
1414 static void
1415 attributes_text_element (GMarkupParseContext *context,
1416                          const gchar         *text,
1417                          gsize                text_len,
1418                          gpointer             user_data,
1419                          GError             **error)
1420 {
1421   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
1422   ActionWidgetInfo *item;
1423
1424   if (!parser_data->response)
1425     return;
1426
1427   item = g_new (ActionWidgetInfo, 1);
1428   item->widget_name = g_strndup (text, text_len);
1429   item->response_id = parser_data->response;
1430   parser_data->items = g_slist_prepend (parser_data->items, item);
1431   parser_data->response = NULL;
1432 }
1433
1434 static const GMarkupParser attributes_parser =
1435   {
1436     attributes_start_element,
1437     NULL,
1438     attributes_text_element,
1439   };
1440
1441 static gboolean
1442 gtk_dialog_buildable_custom_tag_start (GtkBuildable  *buildable,
1443                                        GtkBuilder    *builder,
1444                                        GObject       *child,
1445                                        const gchar   *tagname,
1446                                        GMarkupParser *parser,
1447                                        gpointer      *data)
1448 {
1449   ActionWidgetsSubParserData *parser_data;
1450
1451   if (child)
1452     return FALSE;
1453
1454   if (strcmp (tagname, "action-widgets") == 0)
1455     {
1456       parser_data = g_slice_new0 (ActionWidgetsSubParserData);
1457       parser_data->dialog = GTK_DIALOG (buildable);
1458       parser_data->items = NULL;
1459
1460       *parser = attributes_parser;
1461       *data = parser_data;
1462       return TRUE;
1463     }
1464
1465   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
1466                                                    tagname, parser, data);
1467 }
1468
1469 static void
1470 gtk_dialog_buildable_custom_finished (GtkBuildable *buildable,
1471                                       GtkBuilder   *builder,
1472                                       GObject      *child,
1473                                       const gchar  *tagname,
1474                                       gpointer      user_data)
1475 {
1476   GtkDialog *dialog = GTK_DIALOG (buildable);
1477   GtkDialogPriv *priv = dialog->priv;
1478   GSList *l;
1479   ActionWidgetsSubParserData *parser_data;
1480   GObject *object;
1481   ResponseData *ad;
1482   guint signal_id;
1483   
1484   if (strcmp (tagname, "action-widgets"))
1485     {
1486     parent_buildable_iface->custom_finished (buildable, builder, child,
1487                                              tagname, user_data);
1488     return;
1489     }
1490
1491   parser_data = (ActionWidgetsSubParserData*)user_data;
1492   parser_data->items = g_slist_reverse (parser_data->items);
1493
1494   for (l = parser_data->items; l; l = l->next)
1495     {
1496       ActionWidgetInfo *item = l->data;
1497
1498       object = gtk_builder_get_object (builder, item->widget_name);
1499       if (!object)
1500         {
1501           g_warning ("Unknown object %s specified in action-widgets of %s",
1502                      item->widget_name,
1503                      gtk_buildable_get_name (GTK_BUILDABLE (buildable)));
1504           continue;
1505         }
1506
1507       ad = get_response_data (GTK_WIDGET (object), TRUE);
1508       ad->response_id = atoi (item->response_id);
1509
1510       if (GTK_IS_BUTTON (object))
1511         signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1512       else
1513         signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1514       
1515       if (signal_id)
1516         {
1517           GClosure *closure;
1518           
1519           closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1520                                            G_OBJECT (dialog));
1521           g_signal_connect_closure_by_id (object,
1522                                           signal_id,
1523                                           0,
1524                                           closure,
1525                                           FALSE);
1526         }
1527
1528       if (ad->response_id == GTK_RESPONSE_HELP)
1529         gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area),
1530                                             GTK_WIDGET (object), TRUE);
1531
1532       g_free (item->widget_name);
1533       g_free (item->response_id);
1534       g_free (item);
1535     }
1536   g_slist_free (parser_data->items);
1537   g_slice_free (ActionWidgetsSubParserData, parser_data);
1538 }
1539
1540 /**
1541  * gtk_dialog_get_action_area:
1542  * @dialog: a #GtkDialog
1543  *
1544  * Returns the action area of @dialog.
1545  *
1546  * Returns: (transfer none): the action area.
1547  *
1548  * Since: 2.14
1549  **/
1550 GtkWidget *
1551 gtk_dialog_get_action_area (GtkDialog *dialog)
1552 {
1553   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1554
1555   return dialog->priv->action_area;
1556 }
1557
1558 /**
1559  * gtk_dialog_get_content_area:
1560  * @dialog: a #GtkDialog
1561  *
1562  * Returns the content area of @dialog.
1563  *
1564  * Returns: (transfer none): the content area #GtkVBox.
1565  *
1566  * Since: 2.14
1567  **/
1568 GtkWidget *
1569 gtk_dialog_get_content_area (GtkDialog *dialog)
1570 {
1571   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1572
1573   return dialog->priv->vbox;
1574 }