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