File filter objects.

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.
This commit is contained in:
Owen Taylor 2003-07-23 15:31:10 +00:00 committed by Owen Taylor
parent 96ba7bb65e
commit 733f4489ad
11 changed files with 1015 additions and 62 deletions

View 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;
}

View 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

View 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 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 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 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);

View 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
*/

View 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)

View File

@ -27,14 +27,17 @@
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);

372
gtk/gtkfilefilter.c Normal file
View File

@ -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;
}

78
gtk/gtkfilefilter.h Normal file
View File

@ -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__ */

View 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;
}

View 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,

View 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);