]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
Some clarification regarding modality. (#112903, Dave Bordoley)
[~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 <config.h>
28 #include "gtkalias.h"
29 #include "gtkbutton.h"
30 #include "gtkdialog.h"
31 #include "gtkhbbox.h"
32 #include "gtklabel.h"
33 #include "gtkhseparator.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
41 #define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_DIALOG, GtkDialogPrivate))
42
43 typedef struct {
44   guint ignore_separator : 1;
45 } GtkDialogPrivate;
46
47 typedef struct _ResponseData ResponseData;
48
49 struct _ResponseData
50 {
51   gint response_id;
52 };
53
54 static void gtk_dialog_class_init (GtkDialogClass *klass);
55 static void gtk_dialog_init       (GtkDialog      *dialog);
56
57 static void gtk_dialog_add_buttons_valist (GtkDialog   *dialog,
58                                            const gchar *first_button_text,
59                                            va_list      args);
60
61 static gint gtk_dialog_delete_event_handler (GtkWidget   *widget,
62                                              GdkEventAny *event,
63                                              gpointer     user_data);
64
65 static void gtk_dialog_set_property      (GObject          *object,
66                                           guint             prop_id,
67                                           const GValue     *value,
68                                           GParamSpec       *pspec);
69 static void gtk_dialog_get_property      (GObject          *object,
70                                           guint             prop_id,
71                                           GValue           *value,
72                                           GParamSpec       *pspec);
73 static void gtk_dialog_style_set         (GtkWidget        *widget,
74                                           GtkStyle         *prev_style);
75 static void gtk_dialog_map               (GtkWidget        *widget);
76
77 static void gtk_dialog_close             (GtkDialog        *dialog);
78
79 static ResponseData* get_response_data   (GtkWidget        *widget,
80                                           gboolean          create);
81
82 enum {
83   PROP_0,
84   PROP_HAS_SEPARATOR
85 };
86
87 enum {
88   RESPONSE,
89   CLOSE,
90   LAST_SIGNAL
91 };
92
93 static gpointer parent_class;
94 static guint dialog_signals[LAST_SIGNAL];
95
96 GType
97 gtk_dialog_get_type (void)
98 {
99   static GType dialog_type = 0;
100
101   if (!dialog_type)
102     {
103       static const GTypeInfo dialog_info =
104       {
105         sizeof (GtkDialogClass),
106         NULL,           /* base_init */
107         NULL,           /* base_finalize */
108         (GClassInitFunc) gtk_dialog_class_init,
109         NULL,           /* class_finalize */
110         NULL,           /* class_data */
111         sizeof (GtkDialog),
112         0,              /* n_preallocs */
113         (GInstanceInitFunc) gtk_dialog_init,
114       };
115
116       dialog_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkDialog",
117                                             &dialog_info, 0);
118     }
119
120   return dialog_type;
121 }
122
123 static void
124 gtk_dialog_class_init (GtkDialogClass *class)
125 {
126   GObjectClass *gobject_class;
127   GtkWidgetClass *widget_class;
128   GtkBindingSet *binding_set;
129   
130   gobject_class = G_OBJECT_CLASS (class);
131   widget_class = GTK_WIDGET_CLASS (class);
132   
133   parent_class = g_type_class_peek_parent (class);
134
135   gobject_class->set_property = gtk_dialog_set_property;
136   gobject_class->get_property = gtk_dialog_get_property;
137   
138   widget_class->map = gtk_dialog_map;
139   widget_class->style_set = gtk_dialog_style_set;
140
141   class->close = gtk_dialog_close;
142   
143   g_type_class_add_private (gobject_class, sizeof (GtkDialogPrivate));
144
145   g_object_class_install_property (gobject_class,
146                                    PROP_HAS_SEPARATOR,
147                                    g_param_spec_boolean ("has_separator",
148                                                          P_("Has separator"),
149                                                          P_("The dialog has a separator bar above its buttons"),
150                                                          TRUE,
151                                                          G_PARAM_READWRITE));
152   
153   dialog_signals[RESPONSE] =
154     g_signal_new ("response",
155                   G_OBJECT_CLASS_TYPE (class),
156                   G_SIGNAL_RUN_LAST,
157                   G_STRUCT_OFFSET (GtkDialogClass, response),
158                   NULL, NULL,
159                   _gtk_marshal_NONE__INT,
160                   G_TYPE_NONE, 1,
161                   G_TYPE_INT);
162
163   dialog_signals[CLOSE] =
164     g_signal_new ("close",
165                   G_OBJECT_CLASS_TYPE (class),
166                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
167                   G_STRUCT_OFFSET (GtkDialogClass, close),
168                   NULL, NULL,
169                   _gtk_marshal_NONE__NONE,
170                   G_TYPE_NONE, 0);
171   
172   gtk_widget_class_install_style_property (widget_class,
173                                            g_param_spec_int ("content_area_border",
174                                                              P_("Content area border"),
175                                                              P_("Width of border around the main dialog area"),
176                                                              0,
177                                                              G_MAXINT,
178                                                              2,
179                                                              G_PARAM_READABLE));
180   gtk_widget_class_install_style_property (widget_class,
181                                            g_param_spec_int ("button_spacing",
182                                                              P_("Button spacing"),
183                                                              P_("Spacing between buttons"),
184                                                              0,
185                                                              G_MAXINT,
186                                                              10,
187                                                              G_PARAM_READABLE));
188   
189   gtk_widget_class_install_style_property (widget_class,
190                                            g_param_spec_int ("action_area_border",
191                                                              P_("Action area border"),
192                                                              P_("Width of border around the button area at the bottom of the dialog"),
193                                                              0,
194                                                              G_MAXINT,
195                                                              5,
196                                                              G_PARAM_READABLE));
197
198   binding_set = gtk_binding_set_by_class (class);
199   
200   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
201                                 "close", 0);
202 }
203
204 static void
205 update_spacings (GtkDialog *dialog)
206 {
207   GtkWidget *widget;
208   gint content_area_border;
209   gint button_spacing;
210   gint action_area_border;
211   
212   widget = GTK_WIDGET (dialog);
213
214   gtk_widget_style_get (widget,
215                         "content_area_border",
216                         &content_area_border,
217                         "button_spacing",
218                         &button_spacing,
219                         "action_area_border",
220                         &action_area_border,
221                         NULL);
222
223   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox),
224                                   content_area_border);
225   gtk_box_set_spacing (GTK_BOX (dialog->action_area),
226                        button_spacing);
227   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area),
228                                   action_area_border);
229 }
230
231 static void
232 gtk_dialog_init (GtkDialog *dialog)
233 {
234   GtkDialogPrivate *priv;
235
236   priv = GET_PRIVATE (dialog);
237   priv->ignore_separator = FALSE;
238
239   /* To avoid breaking old code that prevents destroy on delete event
240    * by connecting a handler, we have to have the FIRST signal
241    * connection on the dialog.
242    */
243   g_signal_connect (dialog,
244                     "delete_event",
245                     G_CALLBACK (gtk_dialog_delete_event_handler),
246                     NULL);
247   
248   dialog->vbox = gtk_vbox_new (FALSE, 0);
249   
250   gtk_container_add (GTK_CONTAINER (dialog), dialog->vbox);
251   gtk_widget_show (dialog->vbox);
252
253   dialog->action_area = gtk_hbutton_box_new ();
254
255   gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area),
256                              GTK_BUTTONBOX_END);  
257
258   gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area,
259                     FALSE, TRUE, 0);
260   gtk_widget_show (dialog->action_area);
261
262   dialog->separator = gtk_hseparator_new ();
263   gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0);
264   gtk_widget_show (dialog->separator);
265
266   gtk_window_set_type_hint (GTK_WINDOW (dialog),
267                             GDK_WINDOW_TYPE_HINT_DIALOG);
268   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
269 }
270
271
272 static void 
273 gtk_dialog_set_property (GObject      *object,
274                          guint         prop_id,
275                          const GValue *value,
276                          GParamSpec   *pspec)
277 {
278   GtkDialog *dialog;
279   
280   dialog = GTK_DIALOG (object);
281   
282   switch (prop_id)
283     {
284     case PROP_HAS_SEPARATOR:
285       gtk_dialog_set_has_separator (dialog, g_value_get_boolean (value));
286       break;
287
288     default:
289       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290       break;
291     }
292 }
293
294 static void 
295 gtk_dialog_get_property (GObject     *object,
296                          guint        prop_id,
297                          GValue      *value,
298                          GParamSpec  *pspec)
299 {
300   GtkDialog *dialog;
301   
302   dialog = GTK_DIALOG (object);
303   
304   switch (prop_id)
305     {
306     case PROP_HAS_SEPARATOR:
307       g_value_set_boolean (value, dialog->separator != NULL);
308       break;
309
310     default:
311       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
312       break;
313     }
314 }
315
316 static gint
317 gtk_dialog_delete_event_handler (GtkWidget   *widget,
318                                  GdkEventAny *event,
319                                  gpointer     user_data)
320 {
321   /* emit response signal */
322   gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
323
324   /* Do the destroy by default */
325   return FALSE;
326 }
327
328 /* A far too tricky heuristic for getting the right initial
329  * focus widget if none was set. What we do is we focus the first
330  * widget in the tab chain, but if this results in the focus
331  * ending up on one of the response widgets _other_ than the
332  * default response, we focus the default response instead.
333  *
334  * Additionally, skip selectable labels when looking for the
335  * right initial focus widget.
336  */
337 static void
338 gtk_dialog_map (GtkWidget *widget)
339 {
340   GtkWindow *window = GTK_WINDOW (widget);
341   GtkDialog *dialog = GTK_DIALOG (widget);
342   
343   GTK_WIDGET_CLASS (parent_class)->map (widget);
344
345   if (!window->focus_widget)
346     {
347       GList *children, *tmp_list;
348       
349       do 
350         {
351           g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
352         }
353       while (GTK_IS_LABEL (window->focus_widget));
354
355       tmp_list = children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
356
357       while (tmp_list)
358         {
359           GtkWidget *child = tmp_list->data;
360
361           if (window->focus_widget == NULL ||
362               (child == window->focus_widget && 
363                child != window->default_widget && 
364                window->default_widget))
365             {
366               gtk_widget_grab_focus (window->default_widget);
367               break;
368             }
369           
370           tmp_list = tmp_list->next;
371         }
372
373       g_list_free (children);
374     }
375 }
376
377 static void
378 gtk_dialog_style_set (GtkWidget *widget,
379                       GtkStyle  *prev_style)
380 {
381   update_spacings (GTK_DIALOG (widget));
382 }
383
384 static GtkWidget *
385 dialog_find_button (GtkDialog *dialog,
386                     gint       response_id)
387 {
388   GList *children, *tmp_list;
389   GtkWidget *child = NULL;
390       
391   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
392
393   for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
394     {
395       ResponseData *rd = get_response_data (tmp_list->data, FALSE);
396       
397       if (rd && rd->response_id == response_id)
398         {
399           child = tmp_list->data;
400           break;
401         }
402     }
403
404   g_list_free (children);
405
406   return child;
407 }
408
409 static void
410 gtk_dialog_close (GtkDialog *dialog)
411 {
412   /* Synthesize delete_event to close dialog. */
413   
414   GtkWidget *widget = GTK_WIDGET (dialog);
415   GdkEvent *event;
416
417   if (!dialog_find_button (dialog, GTK_RESPONSE_CANCEL))
418     return;
419
420   event = gdk_event_new (GDK_DELETE);
421   
422   event->any.window = g_object_ref (widget->window);
423   event->any.send_event = TRUE;
424   
425   gtk_main_do_event (event);
426   gdk_event_free (event);
427 }
428
429 GtkWidget*
430 gtk_dialog_new (void)
431 {
432   return g_object_new (GTK_TYPE_DIALOG, NULL);
433 }
434
435 static GtkWidget*
436 gtk_dialog_new_empty (const gchar     *title,
437                       GtkWindow       *parent,
438                       GtkDialogFlags   flags)
439 {
440   GtkDialog *dialog;
441
442   dialog = g_object_new (GTK_TYPE_DIALOG, NULL);
443
444   if (title)
445     gtk_window_set_title (GTK_WINDOW (dialog), title);
446
447   if (parent)
448     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
449
450   if (flags & GTK_DIALOG_MODAL)
451     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
452   
453   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
454     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
455
456   if (flags & GTK_DIALOG_NO_SEPARATOR)
457     gtk_dialog_set_has_separator (dialog, FALSE);
458   
459   return GTK_WIDGET (dialog);
460 }
461
462 /**
463  * gtk_dialog_new_with_buttons:
464  * @title: Title of the dialog, or %NULL
465  * @parent: Transient parent of the dialog, or %NULL
466  * @flags: from #GtkDialogFlags
467  * @first_button_text: 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 "response" signal with the corresponding
482  * response ID. If a #GtkDialog receives the "delete_event" signal, it
483  * 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
486  * the #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  * <informalexample><programlisting>
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  * </programlisting></informalexample>
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 ResponseData*
527 get_response_data (GtkWidget *widget,
528                    gboolean   create)
529 {
530   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
531                                         "gtk-dialog-response-data");
532
533   if (ad == NULL && create)
534     {
535       ad = g_new (ResponseData, 1);
536       
537       g_object_set_data_full (G_OBJECT (widget),
538                               "gtk-dialog-response-data",
539                               ad,
540                               g_free);
541     }
542
543   return ad;
544 }
545
546 static void
547 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
548 {
549   ResponseData *ad;
550   gint response_id;
551   
552   g_return_if_fail (GTK_IS_DIALOG (dialog));
553
554   response_id = GTK_RESPONSE_NONE;
555   
556   ad = get_response_data (widget, TRUE);
557
558   g_assert (ad != NULL);
559   
560   response_id = ad->response_id;
561
562   gtk_dialog_response (dialog, response_id);
563 }
564
565 /**
566  * gtk_dialog_add_action_widget:
567  * @dialog: a #GtkDialog
568  * @child: an activatable widget
569  * @response_id: response ID for @child
570  * 
571  * Adds an activatable widget to the action area of a #GtkDialog,
572  * connecting a signal handler that will emit the "response" signal on
573  * the dialog when the widget is activated.  The widget is appended to
574  * the end of the dialog's action area.  If you want to add a
575  * non-activatable widget, simply pack it into the
576  * <literal>action_area</literal> field of the #GtkDialog struct.
577  **/
578 void
579 gtk_dialog_add_action_widget (GtkDialog *dialog,
580                               GtkWidget *child,
581                               gint       response_id)
582 {
583   ResponseData *ad;
584   gint signal_id = 0;
585   
586   g_return_if_fail (GTK_IS_DIALOG (dialog));
587   g_return_if_fail (GTK_IS_WIDGET (child));
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 != 0;
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 (dialog->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 (dialog->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  * "response" signal with the given @response_id. The button is appended to the
630  * end of the dialog's action area. The button widget is returned, but usually
631  * you don't need it.
632  *
633  * Return value: 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_FLAGS (button, GTK_CAN_DEFAULT);
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   GList *children;
728   GList *tmp_list;
729
730   g_return_if_fail (GTK_IS_DIALOG (dialog));
731
732   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
733
734   tmp_list = children;
735   while (tmp_list != NULL)
736     {
737       GtkWidget *widget = tmp_list->data;
738       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
739                                             "gtk-dialog-response-data");
740
741       if (rd && rd->response_id == response_id)
742         gtk_widget_set_sensitive (widget, setting);
743
744       tmp_list = g_list_next (tmp_list);
745     }
746
747   g_list_free (children);
748 }
749
750 /**
751  * gtk_dialog_set_default_response:
752  * @dialog: a #GtkDialog
753  * @response_id: a response ID
754  * 
755  * Sets the last widget in the dialog's action area with the given @response_id
756  * as the default widget for the dialog. Pressing "Enter" normally activates
757  * the default widget.
758  **/
759 void
760 gtk_dialog_set_default_response (GtkDialog *dialog,
761                                  gint       response_id)
762 {
763   GList *children;
764   GList *tmp_list;
765
766   g_return_if_fail (GTK_IS_DIALOG (dialog));
767
768   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
769
770   tmp_list = children;
771   while (tmp_list != NULL)
772     {
773       GtkWidget *widget = tmp_list->data;
774       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
775                                             "gtk-dialog-response-data");
776
777       if (rd && rd->response_id == response_id)
778         gtk_widget_grab_default (widget);
779             
780       tmp_list = g_list_next (tmp_list);
781     }
782
783   g_list_free (children);
784 }
785
786 /**
787  * gtk_dialog_set_has_separator:
788  * @dialog: a #GtkDialog
789  * @setting: %TRUE to have a separator
790  *
791  * Sets whether the dialog has a separator above the buttons.
792  * %TRUE by default.
793  **/
794 void
795 gtk_dialog_set_has_separator (GtkDialog *dialog,
796                               gboolean   setting)
797 {
798   GtkDialogPrivate *priv;
799
800   g_return_if_fail (GTK_IS_DIALOG (dialog));
801
802   priv = GET_PRIVATE (dialog);
803
804   /* this might fail if we get called before _init() somehow */
805   g_assert (dialog->vbox != NULL);
806
807   if (priv->ignore_separator)
808     {
809       g_warning ("Ignoring the separator setting");
810       return;
811     }
812   
813   if (setting && dialog->separator == NULL)
814     {
815       dialog->separator = gtk_hseparator_new ();
816       gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0);
817
818       /* The app programmer could screw this up, but, their own fault.
819        * Moves the separator just above the action area.
820        */
821       gtk_box_reorder_child (GTK_BOX (dialog->vbox), dialog->separator, 1);
822       gtk_widget_show (dialog->separator);
823     }
824   else if (!setting && dialog->separator != NULL)
825     {
826       gtk_widget_destroy (dialog->separator);
827       dialog->separator = NULL;
828     }
829
830   g_object_notify (G_OBJECT (dialog), "has_separator");
831 }
832
833 /**
834  * gtk_dialog_get_has_separator:
835  * @dialog: a #GtkDialog
836  * 
837  * Accessor for whether the dialog has a separator.
838  * 
839  * Return value: %TRUE if the dialog has a separator
840  **/
841 gboolean
842 gtk_dialog_get_has_separator (GtkDialog *dialog)
843 {
844   g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE);
845
846   return dialog->separator != NULL;
847 }
848
849 /**
850  * gtk_dialog_response:
851  * @dialog: a #GtkDialog
852  * @response_id: response ID 
853  * 
854  * Emits the "response" signal with the given response ID. Used to
855  * indicate that the user has responded to the dialog in some way;
856  * typically either you or gtk_dialog_run() will be monitoring the
857  * "response" signal and take appropriate action.
858  **/
859 void
860 gtk_dialog_response (GtkDialog *dialog,
861                      gint       response_id)
862 {
863   g_return_if_fail (GTK_IS_DIALOG (dialog));
864
865   g_signal_emit (dialog,
866                  dialog_signals[RESPONSE],
867                  0,
868                  response_id);
869 }
870
871 typedef struct
872 {
873   GtkDialog *dialog;
874   gint response_id;
875   GMainLoop *loop;
876   gboolean destroyed;
877 } RunInfo;
878
879 static void
880 shutdown_loop (RunInfo *ri)
881 {
882   if (g_main_loop_is_running (ri->loop))
883     g_main_loop_quit (ri->loop);
884 }
885
886 static void
887 run_unmap_handler (GtkDialog *dialog, gpointer data)
888 {
889   RunInfo *ri = data;
890
891   shutdown_loop (ri);
892 }
893
894 static void
895 run_response_handler (GtkDialog *dialog,
896                       gint response_id,
897                       gpointer data)
898 {
899   RunInfo *ri;
900
901   ri = data;
902
903   ri->response_id = response_id;
904
905   shutdown_loop (ri);
906 }
907
908 static gint
909 run_delete_handler (GtkDialog *dialog,
910                     GdkEventAny *event,
911                     gpointer data)
912 {
913   RunInfo *ri = data;
914     
915   shutdown_loop (ri);
916   
917   return TRUE; /* Do not destroy */
918 }
919
920 static void
921 run_destroy_handler (GtkDialog *dialog, gpointer data)
922 {
923   RunInfo *ri = data;
924
925   /* shutdown_loop will be called by run_unmap_handler */
926   
927   ri->destroyed = TRUE;
928 }
929
930 /**
931  * gtk_dialog_run:
932  * @dialog: a #GtkDialog
933  * 
934  * Blocks in a recursive main loop until the @dialog either emits the
935  * response signal, or is destroyed. If the dialog is destroyed during the call
936  * to gtk_dialog_run(), gtk_dialog_returns #GTK_RESPONSE_NONE.
937  * Otherwise, it returns the response ID from the "response" signal emission.
938  * Before entering the recursive main loop, gtk_dialog_run() calls
939  * gtk_widget_show() on the dialog for you. Note that you still
940  * need to show any children of the dialog yourself.
941  *
942  * During gtk_dialog_run(), the default behavior of "delete_event" is
943  * disabled; if the dialog receives "delete_event", it will not be
944  * destroyed as windows usually are, and gtk_dialog_run() will return
945  * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
946  * modal. You can force gtk_dialog_run() to return at any time by
947  * calling gtk_dialog_response() to emit the "response"
948  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
949  * idea, because your post-run code won't know whether the dialog was
950  * destroyed or not.
951  *
952  * After gtk_dialog_run() returns, you are responsible for hiding or
953  * destroying the dialog if you wish to do so.
954  *
955  * Typical usage of this function might be:
956  * <informalexample><programlisting>
957  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
958  *   switch (result)
959  *     {
960  *       case GTK_RESPONSE_ACCEPT:
961  *          do_application_specific_something (<!-- -->);
962  *          break;
963  *       default:
964  *          do_nothing_since_dialog_was_cancelled (<!-- -->);
965  *          break;
966  *     }
967  *   gtk_widget_destroy (dialog);
968  * </programlisting></informalexample>
969  * 
970  * Note that even though the recursive main loop gives the effect of a
971  * modal dialog (it prevents the user from interacting with other 
972  * windows while the dialog is run), callbacks such as timeouts, 
973  * IO channel watches, DND drops, etc, <emphasis>will</emphasis> be 
974  * triggered during a gtk_dialog_run() call.
975  * 
976  * Return value: response ID
977  **/
978 gint
979 gtk_dialog_run (GtkDialog *dialog)
980 {
981   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
982   gboolean was_modal;
983   gulong response_handler;
984   gulong unmap_handler;
985   gulong destroy_handler;
986   gulong delete_handler;
987   
988   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
989
990   g_object_ref (dialog);
991
992   was_modal = GTK_WINDOW (dialog)->modal;
993   if (!was_modal)
994     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
995
996   if (!GTK_WIDGET_VISIBLE (dialog))
997     gtk_widget_show (GTK_WIDGET (dialog));
998   
999   response_handler =
1000     g_signal_connect (dialog,
1001                       "response",
1002                       G_CALLBACK (run_response_handler),
1003                       &ri);
1004   
1005   unmap_handler =
1006     g_signal_connect (dialog,
1007                       "unmap",
1008                       G_CALLBACK (run_unmap_handler),
1009                       &ri);
1010   
1011   delete_handler =
1012     g_signal_connect (dialog,
1013                       "delete_event",
1014                       G_CALLBACK (run_delete_handler),
1015                       &ri);
1016   
1017   destroy_handler =
1018     g_signal_connect (dialog,
1019                       "destroy",
1020                       G_CALLBACK (run_destroy_handler),
1021                       &ri);
1022   
1023   ri.loop = g_main_loop_new (NULL, FALSE);
1024
1025   GDK_THREADS_LEAVE ();  
1026   g_main_loop_run (ri.loop);
1027   GDK_THREADS_ENTER ();  
1028
1029   g_main_loop_unref (ri.loop);
1030
1031   ri.loop = NULL;
1032   ri.destroyed = FALSE;
1033   
1034   if (!ri.destroyed)
1035     {
1036       if (!was_modal)
1037         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1038       
1039       g_signal_handler_disconnect (dialog, response_handler);
1040       g_signal_handler_disconnect (dialog, unmap_handler);
1041       g_signal_handler_disconnect (dialog, delete_handler);
1042       g_signal_handler_disconnect (dialog, destroy_handler);
1043     }
1044
1045   g_object_unref (dialog);
1046
1047   return ri.response_id;
1048 }
1049
1050 void
1051 _gtk_dialog_set_ignore_separator (GtkDialog *dialog,
1052                                   gboolean   ignore_separator)
1053 {
1054   GtkDialogPrivate *priv;
1055
1056   priv = GET_PRIVATE (dialog);
1057   priv->ignore_separator = ignore_separator;
1058 }
1059
1060 gint
1061 _gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1062                                      GtkWidget *widget)
1063 {
1064   ResponseData *rd;
1065
1066   rd = get_response_data (widget, FALSE);
1067   if (!rd)
1068     return GTK_RESPONSE_NONE;
1069   else
1070     return rd->response_id;
1071 }
1072
1073 /**
1074  * gtk_alternative_dialog_button_order:
1075  * @screen: a #GdkScreen, or %NULL to use the default screen
1076  *
1077  * Returns %TRUE if dialogs are expected to use an alternative
1078  * button order on the screen @screen. See 
1079  * gtk_dialog_set_alternative_button_order() for more details
1080  * about alternative button order. 
1081  *
1082  * If you need to use this function, you should probably connect
1083  * to the ::notify:gtk-alternative-button-order signal on the
1084  * #GtkSettings object associated to @screen, in order to be 
1085  * notified if the button order setting changes.
1086  *
1087  * Returns: Whether the alternative button order should be used
1088  *
1089  * Since: 2.6
1090  */
1091 gboolean 
1092 gtk_alternative_dialog_button_order (GdkScreen *screen)
1093 {
1094   GtkSettings *settings;
1095   gboolean result;
1096
1097   if (screen)
1098     settings = gtk_settings_get_for_screen (screen);
1099   else
1100     settings = gtk_settings_get_default ();
1101   
1102   g_object_get (settings,
1103                 "gtk-alternative-button-order", &result, NULL);
1104
1105   return result;
1106 }
1107
1108 static void
1109 gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
1110                                                 gint       first_response_id,
1111                                                 va_list    args)
1112 {
1113   GtkWidget *child;
1114   gint response_id;
1115   gint position;
1116
1117   response_id = first_response_id;
1118   position = 0;
1119   while (response_id != -1)
1120     {
1121       /* reorder child with response_id to position */
1122       child = dialog_find_button (dialog, response_id);
1123       gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
1124
1125       response_id = va_arg (args, gint);
1126       position++;
1127     }
1128 }
1129
1130 /**
1131  * gtk_dialog_set_alternative_button_order:
1132  * @dialog: a #GtkDialog
1133  * @first_response_id: a response id used by one @dialog's buttons
1134  * @Varargs: a list of more response ids of @dialog's buttons, terminated by -1
1135  *
1136  * Sets an alternative button order. If the gtk-alternative-button-order 
1137  * setting is set to %TRUE, the dialog buttons are reordered according to 
1138  * the order of the response ids passed to this function.
1139  *
1140  * By default, GTK+ dialogs use the button order advocated by the Gnome 
1141  * <ulink url="http://developer.gnome.org/projects/gup/hig/2.0/">Human 
1142  * Interface Guidelines</ulink> with the affirmative button at the far 
1143  * right, and the cancel button left of it. But the builtin GTK+ dialogs
1144  * and #GtkMessageDialog<!-- -->s do provide an alternative button order,
1145  * which is more suitable on some platforms, e.g. Windows.
1146  *
1147  * Use this function after adding all the buttons to your dialog, as the 
1148  * following example shows:
1149  * <informalexample><programlisting>
1150  * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1151  *                                        GTK_STOCK_CANCEL,
1152  *                                        GTK_RESPONSE_CANCEL);
1153  *  
1154  * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1155  *                                    GTK_STOCK_OK,
1156  *                                    GTK_RESPONSE_OK);
1157  *   
1158  * gtk_widget_grab_default (ok_button);
1159  *   
1160  * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1161  *                                      GTK_STOCK_HELP,
1162  *                                      GTK_RESPONSE_HELP);
1163  *  
1164  * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1165  *                                          GTK_RESPONSE_OK,
1166  *                                          GTK_RESPONSE_CANCEL,
1167  *                                          GTK_RESPONSE_HELP,
1168  *                                          -1);
1169  * </programlisting></informalexample>
1170  * 
1171  * Since: 2.6
1172  */
1173 void 
1174 gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
1175                                          gint       first_response_id,
1176                                          ...)
1177 {
1178   GdkScreen *screen;
1179   va_list args;
1180   
1181   g_return_if_fail (GTK_IS_DIALOG (dialog));
1182
1183   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1184   if (!gtk_alternative_dialog_button_order (screen))
1185       return;
1186
1187   va_start (args, first_response_id);
1188
1189   gtk_dialog_set_alternative_button_order_valist (dialog,
1190                                                   first_response_id,
1191                                                   args);
1192   va_end (args);
1193 }
1194 /**
1195  * gtk_dialog_set_alternative_button_order_from_array:
1196  * @dialog: a #GtkDialog
1197  * @n_params: the number of response ids in @new_order
1198  * @new_order: an array of response ids of @dialog's buttons
1199  *
1200  * Sets an alternative button order. If the gtk-alternative-button-order 
1201  * setting is set to %TRUE, the dialog buttons are reordered according to 
1202  * the order of the response ids in @new_order.
1203  *
1204  * See gtk_dialog_set_alternative_button_order() for more information.
1205  *
1206  * This function is for use by language bindings.
1207  * 
1208  * Since: 2.6
1209  */
1210 void 
1211 gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
1212                                                     gint       n_params,
1213                                                     gint      *new_order)
1214 {
1215   GdkScreen *screen;
1216   GtkWidget *child;
1217   gint position;
1218
1219   g_return_if_fail (GTK_IS_DIALOG (dialog));
1220   g_return_if_fail (new_order != NULL);
1221
1222   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1223   if (!gtk_alternative_dialog_button_order (screen))
1224       return;
1225
1226   for (position = 0; position < n_params; position++)
1227   {
1228       /* reorder child with response_id to position */
1229       child = dialog_find_button (dialog, new_order[position]);
1230       gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
1231     }
1232 }
1233