]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~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 "gtkhseparator.h"
32 #include "gtkmarshalers.h"
33 #include "gtkvbox.h"
34 #include "gdkkeysyms.h"
35 #include "gtkmain.h"
36 #include "gtkintl.h"
37 #include "gtkbindings.h"
38
39 #define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_DIALOG, GtkDialogPrivate))
40
41 typedef struct {
42   guint ignore_separator : 1;
43 } GtkDialogPrivate;
44
45 typedef struct _ResponseData ResponseData;
46
47 struct _ResponseData
48 {
49   gint response_id;
50 };
51
52 static void gtk_dialog_class_init (GtkDialogClass *klass);
53 static void gtk_dialog_init       (GtkDialog      *dialog);
54
55 static void gtk_dialog_add_buttons_valist (GtkDialog   *dialog,
56                                            const gchar *first_button_text,
57                                            va_list      args);
58
59 static gint gtk_dialog_delete_event_handler (GtkWidget   *widget,
60                                              GdkEventAny *event,
61                                              gpointer     user_data);
62
63 static void gtk_dialog_set_property      (GObject          *object,
64                                           guint             prop_id,
65                                           const GValue     *value,
66                                           GParamSpec       *pspec);
67 static void gtk_dialog_get_property      (GObject          *object,
68                                           guint             prop_id,
69                                           GValue           *value,
70                                           GParamSpec       *pspec);
71 static void gtk_dialog_style_set         (GtkWidget        *widget,
72                                           GtkStyle         *prev_style);
73 static void gtk_dialog_map               (GtkWidget        *widget);
74
75 static void gtk_dialog_close             (GtkDialog        *dialog);
76
77 static ResponseData* get_response_data   (GtkWidget        *widget);
78
79 enum {
80   PROP_0,
81   PROP_HAS_SEPARATOR
82 };
83
84 enum {
85   RESPONSE,
86   CLOSE,
87   LAST_SIGNAL
88 };
89
90 static gpointer parent_class;
91 static guint dialog_signals[LAST_SIGNAL];
92
93 GType
94 gtk_dialog_get_type (void)
95 {
96   static GType dialog_type = 0;
97
98   if (!dialog_type)
99     {
100       static const GTypeInfo dialog_info =
101       {
102         sizeof (GtkDialogClass),
103         NULL,           /* base_init */
104         NULL,           /* base_finalize */
105         (GClassInitFunc) gtk_dialog_class_init,
106         NULL,           /* class_finalize */
107         NULL,           /* class_data */
108         sizeof (GtkDialog),
109         0,              /* n_preallocs */
110         (GInstanceInitFunc) gtk_dialog_init,
111       };
112
113       dialog_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkDialog",
114                                             &dialog_info, 0);
115     }
116
117   return dialog_type;
118 }
119
120 static void
121 gtk_dialog_class_init (GtkDialogClass *class)
122 {
123   GObjectClass *gobject_class;
124   GtkWidgetClass *widget_class;
125   GtkBindingSet *binding_set;
126   
127   gobject_class = G_OBJECT_CLASS (class);
128   widget_class = GTK_WIDGET_CLASS (class);
129   
130   parent_class = g_type_class_peek_parent (class);
131
132   gobject_class->set_property = gtk_dialog_set_property;
133   gobject_class->get_property = gtk_dialog_get_property;
134   
135   widget_class->map = gtk_dialog_map;
136   widget_class->style_set = gtk_dialog_style_set;
137
138   class->close = gtk_dialog_close;
139   
140   g_type_class_add_private (gobject_class, sizeof (GtkDialogPrivate));
141
142   g_object_class_install_property (gobject_class,
143                                    PROP_HAS_SEPARATOR,
144                                    g_param_spec_boolean ("has_separator",
145                                                          P_("Has separator"),
146                                                          P_("The dialog has a separator bar above its buttons"),
147                                                          TRUE,
148                                                          G_PARAM_READWRITE));
149   
150   dialog_signals[RESPONSE] =
151     g_signal_new ("response",
152                   G_OBJECT_CLASS_TYPE (class),
153                   G_SIGNAL_RUN_LAST,
154                   G_STRUCT_OFFSET (GtkDialogClass, response),
155                   NULL, NULL,
156                   _gtk_marshal_NONE__INT,
157                   G_TYPE_NONE, 1,
158                   G_TYPE_INT);
159
160   dialog_signals[CLOSE] =
161     g_signal_new ("close",
162                   G_OBJECT_CLASS_TYPE (class),
163                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
164                   G_STRUCT_OFFSET (GtkDialogClass, close),
165                   NULL, NULL,
166                   _gtk_marshal_NONE__NONE,
167                   G_TYPE_NONE, 0);
168   
169   gtk_widget_class_install_style_property (widget_class,
170                                            g_param_spec_int ("content_area_border",
171                                                              P_("Content area border"),
172                                                              P_("Width of border around the main dialog area"),
173                                                              0,
174                                                              G_MAXINT,
175                                                              2,
176                                                              G_PARAM_READABLE));
177   gtk_widget_class_install_style_property (widget_class,
178                                            g_param_spec_int ("button_spacing",
179                                                              P_("Button spacing"),
180                                                              P_("Spacing between buttons"),
181                                                              0,
182                                                              G_MAXINT,
183                                                              10,
184                                                              G_PARAM_READABLE));
185   
186   gtk_widget_class_install_style_property (widget_class,
187                                            g_param_spec_int ("action_area_border",
188                                                              P_("Action area border"),
189                                                              P_("Width of border around the button area at the bottom of the dialog"),
190                                                              0,
191                                                              G_MAXINT,
192                                                              5,
193                                                              G_PARAM_READABLE));
194
195   binding_set = gtk_binding_set_by_class (class);
196   
197   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
198                                 "close", 0);
199 }
200
201 static void
202 update_spacings (GtkDialog *dialog)
203 {
204   GtkWidget *widget;
205   gint content_area_border;
206   gint button_spacing;
207   gint action_area_border;
208   
209   widget = GTK_WIDGET (dialog);
210
211   gtk_widget_style_get (widget,
212                         "content_area_border",
213                         &content_area_border,
214                         "button_spacing",
215                         &button_spacing,
216                         "action_area_border",
217                         &action_area_border,
218                         NULL);
219
220   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox),
221                                   content_area_border);
222   gtk_box_set_spacing (GTK_BOX (dialog->action_area),
223                        button_spacing);
224   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area),
225                                   action_area_border);
226 }
227
228 static void
229 gtk_dialog_init (GtkDialog *dialog)
230 {
231   GtkDialogPrivate *priv;
232
233   priv = GET_PRIVATE (dialog);
234   priv->ignore_separator = FALSE;
235
236   /* To avoid breaking old code that prevents destroy on delete event
237    * by connecting a handler, we have to have the FIRST signal
238    * connection on the dialog.
239    */
240   g_signal_connect (dialog,
241                     "delete_event",
242                     G_CALLBACK (gtk_dialog_delete_event_handler),
243                     NULL);
244   
245   dialog->vbox = gtk_vbox_new (FALSE, 0);
246   
247   gtk_container_add (GTK_CONTAINER (dialog), dialog->vbox);
248   gtk_widget_show (dialog->vbox);
249
250   dialog->action_area = gtk_hbutton_box_new ();
251
252   gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area),
253                              GTK_BUTTONBOX_END);  
254
255   gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area,
256                     FALSE, TRUE, 0);
257   gtk_widget_show (dialog->action_area);
258
259   dialog->separator = gtk_hseparator_new ();
260   gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0);
261   gtk_widget_show (dialog->separator);
262
263   gtk_window_set_type_hint (GTK_WINDOW (dialog),
264                             GDK_WINDOW_TYPE_HINT_DIALOG);
265   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
266 }
267
268
269 static void 
270 gtk_dialog_set_property (GObject      *object,
271                          guint         prop_id,
272                          const GValue *value,
273                          GParamSpec   *pspec)
274 {
275   GtkDialog *dialog;
276   
277   dialog = GTK_DIALOG (object);
278   
279   switch (prop_id)
280     {
281     case PROP_HAS_SEPARATOR:
282       gtk_dialog_set_has_separator (dialog, g_value_get_boolean (value));
283       break;
284
285     default:
286       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
287       break;
288     }
289 }
290
291 static void 
292 gtk_dialog_get_property (GObject     *object,
293                          guint        prop_id,
294                          GValue      *value,
295                          GParamSpec  *pspec)
296 {
297   GtkDialog *dialog;
298   
299   dialog = GTK_DIALOG (object);
300   
301   switch (prop_id)
302     {
303     case PROP_HAS_SEPARATOR:
304       g_value_set_boolean (value, dialog->separator != NULL);
305       break;
306
307     default:
308       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
309       break;
310     }
311 }
312
313 static gint
314 gtk_dialog_delete_event_handler (GtkWidget   *widget,
315                                  GdkEventAny *event,
316                                  gpointer     user_data)
317 {
318   /* emit response signal */
319   gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
320
321   /* Do the destroy by default */
322   return FALSE;
323 }
324
325 /* A far too tricky heuristic for getting the right initial
326  * focus widget if none was set. What we do is we focus the first
327  * widget in the tab chain, but if this results in the focus
328  * ending up on one of the response widgets _other_ than the
329  * default response, we focus the default response instead.
330  */
331 static void
332 gtk_dialog_map (GtkWidget *widget)
333 {
334   GtkWindow *window = GTK_WINDOW (widget);
335   GtkDialog *dialog = GTK_DIALOG (widget);
336   
337   GTK_WIDGET_CLASS (parent_class)->map (widget);
338
339   if (!window->focus_widget)
340     {
341       GList *children, *tmp_list;
342       
343       g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
344
345       tmp_list = children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
346
347       while (tmp_list)
348         {
349           GtkWidget *child = tmp_list->data;
350
351           if (child == window->focus_widget && child != window->default_widget && window->default_widget)
352             {
353               gtk_widget_grab_focus (window->default_widget);
354               break;
355             }
356           
357           tmp_list = tmp_list->next;
358         }
359
360       g_list_free (children);
361     }
362 }
363
364 static void
365 gtk_dialog_style_set (GtkWidget *widget,
366                       GtkStyle  *prev_style)
367 {
368   update_spacings (GTK_DIALOG (widget));
369 }
370
371 static gboolean
372 dialog_has_cancel (GtkDialog *dialog)
373 {
374   GList *children, *tmp_list;
375   gboolean ret = FALSE;
376       
377   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
378
379   for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
380     {
381       ResponseData *rd = get_response_data (tmp_list->data);
382       
383       if (rd && rd->response_id == GTK_RESPONSE_CANCEL)
384         {
385           ret = TRUE;
386           break;
387         }
388     }
389
390   g_list_free (children);
391
392   return ret;
393 }
394
395 static void
396 gtk_dialog_close (GtkDialog *dialog)
397 {
398   /* Synthesize delete_event to close dialog. */
399   
400   GtkWidget *widget = GTK_WIDGET (dialog);
401   GdkEvent *event;
402
403   if (!dialog_has_cancel (dialog))
404     return;
405
406   event = gdk_event_new (GDK_DELETE);
407   
408   event->any.window = g_object_ref (widget->window);
409   event->any.send_event = TRUE;
410   
411   gtk_main_do_event (event);
412   gdk_event_free (event);
413 }
414
415 GtkWidget*
416 gtk_dialog_new (void)
417 {
418   return g_object_new (GTK_TYPE_DIALOG, NULL);
419 }
420
421 static GtkWidget*
422 gtk_dialog_new_empty (const gchar     *title,
423                       GtkWindow       *parent,
424                       GtkDialogFlags   flags)
425 {
426   GtkDialog *dialog;
427
428   dialog = g_object_new (GTK_TYPE_DIALOG, NULL);
429
430   if (title)
431     gtk_window_set_title (GTK_WINDOW (dialog), title);
432
433   if (parent)
434     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
435
436   if (flags & GTK_DIALOG_MODAL)
437     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
438   
439   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
440     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
441
442   if (flags & GTK_DIALOG_NO_SEPARATOR)
443     gtk_dialog_set_has_separator (dialog, FALSE);
444   
445   return GTK_WIDGET (dialog);
446 }
447
448 /**
449  * gtk_dialog_new_with_buttons:
450  * @title: Title of the dialog, or %NULL
451  * @parent: Transient parent of the dialog, or %NULL
452  * @flags: from #GtkDialogFlags
453  * @first_button_text: stock ID or text to go in first button, or %NULL
454  * @Varargs: response ID for first button, then additional buttons, ending with %NULL
455  * 
456  * Creates a new #GtkDialog with title @title (or %NULL for the default
457  * title; see gtk_window_set_title()) and transient parent @parent (or
458  * %NULL for none; see gtk_window_set_transient_for()). The @flags
459  * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
460  * and/or to have it destroyed along with its transient parent
461  * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
462  * text/response ID pairs should be listed, with a %NULL pointer ending
463  * the list. Button text can be either a stock ID such as
464  * #GTK_STOCK_OK, or some arbitrary text.  A response ID can be
465  * any positive number, or one of the values in the #GtkResponseType
466  * enumeration. If the user clicks one of these dialog buttons,
467  * #GtkDialog will emit the "response" signal with the corresponding
468  * response ID. If a #GtkDialog receives the "delete_event" signal, it
469  * will emit "response" with a response ID of #GTK_RESPONSE_DELETE_EVENT.
470  * However, destroying a dialog does not emit the "response" signal;
471  * so be careful relying on "response" when using
472  * the #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
473  * so the first button in the list will be the leftmost button in the dialog.
474  *
475  * Here's a simple example:
476  * <informalexample><programlisting>
477  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
478  *                                                   main_app_window,
479  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
480  *                                                   GTK_STOCK_OK,
481  *                                                   GTK_RESPONSE_ACCEPT,
482  *                                                   GTK_STOCK_CANCEL,
483  *                                                   GTK_RESPONSE_REJECT,
484  *                                                   NULL);
485  * </programlisting></informalexample>
486  * 
487  * Return value: a new #GtkDialog
488  **/
489 GtkWidget*
490 gtk_dialog_new_with_buttons (const gchar    *title,
491                              GtkWindow      *parent,
492                              GtkDialogFlags  flags,
493                              const gchar    *first_button_text,
494                              ...)
495 {
496   GtkDialog *dialog;
497   va_list args;
498   
499   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
500
501   va_start (args, first_button_text);
502
503   gtk_dialog_add_buttons_valist (dialog,
504                                  first_button_text,
505                                  args);
506   
507   va_end (args);
508
509   return GTK_WIDGET (dialog);
510 }
511
512 static ResponseData*
513 get_response_data (GtkWidget *widget)
514 {
515   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
516                                         "gtk-dialog-response-data");
517
518   if (ad == NULL)
519     {
520       ad = g_new (ResponseData, 1);
521       
522       g_object_set_data_full (G_OBJECT (widget),
523                               "gtk-dialog-response-data",
524                               ad,
525                               g_free);
526     }
527
528   return ad;
529 }
530
531 static void
532 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
533 {
534   ResponseData *ad;
535   gint response_id;
536   
537   g_return_if_fail (GTK_IS_DIALOG (dialog));
538
539   response_id = GTK_RESPONSE_NONE;
540   
541   ad = get_response_data (widget);
542
543   g_assert (ad != NULL);
544   
545   response_id = ad->response_id;
546
547   gtk_dialog_response (dialog, response_id);
548 }
549
550 /**
551  * gtk_dialog_add_action_widget:
552  * @dialog: a #GtkDialog
553  * @child: an activatable widget
554  * @response_id: response ID for @child
555  * 
556  * Adds an activatable widget to the action area of a #GtkDialog,
557  * connecting a signal handler that will emit the "response" signal on
558  * the dialog when the widget is activated.  The widget is appended to
559  * the end of the dialog's action area.  If you want to add a
560  * non-activatable widget, simply pack it into the
561  * <literal>action_area</literal> field of the #GtkDialog struct.
562  **/
563 void
564 gtk_dialog_add_action_widget (GtkDialog *dialog,
565                               GtkWidget *child,
566                               gint       response_id)
567 {
568   ResponseData *ad;
569   gint signal_id = 0;
570   
571   g_return_if_fail (GTK_IS_DIALOG (dialog));
572   g_return_if_fail (GTK_IS_WIDGET (child));
573
574   ad = get_response_data (child);
575
576   ad->response_id = response_id;
577
578   if (GTK_IS_BUTTON (child))
579     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
580   else
581     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal != 0;
582
583   if (signal_id)
584     {
585       GClosure *closure;
586
587       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
588                                        G_OBJECT (dialog));
589       g_signal_connect_closure_by_id (child,
590                                       signal_id,
591                                       0,
592                                       closure,
593                                       FALSE);
594     }
595   else
596     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
597
598   gtk_box_pack_end (GTK_BOX (dialog->action_area),
599                     child,
600                     FALSE, TRUE, 0);
601   
602   if (response_id == GTK_RESPONSE_HELP)
603     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dialog->action_area), child, TRUE);
604 }
605
606 /**
607  * gtk_dialog_add_button:
608  * @dialog: a #GtkDialog
609  * @button_text: text of button, or stock ID
610  * @response_id: response ID for the button
611  * 
612  * Adds a button with the given text (or a stock button, if @button_text is a
613  * stock ID) and sets things up so that clicking the button will emit the
614  * "response" signal with the given @response_id. The button is appended to the
615  * end of the dialog's action area. The button widget is returned, but usually
616  * you don't need it.
617  *
618  * Return value: the button widget that was added
619  **/
620 GtkWidget*
621 gtk_dialog_add_button (GtkDialog   *dialog,
622                        const gchar *button_text,
623                        gint         response_id)
624 {
625   GtkWidget *button;
626   
627   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
628   g_return_val_if_fail (button_text != NULL, NULL);
629
630   button = gtk_button_new_from_stock (button_text);
631
632   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
633   
634   gtk_widget_show (button);
635   
636   gtk_dialog_add_action_widget (dialog,
637                                 button,
638                                 response_id);
639
640   return button;
641 }
642
643 static void
644 gtk_dialog_add_buttons_valist (GtkDialog      *dialog,
645                                const gchar    *first_button_text,
646                                va_list         args)
647 {
648   const gchar* text;
649   gint response_id;
650
651   g_return_if_fail (GTK_IS_DIALOG (dialog));
652   
653   if (first_button_text == NULL)
654     return;
655   
656   text = first_button_text;
657   response_id = va_arg (args, gint);
658
659   while (text != NULL)
660     {
661       gtk_dialog_add_button (dialog, text, response_id);
662
663       text = va_arg (args, gchar*);
664       if (text == NULL)
665         break;
666       response_id = va_arg (args, int);
667     }
668 }
669
670 /**
671  * gtk_dialog_add_buttons:
672  * @dialog: a #GtkDialog
673  * @first_button_text: button text or stock ID
674  * @Varargs: response ID for first button, then more text-response_id pairs
675  * 
676  * Adds more buttons, same as calling gtk_dialog_add_button()
677  * repeatedly.  The variable argument list should be %NULL-terminated
678  * as with gtk_dialog_new_with_buttons(). Each button must have both
679  * text and response ID.
680  **/
681 void
682 gtk_dialog_add_buttons (GtkDialog   *dialog,
683                         const gchar *first_button_text,
684                         ...)
685 {  
686   va_list args;
687
688   va_start (args, first_button_text);
689
690   gtk_dialog_add_buttons_valist (dialog,
691                                  first_button_text,
692                                  args);
693   
694   va_end (args);
695 }
696
697 /**
698  * gtk_dialog_set_response_sensitive:
699  * @dialog: a #GtkDialog
700  * @response_id: a response ID
701  * @setting: %TRUE for sensitive
702  *
703  * Calls <literal>gtk_widget_set_sensitive (widget, @setting)</literal> 
704  * for each widget in the dialog's action area with the given @response_id.
705  * A convenient way to sensitize/desensitize dialog buttons.
706  **/
707 void
708 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
709                                    gint       response_id,
710                                    gboolean   setting)
711 {
712   GList *children;
713   GList *tmp_list;
714
715   g_return_if_fail (GTK_IS_DIALOG (dialog));
716
717   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
718
719   tmp_list = children;
720   while (tmp_list != NULL)
721     {
722       GtkWidget *widget = tmp_list->data;
723       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
724                                             "gtk-dialog-response-data");
725
726       if (rd && rd->response_id == response_id)
727         gtk_widget_set_sensitive (widget, setting);
728
729       tmp_list = g_list_next (tmp_list);
730     }
731
732   g_list_free (children);
733 }
734
735 /**
736  * gtk_dialog_set_default_response:
737  * @dialog: a #GtkDialog
738  * @response_id: a response ID
739  * 
740  * Sets the last widget in the dialog's action area with the given @response_id
741  * as the default widget for the dialog. Pressing "Enter" normally activates
742  * the default widget.
743  **/
744 void
745 gtk_dialog_set_default_response (GtkDialog *dialog,
746                                  gint       response_id)
747 {
748   GList *children;
749   GList *tmp_list;
750
751   g_return_if_fail (GTK_IS_DIALOG (dialog));
752
753   children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
754
755   tmp_list = children;
756   while (tmp_list != NULL)
757     {
758       GtkWidget *widget = tmp_list->data;
759       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
760                                             "gtk-dialog-response-data");
761
762       if (rd && rd->response_id == response_id)
763         gtk_widget_grab_default (widget);
764             
765       tmp_list = g_list_next (tmp_list);
766     }
767
768   g_list_free (children);
769 }
770
771 /**
772  * gtk_dialog_set_has_separator:
773  * @dialog: a #GtkDialog
774  * @setting: %TRUE to have a separator
775  *
776  * Sets whether the dialog has a separator above the buttons.
777  * %TRUE by default.
778  **/
779 void
780 gtk_dialog_set_has_separator (GtkDialog *dialog,
781                               gboolean   setting)
782 {
783   GtkDialogPrivate *priv;
784
785   g_return_if_fail (GTK_IS_DIALOG (dialog));
786
787   priv = GET_PRIVATE (dialog);
788
789   /* this might fail if we get called before _init() somehow */
790   g_assert (dialog->vbox != NULL);
791
792   if (priv->ignore_separator)
793     {
794       g_warning ("Ignoring the separator setting");
795       return;
796     }
797   
798   if (setting && dialog->separator == NULL)
799     {
800       dialog->separator = gtk_hseparator_new ();
801       gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0);
802
803       /* The app programmer could screw this up, but, their own fault.
804        * Moves the separator just above the action area.
805        */
806       gtk_box_reorder_child (GTK_BOX (dialog->vbox), dialog->separator, 1);
807       gtk_widget_show (dialog->separator);
808     }
809   else if (!setting && dialog->separator != NULL)
810     {
811       gtk_widget_destroy (dialog->separator);
812       dialog->separator = NULL;
813     }
814
815   g_object_notify (G_OBJECT (dialog), "has_separator");
816 }
817
818 /**
819  * gtk_dialog_get_has_separator:
820  * @dialog: a #GtkDialog
821  * 
822  * Accessor for whether the dialog has a separator.
823  * 
824  * Return value: %TRUE if the dialog has a separator
825  **/
826 gboolean
827 gtk_dialog_get_has_separator (GtkDialog *dialog)
828 {
829   g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE);
830
831   return dialog->separator != NULL;
832 }
833
834 /**
835  * gtk_dialog_response:
836  * @dialog: a #GtkDialog
837  * @response_id: response ID 
838  * 
839  * Emits the "response" signal with the given response ID. Used to
840  * indicate that the user has responded to the dialog in some way;
841  * typically either you or gtk_dialog_run() will be monitoring the
842  * "response" signal and take appropriate action.
843  **/
844 void
845 gtk_dialog_response (GtkDialog *dialog,
846                      gint       response_id)
847 {
848   g_return_if_fail (GTK_IS_DIALOG (dialog));
849
850   g_signal_emit (dialog,
851                  dialog_signals[RESPONSE],
852                  0,
853                  response_id);
854 }
855
856 typedef struct
857 {
858   GtkDialog *dialog;
859   gint response_id;
860   GMainLoop *loop;
861   gboolean destroyed;
862 } RunInfo;
863
864 static void
865 shutdown_loop (RunInfo *ri)
866 {
867   if (g_main_loop_is_running (ri->loop))
868     g_main_loop_quit (ri->loop);
869 }
870
871 static void
872 run_unmap_handler (GtkDialog *dialog, gpointer data)
873 {
874   RunInfo *ri = data;
875
876   shutdown_loop (ri);
877 }
878
879 static void
880 run_response_handler (GtkDialog *dialog,
881                       gint response_id,
882                       gpointer data)
883 {
884   RunInfo *ri;
885
886   ri = data;
887
888   ri->response_id = response_id;
889
890   shutdown_loop (ri);
891 }
892
893 static gint
894 run_delete_handler (GtkDialog *dialog,
895                     GdkEventAny *event,
896                     gpointer data)
897 {
898   RunInfo *ri = data;
899     
900   shutdown_loop (ri);
901   
902   return TRUE; /* Do not destroy */
903 }
904
905 static void
906 run_destroy_handler (GtkDialog *dialog, gpointer data)
907 {
908   RunInfo *ri = data;
909
910   /* shutdown_loop will be called by run_unmap_handler */
911   
912   ri->destroyed = TRUE;
913 }
914
915 /**
916  * gtk_dialog_run:
917  * @dialog: a #GtkDialog
918  * 
919  * Blocks in a recursive main loop until the @dialog either emits the
920  * response signal, or is destroyed. If the dialog is destroyed during the call
921  * to gtk_dialog_run(), gtk_dialog_returns #GTK_RESPONSE_NONE.
922  * Otherwise, it returns the response ID from the "response" signal emission.
923  * Before entering the recursive main loop, gtk_dialog_run() calls
924  * gtk_widget_show() on the dialog for you. Note that you still
925  * need to show any children of the dialog yourself.
926  *
927  * During gtk_dialog_run(), the default behavior of "delete_event" is
928  * disabled; if the dialog receives "delete_event", it will not be
929  * destroyed as windows usually are, and gtk_dialog_run() will return
930  * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
931  * modal. You can force gtk_dialog_run() to return at any time by
932  * calling gtk_dialog_response() to emit the "response"
933  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
934  * idea, because your post-run code won't know whether the dialog was
935  * destroyed or not.
936  *
937  * After gtk_dialog_run() returns, you are responsible for hiding or
938  * destroying the dialog if you wish to do so.
939  *
940  * Typical usage of this function might be:
941  * <informalexample><programlisting>
942  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
943  *   switch (result)
944  *     {
945  *       case GTK_RESPONSE_ACCEPT:
946  *          do_application_specific_something (<!-- -->);
947  *          break;
948  *       default:
949  *          do_nothing_since_dialog_was_cancelled (<!-- -->);
950  *          break;
951  *     }
952  *   gtk_widget_destroy (dialog);
953  * </programlisting></informalexample>
954  * 
955  * Return value: response ID
956  **/
957 gint
958 gtk_dialog_run (GtkDialog *dialog)
959 {
960   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
961   gboolean was_modal;
962   gulong response_handler;
963   gulong unmap_handler;
964   gulong destroy_handler;
965   gulong delete_handler;
966   
967   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
968
969   g_object_ref (dialog);
970
971   was_modal = GTK_WINDOW (dialog)->modal;
972   if (!was_modal)
973     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
974
975   if (!GTK_WIDGET_VISIBLE (dialog))
976     gtk_widget_show (GTK_WIDGET (dialog));
977   
978   response_handler =
979     g_signal_connect (dialog,
980                       "response",
981                       G_CALLBACK (run_response_handler),
982                       &ri);
983   
984   unmap_handler =
985     g_signal_connect (dialog,
986                       "unmap",
987                       G_CALLBACK (run_unmap_handler),
988                       &ri);
989   
990   delete_handler =
991     g_signal_connect (dialog,
992                       "delete_event",
993                       G_CALLBACK (run_delete_handler),
994                       &ri);
995   
996   destroy_handler =
997     g_signal_connect (dialog,
998                       "destroy",
999                       G_CALLBACK (run_destroy_handler),
1000                       &ri);
1001   
1002   ri.loop = g_main_loop_new (NULL, FALSE);
1003
1004   GDK_THREADS_LEAVE ();  
1005   g_main_loop_run (ri.loop);
1006   GDK_THREADS_ENTER ();  
1007
1008   g_main_loop_unref (ri.loop);
1009
1010   ri.loop = NULL;
1011   ri.destroyed = FALSE;
1012   
1013   if (!ri.destroyed)
1014     {
1015       if (!was_modal)
1016         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
1017       
1018       g_signal_handler_disconnect (dialog, response_handler);
1019       g_signal_handler_disconnect (dialog, unmap_handler);
1020       g_signal_handler_disconnect (dialog, delete_handler);
1021       g_signal_handler_disconnect (dialog, destroy_handler);
1022     }
1023
1024   g_object_unref (dialog);
1025
1026   return ri.response_id;
1027 }
1028
1029 void
1030 _gtk_dialog_set_ignore_separator (GtkDialog *dialog,
1031                                   gboolean   ignore_separator)
1032 {
1033   GtkDialogPrivate *priv;
1034
1035   priv = GET_PRIVATE (dialog);
1036   priv->ignore_separator = ignore_separator;
1037 }