]> Pileus Git - ~andy/gtk/blob - tests/testfilechooser.c
Plug leak.
[~andy/gtk] / tests / testfilechooser.c
1 #include "config.h"
2
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <stdlib.h>
7 #include <time.h>
8 #ifdef HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11 #include <gtk/gtk.h>
12
13 #ifdef G_OS_WIN32
14 #  define WIN32_MEAN_AND_LEAN
15 #  include <windows.h> /* ExtractAssociatedIcon */
16 #  include <io.h>
17 #  define localtime_r(t,b) localtime(t)
18 #  ifndef S_ISREG
19 #    define S_ISREG(m) ((m) & _S_IFREG)
20 #  endif
21 #  include <gdk/win32/gdkwin32.h> /* gdk_win32_hdc_get */
22 #endif
23
24 #include "prop-editor.h"
25
26 static GtkWidget *preview_label;
27 static GtkWidget *preview_image;
28 static GtkFileChooserAction action;
29
30 static void
31 print_current_folder (GtkFileChooser *chooser)
32 {
33   gchar *uri;
34
35   uri = gtk_file_chooser_get_current_folder_uri (chooser);
36   g_print ("Current folder changed :\n  %s\n", uri ? uri : "(null)");
37   g_free (uri);
38 }
39
40 static void
41 print_selected (GtkFileChooser *chooser)
42 {
43   GSList *uris = gtk_file_chooser_get_uris (chooser);
44   GSList *tmp_list;
45
46   g_print ("Selection changed :\n");
47   for (tmp_list = uris; tmp_list; tmp_list = tmp_list->next)
48     {
49       gchar *uri = tmp_list->data;
50       g_print ("  %s\n", uri);
51       g_free (uri);
52     }
53   g_print ("\n");
54   g_slist_free (uris);
55 }
56
57 static void
58 response_cb (GtkDialog *dialog,
59              gint       response_id)
60 {
61   if (response_id == GTK_RESPONSE_OK)
62     {
63       GSList *list;
64
65       list = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
66
67       if (list)
68         {
69           GSList *l;
70
71           g_print ("Selected files:\n");
72
73           for (l = list; l; l = l->next)
74             {
75               g_print ("%s\n", (char *) l->data);
76               g_free (l->data);
77             }
78
79           g_slist_free (list);
80         }
81       else
82         g_print ("No selected files\n");
83     }
84   else
85     g_print ("Dialog was closed\n");
86
87   gtk_main_quit ();
88 }
89
90 static gboolean
91 no_backup_files_filter (const GtkFileFilterInfo *filter_info,
92                         gpointer                 data)
93 {
94   gsize len = filter_info->display_name ? strlen (filter_info->display_name) : 0;
95   if (len > 0 && filter_info->display_name[len - 1] == '~')
96     return 0;
97   else
98     return 1;
99 }
100
101 static char *
102 format_time (time_t t)
103 {
104   gchar buf[128];
105   struct tm tm_buf;
106   time_t now = time (NULL);
107   const char *format;
108
109   if (abs (now - t) < 24*60*60)
110     format = "%X";
111   else
112     format = "%x";
113
114   localtime_r (&t, &tm_buf);
115   if (strftime (buf, sizeof (buf), format, &tm_buf) == 0)
116     return g_strdup ("<unknown>");
117   else
118     return g_strdup (buf);
119 }
120
121 static char *
122 format_size (gint64 size)
123 {
124   if (size < (gint64)1024)
125     return g_strdup_printf ("%d bytes", (gint)size);
126   else if (size < (gint64)1024*1024)
127     return g_strdup_printf ("%.1f K", size / (1024.));
128   else if (size < (gint64)1024*1024*1024)
129     return g_strdup_printf ("%.1f M", size / (1024.*1024.));
130   else
131     return g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.));
132 }
133
134 #include <stdio.h>
135 #include <errno.h>
136 #define _(s) (s)
137
138 static void
139 size_prepared_cb (GdkPixbufLoader *loader, 
140                   int              width,
141                   int              height,
142                   int             *data)
143 {
144         int des_width = data[0];
145         int des_height = data[1];
146
147         if (des_height >= height && des_width >= width) {
148                 /* Nothing */
149         } else if ((double)height * des_width > (double)width * des_height) {
150                 width = 0.5 + (double)width * des_height / (double)height;
151                 height = des_height;
152         } else {
153                 height = 0.5 + (double)height * des_width / (double)width;
154                 width = des_width;
155         }
156
157         gdk_pixbuf_loader_set_size (loader, width, height);
158 }
159
160 GdkPixbuf *
161 my_new_from_file_at_size (const char *filename,
162                           int         width, 
163                           int         height,
164                           GError    **error)
165 {
166         GdkPixbufLoader *loader;
167         GdkPixbuf       *pixbuf;
168         int              info[2];
169         struct stat st;
170
171         guchar buffer [4096];
172         int length;
173         FILE *f;
174
175         g_return_val_if_fail (filename != NULL, NULL);
176         g_return_val_if_fail (width > 0 && height > 0, NULL);
177
178         if (stat (filename, &st) != 0) {
179                 g_set_error (error,
180                              G_FILE_ERROR,
181                              g_file_error_from_errno (errno),
182                              _("Could not get information for file '%s': %s"),
183                              filename, g_strerror (errno));
184                 return NULL;
185         }
186
187         if (!S_ISREG (st.st_mode))
188                 return NULL;
189
190         f = fopen (filename, "rb");
191         if (!f) {
192                 g_set_error (error,
193                              G_FILE_ERROR,
194                              g_file_error_from_errno (errno),
195                              _("Failed to open file '%s': %s"),
196                              filename, g_strerror (errno));
197                 return NULL;
198         }
199         
200         loader = gdk_pixbuf_loader_new ();
201 #ifdef DONT_PRESERVE_ASPECT
202         gdk_pixbuf_loader_set_size (loader, width, height);
203 #else
204         info[0] = width;
205         info[1] = height;
206         g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), info);
207 #endif  
208
209         while (!feof (f)) {
210                 length = fread (buffer, 1, sizeof (buffer), f);
211                 if (length > 0)
212                         if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
213                                 gdk_pixbuf_loader_close (loader, NULL);
214                                 fclose (f);
215                                 g_object_unref (G_OBJECT (loader));
216                                 return NULL;
217                         }
218         }
219
220         fclose (f);
221
222         if (!gdk_pixbuf_loader_close (loader, error)) {
223                 g_object_unref (G_OBJECT (loader));
224                 return NULL;
225         }
226
227         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
228
229         if (!pixbuf) {
230                 g_object_unref (G_OBJECT (loader));
231                 g_set_error (error,
232                              GDK_PIXBUF_ERROR,
233                              GDK_PIXBUF_ERROR_FAILED,
234                              _("Failed to load image '%s': reason not known, probably a corrupt image file"),
235                              filename);
236                 return NULL;
237         }
238
239         g_object_ref (pixbuf);
240
241         g_object_unref (G_OBJECT (loader));
242
243         return pixbuf;
244 }
245
246 static void
247 update_preview_cb (GtkFileChooser *chooser)
248 {
249   gchar *filename = gtk_file_chooser_get_preview_filename (chooser);
250   gboolean have_preview = FALSE;
251   
252   if (filename)
253     {
254       GdkPixbuf *pixbuf;
255       GError *error = NULL;
256
257       pixbuf = my_new_from_file_at_size (filename, 128, 128, &error);
258       if (pixbuf)
259         {
260           gtk_image_set_from_pixbuf (GTK_IMAGE (preview_image), pixbuf);
261           g_object_unref (pixbuf);
262           gtk_widget_show (preview_image);
263           gtk_widget_hide (preview_label);
264           have_preview = TRUE;
265         }
266       else
267         {
268           struct stat buf;
269           if (stat (filename, &buf) == 0)
270             {
271               gchar *preview_text;
272               gchar *size_str;
273               gchar *modified_time;
274               
275               size_str = format_size (buf.st_size);
276               modified_time = format_time (buf.st_mtime);
277               
278               preview_text = g_strdup_printf ("<i>Modified:</i>\t%s\n"
279                                               "<i>Size:</i>\t%s\n",
280                                               modified_time,
281                                               size_str);
282               gtk_label_set_markup (GTK_LABEL (preview_label), preview_text);
283               g_free (modified_time);
284               g_free (size_str);
285               g_free (preview_text);
286               
287               gtk_widget_hide (preview_image);
288               gtk_widget_show (preview_label);
289               have_preview = TRUE;
290             }
291         }
292       
293       g_free (filename);
294
295       if (error)
296         g_error_free (error);
297     }
298
299   gtk_file_chooser_set_preview_widget_active (chooser, have_preview);
300 }
301
302 static void
303 set_folder_nonexistent_cb (GtkButton      *button,
304                            GtkFileChooser *chooser)
305 {
306   gtk_file_chooser_set_current_folder (chooser, "/nonexistent");
307 }
308
309 static void
310 set_folder_existing_nonexistent_cb (GtkButton      *button,
311                                     GtkFileChooser *chooser)
312 {
313   gtk_file_chooser_set_current_folder (chooser, "/usr/nonexistent");
314 }
315
316 static void
317 set_filename_nonexistent_cb (GtkButton      *button,
318                              GtkFileChooser *chooser)
319 {
320   gtk_file_chooser_set_filename (chooser, "/nonexistent");
321 }
322
323 static void
324 set_filename_existing_nonexistent_cb (GtkButton      *button,
325                                       GtkFileChooser *chooser)
326 {
327   gtk_file_chooser_set_filename (chooser, "/usr/nonexistent");
328 }
329
330 int
331 main (int argc, char **argv)
332 {
333   GtkWidget *control_window;
334   GtkWidget *vbbox;
335   GtkWidget *button;
336   GtkWidget *dialog;
337   GtkWidget *prop_editor;
338   GtkWidget *extra;
339   GtkFileFilter *filter;
340   GtkWidget *preview_vbox;
341   int i;
342   
343   gtk_init (&argc, &argv);
344
345   action = GTK_FILE_CHOOSER_ACTION_OPEN;
346
347   /* lame-o arg parsing */
348   for (i = 1; i < argc; i++)
349     {
350       if (! strcmp ("--action=open", argv[i]))
351         action = GTK_FILE_CHOOSER_ACTION_OPEN;
352       else if (! strcmp ("--action=save", argv[i]))
353         action = GTK_FILE_CHOOSER_ACTION_SAVE;
354       else if (! strcmp ("--action=select_folder", argv[i]))
355         action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
356       else if (! strcmp ("--action=create_folder", argv[i]))
357         action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
358     }
359
360   dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
361                          "action", action,
362                          "file-system-backend", "gtk+",
363                          NULL);
364   switch (action)
365     {
366     case GTK_FILE_CHOOSER_ACTION_OPEN:
367     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
368       gtk_window_set_title (GTK_WINDOW (dialog), "Select a file");
369       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
370                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
371                               GTK_STOCK_OPEN, GTK_RESPONSE_OK,
372                               NULL);
373       break;
374     case GTK_FILE_CHOOSER_ACTION_SAVE:
375     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
376       gtk_window_set_title (GTK_WINDOW (dialog), "Save a file");
377       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
378                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
379                               GTK_STOCK_SAVE, GTK_RESPONSE_OK,
380                               NULL);
381       break;
382     }
383   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
384   
385   g_signal_connect (dialog, "selection-changed",
386                     G_CALLBACK (print_selected), NULL);
387   g_signal_connect (dialog, "current-folder-changed",
388                     G_CALLBACK (print_current_folder), NULL);
389   g_signal_connect (dialog, "response",
390                     G_CALLBACK (response_cb), NULL);
391
392   /* Filters */
393   filter = gtk_file_filter_new ();
394   gtk_file_filter_set_name (filter, "All Files");
395   gtk_file_filter_add_pattern (filter, "*");
396   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
397   
398   filter = gtk_file_filter_new ();
399   gtk_file_filter_set_name (filter, "No backup files");
400   gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_DISPLAY_NAME,
401                               no_backup_files_filter, NULL, NULL);
402   gtk_file_filter_add_mime_type (filter, "image/png");
403   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
404
405   /* Make this filter the default */
406   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
407   
408   filter = gtk_file_filter_new ();
409   gtk_file_filter_set_name (filter, "PNG and JPEG");
410   gtk_file_filter_add_mime_type (filter, "image/jpeg");
411   gtk_file_filter_add_mime_type (filter, "image/png");
412   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
413
414   /* Preview widget */
415   preview_vbox = gtk_vbox_new (0, FALSE);
416   gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox);
417   
418   preview_label = gtk_label_new (NULL);
419   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_label, TRUE, TRUE, 0);
420   gtk_misc_set_padding (GTK_MISC (preview_label), 6, 6);
421   
422   preview_image = gtk_image_new ();
423   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_image, TRUE, TRUE, 0);
424   gtk_misc_set_padding (GTK_MISC (preview_image), 6, 6);
425   
426   update_preview_cb (GTK_FILE_CHOOSER (dialog));
427   g_signal_connect (dialog, "update-preview",
428                     G_CALLBACK (update_preview_cb), NULL);
429
430   /* Extra widget */
431
432   extra = gtk_check_button_new_with_mnemonic ("Lar_t whoever asks about this button");
433   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extra), TRUE);
434   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), extra);
435
436   /* Shortcuts */
437
438   gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (dialog),
439                                             "file:///usr/share/pixmaps",
440                                             NULL);
441
442   /* show_all() to reveal bugs in composite widget handling */
443   gtk_widget_show_all (dialog);
444
445   /* Extra controls for manipulating the test environment
446    */
447   prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);
448
449   control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
450   
451   vbbox = gtk_vbutton_box_new ();
452   gtk_container_add (GTK_CONTAINER (control_window), vbbox);
453
454   button = gtk_button_new_with_mnemonic ("_Select all");
455   gtk_container_add (GTK_CONTAINER (vbbox), button);
456   g_signal_connect_swapped (button, "clicked",
457                             G_CALLBACK (gtk_file_chooser_select_all), dialog);
458   
459   button = gtk_button_new_with_mnemonic ("_Unselect all");
460   gtk_container_add (GTK_CONTAINER (vbbox), button);
461   g_signal_connect_swapped (button, "clicked",
462                             G_CALLBACK (gtk_file_chooser_unselect_all), dialog);
463
464   button = gtk_button_new_with_label ("set_current_folder (\"/nonexistent\")");
465   gtk_container_add (GTK_CONTAINER (vbbox), button);
466   g_signal_connect (button, "clicked",
467                     G_CALLBACK (set_folder_nonexistent_cb), dialog);
468
469   button = gtk_button_new_with_label ("set_current_folder (\"/usr/nonexistent\"");
470   gtk_container_add (GTK_CONTAINER (vbbox), button);
471   g_signal_connect (button, "clicked",
472                     G_CALLBACK (set_folder_existing_nonexistent_cb), dialog);
473
474   button = gtk_button_new_with_label ("set_filename (\"/nonexistent\"");
475   gtk_container_add (GTK_CONTAINER (vbbox), button);
476   g_signal_connect (button, "clicked",
477                     G_CALLBACK (set_filename_nonexistent_cb), dialog);
478
479   button = gtk_button_new_with_label ("set_filename (\"/usr/nonexistent\"");
480   gtk_container_add (GTK_CONTAINER (vbbox), button);
481   g_signal_connect (button, "clicked",
482                     G_CALLBACK (set_filename_existing_nonexistent_cb), dialog);
483
484   gtk_widget_show_all (control_window);
485   
486   gtk_main ();
487
488   return 0;
489 }