]> Pileus Git - ~andy/gtk/commitdiff
File filter objects.
authorOwen Taylor <otaylor@redhat.com>
Wed, 23 Jul 2003 15:31:10 +0000 (15:31 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Wed, 23 Jul 2003 15:31:10 +0000 (15:31 +0000)
Wed Jul 23 11:23:43 2003  Owen Taylor  <otaylor@redhat.com>

        * gtkfilefilter.[ch]: File filter objects.

        * gtkfilechooser.[ch] gtkfilechooserutils.[ch]: Add
        file filtering to API.

        * gtkfilechooserimpldefault.c: Implement file filters.

        * testfilechooser.c: Try out the filter functionality.

        * gtkfilesystemmodel.c: Add
        _gtk_file_system_model_set_filter() to set a callback
        function for filtering.

        * gtkfilechooserutils.c: Propagate property notification
        to the receiver.

        * fnmatch.c: Copy this from GTK+ temporarily to get
        UTF-8 pattern matching functionality.

gtk/gtkfilechooser.c
gtk/gtkfilechooser.h
gtk/gtkfilechooserdefault.c
gtk/gtkfilechooserprivate.h
gtk/gtkfilechooserutils.c
gtk/gtkfilechooserutils.h
gtk/gtkfilefilter.c [new file with mode: 0644]
gtk/gtkfilefilter.h [new file with mode: 0644]
gtk/gtkfilesystemmodel.c
gtk/gtkfilesystemmodel.h
tests/testfilechooser.c

index 8daedd81254e472dea9ee4dff75644e2eab8d077..d1a6651d8460c5313181e52c353c4b13296df15e 100644 (file)
@@ -91,6 +91,12 @@ gtk_file_chooser_base_init (gpointer g_iface)
                                                                _("File system object to use"),
                                                                GTK_TYPE_FILE_SYSTEM,
                                                                G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+      g_object_interface_install_property (g_iface,
+                                          g_param_spec_object ("filter",
+                                                               _("Filter"),
+                                                               _("The current filter for selecting which files are displayed"),
+                                                               GTK_TYPE_FILE_FILTER,
+                                                               G_PARAM_READWRITE));
       g_object_interface_install_property (g_iface,
                                           g_param_spec_boolean ("folder-mode",
                                                                 _("Folder Mode"),
@@ -932,6 +938,13 @@ gtk_file_chooser_get_preview_widget (GtkFileChooser *chooser)
   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
 
   g_object_get (chooser, "preview-widget", &preview_widget, NULL);
+  
+  /* Horrid hack; g_object_get() refs returned objects but
+   * that contradicts the memory management conventions
+   * for accessors.
+   */
+  if (preview_widget)
+    g_object_unref (preview_widget);
 
   return preview_widget;
 }
@@ -1046,3 +1059,107 @@ gtk_file_chooser_get_preview_uri (GtkFileChooser *chooser)
 
   return result;
 }
+
+/**
+ * gtk_file_chooser_add_filter:
+ * @chooser: a #GtkFileChooser
+ * @filter: a #GtkFileFilter
+ * 
+ * Adds @filter to the list of filters that the user can select between.
+ * When a filter is selected, only files that are passed by that
+ * filter are displayed.
+ **/
+void
+gtk_file_chooser_add_filter (GtkFileChooser *chooser,
+                            GtkFileFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+  GTK_FILE_CHOOSER_GET_IFACE (chooser)->add_filter (chooser, filter);
+}
+
+/**
+ * gtk_file_chooser_add_filter:
+ * @chooser: a #GtkFileChooser
+ * @filter: a #GtkFileFilter
+ * 
+ * Removes @filter from the list of filters that the user can select between.
+ **/
+void
+gtk_file_chooser_remove_filter (GtkFileChooser *chooser,
+                               GtkFileFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+  GTK_FILE_CHOOSER_GET_IFACE (chooser)->remove_filter (chooser, filter);
+}
+
+/**
+ * gtk_file_chooser_list_filters:
+ * @choooser: a #GtkFileChooser
+ * 
+ * Lists the current set of user-selectable filters; see
+ * gtk_file_chooser_add_filter(), gtk_file_chooser_remove_filter().
+ * 
+ * Return value: a #GSList containing the current set of
+ *  user selectable filters. The contents of the list are
+ *  owned by GTK+, but you must free the list itself with
+ *  g_slist_free() when you are done with it.
+ **/
+GSList *
+gtk_file_chooser_list_filters  (GtkFileChooser *chooser)
+{
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+  return GTK_FILE_CHOOSER_GET_IFACE (chooser)->list_filters (chooser);
+}
+
+/**
+ * gtk_file_chooser_set_filter:
+ * @chooser: a #GtkFileChooser
+ * @filter: a #GtkFileFilter
+ * 
+ * Sets the current filter; only the files that pass the
+ * filter will be displayed. If the user-selectable list of filters
+ * is non-empty, then the filter should be one of the filters
+ * in that list. Setting the current filter when the list of
+ * filters is empty is useful if you want to restrict the displayed
+ * set of files without letting the user change it.
+ **/
+void
+gtk_file_chooser_set_filter (GtkFileChooser *chooser,
+                            GtkFileFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+
+  g_object_set (chooser, "filter", filter, NULL);
+}
+
+/**
+ * gtk_file_chooser_get_filter:
+ * @chooser: a #GtkFileChooser
+ * 
+ * Gets the current filter; see gtk_file_chooser_set_filter().
+ * 
+ * Return value: the current filter, or %NULL
+ **/
+GtkFileFilter *
+gtk_file_chooser_get_filter (GtkFileChooser *chooser)
+{
+  GtkFileFilter *filter;
+  
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+  g_object_get (chooser, "filter", &filter, NULL);
+  /* Horrid hack; g_object_get() refs returned objects but
+   * that contradicts the memory management conventions
+   * for accessors.
+   */
+  if (filter)
+    g_object_unref (filter);
+
+  return filter;
+}
+
+
index 145cb2c1f14b00267245d6c913d96856eb6e9b32..1b708713d10976b1dbd3e076a4e86b70ba9ec3bb 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef __GTK_FILE_CHOOSER_H__
 #define __GTK_FILE_CHOOSER_H__
 
+#include "gtkfilefilter.h"
 #include <gtk/gtkwidget.h>
 
 G_BEGIN_DECLS
@@ -100,29 +101,19 @@ 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);
 
-
-#if 0
-/* Filters
+/* List of user selectable filters
  */
-void   gtk_file_chooser_add_filter    (GtkFileChooser *chooser,
-                                      GtkFileFilter  *filter);
-void   gtk_file_chooser_remove_filter (GtkFileChooser *chooser,
-                                      GtkFileFilter  *filter);
-GList *gtk_file_chooser_get_filters   (GtkFileChooser  *chooser);
-
-
-/************************************************/
+void    gtk_file_chooser_add_filter    (GtkFileChooser *chooser,
+                                       GtkFileFilter  *filter);
+void    gtk_file_chooser_remove_filter (GtkFileChooser *chooser,
+                                       GtkFileFilter  *filter);
+GSList *gtk_file_chooser_list_filters  (GtkFileChooser *chooser);
 
-static gboolean (*GtkFileFilterFunc) (const char *uri,
-                                     const char *filename,
-                                     gpointer    data);
-
-GtkFileFilter *gtk_file_filter_new_pattern   (const char            *pattern);
-GtkFileFilter *gtk_file_filter_new_mime_type (const char            *mime_type);
-GtkFileFilter *gtk_file_filter_new_callback  (GtkFileFilterFunction *func,
-                                             gpointer               data,
-                                             GDestroyNotify         notify);
-#endif
+/* Current filter
+ */
+void           gtk_file_chooser_set_filter (GtkFileChooser *chooser,
+                                          GtkFileFilter  *filter);
+GtkFileFilter *gtk_file_chooser_get_filter (GtkFileChooser *chooser);
 
 G_END_DECLS
 
index 9c5222e79b3fc89b925e390415172043e75c4c7c..3a3c378e4a6fbf034a3cbda0774cf737c6eff11c 100644 (file)
@@ -25,6 +25,7 @@
 #include "gtkfilechooser.h"
 #include "gtkfilesystemmodel.h"
 
+#include <gtk/gtkalignment.h>
 #include <gtk/gtkcellrendererpixbuf.h>
 #include <gtk/gtkcellrenderertext.h>
 #include <gtk/gtkentry.h>
@@ -32,6 +33,8 @@
 #include <gtk/gtkhpaned.h>
 #include <gtk/gtkicontheme.h>
 #include <gtk/gtklabel.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkoptionmenu.h>
 #include <gtk/gtkscrolledwindow.h>
 #include <gtk/gtktreeview.h>
 #include <gtk/gtktreemodelsort.h>
@@ -62,6 +65,9 @@ struct _GtkFileChooserImplDefault
 
   GtkFileChooserAction action;
 
+  GtkFileFilter *current_filter;
+  GSList *filters;
+  
   GtkFilePath *current_folder;
 
   guint folder_mode : 1;
@@ -70,6 +76,8 @@ struct _GtkFileChooserImplDefault
   guint select_multiple : 1;
   guint show_hidden : 1;
 
+  GtkWidget *filter_alignment;
+  GtkWidget *filter_option_menu;
   GtkWidget *tree_scrollwin;
   GtkWidget *tree;
   GtkWidget *list_scrollwin;
@@ -94,6 +102,7 @@ static void     gtk_file_chooser_impl_default_get_property (GObject
                                                            guint                  prop_id,
                                                            GValue                *value,
                                                            GParamSpec            *pspec);
+static void     gtk_file_chooser_impl_default_show_all     (GtkWidget             *widget);
 
 static void           gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser    *chooser,
                                                                        const GtkFilePath *path);
@@ -108,13 +117,23 @@ static void           gtk_file_chooser_impl_default_select_all         (GtkFileC
 static void           gtk_file_chooser_impl_default_unselect_all       (GtkFileChooser    *chooser);
 static GSList *       gtk_file_chooser_impl_default_get_paths          (GtkFileChooser    *chooser);
 static GtkFileSystem *gtk_file_chooser_impl_default_get_file_system    (GtkFileChooser    *chooser);
-
-static void tree_selection_changed (GtkTreeSelection          *tree_selection,
-                                   GtkFileChooserImplDefault *impl);
-static void list_selection_changed (GtkTreeSelection          *tree_selection,
-                                   GtkFileChooserImplDefault *impl);
-static void entry_activate         (GtkEntry                  *entry,
-                                   GtkFileChooserImplDefault *impl);
+static void           gtk_file_chooser_impl_default_add_filter         (GtkFileChooser    *chooser,
+                                                                       GtkFileFilter     *filter);
+static void           gtk_file_chooser_impl_default_remove_filter      (GtkFileChooser    *chooser,
+                                                                       GtkFileFilter     *filter);
+static GSList *       gtk_file_chooser_impl_default_list_filters       (GtkFileChooser    *chooser);
+
+static void set_current_filter (GtkFileChooserImplDefault *impl,
+                               GtkFileFilter             *filter);
+
+static void filter_option_menu_changed (GtkOptionMenu             *option_menu,
+                                       GtkFileChooserImplDefault *impl);
+static void tree_selection_changed     (GtkTreeSelection          *tree_selection,
+                                       GtkFileChooserImplDefault *impl);
+static void list_selection_changed     (GtkTreeSelection          *tree_selection,
+                                       GtkFileChooserImplDefault *impl);
+static void entry_activate             (GtkEntry                  *entry,
+                                       GtkFileChooserImplDefault *impl);
 
 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
                                 GtkCellRenderer   *cell,
@@ -180,6 +199,7 @@ static void
 gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
 
   parent_class = g_type_class_peek_parent (class);
 
@@ -188,6 +208,8 @@ gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
   gobject_class->set_property = gtk_file_chooser_impl_default_set_property;
   gobject_class->get_property = gtk_file_chooser_impl_default_get_property;
 
+  widget_class->show_all = gtk_file_chooser_impl_default_show_all;
+
   _gtk_file_chooser_install_properties (gobject_class);
 }
 
@@ -203,6 +225,9 @@ gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface)
   iface->set_current_folder = gtk_file_chooser_impl_default_set_current_folder;
   iface->get_current_folder = gtk_file_chooser_impl_default_get_current_folder;
   iface->set_current_name = gtk_file_chooser_impl_default_set_current_name;
+  iface->add_filter = gtk_file_chooser_impl_default_add_filter;
+  iface->remove_filter = gtk_file_chooser_impl_default_remove_filter;
+  iface->list_filters = gtk_file_chooser_impl_default_list_filters;
 }
 
 static void
@@ -274,6 +299,30 @@ gtk_file_chooser_impl_default_constructor (GType                  type,
 
   gtk_widget_push_composite_child ();
 
+  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);
+  /* Don't show filter initially */
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_container_add (GTK_CONTAINER (impl->filter_alignment), hbox);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new_with_mnemonic ("Files of _type:");
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  impl->filter_option_menu = gtk_option_menu_new ();
+  gtk_option_menu_set_menu (GTK_OPTION_MENU (impl->filter_option_menu),
+                           gtk_menu_new ());
+  gtk_box_pack_start (GTK_BOX (hbox), impl->filter_option_menu, FALSE, FALSE, 0);
+  gtk_widget_show (impl->filter_option_menu);
+
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->filter_option_menu);
+
+  g_signal_connect (impl->filter_option_menu, "changed",
+                   G_CALLBACK (filter_option_menu_changed), impl);
+
   hpaned = gtk_hpaned_new ();
   gtk_box_pack_start (GTK_BOX (impl), hpaned, TRUE, TRUE, 0);
   gtk_widget_show (hpaned);
@@ -415,6 +464,9 @@ gtk_file_chooser_impl_default_set_property (GObject         *object,
          }
       }
       break;
+    case GTK_FILE_CHOOSER_PROP_FILTER:
+      set_current_filter (impl, g_value_get_object (value));
+      break;
     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
       {        
        gboolean folder_mode = g_value_get_boolean (value);
@@ -481,6 +533,9 @@ gtk_file_chooser_impl_default_get_property (GObject         *object,
     case GTK_FILE_CHOOSER_PROP_ACTION:
       g_value_set_enum (value, impl->action);
       break;
+    case GTK_FILE_CHOOSER_PROP_FILTER:
+      g_value_set_object (value, impl->current_filter);
+      break;
     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
       g_value_set_boolean (value, impl->folder_mode);
       break;
@@ -505,6 +560,16 @@ gtk_file_chooser_impl_default_get_property (GObject         *object,
     }
 }
 
+/* We override show-all since we have internal widgets that
+ * shouldn't be shown when you call show_all(), like the filter
+ * option menu.
+ */
+static void
+gtk_file_chooser_impl_default_show_all (GtkWidget *widget)
+{
+  gtk_widget_show (widget);
+}
+
 static void
 expand_and_select_func (GtkFileSystemModel *model,
                        GtkTreePath        *path,
@@ -587,7 +652,7 @@ gtk_file_chooser_impl_default_select_path (GtkFileChooser    *chooser,
       _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
       gtk_file_path_free (parent_path);
       _gtk_file_system_model_path_do (impl->list_model, path,
-                                    select_func, impl);
+                                     select_func, impl);
     }
 }
 
@@ -691,6 +756,209 @@ gtk_file_chooser_impl_default_get_file_system (GtkFileChooser *chooser)
   return impl->file_system;
 }
 
+static GtkWidget *
+find_filter_menu_item (GtkFileChooserImplDefault *impl,
+                      GtkFileFilter             *filter,
+                      gint                      *index_return)
+{
+  GtkWidget *menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
+  GList *children = gtk_container_get_children (GTK_CONTAINER (menu));
+  GList *tmp_list;
+  int index = 0;
+
+  if (index_return)
+    *index_return = -1;
+
+  for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
+    {
+      if (g_object_get_data (tmp_list->data, "gtk-file-filter") == filter)
+       {
+         if (index_return)
+           *index_return = index;
+         return tmp_list->data;
+       }
+      index++;
+    }
+
+  g_list_free (children);
+  
+  return NULL;
+}
+
+static void
+gtk_file_chooser_impl_default_add_filter (GtkFileChooser *chooser,
+                                         GtkFileFilter  *filter)
+{
+  GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+  GtkWidget *menu;
+  GtkWidget *menu_item;
+  const gchar *name;
+
+  if (g_slist_find (impl->filters, filter))
+    {
+      g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
+      return;
+    }
+
+  g_object_ref (filter);
+  gtk_object_sink (GTK_OBJECT (filter));
+  impl->filters = g_slist_append (impl->filters, filter);
+
+  name = gtk_file_filter_get_name (filter);
+  if (!name)
+    name = "Untitled filter";  /* Place-holder, doesn't need to be marked for translation */
+  
+  menu_item = gtk_menu_item_new_with_label (name);
+  g_object_set_data (G_OBJECT (menu_item), "gtk-file-filter", filter);
+  gtk_widget_show (menu_item);
+  
+  menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+  /* Option menus don't react to menu size changes properly */
+  gtk_widget_size_request (menu, NULL);
+
+  if (!g_slist_find (impl->filters, impl->current_filter))
+    set_current_filter (impl, filter);
+
+  gtk_widget_show (impl->filter_alignment);
+}
+
+static void
+gtk_file_chooser_impl_default_remove_filter (GtkFileChooser    *chooser,
+                                            GtkFileFilter     *filter)
+{
+  GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+  GtkWidget *menu;
+  GtkWidget *menu_item;
+    
+  if (!g_slist_find (impl->filters, filter))
+    {
+      g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
+      return;
+    }
+
+  impl->filters = g_slist_remove (impl->filters, filter);
+
+  if (filter == impl->current_filter)
+    {
+      if (impl->filters)
+       set_current_filter (impl, impl->filters->data);
+      else
+       set_current_filter (impl, NULL);
+    }
+
+  menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
+  menu_item = find_filter_menu_item (impl, filter, NULL);
+  g_assert (menu_item);
+  gtk_widget_destroy (menu_item);
+  /* Option menus don't react to menu size changes properly */
+  gtk_widget_size_request (menu, NULL);
+  
+  g_object_unref (filter);
+
+  if (!impl->filters)
+    gtk_widget_hide (impl->filter_alignment);
+}
+
+static GSList *
+gtk_file_chooser_impl_default_list_filters (GtkFileChooser *chooser)
+{
+  GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+
+  return g_slist_copy (impl->filters);
+}
+
+static gboolean
+list_model_filter_func (GtkFileSystemModel *model,
+                       GtkFilePath        *path,
+                       const GtkFileInfo  *file_info,
+                       gpointer            user_data)
+{
+  GtkFileChooserImplDefault *impl = user_data;
+  GtkFileFilterInfo filter_info;
+  GtkFileFilterFlags needed;
+  gboolean result;
+  
+  if (!impl->current_filter)
+    return TRUE;
+
+  filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
+    
+  needed = gtk_file_filter_get_needed (impl->current_filter);
+  
+  filter_info.display_name = gtk_file_info_get_display_name (file_info);
+  filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
+
+  if (needed & GTK_FILE_FILTER_FILENAME)
+    {
+      filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
+      if (filter_info.filename)
+       filter_info.contains |= GTK_FILE_FILTER_FILENAME;
+    }
+  else
+    filter_info.filename = NULL;
+
+  if (needed & GTK_FILE_FILTER_URI)
+    {
+      filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
+      if (filter_info.filename)
+       filter_info.contains |= GTK_FILE_FILTER_URI;
+    }
+  else
+    filter_info.uri = NULL;
+
+  result = gtk_file_filter_filter (impl->current_filter, &filter_info);
+
+  if (filter_info.filename)
+    g_free ((gchar *)filter_info.filename);
+  if (filter_info.uri)
+    g_free ((gchar *)filter_info.uri);
+
+  return result;
+}
+
+static void
+install_list_model_filter (GtkFileChooserImplDefault *impl)
+{
+  if (impl->current_filter)
+    _gtk_file_system_model_set_filter (impl->list_model,
+                                      list_model_filter_func,
+                                      impl);
+}
+
+static void
+set_current_filter (GtkFileChooserImplDefault *impl,
+                   GtkFileFilter             *filter)
+{
+  if (impl->current_filter != filter)
+    {
+      int menu_item_index;
+
+      /* If we have filters, new filter must be one of them
+       */
+      find_filter_menu_item (impl, filter, &menu_item_index);
+      if (impl->filters && menu_item_index < 0)
+       return;
+      
+      if (impl->current_filter)
+       g_object_unref (impl->current_filter);
+      impl->current_filter = filter;
+      if (impl->current_filter)
+       {
+         g_object_ref (impl->current_filter);
+         gtk_object_sink (GTK_OBJECT (filter));
+       }
+      
+      if (impl->filters)
+       gtk_option_menu_set_history (GTK_OPTION_MENU (impl->filter_option_menu),
+                                    menu_item_index);
+
+      install_list_model_filter (impl);
+
+      g_object_notify (G_OBJECT (impl), "filter");
+    }
+}
+
 static gint
 name_sort_func (GtkTreeModel *model,
                GtkTreeIter  *a,
@@ -797,6 +1065,16 @@ update_chooser_entry (GtkFileChooserImplDefault *impl)
                                         gtk_file_info_get_display_name (info));
 }
      
+static void
+filter_option_menu_changed (GtkOptionMenu             *option_menu,
+                           GtkFileChooserImplDefault *impl)
+{
+  gint new_index = gtk_option_menu_get_history (GTK_OPTION_MENU (option_menu));
+  GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
+
+  set_current_filter (impl, new_filter);
+}
+
 static void
 tree_selection_changed (GtkTreeSelection          *selection,
                        GtkFileChooserImplDefault *impl)
@@ -843,6 +1121,7 @@ tree_selection_changed (GtkTreeSelection          *selection,
                                                 GTK_FILE_INFO_DISPLAY_NAME |
                                                 GTK_FILE_INFO_SIZE); 
   _gtk_file_system_model_set_show_folders (impl->list_model, FALSE);
+  install_list_model_filter (impl);
   
   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->list_model));
   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), 0, name_sort_func, impl, NULL);
index d3b73f1ec06569f477d0a4e91e5ad6d9c14a1875..6511b69039af679052d86ae60daf2fa6f85eed78 100644 (file)
@@ -34,18 +34,6 @@ struct _GtkFileChooserIface
 {
   GTypeInterface base_iface;
 
-  /* GtkFileChooser interface has the following properties:
-   *
-   *  action:          GtkFileChooserAction
-   *  folder_mode:     boolean
-   *  select_multiple: boolean
-   *  show_hidden:     boolean
-   *  local_only:      boolean
-   *
-   *  preview_widget:  GtkWidget
-   *  preview_widget_active: boolean
-   */
-
   /* Methods
    */
   void           (*set_current_folder) (GtkFileChooser    *chooser,
@@ -61,6 +49,12 @@ struct _GtkFileChooserIface
   void           (*unselect_all)       (GtkFileChooser    *chooser);
   GSList *       (*get_paths)          (GtkFileChooser    *chooser);
   GtkFileSystem *(*get_file_system)    (GtkFileChooser    *chooser);
+  void           (*add_filter)         (GtkFileChooser    *chooser,
+                                       GtkFileFilter     *filter);
+  void           (*remove_filter)      (GtkFileChooser    *chooser,
+                                       GtkFileFilter     *filter);
+  GSList *       (*list_filters)       (GtkFileChooser    *chooser);
+
   
   /* Signals
    */
index 9c0e51ced04ba7b6cb91de698ba6b00393ac41f0..b6648f21c1e6373a47b3cf8d044756f9c93ad26b 100644 (file)
@@ -37,6 +37,14 @@ static void           delegate_select_all             (GtkFileChooser    *choose
 static void           delegate_unselect_all           (GtkFileChooser    *chooser);
 static GSList *       delegate_get_paths              (GtkFileChooser    *chooser);
 static GtkFileSystem *delegate_get_file_system        (GtkFileChooser    *chooser);
+static void           delegate_add_filter             (GtkFileChooser    *chooser,
+                                                      GtkFileFilter     *filter);
+static void           delegate_remove_filter          (GtkFileChooser    *chooser,
+                                                      GtkFileFilter     *filter);
+static GSList *       delegate_list_filters           (GtkFileChooser    *chooser);
+static void           delegate_notify                 (GObject           *object,
+                                                      GParamSpec        *pspec,
+                                                      gpointer           data);
 static void           delegate_current_folder_changed (GtkFileChooser    *chooser,
                                                       gpointer           data);
 static void           delegate_selection_changed      (GtkFileChooser    *chooser,
@@ -66,6 +74,11 @@ _gtk_file_chooser_install_properties (GObjectClass *klass)
                                   g_param_spec_override ("file-system",
                                                          GTK_TYPE_FILE_SYSTEM,
                                                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (klass,
+                                  GTK_FILE_CHOOSER_PROP_FILTER,
+                                  g_param_spec_override ("filter",
+                                                         GTK_TYPE_FILE_FILTER,
+                                                         G_PARAM_READWRITE));
   g_object_class_install_property (klass,
                                   GTK_FILE_CHOOSER_PROP_FOLDER_MODE,
                                   g_param_spec_override ("folder-mode",
@@ -121,6 +134,9 @@ _gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface)
   iface->unselect_all = delegate_unselect_all;
   iface->get_paths = delegate_get_paths;
   iface->get_file_system = delegate_get_file_system;
+  iface->add_filter = delegate_add_filter;
+  iface->remove_filter = delegate_remove_filter;
+  iface->list_filters = delegate_list_filters;
 }
 
 /**
@@ -143,6 +159,8 @@ _gtk_file_chooser_set_delegate (GtkFileChooser *receiver,
   
   g_object_set_data (G_OBJECT (receiver), "gtk-file-chooser-delegate", delegate);
 
+  g_signal_connect (delegate, "notify",
+                   G_CALLBACK (delegate_notify), receiver);
   g_signal_connect (delegate, "current-folder-changed",
                    G_CALLBACK (delegate_current_folder_changed), receiver);
   g_signal_connect (delegate, "selection-changed",
@@ -193,6 +211,26 @@ delegate_get_file_system (GtkFileChooser *chooser)
   return _gtk_file_chooser_get_file_system (get_delegate (chooser));
 }
 
+static void
+delegate_add_filter (GtkFileChooser *chooser,
+                    GtkFileFilter  *filter)
+{
+  gtk_file_chooser_add_filter (get_delegate (chooser), filter);
+}
+
+static void
+delegate_remove_filter (GtkFileChooser *chooser,
+                       GtkFileFilter  *filter)
+{
+  gtk_file_chooser_remove_filter (get_delegate (chooser), filter);
+}
+
+static GSList *
+delegate_list_filters (GtkFileChooser *chooser)
+{
+  return gtk_file_chooser_list_filters (get_delegate (chooser));
+}
+
 static void
 delegate_set_current_folder (GtkFileChooser    *chooser,
                             const GtkFilePath *path)
@@ -213,6 +251,18 @@ delegate_set_current_name (GtkFileChooser *chooser,
   gtk_file_chooser_set_current_name (get_delegate (chooser), name);
 }
 
+static void
+delegate_notify (GObject    *object,
+                GParamSpec *pspec,
+                gpointer    data)
+{
+  if (pspec->param_id >= GTK_FILE_CHOOSER_PROP_FIRST &&
+      pspec->param_id <= GTK_FILE_CHOOSER_PROP_LAST)
+    {
+      g_object_notify (data, pspec->name);
+    }
+}
+
 static void
 delegate_selection_changed (GtkFileChooser *chooser,
                            gpointer        data)
index 56e47d55ded42c0f103c56f3592517283c1b8e4a..874e56da31bc365f5f8dd0a053f19b3dda220496 100644 (file)
 G_BEGIN_DECLS
 
 typedef enum {
-  GTK_FILE_CHOOSER_PROP_ACTION                 = 0x1000,
+  GTK_FILE_CHOOSER_PROP_FIRST                  = 0x1000,
+  GTK_FILE_CHOOSER_PROP_ACTION                 = GTK_FILE_CHOOSER_PROP_FIRST,
   GTK_FILE_CHOOSER_PROP_FILE_SYSTEM,
+  GTK_FILE_CHOOSER_PROP_FILTER,
   GTK_FILE_CHOOSER_PROP_FOLDER_MODE,            
   GTK_FILE_CHOOSER_PROP_LOCAL_ONLY,
   GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET, 
   GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE,
   GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE,
-  GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN
+  GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN,
+  GTK_FILE_CHOOSER_PROP_LAST                   = GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN,
 } GtkFileChooserProp;
 
 void _gtk_file_chooser_install_properties (GObjectClass *klass);
diff --git a/gtk/gtkfilefilter.c b/gtk/gtkfilefilter.c
new file mode 100644 (file)
index 0000000..b57bf25
--- /dev/null
@@ -0,0 +1,372 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilefilter.c: Filters for selecting a file subset
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkfilefilter.h"
+
+#include <gtk/gtkobject.h>
+
+#include <string.h>
+
+typedef struct _GtkFileFilterClass GtkFileFilterClass;
+typedef struct _FilterRule FilterRule;
+
+#define GTK_FILE_FILTER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
+#define GTK_IS_FILE_FILTER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FILTER))
+#define GTK_FILE_FILTER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
+
+typedef enum {
+  FILTER_RULE_PATTERN,
+  FILTER_RULE_MIME_TYPE,
+  FILTER_RULE_CUSTOM,
+} FilterRuleType;
+
+struct _GtkFileFilterClass
+{
+  GtkObjectClass parent_class;
+};
+
+struct _GtkFileFilter
+{
+  GtkObject parent_instance;
+  
+  gchar *name;
+  GSList *rules;
+
+  GtkFileFilterFlags needed;
+};
+
+struct _FilterRule
+{
+  FilterRuleType type;
+  GtkFileFilterFlags needed;
+  
+  union {
+    gchar *pattern;
+    gchar *mime_type;
+    struct {
+      GtkFileFilterFunc func;
+      gpointer data;
+      GDestroyNotify notify;
+    } custom;
+  } u;
+};
+
+static void gtk_file_filter_class_init (GtkFileFilterClass *class);
+static void gtk_file_filter_finalize   (GObject            *object);
+
+static GObjectClass *parent_class;
+
+GType
+gtk_file_filter_get_type (void)
+{
+  static GType file_filter_type = 0;
+
+  if (!file_filter_type)
+    {
+      static const GTypeInfo file_filter_info =
+      {
+       sizeof (GtkFileFilterClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_file_filter_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkFileFilter),
+       0,              /* n_preallocs */
+       NULL            /* init */
+      };
+      
+      file_filter_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkFileFilter",
+                                                &file_filter_info, 0);
+    }
+
+  return file_filter_type;
+}
+
+static void
+gtk_file_filter_class_init (GtkFileFilterClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  gobject_class->finalize = gtk_file_filter_finalize;
+}
+
+static void
+filter_rule_free (FilterRule *rule)
+{
+  switch (rule->type)
+    {
+    case FILTER_RULE_MIME_TYPE:
+      g_free (rule->u.mime_type);
+      break;
+    case FILTER_RULE_PATTERN:
+      g_free (rule->u.pattern);
+      break;
+    case FILTER_RULE_CUSTOM:
+      if (rule->u.custom.notify)
+       rule->u.custom.notify (rule->u.custom.data);
+      break;
+    }
+
+  g_free (rule);
+}
+
+static void
+gtk_file_filter_finalize (GObject  *object)
+{
+  GtkFileFilter *filter = GTK_FILE_FILTER (filter);
+
+  g_slist_foreach (filter->rules, (GFunc)filter_rule_free, NULL);
+
+  parent_class->finalize (object);
+}
+
+/**
+ * gtk_file_filter_new:
+ * 
+ * Creates a new #GtkFileFilter with no rules added to it.
+ * Such a filter doesn't accept any files, so is not
+ * particularly useful until you add rules with
+ * gtk_file_filter_add_mime_type(), gtk_file_filter_add_pattern(),
+ * or gtk_file_filter_add_custom(). To create a filter
+ * that accepts any file, use:
+ *
+ * <informalexample><programlisting>
+ * GtkFileFilter *filter = gtk_file_filter_new ();
+ * gtk_file_filter_add_pattern (filter, "*");
+ * </programlisting></programlisting>
+ * 
+ * Return value: a new #GtkFileFilter
+ **/
+GtkFileFilter *
+gtk_file_filter_new (void)
+{
+  return g_object_new (GTK_TYPE_FILE_FILTER, NULL);
+}
+
+/**
+ * gtk_file_filter_set_name:
+ * @filter: a #GtkFileFilter
+ * @name: the human-readable-name for the filter, or %NULL
+ *   to remove any existing name.
+ * 
+ * Sets the human-readable name of the filter; this is the string
+ * that will be displayed in the file selector user interface if
+ * there is a selectable list of filters.
+ **/
+void
+gtk_file_filter_set_name (GtkFileFilter *filter,
+                         const gchar   *name)
+{
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  
+  if (filter->name)
+    g_free (filter->name);
+
+  filter->name = g_strdup (name);
+}
+
+/**
+ * gtk_file_filter_get_name:
+ * @filter: a #GtkFileFilter
+ * 
+ * Gets the human-readable name for the filter. See gtk_file_filter_set_name().
+ * 
+ * Return value: The human-readable name of the filter,
+ *   or %NULL. This value is owned by GTK+ and must not
+ *   be modified or freed.
+ **/
+G_CONST_RETURN gchar *
+gtk_file_filter_get_name (GtkFileFilter *filter)
+{
+  g_return_val_if_fail (GTK_IS_FILE_FILTER (filter), NULL);
+  
+  return filter->name;
+}
+
+static void
+file_filter_add_rule (GtkFileFilter *filter,
+                     FilterRule    *rule)
+{
+  filter->needed |= rule->needed;
+  filter->rules = g_slist_append (filter->rules, rule);
+}
+
+/**
+ * gtk_file_filter_add_mime_type:
+ * @filter: A #GtkFileFilter
+ * @mime_type: name of a MIME type
+ * 
+ * Adds a rule allowing a given mime type to @filter.
+ **/
+void
+gtk_file_filter_add_mime_type (GtkFileFilter *filter,
+                              const gchar   *mime_type)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  g_return_if_fail (mime_type != NULL);
+
+  rule = g_new (FilterRule, 1);
+  rule->type = FILTER_RULE_MIME_TYPE;
+  rule->needed = GTK_FILE_FILTER_MIME_TYPE;
+  rule->u.mime_type = g_strdup (mime_type);
+
+  file_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_file_filter_add_pattern:
+ * @filter: a #GtkFileFilter
+ * @pattern: a shell style glob
+ * 
+ * Adds a rule allowing a shell style glob to a filter.
+ **/
+void
+gtk_file_filter_add_pattern (GtkFileFilter *filter,
+                            const gchar   *pattern)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  g_return_if_fail (pattern != NULL);
+
+  rule = g_new (FilterRule, 1);
+  rule->type = FILTER_RULE_PATTERN;
+  rule->needed = GTK_FILE_FILTER_DISPLAY_NAME;
+  rule->u.pattern = g_strdup (pattern);
+
+  file_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_file_filter_add_custom:
+ * @filter: a #GtkFileFilter
+ * @needed: bitfield of flags indicating the information that the custom
+ *          filter function needs.
+ * @func: callback function; if the function returns %TRUE, then
+ *   the file will be displayed.
+ * @data: data to pass to @func
+ * @notify: function to call to free @data when it is no longer needed.
+ * 
+ * Adds rule to a filter that allows files based on a custom callback
+ * function. The bitfield @needed which is passed in provides information
+ * about what sorts of information that the filter function needs;
+ * this allows GTK+ to avoid retrieving expensive information when
+ * it isn't needed by the filter.
+ **/
+void
+gtk_file_filter_add_custom (GtkFileFilter         *filter,
+                           GtkFileFilterFlags     needed,
+                           GtkFileFilterFunc      func,
+                           gpointer               data,
+                           GDestroyNotify         notify)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  g_return_if_fail (func != NULL);
+
+  rule = g_new (FilterRule, 1);
+  rule->type = FILTER_RULE_CUSTOM;
+  rule->needed = needed;
+  rule->u.custom.func = func;
+  rule->u.custom.data = data;
+  rule->u.custom.notify = notify;
+
+  file_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_file_filter_get_needed:
+ * @filter: a #GtkFileFilter
+ * 
+ * Gets the fields that need to be filled in for the structure
+ * passed to gtk_file_filter_filter()
+ * 
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkFileChooser.
+ * 
+ * Return value: bitfield of flags indicating needed fields when
+ *   calling gtk_file_filter_filter()
+ **/
+GtkFileFilterFlags
+gtk_file_filter_get_needed (GtkFileFilter *filter)
+{
+  return filter->needed;
+}
+
+/* Remove once we merge into GTK+ and use _gtk_fnmatch().
+ */   
+gboolean _gtk_file_chooser_fnmatch (const char *pattern,
+                                   const char *string);
+
+/**
+ * gtk_file_filter_filter:
+ * @filter: a #GtkFileFilter
+ * @filter_info: a #GtkFileFilterInfo structure containing information
+ *  about a file.
+ * 
+ * Tests whether a file should be displayed according to @filter.
+ * The #GtkFileFilterInfo structure @filter_info should include
+ * the fields returned feom gtk_file_filter_get_needed().
+ *
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkFileChooser.
+ * 
+ * Return value: %TRUE if the file should be displayed
+ **/
+gboolean
+gtk_file_filter_filter (GtkFileFilter           *filter,
+                       const GtkFileFilterInfo *filter_info)
+{
+  GSList *tmp_list;
+
+  for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
+    {
+      FilterRule *rule = tmp_list->data;
+
+      if ((filter_info->contains & rule->needed) != rule->needed)
+       continue;
+      
+      switch (rule->type)
+       {
+       case FILTER_RULE_MIME_TYPE:
+         if (strcmp (rule->u.mime_type, filter_info->mime_type) == 0)
+           return TRUE;
+         break;
+       case FILTER_RULE_PATTERN:
+         if (_gtk_file_chooser_fnmatch (rule->u.pattern, filter_info->display_name))
+           return TRUE;
+         break;
+       case FILTER_RULE_CUSTOM:
+         if (rule->u.custom.func (filter_info, rule->u.custom.data))
+           return TRUE;
+         break;
+       }
+    }
+
+  return FALSE;
+}
diff --git a/gtk/gtkfilefilter.h b/gtk/gtkfilefilter.h
new file mode 100644 (file)
index 0000000..01b70b3
--- /dev/null
@@ -0,0 +1,78 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilefilter.h: Filters for selecting a file subset
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_FILE_FILTER_H__
+#define __GTK_FILE_FILTER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_FILTER              (gtk_file_filter_get_type ())
+#define GTK_FILE_FILTER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FILTER, GtkFileFilter))
+#define GTK_IS_FILE_FILTER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FILTER))
+
+typedef struct _GtkFileFilter     GtkFileFilter;
+typedef struct _GtkFileFilterInfo GtkFileFilterInfo;
+
+typedef enum {
+  GTK_FILE_FILTER_FILENAME     = 1 << 0,
+  GTK_FILE_FILTER_URI          = 1 << 1,
+  GTK_FILE_FILTER_DISPLAY_NAME = 1 << 2,
+  GTK_FILE_FILTER_MIME_TYPE    = 1 << 3
+} GtkFileFilterFlags;
+
+typedef gboolean (*GtkFileFilterFunc) (const GtkFileFilterInfo *filter_info,
+                                      gpointer                 data);
+
+struct _GtkFileFilterInfo
+{
+  GtkFileFilterFlags contains;
+  
+  const gchar *filename;
+  const gchar *uri;
+  const gchar *display_name;
+  const gchar *mime_type;
+};
+
+GType gtk_file_filter_get_type (void);
+
+GtkFileFilter *       gtk_file_filter_new      (void);
+void                  gtk_file_filter_set_name (GtkFileFilter *filter,
+                                               const gchar   *name);
+G_CONST_RETURN gchar *gtk_file_filter_get_name (GtkFileFilter *filter);
+
+void     gtk_file_filter_add_mime_type (GtkFileFilter         *filter,
+                                       const gchar           *mime_type);
+void     gtk_file_filter_add_pattern   (GtkFileFilter         *filter,
+                                       const gchar           *pattern);
+void     gtk_file_filter_add_custom    (GtkFileFilter         *filter,
+                                       GtkFileFilterFlags     needed,
+                                       GtkFileFilterFunc      func,
+                                       gpointer               data,
+                                       GDestroyNotify         notify);
+
+GtkFileFilterFlags gtk_file_filter_get_needed (GtkFileFilter           *filter);
+gboolean           gtk_file_filter_filter     (GtkFileFilter           *filter,
+                                              const GtkFileFilterInfo *filter_info);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_FILTER_H__ */
index ed738481b676fe2a8f6307d1e055aa1f3a34abd2..2a28d441526d78bc808c17b96de36840a8cb9ef0 100644 (file)
@@ -44,6 +44,9 @@ struct _GtkFileSystemModel
   FileModelNode  *roots;
   GtkFileFolder  *root_folder;
 
+  GtkFileSystemModelFilter filter_func;
+  gpointer filter_data;
+
   GSList *idle_clears;
   GSource *idle_clear_source;
 
@@ -561,7 +564,8 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
                            GtkFileInfoType    types)
 {
   GtkFileSystemModel *model;
-  GSList *roots, *tmp_list;
+  GSList *roots = NULL;
+  GSList *tmp_list;
 
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
 
@@ -686,6 +690,16 @@ model_refilter_recurse (GtkFileSystemModel *model,
     }
 }
 
+static void
+model_refilter_all (GtkFileSystemModel *model)
+{
+  GtkTreePath *path;
+
+  path = gtk_tree_path_new ();
+  model_refilter_recurse (model, NULL, path);
+  gtk_tree_path_free (path);
+}
+
 /**
  * _gtk_file_system_model_set_show_hidden:
  * @model: a #GtkFileSystemModel
@@ -702,13 +716,8 @@ _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
 
   if (show_hidden != model->show_hidden)
     {
-      GtkTreePath *path;
-
       model->show_hidden = show_hidden;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -728,13 +737,8 @@ _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
 
   if (show_folders != model->show_folders)
     {
-      GtkTreePath *path;
-
       model->show_folders = show_folders;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -755,13 +759,8 @@ _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
 
   if (show_files != model->show_files)
     {
-      GtkTreePath *path;
-
       model->show_files = show_files;
-
-      path = gtk_tree_path_new ();
-      model_refilter_recurse (model, NULL, path);
-      gtk_tree_path_free (path);
+      model_refilter_all (model);
     }
 }
 
@@ -897,6 +896,29 @@ find_and_ref_path (GtkFileSystemModel  *model,
   return NULL;
 }
 
+/**
+ * _gtk_file_system_model_set_filter:
+ * @mode: a #GtkFileSystemModel
+ * @filter: function to be called for each file
+ * @user_data: data to pass to @filter
+ * 
+ * Sets a callback called for each file/directory to see whether
+ * it should be included in model. If this function was made
+ * public, we'd want to include a GDestroyNotify as well.
+ **/
+void
+_gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
+                                  GtkFileSystemModelFilter filter,
+                                  gpointer                 user_data)
+{
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+  
+  model->filter_func = filter;
+  model->filter_data = user_data;
+
+  model_refilter_all (model);
+}
+
 /**
  * _gtk_file_system_model_path_do:
  * @model: a #GtkFileSystemModel
@@ -1024,6 +1046,8 @@ file_model_node_is_visible (GtkFileSystemModel *model,
        return FALSE;
       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
        return FALSE;
+      if (model->filter_func && !model->filter_func (model, node->path, info, model->filter_data))
+       return FALSE;
 
       return TRUE;
     }
index e84d004def369c475fbc66872c493715a11e7cb9..759c1ab3c1108b322d93fc407923a5a2d8bf2586 100644 (file)
@@ -56,6 +56,14 @@ void                _gtk_file_system_model_set_show_folders (GtkFileSystemModel
 void                _gtk_file_system_model_set_show_files   (GtkFileSystemModel *model,
                                                             gboolean            show_files);
 
+typedef gboolean (*GtkFileSystemModelFilter) (GtkFileSystemModel *model,
+                                             GtkFilePath        *path,
+                                             const GtkFileInfo  *info,
+                                             gpointer            user_data);
+
+void     _gtk_file_system_model_set_filter (GtkFileSystemModel      *model,
+                                           GtkFileSystemModelFilter filter,
+                                           gpointer                 user_data);
 
 typedef void (*GtkFileSystemModelPathFunc) (GtkFileSystemModel *model,
                                            GtkTreePath        *path,
index 95696dbb8e102e35d5265a4a207fda1330c68ea8..51f91e9188e557b9462aebea3f4f4a39d183a0f2 100644 (file)
@@ -1,3 +1,5 @@
+#include <string.h>
+
 #include <gtk/gtk.h>
 #include "gtkfilechooserdialog.h"
 #include "gtkfilechooser.h"
@@ -43,6 +45,17 @@ response_cb (GtkDialog *dialog,
   gtk_main_quit ();
 }
 
+static gboolean
+no_backup_files_filter (const GtkFileFilterInfo *filter_info,
+                       gpointer                 data)
+{
+  gsize len = strlen (filter_info->display_name);
+  if (len > 0 && filter_info->display_name[len - 1] == '~')
+    return 0;
+  else
+    return 1;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -52,6 +65,7 @@ main (int argc, char **argv)
   GtkWidget *dialog;
   GtkWidget *prop_editor;
   GtkFileSystem *file_system;
+  GtkFileFilter *filter;
   
   gtk_init (&argc, &argv);
 
@@ -79,9 +93,32 @@ main (int argc, char **argv)
                    G_CALLBACK (print_current_folder), NULL);
   g_signal_connect (dialog, "response",
                    G_CALLBACK (response_cb), NULL);
+
+  /* Filters */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, "All Files");
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+  
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, "No backup files");
+  gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_DISPLAY_NAME,
+                             no_backup_files_filter, NULL, NULL);
+  gtk_file_filter_add_mime_type (filter, "image/png");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  /* Make this filter the default */
+  gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
   
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, "PNG and JPEG");
+  gtk_file_filter_add_mime_type (filter, "image/jpeg");
+  gtk_file_filter_add_mime_type (filter, "image/png");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
   gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 400);
-  gtk_widget_show (dialog);
+  /* show_all() to reveal bugs in composite widget handling */
+  gtk_widget_show_all (dialog);
 
   prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);