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