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