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