]> Pileus Git - ~andy/gtk/blob - tests/testfilechooser.c
4e6da60f442ed39a7ceb19d578e7046083330a4f
[~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                 int errsv = errno;
203
204                 g_set_error (error,
205                              G_FILE_ERROR,
206                              g_file_error_from_errno (errsv),
207                              _("Could not get information for file '%s': %s"),
208                              filename, g_strerror (errsv));
209                 return NULL;
210         }
211
212         if (!S_ISREG (st.st_mode))
213                 return NULL;
214
215         f = fopen (filename, "rb");
216         if (!f) {
217                 int errsv = errno;
218
219                 g_set_error (error,
220                              G_FILE_ERROR,
221                              g_file_error_from_errno (errsv),
222                              _("Failed to open file '%s': %s"),
223                              filename, g_strerror (errsv));
224                 return NULL;
225         }
226
227         loader = gdk_pixbuf_loader_new ();
228 #ifdef DONT_PRESERVE_ASPECT
229         gdk_pixbuf_loader_set_size (loader, width, height);
230 #else
231         info[0] = width;
232         info[1] = height;
233         g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), info);
234 #endif
235
236         while (!feof (f)) {
237                 length = fread (buffer, 1, sizeof (buffer), f);
238                 if (length > 0)
239                         if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
240                                 gdk_pixbuf_loader_close (loader, NULL);
241                                 fclose (f);
242                                 g_object_unref (loader);
243                                 return NULL;
244                         }
245         }
246
247         fclose (f);
248
249         g_assert (*error == NULL);
250         if (!gdk_pixbuf_loader_close (loader, error)) {
251                 g_object_unref (loader);
252                 return NULL;
253         }
254
255         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
256
257         if (!pixbuf) {
258                 g_object_unref (loader);
259
260                 /* did the loader set an error? */
261                 if (*error != NULL)
262                         return NULL;
263
264                 g_set_error (error,
265                              GDK_PIXBUF_ERROR,
266                              GDK_PIXBUF_ERROR_FAILED,
267                              _("Failed to load image '%s': reason not known, probably a corrupt image file"),
268                              filename);
269                 return NULL;
270         }
271
272         g_object_ref (pixbuf);
273
274         g_object_unref (loader);
275
276         return pixbuf;
277 }
278
279 static void
280 update_preview_cb (GtkFileChooser *chooser)
281 {
282   gchar *filename = gtk_file_chooser_get_preview_filename (chooser);
283   gboolean have_preview = FALSE;
284
285   if (filename)
286     {
287       GdkPixbuf *pixbuf;
288       GError *error = NULL;
289
290       pixbuf = my_new_from_file_at_size (filename, 128, 128, &error);
291       if (pixbuf)
292         {
293           gtk_image_set_from_pixbuf (GTK_IMAGE (preview_image), pixbuf);
294           g_object_unref (pixbuf);
295           gtk_widget_show (preview_image);
296           gtk_widget_hide (preview_label);
297           have_preview = TRUE;
298         }
299       else
300         {
301           struct stat buf;
302           if (stat (filename, &buf) == 0)
303             {
304               gchar *preview_text;
305               gchar *size_str;
306               gchar *modified_time;
307
308               size_str = format_size (buf.st_size);
309               modified_time = format_time (buf.st_mtime);
310
311               preview_text = g_strdup_printf ("<i>Modified:</i>\t%s\n"
312                                               "<i>Size:</i>\t%s\n",
313                                               modified_time,
314                                               size_str);
315               gtk_label_set_markup (GTK_LABEL (preview_label), preview_text);
316               g_free (modified_time);
317               g_free (size_str);
318               g_free (preview_text);
319
320               gtk_widget_hide (preview_image);
321               gtk_widget_show (preview_label);
322               have_preview = TRUE;
323             }
324         }
325
326       g_free (filename);
327
328       if (error)
329         g_error_free (error);
330     }
331
332   gtk_file_chooser_set_preview_widget_active (chooser, have_preview);
333 }
334
335 static void
336 set_current_folder (GtkFileChooser *chooser,
337                     const char     *name)
338 {
339   if (!gtk_file_chooser_set_current_folder (chooser, name))
340     {
341       GtkWidget *dialog;
342
343       dialog = gtk_message_dialog_new (GTK_WINDOW (chooser),
344                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
345                                        GTK_MESSAGE_ERROR,
346                                        GTK_BUTTONS_CLOSE,
347                                        "Could not set the folder to %s",
348                                        name);
349       gtk_dialog_run (GTK_DIALOG (dialog));
350       gtk_widget_destroy (dialog);
351     }
352 }
353
354 static void
355 set_folder_nonexistent_cb (GtkButton      *button,
356                            GtkFileChooser *chooser)
357 {
358   set_current_folder (chooser, "/nonexistent");
359 }
360
361 static void
362 set_folder_existing_nonexistent_cb (GtkButton      *button,
363                                     GtkFileChooser *chooser)
364 {
365   set_current_folder (chooser, "/usr/nonexistent");
366 }
367
368 static void
369 set_filename (GtkFileChooser *chooser,
370               const char     *name)
371 {
372   if (!gtk_file_chooser_set_filename (chooser, name))
373     {
374       GtkWidget *dialog;
375
376       dialog = gtk_message_dialog_new (GTK_WINDOW (chooser),
377                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
378                                        GTK_MESSAGE_ERROR,
379                                        GTK_BUTTONS_CLOSE,
380                                        "Could not select %s",
381                                        name);
382       gtk_dialog_run (GTK_DIALOG (dialog));
383       gtk_widget_destroy (dialog);
384     }
385 }
386
387 static void
388 set_filename_nonexistent_cb (GtkButton      *button,
389                              GtkFileChooser *chooser)
390 {
391   set_filename (chooser, "/nonexistent");
392 }
393
394 static void
395 set_filename_existing_nonexistent_cb (GtkButton      *button,
396                                       GtkFileChooser *chooser)
397 {
398   set_filename (chooser, "/usr/nonexistent");
399 }
400
401 static void
402 unmap_and_remap_cb (GtkButton *button,
403                     GtkFileChooser *chooser)
404 {
405   gtk_widget_hide (GTK_WIDGET (chooser));
406   gtk_widget_show (GTK_WIDGET (chooser));
407 }
408
409 static void
410 kill_dependent (GtkWindow *win, GtkWidget *dep)
411 {
412   gtk_widget_destroy (dep);
413   g_object_unref (dep);
414 }
415
416 static void
417 notify_multiple_cb (GtkWidget  *dialog,
418                     GParamSpec *pspec,
419                     GtkWidget  *button)
420 {
421   gboolean multiple;
422
423   multiple = gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (dialog));
424
425   gtk_widget_set_sensitive (button, multiple);
426 }
427
428 static GtkFileChooserConfirmation
429 confirm_overwrite_cb (GtkFileChooser *chooser,
430                       gpointer        data)
431 {
432   GtkWidget *dialog;
433   GtkWidget *button;
434   int response;
435   GtkFileChooserConfirmation conf;
436
437   dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (chooser))),
438                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
439                                    GTK_MESSAGE_QUESTION,
440                                    GTK_BUTTONS_NONE,
441                                    "What do you want to do?");
442
443   button = gtk_button_new_with_label ("Use the stock confirmation dialog");
444   gtk_widget_show (button);
445   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 1);
446
447   button = gtk_button_new_with_label ("Type a new file name");
448   gtk_widget_show (button);
449   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 2);
450
451   button = gtk_button_new_with_label ("Accept the file name");
452   gtk_widget_show (button);
453   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 3);
454
455   response = gtk_dialog_run (GTK_DIALOG (dialog));
456
457   switch (response)
458     {
459     case 1:
460       conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
461       break;
462
463     case 3:
464       conf = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
465       break;
466
467     default:
468       conf = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN;
469       break;
470     }
471
472   gtk_widget_destroy (dialog);
473
474   return conf;
475 }
476
477 int
478 main (int argc, char **argv)
479 {
480   GtkWidget *control_window;
481   GtkWidget *vbbox;
482   GtkWidget *button;
483   GtkWidget *dialog;
484   GtkWidget *prop_editor;
485   GtkWidget *extra;
486   GtkFileFilter *filter;
487   GtkWidget *preview_vbox;
488   gboolean force_rtl = FALSE;
489   gboolean multiple = FALSE;
490   char *action_arg = NULL;
491   char *initial_filename = NULL;
492   char *initial_folder = NULL;
493   GError *error = NULL;
494   GOptionEntry options[] = {
495     { "action", 'a', 0, G_OPTION_ARG_STRING, &action_arg, "Filechooser action", "ACTION" },
496     { "multiple", 'm', 0, G_OPTION_ARG_NONE, &multiple, "Select-multiple", NULL },
497     { "right-to-left", 'r', 0, G_OPTION_ARG_NONE, &force_rtl, "Force right-to-left layout.", NULL },
498     { "initial-filename", 'f', 0, G_OPTION_ARG_FILENAME, &initial_filename, "Initial filename to select", "FILENAME" },
499     { "initial-folder", 'F', 0, G_OPTION_ARG_FILENAME, &initial_folder, "Initial folder to show", "FILENAME" },
500     { NULL }
501   };
502
503   if (!gtk_init_with_args (&argc, &argv, "", options, NULL, &error))
504     {
505       g_print ("Failed to parse args: %s\n", error->message);
506       g_error_free (error);
507       return 1;
508     }
509
510   if (initial_filename && initial_folder)
511     {
512       g_print ("Only one of --initial-filename and --initial-folder may be specified");
513       return 1;
514     }
515
516   if (force_rtl)
517     gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
518
519   action = GTK_FILE_CHOOSER_ACTION_OPEN;
520
521   if (action_arg != NULL)
522     {
523       if (! strcmp ("open", action_arg))
524         action = GTK_FILE_CHOOSER_ACTION_OPEN;
525       else if (! strcmp ("save", action_arg))
526         action = GTK_FILE_CHOOSER_ACTION_SAVE;
527       else if (! strcmp ("select_folder", action_arg))
528         action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
529       else if (! strcmp ("create_folder", action_arg))
530         action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
531
532       g_free (action_arg);
533     }
534
535   dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
536                          "action", action,
537                          "select-multiple", multiple,
538                          NULL);
539
540   switch (action)
541     {
542     case GTK_FILE_CHOOSER_ACTION_OPEN:
543     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
544       gtk_window_set_title (GTK_WINDOW (dialog), "Select a file");
545       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
546                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
547                               GTK_STOCK_OPEN, GTK_RESPONSE_OK,
548                               NULL);
549       break;
550     case GTK_FILE_CHOOSER_ACTION_SAVE:
551     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
552       gtk_window_set_title (GTK_WINDOW (dialog), "Save a file");
553       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
554                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
555                               GTK_STOCK_SAVE, GTK_RESPONSE_OK,
556                               NULL);
557       break;
558     }
559   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
560
561   g_signal_connect (dialog, "selection-changed",
562                     G_CALLBACK (print_selected), NULL);
563   g_signal_connect (dialog, "current-folder-changed",
564                     G_CALLBACK (print_current_folder), NULL);
565   g_signal_connect (dialog, "response",
566                     G_CALLBACK (response_cb), NULL);
567   g_signal_connect (dialog, "confirm-overwrite",
568                     G_CALLBACK (confirm_overwrite_cb), NULL);
569
570   /* Filters */
571   filter = gtk_file_filter_new ();
572   gtk_file_filter_set_name (filter, "All Files");
573   gtk_file_filter_add_pattern (filter, "*");
574   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
575
576   filter = gtk_file_filter_new ();
577   gtk_file_filter_set_name (filter, "No backup files");
578   gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_DISPLAY_NAME,
579                               no_backup_files_filter, NULL, NULL);
580   gtk_file_filter_add_mime_type (filter, "image/png");
581   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
582
583   g_signal_connect (dialog, "notify::filter",
584                     G_CALLBACK (filter_changed), NULL);
585
586   /* Make this filter the default */
587   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
588
589   filter = gtk_file_filter_new ();
590   gtk_file_filter_set_name (filter, "PNG and JPEG");
591   gtk_file_filter_add_mime_type (filter, "image/jpeg");
592   gtk_file_filter_add_mime_type (filter, "image/png");
593   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
594
595   filter = gtk_file_filter_new ();
596   gtk_file_filter_set_name (filter, "Images");
597   gtk_file_filter_add_pixbuf_formats (filter);
598   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
599
600   /* Preview widget */
601   /* THIS IS A TERRIBLE PREVIEW WIDGET, AND SHOULD NOT BE COPIED AT ALL.
602    */
603   preview_vbox = gtk_vbox_new (0, FALSE);
604   /*gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox);*/
605
606   preview_label = gtk_label_new (NULL);
607   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_label, TRUE, TRUE, 0);
608   gtk_misc_set_padding (GTK_MISC (preview_label), 6, 6);
609
610   preview_image = gtk_image_new ();
611   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_image, TRUE, TRUE, 0);
612   gtk_misc_set_padding (GTK_MISC (preview_image), 6, 6);
613
614   update_preview_cb (GTK_FILE_CHOOSER (dialog));
615   g_signal_connect (dialog, "update-preview",
616                     G_CALLBACK (update_preview_cb), NULL);
617
618   /* Extra widget */
619
620   extra = gtk_check_button_new_with_mnemonic ("Lar_t whoever asks about this button");
621   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extra), TRUE);
622   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), extra);
623
624   /* Shortcuts */
625
626   gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (dialog),
627                                             "file:///usr/share/pixmaps",
628                                             NULL);
629
630   /* Initial filename or folder */
631
632   if (initial_filename)
633     set_filename (GTK_FILE_CHOOSER (dialog), initial_filename);
634
635   if (initial_folder)
636     set_current_folder (GTK_FILE_CHOOSER (dialog), initial_folder);
637
638   /* show_all() to reveal bugs in composite widget handling */
639   gtk_widget_show_all (dialog);
640
641   /* Extra controls for manipulating the test environment
642    */
643   prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);
644
645   control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
646
647   vbbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
648   gtk_container_add (GTK_CONTAINER (control_window), vbbox);
649
650   button = gtk_button_new_with_mnemonic ("_Select all");
651   gtk_widget_set_sensitive (button, multiple);
652   gtk_container_add (GTK_CONTAINER (vbbox), button);
653   g_signal_connect_swapped (button, "clicked",
654                             G_CALLBACK (gtk_file_chooser_select_all), dialog);
655   g_signal_connect (dialog, "notify::select-multiple",
656                     G_CALLBACK (notify_multiple_cb), button);
657
658   button = gtk_button_new_with_mnemonic ("_Unselect all");
659   gtk_container_add (GTK_CONTAINER (vbbox), button);
660   g_signal_connect_swapped (button, "clicked",
661                             G_CALLBACK (gtk_file_chooser_unselect_all), dialog);
662
663   button = gtk_button_new_with_label ("set_current_folder (\"/nonexistent\")");
664   gtk_container_add (GTK_CONTAINER (vbbox), button);
665   g_signal_connect (button, "clicked",
666                     G_CALLBACK (set_folder_nonexistent_cb), dialog);
667
668   button = gtk_button_new_with_label ("set_current_folder (\"/usr/nonexistent\")");
669   gtk_container_add (GTK_CONTAINER (vbbox), button);
670   g_signal_connect (button, "clicked",
671                     G_CALLBACK (set_folder_existing_nonexistent_cb), dialog);
672
673   button = gtk_button_new_with_label ("set_filename (\"/nonexistent\")");
674   gtk_container_add (GTK_CONTAINER (vbbox), button);
675   g_signal_connect (button, "clicked",
676                     G_CALLBACK (set_filename_nonexistent_cb), dialog);
677
678   button = gtk_button_new_with_label ("set_filename (\"/usr/nonexistent\")");
679   gtk_container_add (GTK_CONTAINER (vbbox), button);
680   g_signal_connect (button, "clicked",
681                     G_CALLBACK (set_filename_existing_nonexistent_cb), dialog);
682
683   button = gtk_button_new_with_label ("Unmap and remap");
684   gtk_container_add (GTK_CONTAINER (vbbox), button);
685   g_signal_connect (button, "clicked",
686                     G_CALLBACK (unmap_and_remap_cb), dialog);
687
688   gtk_widget_show_all (control_window);
689
690   g_object_ref (control_window);
691   g_signal_connect (dialog, "destroy",
692                     G_CALLBACK (kill_dependent), control_window);
693
694   /* We need to hold a ref until we have destroyed the widgets, just in case
695    * someone else destroys them.  We explicitly destroy windows to catch leaks.
696    */
697   g_object_ref (dialog);
698   gtk_main ();
699   gtk_widget_destroy (dialog);
700   g_object_unref (dialog);
701
702   return 0;
703 }