diff options
author | Owen Taylor <otaylor@src.gnome.org> | 2003-03-21 20:34:02 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2003-03-21 20:34:02 +0000 |
commit | e3849905a9f6945ab664a026a2af78958d02641f (patch) | |
tree | 3c4d7df4785e8f01118f0bb4aad5cfa54431ba77 | |
parent | 250fd7cc43af62578cbcd58a90e1fe0fb706eed1 (diff) | |
download | gtk+-e3849905a9f6945ab664a026a2af78958d02641f.tar.gz |
Initial revision
-rw-r--r-- | gtk/gtkfilechooser.c | 419 | ||||
-rw-r--r-- | gtk/gtkfilechooser.h | 167 | ||||
-rw-r--r-- | gtk/gtkfilechooserdefault.c | 561 | ||||
-rw-r--r-- | gtk/gtkfilechooserdefault.h | 40 | ||||
-rw-r--r-- | gtk/gtkfilechooserdialog.c | 172 | ||||
-rw-r--r-- | gtk/gtkfilechooserdialog.h | 62 | ||||
-rw-r--r-- | gtk/gtkfilechooserutils.c | 172 | ||||
-rw-r--r-- | gtk/gtkfilechooserutils.h | 47 | ||||
-rw-r--r-- | gtk/gtkfilechooserwidget.c | 155 | ||||
-rw-r--r-- | gtk/gtkfilechooserwidget.h | 58 | ||||
-rw-r--r-- | gtk/gtkfilesystem.c | 465 | ||||
-rw-r--r-- | gtk/gtkfilesystem.h | 209 | ||||
-rw-r--r-- | gtk/gtkfilesystemmodel.c | 1040 | ||||
-rw-r--r-- | gtk/gtkfilesystemmodel.h | 72 | ||||
-rw-r--r-- | gtk/gtkfilesystemunix.c | 588 | ||||
-rw-r--r-- | gtk/gtkfilesystemunix.h | 38 | ||||
-rw-r--r-- | tests/testfilechooser.c | 79 |
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; +} |