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