summaryrefslogtreecommitdiff
path: root/libcaja-private/caja-bookmark.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private/caja-bookmark.c')
-rw-r--r--libcaja-private/caja-bookmark.c672
1 files changed, 672 insertions, 0 deletions
diff --git a/libcaja-private/caja-bookmark.c b/libcaja-private/caja-bookmark.c
new file mode 100644
index 00000000..bf174247
--- /dev/null
+++ b/libcaja-private/caja-bookmark.c
@@ -0,0 +1,672 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* caja-bookmark.c - implementation of individual bookmarks.
+
+ Copyright (C) 1999, 2000 Eazel, Inc.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: John Sullivan <[email protected]>
+*/
+
+#include <config.h>
+#include "caja-bookmark.h"
+
+#include "caja-file.h"
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-string.h>
+#include <eel/eel-vfs-extensions.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <libcaja-private/caja-file.h>
+#include <libcaja-private/caja-icon-names.h>
+
+enum
+{
+ APPEARANCE_CHANGED,
+ CONTENTS_CHANGED,
+ LAST_SIGNAL
+};
+
+#define ELLIPSISED_MENU_ITEM_MIN_CHARS 32
+
+static guint signals[LAST_SIGNAL];
+
+struct CajaBookmarkDetails
+{
+ char *name;
+ gboolean has_custom_name;
+ GFile *location;
+ GIcon *icon;
+ CajaFile *file;
+
+ char *scroll_file;
+};
+
+static void caja_bookmark_connect_file (CajaBookmark *file);
+static void caja_bookmark_disconnect_file (CajaBookmark *file);
+
+G_DEFINE_TYPE (CajaBookmark, caja_bookmark, G_TYPE_OBJECT);
+
+/* GObject methods. */
+
+static void
+caja_bookmark_finalize (GObject *object)
+{
+ CajaBookmark *bookmark;
+
+ g_assert (CAJA_IS_BOOKMARK (object));
+
+ bookmark = CAJA_BOOKMARK (object);
+
+ caja_bookmark_disconnect_file (bookmark);
+
+ g_free (bookmark->details->name);
+ g_object_unref (bookmark->details->location);
+ if (bookmark->details->icon)
+ {
+ g_object_unref (bookmark->details->icon);
+ }
+ g_free (bookmark->details->scroll_file);
+ g_free (bookmark->details);
+
+ G_OBJECT_CLASS (caja_bookmark_parent_class)->finalize (object);
+}
+
+/* Initialization. */
+
+static void
+caja_bookmark_class_init (CajaBookmarkClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = caja_bookmark_finalize;
+
+ signals[APPEARANCE_CHANGED] =
+ g_signal_new ("appearance_changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaBookmarkClass, appearance_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[CONTENTS_CHANGED] =
+ g_signal_new ("contents_changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaBookmarkClass, contents_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+}
+
+static void
+caja_bookmark_init (CajaBookmark *bookmark)
+{
+ bookmark->details = g_new0 (CajaBookmarkDetails, 1);
+}
+
+/**
+ * caja_bookmark_compare_with:
+ *
+ * Check whether two bookmarks are considered identical.
+ * @a: first CajaBookmark*.
+ * @b: second CajaBookmark*.
+ *
+ * Return value: 0 if @a and @b have same name and uri, 1 otherwise
+ * (GCompareFunc style)
+ **/
+int
+caja_bookmark_compare_with (gconstpointer a, gconstpointer b)
+{
+ CajaBookmark *bookmark_a;
+ CajaBookmark *bookmark_b;
+
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (a), 1);
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (b), 1);
+
+ bookmark_a = CAJA_BOOKMARK (a);
+ bookmark_b = CAJA_BOOKMARK (b);
+
+ if (eel_strcmp (bookmark_a->details->name,
+ bookmark_b->details->name) != 0)
+ {
+ return 1;
+ }
+
+ if (!g_file_equal (bookmark_a->details->location,
+ bookmark_b->details->location))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * caja_bookmark_compare_uris:
+ *
+ * Check whether the uris of two bookmarks are for the same location.
+ * @a: first CajaBookmark*.
+ * @b: second CajaBookmark*.
+ *
+ * Return value: 0 if @a and @b have matching uri, 1 otherwise
+ * (GCompareFunc style)
+ **/
+int
+caja_bookmark_compare_uris (gconstpointer a, gconstpointer b)
+{
+ CajaBookmark *bookmark_a;
+ CajaBookmark *bookmark_b;
+
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (a), 1);
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (b), 1);
+
+ bookmark_a = CAJA_BOOKMARK (a);
+ bookmark_b = CAJA_BOOKMARK (b);
+
+ return !g_file_equal (bookmark_a->details->location,
+ bookmark_b->details->location);
+}
+
+CajaBookmark *
+caja_bookmark_copy (CajaBookmark *bookmark)
+{
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (bookmark), NULL);
+
+ return caja_bookmark_new (
+ bookmark->details->location,
+ bookmark->details->name,
+ bookmark->details->has_custom_name,
+ bookmark->details->icon);
+}
+
+char *
+caja_bookmark_get_name (CajaBookmark *bookmark)
+{
+ g_return_val_if_fail(CAJA_IS_BOOKMARK (bookmark), NULL);
+
+ return g_strdup (bookmark->details->name);
+}
+
+
+gboolean
+caja_bookmark_get_has_custom_name (CajaBookmark *bookmark)
+{
+ g_return_val_if_fail(CAJA_IS_BOOKMARK (bookmark), FALSE);
+
+ return (bookmark->details->has_custom_name);
+}
+
+
+GdkPixbuf *
+caja_bookmark_get_pixbuf (CajaBookmark *bookmark,
+ GtkIconSize stock_size)
+{
+ GdkPixbuf *result;
+ GIcon *icon;
+ CajaIconInfo *info;
+ int pixel_size;
+
+
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (bookmark), NULL);
+
+ icon = caja_bookmark_get_icon (bookmark);
+ if (icon == NULL)
+ {
+ return NULL;
+ }
+
+ pixel_size = caja_get_icon_size_for_stock_size (stock_size);
+ info = caja_icon_info_lookup (icon, pixel_size);
+ result = caja_icon_info_get_pixbuf_at_size (info, pixel_size);
+ g_object_unref (info);
+
+ g_object_unref (icon);
+
+ return result;
+}
+
+GIcon *
+caja_bookmark_get_icon (CajaBookmark *bookmark)
+{
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (bookmark), NULL);
+
+ /* Try to connect a file in case file exists now but didn't earlier. */
+ caja_bookmark_connect_file (bookmark);
+
+ if (bookmark->details->icon)
+ {
+ return g_object_ref (bookmark->details->icon);
+ }
+ return NULL;
+}
+
+GFile *
+caja_bookmark_get_location (CajaBookmark *bookmark)
+{
+ g_return_val_if_fail(CAJA_IS_BOOKMARK (bookmark), NULL);
+
+ /* Try to connect a file in case file exists now but didn't earlier.
+ * This allows a bookmark to update its image properly in the case
+ * where a new file appears with the same URI as a previously-deleted
+ * file. Calling connect_file here means that attempts to activate the
+ * bookmark will update its image if possible.
+ */
+ caja_bookmark_connect_file (bookmark);
+
+ return g_object_ref (bookmark->details->location);
+}
+
+char *
+caja_bookmark_get_uri (CajaBookmark *bookmark)
+{
+ GFile *file;
+ char *uri;
+
+ file = caja_bookmark_get_location (bookmark);
+ uri = g_file_get_uri (file);
+ g_object_unref (file);
+ return uri;
+}
+
+
+/**
+ * caja_bookmark_set_name:
+ *
+ * Change the user-displayed name of a bookmark.
+ * @new_name: The new user-displayed name for this bookmark, mustn't be NULL.
+ *
+ * Returns: TRUE if the name changed else FALSE.
+ **/
+gboolean
+caja_bookmark_set_name (CajaBookmark *bookmark, const char *new_name)
+{
+ g_return_val_if_fail (new_name != NULL, FALSE);
+ g_return_val_if_fail (CAJA_IS_BOOKMARK (bookmark), FALSE);
+
+ if (strcmp (new_name, bookmark->details->name) == 0)
+ {
+ return FALSE;
+ }
+ else if (!bookmark->details->has_custom_name)
+ {
+ bookmark->details->has_custom_name = TRUE;
+ }
+
+ g_free (bookmark->details->name);
+ bookmark->details->name = g_strdup (new_name);
+
+ g_signal_emit (bookmark, signals[APPEARANCE_CHANGED], 0);
+
+ if (bookmark->details->has_custom_name)
+ {
+ g_signal_emit (bookmark, signals[CONTENTS_CHANGED], 0);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+caja_bookmark_icon_is_different (CajaBookmark *bookmark,
+ GIcon *new_icon)
+{
+ g_assert (CAJA_IS_BOOKMARK (bookmark));
+ g_assert (new_icon != NULL);
+
+ if (bookmark->details->icon == NULL)
+ {
+ return TRUE;
+ }
+
+ return !g_icon_equal (bookmark->details->icon, new_icon) != 0;
+}
+
+/**
+ * Update icon if there's a better one available.
+ * Return TRUE if the icon changed.
+ */
+static gboolean
+caja_bookmark_update_icon (CajaBookmark *bookmark)
+{
+ GIcon *new_icon;
+
+ g_assert (CAJA_IS_BOOKMARK (bookmark));
+
+ if (bookmark->details->file == NULL)
+ {
+ return FALSE;
+ }
+
+ if (!caja_file_is_local (bookmark->details->file))
+ {
+ /* never update icons for remote bookmarks */
+ return FALSE;
+ }
+
+ if (!caja_file_is_not_yet_confirmed (bookmark->details->file) &&
+ caja_file_check_if_ready (bookmark->details->file,
+ CAJA_FILE_ATTRIBUTES_FOR_ICON))
+ {
+ new_icon = caja_file_get_gicon (bookmark->details->file, 0);
+ if (caja_bookmark_icon_is_different (bookmark, new_icon))
+ {
+ if (bookmark->details->icon)
+ {
+ g_object_unref (bookmark->details->icon);
+ }
+ bookmark->details->icon = new_icon;
+ return TRUE;
+ }
+ g_object_unref (new_icon);
+ }
+
+ return FALSE;
+}
+
+static void
+bookmark_file_changed_callback (CajaFile *file, CajaBookmark *bookmark)
+{
+ GFile *location;
+ gboolean should_emit_appearance_changed_signal;
+ gboolean should_emit_contents_changed_signal;
+ char *display_name;
+
+ g_assert (CAJA_IS_FILE (file));
+ g_assert (CAJA_IS_BOOKMARK (bookmark));
+ g_assert (file == bookmark->details->file);
+
+ should_emit_appearance_changed_signal = FALSE;
+ should_emit_contents_changed_signal = FALSE;
+ location = caja_file_get_location (file);
+
+ if (!g_file_equal (bookmark->details->location, location) &&
+ !caja_file_is_in_trash (file))
+ {
+ g_object_unref (bookmark->details->location);
+ bookmark->details->location = location;
+ should_emit_contents_changed_signal = TRUE;
+ }
+ else
+ {
+ g_object_unref (location);
+ }
+
+ if (caja_file_is_gone (file) ||
+ caja_file_is_in_trash (file))
+ {
+ /* The file we were monitoring has been trashed, deleted,
+ * or moved in a way that we didn't notice. We should make
+ * a spanking new CajaFile object for this
+ * location so if a new file appears in this place
+ * we will notice. However, we can't immediately do so
+ * because creating a new CajaFile directly as a result
+ * of noticing a file goes away may trigger i/o on that file
+ * again, noticeing it is gone, leading to a loop.
+ * So, the new CajaFile is created when the bookmark
+ * is used again. However, this is not really a problem, as
+ * we don't want to change the icon or anything about the
+ * bookmark just because its not there anymore.
+ */
+ caja_bookmark_disconnect_file (bookmark);
+ }
+ else if (caja_bookmark_update_icon (bookmark))
+ {
+ /* File hasn't gone away, but it has changed
+ * in a way that affected its icon.
+ */
+ should_emit_appearance_changed_signal = TRUE;
+ }
+
+ if (!bookmark->details->has_custom_name)
+ {
+ display_name = caja_file_get_display_name (file);
+
+ if (strcmp (bookmark->details->name, display_name) != 0)
+ {
+ g_free (bookmark->details->name);
+ bookmark->details->name = display_name;
+ should_emit_appearance_changed_signal = TRUE;
+ }
+ else
+ {
+ g_free (display_name);
+ }
+ }
+
+ if (should_emit_appearance_changed_signal)
+ {
+ g_signal_emit (bookmark, signals[APPEARANCE_CHANGED], 0);
+ }
+
+ if (should_emit_contents_changed_signal)
+ {
+ g_signal_emit (bookmark, signals[CONTENTS_CHANGED], 0);
+ }
+}
+
+/**
+ * caja_bookmark_set_icon_to_default:
+ *
+ * Reset the icon to either the missing bookmark icon or the generic
+ * bookmark icon, depending on whether the file still exists.
+ */
+static void
+caja_bookmark_set_icon_to_default (CajaBookmark *bookmark)
+{
+ GIcon *icon, *emblemed_icon, *folder;
+ GEmblem *emblem;
+
+ if (bookmark->details->icon)
+ {
+ g_object_unref (bookmark->details->icon);
+ }
+
+ folder = g_themed_icon_new (CAJA_ICON_FOLDER);
+
+ if (caja_bookmark_uri_known_not_to_exist (bookmark))
+ {
+ icon = g_themed_icon_new (GTK_STOCK_DIALOG_WARNING);
+ emblem = g_emblem_new (icon);
+
+ emblemed_icon = g_emblemed_icon_new (folder, emblem);
+
+ g_object_unref (emblem);
+ g_object_unref (icon);
+ g_object_unref (folder);
+
+ folder = emblemed_icon;
+ }
+
+ bookmark->details->icon = folder;
+}
+
+static void
+caja_bookmark_disconnect_file (CajaBookmark *bookmark)
+{
+ g_assert (CAJA_IS_BOOKMARK (bookmark));
+
+ if (bookmark->details->file != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (bookmark->details->file,
+ G_CALLBACK (bookmark_file_changed_callback),
+ bookmark);
+ caja_file_unref (bookmark->details->file);
+ bookmark->details->file = NULL;
+ }
+
+ if (bookmark->details->icon != NULL)
+ {
+ g_object_unref (bookmark->details->icon);
+ bookmark->details->icon = NULL;
+ }
+}
+
+static void
+caja_bookmark_connect_file (CajaBookmark *bookmark)
+{
+ char *display_name;
+
+ g_assert (CAJA_IS_BOOKMARK (bookmark));
+
+ if (bookmark->details->file != NULL)
+ {
+ return;
+ }
+
+ if (!caja_bookmark_uri_known_not_to_exist (bookmark))
+ {
+ bookmark->details->file = caja_file_get (bookmark->details->location);
+ g_assert (!caja_file_is_gone (bookmark->details->file));
+
+ g_signal_connect_object (bookmark->details->file, "changed",
+ G_CALLBACK (bookmark_file_changed_callback), bookmark, 0);
+ }
+
+ /* Set icon based on available information; don't force network i/o
+ * to get any currently unknown information.
+ */
+ if (!caja_bookmark_update_icon (bookmark))
+ {
+ if (bookmark->details->icon == NULL || bookmark->details->file == NULL)
+ {
+ caja_bookmark_set_icon_to_default (bookmark);
+ }
+ }
+
+ if (!bookmark->details->has_custom_name &&
+ bookmark->details->file &&
+ caja_file_check_if_ready (bookmark->details->file, CAJA_FILE_ATTRIBUTE_INFO))
+ {
+ display_name = caja_file_get_display_name (bookmark->details->file);
+ if (strcmp (bookmark->details->name, display_name) != 0)
+ {
+ g_free (bookmark->details->name);
+ bookmark->details->name = display_name;
+ }
+ else
+ {
+ g_free (display_name);
+ }
+ }
+}
+
+CajaBookmark *
+caja_bookmark_new (GFile *location, const char *name, gboolean has_custom_name,
+ GIcon *icon)
+{
+ CajaBookmark *new_bookmark;
+
+ new_bookmark = CAJA_BOOKMARK (g_object_new (CAJA_TYPE_BOOKMARK, NULL));
+ g_object_ref_sink (new_bookmark);
+
+ new_bookmark->details->name = g_strdup (name);
+ new_bookmark->details->location = g_object_ref (location);
+ new_bookmark->details->has_custom_name = has_custom_name;
+ if (icon)
+ {
+ new_bookmark->details->icon = g_object_ref (icon);
+ }
+
+ caja_bookmark_connect_file (new_bookmark);
+
+ return new_bookmark;
+}
+
+static GtkWidget *
+create_image_widget_for_bookmark (CajaBookmark *bookmark)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *widget;
+
+ pixbuf = caja_bookmark_get_pixbuf (bookmark, GTK_ICON_SIZE_MENU);
+ if (pixbuf == NULL)
+ {
+ return NULL;
+ }
+
+ widget = gtk_image_new_from_pixbuf (pixbuf);
+
+ g_object_unref (pixbuf);
+ return widget;
+}
+
+/**
+ * caja_bookmark_menu_item_new:
+ *
+ * Return a menu item representing a bookmark.
+ * @bookmark: The bookmark the menu item represents.
+ * Return value: A newly-created bookmark, not yet shown.
+ **/
+GtkWidget *
+caja_bookmark_menu_item_new (CajaBookmark *bookmark)
+{
+ GtkWidget *menu_item;
+ GtkWidget *image_widget;
+ GtkLabel *label;
+
+ menu_item = gtk_image_menu_item_new_with_label (bookmark->details->name);
+ label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (menu_item)));
+ gtk_label_set_use_underline (label, FALSE);
+ gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (label, ELLIPSISED_MENU_ITEM_MIN_CHARS);
+
+ image_widget = create_image_widget_for_bookmark (bookmark);
+ if (image_widget != NULL)
+ {
+ gtk_widget_show (image_widget);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+ image_widget);
+ }
+
+ return menu_item;
+}
+
+gboolean
+caja_bookmark_uri_known_not_to_exist (CajaBookmark *bookmark)
+{
+ char *path_name;
+ gboolean exists;
+
+ /* Convert to a path, returning FALSE if not local. */
+ if (!g_file_is_native (bookmark->details->location))
+ {
+ return FALSE;
+ }
+ path_name = g_file_get_path (bookmark->details->location);
+
+ /* Now check if the file exists (sync. call OK because it is local). */
+ exists = g_file_test (path_name, G_FILE_TEST_EXISTS);
+ g_free (path_name);
+
+ return !exists;
+}
+
+void
+caja_bookmark_set_scroll_pos (CajaBookmark *bookmark,
+ const char *uri)
+{
+ g_free (bookmark->details->scroll_file);
+ bookmark->details->scroll_file = g_strdup (uri);
+}
+
+char *
+caja_bookmark_get_scroll_pos (CajaBookmark *bookmark)
+{
+ return g_strdup (bookmark->details->scroll_file);
+}