]> Pileus Git - ~andy/gtk/commitdiff
Add over-big test case for preview (much of size from a modified copy of
authorOwen Taylor <otaylor@redhat.com>
Wed, 23 Jul 2003 22:30:32 +0000 (22:30 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Wed, 23 Jul 2003 22:30:32 +0000 (22:30 +0000)
Wed Jul 23 17:52:01 2003  Owen Taylor  <otaylor@redhat.com>

        * 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
gtk/gtkfilechooser.h
gtk/gtkfilechooserdefault.c
gtk/gtkfilechooserprivate.h
gtk/gtkfilechooserutils.c
tests/testfilechooser.c

index d1a6651d8460c5313181e52c353c4b13296df15e..c04ea5aef9f41ac5d61adaef2e732bfb4b53f46e 100644 (file)
@@ -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;
 }
-
-
index 1b708713d10976b1dbd3e076a4e86b70ba9ec3bb..afc797d7cf59f2a1082893cd79e47cce9c7e963c 100644 (file)
@@ -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
  */
index 3a3c378e4a6fbf034a3cbda0774cf737c6eff11c..25cdeeb5973f3b50b2a85c762ef44bc73ed964f6 100644 (file)
@@ -29,6 +29,7 @@
 #include <gtk/gtkcellrendererpixbuf.h>
 #include <gtk/gtkcellrenderertext.h>
 #include <gtk/gtkentry.h>
+#include <gtk/gtkframe.h>
 #include <gtk/gtkhbox.h>
 #include <gtk/gtkhpaned.h>
 #include <gtk/gtkicontheme.h>
@@ -36,6 +37,7 @@
 #include <gtk/gtkmenuitem.h>
 #include <gtk/gtkoptionmenu.h>
 #include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtktable.h>
 #include <gtk/gtktreeview.h>
 #include <gtk/gtktreemodelsort.h>
 #include <gtk/gtktreeselection.h>
@@ -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);
 }
index 6511b69039af679052d86ae60daf2fa6f85eed78..17343854acf4d84b41ad4adba8042aa7385447a3 100644 (file)
@@ -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
 
index b6648f21c1e6373a47b3cf8d044756f9c93ad26b..e60c82660d23bc39352aaed43520f352219d05e3 100644 (file)
@@ -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");
 }
index 51f91e9188e557b9462aebea3f4f4a39d183a0f2..a4c35e7505a3323a5726c1a8d64c012b60700ba9 100644 (file)
@@ -1,4 +1,9 @@
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
 
 #include <gtk/gtk.h>
 #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 ("<unknown>");
+  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 <stdio.h>
+#include <errno.h>
+#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 ("<i>Modified:</i>\t%s\n"
+                                             "<i>Size:</i>\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);