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