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