summaryrefslogtreecommitdiff
path: root/plugins/filebrowser
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-11-07 16:46:58 -0300
committerPerberos <[email protected]>2011-11-07 16:46:58 -0300
commit528c1e5ff51e213936e800fc5a9a25da99c0bdf2 (patch)
tree77f8aa456b09367ba81f04d4562fc935f898a951 /plugins/filebrowser
downloadpluma-528c1e5ff51e213936e800fc5a9a25da99c0bdf2.tar.bz2
pluma-528c1e5ff51e213936e800fc5a9a25da99c0bdf2.tar.xz
initial
Diffstat (limited to 'plugins/filebrowser')
-rwxr-xr-xplugins/filebrowser/Makefile.am104
-rwxr-xr-xplugins/filebrowser/filebrowser.gedit-plugin.desktop.in10
-rwxr-xr-xplugins/filebrowser/gedit-file-bookmarks-store.c879
-rwxr-xr-xplugins/filebrowser/gedit-file-bookmarks-store.h90
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-enum-register.c.template20
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-enum-types.c.template45
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-enum-types.h.template29
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-error.h41
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-marshal.list5
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-messages.c1033
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-messages.h35
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-plugin.c1254
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-plugin.h71
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-store.c3625
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-store.h200
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-utils.c198
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-utils.h27
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-view.c1256
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-view.h84
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-widget-ui.xml54
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-widget.c3143
-rwxr-xr-xplugins/filebrowser/gedit-file-browser-widget.h121
-rwxr-xr-xplugins/filebrowser/gedit-file-browser.schemas.in97
23 files changed, 12421 insertions, 0 deletions
diff --git a/plugins/filebrowser/Makefile.am b/plugins/filebrowser/Makefile.am
new file mode 100755
index 00000000..22301d5b
--- /dev/null
+++ b/plugins/filebrowser/Makefile.am
@@ -0,0 +1,104 @@
+# filebrowser
+
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(GEDIT_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+BUILT_SOURCES = \
+ gedit-file-browser-enum-types.h \
+ gedit-file-browser-enum-types.c \
+ gedit-file-browser-marshal.h \
+ gedit-file-browser-marshal.c
+
+plugin_LTLIBRARIES = libfilebrowser.la
+
+NOINST_H_FILES = \
+ gedit-file-bookmarks-store.h \
+ gedit-file-browser-store.h \
+ gedit-file-browser-view.h \
+ gedit-file-browser-widget.h \
+ gedit-file-browser-error.h \
+ gedit-file-browser-utils.h \
+ gedit-file-browser-plugin.h \
+ gedit-file-browser-messages.h
+
+libfilebrowser_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ gedit-file-bookmarks-store.c \
+ gedit-file-browser-store.c \
+ gedit-file-browser-view.c \
+ gedit-file-browser-widget.c \
+ gedit-file-browser-utils.c \
+ gedit-file-browser-plugin.c \
+ gedit-file-browser-messages.c \
+ $(NOINST_H_FILES)
+
+libfilebrowser_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libfilebrowser_la_LIBADD = $(GEDIT_LIBS)
+
+# UI files (if you use ui for your plugin, list those files here)
+uidir = $(GEDIT_PLUGINS_DATA_DIR)/filebrowser
+ui_DATA = gedit-file-browser-widget-ui.xml
+
+plugin_in_files = filebrowser.gedit-plugin.desktop.in
+
+%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+gedit-file-browser-enum-types.h: gedit-file-browser-enum-types.h.template $(NOINST_H_FILES) $(GLIB_MKENUMS)
+ (cd $(srcdir) && $(GLIB_MKENUMS) --template gedit-file-browser-enum-types.h.template $(NOINST_H_FILES)) > $@
+
+gedit-file-browser-enum-types.c: gedit-file-browser-enum-types.c.template gedit-file-browser-enum-register.c.template $(NOINST_H_FILES) $(GLIB_MKENUMS)
+ $(AM_V_GEN) (cd $(srcdir) && \
+ $(GLIB_MKENUMS) --template gedit-file-browser-enum-types.c.template $(NOINST_H_FILES) && \
+ $(GLIB_MKENUMS) --template gedit-file-browser-enum-register.c.template $(NOINST_H_FILES)) > $@
+
+gedit-file-browser-marshal.h: gedit-file-browser-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --header --prefix=gedit_file_browser_marshal > $@
+
+gedit-file-browser-marshal.c: gedit-file-browser-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN) echo "#include \"gedit-file-browser-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=gedit_file_browser_marshal >> $@
+
+plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
+
+schemasdir = $(MATECONF_SCHEMA_FILE_DIR)
+schemas_in_files = gedit-file-browser.schemas.in
+schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)
+@INTLTOOL_SCHEMAS_RULE@
+
+if MATECONF_SCHEMAS_INSTALL
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schemas_DATA) ; do \
+ MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) $(MATECONFTOOL) --makefile-install-rule $(top_builddir)/plugins/filebrowser/$$p ; \
+ done \
+ fi
+else
+install-data-local:
+endif
+
+
+EXTRA_DIST = \
+ $(ui_DATA) \
+ $(plugin_in_files) \
+ $(schemas_in_files) \
+ gedit-file-browser-enum-types.h.template \
+ gedit-file-browser-enum-types.c.template \
+ gedit-file-browser-enum-register.c.template \
+ gedit-file-browser-marshal.list
+
+CLEANFILES = \
+ $(plugin_DATA) \
+ $(schemas_DATA) \
+ $(BUILT_SOURCES)
+
+DISTCLEANFILES = \
+ $(plugin_DATA) \
+ $(schemas_DATA) \
+ $(BUILT_SOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/filebrowser/filebrowser.gedit-plugin.desktop.in b/plugins/filebrowser/filebrowser.gedit-plugin.desktop.in
new file mode 100755
index 00000000..808816c5
--- /dev/null
+++ b/plugins/filebrowser/filebrowser.gedit-plugin.desktop.in
@@ -0,0 +1,10 @@
+[Gedit Plugin]
+Loader=C
+Module=filebrowser
+IAge=2
+_Name=File Browser Pane
+_Description=Easy file access from the side pane
+Icon=system-file-manager
+Authors=Jesse van den Kieboom <[email protected]>
+Copyright=Copyright © 2006 Jesse van den Kieboom
+Website=http://www.gedit.org
diff --git a/plugins/filebrowser/gedit-file-bookmarks-store.c b/plugins/filebrowser/gedit-file-bookmarks-store.c
new file mode 100755
index 00000000..86e7f0c8
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-bookmarks-store.c
@@ -0,0 +1,879 @@
+/*
+ * gedit-file-bookmarks-store.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gedit/gedit-utils.h>
+#include <gedit/gedit-plugin.h>
+
+#include "gedit-file-bookmarks-store.h"
+#include "gedit-file-browser-utils.h"
+
+#define GEDIT_FILE_BOOKMARKS_STORE_GET_PRIVATE(object)( \
+ G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_FILE_BOOKMARKS_STORE, \
+ GeditFileBookmarksStorePrivate))
+
+struct _GeditFileBookmarksStorePrivate
+{
+ GVolumeMonitor * volume_monitor;
+ GFileMonitor * bookmarks_monitor;
+};
+
+static void remove_node (GtkTreeModel * model,
+ GtkTreeIter * iter);
+
+static void on_fs_changed (GVolumeMonitor *monitor,
+ GObject *object,
+ GeditFileBookmarksStore *model);
+
+static void on_bookmarks_file_changed (GFileMonitor * monitor,
+ GFile * file,
+ GFile * other_file,
+ GFileMonitorEvent event_type,
+ GeditFileBookmarksStore * model);
+static gboolean find_with_flags (GtkTreeModel * model,
+ GtkTreeIter * iter,
+ gpointer obj,
+ guint flags,
+ guint notflags);
+
+GEDIT_PLUGIN_DEFINE_TYPE(GeditFileBookmarksStore, gedit_file_bookmarks_store, GTK_TYPE_TREE_STORE)
+
+static void
+gedit_file_bookmarks_store_dispose (GObject * object)
+{
+ GeditFileBookmarksStore *obj = GEDIT_FILE_BOOKMARKS_STORE (object);
+
+ if (obj->priv->volume_monitor != NULL) {
+ g_signal_handlers_disconnect_by_func (obj->priv->volume_monitor,
+ on_fs_changed,
+ obj);
+
+ g_object_unref (obj->priv->volume_monitor);
+ obj->priv->volume_monitor = NULL;
+ }
+
+ if (obj->priv->bookmarks_monitor != NULL) {
+ g_object_unref (obj->priv->bookmarks_monitor);
+ obj->priv->bookmarks_monitor = NULL;
+ }
+
+ G_OBJECT_CLASS (gedit_file_bookmarks_store_parent_class)->dispose (object);
+}
+
+static void
+gedit_file_bookmarks_store_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (gedit_file_bookmarks_store_parent_class)->finalize (object);
+}
+
+static void
+gedit_file_bookmarks_store_class_init (GeditFileBookmarksStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gedit_file_bookmarks_store_dispose;
+ object_class->finalize = gedit_file_bookmarks_store_finalize;
+
+ g_type_class_add_private (object_class, sizeof (GeditFileBookmarksStorePrivate));
+}
+
+static void
+gedit_file_bookmarks_store_init (GeditFileBookmarksStore * obj)
+{
+ obj->priv = GEDIT_FILE_BOOKMARKS_STORE_GET_PRIVATE (obj);
+}
+
+/* Private */
+static void
+add_node (GeditFileBookmarksStore *model,
+ GdkPixbuf *pixbuf,
+ const gchar *name,
+ GObject *obj,
+ guint flags,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter newiter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (model), &newiter, NULL);
+
+ gtk_tree_store_set (GTK_TREE_STORE (model), &newiter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON, pixbuf,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME, name,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT, obj,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, flags,
+ -1);
+
+ if (iter != NULL)
+ *iter = newiter;
+}
+
+static gboolean
+add_file (GeditFileBookmarksStore *model,
+ GFile *file,
+ const gchar *name,
+ guint flags,
+ GtkTreeIter *iter)
+{
+ GdkPixbuf *pixbuf = NULL;
+ gboolean native;
+ gchar *newname;
+
+ native = g_file_is_native (file);
+
+ if (native && !g_file_query_exists (file, NULL)) {
+ return FALSE;
+ }
+
+ if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_HOME)
+ pixbuf = gedit_file_browser_utils_pixbuf_from_theme ("user-home", GTK_ICON_SIZE_MENU);
+ else if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_DESKTOP)
+ pixbuf = gedit_file_browser_utils_pixbuf_from_theme ("user-desktop", GTK_ICON_SIZE_MENU);
+ else if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT)
+ pixbuf = gedit_file_browser_utils_pixbuf_from_theme ("drive-harddisk", GTK_ICON_SIZE_MENU);
+
+ if (pixbuf == NULL) {
+ /* getting the icon is a sync get_info call, so we just do it for local files */
+ if (native) {
+ pixbuf = gedit_file_browser_utils_pixbuf_from_file (file, GTK_ICON_SIZE_MENU);
+ } else {
+ pixbuf = gedit_file_browser_utils_pixbuf_from_theme ("folder", GTK_ICON_SIZE_MENU);
+ }
+ }
+
+ if (name == NULL) {
+ newname = gedit_file_browser_utils_file_basename (file);
+ } else {
+ newname = g_strdup (name);
+ }
+
+ add_node (model, pixbuf, newname, G_OBJECT (file), flags, iter);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ g_free (newname);
+
+ return TRUE;
+}
+
+static void
+check_mount_separator (GeditFileBookmarksStore * model, guint flags,
+ gboolean added)
+{
+ GtkTreeIter iter;
+ gboolean found;
+
+ found =
+ find_with_flags (GTK_TREE_MODEL (model), &iter, NULL,
+ flags |
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR, 0);
+
+ if (added && !found) {
+ /* Add the separator */
+ add_node (model, NULL, NULL, NULL,
+ flags | GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR,
+ NULL);
+ } else if (!added && found) {
+ remove_node (GTK_TREE_MODEL (model), &iter);
+ }
+}
+
+static void
+init_special_directories (GeditFileBookmarksStore * model)
+{
+ gchar const *path;
+ GFile * file;
+
+ path = g_get_home_dir ();
+ if (path != NULL)
+ {
+ file = g_file_new_for_path (path);
+ add_file (model, file, NULL, GEDIT_FILE_BOOKMARKS_STORE_IS_HOME |
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR, NULL);
+ g_object_unref (file);
+ }
+
+ path = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
+ if (path != NULL)
+ {
+ file = g_file_new_for_path (path);
+ add_file (model, file, NULL, GEDIT_FILE_BOOKMARKS_STORE_IS_DESKTOP |
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR, NULL);
+ g_object_unref (file);
+ }
+
+ path = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+ if (path != NULL)
+ {
+ file = g_file_new_for_path (path);
+ add_file (model, file, NULL, GEDIT_FILE_BOOKMARKS_STORE_IS_DOCUMENTS |
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR, NULL);
+ g_object_unref (file);
+ }
+
+ file = g_file_new_for_uri ("file:///");
+ add_file (model, file, _("File System"), GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT, NULL);
+ g_object_unref (file);
+
+ check_mount_separator (model, GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT, TRUE);
+}
+
+static void
+get_fs_properties (gpointer fs,
+ gchar **name,
+ GdkPixbuf **pixbuf,
+ guint *flags)
+{
+ GIcon *icon = NULL;
+
+ *flags = GEDIT_FILE_BOOKMARKS_STORE_IS_FS;
+ *name = NULL;
+ *pixbuf = NULL;
+
+ if (G_IS_DRIVE (fs))
+ {
+ icon = g_drive_get_icon (G_DRIVE (fs));
+ *name = g_drive_get_name (G_DRIVE (fs));
+
+ *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_DRIVE;
+ }
+ else if (G_IS_VOLUME (fs))
+ {
+ icon = g_volume_get_icon (G_VOLUME (fs));
+ *name = g_volume_get_name (G_VOLUME (fs));
+
+ *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_VOLUME;
+ }
+ else if (G_IS_MOUNT (fs))
+ {
+ icon = g_mount_get_icon (G_MOUNT (fs));
+ *name = g_mount_get_name (G_MOUNT (fs));
+
+ *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_MOUNT;
+ }
+
+ if (icon)
+ {
+ *pixbuf = gedit_file_browser_utils_pixbuf_from_icon (icon, GTK_ICON_SIZE_MENU);
+ g_object_unref (icon);
+ }
+}
+
+
+static void
+add_fs (GeditFileBookmarksStore *model,
+ gpointer fs,
+ guint flags,
+ GtkTreeIter *iter)
+{
+ gchar *name;
+ GdkPixbuf *pixbuf;
+ guint fsflags;
+
+ get_fs_properties (fs, &name, &pixbuf, &fsflags);
+ add_node (model, pixbuf, name, fs, flags | fsflags, iter);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ g_free (name);
+ check_mount_separator (model, GEDIT_FILE_BOOKMARKS_STORE_IS_FS, TRUE);
+}
+
+static void
+process_volume_cb (GVolume *volume,
+ GeditFileBookmarksStore *model)
+{
+ GMount *mount;
+ guint flags = GEDIT_FILE_BOOKMARKS_STORE_NONE;
+ mount = g_volume_get_mount (volume);
+
+ /* CHECK: should we use the LOCAL/REMOTE thing still? */
+ if (mount)
+ {
+ /* Show mounted volume */
+ add_fs (model, mount, flags, NULL);
+ g_object_unref (mount);
+ }
+ else if (g_volume_can_mount (volume))
+ {
+ /* We also show the unmounted volume here so users can
+ mount it if they want to access it */
+ add_fs (model, volume, flags, NULL);
+ }
+}
+
+static void
+process_drive_novolumes (GeditFileBookmarksStore *model,
+ GDrive *drive)
+{
+ if (g_drive_is_media_removable (drive) &&
+ !g_drive_is_media_check_automatic (drive) &&
+ g_drive_can_poll_for_media (drive))
+ {
+ /* This can be the case for floppy drives or other
+ drives where media detection fails. We show the
+ drive and poll for media when the user activates
+ it */
+ add_fs (model, drive, GEDIT_FILE_BOOKMARKS_STORE_NONE, NULL);
+ }
+}
+
+static void
+process_drive_cb (GDrive *drive,
+ GeditFileBookmarksStore *model)
+{
+ GList *volumes;
+
+ volumes = g_drive_get_volumes (drive);
+
+ if (volumes)
+ {
+ /* Add all volumes for the drive */
+ g_list_foreach (volumes, (GFunc)process_volume_cb, model);
+ g_list_free (volumes);
+ }
+ else
+ {
+ process_drive_novolumes (model, drive);
+ }
+}
+
+static void
+init_drives (GeditFileBookmarksStore *model)
+{
+ GList *drives;
+
+ drives = g_volume_monitor_get_connected_drives (model->priv->volume_monitor);
+
+ g_list_foreach (drives, (GFunc)process_drive_cb, model);
+ g_list_foreach (drives, (GFunc)g_object_unref, NULL);
+ g_list_free (drives);
+}
+
+static void
+process_volume_nodrive_cb (GVolume *volume,
+ GeditFileBookmarksStore *model)
+{
+ GDrive *drive;
+
+ drive = g_volume_get_drive (volume);
+
+ if (drive)
+ {
+ g_object_unref (drive);
+ return;
+ }
+
+ process_volume_cb (volume, model);
+}
+
+static void
+init_volumes (GeditFileBookmarksStore *model)
+{
+ GList *volumes;
+
+ volumes = g_volume_monitor_get_volumes (model->priv->volume_monitor);
+
+ g_list_foreach (volumes, (GFunc)process_volume_nodrive_cb, model);
+ g_list_foreach (volumes, (GFunc)g_object_unref, NULL);
+ g_list_free (volumes);
+}
+
+static void
+process_mount_novolume_cb (GMount *mount,
+ GeditFileBookmarksStore *model)
+{
+ GVolume *volume;
+
+ volume = g_mount_get_volume (mount);
+
+ if (volume)
+ {
+ g_object_unref (volume);
+ }
+ else if (!g_mount_is_shadowed (mount))
+ {
+ /* Add the mount */
+ add_fs (model, mount, GEDIT_FILE_BOOKMARKS_STORE_NONE, NULL);
+ }
+}
+
+static void
+init_mounts (GeditFileBookmarksStore *model)
+{
+ GList *mounts;
+
+ mounts = g_volume_monitor_get_mounts (model->priv->volume_monitor);
+
+ g_list_foreach (mounts, (GFunc)process_mount_novolume_cb, model);
+ g_list_foreach (mounts, (GFunc)g_object_unref, NULL);
+ g_list_free (mounts);
+}
+
+static void
+init_fs (GeditFileBookmarksStore * model)
+{
+ if (model->priv->volume_monitor == NULL) {
+ const gchar **ptr;
+ const gchar *signals[] = {
+ "drive-connected", "drive-changed", "drive-disconnected",
+ "volume-added", "volume-removed", "volume-changed",
+ "mount-added", "mount-removed", "mount-changed",
+ NULL
+ };
+
+ model->priv->volume_monitor = g_volume_monitor_get ();
+
+ /* Connect signals */
+ for (ptr = signals; *ptr; ptr++)
+ {
+ g_signal_connect (model->priv->volume_monitor,
+ *ptr,
+ G_CALLBACK (on_fs_changed), model);
+ }
+ }
+
+ /* First go through all the connected drives */
+ init_drives (model);
+
+ /* Then add all volumes, not associated with a drive */
+ init_volumes (model);
+
+ /* Then finally add all mounts that have no volume */
+ init_mounts (model);
+}
+
+static gboolean
+add_bookmark (GeditFileBookmarksStore * model,
+ gchar const * name,
+ gchar const * uri)
+{
+ GFile * file;
+ gboolean ret;
+ guint flags = GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK;
+ GtkTreeIter iter;
+
+ file = g_file_new_for_uri (uri);
+
+ if (g_file_is_native (file)) {
+ flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_LOCAL_BOOKMARK;
+ } else {
+ flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_REMOTE_BOOKMARK;
+ }
+
+ ret = add_file (model, file, name, flags, &iter);
+
+ g_object_unref (file);
+
+ return ret;
+}
+
+static void
+init_bookmarks (GeditFileBookmarksStore * model)
+{
+ gchar *bookmarks;
+ GError *error = NULL;
+ gchar *contents;
+ gchar **lines;
+ gchar **line;
+ gboolean added = FALSE;
+
+ /* Read the bookmarks file */
+ bookmarks = g_build_filename (g_get_home_dir (),
+ ".gtk-bookmarks",
+ NULL);
+
+ if (g_file_get_contents (bookmarks, &contents, NULL, &error)) {
+ lines = g_strsplit (contents, "\n", 0);
+
+ for (line = lines; *line; ++line) {
+ if (**line) {
+ gchar *pos;
+ gchar *name;
+
+ /* CHECK: is this really utf8? */
+ pos = g_utf8_strchr (*line, -1, ' ');
+
+ if (pos != NULL) {
+ *pos = '\0';
+ name = pos + 1;
+ } else {
+ name = NULL;
+ }
+
+ /* the bookmarks file should contain valid
+ * URIs, but paranoia is good */
+ if (gedit_utils_is_valid_uri (*line)) {
+ added |= add_bookmark (model, name, *line);
+ }
+ }
+ }
+
+ g_strfreev (lines);
+ g_free (contents);
+
+ /* Add a watch */
+ if (model->priv->bookmarks_monitor == NULL) {
+ GFile * file;
+
+ file = g_file_new_for_path (bookmarks);
+ model->priv->bookmarks_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_object_unref (file);
+
+ g_signal_connect (model->priv->bookmarks_monitor,
+ "changed",
+ (GCallback)on_bookmarks_file_changed,
+ model);
+ }
+ } else {
+ /* The bookmarks file doesn't exist (which is perfectly fine) */
+ g_error_free (error);
+ }
+
+ if (added) {
+ /* Bookmarks separator */
+ add_node (model, NULL, NULL, NULL,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK |
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR, NULL);
+ }
+
+ g_free (bookmarks);
+}
+
+static gint flags_order[] = {
+ GEDIT_FILE_BOOKMARKS_STORE_IS_HOME,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_DESKTOP,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_FS,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK,
+ -1
+};
+
+static gint
+utf8_casecmp (gchar const *s1, const gchar * s2)
+{
+ gchar *n1;
+ gchar *n2;
+ gint result;
+
+ n1 = g_utf8_casefold (s1, -1);
+ n2 = g_utf8_casefold (s2, -1);
+
+ result = g_utf8_collate (n1, n2);
+
+ g_free (n1);
+ g_free (n2);
+
+ return result;
+}
+
+static gint
+bookmarks_compare_names (GtkTreeModel * model, GtkTreeIter * a,
+ GtkTreeIter * b)
+{
+ gchar *n1;
+ gchar *n2;
+ gint result;
+ guint f1;
+ guint f2;
+
+ gtk_tree_model_get (model, a,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME, &n1,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &f1,
+ -1);
+ gtk_tree_model_get (model, b,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME, &n2,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &f2,
+ -1);
+
+ /* do not sort actual bookmarks to keep same order as in caja */
+ if ((f1 & GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK) &&
+ (f2 & GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK))
+ result = 0;
+ else if (n1 == NULL && n2 == NULL)
+ result = 0;
+ else if (n1 == NULL)
+ result = -1;
+ else if (n2 == NULL)
+ result = 1;
+ else
+ result = utf8_casecmp (n1, n2);
+
+ g_free (n1);
+ g_free (n2);
+
+ return result;
+}
+
+static gint
+bookmarks_compare_flags (GtkTreeModel * model, GtkTreeIter * a,
+ GtkTreeIter * b)
+{
+ guint f1;
+ guint f2;
+ gint *flags;
+ guint sep;
+
+ sep = GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR;
+
+ gtk_tree_model_get (model, a,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &f1,
+ -1);
+ gtk_tree_model_get (model, b,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &f2,
+ -1);
+
+ for (flags = flags_order; *flags != -1; ++flags) {
+ if ((f1 & *flags) != (f2 & *flags)) {
+ if (f1 & *flags) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } else if ((f1 & *flags) && (f1 & sep) != (f2 & sep)) {
+ if (f1 & sep)
+ return -1;
+ else
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static gint
+bookmarks_compare_func (GtkTreeModel * model, GtkTreeIter * a,
+ GtkTreeIter * b, gpointer user_data)
+{
+ gint result;
+
+ result = bookmarks_compare_flags (model, a, b);
+
+ if (result == 0)
+ result = bookmarks_compare_names (model, a, b);
+
+ return result;
+}
+
+static gboolean
+find_with_flags (GtkTreeModel * model, GtkTreeIter * iter, gpointer obj,
+ guint flags, guint notflags)
+{
+ GtkTreeIter child;
+ guint childflags = 0;
+ GObject * childobj;
+ gboolean fequal;
+
+ if (!gtk_tree_model_get_iter_first (model, &child))
+ return FALSE;
+
+ do {
+ gtk_tree_model_get (model, &child,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT,
+ &childobj,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS,
+ &childflags, -1);
+
+ fequal = (obj == childobj);
+
+ if (childobj)
+ g_object_unref (childobj);
+
+ if ((obj == NULL || fequal) &&
+ (childflags & flags) == flags
+ && !(childflags & notflags)) {
+ *iter = child;
+ return TRUE;
+ }
+ } while (gtk_tree_model_iter_next (model, &child));
+
+ return FALSE;
+}
+
+static void
+remove_node (GtkTreeModel * model, GtkTreeIter * iter)
+{
+ guint flags;
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!(flags & GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR)) {
+ if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_FS) {
+ check_mount_separator (GEDIT_FILE_BOOKMARKS_STORE (model),
+ flags & GEDIT_FILE_BOOKMARKS_STORE_IS_FS,
+ FALSE);
+ }
+ }
+
+ gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
+}
+
+static void
+remove_bookmarks (GeditFileBookmarksStore * model)
+{
+ GtkTreeIter iter;
+
+ while (find_with_flags (GTK_TREE_MODEL (model), &iter, NULL,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK,
+ 0)) {
+ remove_node (GTK_TREE_MODEL (model), &iter);
+ }
+}
+
+static void
+initialize_fill (GeditFileBookmarksStore * model)
+{
+ init_special_directories (model);
+ init_fs (model);
+ init_bookmarks (model);
+}
+
+/* Public */
+GeditFileBookmarksStore *
+gedit_file_bookmarks_store_new (void)
+{
+ GeditFileBookmarksStore *model;
+ GType column_types[] = {
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_UINT
+ };
+
+ model = g_object_new (GEDIT_TYPE_FILE_BOOKMARKS_STORE, NULL);
+ gtk_tree_store_set_column_types (GTK_TREE_STORE (model),
+ GEDIT_FILE_BOOKMARKS_STORE_N_COLUMNS,
+ column_types);
+
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model),
+ bookmarks_compare_func,
+ NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+ GTK_SORT_ASCENDING);
+
+ initialize_fill (model);
+
+ return model;
+}
+
+gchar *
+gedit_file_bookmarks_store_get_uri (GeditFileBookmarksStore * model,
+ GtkTreeIter * iter)
+{
+ GObject * obj;
+ GFile * file = NULL;
+ guint flags;
+ gchar * ret = NULL;
+ gboolean isfs;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BOOKMARKS_STORE (model), NULL);
+ g_return_val_if_fail (iter != NULL, NULL);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS,
+ &flags,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT,
+ &obj,
+ -1);
+
+ if (obj == NULL)
+ return NULL;
+
+ isfs = (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_FS);
+
+ if (isfs && (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_MOUNT))
+ {
+ file = g_mount_get_root (G_MOUNT (obj));
+ }
+ else if (!isfs)
+ {
+ file = g_object_ref (obj);
+ }
+
+ g_object_unref (obj);
+
+ if (file)
+ {
+ ret = g_file_get_uri (file);
+ g_object_unref (file);
+ }
+
+ return ret;
+}
+
+void
+gedit_file_bookmarks_store_refresh (GeditFileBookmarksStore * model)
+{
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+ initialize_fill (model);
+}
+
+static void
+on_fs_changed (GVolumeMonitor *monitor,
+ GObject *object,
+ GeditFileBookmarksStore *model)
+{
+ GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
+ guint flags = GEDIT_FILE_BOOKMARKS_STORE_IS_FS;
+ guint noflags = GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR;
+ GtkTreeIter iter;
+
+ /* clear all fs items */
+ while (find_with_flags (tree_model, &iter, NULL, flags, noflags))
+ remove_node (tree_model, &iter);
+
+ /* then reinitialize */
+ init_fs (model);
+}
+
+static void
+on_bookmarks_file_changed (GFileMonitor * monitor,
+ GFile * file,
+ GFile * other_file,
+ GFileMonitorEvent event_type,
+ GeditFileBookmarksStore * model)
+{
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ case G_FILE_MONITOR_EVENT_CREATED:
+ /* Re-initialize bookmarks */
+ remove_bookmarks (model);
+ init_bookmarks (model);
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED: // FIXME: shouldn't we also monitor the directory?
+ /* Remove bookmarks */
+ remove_bookmarks (model);
+ g_object_unref (monitor);
+ model->priv->bookmarks_monitor = NULL;
+ break;
+ default:
+ break;
+ }
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-bookmarks-store.h b/plugins/filebrowser/gedit-file-bookmarks-store.h
new file mode 100755
index 00000000..bd20911e
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-bookmarks-store.h
@@ -0,0 +1,90 @@
+/*
+ * gedit-file-bookmarks-store.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BOOKMARKS_STORE_H__
+#define __GEDIT_FILE_BOOKMARKS_STORE_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define GEDIT_TYPE_FILE_BOOKMARKS_STORE (gedit_file_bookmarks_store_get_type ())
+#define GEDIT_FILE_BOOKMARKS_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BOOKMARKS_STORE, GeditFileBookmarksStore))
+#define GEDIT_FILE_BOOKMARKS_STORE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BOOKMARKS_STORE, GeditFileBookmarksStore const))
+#define GEDIT_FILE_BOOKMARKS_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FILE_BOOKMARKS_STORE, GeditFileBookmarksStoreClass))
+#define GEDIT_IS_FILE_BOOKMARKS_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FILE_BOOKMARKS_STORE))
+#define GEDIT_IS_FILE_BOOKMARKS_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FILE_BOOKMARKS_STORE))
+#define GEDIT_FILE_BOOKMARKS_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FILE_BOOKMARKS_STORE, GeditFileBookmarksStoreClass))
+
+typedef struct _GeditFileBookmarksStore GeditFileBookmarksStore;
+typedef struct _GeditFileBookmarksStoreClass GeditFileBookmarksStoreClass;
+typedef struct _GeditFileBookmarksStorePrivate GeditFileBookmarksStorePrivate;
+
+enum
+{
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON = 0,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS,
+ GEDIT_FILE_BOOKMARKS_STORE_N_COLUMNS
+};
+
+enum
+{
+ GEDIT_FILE_BOOKMARKS_STORE_NONE = 0,
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR = 1 << 0, /* Separator item */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR = 1 << 1, /* Special user dir */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_HOME = 1 << 2, /* The special Home user directory */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_DESKTOP = 1 << 3, /* The special Desktop user directory */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_DOCUMENTS = 1 << 4, /* The special Documents user directory */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_FS = 1 << 5, /* A mount object */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_MOUNT = 1 << 6, /* A mount object */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_VOLUME = 1 << 7, /* A volume object */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_DRIVE = 1 << 8, /* A drive object */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT = 1 << 9, /* The root file system (file:///) */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK = 1 << 10, /* A gtk bookmark */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_REMOTE_BOOKMARK = 1 << 11, /* A remote gtk bookmark */
+ GEDIT_FILE_BOOKMARKS_STORE_IS_LOCAL_BOOKMARK = 1 << 12 /* A local gtk bookmark */
+};
+
+struct _GeditFileBookmarksStore
+{
+ GtkTreeStore parent;
+
+ GeditFileBookmarksStorePrivate *priv;
+};
+
+struct _GeditFileBookmarksStoreClass
+{
+ GtkTreeStoreClass parent_class;
+};
+
+GType gedit_file_bookmarks_store_get_type (void) G_GNUC_CONST;
+GType gedit_file_bookmarks_store_register_type (GTypeModule * module);
+
+GeditFileBookmarksStore *gedit_file_bookmarks_store_new (void);
+gchar *gedit_file_bookmarks_store_get_uri (GeditFileBookmarksStore * model,
+ GtkTreeIter * iter);
+void gedit_file_bookmarks_store_refresh (GeditFileBookmarksStore * model);
+
+G_END_DECLS
+#endif /* __GEDIT_FILE_BOOKMARKS_STORE_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-enum-register.c.template b/plugins/filebrowser/gedit-file-browser-enum-register.c.template
new file mode 100755
index 00000000..63a9c562
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-enum-register.c.template
@@ -0,0 +1,20 @@
+/*** BEGIN file-header ***/
+void
+gedit_file_browser_enum_and_flag_register_type (GTypeModule * module)
+{
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+ /* Enumerations from "@filename@" */
+
+/*** END file-production ***/
+
+/*** BEGIN enumeration-production ***/
+ register_@enum_name@ (module);
+
+/*** END enumeration-production ***/
+
+/*** BEGIN file-tail ***/
+}
+
+/*** END file-tail ***/
diff --git a/plugins/filebrowser/gedit-file-browser-enum-types.c.template b/plugins/filebrowser/gedit-file-browser-enum-types.c.template
new file mode 100755
index 00000000..4e89370d
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-enum-types.c.template
@@ -0,0 +1,45 @@
+/*** BEGIN file-header ***/
+#include "gedit-file-browser-enum-types.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+static GType @enum_name@_type = 0;
+
+static GType
+register_@enum_name@ (GTypeModule *module)
+{
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@,
+ "@VALUENAME@",
+ "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ @enum_name@_type =
+ g_type_module_register_@type@ (module,
+ "@EnumName@",
+ values);
+
+ return @enum_name@_type;
+}
+
+GType
+@enum_name@_get_type (void)
+{
+ return @enum_name@_type;
+}
+
+/*** END value-tail ***/
diff --git a/plugins/filebrowser/gedit-file-browser-enum-types.h.template b/plugins/filebrowser/gedit-file-browser-enum-types.h.template
new file mode 100755
index 00000000..aea4fad9
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-enum-types.h.template
@@ -0,0 +1,29 @@
+/*** BEGIN file-header ***/
+#ifndef __GEDIT_FILE_BROWSER_ENUM_TYPES_H__
+#define __GEDIT_FILE_BROWSER_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* Enumerations from "@filename@" */
+
+/*** END file-production ***/
+
+/*** BEGIN enumeration-production ***/
+#define GEDIT_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+
+/*** END enumeration-production ***/
+
+/*** BEGIN file-tail ***/
+void gedit_file_browser_enum_and_flag_register_type (GTypeModule * module);
+
+G_END_DECLS
+
+#endif /* __GEDIT_FILE_BROWSER_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
diff --git a/plugins/filebrowser/gedit-file-browser-error.h b/plugins/filebrowser/gedit-file-browser-error.h
new file mode 100755
index 00000000..ec5b8618
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-error.h
@@ -0,0 +1,41 @@
+/*
+ * gedit-file-browser-error.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_ERROR_H__
+#define __GEDIT_FILE_BROWSER_ERROR_H__
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GEDIT_FILE_BROWSER_ERROR_NONE,
+ GEDIT_FILE_BROWSER_ERROR_RENAME,
+ GEDIT_FILE_BROWSER_ERROR_DELETE,
+ GEDIT_FILE_BROWSER_ERROR_NEW_FILE,
+ GEDIT_FILE_BROWSER_ERROR_NEW_DIRECTORY,
+ GEDIT_FILE_BROWSER_ERROR_OPEN_DIRECTORY,
+ GEDIT_FILE_BROWSER_ERROR_SET_ROOT,
+ GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY,
+ GEDIT_FILE_BROWSER_ERROR_NUM
+} GeditFileBrowserError;
+
+G_END_DECLS
+
+#endif /* __GEDIT_FILE_BROWSER_ERROR_H__ */
diff --git a/plugins/filebrowser/gedit-file-browser-marshal.list b/plugins/filebrowser/gedit-file-browser-marshal.list
new file mode 100755
index 00000000..5fa72c8b
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-marshal.list
@@ -0,0 +1,5 @@
+VOID:UINT,STRING
+VOID:STRING,STRING
+BOOL:OBJECT,POINTER
+BOOL:POINTER
+BOOL:VOID
diff --git a/plugins/filebrowser/gedit-file-browser-messages.c b/plugins/filebrowser/gedit-file-browser-messages.c
new file mode 100755
index 00000000..b587edf1
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-messages.c
@@ -0,0 +1,1033 @@
+#include "gedit-file-browser-messages.h"
+#include "gedit-file-browser-store.h"
+#include <gedit/gedit-message.h>
+
+#define MESSAGE_OBJECT_PATH "/plugins/filebrowser"
+#define WINDOW_DATA_KEY "GeditFileBrowserMessagesWindowData"
+
+#define BUS_CONNECT(bus, name, data) gedit_message_bus_connect(bus, MESSAGE_OBJECT_PATH, #name, (GeditMessageCallback) message_##name##_cb, data, NULL)
+
+typedef struct
+{
+ GeditWindow *window;
+ GeditMessage *message;
+} MessageCacheData;
+
+typedef struct
+{
+ guint row_inserted_id;
+ guint row_deleted_id;
+ guint root_changed_id;
+ guint begin_loading_id;
+ guint end_loading_id;
+
+ GList *merge_ids;
+ GtkActionGroup *merged_actions;
+
+ GeditMessageBus *bus;
+ GeditFileBrowserWidget *widget;
+ GHashTable *row_tracking;
+
+ GHashTable *filters;
+} WindowData;
+
+typedef struct
+{
+ gulong id;
+
+ GeditWindow *window;
+ GeditMessage *message;
+} FilterData;
+
+static WindowData *
+window_data_new (GeditWindow *window,
+ GeditFileBrowserWidget *widget)
+{
+ WindowData *data = g_slice_new (WindowData);
+ GtkUIManager *manager;
+ GList *groups;
+
+ data->bus = gedit_window_get_message_bus (window);
+ data->widget = widget;
+ data->row_tracking = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)gtk_tree_row_reference_free);
+
+ data->filters = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ NULL);
+
+ manager = gedit_file_browser_widget_get_ui_manager (widget);
+
+ data->merge_ids = NULL;
+ data->merged_actions = gtk_action_group_new ("MessageMergedActions");
+
+ groups = gtk_ui_manager_get_action_groups (manager);
+ gtk_ui_manager_insert_action_group (manager, data->merged_actions, g_list_length (groups));
+
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, data);
+
+ return data;
+}
+
+static WindowData *
+get_window_data (GeditWindow * window)
+{
+ return (WindowData *) (g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY));
+}
+
+static void
+window_data_free (GeditWindow *window)
+{
+ WindowData *data = get_window_data (window);
+ GtkUIManager *manager;
+ GList *item;
+
+ g_hash_table_destroy (data->row_tracking);
+ g_hash_table_destroy (data->filters);
+
+ manager = gedit_file_browser_widget_get_ui_manager (data->widget);
+ gtk_ui_manager_remove_action_group (manager, data->merged_actions);
+
+ for (item = data->merge_ids; item; item = item->next)
+ gtk_ui_manager_remove_ui (manager, GPOINTER_TO_INT (item->data));
+
+ g_list_free (data->merge_ids);
+ g_object_unref (data->merged_actions);
+
+ g_slice_free (WindowData, data);
+
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
+}
+
+static FilterData *
+filter_data_new (GeditWindow *window,
+ GeditMessage *message)
+{
+ FilterData *data = g_slice_new (FilterData);
+ WindowData *wdata;
+
+ data->window = window;
+ data->id = 0;
+ data->message = message;
+
+ wdata = get_window_data (window);
+
+ g_hash_table_insert (wdata->filters,
+ gedit_message_type_identifier (gedit_message_get_object_path (message),
+ gedit_message_get_method (message)),
+ data);
+
+ return data;
+}
+
+static void
+filter_data_free (FilterData *data)
+{
+ WindowData *wdata = get_window_data (data->window);
+ gchar *identifier;
+
+ identifier = gedit_message_type_identifier (gedit_message_get_object_path (data->message),
+ gedit_message_get_method (data->message));
+
+ g_hash_table_remove (wdata->filters, identifier);
+ g_free (identifier);
+
+ g_object_unref (data->message);
+ g_slice_free (FilterData, data);
+}
+
+static GtkTreePath *
+track_row_lookup (WindowData *data,
+ const gchar *id)
+{
+ GtkTreeRowReference *ref;
+
+ ref = (GtkTreeRowReference *)g_hash_table_lookup (data->row_tracking, id);
+
+ if (!ref)
+ return NULL;
+
+ return gtk_tree_row_reference_get_path (ref);
+}
+
+static void
+message_cache_data_free (MessageCacheData *data)
+{
+ g_object_unref (data->message);
+ g_slice_free (MessageCacheData, data);
+}
+
+static MessageCacheData *
+message_cache_data_new (GeditWindow *window,
+ GeditMessage *message)
+{
+ MessageCacheData *data = g_slice_new (MessageCacheData);
+
+ data->window = window;
+ data->message = message;
+
+ return data;
+}
+
+static void
+message_get_root_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ GeditFileBrowserStore *store;
+ gchar *uri;
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+ uri = gedit_file_browser_store_get_virtual_root (store);
+
+ gedit_message_set (message, "uri", uri, NULL);
+ g_free (uri);
+}
+
+static void
+message_set_root_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gchar *root = NULL;
+ gchar *virtual = NULL;
+
+ gedit_message_get (message, "uri", &root, NULL);
+
+ if (!root)
+ return;
+
+ if (gedit_message_has_key (message, "virtual"))
+ gedit_message_get (message, "virtual", &virtual, NULL);
+
+ if (virtual)
+ gedit_file_browser_widget_set_root_and_virtual_root (data->widget, root, virtual);
+ else
+ gedit_file_browser_widget_set_root (data->widget, root, TRUE);
+
+ g_free (root);
+ g_free (virtual);
+}
+
+static void
+message_set_emblem_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gchar *id = NULL;
+ gchar *emblem = NULL;
+ GtkTreePath *path;
+ GeditFileBrowserStore *store;
+
+ gedit_message_get (message, "id", &id, "emblem", &emblem, NULL);
+
+ if (!id || !emblem)
+ {
+ g_free (id);
+ g_free (emblem);
+
+ return;
+ }
+
+ path = track_row_lookup (data, id);
+
+ if (path != NULL)
+ {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ emblem,
+ 10,
+ 0,
+ &error);
+
+ if (pixbuf)
+ {
+ GValue value = { 0, };
+ GtkTreeIter iter;
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path))
+ {
+ g_value_init (&value, GDK_TYPE_PIXBUF);
+ g_value_set_object (&value, pixbuf);
+
+ gedit_file_browser_store_set_value (store,
+ &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM,
+ &value);
+
+ g_value_unset (&value);
+ }
+
+ g_object_unref (pixbuf);
+ }
+
+ if (error)
+ g_error_free (error);
+ }
+
+ g_free (id);
+ g_free (emblem);
+}
+
+static gchar *
+item_id (const gchar *path,
+ const gchar *uri)
+{
+ return g_strconcat (path, "::", uri, NULL);
+}
+
+static gchar *
+track_row (WindowData *data,
+ GeditFileBrowserStore *store,
+ GtkTreePath *path,
+ const gchar *uri)
+{
+ GtkTreeRowReference *ref;
+ gchar *id;
+ gchar *pathstr;
+
+ pathstr = gtk_tree_path_to_string (path);
+ id = item_id (pathstr, uri);
+
+ ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
+ g_hash_table_insert (data->row_tracking, g_strdup (id), ref);
+
+ g_free (pathstr);
+
+ return id;
+}
+
+static void
+set_item_message (WindowData *data,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ GeditMessage *message)
+{
+ GeditFileBrowserStore *store;
+ gchar *uri = NULL;
+ guint flags = 0;
+ gchar *track_id;
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!uri)
+ return;
+
+ if (path && gtk_tree_path_get_depth (path) != 0)
+ track_id = track_row (data, store, path, uri);
+ else
+ track_id = NULL;
+
+ gedit_message_set (message,
+ "id", track_id,
+ "uri", uri,
+ NULL);
+
+ if (gedit_message_has_key (message, "is_directory"))
+ {
+ gedit_message_set (message,
+ "is_directory", FILE_IS_DIR (flags),
+ NULL);
+ }
+
+ g_free (uri);
+ g_free (track_id);
+}
+
+static gboolean
+custom_message_filter_func (GeditFileBrowserWidget *widget,
+ GeditFileBrowserStore *store,
+ GtkTreeIter *iter,
+ FilterData *data)
+{
+ WindowData *wdata = get_window_data (data->window);
+ gchar *uri = NULL;
+ guint flags = 0;
+ gboolean filter = FALSE;
+ GtkTreePath *path;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!uri || FILE_IS_DUMMY (flags))
+ {
+ g_free (uri);
+ return FALSE;
+ }
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
+ set_item_message (wdata, iter, path, data->message);
+ gtk_tree_path_free (path);
+
+ gedit_message_set (data->message, "filter", filter, NULL);
+
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+ gedit_message_get (data->message, "filter", &filter, NULL);
+
+ return !filter;
+}
+
+static void
+message_add_filter_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ GeditWindow *window)
+{
+ gchar *object_path = NULL;
+ gchar *method = NULL;
+ gulong id;
+ GeditMessageType *message_type;
+ GeditMessage *cbmessage;
+ FilterData *filter_data;
+ WindowData *data = get_window_data (window);
+
+ gedit_message_get (message,
+ "object_path", &object_path,
+ "method", &method,
+ NULL);
+
+ // Check if there exists such a 'callback' message
+ if (!object_path || !method)
+ {
+ g_free (object_path);
+ g_free (method);
+
+ return;
+ }
+
+ message_type = gedit_message_bus_lookup (bus, object_path, method);
+
+ if (!message_type)
+ {
+ g_free (object_path);
+ g_free (method);
+
+ return;
+ }
+
+ // Check if the message type has the correct arguments
+ if (gedit_message_type_lookup (message_type, "id") != G_TYPE_STRING ||
+ gedit_message_type_lookup (message_type, "uri") != G_TYPE_STRING ||
+ gedit_message_type_lookup (message_type, "is_directory") != G_TYPE_BOOLEAN ||
+ gedit_message_type_lookup (message_type, "filter") != G_TYPE_BOOLEAN)
+ {
+ return;
+ }
+
+ cbmessage = gedit_message_type_instantiate (message_type,
+ "id", NULL,
+ "uri", NULL,
+ "is_directory", FALSE,
+ "filter", FALSE,
+ NULL);
+
+ // Register the custom filter on the widget
+ filter_data = filter_data_new (window, cbmessage);
+ id = gedit_file_browser_widget_add_filter (data->widget,
+ (GeditFileBrowserWidgetFilterFunc)custom_message_filter_func,
+ filter_data,
+ (GDestroyNotify)filter_data_free);
+
+ filter_data->id = id;
+}
+
+static void
+message_remove_filter_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gulong id = 0;
+
+ gedit_message_get (message, "id", &id, NULL);
+
+ if (!id)
+ return;
+
+ gedit_file_browser_widget_remove_filter (data->widget, id);
+}
+
+static void
+message_up_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ GeditFileBrowserStore *store = gedit_file_browser_widget_get_browser_store (data->widget);
+
+ gedit_file_browser_store_set_virtual_root_up (store);
+}
+
+static void
+message_history_back_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gedit_file_browser_widget_history_back (data->widget);
+}
+
+static void
+message_history_forward_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gedit_file_browser_widget_history_forward (data->widget);
+}
+
+static void
+message_refresh_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gedit_file_browser_widget_refresh (data->widget);
+}
+
+static void
+message_set_show_hidden_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gboolean active = FALSE;
+ GeditFileBrowserStore *store;
+ GeditFileBrowserStoreFilterMode mode;
+
+ gedit_message_get (message, "active", &active, NULL);
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+ mode = gedit_file_browser_store_get_filter_mode (store);
+
+ if (active)
+ mode &= ~GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
+ else
+ mode |= GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
+
+ gedit_file_browser_store_set_filter_mode (store, mode);
+}
+
+static void
+message_set_show_binary_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gboolean active = FALSE;
+ GeditFileBrowserStore *store;
+ GeditFileBrowserStoreFilterMode mode;
+
+ gedit_message_get (message, "active", &active, NULL);
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+ mode = gedit_file_browser_store_get_filter_mode (store);
+
+ if (active)
+ mode &= ~GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY;
+ else
+ mode |= GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY;
+
+ gedit_file_browser_store_set_filter_mode (store, mode);
+}
+
+static void
+message_show_bookmarks_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gedit_file_browser_widget_show_bookmarks (data->widget);
+}
+
+static void
+message_show_files_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ gedit_file_browser_widget_show_files (data->widget);
+}
+
+static void
+message_add_context_item_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ GtkAction *action = NULL;
+ gchar *path = NULL;
+ gchar *name;
+ GtkUIManager *manager;
+ guint merge_id;
+
+ gedit_message_get (message,
+ "action", &action,
+ "path", &path,
+ NULL);
+
+ if (!action || !path)
+ {
+ if (action)
+ g_object_unref (action);
+
+ g_free (path);
+ return;
+ }
+
+ gtk_action_group_add_action (data->merged_actions, action);
+ manager = gedit_file_browser_widget_get_ui_manager (data->widget);
+ name = g_strconcat (gtk_action_get_name (action), "MenuItem", NULL);
+ merge_id = gtk_ui_manager_new_merge_id (manager);
+
+ gtk_ui_manager_add_ui (manager,
+ merge_id,
+ path,
+ name,
+ gtk_action_get_name (action),
+ GTK_UI_MANAGER_AUTO,
+ FALSE);
+
+ if (gtk_ui_manager_get_widget (manager, path))
+ {
+ data->merge_ids = g_list_prepend (data->merge_ids, GINT_TO_POINTER (merge_id));
+ gedit_message_set (message, "id", merge_id, NULL);
+ }
+ else
+ {
+ gedit_message_set (message, "id", 0, NULL);
+ }
+
+ g_object_unref (action);
+ g_free (path);
+ g_free (name);
+}
+
+static void
+message_remove_context_item_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ guint merge_id = 0;
+ GtkUIManager *manager;
+
+ gedit_message_get (message, "id", &merge_id, NULL);
+
+ if (merge_id == 0)
+ return;
+
+ manager = gedit_file_browser_widget_get_ui_manager (data->widget);
+
+ data->merge_ids = g_list_remove (data->merge_ids, GINT_TO_POINTER (merge_id));
+ gtk_ui_manager_remove_ui (manager, merge_id);
+}
+
+static void
+message_get_view_cb (GeditMessageBus *bus,
+ GeditMessage *message,
+ WindowData *data)
+{
+ GeditFileBrowserView *view;
+ view = gedit_file_browser_widget_get_browser_view (data->widget);
+
+ gedit_message_set (message, "view", view, NULL);
+}
+
+static void
+register_methods (GeditWindow *window,
+ GeditFileBrowserWidget *widget)
+{
+ GeditMessageBus *bus = gedit_window_get_message_bus (window);
+ WindowData *data = get_window_data (window);
+
+ /* Register method calls */
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "get_root",
+ 1,
+ "uri", G_TYPE_STRING,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "set_root",
+ 1,
+ "uri", G_TYPE_STRING,
+ "virtual", G_TYPE_STRING,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "set_emblem",
+ 0,
+ "id", G_TYPE_STRING,
+ "emblem", G_TYPE_STRING,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "add_filter",
+ 1,
+ "object_path", G_TYPE_STRING,
+ "method", G_TYPE_STRING,
+ "id", G_TYPE_ULONG,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "remove_filter",
+ 0,
+ "id", G_TYPE_ULONG,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "add_context_item",
+ 1,
+ "action", GTK_TYPE_ACTION,
+ "path", G_TYPE_STRING,
+ "id", G_TYPE_UINT,
+ NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "remove_context_item",
+ 0,
+ "id", G_TYPE_UINT,
+ NULL);
+
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "up", 0, NULL);
+
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "history_back", 0, NULL);
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "history_forward", 0, NULL);
+
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "refresh", 0, NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "set_show_hidden",
+ 0,
+ "active", G_TYPE_BOOLEAN,
+ NULL);
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "set_show_binary",
+ 0,
+ "active", G_TYPE_BOOLEAN,
+ NULL);
+
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "show_bookmarks", 0, NULL);
+ gedit_message_bus_register (bus, MESSAGE_OBJECT_PATH, "show_files", 0, NULL);
+
+ gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "get_view",
+ 1,
+ "view", GEDIT_TYPE_FILE_BROWSER_VIEW,
+ NULL);
+
+ BUS_CONNECT (bus, get_root, data);
+ BUS_CONNECT (bus, set_root, data);
+ BUS_CONNECT (bus, set_emblem, data);
+ BUS_CONNECT (bus, add_filter, window);
+ BUS_CONNECT (bus, remove_filter, data);
+
+ BUS_CONNECT (bus, add_context_item, data);
+ BUS_CONNECT (bus, remove_context_item, data);
+
+ BUS_CONNECT (bus, up, data);
+ BUS_CONNECT (bus, history_back, data);
+ BUS_CONNECT (bus, history_forward, data);
+
+ BUS_CONNECT (bus, refresh, data);
+
+ BUS_CONNECT (bus, set_show_hidden, data);
+ BUS_CONNECT (bus, set_show_binary, data);
+
+ BUS_CONNECT (bus, show_bookmarks, data);
+ BUS_CONNECT (bus, show_files, data);
+
+ BUS_CONNECT (bus, get_view, data);
+}
+
+static void
+store_row_inserted (GeditFileBrowserStore *store,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ MessageCacheData *data)
+{
+ gchar *uri = NULL;
+ guint flags = 0;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!FILE_IS_DUMMY (flags) && !FILE_IS_FILTERED (flags))
+ {
+ WindowData *wdata = get_window_data (data->window);
+
+ set_item_message (wdata, iter, path, data->message);
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+ }
+
+ g_free (uri);
+}
+
+static void
+store_row_deleted (GeditFileBrowserStore *store,
+ GtkTreePath *path,
+ MessageCacheData *data)
+{
+ GtkTreeIter iter;
+ gchar *uri = NULL;
+ guint flags = 0;
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!FILE_IS_DUMMY (flags) && !FILE_IS_FILTERED (flags))
+ {
+ WindowData *wdata = get_window_data (data->window);
+
+ set_item_message (wdata, &iter, path, data->message);
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+ }
+
+ g_free (uri);
+}
+
+static void
+store_virtual_root_changed (GeditFileBrowserStore *store,
+ GParamSpec *spec,
+ MessageCacheData *data)
+{
+ WindowData *wdata = get_window_data (data->window);
+ gchar *uri;
+
+ uri = gedit_file_browser_store_get_virtual_root (store);
+
+ if (!uri)
+ return;
+
+ gedit_message_set (data->message,
+ "uri", uri,
+ NULL);
+
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+
+ g_free (uri);
+}
+
+static void
+store_begin_loading (GeditFileBrowserStore *store,
+ GtkTreeIter *iter,
+ MessageCacheData *data)
+{
+ GtkTreePath *path;
+ WindowData *wdata = get_window_data (data->window);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
+
+ set_item_message (wdata, iter, path, data->message);
+
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+ gtk_tree_path_free (path);
+}
+
+static void
+store_end_loading (GeditFileBrowserStore *store,
+ GtkTreeIter *iter,
+ MessageCacheData *data)
+{
+ GtkTreePath *path;
+ WindowData *wdata = get_window_data (data->window);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
+
+ set_item_message (wdata, iter, path, data->message);
+
+ gedit_message_bus_send_message_sync (wdata->bus, data->message);
+ gtk_tree_path_free (path);
+}
+
+static void
+register_signals (GeditWindow *window,
+ GeditFileBrowserWidget *widget)
+{
+ GeditMessageBus *bus = gedit_window_get_message_bus (window);
+ GeditFileBrowserStore *store;
+ GeditMessageType *inserted_type;
+ GeditMessageType *deleted_type;
+ GeditMessageType *begin_loading_type;
+ GeditMessageType *end_loading_type;
+ GeditMessageType *root_changed_type;
+
+ GeditMessage *message;
+ WindowData *data;
+
+ /* Register signals */
+ root_changed_type = gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "root_changed",
+ 0,
+ "id", G_TYPE_STRING,
+ "uri", G_TYPE_STRING,
+ NULL);
+
+ begin_loading_type = gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "begin_loading",
+ 0,
+ "id", G_TYPE_STRING,
+ "uri", G_TYPE_STRING,
+ NULL);
+
+ end_loading_type = gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "end_loading",
+ 0,
+ "id", G_TYPE_STRING,
+ "uri", G_TYPE_STRING,
+ NULL);
+
+ inserted_type = gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "inserted",
+ 0,
+ "id", G_TYPE_STRING,
+ "uri", G_TYPE_STRING,
+ "is_directory", G_TYPE_BOOLEAN,
+ NULL);
+
+ deleted_type = gedit_message_bus_register (bus,
+ MESSAGE_OBJECT_PATH, "deleted",
+ 0,
+ "id", G_TYPE_STRING,
+ "uri", G_TYPE_STRING,
+ "is_directory", G_TYPE_BOOLEAN,
+ NULL);
+
+ store = gedit_file_browser_widget_get_browser_store (widget);
+
+ message = gedit_message_type_instantiate (inserted_type,
+ "id", NULL,
+ "uri", NULL,
+ "is_directory", FALSE,
+ NULL);
+
+ data = get_window_data (window);
+
+ data->row_inserted_id =
+ g_signal_connect_data (store,
+ "row-inserted",
+ G_CALLBACK (store_row_inserted),
+ message_cache_data_new (window, message),
+ (GClosureNotify)message_cache_data_free,
+ 0);
+
+ message = gedit_message_type_instantiate (deleted_type,
+ "id", NULL,
+ "uri", NULL,
+ "is_directory", FALSE,
+ NULL);
+ data->row_deleted_id =
+ g_signal_connect_data (store,
+ "row-deleted",
+ G_CALLBACK (store_row_deleted),
+ message_cache_data_new (window, message),
+ (GClosureNotify)message_cache_data_free,
+ 0);
+
+ message = gedit_message_type_instantiate (root_changed_type,
+ "id", NULL,
+ "uri", NULL,
+ NULL);
+ data->root_changed_id =
+ g_signal_connect_data (store,
+ "notify::virtual-root",
+ G_CALLBACK (store_virtual_root_changed),
+ message_cache_data_new (window, message),
+ (GClosureNotify)message_cache_data_free,
+ 0);
+
+ message = gedit_message_type_instantiate (begin_loading_type,
+ "id", NULL,
+ "uri", NULL,
+ NULL);
+ data->begin_loading_id =
+ g_signal_connect_data (store,
+ "begin_loading",
+ G_CALLBACK (store_begin_loading),
+ message_cache_data_new (window, message),
+ (GClosureNotify)message_cache_data_free,
+ 0);
+
+ message = gedit_message_type_instantiate (end_loading_type,
+ "id", NULL,
+ "uri", NULL,
+ NULL);
+ data->end_loading_id =
+ g_signal_connect_data (store,
+ "end_loading",
+ G_CALLBACK (store_end_loading),
+ message_cache_data_new (window, message),
+ (GClosureNotify)message_cache_data_free,
+ 0);
+}
+
+static void
+message_unregistered (GeditMessageBus *bus,
+ GeditMessageType *message_type,
+ GeditWindow *window)
+{
+ gchar *identifier = gedit_message_type_identifier (gedit_message_type_get_object_path (message_type),
+ gedit_message_type_get_method (message_type));
+ FilterData *data;
+ WindowData *wdata = get_window_data (window);
+
+ data = g_hash_table_lookup (wdata->filters, identifier);
+
+ if (data)
+ gedit_file_browser_widget_remove_filter (wdata->widget, data->id);
+
+ g_free (identifier);
+}
+
+void
+gedit_file_browser_messages_register (GeditWindow *window,
+ GeditFileBrowserWidget *widget)
+{
+ window_data_new (window, widget);
+
+ register_methods (window, widget);
+ register_signals (window, widget);
+
+ g_signal_connect (gedit_window_get_message_bus (window),
+ "unregistered",
+ G_CALLBACK (message_unregistered),
+ window);
+}
+
+static void
+cleanup_signals (GeditWindow *window)
+{
+ WindowData *data = get_window_data (window);
+ GeditFileBrowserStore *store;
+
+ store = gedit_file_browser_widget_get_browser_store (data->widget);
+
+ g_signal_handler_disconnect (store, data->row_inserted_id);
+ g_signal_handler_disconnect (store, data->row_deleted_id);
+ g_signal_handler_disconnect (store, data->root_changed_id);
+ g_signal_handler_disconnect (store, data->begin_loading_id);
+ g_signal_handler_disconnect (store, data->end_loading_id);
+
+ g_signal_handlers_disconnect_by_func (data->bus, message_unregistered, window);
+}
+
+void
+gedit_file_browser_messages_unregister (GeditWindow *window)
+{
+ GeditMessageBus *bus = gedit_window_get_message_bus (window);
+
+ cleanup_signals (window);
+ gedit_message_bus_unregister_all (bus, MESSAGE_OBJECT_PATH);
+
+ window_data_free (window);
+}
diff --git a/plugins/filebrowser/gedit-file-browser-messages.h b/plugins/filebrowser/gedit-file-browser-messages.h
new file mode 100755
index 00000000..e62094e8
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-messages.h
@@ -0,0 +1,35 @@
+/*
+ * gedit-file-browser-messages.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2008 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_MESSAGES_H__
+#define __GEDIT_FILE_BROWSER_MESSAGES_H__
+
+#include <gedit/gedit-window.h>
+#include <gedit/gedit-message-bus.h>
+#include "gedit-file-browser-widget.h"
+
+void gedit_file_browser_messages_register (GeditWindow *window,
+ GeditFileBrowserWidget *widget);
+void gedit_file_browser_messages_unregister (GeditWindow *window);
+
+#endif /* __GEDIT_FILE_BROWSER_MESSAGES_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-plugin.c b/plugins/filebrowser/gedit-file-browser-plugin.c
new file mode 100755
index 00000000..f2da19f5
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-plugin.c
@@ -0,0 +1,1254 @@
+/*
+ * gedit-file-browser-plugin.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gedit/gedit-commands.h>
+#include <gedit/gedit-utils.h>
+#include <gedit/gedit-app.h>
+#include <glib/gi18n-lib.h>
+#include <gedit/gedit-debug.h>
+#include <mateconf/mateconf-client.h>
+#include <string.h>
+
+#include "gedit-file-browser-enum-types.h"
+#include "gedit-file-browser-plugin.h"
+#include "gedit-file-browser-utils.h"
+#include "gedit-file-browser-error.h"
+#include "gedit-file-browser-widget.h"
+#include "gedit-file-browser-messages.h"
+
+#define WINDOW_DATA_KEY "GeditFileBrowserPluginWindowData"
+#define FILE_BROWSER_BASE_KEY "/apps/gedit-2/plugins/filebrowser"
+#define CAJA_CLICK_POLICY_BASE_KEY "/apps/caja/preferences"
+#define CAJA_CLICK_POLICY_KEY "click_policy"
+#define CAJA_ENABLE_DELETE_KEY "enable_delete"
+#define CAJA_CONFIRM_TRASH_KEY "confirm_trash"
+#define TERMINAL_EXEC_KEY "/desktop/mate/applications/terminal/exec"
+
+#define GEDIT_FILE_BROWSER_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_FILE_BROWSER_PLUGIN, GeditFileBrowserPluginPrivate))
+
+struct _GeditFileBrowserPluginPrivate
+{
+ gpointer dummy;
+};
+
+typedef struct _GeditFileBrowserPluginData
+{
+ GeditFileBrowserWidget * tree_widget;
+ gulong merge_id;
+ GtkActionGroup * action_group;
+ GtkActionGroup * single_selection_action_group;
+ gboolean auto_root;
+ gulong end_loading_handle;
+ gboolean confirm_trash;
+
+ guint click_policy_handle;
+ guint enable_delete_handle;
+ guint confirm_trash_handle;
+} GeditFileBrowserPluginData;
+
+static void on_uri_activated_cb (GeditFileBrowserWidget * widget,
+ gchar const *uri,
+ GeditWindow * window);
+static void on_error_cb (GeditFileBrowserWidget * widget,
+ guint code,
+ gchar const *message,
+ GeditWindow * window);
+static void on_model_set_cb (GeditFileBrowserView * widget,
+ GParamSpec *arg1,
+ GeditWindow * window);
+static void on_virtual_root_changed_cb (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditWindow * window);
+static void on_filter_mode_changed_cb (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditWindow * window);
+static void on_rename_cb (GeditFileBrowserStore * model,
+ const gchar * olduri,
+ const gchar * newuri,
+ GeditWindow * window);
+static void on_filter_pattern_changed_cb (GeditFileBrowserWidget * widget,
+ GParamSpec * param,
+ GeditWindow * window);
+static void on_tab_added_cb (GeditWindow * window,
+ GeditTab * tab,
+ GeditFileBrowserPluginData * data);
+static gboolean on_confirm_delete_cb (GeditFileBrowserWidget * widget,
+ GeditFileBrowserStore * store,
+ GList * rows,
+ GeditWindow * window);
+static gboolean on_confirm_no_trash_cb (GeditFileBrowserWidget * widget,
+ GList * files,
+ GeditWindow * window);
+
+GEDIT_PLUGIN_REGISTER_TYPE_WITH_CODE (GeditFileBrowserPlugin, filetree_plugin, \
+ gedit_file_browser_enum_and_flag_register_type (type_module); \
+ gedit_file_browser_store_register_type (type_module); \
+ gedit_file_bookmarks_store_register_type (type_module); \
+ gedit_file_browser_view_register_type (type_module); \
+ gedit_file_browser_widget_register_type (type_module); \
+)
+
+
+static void
+filetree_plugin_init (GeditFileBrowserPlugin * plugin)
+{
+ plugin->priv = GEDIT_FILE_BROWSER_PLUGIN_GET_PRIVATE (plugin);
+}
+
+static void
+filetree_plugin_finalize (GObject * object)
+{
+ //GeditFileBrowserPlugin * plugin = GEDIT_FILE_BROWSER_PLUGIN (object);
+
+ G_OBJECT_CLASS (filetree_plugin_parent_class)->finalize (object);
+}
+
+static GeditFileBrowserPluginData *
+get_plugin_data (GeditWindow * window)
+{
+ return (GeditFileBrowserPluginData *) (g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY));
+}
+
+static void
+on_end_loading_cb (GeditFileBrowserStore * store,
+ GtkTreeIter * iter,
+ GeditFileBrowserPluginData * data)
+{
+ /* Disconnect the signal */
+ g_signal_handler_disconnect (store, data->end_loading_handle);
+ data->end_loading_handle = 0;
+ data->auto_root = FALSE;
+}
+
+static void
+prepare_auto_root (GeditFileBrowserPluginData *data)
+{
+ GeditFileBrowserStore *store;
+
+ data->auto_root = TRUE;
+
+ store = gedit_file_browser_widget_get_browser_store (data->tree_widget);
+
+ if (data->end_loading_handle != 0) {
+ g_signal_handler_disconnect (store, data->end_loading_handle);
+ data->end_loading_handle = 0;
+ }
+
+ data->end_loading_handle = g_signal_connect (store,
+ "end-loading",
+ G_CALLBACK (on_end_loading_cb),
+ data);
+}
+
+static void
+restore_default_location (GeditFileBrowserPluginData *data)
+{
+ gchar * root;
+ gchar * virtual_root;
+ gboolean bookmarks;
+ gboolean remote;
+ MateConfClient * client;
+
+ client = mateconf_client_get_default ();
+ if (!client)
+ return;
+
+ bookmarks = !mateconf_client_get_bool (client,
+ FILE_BROWSER_BASE_KEY "/on_load/tree_view",
+ NULL);
+
+ if (bookmarks) {
+ g_object_unref (client);
+ gedit_file_browser_widget_show_bookmarks (data->tree_widget);
+ return;
+ }
+
+ root = mateconf_client_get_string (client,
+ FILE_BROWSER_BASE_KEY "/on_load/root",
+ NULL);
+ virtual_root = mateconf_client_get_string (client,
+ FILE_BROWSER_BASE_KEY "/on_load/virtual_root",
+ NULL);
+
+ remote = mateconf_client_get_bool (client,
+ FILE_BROWSER_BASE_KEY "/on_load/enable_remote",
+ NULL);
+
+ if (root != NULL && *root != '\0') {
+ GFile *file;
+
+ file = g_file_new_for_uri (root);
+
+ if (remote || g_file_is_native (file)) {
+ if (virtual_root != NULL && *virtual_root != '\0') {
+ prepare_auto_root (data);
+ gedit_file_browser_widget_set_root_and_virtual_root (data->tree_widget,
+ root,
+ virtual_root);
+ } else {
+ prepare_auto_root (data);
+ gedit_file_browser_widget_set_root (data->tree_widget,
+ root,
+ TRUE);
+ }
+ }
+
+ g_object_unref (file);
+ }
+
+ g_object_unref (client);
+ g_free (root);
+ g_free (virtual_root);
+}
+
+static void
+restore_filter (GeditFileBrowserPluginData * data)
+{
+ MateConfClient * client;
+ gchar *filter_mode;
+ GeditFileBrowserStoreFilterMode mode;
+ gchar *pattern;
+
+ client = mateconf_client_get_default ();
+ if (!client)
+ return;
+
+ /* Get filter_mode */
+ filter_mode = mateconf_client_get_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_mode",
+ NULL);
+
+ /* Filter mode */
+ mode = gedit_file_browser_store_filter_mode_get_default ();
+
+ if (filter_mode != NULL) {
+ if (strcmp (filter_mode, "hidden") == 0) {
+ mode = GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
+ } else if (strcmp (filter_mode, "binary") == 0) {
+ mode = GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY;
+ } else if (strcmp (filter_mode, "hidden_and_binary") == 0 ||
+ strcmp (filter_mode, "binary_and_hidden") == 0) {
+ mode = GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN |
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY;
+ } else if (strcmp (filter_mode, "none") == 0 ||
+ *filter_mode == '\0') {
+ mode = GEDIT_FILE_BROWSER_STORE_FILTER_MODE_NONE;
+ }
+ }
+
+ /* Set the filter mode */
+ gedit_file_browser_store_set_filter_mode (
+ gedit_file_browser_widget_get_browser_store (data->tree_widget),
+ mode);
+
+ pattern = mateconf_client_get_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_pattern",
+ NULL);
+
+ gedit_file_browser_widget_set_filter_pattern (data->tree_widget,
+ pattern);
+
+ g_object_unref (client);
+ g_free (filter_mode);
+ g_free (pattern);
+}
+
+static GeditFileBrowserViewClickPolicy
+click_policy_from_string (gchar const *click_policy)
+{
+ if (click_policy && strcmp (click_policy, "single") == 0)
+ return GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE;
+ else
+ return GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE;
+}
+
+static void
+on_click_policy_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ MateConfValue *value;
+ GeditFileBrowserPluginData * data;
+ gchar const *click_policy;
+ GeditFileBrowserViewClickPolicy policy = GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE;
+ GeditFileBrowserView *view;
+
+ data = (GeditFileBrowserPluginData *)(user_data);
+ value = mateconf_entry_get_value (entry);
+
+ if (value && value->type == MATECONF_VALUE_STRING) {
+ click_policy = mateconf_value_get_string (value);
+
+ policy = click_policy_from_string (click_policy);
+ }
+
+ view = gedit_file_browser_widget_get_browser_view (data->tree_widget);
+ gedit_file_browser_view_set_click_policy (view, policy);
+}
+
+static void
+on_enable_delete_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ MateConfValue *value;
+ GeditFileBrowserPluginData *data;
+ gboolean enable = FALSE;
+
+ data = (GeditFileBrowserPluginData *)(user_data);
+ value = mateconf_entry_get_value (entry);
+
+ if (value && value->type == MATECONF_VALUE_BOOL)
+ enable = mateconf_value_get_bool (value);
+
+ g_object_set (G_OBJECT (data->tree_widget), "enable-delete", enable, NULL);
+}
+
+static void
+on_confirm_trash_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ MateConfValue *value;
+ GeditFileBrowserPluginData *data;
+ gboolean enable = FALSE;
+
+ data = (GeditFileBrowserPluginData *)(user_data);
+ value = mateconf_entry_get_value (entry);
+
+ if (value && value->type == MATECONF_VALUE_BOOL)
+ enable = mateconf_value_get_bool (value);
+
+ data->confirm_trash = enable;
+}
+
+static void
+install_caja_prefs (GeditFileBrowserPluginData *data)
+{
+ MateConfClient *client;
+ gchar *pref;
+ gboolean prefb;
+ GeditFileBrowserViewClickPolicy policy;
+ GeditFileBrowserView *view;
+
+ client = mateconf_client_get_default ();
+ if (!client)
+ return;
+
+ mateconf_client_add_dir (client,
+ CAJA_CLICK_POLICY_BASE_KEY,
+ MATECONF_CLIENT_PRELOAD_NONE,
+ NULL);
+
+ /* Get click_policy */
+ pref = mateconf_client_get_string (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_CLICK_POLICY_KEY,
+ NULL);
+
+ policy = click_policy_from_string (pref);
+
+ view = gedit_file_browser_widget_get_browser_view (data->tree_widget);
+ gedit_file_browser_view_set_click_policy (view, policy);
+
+ if (pref) {
+ data->click_policy_handle =
+ mateconf_client_notify_add (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_CLICK_POLICY_KEY,
+ on_click_policy_changed,
+ data,
+ NULL,
+ NULL);
+ g_free (pref);
+ }
+
+ /* Get enable_delete */
+ prefb = mateconf_client_get_bool (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_ENABLE_DELETE_KEY,
+ NULL);
+
+ g_object_set (G_OBJECT (data->tree_widget), "enable-delete", prefb, NULL);
+
+ data->enable_delete_handle =
+ mateconf_client_notify_add (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_ENABLE_DELETE_KEY,
+ on_enable_delete_changed,
+ data,
+ NULL,
+ NULL);
+
+ /* Get confirm_trash */
+ prefb = mateconf_client_get_bool (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_CONFIRM_TRASH_KEY,
+ NULL);
+
+ data->confirm_trash = prefb;
+
+ data->confirm_trash_handle =
+ mateconf_client_notify_add (client,
+ CAJA_CLICK_POLICY_BASE_KEY "/" CAJA_CONFIRM_TRASH_KEY,
+ on_confirm_trash_changed,
+ data,
+ NULL,
+ NULL);
+ g_object_unref (client);
+}
+
+static void
+set_root_from_doc (GeditFileBrowserPluginData * data,
+ GeditDocument * doc)
+{
+ GFile *file;
+ GFile *parent;
+
+ if (doc == NULL)
+ return;
+
+ file = gedit_document_get_location (doc);
+ if (file == NULL)
+ return;
+
+ parent = g_file_get_parent (file);
+
+ if (parent != NULL) {
+ gchar * root;
+
+ root = g_file_get_uri (parent);
+
+ gedit_file_browser_widget_set_root (data->tree_widget,
+ root,
+ TRUE);
+
+ g_object_unref (parent);
+ g_free (root);
+ }
+
+ g_object_unref (file);
+}
+
+static void
+on_action_set_active_root (GtkAction * action,
+ GeditWindow * window)
+{
+ GeditFileBrowserPluginData *data;
+
+ data = get_plugin_data (window);
+ set_root_from_doc (data,
+ gedit_window_get_active_document (window));
+}
+
+static gchar *
+get_terminal (void)
+{
+ MateConfClient * client;
+ gchar * terminal;
+
+ client = mateconf_client_get_default ();
+ terminal = mateconf_client_get_string (client,
+ TERMINAL_EXEC_KEY,
+ NULL);
+ g_object_unref (client);
+
+ if (terminal == NULL) {
+ const gchar *term = g_getenv ("TERM");
+
+ if (term != NULL)
+ terminal = g_strdup (term);
+ else
+ terminal = g_strdup ("xterm");
+ }
+
+ return terminal;
+}
+
+static void
+on_action_open_terminal (GtkAction * action,
+ GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data;
+ gchar * terminal;
+ gchar * wd = NULL;
+ gchar * local;
+ gchar * argv[2];
+ GFile * file;
+
+ GtkTreeIter iter;
+ GeditFileBrowserStore * store;
+
+ data = get_plugin_data (window);
+
+ /* Get the current directory */
+ if (!gedit_file_browser_widget_get_selected_directory (data->tree_widget, &iter))
+ return;
+
+ store = gedit_file_browser_widget_get_browser_store (data->tree_widget);
+ gtk_tree_model_get (GTK_TREE_MODEL (store),
+ &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &wd,
+ -1);
+
+ if (wd == NULL)
+ return;
+
+ terminal = get_terminal ();
+
+ file = g_file_new_for_uri (wd);
+ local = g_file_get_path (file);
+ g_object_unref (file);
+
+ argv[0] = terminal;
+ argv[1] = NULL;
+
+ g_spawn_async (local,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ g_free (terminal);
+ g_free (wd);
+ g_free (local);
+}
+
+static void
+on_selection_changed_cb (GtkTreeSelection *selection,
+ GeditWindow *window)
+{
+ GeditFileBrowserPluginData * data;
+ GtkTreeView * tree_view;
+ GtkTreeModel * model;
+ GtkTreeIter iter;
+ gboolean sensitive;
+ gchar * uri;
+
+ data = get_plugin_data (window);
+
+ tree_view = GTK_TREE_VIEW (gedit_file_browser_widget_get_browser_view (data->tree_widget));
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ sensitive = gedit_file_browser_widget_get_selected_directory (data->tree_widget, &iter);
+
+ if (sensitive) {
+ gtk_tree_model_get (model, &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri, -1);
+
+ sensitive = gedit_utils_uri_has_file_scheme (uri);
+ g_free (uri);
+ }
+
+ gtk_action_set_sensitive (
+ gtk_action_group_get_action (data->single_selection_action_group,
+ "OpenTerminal"),
+ sensitive);
+}
+
+#define POPUP_UI "" \
+"<ui>" \
+" <popup name=\"FilePopup\">" \
+" <placeholder name=\"FilePopup_Opt1\">" \
+" <menuitem action=\"SetActiveRoot\"/>" \
+" </placeholder>" \
+" <placeholder name=\"FilePopup_Opt4\">" \
+" <menuitem action=\"OpenTerminal\"/>" \
+" </placeholder>" \
+" </popup>" \
+" <popup name=\"BookmarkPopup\">" \
+" <placeholder name=\"BookmarkPopup_Opt1\">" \
+" <menuitem action=\"SetActiveRoot\"/>" \
+" </placeholder>" \
+" </popup>" \
+"</ui>"
+
+static GtkActionEntry extra_actions[] =
+{
+ {"SetActiveRoot", GTK_STOCK_JUMP_TO, N_("_Set root to active document"),
+ NULL,
+ N_("Set the root to the active document location"),
+ G_CALLBACK (on_action_set_active_root)}
+};
+
+static GtkActionEntry extra_single_selection_actions[] = {
+ {"OpenTerminal", "utilities-terminal", N_("_Open terminal here"),
+ NULL,
+ N_("Open a terminal at the currently opened directory"),
+ G_CALLBACK (on_action_open_terminal)}
+};
+
+static void
+add_popup_ui (GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data;
+ GtkUIManager * manager;
+ GtkActionGroup * action_group;
+ GError * error = NULL;
+
+ data = get_plugin_data (window);
+ manager = gedit_file_browser_widget_get_ui_manager (data->tree_widget);
+
+ action_group = gtk_action_group_new ("FileBrowserPluginExtra");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ extra_actions,
+ G_N_ELEMENTS (extra_actions),
+ window);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ data->action_group = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserPluginSingleSelectionExtra");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ extra_single_selection_actions,
+ G_N_ELEMENTS (extra_single_selection_actions),
+ window);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ data->single_selection_action_group = action_group;
+
+ data->merge_id = gtk_ui_manager_add_ui_from_string (manager,
+ POPUP_UI,
+ -1,
+ &error);
+
+ if (data->merge_id == 0) {
+ g_warning("Unable to merge UI: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+static void
+remove_popup_ui (GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data;
+ GtkUIManager * manager;
+
+ data = get_plugin_data (window);
+ manager = gedit_file_browser_widget_get_ui_manager (data->tree_widget);
+ gtk_ui_manager_remove_ui (manager, data->merge_id);
+
+ gtk_ui_manager_remove_action_group (manager, data->action_group);
+ g_object_unref (data->action_group);
+
+ gtk_ui_manager_remove_action_group (manager, data->single_selection_action_group);
+ g_object_unref (data->single_selection_action_group);
+}
+
+static void
+impl_updateui (GeditPlugin * plugin, GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data;
+ GeditDocument * doc;
+
+ data = get_plugin_data (window);
+
+ doc = gedit_window_get_active_document (window);
+
+ gtk_action_set_sensitive (gtk_action_group_get_action (data->action_group,
+ "SetActiveRoot"),
+ doc != NULL &&
+ !gedit_document_is_untitled (doc));
+}
+
+static void
+impl_activate (GeditPlugin * plugin, GeditWindow * window)
+{
+ GeditPanel * panel;
+ GeditFileBrowserPluginData * data;
+ GtkWidget * image;
+ GdkPixbuf * pixbuf;
+ GeditFileBrowserStore * store;
+ gchar *data_dir;
+
+ data = g_new0 (GeditFileBrowserPluginData, 1);
+
+ data_dir = gedit_plugin_get_data_dir (plugin);
+ data->tree_widget = GEDIT_FILE_BROWSER_WIDGET (gedit_file_browser_widget_new (data_dir));
+ g_free (data_dir);
+
+ g_signal_connect (data->tree_widget,
+ "uri-activated",
+ G_CALLBACK (on_uri_activated_cb), window);
+
+ g_signal_connect (data->tree_widget,
+ "error", G_CALLBACK (on_error_cb), window);
+
+ g_signal_connect (data->tree_widget,
+ "notify::filter-pattern",
+ G_CALLBACK (on_filter_pattern_changed_cb),
+ window);
+
+ g_signal_connect (data->tree_widget,
+ "confirm-delete",
+ G_CALLBACK (on_confirm_delete_cb),
+ window);
+
+ g_signal_connect (data->tree_widget,
+ "confirm-no-trash",
+ G_CALLBACK (on_confirm_no_trash_cb),
+ window);
+
+ g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW
+ (gedit_file_browser_widget_get_browser_view
+ (data->tree_widget))),
+ "changed",
+ G_CALLBACK (on_selection_changed_cb),
+ window);
+
+ panel = gedit_window_get_side_panel (window);
+ pixbuf = gedit_file_browser_utils_pixbuf_from_theme("system-file-manager",
+ GTK_ICON_SIZE_MENU);
+
+ if (pixbuf) {
+ image = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
+ } else {
+ image = gtk_image_new_from_stock(GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU);
+ }
+
+ gtk_widget_show(image);
+ gedit_panel_add_item (panel,
+ GTK_WIDGET (data->tree_widget),
+ _("File Browser"),
+ image);
+ gtk_widget_show (GTK_WIDGET (data->tree_widget));
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, data);
+
+ add_popup_ui (window);
+
+ /* Restore filter options */
+ restore_filter (data);
+
+ /* Install caja preferences */
+ install_caja_prefs (data);
+
+ /* Connect signals to store the last visited location */
+ g_signal_connect (gedit_file_browser_widget_get_browser_view (data->tree_widget),
+ "notify::model",
+ G_CALLBACK (on_model_set_cb),
+ window);
+
+ store = gedit_file_browser_widget_get_browser_store (data->tree_widget);
+ g_signal_connect (store,
+ "notify::virtual-root",
+ G_CALLBACK (on_virtual_root_changed_cb),
+ window);
+
+ g_signal_connect (store,
+ "notify::filter-mode",
+ G_CALLBACK (on_filter_mode_changed_cb),
+ window);
+
+ g_signal_connect (store,
+ "rename",
+ G_CALLBACK (on_rename_cb),
+ window);
+
+ g_signal_connect (window,
+ "tab-added",
+ G_CALLBACK (on_tab_added_cb),
+ data);
+
+ /* Register messages on the bus */
+ gedit_file_browser_messages_register (window, data->tree_widget);
+
+ impl_updateui (plugin, window);
+}
+
+static void
+impl_deactivate (GeditPlugin * plugin, GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data;
+ GeditPanel * panel;
+ MateConfClient *client;
+
+ data = get_plugin_data (window);
+
+ /* Unregister messages from the bus */
+ gedit_file_browser_messages_unregister (window);
+
+ /* Disconnect signals */
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (on_tab_added_cb),
+ data);
+
+ client = mateconf_client_get_default ();
+ mateconf_client_remove_dir (client, CAJA_CLICK_POLICY_BASE_KEY, NULL);
+
+ if (data->click_policy_handle)
+ mateconf_client_notify_remove (client, data->click_policy_handle);
+
+ if (data->enable_delete_handle)
+ mateconf_client_notify_remove (client, data->enable_delete_handle);
+
+ if (data->confirm_trash_handle)
+ mateconf_client_notify_remove (client, data->confirm_trash_handle);
+
+ g_object_unref (client);
+ remove_popup_ui (window);
+
+ panel = gedit_window_get_side_panel (window);
+ gedit_panel_remove_item (panel, GTK_WIDGET (data->tree_widget));
+
+ g_free (data);
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
+}
+
+static void
+filetree_plugin_class_init (GeditFileBrowserPluginClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeditPluginClass * plugin_class = GEDIT_PLUGIN_CLASS (klass);
+
+ object_class->finalize = filetree_plugin_finalize;
+
+ plugin_class->activate = impl_activate;
+ plugin_class->deactivate = impl_deactivate;
+ plugin_class->update_ui = impl_updateui;
+
+ g_type_class_add_private (object_class,
+ sizeof (GeditFileBrowserPluginPrivate));
+}
+
+/* Callbacks */
+static void
+on_uri_activated_cb (GeditFileBrowserWidget * tree_widget,
+ gchar const *uri, GeditWindow * window)
+{
+ gedit_commands_load_uri (window, uri, NULL, 0);
+}
+
+static void
+on_error_cb (GeditFileBrowserWidget * tree_widget,
+ guint code, gchar const *message, GeditWindow * window)
+{
+ gchar * title;
+ GtkWidget * dlg;
+ GeditFileBrowserPluginData * data;
+
+ data = get_plugin_data (window);
+
+ /* Do not show the error when the root has been set automatically */
+ if (data->auto_root && (code == GEDIT_FILE_BROWSER_ERROR_SET_ROOT ||
+ code == GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY))
+ {
+ /* Show bookmarks */
+ gedit_file_browser_widget_show_bookmarks (data->tree_widget);
+ return;
+ }
+
+ switch (code) {
+ case GEDIT_FILE_BROWSER_ERROR_NEW_DIRECTORY:
+ title =
+ _("An error occurred while creating a new directory");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_NEW_FILE:
+ title = _("An error occurred while creating a new file");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_RENAME:
+ title =
+ _
+ ("An error occurred while renaming a file or directory");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_DELETE:
+ title =
+ _
+ ("An error occurred while deleting a file or directory");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_OPEN_DIRECTORY:
+ title =
+ _
+ ("An error occurred while opening a directory in the file manager");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_SET_ROOT:
+ title =
+ _("An error occurred while setting a root directory");
+ break;
+ case GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY:
+ title =
+ _("An error occurred while loading a directory");
+ break;
+ default:
+ title = _("An error occurred");
+ break;
+ }
+
+ dlg = gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "%s", title);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
+ "%s", message);
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+}
+
+static void
+on_model_set_cb (GeditFileBrowserView * widget,
+ GParamSpec *arg1,
+ GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data = get_plugin_data (window);
+ GtkTreeModel * model;
+ MateConfClient * client;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (gedit_file_browser_widget_get_browser_view (data->tree_widget)));
+
+ if (model == NULL)
+ return;
+
+ client = mateconf_client_get_default ();
+ mateconf_client_set_bool (client,
+ FILE_BROWSER_BASE_KEY "/on_load/tree_view",
+ GEDIT_IS_FILE_BROWSER_STORE (model),
+ NULL);
+ g_object_unref (client);
+}
+
+static void
+on_filter_mode_changed_cb (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditWindow * window)
+{
+ MateConfClient * client;
+ GeditFileBrowserStoreFilterMode mode;
+
+ client = mateconf_client_get_default ();
+
+ if (!client)
+ return;
+
+ mode = gedit_file_browser_store_get_filter_mode (model);
+
+ if ((mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN) &&
+ (mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY)) {
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_mode",
+ "hidden_and_binary",
+ NULL);
+ } else if (mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN) {
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_mode",
+ "hidden",
+ NULL);
+ } else if (mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY) {
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_mode",
+ "binary",
+ NULL);
+ } else {
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_mode",
+ "none",
+ NULL);
+ }
+
+ g_object_unref (client);
+
+}
+
+static void
+on_rename_cb (GeditFileBrowserStore * store,
+ const gchar * olduri,
+ const gchar * newuri,
+ GeditWindow * window)
+{
+ GeditApp * app;
+ GList * documents;
+ GList * item;
+ GeditDocument * doc;
+ GFile * docfile;
+ GFile * oldfile;
+ GFile * newfile;
+ gchar * uri;
+
+ /* Find all documents and set its uri to newuri where it matches olduri */
+ app = gedit_app_get_default ();
+ documents = gedit_app_get_documents (app);
+
+ oldfile = g_file_new_for_uri (olduri);
+ newfile = g_file_new_for_uri (newuri);
+
+ for (item = documents; item; item = item->next) {
+ doc = GEDIT_DOCUMENT (item->data);
+ uri = gedit_document_get_uri (doc);
+
+ if (!uri)
+ continue;
+
+ docfile = g_file_new_for_uri (uri);
+
+ if (g_file_equal (docfile, oldfile)) {
+ gedit_document_set_uri (doc, newuri);
+ } else {
+ gchar *relative;
+
+ relative = g_file_get_relative_path (oldfile, docfile);
+
+ if (relative) {
+ /* relative now contains the part in docfile without
+ the prefix oldfile */
+
+ g_object_unref (docfile);
+ g_free (uri);
+
+ docfile = g_file_get_child (newfile, relative);
+ uri = g_file_get_uri (docfile);
+
+ gedit_document_set_uri (doc, uri);
+ }
+
+ g_free (relative);
+ }
+
+ g_free (uri);
+ g_object_unref (docfile);
+ }
+
+ g_object_unref (oldfile);
+ g_object_unref (newfile);
+
+ g_list_free (documents);
+}
+
+static void
+on_filter_pattern_changed_cb (GeditFileBrowserWidget * widget,
+ GParamSpec * param,
+ GeditWindow * window)
+{
+ MateConfClient * client;
+ gchar * pattern;
+
+ client = mateconf_client_get_default ();
+
+ if (!client)
+ return;
+
+ g_object_get (G_OBJECT (widget), "filter-pattern", &pattern, NULL);
+
+ if (pattern == NULL)
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_pattern",
+ "",
+ NULL);
+ else
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/filter_pattern",
+ pattern,
+ NULL);
+
+ g_free (pattern);
+}
+
+static void
+on_virtual_root_changed_cb (GeditFileBrowserStore * store,
+ GParamSpec * param,
+ GeditWindow * window)
+{
+ GeditFileBrowserPluginData * data = get_plugin_data (window);
+ gchar * root;
+ gchar * virtual_root;
+ MateConfClient * client;
+
+ root = gedit_file_browser_store_get_root (store);
+
+ if (!root)
+ return;
+
+ client = mateconf_client_get_default ();
+
+ if (!client)
+ return;
+
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/on_load/root",
+ root,
+ NULL);
+
+ virtual_root = gedit_file_browser_store_get_virtual_root (store);
+
+ if (!virtual_root) {
+ /* Set virtual to same as root then */
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/on_load/virtual_root",
+ root,
+ NULL);
+ } else {
+ mateconf_client_set_string (client,
+ FILE_BROWSER_BASE_KEY "/on_load/virtual_root",
+ virtual_root,
+ NULL);
+ }
+
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (on_tab_added_cb),
+ data);
+
+ g_object_unref (client);
+ g_free (root);
+ g_free (virtual_root);
+}
+
+static void
+on_tab_added_cb (GeditWindow * window,
+ GeditTab * tab,
+ GeditFileBrowserPluginData * data)
+{
+ MateConfClient *client;
+ gboolean open;
+ gboolean load_default = TRUE;
+
+ client = mateconf_client_get_default ();
+
+ if (!client)
+ return;
+
+ open = mateconf_client_get_bool (client,
+ FILE_BROWSER_BASE_KEY "/open_at_first_doc",
+ NULL);
+
+ if (open) {
+ GeditDocument *doc;
+ gchar *uri;
+
+ doc = gedit_tab_get_document (tab);
+
+ uri = gedit_document_get_uri (doc);
+
+ if (uri != NULL && gedit_utils_uri_has_file_scheme (uri)) {
+ prepare_auto_root (data);
+ set_root_from_doc (data, doc);
+ load_default = FALSE;
+ }
+
+ g_free (uri);
+ }
+
+ if (load_default)
+ restore_default_location (data);
+
+ g_object_unref (client);
+
+ /* Disconnect this signal, it's only called once */
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (on_tab_added_cb),
+ data);
+}
+
+static gchar *
+get_filename_from_path (GtkTreeModel *model, GtkTreePath *path)
+{
+ GtkTreeIter iter;
+ gchar *uri;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ -1);
+
+ return gedit_file_browser_utils_uri_basename (uri);
+}
+
+static gboolean
+on_confirm_no_trash_cb (GeditFileBrowserWidget * widget,
+ GList * files,
+ GeditWindow * window)
+{
+ gchar *normal;
+ gchar *message;
+ gchar *secondary;
+ gboolean result;
+
+ message = _("Cannot move file to trash, do you\nwant to delete permanently?");
+
+ if (files->next == NULL) {
+ normal = gedit_file_browser_utils_file_basename (G_FILE (files->data));
+ secondary = g_strdup_printf (_("The file \"%s\" cannot be moved to the trash."), normal);
+ g_free (normal);
+ } else {
+ secondary = g_strdup (_("The selected files cannot be moved to the trash."));
+ }
+
+ result = gedit_file_browser_utils_confirmation_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ message,
+ secondary,
+ GTK_STOCK_DELETE,
+ NULL);
+ g_free (secondary);
+
+ return result;
+}
+
+static gboolean
+on_confirm_delete_cb (GeditFileBrowserWidget *widget,
+ GeditFileBrowserStore *store,
+ GList *paths,
+ GeditWindow *window)
+{
+ gchar *normal;
+ gchar *message;
+ gchar *secondary;
+ gboolean result;
+ GeditFileBrowserPluginData *data;
+
+ data = get_plugin_data (window);
+
+ if (!data->confirm_trash)
+ return TRUE;
+
+ if (paths->next == NULL) {
+ normal = get_filename_from_path (GTK_TREE_MODEL (store), (GtkTreePath *)(paths->data));
+ message = g_strdup_printf (_("Are you sure you want to permanently delete \"%s\"?"), normal);
+ g_free (normal);
+ } else {
+ message = g_strdup (_("Are you sure you want to permanently delete the selected files?"));
+ }
+
+ secondary = _("If you delete an item, it is permanently lost.");
+
+ result = gedit_file_browser_utils_confirmation_dialog (window,
+ GTK_MESSAGE_QUESTION,
+ message,
+ secondary,
+ GTK_STOCK_DELETE,
+ NULL);
+
+ g_free (message);
+
+ return result;
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-plugin.h b/plugins/filebrowser/gedit-file-browser-plugin.h
new file mode 100755
index 00000000..19ca86bf
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-plugin.h
@@ -0,0 +1,71 @@
+/*
+ * gedit-file-browser-plugin.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_PLUGIN_H__
+#define __GEDIT_FILE_BROWSER_PLUGIN_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gedit/gedit-plugin.h>
+
+G_BEGIN_DECLS
+/*
+ * Type checking and casting macros
+ */
+#define GEDIT_TYPE_FILE_BROWSER_PLUGIN (filetree_plugin_get_type ())
+#define GEDIT_FILE_BROWSER_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_FILE_BROWSER_PLUGIN, GeditFileBrowserPlugin))
+#define GEDIT_FILE_BROWSER_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_FILE_BROWSER_PLUGIN, GeditFileBrowserPluginClass))
+#define GEDIT_IS_FILE_BROWSER_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_FILE_BROWSER_PLUGIN))
+#define GEDIT_IS_FILE_BROWSER_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_FILE_BROWSER_PLUGIN))
+#define GEDIT_FILE_BROWSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_FILE_BROWSER_PLUGIN, GeditFileBrowserPluginClass))
+
+/* Private structure type */
+typedef struct _GeditFileBrowserPluginPrivate GeditFileBrowserPluginPrivate;
+typedef struct _GeditFileBrowserPlugin GeditFileBrowserPlugin;
+typedef struct _GeditFileBrowserPluginClass GeditFileBrowserPluginClass;
+
+struct _GeditFileBrowserPlugin
+{
+ GeditPlugin parent_instance;
+
+ /*< private > */
+ GeditFileBrowserPluginPrivate *priv;
+};
+
+
+
+struct _GeditFileBrowserPluginClass
+{
+ GeditPluginClass parent_class;
+};
+
+/*
+ * Public methods
+ */
+GType filetree_plugin_get_type (void) G_GNUC_CONST;
+
+/* All the plugins must implement this function */
+G_MODULE_EXPORT GType register_gedit_plugin (GTypeModule * module);
+
+G_END_DECLS
+#endif /* __GEDIT_FILE_BROWSER_PLUGIN_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-store.c b/plugins/filebrowser/gedit-file-browser-store.c
new file mode 100755
index 00000000..6c4f5b51
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-store.c
@@ -0,0 +1,3625 @@
+/*
+ * gedit-file-browser-store.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gedit/gedit-plugin.h>
+#include <gedit/gedit-utils.h>
+
+#include "gedit-file-browser-store.h"
+#include "gedit-file-browser-marshal.h"
+#include "gedit-file-browser-enum-types.h"
+#include "gedit-file-browser-error.h"
+#include "gedit-file-browser-utils.h"
+
+#define GEDIT_FILE_BROWSER_STORE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GEDIT_TYPE_FILE_BROWSER_STORE, \
+ GeditFileBrowserStorePrivate))
+
+#define NODE_IS_DIR(node) (FILE_IS_DIR((node)->flags))
+#define NODE_IS_HIDDEN(node) (FILE_IS_HIDDEN((node)->flags))
+#define NODE_IS_TEXT(node) (FILE_IS_TEXT((node)->flags))
+#define NODE_LOADED(node) (FILE_LOADED((node)->flags))
+#define NODE_IS_FILTERED(node) (FILE_IS_FILTERED((node)->flags))
+#define NODE_IS_DUMMY(node) (FILE_IS_DUMMY((node)->flags))
+
+#define FILE_BROWSER_NODE_DIR(node) ((FileBrowserNodeDir *)(node))
+
+#define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
+#define STANDARD_ATTRIBUTE_TYPES G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \
+ G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP "," \
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," \
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
+ G_FILE_ATTRIBUTE_STANDARD_ICON
+
+typedef struct _FileBrowserNode FileBrowserNode;
+typedef struct _FileBrowserNodeDir FileBrowserNodeDir;
+typedef struct _AsyncData AsyncData;
+typedef struct _AsyncNode AsyncNode;
+
+typedef gint (*SortFunc) (FileBrowserNode * node1,
+ FileBrowserNode * node2);
+
+struct _AsyncData
+{
+ GeditFileBrowserStore * model;
+ GCancellable * cancellable;
+ gboolean trash;
+ GList * files;
+ GList * iter;
+ gboolean removed;
+};
+
+struct _AsyncNode
+{
+ FileBrowserNodeDir *dir;
+ GCancellable *cancellable;
+ GSList *original_children;
+};
+
+typedef struct {
+ GeditFileBrowserStore * model;
+ gchar * virtual_root;
+ GMountOperation * operation;
+ GCancellable * cancellable;
+} MountInfo;
+
+struct _FileBrowserNode
+{
+ GFile *file;
+ guint flags;
+ gchar *name;
+
+ GdkPixbuf *icon;
+ GdkPixbuf *emblem;
+
+ FileBrowserNode *parent;
+ gint pos;
+ gboolean inserted;
+};
+
+struct _FileBrowserNodeDir
+{
+ FileBrowserNode node;
+ GSList *children;
+ GHashTable *hidden_file_hash;
+
+ GCancellable *cancellable;
+ GFileMonitor *monitor;
+ GeditFileBrowserStore *model;
+};
+
+struct _GeditFileBrowserStorePrivate
+{
+ FileBrowserNode *root;
+ FileBrowserNode *virtual_root;
+ GType column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_NUM];
+
+ GeditFileBrowserStoreFilterMode filter_mode;
+ GeditFileBrowserStoreFilterFunc filter_func;
+ gpointer filter_user_data;
+
+ SortFunc sort_func;
+
+ GSList *async_handles;
+ MountInfo *mount_info;
+};
+
+static FileBrowserNode *model_find_node (GeditFileBrowserStore *model,
+ FileBrowserNode *node,
+ GFile *uri);
+static void model_remove_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GtkTreePath * path,
+ gboolean free_nodes);
+
+static void set_virtual_root_from_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node);
+
+static void gedit_file_browser_store_iface_init (GtkTreeModelIface * iface);
+static GtkTreeModelFlags gedit_file_browser_store_get_flags (GtkTreeModel * tree_model);
+static gint gedit_file_browser_store_get_n_columns (GtkTreeModel * tree_model);
+static GType gedit_file_browser_store_get_column_type (GtkTreeModel * tree_model,
+ gint index);
+static gboolean gedit_file_browser_store_get_iter (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreePath * path);
+static GtkTreePath *gedit_file_browser_store_get_path (GtkTreeModel * tree_model,
+ GtkTreeIter * iter);
+static void gedit_file_browser_store_get_value (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ gint column,
+ GValue * value);
+static gboolean gedit_file_browser_store_iter_next (GtkTreeModel * tree_model,
+ GtkTreeIter * iter);
+static gboolean gedit_file_browser_store_iter_children (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * parent);
+static gboolean gedit_file_browser_store_iter_has_child (GtkTreeModel * tree_model,
+ GtkTreeIter * iter);
+static gint gedit_file_browser_store_iter_n_children (GtkTreeModel * tree_model,
+ GtkTreeIter * iter);
+static gboolean gedit_file_browser_store_iter_nth_child (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * parent,
+ gint n);
+static gboolean gedit_file_browser_store_iter_parent (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * child);
+static void gedit_file_browser_store_row_inserted (GtkTreeModel * tree_model,
+ GtkTreePath * path,
+ GtkTreeIter * iter);
+
+static void gedit_file_browser_store_drag_source_init (GtkTreeDragSourceIface * iface);
+static gboolean gedit_file_browser_store_row_draggable (GtkTreeDragSource * drag_source,
+ GtkTreePath * path);
+static gboolean gedit_file_browser_store_drag_data_delete (GtkTreeDragSource * drag_source,
+ GtkTreePath * path);
+static gboolean gedit_file_browser_store_drag_data_get (GtkTreeDragSource * drag_source,
+ GtkTreePath * path,
+ GtkSelectionData * selection_data);
+
+static void file_browser_node_free (GeditFileBrowserStore * model,
+ FileBrowserNode * node);
+static void model_add_node (GeditFileBrowserStore * model,
+ FileBrowserNode * child,
+ FileBrowserNode * parent);
+static void model_clear (GeditFileBrowserStore * model,
+ gboolean free_nodes);
+static gint model_sort_default (FileBrowserNode * node1,
+ FileBrowserNode * node2);
+static void model_check_dummy (GeditFileBrowserStore * model,
+ FileBrowserNode * node);
+static void next_files_async (GFileEnumerator * enumerator,
+ AsyncNode * async);
+
+GEDIT_PLUGIN_DEFINE_TYPE_WITH_CODE (GeditFileBrowserStore, gedit_file_browser_store,
+ G_TYPE_OBJECT,
+ GEDIT_PLUGIN_IMPLEMENT_INTERFACE (gedit_file_browser_store_tree_model,
+ GTK_TYPE_TREE_MODEL,
+ gedit_file_browser_store_iface_init)
+ GEDIT_PLUGIN_IMPLEMENT_INTERFACE (gedit_file_browser_store_drag_source,
+ GTK_TYPE_TREE_DRAG_SOURCE,
+ gedit_file_browser_store_drag_source_init))
+
+/* Properties */
+enum {
+ PROP_0,
+
+ PROP_ROOT,
+ PROP_VIRTUAL_ROOT,
+ PROP_FILTER_MODE
+};
+
+/* Signals */
+enum
+{
+ BEGIN_LOADING,
+ END_LOADING,
+ ERROR,
+ NO_TRASH,
+ RENAME,
+ BEGIN_REFRESH,
+ END_REFRESH,
+ UNLOAD,
+ NUM_SIGNALS
+};
+
+static guint model_signals[NUM_SIGNALS] = { 0 };
+
+static void
+cancel_mount_operation (GeditFileBrowserStore *obj)
+{
+ if (obj->priv->mount_info != NULL)
+ {
+ obj->priv->mount_info->model = NULL;
+ g_cancellable_cancel (obj->priv->mount_info->cancellable);
+ obj->priv->mount_info = NULL;
+ }
+}
+
+static void
+gedit_file_browser_store_finalize (GObject * object)
+{
+ GeditFileBrowserStore *obj = GEDIT_FILE_BROWSER_STORE (object);
+ GSList *item;
+
+ /* Free all the nodes */
+ file_browser_node_free (obj, obj->priv->root);
+
+ /* Cancel any asynchronous operations */
+ for (item = obj->priv->async_handles; item; item = item->next)
+ {
+ AsyncData *data = (AsyncData *) (item->data);
+ g_cancellable_cancel (data->cancellable);
+
+ data->removed = TRUE;
+ }
+
+ cancel_mount_operation (obj);
+
+ g_slist_free (obj->priv->async_handles);
+ G_OBJECT_CLASS (gedit_file_browser_store_parent_class)->finalize (object);
+}
+
+static void
+set_gvalue_from_node (GValue *value,
+ FileBrowserNode *node)
+{
+ gchar * uri;
+
+ if (node == NULL || !node->file) {
+ g_value_set_string (value, NULL);
+ } else {
+ uri = g_file_get_uri (node->file);
+ g_value_take_string (value, uri);
+ }
+}
+
+static void
+gedit_file_browser_store_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserStore *obj = GEDIT_FILE_BROWSER_STORE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ROOT:
+ set_gvalue_from_node (value, obj->priv->root);
+ break;
+ case PROP_VIRTUAL_ROOT:
+ set_gvalue_from_node (value, obj->priv->virtual_root);
+ break;
+ case PROP_FILTER_MODE:
+ g_value_set_flags (value, obj->priv->filter_mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_file_browser_store_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserStore *obj = GEDIT_FILE_BROWSER_STORE (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILTER_MODE:
+ gedit_file_browser_store_set_filter_mode (obj,
+ g_value_get_flags (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_file_browser_store_class_init (GeditFileBrowserStoreClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gedit_file_browser_store_finalize;
+
+ object_class->get_property = gedit_file_browser_store_get_property;
+ object_class->set_property = gedit_file_browser_store_set_property;
+
+ g_object_class_install_property (object_class, PROP_ROOT,
+ g_param_spec_string ("root",
+ "Root",
+ "The root uri",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class, PROP_VIRTUAL_ROOT,
+ g_param_spec_string ("virtual-root",
+ "Virtual Root",
+ "The virtual root uri",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class, PROP_FILTER_MODE,
+ g_param_spec_flags ("filter-mode",
+ "Filter Mode",
+ "The filter mode",
+ GEDIT_TYPE_FILE_BROWSER_STORE_FILTER_MODE,
+ gedit_file_browser_store_filter_mode_get_default (),
+ G_PARAM_READWRITE));
+
+ model_signals[BEGIN_LOADING] =
+ g_signal_new ("begin-loading",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ begin_loading), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1,
+ GTK_TYPE_TREE_ITER);
+ model_signals[END_LOADING] =
+ g_signal_new ("end-loading",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ end_loading), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1,
+ GTK_TYPE_TREE_ITER);
+ model_signals[ERROR] =
+ g_signal_new ("error", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ error), NULL, NULL,
+ gedit_file_browser_marshal_VOID__UINT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
+ model_signals[NO_TRASH] =
+ g_signal_new ("no-trash", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ no_trash), g_signal_accumulator_true_handled, NULL,
+ gedit_file_browser_marshal_BOOL__POINTER,
+ G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+ model_signals[RENAME] =
+ g_signal_new ("rename",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ rename), NULL, NULL,
+ gedit_file_browser_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ model_signals[BEGIN_REFRESH] =
+ g_signal_new ("begin-refresh",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ begin_refresh), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ model_signals[END_REFRESH] =
+ g_signal_new ("end-refresh",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ end_refresh), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ model_signals[UNLOAD] =
+ g_signal_new ("unload",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserStoreClass,
+ unload), NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ g_type_class_add_private (object_class,
+ sizeof (GeditFileBrowserStorePrivate));
+}
+
+static void
+gedit_file_browser_store_iface_init (GtkTreeModelIface * iface)
+{
+ iface->get_flags = gedit_file_browser_store_get_flags;
+ iface->get_n_columns = gedit_file_browser_store_get_n_columns;
+ iface->get_column_type = gedit_file_browser_store_get_column_type;
+ iface->get_iter = gedit_file_browser_store_get_iter;
+ iface->get_path = gedit_file_browser_store_get_path;
+ iface->get_value = gedit_file_browser_store_get_value;
+ iface->iter_next = gedit_file_browser_store_iter_next;
+ iface->iter_children = gedit_file_browser_store_iter_children;
+ iface->iter_has_child = gedit_file_browser_store_iter_has_child;
+ iface->iter_n_children = gedit_file_browser_store_iter_n_children;
+ iface->iter_nth_child = gedit_file_browser_store_iter_nth_child;
+ iface->iter_parent = gedit_file_browser_store_iter_parent;
+ iface->row_inserted = gedit_file_browser_store_row_inserted;
+}
+
+static void
+gedit_file_browser_store_drag_source_init (GtkTreeDragSourceIface * iface)
+{
+ iface->row_draggable = gedit_file_browser_store_row_draggable;
+ iface->drag_data_delete = gedit_file_browser_store_drag_data_delete;
+ iface->drag_data_get = gedit_file_browser_store_drag_data_get;
+}
+
+static void
+gedit_file_browser_store_init (GeditFileBrowserStore * obj)
+{
+ obj->priv = GEDIT_FILE_BROWSER_STORE_GET_PRIVATE (obj);
+
+ obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_URI] =
+ G_TYPE_STRING;
+ obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_NAME] =
+ G_TYPE_STRING;
+ obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS] =
+ G_TYPE_UINT;
+ obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_ICON] =
+ GDK_TYPE_PIXBUF;
+ obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM] =
+ GDK_TYPE_PIXBUF;
+
+ // Default filter mode is hiding the hidden files
+ obj->priv->filter_mode = gedit_file_browser_store_filter_mode_get_default ();
+ obj->priv->sort_func = model_sort_default;
+}
+
+static gboolean
+node_has_parent (FileBrowserNode * node, FileBrowserNode * parent)
+{
+ if (node->parent == NULL)
+ return FALSE;
+
+ if (node->parent == parent)
+ return TRUE;
+
+ return node_has_parent (node->parent, parent);
+}
+
+static gboolean
+node_in_tree (GeditFileBrowserStore * model, FileBrowserNode * node)
+{
+ return node_has_parent (node, model->priv->virtual_root);
+}
+
+static gboolean
+model_node_visibility (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ if (node == NULL)
+ return FALSE;
+
+ if (NODE_IS_DUMMY (node))
+ return !NODE_IS_HIDDEN (node);
+
+ if (node == model->priv->virtual_root)
+ return TRUE;
+
+ if (!node_has_parent (node, model->priv->virtual_root))
+ return FALSE;
+
+ return !NODE_IS_FILTERED (node);
+}
+
+static gboolean
+model_node_inserted (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ return node == model->priv->virtual_root || (model_node_visibility (model, node) && node->inserted);
+}
+
+/* Interface implementation */
+
+static GtkTreeModelFlags
+gedit_file_browser_store_get_flags (GtkTreeModel * tree_model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ (GtkTreeModelFlags) 0);
+
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint
+gedit_file_browser_store_get_n_columns (GtkTreeModel * tree_model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model), 0);
+
+ return GEDIT_FILE_BROWSER_STORE_COLUMN_NUM;
+}
+
+static GType
+gedit_file_browser_store_get_column_type (GtkTreeModel * tree_model, gint idx)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ G_TYPE_INVALID);
+ g_return_val_if_fail (idx < GEDIT_FILE_BROWSER_STORE_COLUMN_NUM &&
+ idx >= 0, G_TYPE_INVALID);
+
+ return GEDIT_FILE_BROWSER_STORE (tree_model)->priv->column_types[idx];
+}
+
+static gboolean
+gedit_file_browser_store_get_iter (GtkTreeModel * tree_model,
+ GtkTreeIter * iter, GtkTreePath * path)
+{
+ gint * indices, depth, i;
+ FileBrowserNode * node;
+ GeditFileBrowserStore * model;
+ gint num;
+
+ g_assert (GEDIT_IS_FILE_BROWSER_STORE (tree_model));
+ g_assert (path != NULL);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+ indices = gtk_tree_path_get_indices (path);
+ depth = gtk_tree_path_get_depth (path);
+ node = model->priv->virtual_root;
+
+ for (i = 0; i < depth; ++i) {
+ GSList * item;
+
+ if (node == NULL)
+ return FALSE;
+
+ num = 0;
+
+ if (!NODE_IS_DIR (node))
+ return FALSE;
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
+ FileBrowserNode * child;
+
+ child = (FileBrowserNode *) (item->data);
+
+ if (model_node_inserted (model, child)) {
+ if (num == indices[i]) {
+ node = child;
+ break;
+ }
+
+ num++;
+ }
+ }
+
+ if (item == NULL)
+ return FALSE;
+
+ node = (FileBrowserNode *) (item->data);
+ }
+
+ iter->user_data = node;
+ iter->user_data2 = NULL;
+ iter->user_data3 = NULL;
+
+ return node != NULL;
+}
+
+static GtkTreePath *
+gedit_file_browser_store_get_path_real (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ GtkTreePath *path;
+ gint num = 0;
+
+ path = gtk_tree_path_new ();
+
+ while (node != model->priv->virtual_root) {
+ GSList *item;
+
+ if (node->parent == NULL) {
+ gtk_tree_path_free (path);
+ return NULL;
+ }
+
+ num = 0;
+
+ for (item = FILE_BROWSER_NODE_DIR (node->parent)->children; item; item = item->next) {
+ FileBrowserNode *check;
+
+ check = (FileBrowserNode *) (item->data);
+
+ if (model_node_visibility (model, check) && (check == node || check->inserted)) {
+ if (check == node) {
+ gtk_tree_path_prepend_index (path,
+ num);
+ break;
+ }
+
+ ++num;
+ } else if (check == node) {
+ if (NODE_IS_DUMMY (node))
+ g_warning ("Dummy not visible???");
+
+ gtk_tree_path_free (path);
+ return NULL;
+ }
+ }
+
+ node = node->parent;
+ }
+
+ return path;
+}
+
+static GtkTreePath *
+gedit_file_browser_store_get_path (GtkTreeModel * tree_model,
+ GtkTreeIter * iter)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model), NULL);
+ g_return_val_if_fail (iter != NULL, NULL);
+ g_return_val_if_fail (iter->user_data != NULL, NULL);
+
+ return gedit_file_browser_store_get_path_real (GEDIT_FILE_BROWSER_STORE (tree_model),
+ (FileBrowserNode *) (iter->user_data));
+}
+
+static void
+gedit_file_browser_store_get_value (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ gint column,
+ GValue * value)
+{
+ FileBrowserNode *node;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (iter->user_data != NULL);
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ g_value_init (value, GEDIT_FILE_BROWSER_STORE (tree_model)->priv->column_types[column]);
+
+ switch (column) {
+ case GEDIT_FILE_BROWSER_STORE_COLUMN_URI:
+ set_gvalue_from_node (value, node);
+ break;
+ case GEDIT_FILE_BROWSER_STORE_COLUMN_NAME:
+ g_value_set_string (value, node->name);
+ break;
+ case GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS:
+ g_value_set_uint (value, node->flags);
+ break;
+ case GEDIT_FILE_BROWSER_STORE_COLUMN_ICON:
+ g_value_set_object (value, node->icon);
+ break;
+ case GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM:
+ g_value_set_object (value, node->emblem);
+ break;
+ default:
+ g_return_if_reached ();
+ }
+}
+
+static gboolean
+gedit_file_browser_store_iter_next (GtkTreeModel * tree_model,
+ GtkTreeIter * iter)
+{
+ GeditFileBrowserStore * model;
+ FileBrowserNode * node;
+ GSList * item;
+ GSList * first;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter->user_data != NULL, FALSE);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (node->parent == NULL)
+ return FALSE;
+
+ first = g_slist_next (g_slist_find (FILE_BROWSER_NODE_DIR (node->parent)->children, node));
+
+ for (item = first; item; item = item->next) {
+ if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
+ iter->user_data = item->data;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gedit_file_browser_store_iter_children (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * parent)
+{
+ FileBrowserNode * node;
+ GeditFileBrowserStore * model;
+ GSList * item;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ FALSE);
+ g_return_val_if_fail (parent == NULL
+ || parent->user_data != NULL, FALSE);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+
+ if (parent == NULL)
+ node = model->priv->virtual_root;
+ else
+ node = (FileBrowserNode *) (parent->user_data);
+
+ if (node == NULL)
+ return FALSE;
+
+ if (!NODE_IS_DIR (node))
+ return FALSE;
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
+ if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
+ iter->user_data = item->data;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+filter_tree_model_iter_has_child_real (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ GSList *item;
+
+ if (!NODE_IS_DIR (node))
+ return FALSE;
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
+ if (model_node_inserted (model, (FileBrowserNode *) (item->data)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gedit_file_browser_store_iter_has_child (GtkTreeModel * tree_model,
+ GtkTreeIter * iter)
+{
+ FileBrowserNode *node;
+ GeditFileBrowserStore *model;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ FALSE);
+ g_return_val_if_fail (iter == NULL
+ || iter->user_data != NULL, FALSE);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+
+ if (iter == NULL)
+ node = model->priv->virtual_root;
+ else
+ node = (FileBrowserNode *) (iter->user_data);
+
+ return filter_tree_model_iter_has_child_real (model, node);
+}
+
+static gint
+gedit_file_browser_store_iter_n_children (GtkTreeModel * tree_model,
+ GtkTreeIter * iter)
+{
+ FileBrowserNode *node;
+ GeditFileBrowserStore *model;
+ GSList *item;
+ gint num = 0;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ FALSE);
+ g_return_val_if_fail (iter == NULL
+ || iter->user_data != NULL, FALSE);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+
+ if (iter == NULL)
+ node = model->priv->virtual_root;
+ else
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (!NODE_IS_DIR (node))
+ return 0;
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next)
+ if (model_node_inserted (model, (FileBrowserNode *) (item->data)))
+ ++num;
+
+ return num;
+}
+
+static gboolean
+gedit_file_browser_store_iter_nth_child (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * parent, gint n)
+{
+ FileBrowserNode *node;
+ GeditFileBrowserStore *model;
+ GSList *item;
+ gint num = 0;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model),
+ FALSE);
+ g_return_val_if_fail (parent == NULL
+ || parent->user_data != NULL, FALSE);
+
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+
+ if (parent == NULL)
+ node = model->priv->virtual_root;
+ else
+ node = (FileBrowserNode *) (parent->user_data);
+
+ if (!NODE_IS_DIR (node))
+ return FALSE;
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
+ item = item->next) {
+ if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
+ if (num == n) {
+ iter->user_data = item->data;
+ return TRUE;
+ }
+
+ ++num;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gedit_file_browser_store_iter_parent (GtkTreeModel * tree_model,
+ GtkTreeIter * iter,
+ GtkTreeIter * child)
+{
+ FileBrowserNode *node;
+ GeditFileBrowserStore *model;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model), FALSE);
+ g_return_val_if_fail (child != NULL, FALSE);
+ g_return_val_if_fail (child->user_data != NULL, FALSE);
+
+ node = (FileBrowserNode *) (child->user_data);
+ model = GEDIT_FILE_BROWSER_STORE (tree_model);
+
+ if (!node_in_tree (model, node))
+ return FALSE;
+
+ if (node->parent == NULL)
+ return FALSE;
+
+ iter->user_data = node->parent;
+ return TRUE;
+}
+
+static void
+gedit_file_browser_store_row_inserted (GtkTreeModel * tree_model,
+ GtkTreePath * path,
+ GtkTreeIter * iter)
+{
+ FileBrowserNode * node = (FileBrowserNode *)(iter->user_data);
+
+ node->inserted = TRUE;
+}
+
+static gboolean
+gedit_file_browser_store_row_draggable (GtkTreeDragSource * drag_source,
+ GtkTreePath * path)
+{
+ GtkTreeIter iter;
+ GeditFileBrowserStoreFlag flags;
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source),
+ &iter, path))
+ {
+ return FALSE;
+ }
+
+ gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ return !FILE_IS_DUMMY(flags);
+}
+
+static gboolean
+gedit_file_browser_store_drag_data_delete (GtkTreeDragSource * drag_source,
+ GtkTreePath * path)
+{
+ return FALSE;
+}
+
+static gboolean
+gedit_file_browser_store_drag_data_get (GtkTreeDragSource * drag_source,
+ GtkTreePath * path,
+ GtkSelectionData * selection_data)
+{
+ GtkTreeIter iter;
+ gchar *uri;
+ gchar *uris[2] = {0, };
+ gboolean ret;
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source),
+ &iter, path))
+ {
+ return FALSE;
+ }
+
+ gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ -1);
+
+ g_assert (uri);
+
+ uris[0] = uri;
+ ret = gtk_selection_data_set_uris (selection_data, uris);
+
+ g_free (uri);
+
+ return ret;
+}
+
+#define FILTER_HIDDEN(mode) (mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN)
+#define FILTER_BINARY(mode) (mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY)
+
+/* Private */
+static void
+model_begin_loading (GeditFileBrowserStore * model, FileBrowserNode * node)
+{
+ GtkTreeIter iter;
+
+ iter.user_data = node;
+ g_signal_emit (model, model_signals[BEGIN_LOADING], 0, &iter);
+}
+
+static void
+model_end_loading (GeditFileBrowserStore * model, FileBrowserNode * node)
+{
+ GtkTreeIter iter;
+
+ iter.user_data = node;
+ g_signal_emit (model, model_signals[END_LOADING], 0, &iter);
+}
+
+static void
+model_node_update_visibility (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ GtkTreeIter iter;
+
+ node->flags &= ~GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
+
+ if (FILTER_HIDDEN (model->priv->filter_mode) &&
+ NODE_IS_HIDDEN (node))
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
+ else if (FILTER_BINARY (model->priv->filter_mode) &&
+ (!NODE_IS_TEXT (node) && !NODE_IS_DIR (node)))
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
+ else if (model->priv->filter_func) {
+ iter.user_data = node;
+
+ if (!model->priv->
+ filter_func (model, &iter,
+ model->priv->filter_user_data))
+ node->flags |=
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
+ }
+}
+
+static gint
+collate_nodes (FileBrowserNode * node1, FileBrowserNode * node2)
+{
+ if (node1->name == NULL)
+ return -1;
+ else if (node2->name == NULL)
+ return 1;
+ else {
+ gchar *k1, *k2;
+ gint result;
+
+ k1 = g_utf8_collate_key_for_filename (node1->name, -1);
+ k2 = g_utf8_collate_key_for_filename (node2->name, -1);
+
+ result = strcmp (k1, k2);
+
+ g_free (k1);
+ g_free (k2);
+
+ return result;
+ }
+}
+
+static gint
+model_sort_default (FileBrowserNode * node1, FileBrowserNode * node2)
+{
+ gint f1;
+ gint f2;
+
+ f1 = NODE_IS_DUMMY (node1);
+ f2 = NODE_IS_DUMMY (node2);
+
+ if (f1 && f2)
+ {
+ return 0;
+ }
+ else if (f1 || f2)
+ {
+ return f1 ? -1 : 1;
+ }
+
+ f1 = NODE_IS_DIR (node1);
+ f2 = NODE_IS_DIR (node2);
+
+ if (f1 != f2)
+ {
+ return f1 ? -1 : 1;
+ }
+
+ f1 = NODE_IS_HIDDEN (node1);
+ f2 = NODE_IS_HIDDEN (node2);
+
+ if (f1 != f2)
+ {
+ return f2 ? -1 : 1;
+ }
+
+ return collate_nodes (node1, node2);
+}
+
+static void
+model_resort_node (GeditFileBrowserStore * model, FileBrowserNode * node)
+{
+ FileBrowserNodeDir *dir;
+ GSList *item;
+ FileBrowserNode *child;
+ gint pos = 0;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gint *neworder;
+
+ dir = FILE_BROWSER_NODE_DIR (node->parent);
+
+ if (!model_node_visibility (model, node->parent)) {
+ /* Just sort the children of the parent */
+ dir->children = g_slist_sort (dir->children,
+ (GCompareFunc) (model->priv->
+ sort_func));
+ } else {
+ /* Store current positions */
+ for (item = dir->children; item; item = item->next) {
+ child = (FileBrowserNode *) (item->data);
+
+ if (model_node_visibility (model, child))
+ child->pos = pos++;
+ }
+
+ dir->children = g_slist_sort (dir->children,
+ (GCompareFunc) (model->priv->
+ sort_func));
+ neworder = g_new (gint, pos);
+ pos = 0;
+
+ /* Store the new positions */
+ for (item = dir->children; item; item = item->next) {
+ child = (FileBrowserNode *) (item->data);
+
+ if (model_node_visibility (model, child))
+ neworder[pos++] = child->pos;
+ }
+
+ iter.user_data = node->parent;
+ path =
+ gedit_file_browser_store_get_path_real (model,
+ node->parent);
+
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
+ path, &iter, neworder);
+
+ g_free (neworder);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+row_changed (GeditFileBrowserStore * model,
+ GtkTreePath ** path,
+ GtkTreeIter * iter)
+{
+ GtkTreeRowReference *ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), *path);
+
+ /* Insert a copy of the actual path here because the row-inserted
+ signal may alter the path */
+ gtk_tree_model_row_changed (GTK_TREE_MODEL(model), *path, iter);
+ gtk_tree_path_free (*path);
+
+ *path = gtk_tree_row_reference_get_path (ref);
+ gtk_tree_row_reference_free (ref);
+}
+
+static void
+row_inserted (GeditFileBrowserStore * model,
+ GtkTreePath ** path,
+ GtkTreeIter * iter)
+{
+ /* This function creates a row reference for the path because it's
+ uncertain what might change the actual model/view when we insert
+ a node, maybe another directory load is triggered for example.
+ Because functions that use this function rely on the notion that
+ the path remains pointed towards the inserted node, we use the
+ reference to keep track. */
+ GtkTreeRowReference *ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), *path);
+ GtkTreePath * copy = gtk_tree_path_copy (*path);
+
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), copy, iter);
+ gtk_tree_path_free (copy);
+
+ if (ref)
+ {
+ gtk_tree_path_free (*path);
+
+ /* To restore the path, we get the path from the reference. But, since
+ we inserted a row, the path will be one index further than the
+ actual path of our node. We therefore call gtk_tree_path_prev */
+ *path = gtk_tree_row_reference_get_path (ref);
+ gtk_tree_path_prev (*path);
+ }
+
+ gtk_tree_row_reference_free (ref);
+}
+
+static void
+row_deleted (GeditFileBrowserStore * model,
+ const GtkTreePath * path)
+{
+ GtkTreePath *copy = gtk_tree_path_copy (path);
+
+ /* Delete a copy of the actual path here because the row-deleted
+ signal may alter the path */
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL(model), copy);
+ gtk_tree_path_free (copy);
+}
+
+static void
+model_refilter_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GtkTreePath ** path)
+{
+ gboolean old_visible;
+ gboolean new_visible;
+ FileBrowserNodeDir *dir;
+ GSList *item;
+ GtkTreeIter iter;
+ GtkTreePath *tmppath = NULL;
+ gboolean in_tree;
+
+ if (node == NULL)
+ return;
+
+ old_visible = model_node_visibility (model, node);
+ model_node_update_visibility (model, node);
+
+ in_tree = node_in_tree (model, node);
+
+ if (path == NULL)
+ {
+ if (in_tree)
+ tmppath = gedit_file_browser_store_get_path_real (model,
+ node);
+ else
+ tmppath = gtk_tree_path_new_first ();
+
+ path = &tmppath;
+ }
+
+ if (NODE_IS_DIR (node)) {
+ if (in_tree)
+ gtk_tree_path_down (*path);
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ for (item = dir->children; item; item = item->next) {
+ model_refilter_node (model,
+ (FileBrowserNode *) (item->data),
+ path);
+ }
+
+ if (in_tree)
+ gtk_tree_path_up (*path);
+ }
+
+ if (in_tree) {
+ new_visible = model_node_visibility (model, node);
+
+ if (old_visible != new_visible) {
+ if (old_visible) {
+ node->inserted = FALSE;
+ row_deleted (model, *path);
+ } else {
+ iter.user_data = node;
+ row_inserted (model, path, &iter);
+ gtk_tree_path_next (*path);
+ }
+ } else if (old_visible) {
+ gtk_tree_path_next (*path);
+ }
+ }
+
+ model_check_dummy (model, node);
+
+ if (tmppath)
+ gtk_tree_path_free (tmppath);
+}
+
+static void
+model_refilter (GeditFileBrowserStore * model)
+{
+ model_refilter_node (model, model->priv->root, NULL);
+}
+
+static void
+file_browser_node_set_name (FileBrowserNode * node)
+{
+ g_free (node->name);
+
+ if (node->file) {
+ node->name = gedit_file_browser_utils_file_basename (node->file);
+ } else {
+ node->name = NULL;
+ }
+}
+
+static void
+file_browser_node_init (FileBrowserNode * node, GFile * file,
+ FileBrowserNode * parent)
+{
+ if (file != NULL) {
+ node->file = g_object_ref (file);
+ file_browser_node_set_name (node);
+ }
+
+ node->parent = parent;
+}
+
+static FileBrowserNode *
+file_browser_node_new (GFile * file, FileBrowserNode * parent)
+{
+ FileBrowserNode *node = g_slice_new0 (FileBrowserNode);
+
+ file_browser_node_init (node, file, parent);
+ return node;
+}
+
+static FileBrowserNode *
+file_browser_node_dir_new (GeditFileBrowserStore * model,
+ GFile * file, FileBrowserNode * parent)
+{
+ FileBrowserNode *node =
+ (FileBrowserNode *) g_slice_new0 (FileBrowserNodeDir);
+
+ file_browser_node_init (node, file, parent);
+
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY;
+
+ FILE_BROWSER_NODE_DIR (node)->model = model;
+
+ return node;
+}
+
+static void
+file_browser_node_free_children (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ GSList *item;
+
+ if (node == NULL)
+ return;
+
+ if (NODE_IS_DIR (node)) {
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
+ item = item->next)
+ file_browser_node_free (model,
+ (FileBrowserNode *) (item->
+ data));
+
+ g_slist_free (FILE_BROWSER_NODE_DIR (node)->children);
+ FILE_BROWSER_NODE_DIR (node)->children = NULL;
+
+ /* This node is no longer loaded */
+ node->flags &= ~GEDIT_FILE_BROWSER_STORE_FLAG_LOADED;
+ }
+}
+
+static void
+file_browser_node_free (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ gchar *uri;
+
+ if (node == NULL)
+ return;
+
+ if (NODE_IS_DIR (node))
+ {
+ FileBrowserNodeDir *dir;
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ if (dir->cancellable) {
+ g_cancellable_cancel (dir->cancellable);
+ g_object_unref (dir->cancellable);
+
+ model_end_loading (model, node);
+ }
+
+ file_browser_node_free_children (model, node);
+
+ if (dir->monitor) {
+ g_file_monitor_cancel (dir->monitor);
+ g_object_unref (dir->monitor);
+ }
+
+ if (dir->hidden_file_hash)
+ g_hash_table_destroy (dir->hidden_file_hash);
+ }
+
+ if (node->file)
+ {
+ uri = g_file_get_uri (node->file);
+ g_signal_emit (model, model_signals[UNLOAD], 0, uri);
+
+ g_free (uri);
+ g_object_unref (node->file);
+ }
+
+ if (node->icon)
+ g_object_unref (node->icon);
+
+ if (node->emblem)
+ g_object_unref (node->emblem);
+
+ g_free (node->name);
+
+ if (NODE_IS_DIR (node))
+ g_slice_free (FileBrowserNodeDir, (FileBrowserNodeDir *)node);
+ else
+ g_slice_free (FileBrowserNode, (FileBrowserNode *)node);
+}
+
+/**
+ * model_remove_node_children:
+ * @model: the #GeditFileBrowserStore
+ * @node: the FileBrowserNode to remove
+ * @path: the path of the node, or NULL to let the path be calculated
+ * @free_nodes: whether to also remove the nodes from memory
+ *
+ * Removes all the children of node from the model. This function is used
+ * to remove the child nodes from the _model_. Don't use it to just free
+ * a node.
+ **/
+static void
+model_remove_node_children (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GtkTreePath * path,
+ gboolean free_nodes)
+{
+ FileBrowserNodeDir *dir;
+ GtkTreePath *path_child;
+ GSList *list;
+ GSList *item;
+
+ if (node == NULL || !NODE_IS_DIR (node))
+ return;
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ if (dir->children == NULL)
+ return;
+
+ if (!model_node_visibility (model, node)) {
+ // Node is invisible and therefore the children can just
+ // be freed
+ if (free_nodes)
+ file_browser_node_free_children (model, node);
+
+ return;
+ }
+
+ if (path == NULL)
+ path_child =
+ gedit_file_browser_store_get_path_real (model, node);
+ else
+ path_child = gtk_tree_path_copy (path);
+
+ gtk_tree_path_down (path_child);
+
+ list = g_slist_copy (dir->children);
+
+ for (item = list; item; item = item->next) {
+ model_remove_node (model, (FileBrowserNode *) (item->data),
+ path_child, free_nodes);
+ }
+
+ g_slist_free (list);
+ gtk_tree_path_free (path_child);
+}
+
+/**
+ * model_remove_node:
+ * @model: the #GeditFileBrowserStore
+ * @node: the FileBrowserNode to remove
+ * @path: the path to use to remove this node, or NULL to use the path
+ * calculated from the node itself
+ * @free_nodes: whether to also remove the nodes from memory
+ *
+ * Removes this node and all its children from the model. This function is used
+ * to remove the node from the _model_. Don't use it to just free
+ * a node.
+ **/
+static void
+model_remove_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GtkTreePath * path,
+ gboolean free_nodes)
+{
+ gboolean free_path = FALSE;
+ FileBrowserNode *parent;
+
+ if (path == NULL) {
+ path =
+ gedit_file_browser_store_get_path_real (model, node);
+ free_path = TRUE;
+ }
+
+ model_remove_node_children (model, node, path, free_nodes);
+
+ /* Only delete if the node is visible in the tree (but only when it's
+ not the virtual root) */
+ if (model_node_visibility (model, node) && node != model->priv->virtual_root)
+ {
+ node->inserted = FALSE;
+ row_deleted (model, path);
+ }
+
+ if (free_path)
+ gtk_tree_path_free (path);
+
+ parent = node->parent;
+
+ if (free_nodes) {
+ /* Remove the node from the parents children list */
+ if (parent)
+ FILE_BROWSER_NODE_DIR (node->parent)->children =
+ g_slist_remove (FILE_BROWSER_NODE_DIR
+ (node->parent)->children,
+ node);
+ }
+
+ /* If this is the virtual root, than set the parent as the virtual root */
+ if (node == model->priv->virtual_root)
+ set_virtual_root_from_node (model, parent);
+ else if (parent && model_node_visibility (model, parent) && !(free_nodes && NODE_IS_DUMMY(node)))
+ model_check_dummy (model, parent);
+
+ /* Now free the node if necessary */
+ if (free_nodes)
+ file_browser_node_free (model, node);
+}
+
+/**
+ * model_clear:
+ * @model: the #GeditFileBrowserStore
+ * @free_nodes: whether to also remove the nodes from memory
+ *
+ * Removes all nodes from the model. This function is used
+ * to remove all the nodes from the _model_. Don't use it to just free the
+ * nodes in the model.
+ **/
+static void
+model_clear (GeditFileBrowserStore * model, gboolean free_nodes)
+{
+ GtkTreePath *path;
+ FileBrowserNodeDir *dir;
+ FileBrowserNode *dummy;
+
+ path = gtk_tree_path_new ();
+ model_remove_node_children (model, model->priv->virtual_root, path,
+ free_nodes);
+ gtk_tree_path_free (path);
+
+ /* Remove the dummy if there is one */
+ if (model->priv->virtual_root) {
+ dir = FILE_BROWSER_NODE_DIR (model->priv->virtual_root);
+
+ if (dir->children != NULL) {
+ dummy = (FileBrowserNode *) (dir->children->data);
+
+ if (NODE_IS_DUMMY (dummy)
+ && model_node_visibility (model, dummy)) {
+ path = gtk_tree_path_new_first ();
+
+ dummy->inserted = FALSE;
+ row_deleted (model, path);
+ gtk_tree_path_free (path);
+ }
+ }
+ }
+}
+
+static void
+file_browser_node_unload (GeditFileBrowserStore * model,
+ FileBrowserNode * node, gboolean remove_children)
+{
+ FileBrowserNodeDir *dir;
+
+ if (node == NULL)
+ return;
+
+ if (!NODE_IS_DIR (node) || !NODE_LOADED (node))
+ return;
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ if (remove_children)
+ model_remove_node_children (model, node, NULL, TRUE);
+
+ if (dir->cancellable) {
+ g_cancellable_cancel (dir->cancellable);
+ g_object_unref (dir->cancellable);
+
+ model_end_loading (model, node);
+ dir->cancellable = NULL;
+ }
+
+ if (dir->monitor) {
+ g_file_monitor_cancel (dir->monitor);
+ g_object_unref (dir->monitor);
+
+ dir->monitor = NULL;
+ }
+
+ node->flags &= ~GEDIT_FILE_BROWSER_STORE_FLAG_LOADED;
+}
+
+static void
+model_recomposite_icon_real (GeditFileBrowserStore * tree_model,
+ FileBrowserNode * node,
+ GFileInfo * info)
+{
+ GdkPixbuf *icon;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model));
+ g_return_if_fail (node != NULL);
+
+ if (node->file == NULL)
+ return;
+
+ if (info) {
+ GIcon *gicon = g_file_info_get_icon (info);
+ if (gicon != NULL)
+ icon = gedit_file_browser_utils_pixbuf_from_icon (gicon, GTK_ICON_SIZE_MENU);
+ else
+ icon = NULL;
+ } else {
+ icon = gedit_file_browser_utils_pixbuf_from_file (node->file, GTK_ICON_SIZE_MENU);
+ }
+
+ if (node->icon)
+ g_object_unref (node->icon);
+
+ if (node->emblem) {
+ gint icon_size;
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size);
+
+ if (icon == NULL) {
+ node->icon =
+ gdk_pixbuf_new (gdk_pixbuf_get_colorspace (node->emblem),
+ gdk_pixbuf_get_has_alpha (node->emblem),
+ gdk_pixbuf_get_bits_per_sample (node->emblem),
+ icon_size,
+ icon_size);
+ } else {
+ node->icon = gdk_pixbuf_copy (icon);
+ g_object_unref (icon);
+ }
+
+ gdk_pixbuf_composite (node->emblem, node->icon,
+ icon_size - 10, icon_size - 10, 10,
+ 10, icon_size - 10, icon_size - 10,
+ 1, 1, GDK_INTERP_NEAREST, 255);
+ } else {
+ node->icon = icon;
+ }
+}
+
+static void
+model_recomposite_icon (GeditFileBrowserStore * tree_model,
+ GtkTreeIter * iter)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (iter->user_data != NULL);
+
+ model_recomposite_icon_real (tree_model,
+ (FileBrowserNode *) (iter->user_data),
+ NULL);
+}
+
+static FileBrowserNode *
+model_create_dummy_node (GeditFileBrowserStore * model,
+ FileBrowserNode * parent)
+{
+ FileBrowserNode *dummy;
+
+ dummy = file_browser_node_new (NULL, parent);
+ dummy->name = g_strdup (_("(Empty)"));
+
+ dummy->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_DUMMY;
+ dummy->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ return dummy;
+}
+
+static FileBrowserNode *
+model_add_dummy_node (GeditFileBrowserStore * model,
+ FileBrowserNode * parent)
+{
+ FileBrowserNode *dummy;
+
+ dummy = model_create_dummy_node (model, parent);
+
+ if (model_node_visibility (model, parent))
+ dummy->flags &= ~GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ model_add_node (model, dummy, parent);
+
+ return dummy;
+}
+
+static void
+model_check_dummy (GeditFileBrowserStore * model, FileBrowserNode * node)
+{
+ // Hide the dummy child if needed
+ if (NODE_IS_DIR (node)) {
+ FileBrowserNode *dummy;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ guint flags;
+ FileBrowserNodeDir *dir;
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ if (dir->children == NULL) {
+ model_add_dummy_node (model, node);
+ return;
+ }
+
+ dummy = (FileBrowserNode *) (dir->children->data);
+
+ if (!NODE_IS_DUMMY (dummy)) {
+ dummy = model_create_dummy_node (model, node);
+ dir->children = g_slist_prepend (dir->children, dummy);
+ }
+
+ if (!model_node_visibility (model, node)) {
+ dummy->flags |=
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+ return;
+ }
+
+ /* Temporarily set the node to invisible to check
+ * for real children */
+ flags = dummy->flags;
+ dummy->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ if (!filter_tree_model_iter_has_child_real (model, node)) {
+ dummy->flags &=
+ ~GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ if (FILE_IS_HIDDEN (flags)) {
+ // Was hidden, needs to be inserted
+ iter.user_data = dummy;
+ path =
+ gedit_file_browser_store_get_path_real
+ (model, dummy);
+
+ row_inserted (model, &path, &iter);
+ gtk_tree_path_free (path);
+ }
+ } else {
+ if (!FILE_IS_HIDDEN (flags)) {
+ // Was shown, needs to be removed
+
+ // To get the path we need to set it to visible temporarily
+ dummy->flags &=
+ ~GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+ path =
+ gedit_file_browser_store_get_path_real
+ (model, dummy);
+ dummy->flags |=
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ dummy->inserted = FALSE;
+ row_deleted (model, path);
+ gtk_tree_path_free (path);
+ }
+ }
+ }
+}
+
+static void
+insert_node_sorted (GeditFileBrowserStore * model,
+ FileBrowserNode * child,
+ FileBrowserNode * parent)
+{
+ FileBrowserNodeDir *dir;
+
+ dir = FILE_BROWSER_NODE_DIR (parent);
+
+ if (model->priv->sort_func == NULL) {
+ dir->children = g_slist_append (dir->children, child);
+ } else {
+ dir->children =
+ g_slist_insert_sorted (dir->children, child,
+ (GCompareFunc) (model->priv->
+ sort_func));
+ }
+}
+
+static void
+model_add_node (GeditFileBrowserStore * model, FileBrowserNode * child,
+ FileBrowserNode * parent)
+{
+ /* Add child to parents children */
+ insert_node_sorted (model, child, parent);
+
+ if (model_node_visibility (model, parent) &&
+ model_node_visibility (model, child)) {
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ iter.user_data = child;
+ path = gedit_file_browser_store_get_path_real (model, child);
+
+ /* Emit row inserted */
+ row_inserted (model, &path, &iter);
+ gtk_tree_path_free (path);
+ }
+
+ model_check_dummy (model, parent);
+ model_check_dummy (model, child);
+}
+
+static void
+model_add_nodes_batch (GeditFileBrowserStore * model,
+ GSList * children,
+ FileBrowserNode * parent)
+{
+ GSList *sorted_children;
+ GSList *child;
+ GSList *prev;
+ GSList *l;
+ FileBrowserNodeDir *dir;
+
+ dir = FILE_BROWSER_NODE_DIR (parent);
+
+ sorted_children = g_slist_sort (children, (GCompareFunc) model->priv->sort_func);
+
+ child = sorted_children;
+ l = dir->children;
+ prev = NULL;
+
+ model_check_dummy (model, parent);
+
+ while (child) {
+ FileBrowserNode *node = child->data;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ /* reached the end of the first list, just append the second */
+ if (l == NULL) {
+
+ dir->children = g_slist_concat (dir->children, child);
+
+ for (l = child; l; l = l->next) {
+ if (model_node_visibility (model, parent) &&
+ model_node_visibility (model, l->data)) {
+ iter.user_data = l->data;
+ path = gedit_file_browser_store_get_path_real (model, l->data);
+
+ // Emit row inserted
+ row_inserted (model, &path, &iter);
+ gtk_tree_path_free (path);
+ }
+
+ model_check_dummy (model, l->data);
+ }
+
+ break;
+ }
+
+ if (model->priv->sort_func (l->data, node) > 0) {
+ GSList *next_child;
+
+ if (prev == NULL) {
+ /* prepend to the list */
+ dir->children = g_slist_prepend (dir->children, child);
+ } else {
+ prev->next = child;
+ }
+
+ next_child = child->next;
+ prev = child;
+ child->next = l;
+ child = next_child;
+
+ if (model_node_visibility (model, parent) &&
+ model_node_visibility (model, node)) {
+ iter.user_data = node;
+ path = gedit_file_browser_store_get_path_real (model, node);
+
+ // Emit row inserted
+ row_inserted (model, &path, &iter);
+ gtk_tree_path_free (path);
+ }
+
+ model_check_dummy (model, node);
+
+ /* try again at the same l position with the
+ * next child */
+ } else {
+
+ /* Move to the next item in the list */
+ prev = l;
+ l = l->next;
+ }
+ }
+}
+
+static gchar const *
+backup_content_type (GFileInfo * info)
+{
+ gchar const * content;
+
+ if (!g_file_info_get_is_backup (info))
+ return NULL;
+
+ content = g_file_info_get_content_type (info);
+
+ if (!content || g_content_type_equals (content, "application/x-trash"))
+ return "text/plain";
+
+ return content;
+}
+
+static void
+file_browser_node_set_from_info (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GFileInfo * info,
+ gboolean isadded)
+{
+ FileBrowserNodeDir * dir;
+ gchar const * content;
+ gchar const * name;
+ gboolean free_info = FALSE;
+ GtkTreePath * path;
+ gchar * uri;
+ GError * error = NULL;
+
+ if (info == NULL) {
+ info = g_file_query_info (node->file,
+ STANDARD_ATTRIBUTE_TYPES,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &error);
+
+ if (!info) {
+ if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)) {
+ uri = g_file_get_uri (node->file);
+ g_warning ("Could not get info for %s: %s", uri, error->message);
+ g_free (uri);
+ }
+ g_error_free (error);
+
+ return;
+ }
+
+ free_info = TRUE;
+ }
+
+ dir = FILE_BROWSER_NODE_DIR (node->parent);
+ name = g_file_info_get_name (info);
+
+ if (g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info))
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+ else if (dir != NULL && dir->hidden_file_hash != NULL &&
+ g_hash_table_lookup (dir->hidden_file_hash, name) != NULL)
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY;
+ else {
+ if (!(content = backup_content_type (info)))
+ content = g_file_info_get_content_type (info);
+
+ if (!content ||
+ g_content_type_is_unknown (content) ||
+ g_content_type_is_a (content, "text/plain"))
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_TEXT;
+ }
+
+ model_recomposite_icon_real (model, node, info);
+
+ if (free_info)
+ g_object_unref (info);
+
+ if (isadded) {
+ path = gedit_file_browser_store_get_path_real (model, node);
+ model_refilter_node (model, node, &path);
+ gtk_tree_path_free (path);
+
+ model_check_dummy (model, node->parent);
+ } else {
+ model_node_update_visibility (model, node);
+ }
+}
+
+static FileBrowserNode *
+node_list_contains_file (GSList *children, GFile * file)
+{
+ GSList *item;
+
+ for (item = children; item; item = item->next) {
+ FileBrowserNode *node;
+
+ node = (FileBrowserNode *) (item->data);
+
+ if (node->file != NULL
+ && g_file_equal (node->file, file))
+ return node;
+ }
+
+ return NULL;
+}
+
+static FileBrowserNode *
+model_add_node_from_file (GeditFileBrowserStore * model,
+ FileBrowserNode * parent,
+ GFile * file,
+ GFileInfo * info)
+{
+ FileBrowserNode *node;
+ gboolean free_info = FALSE;
+ GError * error = NULL;
+
+ if ((node = node_list_contains_file (FILE_BROWSER_NODE_DIR (parent)->children, file)) == NULL) {
+ if (info == NULL) {
+ info = g_file_query_info (file,
+ STANDARD_ATTRIBUTE_TYPES,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &error);
+ free_info = TRUE;
+ }
+
+ if (!info) {
+ g_warning ("Error querying file info: %s", error->message);
+ g_error_free (error);
+
+ /* FIXME: What to do now then... */
+ node = file_browser_node_new (file, parent);
+ } else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ node = file_browser_node_dir_new (model, file, parent);
+ } else {
+ node = file_browser_node_new (file, parent);
+ }
+
+ file_browser_node_set_from_info (model, node, info, FALSE);
+ model_add_node (model, node, parent);
+
+ if (info && free_info)
+ g_object_unref (info);
+ }
+
+ return node;
+}
+
+/* We pass in a copy of the list of parent->children so that we do
+ * not have to check if a file already exists among the ones we just
+ * added */
+static void
+model_add_nodes_from_files (GeditFileBrowserStore * model,
+ FileBrowserNode * parent,
+ GSList * original_children,
+ GList * files)
+{
+ GList *item;
+ GSList *nodes = NULL;
+
+ for (item = files; item; item = item->next) {
+ GFileInfo *info = G_FILE_INFO (item->data);
+ GFileType type;
+ gchar const * name;
+ GFile * file;
+ FileBrowserNode *node;
+
+ type = g_file_info_get_file_type (info);
+
+ /* Skip all non regular, non directory files */
+ if (type != G_FILE_TYPE_REGULAR &&
+ type != G_FILE_TYPE_DIRECTORY &&
+ type != G_FILE_TYPE_SYMBOLIC_LINK) {
+ g_object_unref (info);
+ continue;
+ }
+
+ name = g_file_info_get_name (info);
+
+ /* Skip '.' and '..' directories */
+ if (type == G_FILE_TYPE_DIRECTORY &&
+ (strcmp (name, ".") == 0 ||
+ strcmp (name, "..") == 0)) {
+ continue;
+ }
+
+ file = g_file_get_child (parent->file, name);
+
+ if ((node = node_list_contains_file (original_children, file)) == NULL) {
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ node = file_browser_node_dir_new (model, file, parent);
+ } else {
+ node = file_browser_node_new (file, parent);
+ }
+
+ file_browser_node_set_from_info (model, node, info, FALSE);
+
+ nodes = g_slist_prepend (nodes, node);
+ }
+
+ g_object_unref (file);
+ g_object_unref (info);
+ }
+
+ if (nodes)
+ model_add_nodes_batch (model, nodes, parent);
+}
+
+static FileBrowserNode *
+model_add_node_from_dir (GeditFileBrowserStore * model,
+ FileBrowserNode * parent,
+ GFile * file)
+{
+ FileBrowserNode *node;
+
+ /* Check if it already exists */
+ if ((node = node_list_contains_file (FILE_BROWSER_NODE_DIR (parent)->children, file)) == NULL) {
+ node = file_browser_node_dir_new (model, file, parent);
+ file_browser_node_set_from_info (model, node, NULL, FALSE);
+
+ if (node->name == NULL) {
+ file_browser_node_set_name (node);
+ }
+
+ if (node->icon == NULL) {
+ node->icon = gedit_file_browser_utils_pixbuf_from_theme ("folder", GTK_ICON_SIZE_MENU);
+ }
+
+ model_add_node (model, node, parent);
+ }
+
+ return node;
+}
+
+/* Read is sync, but we only do it for local files */
+static void
+parse_dot_hidden_file (FileBrowserNode *directory)
+{
+ gsize file_size;
+ char *file_contents;
+ GFile *child;
+ GFileInfo *info;
+ GFileType type;
+ int i;
+ FileBrowserNodeDir * dir = FILE_BROWSER_NODE_DIR (directory);
+
+ /* FIXME: We only support .hidden on file: uri's for the moment.
+ * Need to figure out if we should do this async or sync to extend
+ * it to all types of uris.
+ */
+ if (directory->file == NULL || !g_file_is_native (directory->file)) {
+ return;
+ }
+
+ child = g_file_get_child (directory->file, ".hidden");
+ info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+ type = info ? g_file_info_get_file_type (info) : G_FILE_TYPE_UNKNOWN;
+
+ if (info)
+ g_object_unref (info);
+
+ if (type != G_FILE_TYPE_REGULAR) {
+ g_object_unref (child);
+
+ return;
+ }
+
+ if (!g_file_load_contents (child, NULL, &file_contents, &file_size, NULL, NULL)) {
+ g_object_unref (child);
+ return;
+ }
+
+ g_object_unref (child);
+
+ if (dir->hidden_file_hash == NULL) {
+ dir->hidden_file_hash =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ }
+
+ /* Now parse the data */
+ i = 0;
+ while (i < file_size) {
+ int start;
+
+ start = i;
+ while (i < file_size && file_contents[i] != '\n') {
+ i++;
+ }
+
+ if (i > start) {
+ char *hidden_filename;
+
+ hidden_filename = g_strndup (file_contents + start, i - start);
+ g_hash_table_insert (dir->hidden_file_hash,
+ hidden_filename, hidden_filename);
+ }
+
+ i++;
+
+ }
+
+ g_free (file_contents);
+}
+
+static void
+on_directory_monitor_event (GFileMonitor * monitor,
+ GFile * file,
+ GFile * other_file,
+ GFileMonitorEvent event_type,
+ FileBrowserNode * parent)
+{
+ FileBrowserNode *node;
+ FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (parent);
+
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_DELETED:
+ node = node_list_contains_file (dir->children, file);
+
+ if (node != NULL) {
+ model_remove_node (dir->model, node, NULL, TRUE);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_CREATED:
+ if (g_file_query_exists (file, NULL)) {
+ model_add_node_from_file (dir->model, parent, file, NULL);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+async_node_free (AsyncNode *async)
+{
+ g_object_unref (async->cancellable);
+ g_slist_free (async->original_children);
+ g_free (async);
+}
+
+static void
+model_iterate_next_files_cb (GFileEnumerator * enumerator,
+ GAsyncResult * result,
+ AsyncNode * async)
+{
+ GList * files;
+ GError * error = NULL;
+ FileBrowserNodeDir * dir = async->dir;
+ FileBrowserNode * parent = (FileBrowserNode *)dir;
+
+ files = g_file_enumerator_next_files_finish (enumerator, result, &error);
+
+ if (files == NULL) {
+ g_file_enumerator_close (enumerator, NULL, NULL);
+ async_node_free (async);
+
+ if (!error)
+ {
+ /* We're done loading */
+ g_object_unref (dir->cancellable);
+ dir->cancellable = NULL;
+
+/*
+ * FIXME: This is temporarly, it is a bug in gio:
+ * http://bugzilla.mate.org/show_bug.cgi?id=565924
+ */
+#ifndef G_OS_WIN32
+ if (g_file_is_native (parent->file) && dir->monitor == NULL) {
+ dir->monitor = g_file_monitor_directory (parent->file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ NULL);
+ if (dir->monitor != NULL)
+ {
+ g_signal_connect (dir->monitor,
+ "changed",
+ G_CALLBACK (on_directory_monitor_event),
+ parent);
+ }
+ }
+#endif
+
+ model_check_dummy (dir->model, parent);
+ model_end_loading (dir->model, parent);
+ } else {
+ /* Simply return if we were cancelled */
+ if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
+ return;
+
+ /* Otherwise handle the error appropriately */
+ g_signal_emit (dir->model,
+ model_signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY,
+ error->message);
+
+ file_browser_node_unload (dir->model, (FileBrowserNode *)parent, TRUE);
+ g_error_free (error);
+ }
+ } else if (g_cancellable_is_cancelled (async->cancellable)) {
+ /* Check cancel state manually */
+ g_file_enumerator_close (enumerator, NULL, NULL);
+ async_node_free (async);
+ } else {
+ model_add_nodes_from_files (dir->model, parent, async->original_children, files);
+
+ g_list_free (files);
+ next_files_async (enumerator, async);
+ }
+}
+
+static void
+next_files_async (GFileEnumerator * enumerator,
+ AsyncNode * async)
+{
+ g_file_enumerator_next_files_async (enumerator,
+ DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
+ G_PRIORITY_DEFAULT,
+ async->cancellable,
+ (GAsyncReadyCallback)model_iterate_next_files_cb,
+ async);
+}
+
+static void
+model_iterate_children_cb (GFile * file,
+ GAsyncResult * result,
+ AsyncNode * async)
+{
+ GError * error = NULL;
+ GFileEnumerator * enumerator;
+
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_node_free (async);
+ return;
+ }
+
+ enumerator = g_file_enumerate_children_finish (file, result, &error);
+
+ if (enumerator == NULL) {
+ /* Simply return if we were cancelled or if the dir is not there */
+ FileBrowserNodeDir *dir = async->dir;
+
+ /* Otherwise handle the error appropriately */
+ g_signal_emit (dir->model,
+ model_signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY,
+ error->message);
+
+ file_browser_node_unload (dir->model, (FileBrowserNode *)dir, TRUE);
+ g_error_free (error);
+ async_node_free (async);
+ } else {
+ next_files_async (enumerator, async);
+ }
+}
+
+static void
+model_load_directory (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ FileBrowserNodeDir *dir;
+ AsyncNode *async;
+
+ g_return_if_fail (NODE_IS_DIR (node));
+
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ /* Cancel a previous load */
+ if (dir->cancellable != NULL) {
+ file_browser_node_unload (dir->model, node, TRUE);
+ }
+
+ node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_LOADED;
+ model_begin_loading (model, node);
+
+ /* Read the '.hidden' file first (if any) */
+ parse_dot_hidden_file (node);
+
+ dir->cancellable = g_cancellable_new ();
+
+ async = g_new (AsyncNode, 1);
+ async->dir = dir;
+ async->cancellable = g_object_ref (dir->cancellable);
+ async->original_children = g_slist_copy (dir->children);
+
+ /* Start loading async */
+ g_file_enumerate_children_async (node->file,
+ STANDARD_ATTRIBUTE_TYPES,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ async->cancellable,
+ (GAsyncReadyCallback)model_iterate_children_cb,
+ async);
+}
+
+static GList *
+get_parent_files (GeditFileBrowserStore * model, GFile * file)
+{
+ GList * result = NULL;
+
+ result = g_list_prepend (result, g_object_ref (file));
+
+ while ((file = g_file_get_parent (file))) {
+ if (g_file_equal (file, model->priv->root->file)) {
+ g_object_unref (file);
+ break;
+ }
+
+ result = g_list_prepend (result, file);
+ }
+
+ return result;
+}
+
+static void
+model_fill (GeditFileBrowserStore * model, FileBrowserNode * node,
+ GtkTreePath ** path)
+{
+ gboolean free_path = FALSE;
+ GtkTreeIter iter = {0,};
+ GSList *item;
+ FileBrowserNode *child;
+
+ if (node == NULL) {
+ node = model->priv->virtual_root;
+ *path = gtk_tree_path_new ();
+ free_path = TRUE;
+ }
+
+ if (*path == NULL) {
+ *path =
+ gedit_file_browser_store_get_path_real (model, node);
+ free_path = TRUE;
+ }
+
+ if (!model_node_visibility (model, node)) {
+ if (free_path)
+ gtk_tree_path_free (*path);
+
+ return;
+ }
+
+ if (node != model->priv->virtual_root) {
+ /* Insert node */
+ iter.user_data = node;
+
+ row_inserted(model, path, &iter);
+ }
+
+ if (NODE_IS_DIR (node)) {
+ /* Go to the first child */
+ gtk_tree_path_down (*path);
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
+ item = item->next) {
+ child = (FileBrowserNode *) (item->data);
+
+ if (model_node_visibility (model, child)) {
+ model_fill (model, child, path);
+
+ /* Increase path for next child */
+ gtk_tree_path_next (*path);
+ }
+ }
+
+ /* Move back up to node path */
+ gtk_tree_path_up (*path);
+ }
+
+ model_check_dummy (model, node);
+
+ if (free_path)
+ gtk_tree_path_free (*path);
+}
+
+static void
+set_virtual_root_from_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node)
+{
+ FileBrowserNode *next;
+ FileBrowserNode *prev;
+ FileBrowserNode *check;
+ FileBrowserNodeDir *dir;
+ GSList *item;
+ GSList *copy;
+ GtkTreePath *empty = NULL;
+
+ prev = node;
+ next = prev->parent;
+
+ /* Free all the nodes below that we don't need in cache */
+ while (prev != model->priv->root) {
+ dir = FILE_BROWSER_NODE_DIR (next);
+ copy = g_slist_copy (dir->children);
+
+ for (item = copy; item; item = item->next) {
+ check = (FileBrowserNode *) (item->data);
+
+ if (prev == node) {
+ /* Only free the children, keeping this depth in cache */
+ if (check != node) {
+ file_browser_node_free_children
+ (model, check);
+ file_browser_node_unload (model,
+ check,
+ FALSE);
+ }
+ } else if (check != prev) {
+ /* Only free when the node is not in the chain */
+ dir->children =
+ g_slist_remove (dir->children, check);
+ file_browser_node_free (model, check);
+ }
+ }
+
+ if (prev != node)
+ file_browser_node_unload (model, next, FALSE);
+
+ g_slist_free (copy);
+ prev = next;
+ next = prev->parent;
+ }
+
+ /* Free all the nodes up that we don't need in cache */
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
+ item = item->next) {
+ check = (FileBrowserNode *) (item->data);
+
+ if (NODE_IS_DIR (check)) {
+ for (copy =
+ FILE_BROWSER_NODE_DIR (check)->children; copy;
+ copy = copy->next) {
+ file_browser_node_free_children (model,
+ (FileBrowserNode
+ *)
+ (copy->
+ data));
+ file_browser_node_unload (model,
+ (FileBrowserNode
+ *) (copy->data),
+ FALSE);
+ }
+ } else if (NODE_IS_DUMMY (check)) {
+ check->flags |=
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
+ }
+ }
+
+ /* Now finally, set the virtual root, and load it up! */
+ model->priv->virtual_root = node;
+
+ /* Notify that the virtual-root has changed before loading up new nodes so that the
+ "root_changed" signal can be emitted before any "inserted" signals */
+ g_object_notify (G_OBJECT (model), "virtual-root");
+
+ model_fill (model, NULL, &empty);
+
+ if (!NODE_LOADED (node))
+ model_load_directory (model, node);
+}
+
+static void
+set_virtual_root_from_file (GeditFileBrowserStore * model,
+ GFile * file)
+{
+ GList * files;
+ GList * item;
+ FileBrowserNode * parent;
+ GFile * check;
+
+ /* Always clear the model before altering the nodes */
+ model_clear (model, FALSE);
+
+ /* Create the node path, get all the uri's */
+ files = get_parent_files (model, file);
+ parent = model->priv->root;
+
+ for (item = files; item; item = item->next) {
+ check = G_FILE (item->data);
+
+ parent = model_add_node_from_dir (model, parent, check);
+ g_object_unref (check);
+ }
+
+ g_list_free (files);
+ set_virtual_root_from_node (model, parent);
+}
+
+static FileBrowserNode *
+model_find_node_children (GeditFileBrowserStore * model,
+ FileBrowserNode * parent,
+ GFile * file)
+{
+ FileBrowserNodeDir *dir;
+ FileBrowserNode *child;
+ FileBrowserNode *result;
+ GSList *children;
+
+ if (!NODE_IS_DIR (parent))
+ return NULL;
+
+ dir = FILE_BROWSER_NODE_DIR (parent);
+
+ for (children = dir->children; children; children = children->next) {
+ child = (FileBrowserNode *)(children->data);
+
+ result = model_find_node (model, child, file);
+
+ if (result)
+ return result;
+ }
+
+ return NULL;
+}
+
+static FileBrowserNode *
+model_find_node (GeditFileBrowserStore * model,
+ FileBrowserNode * node,
+ GFile * file)
+{
+ if (node == NULL)
+ node = model->priv->root;
+
+ if (node->file && g_file_equal (node->file, file))
+ return node;
+
+ if (NODE_IS_DIR (node) && g_file_has_prefix (file, node->file))
+ return model_find_node_children (model, node, file);
+
+ return NULL;
+}
+
+static GQuark
+gedit_file_browser_store_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0)) {
+ quark = g_quark_from_string ("gedit_file_browser_store_error");
+ }
+
+ return quark;
+}
+
+static GFile *
+unique_new_name (GFile * directory, gchar const * name)
+{
+ GFile * newuri = NULL;
+ guint num = 0;
+ gchar * newname;
+
+ while (newuri == NULL || g_file_query_exists (newuri, NULL)) {
+ if (newuri != NULL)
+ g_object_unref (newuri);
+
+ if (num == 0)
+ newname = g_strdup (name);
+ else
+ newname = g_strdup_printf ("%s(%d)", name, num);
+
+ newuri = g_file_get_child (directory, newname);
+ g_free (newname);
+
+ ++num;
+ }
+
+ return newuri;
+}
+
+static GeditFileBrowserStoreResult
+model_root_mounted (GeditFileBrowserStore * model, gchar const * virtual_root)
+{
+ model_check_dummy (model, model->priv->root);
+ g_object_notify (G_OBJECT (model), "root");
+
+ if (virtual_root != NULL)
+ return
+ gedit_file_browser_store_set_virtual_root_from_string
+ (model, virtual_root);
+ else
+ set_virtual_root_from_node (model,
+ model->priv->root);
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+static void
+handle_root_error (GeditFileBrowserStore * model, GError *error)
+{
+ FileBrowserNode * root;
+
+ g_signal_emit (model,
+ model_signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_SET_ROOT,
+ error->message);
+
+ /* Set the virtual root to the root */
+ root = model->priv->root;
+ model->priv->virtual_root = root;
+
+ /* Set the root to be loaded */
+ root->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_LOADED;
+
+ /* Check the dummy */
+ model_check_dummy (model, root);
+
+ g_object_notify (G_OBJECT (model), "root");
+ g_object_notify (G_OBJECT (model), "virtual-root");
+}
+
+static void
+mount_cb (GFile * file,
+ GAsyncResult * res,
+ MountInfo * mount_info)
+{
+ gboolean mounted;
+ GError * error = NULL;
+ GeditFileBrowserStore * model = mount_info->model;
+
+ mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
+
+ if (mount_info->model)
+ {
+ model->priv->mount_info = NULL;
+ model_end_loading (model, model->priv->root);
+ }
+
+ if (!mount_info->model || g_cancellable_is_cancelled (mount_info->cancellable))
+ {
+ // Reset because it might be reused?
+ g_cancellable_reset (mount_info->cancellable);
+ }
+ else if (mounted)
+ {
+ model_root_mounted (model, mount_info->virtual_root);
+ }
+ else if (error->code != G_IO_ERROR_CANCELLED)
+ {
+ handle_root_error (model, error);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ g_object_unref (mount_info->operation);
+ g_object_unref (mount_info->cancellable);
+ g_free (mount_info->virtual_root);
+
+ g_free (mount_info);
+}
+
+static GeditFileBrowserStoreResult
+model_mount_root (GeditFileBrowserStore * model, gchar const * virtual_root)
+{
+ GFileInfo * info;
+ GError * error = NULL;
+ MountInfo * mount_info;
+
+ info = g_file_query_info (model->priv->root->file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ &error);
+
+ if (!info) {
+ if (error->code == G_IO_ERROR_NOT_MOUNTED) {
+ /* Try to mount it */
+ FILE_BROWSER_NODE_DIR (model->priv->root)->cancellable = g_cancellable_new ();
+
+ mount_info = g_new(MountInfo, 1);
+ mount_info->model = model;
+ mount_info->virtual_root = g_strdup (virtual_root);
+
+ /* FIXME: we should be setting the correct window */
+ mount_info->operation = gtk_mount_operation_new (NULL);
+ mount_info->cancellable = g_object_ref (FILE_BROWSER_NODE_DIR (model->priv->root)->cancellable);
+
+ model_begin_loading (model, model->priv->root);
+ g_file_mount_enclosing_volume (model->priv->root->file,
+ G_MOUNT_MOUNT_NONE,
+ mount_info->operation,
+ mount_info->cancellable,
+ (GAsyncReadyCallback)mount_cb,
+ mount_info);
+
+ model->priv->mount_info = mount_info;
+ return GEDIT_FILE_BROWSER_STORE_RESULT_MOUNTING;
+ }
+ else
+ {
+ handle_root_error (model, error);
+ }
+
+ g_error_free (error);
+ } else {
+ g_object_unref (info);
+
+ return model_root_mounted (model, virtual_root);
+ }
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+/* Public */
+GeditFileBrowserStore *
+gedit_file_browser_store_new (gchar const *root)
+{
+ GeditFileBrowserStore *obj =
+ GEDIT_FILE_BROWSER_STORE (g_object_new
+ (GEDIT_TYPE_FILE_BROWSER_STORE,
+ NULL));
+
+ gedit_file_browser_store_set_root (obj, root);
+ return obj;
+}
+
+void
+gedit_file_browser_store_set_value (GeditFileBrowserStore * tree_model,
+ GtkTreeIter * iter, gint column,
+ GValue * value)
+{
+ gpointer data;
+ FileBrowserNode *node;
+ GtkTreePath *path;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (tree_model));
+ g_return_if_fail (column ==
+ GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM);
+ g_return_if_fail (G_VALUE_HOLDS_OBJECT (value));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (iter->user_data != NULL);
+
+ data = g_value_get_object (value);
+
+ if (data)
+ g_return_if_fail (GDK_IS_PIXBUF (data));
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (node->emblem)
+ g_object_unref (node->emblem);
+
+ if (data)
+ node->emblem = g_object_ref (GDK_PIXBUF (data));
+ else
+ node->emblem = NULL;
+
+ model_recomposite_icon (tree_model, iter);
+
+ if (model_node_visibility (tree_model, node)) {
+ path = gedit_file_browser_store_get_path (GTK_TREE_MODEL (tree_model),
+ iter);
+ row_changed (tree_model, &path, iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root (GeditFileBrowserStore * model,
+ GtkTreeIter * iter)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+ g_return_val_if_fail (iter != NULL,
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+ g_return_val_if_fail (iter->user_data != NULL,
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ model_clear (model, FALSE);
+ set_virtual_root_from_node (model,
+ (FileBrowserNode *) (iter->user_data));
+
+ return TRUE;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_from_string
+ (GeditFileBrowserStore * model, gchar const *root) {
+ GFile *file;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ file = g_file_new_for_uri (root);
+ if (file == NULL) {
+ g_warning ("Invalid uri (%s)", root);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+ }
+
+ /* Check if uri is already the virtual root */
+ if (model->priv->virtual_root &&
+ g_file_equal (model->priv->virtual_root->file, file)) {
+ g_object_unref (file);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+ }
+
+ /* Check if uri is the root itself */
+ if (g_file_equal (model->priv->root->file, file)) {
+ g_object_unref (file);
+
+ /* Always clear the model before altering the nodes */
+ model_clear (model, FALSE);
+ set_virtual_root_from_node (model, model->priv->root);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+ }
+
+ if (!g_file_has_prefix (file, model->priv->root->file)) {
+ gchar *str, *str1;
+
+ str = g_file_get_parse_name (model->priv->root->file);
+ str1 = g_file_get_parse_name (file);
+
+ g_warning
+ ("Virtual root (%s) is not below actual root (%s)",
+ str1, str);
+
+ g_free (str);
+ g_free (str1);
+
+ g_object_unref (file);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_ERROR;
+ }
+
+ set_virtual_root_from_file (model, file);
+ g_object_unref (file);
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_top (GeditFileBrowserStore *
+ model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ if (model->priv->virtual_root == model->priv->root)
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+
+ model_clear (model, FALSE);
+ set_virtual_root_from_node (model, model->priv->root);
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_up (GeditFileBrowserStore *
+ model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ if (model->priv->virtual_root == model->priv->root)
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+
+ model_clear (model, FALSE);
+ set_virtual_root_from_node (model,
+ model->priv->virtual_root->parent);
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+gboolean
+gedit_file_browser_store_get_iter_virtual_root (GeditFileBrowserStore *
+ model, GtkTreeIter * iter)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (model->priv->virtual_root == NULL)
+ return FALSE;
+
+ iter->user_data = model->priv->virtual_root;
+ return TRUE;
+}
+
+gboolean
+gedit_file_browser_store_get_iter_root (GeditFileBrowserStore * model,
+ GtkTreeIter * iter)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (model->priv->root == NULL)
+ return FALSE;
+
+ iter->user_data = model->priv->root;
+ return TRUE;
+}
+
+gboolean
+gedit_file_browser_store_iter_equal (GeditFileBrowserStore * model,
+ GtkTreeIter * iter1,
+ GtkTreeIter * iter2)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (iter1 != NULL, FALSE);
+ g_return_val_if_fail (iter2 != NULL, FALSE);
+ g_return_val_if_fail (iter1->user_data != NULL, FALSE);
+ g_return_val_if_fail (iter2->user_data != NULL, FALSE);
+
+ return (iter1->user_data == iter2->user_data);
+}
+
+void
+gedit_file_browser_store_cancel_mount_operation (GeditFileBrowserStore *store)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (store));
+
+ cancel_mount_operation (store);
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_root_and_virtual_root (GeditFileBrowserStore *
+ model,
+ gchar const *root,
+ gchar const *virtual_root)
+{
+ GFile * file = NULL;
+ GFile * vfile = NULL;
+ FileBrowserNode * node;
+ gboolean equal = FALSE;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ if (root == NULL && model->priv->root == NULL)
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+
+ if (root != NULL) {
+ file = g_file_new_for_uri (root);
+ }
+
+ if (root != NULL && model->priv->root != NULL) {
+ equal = g_file_equal (file, model->priv->root->file);
+
+ if (equal && virtual_root == NULL) {
+ g_object_unref (file);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+ }
+ }
+
+ if (virtual_root) {
+ vfile = g_file_new_for_uri (virtual_root);
+
+ if (equal && g_file_equal (vfile, model->priv->virtual_root->file)) {
+ if (file)
+ g_object_unref (file);
+
+ g_object_unref (vfile);
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+ }
+
+ g_object_unref (vfile);
+ }
+
+ /* make sure to cancel any previous mount operations */
+ cancel_mount_operation (model);
+
+ /* Always clear the model before altering the nodes */
+ model_clear (model, TRUE);
+ file_browser_node_free (model, model->priv->root);
+
+ model->priv->root = NULL;
+ model->priv->virtual_root = NULL;
+
+ if (file != NULL) {
+ /* Create the root node */
+ node = file_browser_node_dir_new (model, file, NULL);
+
+ g_object_unref (file);
+
+ model->priv->root = node;
+ return model_mount_root (model, virtual_root);
+ } else {
+ g_object_notify (G_OBJECT (model), "root");
+ g_object_notify (G_OBJECT (model), "virtual-root");
+ }
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_root (GeditFileBrowserStore * model,
+ gchar const *root)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model),
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+ return gedit_file_browser_store_set_root_and_virtual_root (model,
+ root,
+ NULL);
+}
+
+gchar *
+gedit_file_browser_store_get_root (GeditFileBrowserStore * model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), NULL);
+
+ if (model->priv->root == NULL || model->priv->root->file == NULL)
+ return NULL;
+ else
+ return g_file_get_uri (model->priv->root->file);
+}
+
+gchar *
+gedit_file_browser_store_get_virtual_root (GeditFileBrowserStore * model)
+{
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), NULL);
+
+ if (model->priv->virtual_root == NULL || model->priv->virtual_root->file == NULL)
+ return NULL;
+ else
+ return g_file_get_uri (model->priv->virtual_root->file);
+}
+
+void
+_gedit_file_browser_store_iter_expanded (GeditFileBrowserStore * model,
+ GtkTreeIter * iter)
+{
+ FileBrowserNode *node;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (iter->user_data != NULL);
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (NODE_IS_DIR (node) && !NODE_LOADED (node)) {
+ /* Load it now */
+ model_load_directory (model, node);
+ }
+}
+
+void
+_gedit_file_browser_store_iter_collapsed (GeditFileBrowserStore * model,
+ GtkTreeIter * iter)
+{
+ FileBrowserNode *node;
+ GSList *item;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (iter->user_data != NULL);
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (NODE_IS_DIR (node) && NODE_LOADED (node)) {
+ /* Unload children of the children, keeping 1 depth in cache */
+
+ for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
+ item = item->next) {
+ node = (FileBrowserNode *) (item->data);
+
+ if (NODE_IS_DIR (node) && NODE_LOADED (node)) {
+ file_browser_node_unload (model, node,
+ TRUE);
+ model_check_dummy (model, node);
+ }
+ }
+ }
+}
+
+GeditFileBrowserStoreFilterMode
+gedit_file_browser_store_get_filter_mode (GeditFileBrowserStore * model)
+{
+ return model->priv->filter_mode;
+}
+
+void
+gedit_file_browser_store_set_filter_mode (GeditFileBrowserStore * model,
+ GeditFileBrowserStoreFilterMode
+ mode)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model));
+
+ if (model->priv->filter_mode == mode)
+ return;
+
+ model->priv->filter_mode = mode;
+ model_refilter (model);
+
+ g_object_notify (G_OBJECT (model), "filter-mode");
+}
+
+void
+gedit_file_browser_store_set_filter_func (GeditFileBrowserStore * model,
+ GeditFileBrowserStoreFilterFunc
+ func, gpointer user_data)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model));
+
+ model->priv->filter_func = func;
+ model->priv->filter_user_data = user_data;
+ model_refilter (model);
+}
+
+void
+gedit_file_browser_store_refilter (GeditFileBrowserStore * model)
+{
+ model_refilter (model);
+}
+
+GeditFileBrowserStoreFilterMode
+gedit_file_browser_store_filter_mode_get_default (void)
+{
+ return GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
+}
+
+void
+gedit_file_browser_store_refresh (GeditFileBrowserStore * model)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model));
+
+ if (model->priv->root == NULL || model->priv->virtual_root == NULL)
+ return;
+
+ /* Clear the model */
+ g_signal_emit (model, model_signals[BEGIN_REFRESH], 0);
+ file_browser_node_unload (model, model->priv->virtual_root, TRUE);
+ model_load_directory (model, model->priv->virtual_root);
+ g_signal_emit (model, model_signals[END_REFRESH], 0);
+}
+
+static void
+reparent_node (FileBrowserNode * node, gboolean reparent)
+{
+ FileBrowserNodeDir * dir;
+ GSList * child;
+ GFile * parent;
+ gchar * base;
+
+ if (!node->file) {
+ return;
+ }
+
+ if (reparent) {
+ parent = node->parent->file;
+ base = g_file_get_basename (node->file);
+ g_object_unref (node->file);
+
+ node->file = g_file_get_child (parent, base);
+ g_free (base);
+ }
+
+ if (NODE_IS_DIR (node)) {
+ dir = FILE_BROWSER_NODE_DIR (node);
+
+ for (child = dir->children; child; child = child->next) {
+ reparent_node ((FileBrowserNode *)child->data, TRUE);
+ }
+ }
+}
+
+gboolean
+gedit_file_browser_store_rename (GeditFileBrowserStore * model,
+ GtkTreeIter * iter,
+ const gchar * new_name,
+ GError ** error)
+{
+ FileBrowserNode *node;
+ GFile * file;
+ GFile * parent;
+ GFile * previous;
+ GError * err = NULL;
+ gchar * olduri;
+ gchar * newuri;
+ GtkTreePath *path;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter->user_data != NULL, FALSE);
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ parent = g_file_get_parent (node->file);
+ g_return_val_if_fail (parent != NULL, FALSE);
+
+ file = g_file_get_child (parent, new_name);
+ g_object_unref (parent);
+
+ if (g_file_equal (node->file, file)) {
+ g_object_unref (file);
+ return TRUE;
+ }
+
+ if (g_file_move (node->file, file, G_FILE_COPY_NONE, NULL, NULL, NULL, &err)) {
+ previous = node->file;
+ node->file = file;
+
+ /* This makes sure the actual info for the node is requeried */
+ file_browser_node_set_name (node);
+ file_browser_node_set_from_info (model, node, NULL, TRUE);
+
+ reparent_node (node, FALSE);
+
+ if (model_node_visibility (model, node)) {
+ path = gedit_file_browser_store_get_path_real (model, node);
+ row_changed (model, &path, iter);
+ gtk_tree_path_free (path);
+
+ /* Reorder this item */
+ model_resort_node (model, node);
+ } else {
+ g_object_unref (previous);
+
+ if (error != NULL)
+ *error = g_error_new_literal (gedit_file_browser_store_error_quark (),
+ GEDIT_FILE_BROWSER_ERROR_RENAME,
+ _("The renamed file is currently filtered out. You need to adjust your filter settings to make the file visible"));
+ return FALSE;
+ }
+
+ olduri = g_file_get_uri (previous);
+ newuri = g_file_get_uri (node->file);
+
+ g_signal_emit (model, model_signals[RENAME], 0, olduri, newuri);
+
+ g_object_unref (previous);
+ g_free (olduri);
+ g_free (newuri);
+
+ return TRUE;
+ } else {
+ g_object_unref (file);
+
+ if (err) {
+ if (error != NULL) {
+ *error =
+ g_error_new_literal
+ (gedit_file_browser_store_error_quark (),
+ GEDIT_FILE_BROWSER_ERROR_RENAME,
+ err->message);
+ }
+
+ g_error_free (err);
+ }
+
+ return FALSE;
+ }
+}
+
+static void
+async_data_free (AsyncData * data)
+{
+ g_object_unref (data->cancellable);
+
+ g_list_foreach (data->files, (GFunc)g_object_unref, NULL);
+ g_list_free (data->files);
+
+ if (!data->removed)
+ data->model->priv->async_handles = g_slist_remove (data->model->priv->async_handles, data);
+
+ g_free (data);
+}
+
+static gboolean
+emit_no_trash (AsyncData * data)
+{
+ /* Emit the no trash error */
+ gboolean ret;
+
+ g_signal_emit (data->model, model_signals[NO_TRASH], 0, data->files, &ret);
+ return ret;
+}
+
+typedef struct {
+ GeditFileBrowserStore * model;
+ GFile * file;
+} IdleDelete;
+
+static gboolean
+file_deleted (IdleDelete * data)
+{
+ FileBrowserNode * node;
+ node = model_find_node (data->model, NULL, data->file);
+
+ if (node != NULL)
+ model_remove_node (data->model, node, NULL, TRUE);
+
+ return FALSE;
+}
+
+static gboolean
+delete_files (GIOSchedulerJob * job,
+ GCancellable * cancellable,
+ AsyncData * data)
+{
+ GFile * file;
+ GError * error = NULL;
+ gboolean ret;
+ gint code;
+ IdleDelete delete;
+
+ /* Check if our job is done */
+ if (!data->iter)
+ return FALSE;
+
+ /* Move a file to the trash */
+ file = G_FILE (data->iter->data);
+
+ if (data->trash)
+ ret = g_file_trash (file, cancellable, &error);
+ else
+ ret = g_file_delete (file, cancellable, &error);
+
+ if (ret) {
+ delete.model = data->model;
+ delete.file = file;
+
+ /* Remove the file from the model in the main loop */
+ g_io_scheduler_job_send_to_mainloop (job, (GSourceFunc)file_deleted, &delete, NULL);
+ } else if (!ret && error) {
+ code = error->code;
+ g_error_free (error);
+
+ if (data->trash && code == G_IO_ERROR_NOT_SUPPORTED) {
+ /* Trash is not supported on this system ... */
+ if (g_io_scheduler_job_send_to_mainloop (job, (GSourceFunc)emit_no_trash, data, NULL))
+ {
+ /* Changes this into a delete job */
+ data->trash = FALSE;
+ data->iter = data->files;
+
+ return TRUE;
+ }
+
+ /* End the job */
+ return FALSE;
+ } else if (code == G_IO_ERROR_CANCELLED) {
+ /* Job has been cancelled, just let the job end */
+ return FALSE;
+ }
+ }
+
+ /* Process the next item */
+ data->iter = data->iter->next;
+ return TRUE;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_delete_all (GeditFileBrowserStore *model,
+ GList *rows, gboolean trash)
+{
+ FileBrowserNode * node;
+ AsyncData * data;
+ GList * files = NULL;
+ GList * row;
+ GtkTreeIter iter;
+ GtkTreePath * prev = NULL;
+ GtkTreePath * path;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ if (rows == NULL)
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+
+ /* First we sort the paths so that we can later on remove any
+ files/directories that are actually subfiles/directories of
+ a directory that's also deleted */
+ rows = g_list_sort (g_list_copy (rows), (GCompareFunc)gtk_tree_path_compare);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+ continue;
+
+ /* Skip if the current path is actually a descendant of the
+ previous path */
+ if (prev != NULL && gtk_tree_path_is_descendant (path, prev))
+ continue;
+
+ prev = path;
+ node = (FileBrowserNode *)(iter.user_data);
+ files = g_list_prepend (files, g_object_ref (node->file));
+ }
+
+ data = g_new (AsyncData, 1);
+
+ data->model = model;
+ data->cancellable = g_cancellable_new ();
+ data->files = files;
+ data->trash = trash;
+ data->iter = files;
+ data->removed = FALSE;
+
+ model->priv->async_handles =
+ g_slist_prepend (model->priv->async_handles, data);
+
+ g_io_scheduler_push_job ((GIOSchedulerJobFunc)delete_files,
+ data,
+ (GDestroyNotify)async_data_free,
+ G_PRIORITY_DEFAULT,
+ data->cancellable);
+ g_list_free (rows);
+
+ return GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_delete (GeditFileBrowserStore * model,
+ GtkTreeIter * iter, gboolean trash)
+{
+ FileBrowserNode *node;
+ GList *rows = NULL;
+ GeditFileBrowserStoreResult result;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+ g_return_val_if_fail (iter != NULL, GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+ g_return_val_if_fail (iter->user_data != NULL, GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
+
+ node = (FileBrowserNode *) (iter->user_data);
+
+ if (NODE_IS_DUMMY (node))
+ return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
+
+ rows = g_list_append(NULL, gedit_file_browser_store_get_path_real (model, node));
+ result = gedit_file_browser_store_delete_all (model, rows, trash);
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ return result;
+}
+
+gboolean
+gedit_file_browser_store_new_file (GeditFileBrowserStore * model,
+ GtkTreeIter * parent,
+ GtkTreeIter * iter)
+{
+ GFile * file;
+ GFileOutputStream * stream;
+ FileBrowserNodeDir *parent_node;
+ gboolean result = FALSE;
+ FileBrowserNode *node;
+ GError * error = NULL;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (parent != NULL, FALSE);
+ g_return_val_if_fail (parent->user_data != NULL, FALSE);
+ g_return_val_if_fail (NODE_IS_DIR
+ ((FileBrowserNode *) (parent->user_data)),
+ FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ parent_node = FILE_BROWSER_NODE_DIR (parent->user_data);
+ /* Translators: This is the default name of new files created by the file browser pane. */
+ file = unique_new_name (((FileBrowserNode *) parent_node)->file, _("file"));
+
+ stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
+
+ if (!stream)
+ {
+ g_signal_emit (model, model_signals[ERROR], 0,
+ GEDIT_FILE_BROWSER_ERROR_NEW_FILE,
+ error->message);
+ g_error_free (error);
+ } else {
+ g_object_unref (stream);
+ node = model_add_node_from_file (model,
+ (FileBrowserNode *)parent_node,
+ file,
+ NULL);
+
+ if (model_node_visibility (model, node)) {
+ iter->user_data = node;
+ result = TRUE;
+ } else {
+ g_signal_emit (model, model_signals[ERROR], 0,
+ GEDIT_FILE_BROWSER_ERROR_NEW_FILE,
+ _
+ ("The new file is currently filtered out. You need to adjust your filter settings to make the file visible"));
+ }
+ }
+
+ g_object_unref (file);
+ return result;
+}
+
+gboolean
+gedit_file_browser_store_new_directory (GeditFileBrowserStore * model,
+ GtkTreeIter * parent,
+ GtkTreeIter * iter)
+{
+ GFile * file;
+ FileBrowserNodeDir *parent_node;
+ GError * error = NULL;
+ FileBrowserNode *node;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), FALSE);
+ g_return_val_if_fail (parent != NULL, FALSE);
+ g_return_val_if_fail (parent->user_data != NULL, FALSE);
+ g_return_val_if_fail (NODE_IS_DIR
+ ((FileBrowserNode *) (parent->user_data)),
+ FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ parent_node = FILE_BROWSER_NODE_DIR (parent->user_data);
+ /* Translators: This is the default name of new directories created by the file browser pane. */
+ file = unique_new_name (((FileBrowserNode *) parent_node)->file, _("directory"));
+
+ if (!g_file_make_directory (file, NULL, &error)) {
+ g_signal_emit (model, model_signals[ERROR], 0,
+ GEDIT_FILE_BROWSER_ERROR_NEW_DIRECTORY,
+ error->message);
+ g_error_free (error);
+ } else {
+ node = model_add_node_from_file (model,
+ (FileBrowserNode *)parent_node,
+ file,
+ NULL);
+
+ if (model_node_visibility (model, node)) {
+ iter->user_data = node;
+ result = TRUE;
+ } else {
+ g_signal_emit (model, model_signals[ERROR], 0,
+ GEDIT_FILE_BROWSER_ERROR_NEW_FILE,
+ _
+ ("The new directory is currently filtered out. You need to adjust your filter settings to make the directory visible"));
+ }
+ }
+
+ g_object_unref (file);
+ return result;
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-store.h b/plugins/filebrowser/gedit-file-browser-store.h
new file mode 100755
index 00000000..f31da327
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-store.h
@@ -0,0 +1,200 @@
+/*
+ * gedit-file-browser-store.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_STORE_H__
+#define __GEDIT_FILE_BROWSER_STORE_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define GEDIT_TYPE_FILE_BROWSER_STORE (gedit_file_browser_store_get_type ())
+#define GEDIT_FILE_BROWSER_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_STORE, GeditFileBrowserStore))
+#define GEDIT_FILE_BROWSER_STORE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_STORE, GeditFileBrowserStore const))
+#define GEDIT_FILE_BROWSER_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FILE_BROWSER_STORE, GeditFileBrowserStoreClass))
+#define GEDIT_IS_FILE_BROWSER_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FILE_BROWSER_STORE))
+#define GEDIT_IS_FILE_BROWSER_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FILE_BROWSER_STORE))
+#define GEDIT_FILE_BROWSER_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FILE_BROWSER_STORE, GeditFileBrowserStoreClass))
+
+typedef enum
+{
+ GEDIT_FILE_BROWSER_STORE_COLUMN_ICON = 0,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_NAME,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_NUM
+} GeditFileBrowserStoreColumn;
+
+typedef enum
+{
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY = 1 << 0,
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN = 1 << 1,
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_TEXT = 1 << 2,
+ GEDIT_FILE_BROWSER_STORE_FLAG_LOADED = 1 << 3,
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED = 1 << 4,
+ GEDIT_FILE_BROWSER_STORE_FLAG_IS_DUMMY = 1 << 5
+} GeditFileBrowserStoreFlag;
+
+typedef enum
+{
+ GEDIT_FILE_BROWSER_STORE_RESULT_OK,
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE,
+ GEDIT_FILE_BROWSER_STORE_RESULT_ERROR,
+ GEDIT_FILE_BROWSER_STORE_RESULT_NO_TRASH,
+ GEDIT_FILE_BROWSER_STORE_RESULT_MOUNTING,
+ GEDIT_FILE_BROWSER_STORE_RESULT_NUM
+} GeditFileBrowserStoreResult;
+
+typedef enum
+{
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_NONE = 0,
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN = 1 << 0,
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY = 1 << 1
+} GeditFileBrowserStoreFilterMode;
+
+#define FILE_IS_DIR(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY)
+#define FILE_IS_HIDDEN(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN)
+#define FILE_IS_TEXT(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_IS_TEXT)
+#define FILE_LOADED(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_LOADED)
+#define FILE_IS_FILTERED(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED)
+#define FILE_IS_DUMMY(flags) (flags & GEDIT_FILE_BROWSER_STORE_FLAG_IS_DUMMY)
+
+typedef struct _GeditFileBrowserStore GeditFileBrowserStore;
+typedef struct _GeditFileBrowserStoreClass GeditFileBrowserStoreClass;
+typedef struct _GeditFileBrowserStorePrivate GeditFileBrowserStorePrivate;
+
+typedef gboolean (*GeditFileBrowserStoreFilterFunc) (GeditFileBrowserStore
+ * model,
+ GtkTreeIter * iter,
+ gpointer user_data);
+
+struct _GeditFileBrowserStore
+{
+ GObject parent;
+
+ GeditFileBrowserStorePrivate *priv;
+};
+
+struct _GeditFileBrowserStoreClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*begin_loading) (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+ void (*end_loading) (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+ void (*error) (GeditFileBrowserStore * model,
+ guint code,
+ gchar * message);
+ gboolean (*no_trash) (GeditFileBrowserStore * model,
+ GList * files);
+ void (*rename) (GeditFileBrowserStore * model,
+ const gchar * olduri,
+ const gchar * newuri);
+ void (*begin_refresh) (GeditFileBrowserStore * model);
+ void (*end_refresh) (GeditFileBrowserStore * model);
+ void (*unload) (GeditFileBrowserStore * model,
+ const gchar * uri);
+};
+
+GType gedit_file_browser_store_get_type (void) G_GNUC_CONST;
+GType gedit_file_browser_store_register_type (GTypeModule * module);
+
+GeditFileBrowserStore *gedit_file_browser_store_new (gchar const *root);
+
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_root_and_virtual_root (GeditFileBrowserStore * model,
+ gchar const *root,
+ gchar const *virtual_root);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_root (GeditFileBrowserStore * model,
+ gchar const *root);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_from_string (GeditFileBrowserStore * model,
+ gchar const *root);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_up (GeditFileBrowserStore * model);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_set_virtual_root_top (GeditFileBrowserStore * model);
+
+gboolean
+gedit_file_browser_store_get_iter_virtual_root (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+gboolean gedit_file_browser_store_get_iter_root (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+gchar * gedit_file_browser_store_get_root (GeditFileBrowserStore * model);
+gchar * gedit_file_browser_store_get_virtual_root (GeditFileBrowserStore * model);
+
+gboolean gedit_file_browser_store_iter_equal (GeditFileBrowserStore * model,
+ GtkTreeIter * iter1,
+ GtkTreeIter * iter2);
+
+void gedit_file_browser_store_set_value (GeditFileBrowserStore * tree_model,
+ GtkTreeIter * iter,
+ gint column,
+ GValue * value);
+
+void _gedit_file_browser_store_iter_expanded (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+void _gedit_file_browser_store_iter_collapsed (GeditFileBrowserStore * model,
+ GtkTreeIter * iter);
+
+GeditFileBrowserStoreFilterMode
+gedit_file_browser_store_get_filter_mode (GeditFileBrowserStore * model);
+void gedit_file_browser_store_set_filter_mode (GeditFileBrowserStore * model,
+ GeditFileBrowserStoreFilterMode mode);
+void gedit_file_browser_store_set_filter_func (GeditFileBrowserStore * model,
+ GeditFileBrowserStoreFilterFunc func,
+ gpointer user_data);
+void gedit_file_browser_store_refilter (GeditFileBrowserStore * model);
+GeditFileBrowserStoreFilterMode
+gedit_file_browser_store_filter_mode_get_default (void);
+
+void gedit_file_browser_store_refresh (GeditFileBrowserStore * model);
+gboolean gedit_file_browser_store_rename (GeditFileBrowserStore * model,
+ GtkTreeIter * iter,
+ gchar const *new_name,
+ GError ** error);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_delete (GeditFileBrowserStore * model,
+ GtkTreeIter * iter,
+ gboolean trash);
+GeditFileBrowserStoreResult
+gedit_file_browser_store_delete_all (GeditFileBrowserStore * model,
+ GList *rows,
+ gboolean trash);
+
+gboolean gedit_file_browser_store_new_file (GeditFileBrowserStore * model,
+ GtkTreeIter * parent,
+ GtkTreeIter * iter);
+gboolean gedit_file_browser_store_new_directory (GeditFileBrowserStore * model,
+ GtkTreeIter * parent,
+ GtkTreeIter * iter);
+
+void gedit_file_browser_store_cancel_mount_operation (GeditFileBrowserStore *store);
+
+G_END_DECLS
+#endif /* __GEDIT_FILE_BROWSER_STORE_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-utils.c b/plugins/filebrowser/gedit-file-browser-utils.c
new file mode 100755
index 00000000..d8f4028a
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-utils.c
@@ -0,0 +1,198 @@
+/*
+ * gedit-file-bookmarks-store.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gedit-file-browser-utils.h"
+#include <gedit/gedit-utils.h>
+
+static GdkPixbuf *
+process_icon_pixbuf (GdkPixbuf * pixbuf,
+ gchar const * name,
+ gint size,
+ GError * error)
+{
+ GdkPixbuf * scale;
+
+ if (error != NULL) {
+ g_warning ("Could not load theme icon %s: %s",
+ name,
+ error->message);
+ g_error_free (error);
+ }
+
+ if (pixbuf && gdk_pixbuf_get_width (pixbuf) > size) {
+ scale = gdk_pixbuf_scale_simple (pixbuf,
+ size,
+ size,
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = scale;
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gedit_file_browser_utils_pixbuf_from_theme (gchar const * name,
+ GtkIconSize size)
+{
+ gint width;
+ GError *error = NULL;
+ GdkPixbuf *pixbuf;
+
+ gtk_icon_size_lookup (size, &width, NULL);
+
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ name,
+ width,
+ 0,
+ &error);
+
+ pixbuf = process_icon_pixbuf (pixbuf, name, width, error);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gedit_file_browser_utils_pixbuf_from_icon (GIcon * icon,
+ GtkIconSize size)
+{
+ GdkPixbuf * ret = NULL;
+ GtkIconTheme *theme;
+ GtkIconInfo *info;
+ gint width;
+
+ if (!icon)
+ return NULL;
+
+ theme = gtk_icon_theme_get_default ();
+ gtk_icon_size_lookup (size, &width, NULL);
+
+ info = gtk_icon_theme_lookup_by_gicon (theme,
+ icon,
+ width,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+
+ if (!info)
+ return NULL;
+
+ ret = gtk_icon_info_load_icon (info, NULL);
+ gtk_icon_info_free (info);
+
+ return ret;
+}
+
+GdkPixbuf *
+gedit_file_browser_utils_pixbuf_from_file (GFile * file,
+ GtkIconSize size)
+{
+ GIcon * icon;
+ GFileInfo * info;
+ GdkPixbuf * ret = NULL;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_ICON,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+
+ if (!info)
+ return NULL;
+
+ icon = g_file_info_get_icon (info);
+ if (icon != NULL)
+ ret = gedit_file_browser_utils_pixbuf_from_icon (icon, size);
+
+ g_object_unref (info);
+
+ return ret;
+}
+
+gchar *
+gedit_file_browser_utils_file_basename (GFile * file)
+{
+ gchar *uri;
+ gchar *ret;
+
+ uri = g_file_get_uri (file);
+ ret = gedit_file_browser_utils_uri_basename (uri);
+ g_free (uri);
+
+ return ret;
+}
+
+gchar *
+gedit_file_browser_utils_uri_basename (gchar const * uri)
+{
+ return gedit_utils_basename_for_display (uri);
+}
+
+gboolean
+gedit_file_browser_utils_confirmation_dialog (GeditWindow * window,
+ GtkMessageType type,
+ gchar const *message,
+ gchar const *secondary,
+ gchar const * button_stock,
+ gchar const * button_label)
+{
+ GtkWidget *dlg;
+ gint ret;
+ GtkWidget *button;
+
+ dlg = gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ type,
+ GTK_BUTTONS_NONE, "%s", message);
+
+ if (secondary)
+ gtk_message_dialog_format_secondary_text
+ (GTK_MESSAGE_DIALOG (dlg), "%s", secondary);
+
+ /* Add a cancel button */
+ button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_widget_show (button);
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_dialog_add_action_widget (GTK_DIALOG (dlg),
+ button,
+ GTK_RESPONSE_CANCEL);
+
+ /* Add custom button */
+ button = gtk_button_new_from_stock (button_stock);
+
+ if (button_label) {
+ gtk_button_set_use_stock (GTK_BUTTON (button), FALSE);
+ gtk_button_set_label (GTK_BUTTON (button), button_label);
+ }
+
+ gtk_widget_show (button);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_dialog_add_action_widget (GTK_DIALOG (dlg),
+ button,
+ GTK_RESPONSE_OK);
+
+ ret = gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+
+ return (ret == GTK_RESPONSE_OK);
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-utils.h b/plugins/filebrowser/gedit-file-browser-utils.h
new file mode 100755
index 00000000..fc9acbce
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-utils.h
@@ -0,0 +1,27 @@
+#ifndef __GEDIT_FILE_BROWSER_UTILS_H__
+#define __GEDIT_FILE_BROWSER_UTILS_H__
+
+#include <gedit/gedit-window.h>
+#include <gio/gio.h>
+
+GdkPixbuf *gedit_file_browser_utils_pixbuf_from_theme (gchar const *name,
+ GtkIconSize size);
+
+GdkPixbuf *gedit_file_browser_utils_pixbuf_from_icon (GIcon * icon,
+ GtkIconSize size);
+GdkPixbuf *gedit_file_browser_utils_pixbuf_from_file (GFile * file,
+ GtkIconSize size);
+
+gchar * gedit_file_browser_utils_file_basename (GFile * file);
+gchar * gedit_file_browser_utils_uri_basename (gchar const * uri);
+
+gboolean gedit_file_browser_utils_confirmation_dialog (GeditWindow * window,
+ GtkMessageType type,
+ gchar const *message,
+ gchar const *secondary,
+ gchar const * button_stock,
+ gchar const * button_label);
+
+#endif /* __GEDIT_FILE_BROWSER_UTILS_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-view.c b/plugins/filebrowser/gedit-file-browser-view.c
new file mode 100755
index 00000000..05733da1
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-view.c
@@ -0,0 +1,1256 @@
+/*
+ * gedit-file-browser-view.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gio/gio.h>
+#include <gedit/gedit-plugin.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gedit-file-browser-store.h"
+#include "gedit-file-bookmarks-store.h"
+#include "gedit-file-browser-view.h"
+#include "gedit-file-browser-marshal.h"
+#include "gedit-file-browser-enum-types.h"
+
+#define GEDIT_FILE_BROWSER_VIEW_GET_PRIVATE(object)( \
+ G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GEDIT_TYPE_FILE_BROWSER_VIEW, GeditFileBrowserViewPrivate))
+
+struct _GeditFileBrowserViewPrivate
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *pixbuf_renderer;
+ GtkCellRenderer *text_renderer;
+
+ GtkTreeModel *model;
+ GtkTreeRowReference *editable;
+
+ GdkCursor *busy_cursor;
+
+ /* CLick policy */
+ GeditFileBrowserViewClickPolicy click_policy;
+ GtkTreePath *double_click_path[2]; /* Both clicks in a double click need to be on the same row */
+ GtkTreePath *hover_path;
+ GdkCursor *hand_cursor;
+ gboolean ignore_release;
+ gboolean selected_on_button_down;
+ gint drag_button;
+ gboolean drag_started;
+
+ gboolean restore_expand_state;
+ gboolean is_refresh;
+ GHashTable * expand_state;
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_CLICK_POLICY,
+ PROP_RESTORE_EXPAND_STATE
+};
+
+/* Signals */
+enum
+{
+ ERROR,
+ FILE_ACTIVATED,
+ DIRECTORY_ACTIVATED,
+ BOOKMARK_ACTIVATED,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0 };
+
+static const GtkTargetEntry drag_source_targets[] = {
+ { "text/uri-list", 0, 0 }
+};
+
+GEDIT_PLUGIN_DEFINE_TYPE (GeditFileBrowserView, gedit_file_browser_view,
+ GTK_TYPE_TREE_VIEW)
+
+static void on_cell_edited (GtkCellRendererText * cell,
+ gchar * path,
+ gchar * new_text,
+ GeditFileBrowserView * tree_view);
+
+static void on_begin_refresh (GeditFileBrowserStore * model,
+ GeditFileBrowserView * view);
+static void on_end_refresh (GeditFileBrowserStore * model,
+ GeditFileBrowserView * view);
+
+static void on_unload (GeditFileBrowserStore * model,
+ gchar const * uri,
+ GeditFileBrowserView * view);
+
+static void on_row_inserted (GeditFileBrowserStore * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ GeditFileBrowserView * view);
+
+static void
+gedit_file_browser_view_finalize (GObject * object)
+{
+ GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW(object);
+
+ if (obj->priv->hand_cursor)
+ gdk_cursor_unref(obj->priv->hand_cursor);
+
+ if (obj->priv->hover_path)
+ gtk_tree_path_free (obj->priv->hover_path);
+
+ if (obj->priv->expand_state)
+ {
+ g_hash_table_destroy (obj->priv->expand_state);
+ obj->priv->expand_state = NULL;
+ }
+
+ gdk_cursor_unref (obj->priv->busy_cursor);
+
+ G_OBJECT_CLASS (gedit_file_browser_view_parent_class)->
+ finalize (object);
+}
+
+static void
+add_expand_state (GeditFileBrowserView * view,
+ gchar const * uri)
+{
+ GFile * file;
+
+ if (!uri)
+ return;
+
+ file = g_file_new_for_uri (uri);
+
+ if (view->priv->expand_state)
+ g_hash_table_insert (view->priv->expand_state, file, file);
+ else
+ g_object_unref (file);
+}
+
+static void
+remove_expand_state (GeditFileBrowserView * view,
+ gchar const * uri)
+{
+ GFile * file;
+
+ if (!uri)
+ return;
+
+ file = g_file_new_for_uri (uri);
+
+ if (view->priv->expand_state)
+ g_hash_table_remove (view->priv->expand_state, file);
+
+ g_object_unref (file);
+}
+
+static void
+row_expanded (GtkTreeView * tree_view,
+ GtkTreeIter * iter,
+ GtkTreePath * path)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (tree_view);
+ gchar * uri;
+
+ if (GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_expanded)
+ GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_expanded (tree_view, iter, path);
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ return;
+
+ if (view->priv->restore_expand_state)
+ {
+ gtk_tree_model_get (view->priv->model,
+ iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri,
+ -1);
+
+ add_expand_state (view, uri);
+ g_free (uri);
+ }
+
+ _gedit_file_browser_store_iter_expanded (GEDIT_FILE_BROWSER_STORE (view->priv->model),
+ iter);
+}
+
+static void
+row_collapsed (GtkTreeView * tree_view,
+ GtkTreeIter * iter,
+ GtkTreePath * path)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (tree_view);
+ gchar * uri;
+
+ if (GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_collapsed)
+ GTK_TREE_VIEW_CLASS (gedit_file_browser_view_parent_class)->row_collapsed (tree_view, iter, path);
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ return;
+
+ if (view->priv->restore_expand_state)
+ {
+ gtk_tree_model_get (view->priv->model,
+ iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri,
+ -1);
+
+ remove_expand_state (view, uri);
+ g_free (uri);
+ }
+
+ _gedit_file_browser_store_iter_collapsed (GEDIT_FILE_BROWSER_STORE (view->priv->model),
+ iter);
+}
+
+static gboolean
+leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
+
+ if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE &&
+ view->priv->hover_path != NULL) {
+ gtk_tree_path_free (view->priv->hover_path);
+ view->priv->hover_path = NULL;
+ }
+
+ // Chainup
+ return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->leave_notify_event (widget, event);
+}
+
+static gboolean
+enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
+
+ if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE) {
+ if (view->priv->hover_path != NULL)
+ gtk_tree_path_free (view->priv->hover_path);
+
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &view->priv->hover_path,
+ NULL, NULL, NULL);
+
+ if (view->priv->hover_path != NULL)
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ view->priv->hand_cursor);
+ }
+
+ // Chainup
+ return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->enter_notify_event (widget, event);
+}
+
+static gboolean
+motion_notify_event (GtkWidget * widget,
+ GdkEventMotion * event)
+{
+ GtkTreePath *old_hover_path;
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
+
+ if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE) {
+ old_hover_path = view->priv->hover_path;
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &view->priv->hover_path,
+ NULL, NULL, NULL);
+
+ if ((old_hover_path != NULL) != (view->priv->hover_path != NULL)) {
+ if (view->priv->hover_path != NULL)
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ view->priv->hand_cursor);
+ else
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ NULL);
+ }
+
+ if (old_hover_path != NULL)
+ gtk_tree_path_free (old_hover_path);
+ }
+
+ // Chainup
+ return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->motion_notify_event (widget, event);
+}
+
+static void
+set_click_policy_property (GeditFileBrowserView *obj,
+ GeditFileBrowserViewClickPolicy click_policy)
+{
+ GtkTreeIter iter;
+ GdkDisplay *display;
+ GdkWindow *win;
+
+ obj->priv->click_policy = click_policy;
+
+ if (click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE) {
+ if (obj->priv->hand_cursor == NULL)
+ obj->priv->hand_cursor = gdk_cursor_new(GDK_HAND2);
+ } else if (click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE) {
+ if (obj->priv->hover_path != NULL) {
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (obj->priv->model),
+ &iter, obj->priv->hover_path))
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (obj->priv->model),
+ obj->priv->hover_path, &iter);
+
+ gtk_tree_path_free (obj->priv->hover_path);
+ obj->priv->hover_path = NULL;
+ }
+
+ if (GTK_WIDGET_REALIZED (GTK_WIDGET (obj))) {
+ win = gtk_widget_get_window (GTK_WIDGET (obj));
+ gdk_window_set_cursor (win, NULL);
+
+ display = gtk_widget_get_display (GTK_WIDGET (obj));
+
+ if (display != NULL)
+ gdk_display_flush (display);
+ }
+
+ if (obj->priv->hand_cursor) {
+ gdk_cursor_unref (obj->priv->hand_cursor);
+ obj->priv->hand_cursor = NULL;
+ }
+ }
+}
+
+static void
+directory_activated (GeditFileBrowserView *view,
+ GtkTreeIter *iter)
+{
+ gedit_file_browser_store_set_virtual_root (GEDIT_FILE_BROWSER_STORE (view->priv->model), iter);
+}
+
+static void
+activate_selected_files (GeditFileBrowserView *view) {
+ GtkTreeView *tree_view = GTK_TREE_VIEW (view);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
+ GList *rows, *row;
+ GtkTreePath *directory = NULL;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GeditFileBrowserStoreFlag flags;
+
+ rows = gtk_tree_selection_get_selected_rows (selection, &view->priv->model);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ /* Get iter from path */
+ if (!gtk_tree_model_get_iter (view->priv->model, &iter, path))
+ continue;
+
+ gtk_tree_model_get (view->priv->model, &iter, GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, -1);
+
+ if (FILE_IS_DIR (flags)) {
+ if (directory == NULL)
+ directory = path;
+
+ } else if (!FILE_IS_DUMMY (flags)) {
+ g_signal_emit (view, signals[FILE_ACTIVATED], 0, &iter);
+ }
+ }
+
+ if (directory != NULL) {
+ if (gtk_tree_model_get_iter (view->priv->model, &iter, directory))
+ g_signal_emit (view, signals[DIRECTORY_ACTIVATED], 0, &iter);
+ }
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+}
+
+static void
+activate_selected_bookmark (GeditFileBrowserView *view) {
+ GtkTreeView *tree_view = GTK_TREE_VIEW (view);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &view->priv->model, &iter))
+ g_signal_emit (view, signals[BOOKMARK_ACTIVATED], 0, &iter);
+}
+
+static void
+activate_selected_items (GeditFileBrowserView *view)
+{
+ if (GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ activate_selected_files (view);
+ else if (GEDIT_IS_FILE_BOOKMARKS_STORE (view->priv->model))
+ activate_selected_bookmark (view);
+}
+
+static void
+toggle_hidden_filter (GeditFileBrowserView *view)
+{
+ GeditFileBrowserStoreFilterMode mode;
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ {
+ mode = gedit_file_browser_store_get_filter_mode
+ (GEDIT_FILE_BROWSER_STORE (view->priv->model));
+ mode ^= GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
+ gedit_file_browser_store_set_filter_mode
+ (GEDIT_FILE_BROWSER_STORE (view->priv->model), mode);
+ }
+}
+
+static gboolean
+button_event_modifies_selection (GdkEventButton *event)
+{
+ return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
+}
+
+static void
+drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
+
+ view->priv->drag_button = 0;
+ view->priv->drag_started = TRUE;
+
+ /* Chain up */
+ GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->drag_begin (widget, context);
+}
+
+static void
+did_not_drag (GeditFileBrowserView *view,
+ GdkEventButton *event)
+{
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ tree_view = GTK_TREE_VIEW (view);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
+ &path, NULL, NULL, NULL)) {
+ if ((view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE)
+ && !button_event_modifies_selection(event)
+ && (event->button == 1 || event->button == 2)) {
+ /* Activate all selected items, and leave them selected */
+ activate_selected_items (view);
+ } else if ((event->button == 1 || event->button == 2)
+ && ((event->state & GDK_CONTROL_MASK) != 0 ||
+ (event->state & GDK_SHIFT_MASK) == 0)
+ && view->priv->selected_on_button_down) {
+ if (!button_event_modifies_selection (event)) {
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_path (selection, path);
+ } else {
+ gtk_tree_selection_unselect_path (selection, path);
+ }
+ }
+
+ gtk_tree_path_free (path);
+ }
+}
+
+static gboolean
+button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget);
+
+ if (event->button == view->priv->drag_button) {
+ view->priv->drag_button = 0;
+
+ if (!view->priv->drag_started &&
+ !view->priv->ignore_release)
+ did_not_drag (view, event);
+ }
+
+ /* Chain up */
+ return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->button_release_event (widget, event);
+}
+
+static gboolean
+button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ int double_click_time;
+ static int click_count = 0;
+ static guint32 last_click_time = 0;
+ GeditFileBrowserView *view;
+ GtkTreeView *tree_view;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ int expander_size;
+ int horizontal_separator;
+ gboolean on_expander;
+ gboolean call_parent;
+ gboolean selected;
+ GtkWidgetClass *widget_parent = GTK_WIDGET_CLASS(gedit_file_browser_view_parent_class);
+
+ tree_view = GTK_TREE_VIEW (widget);
+ view = GEDIT_FILE_BROWSER_VIEW (widget);
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ /* Get double click time */
+ g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
+ "gtk-double-click-time", &double_click_time,
+ NULL);
+
+ /* Determine click count */
+ if (event->time - last_click_time < double_click_time)
+ click_count++;
+ else
+ click_count = 0;
+
+ last_click_time = event->time;
+
+ /* Ignore double click if we are in single click mode */
+ if (view->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE &&
+ click_count >= 2) {
+ return TRUE;
+ }
+
+ view->priv->ignore_release = FALSE;
+ call_parent = TRUE;
+
+ if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
+ &path, NULL, NULL, NULL)) {
+ /* Keep track of path of last click so double clicks only happen
+ * on the same item */
+ if ((event->button == 1 || event->button == 2) &&
+ event->type == GDK_BUTTON_PRESS) {
+ if (view->priv->double_click_path[1])
+ gtk_tree_path_free (view->priv->double_click_path[1]);
+
+ view->priv->double_click_path[1] = view->priv->double_click_path[0];
+ view->priv->double_click_path[0] = gtk_tree_path_copy (path);
+ }
+
+ if (event->type == GDK_2BUTTON_PRESS) {
+ if (view->priv->double_click_path[1] &&
+ gtk_tree_path_compare (view->priv->double_click_path[0], view->priv->double_click_path[1]) == 0)
+ activate_selected_items (view);
+
+ /* Chain up */
+ widget_parent->button_press_event (widget, event);
+ } else {
+ /* We're going to filter out some situations where
+ * we can't let the default code run because all
+ * but one row would be would be deselected. We don't
+ * want that; we want the right click menu or single
+ * click to apply to everything that's currently selected. */
+ selected = gtk_tree_selection_path_is_selected (selection, path);
+
+ if (event->button == 3 && selected)
+ call_parent = FALSE;
+
+ if ((event->button == 1 || event->button == 2) &&
+ ((event->state & GDK_CONTROL_MASK) != 0 ||
+ (event->state & GDK_SHIFT_MASK) == 0)) {
+ gtk_widget_style_get (widget,
+ "expander-size", &expander_size,
+ "horizontal-separator", &horizontal_separator,
+ NULL);
+ on_expander = (event->x <= horizontal_separator / 2 +
+ gtk_tree_path_get_depth (path) * expander_size);
+
+ view->priv->selected_on_button_down = selected;
+
+ if (selected) {
+ call_parent = on_expander || gtk_tree_selection_count_selected_rows (selection) == 1;
+ view->priv->ignore_release = call_parent && view->priv->click_policy != GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE;
+ } else if ((event->state & GDK_CONTROL_MASK) != 0) {
+ call_parent = FALSE;
+ gtk_tree_selection_select_path (selection, path);
+ } else {
+ view->priv->ignore_release = on_expander;
+ }
+ }
+
+ if (call_parent) {
+ /* Chain up */
+ widget_parent->button_press_event (widget, event);
+ } else if (selected) {
+ gtk_widget_grab_focus (widget);
+ }
+
+ if ((event->button == 1 || event->button == 2) &&
+ event->type == GDK_BUTTON_PRESS) {
+ view->priv->drag_started = FALSE;
+ view->priv->drag_button = event->button;
+ }
+ }
+
+ gtk_tree_path_free (path);
+ } else {
+ if ((event->button == 1 || event->button == 2) &&
+ event->type == GDK_BUTTON_PRESS) {
+ if (view->priv->double_click_path[1])
+ gtk_tree_path_free (view->priv->double_click_path[1]);
+
+ view->priv->double_click_path[1] = view->priv->double_click_path[0];
+ view->priv->double_click_path[0] = NULL;
+ }
+
+ gtk_tree_selection_unselect_all (selection);
+ /* Chain up */
+ widget_parent->button_press_event (widget, event);
+ }
+
+ /* We already chained up if nescessary, so just return TRUE */
+ return TRUE;
+}
+
+static gboolean
+key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GeditFileBrowserView *view;
+ guint modifiers;
+ gboolean handled;
+
+ view = GEDIT_FILE_BROWSER_VIEW (widget);
+ handled = FALSE;
+
+ modifiers = gtk_accelerator_get_default_mod_mask ();
+
+ switch (event->keyval) {
+ case GDK_space:
+ if (event->state & GDK_CONTROL_MASK) {
+ handled = FALSE;
+ break;
+ }
+ if (!GTK_WIDGET_HAS_FOCUS (widget)) {
+ handled = FALSE;
+ break;
+ }
+
+ activate_selected_items (view);
+ handled = TRUE;
+ break;
+
+ case GDK_Return:
+ case GDK_KP_Enter:
+ activate_selected_items (view);
+ handled = TRUE;
+ break;
+
+ case GDK_h:
+ if ((event->state & modifiers) == GDK_CONTROL_MASK) {
+ toggle_hidden_filter (view);
+ handled = TRUE;
+ break;
+ }
+
+ default:
+ handled = FALSE;
+ }
+
+ /* Chain up */
+ if (!handled)
+ return GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class)->key_press_event (widget, event);
+
+ return TRUE;
+}
+
+static void
+fill_expand_state (GeditFileBrowserView * view, GtkTreeIter * iter)
+{
+ GtkTreePath * path;
+ GtkTreeIter child;
+ gchar * uri;
+
+ if (!gtk_tree_model_iter_has_child (view->priv->model, iter))
+ return;
+
+ path = gtk_tree_model_get_path (view->priv->model, iter);
+
+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), path))
+ {
+ gtk_tree_model_get (view->priv->model,
+ iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri,
+ -1);
+
+ add_expand_state (view, uri);
+ g_free (uri);
+ }
+
+ if (gtk_tree_model_iter_children (view->priv->model, &child, iter))
+ {
+ do {
+ fill_expand_state (view, &child);
+ } while (gtk_tree_model_iter_next (view->priv->model, &child));
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+uninstall_restore_signals (GeditFileBrowserView * tree_view,
+ GtkTreeModel * model)
+{
+ g_signal_handlers_disconnect_by_func (model,
+ on_begin_refresh,
+ tree_view);
+
+ g_signal_handlers_disconnect_by_func (model,
+ on_end_refresh,
+ tree_view);
+
+ g_signal_handlers_disconnect_by_func (model,
+ on_unload,
+ tree_view);
+
+ g_signal_handlers_disconnect_by_func (model,
+ on_row_inserted,
+ tree_view);
+}
+
+static void
+install_restore_signals (GeditFileBrowserView * tree_view,
+ GtkTreeModel * model)
+{
+ g_signal_connect (model,
+ "begin-refresh",
+ G_CALLBACK (on_begin_refresh),
+ tree_view);
+
+ g_signal_connect (model,
+ "end-refresh",
+ G_CALLBACK (on_end_refresh),
+ tree_view);
+
+ g_signal_connect (model,
+ "unload",
+ G_CALLBACK (on_unload),
+ tree_view);
+
+ g_signal_connect_after (model,
+ "row-inserted",
+ G_CALLBACK (on_row_inserted),
+ tree_view);
+}
+
+static void
+set_restore_expand_state (GeditFileBrowserView * view,
+ gboolean state)
+{
+ if (state == view->priv->restore_expand_state)
+ return;
+
+ if (view->priv->expand_state)
+ {
+ g_hash_table_destroy (view->priv->expand_state);
+ view->priv->expand_state = NULL;
+ }
+
+ if (state)
+ {
+ view->priv->expand_state = g_hash_table_new_full (g_file_hash,
+ (GEqualFunc)g_file_equal,
+ g_object_unref,
+ NULL);
+
+ if (view->priv->model && GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ {
+ fill_expand_state (view, NULL);
+
+ install_restore_signals (view, view->priv->model);
+ }
+ }
+ else if (view->priv->model && GEDIT_IS_FILE_BROWSER_STORE (view->priv->model))
+ {
+ uninstall_restore_signals (view, view->priv->model);
+ }
+
+ view->priv->restore_expand_state = state;
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_CLICK_POLICY:
+ g_value_set_enum (value, obj->priv->click_policy);
+ break;
+ case PROP_RESTORE_EXPAND_STATE:
+ g_value_set_boolean (value, obj->priv->restore_expand_state);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserView *obj = GEDIT_FILE_BROWSER_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_CLICK_POLICY:
+ set_click_policy_property (obj, g_value_get_enum (value));
+ break;
+ case PROP_RESTORE_EXPAND_STATE:
+ set_restore_expand_state (obj, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_file_browser_view_class_init (GeditFileBrowserViewClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkTreeViewClass *tree_view_class = GTK_TREE_VIEW_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gedit_file_browser_view_finalize;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ /* Event handlers */
+ widget_class->motion_notify_event = motion_notify_event;
+ widget_class->enter_notify_event = enter_notify_event;
+ widget_class->leave_notify_event = leave_notify_event;
+ widget_class->button_press_event = button_press_event;
+ widget_class->button_release_event = button_release_event;
+ widget_class->drag_begin = drag_begin;
+ widget_class->key_press_event = key_press_event;
+
+ /* Tree view handlers */
+ tree_view_class->row_expanded = row_expanded;
+ tree_view_class->row_collapsed = row_collapsed;
+
+ /* Default handlers */
+ klass->directory_activated = directory_activated;
+
+ g_object_class_install_property (object_class, PROP_CLICK_POLICY,
+ g_param_spec_enum ("click-policy",
+ "Click Policy",
+ "The click policy",
+ GEDIT_TYPE_FILE_BROWSER_VIEW_CLICK_POLICY,
+ GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_RESTORE_EXPAND_STATE,
+ g_param_spec_boolean ("restore-expand-state",
+ "Restore Expand State",
+ "Restore expanded state of loaded directories",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ signals[ERROR] =
+ g_signal_new ("error",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserViewClass,
+ error), NULL, NULL,
+ gedit_file_browser_marshal_VOID__UINT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
+ signals[FILE_ACTIVATED] =
+ g_signal_new ("file-activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserViewClass,
+ file_activated), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
+ signals[DIRECTORY_ACTIVATED] =
+ g_signal_new ("directory-activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserViewClass,
+ directory_activated), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
+ signals[BOOKMARK_ACTIVATED] =
+ g_signal_new ("bookmark-activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserViewClass,
+ bookmark_activated), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
+
+ g_type_class_add_private (object_class,
+ sizeof (GeditFileBrowserViewPrivate));
+}
+
+static void
+cell_data_cb (GtkTreeViewColumn * tree_column, GtkCellRenderer * cell,
+ GtkTreeModel * tree_model, GtkTreeIter * iter,
+ GeditFileBrowserView * obj)
+{
+ GtkTreePath *path;
+ PangoUnderline underline = PANGO_UNDERLINE_NONE;
+ gboolean editable = FALSE;
+
+ path = gtk_tree_model_get_path (tree_model, iter);
+
+ if (obj->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE) {
+ if (obj->priv->hover_path != NULL &&
+ gtk_tree_path_compare (path, obj->priv->hover_path) == 0)
+ underline = PANGO_UNDERLINE_SINGLE;
+ }
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (tree_model))
+ {
+ if (obj->priv->editable != NULL &&
+ gtk_tree_row_reference_valid (obj->priv->editable))
+ {
+ GtkTreePath *edpath = gtk_tree_row_reference_get_path (obj->priv->editable);
+
+ editable = edpath && gtk_tree_path_compare (path, edpath) == 0;
+ }
+ }
+
+ gtk_tree_path_free (path);
+ g_object_set (cell, "editable", editable, "underline", underline, NULL);
+}
+
+static void
+gedit_file_browser_view_init (GeditFileBrowserView * obj)
+{
+ obj->priv = GEDIT_FILE_BROWSER_VIEW_GET_PRIVATE (obj);
+
+ obj->priv->column = gtk_tree_view_column_new ();
+
+ obj->priv->pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (obj->priv->column,
+ obj->priv->pixbuf_renderer,
+ FALSE);
+ gtk_tree_view_column_add_attribute (obj->priv->column,
+ obj->priv->pixbuf_renderer,
+ "pixbuf",
+ GEDIT_FILE_BROWSER_STORE_COLUMN_ICON);
+
+ obj->priv->text_renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (obj->priv->column,
+ obj->priv->text_renderer, TRUE);
+ gtk_tree_view_column_add_attribute (obj->priv->column,
+ obj->priv->text_renderer,
+ "text",
+ GEDIT_FILE_BROWSER_STORE_COLUMN_NAME);
+
+ g_signal_connect (obj->priv->text_renderer, "edited",
+ G_CALLBACK (on_cell_edited), obj);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (obj),
+ obj->priv->column);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (obj), FALSE);
+
+ gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (obj),
+ GDK_BUTTON1_MASK,
+ drag_source_targets,
+ G_N_ELEMENTS (drag_source_targets),
+ GDK_ACTION_COPY);
+
+ obj->priv->busy_cursor = gdk_cursor_new (GDK_WATCH);
+}
+
+static gboolean
+bookmarks_separator_func (GtkTreeModel * model, GtkTreeIter * iter,
+ gpointer user_data)
+{
+ guint flags;
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS,
+ &flags, -1);
+
+ return (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR);
+}
+
+/* Public */
+GtkWidget *
+gedit_file_browser_view_new (void)
+{
+ GeditFileBrowserView *obj =
+ GEDIT_FILE_BROWSER_VIEW (g_object_new
+ (GEDIT_TYPE_FILE_BROWSER_VIEW, NULL));
+
+ return GTK_WIDGET (obj);
+}
+
+void
+gedit_file_browser_view_set_model (GeditFileBrowserView * tree_view,
+ GtkTreeModel * model)
+{
+ GtkTreeSelection *selection;
+
+ if (tree_view->priv->model == model)
+ return;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+ if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) {
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW
+ (tree_view),
+ bookmarks_separator_func,
+ NULL, NULL);
+ gtk_tree_view_column_set_cell_data_func (tree_view->priv->
+ column,
+ tree_view->priv->
+ text_renderer,
+ (GtkTreeCellDataFunc)
+ cell_data_cb,
+ tree_view, NULL);
+ } else {
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW
+ (tree_view), NULL,
+ NULL, NULL);
+ gtk_tree_view_column_set_cell_data_func (tree_view->priv->
+ column,
+ tree_view->priv->
+ text_renderer,
+ (GtkTreeCellDataFunc)
+ cell_data_cb,
+ tree_view, NULL);
+
+ if (tree_view->priv->restore_expand_state)
+ install_restore_signals (tree_view, model);
+
+ }
+
+ if (tree_view->priv->hover_path != NULL) {
+ gtk_tree_path_free (tree_view->priv->hover_path);
+ tree_view->priv->hover_path = NULL;
+ }
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (tree_view->priv->model)) {
+ if (tree_view->priv->restore_expand_state)
+ uninstall_restore_signals (tree_view,
+ tree_view->priv->model);
+ }
+
+ tree_view->priv->model = model;
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), model);
+}
+
+void
+gedit_file_browser_view_start_rename (GeditFileBrowserView * tree_view,
+ GtkTreeIter * iter)
+{
+ guint flags;
+ GtkTreeRowReference *rowref;
+ GtkTreePath *path;
+
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE
+ (tree_view->priv->model));
+ g_return_if_fail (iter != NULL);
+
+ gtk_tree_model_get (tree_view->priv->model, iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!(FILE_IS_DIR (flags) || !FILE_IS_DUMMY (flags)))
+ return;
+
+ path = gtk_tree_model_get_path (tree_view->priv->model, iter);
+ rowref = gtk_tree_row_reference_new (tree_view->priv->model, path);
+
+ /* Start editing */
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+
+ if (gtk_tree_path_up (path))
+ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree_view),
+ path);
+
+ gtk_tree_path_free (path);
+ tree_view->priv->editable = rowref;
+
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view),
+ gtk_tree_row_reference_get_path (tree_view->priv->editable),
+ tree_view->priv->column, TRUE);
+
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view),
+ gtk_tree_row_reference_get_path (tree_view->priv->editable),
+ tree_view->priv->column,
+ FALSE, 0.0, 0.0);
+}
+
+void
+gedit_file_browser_view_set_click_policy (GeditFileBrowserView *tree_view,
+ GeditFileBrowserViewClickPolicy policy)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
+
+ set_click_policy_property (tree_view, policy);
+
+ g_object_notify (G_OBJECT (tree_view), "click-policy");
+}
+
+void
+gedit_file_browser_view_set_restore_expand_state (GeditFileBrowserView * tree_view,
+ gboolean restore_expand_state)
+{
+ g_return_if_fail (GEDIT_IS_FILE_BROWSER_VIEW (tree_view));
+
+ set_restore_expand_state (tree_view, restore_expand_state);
+ g_object_notify (G_OBJECT (tree_view), "restore-expand-state");
+}
+
+/* Signal handlers */
+static void
+on_cell_edited (GtkCellRendererText * cell, gchar * path, gchar * new_text,
+ GeditFileBrowserView * tree_view)
+{
+ GtkTreePath * treepath;
+ GtkTreeIter iter;
+ gboolean ret;
+ GError * error = NULL;
+
+ gtk_tree_row_reference_free (tree_view->priv->editable);
+ tree_view->priv->editable = NULL;
+
+ if (new_text == NULL || *new_text == '\0')
+ return;
+
+ treepath = gtk_tree_path_new_from_string (path);
+ ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model), &iter, treepath);
+ gtk_tree_path_free (treepath);
+
+ if (ret) {
+ if (gedit_file_browser_store_rename (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model),
+ &iter, new_text, &error)) {
+ treepath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_view->priv->model), &iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view),
+ treepath, NULL,
+ FALSE, 0.0, 0.0);
+ gtk_tree_path_free (treepath);
+ }
+ else {
+ if (error) {
+ g_signal_emit (tree_view, signals[ERROR], 0,
+ error->code, error->message);
+ g_error_free (error);
+ }
+ }
+ }
+}
+
+static void
+on_begin_refresh (GeditFileBrowserStore * model,
+ GeditFileBrowserView * view)
+{
+ /* Store the refresh state, so we can handle unloading of nodes while
+ refreshing properly */
+ view->priv->is_refresh = TRUE;
+}
+
+static void
+on_end_refresh (GeditFileBrowserStore * model,
+ GeditFileBrowserView * view)
+{
+ /* Store the refresh state, so we can handle unloading of nodes while
+ refreshing properly */
+ view->priv->is_refresh = FALSE;
+}
+
+static void
+on_unload (GeditFileBrowserStore * model,
+ gchar const * uri,
+ GeditFileBrowserView * view)
+{
+ /* Don't remove the expand state if we are refreshing */
+ if (!view->priv->restore_expand_state || view->priv->is_refresh)
+ return;
+
+ remove_expand_state (view, uri);
+}
+
+static void
+restore_expand_state (GeditFileBrowserView * view,
+ GeditFileBrowserStore * model,
+ GtkTreeIter * iter)
+{
+ gchar * uri;
+ GFile * file;
+ GtkTreePath * path;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (model),
+ iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri,
+ -1);
+
+ if (!uri)
+ return;
+
+ file = g_file_new_for_uri (uri);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
+
+ if (g_hash_table_lookup (view->priv->expand_state, file))
+ {
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (view),
+ path,
+ FALSE);
+ }
+
+ gtk_tree_path_free (path);
+
+ g_object_unref (file);
+ g_free (uri);
+}
+
+static void
+on_row_inserted (GeditFileBrowserStore * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ GeditFileBrowserView * view)
+{
+ GtkTreeIter parent;
+ GtkTreePath * copy;
+
+ if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (model), iter))
+ restore_expand_state (view, model, iter);
+
+ copy = gtk_tree_path_copy (path);
+
+ if (gtk_tree_path_up (copy) &&
+ (gtk_tree_path_get_depth (copy) != 0) &&
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &parent, copy))
+ {
+ restore_expand_state (view, model, &parent);
+ }
+
+ gtk_tree_path_free (copy);
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-view.h b/plugins/filebrowser/gedit-file-browser-view.h
new file mode 100755
index 00000000..a5ada255
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-view.h
@@ -0,0 +1,84 @@
+/*
+ * gedit-file-browser-view.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_VIEW_H__
+#define __GEDIT_FILE_BROWSER_VIEW_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define GEDIT_TYPE_FILE_BROWSER_VIEW (gedit_file_browser_view_get_type ())
+#define GEDIT_FILE_BROWSER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_VIEW, GeditFileBrowserView))
+#define GEDIT_FILE_BROWSER_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_VIEW, GeditFileBrowserView const))
+#define GEDIT_FILE_BROWSER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FILE_BROWSER_VIEW, GeditFileBrowserViewClass))
+#define GEDIT_IS_FILE_BROWSER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FILE_BROWSER_VIEW))
+#define GEDIT_IS_FILE_BROWSER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FILE_BROWSER_VIEW))
+#define GEDIT_FILE_BROWSER_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FILE_BROWSER_VIEW, GeditFileBrowserViewClass))
+
+typedef struct _GeditFileBrowserView GeditFileBrowserView;
+typedef struct _GeditFileBrowserViewClass GeditFileBrowserViewClass;
+typedef struct _GeditFileBrowserViewPrivate GeditFileBrowserViewPrivate;
+
+typedef enum {
+ GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE,
+ GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE
+} GeditFileBrowserViewClickPolicy;
+
+struct _GeditFileBrowserView
+{
+ GtkTreeView parent;
+
+ GeditFileBrowserViewPrivate *priv;
+};
+
+struct _GeditFileBrowserViewClass
+{
+ GtkTreeViewClass parent_class;
+
+ /* Signals */
+ void (*error) (GeditFileBrowserView * filetree,
+ guint code,
+ gchar const *message);
+ void (*file_activated) (GeditFileBrowserView * filetree,
+ GtkTreeIter *iter);
+ void (*directory_activated) (GeditFileBrowserView * filetree,
+ GtkTreeIter *iter);
+ void (*bookmark_activated) (GeditFileBrowserView * filetree,
+ GtkTreeIter *iter);
+};
+
+GType gedit_file_browser_view_get_type (void) G_GNUC_CONST;
+GType gedit_file_browser_view_register_type (GTypeModule * module);
+
+GtkWidget *gedit_file_browser_view_new (void);
+void gedit_file_browser_view_set_model (GeditFileBrowserView * tree_view,
+ GtkTreeModel * model);
+void gedit_file_browser_view_start_rename (GeditFileBrowserView * tree_view,
+ GtkTreeIter * iter);
+void gedit_file_browser_view_set_click_policy (GeditFileBrowserView * tree_view,
+ GeditFileBrowserViewClickPolicy policy);
+void gedit_file_browser_view_set_restore_expand_state (GeditFileBrowserView * tree_view,
+ gboolean restore_expand_state);
+
+G_END_DECLS
+#endif /* __GEDIT_FILE_BROWSER_VIEW_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-widget-ui.xml b/plugins/filebrowser/gedit-file-browser-widget-ui.xml
new file mode 100755
index 00000000..472fd185
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-widget-ui.xml
@@ -0,0 +1,54 @@
+<ui>
+ <toolbar name="ToolBar">
+ <placeholder name="Tool_Opt1"/>
+ <toolitem action="DirectoryUp"/>
+ <separator/>
+ <toolitem action="DirectoryRefresh"/>
+ <separator/>
+ <placeholder name="Tool_Opt2"/>
+ <separator/>
+ <toolitem action="FilterHidden"/>
+ <separator/>
+ <placeholder name="Tool_Opt3"/>
+ </toolbar>
+
+ <popup name="FilePopup" action="FilePopupAction">
+ <menuitem action="FileOpen" />
+ <placeholder name="FilePopup_Opt1" />
+ <separator/>
+ <menuitem action="DirectoryNew" />
+ <menuitem action="FileNew" />
+ <separator/>
+ <placeholder name="FilePopup_Opt2" />
+ <separator/>
+ <menuitem action="FileRename"/>
+ <menuitem action="FileMoveToTrash"/>
+ <menuitem action="FileDelete"/>
+ <separator/>
+ <placeholder name="FilePopup_Opt3" />
+ <separator/>
+ <menuitem action="DirectoryRefresh"/>
+ <menuitem action="DirectoryOpen"/>
+ <placeholder name="FilePopup_Opt4" />
+ <separator/>
+ <placeholder name="FilePopup_Opt5" />
+ <separator/>
+ <menu name="FilterMenu" action="FilterMenuAction">
+ <menuitem action="FilterHidden"/>
+ <menuitem action="FilterBinary"/>
+ </menu>
+ <placeholder name="FilePopup_Opt6" />
+ <separator/>
+ </popup>
+
+ <popup name="BookmarkPopup" action="BookmarkPopupAction">
+ <placeholder name="BookmarkPopup_Opt1" />
+ <separator/>
+ <placeholder name="BookmarkPopup_Opt2" />
+ <separator/>
+ <menuitem action="BookmarkOpen"/>
+ <placeholder name="BookmarkPopup_Opt3" />
+ <separator/>
+ <placeholder name="BookmarkPopup_Opt4" />
+ </popup>
+</ui>
diff --git a/plugins/filebrowser/gedit-file-browser-widget.c b/plugins/filebrowser/gedit-file-browser-widget.c
new file mode 100755
index 00000000..e8a73cce
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-widget.c
@@ -0,0 +1,3143 @@
+/*
+ * gedit-file-browser-widget.c - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gedit/gedit-utils.h>
+#include <gedit/gedit-plugin.h>
+
+#include "gedit-file-browser-utils.h"
+#include "gedit-file-browser-error.h"
+#include "gedit-file-browser-widget.h"
+#include "gedit-file-browser-view.h"
+#include "gedit-file-browser-store.h"
+#include "gedit-file-bookmarks-store.h"
+#include "gedit-file-browser-marshal.h"
+#include "gedit-file-browser-enum-types.h"
+
+#define GEDIT_FILE_BROWSER_WIDGET_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GEDIT_TYPE_FILE_BROWSER_WIDGET, \
+ GeditFileBrowserWidgetPrivate))
+
+#define XML_UI_FILE "gedit-file-browser-widget-ui.xml"
+#define LOCATION_DATA_KEY "gedit-file-browser-widget-location"
+
+enum
+{
+ BOOKMARKS_ID,
+ SEPARATOR_CUSTOM_ID,
+ SEPARATOR_ID,
+ PATH_ID,
+ NUM_DEFAULT_IDS
+};
+
+enum
+{
+ COLUMN_INDENT,
+ COLUMN_ICON,
+ COLUMN_NAME,
+ COLUMN_FILE,
+ COLUMN_ID,
+ N_COLUMNS
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_FILTER_PATTERN,
+ PROP_ENABLE_DELETE
+};
+
+/* Signals */
+enum
+{
+ URI_ACTIVATED,
+ ERROR,
+ CONFIRM_DELETE,
+ CONFIRM_NO_TRASH,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0 };
+
+typedef struct _SignalNode
+{
+ GObject *object;
+ gulong id;
+} SignalNode;
+
+typedef struct
+{
+ gulong id;
+ GeditFileBrowserWidgetFilterFunc func;
+ gpointer user_data;
+ GDestroyNotify destroy_notify;
+} FilterFunc;
+
+typedef struct
+{
+ GFile *root;
+ GFile *virtual_root;
+} Location;
+
+typedef struct
+{
+ gchar *name;
+ GdkPixbuf *icon;
+} NameIcon;
+
+struct _GeditFileBrowserWidgetPrivate
+{
+ GeditFileBrowserView *treeview;
+ GeditFileBrowserStore *file_store;
+ GeditFileBookmarksStore *bookmarks_store;
+
+ GHashTable *bookmarks_hash;
+
+ GtkWidget *combo;
+ GtkTreeStore *combo_model;
+
+ GtkWidget *filter_expander;
+ GtkWidget *filter_entry;
+
+ GtkUIManager *manager;
+ GtkActionGroup *action_group;
+ GtkActionGroup *action_group_selection;
+ GtkActionGroup *action_group_file_selection;
+ GtkActionGroup *action_group_single_selection;
+ GtkActionGroup *action_group_single_most_selection;
+ GtkActionGroup *action_group_sensitive;
+ GtkActionGroup *bookmark_action_group;
+
+ GSList *signal_pool;
+
+ GSList *filter_funcs;
+ gulong filter_id;
+ gulong glob_filter_id;
+ GPatternSpec *filter_pattern;
+ gchar *filter_pattern_str;
+
+ GList *locations;
+ GList *current_location;
+ gboolean changing_location;
+ GtkWidget *location_previous_menu;
+ GtkWidget *location_next_menu;
+ GtkWidget *current_location_menu_item;
+
+ gboolean enable_delete;
+
+ GCancellable *cancellable;
+
+ GdkCursor *busy_cursor;
+};
+
+static void set_enable_delete (GeditFileBrowserWidget *obj,
+ gboolean enable);
+static void on_model_set (GObject * gobject,
+ GParamSpec * arg1,
+ GeditFileBrowserWidget * obj);
+static void on_treeview_error (GeditFileBrowserView * tree_view,
+ guint code,
+ gchar * message,
+ GeditFileBrowserWidget * obj);
+static void on_file_store_error (GeditFileBrowserStore * store,
+ guint code,
+ gchar * message,
+ GeditFileBrowserWidget * obj);
+static gboolean on_file_store_no_trash (GeditFileBrowserStore * store,
+ GList * files,
+ GeditFileBrowserWidget * obj);
+static void on_combo_changed (GtkComboBox * combo,
+ GeditFileBrowserWidget * obj);
+static gboolean on_treeview_popup_menu (GeditFileBrowserView * treeview,
+ GeditFileBrowserWidget * obj);
+static gboolean on_treeview_button_press_event (GeditFileBrowserView * treeview,
+ GdkEventButton * event,
+ GeditFileBrowserWidget * obj);
+static gboolean on_treeview_key_press_event (GeditFileBrowserView * treeview,
+ GdkEventKey * event,
+ GeditFileBrowserWidget * obj);
+static void on_selection_changed (GtkTreeSelection * selection,
+ GeditFileBrowserWidget * obj);
+
+static void on_virtual_root_changed (GeditFileBrowserStore * model,
+ GParamSpec *param,
+ GeditFileBrowserWidget * obj);
+
+static gboolean on_entry_filter_activate (GeditFileBrowserWidget * obj);
+static void on_location_jump_activate (GtkMenuItem * item,
+ GeditFileBrowserWidget * obj);
+static void on_bookmarks_row_changed (GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ GeditFileBrowserWidget * obj);
+static void on_bookmarks_row_deleted (GtkTreeModel * model,
+ GtkTreePath * path,
+ GeditFileBrowserWidget * obj);
+static void on_filter_mode_changed (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_previous (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_next (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_up (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_new (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_file_open (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_file_new (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_file_rename (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_file_delete (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_file_move_to_trash (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_refresh (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_directory_open (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_filter_hidden (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_filter_binary (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+static void on_action_bookmark_open (GtkAction * action,
+ GeditFileBrowserWidget * obj);
+
+GEDIT_PLUGIN_DEFINE_TYPE (GeditFileBrowserWidget, gedit_file_browser_widget,
+ GTK_TYPE_VBOX)
+
+static void
+free_name_icon (gpointer data)
+{
+ NameIcon * item;
+
+ if (data == NULL)
+ return;
+
+ item = (NameIcon *)(data);
+
+ g_free (item->name);
+
+ if (item->icon)
+ g_object_unref (item->icon);
+
+ g_free (item);
+}
+
+static FilterFunc *
+filter_func_new (GeditFileBrowserWidget * obj,
+ GeditFileBrowserWidgetFilterFunc func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ FilterFunc *result;
+
+ result = g_new (FilterFunc, 1);
+
+ result->id = ++obj->priv->filter_id;
+ result->func = func;
+ result->user_data = user_data;
+ result->destroy_notify = notify;
+ return result;
+}
+
+static void
+location_free (Location * loc)
+{
+ if (loc->root)
+ g_object_unref (loc->root);
+
+ if (loc->virtual_root)
+ g_object_unref (loc->virtual_root);
+
+ g_free (loc);
+}
+
+static gboolean
+combo_find_by_id (GeditFileBrowserWidget * obj, guint id,
+ GtkTreeIter * iter)
+{
+ guint checkid;
+ GtkTreeModel *model = GTK_TREE_MODEL (obj->priv->combo_model);
+
+ if (iter == NULL)
+ return FALSE;
+
+ if (gtk_tree_model_get_iter_first (model, iter)) {
+ do {
+ gtk_tree_model_get (model, iter, COLUMN_ID,
+ &checkid, -1);
+
+ if (checkid == id)
+ return TRUE;
+ } while (gtk_tree_model_iter_next (model, iter));
+ }
+
+ return FALSE;
+}
+
+static void
+remove_path_items (GeditFileBrowserWidget * obj)
+{
+ GtkTreeIter iter;
+
+ while (combo_find_by_id (obj, PATH_ID, &iter))
+ gtk_tree_store_remove (obj->priv->combo_model, &iter);
+}
+
+static void
+cancel_async_operation (GeditFileBrowserWidget *widget)
+{
+ if (!widget->priv->cancellable)
+ return;
+
+ g_cancellable_cancel (widget->priv->cancellable);
+ g_object_unref (widget->priv->cancellable);
+
+ widget->priv->cancellable = NULL;
+}
+
+static void
+gedit_file_browser_widget_finalize (GObject * object)
+{
+ GeditFileBrowserWidget *obj = GEDIT_FILE_BROWSER_WIDGET (object);
+ GList *loc;
+
+ remove_path_items (obj);
+ gedit_file_browser_store_set_filter_func (obj->priv->file_store,
+ NULL, NULL);
+
+ g_object_unref (obj->priv->manager);
+ g_object_unref (obj->priv->file_store);
+ g_object_unref (obj->priv->bookmarks_store);
+ g_object_unref (obj->priv->combo_model);
+
+ g_slist_foreach (obj->priv->filter_funcs, (GFunc) g_free, NULL);
+ g_slist_free (obj->priv->filter_funcs);
+
+ for (loc = obj->priv->locations; loc; loc = loc->next)
+ location_free ((Location *) (loc->data));
+
+ if (obj->priv->current_location_menu_item)
+ g_object_unref (obj->priv->current_location_menu_item);
+
+ g_list_free (obj->priv->locations);
+
+ g_hash_table_destroy (obj->priv->bookmarks_hash);
+
+ cancel_async_operation (obj);
+
+ gdk_cursor_unref (obj->priv->busy_cursor);
+
+ G_OBJECT_CLASS (gedit_file_browser_widget_parent_class)->finalize (object);
+}
+
+static void
+gedit_file_browser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserWidget *obj = GEDIT_FILE_BROWSER_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILTER_PATTERN:
+ g_value_set_string (value, obj->priv->filter_pattern_str);
+ break;
+ case PROP_ENABLE_DELETE:
+ g_value_set_boolean (value, obj->priv->enable_delete);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_file_browser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GeditFileBrowserWidget *obj = GEDIT_FILE_BROWSER_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILTER_PATTERN:
+ gedit_file_browser_widget_set_filter_pattern (obj,
+ g_value_get_string (value));
+ break;
+ case PROP_ENABLE_DELETE:
+ set_enable_delete (obj, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gedit_file_browser_widget_class_init (GeditFileBrowserWidgetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gedit_file_browser_widget_finalize;
+
+ object_class->get_property = gedit_file_browser_widget_get_property;
+ object_class->set_property = gedit_file_browser_widget_set_property;
+
+ g_object_class_install_property (object_class, PROP_FILTER_PATTERN,
+ g_param_spec_string ("filter-pattern",
+ "Filter Pattern",
+ "The filter pattern",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_ENABLE_DELETE,
+ g_param_spec_boolean ("enable-delete",
+ "Enable delete",
+ "Enable permanently deleting items",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ signals[URI_ACTIVATED] =
+ g_signal_new ("uri-activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserWidgetClass,
+ uri_activated), NULL, NULL,
+ g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+ signals[ERROR] =
+ g_signal_new ("error", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserWidgetClass,
+ error), NULL, NULL,
+ gedit_file_browser_marshal_VOID__UINT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
+
+ signals[CONFIRM_DELETE] =
+ g_signal_new ("confirm-delete", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserWidgetClass,
+ confirm_delete),
+ g_signal_accumulator_true_handled,
+ NULL,
+ gedit_file_browser_marshal_BOOL__OBJECT_POINTER,
+ G_TYPE_BOOLEAN,
+ 2,
+ G_TYPE_OBJECT,
+ G_TYPE_POINTER);
+
+ signals[CONFIRM_NO_TRASH] =
+ g_signal_new ("confirm-no-trash", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GeditFileBrowserWidgetClass,
+ confirm_no_trash),
+ g_signal_accumulator_true_handled,
+ NULL,
+ gedit_file_browser_marshal_BOOL__POINTER,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_POINTER);
+
+ g_type_class_add_private (object_class,
+ sizeof (GeditFileBrowserWidgetPrivate));
+}
+
+static void
+add_signal (GeditFileBrowserWidget * obj, gpointer object, gulong id)
+{
+ SignalNode *node = g_new (SignalNode, 1);
+
+ node->object = G_OBJECT (object);
+ node->id = id;
+
+ obj->priv->signal_pool =
+ g_slist_prepend (obj->priv->signal_pool, node);
+}
+
+static void
+clear_signals (GeditFileBrowserWidget * obj)
+{
+ GSList *item;
+ SignalNode *node;
+
+ for (item = obj->priv->signal_pool; item; item = item->next) {
+ node = (SignalNode *) (item->data);
+
+ g_signal_handler_disconnect (node->object, node->id);
+ g_free (item->data);
+ }
+
+ g_slist_free (obj->priv->signal_pool);
+ obj->priv->signal_pool = NULL;
+}
+
+static gboolean
+separator_func (GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
+{
+ guint id;
+
+ gtk_tree_model_get (model, iter, COLUMN_ID, &id, -1);
+
+ return (id == SEPARATOR_ID);
+}
+
+static gboolean
+get_from_bookmark_file (GeditFileBrowserWidget * obj, GFile * file,
+ gchar ** name, GdkPixbuf ** icon)
+{
+ gpointer data;
+ NameIcon * item;
+
+ data = g_hash_table_lookup (obj->priv->bookmarks_hash, file);
+
+ if (data == NULL)
+ return FALSE;
+
+ item = (NameIcon *)data;
+
+ *name = g_strdup (item->name);
+ *icon = item->icon;
+
+ if (item->icon != NULL)
+ {
+ g_object_ref (item->icon);
+ }
+
+ return TRUE;
+}
+
+static void
+insert_path_item (GeditFileBrowserWidget * obj,
+ GFile * file,
+ GtkTreeIter * after,
+ GtkTreeIter * iter,
+ guint indent)
+{
+ gchar * unescape;
+ GdkPixbuf * icon = NULL;
+
+ /* Try to get the icon and name from the bookmarks hash */
+ if (!get_from_bookmark_file (obj, file, &unescape, &icon)) {
+ /* It's not a bookmark, fetch the name and the icon ourselves */
+ unescape = gedit_file_browser_utils_file_basename (file);
+
+ /* Get the icon */
+ icon = gedit_file_browser_utils_pixbuf_from_file (file, GTK_ICON_SIZE_MENU);
+ }
+
+ gtk_tree_store_insert_after (obj->priv->combo_model, iter, NULL,
+ after);
+
+ gtk_tree_store_set (obj->priv->combo_model,
+ iter,
+ COLUMN_INDENT, indent,
+ COLUMN_ICON, icon,
+ COLUMN_NAME, unescape,
+ COLUMN_FILE, file,
+ COLUMN_ID, PATH_ID,
+ -1);
+
+ if (icon)
+ g_object_unref (icon);
+
+ g_free (unescape);
+}
+
+static void
+insert_separator_item (GeditFileBrowserWidget * obj)
+{
+ GtkTreeIter iter;
+
+ gtk_tree_store_insert (obj->priv->combo_model, &iter, NULL, 1);
+ gtk_tree_store_set (obj->priv->combo_model, &iter,
+ COLUMN_ICON, NULL,
+ COLUMN_NAME, NULL,
+ COLUMN_ID, SEPARATOR_ID, -1);
+}
+
+static void
+combo_set_active_by_id (GeditFileBrowserWidget * obj, guint id)
+{
+ GtkTreeIter iter;
+
+ if (combo_find_by_id (obj, id, &iter))
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX
+ (obj->priv->combo), &iter);
+}
+
+static guint
+uri_num_parents (GFile * from, GFile * to)
+{
+ /* Determine the number of 'levels' to get from #from to #to. */
+ guint parents = 0;
+ GFile * parent;
+
+ if (from == NULL)
+ return 0;
+
+ g_object_ref (from);
+
+ while ((parent = g_file_get_parent (from)) && !(to && g_file_equal (from, to))) {
+ g_object_unref (from);
+ from = parent;
+
+ ++parents;
+ }
+
+ g_object_unref (from);
+ return parents;
+}
+
+static void
+insert_location_path (GeditFileBrowserWidget * obj)
+{
+ Location *loc;
+ GFile *current = NULL;
+ GFile * tmp;
+ GtkTreeIter separator;
+ GtkTreeIter iter;
+ guint indent;
+
+ if (!obj->priv->current_location) {
+ g_message ("insert_location_path: no current location");
+ return;
+ }
+
+ loc = (Location *) (obj->priv->current_location->data);
+
+ current = loc->virtual_root;
+ combo_find_by_id (obj, SEPARATOR_ID, &separator);
+
+ indent = uri_num_parents (loc->virtual_root, loc->root);
+
+ while (current != NULL) {
+ insert_path_item (obj, current, &separator, &iter, indent--);
+
+ if (current == loc->virtual_root) {
+ g_signal_handlers_block_by_func (obj->priv->combo,
+ on_combo_changed,
+ obj);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX
+ (obj->priv->combo),
+ &iter);
+ g_signal_handlers_unblock_by_func (obj->priv->
+ combo,
+ on_combo_changed,
+ obj);
+ }
+
+ if (g_file_equal (current, loc->root) || !gedit_utils_file_has_parent (current)) {
+ if (current != loc->virtual_root)
+ g_object_unref (current);
+ break;
+ }
+
+ tmp = g_file_get_parent (current);
+
+ if (current != loc->virtual_root)
+ g_object_unref (current);
+
+ current = tmp;
+ }
+}
+
+static void
+check_current_item (GeditFileBrowserWidget * obj, gboolean show_path)
+{
+ GtkTreeIter separator;
+ gboolean has_sep;
+
+ remove_path_items (obj);
+ has_sep = combo_find_by_id (obj, SEPARATOR_ID, &separator);
+
+ if (show_path) {
+ if (!has_sep)
+ insert_separator_item (obj);
+
+ insert_location_path (obj);
+ } else if (has_sep)
+ gtk_tree_store_remove (obj->priv->combo_model, &separator);
+}
+
+static void
+fill_combo_model (GeditFileBrowserWidget * obj)
+{
+ GtkTreeStore *store = obj->priv->combo_model;
+ GtkTreeIter iter;
+ GdkPixbuf *icon;
+
+ icon = gedit_file_browser_utils_pixbuf_from_theme (GTK_STOCK_HOME, GTK_ICON_SIZE_MENU);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COLUMN_ICON, icon,
+ COLUMN_NAME, _("Bookmarks"),
+ COLUMN_ID, BOOKMARKS_ID, -1);
+ g_object_unref (icon);
+
+ gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (obj->priv->combo),
+ separator_func, obj, NULL);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (obj->priv->combo), 0);
+}
+
+static void
+indent_cell_data_func (GtkCellLayout * cell_layout,
+ GtkCellRenderer * cell,
+ GtkTreeModel * model,
+ GtkTreeIter * iter,
+ gpointer data)
+{
+ gchar * indent;
+ guint num;
+
+ gtk_tree_model_get (model, iter, COLUMN_INDENT, &num, -1);
+
+ if (num == 0)
+ g_object_set (cell, "text", "", NULL);
+ else {
+ indent = g_strnfill (num * 2, ' ');
+
+ g_object_set (cell, "text", indent, NULL);
+ g_free (indent);
+ }
+}
+
+static void
+create_combo (GeditFileBrowserWidget * obj)
+{
+ GtkCellRenderer *renderer;
+
+ obj->priv->combo_model = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_UINT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_FILE,
+ G_TYPE_UINT);
+ obj->priv->combo =
+ gtk_combo_box_new_with_model (GTK_TREE_MODEL
+ (obj->priv->combo_model));
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj->priv->combo),
+ renderer, FALSE);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT
+ (obj->priv->combo), renderer,
+ indent_cell_data_func, obj, NULL);
+
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj->priv->combo),
+ renderer, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (obj->priv->combo),
+ renderer, "pixbuf", COLUMN_ICON);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj->priv->combo),
+ renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (obj->priv->combo),
+ renderer, "text", COLUMN_NAME);
+
+ g_object_set (renderer, "ellipsize-set", TRUE,
+ "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+ gtk_box_pack_start (GTK_BOX (obj), GTK_WIDGET (obj->priv->combo),
+ FALSE, FALSE, 0);
+
+ fill_combo_model (obj);
+ g_signal_connect (obj->priv->combo, "changed",
+ G_CALLBACK (on_combo_changed), obj);
+
+ gtk_widget_show (obj->priv->combo);
+}
+
+static GtkActionEntry toplevel_actions[] =
+{
+ {"FilterMenuAction", NULL, N_("_Filter")}
+};
+
+static const GtkActionEntry tree_actions_selection[] =
+{
+ {"FileMoveToTrash", "mate-stock-trash", N_("_Move to Trash"), NULL,
+ N_("Move selected file or folder to trash"),
+ G_CALLBACK (on_action_file_move_to_trash)},
+ {"FileDelete", GTK_STOCK_DELETE, N_("_Delete"), NULL,
+ N_("Delete selected file or folder"),
+ G_CALLBACK (on_action_file_delete)}
+};
+
+static const GtkActionEntry tree_actions_file_selection[] =
+{
+ {"FileOpen", GTK_STOCK_OPEN, NULL, NULL,
+ N_("Open selected file"),
+ G_CALLBACK (on_action_file_open)}
+};
+
+static const GtkActionEntry tree_actions[] =
+{
+ {"DirectoryUp", GTK_STOCK_GO_UP, N_("Up"), NULL,
+ N_("Open the parent folder"), G_CALLBACK (on_action_directory_up)}
+};
+
+static const GtkActionEntry tree_actions_single_most_selection[] =
+{
+ {"DirectoryNew", GTK_STOCK_ADD, N_("_New Folder"), NULL,
+ N_("Add new empty folder"),
+ G_CALLBACK (on_action_directory_new)},
+ {"FileNew", GTK_STOCK_NEW, N_("New F_ile"), NULL,
+ N_("Add new empty file"), G_CALLBACK (on_action_file_new)}
+};
+
+static const GtkActionEntry tree_actions_single_selection[] =
+{
+ {"FileRename", NULL, N_("_Rename"), NULL,
+ N_("Rename selected file or folder"),
+ G_CALLBACK (on_action_file_rename)}
+};
+
+static const GtkActionEntry tree_actions_sensitive[] =
+{
+ {"DirectoryPrevious", GTK_STOCK_GO_BACK, N_("_Previous Location"),
+ NULL,
+ N_("Go to the previous visited location"),
+ G_CALLBACK (on_action_directory_previous)},
+ {"DirectoryNext", GTK_STOCK_GO_FORWARD, N_("_Next Location"), NULL,
+ N_("Go to the next visited location"), G_CALLBACK (on_action_directory_next)},
+ {"DirectoryRefresh", GTK_STOCK_REFRESH, N_("Re_fresh View"), NULL,
+ N_("Refresh the view"), G_CALLBACK (on_action_directory_refresh)},
+ {"DirectoryOpen", GTK_STOCK_OPEN, N_("_View Folder"), NULL,
+ N_("View folder in file manager"),
+ G_CALLBACK (on_action_directory_open)}
+};
+
+static const GtkToggleActionEntry tree_actions_toggle[] =
+{
+ {"FilterHidden", GTK_STOCK_DIALOG_AUTHENTICATION,
+ N_("Show _Hidden"), NULL,
+ N_("Show hidden files and folders"),
+ G_CALLBACK (on_action_filter_hidden), FALSE},
+ {"FilterBinary", NULL, N_("Show _Binary"), NULL,
+ N_("Show binary files"), G_CALLBACK (on_action_filter_binary),
+ FALSE}
+};
+
+static const GtkActionEntry bookmark_actions[] =
+{
+ {"BookmarkOpen", GTK_STOCK_OPEN, N_("_View Folder"), NULL,
+ N_("View folder in file manager"), G_CALLBACK (on_action_bookmark_open)}
+};
+
+static void
+create_toolbar (GeditFileBrowserWidget * obj,
+ const gchar *data_dir)
+{
+ GtkUIManager *manager;
+ GError *error = NULL;
+ GtkActionGroup *action_group;
+ GtkWidget *toolbar;
+ GtkWidget *widget;
+ GtkAction *action;
+ gchar *ui_file;
+
+ manager = gtk_ui_manager_new ();
+ obj->priv->manager = manager;
+
+ ui_file = g_build_filename (data_dir, XML_UI_FILE, NULL);
+ gtk_ui_manager_add_ui_from_file (manager, ui_file, &error);
+
+ g_free (ui_file);
+
+ if (error != NULL) {
+ g_warning ("Error in adding ui from file %s: %s",
+ XML_UI_FILE, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetActionGroupToplevel");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ toplevel_actions,
+ G_N_ELEMENTS (toplevel_actions),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions,
+ G_N_ELEMENTS (tree_actions),
+ obj);
+ gtk_action_group_add_toggle_actions (action_group,
+ tree_actions_toggle,
+ G_N_ELEMENTS (tree_actions_toggle),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetSelectionActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions_selection,
+ G_N_ELEMENTS (tree_actions_selection),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group_selection = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetFileSelectionActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions_file_selection,
+ G_N_ELEMENTS (tree_actions_file_selection),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group_file_selection = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetSingleSelectionActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions_single_selection,
+ G_N_ELEMENTS (tree_actions_single_selection),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group_single_selection = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetSingleMostSelectionActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions_single_most_selection,
+ G_N_ELEMENTS (tree_actions_single_most_selection),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group_single_most_selection = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetSensitiveActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ tree_actions_sensitive,
+ G_N_ELEMENTS (tree_actions_sensitive),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->action_group_sensitive = action_group;
+
+ action_group = gtk_action_group_new ("FileBrowserWidgetBookmarkActionGroup");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group,
+ bookmark_actions,
+ G_N_ELEMENTS (bookmark_actions),
+ obj);
+ gtk_ui_manager_insert_action_group (manager, action_group, 0);
+ obj->priv->bookmark_action_group = action_group;
+
+ action = gtk_action_group_get_action (obj->priv->action_group_sensitive,
+ "DirectoryPrevious");
+ gtk_action_set_sensitive (action, FALSE);
+
+ action = gtk_action_group_get_action (obj->priv->action_group_sensitive,
+ "DirectoryNext");
+ gtk_action_set_sensitive (action, FALSE);
+
+ toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
+
+ /* Previous directory menu tool item */
+ obj->priv->location_previous_menu = gtk_menu_new ();
+ gtk_widget_show (obj->priv->location_previous_menu);
+
+ widget = GTK_WIDGET (gtk_menu_tool_button_new_from_stock (GTK_STOCK_GO_BACK));
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (widget),
+ obj->priv->location_previous_menu);
+
+ g_object_set (widget, "label", _("Previous location"), NULL);
+ gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (widget),
+ _("Go to previous location"));
+ gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (widget),
+ _("Go to a previously opened location"));
+
+ action = gtk_action_group_get_action (obj->priv->action_group_sensitive,
+ "DirectoryPrevious");
+ g_object_set (action, "is_important", TRUE, "short_label",
+ _("Previous location"), NULL);
+ gtk_action_connect_proxy (action, widget);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (widget), 0);
+
+ /* Next directory menu tool item */
+ obj->priv->location_next_menu = gtk_menu_new ();
+ gtk_widget_show (obj->priv->location_next_menu);
+
+ widget = GTK_WIDGET (gtk_menu_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD));
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (widget),
+ obj->priv->location_next_menu);
+
+ g_object_set (widget, "label", _("Next location"), NULL);
+ gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (widget),
+ _("Go to next location"));
+ gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (widget),
+ _("Go to a previously opened location"));
+
+ action = gtk_action_group_get_action (obj->priv->action_group_sensitive,
+ "DirectoryNext");
+ g_object_set (action, "is_important", TRUE, "short_label",
+ _("Previous location"), NULL);
+ gtk_action_connect_proxy (action, widget);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (widget), 1);
+
+ gtk_box_pack_start (GTK_BOX (obj), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ set_enable_delete (obj, obj->priv->enable_delete);
+}
+
+static void
+set_enable_delete (GeditFileBrowserWidget *obj,
+ gboolean enable)
+{
+ GtkAction *action;
+ obj->priv->enable_delete = enable;
+
+ if (obj->priv->action_group_selection == NULL)
+ return;
+
+ action =
+ gtk_action_group_get_action (obj->priv->action_group_selection,
+ "FileDelete");
+
+ g_object_set (action, "visible", enable, "sensitive", enable, NULL);
+}
+
+static gboolean
+filter_real (GeditFileBrowserStore * model, GtkTreeIter * iter,
+ GeditFileBrowserWidget * obj)
+{
+ GSList *item;
+ FilterFunc *func;
+
+ for (item = obj->priv->filter_funcs; item; item = item->next) {
+ func = (FilterFunc *) (item->data);
+
+ if (!func->func (obj, model, iter, func->user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+add_bookmark_hash (GeditFileBrowserWidget * obj,
+ GtkTreeIter * iter)
+{
+ GtkTreeModel *model;
+ GdkPixbuf * pixbuf;
+ gchar * name;
+ gchar * uri;
+ GFile * file;
+ NameIcon * item;
+
+ model = GTK_TREE_MODEL (obj->priv->bookmarks_store);
+
+ uri = gedit_file_bookmarks_store_get_uri (obj->priv->
+ bookmarks_store,
+ iter);
+
+ if (uri == NULL)
+ return;
+
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON,
+ &pixbuf,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME,
+ &name, -1);
+
+ item = g_new (NameIcon, 1);
+ item->name = name;
+ item->icon = pixbuf;
+
+ g_hash_table_insert (obj->priv->bookmarks_hash,
+ file,
+ item);
+}
+
+static void
+init_bookmarks_hash (GeditFileBrowserWidget * obj)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = GTK_TREE_MODEL (obj->priv->bookmarks_store);
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ return;
+
+ do {
+ add_bookmark_hash (obj, &iter);
+ } while (gtk_tree_model_iter_next (model, &iter));
+
+ g_signal_connect (obj->priv->bookmarks_store,
+ "row-changed",
+ G_CALLBACK (on_bookmarks_row_changed),
+ obj);
+
+ g_signal_connect (obj->priv->bookmarks_store,
+ "row-deleted",
+ G_CALLBACK (on_bookmarks_row_deleted),
+ obj);
+}
+
+static void
+on_begin_loading (GeditFileBrowserStore *model,
+ GtkTreeIter *iter,
+ GeditFileBrowserWidget *obj)
+{
+ if (!GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (obj->priv->treeview))))
+ return;
+
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (obj)),
+ obj->priv->busy_cursor);
+}
+
+static void
+on_end_loading (GeditFileBrowserStore *model,
+ GtkTreeIter *iter,
+ GeditFileBrowserWidget *obj)
+{
+ if (!GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (obj->priv->treeview))))
+ return;
+
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (obj)), NULL);
+}
+
+static void
+create_tree (GeditFileBrowserWidget * obj)
+{
+ GtkWidget *sw;
+
+ obj->priv->file_store = gedit_file_browser_store_new (NULL);
+ obj->priv->bookmarks_store = gedit_file_bookmarks_store_new ();
+ obj->priv->treeview =
+ GEDIT_FILE_BROWSER_VIEW (gedit_file_browser_view_new ());
+
+ gedit_file_browser_view_set_restore_expand_state (obj->priv->treeview, TRUE);
+
+ gedit_file_browser_store_set_filter_mode (obj->priv->file_store,
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN
+ |
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY);
+ gedit_file_browser_store_set_filter_func (obj->priv->file_store,
+ (GeditFileBrowserStoreFilterFunc)
+ filter_real, obj);
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_container_add (GTK_CONTAINER (sw),
+ GTK_WIDGET (obj->priv->treeview));
+ gtk_box_pack_start (GTK_BOX (obj), sw, TRUE, TRUE, 0);
+
+ g_signal_connect (obj->priv->treeview, "notify::model",
+ G_CALLBACK (on_model_set), obj);
+ g_signal_connect (obj->priv->treeview, "error",
+ G_CALLBACK (on_treeview_error), obj);
+ g_signal_connect (obj->priv->treeview, "popup-menu",
+ G_CALLBACK (on_treeview_popup_menu), obj);
+ g_signal_connect (obj->priv->treeview, "button-press-event",
+ G_CALLBACK (on_treeview_button_press_event),
+ obj);
+ g_signal_connect (obj->priv->treeview, "key-press-event",
+ G_CALLBACK (on_treeview_key_press_event), obj);
+
+ g_signal_connect (gtk_tree_view_get_selection
+ (GTK_TREE_VIEW (obj->priv->treeview)), "changed",
+ G_CALLBACK (on_selection_changed), obj);
+ g_signal_connect (obj->priv->file_store, "notify::filter-mode",
+ G_CALLBACK (on_filter_mode_changed), obj);
+
+ g_signal_connect (obj->priv->file_store, "notify::virtual-root",
+ G_CALLBACK (on_virtual_root_changed), obj);
+
+ g_signal_connect (obj->priv->file_store, "begin-loading",
+ G_CALLBACK (on_begin_loading), obj);
+
+ g_signal_connect (obj->priv->file_store, "end-loading",
+ G_CALLBACK (on_end_loading), obj);
+
+ g_signal_connect (obj->priv->file_store, "error",
+ G_CALLBACK (on_file_store_error), obj);
+
+ init_bookmarks_hash (obj);
+
+ gtk_widget_show (sw);
+ gtk_widget_show (GTK_WIDGET (obj->priv->treeview));
+}
+
+static void
+create_filter (GeditFileBrowserWidget * obj)
+{
+ GtkWidget *expander;
+ GtkWidget *vbox;
+ GtkWidget *entry;
+
+ expander = gtk_expander_new_with_mnemonic (_("_Match Filename"));
+ gtk_widget_show (expander);
+ gtk_box_pack_start (GTK_BOX (obj), expander, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 3);
+ gtk_widget_show (vbox);
+
+ obj->priv->filter_expander = expander;
+
+ entry = gtk_entry_new ();
+ gtk_widget_show (entry);
+
+ obj->priv->filter_entry = entry;
+
+ g_signal_connect_swapped (entry, "activate",
+ G_CALLBACK (on_entry_filter_activate),
+ obj);
+ g_signal_connect_swapped (entry, "focus_out_event",
+ G_CALLBACK (on_entry_filter_activate),
+ obj);
+
+ gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (expander), vbox);
+}
+
+static void
+gedit_file_browser_widget_init (GeditFileBrowserWidget * obj)
+{
+ obj->priv = GEDIT_FILE_BROWSER_WIDGET_GET_PRIVATE (obj);
+
+ obj->priv->bookmarks_hash = g_hash_table_new_full (g_file_hash,
+ (GEqualFunc)g_file_equal,
+ g_object_unref,
+ free_name_icon);
+
+ gtk_box_set_spacing (GTK_BOX (obj), 3);
+
+ obj->priv->busy_cursor = gdk_cursor_new (GDK_WATCH);
+}
+
+/* Private */
+
+static void
+update_sensitivity (GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ GtkAction *action;
+ gint mode;
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model)) {
+ gtk_action_group_set_sensitive (obj->priv->action_group,
+ TRUE);
+ gtk_action_group_set_sensitive (obj->priv->bookmark_action_group,
+ FALSE);
+
+ mode =
+ gedit_file_browser_store_get_filter_mode
+ (GEDIT_FILE_BROWSER_STORE (model));
+
+ action =
+ gtk_action_group_get_action (obj->priv->action_group,
+ "FilterHidden");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ !(mode &
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN));
+ } else if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) {
+ gtk_action_group_set_sensitive (obj->priv->action_group,
+ FALSE);
+ gtk_action_group_set_sensitive (obj->priv->bookmark_action_group,
+ TRUE);
+
+ /* Set the filter toggle to normal up state, just for visual pleasure */
+ action =
+ gtk_action_group_get_action (obj->priv->action_group,
+ "FilterHidden");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ FALSE);
+ }
+
+ on_selection_changed (gtk_tree_view_get_selection
+ (GTK_TREE_VIEW (obj->priv->treeview)), obj);
+}
+
+static gboolean
+gedit_file_browser_widget_get_first_selected (GeditFileBrowserWidget *obj,
+ GtkTreeIter *iter)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+ GtkTreeModel *model;
+ GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
+ gboolean result;
+
+ if (!rows)
+ return FALSE;
+
+ result = gtk_tree_model_get_iter(model, iter, (GtkTreePath *)(rows->data));
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ return result;
+}
+
+static gboolean
+popup_menu (GeditFileBrowserWidget * obj, GdkEventButton * event, GtkTreeModel * model)
+{
+ GtkWidget *menu;
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model))
+ menu = gtk_ui_manager_get_widget (obj->priv->manager, "/FilePopup");
+ else if (GEDIT_IS_FILE_BOOKMARKS_STORE (model))
+ menu = gtk_ui_manager_get_widget (obj->priv->manager, "/BookmarkPopup");
+ else
+ return FALSE;
+
+ g_return_val_if_fail (menu != NULL, FALSE);
+
+ if (event != NULL) {
+ GtkTreeSelection *selection;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (gtk_tree_selection_count_selected_rows (selection) <= 1) {
+ GtkTreePath *path;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (obj->priv->treeview),
+ (gint)event->x, (gint)event->y,
+ &path, NULL, NULL, NULL))
+ {
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+ }
+ }
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+ } else {
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ gedit_utils_menu_position_under_tree_view,
+ obj->priv->treeview, 0,
+ gtk_get_current_event_time ());
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+filter_glob (GeditFileBrowserWidget * obj, GeditFileBrowserStore * store,
+ GtkTreeIter * iter, gpointer user_data)
+{
+ gchar *name;
+ gboolean result;
+ guint flags;
+
+ if (obj->priv->filter_pattern == NULL)
+ return TRUE;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_NAME, &name,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (FILE_IS_DIR (flags) || FILE_IS_DUMMY (flags))
+ result = TRUE;
+ else
+ result =
+ g_pattern_match_string (obj->priv->filter_pattern,
+ name);
+
+ g_free (name);
+
+ return result;
+}
+
+static void
+rename_selected_file (GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ if (gedit_file_browser_widget_get_first_selected (obj, &iter))
+ gedit_file_browser_view_start_rename (obj->priv->treeview,
+ &iter);
+}
+
+static GList *
+get_deletable_files (GeditFileBrowserWidget *obj) {
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GList *rows;
+ GList *row;
+ GList *paths = NULL;
+ guint flags;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ /* Get all selected files */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+ rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ continue;
+
+ gtk_tree_model_get (model, &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS,
+ &flags, -1);
+
+ if (FILE_IS_DUMMY (flags))
+ continue;
+
+ paths = g_list_append (paths, gtk_tree_path_copy (path));
+ }
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ return paths;
+}
+
+static gboolean
+delete_selected_files (GeditFileBrowserWidget * obj, gboolean trash)
+{
+ GtkTreeModel *model;
+ gboolean confirm;
+ GeditFileBrowserStoreResult result;
+ GList *rows;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return FALSE;
+
+ rows = get_deletable_files (obj);
+
+ if (!rows)
+ return FALSE;
+
+ if (!trash) {
+ g_signal_emit (obj, signals[CONFIRM_DELETE], 0, model, rows, &confirm);
+
+ if (!confirm)
+ return FALSE;
+ }
+
+ result = gedit_file_browser_store_delete_all (GEDIT_FILE_BROWSER_STORE (model),
+ rows, trash);
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ return result == GEDIT_FILE_BROWSER_STORE_RESULT_OK;
+}
+
+static gboolean
+on_file_store_no_trash (GeditFileBrowserStore * store,
+ GList * files,
+ GeditFileBrowserWidget * obj)
+{
+ gboolean confirm = FALSE;
+
+ g_signal_emit (obj, signals[CONFIRM_NO_TRASH], 0, files, &confirm);
+
+ return confirm;
+}
+
+static GFile *
+get_topmost_file (GFile * file)
+{
+ GFile * tmp;
+ GFile * current;
+
+ current = g_object_ref (file);
+
+ while ((tmp = g_file_get_parent (current)) != NULL) {
+ g_object_unref (current);
+ current = tmp;
+ }
+
+ return current;
+}
+
+static GtkWidget *
+create_goto_menu_item (GeditFileBrowserWidget * obj, GList * item,
+ GdkPixbuf * icon)
+{
+ GtkWidget *result;
+ GtkWidget *image;
+ gchar *unescape;
+ GdkPixbuf *pixbuf = NULL;
+ Location *loc;
+
+ loc = (Location *) (item->data);
+
+ if (!get_from_bookmark_file (obj, loc->virtual_root, &unescape, &pixbuf)) {
+ unescape = gedit_file_browser_utils_file_basename (loc->virtual_root);
+
+ if (icon)
+ pixbuf = g_object_ref (icon);
+ }
+
+ if (pixbuf) {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ gtk_widget_show (image);
+
+ result = gtk_image_menu_item_new_with_label (unescape);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (result),
+ image);
+ } else {
+ result = gtk_menu_item_new_with_label (unescape);
+ }
+
+ g_object_set_data (G_OBJECT (result), LOCATION_DATA_KEY, item);
+ g_signal_connect (result, "activate",
+ G_CALLBACK (on_location_jump_activate), obj);
+
+ gtk_widget_show (result);
+
+ g_free (unescape);
+
+ return result;
+}
+
+static GList *
+list_next_iterator (GList * list)
+{
+ if (!list)
+ return NULL;
+
+ return list->next;
+}
+
+static GList *
+list_prev_iterator (GList * list)
+{
+ if (!list)
+ return NULL;
+
+ return list->prev;
+}
+
+static void
+jump_to_location (GeditFileBrowserWidget * obj, GList * item,
+ gboolean previous)
+{
+ Location *loc;
+ GtkWidget *widget;
+ GList *children;
+ GList *child;
+ GList *(*iter_func) (GList *);
+ GtkWidget *menu_from;
+ GtkWidget *menu_to;
+ gchar *root;
+ gchar *virtual_root;
+
+ if (!obj->priv->locations)
+ return;
+
+ if (previous) {
+ iter_func = list_next_iterator;
+ menu_from = obj->priv->location_previous_menu;
+ menu_to = obj->priv->location_next_menu;
+ } else {
+ iter_func = list_prev_iterator;
+ menu_from = obj->priv->location_next_menu;
+ menu_to = obj->priv->location_previous_menu;
+ }
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu_from));
+ child = children;
+
+ /* This is the menuitem for the current location, which is the first
+ to be added to the menu */
+ widget = obj->priv->current_location_menu_item;
+
+ while (obj->priv->current_location != item) {
+ if (widget) {
+ /* Prepend the menu item to the menu */
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu_to),
+ widget);
+
+ g_object_unref (widget);
+ }
+
+ widget = GTK_WIDGET (child->data);
+
+ /* Make sure the widget isn't destroyed when removed */
+ g_object_ref (widget);
+ gtk_container_remove (GTK_CONTAINER (menu_from), widget);
+
+ obj->priv->current_location_menu_item = widget;
+
+ if (obj->priv->current_location == NULL) {
+ obj->priv->current_location = obj->priv->locations;
+
+ if (obj->priv->current_location == item)
+ break;
+ } else {
+ obj->priv->current_location =
+ iter_func (obj->priv->current_location);
+ }
+
+ child = child->next;
+ }
+
+ g_list_free (children);
+
+ obj->priv->changing_location = TRUE;
+
+ loc = (Location *) (obj->priv->current_location->data);
+
+ /* Set the new root + virtual root */
+ root = g_file_get_uri (loc->root);
+ virtual_root = g_file_get_uri (loc->virtual_root);
+
+ gedit_file_browser_widget_set_root_and_virtual_root (obj,
+ root,
+ virtual_root);
+
+ g_free (root);
+ g_free (virtual_root);
+
+ obj->priv->changing_location = FALSE;
+}
+
+static void
+clear_next_locations (GeditFileBrowserWidget * obj)
+{
+ GList *children;
+ GList *item;
+
+ if (obj->priv->current_location == NULL)
+ return;
+
+ while (obj->priv->current_location->prev) {
+ location_free ((Location *) (obj->priv->current_location->
+ prev->data));
+ obj->priv->locations =
+ g_list_remove_link (obj->priv->locations,
+ obj->priv->current_location->prev);
+ }
+
+ children =
+ gtk_container_get_children (GTK_CONTAINER
+ (obj->priv->location_next_menu));
+
+ for (item = children; item; item = item->next) {
+ gtk_container_remove (GTK_CONTAINER
+ (obj->priv->location_next_menu),
+ GTK_WIDGET (item->data));
+ }
+
+ g_list_free (children);
+
+ gtk_action_set_sensitive (gtk_action_group_get_action
+ (obj->priv->action_group_sensitive,
+ "DirectoryNext"), FALSE);
+}
+
+static void
+update_filter_mode (GeditFileBrowserWidget * obj,
+ GtkAction * action,
+ GeditFileBrowserStoreFilterMode mode)
+{
+ gboolean active =
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ GtkTreeModel *model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ gint now;
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model)) {
+ now =
+ gedit_file_browser_store_get_filter_mode
+ (GEDIT_FILE_BROWSER_STORE (model));
+
+ if (active)
+ now &= ~mode;
+ else
+ now |= mode;
+
+ gedit_file_browser_store_set_filter_mode
+ (GEDIT_FILE_BROWSER_STORE (model), now);
+ }
+}
+
+static void
+set_filter_pattern_real (GeditFileBrowserWidget * obj,
+ gchar const * pattern,
+ gboolean update_entry)
+{
+ GtkTreeModel *model;
+
+ model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (pattern != NULL && *pattern == '\0')
+ pattern = NULL;
+
+ if (pattern == NULL && obj->priv->filter_pattern_str == NULL)
+ return;
+
+ if (pattern != NULL && obj->priv->filter_pattern_str != NULL &&
+ strcmp (pattern, obj->priv->filter_pattern_str) == 0)
+ return;
+
+ /* Free the old pattern */
+ g_free (obj->priv->filter_pattern_str);
+ obj->priv->filter_pattern_str = g_strdup (pattern);
+
+ if (obj->priv->filter_pattern) {
+ g_pattern_spec_free (obj->priv->filter_pattern);
+ obj->priv->filter_pattern = NULL;
+ }
+
+ if (pattern == NULL) {
+ if (obj->priv->glob_filter_id != 0) {
+ gedit_file_browser_widget_remove_filter (obj,
+ obj->
+ priv->
+ glob_filter_id);
+ obj->priv->glob_filter_id = 0;
+ }
+ } else {
+ obj->priv->filter_pattern = g_pattern_spec_new (pattern);
+
+ if (obj->priv->glob_filter_id == 0)
+ obj->priv->glob_filter_id =
+ gedit_file_browser_widget_add_filter (obj,
+ filter_glob,
+ NULL,
+ NULL);
+ }
+
+ if (update_entry) {
+ if (obj->priv->filter_pattern_str == NULL)
+ gtk_entry_set_text (GTK_ENTRY (obj->priv->filter_entry),
+ "");
+ else {
+ gtk_entry_set_text (GTK_ENTRY (obj->priv->filter_entry),
+ obj->priv->filter_pattern_str);
+
+ gtk_expander_set_expanded (GTK_EXPANDER (obj->priv->filter_expander),
+ TRUE);
+ }
+ }
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model))
+ gedit_file_browser_store_refilter (GEDIT_FILE_BROWSER_STORE
+ (model));
+
+ g_object_notify (G_OBJECT (obj), "filter-pattern");
+}
+
+
+/* Public */
+
+GtkWidget *
+gedit_file_browser_widget_new (const gchar *data_dir)
+{
+ GeditFileBrowserWidget *obj =
+ g_object_new (GEDIT_TYPE_FILE_BROWSER_WIDGET, NULL);
+
+ create_toolbar (obj, data_dir);
+ create_combo (obj);
+ create_tree (obj);
+ create_filter (obj);
+
+ gedit_file_browser_widget_show_bookmarks (obj);
+
+ return GTK_WIDGET (obj);
+}
+
+void
+gedit_file_browser_widget_show_bookmarks (GeditFileBrowserWidget * obj)
+{
+ /* Select bookmarks in the combo box */
+ g_signal_handlers_block_by_func (obj->priv->combo,
+ on_combo_changed, obj);
+ combo_set_active_by_id (obj, BOOKMARKS_ID);
+ g_signal_handlers_unblock_by_func (obj->priv->combo,
+ on_combo_changed, obj);
+
+ check_current_item (obj, FALSE);
+
+ gedit_file_browser_view_set_model (obj->priv->treeview,
+ GTK_TREE_MODEL (obj->priv->
+ bookmarks_store));
+}
+
+static void
+show_files_real (GeditFileBrowserWidget *obj,
+ gboolean do_root_changed)
+{
+ gedit_file_browser_view_set_model (obj->priv->treeview,
+ GTK_TREE_MODEL (obj->priv->
+ file_store));
+
+ if (do_root_changed)
+ on_virtual_root_changed (obj->priv->file_store, NULL, obj);
+}
+
+void
+gedit_file_browser_widget_show_files (GeditFileBrowserWidget * obj)
+{
+ show_files_real (obj, TRUE);
+}
+
+void
+gedit_file_browser_widget_set_root_and_virtual_root (GeditFileBrowserWidget *obj,
+ gchar const *root,
+ gchar const *virtual_root)
+{
+ GeditFileBrowserStoreResult result;
+
+ if (!virtual_root)
+ result =
+ gedit_file_browser_store_set_root_and_virtual_root
+ (obj->priv->file_store, root, root);
+ else
+ result =
+ gedit_file_browser_store_set_root_and_virtual_root
+ (obj->priv->file_store, root, virtual_root);
+
+ if (result == GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE)
+ show_files_real (obj, TRUE);
+}
+
+void
+gedit_file_browser_widget_set_root (GeditFileBrowserWidget * obj,
+ gchar const *root,
+ gboolean virtual_root)
+{
+ GFile *file;
+ GFile *parent;
+ gchar *str;
+
+ if (!virtual_root) {
+ gedit_file_browser_widget_set_root_and_virtual_root (obj,
+ root,
+ NULL);
+ return;
+ }
+
+ if (!root)
+ return;
+
+ file = g_file_new_for_uri (root);
+ parent = get_topmost_file (file);
+ str = g_file_get_uri (parent);
+
+ gedit_file_browser_widget_set_root_and_virtual_root
+ (obj, str, root);
+
+ g_free (str);
+
+ g_object_unref (file);
+ g_object_unref (parent);
+}
+
+GeditFileBrowserStore *
+gedit_file_browser_widget_get_browser_store (GeditFileBrowserWidget * obj)
+{
+ return obj->priv->file_store;
+}
+
+GeditFileBookmarksStore *
+gedit_file_browser_widget_get_bookmarks_store (GeditFileBrowserWidget * obj)
+{
+ return obj->priv->bookmarks_store;
+}
+
+GeditFileBrowserView *
+gedit_file_browser_widget_get_browser_view (GeditFileBrowserWidget * obj)
+{
+ return obj->priv->treeview;
+}
+
+GtkUIManager *
+gedit_file_browser_widget_get_ui_manager (GeditFileBrowserWidget * obj)
+{
+ return obj->priv->manager;
+}
+
+GtkWidget *
+gedit_file_browser_widget_get_filter_entry (GeditFileBrowserWidget * obj)
+{
+ return obj->priv->filter_entry;
+}
+
+gulong
+gedit_file_browser_widget_add_filter (GeditFileBrowserWidget * obj,
+ GeditFileBrowserWidgetFilterFunc func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ FilterFunc *f;
+ GtkTreeModel *model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ f = filter_func_new (obj, func, user_data, notify);
+ obj->priv->filter_funcs =
+ g_slist_append (obj->priv->filter_funcs, f);
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model))
+ gedit_file_browser_store_refilter (GEDIT_FILE_BROWSER_STORE
+ (model));
+
+ return f->id;
+}
+
+void
+gedit_file_browser_widget_remove_filter (GeditFileBrowserWidget * obj,
+ gulong id)
+{
+ GSList *item;
+ FilterFunc *func;
+
+ for (item = obj->priv->filter_funcs; item; item = item->next)
+ {
+ func = (FilterFunc *) (item->data);
+
+ if (func->id == id)
+ {
+ if (func->destroy_notify)
+ func->destroy_notify (func->user_data);
+
+ obj->priv->filter_funcs =
+ g_slist_remove_link (obj->priv->filter_funcs,
+ item);
+ g_free (func);
+ break;
+ }
+ }
+}
+
+void
+gedit_file_browser_widget_set_filter_pattern (GeditFileBrowserWidget * obj,
+ gchar const *pattern)
+{
+ set_filter_pattern_real (obj, pattern, TRUE);
+}
+
+gboolean
+gedit_file_browser_widget_get_selected_directory (GeditFileBrowserWidget * obj,
+ GtkTreeIter * iter)
+{
+ GtkTreeModel *model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ GtkTreeIter parent;
+ guint flags;
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return FALSE;
+
+ if (!gedit_file_browser_widget_get_first_selected (obj, iter)) {
+ if (!gedit_file_browser_store_get_iter_virtual_root
+ (GEDIT_FILE_BROWSER_STORE (model), iter))
+ return FALSE;
+ }
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!FILE_IS_DIR (flags)) {
+ /* Get the parent, because the selection is a file */
+ gtk_tree_model_iter_parent (model, &parent, iter);
+ *iter = parent;
+ }
+
+ return TRUE;
+}
+
+static guint
+gedit_file_browser_widget_get_num_selected_files_or_directories (GeditFileBrowserWidget *obj,
+ guint *files,
+ guint *dirs)
+{
+ GList *rows, *row;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GeditFileBrowserStoreFlag flags;
+ guint result = 0;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (GEDIT_IS_FILE_BOOKMARKS_STORE (model))
+ return 0;
+
+ rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ /* Get iter from path */
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ continue;
+
+ gtk_tree_model_get (model, &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ -1);
+
+ if (!FILE_IS_DUMMY (flags)) {
+ if (!FILE_IS_DIR (flags))
+ ++(*files);
+ else
+ ++(*dirs);
+
+ ++result;
+ }
+ }
+
+ g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (rows);
+
+ return result;
+}
+
+typedef struct
+{
+ GeditFileBrowserWidget *widget;
+ GCancellable *cancellable;
+} AsyncData;
+
+static AsyncData *
+async_data_new (GeditFileBrowserWidget *widget)
+{
+ AsyncData *ret;
+
+ ret = g_new (AsyncData, 1);
+ ret->widget = widget;
+
+ cancel_async_operation (widget);
+ widget->priv->cancellable = g_cancellable_new ();
+
+ ret->cancellable = g_object_ref (widget->priv->cancellable);
+
+ return ret;
+}
+
+static void
+async_free (AsyncData *async)
+{
+ g_object_unref (async->cancellable);
+ g_free (async);
+}
+
+static void
+set_busy (GeditFileBrowserWidget *obj, gboolean busy)
+{
+ GdkCursor *cursor;
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (GTK_WIDGET (obj->priv->treeview));
+
+ if (!GDK_IS_WINDOW (window))
+ return;
+
+ if (busy)
+ {
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (window, cursor);
+ gdk_cursor_unref (cursor);
+ }
+ else
+ {
+ gdk_window_set_cursor (window, NULL);
+ }
+}
+
+static void try_mount_volume (GeditFileBrowserWidget *widget, GVolume *volume);
+
+static void
+activate_mount (GeditFileBrowserWidget *widget,
+ GVolume *volume,
+ GMount *mount)
+{
+ GFile *root;
+ gchar *uri;
+
+ if (!mount)
+ {
+ gchar *message;
+ gchar *name;
+
+ name = g_volume_get_name (volume);
+ message = g_strdup_printf (_("No mount object for mounted volume: %s"), name);
+
+ g_signal_emit (widget,
+ signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_SET_ROOT,
+ message);
+
+ g_free (name);
+ g_free (message);
+ return;
+ }
+
+ root = g_mount_get_root (mount);
+ uri = g_file_get_uri (root);
+
+ gedit_file_browser_widget_set_root (widget, uri, FALSE);
+
+ g_free (uri);
+ g_object_unref (root);
+}
+
+static void
+try_activate_drive (GeditFileBrowserWidget *widget,
+ GDrive *drive)
+{
+ GList *volumes;
+ GVolume *volume;
+ GMount *mount;
+
+ volumes = g_drive_get_volumes (drive);
+
+ volume = G_VOLUME (volumes->data);
+ mount = g_volume_get_mount (volume);
+
+ if (mount)
+ {
+ /* try set the root of the mount */
+ activate_mount (widget, volume, mount);
+ g_object_unref (mount);
+ }
+ else
+ {
+ /* try to mount it then? */
+ try_mount_volume (widget, volume);
+ }
+
+ g_list_foreach (volumes, (GFunc)g_object_unref, NULL);
+ g_list_free (volumes);
+}
+
+static void
+poll_for_media_cb (GDrive *drive,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+
+ /* check for cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_free (async);
+ return;
+ }
+
+ /* finish poll operation */
+ set_busy (async->widget, FALSE);
+
+ if (g_drive_poll_for_media_finish (drive, res, &error) &&
+ g_drive_has_media (drive) &&
+ g_drive_has_volumes (drive))
+ {
+ try_activate_drive (async->widget, drive);
+ }
+ else
+ {
+ gchar *message;
+ gchar *name;
+
+ name = g_drive_get_name (drive);
+ message = g_strdup_printf (_("Could not open media: %s"), name);
+
+ g_signal_emit (async->widget,
+ signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_SET_ROOT,
+ message);
+
+ g_free (name);
+ g_free (message);
+
+ g_error_free (error);
+ }
+
+ async_free (async);
+}
+
+static void
+mount_volume_cb (GVolume *volume,
+ GAsyncResult *res,
+ AsyncData *async)
+{
+ GError *error = NULL;
+
+ /* check for cancelled state */
+ if (g_cancellable_is_cancelled (async->cancellable))
+ {
+ async_free (async);
+ return;
+ }
+
+ if (g_volume_mount_finish (volume, res, &error))
+ {
+ GMount *mount;
+
+ mount = g_volume_get_mount (volume);
+ activate_mount (async->widget, volume, mount);
+
+ if (mount)
+ g_object_unref (mount);
+ }
+ else
+ {
+ gchar *message;
+ gchar *name;
+
+ name = g_volume_get_name (volume);
+ message = g_strdup_printf (_("Could not mount volume: %s"), name);
+
+ g_signal_emit (async->widget,
+ signals[ERROR],
+ 0,
+ GEDIT_FILE_BROWSER_ERROR_SET_ROOT,
+ message);
+
+ g_free (name);
+ g_free (message);
+
+ g_error_free (error);
+ }
+
+ set_busy (async->widget, FALSE);
+ async_free (async);
+}
+
+static void
+activate_drive (GeditFileBrowserWidget *obj,
+ GtkTreeIter *iter)
+{
+ GDrive *drive;
+ AsyncData *async;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (obj->priv->bookmarks_store), iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT,
+ &drive, -1);
+
+ /* most common use case is a floppy drive, we'll poll for media and
+ go from there */
+ async = async_data_new (obj);
+ g_drive_poll_for_media (drive,
+ async->cancellable,
+ (GAsyncReadyCallback)poll_for_media_cb,
+ async);
+
+ g_object_unref (drive);
+ set_busy (obj, TRUE);
+}
+
+static void
+try_mount_volume (GeditFileBrowserWidget *widget,
+ GVolume *volume)
+{
+ GMountOperation *operation;
+ AsyncData *async;
+
+ operation = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))));
+ async = async_data_new (widget);
+
+ g_volume_mount (volume,
+ G_MOUNT_MOUNT_NONE,
+ operation,
+ async->cancellable,
+ (GAsyncReadyCallback)mount_volume_cb,
+ async);
+
+ g_object_unref (operation);
+ set_busy (widget, TRUE);
+}
+
+static void
+activate_volume (GeditFileBrowserWidget *obj,
+ GtkTreeIter *iter)
+{
+ GVolume *volume;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (obj->priv->bookmarks_store), iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_OBJECT,
+ &volume, -1);
+
+ /* see if we can mount the volume */
+ try_mount_volume (obj, volume);
+ g_object_unref (volume);
+}
+
+void
+gedit_file_browser_widget_refresh (GeditFileBrowserWidget *obj)
+{
+ GtkTreeModel *model =
+ gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model))
+ gedit_file_browser_store_refresh (GEDIT_FILE_BROWSER_STORE
+ (model));
+ else if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) {
+ g_hash_table_ref (obj->priv->bookmarks_hash);
+ g_hash_table_destroy (obj->priv->bookmarks_hash);
+
+ gedit_file_bookmarks_store_refresh
+ (GEDIT_FILE_BOOKMARKS_STORE (model));
+ }
+}
+
+void
+gedit_file_browser_widget_history_back (GeditFileBrowserWidget *obj)
+{
+ if (obj->priv->locations) {
+ if (obj->priv->current_location)
+ jump_to_location (obj,
+ obj->priv->current_location->
+ next, TRUE);
+ else {
+ jump_to_location (obj, obj->priv->locations, TRUE);
+ }
+ }
+}
+
+void
+gedit_file_browser_widget_history_forward (GeditFileBrowserWidget *obj)
+{
+ if (obj->priv->locations)
+ jump_to_location (obj, obj->priv->current_location->prev,
+ FALSE);
+}
+
+static void
+bookmark_open (GeditFileBrowserWidget *obj,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gchar *uri;
+ gint flags;
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BOOKMARKS_STORE_COLUMN_FLAGS,
+ &flags, -1);
+
+ if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_DRIVE)
+ {
+ /* handle a drive node */
+ gedit_file_browser_store_cancel_mount_operation (obj->priv->file_store);
+ activate_drive (obj, iter);
+ return;
+ }
+ else if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_VOLUME)
+ {
+ /* handle a volume node */
+ gedit_file_browser_store_cancel_mount_operation (obj->priv->file_store);
+ activate_volume (obj, iter);
+ return;
+ }
+
+ uri =
+ gedit_file_bookmarks_store_get_uri
+ (GEDIT_FILE_BOOKMARKS_STORE (model), iter);
+
+ if (uri) {
+ /* here we check if the bookmark is a mount point, or if it
+ is a remote bookmark. If that's the case, we will set the
+ root to the uri of the bookmark and not try to set the
+ topmost parent as root (since that may as well not be the
+ mount point anymore) */
+ if ((flags & GEDIT_FILE_BOOKMARKS_STORE_IS_MOUNT) ||
+ (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_REMOTE_BOOKMARK)) {
+ gedit_file_browser_widget_set_root (obj,
+ uri,
+ FALSE);
+ } else {
+ gedit_file_browser_widget_set_root (obj,
+ uri,
+ TRUE);
+ }
+ } else {
+ g_warning ("No uri!");
+ }
+
+ g_free (uri);
+}
+
+static void
+file_open (GeditFileBrowserWidget *obj,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gchar *uri;
+ gint flags;
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ -1);
+
+ if (!FILE_IS_DIR (flags) && !FILE_IS_DUMMY (flags)) {
+ g_signal_emit (obj, signals[URI_ACTIVATED], 0, uri);
+ }
+
+ g_free (uri);
+}
+
+static gboolean
+directory_open (GeditFileBrowserWidget *obj,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gboolean result = FALSE;
+ GError *error = NULL;
+ gchar *uri = NULL;
+ GeditFileBrowserStoreFlag flags;
+
+ gtk_tree_model_get (model, iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI, &uri,
+ -1);
+
+ if (FILE_IS_DIR (flags)) {
+ result = TRUE;
+
+ if (!gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (obj)), uri, GDK_CURRENT_TIME, &error)) {
+ g_signal_emit (obj, signals[ERROR], 0,
+ GEDIT_FILE_BROWSER_ERROR_OPEN_DIRECTORY,
+ error->message);
+
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+
+ g_free (uri);
+
+ return result;
+}
+
+static void
+on_bookmark_activated (GeditFileBrowserView *tree_view,
+ GtkTreeIter *iter,
+ GeditFileBrowserWidget *obj)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+
+ bookmark_open (obj, model, iter);
+}
+
+static void
+on_file_activated (GeditFileBrowserView *tree_view,
+ GtkTreeIter *iter,
+ GeditFileBrowserWidget *obj)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+
+ file_open (obj, model, iter);
+}
+
+static gboolean
+virtual_root_is_root (GeditFileBrowserWidget * obj,
+ GeditFileBrowserStore * model)
+{
+ GtkTreeIter root;
+ GtkTreeIter virtual_root;
+
+ if (!gedit_file_browser_store_get_iter_root (model, &root))
+ return TRUE;
+
+ if (!gedit_file_browser_store_get_iter_virtual_root (model, &virtual_root))
+ return TRUE;
+
+ return gedit_file_browser_store_iter_equal (model, &root, &virtual_root);
+}
+
+static void
+on_virtual_root_changed (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditFileBrowserWidget * obj)
+{
+ GtkTreeIter iter;
+ gchar *uri;
+ gchar *root_uri;
+ GtkTreeIter root;
+ GtkAction *action;
+ Location *loc;
+ GdkPixbuf *pixbuf;
+
+ if (gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)) !=
+ GTK_TREE_MODEL (obj->priv->file_store))
+ {
+ show_files_real (obj, FALSE);
+ }
+
+ if (gedit_file_browser_store_get_iter_virtual_root (model, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_URI,
+ &uri, -1);
+
+ if (gedit_file_browser_store_get_iter_root (model, &root)) {
+ if (!obj->priv->changing_location) {
+ /* Remove all items from obj->priv->current_location on */
+ if (obj->priv->current_location)
+ clear_next_locations (obj);
+
+ root_uri =
+ gedit_file_browser_store_get_root
+ (model);
+
+ loc = g_new (Location, 1);
+ loc->root = g_file_new_for_uri (root_uri);
+ loc->virtual_root = g_file_new_for_uri (uri);
+ g_free (root_uri);
+
+ if (obj->priv->current_location) {
+ /* Add current location to the menu so we can go back
+ to it later */
+ gtk_menu_shell_prepend
+ (GTK_MENU_SHELL
+ (obj->priv->
+ location_previous_menu),
+ obj->priv->
+ current_location_menu_item);
+ }
+
+ obj->priv->locations =
+ g_list_prepend (obj->priv->locations,
+ loc);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (model),
+ &iter,
+ GEDIT_FILE_BROWSER_STORE_COLUMN_ICON,
+ &pixbuf, -1);
+
+ obj->priv->current_location =
+ obj->priv->locations;
+ obj->priv->current_location_menu_item =
+ create_goto_menu_item (obj,
+ obj->priv->
+ current_location,
+ pixbuf);
+
+ g_object_ref_sink (obj->priv->
+ current_location_menu_item);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ }
+
+ action =
+ gtk_action_group_get_action (obj->priv->
+ action_group,
+ "DirectoryUp");
+ gtk_action_set_sensitive (action,
+ !virtual_root_is_root (obj, model));
+
+ action =
+ gtk_action_group_get_action (obj->priv->
+ action_group_sensitive,
+ "DirectoryPrevious");
+ gtk_action_set_sensitive (action,
+ obj->priv->
+ current_location != NULL
+ && obj->priv->
+ current_location->next !=
+ NULL);
+
+ action =
+ gtk_action_group_get_action (obj->priv->
+ action_group_sensitive,
+ "DirectoryNext");
+ gtk_action_set_sensitive (action,
+ obj->priv->
+ current_location != NULL
+ && obj->priv->
+ current_location->prev !=
+ NULL);
+ }
+
+ check_current_item (obj, TRUE);
+ g_free (uri);
+ } else {
+ g_message ("NO!");
+ }
+}
+
+static void
+on_model_set (GObject * gobject, GParamSpec * arg1,
+ GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (gobject));
+
+ clear_signals (obj);
+
+ if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) {
+ clear_next_locations (obj);
+
+ /* Add the current location to the back menu */
+ if (obj->priv->current_location) {
+ GtkAction *action;
+
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (obj->priv->location_previous_menu),
+ obj->priv->current_location_menu_item);
+
+ g_object_unref (obj->priv->current_location_menu_item);
+ obj->priv->current_location = NULL;
+ obj->priv->current_location_menu_item = NULL;
+
+ action = gtk_action_group_get_action (obj->priv->action_group_sensitive,
+ "DirectoryPrevious");
+ gtk_action_set_sensitive (action, TRUE);
+ }
+
+ gtk_widget_set_sensitive (obj->priv->filter_expander, FALSE);
+
+ add_signal (obj, gobject,
+ g_signal_connect (gobject, "bookmark-activated",
+ G_CALLBACK
+ (on_bookmark_activated), obj));
+ } else if (GEDIT_IS_FILE_BROWSER_STORE (model)) {
+ /* make sure any async operation is cancelled */
+ cancel_async_operation (obj);
+
+ add_signal (obj, gobject,
+ g_signal_connect (gobject, "file-activated",
+ G_CALLBACK
+ (on_file_activated), obj));
+
+ add_signal (obj, model,
+ g_signal_connect (model, "no-trash",
+ G_CALLBACK
+ (on_file_store_no_trash), obj));
+
+ gtk_widget_set_sensitive (obj->priv->filter_expander, TRUE);
+ }
+
+ update_sensitivity (obj);
+}
+
+static void
+on_file_store_error (GeditFileBrowserStore * store, guint code,
+ gchar * message, GeditFileBrowserWidget * obj)
+{
+ g_signal_emit (obj, signals[ERROR], 0, code, message);
+}
+
+static void
+on_treeview_error (GeditFileBrowserView * tree_view, guint code,
+ gchar * message, GeditFileBrowserWidget * obj)
+{
+ g_signal_emit (obj, signals[ERROR], 0, code, message);
+}
+
+static void
+on_combo_changed (GtkComboBox * combo, GeditFileBrowserWidget * obj)
+{
+ GtkTreeIter iter;
+ guint id;
+ gchar * uri;
+ GFile * file;
+
+ if (!gtk_combo_box_get_active_iter (combo, &iter))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (obj->priv->combo_model), &iter,
+ COLUMN_ID, &id, -1);
+
+ switch (id) {
+ case BOOKMARKS_ID:
+ gedit_file_browser_widget_show_bookmarks (obj);
+ break;
+
+ case PATH_ID:
+ gtk_tree_model_get (GTK_TREE_MODEL
+ (obj->priv->combo_model), &iter,
+ COLUMN_FILE, &file, -1);
+
+ uri = g_file_get_uri (file);
+ gedit_file_browser_store_set_virtual_root_from_string
+ (obj->priv->file_store, uri);
+
+ g_free (uri);
+ g_object_unref (file);
+ break;
+ }
+}
+
+static gboolean
+on_treeview_popup_menu (GeditFileBrowserView * treeview,
+ GeditFileBrowserWidget * obj)
+{
+ return popup_menu (obj, NULL, gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
+}
+
+static gboolean
+on_treeview_button_press_event (GeditFileBrowserView * treeview,
+ GdkEventButton * event,
+ GeditFileBrowserWidget * obj)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ return popup_menu (obj, event,
+ gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+do_change_directory (GeditFileBrowserWidget * obj,
+ GdkEventKey * event)
+{
+ GtkAction * action = NULL;
+
+ if ((event->state &
+ (~GDK_CONTROL_MASK & ~GDK_SHIFT_MASK & ~GDK_MOD1_MASK)) ==
+ event->state && event->keyval == GDK_BackSpace)
+ action = gtk_action_group_get_action (obj->priv->
+ action_group_sensitive,
+ "DirectoryPrevious");
+ else if (!((event->state & GDK_MOD1_MASK) &&
+ (event->state & (~GDK_CONTROL_MASK & ~GDK_SHIFT_MASK)) == event->state))
+ return FALSE;
+
+ switch (event->keyval) {
+ case GDK_Left:
+ action = gtk_action_group_get_action (obj->priv->
+ action_group_sensitive,
+ "DirectoryPrevious");
+ break;
+ case GDK_Right:
+ action = gtk_action_group_get_action (obj->priv->
+ action_group_sensitive,
+ "DirectoryNext");
+ break;
+ case GDK_Up:
+ action = gtk_action_group_get_action (obj->priv->
+ action_group,
+ "DirectoryUp");
+ break;
+ default:
+ break;
+ }
+
+ if (action != NULL) {
+ gtk_action_activate (action);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+on_treeview_key_press_event (GeditFileBrowserView * treeview,
+ GdkEventKey * event,
+ GeditFileBrowserWidget * obj)
+{
+ guint modifiers;
+
+ if (do_change_directory (obj, event))
+ return TRUE;
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE
+ (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview))))
+ return FALSE;
+
+ modifiers = gtk_accelerator_get_default_mod_mask ();
+
+ if (event->keyval == GDK_Delete
+ || event->keyval == GDK_KP_Delete) {
+
+ if ((event->state & modifiers) == GDK_SHIFT_MASK) {
+ if (obj->priv->enable_delete) {
+ delete_selected_files (obj, FALSE);
+ return TRUE;
+ }
+ } else if ((event->state & modifiers) == 0) {
+ delete_selected_files (obj, TRUE);
+ return TRUE;
+ }
+ }
+
+ if ((event->keyval == GDK_F2)
+ && (event->state & modifiers) == 0) {
+ rename_selected_file (obj);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+on_selection_changed (GtkTreeSelection * selection,
+ GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ guint selected = 0;
+ guint files = 0;
+ guint dirs = 0;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (GEDIT_IS_FILE_BROWSER_STORE (model))
+ {
+ selected = gedit_file_browser_widget_get_num_selected_files_or_directories (obj,
+ &files,
+ &dirs);
+ }
+
+ gtk_action_group_set_sensitive (obj->priv->action_group_selection,
+ selected > 0);
+ gtk_action_group_set_sensitive (obj->priv->action_group_file_selection,
+ (selected > 0) && (selected == files));
+ gtk_action_group_set_sensitive (obj->priv->action_group_single_selection,
+ selected == 1);
+ gtk_action_group_set_sensitive (obj->priv->action_group_single_most_selection,
+ selected <= 1);
+}
+
+static gboolean
+on_entry_filter_activate (GeditFileBrowserWidget * obj)
+{
+ gchar const *text;
+
+ text = gtk_entry_get_text (GTK_ENTRY (obj->priv->filter_entry));
+ set_filter_pattern_real (obj, text, FALSE);
+
+ return FALSE;
+}
+
+static void
+on_location_jump_activate (GtkMenuItem * item,
+ GeditFileBrowserWidget * obj)
+{
+ GList *location;
+
+ location = g_object_get_data (G_OBJECT (item), LOCATION_DATA_KEY);
+
+ if (obj->priv->current_location) {
+ jump_to_location (obj, location,
+ g_list_position (obj->priv->locations,
+ location) >
+ g_list_position (obj->priv->locations,
+ obj->priv->
+ current_location));
+ } else {
+ jump_to_location (obj, location, TRUE);
+ }
+
+}
+
+static void
+on_bookmarks_row_changed (GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ GeditFileBrowserWidget *obj)
+{
+ add_bookmark_hash (obj, iter);
+}
+
+static void
+on_bookmarks_row_deleted (GtkTreeModel * model,
+ GtkTreePath * path,
+ GeditFileBrowserWidget *obj)
+{
+ GtkTreeIter iter;
+ gchar * uri;
+ GFile * file;
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ uri = gedit_file_bookmarks_store_get_uri (obj->priv->bookmarks_store, &iter);
+
+ if (!uri)
+ return;
+
+ file = g_file_new_for_uri (uri);
+ g_hash_table_remove (obj->priv->bookmarks_hash, file);
+
+ g_object_unref (file);
+ g_free (uri);
+}
+
+static void
+on_filter_mode_changed (GeditFileBrowserStore * model,
+ GParamSpec * param,
+ GeditFileBrowserWidget * obj)
+{
+ gint mode;
+ GtkToggleAction * action;
+ gboolean active;
+
+ mode = gedit_file_browser_store_get_filter_mode (model);
+
+ action = GTK_TOGGLE_ACTION (gtk_action_group_get_action (obj->priv->action_group,
+ "FilterHidden"));
+ active = !(mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN);
+
+ if (active != gtk_toggle_action_get_active (action))
+ gtk_toggle_action_set_active (action, active);
+
+ action = GTK_TOGGLE_ACTION (gtk_action_group_get_action (obj->priv->action_group,
+ "FilterBinary"));
+ active = !(mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY);
+
+ if (active != gtk_toggle_action_get_active (action))
+ gtk_toggle_action_set_active (action, active);
+}
+
+static void
+on_action_directory_next (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ gedit_file_browser_widget_history_forward (obj);
+}
+
+static void
+on_action_directory_previous (GtkAction * action,
+ GeditFileBrowserWidget * obj)
+{
+ gedit_file_browser_widget_history_back (obj);
+}
+
+static void
+on_action_directory_up (GtkAction * action,
+ GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ gedit_file_browser_store_set_virtual_root_up (GEDIT_FILE_BROWSER_STORE (model));
+}
+
+static void
+on_action_directory_new (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeIter parent;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ if (!gedit_file_browser_widget_get_selected_directory (obj, &parent))
+ return;
+
+ if (gedit_file_browser_store_new_directory
+ (GEDIT_FILE_BROWSER_STORE (model), &parent, &iter)) {
+ gedit_file_browser_view_start_rename (obj->priv->treeview,
+ &iter);
+ }
+}
+
+static void
+on_action_file_open (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GList *rows;
+ GList *row;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ file_open (obj, model, &iter);
+
+ gtk_tree_path_free (path);
+ }
+
+ g_list_free (rows);
+}
+
+static void
+on_action_file_new (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeIter parent;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ if (!gedit_file_browser_widget_get_selected_directory (obj, &parent))
+ return;
+
+ if (gedit_file_browser_store_new_file
+ (GEDIT_FILE_BROWSER_STORE (model), &parent, &iter)) {
+ gedit_file_browser_view_start_rename (obj->priv->treeview,
+ &iter);
+ }
+}
+
+static void
+on_action_file_rename (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ rename_selected_file (obj);
+}
+
+static void
+on_action_file_delete (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ delete_selected_files (obj, FALSE);
+}
+
+static void
+on_action_file_move_to_trash (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ delete_selected_files (obj, TRUE);
+}
+
+static void
+on_action_directory_refresh (GtkAction * action,
+ GeditFileBrowserWidget * obj)
+{
+ gedit_file_browser_widget_refresh (obj);
+}
+
+static void
+on_action_directory_open (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GList *rows;
+ GList *row;
+ gboolean directory_opened = FALSE;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BROWSER_STORE (model))
+ return;
+
+ rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ for (row = rows; row; row = row->next) {
+ path = (GtkTreePath *)(row->data);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ directory_opened |= directory_open (obj, model, &iter);
+
+ gtk_tree_path_free (path);
+ }
+
+ if (!directory_opened) {
+ if (gedit_file_browser_widget_get_selected_directory (obj, &iter))
+ directory_open (obj, model, &iter);
+ }
+
+ g_list_free (rows);
+}
+
+static void
+on_action_filter_hidden (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ update_filter_mode (obj,
+ action,
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN);
+}
+
+static void
+on_action_filter_binary (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ update_filter_mode (obj,
+ action,
+ GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY);
+}
+
+static void
+on_action_bookmark_open (GtkAction * action, GeditFileBrowserWidget * obj)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview));
+
+ if (!GEDIT_IS_FILE_BOOKMARKS_STORE (model))
+ return;
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ bookmark_open (obj, model, &iter);
+}
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser-widget.h b/plugins/filebrowser/gedit-file-browser-widget.h
new file mode 100755
index 00000000..e9cc2a0e
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser-widget.h
@@ -0,0 +1,121 @@
+/*
+ * gedit-file-browser-widget.h - Gedit plugin providing easy file access
+ * from the sidepanel
+ *
+ * Copyright (C) 2006 - Jesse van den Kieboom <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_FILE_BROWSER_WIDGET_H__
+#define __GEDIT_FILE_BROWSER_WIDGET_H__
+
+#include <gtk/gtk.h>
+#include "gedit-file-browser-store.h"
+#include "gedit-file-bookmarks-store.h"
+#include "gedit-file-browser-view.h"
+
+G_BEGIN_DECLS
+#define GEDIT_TYPE_FILE_BROWSER_WIDGET (gedit_file_browser_widget_get_type ())
+#define GEDIT_FILE_BROWSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_WIDGET, GeditFileBrowserWidget))
+#define GEDIT_FILE_BROWSER_WIDGET_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_BROWSER_WIDGET, GeditFileBrowserWidget const))
+#define GEDIT_FILE_BROWSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FILE_BROWSER_WIDGET, GeditFileBrowserWidgetClass))
+#define GEDIT_IS_FILE_BROWSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FILE_BROWSER_WIDGET))
+#define GEDIT_IS_FILE_BROWSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FILE_BROWSER_WIDGET))
+#define GEDIT_FILE_BROWSER_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FILE_BROWSER_WIDGET, GeditFileBrowserWidgetClass))
+
+typedef struct _GeditFileBrowserWidget GeditFileBrowserWidget;
+typedef struct _GeditFileBrowserWidgetClass GeditFileBrowserWidgetClass;
+typedef struct _GeditFileBrowserWidgetPrivate GeditFileBrowserWidgetPrivate;
+
+typedef
+gboolean (*GeditFileBrowserWidgetFilterFunc) (GeditFileBrowserWidget * obj,
+ GeditFileBrowserStore *
+ model, GtkTreeIter * iter,
+ gpointer user_data);
+
+struct _GeditFileBrowserWidget
+{
+ GtkVBox parent;
+
+ GeditFileBrowserWidgetPrivate *priv;
+};
+
+struct _GeditFileBrowserWidgetClass
+{
+ GtkVBoxClass parent_class;
+
+ /* Signals */
+ void (*uri_activated) (GeditFileBrowserWidget * widget,
+ gchar const *uri);
+ void (*error) (GeditFileBrowserWidget * widget,
+ guint code,
+ gchar const *message);
+ gboolean (*confirm_delete) (GeditFileBrowserWidget * widget,
+ GeditFileBrowserStore * model,
+ GList *list);
+ gboolean (*confirm_no_trash) (GeditFileBrowserWidget * widget,
+ GList *list);
+};
+
+GType gedit_file_browser_widget_get_type (void) G_GNUC_CONST;
+GType gedit_file_browser_widget_register_type (GTypeModule * module);
+
+GtkWidget *gedit_file_browser_widget_new (const gchar *data_dir);
+
+void gedit_file_browser_widget_show_bookmarks (GeditFileBrowserWidget * obj);
+void gedit_file_browser_widget_show_files (GeditFileBrowserWidget * obj);
+
+void gedit_file_browser_widget_set_root (GeditFileBrowserWidget * obj,
+ gchar const *root,
+ gboolean virtual_root);
+void
+gedit_file_browser_widget_set_root_and_virtual_root (GeditFileBrowserWidget * obj,
+ gchar const *root,
+ gchar const *virtual_root);
+
+gboolean
+gedit_file_browser_widget_get_selected_directory (GeditFileBrowserWidget * obj,
+ GtkTreeIter * iter);
+
+GeditFileBrowserStore *
+gedit_file_browser_widget_get_browser_store (GeditFileBrowserWidget * obj);
+GeditFileBookmarksStore *
+gedit_file_browser_widget_get_bookmarks_store (GeditFileBrowserWidget * obj);
+GeditFileBrowserView *
+gedit_file_browser_widget_get_browser_view (GeditFileBrowserWidget * obj);
+GtkWidget *
+gedit_file_browser_widget_get_filter_entry (GeditFileBrowserWidget * obj);
+
+GtkUIManager *
+gedit_file_browser_widget_get_ui_manager (GeditFileBrowserWidget * obj);
+
+gulong gedit_file_browser_widget_add_filter (GeditFileBrowserWidget * obj,
+ GeditFileBrowserWidgetFilterFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+void gedit_file_browser_widget_remove_filter (GeditFileBrowserWidget * obj,
+ gulong id);
+void gedit_file_browser_widget_set_filter_pattern (GeditFileBrowserWidget * obj,
+ gchar const *pattern);
+
+void gedit_file_browser_widget_refresh (GeditFileBrowserWidget * obj);
+void gedit_file_browser_widget_history_back (GeditFileBrowserWidget * obj);
+void gedit_file_browser_widget_history_forward (GeditFileBrowserWidget * obj);
+
+G_END_DECLS
+#endif /* __GEDIT_FILE_BROWSER_WIDGET_H__ */
+
+// ex:ts=8:noet:
diff --git a/plugins/filebrowser/gedit-file-browser.schemas.in b/plugins/filebrowser/gedit-file-browser.schemas.in
new file mode 100755
index 00000000..c80c8eec
--- /dev/null
+++ b/plugins/filebrowser/gedit-file-browser.schemas.in
@@ -0,0 +1,97 @@
+<mateconfschemafile>
+ <schemalist>
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/on_load/tree_view</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/on_load/tree_view</applyto>
+ <owner>gedit</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Open With Tree View</short>
+ <long>Open the tree view when the file browser plugin gets loaded instead of the bookmarks view</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/on_load/root</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/on_load/root</applyto>
+ <owner>gedit</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>File Browser Root Directory</short>
+ <long>The file browser root directory to use when loading the file
+ browser plugin and onload/tree_view is TRUE.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/on_load/virtual_root</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/on_load/virtual_root</applyto>
+ <owner>gedit</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>File Browser Virtual Root Directory</short>
+ <long>The file browser virtual root directory to use when loading the
+ file browser plugin when onload/tree_view is TRUE. The virtual root
+ must always be below the actual root.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/on_load/enable_remote</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/on_load/enable_remote</applyto>
+ <owner>gedit</owner>
+ <type>bool</type>
+ <default>FALSE</default>
+ <locale name="C">
+ <short>Enable Restore of Remote Locations</short>
+ <long>Sets whether to enable restoring of remote locations.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/open_at_first_doc</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/open_at_first_doc</applyto>
+ <owner>gedit</owner>
+ <type>bool</type>
+ <default>TRUE</default>
+ <locale name="C">
+ <short>Set Location to First Document</short>
+ <long>If TRUE the file browser plugin will view the directory of
+ the first opened document given that the file browser hasn't been
+ used yet. (Thus this generally applies to opening a document from
+ the command line or opening it with Caja, etc.)</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/filter_mode</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/filter_mode</applyto>
+ <owner>gedit</owner>
+ <type>string</type>
+ <default>hidden_and_binary</default>
+ <locale name="C">
+ <short>File Browser Filter Mode</short>
+ <long>This value determines what files get filtered from the file
+ browser. Valid values are: none (filter nothing),
+ hidden (filter hidden files), binary (filter binary files) and
+ hidden_and_binary (filter both hidden and binary files).</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/gedit-2/plugins/filebrowser/filter_pattern</key>
+ <applyto>/apps/gedit-2/plugins/filebrowser/filter_pattern</applyto>
+ <owner>gedit</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>File Browser Filter Pattern</short>
+ <long>The filter pattern to filter the file browser with. This filter
+ works on top of the filter_mode.</long>
+ </locale>
+ </schema>
+ </schemalist>
+</mateconfschemafile>