2 * Copyright (C) 2003 Red Hat, Inc.
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.
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.
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.
23 #include <sys/types.h>
34 # define localtime_r(t,b) *(b) = *localtime (t)
36 # define S_ISREG(m) ((m) & _S_IFREG)
40 #include "prop-editor.h"
42 static GtkWidget
*preview_label
;
43 static GtkWidget
*preview_image
;
44 static GtkFileChooserAction action
;
47 print_current_folder (GtkFileChooser
*chooser
)
51 uri
= gtk_file_chooser_get_current_folder_uri (chooser
);
52 g_print ("Current folder changed :\n %s\n", uri
? uri
: "(null)");
57 print_selected (GtkFileChooser
*chooser
)
59 GSList
*uris
= gtk_file_chooser_get_uris (chooser
);
62 g_print ("Selection changed :\n");
63 for (tmp_list
= uris
; tmp_list
; tmp_list
= tmp_list
->next
)
65 gchar
*uri
= tmp_list
->data
;
66 g_print (" %s\n", uri
);
74 response_cb (GtkDialog
*dialog
,
77 if (response_id
== GTK_RESPONSE_OK
)
81 list
= gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog
));
87 g_print ("Selected files:\n");
89 for (l
= list
; l
; l
= l
->next
)
91 g_print ("%s\n", (char *) l
->data
);
98 g_print ("No selected files\n");
101 g_print ("Dialog was closed\n");
107 no_backup_files_filter (const GtkFileFilterInfo
*filter_info
,
110 gsize len
= filter_info
->display_name
? strlen (filter_info
->display_name
) : 0;
111 if (len
> 0 && filter_info
->display_name
[len
- 1] == '~')
118 filter_changed (GtkFileChooserDialog
*dialog
,
121 g_print ("file filter changed\n");
125 format_time (time_t t
)
129 time_t now
= time (NULL
);
132 if (abs (now
- t
) < 24*60*60)
137 localtime_r (&t
, &tm_buf
);
138 if (strftime (buf
, sizeof (buf
), format
, &tm_buf
) == 0)
139 return g_strdup ("<unknown>");
141 return g_strdup (buf
);
145 format_size (gint64 size
)
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.));
154 return g_strdup_printf ("%.1f G", size
/ (1024.*1024.*1024.));
162 size_prepared_cb (GdkPixbufLoader
*loader
,
167 int des_width
= data
[0];
168 int des_height
= data
[1];
170 if (des_height
>= height
&& des_width
>= width
) {
172 } else if ((double)height
* des_width
> (double)width
* des_height
) {
173 width
= 0.5 + (double)width
* des_height
/ (double)height
;
176 height
= 0.5 + (double)height
* des_width
/ (double)width
;
180 gdk_pixbuf_loader_set_size (loader
, width
, height
);
184 my_new_from_file_at_size (const char *filename
,
189 GdkPixbufLoader
*loader
;
194 guchar buffer
[4096];
198 g_return_val_if_fail (filename
!= NULL
, NULL
);
199 g_return_val_if_fail (width
> 0 && height
> 0, NULL
);
201 if (stat (filename
, &st
) != 0) {
206 g_file_error_from_errno (errsv
),
207 _("Could not get information for file '%s': %s"),
208 filename
, g_strerror (errsv
));
212 if (!S_ISREG (st
.st_mode
))
215 f
= fopen (filename
, "rb");
221 g_file_error_from_errno (errsv
),
222 _("Failed to open file '%s': %s"),
223 filename
, g_strerror (errsv
));
227 loader
= gdk_pixbuf_loader_new ();
228 #ifdef DONT_PRESERVE_ASPECT
229 gdk_pixbuf_loader_set_size (loader
, width
, height
);
233 g_signal_connect (loader
, "size-prepared", G_CALLBACK (size_prepared_cb
), info
);
237 length
= fread (buffer
, 1, sizeof (buffer
), f
);
239 if (!gdk_pixbuf_loader_write (loader
, buffer
, length
, error
)) {
240 gdk_pixbuf_loader_close (loader
, NULL
);
242 g_object_unref (loader
);
249 g_assert (*error
== NULL
);
250 if (!gdk_pixbuf_loader_close (loader
, error
)) {
251 g_object_unref (loader
);
255 pixbuf
= gdk_pixbuf_loader_get_pixbuf (loader
);
258 g_object_unref (loader
);
260 /* did the loader set an error? */
266 GDK_PIXBUF_ERROR_FAILED
,
267 _("Failed to load image '%s': reason not known, probably a corrupt image file"),
272 g_object_ref (pixbuf
);
274 g_object_unref (loader
);
280 update_preview_cb (GtkFileChooser
*chooser
)
282 gchar
*filename
= gtk_file_chooser_get_preview_filename (chooser
);
283 gboolean have_preview
= FALSE
;
288 GError
*error
= NULL
;
290 pixbuf
= my_new_from_file_at_size (filename
, 128, 128, &error
);
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
);
302 if (stat (filename
, &buf
) == 0)
306 gchar
*modified_time
;
308 size_str
= format_size (buf
.st_size
);
309 modified_time
= format_time (buf
.st_mtime
);
311 preview_text
= g_strdup_printf ("<i>Modified:</i>\t%s\n"
312 "<i>Size:</i>\t%s\n",
315 gtk_label_set_markup (GTK_LABEL (preview_label
), preview_text
);
316 g_free (modified_time
);
318 g_free (preview_text
);
320 gtk_widget_hide (preview_image
);
321 gtk_widget_show (preview_label
);
329 g_error_free (error
);
332 gtk_file_chooser_set_preview_widget_active (chooser
, have_preview
);
336 set_current_folder (GtkFileChooser
*chooser
,
339 if (!gtk_file_chooser_set_current_folder (chooser
, name
))
343 dialog
= gtk_message_dialog_new (GTK_WINDOW (chooser
),
344 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
347 "Could not set the folder to %s",
349 gtk_dialog_run (GTK_DIALOG (dialog
));
350 gtk_widget_destroy (dialog
);
355 set_folder_nonexistent_cb (GtkButton
*button
,
356 GtkFileChooser
*chooser
)
358 set_current_folder (chooser
, "/nonexistent");
362 set_folder_existing_nonexistent_cb (GtkButton
*button
,
363 GtkFileChooser
*chooser
)
365 set_current_folder (chooser
, "/usr/nonexistent");
369 set_filename (GtkFileChooser
*chooser
,
372 if (!gtk_file_chooser_set_filename (chooser
, name
))
376 dialog
= gtk_message_dialog_new (GTK_WINDOW (chooser
),
377 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
380 "Could not select %s",
382 gtk_dialog_run (GTK_DIALOG (dialog
));
383 gtk_widget_destroy (dialog
);
388 set_filename_nonexistent_cb (GtkButton
*button
,
389 GtkFileChooser
*chooser
)
391 set_filename (chooser
, "/nonexistent");
395 set_filename_existing_nonexistent_cb (GtkButton
*button
,
396 GtkFileChooser
*chooser
)
398 set_filename (chooser
, "/usr/nonexistent");
402 unmap_and_remap_cb (GtkButton
*button
,
403 GtkFileChooser
*chooser
)
405 gtk_widget_hide (GTK_WIDGET (chooser
));
406 gtk_widget_show (GTK_WIDGET (chooser
));
410 kill_dependent (GtkWindow
*win
, GtkObject
*dep
)
412 gtk_object_destroy (dep
);
413 g_object_unref (dep
);
417 notify_multiple_cb (GtkWidget
*dialog
,
423 multiple
= gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (dialog
));
425 gtk_widget_set_sensitive (button
, multiple
);
428 static GtkFileChooserConfirmation
429 confirm_overwrite_cb (GtkFileChooser
*chooser
,
435 GtkFileChooserConfirmation conf
;
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
,
441 "What do you want to do?");
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);
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);
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);
455 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
460 conf
= GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM
;
464 conf
= GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME
;
468 conf
= GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN
;
472 gtk_widget_destroy (dialog
);
478 main (int argc
, char **argv
)
480 GtkWidget
*control_window
;
484 GtkWidget
*prop_editor
;
486 GtkFileFilter
*filter
;
487 GtkWidget
*preview_vbox
;
488 gboolean force_rtl
= FALSE
;
489 gboolean multiple
= FALSE
;
490 char *action_arg
= NULL
;
491 char *backend
= NULL
;
492 GError
*error
= NULL
;
493 GOptionEntry options
[] = {
494 { "action", 'a', 0, G_OPTION_ARG_STRING
, &action
, "Filechooser action", "ACTION" },
495 { "backend", 'b', 0, G_OPTION_ARG_STRING
, &backend
, "Filechooser backend (default: gtk+)", "BACKEND" },
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
},
501 if (!gtk_init_with_args (&argc
, &argv
, "", options
, NULL
, &error
))
503 g_print ("Failed to parse args: %s\n", error
->message
);
504 g_error_free (error
);
509 gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL
);
511 action
= GTK_FILE_CHOOSER_ACTION_OPEN
;
513 if (action_arg
!= NULL
)
515 if (! strcmp ("open", action_arg
))
516 action
= GTK_FILE_CHOOSER_ACTION_OPEN
;
517 else if (! strcmp ("save", action_arg
))
518 action
= GTK_FILE_CHOOSER_ACTION_SAVE
;
519 else if (! strcmp ("select_folder", action_arg
))
520 action
= GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
;
521 else if (! strcmp ("create_folder", action_arg
))
522 action
= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
;
528 backend
= g_strdup ("gtk+");
530 dialog
= g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG
,
532 "file-system-backend", backend
,
533 "select-multiple", multiple
,
540 case GTK_FILE_CHOOSER_ACTION_OPEN
:
541 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
:
542 gtk_window_set_title (GTK_WINDOW (dialog
), "Select a file");
543 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
544 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
545 GTK_STOCK_OPEN
, GTK_RESPONSE_OK
,
548 case GTK_FILE_CHOOSER_ACTION_SAVE
:
549 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
:
550 gtk_window_set_title (GTK_WINDOW (dialog
), "Save a file");
551 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
552 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
553 GTK_STOCK_SAVE
, GTK_RESPONSE_OK
,
557 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
);
559 g_signal_connect (dialog
, "selection-changed",
560 G_CALLBACK (print_selected
), NULL
);
561 g_signal_connect (dialog
, "current-folder-changed",
562 G_CALLBACK (print_current_folder
), NULL
);
563 g_signal_connect (dialog
, "response",
564 G_CALLBACK (response_cb
), NULL
);
565 g_signal_connect (dialog
, "confirm-overwrite",
566 G_CALLBACK (confirm_overwrite_cb
), NULL
);
569 filter
= gtk_file_filter_new ();
570 gtk_file_filter_set_name (filter
, "All Files");
571 gtk_file_filter_add_pattern (filter
, "*");
572 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog
), filter
);
574 filter
= gtk_file_filter_new ();
575 gtk_file_filter_set_name (filter
, "No backup files");
576 gtk_file_filter_add_custom (filter
, GTK_FILE_FILTER_DISPLAY_NAME
,
577 no_backup_files_filter
, NULL
, NULL
);
578 gtk_file_filter_add_mime_type (filter
, "image/png");
579 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog
), filter
);
581 g_signal_connect (dialog
, "notify::filter",
582 G_CALLBACK (filter_changed
), NULL
);
584 /* Make this filter the default */
585 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog
), filter
);
587 filter
= gtk_file_filter_new ();
588 gtk_file_filter_set_name (filter
, "PNG and JPEG");
589 gtk_file_filter_add_mime_type (filter
, "image/jpeg");
590 gtk_file_filter_add_mime_type (filter
, "image/png");
591 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog
), filter
);
593 filter
= gtk_file_filter_new ();
594 gtk_file_filter_set_name (filter
, "Images");
595 gtk_file_filter_add_pixbuf_formats (filter
);
596 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog
), filter
);
599 /* THIS IS A TERRIBLE PREVIEW WIDGET, AND SHOULD NOT BE COPIED AT ALL.
601 preview_vbox
= gtk_vbox_new (0, FALSE
);
602 /*gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox);*/
604 preview_label
= gtk_label_new (NULL
);
605 gtk_box_pack_start (GTK_BOX (preview_vbox
), preview_label
, TRUE
, TRUE
, 0);
606 gtk_misc_set_padding (GTK_MISC (preview_label
), 6, 6);
608 preview_image
= gtk_image_new ();
609 gtk_box_pack_start (GTK_BOX (preview_vbox
), preview_image
, TRUE
, TRUE
, 0);
610 gtk_misc_set_padding (GTK_MISC (preview_image
), 6, 6);
612 update_preview_cb (GTK_FILE_CHOOSER (dialog
));
613 g_signal_connect (dialog
, "update-preview",
614 G_CALLBACK (update_preview_cb
), NULL
);
618 extra
= gtk_check_button_new_with_mnemonic ("Lar_t whoever asks about this button");
619 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extra
), TRUE
);
620 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog
), extra
);
624 gtk_file_chooser_add_shortcut_folder_uri (GTK_FILE_CHOOSER (dialog
),
625 "file:///usr/share/pixmaps",
628 /* show_all() to reveal bugs in composite widget handling */
629 gtk_widget_show_all (dialog
);
631 /* Extra controls for manipulating the test environment
633 prop_editor
= create_prop_editor (G_OBJECT (dialog
), GTK_TYPE_FILE_CHOOSER
);
635 control_window
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
637 vbbox
= gtk_vbutton_box_new ();
638 gtk_container_add (GTK_CONTAINER (control_window
), vbbox
);
640 button
= gtk_button_new_with_mnemonic ("_Select all");
641 gtk_widget_set_sensitive (button
, multiple
);
642 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
643 g_signal_connect_swapped (button
, "clicked",
644 G_CALLBACK (gtk_file_chooser_select_all
), dialog
);
645 g_signal_connect (dialog
, "notify::select-multiple",
646 G_CALLBACK (notify_multiple_cb
), button
);
648 button
= gtk_button_new_with_mnemonic ("_Unselect all");
649 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
650 g_signal_connect_swapped (button
, "clicked",
651 G_CALLBACK (gtk_file_chooser_unselect_all
), dialog
);
653 button
= gtk_button_new_with_label ("set_current_folder (\"/nonexistent\")");
654 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
655 g_signal_connect (button
, "clicked",
656 G_CALLBACK (set_folder_nonexistent_cb
), dialog
);
658 button
= gtk_button_new_with_label ("set_current_folder (\"/usr/nonexistent\")");
659 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
660 g_signal_connect (button
, "clicked",
661 G_CALLBACK (set_folder_existing_nonexistent_cb
), dialog
);
663 button
= gtk_button_new_with_label ("set_filename (\"/nonexistent\")");
664 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
665 g_signal_connect (button
, "clicked",
666 G_CALLBACK (set_filename_nonexistent_cb
), dialog
);
668 button
= gtk_button_new_with_label ("set_filename (\"/usr/nonexistent\")");
669 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
670 g_signal_connect (button
, "clicked",
671 G_CALLBACK (set_filename_existing_nonexistent_cb
), dialog
);
673 button
= gtk_button_new_with_label ("Unmap and remap");
674 gtk_container_add (GTK_CONTAINER (vbbox
), button
);
675 g_signal_connect (button
, "clicked",
676 G_CALLBACK (unmap_and_remap_cb
), dialog
);
678 gtk_widget_show_all (control_window
);
680 g_object_ref (control_window
);
681 g_signal_connect (dialog
, "destroy",
682 G_CALLBACK (kill_dependent
), control_window
);
684 /* We need to hold a ref until we have destroyed the widgets, just in case
685 * someone else destroys them. We explicitly destroy windows to catch leaks.
687 g_object_ref (dialog
);
689 gtk_widget_destroy (dialog
);
690 g_object_unref (dialog
);