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