]> Pileus Git - ~andy/gtk/blob - tests/testfilechooser.c
Add a --backend option. (#516073, Christian Persch)
[~andy/gtk] / tests / testfilechooser.c
1 /* testfilechooser.c
2  * Copyright (C) 2003  Red Hat, Inc.
3  * Author: Owen Taylor
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #include <config.h>
21
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <gtk/gtk.h>
31
32 #ifdef G_OS_WIN32
33 #  include <io.h>
34 #  define localtime_r(t,b) *(b) = *localtime (t)
35 #  ifndef S_ISREG
36 #    define S_ISREG(m) ((m) & _S_IFREG)
37 #  endif
38 #endif
39
40 #include "prop-editor.h"
41
42 static GtkWidget *preview_label;
43 static GtkWidget *preview_image;
44 static GtkFileChooserAction action;
45
46 static void
47 print_current_folder (GtkFileChooser *chooser)
48 {
49   gchar *uri;
50
51   uri = gtk_file_chooser_get_current_folder_uri (chooser);
52   g_print ("Current folder changed :\n  %s\n", uri ? uri : "(null)");
53   g_free (uri);
54 }
55
56 static void
57 print_selected (GtkFileChooser *chooser)
58 {
59   GSList *uris = gtk_file_chooser_get_uris (chooser);
60   GSList *tmp_list;
61
62   g_print ("Selection changed :\n");
63   for (tmp_list = uris; tmp_list; tmp_list = tmp_list->next)
64     {
65       gchar *uri = tmp_list->data;
66       g_print ("  %s\n", uri);
67       g_free (uri);
68     }
69   g_print ("\n");
70   g_slist_free (uris);
71 }
72
73 static void
74 response_cb (GtkDialog *dialog,
75              gint       response_id)
76 {
77   if (response_id == GTK_RESPONSE_OK)
78     {
79       GSList *list;
80
81       list = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
82
83       if (list)
84         {
85           GSList *l;
86
87           g_print ("Selected files:\n");
88
89           for (l = list; l; l = l->next)
90             {
91               g_print ("%s\n", (char *) l->data);
92               g_free (l->data);
93             }
94
95           g_slist_free (list);
96         }
97       else
98         g_print ("No selected files\n");
99     }
100   else
101     g_print ("Dialog was closed\n");
102
103   gtk_main_quit ();
104 }
105
106 static gboolean
107 no_backup_files_filter (const GtkFileFilterInfo *filter_info,
108                         gpointer                 data)
109 {
110   gsize len = filter_info->display_name ? strlen (filter_info->display_name) : 0;
111   if (len > 0 && filter_info->display_name[len - 1] == '~')
112     return 0;
113   else
114     return 1;
115 }
116
117 static void
118 filter_changed (GtkFileChooserDialog *dialog,
119                 gpointer              data)
120 {
121   g_print ("file filter changed\n");
122 }
123
124 static char *
125 format_time (time_t t)
126 {
127   gchar buf[128];
128   struct tm tm_buf;
129   time_t now = time (NULL);
130   const char *format;
131
132   if (abs (now - t) < 24*60*60)
133     format = "%X";
134   else
135     format = "%x";
136
137   localtime_r (&t, &tm_buf);
138   if (strftime (buf, sizeof (buf), format, &tm_buf) == 0)
139     return g_strdup ("<unknown>");
140   else
141     return g_strdup (buf);
142 }
143
144 static char *
145 format_size (gint64 size)
146 {
147   if (size < (gint64)1024)
148     return g_strdup_printf ("%d bytes", (gint)size);
149   else if (size < (gint64)1024*1024)
150     return g_strdup_printf ("%.1f K", size / (1024.));
151   else if (size < (gint64)1024*1024*1024)
152     return g_strdup_printf ("%.1f M", size / (1024.*1024.));
153   else
154     return g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.));
155 }
156
157 #include <stdio.h>
158 #include <errno.h>
159 #define _(s) (s)
160
161 static void
162 size_prepared_cb (GdkPixbufLoader *loader,
163                   int              width,
164                   int              height,
165                   int             *data)
166 {
167         int des_width = data[0];
168         int des_height = data[1];
169
170         if (des_height >= height && des_width >= width) {
171                 /* Nothing */
172         } else if ((double)height * des_width > (double)width * des_height) {
173                 width = 0.5 + (double)width * des_height / (double)height;
174                 height = des_height;
175         } else {
176                 height = 0.5 + (double)height * des_width / (double)width;
177                 width = des_width;
178         }
179
180         gdk_pixbuf_loader_set_size (loader, width, height);
181 }
182
183 GdkPixbuf *
184 my_new_from_file_at_size (const char *filename,
185                           int         width,
186                           int         height,
187                           GError    **error)
188 {
189         GdkPixbufLoader *loader;
190         GdkPixbuf       *pixbuf;
191         int              info[2];
192         struct stat st;
193
194         guchar buffer [4096];
195         int length;
196         FILE *f;
197
198         g_return_val_if_fail (filename != NULL, NULL);
199         g_return_val_if_fail (width > 0 && height > 0, NULL);
200
201         if (stat (filename, &st) != 0) {
202                 g_set_error (error,
203                              G_FILE_ERROR,
204                              g_file_error_from_errno (errno),
205                              _("Could not get information for file '%s': %s"),
206                              filename, g_strerror (errno));
207                 return NULL;
208         }
209
210         if (!S_ISREG (st.st_mode))
211                 return NULL;
212
213         f = fopen (filename, "rb");
214         if (!f) {
215                 g_set_error (error,
216                              G_FILE_ERROR,
217                              g_file_error_from_errno (errno),
218                              _("Failed to open file '%s': %s"),
219                              filename, g_strerror (errno));
220                 return NULL;
221         }
222
223         loader = gdk_pixbuf_loader_new ();
224 #ifdef DONT_PRESERVE_ASPECT
225         gdk_pixbuf_loader_set_size (loader, width, height);
226 #else
227         info[0] = width;
228         info[1] = height;
229         g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), info);
230 #endif
231
232         while (!feof (f)) {
233                 length = fread (buffer, 1, sizeof (buffer), f);
234                 if (length > 0)
235                         if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
236                                 gdk_pixbuf_loader_close (loader, NULL);
237                                 fclose (f);
238                                 g_object_unref (loader);
239                                 return NULL;
240                         }
241         }
242
243         fclose (f);
244
245         g_assert (*error == NULL);
246         if (!gdk_pixbuf_loader_close (loader, error)) {
247                 g_object_unref (loader);
248                 return NULL;
249         }
250
251         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
252
253         if (!pixbuf) {
254                 g_object_unref (loader);
255
256                 /* did the loader set an error? */
257                 if (*error != NULL)
258                         return NULL;
259
260                 g_set_error (error,
261                              GDK_PIXBUF_ERROR,
262                              GDK_PIXBUF_ERROR_FAILED,
263                              _("Failed to load image '%s': reason not known, probably a corrupt image file"),
264                              filename);
265                 return NULL;
266         }
267
268         g_object_ref (pixbuf);
269
270         g_object_unref (loader);
271
272         return pixbuf;
273 }
274
275 static void
276 update_preview_cb (GtkFileChooser *chooser)
277 {
278   gchar *filename = gtk_file_chooser_get_preview_filename (chooser);
279   gboolean have_preview = FALSE;
280
281   if (filename)
282     {
283       GdkPixbuf *pixbuf;
284       GError *error = NULL;
285
286       pixbuf = my_new_from_file_at_size (filename, 128, 128, &error);
287       if (pixbuf)
288         {
289           gtk_image_set_from_pixbuf (GTK_IMAGE (preview_image), pixbuf);
290           g_object_unref (pixbuf);
291           gtk_widget_show (preview_image);
292           gtk_widget_hide (preview_label);
293           have_preview = TRUE;
294         }
295       else
296         {
297           struct stat buf;
298           if (stat (filename, &buf) == 0)
299             {
300               gchar *preview_text;
301               gchar *size_str;
302               gchar *modified_time;
303
304               size_str = format_size (buf.st_size);
305               modified_time = format_time (buf.st_mtime);
306
307               preview_text = g_strdup_printf ("<i>Modified:</i>\t%s\n"
308                                               "<i>Size:</i>\t%s\n",
309                                               modified_time,
310                                               size_str);
311               gtk_label_set_markup (GTK_LABEL (preview_label), preview_text);
312               g_free (modified_time);
313               g_free (size_str);
314               g_free (preview_text);
315
316               gtk_widget_hide (preview_image);
317               gtk_widget_show (preview_label);
318               have_preview = TRUE;
319             }
320         }
321
322       g_free (filename);
323
324       if (error)
325         g_error_free (error);
326     }
327
328   gtk_file_chooser_set_preview_widget_active (chooser, have_preview);
329 }
330
331 static void
332 set_current_folder (GtkFileChooser *chooser,
333                     const char     *name)
334 {
335   if (!gtk_file_chooser_set_current_folder (chooser, name))
336     {
337       GtkWidget *dialog;
338
339       dialog = gtk_message_dialog_new (GTK_WINDOW (chooser),
340                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
341                                        GTK_MESSAGE_ERROR,
342                                        GTK_BUTTONS_CLOSE,
343                                        "Could not set the folder to %s",
344                                        name);
345       gtk_dialog_run (GTK_DIALOG (dialog));
346       gtk_widget_destroy (dialog);
347     }
348 }
349
350 static void
351 set_folder_nonexistent_cb (GtkButton      *button,
352                            GtkFileChooser *chooser)
353 {
354   set_current_folder (chooser, "/nonexistent");
355 }
356
357 static void
358 set_folder_existing_nonexistent_cb (GtkButton      *button,
359                                     GtkFileChooser *chooser)
360 {
361   set_current_folder (chooser, "/usr/nonexistent");
362 }
363
364 static void
365 set_filename (GtkFileChooser *chooser,
366               const char     *name)
367 {
368   if (!gtk_file_chooser_set_filename (chooser, name))
369     {
370       GtkWidget *dialog;
371
372       dialog = gtk_message_dialog_new (GTK_WINDOW (chooser),
373                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
374                                        GTK_MESSAGE_ERROR,
375                                        GTK_BUTTONS_CLOSE,
376                                        "Could not select %s",
377                                        name);
378       gtk_dialog_run (GTK_DIALOG (dialog));
379       gtk_widget_destroy (dialog);
380     }
381 }
382
383 static void
384 set_filename_nonexistent_cb (GtkButton      *button,
385                              GtkFileChooser *chooser)
386 {
387   set_filename (chooser, "/nonexistent");
388 }
389
390 static void
391 set_filename_existing_nonexistent_cb (GtkButton      *button,
392                                       GtkFileChooser *chooser)
393 {
394   set_filename (chooser, "/usr/nonexistent");
395 }
396
397 static void
398 unmap_and_remap_cb (GtkButton *button,
399                     GtkFileChooser *chooser)
400 {
401   gtk_widget_hide (GTK_WIDGET (chooser));
402   gtk_widget_show (GTK_WIDGET (chooser));
403 }
404
405 static void
406 kill_dependent (GtkWindow *win, GtkObject *dep)
407 {
408   gtk_object_destroy (dep);
409   g_object_unref (dep);
410 }
411
412 static void
413 notify_multiple_cb (GtkWidget  *dialog,
414                     GParamSpec *pspec,
415                     GtkWidget  *button)
416 {
417   gboolean multiple;
418
419   multiple = gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (dialog));
420
421   gtk_widget_set_sensitive (button, multiple);
422 }
423
424 static GtkFileChooserConfirmation
425 confirm_overwrite_cb (GtkFileChooser *chooser,
426                       gpointer        data)
427 {
428   GtkWidget *dialog;
429   GtkWidget *button;
430   int response;
431   GtkFileChooserConfirmation conf;
432
433   dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (chooser))),
434                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
435                                    GTK_MESSAGE_QUESTION,
436                                    GTK_BUTTONS_NONE,
437                                    "What do you want to do?");
438
439   button = gtk_button_new_with_label ("Use the stock confirmation dialog");
440   gtk_widget_show (button);
441   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 1);
442
443   button = gtk_button_new_with_label ("Type a new file name");
444   gtk_widget_show (button);
445   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 2);
446
447   button = gtk_button_new_with_label ("Accept the file name");
448   gtk_widget_show (button);
449   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 3);
450
451   response = gtk_dialog_run (GTK_DIALOG (dialog));
452
453   switch (response)
454     {
455     case 1:
456       conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
457       break;
458
459     case 3:
460       conf = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
461       break;
462
463     default:
464       conf = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
465       break;
466     }
467
468   gtk_widget_destroy (dialog);
469
470   return conf;
471 }
472
473 int
474 main (int argc, char **argv)
475 {
476   GtkWidget *control_window;
477   GtkWidget *vbbox;
478   GtkWidget *button;
479   GtkWidget *dialog;
480   GtkWidget *prop_editor;
481   GtkWidget *extra;
482   GtkFileFilter *filter;
483   GtkWidget *preview_vbox;
484   gboolean force_rtl = FALSE;
485   gboolean multiple = FALSE;
486   char *action_arg = NULL;
487   char *backend = NULL;
488   GError *error = NULL;
489   GOptionEntry options[] = {
490     { "action", 'a', 0, G_OPTION_ARG_STRING, &action, "Filechooser action", "ACTION" },
491     { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Filechooser backend (default: gtk+)", "BACKEND" },
492     { "multiple", 'm', 0, G_OPTION_ARG_NONE, &multiple, "Select-multiple", NULL },
493     { "right-to-left", 'r', 0, G_OPTION_ARG_NONE, &force_rtl, "Force right-to-left layout.", NULL },
494     { NULL }
495   };
496
497   if (!gtk_init_with_args (&argc, &argv, "", options, NULL, &error))
498     {
499       g_print ("Failed to parse args: %s\n", error->message);
500       g_error_free (error);
501       return 1;
502     }
503
504   if (force_rtl)
505     gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
506
507   action = GTK_FILE_CHOOSER_ACTION_OPEN;
508
509   if (action_arg != NULL)
510     {
511       if (! strcmp ("open", action_arg))
512         action = GTK_FILE_CHOOSER_ACTION_OPEN;
513       else if (! strcmp ("save", action_arg))
514         action = GTK_FILE_CHOOSER_ACTION_SAVE;
515       else if (! strcmp ("select_folder", action_arg))
516         action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
517       else if (! strcmp ("create_folder", action_arg))
518         action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
519
520       g_free (action_arg);
521     }
522
523   if (backend == NULL)
524     backend = g_strdup ("gtk+");
525
526   dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
527                          "action", action,
528                          "file-system-backend", backend,
529                          "select-multiple", multiple,
530                          NULL);
531
532   g_free (backend);
533
534   switch (action)
535     {
536     case GTK_FILE_CHOOSER_ACTION_OPEN:
537     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
538       gtk_window_set_title (GTK_WINDOW (dialog), "Select a file");
539       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
540                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
541                               GTK_STOCK_OPEN, GTK_RESPONSE_OK,
542                               NULL);
543       break;
544     case GTK_FILE_CHOOSER_ACTION_SAVE:
545     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
546       gtk_window_set_title (GTK_WINDOW (dialog), "Save a file");
547       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
548                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
549                               GTK_STOCK_SAVE, GTK_RESPONSE_OK,
550                               NULL);
551       break;
552     }
553   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
554
555   g_signal_connect (dialog, "selection-changed",
556                     G_CALLBACK (print_selected), NULL);
557   g_signal_connect (dialog, "current-folder-changed",
558                     G_CALLBACK (print_current_folder), NULL);
559   g_signal_connect (dialog, "response",
560                     G_CALLBACK (response_cb), NULL);
561   g_signal_connect (dialog, "confirm-overwrite",
562                     G_CALLBACK (confirm_overwrite_cb), NULL);
563
564   /* Filters */
565   filter = gtk_file_filter_new ();
566   gtk_file_filter_set_name (filter, "All Files");
567   gtk_file_filter_add_pattern (filter, "*");
568   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
569
570   filter = gtk_file_filter_new ();
571   gtk_file_filter_set_name (filter, "No backup files");
572   gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_DISPLAY_NAME,
573                               no_backup_files_filter, NULL, NULL);
574   gtk_file_filter_add_mime_type (filter, "image/png");
575   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
576
577   g_signal_connect (dialog, "notify::filter",
578                     G_CALLBACK (filter_changed), NULL);
579
580   /* Make this filter the default */
581   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
582
583   filter = gtk_file_filter_new ();
584   gtk_file_filter_set_name (filter, "PNG and JPEG");
585   gtk_file_filter_add_mime_type (filter, "image/jpeg");
586   gtk_file_filter_add_mime_type (filter, "image/png");
587   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
588
589   filter = gtk_file_filter_new ();
590   gtk_file_filter_set_name (filter, "Images");
591   gtk_file_filter_add_pixbuf_formats (filter);
592   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
593
594   /* Preview widget */
595   /* THIS IS A TERRIBLE PREVIEW WIDGET, AND SHOULD NOT BE COPIED AT ALL.
596    */
597   preview_vbox = gtk_vbox_new (0, FALSE);
598   /*gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox);*/
599
600   preview_label = gtk_label_new (NULL);
601   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_label, TRUE, TRUE, 0);
602   gtk_misc_set_padding (GTK_MISC (preview_label), 6, 6);
603
604   preview_image = gtk_image_new ();
605   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_image, TRUE, TRUE, 0);
606   gtk_misc_set_padding (GTK_MISC (preview_image), 6, 6);
607
608   update_preview_cb (GTK_FILE_CHOOSER (dialog));
609   g_signal_connect (dialog, "update-preview",
610                     G_CALLBACK (update_preview_cb), NULL);
611
612   /* Extra widget */
613
614   extra = gtk_check_button_new_with_mnemonic ("Lar_t whoever asks about this button");
615   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extra), TRUE);
616   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), extra);
617
618   /* Shortcuts */
619
620   gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (dialog),
621                                             "file:///usr/share/pixmaps",
622                                             NULL);
623
624   /* show_all() to reveal bugs in composite widget handling */
625   gtk_widget_show_all (dialog);
626
627   /* Extra controls for manipulating the test environment
628    */
629   prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);
630
631   control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
632
633   vbbox = gtk_vbutton_box_new ();
634   gtk_container_add (GTK_CONTAINER (control_window), vbbox);
635
636   button = gtk_button_new_with_mnemonic ("_Select all");
637   gtk_widget_set_sensitive (button, multiple);
638   gtk_container_add (GTK_CONTAINER (vbbox), button);
639   g_signal_connect_swapped (button, "clicked",
640                             G_CALLBACK (gtk_file_chooser_select_all), dialog);
641   g_signal_connect (dialog, "notify::select-multiple",
642                     G_CALLBACK (notify_multiple_cb), button);
643
644   button = gtk_button_new_with_mnemonic ("_Unselect all");
645   gtk_container_add (GTK_CONTAINER (vbbox), button);
646   g_signal_connect_swapped (button, "clicked",
647                             G_CALLBACK (gtk_file_chooser_unselect_all), dialog);
648
649   button = gtk_button_new_with_label ("set_current_folder (\"/nonexistent\")");
650   gtk_container_add (GTK_CONTAINER (vbbox), button);
651   g_signal_connect (button, "clicked",
652                     G_CALLBACK (set_folder_nonexistent_cb), dialog);
653
654   button = gtk_button_new_with_label ("set_current_folder (\"/usr/nonexistent\")");
655   gtk_container_add (GTK_CONTAINER (vbbox), button);
656   g_signal_connect (button, "clicked",
657                     G_CALLBACK (set_folder_existing_nonexistent_cb), dialog);
658
659   button = gtk_button_new_with_label ("set_filename (\"/nonexistent\")");
660   gtk_container_add (GTK_CONTAINER (vbbox), button);
661   g_signal_connect (button, "clicked",
662                     G_CALLBACK (set_filename_nonexistent_cb), dialog);
663
664   button = gtk_button_new_with_label ("set_filename (\"/usr/nonexistent\")");
665   gtk_container_add (GTK_CONTAINER (vbbox), button);
666   g_signal_connect (button, "clicked",
667                     G_CALLBACK (set_filename_existing_nonexistent_cb), dialog);
668
669   button = gtk_button_new_with_label ("Unmap and remap");
670   gtk_container_add (GTK_CONTAINER (vbbox), button);
671   g_signal_connect (button, "clicked",
672                     G_CALLBACK (unmap_and_remap_cb), dialog);
673
674   gtk_widget_show_all (control_window);
675
676   g_object_ref (control_window);
677   g_signal_connect (dialog, "destroy",
678                     G_CALLBACK (kill_dependent), control_window);
679
680   /* We need to hold a ref until we have destroyed the widgets, just in case
681    * someone else destroys them.  We explicitly destroy windows to catch leaks.
682    */
683   g_object_ref (dialog);
684   gtk_main ();
685   gtk_widget_destroy (dialog);
686   g_object_unref (dialog);
687
688   return 0;
689 }