]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
new function, turns off decorations for a window.
[~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_box_set_spacing (GTK_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   gint signal_id = 0;
344   
345   g_return_if_fail (GTK_IS_DIALOG (dialog));
346   g_return_if_fail (GTK_IS_WIDGET (child));
347
348   ad = get_response_data (child);
349
350   ad->response_id = response_id;
351
352   if (GTK_IS_BUTTON (child))
353     {
354       signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
355     }
356   else
357     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal != 0;
358
359   if (signal_id)
360     {
361       const gchar* name = gtk_signal_name (signal_id);
362
363       gtk_signal_connect_while_alive (GTK_OBJECT (child),
364                                       name,
365                                       GTK_SIGNAL_FUNC (action_widget_activated),
366                                       dialog,
367                                       GTK_OBJECT (dialog));
368     }
369   else
370     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
371
372   gtk_box_pack_end (GTK_BOX (dialog->action_area),
373                     child,
374                     FALSE, TRUE, 5);  
375 }
376
377 /**
378  * gtk_dialog_add_button:
379  * @dialog: a #GtkDialog
380  * @button_text: text of button, or stock ID
381  * @response_id: response ID for the button
382  * 
383  * Adds a button with the given text (or a stock button, if @button_text is a
384  * stock ID) and sets things up so that clicking the button will emit the
385  * "response" signal with the given @response_id. The button is appended to the
386  * end of the dialog's action area. The button widget is returned, but usually
387  * you don't need it.
388  *
389  * Return value: the button widget that was added
390  **/
391 GtkWidget*
392 gtk_dialog_add_button (GtkDialog   *dialog,
393                        const gchar *button_text,
394                        gint         response_id)
395 {
396   GtkWidget *button;
397   
398   g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
399   g_return_val_if_fail (button_text != NULL, NULL);
400
401   button = gtk_button_new_stock (button_text,
402                                  gtk_window_get_default_accel_group (GTK_WINDOW (dialog)));
403
404   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
405   
406   gtk_widget_show (button);
407   
408   gtk_dialog_add_action_widget (dialog,
409                                 button,
410                                 response_id);
411
412   return button;
413 }
414
415 static void
416 gtk_dialog_add_buttons_valist(GtkDialog      *dialog,
417                               const gchar    *first_button_text,
418                               va_list         args)
419 {
420   const gchar* text;
421   gint response_id;
422
423   g_return_if_fail (GTK_IS_DIALOG (dialog));
424   
425   if (first_button_text == NULL)
426     return;
427   
428   text = first_button_text;
429   response_id = va_arg (args, gint);
430
431   while (text != NULL)
432     {
433       gtk_dialog_add_button (dialog, text, response_id);
434
435       text = va_arg (args, gchar*);
436       if (text == NULL)
437         break;
438       response_id = va_arg (args, int);
439     }
440 }
441
442 /**
443  * gtk_dialog_add_buttons:
444  * @dialog: a #GtkDialog
445  * @first_button_text: button text or stock ID
446  * @Varargs: response ID for first button, then more text-response_id pairs
447  * 
448  * Adds more buttons, same as calling gtk_dialog_add_button()
449  * repeatedly.  The variable argument list should be NULL-terminated
450  * as with gtk_dialog_new_with_buttons(). Each button must have both
451  * text and response ID.
452  * 
453  **/
454 void
455 gtk_dialog_add_buttons (GtkDialog   *dialog,
456                         const gchar *first_button_text,
457                         ...)
458 {  
459   va_list args;
460
461   va_start (args, first_button_text);
462
463   gtk_dialog_add_buttons_valist (dialog,
464                                  first_button_text,
465                                  args);
466   
467   va_end (args);
468 }
469
470 /**
471  * gtk_dialog_set_response_sensitive:
472  * @dialog: a #GtkDialog
473  * @response_id: a response ID
474  * @setting: %TRUE for sensitive
475  *
476  * Calls gtk_widget_set_sensitive (widget, @setting) for each
477  * widget in the dialog's action area with the given @response_id.
478  * A convenient way to sensitize/desensitize dialog buttons.
479  * 
480  **/
481 void
482 gtk_dialog_set_response_sensitive (GtkDialog *dialog,
483                                    gint       response_id,
484                                    gboolean   setting)
485 {
486   GList *children;
487   GList *tmp_list;
488
489   children = gtk_container_children (GTK_CONTAINER (dialog));
490
491   tmp_list = children;
492   while (tmp_list != NULL)
493     {
494       GtkWidget *widget = tmp_list->data;
495       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
496                                             "gtk-dialog-response-data");
497
498       if (rd && rd->response_id == response_id)
499         gtk_widget_set_sensitive (widget, setting);
500
501       tmp_list = g_list_next (tmp_list);
502     }
503
504   g_list_free (children);
505 }
506
507 /**
508  * gtk_dialog_set_default_response:
509  * @dialog: a #GtkDialog
510  * @response_id: a response ID
511  * 
512  * Sets the last widget in the dialog's action area with the given @response_id
513  * as the default widget for the dialog. Pressing "Enter" normally activates
514  * the default widget.
515  * 
516  **/
517 void
518 gtk_dialog_set_default_response (GtkDialog *dialog,
519                                  gint       response_id)
520 {
521   GList *children;
522   GList *tmp_list;
523
524   children = gtk_container_children (GTK_CONTAINER (dialog->action_area));
525
526   tmp_list = children;
527   while (tmp_list != NULL)
528     {
529       GtkWidget *widget = tmp_list->data;
530       ResponseData *rd = g_object_get_data (G_OBJECT (widget),
531                                             "gtk-dialog-response-data");
532
533       if (rd && rd->response_id == response_id)
534         {
535           gtk_widget_grab_default (widget);
536           
537           if (!GTK_WINDOW (dialog)->focus_widget)
538             gtk_widget_grab_focus (widget);
539         }
540             
541       tmp_list = g_list_next (tmp_list);
542     }
543
544   g_list_free (children);
545 }
546
547 /**
548  * gtk_dialog_response:
549  * @dialog: a #GtkDialog
550  * @response_id: response ID 
551  * 
552  * Emits the "response" signal with the given response ID. Used to
553  * indicate that the user has responded to the dialog in some way;
554  * typically either you or gtk_dialog_run() will be monitoring the
555  * "response" signal and take appropriate action.
556  **/
557 void
558 gtk_dialog_response (GtkDialog *dialog,
559                      gint       response_id)
560 {
561   g_return_if_fail (dialog != NULL);
562   g_return_if_fail (GTK_IS_DIALOG (dialog));
563
564   gtk_signal_emit (GTK_OBJECT (dialog),
565                    dialog_signals[RESPONSE],
566                    response_id);
567 }
568
569 typedef struct
570 {
571   GtkDialog *dialog;
572   gint response_id;
573   GMainLoop *loop;
574 } RunInfo;
575
576 static void
577 shutdown_loop (RunInfo *ri)
578 {
579   if (g_main_loop_is_running (ri->loop))
580     g_main_loop_quit (ri->loop);
581 }
582
583 static void
584 run_unmap_handler (GtkDialog *dialog, gpointer data)
585 {
586   RunInfo *ri = data;
587
588   shutdown_loop (ri);
589 }
590
591 static void
592 run_response_handler (GtkDialog *dialog,
593                       gint response_id,
594                       gpointer data)
595 {
596   RunInfo *ri;
597
598   ri = data;
599
600   ri->response_id = response_id;
601
602   shutdown_loop (ri);
603 }
604
605 static gint
606 run_delete_handler (GtkDialog *dialog,
607                     GdkEventAny *event,
608                     gpointer data)
609 {
610   RunInfo *ri = data;
611     
612   shutdown_loop (ri);
613
614   /* emit response signal */
615   gtk_dialog_response (dialog, GTK_RESPONSE_NONE);
616   
617   return TRUE; /* Do not destroy */
618 }
619
620 /**
621  * gtk_dialog_run:
622  * @dialog: a #GtkDialog
623  * 
624  * Blocks in a recursive main loop until the @dialog either emits the
625  * response signal, or is destroyed. If the dialog is destroyed,
626  * gtk_dialog_run() returns GTK_RESPONSE_NONE. Otherwise, it returns
627  * the response ID from the "response" signal emission. Before
628  * entering the recursive main loop, gtk_dialog_run() calls
629  * gtk_widget_show() on the dialog for you. Note that you still
630  * need to show any children of the dialog yourself.
631  *
632  * During gtk_dialog_run(), the default behavior of "delete_event" is
633  * disabled; if the dialog receives "delete_event", it will not be
634  * destroyed as windows usually are, and gtk_dialog_run() will return
635  * GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
636  * modal. You can force gtk_dialog_run() to return at any time by
637  * calling gtk_dialog_response() to emit the "response"
638  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
639  * idea, because your post-run code won't know whether the dialog was
640  * destroyed or not.
641  *
642  * After gtk_dialog_run() returns, you are responsible for hiding or
643  * destroying the dialog if you wish to do so.
644  *
645  * Typical usage of this function might be:
646  * <programlisting>
647  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
648  *   switch (result)
649  *     {
650  *       case GTK_RESPONSE_ACCEPT:
651  *          do_application_specific_something ();
652  *          break;
653  *       default:
654  *          do_nothing_since_dialog_was_cancelled ();
655  *          break;
656  *     }
657  *   gtk_widget_destroy (dialog);
658  * </programlisting>
659  * 
660  * Return value: response ID
661  **/
662 gint
663 gtk_dialog_run (GtkDialog *dialog)
664 {
665   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
666   gboolean was_modal;
667   guint response_handler;
668   guint destroy_handler;
669   guint delete_handler;
670   
671   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
672
673   gtk_object_ref (GTK_OBJECT (dialog));
674
675   if (!GTK_WIDGET_VISIBLE (dialog))
676     gtk_widget_show (GTK_WIDGET (dialog));
677   
678   was_modal = GTK_WINDOW (dialog)->modal;
679   if (!was_modal)
680     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
681
682   response_handler =
683     gtk_signal_connect (GTK_OBJECT (dialog),
684                         "response",
685                         GTK_SIGNAL_FUNC (run_response_handler),
686                         &ri);
687   
688   destroy_handler =
689     gtk_signal_connect (GTK_OBJECT (dialog),
690                         "unmap",
691                         GTK_SIGNAL_FUNC (run_unmap_handler),
692                         &ri);
693   
694   delete_handler =
695     gtk_signal_connect (GTK_OBJECT (dialog),
696                         "delete_event",
697                         GTK_SIGNAL_FUNC (run_delete_handler),
698                         &ri);
699   
700   ri.loop = g_main_new (FALSE);
701
702   g_main_loop_run (ri.loop);
703
704   g_main_loop_unref (ri.loop);
705
706   ri.loop = NULL;
707   
708   if (!GTK_OBJECT_DESTROYED (dialog))
709     {
710       if (!was_modal)
711         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
712       
713       gtk_signal_disconnect (GTK_OBJECT (dialog), destroy_handler);
714       gtk_signal_disconnect (GTK_OBJECT (dialog), response_handler);
715       gtk_signal_disconnect (GTK_OBJECT (dialog), delete_handler);
716     }
717
718   gtk_object_unref (GTK_OBJECT (dialog));
719
720   return ri.response_id;
721 }
722
723
724
725