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