]> Pileus Git - ~andy/gtk/blob - tests/testfilechooser.c
Move the /nonexistant stuff out of the main window, keep the main window
[~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
296   gtk_file_chooser_set_preview_widget_active (chooser, have_preview);
297 }
298
299 static void
300 set_folder_nonexistent_cb (GtkButton      *button,
301                            GtkFileChooser *chooser)
302 {
303   gtk_file_chooser_set_current_folder (chooser, "/nonexistent");
304 }
305
306 static void
307 set_folder_existing_nonexistent_cb (GtkButton      *button,
308                                     GtkFileChooser *chooser)
309 {
310   gtk_file_chooser_set_current_folder (chooser, "/usr/nonexistent");
311 }
312
313 static void
314 set_filename_nonexistent_cb (GtkButton      *button,
315                              GtkFileChooser *chooser)
316 {
317   gtk_file_chooser_set_filename (chooser, "/nonexistent");
318 }
319
320 static void
321 set_filename_existing_nonexistent_cb (GtkButton      *button,
322                                       GtkFileChooser *chooser)
323 {
324   gtk_file_chooser_set_filename (chooser, "/usr/nonexistent");
325 }
326
327 int
328 main (int argc, char **argv)
329 {
330   GtkWidget *control_window;
331   GtkWidget *vbbox;
332   GtkWidget *button;
333   GtkWidget *dialog;
334   GtkWidget *prop_editor;
335   GtkWidget *extra;
336   GtkFileFilter *filter;
337   GtkWidget *preview_vbox;
338   int i;
339   
340   gtk_init (&argc, &argv);
341
342   action = GTK_FILE_CHOOSER_ACTION_OPEN;
343
344   /* lame-o arg parsing */
345   for (i = 1; i < argc; i++)
346     {
347       if (! strcmp ("--action=open", argv[i]))
348         action = GTK_FILE_CHOOSER_ACTION_OPEN;
349       else if (! strcmp ("--action=save", argv[i]))
350         action = GTK_FILE_CHOOSER_ACTION_SAVE;
351       else if (! strcmp ("--action=select_folder", argv[i]))
352         action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
353       else if (! strcmp ("--action=create_folder", argv[i]))
354         action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
355     }
356
357   dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
358                          "action", action,
359                          "file-system-backend", "gnome-vfs",
360                          NULL);
361   switch (action)
362     {
363     case GTK_FILE_CHOOSER_ACTION_OPEN:
364     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
365       gtk_window_set_title (GTK_WINDOW (dialog), "Select a file");
366       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
367                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
368                               GTK_STOCK_OPEN, GTK_RESPONSE_OK,
369                               NULL);
370       break;
371     case GTK_FILE_CHOOSER_ACTION_SAVE:
372     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
373       gtk_window_set_title (GTK_WINDOW (dialog), "Save a file");
374       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
375                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
376                               GTK_STOCK_SAVE, GTK_RESPONSE_OK,
377                               NULL);
378       break;
379     }
380   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
381   
382   g_signal_connect (dialog, "selection-changed",
383                     G_CALLBACK (print_selected), NULL);
384   g_signal_connect (dialog, "current-folder-changed",
385                     G_CALLBACK (print_current_folder), NULL);
386   g_signal_connect (dialog, "response",
387                     G_CALLBACK (response_cb), NULL);
388
389   /* Filters */
390   filter = gtk_file_filter_new ();
391   gtk_file_filter_set_name (filter, "All Files");
392   gtk_file_filter_add_pattern (filter, "*");
393   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
394   
395   filter = gtk_file_filter_new ();
396   gtk_file_filter_set_name (filter, "No backup files");
397   gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_DISPLAY_NAME,
398                               no_backup_files_filter, NULL, NULL);
399   gtk_file_filter_add_mime_type (filter, "image/png");
400   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
401
402   /* Make this filter the default */
403   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
404   
405   filter = gtk_file_filter_new ();
406   gtk_file_filter_set_name (filter, "PNG and JPEG");
407   gtk_file_filter_add_mime_type (filter, "image/jpeg");
408   gtk_file_filter_add_mime_type (filter, "image/png");
409   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
410
411   /* Preview widget */
412   preview_vbox = gtk_vbox_new (0, FALSE);
413   gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox);
414   
415   preview_label = gtk_label_new (NULL);
416   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_label, TRUE, TRUE, 0);
417   gtk_misc_set_padding (GTK_MISC (preview_label), 6, 6);
418   
419   preview_image = gtk_image_new ();
420   gtk_box_pack_start (GTK_BOX (preview_vbox), preview_image, TRUE, TRUE, 0);
421   gtk_misc_set_padding (GTK_MISC (preview_image), 6, 6);
422   
423   update_preview_cb (GTK_FILE_CHOOSER (dialog));
424   g_signal_connect (dialog, "update-preview",
425                     G_CALLBACK (update_preview_cb), NULL);
426
427   /* Extra widget */
428
429   extra = gtk_check_button_new_with_mnemonic ("Lar_t whoever asks about this button");
430   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extra), TRUE);
431   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), extra);
432
433   /* Shortcuts */
434
435   gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (dialog),
436                                             "file:///usr/share/pixmaps",
437                                             NULL);
438
439   /* show_all() to reveal bugs in composite widget handling */
440   gtk_widget_show_all (dialog);
441
442   /* Extra controls for manipulating the test environment
443    */
444   prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);
445
446   control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
447   
448   vbbox = gtk_vbutton_box_new ();
449   gtk_container_add (GTK_CONTAINER (control_window), vbbox);
450
451   button = gtk_button_new_with_mnemonic ("_Select all");
452   gtk_container_add (GTK_CONTAINER (vbbox), button);
453   g_signal_connect_swapped (button, "clicked",
454                             G_CALLBACK (gtk_file_chooser_select_all), dialog);
455   
456   button = gtk_button_new_with_mnemonic ("_Unselect all");
457   gtk_container_add (GTK_CONTAINER (vbbox), button);
458   g_signal_connect_swapped (button, "clicked",
459                             G_CALLBACK (gtk_file_chooser_unselect_all), dialog);
460
461   button = gtk_button_new_with_label ("set_current_folder (\"/nonexistent\")");
462   gtk_container_add (GTK_CONTAINER (vbbox), button);
463   g_signal_connect (button, "clicked",
464                     G_CALLBACK (set_folder_nonexistent_cb), dialog);
465
466   button = gtk_button_new_with_label ("set_current_folder (\"/usr/nonexistent\"");
467   gtk_container_add (GTK_CONTAINER (vbbox), button);
468   g_signal_connect (button, "clicked",
469                     G_CALLBACK (set_folder_existing_nonexistent_cb), dialog);
470
471   button = gtk_button_new_with_label ("set_filename (\"/nonexistent\"");
472   gtk_container_add (GTK_CONTAINER (vbbox), button);
473   g_signal_connect (button, "clicked",
474                     G_CALLBACK (set_filename_nonexistent_cb), dialog);
475
476   button = gtk_button_new_with_label ("set_filename (\"/usr/nonexistent\"");
477   gtk_container_add (GTK_CONTAINER (vbbox), button);
478   g_signal_connect (button, "clicked",
479                     G_CALLBACK (set_filename_existing_nonexistent_cb), dialog);
480
481   gtk_widget_show_all (control_window);
482   
483   gtk_main ();
484
485   return 0;
486 }