]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
Added GdkWindowTypeHint type. Added modal_hint field to GdkWindindow. New
[~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   gtk_window_set_type_hint (GTK_WINDOW (dialog),
144                             GDK_WINDOW_TYPE_HINT_DIALOG);
145 }
146
147 static gint
148 gtk_dialog_delete_event_handler (GtkWidget   *widget,
149                                  GdkEventAny *event,
150                                  gpointer     user_data)
151 {
152   /* emit response signal */
153   gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
154
155   /* Do the destroy by default */
156   return FALSE;
157 }
158
159 static gint
160 gtk_dialog_key_press (GtkWidget   *widget,
161                       GdkEventKey *key)
162 {
163   GdkEventAny event;
164
165   event.type = GDK_DELETE;
166   event.window = widget->window;
167   event.send_event = TRUE;
168
169   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, key))
170     return TRUE;
171
172   if (key->keyval != GDK_Escape)
173     return FALSE;
174
175   /* Synthesize delete_event on key press. */
176   g_object_ref (G_OBJECT (event.window));
177   
178   gtk_main_do_event ((GdkEvent*)&event);
179   
180   g_object_unref (G_OBJECT (event.window));
181
182   return TRUE;
183 }
184
185 GtkWidget*
186 gtk_dialog_new (void)
187 {
188   return GTK_WIDGET (gtk_type_new (GTK_TYPE_DIALOG));
189 }
190
191 static GtkWidget*
192 gtk_dialog_new_empty (const gchar     *title,
193                       GtkWindow       *parent,
194                       GtkDialogFlags   flags)
195 {
196   GtkDialog *dialog;
197
198   dialog = GTK_DIALOG (g_object_new (GTK_TYPE_DIALOG, NULL));
199
200   if (title)
201     gtk_window_set_title (GTK_WINDOW (dialog), title);
202
203   if (parent)
204     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
205
206   if (flags & GTK_DIALOG_MODAL)
207     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
208   
209   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
210     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
211
212   return GTK_WIDGET (dialog);
213 }
214
215 /**
216  * gtk_dialog_new_with_buttons:
217  * @title: Title of the dialog, or NULL
218  * @parent: Transient parent of the dialog, or NULL
219  * @flags: from #GtkDialogFlags
220  * @first_button_text: stock ID or text to go in first button, or NULL
221  * @Varargs: response ID for first button, then additional buttons, ending with NULL
222  * 
223  * Creates a new #GtkDialog with title @title (or NULL for the default
224  * title; see gtk_window_set_title()) and transient parent @parent (or
225  * NULL for none; see gtk_window_set_transient_for()). The @flags
226  * argument can be used to make the dialog modal (GTK_DIALOG_MODAL)
227  * and/or to have it destroyed along with its transient parent
228  * (GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
229  * text/response ID pairs should be listed, with a NULL pointer ending
230  * the list. Button text can be either a stock ID such as
231  * GTK_STOCK_BUTTON_OK, or some arbitrary text.  A response ID can be
232  * any positive number, or one of the values in the #GtkResponseType
233  * enumeration. If the user clicks one of these dialog buttons,
234  * #GtkDialog will emit the "response" signal with the corresponding
235  * response ID. If a #GtkDialog receives the "delete_event" signal, it
236  * will emit "response" with a response ID of GTK_RESPONSE_NONE.
237  * However, destroying a dialog does not emit the "response" signal;
238  * so be careful relying on "response" when using
239  * the GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
240  * so the first button in the list will be the leftmost button in the dialog.
241  *
242  * Here's a simple example:
243  * <programlisting>
244  *  GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog",
245  *                                                   main_app_window,
246  *                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
247  *                                                   GTK_STOCK_BUTTON_OK,
248  *                                                   GTK_RESPONSE_ACCEPT,
249  *                                                   GTK_STOCK_BUTTON_CANCEL,
250  *                                                   GTK_RESPONSE_REJECT,
251  *                                                   NULL);
252  * </programlisting>
253  * 
254  * Return value: a new #GtkDialog
255  **/
256 GtkWidget*
257 gtk_dialog_new_with_buttons (const gchar    *title,
258                              GtkWindow      *parent,
259                              GtkDialogFlags  flags,
260                              const gchar    *first_button_text,
261                              ...)
262 {
263   GtkDialog *dialog;
264   va_list args;
265   
266   dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));
267
268   va_start (args, first_button_text);
269
270   gtk_dialog_add_buttons_valist (dialog,
271                                  first_button_text,
272                                  args);
273   
274   va_end (args);
275
276   return GTK_WIDGET (dialog);
277 }
278
279 typedef struct _ResponseData ResponseData;
280
281 struct _ResponseData
282 {
283   gint response_id;
284 };
285
286 static ResponseData*
287 get_response_data (GtkWidget *widget)
288 {
289   ResponseData *ad = gtk_object_get_data (GTK_OBJECT (widget),
290                                           "gtk-dialog-response-data");
291
292   if (ad == NULL)
293     {
294       ad = g_new (ResponseData, 1);
295       
296       gtk_object_set_data_full (GTK_OBJECT (widget),
297                                 "gtk-dialog-response-data",
298                                 ad,
299                                 g_free);
300     }
301
302   return ad;
303 }
304
305 static void
306 action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
307 {
308   ResponseData *ad;
309   gint response_id;
310   
311   g_return_if_fail (GTK_IS_DIALOG (dialog));
312
313   response_id = GTK_RESPONSE_NONE;
314   
315   ad = get_response_data (widget);
316
317   g_assert (ad != NULL);
318   
319   response_id = ad->response_id;
320
321   gtk_dialog_response (dialog, response_id);
322 }
323 /**
324  * gtk_dialog_add_action_widget:
325  * @dialog: a #GtkDialog
326  * @child: an activatable widget
327  * @response_id: response ID for @child
328  * 
329  * Adds an activatable widget to the action area of a #GtkDialog,
330  * connecting a signal handler that will emit the "response" signal on
331  * the dialog when the widget is activated.  The widget is appended to
332  * the end of the dialog's action area.  If you want to add a
333  * non-activatable widget, simply pack it into the
334  * <literal>action_area</literal> field of the #GtkDialog struct.
335  * 
336  **/
337 void
338 gtk_dialog_add_action_widget  (GtkDialog *dialog,
339                                GtkWidget *child,
340                                gint       response_id)
341 {
342   ResponseData *ad;
343   
344   g_return_if_fail (GTK_IS_DIALOG (dialog));
345   g_return_if_fail (GTK_IS_WIDGET (child));
346
347   ad = get_response_data (child);
348
349   ad->response_id = response_id;
350
351   if (GTK_WIDGET_GET_CLASS (child)->activate_signal != 0)
352     {
353       const gchar* name =
354         gtk_signal_name (GTK_WIDGET_GET_CLASS (child)->activate_signal);
355
356       gtk_signal_connect_while_alive (GTK_OBJECT (child),
357                                       name,
358                                       GTK_SIGNAL_FUNC (action_widget_activated),
359                                       dialog,
360                                       GTK_OBJECT (dialog));
361     }
362   else
363     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
364
365   gtk_box_pack_end (GTK_BOX (dialog->action_area),
366                     child,
367                     FALSE, TRUE, 5);  
368 }
369
370 /**
371  * gtk_dialog_add_button:
372  * @dialog: a #GtkDialog
373  * @button_text: text of button, or stock ID
374  * @response_id: response ID for the button
375  * 
376  * Adds a button with the given text (or a stock button, if @button_text is a
377  * stock ID) and sets things up so that clicking the button will emit the
378  * "response" signal with the given @response_id. The button is appended to the
379  * end of the dialog's action area. The button widget is returned, but usually
380  * you don't need it.
381  *
382  * Return value: the button widget that was added
383  **/
384 GtkWidget*
385 gtk_dialog_add_button (GtkDialog   *dialog,
386                        const gchar *button_text,
387                        gint         response_id)
388 {
389   GtkWidget *button;
390   
391   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
392   g_return_val_if_fail (button_text != NULL, NULL);
393
394   button = gtk_button_new_stock (button_text,
395                                  gtk_window_get_default_accel_group (GTK_WINDOW (dialog)));
396
397   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
398   
399   gtk_widget_show (button);
400   
401   gtk_dialog_add_action_widget (dialog,
402                                 button,
403                                 response_id);
404
405   return button;
406 }
407
408 static void
409 gtk_dialog_add_buttons_valist(GtkDialog      *dialog,
410                               const gchar    *first_button_text,
411                               va_list         args)
412 {
413   const gchar* text;
414   gint response_id;
415
416   g_return_if_fail (GTK_IS_DIALOG (dialog));
417   
418   if (first_button_text == NULL)
419     return;
420   
421   text = first_button_text;
422   response_id = va_arg (args, gint);
423
424   while (text != NULL)
425     {
426       gtk_dialog_add_button (dialog, text, response_id);
427
428       text = va_arg (args, gchar*);
429       if (text == NULL)
430         break;
431       response_id = va_arg (args, int);
432     }
433 }
434
435 /**
436  * gtk_dialog_add_buttons:
437  * @dialog: a #GtkDialog
438  * @first_button_text: button text or stock ID
439  * @Varargs: response ID for first button, then more text-response_id pairs
440  * 
441  * Adds more buttons, same as calling gtk_dialog_add_button()
442  * repeatedly.  The variable argument list should be NULL-terminated
443  * as with gtk_dialog_new_with_buttons(). Each button must have both
444  * text and response ID.
445  * 
446  **/
447 void
448 gtk_dialog_add_buttons (GtkDialog   *dialog,
449                         const gchar *first_button_text,
450                         ...)
451 {  
452   va_list args;
453
454   va_start (args, first_button_text);
455
456   gtk_dialog_add_buttons_valist (dialog,
457                                  first_button_text,
458                                  args);
459   
460   va_end (args);
461 }
462
463 /**
464  * gtk_dialog_set_response_sensitive:
465  * @dialog: a #GtkDialog
466  * @response_id: a response ID
467  * @setting: %TRUE for sensitive
468  *
469  * Calls gtk_widget_set_sensitive (widget, @setting) for each
470  * widget in the dialog's action area with the given @response_id.
471  * A convenient way to sensitize/desensitize dialog buttons.
472  * 
473  **/
474 void
475 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
476                                    gint       response_id,
477                                    gboolean   setting)
478 {
479   GList *children;
480   GList *tmp_list;
481
482   children = gtk_container_children (GTK_CONTAINER (dialog));
483
484   tmp_list = children;
485   while (tmp_list != NULL)
486     {
487       GtkWidget *widget = tmp_list->data;
488       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
489                                             "gtk-dialog-response-data");
490
491       if (rd && rd->response_id == response_id)
492         gtk_widget_set_sensitive (widget, setting);
493
494       tmp_list = g_list_next (tmp_list);
495     }
496
497   g_list_free (children);
498 }
499
500 /**
501  * gtk_dialog_set_default_response:
502  * @dialog: a #GtkDialog
503  * @response_id: a response ID
504  * 
505  * Sets the last widget in the dialog's action area with the given @response_id
506  * as the default widget for the dialog. Pressing "Enter" normally activates
507  * the default widget.
508  * 
509  **/
510 void
511 gtk_dialog_set_default_response (GtkDialog *dialog,
512                                  gint       response_id)
513 {
514   GList *children;
515   GList *tmp_list;
516
517   children = gtk_container_children (GTK_CONTAINER (dialog->action_area));
518
519   tmp_list = children;
520   while (tmp_list != NULL)
521     {
522       GtkWidget *widget = tmp_list->data;
523       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
524                                             "gtk-dialog-response-data");
525
526       if (rd && rd->response_id == response_id)
527         {
528           gtk_widget_grab_default (widget);
529           
530           if (!GTK_WINDOW (dialog)->focus_widget)
531             gtk_widget_grab_focus (widget);
532         }
533             
534       tmp_list = g_list_next (tmp_list);
535     }
536
537   g_list_free (children);
538 }
539
540 /**
541  * gtk_dialog_response:
542  * @dialog: a #GtkDialog
543  * @response_id: response ID 
544  * 
545  * Emits the "response" signal with the given response ID. Used to
546  * indicate that the user has responded to the dialog in some way;
547  * typically either you or gtk_dialog_run() will be monitoring the
548  * "response" signal and take appropriate action.
549  **/
550 void
551 gtk_dialog_response (GtkDialog *dialog,
552                      gint       response_id)
553 {
554   g_return_if_fail (dialog != NULL);
555   g_return_if_fail (GTK_IS_DIALOG (dialog));
556
557   gtk_signal_emit (GTK_OBJECT (dialog),
558                    dialog_signals[RESPONSE],
559                    response_id);
560 }
561
562 typedef struct
563 {
564   GtkDialog *dialog;
565   gint response_id;
566   GMainLoop *loop;
567 } RunInfo;
568
569 static void
570 shutdown_loop (RunInfo *ri)
571 {
572   if (g_main_loop_is_running (ri->loop))
573     g_main_loop_quit (ri->loop);
574 }
575
576 static void
577 run_unmap_handler (GtkDialog *dialog, gpointer data)
578 {
579   RunInfo *ri = data;
580
581   shutdown_loop (ri);
582 }
583
584 static void
585 run_response_handler (GtkDialog *dialog,
586                       gint response_id,
587                       gpointer data)
588 {
589   RunInfo *ri;
590
591   ri = data;
592
593   ri->response_id = response_id;
594
595   shutdown_loop (ri);
596 }
597
598 static gint
599 run_delete_handler (GtkDialog *dialog,
600                     GdkEventAny *event,
601                     gpointer data)
602 {
603   RunInfo *ri = data;
604     
605   shutdown_loop (ri);
606
607   /* emit response signal */
608   gtk_dialog_response (dialog, GTK_RESPONSE_NONE);
609   
610   return TRUE; /* Do not destroy */
611 }
612
613 /**
614  * gtk_dialog_run:
615  * @dialog: a #GtkDialog
616  * 
617  * Blocks in a recursive main loop until the @dialog either emits the
618  * response signal, or is destroyed. If the dialog is destroyed,
619  * gtk_dialog_run() returns GTK_RESPONSE_NONE. Otherwise, it returns
620  * the response ID from the "response" signal emission. Before
621  * entering the recursive main loop, gtk_dialog_run() calls
622  * gtk_widget_show() on the dialog for you. Note that you still
623  * need to show any children of the dialog yourself.
624  *
625  * During gtk_dialog_run(), the default behavior of "delete_event" is
626  * disabled; if the dialog receives "delete_event", it will not be
627  * destroyed as windows usually are, and gtk_dialog_run() will return
628  * GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
629  * modal. You can force gtk_dialog_run() to return at any time by
630  * calling gtk_dialog_response() to emit the "response"
631  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
632  * idea, because your post-run code won't know whether the dialog was
633  * destroyed or not.
634  *
635  * After gtk_dialog_run() returns, you are responsible for hiding or
636  * destroying the dialog if you wish to do so.
637  *
638  * Typical usage of this function might be:
639  * <programlisting>
640  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
641  *   switch (result)
642  *     {
643  *       case GTK_RESPONSE_ACCEPT:
644  *          do_application_specific_something ();
645  *          break;
646  *       default:
647  *          do_nothing_since_dialog_was_cancelled ();
648  *          break;
649  *     }
650  *   gtk_widget_destroy (dialog);
651  * </programlisting>
652  * 
653  * Return value: response ID
654  **/
655 gint
656 gtk_dialog_run (GtkDialog *dialog)
657 {
658   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
659   gboolean was_modal;
660   guint response_handler;
661   guint destroy_handler;
662   guint delete_handler;
663   
664   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
665
666   gtk_object_ref (GTK_OBJECT (dialog));
667
668   if (!GTK_WIDGET_VISIBLE (dialog))
669     gtk_widget_show (GTK_WIDGET (dialog));
670   
671   was_modal = GTK_WINDOW (dialog)->modal;
672   if (!was_modal)
673     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
674
675   response_handler =
676     gtk_signal_connect (GTK_OBJECT (dialog),
677                         "response",
678                         GTK_SIGNAL_FUNC (run_response_handler),
679                         &ri);
680   
681   destroy_handler =
682     gtk_signal_connect (GTK_OBJECT (dialog),
683                         "unmap",
684                         GTK_SIGNAL_FUNC (run_unmap_handler),
685                         &ri);
686   
687   delete_handler =
688     gtk_signal_connect (GTK_OBJECT (dialog),
689                         "delete_event",
690                         GTK_SIGNAL_FUNC (run_delete_handler),
691                         &ri);
692   
693   ri.loop = g_main_new (FALSE);
694
695   g_main_loop_run (ri.loop);
696
697   g_main_loop_unref (ri.loop);
698
699   ri.loop = NULL;
700   
701   if (!GTK_OBJECT_DESTROYED (dialog))
702     {
703       if (!was_modal)
704         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
705       
706       gtk_signal_disconnect (GTK_OBJECT (dialog), destroy_handler);
707       gtk_signal_disconnect (GTK_OBJECT (dialog), response_handler);
708       gtk_signal_disconnect (GTK_OBJECT (dialog), delete_handler);
709     }
710
711   gtk_object_unref (GTK_OBJECT (dialog));
712
713   return ri.response_id;
714 }
715
716
717
718