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