diff options
Diffstat (limited to '')
48 files changed, 14849 insertions, 0 deletions
diff --git a/plugins/filebrowser/filebrowser.plugin.desktop.in b/plugins/filebrowser/filebrowser.plugin.desktop.in new file mode 100644 index 0000000..706168d --- /dev/null +++ b/plugins/filebrowser/filebrowser.plugin.desktop.in @@ -0,0 +1,12 @@ +[Plugin] +Loader=C +Module=filebrowser +IAge=3 +Name=File Browser Panel +Description=Easy file access from the side panel. +# TRANSLATORS: Do NOT translate or transliterate this text! +# This is an icon file name. +Icon=system-file-manager +Authors=Jesse van den Kieboom <jesse@icecrew.nl> +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 100644 index 0000000..df0968d --- /dev/null +++ b/plugins/filebrowser/gedit-file-bookmarks-store.c @@ -0,0 +1,920 @@ +/* + * gedit-file-bookmarks-store.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gedit/gedit-utils.h> + +#include "gedit-file-bookmarks-store.h" +#include "gedit-file-browser-utils.h" + +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); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBookmarksStore, + gedit_file_bookmarks_store, + GTK_TYPE_TREE_STORE, + 0, + G_ADD_PRIVATE_DYNAMIC (GeditFileBookmarksStore)) + +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; + } + + g_clear_object (&obj->priv->bookmarks_monitor); + + G_OBJECT_CLASS (gedit_file_bookmarks_store_parent_class)->dispose (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; +} + +static void +gedit_file_bookmarks_store_class_finalize (GeditFileBookmarksStoreClass *klass) +{ +} + +static void +gedit_file_bookmarks_store_init (GeditFileBookmarksStore *obj) +{ + obj->priv = gedit_file_bookmarks_store_get_instance_private (obj); +} + +/* Private */ +static void +add_node (GeditFileBookmarksStore *model, + GdkPixbuf *pixbuf, + const gchar *icon_name, + 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_ICON_NAME, icon_name, + 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) +{ + gboolean native = g_file_is_native (file); + gchar *icon_name = NULL; + gchar *newname; + + if (native && !g_file_query_exists (file, NULL)) + return FALSE; + + if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_HOME) + icon_name = g_strdup ("user-home-symbolic"); + else if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_DESKTOP) + icon_name = g_strdup ("user-desktop-symbolic"); + else if (flags & GEDIT_FILE_BOOKMARKS_STORE_IS_ROOT) + icon_name = g_strdup ("drive-harddisk-symbolic"); + else + { + /* getting the icon is a sync get_info call, so we just do it for local files */ + if (native) + icon_name = gedit_file_browser_utils_symbolic_icon_name_from_file (file); + else + icon_name = g_strdup ("folder-symbolic"); + } + + if (name == NULL) + newname = gedit_file_browser_utils_file_basename (file); + else + newname = g_strdup (name); + + add_node (model, NULL, icon_name, newname, G_OBJECT (file), flags, iter); + + g_free (icon_name); + g_free (newname); + + return TRUE; +} + +static void +check_mount_separator (GeditFileBookmarksStore *model, + guint flags, + gboolean added) +{ + GtkTreeIter iter; + gboolean 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, 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 = g_get_home_dir (); + GFile *file; + + if (path != NULL) + { + file = g_file_new_for_path (path); + add_file (model, + file, + _("Home"), + GEDIT_FILE_BOOKMARKS_STORE_IS_HOME | GEDIT_FILE_BOOKMARKS_STORE_IS_SPECIAL_DIR, + NULL); + g_object_unref (file); + } + +#if defined(G_OS_WIN32) || defined(OS_OSX) + 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); + } +#endif + + 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, + gchar **icon_name, + guint *flags) +{ + GIcon *icon = NULL; + + *flags = GEDIT_FILE_BOOKMARKS_STORE_IS_FS; + *name = NULL; + *icon_name = NULL; + + if (G_IS_DRIVE (fs)) + { + icon = g_drive_get_symbolic_icon (G_DRIVE (fs)); + *name = g_drive_get_name (G_DRIVE (fs)); + *icon_name = gedit_file_browser_utils_name_from_themed_icon (icon); + + *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_DRIVE; + } + else if (G_IS_VOLUME (fs)) + { + icon = g_volume_get_symbolic_icon (G_VOLUME (fs)); + *name = g_volume_get_name (G_VOLUME (fs)); + *icon_name = gedit_file_browser_utils_name_from_themed_icon (icon); + + *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_VOLUME; + } + else if (G_IS_MOUNT (fs)) + { + icon = g_mount_get_symbolic_icon (G_MOUNT (fs)); + *name = g_mount_get_name (G_MOUNT (fs)); + *icon_name = gedit_file_browser_utils_name_from_themed_icon (icon); + + *flags |= GEDIT_FILE_BOOKMARKS_STORE_IS_MOUNT; + } + + if (icon) + g_object_unref (icon); +} + +static void +add_fs (GeditFileBookmarksStore *model, + gpointer fs, + guint flags, + GtkTreeIter *iter) +{ + gchar *icon_name = NULL; + gchar *name = NULL; + guint fsflags; + + get_fs_properties (fs, &name, &icon_name, &fsflags); + add_node (model, NULL, icon_name, name, fs, flags | fsflags, iter); + + g_free (name); + g_free (icon_name); + check_mount_separator (model, GEDIT_FILE_BOOKMARKS_STORE_IS_FS, TRUE); +} + +static void +process_volume_cb (GVolume *volume, + GeditFileBookmarksStore *model) +{ + GMount *mount = g_volume_get_mount (volume); + guint flags = GEDIT_FILE_BOOKMARKS_STORE_NONE; + + /* 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 = 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 = g_volume_monitor_get_connected_drives (model->priv->volume_monitor); + + g_list_foreach (drives, (GFunc)process_drive_cb, model); + g_list_free_full (drives, g_object_unref); +} + +static void +process_volume_nodrive_cb (GVolume *volume, + GeditFileBookmarksStore *model) +{ + GDrive *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 = g_volume_monitor_get_volumes (model->priv->volume_monitor); + + g_list_foreach (volumes, (GFunc)process_volume_nodrive_cb, model); + g_list_free_full (volumes, g_object_unref); +} + +static void +process_mount_novolume_cb (GMount *mount, + GeditFileBookmarksStore *model) +{ + GVolume *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 = g_volume_monitor_get_mounts (model->priv->volume_monitor); + + g_list_foreach (mounts, (GFunc)process_mount_novolume_cb, model); + g_list_free_full (mounts, g_object_unref); +} + +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 != NULL; ++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 = g_file_new_for_uri (uri); + guint flags = GEDIT_FILE_BOOKMARKS_STORE_IS_BOOKMARK; + GtkTreeIter iter; + gboolean ret; + + 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 gchar * +get_bookmarks_file (void) +{ + return g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); +} + +static gchar * +get_legacy_bookmarks_file (void) +{ + return g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL); +} + +static gboolean +parse_bookmarks_file (GeditFileBookmarksStore *model, + const gchar *bookmarks, + gboolean *added) +{ + GError *error = NULL; + gchar *contents; + gchar **lines; + gchar **line; + + if (!g_file_get_contents (bookmarks, &contents, NULL, &error)) + { + /* The bookmarks file doesn't exist (which is perfectly fine) */ + g_error_free (error); + + return FALSE; + } + + lines = g_strsplit (contents, "\n", 0); + + for (line = lines; *line; ++line) + { + if (**line) + { + GFile *location; + + 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 */ + location = g_file_new_for_uri (*line); + if (gedit_utils_is_valid_location (location)) + { + *added |= add_bookmark (model, name, *line); + } + g_object_unref (location); + } + } + + g_strfreev (lines); + g_free (contents); + + /* Add a watch */ + if (model->priv->bookmarks_monitor == NULL) + { + GFile *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", + G_CALLBACK (on_bookmarks_file_changed), + model); + } + + return TRUE; +} + +static void +init_bookmarks (GeditFileBookmarksStore *model) +{ + gchar *bookmarks = get_bookmarks_file (); + gboolean added = FALSE; + + if (!parse_bookmarks_file (model, bookmarks, &added)) + { + g_free (bookmarks); + + /* try the old location (gtk <= 3.4) */ + bookmarks = get_legacy_bookmarks_file (); + parse_bookmarks_file (model, bookmarks, &added); + } + + if (added) + { + /* Bookmarks separator */ + add_node (model, NULL, 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 nautilus */ + 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 sep = GEDIT_FILE_BOOKMARKS_STORE_IS_SEPARATOR; + guint f1; + guint f2; + gint *flags; + + 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 = 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) && + 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_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; +} + +GFile * +gedit_file_bookmarks_store_get_location (GeditFileBookmarksStore *model, + GtkTreeIter *iter) +{ + GObject *obj; + GFile *file = NULL; + guint flags; + GFile * 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 = (GFile *)g_object_ref (obj); + + g_object_unref (obj); + + if (file) + { + ret = g_file_dup (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; + /* FIXME: shouldn't we also monitor the directory? */ + case G_FILE_MONITOR_EVENT_DELETED: + /* Remove bookmarks */ + remove_bookmarks (model); + g_object_unref (monitor); + model->priv->bookmarks_monitor = NULL; + break; + default: + break; + } +} + +void +_gedit_file_bookmarks_store_register_type (GTypeModule *type_module) +{ + gedit_file_bookmarks_store_register_type (type_module); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-bookmarks-store.h b/plugins/filebrowser/gedit-file-bookmarks-store.h new file mode 100644 index 0000000..19de53b --- /dev/null +++ b/plugins/filebrowser/gedit-file-bookmarks-store.h @@ -0,0 +1,91 @@ +/* + * gedit-file-bookmarks-store.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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_ICON_NAME, + 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; + +GeditFileBookmarksStore *gedit_file_bookmarks_store_new (void); +GFile *gedit_file_bookmarks_store_get_location (GeditFileBookmarksStore *model, + GtkTreeIter *iter); +void gedit_file_bookmarks_store_refresh (GeditFileBookmarksStore *model); + +void _gedit_file_bookmarks_store_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* GEDIT_FILE_BOOKMARKS_STORE_H */ +/* ex:set 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 100644 index 0000000..c97ffff --- /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 "@basename@" */ + +/*** 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-stage1.c.template b/plugins/filebrowser/gedit-file-browser-enum-types-stage1.c.template new file mode 100644 index 0000000..fd58866 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-enum-types-stage1.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 "@basename@" + +/*** 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 100644 index 0000000..c13167a --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-enum-types.h.template @@ -0,0 +1,28 @@ +/*** 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 "@basename@" */ + +/*** 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 100644 index 0000000..89e6ae3 --- /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 <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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 */ +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-messages.c b/plugins/filebrowser/gedit-file-browser-messages.c new file mode 100644 index 0000000..7f25424 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-messages.c @@ -0,0 +1,1074 @@ +/* + * gedit-file-browser-messages.c + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "gedit-file-browser-messages.h" +#include "gedit-file-browser-store.h" +#include "messages/messages.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) +#define BUS_DISCONNECT(bus, name, data) gedit_message_bus_disconnect_by_func(bus, MESSAGE_OBJECT_PATH, #name, (GeditMessageCallback) message_##name##_cb, data) + +typedef struct +{ + GeditWindow *window; + GeditMessage *message; +} MessageCacheData; + +typedef struct +{ + guint row_inserted_id; + guint before_row_deleted_id; + guint root_changed_id; + guint begin_loading_id; + guint end_loading_id; + + 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); + + 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); + + 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); + + g_hash_table_destroy (data->row_tracking); + g_hash_table_destroy (data->filters); + + 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; + GFile *location; + + store = gedit_file_browser_widget_get_browser_store (data->widget); + location = gedit_file_browser_store_get_virtual_root (store); + + if (location) + { + g_object_set (message, "location", location, NULL); + g_object_unref (location); + } +} + +static void +message_set_root_cb (GeditMessageBus *bus, + GeditMessage *message, + WindowData *data) +{ + GFile *root; + GFile *virtual = NULL; + + g_object_get (message, "location", &root, NULL); + + if (!root) + { + return; + } + + g_object_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); + } +} + +static void +message_set_emblem_cb (GeditMessageBus *bus, + GeditMessage *message, + WindowData *data) +{ + gchar *id = NULL; + gchar *emblem = NULL; + GtkTreePath *path; + GeditFileBrowserStore *store; + + g_object_get (message, "id", &id, "emblem", &emblem, NULL); + + if (!id) + { + g_free (id); + g_free (emblem); + + return; + } + + path = track_row_lookup (data, id); + + if (path != NULL) + { + GtkTreeIter iter; + GValue value = G_VALUE_INIT; + GdkPixbuf *pixbuf = NULL; + + if (emblem != NULL) + { + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + emblem, + 10, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL); + } + + 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); + } + + if (pixbuf) + { + g_object_unref (pixbuf); + } + } + + g_free (id); + g_free (emblem); +} + +static void +message_set_markup_cb (GeditMessageBus *bus, + GeditMessage *message, + WindowData *data) +{ + gchar *id = NULL; + gchar *markup = NULL; + GtkTreePath *path; + GeditFileBrowserStore *store; + + g_object_get (message, "id", &id, "markup", &markup, NULL); + + if (!id) + { + g_free (id); + g_free (markup); + + return; + } + + path = track_row_lookup (data, id); + + if (path != NULL) + { + GtkTreeIter iter; + GValue value = G_VALUE_INIT; + + store = gedit_file_browser_widget_get_browser_store (data->widget); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) + { + if (markup == NULL) + { + gchar *name; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_NAME, &name, + -1); + markup = g_markup_escape_text (name, -1); + + g_free (name); + } + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, markup); + + gedit_file_browser_store_set_value (store, + &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, + &value); + + g_value_unset (&value); + } + + gtk_tree_path_free (path); + } + + g_free (id); + g_free (markup); +} + +static gchar * +item_id (const gchar *path, + GFile *location) +{ + gchar *uri; + gchar *id; + + uri = g_file_get_uri (location); + id = g_strconcat (path, "::", uri, NULL); + g_free (uri); + + return id; +} + +static gchar * +track_row (WindowData *data, + GeditFileBrowserStore *store, + GtkTreePath *path, + GFile *location) +{ + GtkTreeRowReference *ref; + gchar *id; + gchar *pathstr; + + pathstr = gtk_tree_path_to_string (path); + id = item_id (pathstr, location); + + 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 *name; + GFile *location; + guint flags = 0; + + store = gedit_file_browser_widget_get_browser_store (data->widget); + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_NAME, &name, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, + -1); + + if (location) + { + gchar *track_id; + + if (path && gtk_tree_path_get_depth (path) != 0) + { + track_id = track_row (data, store, path, location); + } + else + { + track_id = NULL; + } + + g_object_set (message, + "id", track_id, + "location", location, + NULL); + + if (gedit_message_has (message, "name")) + { + g_object_set (message, + "name", name, + NULL); + } + + if (gedit_message_has (message, "is_directory")) + { + g_object_set (message, + "is_directory", + FILE_IS_DIR (flags), + NULL); + } + + g_free (track_id); + g_object_unref (location); + } + + g_free (name); +} + +static gboolean +custom_message_filter_func (GeditFileBrowserWidget *widget, + GeditFileBrowserStore *store, + GtkTreeIter *iter, + FilterData *data) +{ + WindowData *wdata = get_window_data (data->window); + GFile *location; + guint flags = 0; + gboolean filter = FALSE; + GtkTreePath *path; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, + -1); + + if (!location || FILE_IS_DUMMY (flags)) + 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); + + g_object_set (data->message, "filter", filter, NULL); + + gedit_message_bus_send_message_sync (wdata->bus, data->message); + g_object_get (data->message, "filter", &filter, NULL); + + g_object_unref (location); + + return !filter; +} + +static void +message_add_filter_cb (GeditMessageBus *bus, + GeditMessage *message, + GeditWindow *window) +{ + const gchar *object_path = NULL; + const gchar *method = NULL; + gulong id; + GeditMessage *cbmessage; + FilterData *filter_data; + WindowData *data; + GType message_type; + + data = get_window_data (window); + + object_path = gedit_message_get_object_path (message); + method = gedit_message_get_method (message); + + message_type = gedit_message_bus_lookup (bus, object_path, method); + + if (message_type == G_TYPE_INVALID) + { + return; + } + + /* Check if the message type has the correct arguments */ + if (!gedit_message_type_check (message_type, "id", G_TYPE_STRING) || + !gedit_message_type_check (message_type, "location", G_TYPE_FILE) || + !gedit_message_type_check (message_type, "is-directory", G_TYPE_BOOLEAN) || + !gedit_message_type_check (message_type, "filter", G_TYPE_BOOLEAN)) + { + return; + } + + cbmessage = g_object_new (message_type, + "object-path", object_path, + "method", method, + "id", NULL, + "location", 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; + + g_object_get (message, "id", &id, NULL); + + if (!id) + return; + + gedit_file_browser_widget_remove_filter (data->widget, id); +} + +static void +message_extend_context_menu_cb (GeditMessageBus *bus, + GeditMessage *message, + GeditWindow *window) +{ + WindowData *data; + GeditMenuExtension *ext; + + data = get_window_data (window); + + ext = gedit_file_browser_widget_extend_context_menu (data->widget); + + g_object_set (message, "extension", ext, NULL); + g_object_unref (ext); +} + +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; + + g_object_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; + + g_object_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_get_view_cb (GeditMessageBus *bus, + GeditMessage *message, + WindowData *data) +{ + GeditFileBrowserView *view; + view = gedit_file_browser_widget_get_browser_view (data->widget); + + g_object_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, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT, + MESSAGE_OBJECT_PATH, + "get_root"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT, + MESSAGE_OBJECT_PATH, + "set_root"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM, + MESSAGE_OBJECT_PATH, + "set_emblem"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP, + MESSAGE_OBJECT_PATH, + "set_markup"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER, + MESSAGE_OBJECT_PATH, + "add_filter"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID, + MESSAGE_OBJECT_PATH, + "remove_filter"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU, + MESSAGE_OBJECT_PATH, + "extend_context_menu"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "up"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "history_back"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "history_forward"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "refresh"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION, + MESSAGE_OBJECT_PATH, + "set_show_hidden"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION, + MESSAGE_OBJECT_PATH, + "set_show_binary"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "show_bookmarks"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_MESSAGE, + MESSAGE_OBJECT_PATH, + "show_files"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW, + MESSAGE_OBJECT_PATH, + "get_view"); + + BUS_CONNECT (bus, get_root, data); + BUS_CONNECT (bus, set_root, data); + BUS_CONNECT (bus, set_emblem, data); + BUS_CONNECT (bus, set_markup, data); + BUS_CONNECT (bus, add_filter, window); + BUS_CONNECT (bus, remove_filter, data); + BUS_CONNECT (bus, extend_context_menu, window); + + 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) +{ + guint flags = 0; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + 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); + } +} + +static void +store_before_row_deleted (GeditFileBrowserStore *store, + GtkTreePath *path, + MessageCacheData *data) +{ + GtkTreeIter iter; + 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_FLAGS, &flags, + -1); + + if (!FILE_IS_DUMMY (flags) && !FILE_IS_FILTERED (flags)) + { + WindowData *wdata = get_window_data (data->window); + gchar *id; + + set_item_message (wdata, &iter, path, data->message); + + /* Must get the ID before the plugin can modify it */ + g_object_get (data->message, "id", &id, NULL); + + gedit_message_bus_send_message_sync (wdata->bus, data->message); + + g_hash_table_remove (wdata->row_tracking, id); + g_free (id); + } +} + +static void +store_virtual_root_changed (GeditFileBrowserStore *store, + GParamSpec *spec, + MessageCacheData *data) +{ + WindowData *wdata = get_window_data (data->window); + GFile *vroot; + + vroot = gedit_file_browser_store_get_virtual_root (store); + + if (!vroot) + { + return; + } + + g_object_set (data->message, + "location", vroot, + NULL); + + gedit_message_bus_send_message_sync (wdata->bus, data->message); + + g_object_unref (vroot); +} + +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; + + GeditMessage *message; + WindowData *data; + + /* Register signals */ + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + MESSAGE_OBJECT_PATH, + "root_changed"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + MESSAGE_OBJECT_PATH, + "begin_loading"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + MESSAGE_OBJECT_PATH, + "end_loading"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + MESSAGE_OBJECT_PATH, + "inserted"); + + gedit_message_bus_register (bus, + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + MESSAGE_OBJECT_PATH, + "deleted"); + + store = gedit_file_browser_widget_get_browser_store (widget); + + message = g_object_new (GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + "object-path", MESSAGE_OBJECT_PATH, + "method", "inserted", + 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 = g_object_new (GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + "object-path", MESSAGE_OBJECT_PATH, + "method", "deleted", + NULL); + + data->before_row_deleted_id = + g_signal_connect_data (store, + "before-row-deleted", + G_CALLBACK (store_before_row_deleted), + message_cache_data_new (window, message), + (GClosureNotify)message_cache_data_free, + 0); + + message = g_object_new (GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + "object-path", MESSAGE_OBJECT_PATH, + "method", "root_changed", + 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 = g_object_new (GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + "object-path", MESSAGE_OBJECT_PATH, + "method", "begin_loading", + 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 = g_object_new (GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION, + "object-path", MESSAGE_OBJECT_PATH, + "method", "end_loading", + 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, + const gchar *object_path, + const gchar *method, + GeditWindow *window) +{ + gchar *identifier; + FilterData *data; + WindowData *wdata; + + wdata = get_window_data (window); + + identifier = gedit_message_type_identifier (object_path, method); + + 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->before_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); + WindowData *data = get_window_data (window); + + cleanup_signals (window); + + BUS_DISCONNECT (bus, get_root, data); + BUS_DISCONNECT (bus, set_root, data); + BUS_DISCONNECT (bus, set_emblem, data); + BUS_DISCONNECT (bus, set_markup, data); + BUS_DISCONNECT (bus, add_filter, window); + BUS_DISCONNECT (bus, remove_filter, data); + + BUS_DISCONNECT (bus, up, data); + BUS_DISCONNECT (bus, history_back, data); + BUS_DISCONNECT (bus, history_forward, data); + + BUS_DISCONNECT (bus, refresh, data); + + BUS_DISCONNECT (bus, set_show_hidden, data); + BUS_DISCONNECT (bus, set_show_binary, data); + + BUS_DISCONNECT (bus, show_bookmarks, data); + BUS_DISCONNECT (bus, show_files, data); + + BUS_DISCONNECT (bus, get_view, data); + + gedit_message_bus_unregister_all (bus, MESSAGE_OBJECT_PATH); + + window_data_free (window); +} +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-messages.h b/plugins/filebrowser/gedit-file-browser-messages.h new file mode 100644 index 0000000..9fcb316 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-messages.h @@ -0,0 +1,33 @@ +/* + * gedit-file-browser-messages.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2008 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-plugin.c b/plugins/filebrowser/gedit-file-browser-plugin.c new file mode 100644 index 0000000..2e7b79f --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-plugin.c @@ -0,0 +1,943 @@ +/* + * gedit-file-browser-plugin.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n-lib.h> +#include <gmodule.h> +#include <gedit/gedit-app.h> +#include <gedit/gedit-commands.h> +#include <gedit/gedit-debug.h> +#include <gedit/gedit-window.h> +#include <gedit/gedit-window-activatable.h> +#include <gedit/gedit-utils.h> +#include <tepl/tepl.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 FILEBROWSER_BASE_SETTINGS "org.gnome.gedit.plugins.filebrowser" +#define FILEBROWSER_TREE_VIEW "tree-view" +#define FILEBROWSER_ROOT "root" +#define FILEBROWSER_VIRTUAL_ROOT "virtual-root" +#define FILEBROWSER_ENABLE_REMOTE "enable-remote" +#define FILEBROWSER_OPEN_AT_FIRST_DOC "open-at-first-doc" +#define FILEBROWSER_FILTER_MODE "filter-mode" +#define FILEBROWSER_FILTER_PATTERN "filter-pattern" +#define FILEBROWSER_BINARY_PATTERNS "binary-patterns" + +#define NAUTILUS_BASE_SETTINGS "org.gnome.nautilus.preferences" +#define NAUTILUS_FALLBACK_SETTINGS "org.gnome.gedit.plugins.filebrowser.nautilus" +#define NAUTILUS_CLICK_POLICY_KEY "click-policy" + +#define TERMINAL_BASE_SETTINGS "org.gnome.desktop.default-applications.terminal" +#define TERMINAL_EXEC_KEY "exec" + +struct _GeditFileBrowserPluginPrivate +{ + GSettings *settings; + GSettings *nautilus_settings; + GSettings *terminal_settings; + + GeditWindow *window; + + GeditFileBrowserWidget *tree_widget; + gboolean auto_root; + gulong end_loading_handle; + + guint click_policy_handle; +}; + +enum +{ + PROP_0, + PROP_WINDOW +}; + +static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface); + +static void on_location_activated_cb (GeditFileBrowserWidget *widget, + GFile *location, + GeditWindow *window); +static void on_error_cb (GeditFileBrowserWidget *widget, + guint code, + gchar const *message, + GeditFileBrowserPlugin *plugin); +static void on_model_set_cb (GeditFileBrowserView *widget, + GParamSpec *param, + GeditFileBrowserPlugin *plugin); +static void on_virtual_root_changed_cb (GeditFileBrowserStore *model, + GParamSpec *param, + GeditFileBrowserPlugin *plugin); +static void on_rename_cb (GeditFileBrowserStore *model, + GFile *oldfile, + GFile *newfile, + GeditWindow *window); +static void on_tab_added_cb (GeditWindow *window, + GeditTab *tab, + GeditFileBrowserPlugin *plugin); +static gboolean on_confirm_delete_cb (GeditFileBrowserWidget *widget, + GeditFileBrowserStore *store, + GList *rows, + GeditFileBrowserPlugin *plugin); +static gboolean on_confirm_no_trash_cb (GeditFileBrowserWidget *widget, + GList *files, + GeditWindow *window); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBrowserPlugin, + gedit_file_browser_plugin, + G_TYPE_OBJECT, + 0, + G_ADD_PRIVATE_DYNAMIC (GeditFileBrowserPlugin) + G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_WINDOW_ACTIVATABLE, + gedit_window_activatable_iface_init) \ + \ + gedit_file_browser_enum_and_flag_register_type (type_module); \ + _gedit_file_bookmarks_store_register_type (type_module); \ + _gedit_file_browser_store_register_type (type_module); \ + _gedit_file_browser_view_register_type (type_module); \ + _gedit_file_browser_widget_register_type (type_module); \ +) + +static GSettings * +create_nautilus_gsettings (void) +{ + GSettings *nautilus_settings = NULL; + + if (tepl_utils_can_use_gsettings_schema (NAUTILUS_BASE_SETTINGS)) + { + nautilus_settings = g_settings_new (NAUTILUS_BASE_SETTINGS); + + if (tepl_utils_can_use_gsettings_key (nautilus_settings, NAUTILUS_CLICK_POLICY_KEY)) + { + return nautilus_settings; + } + } + + g_clear_object (&nautilus_settings); + return g_settings_new (NAUTILUS_FALLBACK_SETTINGS); +} + +static void +gedit_file_browser_plugin_init (GeditFileBrowserPlugin *plugin) +{ + plugin->priv = gedit_file_browser_plugin_get_instance_private (plugin); + + plugin->priv->settings = g_settings_new (FILEBROWSER_BASE_SETTINGS); + plugin->priv->terminal_settings = g_settings_new (TERMINAL_BASE_SETTINGS); + plugin->priv->nautilus_settings = create_nautilus_gsettings (); +} + +static void +gedit_file_browser_plugin_dispose (GObject *object) +{ + GeditFileBrowserPlugin *plugin = GEDIT_FILE_BROWSER_PLUGIN (object); + + g_clear_object (&plugin->priv->settings); + g_clear_object (&plugin->priv->nautilus_settings); + g_clear_object (&plugin->priv->terminal_settings); + g_clear_object (&plugin->priv->window); + + G_OBJECT_CLASS (gedit_file_browser_plugin_parent_class)->dispose (object); +} + +static void +gedit_file_browser_plugin_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserPlugin *plugin = GEDIT_FILE_BROWSER_PLUGIN (object); + + switch (prop_id) + { + case PROP_WINDOW: + plugin->priv->window = GEDIT_WINDOW (g_value_dup_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gedit_file_browser_plugin_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserPlugin *plugin = GEDIT_FILE_BROWSER_PLUGIN (object); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object (value, plugin->priv->window); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +on_end_loading_cb (GeditFileBrowserStore *store, + GtkTreeIter *iter, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + + /* Disconnect the signal */ + g_signal_handler_disconnect (store, priv->end_loading_handle); + priv->end_loading_handle = 0; + priv->auto_root = FALSE; +} + +static void +prepare_auto_root (GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GeditFileBrowserStore *store; + + priv->auto_root = TRUE; + + store = gedit_file_browser_widget_get_browser_store (priv->tree_widget); + + if (priv->end_loading_handle != 0) + { + g_signal_handler_disconnect (store, priv->end_loading_handle); + priv->end_loading_handle = 0; + } + + priv->end_loading_handle = g_signal_connect (store, + "end-loading", + G_CALLBACK (on_end_loading_cb), + plugin); +} + +static void +restore_default_location (GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + gchar *root; + gchar *virtual_root; + gboolean bookmarks; + gboolean remote; + + bookmarks = !g_settings_get_boolean (priv->settings, + FILEBROWSER_TREE_VIEW); + + if (bookmarks) + { + gedit_file_browser_widget_show_bookmarks (priv->tree_widget); + return; + } + + root = g_settings_get_string (priv->settings, + FILEBROWSER_ROOT); + virtual_root = g_settings_get_string (priv->settings, + FILEBROWSER_VIRTUAL_ROOT); + + remote = g_settings_get_boolean (priv->settings, + FILEBROWSER_ENABLE_REMOTE); + + if (root != NULL && *root != '\0') + { + GFile *rootfile; + GFile *vrootfile; + + rootfile = g_file_new_for_uri (root); + vrootfile = g_file_new_for_uri (virtual_root); + + if (remote || g_file_is_native (rootfile)) + { + if (virtual_root != NULL && *virtual_root != '\0') + { + prepare_auto_root (plugin); + gedit_file_browser_widget_set_root_and_virtual_root (priv->tree_widget, + rootfile, + vrootfile); + } + else + { + prepare_auto_root (plugin); + gedit_file_browser_widget_set_root (priv->tree_widget, + rootfile, + TRUE); + } + } + + g_object_unref (rootfile); + g_object_unref (vrootfile); + } + + g_free (root); + g_free (virtual_root); +} + +static void +on_click_policy_changed (GSettings *settings, + const gchar *key, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GeditFileBrowserViewClickPolicy policy; + GeditFileBrowserView *view; + + policy = g_settings_get_enum (settings, key); + + view = gedit_file_browser_widget_get_browser_view (priv->tree_widget); + gedit_file_browser_view_set_click_policy (view, policy); +} + +static void +install_nautilus_prefs (GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GeditFileBrowserViewClickPolicy policy; + GeditFileBrowserView *view; + + /* Get click_policy */ + policy = g_settings_get_enum (priv->nautilus_settings, + NAUTILUS_CLICK_POLICY_KEY); + + view = gedit_file_browser_widget_get_browser_view (priv->tree_widget); + gedit_file_browser_view_set_click_policy (view, policy); + + priv->click_policy_handle = + g_signal_connect (priv->nautilus_settings, + "changed::" NAUTILUS_CLICK_POLICY_KEY, + G_CALLBACK (on_click_policy_changed), + plugin); +} + +static void +set_root_from_doc (GeditFileBrowserPlugin *plugin, + GeditDocument *doc) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GtkSourceFile *file; + GFile *location; + GFile *parent; + + if (doc == NULL) + { + return; + } + + file = gedit_document_get_file (doc); + location = gtk_source_file_get_location (file); + if (location == NULL) + { + return; + } + + parent = g_file_get_parent (location); + + if (parent != NULL) + { + gedit_file_browser_widget_set_root (priv->tree_widget, + parent, + TRUE); + + g_object_unref (parent); + } +} + +static void +set_active_root (GeditFileBrowserWidget *widget, + GeditFileBrowserPlugin *plugin) +{ + set_root_from_doc (plugin, + gedit_window_get_active_document (plugin->priv->window)); +} + +static gchar * +get_terminal (GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + gchar *terminal; + + terminal = g_settings_get_string (priv->terminal_settings, + TERMINAL_EXEC_KEY); + + 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 +open_in_terminal (GeditFileBrowserWidget *widget, + GFile *location, + GeditFileBrowserPlugin *plugin) +{ + if (location) + { + gchar *terminal; + gchar *local; + gchar *argv[2]; + + terminal = get_terminal (plugin); + local = g_file_get_path (location); + + 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 (local); + } +} + +static void +gedit_file_browser_plugin_update_state (GeditWindowActivatable *activatable) +{ + GeditFileBrowserPluginPrivate *priv = GEDIT_FILE_BROWSER_PLUGIN (activatable)->priv; + GeditDocument *doc; + GFile *location = NULL; + + doc = gedit_window_get_active_document (priv->window); + + if (doc != NULL) + { + TeplFile *file; + + file = tepl_buffer_get_file (TEPL_BUFFER (doc)); + location = tepl_file_get_location (file); + } + + gedit_file_browser_widget_set_active_root_enabled (priv->tree_widget, location != NULL); +} + +static void +gedit_file_browser_plugin_activate (GeditWindowActivatable *activatable) +{ + GeditFileBrowserPlugin *plugin = GEDIT_FILE_BROWSER_PLUGIN (activatable); + GeditFileBrowserPluginPrivate *priv; + GtkWidget *panel; + GeditFileBrowserStore *store; + + priv = plugin->priv; + + priv->tree_widget = GEDIT_FILE_BROWSER_WIDGET (gedit_file_browser_widget_new ()); + + g_signal_connect (priv->tree_widget, + "location-activated", + G_CALLBACK (on_location_activated_cb), priv->window); + + g_signal_connect (priv->tree_widget, + "error", G_CALLBACK (on_error_cb), plugin); + + g_signal_connect (priv->tree_widget, + "confirm-delete", + G_CALLBACK (on_confirm_delete_cb), + plugin); + + g_signal_connect (priv->tree_widget, + "confirm-no-trash", + G_CALLBACK (on_confirm_no_trash_cb), + priv->window); + + g_signal_connect (priv->tree_widget, + "open-in-terminal", + G_CALLBACK (open_in_terminal), + plugin); + + g_signal_connect (priv->tree_widget, + "set-active-root", + G_CALLBACK (set_active_root), + plugin); + + g_settings_bind (priv->settings, + FILEBROWSER_FILTER_PATTERN, + priv->tree_widget, + FILEBROWSER_FILTER_PATTERN, + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + panel = gedit_window_get_side_panel (priv->window); + + gtk_stack_add_titled (GTK_STACK (panel), + GTK_WIDGET (priv->tree_widget), + "GeditFileBrowserPanel", + _("File Browser")); + + gtk_widget_show (GTK_WIDGET (priv->tree_widget)); + + /* Install nautilus preferences */ + install_nautilus_prefs (plugin); + + /* Connect signals to store the last visited location */ + g_signal_connect (gedit_file_browser_widget_get_browser_view (priv->tree_widget), + "notify::model", + G_CALLBACK (on_model_set_cb), + plugin); + + store = gedit_file_browser_widget_get_browser_store (priv->tree_widget); + + g_settings_bind (priv->settings, + FILEBROWSER_FILTER_MODE, + store, + FILEBROWSER_FILTER_MODE, + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + g_settings_bind (priv->settings, + FILEBROWSER_BINARY_PATTERNS, + store, + FILEBROWSER_BINARY_PATTERNS, + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + g_signal_connect (store, + "notify::virtual-root", + G_CALLBACK (on_virtual_root_changed_cb), + plugin); + + g_signal_connect (store, + "rename", + G_CALLBACK (on_rename_cb), + priv->window); + + g_signal_connect (priv->window, + "tab-added", + G_CALLBACK (on_tab_added_cb), + plugin); + + /* Register messages on the bus */ + gedit_file_browser_messages_register (priv->window, priv->tree_widget); + + gedit_file_browser_plugin_update_state (activatable); +} + +static void +gedit_file_browser_plugin_deactivate (GeditWindowActivatable *activatable) +{ + GeditFileBrowserPlugin *plugin = GEDIT_FILE_BROWSER_PLUGIN (activatable); + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GtkWidget *panel; + + + /* Unregister messages from the bus */ + gedit_file_browser_messages_unregister (priv->window); + + /* Disconnect signals */ + g_signal_handlers_disconnect_by_func (priv->window, + G_CALLBACK (on_tab_added_cb), + plugin); + + if (priv->click_policy_handle) + { + g_signal_handler_disconnect (priv->nautilus_settings, + priv->click_policy_handle); + } + + panel = gedit_window_get_side_panel (priv->window); + gtk_container_remove (GTK_CONTAINER (panel), GTK_WIDGET (priv->tree_widget)); +} + +static void +gedit_file_browser_plugin_class_init (GeditFileBrowserPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gedit_file_browser_plugin_dispose; + object_class->set_property = gedit_file_browser_plugin_set_property; + object_class->get_property = gedit_file_browser_plugin_get_property; + + g_object_class_override_property (object_class, PROP_WINDOW, "window"); +} + +static void +gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface) +{ + iface->activate = gedit_file_browser_plugin_activate; + iface->deactivate = gedit_file_browser_plugin_deactivate; + iface->update_state = gedit_file_browser_plugin_update_state; +} + +static void +gedit_file_browser_plugin_class_finalize (GeditFileBrowserPluginClass *klass) +{ +} + +/* Callbacks */ +static void +on_location_activated_cb (GeditFileBrowserWidget *tree_widget, + GFile *location, + GeditWindow *window) +{ + gedit_commands_load_location (window, location, NULL, 0, 0); +} + +static void +on_error_cb (GeditFileBrowserWidget *tree_widget, + guint code, + gchar const *message, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + gchar *title; + GtkWidget *dlg; + + /* Do not show the error when the root has been set automatically */ + if (priv->auto_root && (code == GEDIT_FILE_BROWSER_ERROR_SET_ROOT || + code == GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY)) + { + /* Show bookmarks */ + gedit_file_browser_widget_show_bookmarks (priv->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 (priv->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 *param, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GtkTreeModel *model; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (gedit_file_browser_widget_get_browser_view (priv->tree_widget))); + + if (model == NULL) + return; + + g_settings_set_boolean (priv->settings, + FILEBROWSER_TREE_VIEW, + GEDIT_IS_FILE_BROWSER_STORE (model)); +} + +static void +on_rename_cb (GeditFileBrowserStore *store, + GFile *oldfile, + GFile *newfile, + GeditWindow *window) +{ + GList *documents; + GList *item; + + /* Find all documents and set its uri to newuri where it matches olduri */ + documents = gedit_app_get_documents (GEDIT_APP (g_application_get_default ())); + + for (item = documents; item; item = item->next) + { + GeditDocument *doc; + GtkSourceFile *source_file; + GFile *docfile; + + doc = GEDIT_DOCUMENT (item->data); + source_file = gedit_document_get_file (doc); + docfile = gtk_source_file_get_location (source_file); + + if (docfile == NULL) + { + continue; + } + + if (g_file_equal (docfile, oldfile)) + { + gtk_source_file_set_location (source_file, newfile); + } + else + { + gchar *relative; + + relative = g_file_get_relative_path (oldfile, docfile); + + if (relative != NULL) + { + /* Relative now contains the part in docfile without + the prefix oldfile */ + + docfile = g_file_get_child (newfile, relative); + + gtk_source_file_set_location (source_file, docfile); + + g_object_unref (docfile); + } + + g_free (relative); + } + } + + g_list_free (documents); +} + +static void +on_virtual_root_changed_cb (GeditFileBrowserStore *store, + GParamSpec *param, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + GFile *root; + GFile *virtual_root; + gchar *uri_root = NULL; + + root = gedit_file_browser_store_get_root (store); + + if (!root) + { + return; + } + else + { + uri_root = g_file_get_uri (root); + g_object_unref (root); + } + + g_settings_set_string (priv->settings, + FILEBROWSER_ROOT, + uri_root); + + virtual_root = gedit_file_browser_store_get_virtual_root (store); + + if (!virtual_root) + { + /* Set virtual to same as root then */ + g_settings_set_string (priv->settings, + FILEBROWSER_VIRTUAL_ROOT, + uri_root); + } + else + { + gchar *uri_vroot; + + uri_vroot = g_file_get_uri (virtual_root); + + g_settings_set_string (priv->settings, + FILEBROWSER_VIRTUAL_ROOT, + uri_vroot); + g_free (uri_vroot); + g_object_unref (virtual_root); + } + + g_signal_handlers_disconnect_by_func (priv->window, + G_CALLBACK (on_tab_added_cb), + plugin); + g_free (uri_root); +} + +static void +on_tab_added_cb (GeditWindow *window, + GeditTab *tab, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + gboolean open; + gboolean load_default = TRUE; + + open = g_settings_get_boolean (priv->settings, + FILEBROWSER_OPEN_AT_FIRST_DOC); + + if (open) + { + GeditDocument *doc; + GtkSourceFile *file; + GFile *location; + + doc = gedit_tab_get_document (tab); + file = gedit_document_get_file (doc); + location = gtk_source_file_get_location (file); + + if (location != NULL) + { + if (g_file_has_uri_scheme (location, "file")) + { + prepare_auto_root (plugin); + set_root_from_doc (plugin, doc); + load_default = FALSE; + } + } + } + + if (load_default) + restore_default_location (plugin); + + /* Disconnect this signal, it's only called once */ + g_signal_handlers_disconnect_by_func (window, + G_CALLBACK (on_tab_added_cb), + plugin); +} + +static gchar * +get_filename_from_path (GtkTreeModel *model, + GtkTreePath *path) +{ + GtkTreeIter iter; + GFile *location; + gchar *ret = NULL; + + if (!gtk_tree_model_get_iter (model, &iter, path)) + { + return NULL; + } + + gtk_tree_model_get (model, &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + if (location) + { + ret = gedit_file_browser_utils_file_basename (location); + g_object_unref (location); + } + + return ret; +} + +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, + _("_Delete")); + g_free (secondary); + + return result; +} + +static gboolean +on_confirm_delete_cb (GeditFileBrowserWidget *widget, + GeditFileBrowserStore *store, + GList *paths, + GeditFileBrowserPlugin *plugin) +{ + GeditFileBrowserPluginPrivate *priv = plugin->priv; + gchar *normal; + gchar *message; + gchar *secondary; + gboolean result; + + 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 (priv->window, + GTK_MESSAGE_QUESTION, + message, + secondary, + _("_Delete")); + + g_free (message); + + return result; +} + +G_MODULE_EXPORT void +peas_register_types (PeasObjectModule *module) +{ + gedit_file_browser_plugin_register_type (G_TYPE_MODULE (module)); + + peas_object_module_register_extension_type (module, + GEDIT_TYPE_WINDOW_ACTIVATABLE, + GEDIT_TYPE_FILE_BROWSER_PLUGIN); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-plugin.h b/plugins/filebrowser/gedit-file-browser-plugin.h new file mode 100644 index 0000000..27fe706 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-plugin.h @@ -0,0 +1,70 @@ +/* + * gedit-file-browser-plugin.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GEDIT_FILE_BROWSER_PLUGIN_H +#define GEDIT_FILE_BROWSER_PLUGIN_H + +#include <glib.h> +#include <glib-object.h> +#include <libpeas/peas-extension-base.h> +#include <libpeas/peas-object-module.h> + +G_BEGIN_DECLS +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_FILE_BROWSER_PLUGIN (gedit_file_browser_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 +{ + GObject parent_instance; + + /* < private > */ + GeditFileBrowserPluginPrivate *priv; +}; + +struct _GeditFileBrowserPluginClass +{ + GObjectClass parent_class; +}; + +/* + * Public methods + */ +GType gedit_file_browser_plugin_get_type (void) G_GNUC_CONST; + +/* All the plugins must implement this function */ +G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module); + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_PLUGIN_H */ + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-store.c b/plugins/filebrowser/gedit-file-browser-store.c new file mode 100644 index 0000000..ef19b55 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-store.c @@ -0,0 +1,3672 @@ +/* + * gedit-file-browser-store.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> +#include <gedit/gedit-utils.h> + +#include "gedit-file-browser-store.h" +#include "gedit-file-browser-enum-types.h" +#include "gedit-file-browser-error.h" +#include "gedit-file-browser-utils.h" + +#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; + GFile *virtual_root; + GMountOperation *operation; + GCancellable *cancellable; +} MountInfo; + +struct _FileBrowserNode +{ + GFile *file; + guint flags; + gchar *icon_name; + gchar *name; + gchar *markup; + + GdkPixbuf *icon; + GdkPixbuf *emblem; + + FileBrowserNode *parent; + gint pos; + gboolean inserted; +}; + +struct _FileBrowserNodeDir +{ + FileBrowserNode node; + GSList *children; + + 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; + + gchar **binary_patterns; + GPtrArray *binary_pattern_specs; + + 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); + +static void delete_files (AsyncData *data); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBrowserStore, gedit_file_browser_store, + G_TYPE_OBJECT, + 0, + G_ADD_PRIVATE_DYNAMIC (GeditFileBrowserStore) + G_IMPLEMENT_INTERFACE_DYNAMIC (GTK_TYPE_TREE_MODEL, + gedit_file_browser_store_iface_init) + G_IMPLEMENT_INTERFACE_DYNAMIC (GTK_TYPE_TREE_DRAG_SOURCE, + gedit_file_browser_store_drag_source_init)) + +/* Properties */ +enum { + PROP_0, + + PROP_ROOT, + PROP_VIRTUAL_ROOT, + PROP_FILTER_MODE, + PROP_BINARY_PATTERNS +}; + +/* Signals */ +enum +{ + BEGIN_LOADING, + END_LOADING, + ERROR, + NO_TRASH, + RENAME, + BEGIN_REFRESH, + END_REFRESH, + UNLOAD, + BEFORE_ROW_DELETED, + 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); + + /* Free all the nodes */ + file_browser_node_free (obj, obj->priv->root); + + if (obj->priv->binary_patterns != NULL) + { + g_strfreev (obj->priv->binary_patterns); + g_ptr_array_unref (obj->priv->binary_pattern_specs); + } + + /* Cancel any asynchronous operations */ + for (GSList *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) +{ + if (node == NULL) + g_value_set_object (value, NULL); + else + g_value_set_object (value, node->file); +} + +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; + case PROP_BINARY_PATTERNS: + g_value_set_boxed (value, obj->priv->binary_patterns); + 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_ROOT: + gedit_file_browser_store_set_root (obj, G_FILE (g_value_get_object (value))); + break; + case PROP_FILTER_MODE: + gedit_file_browser_store_set_filter_mode (obj, g_value_get_flags (value)); + break; + case PROP_BINARY_PATTERNS: + gedit_file_browser_store_set_binary_patterns (obj, g_value_get_boxed (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_object ("root", + "Root", + "The root location", + G_TYPE_FILE, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_VIRTUAL_ROOT, + g_param_spec_object ("virtual-root", + "Virtual Root", + "The virtual root location", + G_TYPE_FILE, + 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)); + + g_object_class_install_property (object_class, PROP_BINARY_PATTERNS, + g_param_spec_boxed ("binary-patterns", + "Binary Patterns", + "The binary patterns", + G_TYPE_STRV, + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + G_TYPE_NONE, 2, G_TYPE_FILE, G_TYPE_FILE); + 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, NULL, + 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, NULL, + 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, NULL, + G_TYPE_NONE, 1, G_TYPE_FILE); + model_signals[BEFORE_ROW_DELETED] = + g_signal_new ("before-row-deleted", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditFileBrowserStoreClass, before_row_deleted), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static void +gedit_file_browser_store_class_finalize (GeditFileBrowserStoreClass *klass) +{ +} + +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_instance_private (obj); + + obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION] = G_TYPE_FILE; + obj->priv->column_types[GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP] = 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_ICON_NAME] = 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_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) +{ + GeditFileBrowserStore *model; + FileBrowserNode *node; + gint *indices, depth; + + 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 (guint i = 0; i < depth; ++i) + { + GSList *item; + gint num = 0; + + if (node == NULL) + return FALSE; + + if (!NODE_IS_DIR (node)) + return FALSE; + + for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) + { + FileBrowserNode *child = (FileBrowserNode *)(item->data); + + if (model_node_inserted (model, child)) + { + if (num == indices[i]) + 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 = gtk_tree_path_new (); + gint num = 0; + + while (node != model->priv->virtual_root) + { + if (node->parent == NULL) + { + gtk_tree_path_free (path); + return NULL; + } + + num = 0; + + for (GSList *item = FILE_BROWSER_NODE_DIR (node->parent)->children; item; item = item->next) + { + FileBrowserNode *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_LOCATION: + set_gvalue_from_node (value, node); + break; + case GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP: + g_value_set_string (value, node->markup); + 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_ICON_NAME: + g_value_set_string (value, node->icon_name); + break; + case GEDIT_FILE_BROWSER_STORE_COLUMN_NAME: + g_value_set_string (value, node->name); + 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 *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 (GSList *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; + + 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 (GSList *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) +{ + if (!NODE_IS_DIR (node)) + return FALSE; + + for (GSList *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; + 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 (GSList *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; + 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 (GSList *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; + GFile *location; + 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_LOCATION, &location, + -1); + + g_assert (location); + + uris[0] = g_file_get_uri (location); + ret = gtk_selection_data_set_uris (selection_data, uris); + + g_free (uris[0]); + g_object_unref (location); + + 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; + return; + } + + if (FILTER_BINARY (model->priv->filter_mode) && !NODE_IS_DIR (node)) + { + if (!NODE_IS_TEXT (node)) + { + node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED; + return; + } + else if (model->priv->binary_patterns != NULL) + { + gssize name_length = strlen (node->name); + gchar *name_reversed = g_utf8_strreverse (node->name, name_length); + + for (guint i = 0; i < model->priv->binary_pattern_specs->len; ++i) + { + GPatternSpec *spec = g_ptr_array_index (model->priv->binary_pattern_specs, i); + + if (g_pattern_spec_match (spec, name_length, node->name, name_reversed)) + { + node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_FILTERED; + g_free (name_reversed); + return; + } + } + + g_free (name_reversed); + } + } + + 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 = g_utf8_collate_key_for_filename (node1->name, -1); + gchar *k2 = g_utf8_collate_key_for_filename (node2->name, -1); + gint result = strcmp (k1, k2); + + g_free (k1); + g_free (k2); + + return result; + } +} + +static gint +model_sort_default (FileBrowserNode *node1, + FileBrowserNode *node2) +{ + gint f1 = NODE_IS_DUMMY (node1); + gint 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; + + return collate_nodes (node1, node2); +} + +static void +model_resort_node (GeditFileBrowserStore *model, + FileBrowserNode *node) +{ + FileBrowserNodeDir *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 + { + GtkTreeIter iter; + GtkTreePath *path; + gint *neworder; + gint pos = 0; + + /* Store current positions */ + for (GSList *item = dir->children; item; item = item->next) + { + FileBrowserNode *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 (GSList *item = dir->children; item; item = item->next) + { + FileBrowserNode *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, + FileBrowserNode *node, + const GtkTreePath *path) +{ + gboolean hidden; + GtkTreePath *copy; + + /* We should always be called when the row is still inserted */ + g_return_if_fail (node->inserted == TRUE || NODE_IS_DUMMY (node)); + + hidden = FILE_IS_HIDDEN (node->flags); + node->flags &= ~GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN; + + /* Create temporary copies of the path as the signals may alter it */ + + copy = gtk_tree_path_copy (path); + g_signal_emit (model, model_signals[BEFORE_ROW_DELETED], 0, copy); + gtk_tree_path_free (copy); + + node->inserted = FALSE; + + if (hidden) + node->flags |= GEDIT_FILE_BROWSER_STORE_FLAG_IS_HIDDEN; + + copy = gtk_tree_path_copy (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) + { + row_deleted (model, node, *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); + g_free (node->markup); + + if (node->file) + node->name = gedit_file_browser_utils_file_basename (node->file); + else + node->name = NULL; + + if (node->name) + node->markup = g_markup_escape_text (node->name, -1); + else + node->markup = 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) +{ + if (node == NULL || !NODE_IS_DIR (node)) + return; + + for (GSList *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) +{ + if (node == NULL) + return; + + if (NODE_IS_DIR (node)) + { + FileBrowserNodeDir *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 (node->file) + { + g_signal_emit (model, model_signals[UNLOAD], 0, node->file); + g_object_unref (node->file); + } + + if (node->icon) + g_object_unref (node->icon); + + if (node->emblem) + g_object_unref (node->emblem); + + g_free (node->icon_name); + g_free (node->name); + g_free (node->markup); + + 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; + + 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 (GSList *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) + row_deleted (model, node, path); + + if (free_path) + gtk_tree_path_free (path); + + parent = node->parent; + + /* Remove the node from the parents children list */ + if (free_nodes && 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 = 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) + { + FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (model->priv->virtual_root); + + if (dir->children != NULL) + { + FileBrowserNode *dummy = (FileBrowserNode *)(dir->children->data); + + if (NODE_IS_DUMMY (dummy) && model_node_visibility (model, dummy)) + { + path = gtk_tree_path_new_first (); + row_deleted (model, dummy, 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, FALSE); + } + + /* Fallback to the same icon as the file browser */ + if (!icon) + icon = gedit_file_browser_utils_pixbuf_from_theme ("text-x-generic", 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->markup = g_markup_escape_text (dummy->name, -1); + + 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)) + { + FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (node); + FileBrowserNode *dummy; + GtkTreeIter iter; + GtkTreePath *path; + guint flags; + + 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; + + row_deleted (model, dummy, path); + gtk_tree_path_free (path); + } + } +} + +static void +insert_node_sorted (GeditFileBrowserStore *model, + FileBrowserNode *child, + FileBrowserNode *parent) +{ + FileBrowserNodeDir *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)) + { + GtkTreePath *path = gedit_file_browser_store_get_path_real (model, child); + GtkTreeIter iter; + + iter.user_data = 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) +{ + FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (parent); + GSList *sorted_children = g_slist_sort (children, (GCompareFunc)model->priv->sort_func); + GSList *child = sorted_children; + GSList *prev = NULL; + GSList *l = dir->children; + + 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 gboolean +content_type_is_text (gchar const *content_type) +{ +#ifdef G_OS_WIN32 + gchar *mime; + gboolean ret; +#endif + + if (!content_type || g_content_type_is_unknown (content_type)) + return TRUE; + +#ifndef G_OS_WIN32 + return (g_content_type_is_a (content_type, "text/plain") || + g_content_type_equals (content_type, "application/x-zerosize")); +#else + if (g_content_type_is_a (content_type, "text")) + return TRUE; + + /* This covers a rare case in which on Windows the PerceivedType is + not set to "text" but the Content Type is set to text/plain */ + mime = g_content_type_get_mime_type (content_type); + ret = g_strcmp0 (mime, "text/plain") == 0; + g_free (mime); + + return ret; +#endif +} + +static void +file_browser_node_set_from_info (GeditFileBrowserStore *model, + FileBrowserNode *node, + GFileInfo *info, + gboolean isadded) +{ + gchar const *content; + 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; + } + + if (g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info)) + 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_type_is_text (content)) + 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) +{ + for (GSList *item = children; item; item = item->next) + { + FileBrowserNode *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) +{ + GSList *nodes = NULL; + + for (GList *item = files; item; item = item->next) + { + GFileInfo *info = G_FILE_INFO (item->data); + GFileType type = g_file_info_get_file_type (info); + gchar const *name; + GFile *file; + FileBrowserNode *node; + + /* 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)) + { + g_object_unref (info); + continue; + } + + file = g_file_get_child (parent->file, name); + if (!(node = node_list_contains_file (original_children, file))) + { + if (type == 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); + + node->icon_name = g_strdup ("folder-symbolic"); + + model_add_node (model, node, parent); + } + + return node; +} + +static void +on_directory_monitor_event (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + FileBrowserNode *parent) +{ + FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (parent); + FileBrowserNode *node; + + 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_slice_free (AsyncNode, async); +} + +static void +model_iterate_next_files_cb (GFileEnumerator *enumerator, + GAsyncResult *result, + AsyncNode *async) +{ + GError *error = NULL; + GList *files = g_file_enumerator_next_files_finish (enumerator, result, &error); + FileBrowserNodeDir *dir = async->dir; + FileBrowserNode *parent = (FileBrowserNode *)dir; + + if (files == NULL) + { + g_file_enumerator_close (enumerator, NULL, NULL); + g_object_unref (enumerator); + 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.gnome.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); + g_object_unref (enumerator); + 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; + } + + if (!(enumerator = g_file_enumerate_children_finish (file, result, &error))) + { + /* 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); + + dir->cancellable = g_cancellable_new (); + + async = g_slice_new (AsyncNode); + 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 *prev = node; + FileBrowserNode *next = prev->parent; + FileBrowserNode *check; + FileBrowserNodeDir *dir; + GSList *copy; + GtkTreePath *empty = NULL; + + /* 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 (GSList *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 (GSList *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; + 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 (GList *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; + + if (!NODE_IS_DIR (parent)) + return NULL; + + dir = FILE_BROWSER_NODE_DIR (parent); + + for (GSList *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, + GFile *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_location + (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); + + if (mount_info->virtual_root) + g_object_unref (mount_info->virtual_root); + + g_slice_free (MountInfo, mount_info); +} + +static GeditFileBrowserStoreResult +model_mount_root (GeditFileBrowserStore *model, + GFile *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_slice_new (MountInfo); + mount_info->model = model; + mount_info->virtual_root = g_file_dup (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 (GFile *root) +{ + return GEDIT_FILE_BROWSER_STORE (g_object_new (GEDIT_TYPE_FILE_BROWSER_STORE, + "root", root, + NULL)); +} + +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 (iter != NULL); + g_return_if_fail (iter->user_data != NULL); + + node = (FileBrowserNode *)(iter->user_data); + + if (column == GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP) + { + g_return_if_fail (G_VALUE_HOLDS_STRING (value)); + + data = g_value_dup_string (value); + + if (!data) + data = g_strdup (node->name); + + g_free (node->markup); + node->markup = data; + } + else if (column == GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM) + { + g_return_if_fail (G_VALUE_HOLDS_OBJECT (value)); + + data = g_value_get_object (value); + + g_return_if_fail (GDK_IS_PIXBUF (data) || data == NULL); + + 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); + } + else + { + g_return_if_fail (column == GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP || + column == GEDIT_FILE_BROWSER_STORE_COLUMN_EMBLEM); + } + + 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_location (GeditFileBrowserStore *model, + GFile *root) +{ + g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model), GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE); + + if (root == NULL) + { + gchar *uri = g_file_get_uri (root); + + g_warning ("Invalid uri (%s)", uri); + g_free (uri); + 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, root)) + return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE; + + /* Check if uri is the root itself */ + if (g_file_equal (model->priv->root->file, root)) + { + /* 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 (root, model->priv->root->file)) + { + gchar *str = g_file_get_parse_name (model->priv->root->file); + gchar *str1 = g_file_get_parse_name (root); + + g_warning ("Virtual root (%s) is not below actual root (%s)", str1, str); + + g_free (str); + g_free (str1); + + return GEDIT_FILE_BROWSER_STORE_RESULT_ERROR; + } + + set_virtual_root_from_file (model, root); + + 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, + GFile *root, + GFile *virtual_root) +{ + 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 && model->priv->root != NULL) + { + equal = g_file_equal (root, model->priv->root->file); + + if (equal && virtual_root == NULL) + return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE; + } + + if (virtual_root) + { + if (equal && g_file_equal (virtual_root, model->priv->virtual_root->file)) + return GEDIT_FILE_BROWSER_STORE_RESULT_NO_CHANGE; + } + + /* 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 (root != NULL) + { + /* Create the root node */ + node = file_browser_node_dir_new (model, root, NULL); + + 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, + GFile *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); +} + +GFile * +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_dup (model->priv->root->file); +} + +GFile * +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_dup (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; + + 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 (GSList *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); +} + +const gchar * const * +gedit_file_browser_store_get_binary_patterns (GeditFileBrowserStore *model) +{ + return (const gchar * const *)model->priv->binary_patterns; +} + +void +gedit_file_browser_store_set_binary_patterns (GeditFileBrowserStore *model, + const gchar **binary_patterns) +{ + g_return_if_fail (GEDIT_IS_FILE_BROWSER_STORE (model)); + + if (model->priv->binary_patterns != NULL) + { + g_strfreev (model->priv->binary_patterns); + g_ptr_array_unref (model->priv->binary_pattern_specs); + } + + model->priv->binary_patterns = g_strdupv ((gchar **)binary_patterns); + + if (binary_patterns == NULL) + { + model->priv->binary_pattern_specs = NULL; + } + else + { + gssize n_patterns = g_strv_length ((gchar **) binary_patterns); + + model->priv->binary_pattern_specs = g_ptr_array_sized_new (n_patterns); + g_ptr_array_set_free_func (model->priv->binary_pattern_specs, (GDestroyNotify) g_pattern_spec_free); + + for (guint i = 0; binary_patterns[i] != NULL; ++i) + g_ptr_array_add (model->priv->binary_pattern_specs, g_pattern_spec_new (binary_patterns[i])); + } + + model_refilter (model); + + g_object_notify (G_OBJECT (model), "binary-patterns"); +} + +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) +{ + if (!node->file) + return; + + if (reparent) + { + GFile *parent = node->parent->file; + gchar *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)) + { + FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (node); + + for (GSList *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; + 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; + } + + g_signal_emit (model, model_signals[RENAME], 0, previous, node->file); + + g_object_unref (previous); + + 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_free_full (data->files, g_object_unref); + + if (!data->removed) + data->model->priv->async_handles = g_slist_remove (data->model->priv->async_handles, data); + + g_slice_free (AsyncData, 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; +} + +static void +delete_file_finished (GFile *file, + GAsyncResult *res, + AsyncData *data) +{ + GError *error = NULL; + gboolean ok; + + if (data->trash) + ok = g_file_trash_finish (file, res, &error); + else + ok = g_file_delete_finish (file, res, &error); + + if (ok) + { + /* Remove the file from the model */ + FileBrowserNode *node = model_find_node (data->model, NULL, file); + + if (node != NULL) + model_remove_node (data->model, node, NULL, TRUE); + + /* Process the next file */ + data->iter = data->iter->next; + } + else if (!ok && error != NULL) + { + gint code = error->code; + g_error_free (error); + + if (data->trash && code == G_IO_ERROR_NOT_SUPPORTED) + { + /* Trash is not supported on this system. Ask the user + * if he wants to delete completely the files instead. + */ + if (emit_no_trash (data)) + { + /* Changes this into a delete job */ + data->trash = FALSE; + data->iter = data->files; + } + else + { + /* End the job */ + async_data_free (data); + return; + } + } + else if (code == G_IO_ERROR_CANCELLED) + { + /* Job has been cancelled, end the job */ + async_data_free (data); + return; + } + } + + /* Continue the job */ + delete_files (data); +} + +static void +delete_files (AsyncData *data) +{ + GFile *file; + + /* Check if our job is done */ + if (data->iter == NULL) + { + async_data_free (data); + return; + } + + file = G_FILE (data->iter->data); + + if (data->trash) + { + g_file_trash_async (file, + G_PRIORITY_DEFAULT, + data->cancellable, + (GAsyncReadyCallback)delete_file_finished, + data); + } + else + { + g_file_delete_async (file, + G_PRIORITY_DEFAULT, + data->cancellable, + (GAsyncReadyCallback)delete_file_finished, + data); + } +} + +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_slice_new (AsyncData); + + 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); + + delete_files (data); + 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_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + + 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, _("Untitled 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, _("Untitled Folder")); + + 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; +} + +void +_gedit_file_browser_store_register_type (GTypeModule *type_module) +{ + gedit_file_browser_store_register_type (type_module); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-store.h b/plugins/filebrowser/gedit-file-browser-store.h new file mode 100644 index 0000000..02df0cb --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-store.h @@ -0,0 +1,188 @@ +/* + * gedit-file-browser-store.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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_ICON_NAME, + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, + + /* Columns not in common with GeditFileBookmarksStore */ + GEDIT_FILE_BROWSER_STORE_COLUMN_NAME, + 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, + GFile *oldfile, + GFile *newfile); + void (* begin_refresh) (GeditFileBrowserStore *model); + void (* end_refresh) (GeditFileBrowserStore *model); + void (* unload) (GeditFileBrowserStore *model, + GFile *location); + void (* before_row_deleted) (GeditFileBrowserStore *model, + GtkTreePath *path); +}; + +GType gedit_file_browser_store_get_type (void) G_GNUC_CONST; + +GeditFileBrowserStore *gedit_file_browser_store_new (GFile *root); +GeditFileBrowserStoreResult gedit_file_browser_store_set_root_and_virtual_root (GeditFileBrowserStore *model, + GFile *root, + GFile *virtual_root); +GeditFileBrowserStoreResult gedit_file_browser_store_set_root (GeditFileBrowserStore *model, + GFile *root); +GeditFileBrowserStoreResult gedit_file_browser_store_set_virtual_root (GeditFileBrowserStore *model, + GtkTreeIter *iter); +GeditFileBrowserStoreResult gedit_file_browser_store_set_virtual_root_from_location (GeditFileBrowserStore *model, + GFile *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); +GFile *gedit_file_browser_store_get_root (GeditFileBrowserStore *model); +GFile *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); +const gchar * const *gedit_file_browser_store_get_binary_patterns (GeditFileBrowserStore *model); +void gedit_file_browser_store_set_binary_patterns (GeditFileBrowserStore *model, + const gchar **binary_patterns); +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); + +void _gedit_file_browser_store_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_STORE_H */ +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-utils.c b/plugins/filebrowser/gedit-file-browser-utils.c new file mode 100644 index 0000000..dbca26a --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-utils.c @@ -0,0 +1,223 @@ +/* + * gedit-file-bookmarks-utils.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> +#include <gedit/gedit-utils.h> + +#include "gedit-file-browser-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); + g_object_unref (info); + + return ret; +} + +GdkPixbuf * +gedit_file_browser_utils_pixbuf_from_file (GFile *file, + GtkIconSize size, + gboolean use_symbolic) +{ + GIcon *icon; + GFileInfo *info; + GdkPixbuf *ret = NULL; + const char *attribute; + + attribute = use_symbolic ? G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON : + G_FILE_ATTRIBUTE_STANDARD_ICON; + + info = g_file_query_info (file, + attribute, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + if (!info) + return NULL; + + icon = use_symbolic ? g_file_info_get_symbolic_icon (info) : + 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_symbolic_icon_name_from_file (GFile *file) +{ + GFileInfo *info; + GIcon *icon; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + if (!info) + return NULL; + + if ((icon = g_file_info_get_symbolic_icon (info)) && G_IS_THEMED_ICON (icon)) + { + const gchar * const *names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + return g_strdup (names[0]); + } + + g_object_unref (info); + return NULL; +} + +gchar * +gedit_file_browser_utils_name_from_themed_icon (GIcon *icon) +{ + GtkIconTheme *theme; + const gchar * const *names; + + if (!G_IS_THEMED_ICON (icon)) + return NULL; + + theme = gtk_icon_theme_get_default (); + names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + + if (gtk_icon_theme_has_icon (theme, names[0])) + return g_strdup (names[0]); + + return NULL; +} + +gchar * +gedit_file_browser_utils_file_basename (GFile *file) +{ + return gedit_utils_basename_for_display (file); +} + +gboolean +gedit_file_browser_utils_confirmation_dialog (GeditWindow *window, + GtkMessageType type, + gchar const *message, + gchar const *secondary, + gchar const *button_label) +{ + GtkWidget *dlg; + gint ret; + + 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); + } + + gtk_dialog_add_buttons (GTK_DIALOG (dlg), + _("_Cancel"), GTK_RESPONSE_CANCEL, + button_label, GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_CANCEL); + + ret = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + return (ret == GTK_RESPONSE_OK); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-utils.h b/plugins/filebrowser/gedit-file-browser-utils.h new file mode 100644 index 0000000..165f513 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-utils.h @@ -0,0 +1,46 @@ +/* + * gedit-file-browser-utils.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GEDIT_FILE_BROWSER_UTILS_H +#define GEDIT_FILE_BROWSER_UTILS_H + +#include <gedit/gedit-window.h> +#include <gio/gio.h> + +gchar *gedit_file_browser_utils_name_from_themed_icon (GIcon *icon); +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, + gboolean use_symbolic); +gchar *gedit_file_browser_utils_symbolic_icon_name_from_file (GFile *file); +gchar *gedit_file_browser_utils_file_basename (GFile *file); + +gboolean gedit_file_browser_utils_confirmation_dialog (GeditWindow *window, + GtkMessageType type, + gchar const *message, + gchar const *secondary, + gchar const *button_label); + +#endif /* GEDIT_FILE_BROWSER_UTILS_H */ +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-view.c b/plugins/filebrowser/gedit-file-browser-view.c new file mode 100644 index 0000000..a13f4d7 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-view.c @@ -0,0 +1,1364 @@ +/* + * gedit-file-browser-view.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> + +#include <glib-object.h> +#include <gio/gio.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-enum-types.h" + +struct _GeditFileBrowserViewPrivate +{ + GtkTreeViewColumn *column; + GtkCellRenderer *pixbuf_renderer; + GtkCellRenderer *text_renderer; + + GtkTreeModel *model; + + /* Used when renaming */ + gchar *orig_markup; + GtkTreeRowReference *editable; + + /* Click policy */ + GeditFileBrowserViewClickPolicy click_policy; + /* Both clicks in a double click need to be on the same row */ + GtkTreePath *double_click_path[2]; + 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 } +}; + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBrowserView, + gedit_file_browser_view, + GTK_TYPE_TREE_VIEW, + 0, + G_ADD_PRIVATE_DYNAMIC (GeditFileBrowserView)) + +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, + GFile *location, + 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) + g_object_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; + } + + G_OBJECT_CLASS (gedit_file_browser_view_parent_class)->finalize (object); +} + +static void +add_expand_state (GeditFileBrowserView *view, + GFile *location) +{ + if (!location) + return; + + if (view->priv->expand_state) + g_hash_table_insert (view->priv->expand_state, location, g_object_ref (location)); +} + +static void +remove_expand_state (GeditFileBrowserView *view, + GFile *location) +{ + if (!location) + return; + + if (view->priv->expand_state) + g_hash_table_remove (view->priv->expand_state, location); +} + +static void +row_expanded (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (tree_view); + + 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) + { + GFile *location; + + gtk_tree_model_get (view->priv->model, + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + add_expand_state (view, location); + + if (location) + g_object_unref (location); + } + + _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); + + 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) + { + GFile *location; + + gtk_tree_model_get (view->priv->model, + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + remove_expand_state (view, location); + + if (location) + g_object_unref (location); + } + + _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) +{ + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (obj)); + + 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_from_name (display, "pointer"); + } + else if (click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE) + { + if (obj->priv->hover_path != NULL) + { + GtkTreeIter iter; + + 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_get_realized (GTK_WIDGET (obj))) + { + GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (obj)); + + gdk_window_set_cursor (win, NULL); + + if (display != NULL) + gdk_display_flush (display); + } + + if (obj->priv->hand_cursor) + { + g_object_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 = gtk_tree_selection_get_selected_rows (selection, &view->priv->model); + GList *row; + GtkTreePath *directory = NULL; + GtkTreePath *path; + GtkTreeIter iter; + GeditFileBrowserStoreFlag flags; + + 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) && directory == NULL) + directory = path; + else if (!FILE_IS_DUMMY (flags)) + g_signal_emit (view, signals[FILE_ACTIVATED], 0, &iter); + } + + if (directory != NULL && + gtk_tree_model_get_iter (view->priv->model, &iter, directory)) + { + g_signal_emit (view, signals[DIRECTORY_ACTIVATED], 0, &iter); + } + + g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free); +} + +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 +expand_or_collapse_selected_item (GeditFileBrowserView *view, + gboolean collapse) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (view); + GtkTreePath *path = NULL; + + gtk_tree_view_get_cursor (tree_view, &path, NULL); + + if (path == NULL) + { + return; + } + + if (collapse) + { + if (!gtk_tree_view_collapse_row (tree_view, path) && + gtk_tree_path_get_depth (path) > 1 && + gtk_tree_path_up (path)) + { + gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); + } + } + else + { + gtk_tree_view_expand_row (tree_view, path, FALSE); + } + + gtk_tree_path_free (path); +} + +static void +row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view); + + /* Make sure the activated row is the only one selected */ + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, path); + + activate_selected_items (GEDIT_FILE_BROWSER_VIEW (tree_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 = GTK_TREE_VIEW (view); + GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view); + GtkTreePath *path; + + 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) +{ + GtkWidgetClass *widget_parent = GTK_WIDGET_CLASS (gedit_file_browser_view_parent_class); + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GeditFileBrowserView *view = GEDIT_FILE_BROWSER_VIEW (widget); + GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view); + int double_click_time; + static int click_count = 0; + static guint32 last_click_time = 0; + GtkTreePath *path; + int expander_size; + int horizontal_separator; + gboolean on_expander; + gboolean call_parent; + gboolean selected; + + /* 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) + { + /* Do not chain up. The row-activated signal is normally + * already sent, which will activate the selected item + * and open the file. + */ + } + 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 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 == GDK_BUTTON_SECONDARY && 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 = GEDIT_FILE_BROWSER_VIEW (widget); + guint modifiers = gtk_accelerator_get_default_mod_mask (); + gboolean handled = FALSE; + + switch (event->keyval) + { + case GDK_KEY_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_KEY_Return: + case GDK_KEY_KP_Enter: + activate_selected_items (view); + handled = TRUE; + break; + + case GDK_KEY_h: + if ((event->state & modifiers) == GDK_CONTROL_MASK) + { + toggle_hidden_filter (view); + handled = TRUE; + } + break; + + case GDK_KEY_Left: + expand_or_collapse_selected_item (view, TRUE); + handled = TRUE; + break; + + case GDK_KEY_Right: + expand_or_collapse_selected_item (view, FALSE); + handled = TRUE; + break; + + default: + handled = FALSE; + break; + } + + /* 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; + + 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)) + { + GFile *location; + + gtk_tree_model_get (view->priv->model, + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + add_expand_state (view, location); + + if (location) + g_object_unref (location); + } + + 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_activated = row_activated; + 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, NULL, + 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, NULL, + 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, NULL, + 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, NULL, + G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER); +} + +static void +gedit_file_browser_view_class_finalize (GeditFileBrowserViewClass *klass) +{ +} + +static void +cell_data_cb (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GeditFileBrowserView *obj) +{ + GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter); + PangoUnderline underline = PANGO_UNDERLINE_NONE; + gboolean editable = FALSE; + + if (obj->priv->click_policy == GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_SINGLE && + 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) && + 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 (edpath); + } + + gtk_tree_path_free (path); + g_object_set (cell, "editable", editable, "underline", underline, NULL); +} + +static void +icon_renderer_cb (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GeditFileBrowserView *obj) +{ + GdkPixbuf *pixbuf; + gchar *icon_name; + gboolean set_pixbuf = FALSE; + + gtk_tree_model_get (tree_model, + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_ICON_NAME, &icon_name, + GEDIT_FILE_BROWSER_STORE_COLUMN_ICON, &pixbuf, + -1); + + if (pixbuf != NULL && (GEDIT_IS_FILE_BROWSER_STORE (tree_model) || icon_name == NULL)) + set_pixbuf = TRUE; + + if (set_pixbuf) + g_object_set (cell, "pixbuf", pixbuf, NULL); + else + g_object_set (cell, "icon-name", icon_name, NULL); + + g_clear_object (&pixbuf); + g_free (icon_name); +} + +static void +gedit_file_browser_view_init (GeditFileBrowserView *obj) +{ + obj->priv = gedit_file_browser_view_get_instance_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_set_cell_data_func (obj->priv->column, + obj->priv->pixbuf_renderer, + (GtkTreeCellDataFunc)icon_renderer_cb, + obj, + NULL); + + 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, + "markup", + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP); + + 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); +} + +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; + gint search_column; + + 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); + search_column = GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME; + } + 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); + search_column = GEDIT_FILE_BROWSER_STORE_COLUMN_NAME; + + 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) && + 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); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view), search_column); +} + +void +gedit_file_browser_view_start_rename (GeditFileBrowserView *tree_view, + GtkTreeIter *iter) +{ + gchar *name; + gchar *markup; + guint flags; + GValue name_escaped = G_VALUE_INIT; + 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_NAME, &name, + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &markup, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, + -1); + + if (!(FILE_IS_DIR (flags) || !FILE_IS_DUMMY (flags))) + { + g_free (name); + g_free (markup); + return; + } + + /* Restore the markup to the original + * name, a plugin might have changed the markup. + */ + g_value_init (&name_escaped, G_TYPE_STRING); + g_value_take_string (&name_escaped, g_markup_escape_text (name, -1)); + gedit_file_browser_store_set_value (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model), + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &name_escaped); + + 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->orig_markup = markup; + tree_view->priv->editable = rowref; + + /* grab focus on the text cell which is editable */ + gtk_tree_view_column_focus_cell (tree_view->priv->column, tree_view->priv->text_renderer); + + path = gtk_tree_row_reference_get_path (tree_view->priv->editable), + gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), path, tree_view->priv->column, TRUE); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view), + path, tree_view->priv->column, + FALSE, 0.0, 0.0); + + gtk_tree_path_free (path); + g_value_unset (&name_escaped); + g_free (name); +} + +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 = gtk_tree_path_new_from_string (path); + GtkTreeIter iter; + gboolean ret; + GValue orig_markup = G_VALUE_INIT; + GError *error = NULL; + + ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model), &iter, treepath); + gtk_tree_path_free (treepath); + + if (ret) + { + /* Restore the original markup */ + g_value_init (&orig_markup, G_TYPE_STRING); + g_value_set_string (&orig_markup, tree_view->priv->orig_markup); + gedit_file_browser_store_set_value (GEDIT_FILE_BROWSER_STORE (tree_view->priv->model), &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_MARKUP, &orig_markup); + + if (new_text != NULL && *new_text != '\0' && + 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); + } + + g_value_unset (&orig_markup); + } + + g_free (tree_view->priv->orig_markup); + tree_view->priv->orig_markup = NULL; + + gtk_tree_row_reference_free (tree_view->priv->editable); + tree_view->priv->editable = NULL; +} + +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, + GFile *location, + 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, location); +} + +static void +restore_expand_state (GeditFileBrowserView *view, + GeditFileBrowserStore *model, + GtkTreeIter *iter) +{ + GFile *location; + + gtk_tree_model_get (GTK_TREE_MODEL (model), + iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + if (location) + { + GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + + if (g_hash_table_lookup (view->priv->expand_state, location)) + gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, FALSE); + + gtk_tree_path_free (path); + g_object_unref (location); + } +} + +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); +} + +void +_gedit_file_browser_view_register_type (GTypeModule *type_module) +{ + gedit_file_browser_view_register_type (type_module); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-view.h b/plugins/filebrowser/gedit-file-browser-view.h new file mode 100644 index 0000000..8c2efc8 --- /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 <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#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_SINGLE, + GEDIT_FILE_BROWSER_VIEW_CLICK_POLICY_DOUBLE +} 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; + +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); + +void _gedit_file_browser_view_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_VIEW_H */ +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-widget.c b/plugins/filebrowser/gedit-file-browser-widget.c new file mode 100644 index 0000000..00585b4 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-widget.c @@ -0,0 +1,3152 @@ +/* + * gedit-file-browser-widget.c - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#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-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-enum-types.h" + +#define LOCATION_DATA_KEY "gedit-file-browser-widget-location" + +enum +{ + BOOKMARKS_ID, + SEPARATOR_CUSTOM_ID, + SEPARATOR_ID, + PATH_ID, + NUM_DEFAULT_IDS +}; + +enum +{ + COLUMN_ICON, + COLUMN_ICON_NAME, + COLUMN_NAME, + COLUMN_FILE, + COLUMN_ID, + N_COLUMNS +}; + +/* Properties */ +enum +{ + PROP_0, + + PROP_FILTER_PATTERN, +}; + +/* Signals */ +enum +{ + LOCATION_ACTIVATED, + ERROR, + CONFIRM_DELETE, + CONFIRM_NO_TRASH, + OPEN_IN_TERMINAL, + SET_ACTIVE_ROOT, + 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; + gchar *icon_name; + GdkPixbuf *icon; +} NameIcon; + +struct _GeditFileBrowserWidgetPrivate +{ + GeditFileBrowserView *treeview; + GeditFileBrowserStore *file_store; + GeditFileBookmarksStore *bookmarks_store; + + GHashTable *bookmarks_hash; + + GMenuModel *dir_menu; + GMenuModel *bookmarks_menu; + + GtkWidget *previous_button; + GtkWidget *next_button; + + GtkWidget *locations_button; + GtkWidget *locations_popover; + GtkWidget *locations_treeview; + GtkTreeViewColumn *treeview_icon_column; + GtkCellRenderer *treeview_icon_renderer; + GtkTreeSelection *locations_treeview_selection; + GtkWidget *locations_button_arrow; + GtkWidget *locations_cellview; + GtkListStore *locations_model; + + GtkWidget *location_entry; + + GtkWidget *filter_entry_revealer; + GtkWidget *filter_entry; + + GSimpleActionGroup *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; + + GCancellable *cancellable; + + GdkCursor *busy_cursor; +}; + +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 gboolean on_location_button_press_event (GtkWidget *button, + GdkEventButton *event, + GeditFileBrowserWidget *obj); +static void on_location_entry_activate (GtkEntry *entry, + GeditFileBrowserWidget *obj); +static gboolean + on_location_entry_focus_out_event (GtkWidget *entry, + GdkEvent *event, + GeditFileBrowserWidget *obj); +static gboolean + on_location_entry_key_press_event (GtkWidget *entry, + GdkEventKey *event, + 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 previous_location_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void next_location_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void up_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void home_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void new_folder_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void open_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void new_file_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void rename_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void delete_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void move_to_trash_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void refresh_view_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void view_folder_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void change_show_hidden_state (GSimpleAction *action, + GVariant *state, + gpointer user_data); +static void change_show_binary_state (GSimpleAction *action, + GVariant *state, + gpointer user_data); +static void change_show_match_filename (GSimpleAction *action, + GVariant *state, + gpointer user_data); +static void open_in_terminal_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void set_active_root_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); +static void on_locations_treeview_selection_changed (GtkTreeSelection *treeselection, + GeditFileBrowserWidget *obj); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditFileBrowserWidget, + gedit_file_browser_widget, + GTK_TYPE_GRID, + 0, + G_ADD_PRIVATE_DYNAMIC (GeditFileBrowserWidget)) + +static void +free_name_icon (gpointer data) +{ + NameIcon *item = (NameIcon *)(data); + + if (item == NULL) + return; + + g_free (item->icon_name); + g_free (item->name); + + if (item->icon) + g_object_unref (item->icon); + + g_slice_free (NameIcon, item); +} + +static FilterFunc * +filter_func_new (GeditFileBrowserWidget *obj, + GeditFileBrowserWidgetFilterFunc func, + gpointer user_data, + GDestroyNotify notify) +{ + FilterFunc *result = g_slice_new (FilterFunc); + + 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_slice_free (Location, loc); +} + +static gboolean +locations_find_by_id (GeditFileBrowserWidget *obj, + guint id, + GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL (obj->priv->locations_model); + guint checkid; + + 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 +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 +filter_func_free (FilterFunc *func) +{ + g_slice_free (FilterFunc, func); +} + +static void +gedit_file_browser_widget_finalize (GObject *object) +{ + GeditFileBrowserWidgetPrivate *priv = GEDIT_FILE_BROWSER_WIDGET (object)->priv; + + g_free (priv->filter_pattern_str); + + G_OBJECT_CLASS (gedit_file_browser_widget_parent_class)->finalize (object); +} + +static void +clear_signals (GeditFileBrowserWidget *obj) +{ + GSList *item = obj->priv->signal_pool; + SignalNode *node; + + while (item != NULL) + { + node = (SignalNode *) (item->data); + item = g_slist_delete_link (item, item); + + g_signal_handler_disconnect (node->object, node->id); + g_slice_free (SignalNode, node); + } + + obj->priv->signal_pool = NULL; +} + +static void +gedit_file_browser_widget_dispose (GObject *object) +{ + GeditFileBrowserWidget *obj = GEDIT_FILE_BROWSER_WIDGET (object); + GeditFileBrowserWidgetPrivate *priv = obj->priv; + + clear_signals (obj); + g_clear_object (&priv->file_store); + g_clear_object (&priv->bookmarks_store); + + g_slist_free_full (priv->filter_funcs, (GDestroyNotify)filter_func_free); + g_list_free_full (priv->locations, (GDestroyNotify)location_free); + + if (priv->bookmarks_hash != NULL) + { + g_hash_table_unref (priv->bookmarks_hash); + priv->bookmarks_hash = NULL; + } + + cancel_async_operation (obj); + + g_clear_object (&obj->priv->current_location_menu_item); + g_clear_object (&priv->busy_cursor); + g_clear_object (&priv->dir_menu); + g_clear_object (&priv->bookmarks_menu); + + G_OBJECT_CLASS (gedit_file_browser_widget_parent_class)->dispose (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; + 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; + 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); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gedit_file_browser_widget_finalize; + object_class->dispose = gedit_file_browser_widget_dispose; + + 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", + "", + G_PARAM_READWRITE)); + + signals[LOCATION_ACTIVATED] = + g_signal_new ("location-activated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditFileBrowserWidgetClass, location_activated), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_FILE); + + signals[ERROR] = + g_signal_new ("error", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditFileBrowserWidgetClass, error), + NULL, NULL, NULL, + 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, NULL, + 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, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_POINTER); + + signals[OPEN_IN_TERMINAL] = + g_signal_new ("open-in-terminal", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditFileBrowserWidgetClass, open_in_terminal), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_FILE); + + signals[SET_ACTIVE_ROOT] = + g_signal_new ("set-active-root", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GeditFileBrowserWidgetClass, set_active_root), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /* Bind class to template */ + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/gedit/plugins/file-browser/ui/gedit-file-browser-widget.ui"); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, previous_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, next_button); + + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_button); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_popover); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_treeview); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_treeview_selection); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, treeview_icon_column); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, treeview_icon_renderer); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_cellview); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_button_arrow); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, locations_model); + + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, location_entry); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, treeview); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, filter_entry_revealer); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, filter_entry); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, location_previous_menu); + gtk_widget_class_bind_template_child_private (widget_class, GeditFileBrowserWidget, location_next_menu); +} + +static void +gedit_file_browser_widget_class_finalize (GeditFileBrowserWidgetClass *klass) +{ +} + +static void +add_signal (GeditFileBrowserWidget *obj, + gpointer object, + gulong id) +{ + SignalNode *node = g_slice_new (SignalNode); + + node->object = G_OBJECT (object); + node->id = id; + + obj->priv->signal_pool = g_slist_prepend (obj->priv->signal_pool, node); +} + +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, + gchar **icon_name, + GdkPixbuf **icon) +{ + NameIcon *item = (NameIcon *)g_hash_table_lookup (obj->priv->bookmarks_hash, file); + + if (item == NULL) + return FALSE; + + *name = g_strdup (item->name); + *icon_name = g_strdup (item->icon_name); + + if (icon != NULL && item->icon != NULL) + { + *icon = g_object_ref (item->icon); + } + + return TRUE; +} + +static void +insert_path_item (GeditFileBrowserWidget *obj, + GFile *file, + GtkTreeIter *after, + GtkTreeIter *iter) +{ + gchar *unescape = NULL; + gchar *icon_name = NULL; + GdkPixbuf *icon = NULL; + + /* Try to get the icon and name from the bookmarks hash */ + if (!get_from_bookmark_file (obj, file, &unescape, &icon_name, &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_name = gedit_file_browser_utils_symbolic_icon_name_from_file (file); + } + + gtk_list_store_insert_after (obj->priv->locations_model, iter, after); + + gtk_list_store_set (obj->priv->locations_model, + iter, + COLUMN_ICON, icon, + COLUMN_ICON_NAME, icon_name, + COLUMN_NAME, unescape, + COLUMN_FILE, file, + COLUMN_ID, PATH_ID, + -1); + + if (icon) + g_object_unref (icon); + + g_free (icon_name); + g_free (unescape); +} + +static void +insert_separator_item (GeditFileBrowserWidget *obj) +{ + GtkTreeIter iter; + + gtk_list_store_insert (obj->priv->locations_model, &iter, 1); + gtk_list_store_set (obj->priv->locations_model, &iter, + COLUMN_ICON, NULL, + COLUMN_ICON_NAME, NULL, + COLUMN_NAME, NULL, + COLUMN_ID, SEPARATOR_ID, -1); +} + +static void +insert_location_path (GeditFileBrowserWidget *obj) +{ + GeditFileBrowserWidgetPrivate *priv = obj->priv; + Location *loc; + GFile *current = NULL; + GFile *tmp; + GtkTreeIter separator; + GtkTreeIter iter; + + if (!priv->current_location) + { + g_message ("insert_location_path: no current location"); + return; + } + + loc = (Location *)(priv->current_location->data); + + current = loc->virtual_root; + locations_find_by_id (obj, SEPARATOR_ID, &separator); + + while (current != NULL) + { + insert_path_item (obj, current, &separator, &iter); + + if (current == loc->virtual_root) + { + + g_signal_handlers_block_by_func (priv->locations_treeview, + on_locations_treeview_selection_changed, + obj); + + gtk_tree_selection_select_iter (priv->locations_treeview_selection, &iter); + + g_signal_handlers_unblock_by_func (priv->locations_treeview, + on_locations_treeview_selection_changed, + obj); + } + + if (g_file_equal (current, loc->root) || + !g_file_has_parent (current, NULL)) + { + 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 +remove_path_items (GeditFileBrowserWidget *obj) +{ + GtkTreeIter iter; + + while (locations_find_by_id (obj, PATH_ID, &iter)) + { + gtk_list_store_remove (obj->priv->locations_model, &iter); + } +} + +static void +check_current_item (GeditFileBrowserWidget *obj, + gboolean show_path) +{ + GtkTreeIter separator; + gboolean has_sep; + + remove_path_items (obj); + has_sep = locations_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_list_store_remove (obj->priv->locations_model, &separator); + } +} + +static void +fill_locations_model (GeditFileBrowserWidget *obj) +{ + GeditFileBrowserWidgetPrivate *priv = obj->priv; + GtkTreeIter iter; + + gtk_list_store_append (priv->locations_model, &iter); + gtk_list_store_set (priv->locations_model, + &iter, + COLUMN_ICON, NULL, + COLUMN_ICON_NAME, "user-bookmarks-symbolic", + COLUMN_NAME, _("Bookmarks"), + COLUMN_ID, BOOKMARKS_ID, -1); + + gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->locations_treeview), + separator_func, + obj, + NULL); + + gtk_tree_selection_select_iter (priv->locations_treeview_selection, &iter); + + on_locations_treeview_selection_changed (priv->locations_treeview_selection, obj); + gedit_file_browser_widget_show_bookmarks (obj); +} + +static gboolean +filter_real (GeditFileBrowserStore *model, + GtkTreeIter *iter, + GeditFileBrowserWidget *obj) +{ + GSList *item; + + for (item = obj->priv->filter_funcs; item; item = item->next) + { + FilterFunc *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 = GTK_TREE_MODEL (obj->priv->bookmarks_store); + GdkPixbuf *pixbuf; + gchar *name; + gchar *icon_name; + GFile *location; + NameIcon *item; + + if (!(location = gedit_file_bookmarks_store_get_location (obj->priv->bookmarks_store, iter))) + return; + + gtk_tree_model_get (model, + iter, + GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON, &pixbuf, + GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON_NAME, &icon_name, + GEDIT_FILE_BOOKMARKS_STORE_COLUMN_NAME, &name, + -1); + + item = g_slice_new (NameIcon); + item->name = name; + item->icon_name = icon_name; + item->icon = pixbuf; + + g_hash_table_insert (obj->priv->bookmarks_hash, + location, + item); +} + +static void +init_bookmarks_hash (GeditFileBrowserWidget *obj) +{ + GtkTreeModel *model = GTK_TREE_MODEL (obj->priv->bookmarks_store); + GtkTreeIter iter; + + 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 +on_locations_treeview_row_activated (GtkTreeView *locations_treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + GeditFileBrowserWidget *obj) +{ + GeditFileBrowserWidgetPrivate *priv = obj->priv; + GtkTreeIter iter; + guint id = G_MAXUINT; + GFile *file; + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->locations_model), &iter, path)) + { + gtk_tree_model_get (GTK_TREE_MODEL (priv->locations_model), &iter, COLUMN_ID, &id, -1); + } + + if (id == BOOKMARKS_ID) + { + gedit_file_browser_widget_show_bookmarks (obj); + } + else if (id == PATH_ID) + { + gtk_tree_model_get (GTK_TREE_MODEL (priv->locations_model), + &iter, + COLUMN_FILE, &file, + -1); + + gedit_file_browser_store_set_virtual_root_from_location (priv->file_store, file); + + g_object_unref (file); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->locations_cellview), path); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->locations_button), FALSE); +} + +static GActionEntry browser_entries[] = { + { "open", open_activated }, + { "set_active_root", set_active_root_activated }, + { "new_folder", new_folder_activated }, + { "new_file", new_file_activated }, + { "rename", rename_activated }, + { "move_to_trash", move_to_trash_activated }, + { "delete", delete_activated }, + { "refresh_view", refresh_view_activated }, + { "view_folder", view_folder_activated }, + { "open_in_terminal", open_in_terminal_activated }, + { "show_hidden", NULL, NULL, "false", change_show_hidden_state }, + { "show_binary", NULL, NULL, "false", change_show_binary_state }, + { "show_match_filename", NULL, NULL, "false", change_show_match_filename }, + { "previous_location", previous_location_activated }, + { "next_location", next_location_activated }, + { "up", up_activated }, + { "home", home_activated } +}; + +static void +locations_icon_renderer_cb (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + GeditFileBrowserWidget *obj) +{ + GdkPixbuf *pixbuf; + gchar *icon_name; + + gtk_tree_model_get (tree_model, + iter, + GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON_NAME, &icon_name, + GEDIT_FILE_BOOKMARKS_STORE_COLUMN_ICON, &pixbuf, + -1); + + if (icon_name != NULL) + g_object_set (cell, "icon-name", icon_name, NULL); + else + g_object_set (cell, "pixbuf", pixbuf, NULL); + + g_clear_object (&pixbuf); + g_free (icon_name); +} + +static void +gedit_file_browser_widget_init (GeditFileBrowserWidget *obj) +{ + GtkBuilder *builder; + GdkDisplay *display; + GAction *action; + GError *error = NULL; + + obj->priv = gedit_file_browser_widget_get_instance_private (obj); + + obj->priv->filter_pattern_str = g_strdup (""); + obj->priv->bookmarks_hash = g_hash_table_new_full (g_file_hash, + (GEqualFunc)g_file_equal, + g_object_unref, + free_name_icon); + + display = gtk_widget_get_display (GTK_WIDGET (obj)); + obj->priv->busy_cursor = gdk_cursor_new_from_name (display, "progress"); + + builder = gtk_builder_new (); + if (!gtk_builder_add_from_resource (builder, + "/org/gnome/gedit/plugins/file-browser/ui/gedit-file-browser-menus.ui", + &error)) + { + g_warning ("loading menu builder file: %s", error->message); + g_error_free (error); + } + else + { + obj->priv->dir_menu = G_MENU_MODEL (g_object_ref (gtk_builder_get_object (builder, "dir-menu"))); + obj->priv->bookmarks_menu = G_MENU_MODEL (g_object_ref (gtk_builder_get_object (builder, "bookmarks-menu"))); + } + + g_object_unref (builder); + + obj->priv->action_group = g_simple_action_group_new (); + g_action_map_add_action_entries (G_ACTION_MAP (obj->priv->action_group), + browser_entries, + G_N_ELEMENTS (browser_entries), + obj); + + /* set initial sensitivity */ + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "previous_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "next_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + gtk_widget_insert_action_group (GTK_WIDGET (obj), + "browser", + G_ACTION_GROUP (obj->priv->action_group)); + + gtk_widget_init_template (GTK_WIDGET (obj)); + + g_signal_connect (obj->priv->previous_button, "button-press-event", + G_CALLBACK (on_location_button_press_event), obj); + g_signal_connect (obj->priv->next_button, "button-press-event", + G_CALLBACK (on_location_button_press_event), obj); + + /* locations popover */ + gtk_tree_selection_set_mode (obj->priv->locations_treeview_selection, GTK_SELECTION_SINGLE); + gtk_tree_view_column_set_cell_data_func (obj->priv->treeview_icon_column, + obj->priv->treeview_icon_renderer, + (GtkTreeCellDataFunc)locations_icon_renderer_cb, + obj, + NULL); + fill_locations_model (obj); + + g_signal_connect (obj->priv->locations_treeview_selection, "changed", + G_CALLBACK (on_locations_treeview_selection_changed), obj); + g_signal_connect (obj->priv->locations_treeview, "row-activated", + G_CALLBACK (on_locations_treeview_row_activated), obj); + + g_signal_connect (obj->priv->location_entry, "activate", + G_CALLBACK (on_location_entry_activate), obj); + g_signal_connect (obj->priv->location_entry, "focus-out-event", + G_CALLBACK (on_location_entry_focus_out_event), obj); + g_signal_connect (obj->priv->location_entry, "key-press-event", + G_CALLBACK (on_location_entry_key_press_event), obj); + + /* tree view */ + obj->priv->file_store = gedit_file_browser_store_new (NULL); + obj->priv->bookmarks_store = gedit_file_bookmarks_store_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); + + 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); + + /* filter */ + g_signal_connect_swapped (obj->priv->filter_entry, "activate", + G_CALLBACK (on_entry_filter_activate), + obj); + g_signal_connect_swapped (obj->priv->filter_entry, "focus_out_event", + G_CALLBACK (on_entry_filter_activate), + obj); +} + +/* Private */ + +static void +update_sensitivity (GeditFileBrowserWidget *obj) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + GAction *action; + gint mode; + + if (GEDIT_IS_FILE_BROWSER_STORE (model)) + { + mode = gedit_file_browser_store_get_filter_mode (GEDIT_FILE_BROWSER_STORE (model)); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_hidden"); + + g_action_change_state (action, + g_variant_new_boolean (!(mode & + GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN))); + + /* sensitivity */ + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "up"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "home"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_hidden"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_binary"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_match_filename"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + else if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) + { + /* Set the filter toggle to normal up state, just for visual pleasure */ + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_hidden"); + + g_action_change_state (action, g_variant_new_boolean (FALSE)); + + /* sensitivity */ + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "up"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "home"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_hidden"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_binary"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), + "show_match_filename"); + g_simple_action_set_enabled (G_SIMPLE_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_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + + return result; +} + +static gboolean +popup_menu (GeditFileBrowserWidget *obj, + GtkTreeView *treeview, + GdkEventButton *event, + GtkTreeModel *model) +{ + GtkWidget *menu; + GMenuModel *menu_model; + + if (GEDIT_IS_FILE_BROWSER_STORE (model)) + menu_model = obj->priv->dir_menu; + else if (GEDIT_IS_FILE_BOOKMARKS_STORE (model)) + menu_model = obj->priv->bookmarks_menu; + else + return FALSE; + + menu = gtk_menu_new_from_model (menu_model); + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (obj), NULL); + + if (event != NULL) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (treeview); + + if (gtk_tree_selection_count_selected_rows (selection) <= 1) + { + GtkTreePath *path; + + if (gtk_tree_view_get_path_at_pos (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_at_pointer (GTK_MENU (menu), (GdkEvent *)event); + } + else + { + GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (treeview)); + GdkGravity rect_gravity = GDK_GRAVITY_EAST; + GdkGravity menu_gravity = GDK_GRAVITY_NORTH_WEST; + GdkRectangle rect; + + if (gedit_utils_menu_position_under_tree_view (treeview, &rect)) + { + if (gtk_widget_get_direction (GTK_WIDGET (treeview)) == GTK_TEXT_DIR_RTL) + { + rect_gravity = GDK_GRAVITY_WEST; + menu_gravity = GDK_GRAVITY_NORTH_EAST; + } + + gtk_menu_popup_at_rect (GTK_MENU (menu), window, &rect, rect_gravity, menu_gravity, NULL); + 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_spec_match_string (obj->priv->filter_pattern, name); + + g_free (name); + return result; +} + +static void +rename_selected_file (GeditFileBrowserWidget *obj) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + GtkTreeIter iter; + + 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) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview)); + GList *rows = gtk_tree_selection_get_selected_rows (selection, &model); + GList *row; + GList *paths = NULL; + + for (row = rows; row; row = row->next) + { + GtkTreePath *path = (GtkTreePath *)(row->data); + GtkTreeIter iter; + guint flags; + + 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_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + + return paths; +} + +static gboolean +delete_selected_files (GeditFileBrowserWidget *obj, + gboolean trash) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + gboolean confirm; + GeditFileBrowserStoreResult result; + GList *rows; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return FALSE; + + if (!(rows = get_deletable_files (obj))) + 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_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + + return result == GEDIT_FILE_BROWSER_STORE_RESULT_OK; +} + +static void +show_location_entry (GeditFileBrowserWidget *obj, + const gchar *location) +{ + g_warn_if_fail (location != NULL); + + gtk_entry_set_text (GTK_ENTRY (obj->priv->location_entry), location); + + gtk_widget_show (obj->priv->location_entry); + gtk_widget_grab_focus (obj->priv->location_entry); + + /* grab_focus() causes the entry's text to become + * selected so, unselect it and move the cursor to the end. + */ + gtk_editable_set_position (GTK_EDITABLE (obj->priv->location_entry), -1); +} + +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 *current = g_object_ref (file); + GFile *tmp; + + 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) +{ + Location *loc = (Location *) (item->data); + GtkWidget *result; + gchar *icon_name = NULL; + gchar *unescape = NULL; + + if (!get_from_bookmark_file (obj, loc->virtual_root, &unescape, &icon_name, NULL)) + unescape = gedit_file_browser_utils_file_basename (loc->virtual_root); + + 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 (icon_name); + 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; + + 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 */ + gedit_file_browser_widget_set_root_and_virtual_root (obj, + loc->root, + loc->virtual_root); + + obj->priv->changing_location = FALSE; +} + +static void +clear_next_locations (GeditFileBrowserWidget *obj) +{ + GAction *action; + 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); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "next_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); +} + +static void +update_filter_mode (GeditFileBrowserWidget *obj, + GSimpleAction *action, + GVariant *state, + GeditFileBrowserStoreFilterMode mode) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + + if (GEDIT_IS_FILE_BROWSER_STORE (model)) + { + gint now = gedit_file_browser_store_get_filter_mode (GEDIT_FILE_BROWSER_STORE (model)); + + if (g_variant_get_boolean(state)) + now &= ~mode; + else + now |= mode; + + gedit_file_browser_store_set_filter_mode (GEDIT_FILE_BROWSER_STORE (model), now); + + } + + g_simple_action_set_state (action, state); +} + +static void +set_filter_pattern_real (GeditFileBrowserWidget *obj, + gchar const *pattern, + gboolean update_entry) +{ + GtkTreeModel *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 == '\0') + return; + + if (pattern != NULL && strcmp (pattern, obj->priv->filter_pattern_str) == 0) + return; + + /* Free the old pattern */ + g_free (obj->priv->filter_pattern_str); + + if (pattern == NULL) + obj->priv->filter_pattern_str = g_strdup (""); + else + 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) + gtk_entry_set_text (GTK_ENTRY (obj->priv->filter_entry), obj->priv->filter_pattern_str); + + 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 (void) +{ + GeditFileBrowserWidget *obj = g_object_new (GEDIT_TYPE_FILE_BROWSER_WIDGET, NULL); + + gedit_file_browser_widget_show_bookmarks (obj); + + return GTK_WIDGET (obj); +} + +void +gedit_file_browser_widget_show_bookmarks (GeditFileBrowserWidget *obj) +{ + GtkTreePath *path; + GtkTreeIter iter; + + gtk_widget_set_sensitive (obj->priv->locations_button, FALSE); + gtk_widget_hide (obj->priv->locations_button_arrow); + locations_find_by_id (obj, BOOKMARKS_ID, &iter); + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (obj->priv->locations_model), &iter); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (obj->priv->locations_cellview), path); + gtk_tree_path_free (path); + + 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) +{ + gtk_widget_set_sensitive (obj->priv->locations_button, TRUE); + gtk_widget_show (obj->priv->locations_button_arrow); + + 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, + GFile *root, + GFile *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, + GFile *root, + gboolean virtual_root) +{ + GFile *parent; + + if (!virtual_root) + { + gedit_file_browser_widget_set_root_and_virtual_root (obj, + root, + NULL); + return; + } + + if (!root) + return; + + parent = get_topmost_file (root); + gedit_file_browser_widget_set_root_and_virtual_root (obj, parent, root); + 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; +} + +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 = filter_func_new (obj, func, user_data, notify);; + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + + 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; + + for (item = obj->priv->filter_funcs; item; item = item->next) + { + FilterFunc *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); + + filter_func_free (func); + break; + } + } +} + +void +gedit_file_browser_widget_set_filter_pattern (GeditFileBrowserWidget *obj, + gchar const *pattern) +{ + gboolean show; + GAction *action; + + /* if pattern is not null, reveal the entry */ + show = pattern != NULL && *pattern != '\0'; + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "show_match_filename"); + g_action_change_state (action, g_variant_new_boolean (show)); + + 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) && + !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; +} + +void +gedit_file_browser_widget_set_active_root_enabled (GeditFileBrowserWidget *widget, + gboolean enabled) +{ + GAction *action; + + g_return_if_fail (GEDIT_IS_FILE_BROWSER_WIDGET (widget)); + + action = g_action_map_lookup_action (G_ACTION_MAP (widget->priv->action_group), "set_active_root"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); +} + +static guint +gedit_file_browser_widget_get_num_selected_files_or_directories (GeditFileBrowserWidget *obj, + guint *files, + guint *dirs) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj->priv->treeview)); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + GList *rows, *row; + guint result = 0; + + 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) + { + GtkTreePath *path = (GtkTreePath *)(row->data); + GeditFileBrowserStoreFlag flags; + GtkTreeIter iter; + + /* 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_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + + return result; +} + +typedef struct +{ + GeditFileBrowserWidget *widget; + GCancellable *cancellable; +} AsyncData; + +static AsyncData * +async_data_new (GeditFileBrowserWidget *widget) +{ + AsyncData *ret = g_slice_new (AsyncData); + + 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_slice_free (AsyncData, async); +} + +static void +set_busy (GeditFileBrowserWidget *obj, + gboolean busy) +{ + GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (obj->priv->treeview)); + + if (!GDK_IS_WINDOW (window)) + return; + + if (busy) + { + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (obj)); + GdkCursor *cursor= gdk_cursor_new_from_name (display, "progress"); + + gdk_window_set_cursor (window, cursor); + g_clear_object (&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; + + if (!mount) + { + gchar *name = g_volume_get_name (volume); + gchar *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); + + gedit_file_browser_widget_set_root (widget, root, FALSE); + + g_object_unref (root); +} + +static void +try_activate_drive (GeditFileBrowserWidget *widget, + GDrive *drive) +{ + GList *volumes = g_drive_get_volumes (drive); + GVolume *volume = G_VOLUME (volumes->data); + GMount *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_free_full (volumes, g_object_unref); +} + +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 *name = g_drive_get_name (drive); + gchar *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 = g_volume_get_mount (volume); + + activate_mount (async->widget, volume, mount); + + if (mount) + g_object_unref (mount); + } + else + { + gchar *name = g_volume_get_name (volume); + gchar *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 = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget)))); + AsyncData *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)); + } +} + +GeditMenuExtension * +gedit_file_browser_widget_extend_context_menu (GeditFileBrowserWidget *obj) +{ + guint i, n_items; + GMenuModel *section = NULL; + + g_return_val_if_fail (GEDIT_IS_FILE_BROWSER_WIDGET (obj), NULL); + + n_items = g_menu_model_get_n_items (obj->priv->dir_menu); + + for (i = 0; i < n_items && !section; i++) + { + gchar *id = NULL; + + if (g_menu_model_get_item_attribute (obj->priv->dir_menu, i, "id", "s", &id) && + strcmp (id, "extension-section") == 0) + { + section = g_menu_model_get_item_link (obj->priv->dir_menu, i, G_MENU_LINK_SECTION); + } + + g_free (id); + } + + return section != NULL ? gedit_menu_extension_new (G_MENU (section)) : NULL; +} + +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) +{ + GFile *location; + 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; + } + + if ((location = gedit_file_bookmarks_store_get_location (GEDIT_FILE_BOOKMARKS_STORE (model), iter))) + { + /* 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, location, FALSE); + } + else + { + gedit_file_browser_widget_set_root (obj, location, TRUE); + } + + g_object_unref (location); + } + else + { + g_warning ("No uri!"); + } +} + +static void +file_open (GeditFileBrowserWidget *obj, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + GFile *location; + gint flags; + + gtk_tree_model_get (model, iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + if (!FILE_IS_DIR (flags) && !FILE_IS_DUMMY (flags)) + g_signal_emit (obj, signals[LOCATION_ACTIVATED], 0, location); + + if (location) + g_object_unref (location); +} + +static gboolean +directory_open (GeditFileBrowserWidget *obj, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + gboolean result = FALSE; + GError *error = NULL; + GFile *location; + GeditFileBrowserStoreFlag flags; + + gtk_tree_model_get (model, iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + if (FILE_IS_DIR (flags) && location) + { + gchar *uri = g_file_get_uri (location); + GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (obj))); + + result = TRUE; + + if (!gtk_show_uri_on_window (window, 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); + g_object_unref (location); + } + + 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; + + 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)) + { + GFile *location; + GtkTreeIter root; + + gtk_tree_model_get (GTK_TREE_MODEL (model), + &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &location, + -1); + + if (gedit_file_browser_store_get_iter_root (model, &root)) + { + GAction *action; + + if (!obj->priv->changing_location) + { + Location *loc; + + /* Remove all items from obj->priv->current_location on */ + if (obj->priv->current_location) + clear_next_locations (obj); + + loc = g_slice_new (Location); + loc->root = gedit_file_browser_store_get_root (model); + loc->virtual_root = g_object_ref (location); + + 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); + + obj->priv->current_location = obj->priv->locations; + obj->priv->current_location_menu_item = create_goto_menu_item (obj, + obj->priv->current_location); + + g_object_ref_sink (obj->priv->current_location_menu_item); + } + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "up"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !virtual_root_is_root (obj, model)); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "previous_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), + obj->priv->current_location != NULL && + obj->priv->current_location->next != NULL); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "next_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), + obj->priv->current_location != NULL && + obj->priv->current_location->prev != NULL); + } + + check_current_item (obj, TRUE); + + if (location) + g_object_unref (location); + } + 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) + { + GAction *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 = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "previous_location"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); + } + + gtk_widget_hide (obj->priv->filter_entry_revealer); + + 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_show (obj->priv->filter_entry_revealer); + } + + 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 gboolean +on_location_button_press_event (GtkWidget *button, + GdkEventButton *event, + GeditFileBrowserWidget *obj) +{ + GtkWidget *menu; + + if (event->button != GDK_BUTTON_SECONDARY) + return FALSE; + + if (button == obj->priv->previous_button) + menu = obj->priv->location_previous_menu; + else + menu = obj->priv->location_next_menu; + + gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event); + + return TRUE; +} + +static void +on_locations_treeview_selection_changed (GtkTreeSelection *treeview_selection, + GeditFileBrowserWidget *obj) +{ + GeditFileBrowserWidgetPrivate *priv = obj->priv; + GtkTreeModel *model = GTK_TREE_MODEL (priv->locations_model); + GtkTreePath *path; + GtkTreeIter iter; + + if (!gtk_tree_selection_get_selected (treeview_selection, &model, &iter)) + return; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (obj->priv->locations_model), &iter); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (obj->priv->locations_cellview), path); + gtk_tree_path_free (path); +} + +static void +on_location_entry_activate (GtkEntry *entry, + GeditFileBrowserWidget *obj) +{ + gchar *location = g_strdup (gtk_entry_get_text (entry)); + GFile *root; + gchar *cwd; + GFile *new_root; + + if (g_str_has_prefix (location, "~/")) + { + gchar *tmp = location; + + location = g_strdup_printf ("%s/%s", g_get_home_dir (), tmp + strlen ("~/")); + + g_free (tmp); + } + + root = gedit_file_browser_store_get_virtual_root (obj->priv->file_store); + cwd = g_file_get_path (root); + + if (cwd == NULL) + cwd = g_file_get_uri (root); + + new_root = g_file_new_for_commandline_arg_and_cwd (location, cwd); + + if (g_file_query_file_type (new_root, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) + { + gchar *display_name = g_file_get_parse_name (new_root); + gchar *msg = g_strdup_printf (_("Error when loading “%s”: No such directory"), display_name); + + g_signal_emit (obj, signals[ERROR], 0, GEDIT_FILE_BROWSER_ERROR_LOAD_DIRECTORY, msg); + + g_free (msg); + g_free (display_name); + } + else + { + gtk_widget_grab_focus (GTK_WIDGET (obj->priv->treeview)); + gtk_widget_hide (obj->priv->location_entry); + + gedit_file_browser_widget_set_root (obj, new_root, TRUE); + } + + g_object_unref (new_root); + g_free (cwd); + g_object_unref (root); + g_free (location); +} + +static gboolean +on_location_entry_focus_out_event (GtkWidget *entry, + GdkEvent *event, + GeditFileBrowserWidget *obj) +{ + gtk_widget_hide (entry); + return FALSE; +} + +static gboolean +on_location_entry_key_press_event (GtkWidget *entry, + GdkEventKey *event, + GeditFileBrowserWidget *obj) +{ + guint modifiers = gtk_accelerator_get_default_mod_mask (); + + if (event->keyval == GDK_KEY_Escape && + (event->state & modifiers) == 0) + { + gtk_widget_grab_focus (GTK_WIDGET (obj->priv->treeview)); + gtk_widget_hide (entry); + return TRUE; + } + + return FALSE; +} + +static gboolean +on_treeview_popup_menu (GeditFileBrowserView *treeview, + GeditFileBrowserWidget *obj) +{ + return popup_menu (obj, GTK_TREE_VIEW (treeview), 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 == GDK_BUTTON_SECONDARY) + return popup_menu (obj, + GTK_TREE_VIEW (treeview), + event, + gtk_tree_view_get_model (GTK_TREE_VIEW (treeview))); + + return FALSE; +} + +static gboolean +do_change_directory (GeditFileBrowserWidget *obj, + GdkEventKey *event) +{ + GAction *action = NULL; + + if ((event->state & + (~GDK_CONTROL_MASK & ~GDK_SHIFT_MASK & ~GDK_MOD1_MASK)) == + event->state && event->keyval == GDK_KEY_BackSpace) + { + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "previous_location"); + } + else if (!((event->state & GDK_MOD1_MASK) && + (event->state & (~GDK_CONTROL_MASK & ~GDK_SHIFT_MASK)) == event->state)) + { + return FALSE; + } + + switch (event->keyval) + { + case GDK_KEY_Home: + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "home"); + break; + case GDK_KEY_Left: + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "previous_location"); + break; + case GDK_KEY_Right: + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "next_location"); + break; + case GDK_KEY_Up: + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "up"); + break; + default: + break; + } + + if (action != NULL) + { + g_action_activate (action, NULL); + return TRUE; + } + + return FALSE; +} + +static gboolean +on_treeview_key_press_event (GeditFileBrowserView *treeview, + GdkEventKey *event, + GeditFileBrowserWidget *obj) +{ + GtkTreeModel *model; + guint modifiers; + + if (do_change_directory (obj, event)) + return TRUE; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return FALSE; + + modifiers = gtk_accelerator_get_default_mod_mask (); + + if (event->keyval == GDK_KEY_Delete || + event->keyval == GDK_KEY_KP_Delete) + { + + if ((event->state & modifiers) == GDK_SHIFT_MASK) + { + delete_selected_files (obj, FALSE); + return TRUE; + } + else if ((event->state & modifiers) == 0) + { + delete_selected_files (obj, TRUE); + return TRUE; + } + } + + if ((event->keyval == GDK_KEY_F2) && (event->state & modifiers) == 0) + { + rename_selected_file (obj); + return TRUE; + } + + if (event->keyval == GDK_KEY_l && + (event->state & modifiers) == GDK_CONTROL_MASK) + { + show_location_entry (obj, ""); + return TRUE; + } + + if (event->keyval == GDK_KEY_slash || + event->keyval == GDK_KEY_KP_Divide || +#ifdef G_OS_WIN32 + event->keyval == GDK_KEY_backslash || +#endif + event->keyval == GDK_KEY_asciitilde) + { + gchar location[2] = {'\0', '\0'}; + + location[0] = gdk_keyval_to_unicode (event->keyval); + + show_location_entry (obj, location); + return TRUE; + } + + return FALSE; +} + +static void +on_selection_changed (GtkTreeSelection *selection, + GeditFileBrowserWidget *obj) +{ + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (obj->priv->treeview)); + GAction *action; + guint selected = 0; + guint files = 0; + guint dirs = 0; + + if (GEDIT_IS_FILE_BROWSER_STORE (model)) + selected = gedit_file_browser_widget_get_num_selected_files_or_directories (obj, &files, &dirs); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "move_to_trash"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected > 0); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "delete"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected > 0); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "open"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (selected > 0) && (selected == files)); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "rename"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected == 1); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "open_in_terminal"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected == 1); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "new_folder"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected <= 1); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "new_file"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected <= 1); +} + +static gboolean +on_entry_filter_activate (GeditFileBrowserWidget *obj) +{ + gchar const *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 = 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; + GFile *location; + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + if (!(location = gedit_file_bookmarks_store_get_location (obj->priv->bookmarks_store, &iter))) + return; + + g_hash_table_remove (obj->priv->bookmarks_hash, location); + + g_object_unref (location); +} + +static void +on_filter_mode_changed (GeditFileBrowserStore *model, + GParamSpec *param, + GeditFileBrowserWidget *obj) +{ + gint mode = gedit_file_browser_store_get_filter_mode (model); + GAction *action; + GVariant *variant; + gboolean active; + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "show_hidden"); + active = !(mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN); + variant = g_action_get_state (action); + + if (active != g_variant_get_boolean (variant)) + g_action_change_state (action, g_variant_new_boolean (active)); + + g_variant_unref (variant); + + action = g_action_map_lookup_action (G_ACTION_MAP (obj->priv->action_group), "show_binary"); + active = !(mode & GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY); + variant = g_action_get_state (action); + + if (active != g_variant_get_boolean (variant)) + g_action_change_state (action, g_variant_new_boolean (active)); + + g_variant_unref (variant); +} + +static void +next_location_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + gedit_file_browser_widget_history_forward (GEDIT_FILE_BROWSER_WIDGET (user_data)); +} + +static void +previous_location_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + gedit_file_browser_widget_history_back (GEDIT_FILE_BROWSER_WIDGET (user_data)); +} + +static void +up_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->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 +home_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview)); + GFile *home_location; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return; + + home_location = g_file_new_for_path (g_get_home_dir ()); + gedit_file_browser_widget_set_root (widget, home_location, TRUE); + + g_object_unref (home_location); +} + +static void +new_folder_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview)); + GtkTreeIter parent; + GtkTreeIter iter; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return; + + if (!gedit_file_browser_widget_get_selected_directory (widget, &parent)) + return; + + if (gedit_file_browser_store_new_directory (GEDIT_FILE_BROWSER_STORE (model), &parent, &iter)) + gedit_file_browser_view_start_rename (widget->priv->treeview, &iter); +} + +static void +open_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview)); + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->treeview)); + GList *rows; + GList *row; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return; + + rows = gtk_tree_selection_get_selected_rows (selection, &model); + for (row = rows; row; row = row->next) + { + GtkTreePath *path = (GtkTreePath *)(row->data); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (model, &iter, path)) + file_open (widget, model, &iter); + + gtk_tree_path_free (path); + } + + g_list_free (rows); +} + +static void +new_file_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview)); + GtkTreeIter parent; + GtkTreeIter iter; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return; + + if (!gedit_file_browser_widget_get_selected_directory (widget, &parent)) + return; + + if (gedit_file_browser_store_new_file (GEDIT_FILE_BROWSER_STORE (model), &parent, &iter)) + gedit_file_browser_view_start_rename (widget->priv->treeview, &iter); +} + +static void +rename_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + rename_selected_file (widget); +} + +static void +delete_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + delete_selected_files (widget, FALSE); +} + +static void +move_to_trash_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + delete_selected_files (widget, TRUE); +} + +static void +refresh_view_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + gedit_file_browser_widget_refresh (widget); +} + +static void +view_folder_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview)); + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->treeview)); + GtkTreeIter iter; + GList *rows; + GList *row; + gboolean directory_opened = FALSE; + + if (!GEDIT_IS_FILE_BROWSER_STORE (model)) + return; + + rows = gtk_tree_selection_get_selected_rows (selection, &model); + for (row = rows; row; row = row->next) + { + GtkTreePath *path = (GtkTreePath *)(row->data); + + if (gtk_tree_model_get_iter (model, &iter, path)) + directory_opened |= directory_open (widget, model, &iter); + + gtk_tree_path_free (path); + } + + if (!directory_opened && gedit_file_browser_widget_get_selected_directory (widget, &iter)) + directory_open (widget, model, &iter); + + g_list_free (rows); +} + +static void +change_show_hidden_state (GSimpleAction *action, + GVariant *state, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + update_filter_mode (widget, + action, + state, + GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN); +} + +static void +change_show_binary_state (GSimpleAction *action, + GVariant *state, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + update_filter_mode (widget, + action, + state, + GEDIT_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY); +} + +static void +change_show_match_filename (GSimpleAction *action, + GVariant *state, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + gboolean visible = g_variant_get_boolean (state); + + gtk_revealer_set_reveal_child (GTK_REVEALER (widget->priv->filter_entry_revealer), visible); + + if (visible) + gtk_widget_grab_focus (widget->priv->filter_entry); + else + /* clear the filter */ + set_filter_pattern_real (widget, NULL, TRUE); + + g_simple_action_set_state (action, state); +} + +static void +open_in_terminal_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + GtkTreeIter iter; + GFile *file; + + /* Get the current directory */ + if (!gedit_file_browser_widget_get_selected_directory (widget, &iter)) + return; + + gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->file_store), + &iter, + GEDIT_FILE_BROWSER_STORE_COLUMN_LOCATION, &file, + -1); + + g_signal_emit (widget, signals[OPEN_IN_TERMINAL], 0, file); + + g_object_unref (file); +} + +static void +set_active_root_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GeditFileBrowserWidget *widget = GEDIT_FILE_BROWSER_WIDGET (user_data); + + g_signal_emit (widget, signals[SET_ACTIVE_ROOT], 0); +} + +void +_gedit_file_browser_widget_register_type (GTypeModule *type_module) +{ + gedit_file_browser_widget_register_type (type_module); +} + +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/gedit-file-browser-widget.h b/plugins/filebrowser/gedit-file-browser-widget.h new file mode 100644 index 0000000..8da8312 --- /dev/null +++ b/plugins/filebrowser/gedit-file-browser-widget.h @@ -0,0 +1,126 @@ +/* + * gedit-file-browser-widget.h - Gedit plugin providing easy file access + * from the sidepanel + * + * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GEDIT_FILE_BROWSER_WIDGET_H +#define GEDIT_FILE_BROWSER_WIDGET_H + +#include <gtk/gtk.h> +#include <gedit/gedit-menu-extension.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 +{ + GtkBox parent; + + GeditFileBrowserWidgetPrivate *priv; +}; + +struct _GeditFileBrowserWidgetClass +{ + GtkBoxClass parent_class; + + /* Signals */ + void (* location_activated) (GeditFileBrowserWidget *widget, + GFile *location); + 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); + void (* open_in_terminal) (GeditFileBrowserWidget *widget, + GFile *location); + void (* set_active_root) (GeditFileBrowserWidget *widget); +}; + +GType gedit_file_browser_widget_get_type (void) G_GNUC_CONST; + +GtkWidget *gedit_file_browser_widget_new (void); + +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, + GFile *root, + gboolean virtual_root); +void gedit_file_browser_widget_set_root_and_virtual_root + (GeditFileBrowserWidget *obj, + GFile *root, + GFile *virtual_root); + +gboolean gedit_file_browser_widget_get_selected_directory + (GeditFileBrowserWidget *obj, + GtkTreeIter *iter); + +void gedit_file_browser_widget_set_active_root_enabled (GeditFileBrowserWidget *widget, + gboolean enabled); + +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); + +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); +GeditMenuExtension * + gedit_file_browser_widget_extend_context_menu (GeditFileBrowserWidget *obj); +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); + +void _gedit_file_browser_widget_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_WIDGET_H */ +/* ex:set ts=8 noet: */ diff --git a/plugins/filebrowser/meson.build b/plugins/filebrowser/meson.build new file mode 100644 index 0000000..ba66652 --- /dev/null +++ b/plugins/filebrowser/meson.build @@ -0,0 +1,127 @@ +libfilebrowser_public_h = files( + 'gedit-file-bookmarks-store.h', + 'gedit-file-browser-error.h', + 'gedit-file-browser-store.h', + 'gedit-file-browser-view.h', + 'gedit-file-browser-widget.h', + 'gedit-file-browser-utils.h', + 'gedit-file-browser-plugin.h', + 'gedit-file-browser-messages.h', +) + +libfilebrowser_sources = files( + 'gedit-file-bookmarks-store.c', + 'gedit-file-browser-messages.c', + 'gedit-file-browser-plugin.c', + 'gedit-file-browser-store.c', + 'gedit-file-browser-utils.c', + 'gedit-file-browser-view.c', + 'gedit-file-browser-widget.c', +) + +libfilebrowser_deps = [ + libgedit_dep, +] + +subdir('messages') + +libfilebrowser_register_enums = gnome.mkenums( + 'gedit-file-browser-enum-register', + sources: libfilebrowser_public_h, + c_template: 'gedit-file-browser-enum-register.c.template', +) + +libfilebrowser_type_enums = gnome.mkenums( + 'gedit-file-browser-enum-types', + depends : [libfilebrowser_register_enums], + sources: libfilebrowser_public_h, + h_template: 'gedit-file-browser-enum-types.h.template', + c_template: 'gedit-file-browser-enum-types-stage1.c.template', +) + +# cat won't work on Windows so this +# will need to be reimplemented as a script +cat = find_program('cat') + +# Combine the 2 C mkenums templates together before compiling +libfilebrowser_enums_c = custom_target('libfilebrowser_enums_c', + input: [libfilebrowser_type_enums.get(0), + libfilebrowser_register_enums], + output: 'gedit-file-browser-enum-types.c', + command: [cat, '@INPUT0@', '@INPUT1@'], + # redirects the command output since we can't use >> here + capture: true, +) + +libfilebrowser_sources += [ + libfilebrowser_enums_c, + libfilebrowser_type_enums.get(1), +] + +subdir('resources') + +libfilebrowser_sha = shared_module( + 'filebrowser', + sources: libfilebrowser_sources, + include_directories: root_include_dir, + dependencies: libfilebrowser_deps, + install: true, + install_dir: join_paths( + pkglibdir, + 'plugins', + ), + name_suffix: module_suffix, +) + +# FIXME: https://github.com/mesonbuild/meson/issues/1687 +custom_target( + 'org.gnome.gedit.plugins.filebrowser.enums.xml', + input : libfilebrowser_sources + libfilebrowser_public_h, + output: 'org.gnome.gedit.plugins.filebrowser.enums.xml', + capture: true, + command: [ + 'glib-mkenums', + '--comments', '<!-- @comment@ -->', + '--fhead', '<schemalist>', + '--vhead', ' <@type@ id="org.gnome.gedit.plugins.filebrowser.@EnumName@">', + '--vprod', ' <value nick="@valuenick@" value="@valuenum@"/>', + '--vtail', ' </@type@>', + '--ftail', '</schemalist>', + '@INPUT@' + ], + install: true, + install_dir: join_paths( + glibdir, + 'schemas', + ) +) + +filebrowser_gschema_file = files('org.gnome.gedit.plugins.filebrowser.gschema.xml') +install_data( + filebrowser_gschema_file, + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'glib-2.0/schemas') +) + +if xmllint.found() + test( + 'validate-filebrowser-gschema', + xmllint, + args: [ + '--noout', + '--dtdvalid', gschema_dtd, + filebrowser_gschema_file, + ] + ) +endif + +custom_target( + 'filebrowser.plugin', + input: 'filebrowser.plugin.desktop.in', + output: 'filebrowser.plugin', + command: msgfmt_plugin_cmd, + install: true, + install_dir: join_paths( + pkglibdir, + 'plugins', + ) +) diff --git a/plugins/filebrowser/messages.xml b/plugins/filebrowser/messages.xml new file mode 100644 index 0000000..e2b7137 --- /dev/null +++ b/plugins/filebrowser/messages.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<messages> + <message namespace="Gedit" name="FileBrowserMessageGetRoot"> + <include>gio/gio.h</include> + <property name="location" type="object" gtype="G_TYPE_FILE"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageSetRoot"> + <include>gio/gio.h</include> + <property name="location" type="object" gtype="G_TYPE_FILE"/> + <property name="virtual" type="string"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageSetEmblem"> + <property name="id" type="string"/> + <property name="emblem" type="string"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageSetMarkup"> + <property name="id" type="string"/> + <property name="markup" type="string"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageAddFilter"> + <property name="object_path" type="string"/> + <property name="method" type="string"/> + <property name="id" type="uint"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageId"> + <property name="id" type="uint"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageExtendContextMenu"> + <include system="yes">gedit/gedit-menu-extension.h</include> + <property name="extension" type="object" gtype="GEDIT_TYPE_MENU_EXTENSION"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageActivation"> + <property name="active" type="boolean"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageGetView"> + <include>plugins/filebrowser/gedit-file-browser-view.h</include> + <property name="view" type="object" gtype="GEDIT_TYPE_FILE_BROWSER_VIEW"/> + </message> + <message namespace="Gedit" name="FileBrowserMessageIdLocation"> + <include>gio/gio.h</include> + <property name="id" type="string"/> + <property name="name" type="string"/> + <property name="location" type="object" gtype="G_TYPE_FILE"/> + <property name="is-directory" type="boolean"/> + </message> +</messages> +<!-- vi:ex:ts=2:et --> diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-activation.c b/plugins/filebrowser/messages/gedit-file-browser-message-activation.c new file mode 100644 index 0000000..0138e0d --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-activation.c @@ -0,0 +1,105 @@ + +/* + * gedit-file-browser-message-activation.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-activation.h" + +enum +{ + PROP_0, + + PROP_ACTIVE, +}; + +struct _GeditFileBrowserMessageActivationPrivate +{ + gboolean active; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageActivation, + gedit_file_browser_message_activation, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageActivation)) + +static void +gedit_file_browser_message_activation_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageActivation *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION (obj); + + switch (prop_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, msg->priv->active); + break; + } +} + +static void +gedit_file_browser_message_activation_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageActivation *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION (obj); + + switch (prop_id) + { + case PROP_ACTIVE: + msg->priv->active = g_value_get_boolean (value); + break; + } +} + +static void +gedit_file_browser_message_activation_class_init (GeditFileBrowserMessageActivationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = gedit_file_browser_message_activation_get_property; + object_class->set_property = gedit_file_browser_message_activation_set_property; + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + "Active", + "Active", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_activation_init (GeditFileBrowserMessageActivation *message) +{ + message->priv = gedit_file_browser_message_activation_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-activation.h b/plugins/filebrowser/messages/gedit-file-browser-message-activation.h new file mode 100644 index 0000000..bd3b1ed --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-activation.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-activation.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_H +#define GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION (gedit_file_browser_message_activation_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION,\ + GeditFileBrowserMessageActivation)) +#define GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION,\ + GeditFileBrowserMessageActivation const)) +#define GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION,\ + GeditFileBrowserMessageActivationClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ACTIVATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ACTIVATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION)) +#define GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ACTIVATION,\ + GeditFileBrowserMessageActivationClass)) + +typedef struct _GeditFileBrowserMessageActivation GeditFileBrowserMessageActivation; +typedef struct _GeditFileBrowserMessageActivationClass GeditFileBrowserMessageActivationClass; +typedef struct _GeditFileBrowserMessageActivationPrivate GeditFileBrowserMessageActivationPrivate; + +struct _GeditFileBrowserMessageActivation +{ + GeditMessage parent; + + GeditFileBrowserMessageActivationPrivate *priv; +}; + +struct _GeditFileBrowserMessageActivationClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_activation_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_ACTIVATION_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.c b/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.c new file mode 100644 index 0000000..f66a32d --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.c @@ -0,0 +1,162 @@ + +/* + * gedit-file-browser-message-add-filter.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-add-filter.h" + +enum +{ + PROP_0, + + PROP_OBJECT_PATH, + PROP_METHOD, + PROP_ID, +}; + +struct _GeditFileBrowserMessageAddFilterPrivate +{ + gchar *object_path; + gchar *method; + guint id; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageAddFilter, + gedit_file_browser_message_add_filter, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageAddFilter)) + +static void +gedit_file_browser_message_add_filter_finalize (GObject *obj) +{ + GeditFileBrowserMessageAddFilter *msg = GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER (obj); + + g_free (msg->priv->object_path); + g_free (msg->priv->method); + + G_OBJECT_CLASS (gedit_file_browser_message_add_filter_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_add_filter_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageAddFilter *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER (obj); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + g_value_set_string (value, msg->priv->object_path); + break; + case PROP_METHOD: + g_value_set_string (value, msg->priv->method); + break; + case PROP_ID: + g_value_set_uint (value, msg->priv->id); + break; + } +} + +static void +gedit_file_browser_message_add_filter_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageAddFilter *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER (obj); + + switch (prop_id) + { + case PROP_OBJECT_PATH: + { + g_free (msg->priv->object_path); + msg->priv->object_path = g_value_dup_string (value); + break; + } + case PROP_METHOD: + { + g_free (msg->priv->method); + msg->priv->method = g_value_dup_string (value); + break; + } + case PROP_ID: + msg->priv->id = g_value_get_uint (value); + break; + } +} + +static void +gedit_file_browser_message_add_filter_class_init (GeditFileBrowserMessageAddFilterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_add_filter_finalize; + + object_class->get_property = gedit_file_browser_message_add_filter_get_property; + object_class->set_property = gedit_file_browser_message_add_filter_set_property; + + g_object_class_install_property (object_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "Object Path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_METHOD, + g_param_spec_string ("method", + "Method", + "Method", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_uint ("id", + "Id", + "Id", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_add_filter_init (GeditFileBrowserMessageAddFilter *message) +{ + message->priv = gedit_file_browser_message_add_filter_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.h b/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.h new file mode 100644 index 0000000..4a5b35c --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-add-filter.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-add-filter.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_H +#define GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER (gedit_file_browser_message_add_filter_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER,\ + GeditFileBrowserMessageAddFilter)) +#define GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER,\ + GeditFileBrowserMessageAddFilter const)) +#define GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER,\ + GeditFileBrowserMessageAddFilterClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ADD_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ADD_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER)) +#define GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ADD_FILTER,\ + GeditFileBrowserMessageAddFilterClass)) + +typedef struct _GeditFileBrowserMessageAddFilter GeditFileBrowserMessageAddFilter; +typedef struct _GeditFileBrowserMessageAddFilterClass GeditFileBrowserMessageAddFilterClass; +typedef struct _GeditFileBrowserMessageAddFilterPrivate GeditFileBrowserMessageAddFilterPrivate; + +struct _GeditFileBrowserMessageAddFilter +{ + GeditMessage parent; + + GeditFileBrowserMessageAddFilterPrivate *priv; +}; + +struct _GeditFileBrowserMessageAddFilterClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_add_filter_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_ADD_FILTER_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.c b/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.c new file mode 100644 index 0000000..4bb8682 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.c @@ -0,0 +1,128 @@ + +/* + * gedit-file-browser-message-extend-context-menu.c + * This file is part of gedit + * + * Copyright (C) 2014 - Paolo Borelli + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-extend-context-menu.h" +#include <gedit/gedit-menu-extension.h> + +enum +{ + PROP_0, + + PROP_EXTENSION, +}; + +struct _GeditFileBrowserMessageExtendContextMenuPrivate +{ + GeditMenuExtension *extension; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageExtendContextMenu, + gedit_file_browser_message_extend_context_menu, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageExtendContextMenu)) + +static void +gedit_file_browser_message_extend_context_menu_finalize (GObject *obj) +{ + GeditFileBrowserMessageExtendContextMenu *msg = GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU (obj); + + if (msg->priv->extension) + { + g_object_unref (msg->priv->extension); + } + + G_OBJECT_CLASS (gedit_file_browser_message_extend_context_menu_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_extend_context_menu_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageExtendContextMenu *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU (obj); + + switch (prop_id) + { + case PROP_EXTENSION: + g_value_set_object (value, msg->priv->extension); + break; + } +} + +static void +gedit_file_browser_message_extend_context_menu_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageExtendContextMenu *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU (obj); + + switch (prop_id) + { + case PROP_EXTENSION: + { + if (msg->priv->extension) + { + g_object_unref (msg->priv->extension); + } + msg->priv->extension = g_value_dup_object (value); + break; + } + } +} + +static void +gedit_file_browser_message_extend_context_menu_class_init (GeditFileBrowserMessageExtendContextMenuClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_extend_context_menu_finalize; + + object_class->get_property = gedit_file_browser_message_extend_context_menu_get_property; + object_class->set_property = gedit_file_browser_message_extend_context_menu_set_property; + + g_object_class_install_property (object_class, + PROP_EXTENSION, + g_param_spec_object ("extension", + "Extension", + "Extension", + GEDIT_TYPE_MENU_EXTENSION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_extend_context_menu_init (GeditFileBrowserMessageExtendContextMenu *message) +{ + message->priv = gedit_file_browser_message_extend_context_menu_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.h b/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.h new file mode 100644 index 0000000..643485d --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-extend-context-menu.h @@ -0,0 +1,70 @@ + +/* + * gedit-file-browser-message-extend-context-menu.h + * This file is part of gedit + * + * Copyright (C) 2014 - Paolo Borelli + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_H +#define GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU (gedit_file_browser_message_extend_context_menu_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU,\ + GeditFileBrowserMessageExtendContextMenu)) +#define GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU,\ + GeditFileBrowserMessageExtendContextMenu const)) +#define GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU,\ + GeditFileBrowserMessageExtendContextMenuClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU)) +#define GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU,\ + GeditFileBrowserMessageExtendContextMenuClass)) + +typedef struct _GeditFileBrowserMessageExtendContextMenu GeditFileBrowserMessageExtendContextMenu; +typedef struct _GeditFileBrowserMessageExtendContextMenuClass GeditFileBrowserMessageExtendContextMenuClass; +typedef struct _GeditFileBrowserMessageExtendContextMenuPrivate GeditFileBrowserMessageExtendContextMenuPrivate; + +struct _GeditFileBrowserMessageExtendContextMenu +{ + GeditMessage parent; + + GeditFileBrowserMessageExtendContextMenuPrivate *priv; +}; + +struct _GeditFileBrowserMessageExtendContextMenuClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_extend_context_menu_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_EXTEND_CONTEXT_MENU_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-get-root.c b/plugins/filebrowser/messages/gedit-file-browser-message-get-root.c new file mode 100644 index 0000000..b766ac1 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-get-root.c @@ -0,0 +1,127 @@ + +/* + * gedit-file-browser-message-get-root.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-get-root.h" +#include "gio/gio.h" + +enum +{ + PROP_0, + + PROP_LOCATION, +}; + +struct _GeditFileBrowserMessageGetRootPrivate +{ + GFile *location; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageGetRoot, + gedit_file_browser_message_get_root, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageGetRoot)) + +static void +gedit_file_browser_message_get_root_finalize (GObject *obj) +{ + GeditFileBrowserMessageGetRoot *msg = GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT (obj); + + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + + G_OBJECT_CLASS (gedit_file_browser_message_get_root_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_get_root_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageGetRoot *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT (obj); + + switch (prop_id) + { + case PROP_LOCATION: + g_value_set_object (value, msg->priv->location); + break; + } +} + +static void +gedit_file_browser_message_get_root_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageGetRoot *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT (obj); + + switch (prop_id) + { + case PROP_LOCATION: + { + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + msg->priv->location = g_value_dup_object (value); + break; + } + } +} + +static void +gedit_file_browser_message_get_root_class_init (GeditFileBrowserMessageGetRootClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_get_root_finalize; + + object_class->get_property = gedit_file_browser_message_get_root_get_property; + object_class->set_property = gedit_file_browser_message_get_root_set_property; + + g_object_class_install_property (object_class, + PROP_LOCATION, + g_param_spec_object ("location", + "Location", + "Location", + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_get_root_init (GeditFileBrowserMessageGetRoot *message) +{ + message->priv = gedit_file_browser_message_get_root_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-get-root.h b/plugins/filebrowser/messages/gedit-file-browser-message-get-root.h new file mode 100644 index 0000000..8cc114d --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-get-root.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-get-root.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_H +#define GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT (gedit_file_browser_message_get_root_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT,\ + GeditFileBrowserMessageGetRoot)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT,\ + GeditFileBrowserMessageGetRoot const)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT,\ + GeditFileBrowserMessageGetRootClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_GET_ROOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_GET_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_ROOT,\ + GeditFileBrowserMessageGetRootClass)) + +typedef struct _GeditFileBrowserMessageGetRoot GeditFileBrowserMessageGetRoot; +typedef struct _GeditFileBrowserMessageGetRootClass GeditFileBrowserMessageGetRootClass; +typedef struct _GeditFileBrowserMessageGetRootPrivate GeditFileBrowserMessageGetRootPrivate; + +struct _GeditFileBrowserMessageGetRoot +{ + GeditMessage parent; + + GeditFileBrowserMessageGetRootPrivate *priv; +}; + +struct _GeditFileBrowserMessageGetRootClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_get_root_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_GET_ROOT_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-get-view.c b/plugins/filebrowser/messages/gedit-file-browser-message-get-view.c new file mode 100644 index 0000000..17fe757 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-get-view.c @@ -0,0 +1,127 @@ + +/* + * gedit-file-browser-message-get-view.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-get-view.h" +#include "plugins/filebrowser/gedit-file-browser-view.h" + +enum +{ + PROP_0, + + PROP_VIEW, +}; + +struct _GeditFileBrowserMessageGetViewPrivate +{ + GeditFileBrowserView *view; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageGetView, + gedit_file_browser_message_get_view, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageGetView)) + +static void +gedit_file_browser_message_get_view_finalize (GObject *obj) +{ + GeditFileBrowserMessageGetView *msg = GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW (obj); + + if (msg->priv->view) + { + g_object_unref (msg->priv->view); + } + + G_OBJECT_CLASS (gedit_file_browser_message_get_view_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_get_view_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageGetView *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW (obj); + + switch (prop_id) + { + case PROP_VIEW: + g_value_set_object (value, msg->priv->view); + break; + } +} + +static void +gedit_file_browser_message_get_view_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageGetView *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW (obj); + + switch (prop_id) + { + case PROP_VIEW: + { + if (msg->priv->view) + { + g_object_unref (msg->priv->view); + } + msg->priv->view = g_value_dup_object (value); + break; + } + } +} + +static void +gedit_file_browser_message_get_view_class_init (GeditFileBrowserMessageGetViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_get_view_finalize; + + object_class->get_property = gedit_file_browser_message_get_view_get_property; + object_class->set_property = gedit_file_browser_message_get_view_set_property; + + g_object_class_install_property (object_class, + PROP_VIEW, + g_param_spec_object ("view", + "View", + "View", + GEDIT_TYPE_FILE_BROWSER_VIEW, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_get_view_init (GeditFileBrowserMessageGetView *message) +{ + message->priv = gedit_file_browser_message_get_view_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-get-view.h b/plugins/filebrowser/messages/gedit-file-browser-message-get-view.h new file mode 100644 index 0000000..452abbc --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-get-view.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-get-view.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_H +#define GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW (gedit_file_browser_message_get_view_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW,\ + GeditFileBrowserMessageGetView)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW,\ + GeditFileBrowserMessageGetView const)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW,\ + GeditFileBrowserMessageGetViewClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_GET_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_GET_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW)) +#define GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_GET_VIEW,\ + GeditFileBrowserMessageGetViewClass)) + +typedef struct _GeditFileBrowserMessageGetView GeditFileBrowserMessageGetView; +typedef struct _GeditFileBrowserMessageGetViewClass GeditFileBrowserMessageGetViewClass; +typedef struct _GeditFileBrowserMessageGetViewPrivate GeditFileBrowserMessageGetViewPrivate; + +struct _GeditFileBrowserMessageGetView +{ + GeditMessage parent; + + GeditFileBrowserMessageGetViewPrivate *priv; +}; + +struct _GeditFileBrowserMessageGetViewClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_get_view_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_GET_VIEW_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-id-location.c b/plugins/filebrowser/messages/gedit-file-browser-message-id-location.c new file mode 100644 index 0000000..36c33c5 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-id-location.c @@ -0,0 +1,190 @@ + +/* + * gedit-file-browser-message-id-location.c + * This file is part of gedit + * + * Copyright (C) 2013 - Garrett Regier + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-id-location.h" +#include "gio/gio.h" + +enum +{ + PROP_0, + + PROP_ID, + PROP_NAME, + PROP_LOCATION, + PROP_IS_DIRECTORY, +}; + +struct _GeditFileBrowserMessageIdLocationPrivate +{ + gchar *id; + gchar *name; + GFile *location; + gboolean is_directory; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageIdLocation, + gedit_file_browser_message_id_location, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageIdLocation)) + +static void +gedit_file_browser_message_id_location_finalize (GObject *obj) +{ + GeditFileBrowserMessageIdLocation *msg = GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION (obj); + + g_free (msg->priv->id); + g_free (msg->priv->name); + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + + G_OBJECT_CLASS (gedit_file_browser_message_id_location_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_id_location_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageIdLocation *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION (obj); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, msg->priv->id); + break; + case PROP_NAME: + g_value_set_string (value, msg->priv->name); + break; + case PROP_LOCATION: + g_value_set_object (value, msg->priv->location); + break; + case PROP_IS_DIRECTORY: + g_value_set_boolean (value, msg->priv->is_directory); + break; + } +} + +static void +gedit_file_browser_message_id_location_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageIdLocation *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION (obj); + + switch (prop_id) + { + case PROP_ID: + { + g_free (msg->priv->id); + msg->priv->id = g_value_dup_string (value); + break; + } + case PROP_NAME: + { + g_free (msg->priv->name); + msg->priv->name = g_value_dup_string (value); + break; + } + case PROP_LOCATION: + { + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + msg->priv->location = g_value_dup_object (value); + break; + } + case PROP_IS_DIRECTORY: + msg->priv->is_directory = g_value_get_boolean (value); + break; + } +} + +static void +gedit_file_browser_message_id_location_class_init (GeditFileBrowserMessageIdLocationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_id_location_finalize; + + object_class->get_property = gedit_file_browser_message_id_location_get_property; + object_class->set_property = gedit_file_browser_message_id_location_set_property; + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "Id", + "Id", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_LOCATION, + g_param_spec_object ("location", + "Location", + "Location", + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_IS_DIRECTORY, + g_param_spec_boolean ("is-directory", + "Is Directory", + "Is Directory", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_id_location_init (GeditFileBrowserMessageIdLocation *message) +{ + message->priv = gedit_file_browser_message_id_location_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-id-location.h b/plugins/filebrowser/messages/gedit-file-browser-message-id-location.h new file mode 100644 index 0000000..0af4c77 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-id-location.h @@ -0,0 +1,70 @@ + +/* + * gedit-file-browser-message-id-location.h + * This file is part of gedit + * + * Copyright (C) 2013 - Garrett Regier + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_H +#define GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION (gedit_file_browser_message_id_location_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION,\ + GeditFileBrowserMessageIdLocation)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION,\ + GeditFileBrowserMessageIdLocation const)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION,\ + GeditFileBrowserMessageIdLocationClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ID_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ID_LOCATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID_LOCATION,\ + GeditFileBrowserMessageIdLocationClass)) + +typedef struct _GeditFileBrowserMessageIdLocation GeditFileBrowserMessageIdLocation; +typedef struct _GeditFileBrowserMessageIdLocationClass GeditFileBrowserMessageIdLocationClass; +typedef struct _GeditFileBrowserMessageIdLocationPrivate GeditFileBrowserMessageIdLocationPrivate; + +struct _GeditFileBrowserMessageIdLocation +{ + GeditMessage parent; + + GeditFileBrowserMessageIdLocationPrivate *priv; +}; + +struct _GeditFileBrowserMessageIdLocationClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_id_location_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_ID_LOCATION_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-id.c b/plugins/filebrowser/messages/gedit-file-browser-message-id.c new file mode 100644 index 0000000..fec490f --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-id.c @@ -0,0 +1,107 @@ + +/* + * gedit-file-browser-message-id.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-id.h" + +enum +{ + PROP_0, + + PROP_ID, +}; + +struct _GeditFileBrowserMessageIdPrivate +{ + guint id; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageId, + gedit_file_browser_message_id, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageId)) + +static void +gedit_file_browser_message_id_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageId *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ID (obj); + + switch (prop_id) + { + case PROP_ID: + g_value_set_uint (value, msg->priv->id); + break; + } +} + +static void +gedit_file_browser_message_id_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageId *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_ID (obj); + + switch (prop_id) + { + case PROP_ID: + msg->priv->id = g_value_get_uint (value); + break; + } +} + +static void +gedit_file_browser_message_id_class_init (GeditFileBrowserMessageIdClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = gedit_file_browser_message_id_get_property; + object_class->set_property = gedit_file_browser_message_id_set_property; + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_uint ("id", + "Id", + "Id", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_id_init (GeditFileBrowserMessageId *message) +{ + message->priv = gedit_file_browser_message_id_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-id.h b/plugins/filebrowser/messages/gedit-file-browser-message-id.h new file mode 100644 index 0000000..8351c3b --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-id.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-id.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_ID_H +#define GEDIT_FILE_BROWSER_MESSAGE_ID_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID (gedit_file_browser_message_id_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_ID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID,\ + GeditFileBrowserMessageId)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID,\ + GeditFileBrowserMessageId const)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID,\ + GeditFileBrowserMessageIdClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_ID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID)) +#define GEDIT_FILE_BROWSER_MESSAGE_ID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_ID,\ + GeditFileBrowserMessageIdClass)) + +typedef struct _GeditFileBrowserMessageId GeditFileBrowserMessageId; +typedef struct _GeditFileBrowserMessageIdClass GeditFileBrowserMessageIdClass; +typedef struct _GeditFileBrowserMessageIdPrivate GeditFileBrowserMessageIdPrivate; + +struct _GeditFileBrowserMessageId +{ + GeditMessage parent; + + GeditFileBrowserMessageIdPrivate *priv; +}; + +struct _GeditFileBrowserMessageIdClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_id_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_ID_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.c b/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.c new file mode 100644 index 0000000..8e63042 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.c @@ -0,0 +1,142 @@ + +/* + * gedit-file-browser-message-set-emblem.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-set-emblem.h" + +enum +{ + PROP_0, + + PROP_ID, + PROP_EMBLEM, +}; + +struct _GeditFileBrowserMessageSetEmblemPrivate +{ + gchar *id; + gchar *emblem; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageSetEmblem, + gedit_file_browser_message_set_emblem, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageSetEmblem)) + +static void +gedit_file_browser_message_set_emblem_finalize (GObject *obj) +{ + GeditFileBrowserMessageSetEmblem *msg = GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM (obj); + + g_free (msg->priv->id); + g_free (msg->priv->emblem); + + G_OBJECT_CLASS (gedit_file_browser_message_set_emblem_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_set_emblem_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetEmblem *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM (obj); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, msg->priv->id); + break; + case PROP_EMBLEM: + g_value_set_string (value, msg->priv->emblem); + break; + } +} + +static void +gedit_file_browser_message_set_emblem_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetEmblem *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM (obj); + + switch (prop_id) + { + case PROP_ID: + { + g_free (msg->priv->id); + msg->priv->id = g_value_dup_string (value); + break; + } + case PROP_EMBLEM: + { + g_free (msg->priv->emblem); + msg->priv->emblem = g_value_dup_string (value); + break; + } + } +} + +static void +gedit_file_browser_message_set_emblem_class_init (GeditFileBrowserMessageSetEmblemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_set_emblem_finalize; + + object_class->get_property = gedit_file_browser_message_set_emblem_get_property; + object_class->set_property = gedit_file_browser_message_set_emblem_set_property; + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "Id", + "Id", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_EMBLEM, + g_param_spec_string ("emblem", + "Emblem", + "Emblem", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_set_emblem_init (GeditFileBrowserMessageSetEmblem *message) +{ + message->priv = gedit_file_browser_message_set_emblem_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.h b/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.h new file mode 100644 index 0000000..6daa795 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-emblem.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-set-emblem.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_H +#define GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM (gedit_file_browser_message_set_emblem_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM,\ + GeditFileBrowserMessageSetEmblem)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM,\ + GeditFileBrowserMessageSetEmblem const)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM,\ + GeditFileBrowserMessageSetEmblemClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_EMBLEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_EMBLEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_EMBLEM,\ + GeditFileBrowserMessageSetEmblemClass)) + +typedef struct _GeditFileBrowserMessageSetEmblem GeditFileBrowserMessageSetEmblem; +typedef struct _GeditFileBrowserMessageSetEmblemClass GeditFileBrowserMessageSetEmblemClass; +typedef struct _GeditFileBrowserMessageSetEmblemPrivate GeditFileBrowserMessageSetEmblemPrivate; + +struct _GeditFileBrowserMessageSetEmblem +{ + GeditMessage parent; + + GeditFileBrowserMessageSetEmblemPrivate *priv; +}; + +struct _GeditFileBrowserMessageSetEmblemClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_set_emblem_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_SET_EMBLEM_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.c b/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.c new file mode 100644 index 0000000..3b14d16 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.c @@ -0,0 +1,143 @@ + +/* + * gedit-file-browser-message-set-markup.c + * This file is part of gedit + * + * Copyright (C) 2013 - Garrett Regier + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-set-markup.h" + +enum +{ + PROP_0, + + PROP_ID, + PROP_MARKUP, +}; + +struct _GeditFileBrowserMessageSetMarkupPrivate +{ + gchar *id; + gchar *markup; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageSetMarkup, + gedit_file_browser_message_set_markup, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageSetMarkup)) + +static void +gedit_file_browser_message_set_markup_finalize (GObject *obj) +{ + GeditFileBrowserMessageSetMarkup *msg = GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP (obj); + + g_free (msg->priv->id); + g_free (msg->priv->markup); + + G_OBJECT_CLASS (gedit_file_browser_message_set_markup_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_set_markup_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetMarkup *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP (obj); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, msg->priv->id); + break; + case PROP_MARKUP: + g_value_set_string (value, msg->priv->markup); + break; + } +} + +static void +gedit_file_browser_message_set_markup_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetMarkup *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP (obj); + + switch (prop_id) + { + case PROP_ID: + { + g_free (msg->priv->id); + msg->priv->id = g_value_dup_string (value); + break; + } + case PROP_MARKUP: + { + g_free (msg->priv->markup); + msg->priv->markup = g_value_dup_string (value); + break; + } + } +} + +static void +gedit_file_browser_message_set_markup_class_init (GeditFileBrowserMessageSetMarkupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_set_markup_finalize; + + object_class->get_property = gedit_file_browser_message_set_markup_get_property; + object_class->set_property = gedit_file_browser_message_set_markup_set_property; + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "Id", + "Id", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_MARKUP, + g_param_spec_string ("markup", + "Markup", + "Markup", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_set_markup_init (GeditFileBrowserMessageSetMarkup *message) +{ + message->priv = gedit_file_browser_message_set_markup_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.h b/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.h new file mode 100644 index 0000000..ab9a1dc --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-markup.h @@ -0,0 +1,70 @@ + +/* + * gedit-file-browser-message-set-markup.h + * This file is part of gedit + * + * Copyright (C) 2013 - Garrett Regier + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_H +#define GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP (gedit_file_browser_message_set_markup_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP,\ + GeditFileBrowserMessageSetMarkup)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP,\ + GeditFileBrowserMessageSetMarkup const)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP,\ + GeditFileBrowserMessageSetMarkupClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_MARKUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_MARKUP,\ + GeditFileBrowserMessageSetMarkupClass)) + +typedef struct _GeditFileBrowserMessageSetMarkup GeditFileBrowserMessageSetMarkup; +typedef struct _GeditFileBrowserMessageSetMarkupClass GeditFileBrowserMessageSetMarkupClass; +typedef struct _GeditFileBrowserMessageSetMarkupPrivate GeditFileBrowserMessageSetMarkupPrivate; + +struct _GeditFileBrowserMessageSetMarkup +{ + GeditMessage parent; + + GeditFileBrowserMessageSetMarkupPrivate *priv; +}; + +struct _GeditFileBrowserMessageSetMarkupClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_set_markup_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_SET_MARKUP_H */ diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-root.c b/plugins/filebrowser/messages/gedit-file-browser-message-set-root.c new file mode 100644 index 0000000..cb3891b --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-root.c @@ -0,0 +1,149 @@ + +/* + * gedit-file-browser-message-set-root.c + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gedit-file-browser-message-set-root.h" +#include "gio/gio.h" + +enum +{ + PROP_0, + + PROP_LOCATION, + PROP_VIRTUAL, +}; + +struct _GeditFileBrowserMessageSetRootPrivate +{ + GFile *location; + gchar *virtual; +}; + +G_DEFINE_TYPE_EXTENDED (GeditFileBrowserMessageSetRoot, + gedit_file_browser_message_set_root, + GEDIT_TYPE_MESSAGE, + 0, + G_ADD_PRIVATE (GeditFileBrowserMessageSetRoot)) + +static void +gedit_file_browser_message_set_root_finalize (GObject *obj) +{ + GeditFileBrowserMessageSetRoot *msg = GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT (obj); + + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + g_free (msg->priv->virtual); + + G_OBJECT_CLASS (gedit_file_browser_message_set_root_parent_class)->finalize (obj); +} + +static void +gedit_file_browser_message_set_root_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetRoot *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT (obj); + + switch (prop_id) + { + case PROP_LOCATION: + g_value_set_object (value, msg->priv->location); + break; + case PROP_VIRTUAL: + g_value_set_string (value, msg->priv->virtual); + break; + } +} + +static void +gedit_file_browser_message_set_root_set_property (GObject *obj, + guint prop_id, + GValue const *value, + GParamSpec *pspec) +{ + GeditFileBrowserMessageSetRoot *msg; + + msg = GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT (obj); + + switch (prop_id) + { + case PROP_LOCATION: + { + if (msg->priv->location) + { + g_object_unref (msg->priv->location); + } + msg->priv->location = g_value_dup_object (value); + break; + } + case PROP_VIRTUAL: + { + g_free (msg->priv->virtual); + msg->priv->virtual = g_value_dup_string (value); + break; + } + } +} + +static void +gedit_file_browser_message_set_root_class_init (GeditFileBrowserMessageSetRootClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = gedit_file_browser_message_set_root_finalize; + + object_class->get_property = gedit_file_browser_message_set_root_get_property; + object_class->set_property = gedit_file_browser_message_set_root_set_property; + + g_object_class_install_property (object_class, + PROP_LOCATION, + g_param_spec_object ("location", + "Location", + "Location", + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_VIRTUAL, + g_param_spec_string ("virtual", + "Virtual", + "Virtual", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +gedit_file_browser_message_set_root_init (GeditFileBrowserMessageSetRoot *message) +{ + message->priv = gedit_file_browser_message_set_root_get_instance_private (message); +} diff --git a/plugins/filebrowser/messages/gedit-file-browser-message-set-root.h b/plugins/filebrowser/messages/gedit-file-browser-message-set-root.h new file mode 100644 index 0000000..8b61c50 --- /dev/null +++ b/plugins/filebrowser/messages/gedit-file-browser-message-set-root.h @@ -0,0 +1,69 @@ + +/* + * gedit-file-browser-message-set-root.h + * This file is part of gedit + * + * Copyright (C) 2014 - Jesse van den Kieboom + * + * gedit 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 of the License, or + * (at your option) any later version. + * + * gedit 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 gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_H +#define GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_H + +#include <gedit/gedit-message.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT (gedit_file_browser_message_set_root_get_type ()) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT,\ + GeditFileBrowserMessageSetRoot)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT,\ + GeditFileBrowserMessageSetRoot const)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT,\ + GeditFileBrowserMessageSetRootClass)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_ROOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT)) +#define GEDIT_IS_FILE_BROWSER_MESSAGE_SET_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT)) +#define GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GEDIT_TYPE_FILE_BROWSER_MESSAGE_SET_ROOT,\ + GeditFileBrowserMessageSetRootClass)) + +typedef struct _GeditFileBrowserMessageSetRoot GeditFileBrowserMessageSetRoot; +typedef struct _GeditFileBrowserMessageSetRootClass GeditFileBrowserMessageSetRootClass; +typedef struct _GeditFileBrowserMessageSetRootPrivate GeditFileBrowserMessageSetRootPrivate; + +struct _GeditFileBrowserMessageSetRoot +{ + GeditMessage parent; + + GeditFileBrowserMessageSetRootPrivate *priv; +}; + +struct _GeditFileBrowserMessageSetRootClass +{ + GeditMessageClass parent_class; +}; + +GType gedit_file_browser_message_set_root_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* GEDIT_FILE_BROWSER_MESSAGE_SET_ROOT_H */ diff --git a/plugins/filebrowser/messages/meson.build b/plugins/filebrowser/messages/meson.build new file mode 100644 index 0000000..ec4fb84 --- /dev/null +++ b/plugins/filebrowser/messages/meson.build @@ -0,0 +1,25 @@ +libfilebrowser_public_h += files( + 'gedit-file-browser-message-activation.h', + 'gedit-file-browser-message-add-filter.h', + 'gedit-file-browser-message-extend-context-menu.h', + 'gedit-file-browser-message-get-root.h', + 'gedit-file-browser-message-get-view.h', + 'gedit-file-browser-message-id.h', + 'gedit-file-browser-message-id-location.h', + 'gedit-file-browser-message-set-emblem.h', + 'gedit-file-browser-message-set-markup.h', + 'gedit-file-browser-message-set-root.h', +) + +libfilebrowser_sources += files( + 'gedit-file-browser-message-activation.c', + 'gedit-file-browser-message-add-filter.c', + 'gedit-file-browser-message-extend-context-menu.c', + 'gedit-file-browser-message-get-root.c', + 'gedit-file-browser-message-get-view.c', + 'gedit-file-browser-message-id.c', + 'gedit-file-browser-message-id-location.c', + 'gedit-file-browser-message-set-emblem.c', + 'gedit-file-browser-message-set-markup.c', + 'gedit-file-browser-message-set-root.c', +) diff --git a/plugins/filebrowser/messages/messages.h b/plugins/filebrowser/messages/messages.h new file mode 100644 index 0000000..4dc0f51 --- /dev/null +++ b/plugins/filebrowser/messages/messages.h @@ -0,0 +1,16 @@ +#ifndef GEDIT_FILE_BROWER_MESSAGES_MESSAGES_H +#define GEDIT_FILE_BROWER_MESSAGES_MESSAGES_H + +#include "gedit-file-browser-message-activation.h" +#include "gedit-file-browser-message-add-filter.h" +#include "gedit-file-browser-message-extend-context-menu.h" +#include "gedit-file-browser-message-get-root.h" +#include "gedit-file-browser-message-get-view.h" +#include "gedit-file-browser-message-id.h" +#include "gedit-file-browser-message-id-location.h" +#include "gedit-file-browser-message-set-emblem.h" +#include "gedit-file-browser-message-set-markup.h" +#include "gedit-file-browser-message-set-root.h" + +#endif /* GEDIT_FILE_BROWER_MESSAGES_MESSAGES_H */ + diff --git a/plugins/filebrowser/org.gnome.gedit.plugins.filebrowser.gschema.xml b/plugins/filebrowser/org.gnome.gedit.plugins.filebrowser.gschema.xml new file mode 100644 index 0000000..8198802 --- /dev/null +++ b/plugins/filebrowser/org.gnome.gedit.plugins.filebrowser.gschema.xml @@ -0,0 +1,55 @@ +<schemalist gettext-domain="gedit"> + <schema id="org.gnome.gedit.plugins.filebrowser" path="/org/gnome/gedit/plugins/filebrowser/"> + <key name="tree-view" type="b"> + <default>true</default> + <summary>Open With Tree View</summary> + <description>Open the tree view when the file browser plugin gets loaded instead of the bookmarks view</description> + </key> + <key name="root" type="s"> + <default>''</default> + <summary>File Browser Root Directory</summary> + <description>The file browser root directory to use when loading the file browser plugin and onload/tree_view is TRUE.</description> + </key> + <key name="virtual-root" type="s"> + <default>''</default> + <summary>File Browser Virtual Root Directory</summary> + <description>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.</description> + </key> + <key name="enable-remote" type="b"> + <default>false</default> + <summary>Enable Restore of Remote Locations</summary> + <description>Sets whether to enable restoring of remote locations.</description> + </key> + <key name="open-at-first-doc" type="b"> + <default>true</default> + <summary>Set Location to First Document</summary> + <description>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 Nautilus, etc.)</description> + </key> + <key name="filter-mode" flags="org.gnome.gedit.plugins.filebrowser.GeditFileBrowserStoreFilterMode"> + <default>['hide-hidden', 'hide-binary']</default> + <summary>File Browser Filter Mode</summary> + <description>This value determines what files get filtered from the file browser. Valid values are: none (filter nothing), hide-hidden (filter hidden files) and hide-binary (filter binary files).</description> + </key> + <key name="filter-pattern" type="s"> + <default>''</default> + <summary>File Browser Filter Pattern</summary> + <description>The filter pattern to filter the file browser with. This filter works on top of the filter_mode.</description> + </key> + <key name="binary-patterns" type="as"> + <default>['*.la', '*.lo']</default> + <summary>File Browser Binary Patterns</summary> + <description>The supplemental patterns to use when filtering binary files.</description> + </key> + </schema> + + <enum id="org.gnome.gedit.plugins.filebrowser.nautilus.ClickPolicy"> + <value value="0" nick="single"/> + <value value="1" nick="double"/> + </enum> + + <schema id="org.gnome.gedit.plugins.filebrowser.nautilus" path="/org/gnome/gedit/plugins/filebrowser/nautilus/"> + <key name="click-policy" enum="org.gnome.gedit.plugins.filebrowser.nautilus.ClickPolicy"> + <default>'double'</default> + </key> + </schema> +</schemalist> diff --git a/plugins/filebrowser/resources/gedit-file-browser.gresource.xml b/plugins/filebrowser/resources/gedit-file-browser.gresource.xml new file mode 100644 index 0000000..273bd26 --- /dev/null +++ b/plugins/filebrowser/resources/gedit-file-browser.gresource.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/gnome/gedit/plugins/file-browser"> + <file preprocess="xml-stripblanks">ui/gedit-file-browser-menus.ui</file> + <file preprocess="xml-stripblanks">ui/gedit-file-browser-widget.ui</file> + </gresource> +</gresources> diff --git a/plugins/filebrowser/resources/meson.build b/plugins/filebrowser/resources/meson.build new file mode 100644 index 0000000..da2b577 --- /dev/null +++ b/plugins/filebrowser/resources/meson.build @@ -0,0 +1,8 @@ +libfilebrowser_res = gnome.compile_resources( + 'gedit-file-browser-resources', + 'gedit-file-browser.gresource.xml', +) + +libfilebrowser_sources += [ + libfilebrowser_res.get(0), +] diff --git a/plugins/filebrowser/resources/ui/gedit-file-browser-menus.ui b/plugins/filebrowser/resources/ui/gedit-file-browser-menus.ui new file mode 100644 index 0000000..9134268 --- /dev/null +++ b/plugins/filebrowser/resources/ui/gedit-file-browser-menus.ui @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <menu id="dir-menu"> + <section> + <item> + <attribute name="label" translatable="yes">_Open</attribute> + <attribute name="action">browser.open</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Set Root to Active Document</attribute> + <attribute name="action">browser.set_active_root</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_New Folder</attribute> + <attribute name="action">browser.new_folder</attribute> + </item> + <item> + <attribute name="label" translatable="yes">New F_ile</attribute> + <attribute name="action">browser.new_file</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Rename…</attribute> + <attribute name="action">browser.rename</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_Move to Trash</attribute> + <attribute name="action">browser.move_to_trash</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_Delete</attribute> + <attribute name="action">browser.delete</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">Re_fresh View</attribute> + <attribute name="action">browser.refresh_view</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_View Folder</attribute> + <attribute name="action">browser.view_folder</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_Open in Terminal</attribute> + <attribute name="action">browser.open_in_terminal</attribute> + </item> + </section> + <submenu> + <attribute name="label" translatable="yes">_Filter</attribute> + <section> + <item> + <attribute name="label" translatable="yes">Show _Hidden</attribute> + <attribute name="action">browser.show_hidden</attribute> + </item> + <item> + <attribute name="label" translatable="yes">Show _Binary</attribute> + <attribute name="action">browser.show_binary</attribute> + </item> + <item> + <attribute name="label" translatable="yes">Match Filename</attribute> + <attribute name="action">browser.show_match_filename</attribute> + </item> + </section> + </submenu> + <section> + <attribute name="id">extension-section</attribute> + </section> + </menu> + <menu id="bookmarks-menu"> + <section> + <item> + <attribute name="label" translatable="yes">_Set Root to Active Document</attribute> + <attribute name="action">browser.set_active_root</attribute> + </item> + </section> + </menu> +</interface> diff --git a/plugins/filebrowser/resources/ui/gedit-file-browser-widget.ui b/plugins/filebrowser/resources/ui/gedit-file-browser-widget.ui new file mode 100644 index 0000000..e4fb300 --- /dev/null +++ b/plugins/filebrowser/resources/ui/gedit-file-browser-widget.ui @@ -0,0 +1,275 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.6 --> + <object class="GtkListStore" id="locations_model"> + <columns> + <!-- column-name icon --> + <column type="GdkPixbuf"/> + <!-- column-name icon name --> + <column type="gchararray"/> + <!-- column-name name --> + <column type="gchararray"/> + <!-- column-name file --> + <column type="GFile"/> + <!-- column-name id --> + <column type="guint"/> + </columns> + </object> + <object class="GtkPopover" id="locations_popover"> + <property name="can_focus">True</property> + <property name="visible">True</property> + <property name="width-request">300</property> + <property name="height-request">300</property> + <child> + <object class="GtkScrolledWindow" id="locations_scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> + <property name="margin">6</property> + <property name="hscrollbar_policy">never</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <child> + <object class="GtkTreeView" id="locations_treeview"> + <property name="visible">True</property> + <property name="headers_visible">False</property> + <property name="can_focus">True</property> + <property name="model">locations_model</property> + <property name="activate-on-single-click">True</property> + <child> + <object class="GtkTreeViewColumn" id="treeview_icon_column"> + <child> + <object class="GtkCellRendererPixbuf" id="treeview_icon_renderer"/> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeview_name_column"> + <child> + <object class="GtkCellRendererText" id="treeview_name_renderer"> + <property name="ellipsize">end</property> + </object> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="locations_treeview_selection"/> + </child> + </object> + </child> + </object> + </child> + </object> + <object class="GtkMenu" id="location_previous_menu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <object class="GtkMenu" id="location_next_menu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <template class="GeditFileBrowserWidget" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkBox" id="toolbar"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin">3</property> + <property name="spacing">3</property> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkButton" id="previous_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="action_name">browser.previous_location</property> + <property name="image">previous_image</property> + <style> + <class name="small-button"/> + </style> + </object> + </child> + <child> + <object class="GtkButton" id="next_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="action_name">browser.next_location</property> + <property name="image">next_image</property> + <style> + <class name="small-button"/> + </style> + </object> + </child> + <style> + <class name="linked"/> + </style> + </object> + </child> + <child> + <object class="GtkButton" id="button3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="action_name">browser.up</property> + <property name="image">up_image</property> + <style> + <class name="small-button"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkMenuButton" id="locations_button"> + <property name="visible">True</property> + <property name="valign">center</property> + <property name="use_popover">True</property> + <property name="popover">locations_popover</property> + <style> + <class name="text-button"/> + <class name="image-button"/> + <class name="small-button"/> + </style> + <child> + <object class="GtkBox" id="locations_button_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_focus">False</property> + <property name="is_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkCellView" id="locations_cellview"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="model">locations_model</property> + <child> + <object class="GtkCellRendererPixbuf" id="cellview_icon_renderer"/> + </child> + <child> + <object class="GtkCellRendererText" id="cellview_name_renderer"> + <property name="ellipsize">end</property> + </object> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkImage" id="locations_button_arrow"> + <property name="visible">True</property> + <property name="valign">baseline</property> + <property name="icon_name">pan-down-symbolic</property> + </object> + <packing> + <property name="pack-type">GTK_PACK_END</property> + </packing> + </child> + </object> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="locations_button_a11y"> + <property name="accessible-name" translatable="yes">History</property> + <property name="accessible-description" translatable="yes">Open history menu</property> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkSearchEntry" id="location_entry"> + <property name="visible">False</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + <property name="primary_icon_name">folder-symbolic</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <child> + <object class="GeditFileBrowserView" id="treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection1"/> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkRevealer" id="filter_entry_revealer"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="reveal_child">False</property> + <property name="valign">start</property> + <child> + <object class="GtkEntry" id="filter_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + <property name="placeholder_text">Match Filename</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + </template> + <object class="GtkImage" id="previous_image"> + <property name="visible">True</property> + <property name="icon_name">go-previous-symbolic</property> + <property name="icon-size">2</property> + </object> + <object class="GtkImage" id="next_image"> + <property name="visible">True</property> + <property name="icon_name">go-next-symbolic</property> + <property name="icon-size">2</property> + </object> + <object class="GtkImage" id="up_image"> + <property name="visible">True</property> + <property name="icon_name">go-up-symbolic</property> + <property name="icon-size">2</property> + </object> +</interface> |