]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
Don't try to focus the default_widget if there is none. (#169881, Thomas
[~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       GtkWidget *first_focus = NULL;
349       
350       do 
351         {
352           g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
353
354           if (first_focus == NULL)
355             first_focus = window->focus_widget;
356           else if (first_focus == window->focus_widget)
357             break;
358         }
359       while (GTK_IS_LABEL (window->focus_widget));
360
361       tmp_list = children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
362       
363       while (tmp_list)
364         {
365           GtkWidget *child = tmp_list->data;
366           
367           if ((window->focus_widget == NULL || 
368                child == window->focus_widget) && 
369               child != window->default_widget &&
370               window->default_widget)
371             {
372               gtk_widget_grab_focus (window->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_set (GtkWidget *widget,
385                       GtkStyle  *prev_style)
386 {
387   update_spacings (GTK_DIALOG (widget));
388 }
389
390 static GtkWidget *
391 dialog_find_button (GtkDialog *dialog,
392                     gint       response_id)
393 {
394   GList *children, *tmp_list;
395   GtkWidget *child = NULL;
396       
397   children = gtk_container_get_children (GTK_CONTAINER (dialog->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   if (!dialog_find_button (dialog, GTK_RESPONSE_CANCEL))
424     return;
425
426   event = gdk_event_new (GDK_DELETE);
427   
428   event->any.window = g_object_ref (widget->window);
429   event->any.send_event = TRUE;
430   
431   gtk_main_do_event (event);
432   gdk_event_free (event);
433 }
434
435 GtkWidget*
436 gtk_dialog_new (void)
437 {
438   return g_object_new (GTK_TYPE_DIALOG, NULL);
439 }
440
441 static GtkWidget*
442 gtk_dialog_new_empty (const gchar     *title,
443                       GtkWindow       *parent,
444                       GtkDialogFlags   flags)
445 {
446   GtkDialog *dialog;
447
448   dialog = g_object_new (GTK_TYPE_DIALOG, NULL);
449
450   if (title)
451     gtk_window_set_title (GTK_WINDOW (dialog), title);
452
453   if (parent)
454     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
455
456   if (flags & GTK_DIALOG_MODAL)
457     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
458   
459   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
460     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
461
462   if (flags & GTK_DIALOG_NO_SEPARATOR)
463     gtk_dialog_set_has_separator (dialog, FALSE);
464   
465   return GTK_WIDGET (dialog);
466 }
467
468 /**
469  * gtk_dialog_new_with_buttons:
470  * @title: Title of the dialog, or %NULL
471  * @parent: Transient parent of the dialog, or %NULL
472  * @flags: from #GtkDialogFlags
473  * @first_button_text: stock ID or text to go in first button, or %NULL
474  * @Varargs: response ID for first button, then additional buttons, ending with %NULL
475  * 
476  * Creates a new #GtkDialog with title @title (or %NULL for the default
477  * title; see gtk_window_set_title()) and transient parent @parent (or
478  * %NULL for none; see gtk_window_set_transient_for()). The @flags
479  * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
480  * and/or to have it destroyed along with its transient parent
481  * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
482  * text/response ID pairs should be listed, with a %NULL pointer ending
483  * the list. Button text can be either a stock ID such as
484  * #GTK_STOCK_OK, or some arbitrary text.  A response ID can be
485  * any positive number, or one of the values in the #GtkResponseType
486  * enumeration. If the user clicks one of these dialog buttons,
487  * #GtkDialog will emit the "response" signal with the corresponding
488  * response ID. If a #GtkDialog receives the "delete_event" signal, it
489  * will emit "response" with a response ID of #GTK_RESPONSE_DELETE_EVENT.
490  * However, destroying a dialog does not emit the "response" signal;
491  * so be careful relying on "response" when using
492  * the #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
493  * so the first button in the list will be the leftmost button in the dialog.
494  *
495  * Here's a simple example:
496  * <informalexample><programlisting>
497  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
498  *                                                   main_app_window,
499  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
500  *                                                   GTK_STOCK_OK,
501  *                                                   GTK_RESPONSE_ACCEPT,
502  *                                                   GTK_STOCK_CANCEL,
503  *                                                   GTK_RESPONSE_REJECT,
504  *                                                   NULL);
505  * </programlisting></informalexample>
506  * 
507  * Return value: a new #GtkDialog
508  **/
509 GtkWidget*
510 gtk_dialog_new_with_buttons (const gchar    *title,
511                              GtkWindow      *parent,
512                              GtkDialogFlags  flags,
513                              const gchar    *first_button_text,
514                              ...)
515 {
516   GtkDialog *dialog;
517   va_list args;
518   
519   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
520
521   va_start (args, first_button_text);
522
523   gtk_dialog_add_buttons_valist (dialog,
524                                  first_button_text,
525                                  args);
526   
527   va_end (args);
528
529   return GTK_WIDGET (dialog);
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_new (ResponseData, 1);
542       
543       g_object_set_data_full (G_OBJECT (widget),
544                               "gtk-dialog-response-data",
545                               ad,
546                               g_free);
547     }
548
549   return ad;
550 }
551
552 static void
553 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
554 {
555   ResponseData *ad;
556   gint response_id;
557   
558   g_return_if_fail (GTK_IS_DIALOG (dialog));
559
560   response_id = GTK_RESPONSE_NONE;
561   
562   ad = get_response_data (widget, TRUE);
563
564   g_assert (ad != NULL);
565   
566   response_id = ad->response_id;
567
568   gtk_dialog_response (dialog, response_id);
569 }
570
571 /**
572  * gtk_dialog_add_action_widget:
573  * @dialog: a #GtkDialog
574  * @child: an activatable widget
575  * @response_id: response ID for @child
576  * 
577  * Adds an activatable widget to the action area of a #GtkDialog,
578  * connecting a signal handler that will emit the "response" signal on
579  * the dialog when the widget is activated.  The widget is appended to
580  * the end of the dialog's action area.  If you want to add a
581  * non-activatable widget, simply pack it into the
582  * <literal>action_area</literal> field of the #GtkDialog struct.
583  **/
584 void
585 gtk_dialog_add_action_widget (GtkDialog *dialog,
586                               GtkWidget *child,
587                               gint       response_id)
588 {
589   ResponseData *ad;
590   gint signal_id = 0;
591   
592   g_return_if_fail (GTK_IS_DIALOG (dialog));
593   g_return_if_fail (GTK_IS_WIDGET (child));
594
595   ad = get_response_data (child, TRUE);
596
597   ad->response_id = response_id;
598
599   if (GTK_IS_BUTTON (child))
600     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
601   else
602     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal != 0;
603
604   if (signal_id)
605     {
606       GClosure *closure;
607
608       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
609                                        G_OBJECT (dialog));
610       g_signal_connect_closure_by_id (child,
611                                       signal_id,
612                                       0,
613                                       closure,
614                                       FALSE);
615     }
616   else
617     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
618
619   gtk_box_pack_end (GTK_BOX (dialog->action_area),
620                     child,
621                     FALSE, TRUE, 0);
622   
623   if (response_id == GTK_RESPONSE_HELP)
624     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dialog->action_area), child, TRUE);
625 }
626
627 /**
628  * gtk_dialog_add_button:
629  * @dialog: a #GtkDialog
630  * @button_text: text of button, or stock ID
631  * @response_id: response ID for the button
632  * 
633  * Adds a button with the given text (or a stock button, if @button_text is a
634  * stock ID) and sets things up so that clicking the button will emit the
635  * "response" signal with the given @response_id. The button is appended to the
636  * end of the dialog's action area. The button widget is returned, but usually
637  * you don't need it.
638  *
639  * Return value: the button widget that was added
640  **/
641 GtkWidget*
642 gtk_dialog_add_button (GtkDialog   *dialog,
643                        const gchar *button_text,
644                        gint         response_id)
645 {
646   GtkWidget *button;
647   
648   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
649   g_return_val_if_fail (button_text != NULL, NULL);
650
651   button = gtk_button_new_from_stock (button_text);
652
653   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
654   
655   gtk_widget_show (button);
656   
657   gtk_dialog_add_action_widget (dialog,
658                                 button,
659                                 response_id);
660
661   return button;
662 }
663
664 static void
665 gtk_dialog_add_buttons_valist (GtkDialog      *dialog,
666                                const gchar    *first_button_text,
667                                va_list         args)
668 {
669   const gchar* text;
670   gint response_id;
671
672   g_return_if_fail (GTK_IS_DIALOG (dialog));
673   
674   if (first_button_text == NULL)
675     return;
676   
677   text = first_button_text;
678   response_id = va_arg (args, gint);
679
680   while (text != NULL)
681     {
682       gtk_dialog_add_button (dialog, text, response_id);
683
684       text = va_arg (args, gchar*);
685       if (text == NULL)
686         break;
687       response_id = va_arg (args, int);
688     }
689 }
690
691 /**
692  * gtk_dialog_add_buttons:
693  * @dialog: a #GtkDialog
694  * @first_button_text: button text or stock ID
695  * @Varargs: response ID for first button, then more text-response_id pairs
696  * 
697  * Adds more buttons, same as calling gtk_dialog_add_button()
698  * repeatedly.  The variable argument list should be %NULL-terminated
699  * as with gtk_dialog_new_with_buttons(). Each button must have both
700  * text and response ID.
701  **/
702 void
703 gtk_dialog_add_buttons (GtkDialog   *dialog,
704                         const gchar *first_button_text,
705                         ...)
706 {  
707   va_list args;
708
709   va_start (args, first_button_text);
710
711   gtk_dialog_add_buttons_valist (dialog,
712                                  first_button_text,
713                                  args);
714   
715   va_end (args);
716 }
717
718 /**
719  * gtk_dialog_set_response_sensitive:
720  * @dialog: a #GtkDialog
721  * @response_id: a response ID
722  * @setting: %TRUE for sensitive
723  *
724  * Calls <literal>gtk_widget_set_sensitive (widget, @setting)</literal> 
725  * for each widget in the dialog's action area with the given @response_id.
726  * A convenient way to sensitize/desensitize dialog buttons.
727  **/
728 void
729 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
730                                    gint       response_id,
731                                    gboolean   setting)
732 {
733   GList *children;
734   GList *tmp_list;
735
736   g_return_if_fail (GTK_IS_DIALOG (dialog));
737
738   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
739
740   tmp_list = children;
741   while (tmp_list != NULL)
742     {
743       GtkWidget *widget = tmp_list->data;
744       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
745                                             "gtk-dialog-response-data");
746
747       if (rd && rd->response_id == response_id)
748         gtk_widget_set_sensitive (widget, setting);
749
750       tmp_list = g_list_next (tmp_list);
751     }
752
753   g_list_free (children);
754 }
755
756 /**
757  * gtk_dialog_set_default_response:
758  * @dialog: a #GtkDialog
759  * @response_id: a response ID
760  * 
761  * Sets the last widget in the dialog's action area with the given @response_id
762  * as the default widget for the dialog. Pressing "Enter" normally activates
763  * the default widget.
764  **/
765 void
766 gtk_dialog_set_default_response (GtkDialog *dialog,
767                                  gint       response_id)
768 {
769   GList *children;
770   GList *tmp_list;
771
772   g_return_if_fail (GTK_IS_DIALOG (dialog));
773
774   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
775
776   tmp_list = children;
777   while (tmp_list != NULL)
778     {
779       GtkWidget *widget = tmp_list->data;
780       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
781                                             "gtk-dialog-response-data");
782
783       if (rd && rd->response_id == response_id)
784         gtk_widget_grab_default (widget);
785             
786       tmp_list = g_list_next (tmp_list);
787     }
788
789   g_list_free (children);
790 }
791
792 /**
793  * gtk_dialog_set_has_separator:
794  * @dialog: a #GtkDialog
795  * @setting: %TRUE to have a separator
796  *
797  * Sets whether the dialog has a separator above the buttons.
798  * %TRUE by default.
799  **/
800 void
801 gtk_dialog_set_has_separator (GtkDialog *dialog,
802                               gboolean   setting)
803 {
804   GtkDialogPrivate *priv;
805
806   g_return_if_fail (GTK_IS_DIALOG (dialog));
807
808   priv = GET_PRIVATE (dialog);
809
810   /* this might fail if we get called before _init() somehow */
811   g_assert (dialog->vbox != NULL);
812
813   if (priv->ignore_separator)
814     {
815       g_warning ("Ignoring the separator setting");
816       return;
817     }
818   
819   if (setting && dialog->separator == NULL)
820     {
821       dialog->separator = gtk_hseparator_new ();
822       gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0);
823
824       /* The app programmer could screw this up, but, their own fault.
825        * Moves the separator just above the action area.
826        */
827       gtk_box_reorder_child (GTK_BOX (dialog->vbox), dialog->separator, 1);
828       gtk_widget_show (dialog->separator);
829     }
830   else if (!setting && dialog->separator != NULL)
831     {
832       gtk_widget_destroy (dialog->separator);
833       dialog->separator = NULL;
834     }
835
836   g_object_notify (G_OBJECT (dialog), "has_separator");
837 }
838
839 /**
840  * gtk_dialog_get_has_separator:
841  * @dialog: a #GtkDialog
842  * 
843  * Accessor for whether the dialog has a separator.
844  * 
845  * Return value: %TRUE if the dialog has a separator
846  **/
847 gboolean
848 gtk_dialog_get_has_separator (GtkDialog *dialog)
849 {
850   g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE);
851
852   return dialog->separator != NULL;
853 }
854
855 /**
856  * gtk_dialog_response:
857  * @dialog: a #GtkDialog
858  * @response_id: response ID 
859  * 
860  * Emits the "response" signal with the given response ID. Used to
861  * indicate that the user has responded to the dialog in some way;
862  * typically either you or gtk_dialog_run() will be monitoring the
863  * "response" signal and take appropriate action.
864  **/
865 void
866 gtk_dialog_response (GtkDialog *dialog,
867                      gint       response_id)
868 {
869   g_return_if_fail (GTK_IS_DIALOG (dialog));
870
871   g_signal_emit (dialog,
872                  dialog_signals[RESPONSE],
873                  0,
874                  response_id);
875 }
876
877 typedef struct
878 {
879   GtkDialog *dialog;
880   gint response_id;
881   GMainLoop *loop;
882   gboolean destroyed;
883 } RunInfo;
884
885 static void
886 shutdown_loop (RunInfo *ri)
887 {
888   if (g_main_loop_is_running (ri->loop))
889     g_main_loop_quit (ri->loop);
890 }
891
892 static void
893 run_unmap_handler (GtkDialog *dialog, gpointer data)
894 {
895   RunInfo *ri = data;
896
897   shutdown_loop (ri);
898 }
899
900 static void
901 run_response_handler (GtkDialog *dialog,
902                       gint response_id,
903                       gpointer data)
904 {
905   RunInfo *ri;
906
907   ri = data;
908
909   ri->response_id = response_id;
910
911   shutdown_loop (ri);
912 }
913
914 static gint
915 run_delete_handler (GtkDialog *dialog,
916                     GdkEventAny *event,
917                     gpointer data)
918 {
919   RunInfo *ri = data;
920     
921   shutdown_loop (ri);
922   
923   return TRUE; /* Do not destroy */
924 }
925
926 static void
927 run_destroy_handler (GtkDialog *dialog, gpointer data)
928 {
929   RunInfo *ri = data;
930
931   /* shutdown_loop will be called by run_unmap_handler */
932   
933   ri->destroyed = TRUE;
934 }
935
936 /**
937  * gtk_dialog_run:
938  * @dialog: a #GtkDialog
939  * 
940  * Blocks in a recursive main loop until the @dialog either emits the
941  * response signal, or is destroyed. If the dialog is destroyed during the call
942  * to gtk_dialog_run(), gtk_dialog_returns #GTK_RESPONSE_NONE.
943  * Otherwise, it returns the response ID from the "response" signal emission.
944  * Before entering the recursive main loop, gtk_dialog_run() calls
945  * gtk_widget_show() on the dialog for you. Note that you still
946  * need to show any children of the dialog yourself.
947  *
948  * During gtk_dialog_run(), the default behavior of "delete_event" is
949  * disabled; if the dialog receives "delete_event", it will not be
950  * destroyed as windows usually are, and gtk_dialog_run() will return
951  * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
952  * modal. You can force gtk_dialog_run() to return at any time by
953  * calling gtk_dialog_response() to emit the "response"
954  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
955  * idea, because your post-run code won't know whether the dialog was
956  * destroyed or not.
957  *
958  * After gtk_dialog_run() returns, you are responsible for hiding or
959  * destroying the dialog if you wish to do so.
960  *
961  * Typical usage of this function might be:
962  * <informalexample><programlisting>
963  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
964  *   switch (result)
965  *     {
966  *       case GTK_RESPONSE_ACCEPT:
967  *          do_application_specific_something (<!-- -->);
968  *          break;
969  *       default:
970  *          do_nothing_since_dialog_was_cancelled (<!-- -->);
971  *          break;
972  *     }
973  *   gtk_widget_destroy (dialog);
974  * </programlisting></informalexample>
975  * 
976  * Note that even though the recursive main loop gives the effect of a
977  * modal dialog (it prevents the user from interacting with other 
978  * windows while the dialog is run), callbacks such as timeouts, 
979  * IO channel watches, DND drops, etc, <emphasis>will</emphasis> be 
980  * triggered during a gtk_dialog_run() call.
981  * 
982  * Return value: response ID
983  **/
984 gint
985 gtk_dialog_run (GtkDialog *dialog)
986 {
987   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
988   gboolean was_modal;
989   gulong response_handler;
990   gulong unmap_handler;
991   gulong destroy_handler;
992   gulong delete_handler;
993   
994   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
995
996   g_object_ref (dialog);
997
998   was_modal = GTK_WINDOW (dialog)->modal;
999   if (!was_modal)
1000     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1001
1002   if (!GTK_WIDGET_VISIBLE (dialog))
1003     gtk_widget_show (GTK_WIDGET (dialog));
1004   
1005   response_handler =
1006     g_signal_connect (dialog,
1007                       "response",
1008                       G_CALLBACK (run_response_handler),
1009                       &ri);
1010   
1011   unmap_handler =
1012     g_signal_connect (dialog,
1013                       "unmap",
1014                       G_CALLBACK (run_unmap_handler),
1015                       &ri);
1016   
1017   delete_handler =
1018     g_signal_connect (dialog,
1019                       "delete_event",
1020                       G_CALLBACK (run_delete_handler),
1021                       &ri);
1022   
1023   destroy_handler =
1024     g_signal_connect (dialog,
1025                       "destroy",
1026                       G_CALLBACK (run_destroy_handler),
1027                       &ri);
1028   
1029   ri.loop = g_main_loop_new (NULL, FALSE);
1030
1031   GDK_THREADS_LEAVE ();  
1032   g_main_loop_run (ri.loop);
1033   GDK_THREADS_ENTER ();  
1034
1035   g_main_loop_unref (ri.loop);
1036
1037   ri.loop = NULL;
1038   ri.destroyed = FALSE;
1039   
1040   if (!ri.destroyed)
1041     {
1042       if (!was_modal)
1043         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1044       
1045       g_signal_handler_disconnect (dialog, response_handler);
1046       g_signal_handler_disconnect (dialog, unmap_handler);
1047       g_signal_handler_disconnect (dialog, delete_handler);
1048       g_signal_handler_disconnect (dialog, destroy_handler);
1049     }
1050
1051   g_object_unref (dialog);
1052
1053   return ri.response_id;
1054 }
1055
1056 void
1057 _gtk_dialog_set_ignore_separator (GtkDialog *dialog,
1058                                   gboolean   ignore_separator)
1059 {
1060   GtkDialogPrivate *priv;
1061
1062   priv = GET_PRIVATE (dialog);
1063   priv->ignore_separator = ignore_separator;
1064 }
1065
1066 gint
1067 _gtk_dialog_get_response_for_widget (GtkDialog *dialog,
1068                                      GtkWidget *widget)
1069 {
1070   ResponseData *rd;
1071
1072   rd = get_response_data (widget, FALSE);
1073   if (!rd)
1074     return GTK_RESPONSE_NONE;
1075   else
1076     return rd->response_id;
1077 }
1078
1079 /**
1080  * gtk_alternative_dialog_button_order:
1081  * @screen: a #GdkScreen, or %NULL to use the default screen
1082  *
1083  * Returns %TRUE if dialogs are expected to use an alternative
1084  * button order on the screen @screen. See 
1085  * gtk_dialog_set_alternative_button_order() for more details
1086  * about alternative button order. 
1087  *
1088  * If you need to use this function, you should probably connect
1089  * to the ::notify:gtk-alternative-button-order signal on the
1090  * #GtkSettings object associated to @screen, in order to be 
1091  * notified if the button order setting changes.
1092  *
1093  * Returns: Whether the alternative button order should be used
1094  *
1095  * Since: 2.6
1096  */
1097 gboolean 
1098 gtk_alternative_dialog_button_order (GdkScreen *screen)
1099 {
1100   GtkSettings *settings;
1101   gboolean result;
1102
1103   if (screen)
1104     settings = gtk_settings_get_for_screen (screen);
1105   else
1106     settings = gtk_settings_get_default ();
1107   
1108   g_object_get (settings,
1109                 "gtk-alternative-button-order", &result, NULL);
1110
1111   return result;
1112 }
1113
1114 static void
1115 gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
1116                                                 gint       first_response_id,
1117                                                 va_list    args)
1118 {
1119   GtkWidget *child;
1120   gint response_id;
1121   gint position;
1122
1123   response_id = first_response_id;
1124   position = 0;
1125   while (response_id != -1)
1126     {
1127       /* reorder child with response_id to position */
1128       child = dialog_find_button (dialog, response_id);
1129       gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
1130
1131       response_id = va_arg (args, gint);
1132       position++;
1133     }
1134 }
1135
1136 /**
1137  * gtk_dialog_set_alternative_button_order:
1138  * @dialog: a #GtkDialog
1139  * @first_response_id: a response id used by one @dialog's buttons
1140  * @Varargs: a list of more response ids of @dialog's buttons, terminated by -1
1141  *
1142  * Sets an alternative button order. If the gtk-alternative-button-order 
1143  * setting is set to %TRUE, the dialog buttons are reordered according to 
1144  * the order of the response ids passed to this function.
1145  *
1146  * By default, GTK+ dialogs use the button order advocated by the Gnome 
1147  * <ulink url="http://developer.gnome.org/projects/gup/hig/2.0/">Human 
1148  * Interface Guidelines</ulink> with the affirmative button at the far 
1149  * right, and the cancel button left of it. But the builtin GTK+ dialogs
1150  * and #GtkMessageDialog<!-- -->s do provide an alternative button order,
1151  * which is more suitable on some platforms, e.g. Windows.
1152  *
1153  * Use this function after adding all the buttons to your dialog, as the 
1154  * following example shows:
1155  * <informalexample><programlisting>
1156  * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1157  *                                        GTK_STOCK_CANCEL,
1158  *                                        GTK_RESPONSE_CANCEL);
1159  *  
1160  * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1161  *                                    GTK_STOCK_OK,
1162  *                                    GTK_RESPONSE_OK);
1163  *   
1164  * gtk_widget_grab_default (ok_button);
1165  *   
1166  * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1167  *                                      GTK_STOCK_HELP,
1168  *                                      GTK_RESPONSE_HELP);
1169  *  
1170  * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1171  *                                          GTK_RESPONSE_OK,
1172  *                                          GTK_RESPONSE_CANCEL,
1173  *                                          GTK_RESPONSE_HELP,
1174  *                                          -1);
1175  * </programlisting></informalexample>
1176  * 
1177  * Since: 2.6
1178  */
1179 void 
1180 gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
1181                                          gint       first_response_id,
1182                                          ...)
1183 {
1184   GdkScreen *screen;
1185   va_list args;
1186   
1187   g_return_if_fail (GTK_IS_DIALOG (dialog));
1188
1189   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1190   if (!gtk_alternative_dialog_button_order (screen))
1191       return;
1192
1193   va_start (args, first_response_id);
1194
1195   gtk_dialog_set_alternative_button_order_valist (dialog,
1196                                                   first_response_id,
1197                                                   args);
1198   va_end (args);
1199 }
1200 /**
1201  * gtk_dialog_set_alternative_button_order_from_array:
1202  * @dialog: a #GtkDialog
1203  * @n_params: the number of response ids in @new_order
1204  * @new_order: an array of response ids of @dialog's buttons
1205  *
1206  * Sets an alternative button order. If the gtk-alternative-button-order 
1207  * setting is set to %TRUE, the dialog buttons are reordered according to 
1208  * the order of the response ids in @new_order.
1209  *
1210  * See gtk_dialog_set_alternative_button_order() for more information.
1211  *
1212  * This function is for use by language bindings.
1213  * 
1214  * Since: 2.6
1215  */
1216 void 
1217 gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
1218                                                     gint       n_params,
1219                                                     gint      *new_order)
1220 {
1221   GdkScreen *screen;
1222   GtkWidget *child;
1223   gint position;
1224
1225   g_return_if_fail (GTK_IS_DIALOG (dialog));
1226   g_return_if_fail (new_order != NULL);
1227
1228   screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
1229   if (!gtk_alternative_dialog_button_order (screen))
1230       return;
1231
1232   for (position = 0; position < n_params; position++)
1233   {
1234       /* reorder child with response_id to position */
1235       child = dialog_find_button (dialog, new_order[position]);
1236       gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
1237     }
1238 }
1239