summaryrefslogtreecommitdiff
path: root/src/caja-bookmarks-sidebar.c
diff options
context:
space:
mode:
authorGordon N. Squash <[email protected]>2020-07-25 12:09:39 -0400
committerGitHub <[email protected]>2020-07-25 18:09:39 +0200
commit969fe79d5b420b89548da7a81fb46a42044b8665 (patch)
treebe368f06e5ab3e52df27c73c69508dcd0d5d35a6 /src/caja-bookmarks-sidebar.c
parentafc16dca546862ff7e19c5bb49568486615b857e (diff)
downloadcaja-969fe79d5b420b89548da7a81fb46a42044b8665.tar.bz2
caja-969fe79d5b420b89548da7a81fb46a42044b8665.tar.xz
Add a "Bookmarks" sidebar to Caja
Added a "Bookmarks" sidebar to Caja which displays a list of the user's own bookmarks. Single-clicking any bookmark in the list directs Caja to that bookmark; middle-clicking on the bookmark directs Caja to open the bookmark in a new tab.
Diffstat (limited to 'src/caja-bookmarks-sidebar.c')
-rw-r--r--src/caja-bookmarks-sidebar.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/caja-bookmarks-sidebar.c b/src/caja-bookmarks-sidebar.c
new file mode 100644
index 00000000..90ff27c9
--- /dev/null
+++ b/src/caja-bookmarks-sidebar.c
@@ -0,0 +1,589 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ * Caja: Bookmarks sidebar
+ *
+ * Copyright (C) 2020 Gordon N. Squash.
+ *
+ * This library 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors: Gordon N. Squash
+ *
+ * Based largely on the Caja Places sidebar and the Caja History sidebar.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <cairo-gobject.h>
+
+#include <eel/eel-gtk-extensions.h>
+
+#include <libcaja-private/caja-file.h>
+#include <libcaja-private/caja-file-utilities.h>
+#include <libcaja-private/caja-file-operations.h>
+
+#include <libcaja-private/caja-bookmark.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-sidebar-provider.h>
+#include <libcaja-private/caja-module.h>
+#include <libcaja-private/caja-signaller.h>
+#include <libcaja-private/caja-window-info.h>
+#include <libcaja-private/caja-window-slot-info.h>
+
+#include "caja-bookmark-list.h"
+#include "caja-window.h"
+
+#include "caja-bookmarks-sidebar.h"
+
+#define CAJA_BOOKMARKS_SIDEBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), CAJA_TYPE_BOOKMARKS_SIDEBAR, CajaBookmarksSidebarClass))
+#define CAJA_IS_BOOKMARKS_SIDEBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CAJA_TYPE_BOOKMARKS_SIDEBAR))
+#define CAJA_IS_BOOKMARKS_SIDEBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), CAJA_TYPE_BOOKMARKS_SIDEBAR))
+
+typedef struct
+{
+ GtkScrolledWindowClass parent;
+} CajaBookmarksSidebarClass;
+
+typedef struct
+{
+ GObject parent;
+} CajaBookmarksSidebarProvider;
+
+typedef struct
+{
+ GObjectClass parent;
+} CajaBookmarksSidebarProviderClass;
+
+enum
+{
+ BOOKMARKS_SIDEBAR_COLUMN_ICON,
+ BOOKMARKS_SIDEBAR_COLUMN_NAME,
+ BOOKMARKS_SIDEBAR_COLUMN_STYLE,
+ BOOKMARKS_SIDEBAR_COLUMN_ICON_VISIBLE,
+ BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK,
+ BOOKMARKS_SIDEBAR_COLUMN_COUNT
+};
+
+static void caja_bookmarks_sidebar_iface_init (CajaSidebarIface *iface);
+static void sidebar_provider_iface_init (CajaSidebarProviderIface *iface);
+static GType caja_bookmarks_sidebar_provider_get_type (void);
+static void caja_bookmarks_sidebar_style_updated (GtkWidget *widget);
+
+G_DEFINE_TYPE_WITH_CODE (CajaBookmarksSidebar, caja_bookmarks_sidebar, GTK_TYPE_SCROLLED_WINDOW,
+ G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR,
+ caja_bookmarks_sidebar_iface_init));
+
+G_DEFINE_TYPE_WITH_CODE (CajaBookmarksSidebarProvider, caja_bookmarks_sidebar_provider, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR_PROVIDER,
+ sidebar_provider_iface_init));
+
+static gboolean
+is_built_in_bookmark (CajaFile *file)
+{
+ gboolean built_in;
+ gint idx;
+
+ built_in = FALSE;
+
+ for (idx = 0; idx < G_USER_N_DIRECTORIES; idx++) {
+ /* PUBLIC_SHARE and TEMPLATES are not in our built-in list */
+ if (caja_file_is_user_special_directory (file, idx)) {
+ if (idx != G_USER_DIRECTORY_PUBLIC_SHARE && idx != G_USER_DIRECTORY_TEMPLATES) {
+ built_in = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ return built_in;
+}
+
+static void
+update_bookmarks (CajaBookmarksSidebar *sidebar)
+{
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ CajaBookmark *bookmark = NULL;
+ int bookmark_count = 0;
+ int index = 0;
+ GFile *root;
+ CajaFile *file;
+ char *bookmark_uri;
+ cairo_surface_t *surface = NULL;
+ char *name;
+ gboolean bookmarks_list_empty = TRUE;
+
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (sidebar->tree_view));
+
+ gtk_list_store_clear (store);
+
+ selection = GTK_TREE_SELECTION (gtk_tree_view_get_selection (sidebar->tree_view));
+
+ bookmark_count = caja_bookmark_list_length (sidebar->bookmarks);
+
+ for (index = 0; index < bookmark_count; index++)
+ {
+ bookmark = caja_bookmark_list_item_at (sidebar->bookmarks, index);
+
+ if (caja_bookmark_uri_known_not_to_exist (bookmark)) {
+ continue;
+ }
+
+ bookmark_uri = caja_bookmark_get_uri (bookmark);
+
+ root = caja_bookmark_get_location (bookmark);
+ file = caja_file_get (root);
+
+ if (is_built_in_bookmark (file)) {
+ g_object_unref (root);
+ caja_file_unref (file);
+ continue;
+ }
+
+ bookmarks_list_empty = FALSE;
+ surface = caja_bookmark_get_surface (bookmark, GTK_ICON_SIZE_MENU);
+ name = caja_bookmark_get_name (bookmark);
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ BOOKMARKS_SIDEBAR_COLUMN_ICON, surface,
+ BOOKMARKS_SIDEBAR_COLUMN_NAME, name,
+ BOOKMARKS_SIDEBAR_COLUMN_STYLE, PANGO_STYLE_NORMAL,
+ BOOKMARKS_SIDEBAR_COLUMN_ICON_VISIBLE, TRUE,
+ BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK, bookmark,
+ -1);
+
+ /* Select the bookmark if we're in the directory the bookmark points to. */
+ if (g_strcmp0 (bookmark_uri, sidebar->current_uri) == 0)
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_object_unref (root);
+ caja_file_unref (file);
+ g_free (bookmark_uri);
+
+ if (surface != NULL)
+ {
+ cairo_surface_destroy (surface);
+ }
+ g_free (name);
+ }
+
+ /* If the user has no user-defined bookmarks, then add one row to the list
+ * which says that no bookmarks have been defined, rather than leaving the
+ * user scratching their head wondering why the list is blank.
+ */
+ if (bookmarks_list_empty)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ BOOKMARKS_SIDEBAR_COLUMN_ICON, NULL,
+ BOOKMARKS_SIDEBAR_COLUMN_NAME, _("No bookmarks defined"),
+ BOOKMARKS_SIDEBAR_COLUMN_STYLE, PANGO_STYLE_ITALIC,
+ BOOKMARKS_SIDEBAR_COLUMN_ICON_VISIBLE, FALSE,
+ BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK, NULL,
+ -1);
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+}
+
+static void
+open_selected_item (CajaBookmarksSidebar *sidebar,
+ GtkTreePath *path,
+ CajaWindowOpenFlags flags)
+{
+ CajaWindowSlotInfo *slot;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ CajaBookmark *bookmark;
+ GFile *location;
+
+ model = gtk_tree_view_get_model (sidebar->tree_view);
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter, BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK,
+ &bookmark, -1);
+
+ if (bookmark == NULL)
+ return;
+
+ /* Navigate to the clicked location. */
+ location = caja_bookmark_get_location (CAJA_BOOKMARK (bookmark));
+
+ slot = caja_window_info_get_active_slot (sidebar->window);
+
+ caja_window_slot_info_open_location (slot, location,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ flags, NULL);
+
+ g_object_unref (location);
+}
+
+static void
+row_activated_callback (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
+{
+ CajaBookmarksSidebar *sidebar;
+
+ sidebar = CAJA_BOOKMARKS_SIDEBAR (user_data);
+ g_assert (sidebar->tree_view == tree_view);
+
+ open_selected_item (sidebar, path, 0);
+}
+
+static gboolean
+button_press_event_callback (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ CajaBookmarksSidebar *sidebar;
+ GtkTreePath *path;
+
+ int open_flags = 0;
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ /**
+ * If the middle button was pressed, open the bookmark in a new tab.
+ */
+ if (event->button == GDK_BUTTON_MIDDLE)
+ open_flags = CAJA_WINDOW_OPEN_FLAG_NEW_TAB;
+
+ /**
+ * If the middle button was not pressed, don't do
+ * anything, since the action will be handled in
+ * row_activated_callback() (above).
+ */
+ else
+ return FALSE;
+ }
+
+ sidebar = CAJA_BOOKMARKS_SIDEBAR (user_data);
+ g_assert (sidebar->tree_view == GTK_TREE_VIEW (widget));
+
+ if (gtk_tree_view_get_path_at_pos (sidebar->tree_view,
+ event->x, event->y,
+ &path, NULL, NULL, NULL))
+ {
+ open_selected_item (sidebar,
+ path,
+ open_flags);
+
+ gtk_tree_path_free (path);
+ }
+
+ /*
+ * If the middle button was pressed, don't let the event bubble up to the
+ * row_activated_callback() (above), because we do not want the row to
+ * be selected: The location of the bookmark was opened in a new tab, not
+ * the current tab.
+ */
+ return TRUE;
+}
+
+static void
+loading_uri_callback (CajaWindowInfo *window,
+ char *location,
+ CajaBookmarksSidebar *sidebar)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+ CajaBookmark *bookmark;
+ char *uri;
+
+ if (strcmp (sidebar->current_uri, location) != 0)
+ {
+ GtkTreeSelection *selection;
+
+ g_free (sidebar->current_uri);
+ sidebar->current_uri = g_strdup (location);
+
+ /* set selection if any place matches location */
+ selection = gtk_tree_view_get_selection (sidebar->tree_view);
+ gtk_tree_selection_unselect_all (selection);
+
+ model = gtk_tree_view_get_model (sidebar->tree_view);
+
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid)
+ {
+ gtk_tree_model_get (model, &iter,
+ BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK, &bookmark,
+ -1);
+
+ uri = caja_bookmark_get_uri (bookmark);
+
+ if (uri != NULL)
+ {
+ if (strcmp (uri, location) == 0)
+ {
+ g_free (uri);
+ gtk_tree_selection_select_iter (selection, &iter);
+ break;
+ }
+ g_free (uri);
+ }
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+ }
+}
+
+/*
+ * If no bookmarks have been defined by the user, then we create one row
+ * in the bookmark list sidebar which says "No bookmarks defined". We
+ * do not want the user to be able to select the row, however, so if the
+ * bookmark is NULL, then refuse selection; if the bookmark is not NULL,
+ * allow selection.
+*/
+static gboolean
+is_row_selectable (GtkTreeSelection *selection,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ gboolean path_currently_selected,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ CajaBookmark *bookmark;
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter, BOOKMARKS_SIDEBAR_COLUMN_BOOKMARK,
+ &bookmark, -1);
+
+ if (bookmark == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+caja_bookmarks_sidebar_init (CajaBookmarksSidebar *sidebar)
+{
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+ gtk_tree_view_set_headers_visible (tree_view, FALSE);
+ gtk_widget_show (GTK_WIDGET (tree_view));
+
+ col = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ());
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_attributes (col, cell,
+ "surface", BOOKMARKS_SIDEBAR_COLUMN_ICON,
+ "visible", BOOKMARKS_SIDEBAR_COLUMN_ICON_VISIBLE,
+ NULL);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (col, cell, TRUE);
+ gtk_tree_view_column_set_attributes (col, cell,
+ "text", BOOKMARKS_SIDEBAR_COLUMN_NAME,
+ "style", BOOKMARKS_SIDEBAR_COLUMN_STYLE,
+ NULL);
+
+ gtk_tree_view_column_set_fixed_width (col, CAJA_ICON_SIZE_SMALLER);
+ gtk_tree_view_append_column (tree_view, col);
+
+ store = gtk_list_store_new (BOOKMARKS_SIDEBAR_COLUMN_COUNT,
+ CAIRO_GOBJECT_TYPE_SURFACE,
+ G_TYPE_STRING,
+ PANGO_TYPE_STYLE,
+ G_TYPE_BOOLEAN,
+ CAJA_TYPE_BOOKMARK);
+
+ gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
+ gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (sidebar), FALSE);
+
+ gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view));
+ gtk_widget_show (GTK_WIDGET (sidebar));
+
+ sidebar->tree_view = tree_view;
+
+ selection = gtk_tree_view_get_selection (tree_view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ /* Please see the comment associated with the function is_row_selectable()
+ * (above).
+ */
+ gtk_tree_selection_set_select_function (selection,
+ is_row_selectable,
+ NULL,
+ NULL);
+
+ g_signal_connect_object
+ (tree_view, "row_activated",
+ G_CALLBACK (row_activated_callback), sidebar, 0);
+
+ g_signal_connect (tree_view, "button-press-event",
+ G_CALLBACK (button_press_event_callback), sidebar);
+
+ eel_gtk_tree_view_set_activate_on_single_click (sidebar->tree_view, TRUE);
+}
+
+static void
+caja_bookmarks_sidebar_finalize (GObject *object)
+{
+ CajaBookmarksSidebar *sidebar;
+
+ sidebar = CAJA_BOOKMARKS_SIDEBAR (object);
+
+ g_free (sidebar->current_uri);
+
+ G_OBJECT_CLASS (caja_bookmarks_sidebar_parent_class)->finalize (object);
+}
+
+static void
+caja_bookmarks_sidebar_class_init (CajaBookmarksSidebarClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = caja_bookmarks_sidebar_finalize;
+
+ GTK_WIDGET_CLASS (class)->style_updated = caja_bookmarks_sidebar_style_updated;
+}
+
+static const char *
+caja_bookmarks_sidebar_get_sidebar_id (CajaSidebar *sidebar)
+{
+ return CAJA_BOOKMARKS_SIDEBAR_ID;
+}
+
+static char *
+caja_bookmarks_sidebar_get_tab_label (CajaSidebar *sidebar)
+{
+ return g_strdup (_("Bookmarks"));
+}
+
+static char *
+caja_bookmarks_sidebar_get_tab_tooltip (CajaSidebar *sidebar)
+{
+ return g_strdup (_("Show a list of your personal bookmarks"));
+}
+
+static GdkPixbuf *
+caja_bookmarks_sidebar_get_tab_icon (CajaSidebar *sidebar)
+{
+ return NULL;
+}
+
+static void
+caja_bookmarks_sidebar_is_visible_changed (CajaSidebar *sidebar,
+ gboolean is_visible)
+{
+ /* Do nothing */
+}
+
+static void
+caja_bookmarks_sidebar_iface_init (CajaSidebarIface *iface)
+{
+ iface->get_sidebar_id = caja_bookmarks_sidebar_get_sidebar_id;
+ iface->get_tab_label = caja_bookmarks_sidebar_get_tab_label;
+ iface->get_tab_tooltip = caja_bookmarks_sidebar_get_tab_tooltip;
+ iface->get_tab_icon = caja_bookmarks_sidebar_get_tab_icon;
+ iface->is_visible_changed = caja_bookmarks_sidebar_is_visible_changed;
+}
+
+static void
+caja_bookmarks_sidebar_set_parent_window (CajaBookmarksSidebar *sidebar,
+ CajaWindowInfo *window)
+{
+ CajaWindowSlotInfo *slot;
+
+ sidebar->window = window;
+
+ slot = caja_window_info_get_active_slot (window);
+
+ sidebar->bookmarks = caja_bookmark_list_new ();
+ sidebar->current_uri = caja_window_slot_info_get_current_location (slot);
+
+ g_signal_connect_object (sidebar->bookmarks, "contents_changed",
+ G_CALLBACK (update_bookmarks),
+ sidebar, G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (window, "loading_uri",
+ G_CALLBACK (loading_uri_callback),
+ sidebar, 0);
+
+ update_bookmarks (sidebar);
+}
+
+static void
+caja_bookmarks_sidebar_style_updated (GtkWidget *widget)
+{
+ CajaBookmarksSidebar *sidebar;
+
+ sidebar = CAJA_BOOKMARKS_SIDEBAR (widget);
+
+ update_bookmarks (sidebar);
+}
+
+static CajaSidebar *
+caja_bookmarks_sidebar_create (CajaSidebarProvider *provider,
+ CajaWindowInfo *window)
+{
+ CajaBookmarksSidebar *sidebar;
+
+ sidebar = g_object_new (caja_bookmarks_sidebar_get_type (), NULL);
+ caja_bookmarks_sidebar_set_parent_window (sidebar, window);
+ g_object_ref_sink (sidebar);
+
+ return CAJA_SIDEBAR (sidebar);
+}
+
+static void
+sidebar_provider_iface_init (CajaSidebarProviderIface *iface)
+{
+ iface->create = caja_bookmarks_sidebar_create;
+}
+
+static void
+caja_bookmarks_sidebar_provider_init (CajaBookmarksSidebarProvider *sidebar)
+{
+}
+
+static void
+caja_bookmarks_sidebar_provider_class_init (CajaBookmarksSidebarProviderClass *class)
+{
+}
+
+void
+caja_bookmarks_sidebar_register (void)
+{
+ caja_module_add_type (caja_bookmarks_sidebar_provider_get_type ());
+}