]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
Remove spurious const.
[~andy/gtk] / gtk / gtkdialog.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkbutton.h"
28 #include "gtkdialog.h"
29 #include "gtkhbbox.h"
30 #include "gtkhseparator.h"
31 #include "gtkvbox.h"
32 #include "gtksignal.h"
33 #include "gdkkeysyms.h"
34 #include "gtkmain.h"
35
36 static void gtk_dialog_class_init (GtkDialogClass *klass);
37 static void gtk_dialog_init       (GtkDialog      *dialog);
38 static gint gtk_dialog_key_press  (GtkWidget      *widget,
39                                    GdkEventKey    *key);                                   
40
41 static void gtk_dialog_add_buttons_valist (GtkDialog   *dialog,
42                                            const gchar *first_button_text,
43                                            va_list      args);
44
45 static gint gtk_dialog_delete_event_handler (GtkWidget   *widget,
46                                              GdkEventAny *event,
47                                              gpointer     user_data);
48
49
50 enum {
51   RESPONSE,
52   LAST_SIGNAL
53 };
54
55 static gpointer parent_class;
56 static guint dialog_signals[LAST_SIGNAL];
57
58 GtkType
59 gtk_dialog_get_type (void)
60 {
61   static GtkType dialog_type = 0;
62
63   if (!dialog_type)
64     {
65       static const GtkTypeInfo dialog_info =
66       {
67         "GtkDialog",
68         sizeof (GtkDialog),
69         sizeof (GtkDialogClass),
70         (GtkClassInitFunc) gtk_dialog_class_init,
71         (GtkObjectInitFunc) gtk_dialog_init,
72         /* reserved_1 */ NULL,
73         /* reserved_2 */ NULL,
74         (GtkClassInitFunc) NULL,
75       };
76
77       dialog_type = gtk_type_unique (GTK_TYPE_WINDOW, &dialog_info);
78     }
79
80   return dialog_type;
81 }
82
83 static void
84 gtk_dialog_class_init (GtkDialogClass *class)
85 {
86   GtkObjectClass *object_class;
87   GtkWidgetClass *widget_class;
88   
89   object_class = (GtkObjectClass*) class;
90   widget_class = (GtkWidgetClass*) class;
91
92   parent_class = g_type_class_peek_parent (class);
93   
94   widget_class->key_press_event = gtk_dialog_key_press;
95
96   dialog_signals[RESPONSE] =
97     gtk_signal_new ("response",
98                     GTK_RUN_LAST,
99                     GTK_CLASS_TYPE (object_class),
100                     GTK_SIGNAL_OFFSET (GtkDialogClass, response),
101                     gtk_marshal_NONE__INT,
102                     GTK_TYPE_NONE, 1,
103                     GTK_TYPE_INT);
104 }
105
106 static void
107 gtk_dialog_init (GtkDialog *dialog)
108 {
109   GtkWidget *separator;
110
111   /* To avoid breaking old code that prevents destroy on delete event
112    * by connecting a handler, we have to have the FIRST signal
113    * connection on the dialog.
114    */
115   gtk_signal_connect (GTK_OBJECT (dialog),
116                       "delete_event",
117                       GTK_SIGNAL_FUNC (gtk_dialog_delete_event_handler),
118                       NULL);
119   
120   dialog->vbox = gtk_vbox_new (FALSE, 0);
121
122   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 2);
123   
124   gtk_container_add (GTK_CONTAINER (dialog), dialog->vbox);
125   gtk_widget_show (dialog->vbox);
126
127   dialog->action_area = gtk_hbutton_box_new ();
128
129   gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area),
130                              GTK_BUTTONBOX_END);
131
132   gtk_button_box_set_spacing (GTK_BUTTON_BOX (dialog->action_area), 5);
133   
134   gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 5);
135   gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area,
136                     FALSE, TRUE, 0);
137   gtk_widget_show (dialog->action_area);
138
139   separator = gtk_hseparator_new ();
140   gtk_box_pack_end (GTK_BOX (dialog->vbox), separator, FALSE, TRUE, 0);
141   gtk_widget_show (separator);
142 }
143
144 static gint
145 gtk_dialog_delete_event_handler (GtkWidget   *widget,
146                                  GdkEventAny *event,
147                                  gpointer     user_data)
148 {
149   /* emit response signal */
150   gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
151
152   /* Do the destroy by default */
153   return FALSE;
154 }
155
156 static gint
157 gtk_dialog_key_press (GtkWidget   *widget,
158                       GdkEventKey *key)
159 {
160   GdkEventAny event;
161
162   event.type = GDK_DELETE;
163   event.window = widget->window;
164   event.send_event = TRUE;
165
166   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, key))
167     return TRUE;
168
169   if (key->keyval != GDK_Escape)
170     return FALSE;
171
172   /* Synthesize delete_event on key press. */
173   g_object_ref (G_OBJECT (event.window));
174   
175   gtk_main_do_event ((GdkEvent*)&event);
176   
177   g_object_unref (G_OBJECT (event.window));
178
179   return TRUE;
180 }
181
182 GtkWidget*
183 gtk_dialog_new (void)
184 {
185   return GTK_WIDGET (gtk_type_new (GTK_TYPE_DIALOG));
186 }
187
188 static GtkWidget*
189 gtk_dialog_new_empty (const gchar     *title,
190                       GtkWindow       *parent,
191                       GtkDialogFlags   flags)
192 {
193   GtkDialog *dialog;
194
195   dialog = GTK_DIALOG (g_object_new (GTK_TYPE_DIALOG, NULL));
196
197   if (title)
198     gtk_window_set_title (GTK_WINDOW (dialog), title);
199
200   if (parent)
201     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
202
203   if (flags & GTK_DIALOG_MODAL)
204     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
205
206   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
207     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
208
209   return GTK_WIDGET (dialog);
210 }
211
212 /**
213  * gtk_dialog_new_with_buttons:
214  * @title: Title of the dialog, or NULL
215  * @parent: Transient parent of the dialog, or NULL
216  * @flags: from #GtkDialogFlags
217  * @first_button_text: stock ID or text to go in first button, or NULL
218  * @Varargs: response ID for first button, then additional buttons, ending with NULL
219  * 
220  * Creates a new #GtkDialog with title @title (or NULL for the default
221  * title; see gtk_window_set_title()) and transient parent @parent (or
222  * NULL for none; see gtk_window_set_transient_for()). The @flags
223  * argument can be used to make the dialog modal (GTK_DIALOG_MODAL)
224  * and/or to have it destroyed along with its transient parent
225  * (GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
226  * text/response ID pairs should be listed, with a NULL pointer ending
227  * the list. Button text can be either a stock ID such as
228  * GTK_STOCK_BUTTON_OK, or some arbitrary text.  A response ID can be
229  * any positive number, or one of the values in the #GtkResponseType
230  * enumeration. If the user clicks one of these dialog buttons,
231  * #GtkDialog will emit the "response" signal with the corresponding
232  * response ID. If a #GtkDialog receives the "delete_event" signal, it
233  * will emit "response" with a response ID of GTK_RESPONSE_NONE.
234  * However, destroying a dialog does not emit the "response" signal;
235  * so be careful relying on "response" when using
236  * the GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
237  * so the first button in the list will be the leftmost button in the dialog.
238  *
239  * Here's a simple example:
240  * <programlisting>
241  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
242  *                                                   main_app_window,
243  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
244  *                                                   GTK_STOCK_BUTTON_OK,
245  *                                                   GTK_RESPONSE_ACCEPT,
246  *                                                   GTK_STOCK_BUTTON_CANCEL,
247  *                                                   GTK_RESPONSE_REJECT,
248  *                                                   NULL);
249  * </programlisting>
250  * 
251  * Return value: a new #GtkDialog
252  **/
253 GtkWidget*
254 gtk_dialog_new_with_buttons (const gchar    *title,
255                              GtkWindow      *parent,
256                              GtkDialogFlags  flags,
257                              const gchar    *first_button_text,
258                              ...)
259 {
260   GtkDialog *dialog;
261   va_list args;
262   
263   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
264
265   va_start (args, first_button_text);
266
267   gtk_dialog_add_buttons_valist (dialog,
268                                  first_button_text,
269                                  args);
270   
271   va_end (args);
272
273   return GTK_WIDGET (dialog);
274 }
275
276 typedef struct _ResponseData ResponseData;
277
278 struct _ResponseData
279 {
280   gint response_id;
281 };
282
283 static ResponseData*
284 get_response_data (GtkWidget *widget)
285 {
286   ResponseData *ad = gtk_object_get_data (GTK_OBJECT (widget),
287                                           "gtk-dialog-response-data");
288
289   if (ad == NULL)
290     {
291       ad = g_new (ResponseData, 1);
292       
293       gtk_object_set_data_full (GTK_OBJECT (widget),
294                                 "gtk-dialog-response-data",
295                                 ad,
296                                 g_free);
297     }
298
299   return ad;
300 }
301
302 static void
303 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
304 {
305   ResponseData *ad;
306   gint response_id;
307   
308   g_return_if_fail (GTK_IS_DIALOG (dialog));
309
310   response_id = GTK_RESPONSE_NONE;
311   
312   ad = get_response_data (widget);
313
314   g_assert (ad != NULL);
315   
316   response_id = ad->response_id;
317
318   gtk_dialog_response (dialog, response_id);
319 }
320 /**
321  * gtk_dialog_add_action_widget:
322  * @dialog: a #GtkDialog
323  * @child: an activatable widget
324  * @response_id: response ID for @child
325  * 
326  * Adds an activatable widget to the action area of a #GtkDialog,
327  * connecting a signal handler that will emit the "response" signal on
328  * the dialog when the widget is activated.  The widget is appended to
329  * the end of the dialog's action area.  If you want to add a
330  * non-activatable widget, simply pack it into the
331  * <literal>action_area</literal> field of the #GtkDialog struct.
332  * 
333  **/
334 void
335 gtk_dialog_add_action_widget  (GtkDialog *dialog,
336                                GtkWidget *child,
337                                gint       response_id)
338 {
339   ResponseData *ad;
340   
341   g_return_if_fail (GTK_IS_DIALOG (dialog));
342   g_return_if_fail (GTK_IS_WIDGET (child));
343
344   ad = get_response_data (child);
345
346   ad->response_id = response_id;
347
348   if (GTK_WIDGET_GET_CLASS (child)->activate_signal != 0)
349     {
350       const gchar* name =
351         gtk_signal_name (GTK_WIDGET_GET_CLASS (child)->activate_signal);
352
353       gtk_signal_connect_while_alive (GTK_OBJECT (child),
354                                       name,
355                                       GTK_SIGNAL_FUNC (action_widget_activated),
356                                       dialog,
357                                       GTK_OBJECT (dialog));
358     }
359   else
360     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
361
362   gtk_box_pack_end (GTK_BOX (dialog->action_area),
363                     child,
364                     FALSE, TRUE, 5);  
365 }
366
367 /**
368  * gtk_dialog_add_button:
369  * @dialog: a #GtkDialog
370  * @button_text: text of button, or stock ID
371  * @response_id: response ID for the button
372  * 
373  * Adds a button with the given text (or a stock button, if @button_text is a
374  * stock ID) and sets things up so that clicking the button will emit the
375  * "response" signal with the given @response_id. The button is appended to the
376  * end of the dialog's action area. The button widget is returned, but usually
377  * you don't need it.
378  *
379  * Return value: the button widget that was added
380  **/
381 GtkWidget*
382 gtk_dialog_add_button (GtkDialog   *dialog,
383                        const gchar *button_text,
384                        gint         response_id)
385 {
386   GtkWidget *button;
387   
388   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
389   g_return_val_if_fail (button_text != NULL, NULL);
390
391   button = gtk_button_new_stock (button_text,
392                                  gtk_window_get_default_accel_group (GTK_WINDOW (dialog)));
393
394   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
395   
396   gtk_widget_show (button);
397   
398   gtk_dialog_add_action_widget (dialog,
399                                 button,
400                                 response_id);
401
402   return button;
403 }
404
405 static void
406 gtk_dialog_add_buttons_valist(GtkDialog      *dialog,
407                               const gchar    *first_button_text,
408                               va_list         args)
409 {
410   const gchar* text;
411   gint response_id;
412
413   g_return_if_fail (GTK_IS_DIALOG (dialog));
414   
415   if (first_button_text == NULL)
416     return;
417   
418   text = first_button_text;
419   response_id = va_arg (args, gint);
420
421   while (text != NULL)
422     {
423       gtk_dialog_add_button (dialog, text, response_id);
424
425       text = va_arg (args, gchar*);
426       if (text == NULL)
427         break;
428       response_id = va_arg (args, int);
429     }
430 }
431
432 /**
433  * gtk_dialog_add_buttons:
434  * @dialog: a #GtkDialog
435  * @first_button_text: button text or stock ID
436  * @Varargs: response ID for first button, then more text-response_id pairs
437  * 
438  * Adds more buttons, same as calling gtk_dialog_add_button()
439  * repeatedly.  The variable argument list should be NULL-terminated
440  * as with gtk_dialog_new_with_buttons(). Each button must have both
441  * text and response ID.
442  * 
443  **/
444 void
445 gtk_dialog_add_buttons (GtkDialog   *dialog,
446                         const gchar *first_button_text,
447                         ...)
448 {  
449   va_list args;
450
451   va_start (args, first_button_text);
452
453   gtk_dialog_add_buttons_valist (dialog,
454                                  first_button_text,
455                                  args);
456   
457   va_end (args);
458 }
459
460 /**
461  * gtk_dialog_set_response_sensitive:
462  * @dialog: a #GtkDialog
463  * @response_id: a response ID
464  * @setting: %TRUE for sensitive
465  *
466  * Calls gtk_widget_set_sensitive (widget, @setting) for each
467  * widget in the dialog's action area with the given @response_id.
468  * A convenient way to sensitize/desensitize dialog buttons.
469  * 
470  **/
471 void
472 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
473                                    gint       response_id,
474                                    gboolean   setting)
475 {
476   GList *children;
477   GList *tmp_list;
478
479   children = gtk_container_children (GTK_CONTAINER (dialog));
480
481   tmp_list = children;
482   while (tmp_list != NULL)
483     {
484       GtkWidget *widget = tmp_list->data;
485       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
486                                             "gtk-dialog-response-data");
487
488       if (rd && rd->response_id == response_id)
489         gtk_widget_set_sensitive (widget, setting);
490
491       tmp_list = g_list_next (tmp_list);
492     }
493
494   g_list_free (children);
495 }
496
497 /**
498  * gtk_dialog_set_default_response:
499  * @dialog: a #GtkDialog
500  * @response_id: a response ID
501  * 
502  * Sets the last widget in the dialog's action area with the given @response_id
503  * as the default widget for the dialog. Pressing "Enter" normally activates
504  * the default widget.
505  * 
506  **/
507 void
508 gtk_dialog_set_default_response (GtkDialog *dialog,
509                                  gint       response_id)
510 {
511   GList *children;
512   GList *tmp_list;
513
514   children = gtk_container_children (GTK_CONTAINER (dialog->action_area));
515
516   tmp_list = children;
517   while (tmp_list != NULL)
518     {
519       GtkWidget *widget = tmp_list->data;
520       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
521                                             "gtk-dialog-response-data");
522
523       if (rd && rd->response_id == response_id)
524         {
525           gtk_widget_grab_default (widget);
526           
527           if (!GTK_WINDOW (dialog)->focus_widget)
528             gtk_widget_grab_focus (widget);
529         }
530             
531       tmp_list = g_list_next (tmp_list);
532     }
533
534   g_list_free (children);
535 }
536
537 /**
538  * gtk_dialog_response:
539  * @dialog: a #GtkDialog
540  * @response_id: response ID 
541  * 
542  * Emits the "response" signal with the given response ID. Used to
543  * indicate that the user has responded to the dialog in some way;
544  * typically either you or gtk_dialog_run() will be monitoring the
545  * "response" signal and take appropriate action.
546  **/
547 void
548 gtk_dialog_response (GtkDialog *dialog,
549                      gint       response_id)
550 {
551   g_return_if_fail (dialog != NULL);
552   g_return_if_fail (GTK_IS_DIALOG (dialog));
553
554   gtk_signal_emit (GTK_OBJECT (dialog),
555                    dialog_signals[RESPONSE],
556                    response_id);
557 }
558
559 typedef struct
560 {
561   GtkDialog *dialog;
562   gint response_id;
563   GMainLoop *loop;
564 } RunInfo;
565
566 static void
567 shutdown_loop (RunInfo *ri)
568 {
569   if (g_main_loop_is_running (ri->loop))
570     g_main_loop_quit (ri->loop);
571 }
572
573 static void
574 run_unmap_handler (GtkDialog *dialog, gpointer data)
575 {
576   RunInfo *ri = data;
577
578   shutdown_loop (ri);
579 }
580
581 static void
582 run_response_handler (GtkDialog *dialog,
583                       gint response_id,
584                       gpointer data)
585 {
586   RunInfo *ri;
587
588   ri = data;
589
590   ri->response_id = response_id;
591
592   shutdown_loop (ri);
593 }
594
595 static gint
596 run_delete_handler (GtkDialog *dialog,
597                     GdkEventAny *event,
598                     gpointer data)
599 {
600   RunInfo *ri = data;
601     
602   shutdown_loop (ri);
603
604   /* emit response signal */
605   gtk_dialog_response (dialog, GTK_RESPONSE_NONE);
606   
607   return TRUE; /* Do not destroy */
608 }
609
610 /**
611  * gtk_dialog_run:
612  * @dialog: a #GtkDialog
613  * 
614  * Blocks in a recursive main loop until the @dialog either emits the
615  * response signal, or is destroyed. If the dialog is destroyed,
616  * gtk_dialog_run() returns GTK_RESPONSE_NONE. Otherwise, it returns
617  * the response ID from the "response" signal emission. Before
618  * entering the recursive main loop, gtk_dialog_run() calls
619  * gtk_widget_show() on the dialog for you. Note that you still
620  * need to show any children of the dialog yourself.
621  *
622  * During gtk_dialog_run(), the default behavior of "delete_event" is
623  * disabled; if the dialog receives "delete_event", it will not be
624  * destroyed as windows usually are, and gtk_dialog_run() will return
625  * GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
626  * modal. You can force gtk_dialog_run() to return at any time by
627  * calling gtk_dialog_response() to emit the "response"
628  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
629  * idea, because your post-run code won't know whether the dialog was
630  * destroyed or not.
631  *
632  * After gtk_dialog_run() returns, you are responsible for hiding or
633  * destroying the dialog if you wish to do so.
634  *
635  * Typical usage of this function might be:
636  * <programlisting>
637  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
638  *   switch (result)
639  *     {
640  *       case GTK_RESPONSE_ACCEPT:
641  *          do_application_specific_something ();
642  *          break;
643  *       default:
644  *          do_nothing_since_dialog_was_cancelled ();
645  *          break;
646  *     }
647  *   gtk_widget_destroy (dialog);
648  * </programlisting>
649  * 
650  * Return value: response ID
651  **/
652 gint
653 gtk_dialog_run (GtkDialog *dialog)
654 {
655   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
656   gboolean was_modal;
657   guint response_handler;
658   guint destroy_handler;
659   guint delete_handler;
660   
661   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
662
663   gtk_object_ref (GTK_OBJECT (dialog));
664
665   if (!GTK_WIDGET_VISIBLE (dialog))
666     gtk_widget_show (GTK_WIDGET (dialog));
667   
668   was_modal = GTK_WINDOW (dialog)->modal;
669   if (!was_modal)
670     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
671
672   response_handler =
673     gtk_signal_connect (GTK_OBJECT (dialog),
674                         "response",
675                         GTK_SIGNAL_FUNC (run_response_handler),
676                         &ri);
677   
678   destroy_handler =
679     gtk_signal_connect (GTK_OBJECT (dialog),
680                         "unmap",
681                         GTK_SIGNAL_FUNC (run_unmap_handler),
682                         &ri);
683   
684   delete_handler =
685     gtk_signal_connect (GTK_OBJECT (dialog),
686                         "delete_event",
687                         GTK_SIGNAL_FUNC (run_delete_handler),
688                         &ri);
689   
690   ri.loop = g_main_new (FALSE);
691
692   g_main_loop_run (ri.loop);
693
694   g_main_loop_unref (ri.loop);
695
696   ri.loop = NULL;
697   
698   if (!GTK_OBJECT_DESTROYED (dialog))
699     {
700       if (!was_modal)
701         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
702       
703       gtk_signal_disconnect (GTK_OBJECT (dialog), destroy_handler);
704       gtk_signal_disconnect (GTK_OBJECT (dialog), response_handler);
705       gtk_signal_disconnect (GTK_OBJECT (dialog), delete_handler);
706     }
707
708   gtk_object_unref (GTK_OBJECT (dialog));
709
710   return ri.response_id;
711 }
712
713
714
715