summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@src.gnome.org>2003-03-21 20:34:02 +0000
committerOwen Taylor <otaylor@src.gnome.org>2003-03-21 20:34:02 +0000
commite3849905a9f6945ab664a026a2af78958d02641f (patch)
tree3c4d7df4785e8f01118f0bb4aad5cfa54431ba77
parent250fd7cc43af62578cbcd58a90e1fe0fb706eed1 (diff)
downloadgtk+-e3849905a9f6945ab664a026a2af78958d02641f.tar.gz
Initial revision
-rw-r--r--gtk/gtkfilechooser.c419
-rw-r--r--gtk/gtkfilechooser.h167
-rw-r--r--gtk/gtkfilechooserdefault.c561
-rw-r--r--gtk/gtkfilechooserdefault.h40
-rw-r--r--gtk/gtkfilechooserdialog.c172
-rw-r--r--gtk/gtkfilechooserdialog.h62
-rw-r--r--gtk/gtkfilechooserutils.c172
-rw-r--r--gtk/gtkfilechooserutils.h47
-rw-r--r--gtk/gtkfilechooserwidget.c155
-rw-r--r--gtk/gtkfilechooserwidget.h58
-rw-r--r--gtk/gtkfilesystem.c465
-rw-r--r--gtk/gtkfilesystem.h209
-rw-r--r--gtk/gtkfilesystemmodel.c1040
-rw-r--r--gtk/gtkfilesystemmodel.h72
-rw-r--r--gtk/gtkfilesystemunix.c588
-rw-r--r--gtk/gtkfilesystemunix.h38
-rw-r--r--tests/testfilechooser.c79
17 files changed, 4344 insertions, 0 deletions
diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c
new file mode 100644
index 0000000000..6717a8bbc7
--- /dev/null
+++ b/gtk/gtkfilechooser.c
@@ -0,0 +1,419 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooser.c: Abstract interface for file selector GUIs
+ * 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 "gtkfilechooser.h"
+#include "gtkfilechooserenums.h"
+
+#define _(str) (str)
+
+static void gtk_file_chooser_base_init (gpointer g_class);
+
+GType
+gtk_file_chooser_get_type (void)
+{
+ static GType file_chooser_type = 0;
+
+ if (!file_chooser_type)
+ {
+ static const GTypeInfo file_chooser_info =
+ {
+ sizeof (GtkFileChooserIface), /* class_size */
+ gtk_file_chooser_base_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ file_chooser_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GtkFileChooser",
+ &file_chooser_info, 0);
+ }
+
+ return file_chooser_type;
+}
+
+static void
+gtk_file_chooser_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
+
+ g_signal_new ("current_folder_changed",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileChooserIface, current_folder_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ g_signal_new ("selection_changed",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileChooserIface, selection_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_interface_install_property (iface_type,
+ g_param_spec_enum ("action",
+ _("Action"),
+ _("The type of action that the file selector is performing"),
+ GTK_TYPE_FILE_CHOOSER_ACTION,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (iface_type,
+ g_param_spec_boolean ("folder_mode",
+ _("Folder Mode"),
+ _("Whether to select folders rather than files"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (iface_type,
+ g_param_spec_boolean ("local_only",
+ _("Local Only"),
+ _("Whether the selected file(s) should be limited to local file: URLs"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (iface_type,
+ g_param_spec_object ("preview_widget",
+ _("Preview widget"),
+ _("Application supplied widget for custom previews."),
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (iface_type,
+ g_param_spec_boolean ("preview_widget_active",
+ _("Preview Widget Active"),
+ _("Whther the application supplied widget for custom previews should be shown."),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (iface_type,
+ g_param_spec_boolean ("select_multiple",
+ _("Select Multiple"),
+ _("Whether to allow multiple files to be selected"),
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property (iface_type,
+ g_param_spec_boolean ("show_hidden",
+ _("Show Hidden"),
+ _("Whether the hidden files and folders should be displayed"),
+ FALSE,
+ G_PARAM_READWRITE));
+ initialized = TRUE;
+ }
+}
+
+void
+gtk_file_chooser_set_action (GtkFileChooser *chooser,
+ GtkFileChooserAction action)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "action", action, NULL);
+}
+
+GtkFileChooserAction
+gtk_file_chooser_get_action (GtkFileChooser *chooser)
+{
+ GtkFileChooserAction action;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "action", &action, NULL);
+
+ return action;
+}
+
+void
+gtk_file_chooser_set_directory_mode (GtkFileChooser *chooser,
+ gboolean directory_mode)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "directory_mode", directory_mode, NULL);
+}
+
+gboolean
+gtk_file_chooser_get_directory_mode (GtkFileChooser *chooser)
+{
+ gboolean directory_mode;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "directory_mode", &directory_mode, NULL);
+
+ return directory_mode;
+}
+
+void
+gtk_file_chooser_set_local_only (GtkFileChooser *chooser,
+ gboolean local_only)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "local_only", local_only, NULL);
+}
+
+gboolean
+gtk_file_chooser_get_local_only (GtkFileChooser *chooser)
+{
+ gboolean local_only;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "local_only", &local_only, NULL);
+
+ return local_only;
+}
+
+void
+gtk_file_chooser_set_select_multiple (GtkFileChooser *chooser,
+ gboolean select_multiple)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "select_multiple", select_multiple, NULL);
+}
+
+gboolean
+gtk_file_chooser_get_select_multiple (GtkFileChooser *chooser)
+{
+ gboolean select_multiple;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "select_multiple", &select_multiple, NULL);
+
+ return select_multiple;
+}
+
+char *
+gtk_file_chooser_get_filename (GtkFileChooser *chooser)
+{
+ GSList *list;
+ gchar *result = NULL;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ list = gtk_file_chooser_get_filenames (chooser);
+ if (list)
+ {
+ result = list->data;
+ list = g_slist_delete_link (list, list);
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+ g_slist_free (list);
+ }
+
+ return result;
+}
+
+void
+gtk_file_chooser_set_filename (GtkFileChooser *chooser,
+ const char *filename)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+}
+
+void
+gtk_file_chooser_select_filename (GtkFileChooser *chooser,
+ const char *filename)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+}
+
+void
+gtk_file_chooser_unselect_filename (GtkFileChooser *chooser,
+ const char *filename)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+ g_return_if_fail (filename != NULL);
+}
+
+GSList *
+gtk_file_chooser_get_filenames (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return NULL;
+}
+
+void
+gtk_file_chooser_set_current_folder (GtkFileChooser *chooser,
+ const gchar *filename)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+ g_return_if_fail (filename != NULL);
+}
+
+gchar *
+gtk_file_chooser_get_current_folder (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return NULL;
+}
+
+gchar *
+gtk_file_chooser_get_uri (GtkFileChooser *chooser)
+{
+ GSList *list;
+ gchar *result = NULL;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ list = gtk_file_chooser_get_uris (chooser);
+ if (list)
+ {
+ result = list->data;
+ list = g_slist_delete_link (list, list);
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+ g_slist_free (list);
+ }
+
+ return result;
+}
+
+void
+gtk_file_chooser_set_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+}
+
+void
+gtk_file_chooser_select_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ GTK_FILE_CHOOSER_GET_IFACE (chooser)->unselect_uri (chooser, uri);
+}
+
+void
+gtk_file_chooser_unselect_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ GTK_FILE_CHOOSER_GET_IFACE (chooser)->unselect_uri (chooser, uri);
+}
+
+void
+gtk_file_chooser_select_all (GtkFileChooser *chooser)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ GTK_FILE_CHOOSER_GET_IFACE (chooser)->select_all (chooser);
+}
+
+void
+gtk_file_chooser_unselect_all (GtkFileChooser *chooser)
+{
+
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ GTK_FILE_CHOOSER_GET_IFACE (chooser)->unselect_all (chooser);
+}
+
+GSList *
+gtk_file_chooser_get_uris (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return GTK_FILE_CHOOSER_GET_IFACE (chooser)->get_uris (chooser);
+}
+
+void
+gtk_file_chooser_set_current_folder_uri (GtkFileChooser *chooser,
+ const gchar *uri)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+ g_return_if_fail (uri != NULL);
+
+ return GTK_FILE_CHOOSER_GET_IFACE (chooser)->set_current_folder (chooser, uri);
+}
+
+
+gchar *
+gtk_file_chooser_get_current_folder_uri (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return GTK_FILE_CHOOSER_GET_IFACE (chooser)->get_current_folder (chooser);
+}
+
+/* Preview widget
+ */
+void
+gtk_file_chooser_set_preview_widget (GtkFileChooser *chooser,
+ GtkWidget *preview_widget)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "preview_widget", preview_widget, NULL);
+}
+
+GtkWidget *
+gtk_file_chooser_get_preview_widget (GtkFileChooser *chooser)
+{
+ GtkWidget *preview_widget;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ g_object_get (chooser, "preview_widget", &preview_widget, NULL);
+
+ return preview_widget;
+}
+
+void
+gtk_file_chooser_set_preview_widget_active (GtkFileChooser *chooser,
+ gboolean active)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+ g_object_set (chooser, "preview_widget_active", active, NULL);
+}
+
+gboolean
+gtk_file_chooser_get_preview_widget_active (GtkFileChooser *chooser)
+{
+ gboolean active;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "preview_widget_active", &active, NULL);
+
+ return active;
+}
+
+const char *
+gtk_file_chooser_get_preview_filename (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return NULL;
+}
+
+const char *
+gtk_file_chooser_get_preview_uri (GtkFileChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+ return NULL;
+}
diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h
new file mode 100644
index 0000000000..7b5d395c4a
--- /dev/null
+++ b/gtk/gtkfilechooser.h
@@ -0,0 +1,167 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooser.h: Abstract interface for file selector GUIs
+ * 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_CHOOSER_H__
+#define __GTK_FILE_CHOOSER_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_CHOOSER (gtk_file_chooser_get_type ())
+#define GTK_FILE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER, GtkFileChooser))
+#define GTK_IS_FILE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_CHOOSER))
+#define GTK_FILE_CHOOSER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_FILE_CHOOSER, GtkFileChooserIface))
+
+typedef struct _GtkFileChooser GtkFileChooser;
+typedef struct _GtkFileChooserIface GtkFileChooserIface;
+
+typedef enum
+{
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_FILE_CHOOSER_ACTION_SAVE
+} GtkFileChooserAction;
+
+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,
+ const char *uri);
+ char * (*get_current_folder) (GtkFileChooser *chooser);
+ void (*select_uri) (GtkFileChooser *chooser,
+ const char *uri);
+ void (*unselect_uri) (GtkFileChooser *chooser,
+ const char *uri);
+ void (*select_all) (GtkFileChooser *chooser);
+ void (*unselect_all) (GtkFileChooser *chooser);
+ GSList *(*get_uris) (GtkFileChooser *chooser);
+
+
+ /* Signals
+ */
+ void (*current_folder_changed) (GtkFileChooser *chooser);
+ void (*selection_changed) (GtkFileChooser *chooser);
+ void (*update_preview) (GtkFileChooser *chooser,
+ const char *uri);
+};
+
+GType gtk_file_chooser_get_type (void);
+
+/* Configuration
+ */
+void gtk_file_chooser_set_action (GtkFileChooser *chooser,
+ GtkFileChooserAction action);
+GtkFileChooserAction gtk_file_chooser_get_action (GtkFileChooser *chooser);
+void gtk_file_chooser_set_directory_mode (GtkFileChooser *chooser,
+ gboolean directory_mode);
+gboolean gtk_file_chooser_get_directory_mode (GtkFileChooser *chooser);
+void gtk_file_chooser_set_local_only (GtkFileChooser *chooser,
+ gboolean files_only);
+gboolean gtk_file_chooser_get_local_only (GtkFileChooser *chooser);
+void gtk_file_chooser_set_select_multiple (GtkFileChooser *chooser,
+ gboolean select_multiple);
+gboolean gtk_file_chooser_get_select_multiple (GtkFileChooser *chooser);
+
+/* Filename manipulation
+ */
+gchar * gtk_file_chooser_get_filename (GtkFileChooser *chooser);
+void gtk_file_chooser_set_filename (GtkFileChooser *chooser,
+ const char *filename);
+void gtk_file_chooser_select_filename (GtkFileChooser *chooser,
+ const char *filename);
+void gtk_file_chooser_unselect_filename (GtkFileChooser *chooser,
+ const char *filename);
+void gtk_file_chooser_select_all (GtkFileChooser *chooser);
+void gtk_file_chooser_unselect_all (GtkFileChooser *chooser);
+GSList *gtk_file_chooser_get_filenames (GtkFileChooser *chooser);
+void gtk_file_chooser_set_current_folder (GtkFileChooser *chooser,
+ const gchar *filename);
+gchar *gtk_file_chooser_get_current_folder (GtkFileChooser *chooser);
+
+
+/* URI manipulation
+ */
+gchar * gtk_file_chooser_get_uri (GtkFileChooser *chooser);
+void gtk_file_chooser_set_uri (GtkFileChooser *chooser,
+ const char *uri);
+void gtk_file_chooser_select_uri (GtkFileChooser *chooser,
+ const char *uri);
+void gtk_file_chooser_unselect_uri (GtkFileChooser *chooser,
+ const char *uri);
+GSList *gtk_file_chooser_get_uris (GtkFileChooser *chooser);
+
+void gtk_file_chooser_set_current_folder_uri (GtkFileChooser *chooser,
+ const gchar *uri);
+gchar *gtk_file_chooser_get_current_folder_uri (GtkFileChooser *chooser);
+
+/* Preview widget
+ */
+void gtk_file_chooser_set_preview_widget (GtkFileChooser *chooser,
+ GtkWidget *preview_widget);
+GtkWidget *gtk_file_chooser_get_preview_widget (GtkFileChooser *chooser);
+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);
+
+
+#if 0
+/* 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);
+
+
+/************************************************/
+
+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
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_H__ */
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
new file mode 100644
index 0000000000..b4eb21a798
--- /dev/null
+++ b/gtk/gtkfilechooserdefault.c
@@ -0,0 +1,561 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserwidget.c: Embeddable file selector widget
+ * 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 "gtkfilechooserimpldefault.h"
+#include "gtkfilechooserenums.h"
+#include "gtkfilechooserutils.h"
+#include "gtkfilechooser.h"
+#include "gtkfilesystemmodel.h"
+
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkhpaned.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkvbox.h>
+
+typedef struct _GtkFileChooserImplDefaultClass GtkFileChooserImplDefaultClass;
+
+#define GTK_FILE_CHOOSER_IMPL_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, GtkFileChooserImplDefaultClass))
+#define GTK_IS_FILE_CHOOSER_IMPL_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT))
+#define GTK_FILE_CHOOSER_IMPL_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, GtkFileChooserImplDefaultClass))
+
+struct _GtkFileChooserImplDefaultClass
+{
+ GtkVBoxClass parent_class;
+};
+
+struct _GtkFileChooserImplDefault
+{
+ GtkVBox parent_instance;
+
+ GtkFileSystem *file_system;
+ GtkFileSystemModel *tree_model;
+ GtkFileSystemModel *list_model;
+
+ GtkFileChooserAction action;
+
+ guint folder_mode : 1;
+ guint local_only : 1;
+ guint preview_widget_active : 1;
+ guint select_multiple : 1;
+ guint show_hidden : 1;
+
+ GtkWidget *tree_scrollwin;
+ GtkWidget *tree;
+ GtkWidget *list_scrollwin;
+ GtkWidget *list;
+ GtkWidget *preview_widget;
+};
+
+static void gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class);
+static void gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface);
+static void gtk_file_chooser_impl_default_init (GtkFileChooserImplDefault *impl);
+static void gtk_file_chooser_impl_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_file_chooser_impl_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser *chooser,
+ const char *uri);
+static char * gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser *chooser);
+static void gtk_file_chooser_impl_default_select_uri (GtkFileChooser *chooser,
+ const char *uri);
+static void gtk_file_chooser_impl_default_unselect_uri (GtkFileChooser *chooser,
+ const char *uri);
+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_uris (GtkFileChooser *chooser);
+
+static void tree_selection_changed (GtkTreeSelection *tree_selection,
+ GtkFileChooserImplDefault *impl);
+static void list_selection_changed (GtkTreeSelection *tree_selection,
+ GtkFileChooserImplDefault *impl);
+
+GType
+_gtk_file_chooser_impl_default_get_type (void)
+{
+ static GType file_chooser_impl_default_type = 0;
+
+ if (!file_chooser_impl_default_type)
+ {
+ static const GTypeInfo file_chooser_impl_default_info =
+ {
+ sizeof (GtkFileChooserImplDefaultClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_chooser_impl_default_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileChooserImplDefault),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_chooser_impl_default_init,
+ };
+
+ static const GInterfaceInfo file_chooser_info =
+ {
+ (GInterfaceInitFunc) gtk_file_chooser_impl_default_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_chooser_impl_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserImplDefault",
+ &file_chooser_impl_default_info, 0);
+ g_type_add_interface_static (file_chooser_impl_default_type,
+ GTK_TYPE_FILE_CHOOSER,
+ &file_chooser_info);
+ }
+
+ return file_chooser_impl_default_type;
+}
+
+static void
+gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_file_chooser_impl_default_set_property;
+ gobject_class->get_property = gtk_file_chooser_impl_default_get_property;
+
+ _gtk_file_chooser_install_properties (gobject_class);
+}
+
+static void
+gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface)
+{
+ iface->select_uri = gtk_file_chooser_impl_default_select_uri;
+ iface->unselect_uri = gtk_file_chooser_impl_default_unselect_uri;
+ iface->select_all = gtk_file_chooser_impl_default_select_all;
+ iface->unselect_all = gtk_file_chooser_impl_default_unselect_all;
+ iface->get_uris = gtk_file_chooser_impl_default_get_uris;
+ iface->set_current_folder = gtk_file_chooser_impl_default_set_current_folder;
+ iface->get_current_folder = gtk_file_chooser_impl_default_get_current_folder;
+}
+
+static void
+gtk_file_chooser_impl_default_init (GtkFileChooserImplDefault *impl)
+{
+ GtkWidget *hpaned;
+ GtkTreeSelection *selection;
+
+ impl->folder_mode = FALSE;
+ impl->local_only = TRUE;
+ impl->preview_widget_active = TRUE;
+ impl->select_multiple = FALSE;
+ impl->show_hidden = FALSE;
+
+ gtk_widget_push_composite_child ();
+
+ hpaned = gtk_hpaned_new ();
+ gtk_box_pack_start (GTK_BOX (impl), hpaned, TRUE, TRUE, 0);
+ gtk_widget_show (hpaned);
+
+ impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_paned_add1 (GTK_PANED (hpaned), impl->tree_scrollwin);
+ gtk_widget_show (impl->tree_scrollwin);
+
+ impl->tree = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (tree_selection_changed), impl);
+
+ gtk_paned_set_position (GTK_PANED (hpaned), 200);
+
+ gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
+ gtk_widget_show (impl->tree);
+
+ impl->list_scrollwin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_paned_add2 (GTK_PANED (hpaned), impl->list_scrollwin);
+ gtk_widget_show (impl->list_scrollwin);
+
+ impl->list = gtk_tree_view_new ();
+ gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list);
+ gtk_widget_show (impl->list);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (list_selection_changed), impl);
+
+ gtk_widget_pop_composite_child ();
+}
+
+static void
+set_preview_widget (GtkFileChooserImplDefault *impl,
+ GtkWidget *preview_widget)
+{
+ if (preview_widget == impl->preview_widget)
+ return;
+
+ if (impl->preview_widget)
+ {
+ g_object_unref (impl->preview_widget);
+ impl->preview_widget = NULL;
+ }
+
+ impl->preview_widget = preview_widget;
+ if (impl->preview_widget)
+ {
+ g_object_ref (impl->preview_widget);
+ gtk_object_sink (GTK_OBJECT (impl->preview_widget));
+ }
+}
+
+static void
+gtk_file_chooser_impl_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case GTK_FILE_CHOOSER_PROP_ACTION:
+ impl->action = g_value_get_enum (value);
+ break;
+ case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
+ {
+ gboolean folder_mode = g_value_get_boolean (value);
+ if (folder_mode != impl->folder_mode)
+ {
+ impl->folder_mode = folder_mode;
+ if (impl->folder_mode)
+ gtk_widget_hide (impl->list_scrollwin);
+ else
+ gtk_widget_show (impl->list_scrollwin);
+ }
+ }
+ break;
+ case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+ impl->local_only = g_value_get_boolean (value);
+ break;
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
+ set_preview_widget (impl, g_value_get_object (value));
+ break;
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
+ impl->preview_widget_active = g_value_get_boolean (value);
+ break;
+ case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
+ {
+ gboolean select_multiple = g_value_get_boolean (value);
+ if (select_multiple != impl->select_multiple)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+
+ impl->select_multiple = select_multiple;
+ gtk_tree_selection_set_mode (selection,
+ (select_multiple ?
+ GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE));
+
+
+ }
+ }
+ break;
+ case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
+ {
+ gboolean show_hidden = g_value_get_boolean (value);
+ if (show_hidden != impl->show_hidden)
+ {
+ impl->show_hidden = show_hidden;
+ _gtk_file_system_model_set_show_hidden (impl->tree_model, show_hidden);
+ _gtk_file_system_model_set_show_hidden (impl->list_model, show_hidden);
+ }
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_file_chooser_impl_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case GTK_FILE_CHOOSER_PROP_ACTION:
+ g_value_set_enum (value, impl->action);
+ break;
+ case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
+ g_value_set_boolean (value, impl->folder_mode);
+ break;
+ case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+ g_value_set_boolean (value, impl->local_only);
+ break;
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
+ g_value_set_object (value, impl->preview_widget);
+ break;
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
+ g_value_set_boolean (value, impl->preview_widget_active);
+ break;
+ case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
+ g_value_set_boolean (value, impl->select_multiple);
+ break;
+ case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
+ g_value_set_boolean (value, impl->show_hidden);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+expand_and_select_func (GtkFileSystemModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkFileChooserImplDefault *impl = user_data;
+ GtkTreeView *tree_view;
+
+ if (model == impl->tree_model)
+ tree_view = GTK_TREE_VIEW (impl->tree);
+ else
+ tree_view = GTK_TREE_VIEW (impl->list);
+
+ gtk_tree_view_expand_to_path (tree_view, path);
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), path, NULL, TRUE, 0.3, 0.0);
+}
+
+static void
+gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser *chooser,
+ const char *uri)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ _gtk_file_system_model_uri_do (impl->tree_model, uri,
+ expand_and_select_func, impl);
+}
+
+static char *
+gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser *chooser)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ const gchar *uri = _gtk_file_system_model_get_uri (impl->tree_model, &iter);
+ return g_strdup (uri);
+ }
+ else
+ return NULL;
+}
+
+static void
+gtk_file_chooser_impl_default_select_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ gchar *parent_uri;
+
+ if (!gtk_file_system_get_parent (impl->file_system, uri, &parent_uri, NULL)) /* NULL-GError */
+ return;
+
+ if (!parent_uri)
+ {
+ gtk_file_chooser_set_current_folder_uri (chooser, uri);
+ }
+ else
+ {
+ gtk_file_chooser_set_current_folder_uri (chooser, parent_uri);
+ g_free (parent_uri);
+ _gtk_file_system_model_uri_do (impl->list_model, uri,
+ expand_and_select_func, impl);
+ }
+}
+
+static void
+unselect_func (GtkFileSystemModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkFileChooserImplDefault *impl = user_data;
+ GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
+
+ gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
+ path);
+}
+
+static void
+gtk_file_chooser_impl_default_unselect_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+
+ _gtk_file_system_model_uri_do (impl->list_model, uri,
+ unselect_func, impl);
+}
+
+static void
+gtk_file_chooser_impl_default_select_all (GtkFileChooser *chooser)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ if (impl->select_multiple)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+ gtk_tree_selection_select_all (selection);
+ }
+}
+
+static void
+gtk_file_chooser_impl_default_unselect_all (GtkFileChooser *chooser)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+
+ gtk_tree_selection_unselect_all (selection);
+}
+
+static void
+get_uris_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ struct {
+ GSList *result;
+ GtkFileChooserImplDefault *impl;
+ } *info = data;
+
+ const gchar *uri = _gtk_file_system_model_get_uri (info->impl->tree_model, iter);
+ info->result = g_slist_prepend (info->result, g_strdup (uri));
+}
+
+static GSList *
+gtk_file_chooser_impl_default_get_uris (GtkFileChooser *chooser)
+{
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+
+ struct {
+ GSList *result;
+ GtkFileChooserImplDefault *impl;
+ } info = { NULL, };
+
+ info.impl = impl;
+
+ gtk_tree_selection_selected_foreach (selection,
+ get_uris_foreach, &info);
+ return g_slist_reverse (info.result);
+}
+
+static void
+tree_selection_changed (GtkTreeSelection *selection,
+ GtkFileChooserImplDefault *impl)
+{
+ GtkTreeIter iter;
+
+ if (impl->list_model)
+ {
+ g_object_unref (impl->list_model);
+ impl->list_model = NULL;
+ }
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ const gchar *uri = _gtk_file_system_model_get_uri (impl->tree_model, &iter);
+
+ impl->list_model = _gtk_file_system_model_new (impl->file_system,
+ uri, 0,
+ GTK_FILE_INFO_DISPLAY_NAME);
+
+ _gtk_file_system_model_set_show_folders (impl->list_model, FALSE);
+ }
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->list),
+ GTK_TREE_MODEL (impl->list_model));
+
+ g_signal_emit_by_name (impl, "current_folder_changed", 0);
+ g_signal_emit_by_name (impl, "selection_changed", 0);
+}
+
+static void
+list_selection_changed (GtkTreeSelection *selection,
+ GtkFileChooserImplDefault *impl)
+{
+ g_signal_emit_by_name (impl, "selection_changed", 0);
+}
+
+void
+name_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GtkFileChooserImplDefault *impl = data;
+ const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter);
+
+ if (info)
+ {
+ g_object_set (cell,
+ "text", gtk_file_info_get_display_name (info),
+ NULL);
+ }
+}
+
+GtkWidget *
+_gtk_file_chooser_impl_default_new (GtkFileSystem *file_system)
+{
+ GtkWidget *result = g_object_new (GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, NULL);
+ GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (result);
+
+ impl->file_system = file_system;
+ impl->tree_model = _gtk_file_system_model_new (file_system, NULL, -1, GTK_FILE_INFO_DISPLAY_NAME);
+ _gtk_file_system_model_set_show_files (impl->tree_model, FALSE);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
+ GTK_TREE_MODEL (impl->tree_model));
+
+ gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
+ "Name",
+ gtk_cell_renderer_text_new (),
+ name_data_func, impl, NULL);
+
+ gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->list), 0,
+ "Name",
+ gtk_cell_renderer_text_new (),
+ name_data_func, impl, NULL);
+
+ return result;
+}
+
diff --git a/gtk/gtkfilechooserdefault.h b/gtk/gtkfilechooserdefault.h
new file mode 100644
index 0000000000..a5416dd1a1
--- /dev/null
+++ b/gtk/gtkfilechooserdefault.h
@@ -0,0 +1,40 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserwidget.h: Embeddable file selector widget
+ * 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_CHOOSER_IMPL_DEFAULT_H__
+#define __GTK_FILE_CHOOSER_IMPL_DEFAULT_H__
+
+#include "gtkfilesystem.h"
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT (_gtk_file_chooser_impl_default_get_type ())
+#define GTK_FILE_CHOOSER_IMPL_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, GtkFileChooserImplDefault))
+#define GTK_IS_FILE_CHOOSER_IMPL_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT))
+
+typedef struct _GtkFileChooserImplDefault GtkFileChooserImplDefault;
+
+GType _gtk_file_chooser_impl_default_get_type (void);
+GtkWidget *_gtk_file_chooser_impl_default_new (GtkFileSystem *file_system);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_IMPL_DEFAULT_H__ */
diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c
new file mode 100644
index 0000000000..c540bb6d0f
--- /dev/null
+++ b/gtk/gtkfilechooserdialog.c
@@ -0,0 +1,172 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserdialog.c: File selector dialog
+ * 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 "gtkfilechooserdialog.h"
+#include "gtkfilechooserwidget.h"
+#include "gtkfilechooserenums.h"
+#include "gtkfilechooserutils.h"
+
+#include <stdarg.h>
+
+struct _GtkFileChooserDialogPrivate
+{
+ GtkWidget *widget;
+};
+
+#define GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE(o) (GTK_FILE_CHOOSER_DIALOG (o)->priv)
+
+static void gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class);
+static void gtk_file_chooser_dialog_init (GtkFileChooserDialog *dialog);
+
+static void gtk_file_chooser_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_file_chooser_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+GType
+gtk_file_chooser_dialog_get_type (void)
+{
+ static GType file_chooser_dialog_type = 0;
+
+ if (!file_chooser_dialog_type)
+ {
+ static const GTypeInfo file_chooser_dialog_info =
+ {
+ sizeof (GtkFileChooserDialogClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_chooser_dialog_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileChooserDialog),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_chooser_dialog_init,
+ };
+
+ static const GInterfaceInfo file_chooser_info =
+ {
+ (GInterfaceInitFunc) _gtk_file_chooser_delegate_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_chooser_dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "GtkFileChooserDialog",
+ &file_chooser_dialog_info, 0);
+ g_type_add_interface_static (file_chooser_dialog_type,
+ GTK_TYPE_FILE_CHOOSER,
+ &file_chooser_info);
+ }
+
+ return file_chooser_dialog_type;
+}
+
+static void
+gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_file_chooser_dialog_set_property;
+ gobject_class->get_property = gtk_file_chooser_dialog_get_property;
+
+ _gtk_file_chooser_install_properties (gobject_class);
+
+ g_type_class_add_private (class, sizeof (GtkFileChooserDialogPrivate));
+}
+
+static void
+gtk_file_chooser_dialog_init (GtkFileChooserDialog *dialog)
+{
+ GtkFileChooserDialogPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+ GTK_TYPE_FILE_CHOOSER_DIALOG,
+ GtkFileChooserDialogPrivate);
+ dialog->priv = priv;
+
+ gtk_widget_push_composite_child ();
+
+ priv->widget = g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET, NULL);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), priv->widget, TRUE, TRUE, 0);
+ gtk_widget_show (priv->widget);
+
+ _gtk_file_chooser_set_delegate (GTK_FILE_CHOOSER (dialog),
+ GTK_FILE_CHOOSER (priv->widget));
+
+ gtk_widget_pop_composite_child ();
+}
+
+static void
+gtk_file_chooser_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ g_object_set_property (G_OBJECT (priv->widget), pspec->name, value);
+}
+
+static void
+gtk_file_chooser_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserDialogPrivate *priv = GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ g_object_get_property (G_OBJECT (priv->widget), pspec->name, value);
+}
+
+GtkWidget *
+gtk_file_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ GtkFileChooserAction action,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *result;
+ va_list varargs;
+ const char *button_text = first_button_text;
+ gint response_id;
+
+ result = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+ "title", title,
+ "action", action,
+ NULL);
+
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (result), parent);
+
+ va_start (varargs, first_button_text);
+
+ while (button_text)
+ {
+ response_id = va_arg (varargs, gint);
+ gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
+ button_text = va_arg (varargs, const gchar *);
+ }
+
+ va_end (varargs);
+
+ return result;
+}
diff --git a/gtk/gtkfilechooserdialog.h b/gtk/gtkfilechooserdialog.h
new file mode 100644
index 0000000000..0e19fe4a9a
--- /dev/null
+++ b/gtk/gtkfilechooserdialog.h
@@ -0,0 +1,62 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserdialog.h: File selector dialog
+ * 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_CHOOSER_DIALOG_H__
+#define __GTK_FILE_CHOOSER_DIALOG_H__
+
+#include <gtk/gtkdialog.h>
+#include "gtkfilechooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_CHOOSER_DIALOG (gtk_file_chooser_dialog_get_type ())
+#define GTK_FILE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER_DIALOG, GtkFileChooserDialog))
+#define GTK_FILE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DIALOG, GtkFileChooserDialogClass))
+#define GTK_IS_FILE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_CHOOSER_DIALOG))
+#define GTK_IS_FILE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DIALOG))
+#define GTK_FILE_CHOOSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DIALOG, GtkFileChooserDialogClass))
+
+typedef struct _GtkFileChooserDialog GtkFileChooserDialog;
+typedef struct _GtkFileChooserDialogClass GtkFileChooserDialogClass;
+
+typedef struct _GtkFileChooserDialogPrivate GtkFileChooserDialogPrivate;
+
+struct _GtkFileChooserDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+struct _GtkFileChooserDialog
+{
+ GtkDialog parent_instance;
+
+ GtkFileChooserDialogPrivate *priv;
+};
+
+GType gtk_file_chooser_dialog_get_type (void);
+GtkWidget *gtk_file_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ GtkFileChooserAction action,
+ const gchar *first_button_text,
+ ...);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_DIALOG_H__ */
diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c
new file mode 100644
index 0000000000..4a88ddcc03
--- /dev/null
+++ b/gtk/gtkfilechooserutils.c
@@ -0,0 +1,172 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserutils.c: Private utility functions useful for
+ * implementing a GtkFileChooser interface
+ * 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 "gtkfilechooserutils.h"
+#include "gtkfilechooser.h"
+#include "gtkfilechooserenums.h"
+
+static void delegate_set_current_folder (GtkFileChooser *chooser,
+ const char *uri);
+static char * delegate_get_current_folder (GtkFileChooser *chooser);
+static void delegate_select_uri (GtkFileChooser *chooser,
+ const char *uri);
+static void delegate_unselect_uri (GtkFileChooser *chooser,
+ const char *uri);
+static void delegate_select_all (GtkFileChooser *chooser);
+static void delegate_unselect_all (GtkFileChooser *chooser);
+static GSList *delegate_get_uris (GtkFileChooser *chooser);
+
+static void delegate_current_folder_changed (GtkFileChooser *chooser,
+ gpointer data);
+static void delegate_selection_changed (GtkFileChooser *chooser,
+ gpointer data);
+
+void
+_gtk_file_chooser_install_properties (GObjectClass *klass)
+{
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_ACTION,
+ g_param_spec_override ("action",
+ GTK_TYPE_FILE_CHOOSER_ACTION,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_FOLDER_MODE,
+ g_param_spec_override ("folder_mode",
+ G_TYPE_BOOLEAN,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_LOCAL_ONLY,
+ g_param_spec_override ("local_only",
+ G_TYPE_BOOLEAN,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET,
+ g_param_spec_override ("preview_widget",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE,
+ g_param_spec_override ("preview_widget_active",
+ G_TYPE_BOOLEAN,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE,
+ g_param_spec_override ("select_multiple",
+ G_TYPE_BOOLEAN,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (klass,
+ GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN,
+ g_param_spec_override ("show_hidden",
+ G_TYPE_BOOLEAN,
+ G_PARAM_READWRITE));
+}
+
+void
+_gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface)
+{
+ iface->set_current_folder = delegate_set_current_folder;
+ iface->get_current_folder = delegate_get_current_folder;
+ iface->select_uri = delegate_select_uri;
+ iface->unselect_uri = delegate_unselect_uri;
+ iface->select_all = delegate_select_all;
+ iface->unselect_all = delegate_unselect_all;
+ iface->get_uris = delegate_get_uris;
+}
+
+void
+_gtk_file_chooser_set_delegate (GtkFileChooser *receiver,
+ GtkFileChooser *delegate)
+{
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (receiver));
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (delegate));
+
+ g_object_set_data (G_OBJECT (receiver), "gtk-file-chooser-delegate", delegate);
+
+ g_signal_connect (delegate, "current_folder_changed",
+ G_CALLBACK (delegate_current_folder_changed), receiver);
+ g_signal_connect (delegate, "selection_changed",
+ G_CALLBACK (delegate_selection_changed), receiver);
+}
+
+GtkFileChooser *
+get_delegate (GtkFileChooser *receiver)
+{
+ return g_object_get_data (G_OBJECT (receiver), "gtk-file-chooser-delegate");
+}
+
+static void
+delegate_select_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ gtk_file_chooser_select_uri (get_delegate (chooser), uri);
+}
+
+static void
+delegate_unselect_uri (GtkFileChooser *chooser,
+ const char *uri)
+{
+ gtk_file_chooser_unselect_uri (get_delegate (chooser), uri);
+}
+
+static void
+delegate_select_all (GtkFileChooser *chooser)
+{
+ gtk_file_chooser_select_all (get_delegate (chooser));
+}
+
+static void
+delegate_unselect_all (GtkFileChooser *chooser)
+{
+ gtk_file_chooser_unselect_all (get_delegate (chooser));
+}
+
+static GSList *
+delegate_get_uris (GtkFileChooser *chooser)
+{
+ return gtk_file_chooser_get_uris (get_delegate (chooser));
+}
+
+static void
+delegate_set_current_folder (GtkFileChooser *chooser,
+ const char *uri)
+{
+ gtk_file_chooser_set_current_folder_uri (chooser, uri);
+}
+
+static char *
+delegate_get_current_folder (GtkFileChooser *chooser)
+{
+ return gtk_file_chooser_get_current_folder_uri (get_delegate (chooser));
+}
+
+static void
+delegate_selection_changed (GtkFileChooser *chooser,
+ gpointer data)
+{
+ g_signal_emit_by_name (data, "selection_changed", 0);
+}
+
+static void
+delegate_current_folder_changed (GtkFileChooser *chooser,
+ gpointer data)
+{
+ g_signal_emit_by_name (data, "current_folder_changed", 0);
+}
diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h
new file mode 100644
index 0000000000..ffb6b3c61e
--- /dev/null
+++ b/gtk/gtkfilechooserutils.h
@@ -0,0 +1,47 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserutils.h: Private utility functions useful for
+ * implementing a GtkFileChooser interface
+ * 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_CHOOSER_UTILS_H__
+#define __GTK_FILE_CHOOSER_UTILS_H__
+
+#include "gtkfilechooser.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GTK_FILE_CHOOSER_PROP_ACTION = 0x1000,
+ 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
+} GtkFileChooserProp;
+
+void _gtk_file_chooser_install_properties (GObjectClass *klass);
+
+void _gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface);
+void _gtk_file_chooser_set_delegate (GtkFileChooser *receiver,
+ GtkFileChooser *delegate);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_UTILS_H__ */
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
new file mode 100644
index 0000000000..3a168bbbe5
--- /dev/null
+++ b/gtk/gtkfilechooserwidget.c
@@ -0,0 +1,155 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserwidget.c: Embeddable file selector widget
+ * 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 "gtkfilechooserwidget.h"
+#include "gtkfilechooserimpldefault.h"
+#include "gtkfilechooserenums.h"
+#include "gtkfilechooserutils.h"
+#include "gtkfilesystemunix.h"
+
+struct _GtkFileChooserWidgetPrivate
+{
+ GtkWidget *impl;
+};
+
+#define GTK_FILE_CHOOSER_WIDGET_GET_PRIVATE(o) (GTK_FILE_CHOOSER_WIDGET (o)->priv)
+
+static void gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class);
+static void gtk_file_chooser_widget_init (GtkFileChooserWidget *chooser_widget);
+static void gtk_file_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_file_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+GType
+gtk_file_chooser_widget_get_type (void)
+{
+ static GType file_chooser_widget_type = 0;
+
+ if (!file_chooser_widget_type)
+ {
+ static const GTypeInfo file_chooser_widget_info =
+ {
+ sizeof (GtkFileChooserWidgetClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_chooser_widget_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileChooserWidget),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_chooser_widget_init,
+ };
+
+ static const GInterfaceInfo file_chooser_info =
+ {
+ (GInterfaceInitFunc) _gtk_file_chooser_delegate_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_chooser_widget_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserWidget",
+ &file_chooser_widget_info, 0);
+ g_type_add_interface_static (file_chooser_widget_type,
+ GTK_TYPE_FILE_CHOOSER,
+ &file_chooser_info);
+ }
+
+ return file_chooser_widget_type;
+}
+
+static void
+gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_file_chooser_widget_set_property;
+ gobject_class->get_property = gtk_file_chooser_widget_get_property;
+
+ _gtk_file_chooser_install_properties (gobject_class);
+
+ g_type_class_add_private (class, sizeof (GtkFileChooserWidgetPrivate));
+}
+
+static void
+gtk_file_chooser_widget_init (GtkFileChooserWidget *chooser_widget)
+{
+ GtkFileChooserWidgetPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (chooser_widget,
+ GTK_TYPE_FILE_CHOOSER_WIDGET,
+ GtkFileChooserWidgetPrivate);
+ gchar *current_folder;
+ gchar *current_folder_uri;
+
+ chooser_widget->priv = priv;
+
+ gtk_widget_push_composite_child ();
+
+ priv->impl = _gtk_file_chooser_impl_default_new (g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL));
+ gtk_box_pack_start (GTK_BOX (chooser_widget), priv->impl, TRUE, TRUE, 0);
+ gtk_widget_show (priv->impl);
+
+ current_folder = g_get_current_dir ();
+ current_folder_uri = g_filename_to_uri (current_folder, NULL, NULL);
+ if (current_folder_uri)
+ {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (priv->impl), current_folder_uri);
+ g_free (current_folder_uri);
+ }
+ g_free (current_folder);
+
+ _gtk_file_chooser_set_delegate (GTK_FILE_CHOOSER (chooser_widget),
+ GTK_FILE_CHOOSER (priv->impl));
+
+ gtk_widget_pop_composite_child ();
+}
+
+static void
+gtk_file_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserWidgetPrivate *priv = GTK_FILE_CHOOSER_WIDGET_GET_PRIVATE (object);
+
+ g_object_set_property (G_OBJECT (priv->impl), pspec->name, value);
+}
+
+static void
+gtk_file_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserWidgetPrivate *priv = GTK_FILE_CHOOSER_WIDGET_GET_PRIVATE (object);
+
+ g_object_get_property (G_OBJECT (priv->impl), pspec->name, value);
+}
+
+GtkWidget *
+gtk_file_chooser_widget_new (GtkFileChooserAction action)
+{
+ return g_object_new (GTK_TYPE_FILE_CHOOSER_WIDGET,
+ "action", action,
+ NULL);
+}
diff --git a/gtk/gtkfilechooserwidget.h b/gtk/gtkfilechooserwidget.h
new file mode 100644
index 0000000000..e52fc11fce
--- /dev/null
+++ b/gtk/gtkfilechooserwidget.h
@@ -0,0 +1,58 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechooserwidget.h: Embeddable file selector widget
+ * 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_CHOOSER_WIDGET_H__
+#define __GTK_FILE_CHOOSER_WIDGET_H__
+
+#include "gtkfilechooser.h"
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_CHOOSER_WIDGET (gtk_file_chooser_widget_get_type ())
+#define GTK_FILE_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER_WIDGET, GtkFileChooserWidget))
+#define GTK_FILE_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_WIDGET, GtkFileChooserWidgetClass))
+#define GTK_IS_FILE_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_CHOOSER_WIDGET))
+#define GTK_IS_FILE_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_WIDGET))
+#define GTK_FILE_CHOOSER_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_WIDGET, GtkFileChooserWidgetClass))
+
+typedef struct _GtkFileChooserWidget GtkFileChooserWidget;
+typedef struct _GtkFileChooserWidgetClass GtkFileChooserWidgetClass;
+
+typedef struct _GtkFileChooserWidgetPrivate GtkFileChooserWidgetPrivate;
+
+struct _GtkFileChooserWidgetClass
+{
+ GtkVBoxClass parent_class;
+};
+
+struct _GtkFileChooserWidget
+{
+ GtkVBox parent_instance;
+
+ GtkFileChooserWidgetPrivate *priv;
+};
+
+GType gtk_file_chooser_widget_get_type (void);
+GtkWidget *gtk_file_chooser_widget_new (GtkFileChooserAction action);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_WIDGET_H__ */
diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c
new file mode 100644
index 0000000000..04a6002ff2
--- /dev/null
+++ b/gtk/gtkfilesystem.c
@@ -0,0 +1,465 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystem.c: Abstract file system interfaces
+ * 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 "gtkfilesystem.h"
+
+struct _GtkFileInfo
+{
+ GtkFileTime modification_time;
+ gint64 size;
+ gchar *display_name;
+ gchar *mime_type;
+ GdkPixbuf *icon;
+ guint is_folder : 1;
+ guint is_hidden : 1;
+};
+
+static void gtk_file_system_base_init (gpointer g_class);
+static void gtk_file_folder_base_init (gpointer g_class);
+
+GQuark
+gtk_file_system_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (quark == 0)
+ quark = g_quark_from_static_string ("gtk-file-system-error-quark");
+ return quark;
+}
+
+/*****************************************
+ * GtkFileInfo *
+ *****************************************/
+GType
+gtk_file_info_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static ("GtkFileInfo",
+ (GBoxedCopyFunc) gtk_file_info_copy,
+ (GBoxedFreeFunc) gtk_file_info_free);
+
+ return our_type;
+}
+
+GtkFileInfo *
+gtk_file_info_new (void)
+{
+ GtkFileInfo *info;
+
+ info = g_new0 (GtkFileInfo, 1);
+
+ return info;
+}
+
+GtkFileInfo *
+gtk_file_info_copy (GtkFileInfo *info)
+{
+ GtkFileInfo *new_info;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ new_info = g_memdup (info, sizeof (GtkFileInfo));
+ if (new_info->display_name)
+ new_info->display_name = g_strdup (new_info->display_name);
+ if (new_info->mime_type)
+ new_info->mime_type = g_strdup (new_info->mime_type);
+ if (new_info->icon)
+ g_object_ref (new_info->icon);
+
+ return new_info;
+}
+
+void
+gtk_file_info_free (GtkFileInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ if (info->display_name)
+ g_free (info->display_name);
+ if (info->mime_type)
+ g_free (info->mime_type);
+ if (info->icon)
+ g_object_unref (info->icon);
+}
+
+G_CONST_RETURN gchar *
+gtk_file_info_get_display_name (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->display_name;
+}
+
+void
+gtk_file_info_set_display_name (GtkFileInfo *info,
+ const gchar *display_name)
+{
+ g_return_if_fail (info != NULL);
+
+ if (info->display_name)
+ g_free (info->display_name);
+
+ info->display_name = g_strdup (display_name);
+}
+
+gboolean
+gtk_file_info_get_is_folder (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return info->is_folder;
+}
+
+void
+gtk_file_info_set_is_folder (GtkFileInfo *info,
+ gboolean is_folder)
+{
+ g_return_if_fail (info != NULL);
+
+ info->is_folder = is_folder != FALSE;
+}
+
+gboolean
+gtk_file_info_get_is_hidden (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return info->is_hidden;
+}
+
+void
+gtk_file_info_set_is_hidden (GtkFileInfo *info,
+ gboolean is_hidden)
+{
+ g_return_if_fail (info != NULL);
+
+ info->is_hidden = is_hidden != FALSE;
+}
+
+G_CONST_RETURN gchar *
+gtk_file_info_get_mime_type (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->mime_type;
+}
+
+void
+gtk_file_info_set_mime_type (GtkFileInfo *info,
+ const gchar *mime_type)
+{
+ g_return_if_fail (info != NULL);
+
+ if (info->mime_type)
+ g_free (info->mime_type);
+
+ info->mime_type = g_strdup (mime_type);
+}
+
+GtkFileTime
+gtk_file_info_get_modification_time (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, 0);
+
+ return info->modification_time;
+}
+
+void
+gtk_file_info_set_modification_time (GtkFileInfo *info,
+ GtkFileTime modification_time)
+{
+ g_return_if_fail (info != NULL);
+
+ info->modification_time = modification_time;
+}
+
+gint64
+gtk_file_info_get_size (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, 0);
+
+ return info->size;
+}
+
+void
+gtk_file_info_set_size (GtkFileInfo *info,
+ gint64 size)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (size < 0);
+
+ info->size = size;
+}
+
+GdkPixbuf *
+gtk_file_info_get_icon (const GtkFileInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->icon;
+}
+
+void
+gtk_file_info_set_icon (GtkFileInfo *info,
+ GdkPixbuf *icon)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (icon == NULL || GDK_IS_PIXBUF (icon));
+
+ if (icon != info->icon)
+ {
+ if (info->icon)
+ g_object_unref (info->icon);
+
+ info->icon = icon;
+
+ if (info->icon)
+ g_object_ref (info->icon);
+ }
+}
+
+/*****************************************
+ * GtkFileSystem *
+ *****************************************/
+GType
+gtk_file_system_get_type (void)
+{
+ static GType file_system_type = 0;
+
+ if (!file_system_type)
+ {
+ static const GTypeInfo file_system_info =
+ {
+ sizeof (GtkFileSystemIface), /* class_size */
+ gtk_file_system_base_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ file_system_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GtkFileSystem",
+ &file_system_info, 0);
+ }
+
+ return file_system_type;
+}
+
+static void
+gtk_file_system_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
+
+ g_signal_new ("roots_changed",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileSystemIface, roots_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ initialized = TRUE;
+ }
+}
+
+GSList *
+gtk_file_system_list_roots (GtkFileSystem *file_system)
+{
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+
+ return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_roots (file_system);
+}
+
+GtkFileInfo *
+gtk_file_system_get_root_info (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_root_info (file_system, uri, types, error);
+}
+
+GtkFileFolder *
+gtk_file_system_get_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, uri, types, error);
+}
+
+gboolean
+gtk_file_system_create_folder(GtkFileSystem *file_system,
+ const gchar *uri,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, uri, error);
+}
+
+gboolean
+gtk_file_system_get_parent (GtkFileSystem *file_system,
+ const gchar *uri,
+ gchar **parent,
+ GError **error)
+{
+ gchar *tmp_parent = NULL;
+ gboolean result;
+
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, uri, &tmp_parent, error);
+ g_assert (result || tmp_parent == NULL);
+
+ if (parent)
+ *parent = tmp_parent;
+
+ return result;
+}
+
+gchar *
+gtk_file_system_make_uri (GtkFileSystem *file_system,
+ const gchar *base_uri,
+ const gchar *display_name,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+ g_return_val_if_fail (base_uri != NULL, NULL);
+ g_return_val_if_fail (display_name != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_uri (file_system, base_uri, display_name, error);
+}
+
+
+/*****************************************
+ * GtkFileFolder *
+ *****************************************/
+GType
+gtk_file_folder_get_type (void)
+{
+ static GType file_folder_type = 0;
+
+ if (!file_folder_type)
+ {
+ static const GTypeInfo file_folder_info =
+ {
+ sizeof (GtkFileFolderIface), /* class_size */
+ gtk_file_folder_base_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GtkFileFolder",
+ &file_folder_info, 0);
+ }
+
+ return file_folder_type;
+}
+
+static void
+gtk_file_folder_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
+
+ g_signal_new ("deleted",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ g_signal_new ("file_added",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileFolderIface, file_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 0);
+ g_signal_new ("file_changed",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileFolderIface, file_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 0);
+ g_signal_new ("file_removed",
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkFileFolderIface, file_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 0);
+
+ initialized = TRUE;
+ }
+}
+
+gboolean
+gtk_file_folder_list_children (GtkFileFolder *folder,
+ GSList **children,
+ GError **error)
+{
+ gboolean result;
+ GSList *tmp_children = NULL;
+
+ g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
+ g_assert (result || tmp_children == NULL);
+
+ if (children)
+ *children = tmp_children;
+
+ return result;
+}
+
+GtkFileInfo *
+gtk_file_folder_get_info (GtkFileFolder *folder,
+ const gchar *uri,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, uri, error);
+}
diff --git a/gtk/gtkfilesystem.h b/gtk/gtkfilesystem.h
new file mode 100644
index 0000000000..bd349a26c8
--- /dev/null
+++ b/gtk/gtkfilesystem.h
@@ -0,0 +1,209 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystem.h: Abstract file system interfaces
+ * 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_SYSTEM_H__
+#define __GTK_FILE_SYSTEM_H__
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+typedef gint64 GtkFileTime;
+
+typedef struct _GtkFileFolder GtkFileFolder;
+typedef struct _GtkFileFolderIface GtkFileFolderIface;
+typedef struct _GtkFileInfo GtkFileInfo;
+typedef struct _GtkFileSystem GtkFileSystem;
+typedef struct _GtkFileSystemIface GtkFileSystemIface;
+
+/* Mask of information about a file, for monitoring and
+ * gtk_file_system_get_info()
+ */
+typedef enum {
+ GTK_FILE_INFO_DISPLAY_NAME = 1 << 0,
+ GTK_FILE_INFO_IS_FOLDER = 1 << 1,
+ GTK_FILE_INFO_IS_HIDDEN = 1 << 2,
+ GTK_FILE_INFO_MIME_TYPE = 1 << 3,
+ GTK_FILE_INFO_MODIFICATION_TIME = 1 << 4,
+ GTK_FILE_INFO_SIZE = 1 << 5,
+ GTK_FILE_INFO_ICON = 1 << 6
+} GtkFileInfoType;
+
+/* GError enumeration for GtkFileSystem
+ */
+
+#define GTK_FILE_SYSTEM_ERROR (gtk_file_system_error_quark ())
+
+typedef enum
+{
+ GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
+ GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
+ GTK_FILE_SYSTEM_ERROR_INVALID_URI,
+ GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
+ GTK_FILE_SYSTEM_ERROR_FAILED,
+} GtkFileSystemError;
+
+GQuark gtk_file_system_error_quark (void);
+
+/* Boxed-type for gtk_file_folder_get_info() results
+ */
+#define GTK_TYPE_FILE_INFO (gtk_file_info_get_type ())
+
+GType gtk_file_info_get_type (void);
+
+GtkFileInfo *gtk_file_info_new (void);
+GtkFileInfo *gtk_file_info_copy (GtkFileInfo *info);
+void gtk_file_info_free (GtkFileInfo *info);
+
+
+G_CONST_RETURN gchar *gtk_file_info_get_display_name (const GtkFileInfo *info);
+void gtk_file_info_set_display_name (GtkFileInfo *info,
+ const gchar *display_name);
+gboolean gtk_file_info_get_is_folder (const GtkFileInfo *info);
+void gtk_file_info_set_is_folder (GtkFileInfo *info,
+ gboolean is_folder);
+gboolean gtk_file_info_get_is_hidden (const GtkFileInfo *info);
+void gtk_file_info_set_is_hidden (GtkFileInfo *info,
+ gboolean is_hidden);
+G_CONST_RETURN gchar *gtk_file_info_get_mime_type (const GtkFileInfo *info);
+void gtk_file_info_set_mime_type (GtkFileInfo *info,
+ const gchar *mime_type);
+GtkFileTime gtk_file_info_get_modification_time (const GtkFileInfo *info);
+void gtk_file_info_set_modification_time (GtkFileInfo *info,
+ GtkFileTime modification_time);
+gint64 gtk_file_info_get_size (const GtkFileInfo *info);
+void gtk_file_info_set_size (GtkFileInfo *info,
+ gint64 size);
+GdkPixbuf * gtk_file_info_get_icon (const GtkFileInfo *info);
+void gtk_file_info_set_icon (GtkFileInfo *info,
+ GdkPixbuf *icon);
+
+/* The base GtkFileSystem interface
+ */
+#define GTK_TYPE_FILE_SYSTEM (gtk_file_system_get_type ())
+#define GTK_FILE_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_SYSTEM, GtkFileSystem))
+#define GTK_IS_FILE_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM))
+#define GTK_FILE_SYSTEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_FILE_SYSTEM, GtkFileSystemIface))
+
+struct _GtkFileSystemIface
+{
+ GTypeInterface base_iface;
+
+ /* Methods
+ */
+ GSList * (*list_roots) (GtkFileSystem *file_system);
+
+ GtkFileInfo * (*get_root_info) (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+
+ GtkFileFolder * (*get_folder) (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+ gboolean (*create_folder) (GtkFileSystem *file_system,
+ const gchar *uri,
+ GError **error);
+
+ /* URI Manipulation
+ */
+ gboolean (*get_parent) (GtkFileSystem *file_system,
+ const gchar *uri,
+ gchar **parent,
+ GError **error);
+ gchar * (*make_uri) (GtkFileSystem *file_system,
+ const gchar *base_uri,
+ const gchar *display_name,
+ GError **error);
+ /* Signals
+ */
+ void (*roots_changed) (GtkFileSystem *file_system);
+};
+
+GType gtk_file_system_get_type (void);
+
+GSList * gtk_file_system_list_roots (GtkFileSystem *file_system);
+GtkFileInfo * gtk_file_system_get_root_info (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+
+gboolean gtk_file_system_get_parent (GtkFileSystem *file_system,
+ const gchar *uri,
+ gchar **parent,
+ GError **error);
+GtkFileFolder *gtk_file_system_get_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+gboolean gtk_file_system_create_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GError **error);
+gchar * gtk_file_system_make_uri (GtkFileSystem *file_system,
+ const gchar *base_uri,
+ const gchar *display_name,
+ GError **error);
+/*
+ * Detailed information about a particular folder
+ */
+#define GTK_TYPE_FILE_FOLDER (gtk_file_folder_get_type ())
+#define GTK_FILE_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER, GtkFileFolder))
+#define GTK_IS_FILE_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER))
+#define GTK_FILE_FOLDER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_FILE_FOLDER, GtkFileFolderIface))
+
+struct _GtkFileFolderIface
+{
+ GTypeInterface base_iface;
+
+ /* Methods
+ */
+ GtkFileInfo * (*get_info) (GtkFileFolder *folder,
+ const gchar *uri,
+ GError **error);
+ gboolean (*list_children) (GtkFileFolder *folder,
+ GSList **children,
+ GError **error);
+
+ /* ??? refresh() ??? */
+
+ /* Signals
+ */
+ void (*deleted) (GtkFileFolder *monitor);
+ void (*file_added) (GtkFileFolder *monitor,
+ const gchar *uri);
+ void (*file_changed) (GtkFileFolder *monitor,
+ const gchar *uri);
+ void (*file_removed) (GtkFileFolder *monitor,
+ const gchar *uri);
+};
+
+GType gtk_file_folder_get_type (void);
+gboolean gtk_file_folder_list_children (GtkFileFolder *folder,
+ GSList **children,
+ GError **error);
+GtkFileInfo *gtk_file_folder_get_info (GtkFileFolder *folder,
+ const gchar *uri,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_SYSTEM_H__ */
diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c
new file mode 100644
index 0000000000..067ff58f1e
--- /dev/null
+++ b/gtk/gtkfilesystemmodel.c
@@ -0,0 +1,1040 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystemmodel.c: GtkTreeModel wrapping a GtkFileSystem
+ * 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 "gtkfilesystemmodel.h"
+#include "gtkfilesystem.h"
+#include <gtk/gtktreemodel.h>
+#include <string.h>
+
+typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
+typedef struct _FileModelNode FileModelNode;
+
+#define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
+#define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
+#define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
+
+struct _GtkFileSystemModelClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkFileSystemModel
+{
+ GObject parent_instance;
+
+ GtkFileSystem *file_system;
+ GtkFileInfoType types;
+ FileModelNode *roots;
+ GtkFileFolder *root_folder;
+
+ GSource *dummy_idle;
+ GSList *dummy_idle_nodes;
+
+ gushort max_depth;
+
+ guint show_hidden : 1;
+ guint show_folders : 1;
+ guint show_files : 1;
+ guint folders_only : 1;
+};
+
+struct _FileModelNode
+{
+ gchar *uri;
+ FileModelNode *next;
+
+ GtkFileInfo *info;
+ GtkFileFolder *folder;
+
+ FileModelNode *children;
+ FileModelNode *parent;
+
+ guint ref_count;
+
+ gushort depth;
+
+ guint loaded : 1;
+ guint has_dummy : 1;
+ guint is_visible : 1;
+ guint has_children : 1;
+};
+
+static void gtk_file_system_model_class_init (GtkFileSystemModelClass *class);
+static void gtk_file_system_model_iface_init (GtkTreeModelIface *iface);
+static void gtk_file_system_model_init (GtkFileSystemModel *model);
+static void gtk_file_system_model_finalize (GObject *object);
+
+static GtkTreeModelFlags gtk_file_system_model_get_flags (GtkTreeModel *tree_model);
+static gint gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model);
+static GType gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void gtk_file_system_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+static void gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+static void queue_dummy_idle (GtkFileSystemModel *model,
+ FileModelNode *node);
+static void unqueue_dummy_idle (GtkFileSystemModel *model,
+ FileModelNode *node);
+
+static FileModelNode *file_model_node_new (const gchar *uri);
+static void file_model_node_free (FileModelNode *node);
+static void file_model_node_ref (FileModelNode *node);
+static void file_model_node_unref (GtkFileSystemModel *model,
+ FileModelNode *node);
+
+const GtkFileInfo * file_model_node_get_info (GtkFileSystemModel *model,
+ FileModelNode *node);
+static gboolean file_model_node_is_visible (GtkFileSystemModel *model,
+ FileModelNode *node);
+static void file_model_node_clear (GtkFileSystemModel *model,
+ FileModelNode *node);
+
+static FileModelNode *file_model_node_get_children (GtkFileSystemModel *model,
+ FileModelNode *node);
+
+GType
+_gtk_file_system_model_get_type (void)
+{
+ static GType file_system_model_type = 0;
+
+ if (!file_system_model_type)
+ {
+ static const GTypeInfo file_system_model_info =
+ {
+ sizeof (GtkFileSystemModelClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_system_model_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileSystemModel),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_system_model_init,
+ };
+
+ static const GInterfaceInfo file_system_info =
+ {
+ (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
+ "GtkFileSystemModel",
+ &file_system_model_info, 0);
+ g_type_add_interface_static (file_system_model_type,
+ GTK_TYPE_TREE_MODEL,
+ &file_system_info);
+ }
+
+ return file_system_model_type;
+}
+
+static void
+gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = gtk_file_system_model_finalize;
+}
+
+static void
+gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = gtk_file_system_model_get_flags;
+ iface->get_n_columns = gtk_file_system_model_get_n_columns;
+ iface->get_column_type = gtk_file_system_model_get_column_type;
+ iface->get_iter = gtk_file_system_model_get_iter;
+ iface->get_path = gtk_file_system_model_get_path;
+ iface->get_value = gtk_file_system_model_get_value;
+ iface->iter_next = gtk_file_system_model_iter_next;
+ iface->iter_children = gtk_file_system_model_iter_children;
+ iface->iter_has_child = gtk_file_system_model_iter_has_child;
+ iface->iter_n_children = gtk_file_system_model_iter_n_children;
+ iface->iter_nth_child = gtk_file_system_model_iter_nth_child;
+ iface->iter_parent = gtk_file_system_model_iter_parent;
+ iface->ref_node = gtk_file_system_model_ref_node;
+ iface->unref_node = gtk_file_system_model_unref_node;
+}
+
+static void
+gtk_file_system_model_init (GtkFileSystemModel *model)
+{
+ model->show_files = TRUE;
+ model->show_folders = TRUE;
+ model->show_hidden = FALSE;
+}
+
+static void
+gtk_file_system_model_finalize (GObject *object)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
+ FileModelNode *children;
+
+ if (model->root_folder)
+ g_object_unref (model->root_folder);
+
+ children = model->roots;
+ while (children)
+ {
+ file_model_node_free (children);
+ children = children->next;
+ }
+}
+
+/*
+ * ******************** GtkTreeModel methods ********************
+ */
+
+static GtkTreeModelFlags
+gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
+
+ if (model->max_depth == 1)
+ flags |= GTK_TREE_MODEL_LIST_ONLY;
+
+ return flags;
+}
+
+static gint
+gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
+{
+ return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
+}
+
+static GType
+gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ switch (index)
+ {
+ case GTK_FILE_SYSTEM_MODEL_URI:
+ return G_TYPE_STRING;
+ case GTK_FILE_SYSTEM_MODEL_INFO:
+ return GTK_TYPE_FILE_INFO;
+ default:
+ g_assert_not_reached ();
+ return G_TYPE_NONE;
+ }
+}
+
+static gboolean
+gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ GtkTreeIter parent;
+ gint *indices;
+ gint depth, i;
+
+ indices = gtk_tree_path_get_indices (path);
+ depth = gtk_tree_path_get_depth (path);
+
+ g_return_val_if_fail (depth > 0, FALSE);
+
+ if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
+ return FALSE;
+
+ for (i = 1; i < depth; i++)
+ {
+ parent = *iter;
+ if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GtkTreePath *
+gtk_file_system_model_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ FileModelNode *node = iter->user_data;
+
+ GtkTreePath *result = gtk_tree_path_new ();
+
+ while (node)
+ {
+ FileModelNode *parent = node->parent;
+ FileModelNode *children;
+ int n = 0;
+
+ if (parent)
+ children = parent->children;
+ else
+ children = model->roots;
+
+ while (children != node)
+ {
+ if (children->is_visible)
+ n++;
+ children = children->next;
+ }
+
+ gtk_tree_path_prepend_index (result, n);
+
+ node = parent;
+ }
+
+ return result;
+}
+
+static void
+gtk_file_system_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ FileModelNode *node = iter->user_data;
+
+ switch (column)
+ {
+ case GTK_FILE_SYSTEM_MODEL_URI:
+ g_value_set_string (value, node->uri);
+ break;
+ case GTK_FILE_SYSTEM_MODEL_INFO:
+ g_value_set_boxed (value, file_model_node_get_info (model, node));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ FileModelNode *node = iter->user_data;
+
+ node = node->next;
+ while (node && !node->is_visible)
+ node = node->next;
+
+ iter->user_data = node;
+
+ return node != NULL;
+}
+
+static gboolean
+gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ FileModelNode *children;
+
+ if (parent)
+ {
+ FileModelNode *parent_node = parent->user_data;
+ children = file_model_node_get_children (model, parent_node);
+ }
+ else
+ {
+ children = model->roots;
+ }
+
+ while (children && !children->is_visible)
+ children = children->next;
+
+ iter->user_data = children;
+
+ return children != NULL;
+}
+
+static gboolean
+gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ FileModelNode *node = iter->user_data;
+
+ /* We never want to go into a directory just to
+ * find out if it has children
+ */
+ if (node->loaded)
+ return node->has_children;
+ else
+ {
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+
+ if (node->depth == model->max_depth)
+ return FALSE;
+ else
+ {
+ const GtkFileInfo *info = file_model_node_get_info (model, node);
+ return gtk_file_info_get_is_folder (info);
+ }
+ }
+}
+
+static gint
+gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ FileModelNode *children;
+ gint n = 0;
+
+ if (iter)
+ {
+ FileModelNode *node = iter->user_data;
+ children = file_model_node_get_children (model, node);
+ }
+ else
+ {
+ children = model->roots;
+ }
+
+ while (children)
+ {
+ if (children->is_visible)
+ n++;
+ children = children->next;
+ }
+
+ return n;
+}
+
+static gboolean
+gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
+ FileModelNode *children;
+
+ if (parent)
+ {
+ FileModelNode *parent_node = parent->user_data;
+ children = file_model_node_get_children (model, parent_node);
+ }
+ else
+ {
+ children = model->roots;
+ }
+
+ while (children && !children->is_visible)
+ children = children->next;
+
+ while (n && children)
+ {
+ n--;
+ children = children->next;
+ while (children && !children->is_visible)
+ children = children->next;
+ }
+
+ iter->user_data = children;
+
+ return children != NULL;
+}
+
+static gboolean
+gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ FileModelNode *node = child->user_data;
+
+ node = node->parent;
+ iter->user_data = node;
+
+ return node != NULL;
+}
+
+static void
+gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ file_model_node_ref (iter->user_data);
+}
+
+static void
+gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
+ iter->user_data);
+}
+
+GtkFileSystemModel *
+_gtk_file_system_model_new (GtkFileSystem *file_system,
+ const gchar *root_uri,
+ gint max_depth,
+ GtkFileInfoType types)
+{
+ GtkFileSystemModel *model;
+ GSList *roots, *tmp_list;
+
+ g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+
+ model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
+ model->file_system = g_object_ref (file_system);
+ if (max_depth < 0)
+ model->max_depth = G_MAXUSHORT;
+ else
+ model->max_depth = MIN (max_depth, G_MAXUSHORT);
+ model->types = types | GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
+
+ if (root_uri)
+ {
+ GSList *child_uris;
+
+ model->root_folder = gtk_file_system_get_folder (file_system, root_uri,
+ model->types,
+ NULL); /* NULL-GError */
+
+ if (model->root_folder &&
+ gtk_file_folder_list_children (model->root_folder,
+ &child_uris,
+ NULL)) /* NULL-GError */
+ roots = child_uris;
+ }
+ else
+ roots = gtk_file_system_list_roots (file_system);
+
+ for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
+ {
+ FileModelNode *node = file_model_node_new (tmp_list->data);
+ g_free (tmp_list->data);
+ node->is_visible = file_model_node_is_visible (model, node);
+ node->next = model->roots;
+ node->depth = 0;
+ model->roots = node;
+ }
+ g_slist_free (roots);
+
+ model->roots = (FileModelNode *)g_slist_reverse ((GSList *)model->roots);
+
+ return model;
+}
+
+static void
+model_refilter_recurse (GtkFileSystemModel *model,
+ FileModelNode *parent,
+ GtkTreePath *path)
+{
+ GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+ int i = 0;
+ FileModelNode *nodes;
+ gboolean has_children = FALSE;
+
+ if (parent && !parent->loaded)
+ return;
+
+ if (parent)
+ nodes = parent->children;
+ else
+ nodes = model->roots;
+
+ while (nodes)
+ {
+ FileModelNode *next = nodes->next;
+ gboolean is_visible;
+
+ gtk_tree_path_append_index (path, i);
+
+ is_visible = file_model_node_is_visible (model, nodes);
+
+ if (!is_visible && nodes->is_visible)
+ {
+ file_model_node_clear (model, nodes);
+ gtk_tree_model_row_deleted (tree_model, path);
+
+ nodes->is_visible = FALSE;
+ }
+ else if (is_visible && !nodes->is_visible)
+ {
+ GtkTreeIter iter;
+
+ iter.user_data = nodes;
+ gtk_tree_model_row_inserted (tree_model, path, &iter);
+
+ nodes->is_visible = TRUE;
+ }
+ else
+ model_refilter_recurse (model, nodes, path);
+
+ if (is_visible)
+ {
+ has_children = TRUE;
+ i++;
+ }
+
+ gtk_tree_path_up (path);
+
+ nodes = next;
+ }
+
+ if (parent && (has_children != parent->has_children))
+ {
+ GtkTreeIter iter;
+
+ parent->has_children = has_children;
+
+ iter.user_data = parent;
+ gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
+ }
+}
+
+void
+_gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
+ gboolean show_hidden)
+{
+ show_hidden = show_hidden != FALSE;
+
+ 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);
+ }
+}
+
+void
+_gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
+ gboolean show_folders)
+{
+ show_folders = show_folders != FALSE;
+
+ 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);
+ }
+}
+
+void
+_gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
+ gboolean show_files)
+{
+ show_files = show_files != FALSE;
+
+ 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);
+ }
+}
+
+const GtkFileInfo *
+_gtk_file_system_model_get_info (GtkFileSystemModel *model,
+ GtkTreeIter *iter)
+{
+ return file_model_node_get_info (model, iter->user_data);
+}
+
+const gchar *
+_gtk_file_system_model_get_uri (GtkFileSystemModel *model,
+ GtkTreeIter *iter)
+{
+ FileModelNode *node = iter->user_data;
+
+ return node->uri;
+}
+
+static void
+unref_node_and_parents (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ file_model_node_unref (model, node);
+ if (node->parent)
+ file_model_node_unref (model, node->parent);
+}
+
+static FileModelNode *
+find_and_ref_uri (GtkFileSystemModel *model,
+ const gchar *uri)
+{
+ gchar *parent_uri;
+ FileModelNode *parent_node;
+ FileModelNode *children;
+
+ if (!gtk_file_system_get_parent (model->file_system, uri, &parent_uri, NULL))
+ return NULL;
+
+ if (parent_uri)
+ {
+ parent_node = find_and_ref_uri (model, parent_uri);
+ g_free (parent_uri);
+
+ if (!parent_node)
+ return NULL;
+ }
+ else
+ parent_node = NULL;
+
+ if (parent_node)
+ children = file_model_node_get_children (model, parent_node);
+ else
+ children = model->roots;
+
+ while (children)
+ {
+ if (children->is_visible &&
+ strcmp (children->uri, uri) == 0)
+ {
+ file_model_node_ref (children);
+ return children;
+ }
+
+ children = children->next;
+ }
+
+ if (parent_node)
+ unref_node_and_parents (model, parent_node);
+
+ return FALSE;
+}
+
+gboolean
+_gtk_file_system_model_uri_do (GtkFileSystemModel *model,
+ const gchar *uri,
+ GtkFileSystemModelURIFunc func,
+ gpointer user_data)
+{
+ FileModelNode *node = find_and_ref_uri (model, uri);
+
+ if (node)
+ {
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ iter.user_data = node;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+
+ (*func) (model, path, &iter, user_data);
+
+ gtk_tree_path_free (path);
+ unref_node_and_parents (model, node);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+dummy_idle_callback (GtkFileSystemModel *model)
+{
+ GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+ GSList *tmp_list;
+
+ for (tmp_list = model->dummy_idle_nodes; tmp_list; tmp_list = tmp_list->next)
+ {
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ FileModelNode *node = tmp_list->data;
+ g_assert (node->children && !node->children->next && !node->children->children);
+
+ iter.user_data = node->children;
+ path = gtk_tree_model_get_path (tree_model, &iter);
+
+ if (node->children->ref_count)
+ gtk_tree_model_row_deleted (tree_model, path);
+
+ gtk_tree_path_up (path);
+ iter.user_data = node;
+ gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
+
+ gtk_tree_path_free (path);
+
+ file_model_node_free (node->children);
+ node->children = NULL;
+ node->has_children = FALSE;
+ node->has_dummy = FALSE;
+ }
+
+ model->dummy_idle_nodes = FALSE;
+ model->dummy_idle = NULL;
+
+ return FALSE;
+}
+
+static void
+queue_dummy_idle (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ model->dummy_idle_nodes = g_slist_prepend (model->dummy_idle_nodes, node);
+
+ if (!model->dummy_idle)
+ {
+ model->dummy_idle = g_idle_source_new ();
+ g_source_set_priority (model->dummy_idle, G_PRIORITY_HIGH_IDLE);
+ g_source_set_closure (model->dummy_idle,
+ g_cclosure_new_object (G_CALLBACK (dummy_idle_callback), G_OBJECT (model)));
+ g_source_attach (model->dummy_idle, NULL);
+ }
+}
+
+static void
+unqueue_dummy_idle (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ model->dummy_idle_nodes = g_slist_remove (model->dummy_idle_nodes, node);
+
+ if (!model->dummy_idle_nodes)
+ g_source_destroy (model->dummy_idle);
+}
+
+static FileModelNode *
+file_model_node_new (const gchar *uri)
+{
+ FileModelNode *node = g_new0 (FileModelNode, 1);
+
+ node->uri = g_strdup (uri);
+
+ return node;
+}
+
+static void
+file_model_node_free (FileModelNode *node)
+{
+ if (node->children)
+ {
+ FileModelNode *children;
+
+ for (children = node->children; children; children = children->next)
+ file_model_node_free (children);
+ }
+
+ if (node->uri)
+ g_free (node->uri);
+
+ if (node->info)
+ gtk_file_info_free (node->info);
+
+ if (node->folder)
+ g_object_unref (node->folder);
+
+ g_free (node);
+}
+
+const GtkFileInfo *
+file_model_node_get_info (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ if (!node->info)
+ {
+ if (node->parent && node->parent->has_dummy)
+ {
+ node->info = gtk_file_info_new ();
+ gtk_file_info_set_display_name (node->info, "Loading...");
+ }
+ else if (node->parent || model->root_folder)
+ {
+ node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
+ node->uri,
+ NULL); /* NULL-GError */
+ }
+ else
+ {
+ node->info = gtk_file_system_get_root_info (model->file_system,
+ node->uri,
+ model->types,
+ NULL); /* NULL-GError */
+ }
+ }
+
+ return node->info;
+}
+
+static gboolean
+file_model_node_is_visible (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ if (model->show_hidden && model->show_folders && model->show_files)
+ return TRUE;
+ else
+ {
+ const GtkFileInfo *info = file_model_node_get_info (model, node);
+ gboolean is_folder = gtk_file_info_get_is_folder (info);
+
+ if (!model->show_folders && is_folder)
+ return FALSE;
+ if (!model->show_files && !is_folder)
+ return FALSE;
+ if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
+ return FALSE;
+
+ return TRUE;
+ }
+}
+
+static void
+file_model_node_clear (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ FileModelNode *children;
+
+ if (node->has_dummy)
+ unqueue_dummy_idle (model, node);
+
+ if (node->folder)
+ {
+ g_object_unref (node->folder);
+ node->folder = NULL;
+ }
+
+ children = node->children;
+ node->children = NULL;
+ node->has_children = FALSE;
+ node->loaded = FALSE;
+
+ while (children)
+ {
+ FileModelNode *next = children->next;
+
+ file_model_node_clear (model, children);
+ file_model_node_free (children);
+
+ children = next;
+ }
+
+ node->ref_count = 0;
+}
+
+static void
+file_model_node_ref (FileModelNode *node)
+{
+ node->ref_count++;
+}
+
+static void
+file_model_node_unref (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ node->ref_count--;
+ if (node->ref_count == 0)
+ file_model_node_clear (model, node);
+}
+
+static FileModelNode *
+file_model_node_get_children (GtkFileSystemModel *model,
+ FileModelNode *node)
+{
+ if (node->ref_count == 0)
+ return NULL;
+
+ if (!node->loaded)
+ {
+ const GtkFileInfo *info = file_model_node_get_info (model, node);
+ gboolean has_children = FALSE;
+ gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
+
+ if (is_folder)
+ node->folder = gtk_file_system_get_folder (model->file_system,
+ node->uri,
+ model->types,
+ NULL); /* NULL-GError */
+
+ if (node->folder)
+ {
+ GSList *child_uris, *tmp_list;
+
+ if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */
+ {
+ for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next)
+ {
+ FileModelNode *child_node = file_model_node_new (tmp_list->data);
+ g_free (tmp_list->data);
+ child_node->next = node->children;
+ child_node->parent = node;
+ child_node->depth = node->depth + 1;
+ child_node->is_visible = file_model_node_is_visible (model, child_node);
+ if (child_node->is_visible)
+ has_children = TRUE;
+ node->children = child_node;
+ }
+ g_slist_free (child_uris);
+ }
+
+ node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
+ }
+
+ node->has_children = has_children;
+
+ if (is_folder && !node->has_children)
+ {
+ /* The hard case ... we claimed this folder had children, but actually
+ * it didn't. We have to add a dummy child, then remove it later
+ */
+ FileModelNode *child_node = file_model_node_new ("***dummy***");
+ child_node->is_visible = TRUE;
+ child_node->parent = node;
+
+ node->children = child_node;
+ node->has_children = TRUE;
+ node->has_dummy = TRUE;
+
+ queue_dummy_idle (model, node);
+ }
+
+ node->loaded = TRUE;
+ }
+
+ return node->children;
+}
diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h
new file mode 100644
index 0000000000..5c2d1f3ff3
--- /dev/null
+++ b/gtk/gtkfilesystemmodel.h
@@ -0,0 +1,72 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystemmodel.h: GtkTreeModel wrapping a GtkFileSystem
+ * 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_SYSTEM_MODEL_H__
+#define __GTK_FILE_SYSTEM_MODEL_H__
+
+#include <glib-object.h>
+#include "gtkfilesystem.h"
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_SYSTEM_MODEL (_gtk_file_system_model_get_type ())
+#define GTK_FILE_SYSTEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModel))
+#define GTK_IS_FILE_SYSTEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM_MODEL))
+
+typedef struct _GtkFileSystemModel GtkFileSystemModel;
+
+GType _gtk_file_system_model_get_type (void);
+
+enum {
+ GTK_FILE_SYSTEM_MODEL_URI,
+ GTK_FILE_SYSTEM_MODEL_INFO,
+ GTK_FILE_SYSTEM_MODEL_N_COLUMNS
+} GtkFileSystemModelColumns;
+
+GtkFileSystemModel *_gtk_file_system_model_new (GtkFileSystem *file_system,
+ const gchar *root_uri,
+ gint max_depth,
+ GtkFileInfoType types);
+const GtkFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model,
+ GtkTreeIter *iter);
+const gchar * _gtk_file_system_model_get_uri (GtkFileSystemModel *model,
+ GtkTreeIter *iter);
+void _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
+ gboolean show_hidden);
+void _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
+ gboolean show_folders);
+void _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
+ gboolean show_files);
+
+
+typedef void (*GtkFileSystemModelURIFunc) (GtkFileSystemModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+gboolean _gtk_file_system_model_uri_do (GtkFileSystemModel *model,
+ const gchar *uri,
+ GtkFileSystemModelURIFunc func,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_SYSTEM_MODEL_H__ */
diff --git a/gtk/gtkfilesystemunix.c b/gtk/gtkfilesystemunix.c
new file mode 100644
index 0000000000..9e42ef978f
--- /dev/null
+++ b/gtk/gtkfilesystemunix.c
@@ -0,0 +1,588 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
+ * 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 "gtkfilesystem.h"
+#include "gtkfilesystemunix.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
+
+#define GTK_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
+#define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
+#define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
+
+struct _GtkFileSystemUnixClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkFileSystemUnix
+{
+ GObject parent_instance;
+};
+
+#define GTK_TYPE_FILE_FOLDER_UNIX (gtk_file_folder_unix_get_type ())
+#define GTK_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
+#define GTK_IS_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
+#define GTK_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
+#define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
+#define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
+
+typedef struct _GtkFileFolderUnix GtkFileFolderUnix;
+typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
+
+struct _GtkFileFolderUnixClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkFileFolderUnix
+{
+ GObject parent_instance;
+
+ GtkFileInfoType types;
+ gchar *filename;
+};
+
+static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
+static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
+static void gtk_file_system_unix_init (GtkFileSystemUnix *impl);
+static void gtk_file_system_unix_finalize (GObject *object);
+
+static GSList * gtk_file_system_unix_list_roots (GtkFileSystem *file_system);
+static GtkFileInfo * gtk_file_system_unix_get_root_info (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+static GtkFileFolder *gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error);
+static gboolean gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GError **error);
+static gboolean gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
+ const gchar *uri,
+ gchar **parent,
+ GError **error);
+static gchar * gtk_file_system_unix_make_uri (GtkFileSystem *file_system,
+ const gchar *base_uri,
+ const gchar *display_name,
+ GError **error);
+
+static GType gtk_file_folder_unix_get_type (void);
+static void gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
+static void gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface);
+static void gtk_file_folder_unix_init (GtkFileFolderUnix *impl);
+static void gtk_file_folder_unix_finalize (GObject *object);
+
+static GtkFileInfo *gtk_file_folder_unix_get_info (GtkFileFolder *folder,
+ const gchar *uri,
+ GError **error);
+static gboolean gtk_file_folder_unix_list_children (GtkFileFolder *folder,
+ GSList **children,
+ GError **error);
+
+static gchar * filename_from_uri (const gchar *filename,
+ GError **error);
+static gchar * filename_to_uri (const gchar *filename);
+static gboolean filename_is_root (const char *filename);
+static GtkFileInfo *filename_get_info (const gchar *filename,
+ GtkFileInfoType types,
+ GError **error);
+
+/*
+ * GtkFileSystemUnix
+ */
+GType
+_gtk_file_system_unix_get_type (void)
+{
+ static GType file_system_unix_type = 0;
+
+ if (!file_system_unix_type)
+ {
+ static const GTypeInfo file_system_unix_info =
+ {
+ sizeof (GtkFileSystemUnixClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_system_unix_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileSystemUnix),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_system_unix_init,
+ };
+
+ static const GInterfaceInfo file_system_info =
+ {
+ (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
+ "GtkFileSystemUnix",
+ &file_system_unix_info, 0);
+ g_type_add_interface_static (file_system_unix_type,
+ GTK_TYPE_FILE_SYSTEM,
+ &file_system_info);
+ }
+
+ return file_system_unix_type;
+}
+
+static void
+gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = gtk_file_system_unix_finalize;
+}
+
+static void
+gtk_file_system_unix_iface_init (GtkFileSystemIface *iface)
+{
+ iface->list_roots = gtk_file_system_unix_list_roots;
+ iface->get_folder = gtk_file_system_unix_get_folder;
+ iface->get_root_info = gtk_file_system_unix_get_root_info;
+ iface->create_folder = gtk_file_system_unix_create_folder;
+ iface->get_parent = gtk_file_system_unix_get_parent;
+ iface->make_uri = gtk_file_system_unix_make_uri;
+}
+
+static void
+gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
+{
+}
+
+static void
+gtk_file_system_unix_finalize (GObject *object)
+{
+}
+
+static GSList *
+gtk_file_system_unix_list_roots (GtkFileSystem *file_system)
+{
+ return g_slist_append (NULL, g_strdup ("file:///"));
+}
+
+static GtkFileInfo *
+gtk_file_system_unix_get_root_info (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error)
+{
+ g_return_val_if_fail (strcmp (uri, "file:///") == 0, NULL);
+
+ return filename_get_info ("/", types, error);
+}
+
+static GtkFileFolder *
+gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GtkFileInfoType types,
+ GError **error)
+{
+ GtkFileFolderUnix *folder_unix;
+ gchar *filename;
+
+ filename = filename_from_uri (uri, error);
+ if (!filename)
+ return NULL;
+
+ folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
+ folder_unix->filename = filename;
+ folder_unix->types = types;
+
+ return GTK_FILE_FOLDER (folder_unix);
+}
+
+static gboolean
+gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
+ const gchar *uri,
+ GError **error)
+{
+ gchar *filename;
+ gboolean result;
+
+ filename = filename_from_uri (uri, error);
+ if (!filename)
+ return FALSE;
+
+ result = mkdir (filename, 0777) != 0;
+
+ if (!result)
+ {
+ gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
+ "error creating directory '%s': %s",
+ filename_utf8 ? filename_utf8 : "???",
+ g_strerror (errno));
+ g_free (filename_utf8);
+ }
+
+ g_free (filename);
+
+ return result;
+}
+
+static gboolean
+gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
+ const gchar *uri,
+ gchar **parent,
+ GError **error)
+{
+ gchar *filename = filename_from_uri (uri, error);
+
+ if (!filename)
+ return FALSE;
+
+ if (filename_is_root (filename))
+ {
+ *parent = NULL;
+ }
+ else
+ {
+ gchar *parent_filename = g_path_get_dirname (filename);
+ *parent = filename_to_uri (parent_filename);
+ g_free (parent_filename);
+ }
+
+ g_free (filename);
+
+ return TRUE;
+}
+
+static gchar *
+gtk_file_system_unix_make_uri (GtkFileSystem *file_system,
+ const gchar *base_uri,
+ const gchar *display_name,
+ GError **error)
+{
+ gchar *base_filename;
+ gchar *filename;
+ gchar *full_filename;
+ GError *tmp_error = NULL;
+ gchar *result;
+
+ base_filename = filename_from_uri (base_uri, error);
+ if (!base_filename)
+ return FALSE;
+
+ filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
+ if (!filename)
+ {
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
+ "%s",
+ tmp_error->message);
+
+ g_error_free (tmp_error);
+ g_free (base_filename);
+
+ return FALSE;
+ }
+
+ full_filename = g_build_filename (base_filename, filename, NULL);
+ result = filename_to_uri (full_filename);
+ g_free (base_filename);
+ g_free (filename);
+ g_free (full_filename);
+
+ return result;
+}
+
+/*
+ * GtkFileFolderUnix
+ */
+static GType
+gtk_file_folder_unix_get_type (void)
+{
+ static GType file_folder_unix_type = 0;
+
+ if (!file_folder_unix_type)
+ {
+ static const GTypeInfo file_folder_unix_info =
+ {
+ sizeof (GtkFileFolderUnixClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_file_folder_unix_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkFileFolderUnix),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_file_folder_unix_init,
+ };
+
+ static const GInterfaceInfo file_folder_info =
+ {
+ (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
+ "GtkFileFolderUnix",
+ &file_folder_unix_info, 0);
+ g_type_add_interface_static (file_folder_unix_type,
+ GTK_TYPE_FILE_FOLDER,
+ &file_folder_info);
+ }
+
+ return file_folder_unix_type;
+}
+
+static void
+gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = gtk_file_folder_unix_finalize;
+}
+
+static void
+gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
+{
+ iface->get_info = gtk_file_folder_unix_get_info;
+ iface->list_children = gtk_file_folder_unix_list_children;
+}
+
+static void
+gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
+{
+}
+
+static void
+gtk_file_folder_unix_finalize (GObject *object)
+{
+ GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
+
+ g_free (folder_unix->filename);
+}
+
+static GtkFileInfo *
+gtk_file_folder_unix_get_info (GtkFileFolder *folder,
+ const gchar *uri,
+ GError **error)
+{
+ GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
+ GtkFileInfo *info;
+ gchar *dirname;
+ gchar *filename;
+
+ filename = filename_from_uri (uri, error);
+ if (!filename)
+ return NULL;
+
+ dirname = g_path_get_dirname (filename);
+ g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
+ g_free (dirname);
+
+ info = filename_get_info (filename, folder_unix->types, error);
+
+ g_free (filename);
+
+ return info;
+}
+
+static gboolean
+gtk_file_folder_unix_list_children (GtkFileFolder *folder,
+ GSList **children,
+ GError **error)
+{
+ GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
+ GError *tmp_error = NULL;
+ GDir *dir;
+
+ *children = NULL;
+
+ dir = g_dir_open (folder_unix->filename, 0, &tmp_error);
+ if (!dir)
+ {
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
+ "%s",
+ tmp_error->message);
+
+ g_error_free (tmp_error);
+
+ return FALSE;
+ }
+
+ while (TRUE)
+ {
+ const gchar *filename = g_dir_read_name (dir);
+ gchar *fullname;
+
+ if (!filename)
+ break;
+
+ fullname = g_build_filename (folder_unix->filename, filename, NULL);
+ *children = g_slist_prepend (*children, filename_to_uri (fullname));
+ g_free (fullname);
+ }
+
+ g_dir_close (dir);
+
+ *children = g_slist_reverse (*children);
+
+ return TRUE;
+}
+
+static GtkFileInfo *
+filename_get_info (const gchar *filename,
+ GtkFileInfoType types,
+ GError **error)
+{
+ GtkFileInfo *info;
+ struct stat statbuf;
+
+ /* If stat fails, try to fall back to lstat to catch broken links
+ */
+ if (stat (filename, &statbuf) != 0 &&
+ lstat (filename, &statbuf) != 0)
+ {
+ gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_NONEXISTANT,
+ "error getting information for '%s': %s",
+ filename_utf8 ? filename_utf8 : "???",
+ g_strerror (errno));
+ g_free (filename_utf8);
+
+ return NULL;
+ }
+
+ info = gtk_file_info_new ();
+
+ if (filename_is_root (filename))
+ {
+ if (types & GTK_FILE_INFO_DISPLAY_NAME)
+ gtk_file_info_set_display_name (info, "/");
+
+ if (types & GTK_FILE_INFO_IS_HIDDEN)
+ gtk_file_info_set_is_hidden (info, FALSE);
+ }
+ else
+ {
+ gchar *basename = g_path_get_basename (filename);
+
+ if (types & GTK_FILE_INFO_DISPLAY_NAME)
+ {
+ gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
+ if (!display_name)
+ display_name = g_strescape (basename, NULL);
+
+ gtk_file_info_set_display_name (info, display_name);
+
+ g_free (display_name);
+ }
+
+ if (types & GTK_FILE_INFO_IS_HIDDEN)
+ {
+ gtk_file_info_set_is_hidden (info, basename[0] == '.');
+ }
+
+ g_free (basename);
+ }
+
+ if (types & GTK_FILE_INFO_IS_FOLDER)
+ {
+ gtk_file_info_set_is_folder (info, S_ISDIR (statbuf.st_mode));
+ }
+
+ if (types & GTK_FILE_INFO_MIME_TYPE)
+ {
+ gtk_file_info_set_mime_type (info, "application/octet-stream");
+ }
+
+ if (types & GTK_FILE_INFO_MODIFICATION_TIME)
+ {
+ gtk_file_info_set_modification_time (info, statbuf.st_mtime);
+ }
+
+ if (types & GTK_FILE_INFO_SIZE)
+ {
+ gtk_file_info_set_size (info, (gint64)512 * (gint16)statbuf.st_blocks);
+ }
+
+ if (types & GTK_FILE_INFO_ICON)
+ {
+ /* NOT YET IMPLEMENTED */
+ }
+
+ return info;
+}
+
+static gchar *
+filename_from_uri (const char *uri,
+ GError **error)
+{
+ GError *tmp_error = NULL;
+ gchar *result;
+
+ result = g_filename_from_uri (uri, NULL, &tmp_error);
+ if (!result)
+ {
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_INVALID_URI,
+ "%s",
+ tmp_error->message);
+
+ g_error_free (tmp_error);
+ }
+
+ return result;
+}
+
+static gchar *
+filename_to_uri (const char *filename)
+{
+ gchar *result;
+
+ result = g_filename_to_uri (filename, NULL, NULL);
+ if (!result)
+ g_warning ("GtkFileSystemUnix: Handling of non-UTF-8-representable-filenames needs to be fixed");
+
+ return result;
+}
+
+static gboolean
+filename_is_root (const char *filename)
+{
+ const gchar *after_root;
+
+ after_root = g_path_skip_root (filename);
+
+ return (*after_root == '\0');
+}
diff --git a/gtk/gtkfilesystemunix.h b/gtk/gtkfilesystemunix.h
new file mode 100644
index 0000000000..03d816efea
--- /dev/null
+++ b/gtk/gtkfilesystemunix.h
@@ -0,0 +1,38 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilesystemunix.h: Default implementation of GtkFileSystem for UNIX-like systems
+ * 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_SYSTEM_UNIX_H__
+#define __GTK_FILE_SYSTEM_UNIX_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_SYSTEM_UNIX (_gtk_file_system_unix_get_type ())
+#define GTK_FILE_SYSTEM_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnix))
+#define GTK_IS_FILE_SYSTEM_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_SYSTEM_UNIX))
+
+typedef struct _GtkFileSystemUnix GtkFileSystemUnix;
+
+GType _gtk_file_system_unix_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_SYSTEM_UNIX_H__ */
diff --git a/tests/testfilechooser.c b/tests/testfilechooser.c
new file mode 100644
index 0000000000..b44f3efe4e
--- /dev/null
+++ b/tests/testfilechooser.c
@@ -0,0 +1,79 @@
+#include <gtk/gtk.h>
+#include "gtkfilechooserdialog.h"
+#include "gtkfilechooser.h"
+#include "prop-editor.h"
+
+static void
+print_current_folder (GtkFileChooser *chooser)
+{
+ gchar *uri;
+
+ uri = gtk_file_chooser_get_current_folder_uri (chooser);
+ g_print ("Current folder changed :\n %s\n", uri);
+ g_free (uri);
+}
+
+static void
+print_selected (GtkFileChooser *chooser)
+{
+ GSList *uris = gtk_file_chooser_get_uris (chooser);
+ GSList *tmp_list;
+
+ g_print ("Selection changed :\n");
+ for (tmp_list = uris; tmp_list; tmp_list = tmp_list->next)
+ {
+ gchar *uri = tmp_list->data;
+ g_print (" %s\n", uri);
+ g_free (uri);
+ }
+ g_print ("\n");
+ g_slist_free (uris);
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *control_window;
+ GtkWidget *vbbox;
+ GtkWidget *button;
+ GtkWidget *dialog;
+ GtkWidget *prop_editor;
+
+ gtk_init (&argc, &argv);
+
+ dialog = gtk_file_chooser_dialog_new ("Select a file", NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+ g_signal_connect (dialog, "selection_changed",
+ G_CALLBACK (print_selected), NULL);
+ g_signal_connect (dialog, "current_folder_changed",
+ G_CALLBACK (print_current_folder), NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 400);
+ gtk_widget_show (dialog);
+
+ prop_editor = create_prop_editor (G_OBJECT (dialog), GTK_TYPE_FILE_CHOOSER);
+
+ control_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ vbbox = gtk_vbutton_box_new ();
+ gtk_container_add (GTK_CONTAINER (control_window), vbbox);
+
+ button = gtk_button_new_with_mnemonic ("_Select all");
+ gtk_container_add (GTK_CONTAINER (vbbox), button);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_file_chooser_select_all), dialog);
+
+ button = gtk_button_new_with_mnemonic ("_Unselect all");
+ gtk_container_add (GTK_CONTAINER (vbbox), button);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_file_chooser_unselect_all), dialog);
+
+ gtk_widget_show_all (control_window);
+
+ gtk_main ();
+
+ return 0;
+}