diff options
Diffstat (limited to 'libcaja-private/caja-bookmark.c')
-rw-r--r-- | libcaja-private/caja-bookmark.c | 672 |
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); +} |