]> Pileus Git - ~andy/gtk/blob - gtk/gtkdialog.c
handle case where there are no rows in the model
[~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));
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         gtk_widget_grab_default (widget);
525
526       tmp_list = g_list_next (tmp_list);
527     }
528
529   g_list_free (children);
530 }
531
532 /**
533  * gtk_dialog_response:
534  * @dialog: a #GtkDialog
535  * @response_id: response ID 
536  * 
537  * Emits the "response" signal with the given response ID. Used to
538  * indicate that the user has responded to the dialog in some way;
539  * typically either you or gtk_dialog_run() will be monitoring the
540  * "response" signal and take appropriate action.
541  **/
542 void
543 gtk_dialog_response (GtkDialog *dialog,
544                      gint       response_id)
545 {
546   g_return_if_fail (dialog != NULL);
547   g_return_if_fail (GTK_IS_DIALOG (dialog));
548
549   gtk_signal_emit (GTK_OBJECT (dialog),
550                    dialog_signals[RESPONSE],
551                    response_id);
552 }
553
554 typedef struct
555 {
556   GtkDialog *dialog;
557   gint response_id;
558   GMainLoop *loop;
559 } RunInfo;
560
561 static void
562 shutdown_loop (RunInfo *ri)
563 {
564   if (g_main_loop_is_running (ri->loop))
565     g_main_loop_quit (ri->loop);
566 }
567
568 static void
569 run_unmap_handler (GtkDialog *dialog, gpointer data)
570 {
571   RunInfo *ri = data;
572
573   shutdown_loop (ri);
574 }
575
576 static void
577 run_response_handler (GtkDialog *dialog,
578                       gint response_id,
579                       gpointer data)
580 {
581   RunInfo *ri;
582
583   ri = data;
584
585   ri->response_id = response_id;
586
587   shutdown_loop (ri);
588 }
589
590 static gint
591 run_delete_handler (GtkDialog *dialog,
592                     GdkEventAny *event,
593                     gpointer data)
594 {
595   RunInfo *ri = data;
596     
597   shutdown_loop (ri);
598
599   /* emit response signal */
600   gtk_dialog_response (dialog, GTK_RESPONSE_NONE);
601   
602   return TRUE; /* Do not destroy */
603 }
604
605 /**
606  * gtk_dialog_run:
607  * @dialog: a #GtkDialog
608  * 
609  * Blocks in a recursive main loop until the @dialog either emits the
610  * response signal, or is destroyed. If the dialog is destroyed,
611  * gtk_dialog_run() returns GTK_RESPONSE_NONE. Otherwise, it returns
612  * the response ID from the "response" signal emission. Before
613  * entering the recursive main loop, gtk_dialog_run() calls
614  * gtk_widget_show() on the dialog for you. Note that you still
615  * need to show any children of the dialog yourself.
616  *
617  * During gtk_dialog_run(), the default behavior of "delete_event" is
618  * disabled; if the dialog receives "delete_event", it will not be
619  * destroyed as windows usually are, and gtk_dialog_run() will return
620  * GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog will be
621  * modal. You can force gtk_dialog_run() to return at any time by
622  * calling gtk_dialog_response() to emit the "response"
623  * signal. Destroying the dialog during gtk_dialog_run() is a very bad
624  * idea, because your post-run code won't know whether the dialog was
625  * destroyed or not.
626  *
627  * After gtk_dialog_run() returns, you are responsible for hiding or
628  * destroying the dialog if you wish to do so.
629  *
630  * Typical usage of this function might be:
631  * <programlisting>
632  *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
633  *   switch (result)
634  *     {
635  *       case GTK_RESPONSE_ACCEPT:
636  *          do_application_specific_something ();
637  *          break;
638  *       default:
639  *          do_nothing_since_dialog_was_cancelled ();
640  *          break;
641  *     }
642  *   gtk_widget_destroy (dialog);
643  * </programlisting>
644  * 
645  * Return value: response ID
646  **/
647 gint
648 gtk_dialog_run (GtkDialog *dialog)
649 {
650   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
651   gboolean was_modal;
652   guint response_handler;
653   guint destroy_handler;
654   guint delete_handler;
655   
656   g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);
657
658   gtk_object_ref (GTK_OBJECT (dialog));
659
660   if (!GTK_WIDGET_VISIBLE (dialog))
661     gtk_widget_show (GTK_WIDGET (dialog));
662   
663   was_modal = GTK_WINDOW (dialog)->modal;
664   if (!was_modal)
665     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
666
667   response_handler =
668     gtk_signal_connect (GTK_OBJECT (dialog),
669                         "response",
670                         GTK_SIGNAL_FUNC (run_response_handler),
671                         &ri);
672   
673   destroy_handler =
674     gtk_signal_connect (GTK_OBJECT (dialog),
675                         "unmap",
676                         GTK_SIGNAL_FUNC (run_unmap_handler),
677                         &ri);
678   
679   delete_handler =
680     gtk_signal_connect (GTK_OBJECT (dialog),
681                         "delete_event",
682                         GTK_SIGNAL_FUNC (run_delete_handler),
683                         &ri);
684   
685   ri.loop = g_main_new (FALSE);
686
687   g_main_loop_run (ri.loop);
688
689   g_main_loop_unref (ri.loop);
690
691   ri.loop = NULL;
692   
693   if (!GTK_OBJECT_DESTROYED (dialog))
694     {
695       if (!was_modal)
696         gtk_window_set_modal (GTK_WINDOW(dialog), FALSE);
697       
698       gtk_signal_disconnect (GTK_OBJECT (dialog), destroy_handler);
699       gtk_signal_disconnect (GTK_OBJECT (dialog), response_handler);
700       gtk_signal_disconnect (GTK_OBJECT (dialog), delete_handler);
701     }
702
703   gtk_object_unref (GTK_OBJECT (dialog));
704
705   return ri.response_id;
706 }
707
708
709
710