diff options
Diffstat (limited to '')
-rw-r--r-- | src/gtk/nautilusgtkbookmarksmanager.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/src/gtk/nautilusgtkbookmarksmanager.c b/src/gtk/nautilusgtkbookmarksmanager.c new file mode 100644 index 0000000..86f3bb4 --- /dev/null +++ b/src/gtk/nautilusgtkbookmarksmanager.c @@ -0,0 +1,643 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * nautilusgtkbookmarksmanager.c: Utilities to manage and monitor ~/.gtk-bookmarks + * Copyright (C) 2003, Red Hat, Inc. + * Copyright (C) 2007-2008 Carlos Garnacho + * Copyright (C) 2011 Suse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: Federico Mena Quintero <federico@gnome.org> + */ + +#include "config.h" +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include "nautilus-enum-types.h" + +#include <string.h> + +#include "nautilusgtkbookmarksmanagerprivate.h" + +static void +_gtk_bookmark_free (gpointer data) +{ + GtkBookmark *bookmark = data; + + g_object_unref (bookmark->file); + g_free (bookmark->label); + g_slice_free (GtkBookmark, bookmark); +} + +static void +set_error_bookmark_doesnt_exist (GFile *file, GError **error) +{ + char *uri = g_file_get_uri (file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + _("%s does not exist in the bookmarks list"), + uri); + + g_free (uri); +} + +static GFile * +get_legacy_bookmarks_file (void) +{ + GFile *file; + char *filename; + + filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); + + return file; +} + +static GFile * +get_bookmarks_file (void) +{ + GFile *file; + char *filename; + + /* Use gtk-3.0's bookmarks file as the format didn't change. + * Add the 3.0 file format to get_legacy_bookmarks_file() when + * the format does change. + */ + filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); + + return file; +} + +static GSList * +parse_bookmarks (const char *contents) +{ + char **lines, *space; + GSList *bookmarks = NULL; + int i; + + lines = g_strsplit (contents, "\n", -1); + + for (i = 0; lines[i]; i++) + { + GtkBookmark *bookmark; + + if (!*lines[i]) + continue; + + if (!g_utf8_validate (lines[i], -1, NULL)) + continue; + + bookmark = g_slice_new0 (GtkBookmark); + + if ((space = strchr (lines[i], ' ')) != NULL) + { + space[0] = '\0'; + bookmark->label = g_strdup (space + 1); + } + + bookmark->file = g_file_new_for_uri (lines[i]); + bookmarks = g_slist_prepend (bookmarks, bookmark); + } + + bookmarks = g_slist_reverse (bookmarks); + g_strfreev (lines); + + return bookmarks; +} + +static GSList * +read_bookmarks (GFile *file) +{ + char *contents; + GSList *bookmarks = NULL; + + if (!g_file_load_contents (file, NULL, &contents, + NULL, NULL, NULL)) + return NULL; + + bookmarks = parse_bookmarks (contents); + + g_free (contents); + + return bookmarks; +} + +static void +notify_changed (NautilusGtkBookmarksManager *manager) +{ + if (manager->changed_func) + manager->changed_func (manager->changed_func_data); +} + +static void +read_bookmarks_finish (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GFile *file = G_FILE (source); + NautilusGtkBookmarksManager *manager = data; + char *contents = NULL; + GError *error = NULL; + + if (!g_file_load_contents_finish (file, result, &contents, NULL, NULL, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to load '%s': %s", g_file_peek_path (file), error->message); + g_error_free (error); + return; + } + + g_slist_free_full (manager->bookmarks, _gtk_bookmark_free); + manager->bookmarks = parse_bookmarks (contents); + + g_free (contents); + + notify_changed (manager); +} + +static void +save_bookmarks (GFile *bookmarks_file, + GSList *bookmarks) +{ + GError *error = NULL; + GString *contents; + GSList *l; + GFile *parent = NULL; + + contents = g_string_new (""); + + for (l = bookmarks; l; l = l->next) + { + GtkBookmark *bookmark = l->data; + char *uri; + + uri = g_file_get_uri (bookmark->file); + if (!uri) + continue; + + g_string_append (contents, uri); + + if (bookmark->label && g_utf8_validate (bookmark->label, -1, NULL)) + g_string_append_printf (contents, " %s", bookmark->label); + + g_string_append_c (contents, '\n'); + g_free (uri); + } + + parent = g_file_get_parent (bookmarks_file); + if (!g_file_make_directory_with_parents (parent, NULL, &error)) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + g_clear_error (&error); + else + goto out; + } + if (!g_file_replace_contents (bookmarks_file, + contents->str, + contents->len, + NULL, FALSE, 0, NULL, + NULL, &error)) + goto out; + + out: + if (error) + { + g_critical ("%s", error->message); + g_error_free (error); + } + g_clear_object (&parent); + g_string_free (contents, TRUE); +} + +static void +bookmarks_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer data) +{ + NautilusGtkBookmarksManager *manager = data; + + switch (event) + { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + case G_FILE_MONITOR_EVENT_CREATED: + g_file_load_contents_async (file, manager->cancellable, read_bookmarks_finish, manager); + break; + + case G_FILE_MONITOR_EVENT_DELETED: + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: + case G_FILE_MONITOR_EVENT_UNMOUNTED: + case G_FILE_MONITOR_EVENT_MOVED: + case G_FILE_MONITOR_EVENT_RENAMED: + case G_FILE_MONITOR_EVENT_MOVED_IN: + case G_FILE_MONITOR_EVENT_MOVED_OUT: + default: + /* ignore at the moment */ + break; + } +} + +NautilusGtkBookmarksManager * +_nautilus_gtk_bookmarks_manager_new (GtkBookmarksChangedFunc changed_func, gpointer changed_func_data) +{ + NautilusGtkBookmarksManager *manager; + GFile *bookmarks_file; + GError *error; + + manager = g_new0 (NautilusGtkBookmarksManager, 1); + + manager->changed_func = changed_func; + manager->changed_func_data = changed_func_data; + + manager->cancellable = g_cancellable_new (); + + bookmarks_file = get_bookmarks_file (); + if (!g_file_query_exists (bookmarks_file, NULL)) + { + GFile *legacy_bookmarks_file; + + /* Read the legacy one and write it to the new one */ + legacy_bookmarks_file = get_legacy_bookmarks_file (); + manager->bookmarks = read_bookmarks (legacy_bookmarks_file); + if (manager->bookmarks) + save_bookmarks (bookmarks_file, manager->bookmarks); + + g_object_unref (legacy_bookmarks_file); + } + else + g_file_load_contents_async (bookmarks_file, manager->cancellable, read_bookmarks_finish, manager); + + error = NULL; + manager->bookmarks_monitor = g_file_monitor_file (bookmarks_file, + G_FILE_MONITOR_NONE, + NULL, &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + else + manager->bookmarks_monitor_changed_id = g_signal_connect (manager->bookmarks_monitor, "changed", + G_CALLBACK (bookmarks_file_changed), manager); + + + g_object_unref (bookmarks_file); + + return manager; +} + +void +_nautilus_gtk_bookmarks_manager_free (NautilusGtkBookmarksManager *manager) +{ + g_return_if_fail (manager != NULL); + + g_cancellable_cancel (manager->cancellable); + g_object_unref (manager->cancellable); + + if (manager->bookmarks_monitor) + { + g_file_monitor_cancel (manager->bookmarks_monitor); + g_signal_handler_disconnect (manager->bookmarks_monitor, manager->bookmarks_monitor_changed_id); + manager->bookmarks_monitor_changed_id = 0; + g_object_unref (manager->bookmarks_monitor); + } + + g_slist_free_full (manager->bookmarks, _gtk_bookmark_free); + + g_free (manager); +} + +GSList * +_nautilus_gtk_bookmarks_manager_list_bookmarks (NautilusGtkBookmarksManager *manager) +{ + GSList *bookmarks, *files = NULL; + + g_return_val_if_fail (manager != NULL, NULL); + + bookmarks = manager->bookmarks; + + while (bookmarks) + { + GtkBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + files = g_slist_prepend (files, g_object_ref (bookmark->file)); + } + + return g_slist_reverse (files); +} + +static GSList * +find_bookmark_link_for_file (GSList *bookmarks, GFile *file, int *position_ret) +{ + int pos; + + pos = 0; + for (; bookmarks; bookmarks = bookmarks->next) + { + GtkBookmark *bookmark = bookmarks->data; + + if (g_file_equal (file, bookmark->file)) + { + if (position_ret) + *position_ret = pos; + + return bookmarks; + } + + pos++; + } + + if (position_ret) + *position_ret = -1; + + return NULL; +} + +gboolean +_nautilus_gtk_bookmarks_manager_has_bookmark (NautilusGtkBookmarksManager *manager, + GFile *file) +{ + GSList *link; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + return (link != NULL); +} + +gboolean +_nautilus_gtk_bookmarks_manager_insert_bookmark (NautilusGtkBookmarksManager *manager, + GFile *file, + int position, + GError **error) +{ + GSList *link; + GtkBookmark *bookmark; + GFile *bookmarks_file; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + + if (link) + { + char *uri; + bookmark = link->data; + uri = g_file_get_uri (bookmark->file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, + _("%s already exists in the bookmarks list"), + uri); + + g_free (uri); + + return FALSE; + } + + bookmark = g_slice_new0 (GtkBookmark); + bookmark->file = g_object_ref (file); + + manager->bookmarks = g_slist_insert (manager->bookmarks, bookmark, position); + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gboolean +_nautilus_gtk_bookmarks_manager_remove_bookmark (NautilusGtkBookmarksManager *manager, + GFile *file, + GError **error) +{ + GSList *link; + GFile *bookmarks_file; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!manager->bookmarks) + return FALSE; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (link) + { + GtkBookmark *bookmark = link->data; + + manager->bookmarks = g_slist_remove_link (manager->bookmarks, link); + _gtk_bookmark_free (bookmark); + g_slist_free_1 (link); + } + else + { + set_error_bookmark_doesnt_exist (file, error); + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gboolean +_nautilus_gtk_bookmarks_manager_reorder_bookmark (NautilusGtkBookmarksManager *manager, + GFile *file, + int new_position, + GError **error) +{ + GSList *link; + GFile *bookmarks_file; + int old_position; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (new_position >= 0, FALSE); + + if (!manager->bookmarks) + return FALSE; + + link = find_bookmark_link_for_file (manager->bookmarks, file, &old_position); + if (new_position == old_position) + return TRUE; + + if (link) + { + GtkBookmark *bookmark = link->data; + + manager->bookmarks = g_slist_remove_link (manager->bookmarks, link); + g_slist_free_1 (link); + + if (new_position > old_position) + new_position--; + + manager->bookmarks = g_slist_insert (manager->bookmarks, bookmark, new_position); + } + else + { + set_error_bookmark_doesnt_exist (file, error); + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +char * +_nautilus_gtk_bookmarks_manager_get_bookmark_label (NautilusGtkBookmarksManager *manager, + GFile *file) +{ + GSList *bookmarks; + char *label = NULL; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (file != NULL, NULL); + + bookmarks = manager->bookmarks; + + while (bookmarks) + { + GtkBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + if (g_file_equal (file, bookmark->file)) + { + label = g_strdup (bookmark->label); + break; + } + } + + return label; +} + +gboolean +_nautilus_gtk_bookmarks_manager_set_bookmark_label (NautilusGtkBookmarksManager *manager, + GFile *file, + const char *label, + GError **error) +{ + GFile *bookmarks_file; + GSList *link; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (file != NULL, FALSE); + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (link) + { + GtkBookmark *bookmark = link->data; + + g_free (bookmark->label); + bookmark->label = g_strdup (label); + } + else + { + set_error_bookmark_doesnt_exist (file, error); + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +static gboolean +_nautilus_gtk_bookmarks_manager_get_xdg_type (NautilusGtkBookmarksManager *manager, + GFile *file, + GUserDirectory *directory) +{ + GSList *link; + gboolean match; + GFile *location; + const char *path; + GUserDirectory dir; + GtkBookmark *bookmark; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (!link) + return FALSE; + + match = FALSE; + bookmark = link->data; + + for (dir = 0; dir < G_USER_N_DIRECTORIES; dir++) + { + path = g_get_user_special_dir (dir); + if (!path) + continue; + + location = g_file_new_for_path (path); + match = g_file_equal (location, bookmark->file); + g_object_unref (location); + + if (match) + break; + } + + if (match && directory != NULL) + *directory = dir; + + return match; +} + +gboolean +_nautilus_gtk_bookmarks_manager_get_is_builtin (NautilusGtkBookmarksManager *manager, + GFile *file) +{ + GUserDirectory xdg_type; + + /* if this is not an XDG dir, it's never builtin */ + if (!_nautilus_gtk_bookmarks_manager_get_xdg_type (manager, file, &xdg_type)) + return FALSE; + + /* exclude XDG locations we don't display by default */ + return _nautilus_gtk_bookmarks_manager_get_is_xdg_dir_builtin (xdg_type); +} + +gboolean +_nautilus_gtk_bookmarks_manager_get_is_xdg_dir_builtin (GUserDirectory xdg_type) +{ + return (xdg_type != G_USER_DIRECTORY_DESKTOP) && + (xdg_type != G_USER_DIRECTORY_TEMPLATES) && + (xdg_type != G_USER_DIRECTORY_PUBLIC_SHARE); +} |