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