From 6237086a6237867685008ef468eaa241b7f12897 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Fri, 22 Jan 2016 19:10:28 -0500 Subject: [PATCH] bookmarks: improve icon display - Make bookmark file loading and saving a completely threaded operation - Store custom icon metadata for bookmarks in a non-gvfs way, so un-mounted items can display with their correct icons and emblems. Fixes #177 Fixes #1071 --- libnemo-private/nemo-bookmark.c | 270 +++++++++++++-- libnemo-private/nemo-bookmark.h | 23 +- m4/intltool.m4 | 25 -- po/Makefile.in.in | 11 +- src/nemo-bookmark-list.c | 585 +++++++++++++++++++++++--------- src/nemo-bookmark-list.h | 2 + src/nemo-bookmarks-window.c | 2 +- src/nemo-places-sidebar.c | 4 +- src/nemo-window-bookmarks.c | 1 + src/nemo-window-manage-views.c | 2 +- 10 files changed, 694 insertions(+), 231 deletions(-) diff --git a/libnemo-private/nemo-bookmark.c b/libnemo-private/nemo-bookmark.c index aa8298487..38d5fb4f4 100644 --- a/libnemo-private/nemo-bookmark.c +++ b/libnemo-private/nemo-bookmark.c @@ -27,6 +27,7 @@ #include #include "nemo-bookmark.h" +#include "nemo-metadata.h" #include #include @@ -51,6 +52,7 @@ enum { PROP_CUSTOM_NAME, PROP_LOCATION, PROP_ICON, + PROP_METADATA, NUM_PROPERTIES }; @@ -68,6 +70,8 @@ struct NemoBookmarkDetails NemoFile *file; char *scroll_file; + + NemoBookmarkMetadata *metadata; }; static void nemo_bookmark_disconnect_file (NemoBookmark *file); @@ -95,11 +99,6 @@ nemo_bookmark_update_icon (NemoBookmark *bookmark) return; } - if (!nemo_file_is_local (bookmark->details->file)) { - /* never update icons for remote bookmarks */ - return; - } - if (!nemo_file_is_not_yet_confirmed (bookmark->details->file) && nemo_file_check_if_ready (bookmark->details->file, NEMO_FILE_ATTRIBUTES_FOR_ICON)) { @@ -136,49 +135,169 @@ bookmark_set_name_from_ready_file (NemoBookmark *self, g_free (display_name); } -static void -nemo_bookmark_set_icon_to_default (NemoBookmark *bookmark) +static GIcon * +get_default_folder_icon (NemoBookmark *bookmark) { - GIcon *icon, *emblemed_icon, *folder; - GEmblem *emblem; - char *uri; + GIcon *ret = NULL; if (g_file_is_native (bookmark->details->location)) { - folder = g_themed_icon_new (NEMO_ICON_FOLDER); + ret = g_themed_icon_new (NEMO_ICON_FOLDER); } else { - uri = g_file_get_uri (bookmark->details->location); + gchar *uri = g_file_get_uri (bookmark->details->location); if (g_str_has_prefix (uri, EEL_SEARCH_URI)) { - folder = g_themed_icon_new (NEMO_ICON_FOLDER_SAVED_SEARCH); + ret = g_themed_icon_new (NEMO_ICON_FOLDER_SAVED_SEARCH); } else { - folder = g_themed_icon_new (NEMO_ICON_FOLDER_REMOTE); + ret = g_themed_icon_new (NEMO_ICON_FOLDER_REMOTE); } g_free (uri); } + return ret; +} + +static GIcon * +construct_default_icon_from_metadata (NemoBookmark *bookmark) +{ + NemoBookmarkMetadata *md = bookmark->details->metadata; + GIcon *ret = NULL; + + if (md->icon_name) { + ret = g_themed_icon_new (md->icon_name); + } else if (md->icon_uri) { + GFile *file = g_file_new_for_uri (md->icon_uri); + ret = g_file_icon_new (file); + g_object_unref (file); + } else { + ret = get_default_folder_icon (bookmark); + } + + if (ret != NULL && md->emblems != NULL) { + gint i = 0; + + GIcon *emb_icon; + GEmblem *emblem; + + emb_icon = g_themed_icon_new (md->emblems[i]); + emblem = g_emblem_new (emb_icon); + + ret = g_emblemed_icon_new (ret, emblem); + + i++; + + while (i < g_strv_length (md->emblems)) { + emb_icon = g_themed_icon_new (md->emblems[i]); + emblem = g_emblem_new (emb_icon); + + g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (ret), emblem); + + i++; + } + } + + return ret; +} + +static void +nemo_bookmark_set_icon_to_default (NemoBookmark *bookmark) +{ + GIcon *icon, *emblemed_icon; + GIcon *folder = NULL; + GEmblem *emblem; + + folder = NULL; + + if (bookmark->details->metadata != NULL) { + folder = construct_default_icon_from_metadata (bookmark); + } + + if (!folder) + folder = get_default_folder_icon (bookmark); + if (!nemo_bookmark_uri_get_exists (bookmark)) { DEBUG ("%s: file does not exist, add emblem", nemo_bookmark_get_name (bookmark)); icon = g_themed_icon_new (GTK_STOCK_DIALOG_WARNING); emblem = g_emblem_new (icon); - emblemed_icon = g_emblemed_icon_new (folder, emblem); + if (G_IS_EMBLEMED_ICON (folder)) + g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (folder), emblem); + else { + emblemed_icon = g_emblemed_icon_new (folder, emblem); + folder = emblemed_icon; + } g_object_unref (emblem); g_object_unref (icon); - g_object_unref (folder); - - folder = emblemed_icon; } DEBUG ("%s: setting icon to default", nemo_bookmark_get_name (bookmark)); g_object_set (bookmark, - "icon", folder, + "icon", G_ICON (folder), NULL); g_object_unref (folder); } +static gint +cmp_glist_strv (gchar **strv, + GList *list) +{ + if (strv == NULL && list == NULL) + return 0; + + if (strv == NULL || list == NULL) + return 1; + + if (g_list_length (list) != g_strv_length (strv)) + return 1; + + gint ret = 0; + + gint i = 0; + GList *ptr; + + for (ptr = list; ptr != NULL; ptr = ptr->next) { + if (g_strcmp0 (ptr->data, strv[i]) != 0) { + ret = 1; + break; + } + + i++; + } + + return ret; +} + +static gboolean +metadata_changed (NemoBookmark *bookmark) +{ + gchar *icon_uri = NULL; + gchar *icon_name = NULL; + GList *emblems = NULL; + + gboolean ret = FALSE; + + nemo_bookmark_get_metadata (bookmark, &icon_uri, &icon_name, &emblems); + + gboolean has_custom = (icon_uri || icon_name || emblems); + gboolean had_custom = bookmark->details->metadata != NULL; + + if ((has_custom && !had_custom) || + (had_custom && !has_custom)) { + ret = TRUE; + } else if (has_custom && had_custom) { + NemoBookmarkMetadata *md = bookmark->details->metadata; + if (g_strcmp0 (md->bookmark_name, bookmark->details->name) != 0 || + g_strcmp0 (md->icon_uri, icon_uri) != 0 || + g_strcmp0 (md->icon_name, icon_name) != 0 || + cmp_glist_strv (md->emblems, emblems) != 0) + ret = TRUE; + } + + return ret; +} + static void bookmark_file_changed_callback (NemoFile *file, NemoBookmark *bookmark) @@ -225,6 +344,10 @@ bookmark_file_changed_callback (NemoFile *file, } else { nemo_bookmark_update_icon (bookmark); bookmark_set_name_from_ready_file (bookmark, file); + + if (metadata_changed (bookmark)) { + g_signal_emit (bookmark, signals[CONTENTS_CHANGED], 0); + } } } @@ -311,6 +434,12 @@ nemo_bookmark_set_property (GObject *object, case PROP_NAME: nemo_bookmark_set_name_internal (self, g_value_get_string (value)); break; + case PROP_METADATA: + if (self->details->metadata) + g_clear_pointer (&self->details->metadata, nemo_bookmark_metadata_free); + + self->details->metadata = g_value_get_pointer (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -338,6 +467,9 @@ nemo_bookmark_get_property (GObject *object, case PROP_CUSTOM_NAME: g_value_set_boolean (value, self->details->has_custom_name); break; + case PROP_METADATA: + g_value_set_pointer (value, self->details->metadata); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -358,6 +490,8 @@ nemo_bookmark_finalize (GObject *object) g_object_unref (bookmark->details->location); g_clear_object (&bookmark->details->icon); + g_clear_pointer (&bookmark->details->metadata, nemo_bookmark_metadata_free); + g_free (bookmark->details->name); g_free (bookmark->details->scroll_file); @@ -420,6 +554,12 @@ nemo_bookmark_class_init (NemoBookmarkClass *class) G_TYPE_ICON, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_METADATA] = + g_param_spec_pointer ("metadata", + "Bookmark's non-gvfs metadata", + "Metadata for defining the bookmark's icon", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); g_type_class_add_private (class, sizeof (NemoBookmarkDetails)); @@ -536,11 +676,12 @@ nemo_bookmark_copy (NemoBookmark *bookmark) { g_return_val_if_fail (NEMO_IS_BOOKMARK (bookmark), NULL); - return nemo_bookmark_new ( - bookmark->details->location, - bookmark->details->has_custom_name ? - bookmark->details->name : NULL, - bookmark->details->icon); + return nemo_bookmark_new (bookmark->details->location, + bookmark->details->has_custom_name ? + bookmark->details->name : NULL, + bookmark->details->icon, + bookmark->details->metadata ? + nemo_bookmark_metadata_copy (bookmark->details->metadata) : NULL); } GIcon * @@ -586,9 +727,10 @@ nemo_bookmark_get_uri (NemoBookmark *bookmark) } NemoBookmark * -nemo_bookmark_new (GFile *location, - const gchar *custom_name, - GIcon *icon) +nemo_bookmark_new (GFile *location, + const gchar *custom_name, + GIcon *icon, + NemoBookmarkMetadata *md) { NemoBookmark *new_bookmark; gchar *name; @@ -603,11 +745,12 @@ nemo_bookmark_new (GFile *location, "icon", icon, "name", name, "custom-name", custom_name != NULL, + "metadata", md, NULL)); g_free (name); - return new_bookmark; -} + return new_bookmark; +} static GtkWidget * create_image_widget_for_bookmark (NemoBookmark *bookmark) @@ -691,4 +834,73 @@ void nemo_bookmark_connect (NemoBookmark *bookmark) { nemo_bookmark_connect_file (bookmark); +} + +void +nemo_bookmark_get_metadata (NemoBookmark *bookmark, + gchar **icon_uri, + gchar **icon_name, + GList **emblems) +{ + if (!bookmark->details->file) + return; + + GList *custom_emblems = NULL; + gchar *custom_icon_uri = NULL; + gchar *custom_icon_name = NULL; + + if (bookmark->details->file && !nemo_file_is_gone (bookmark->details->file)) { + custom_emblems = nemo_file_get_metadata_list (bookmark->details->file, NEMO_METADATA_KEY_EMBLEMS); + custom_icon_uri = nemo_file_get_metadata (bookmark->details->file, NEMO_METADATA_KEY_CUSTOM_ICON, NULL); + custom_icon_name = nemo_file_get_metadata (bookmark->details->file, NEMO_METADATA_KEY_CUSTOM_ICON_NAME, NULL); + } else if (bookmark->details->metadata) { + NemoBookmarkMetadata *md = bookmark->details->metadata; + + custom_icon_uri = g_strdup (md->icon_uri); + custom_icon_name = g_strdup (md->icon_name); + + gint i = 0; + for (i = 0; i < g_strv_length (md->emblems); i++) { + custom_emblems = g_list_append (custom_emblems, g_strdup (md->emblems[i])); + } + } + + if (icon_uri != NULL) + *icon_uri = custom_icon_uri; + if (icon_name != NULL) + *icon_name = custom_icon_name; + if (emblems != NULL) + *emblems = custom_emblems; +} + +NemoBookmarkMetadata * +nemo_bookmark_metadata_new (void) +{ + NemoBookmarkMetadata *meta = g_slice_new0 (NemoBookmarkMetadata); + + return meta; +} + +NemoBookmarkMetadata * +nemo_bookmark_metadata_copy (NemoBookmarkMetadata *meta) +{ + NemoBookmarkMetadata *copy = nemo_bookmark_metadata_new (); + + copy->bookmark_name = g_strdup (meta->bookmark_name); + copy->icon_name = g_strdup (meta->icon_name); + copy->icon_uri = g_strdup (meta->icon_uri); + copy->emblems = g_strdupv (meta->emblems); + + return copy; +} + +void +nemo_bookmark_metadata_free (NemoBookmarkMetadata *metadata) +{ + g_free (metadata->bookmark_name); + g_free (metadata->icon_name); + g_free (metadata->icon_uri); + g_strfreev (metadata->emblems); + + g_slice_free (NemoBookmarkMetadata, metadata); } \ No newline at end of file diff --git a/libnemo-private/nemo-bookmark.h b/libnemo-private/nemo-bookmark.h index 2b0ed49e2..43bd6a258 100644 --- a/libnemo-private/nemo-bookmark.h +++ b/libnemo-private/nemo-bookmark.h @@ -50,6 +50,14 @@ struct NemoBookmark { NemoBookmarkDetails *details; }; +typedef struct +{ + gchar *bookmark_name; + gchar *icon_name; + gchar *icon_uri; + gchar **emblems; +} NemoBookmarkMetadata; + struct NemoBookmarkClass { GObjectClass parent_class; @@ -66,9 +74,10 @@ struct NemoBookmarkClass { typedef struct NemoBookmarkClass NemoBookmarkClass; GType nemo_bookmark_get_type (void); -NemoBookmark * nemo_bookmark_new (GFile *location, - const char *custom_name, - GIcon *icon); +NemoBookmark * nemo_bookmark_new (GFile *location, + const char *custom_name, + GIcon *icon, + NemoBookmarkMetadata *md); NemoBookmark * nemo_bookmark_copy (NemoBookmark *bookmark); const char * nemo_bookmark_get_name (NemoBookmark *bookmark); GFile * nemo_bookmark_get_location (NemoBookmark *bookmark); @@ -93,4 +102,12 @@ GtkWidget * nemo_bookmark_menu_item_new (NemoBookmark *b void nemo_bookmark_connect (NemoBookmark *bookmark); +void nemo_bookmark_get_metadata (NemoBookmark *bookmark, + gchar **icon_uri, + gchar **icon_name, + GList **emblems); +NemoBookmarkMetadata *nemo_bookmark_metadata_new (void); +NemoBookmarkMetadata *nemo_bookmark_metadata_copy (NemoBookmarkMetadata *meta); +void nemo_bookmark_metadata_free (NemoBookmarkMetadata *metadata); + #endif /* NEMO_BOOKMARK_H */ diff --git a/m4/intltool.m4 b/m4/intltool.m4 index 33353eda4..c25b7b1ac 100644 --- a/m4/intltool.m4 +++ b/m4/intltool.m4 @@ -155,31 +155,6 @@ fi # Substitute ALL_LINGUAS so we can use it in po/Makefile AC_SUBST(ALL_LINGUAS) -# Set DATADIRNAME correctly if it is not set yet -# (copied from glib-gettext.m4) -if test -z "$DATADIRNAME"; then - AC_LINK_IFELSE( - [AC_LANG_PROGRAM([[]], - [[extern int _nl_msg_cat_cntr; - return _nl_msg_cat_cntr]])], - [DATADIRNAME=share], - [case $host in - *-*-solaris*) - dnl On Solaris, if bind_textdomain_codeset is in libc, - dnl GNU format message catalog is always supported, - dnl since both are added to the libc all together. - dnl Hence, we'd like to go with DATADIRNAME=share - dnl in this case. - AC_CHECK_FUNC(bind_textdomain_codeset, - [DATADIRNAME=share], [DATADIRNAME=lib]) - ;; - *) - [DATADIRNAME=lib] - ;; - esac]) -fi -AC_SUBST(DATADIRNAME) - IT_PO_SUBDIR([po]) ]) diff --git a/po/Makefile.in.in b/po/Makefile.in.in index 06a8cfe92..fcd2c3b70 100644 --- a/po/Makefile.in.in +++ b/po/Makefile.in.in @@ -33,8 +33,7 @@ exec_prefix = @exec_prefix@ datadir = @datadir@ datarootdir = @datarootdir@ libdir = @libdir@ -DATADIRNAME = @DATADIRNAME@ -itlocaledir = $(prefix)/$(DATADIRNAME)/locale +localedir = @localedir@ subdir = po install_sh = @install_sh@ # Automake >= 1.8 provides @mkdir_p@. @@ -80,7 +79,7 @@ INTLTOOL__v_MSGFMT_0 = @echo " MSGFMT" $@; .po.pox: $(MAKE) $(GETTEXT_PACKAGE).pot - $(MSGMERGE) $< $(GETTEXT_PACKAGE).pot -o $*.pox + $(MSGMERGE) $* $(GETTEXT_PACKAGE).pot -o $*.pox .po.mo: $(INTLTOOL_V_MSGFMT)$(MSGFMT) -o $@ $< @@ -108,7 +107,7 @@ install-data-no: all install-data-yes: all linguas="$(USE_LINGUAS)"; \ for lang in $$linguas; do \ - dir=$(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES; \ + dir=$(DESTDIR)$(localedir)/$$lang/LC_MESSAGES; \ $(mkdir_p) $$dir; \ if test -r $$lang.gmo; then \ $(INSTALL_DATA) $$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ @@ -142,8 +141,8 @@ install-exec installcheck: uninstall: linguas="$(USE_LINGUAS)"; \ for lang in $$linguas; do \ - rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \ - rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo.m; \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo.m; \ done check: all $(GETTEXT_PACKAGE).pot diff --git a/src/nemo-bookmark-list.c b/src/nemo-bookmark-list.c index 78764a2f4..9456393cc 100644 --- a/src/nemo-bookmark-list.c +++ b/src/nemo-bookmark-list.c @@ -41,6 +41,10 @@ #define LOAD_JOB 1 #define SAVE_JOB 2 +#define KEY_ICON_URI "IconUri" +#define KEY_ICON_NAME "IconName" +#define KEY_ICON_EMBLEMS "IconEmblems" + enum { CHANGED, LAST_SIGNAL @@ -55,10 +59,29 @@ static NemoBookmarkList *singleton = NULL; static void nemo_bookmark_list_load_file (NemoBookmarkList *bookmarks); static void nemo_bookmark_list_save_file (NemoBookmarkList *bookmarks); -G_DEFINE_TYPE(NemoBookmarkList, nemo_bookmark_list, G_TYPE_OBJECT) +G_DEFINE_TYPE(NemoBookmarkList, nemo_bookmark_list, G_TYPE_OBJECT); + +static void +ensure_proper_file_permissions (GFile *file) +{ + if (geteuid () == 0) { + struct passwd *pwent; + pwent = gnome_desktop_get_session_user_pwent (); + + gchar *path = g_file_get_path (file); + + if (g_strcmp0 (pwent->pw_dir, g_get_home_dir ()) == 0) { + G_GNUC_UNUSED int res; + + res = chown (path, pwent->pw_uid, pwent->pw_gid); + } + + g_free (path); + } +} static NemoBookmark * -new_bookmark_from_uri (const char *uri, const char *label) +new_bookmark_from_uri (const char *uri, const char *label, NemoBookmarkMetadata *md) { NemoBookmark *new_bookmark; GFile *location; @@ -71,7 +94,7 @@ new_bookmark_from_uri (const char *uri, const char *label) new_bookmark = NULL; if (location) { - new_bookmark = nemo_bookmark_new (location, label, NULL); + new_bookmark = nemo_bookmark_new (location, label, NULL, md); g_object_unref (location); } @@ -119,18 +142,32 @@ bookmark_in_list_changed_callback (NemoBookmark *bookmark, { g_assert (NEMO_IS_BOOKMARK (bookmark)); g_assert (NEMO_IS_BOOKMARK_LIST (bookmarks)); - /* save changes to the list */ nemo_bookmark_list_save_file (bookmarks); } +static gboolean +idle_notify (NemoBookmarkList *bookmarks) +{ + g_signal_emit (bookmarks, signals[CHANGED], 0); + + bookmarks->idle_notify_id = 0; + return FALSE; +} + static void bookmark_in_list_notify (GObject *object, GParamSpec *pspec, NemoBookmarkList *bookmarks) { /* emit the changed signal without saving, as only appearance properties changed */ - g_signal_emit (bookmarks, signals[CHANGED], 0); + + if (bookmarks->idle_notify_id > 0) { + g_source_remove (bookmarks->idle_notify_id); + bookmarks->idle_notify_id = 0; + } + + bookmarks->idle_notify_id = g_idle_add ((GSourceFunc) idle_notify, bookmarks); } static gboolean @@ -191,11 +228,11 @@ stop_monitoring_one (gpointer data, gpointer user_data) } static void -clear_bookmarks (NemoBookmarkList *bookmarks) +clear_bookmarks (NemoBookmarkList *bookmarks, GList *list) { - g_list_foreach (bookmarks->list, stop_monitoring_one, bookmarks); - g_list_free_full (bookmarks->list, g_object_unref); - bookmarks->list = NULL; + g_list_foreach (list, stop_monitoring_one, bookmarks); + g_list_free_full (list, g_object_unref); + list = NULL; } static void @@ -208,7 +245,7 @@ do_finalize (GObject *object) g_queue_free (NEMO_BOOKMARK_LIST (object)->pending_ops); - clear_bookmarks (NEMO_BOOKMARK_LIST (object)); + clear_bookmarks (NEMO_BOOKMARK_LIST (object), NEMO_BOOKMARK_LIST (object)->list); g_object_unref (NEMO_BOOKMARK_LIST (object)->volume_monitor); @@ -235,6 +272,13 @@ do_constructor (GType type, return retval; } +static void +do_constructed (GObject *object) +{ + G_OBJECT_CLASS (nemo_bookmark_list_parent_class)->constructed (object); + + nemo_bookmark_list_load_file (NEMO_BOOKMARK_LIST (object)); +} static void nemo_bookmark_list_class_init (NemoBookmarkListClass *class) @@ -243,6 +287,7 @@ nemo_bookmark_list_class_init (NemoBookmarkListClass *class) object_class->finalize = do_finalize; object_class->constructor = do_constructor; + object_class->constructed = do_constructed; signals[CHANGED] = g_signal_new ("changed", @@ -282,16 +327,18 @@ nemo_bookmark_list_init (NemoBookmarkList *bookmarks) { GFile *file; - bookmarks->pending_ops = g_queue_new (); + bookmarks->list = NULL; + bookmarks->idle_notify_id = 0; - nemo_bookmark_list_load_file (bookmarks); + bookmarks->pending_ops = g_queue_new (); file = nemo_bookmark_list_get_file (); bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL); g_file_monitor_set_rate_limit (bookmarks->monitor, 1000); g_signal_connect (bookmarks->monitor, "changed", - G_CALLBACK (bookmark_monitor_changed_cb), bookmarks); + G_CALLBACK (bookmark_monitor_changed_cb), + bookmarks); g_object_unref (file); @@ -305,6 +352,22 @@ nemo_bookmark_list_init (NemoBookmarkList *bookmarks) G_CALLBACK (volume_monitor_activity_cb), bookmarks); } +static void +connect_bookmark_signals (NemoBookmark *bookmark, + NemoBookmarkList *bookmarks) +{ + g_signal_connect_object (bookmark, "location-mounted", + G_CALLBACK (bookmark_location_mounted_callback), bookmarks, 0); + g_signal_connect_object (bookmark, "contents-changed", + G_CALLBACK (bookmark_in_list_changed_callback), bookmarks, 0); + g_signal_connect_object (bookmark, "notify::icon", + G_CALLBACK (bookmark_in_list_notify), bookmarks, 0); + g_signal_connect_object (bookmark, "notify::name", + G_CALLBACK (bookmark_in_list_notify), bookmarks, 0); + + nemo_bookmark_connect (bookmark); +} + static void insert_bookmark_internal (NemoBookmarkList *bookmarks, NemoBookmark *bookmark, @@ -312,16 +375,7 @@ insert_bookmark_internal (NemoBookmarkList *bookmarks, { bookmarks->list = g_list_insert (bookmarks->list, bookmark, index); - g_signal_connect_object (bookmark, "location-mounted", - G_CALLBACK (bookmark_location_mounted_callback), bookmarks, 0); - g_signal_connect_object (bookmark, "contents-changed", - G_CALLBACK (bookmark_in_list_changed_callback), bookmarks, 0); - g_signal_connect_object (bookmark, "notify::icon", - G_CALLBACK (bookmark_in_list_notify), bookmarks, 0); - g_signal_connect_object (bookmark, "notify::name", - G_CALLBACK (bookmark_in_list_notify), bookmarks, 0); - - nemo_bookmark_connect (bookmark); + connect_bookmark_signals (bookmark, bookmarks); } /** @@ -597,183 +651,385 @@ nemo_bookmark_list_length (NemoBookmarkList *bookmarks) return g_list_length (bookmarks->list); } +static GList * +load_bookmark_metadata_file (NemoBookmarkList *list) +{ /* Part of load_files thread */ + GError *error = NULL; + GKeyFile *kfile = g_key_file_new (); + GList *ret = NULL; + + gchar *filename; + + filename = g_build_filename (g_get_user_config_dir (), + "nemo", + "bookmark-metadata", + NULL); + + if (g_key_file_load_from_file (kfile, + filename, + G_KEY_FILE_NONE, + &error)) { + gchar **items = g_key_file_get_groups (kfile, NULL); + + gint i; + for (i = 0; i < g_strv_length (items); i++) { + NemoBookmarkMetadata *meta = nemo_bookmark_metadata_new (); + + meta->bookmark_name = g_strdup (items[i]); + meta->icon_uri = g_key_file_get_string (kfile, + items[i], + KEY_ICON_URI, + NULL); + meta->icon_name = g_key_file_get_string (kfile, + items[i], + KEY_ICON_NAME, + NULL); + meta->emblems = g_key_file_get_string_list (kfile, + items[i], + KEY_ICON_EMBLEMS, + NULL, + NULL); + + ret = g_list_append (ret, meta); + } + + g_strfreev (items); + } else if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_warning ("Could not load bookmark metadata file: %s\n", error->message); + g_error_free (error); + } + + g_key_file_free (kfile); + + return ret; +} + static void -load_file_finish (NemoBookmarkList *bookmarks, - GObject *source, - GAsyncResult *res) -{ - GError *error = NULL; - gchar *contents = NULL; - - g_file_load_contents_finish (G_FILE (source), - res, &contents, NULL, NULL, &error); - - if (error == NULL) { - char **lines; - int i; - - lines = g_strsplit (contents, "\n", -1); - for (i = 0; lines[i]; i++) { - /* Ignore empty or invalid lines that cannot be parsed properly */ - if (lines[i][0] != '\0' && lines[i][0] != ' ') { - /* gtk 2.7/2.8 might have labels appended to bookmarks which are separated by a space */ - /* we must seperate the bookmark uri and the potential label */ - char *space, *label; - - label = NULL; - space = strchr (lines[i], ' '); - if (space) { - *space = '\0'; - label = g_strdup (space + 1); - } - insert_bookmark_internal (bookmarks, - new_bookmark_from_uri (lines[i], label), - -1); - - g_free (label); - } - } - g_free (contents); - g_strfreev (lines); +load_files_finish (NemoBookmarkList *bookmarks, + GObject *source, + GAsyncResult *res) +{ + GError *error = NULL; - g_signal_emit (bookmarks, signals[CHANGED], 0); - } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { - g_warning ("Could not load bookmark file: %s\n", error->message); - g_error_free (error); - } + GList *new_list = g_task_propagate_pointer (G_TASK (res), &error); + + if (error != NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_warning ("Could not load bookmarks: %s\n", error->message); + g_error_free (error); + return; + } + + GList *old_list = bookmarks->list; + bookmarks->list = new_list; + + clear_bookmarks (bookmarks, old_list); + + g_list_foreach (bookmarks->list, (GFunc) connect_bookmark_signals, bookmarks); + g_signal_emit (bookmarks, signals[CHANGED], 0); +} + +static gint +find_meta_func (gconstpointer a, + gconstpointer b) +{ + return g_strcmp0 (((NemoBookmarkMetadata *) a)->bookmark_name, (gchar *) b); } static void -load_file_async (NemoBookmarkList *self, - GAsyncReadyCallback callback) +load_files_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { - GFile *file; + NemoBookmarkList *list = NEMO_BOOKMARK_LIST (source_object); - file = nemo_bookmark_list_get_file (); - if (!g_file_query_exists (file, NULL)) { - g_object_unref (file); - file = nemo_bookmark_list_get_legacy_file (); - } + GError *error = NULL; + GFile *file; - /* Wipe out old list. */ - clear_bookmarks (self); + file = nemo_bookmark_list_get_file (); - /* keep the bookmark list alive */ - g_object_ref (self); - g_file_load_contents_async (file, NULL, callback, self); + if (!g_file_query_exists (file, NULL)) { + g_object_unref (file); + file = nemo_bookmark_list_get_legacy_file (); + } - g_object_unref (file); + gchar *contents = NULL; + + GList *new_list = NULL; + + if (g_file_load_contents (file, + cancellable, + &contents, + NULL, + NULL, + &error)) { + + GList *metadata = load_bookmark_metadata_file (list); + + gchar **lines; + gint i; + + lines = g_strsplit (contents, "\n", -1); + for (i = 0; lines[i]; i++) { + /* Ignore empty or invalid lines that cannot be parsed properly */ + if (lines[i][0] != '\0' && lines[i][0] != ' ') { + /* gtk 2.7/2.8 might have labels appended to bookmarks which are separated by a space */ + /* we must seperate the bookmark uri and the potential label */ + gchar *space, *label; + + label = NULL; + space = strchr (lines[i], ' '); + if (space) { + *space = '\0'; + label = g_strdup (space + 1); + } + + GList *meta_ptr = g_list_find_custom (metadata, label, (GCompareFunc) find_meta_func); + + NemoBookmarkMetadata *md = NULL; + + if (meta_ptr) { + metadata = g_list_remove_link (metadata, meta_ptr); + md = (NemoBookmarkMetadata *) meta_ptr->data; + g_list_free (meta_ptr); + } + + new_list = g_list_insert (new_list, + new_bookmark_from_uri (lines[i], label, md), + -1); + + g_free (label); + } + } + + g_list_free_full (metadata, (GDestroyNotify) nemo_bookmark_metadata_free); + + g_free (contents); + g_strfreev (lines); + + g_task_return_pointer (task, new_list, NULL); + } else { + g_task_return_error (task, error); + } + + g_object_unref (file); } static void -save_file_finish (NemoBookmarkList *bookmarks, - GObject *source, - GAsyncResult *res) +load_files_async (NemoBookmarkList *self, + GAsyncReadyCallback callback) { - GError *error = NULL; - GFile *file; + GTask *task; - g_file_replace_contents_finish (G_FILE (source), - res, NULL, &error); + g_object_ref (self); - if (error != NULL) { - g_warning ("Unable to replace contents of the bookmarks file: %s", - error->message); - g_error_free (error); - } + task = g_task_new (self, NULL, callback, self); + g_task_run_in_thread (task, load_files_thread); - if (geteuid () == 0) { - struct passwd *pwent; - pwent = gnome_desktop_get_session_user_pwent (); + g_object_unref (task); +} + +static void +save_bookmark_metadata_file (NemoBookmarkList *list) +{ /* Part of save_files thread */ + GError *error = NULL; + GKeyFile *kfile = g_key_file_new (); - gchar *bookmarks_path = g_file_get_path (G_FILE (source)); + gchar *filename; - if (g_strcmp0 (pwent->pw_dir, g_get_home_dir ()) == 0) { - G_GNUC_UNUSED int res; + filename = g_build_filename (g_get_user_config_dir (), + "nemo", + "bookmark-metadata", + NULL); + + GFile *file = g_file_new_for_path (filename); + GFile *parent = g_file_get_parent (file); + + gchar *path = g_file_get_path (parent); + g_mkdir_with_parents (path, 0700); + + g_free (path); + g_object_unref (parent); + + GList *ptr; + + for (ptr = list->list; ptr != NULL; ptr = ptr->next) { + NemoBookmark *bookmark = NEMO_BOOKMARK (ptr->data); + + gchar *icon_uri = NULL; + gchar *icon_name = NULL; + GList *emblems = NULL; + + nemo_bookmark_get_metadata (bookmark, &icon_uri, &icon_name, &emblems); + + if (icon_uri) + g_key_file_set_string (kfile, + nemo_bookmark_get_name (bookmark), + KEY_ICON_URI, + icon_uri); + + if (icon_name) + g_key_file_set_string (kfile, + nemo_bookmark_get_name (bookmark), + KEY_ICON_NAME, + icon_name); + + if (emblems) { + GList *iter; + + GPtrArray *emblem_array = g_ptr_array_new (); + + for (iter = emblems; iter != NULL; iter = iter->next) { + g_ptr_array_add (emblem_array, g_strdup (iter->data)); + } + + g_ptr_array_add (emblem_array, NULL); + + gchar **strv = (char **) g_ptr_array_free (emblem_array, FALSE); + + g_key_file_set_string_list (kfile, + nemo_bookmark_get_name (bookmark), + KEY_ICON_EMBLEMS, + (const gchar * const *) strv, + g_strv_length (strv)); - res = chown (bookmarks_path, pwent->pw_uid, pwent->pw_gid); + g_strfreev (strv); } + + g_free (icon_uri); + g_free (icon_name); + g_list_free_full (emblems, g_free); } - file = nemo_bookmark_list_get_file (); + if (g_key_file_save_to_file (kfile, + filename, + &error)) { + ensure_proper_file_permissions (file); + } else { + g_warning ("Could not save bookmark metadata file: %s\n", error->message); + g_error_free (error); + } - /* re-enable bookmark file monitoring */ - bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL); - g_file_monitor_set_rate_limit (bookmarks->monitor, 1000); - g_signal_connect (bookmarks->monitor, "changed", - G_CALLBACK (bookmark_monitor_changed_cb), bookmarks); + g_key_file_free (kfile); + g_object_unref (file); +} - g_object_unref (file); +static void +save_files_finish (NemoBookmarkList *bookmarks, + GObject *source, + GAsyncResult *res) +{ + GError *error = NULL; + + g_task_propagate_boolean (G_TASK (res), &error); + + if (error != NULL) { + g_warning ("Unable to replace contents of the bookmarks file: %s", + error->message); + g_error_free (error); + } + + GFile *file = nemo_bookmark_list_get_file (); + + /* re-enable bookmark file monitoring */ + bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL); + g_file_monitor_set_rate_limit (bookmarks->monitor, 1000); + g_signal_connect (bookmarks->monitor, "changed", + G_CALLBACK (bookmark_monitor_changed_cb), + bookmarks); + + g_object_unref (file); } static void -save_file_async (NemoBookmarkList *bookmarks, - GAsyncReadyCallback callback) +save_files_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { - GFile *file; - GList *l; - GString *bookmark_string; - GFile *parent; - char *path; - - /* temporarily disable bookmark file monitoring when writing file */ - if (bookmarks->monitor != NULL) { - g_file_monitor_cancel (bookmarks->monitor); - bookmarks->monitor = NULL; - } + NemoBookmarkList *bookmarks = NEMO_BOOKMARK_LIST (source_object); - file = nemo_bookmark_list_get_file (); - /* FIXME: `bookmark_string` is currently leaked as `g_string_free()` is - * never called. - */ - bookmark_string = g_string_new (NULL); - - for (l = bookmarks->list; l; l = l->next) { - NemoBookmark *bookmark; - - bookmark = NEMO_BOOKMARK (l->data); - - /* make sure we save label if it has one for compatibility with GTK 2.7 and 2.8 */ - if (nemo_bookmark_get_has_custom_name (bookmark)) { - const char *label; - char *uri; - label = nemo_bookmark_get_name (bookmark); - uri = nemo_bookmark_get_uri (bookmark); - g_string_append_printf (bookmark_string, - "%s %s\n", uri, label); - g_free (uri); - } else { - char *uri; - uri = nemo_bookmark_get_uri (bookmark); - g_string_append_printf (bookmark_string, "%s\n", uri); - g_free (uri); - } - } + GFile *file; + GList *l; + GString *bookmark_string; + GFile *parent; + char *path; + + /* temporarily disable bookmark file monitoring when writing file */ + if (bookmarks->monitor != NULL) { + g_file_monitor_cancel (bookmarks->monitor); + bookmarks->monitor = NULL; + } - /* keep the bookmark list alive */ - g_object_ref (bookmarks); + file = nemo_bookmark_list_get_file (); - parent = g_file_get_parent (file); - path = g_file_get_path (parent); - g_mkdir_with_parents (path, 0700); - g_free (path); - g_object_unref (parent); + bookmark_string = g_string_new (NULL); - g_file_replace_contents_async (file, bookmark_string->str, - bookmark_string->len, NULL, - FALSE, 0, NULL, callback, - bookmarks); + for (l = bookmarks->list; l; l = l->next) { + NemoBookmark *bookmark; - g_object_unref (file); + bookmark = NEMO_BOOKMARK (l->data); + + const char *label; + char *uri; + label = nemo_bookmark_get_name (bookmark); + uri = nemo_bookmark_get_uri (bookmark); + g_string_append_printf (bookmark_string, + "%s %s\n", uri, label); + g_free (uri); + } + + parent = g_file_get_parent (file); + path = g_file_get_path (parent); + g_mkdir_with_parents (path, 0700); + g_free (path); + g_object_unref (parent); + + GError *error = NULL; + + if (g_file_replace_contents (file, + bookmark_string->str, + bookmark_string->len, + NULL, + FALSE, + 0, + NULL, + NULL, + &error)) { + ensure_proper_file_permissions (file); + g_task_return_boolean (task, TRUE); + } else { + g_task_return_error (task, error); + } + + g_string_free (bookmark_string, TRUE); + + g_object_unref (file); + + save_bookmark_metadata_file (bookmarks); +} + +static void +save_files_async (NemoBookmarkList *self, + GAsyncReadyCallback callback) +{ + GTask *task; + + g_object_ref (self); + + task = g_task_new (self, NULL, callback, self); + g_task_run_in_thread (task, save_files_thread); + + g_object_unref (task); } static void process_next_op (NemoBookmarkList *bookmarks); static void -op_processed_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) +op_processed_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { NemoBookmarkList *self = user_data; int op; @@ -781,9 +1037,9 @@ op_processed_cb (GObject *source, op = GPOINTER_TO_INT (g_queue_pop_tail (self->pending_ops)); if (op == LOAD_JOB) { - load_file_finish (self, source, res); + load_files_finish (self, source, res); } else { - save_file_finish (self, source, res); + save_files_finish (self, source, res); } if (!g_queue_is_empty (self->pending_ops)) { @@ -802,9 +1058,9 @@ process_next_op (NemoBookmarkList *bookmarks) op = GPOINTER_TO_INT (g_queue_peek_tail (bookmarks->pending_ops)); if (op == LOAD_JOB) { - load_file_async (bookmarks, op_processed_cb); + load_files_async (bookmarks, op_processed_cb); } else { - save_file_async (bookmarks, op_processed_cb); + save_files_async (bookmarks, op_processed_cb); } } @@ -883,3 +1139,4 @@ nemo_bookmark_list_set_window_geometry (NemoBookmarkList *bookmarks, nemo_bookmark_list_save_file (bookmarks); } + diff --git a/src/nemo-bookmark-list.h b/src/nemo-bookmark-list.h index da049a692..1a322552c 100644 --- a/src/nemo-bookmark-list.h +++ b/src/nemo-bookmark-list.h @@ -53,6 +53,8 @@ struct NemoBookmarkList { GFileMonitor *monitor; GQueue *pending_ops; GVolumeMonitor *volume_monitor; + + guint idle_notify_id; }; struct NemoBookmarkListClass { diff --git a/src/nemo-bookmarks-window.c b/src/nemo-bookmarks-window.c index 1ce1ac7fc..686b783c6 100644 --- a/src/nemo-bookmarks-window.c +++ b/src/nemo-bookmarks-window.c @@ -827,7 +827,7 @@ update_bookmark_from_text (void) bookmark = nemo_bookmark_new (location, name_text_changed ? gtk_entry_get_text (GTK_ENTRY (name_field)) : NULL, - NULL); + NULL, NULL); g_object_unref (location); diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 6f94e6cb5..0fef0d481 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -1776,7 +1776,7 @@ bookmarks_drop_uris (NemoPlacesSidebar *sidebar, location = g_file_new_for_uri (uri); nemo_file_unref (file); - bookmark = nemo_bookmark_new (location, NULL, NULL); + bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { if (position < sidebar->bookmark_breakpoint || @@ -2515,7 +2515,7 @@ add_bookmark (NemoPlacesSidebar *sidebar) } location = g_file_new_for_uri (uri); - bookmark = nemo_bookmark_new (location, NULL, NULL); + bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { nemo_bookmark_list_append (sidebar->bookmarks, bookmark); diff --git a/src/nemo-window-bookmarks.c b/src/nemo-window-bookmarks.c index f00f9a343..054e8cd46 100644 --- a/src/nemo-window-bookmarks.c +++ b/src/nemo-window-bookmarks.c @@ -374,6 +374,7 @@ update_bookmarks (NemoWindow *window) /* append new set of bookmarks */ bookmark_count = nemo_bookmark_list_length (bookmarks); + for (index = 0; index < bookmark_count; ++index) { bookmark = nemo_bookmark_list_item_at (bookmarks, index); diff --git a/src/nemo-window-manage-views.c b/src/nemo-window-manage-views.c index 9b358c238..143242e02 100644 --- a/src/nemo-window-manage-views.c +++ b/src/nemo-window-manage-views.c @@ -124,7 +124,7 @@ set_displayed_location (NemoWindowSlot *slot, GFile *location) slot->last_location_bookmark = slot->current_location_bookmark; slot->current_location_bookmark = (location == NULL) ? - NULL : nemo_bookmark_new (location, NULL, NULL); + NULL : nemo_bookmark_new (location, NULL, NULL, NULL); } }