From d97d7df9475b2edc112402397fc349d46156d741 Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Wed, 23 Jul 2003 22:30:32 +0000 Subject: [PATCH] Add over-big test case for preview (much of size from a modified copy of Wed Jul 23 17:52:01 2003 Owen Taylor * testfilechooser.c: Add over-big test case for preview (much of size from a modified copy of gdk_pixbuf_new_from_file_at_size()) * gtkfilechooserutils.[ch] gtkfilechooser.c gtkfilechooserprivate.h gtkfilechooserimpldefault.c: Add get_preview_path() as a virtual function; implement update-preview signal that was in the header file. * gtkfilechooserimpldefault.c: Finish a simple preview widget implementation. --- gtk/gtkfilechooser.c | 44 +++++--- gtk/gtkfilechooser.h | 4 +- gtk/gtkfilechooserdefault.c | 130 ++++++++++++++++++++-- gtk/gtkfilechooserprivate.h | 5 +- gtk/gtkfilechooserutils.c | 23 +++- tests/testfilechooser.c | 216 ++++++++++++++++++++++++++++++++++++ 6 files changed, 393 insertions(+), 29 deletions(-) diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c index d1a6651d8..c04ea5aef 100644 --- a/gtk/gtkfilechooser.c +++ b/gtk/gtkfilechooser.c @@ -28,7 +28,6 @@ static void gtk_file_chooser_base_init (gpointer g_iface); static GtkFilePath *gtk_file_chooser_get_path (GtkFileChooser *chooser); -static GtkFilePath *gtk_file_chooser_get_preview_path (GtkFileChooser *chooser); GType gtk_file_chooser_get_type (void) @@ -77,6 +76,13 @@ gtk_file_chooser_base_init (gpointer g_iface) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + g_signal_new ("update-preview", + iface_type, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFileChooserIface, update_preview), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); g_object_interface_install_property (g_iface, g_param_spec_enum ("action", @@ -771,7 +777,7 @@ gtk_file_chooser_get_current_folder_uri (GtkFileChooser *chooser) * @path: the #GtkFilePath for the new folder * * Sets the current folder for @chooser from a #GtkFilePath. - * Internal function, see _gtk_file_chooser_set_current_folder_uri(). + * Internal function, see gtk_file_chooser_set_current_folder_uri(). **/ void _gtk_file_chooser_set_current_folder_path (GtkFileChooser *chooser, @@ -993,10 +999,22 @@ gtk_file_chooser_get_preview_widget_active (GtkFileChooser *chooser) return active; } -static GtkFilePath * -gtk_file_chooser_get_preview_path (GtkFileChooser *chooser) +/** + * gtk_file_chooser_get_preview_filename: + * @chooser: a #GtkFileChooser + * + * Gets the filename that should be previewed in a custom preview + * Internal function, see gtk_file_chooser_get_preview_uri().n + * + * Return value: the #GtkFilePath for the file to preview, or %NULL if no file + * is selected. Free with gtk_file_path_free(). + **/ +GtkFilePath * +_gtk_file_chooser_get_preview_path (GtkFileChooser *chooser) { - return NULL; + g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL); + + return GTK_FILE_CHOOSER_GET_IFACE (chooser)->get_preview_path (chooser); } /** @@ -1006,11 +1024,11 @@ gtk_file_chooser_get_preview_path (GtkFileChooser *chooser) * Gets the filename that should be previewed in a custom preview * widget. See gtk_file_chooser_set_preview_widget(). * - * Return value: the filename to display, or %NULL if no file + * Return value: the filename to preview, or %NULL if no file * is selected, or if the selected file cannot be represented * as a local filename. Free with g_free() **/ -const char * +char * gtk_file_chooser_get_preview_filename (GtkFileChooser *chooser) { GtkFileSystem *file_system; @@ -1020,7 +1038,7 @@ gtk_file_chooser_get_preview_filename (GtkFileChooser *chooser) g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL); file_system = _gtk_file_chooser_get_file_system (chooser); - path = gtk_file_chooser_get_preview_path (chooser); + path = _gtk_file_chooser_get_preview_path (chooser); if (path) { result = gtk_file_system_path_to_filename (file_system, path); @@ -1037,10 +1055,10 @@ gtk_file_chooser_get_preview_filename (GtkFileChooser *chooser) * Gets the URI that should be previewed in a custom preview * widget. See gtk_file_chooser_set_preview_widget(). * - * Return value: the URI to display, or %NULL if no file - * is selected. + * Return value: the URI for the file to preview, or %NULL if no file + * is selected. Free with g_free(). **/ -const char * +char * gtk_file_chooser_get_preview_uri (GtkFileChooser *chooser) { GtkFileSystem *file_system; @@ -1050,7 +1068,7 @@ gtk_file_chooser_get_preview_uri (GtkFileChooser *chooser) g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL); file_system = _gtk_file_chooser_get_file_system (chooser); - path = gtk_file_chooser_get_path (chooser); + path = _gtk_file_chooser_get_preview_path (chooser); if (path) { result = gtk_file_system_path_to_uri (file_system, path); @@ -1161,5 +1179,3 @@ gtk_file_chooser_get_filter (GtkFileChooser *chooser) return filter; } - - diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h index 1b708713d..afc797d7c 100644 --- a/gtk/gtkfilechooser.h +++ b/gtk/gtkfilechooser.h @@ -98,8 +98,8 @@ void gtk_file_chooser_set_preview_widget_active (GtkFileChooser *chooser, gboolean active); gboolean gtk_file_chooser_get_preview_widget_active (GtkFileChooser *chooser); -const char *gtk_file_chooser_get_preview_filename (GtkFileChooser *file_chooser); -const char *gtk_file_chooser_get_preview_uri (GtkFileChooser *file_chooser); +char *gtk_file_chooser_get_preview_filename (GtkFileChooser *file_chooser); +char *gtk_file_chooser_get_preview_uri (GtkFileChooser *file_chooser); /* List of user selectable filters */ diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 3a3c378e4..25cdeeb59 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +71,9 @@ struct _GtkFileChooserImplDefault GSList *filters; GtkFilePath *current_folder; + GtkFilePath *preview_path; + + GtkWidget *preview_frame; guint folder_mode : 1; guint local_only : 1; @@ -116,6 +121,7 @@ static void gtk_file_chooser_impl_default_unselect_path (GtkFileC static void gtk_file_chooser_impl_default_select_all (GtkFileChooser *chooser); static void gtk_file_chooser_impl_default_unselect_all (GtkFileChooser *chooser); static GSList * gtk_file_chooser_impl_default_get_paths (GtkFileChooser *chooser); +static GtkFilePath * gtk_file_chooser_impl_default_get_preview_path (GtkFileChooser *chooser); static GtkFileSystem *gtk_file_chooser_impl_default_get_file_system (GtkFileChooser *chooser); static void gtk_file_chooser_impl_default_add_filter (GtkFileChooser *chooser, GtkFileFilter *filter); @@ -123,8 +129,9 @@ static void gtk_file_chooser_impl_default_remove_filter (GtkFileC GtkFileFilter *filter); static GSList * gtk_file_chooser_impl_default_list_filters (GtkFileChooser *chooser); -static void set_current_filter (GtkFileChooserImplDefault *impl, - GtkFileFilter *filter); +static void set_current_filter (GtkFileChooserImplDefault *impl, + GtkFileFilter *filter); +static void check_preview_change (GtkFileChooserImplDefault *impl); static void filter_option_menu_changed (GtkOptionMenu *option_menu, GtkFileChooserImplDefault *impl); @@ -221,6 +228,7 @@ gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface) iface->select_all = gtk_file_chooser_impl_default_select_all; iface->unselect_all = gtk_file_chooser_impl_default_unselect_all; iface->get_paths = gtk_file_chooser_impl_default_get_paths; + iface->get_preview_path = gtk_file_chooser_impl_default_get_preview_path; iface->get_file_system = gtk_file_chooser_impl_default_get_file_system; iface->set_current_folder = gtk_file_chooser_impl_default_set_current_folder; iface->get_current_folder = gtk_file_chooser_impl_default_get_current_folder; @@ -252,6 +260,15 @@ gtk_file_chooser_impl_default_finalize (GObject *object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +update_preview_widget_visibility (GtkFileChooserImplDefault *impl) +{ + if (impl->preview_widget_active && impl->preview_widget) + gtk_widget_show (impl->preview_frame); + else + gtk_widget_hide (impl->preview_frame); +} + static void set_preview_widget (GtkFileChooserImplDefault *impl, GtkWidget *preview_widget) @@ -263,6 +280,9 @@ set_preview_widget (GtkFileChooserImplDefault *impl, { g_object_unref (impl->preview_widget); impl->preview_widget = NULL; + + gtk_container_remove (GTK_CONTAINER (impl->preview_frame), + impl->preview_widget); } impl->preview_widget = preview_widget; @@ -270,7 +290,13 @@ set_preview_widget (GtkFileChooserImplDefault *impl, { g_object_ref (impl->preview_widget); gtk_object_sink (GTK_OBJECT (impl->preview_widget)); + + gtk_widget_show (impl->preview_widget); + gtk_container_add (GTK_CONTAINER (impl->preview_frame), + impl->preview_widget); } + + update_preview_widget_visibility (impl); } static GObject* @@ -282,6 +308,7 @@ gtk_file_chooser_impl_default_constructor (GType type, GtkTreeViewColumn *column; GtkCellRenderer *renderer; GObject *object; + GtkWidget *table; GtkWidget *hpaned; GtkWidget *hbox; GtkWidget *label; @@ -299,9 +326,17 @@ gtk_file_chooser_impl_default_constructor (GType type, gtk_widget_push_composite_child (); + table = gtk_table_new (3, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (impl), table, TRUE, TRUE, 0); + gtk_widget_show (table); + impl->filter_alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0); gtk_alignment_set_padding (GTK_ALIGNMENT (impl->filter_alignment), 0, 6, 0, 0); - gtk_box_pack_start (GTK_BOX (impl), impl->filter_alignment, FALSE, FALSE, 0); + gtk_table_attach (GTK_TABLE (table), impl->filter_alignment, + 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, 0, + 0, 0); /* Don't show filter initially */ hbox = gtk_hbox_new (FALSE, 6); @@ -324,7 +359,10 @@ gtk_file_chooser_impl_default_constructor (GType type, G_CALLBACK (filter_option_menu_changed), impl); hpaned = gtk_hpaned_new (); - gtk_box_pack_start (GTK_BOX (impl), hpaned, TRUE, TRUE, 0); + gtk_table_attach (GTK_TABLE (table), hpaned, + 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, + 0, 0); gtk_widget_show (hpaned); impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL); @@ -365,7 +403,10 @@ gtk_file_chooser_impl_default_constructor (GType type, G_CALLBACK (list_selection_changed), impl); hbox = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (impl), hbox, FALSE, FALSE, 6); + gtk_table_attach (GTK_TABLE (table), hbox, + 0, 2, 2, 3, + GTK_EXPAND | GTK_FILL, 0, + 0, 6); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic ("_Location:"); @@ -382,6 +423,13 @@ gtk_file_chooser_impl_default_constructor (GType type, gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry); + impl->preview_frame = gtk_frame_new ("Preview"); + gtk_table_attach (GTK_TABLE (table), impl->preview_frame, + 1, 2, 0, 2, + 0, GTK_EXPAND | GTK_FILL, + 0, 0); + /* Don't show preview frame initially */ + #if 0 focus_chain = g_list_append (NULL, impl->entry); focus_chain = g_list_append (focus_chain, impl->tree); @@ -488,6 +536,7 @@ gtk_file_chooser_impl_default_set_property (GObject *object, break; case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE: impl->preview_widget_active = g_value_get_boolean (value); + update_preview_widget_visibility (impl); break; case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE: { @@ -500,6 +549,8 @@ gtk_file_chooser_impl_default_set_property (GObject *object, gtk_tree_selection_set_mode (selection, (select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE)); + /* FIXME: See note in check_preview_change() */ + check_preview_change (impl); } } break; @@ -722,7 +773,7 @@ get_paths_foreach (GtkTreeModel *model, gtk_tree_model_get_iter (GTK_TREE_MODEL (info->impl->list_model), &child_iter, child_path); gtk_tree_path_free (child_path); - file_path = _gtk_file_system_model_get_path (info->impl->tree_model, &child_iter); + file_path = _gtk_file_system_model_get_path (info->impl->list_model, &child_iter); info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path)); } @@ -737,7 +788,7 @@ gtk_file_chooser_impl_default_get_paths (GtkFileChooser *chooser) GtkFileChooserImplDefault *impl; } info = { NULL, }; - if (!gtk_tree_view_get_model (GTK_TREE_VIEW (impl->list))) + if (!impl->sort_model) return NULL; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list)); @@ -748,6 +799,17 @@ gtk_file_chooser_impl_default_get_paths (GtkFileChooser *chooser) return g_slist_reverse (info.result); } +static GtkFilePath * +gtk_file_chooser_impl_default_get_preview_path (GtkFileChooser *chooser) +{ + GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser); + + if (impl->preview_path) + return gtk_file_path_copy (impl->preview_path); + else + return NULL; +} + static GtkFileSystem * gtk_file_chooser_impl_default_get_file_system (GtkFileChooser *chooser) { @@ -1052,7 +1114,13 @@ update_chooser_entry (GtkFileChooserImplDefault *impl) GtkTreeIter iter; GtkTreeIter child_iter; - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + /* Fixing this for multiple selection involves getting the full + * selection and diffing to find out what the most recently selected + * file is; there is logic in GtkFileSelection that probably can + * be copied; check_preview_change() is similar. + */ + if (impl->select_multiple || + !gtk_tree_selection_get_selected (selection, NULL, &iter)) return; gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, @@ -1075,6 +1143,49 @@ filter_option_menu_changed (GtkOptionMenu *option_menu, set_current_filter (impl, new_filter); } +static void +check_preview_change (GtkFileChooserImplDefault *impl) +{ + const GtkFilePath *new_path = NULL; + + /* Fixing preview for multiple selection involves getting the full + * selection and diffing to find out what the most recently selected + * file is; there is logic in GtkFileSelection that probably can + * be copied. update_chooser_entry() is similar. + */ + if (impl->sort_model && !impl->select_multiple) + { + GtkTreeSelection *selection; + GtkTreeIter iter; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list)); + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, + &child_iter, &iter); + + new_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter); + } + } + + if (new_path != impl->preview_path && + !(new_path && impl->preview_path && + gtk_file_path_compare (new_path, impl->preview_path) == 0)) + { + if (impl->preview_path) + gtk_file_path_free (impl->preview_path); + + if (new_path) + impl->preview_path = gtk_file_path_copy (new_path); + else + impl->preview_path = NULL; + + g_signal_emit_by_name (impl, "update-preview"); + } +} + static void tree_selection_changed (GtkTreeSelection *selection, GtkFileChooserImplDefault *impl) @@ -1137,9 +1248,9 @@ tree_selection_changed (GtkTreeSelection *selection, g_signal_emit_by_name (impl, "current-folder-changed", 0); update_chooser_entry (impl); + check_preview_change (impl); g_signal_emit_by_name (impl, "selection-changed", 0); - } static void @@ -1147,6 +1258,7 @@ list_selection_changed (GtkTreeSelection *selection, GtkFileChooserImplDefault *impl) { update_chooser_entry (impl); + check_preview_change (impl); g_signal_emit_by_name (impl, "selection-changed", 0); } diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index 6511b6903..17343854a 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -48,6 +48,7 @@ struct _GtkFileChooserIface void (*select_all) (GtkFileChooser *chooser); void (*unselect_all) (GtkFileChooser *chooser); GSList * (*get_paths) (GtkFileChooser *chooser); + GtkFilePath * (*get_preview_path) (GtkFileChooser *chooser); GtkFileSystem *(*get_file_system) (GtkFileChooser *chooser); void (*add_filter) (GtkFileChooser *chooser, GtkFileFilter *filter); @@ -60,8 +61,7 @@ struct _GtkFileChooserIface */ void (*current_folder_changed) (GtkFileChooser *chooser); void (*selection_changed) (GtkFileChooser *chooser); - void (*update_preview) (GtkFileChooser *chooser, - const gchar *uri); + void (*update_preview) (GtkFileChooser *chooser); }; GtkFileSystem *_gtk_file_chooser_get_file_system (GtkFileChooser *chooser); @@ -73,6 +73,7 @@ void _gtk_file_chooser_select_path (GtkFileChooser *cho void _gtk_file_chooser_unselect_path (GtkFileChooser *chooser, const GtkFilePath *path); GSList * _gtk_file_chooser_get_paths (GtkFileChooser *chooser); +GtkFilePath * _gtk_file_chooser_get_preview_path (GtkFileChooser *chooser); G_END_DECLS diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c index b6648f21c..e60c82660 100644 --- a/gtk/gtkfilechooserutils.c +++ b/gtk/gtkfilechooserutils.c @@ -36,6 +36,7 @@ static void delegate_unselect_path (GtkFileChooser *choose static void delegate_select_all (GtkFileChooser *chooser); static void delegate_unselect_all (GtkFileChooser *chooser); static GSList * delegate_get_paths (GtkFileChooser *chooser); +static GtkFilePath * delegate_get_preview_path (GtkFileChooser *chooser); static GtkFileSystem *delegate_get_file_system (GtkFileChooser *chooser); static void delegate_add_filter (GtkFileChooser *chooser, GtkFileFilter *filter); @@ -49,6 +50,8 @@ static void delegate_current_folder_changed (GtkFileChooser *choose gpointer data); static void delegate_selection_changed (GtkFileChooser *chooser, gpointer data); +static void delegate_update_preview (GtkFileChooser *chooser, + gpointer data); /** * _gtk_file_chooser_install_properties: @@ -133,6 +136,7 @@ _gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface) iface->select_all = delegate_select_all; iface->unselect_all = delegate_unselect_all; iface->get_paths = delegate_get_paths; + iface->get_preview_path = delegate_get_preview_path; iface->get_file_system = delegate_get_file_system; iface->add_filter = delegate_add_filter; iface->remove_filter = delegate_remove_filter; @@ -165,6 +169,8 @@ _gtk_file_chooser_set_delegate (GtkFileChooser *receiver, G_CALLBACK (delegate_current_folder_changed), receiver); g_signal_connect (delegate, "selection-changed", G_CALLBACK (delegate_selection_changed), receiver); + g_signal_connect (delegate, "update-preview", + G_CALLBACK (delegate_update_preview), receiver); } static GtkFileChooser * @@ -205,6 +211,12 @@ delegate_get_paths (GtkFileChooser *chooser) return _gtk_file_chooser_get_paths (get_delegate (chooser)); } +static GtkFilePath * +delegate_get_preview_path (GtkFileChooser *chooser) +{ + return _gtk_file_chooser_get_preview_path (get_delegate (chooser)); +} + static GtkFileSystem * delegate_get_file_system (GtkFileChooser *chooser) { @@ -267,12 +279,19 @@ static void delegate_selection_changed (GtkFileChooser *chooser, gpointer data) { - g_signal_emit_by_name (data, "selection-changed", 0); + g_signal_emit_by_name (data, "selection-changed"); } static void delegate_current_folder_changed (GtkFileChooser *chooser, gpointer data) { - g_signal_emit_by_name (data, "current-folder-changed", 0); + g_signal_emit_by_name (data, "current-folder-changed"); +} + +static void +delegate_update_preview (GtkFileChooser *chooser, + gpointer data) +{ + g_signal_emit_by_name (data, "update-preview"); } diff --git a/tests/testfilechooser.c b/tests/testfilechooser.c index 51f91e918..a4c35e750 100644 --- a/tests/testfilechooser.c +++ b/tests/testfilechooser.c @@ -1,4 +1,9 @@ #include +#include +#include +#include +#include +#include #include #include "gtkfilechooserdialog.h" @@ -11,6 +16,9 @@ #include "gtkfilesystemunix.h" #endif +static GtkWidget *preview_label; +static GtkWidget *preview_image; + static void print_current_folder (GtkFileChooser *chooser) { @@ -56,6 +64,195 @@ no_backup_files_filter (const GtkFileFilterInfo *filter_info, return 1; } +static char * +format_time (time_t t) +{ + gchar buf[128]; + struct tm tm_buf; + time_t now = time (NULL); + const char *format; + + if (abs (now - t) < 24*60*60) + format = "%X"; + else + format = "%x"; + + localtime_r (&t, &tm_buf); + if (strftime (buf, sizeof (buf), format, &tm_buf) == 0) + return g_strdup (""); + else + return g_strdup (buf); +} + +static char * +format_size (gint64 size) +{ + if (size < (gint64)1024) + return g_strdup_printf ("%d bytes", (gint)size); + else if (size < (gint64)1024*1024) + return g_strdup_printf ("%.1f K", size / (1024.)); + else if (size < (gint64)1024*1024*1024) + return g_strdup_printf ("%.1f M", size / (1024.*1024.)); + else + return g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.)); +} + +#include +#include +#define _(s) (s) + +static void +size_prepared_cb (GdkPixbufLoader *loader, + int width, + int height, + gpointer data) +{ + struct { + int width; + int height; + } *info = data; + + if ((double)height * (double)info->width > + (double)width * (double)info->height) { + width = 0.5 + (double)width * (double)info->height / (double)height; + height = info->height; + } else { + height = 0.5 + (double)height * (double)info->width / (double)width; + width = info->width; + } + + gdk_pixbuf_loader_set_size (loader, width, height); +} + +GdkPixbuf * +my_new_from_file_at_size (const char *filename, + int width, + int height, + GError **error) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + struct { + int width; + int height; + } info; + + guchar buffer [4096]; + int length; + FILE *f; + + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + f = fopen (filename, "rb"); + if (!f) { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + _("Failed to open file '%s': %s"), + filename, g_strerror (errno)); + return NULL; + } + + loader = gdk_pixbuf_loader_new (); +#ifdef DONT_PRESERVE_ASPECT + gdk_pixbuf_loader_set_size (loader, width, height); +#else + info.width = width; + info.height = height; + g_signal_connect (loader, "size-prepared", G_CALLBACK (&size_prepared_cb), &info); +#endif + + while (!feof (f)) { + length = fread (buffer, 1, sizeof (buffer), f); + if (length > 0) + if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) { + gdk_pixbuf_loader_close (loader, NULL); + fclose (f); + g_object_unref (G_OBJECT (loader)); + return NULL; + } + } + + fclose (f); + + if (!gdk_pixbuf_loader_close (loader, error)) { + g_object_unref (G_OBJECT (loader)); + return NULL; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (!pixbuf) { + g_object_unref (G_OBJECT (loader)); + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_FAILED, + _("Failed to load image '%s': reason not known, probably a corrupt image file"), + filename); + return NULL; + } + + g_object_ref (pixbuf); + + g_object_unref (G_OBJECT (loader)); + + return pixbuf; +} + +static void +update_preview_cb (GtkFileChooser *chooser) +{ + gchar *filename = gtk_file_chooser_get_preview_filename (chooser); + gboolean have_preview = FALSE; + + if (filename) + { + GdkPixbuf *pixbuf; + GError *error = NULL; + + pixbuf = my_new_from_file_at_size (filename, 128, 128, &error); + if (pixbuf) + { + gtk_image_set_from_pixbuf (GTK_IMAGE (preview_image), pixbuf); + g_object_unref (pixbuf); + gtk_widget_show (preview_image); + gtk_widget_hide (preview_label); + have_preview = TRUE; + } + else + { + struct stat buf; + if (stat (filename, &buf) == 0) + { + gchar *preview_text; + gchar *size_str; + gchar *modified_time; + + size_str = format_size (buf.st_size); + modified_time = format_time (buf.st_mtime); + + preview_text = g_strdup_printf ("Modified:\t%s\n" + "Size:\t%s\n", + modified_time, + size_str); + gtk_label_set_markup (GTK_LABEL (preview_label), preview_text); + g_free (modified_time); + g_free (size_str); + g_free (preview_text); + + gtk_widget_hide (preview_image); + gtk_widget_show (preview_label); + have_preview = TRUE; + } + } + + g_free (filename); + } + + gtk_file_chooser_set_preview_widget_active (chooser, have_preview); +} + int main (int argc, char **argv) { @@ -66,6 +263,7 @@ main (int argc, char **argv) GtkWidget *prop_editor; GtkFileSystem *file_system; GtkFileFilter *filter; + GtkWidget *preview_vbox; gtk_init (&argc, &argv); @@ -116,10 +314,28 @@ main (int argc, char **argv) gtk_file_filter_add_mime_type (filter, "image/png"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + /* Preview widget */ + preview_vbox = gtk_vbox_new (0, FALSE); + gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog), preview_vbox); + + preview_label = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX (preview_vbox), preview_label, TRUE, TRUE, 0); + gtk_misc_set_padding (GTK_MISC (preview_label), 6, 6); + + preview_image = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (preview_vbox), preview_image, TRUE, TRUE, 0); + gtk_misc_set_padding (GTK_MISC (preview_image), 6, 6); + + update_preview_cb (GTK_FILE_CHOOSER (dialog)); + g_signal_connect (dialog, "update-preview", + G_CALLBACK (update_preview_cb), NULL); + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 400); /* show_all() to reveal bugs in composite widget handling */ gtk_widget_show_all (dialog); + /* Extra controls for manipulating the test environment + */ prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER); control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); -- 2.43.2