[nautilus/wip/alexpandelea/tags] implement tags



commit 5d05180eba0adbabc84b2d9a33f4a3e97f01274b
Author: Alexandru Pandelea <alexandru pandelea gmail com>
Date:   Wed Jun 28 17:47:41 2017 +0100

    implement tags

 src/meson.build                                    |   10 +-
 src/nautilus-application.c                         |   25 +
 src/nautilus-bookmark-list.c                       |    5 +
 src/nautilus-column-utilities.c                    |   12 +
 src/nautilus-directory.c                           |    1 +
 src/nautilus-favorite-directory.c                  |  577 ++++++++
 src/nautilus-favorite-directory.h                  |   35 +
 src/nautilus-file-utilities.c                      |   18 +-
 src/nautilus-file-utilities.h                      |    1 +
 src/nautilus-file.c                                |   86 ++
 src/nautilus-file.h                                |    2 +
 src/nautilus-files-view.c                          |  128 ++
 src/nautilus-files-view.h                          |    4 +
 src/nautilus-list-view-private.h                   |    4 +
 src/nautilus-list-view.c                           |  186 +++-
 src/nautilus-list-view.h                           |    1 +
 src/nautilus-pathbar.c                             |   12 +-
 src/nautilus-query.c                               |   19 +
 src/nautilus-query.h                               |    4 +
 src/nautilus-search-directory.c                    |    1 +
 src/nautilus-search-engine-model.c                 |   17 +
 src/nautilus-search-engine-simple.c                |   19 +-
 src/nautilus-search-engine-tracker.c               |    5 +
 src/nautilus-search-engine.c                       |    1 +
 src/nautilus-tag-manager.c                         | 1399 ++++++++++++++++++++
 src/nautilus-tag-manager.h                         |  113 ++
 src/nautilus-tag-widget.c                          |  304 +++++
 src/nautilus-tag-widget.h                          |   51 +
 src/nautilus-tags-dialog.c                         |  620 +++++++++
 src/nautilus-tags-dialog.h                         |   39 +
 src/nautilus-window.c                              |   14 +
 src/resources/nautilus.gresource.xml               |    1 +
 .../ui/nautilus-files-view-context-menus.ui        |   17 +
 src/resources/ui/nautilus-tags-dialog.ui           |  157 +++
 src/resources/ui/nautilus-window.ui                |    2 +
 35 files changed, 3870 insertions(+), 20 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 83723b9..2ca2e84 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -264,7 +264,15 @@ if get_option ('enable-tracker')
                             'nautilus-batch-rename-utilities.c',
                             'nautilus-batch-rename-utilities.h',
                             'nautilus-search-engine-tracker.c',
-                            'nautilus-search-engine-tracker.h']
+                            'nautilus-search-engine-tracker.h',
+                            'nautilus-tags-dialog.c',
+                            'nautilus-tags-dialog.h',
+                            'nautilus-tag-manager.c',
+                            'nautilus-tag-manager.h',
+                            'nautilus-tag-widget.c',
+                            'nautilus-tag-widget.h',
+                            'nautilus-favorite-directory.c',
+                            'nautilus-favorite-directory.h']
 endif
 
 nautilus_deps = [glib,
diff --git a/src/nautilus-application.c b/src/nautilus-application.c
index 77443f5..108319f 100644
--- a/src/nautilus-application.c
+++ b/src/nautilus-application.c
@@ -39,6 +39,7 @@
 #include "nautilus-window.h"
 #include "nautilus-window-slot.h"
 #include "nautilus-preferences-window.h"
+#include "nautilus-tag-manager.h"
 
 #include "nautilus-directory-private.h"
 #include "nautilus-file-utilities.h"
@@ -80,6 +81,11 @@ typedef struct
     GHashTable *notifications;
 
     NautilusFileUndoManager *undo_manager;
+
+    NautilusTagManager *tag_manager;
+    GCancellable *tag_manager_tags_cancellable;
+    GCancellable *tag_manager_notifier_cancellable;
+    GCancellable *tag_manager_favorite_cancellable;
 } NautilusApplicationPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (NautilusApplication, nautilus_application, GTK_TYPE_APPLICATION);
@@ -612,6 +618,17 @@ nautilus_application_finalize (GObject *object)
 
     g_clear_object (&priv->undo_manager);
 
+    g_clear_object (&priv->tag_manager);
+
+    g_cancellable_cancel (priv->tag_manager_tags_cancellable);
+    g_clear_object (&priv->tag_manager_tags_cancellable);
+
+    g_cancellable_cancel (priv->tag_manager_notifier_cancellable);
+    g_clear_object (&priv->tag_manager_notifier_cancellable);
+
+    g_cancellable_cancel (priv->tag_manager_favorite_cancellable);
+    g_clear_object (&priv->tag_manager_favorite_cancellable);
+
     G_OBJECT_CLASS (nautilus_application_parent_class)->finalize (object);
 }
 
@@ -1077,6 +1094,14 @@ nautilus_application_init (NautilusApplication *self)
 
     priv->undo_manager = nautilus_file_undo_manager_new ();
 
+    priv->tag_manager_tags_cancellable = g_cancellable_new ();
+    priv->tag_manager_notifier_cancellable = g_cancellable_new ();
+    priv->tag_manager_favorite_cancellable = g_cancellable_new ();
+
+    priv->tag_manager = nautilus_tag_manager_new (priv->tag_manager_tags_cancellable,
+                                                  priv->tag_manager_notifier_cancellable,
+                                                  priv->tag_manager_favorite_cancellable);
+
     g_application_add_main_option_entries (G_APPLICATION (self), options);
 
     nautilus_ensure_extension_points ();
diff --git a/src/nautilus-bookmark-list.c b/src/nautilus-bookmark-list.c
index d7a0ea0..926d949 100644
--- a/src/nautilus-bookmark-list.c
+++ b/src/nautilus-bookmark-list.c
@@ -643,6 +643,11 @@ nautilus_bookmark_list_can_bookmark_location (NautilusBookmarkList *list,
         return FALSE;
     }
 
+    if (nautilus_is_favorite_directory (location))
+    {
+        return FALSE;
+    }
+
     bookmark = nautilus_bookmark_new (location, NULL);
     is_builtin = nautilus_bookmark_get_is_builtin (bookmark);
     g_object_unref (bookmark);
diff --git a/src/nautilus-column-utilities.c b/src/nautilus-column-utilities.c
index 1a2a092..4a25722 100644
--- a/src/nautilus-column-utilities.c
+++ b/src/nautilus-column-utilities.c
@@ -41,6 +41,7 @@ static const char *default_column_order[] =
     "date_modified_with_time",
     "date_modified",
     "date_accessed",
+    "favorite",
     "recency",
     NULL
 };
@@ -71,6 +72,17 @@ get_builtin_columns (void)
                                            "label", _("Type"),
                                            "description", _("The type of the file."),
                                            NULL));
+
+    columns = g_list_append (columns,
+                             g_object_new (NAUTILUS_TYPE_COLUMN,
+                                           "name", "favorite",
+                                           "attribute", "favorite",
+                                           "label", _("Star"),
+                                           "description", _("Shows if file is favorite."),
+                                           "default-sort-order", GTK_SORT_DESCENDING,
+                                           "xalign", 0.5,
+                                           NULL));
+
     columns = g_list_append (columns,
                              g_object_new (NAUTILUS_TYPE_COLUMN,
                                            "name", "date_modified",
diff --git a/src/nautilus-directory.c b/src/nautilus-directory.c
index c2ff911..647cee8 100644
--- a/src/nautilus-directory.c
+++ b/src/nautilus-directory.c
@@ -27,6 +27,7 @@
 #include "nautilus-file-private.h"
 #include "nautilus-file-utilities.h"
 #include "nautilus-search-directory.h"
+#include "nautilus-favorite-directory.h"
 #include "nautilus-search-directory-file.h"
 #include "nautilus-vfs-file.h"
 #include "nautilus-global-preferences.h"
diff --git a/src/nautilus-favorite-directory.c b/src/nautilus-favorite-directory.c
new file mode 100644
index 0000000..dd76db3
--- /dev/null
+++ b/src/nautilus-favorite-directory.c
@@ -0,0 +1,577 @@
+/* nautilus-favorite-directory.c
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-favorite-directory.h"
+#include "nautilus-tag-manager.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-directory-private.h"
+#include <glib/gi18n.h>
+
+struct _NautilusFavoriteDirectory
+{
+    NautilusDirectory parent;
+
+    NautilusTagManager *tag_manager;
+    GList *files;
+
+    GList *monitor_list;
+    GList *callback_list;
+    GList *pending_callback_list;
+};
+
+typedef struct
+{
+    gboolean monitor_hidden_files;
+    NautilusFileAttributes monitor_attributes;
+
+    gconstpointer client;
+} FavoriteMonitor;
+
+typedef struct
+{
+    NautilusFavoriteDirectory *favorite_directory;
+
+    NautilusDirectoryCallback callback;
+    gpointer callback_data;
+
+    NautilusFileAttributes wait_for_attributes;
+    gboolean wait_for_file_list;
+    GList *file_list;
+} FavoriteCallback;
+
+G_DEFINE_TYPE_WITH_CODE (NautilusFavoriteDirectory, nautilus_favorite_directory, NAUTILUS_TYPE_DIRECTORY,
+                         nautilus_ensure_extension_points ();
+                         g_io_extension_point_implement (NAUTILUS_DIRECTORY_PROVIDER_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         NAUTILUS_FAVORITE_DIRECTORY_PROVIDER_NAME,
+                                                         0));
+
+static void
+file_changed (NautilusFile              *file,
+              NautilusFavoriteDirectory *favorite)
+{
+    GList list;
+
+    list.data = file;
+    list.next = NULL;
+
+    nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (favorite), &list);
+}
+
+static void
+nautilus_favorite_directory_update_files (NautilusFavoriteDirectory *self)
+{
+    GList *l;
+    GList *tmp_l;
+    GList *new_favorite_files;
+    GList *monitor_list;
+    FavoriteMonitor *monitor;
+    NautilusFile *file;
+    GHashTable *uri_table;
+    GList *files_added;
+    GList *files_removed;
+    gchar *uri;
+
+    files_added = NULL;
+    files_removed = NULL;
+
+    uri_table = g_hash_table_new_full (g_str_hash,
+                                       g_str_equal,
+                                       (GDestroyNotify) g_free,
+                                       NULL);
+
+    for (l = self->files; l != NULL; l = l->next)
+    {
+        g_hash_table_add (uri_table, nautilus_file_get_uri (NAUTILUS_FILE (l->data)));
+    }
+
+    new_favorite_files = nautilus_tag_manager_get_favorite_files (self->tag_manager);
+
+    for (l = new_favorite_files; l != NULL; l = l->next)
+    {
+        if (!g_hash_table_contains (uri_table, l->data))
+        {
+            file = nautilus_file_get_by_uri ((gchar*) l->data);
+
+            for (monitor_list = self->monitor_list; monitor_list; monitor_list = monitor_list->next)
+            {
+                monitor = monitor_list->data;
+
+                /* Add monitors */
+                nautilus_file_monitor_add (file, monitor, monitor->monitor_attributes);
+            }
+
+            g_signal_connect (file, "changed", G_CALLBACK (file_changed), self);
+
+            files_added = g_list_prepend (files_added, file);
+        }
+    }
+
+    l = self->files;
+    while (l != NULL)
+    {
+        uri = nautilus_file_get_uri (NAUTILUS_FILE (l->data));
+
+        if (!nautilus_tag_manager_file_is_favorite (self->tag_manager, uri))
+        {
+            files_removed = g_list_prepend (files_removed,
+                                            nautilus_file_ref (NAUTILUS_FILE (l->data)));
+
+            g_signal_handlers_disconnect_by_func (NAUTILUS_FILE (l->data),
+                                                  file_changed,
+                                                  self);
+
+            /* Remove monitors */
+            for (monitor_list = self->monitor_list; monitor_list;
+                 monitor_list = monitor_list->next)
+            {
+                monitor = monitor_list->data;
+                nautilus_file_monitor_remove (NAUTILUS_FILE (l->data), monitor);
+            }
+
+            if (l == self->files)
+            {
+                self->files = g_list_delete_link (self->files, l);
+                l = self->files;
+            }
+            else
+            {
+                tmp_l = l->prev;
+                self->files = g_list_delete_link (self->files, l);
+                l = tmp_l->next;
+            }
+        }
+        else
+        {
+            l = l->next;
+        }
+
+        g_free (uri);
+    }
+
+    if (files_added)
+    {
+        nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (self), files_added);
+
+        for (l = files_added; l != NULL; l = l->next)
+        {
+            self->files = g_list_prepend (self->files, nautilus_file_ref (NAUTILUS_FILE (l->data)));
+        }
+    }
+
+    if (files_removed)
+    {
+        nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), files_removed);
+    }
+
+    nautilus_file_list_free (files_added);
+    nautilus_file_list_free (files_removed);
+    g_hash_table_destroy (uri_table);
+}
+
+static void
+on_favorites_files_changed (NautilusTagManager        *tag_manager,
+                            NautilusFavoriteDirectory *self)
+{
+    nautilus_favorite_directory_update_files (self);
+}
+
+static gboolean
+real_contains_file (NautilusDirectory *directory,
+                                           NautilusFile      *file)
+{
+    NautilusFavoriteDirectory *self;
+    g_autofree gchar *uri = NULL;
+
+    self = NAUTILUS_FAVORITE_DIRECTORY (directory);
+
+    uri = nautilus_file_get_uri (file);
+
+    return nautilus_tag_manager_file_is_favorite (self->tag_manager, uri);
+}
+
+static gboolean
+real_is_editable (NautilusDirectory *directory)
+{
+    return FALSE;
+}
+
+static void
+real_force_reload (NautilusDirectory *directory)
+{
+    nautilus_favorite_directory_update_files (NAUTILUS_FAVORITE_DIRECTORY (directory));
+}
+
+static void
+real_call_when_ready (NautilusDirectory         *directory,
+                      NautilusFileAttributes     file_attributes,
+                      gboolean                   wait_for_file_list,
+                      NautilusDirectoryCallback  callback,
+                      gpointer                   callback_data)
+{
+    GList *file_list;
+    NautilusFavoriteDirectory *favorite;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (directory);
+
+    file_list = nautilus_file_list_copy (favorite->files);
+
+    callback (NAUTILUS_DIRECTORY (directory),
+                                  file_list,
+                                  callback_data);
+}
+
+static gboolean
+real_are_all_files_seen (NautilusDirectory *directory)
+{
+    return TRUE;
+}
+
+static void
+real_file_monitor_add (NautilusDirectory         *directory,
+                       gconstpointer              client,
+                       gboolean                   monitor_hidden_files,
+                       NautilusFileAttributes     file_attributes,
+                       NautilusDirectoryCallback  callback,
+                       gpointer                   callback_data)
+{
+    GList *list;
+    FavoriteMonitor *monitor;
+    NautilusFavoriteDirectory *favorite;
+    NautilusFile *file;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (directory);
+
+    monitor = g_new0 (FavoriteMonitor, 1);
+    monitor->monitor_hidden_files = monitor_hidden_files;
+    monitor->monitor_attributes = file_attributes;
+    monitor->client = client;
+
+    favorite->monitor_list = g_list_prepend (favorite->monitor_list, monitor);
+
+    if (callback != NULL)
+    {
+        (*callback)(directory, favorite->files, callback_data);
+    }
+
+    for (list = favorite->files; list != NULL; list = list->next)
+    {
+        file = list->data;
+
+        /* Add monitors */
+        nautilus_file_monitor_add (file, monitor, file_attributes);
+    }
+}
+
+static void
+favorite_monitor_destroy (FavoriteMonitor           *monitor,
+                          NautilusFavoriteDirectory *favorite)
+{
+    GList *l;
+    NautilusFile *file;
+
+    for (l = favorite->files; l != NULL; l = l->next)
+    {
+        file = l->data;
+
+        nautilus_file_monitor_remove (file, monitor);
+    }
+
+    g_free (monitor);
+}
+
+static void
+real_monitor_remove (NautilusDirectory *directory,
+                     gconstpointer      client)
+{
+    NautilusFavoriteDirectory *favorite;
+    FavoriteMonitor *monitor;
+    GList *list;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (directory);
+
+    for (list = favorite->monitor_list; list != NULL; list = list->next)
+    {
+        monitor = list->data;
+
+        if (monitor->client == client)
+        {
+            favorite->monitor_list = g_list_delete_link (favorite->monitor_list, list);
+
+            favorite_monitor_destroy (monitor, favorite);
+
+            break;
+        }
+    }
+}
+
+static gboolean
+real_handles_location (GFile *location)
+{
+    g_autofree gchar *uri = NULL;
+
+    uri = g_file_get_uri (location);
+
+    if (g_strcmp0 (uri, "favorites:///") == 0)
+    {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static FavoriteCallback*
+favorite_callback_find_pending (NautilusFavoriteDirectory *favorite,
+                                NautilusDirectoryCallback  callback,
+                                gpointer                   callback_data)
+{
+    FavoriteCallback *favorite_callback;
+    GList *list;
+
+    for (list = favorite->pending_callback_list; list != NULL; list = list->next)
+    {
+        favorite_callback = list->data;
+
+        if (favorite_callback->callback == callback &&
+            favorite_callback->callback_data == callback_data)
+        {
+            return favorite_callback;
+        }
+    }
+
+    return NULL;
+}
+
+static FavoriteCallback*
+favorite_callback_find (NautilusFavoriteDirectory *favorite,
+                        NautilusDirectoryCallback  callback,
+                        gpointer                   callback_data)
+{
+    FavoriteCallback *favorite_callback;
+    GList *list;
+
+    for (list = favorite->callback_list; list != NULL; list = list->next)
+    {
+        favorite_callback = list->data;
+
+        if (favorite_callback->callback == callback &&
+            favorite_callback->callback_data == callback_data)
+        {
+            return favorite_callback;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+favorite_callback_destroy (FavoriteCallback *favorite_callback)
+{
+    nautilus_file_list_free (favorite_callback->file_list);
+
+    g_free (favorite_callback);
+}
+
+static void
+real_cancel_callback (NautilusDirectory        *directory,
+                      NautilusDirectoryCallback callback,
+                      gpointer                  callback_data)
+{
+    NautilusFavoriteDirectory *favorite;
+    FavoriteCallback *favorite_callback;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (directory);
+    favorite_callback = favorite_callback_find (favorite, callback, callback_data);
+
+    if (favorite_callback)
+    {
+        favorite->callback_list = g_list_remove (favorite->callback_list, favorite_callback);
+
+        favorite_callback_destroy (favorite_callback);
+
+        return;
+    }
+
+    /* Check for a pending callback */
+    favorite_callback = favorite_callback_find_pending (favorite, callback, callback_data);
+
+    if (favorite_callback)
+    {
+        favorite->pending_callback_list = g_list_remove (favorite->pending_callback_list, favorite_callback);
+
+        favorite_callback_destroy (favorite_callback);
+    }
+}
+
+static GList*
+real_get_file_list (NautilusDirectory *directory)
+{
+    NautilusFavoriteDirectory *favorite;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (directory);
+
+    return nautilus_file_list_copy (favorite->files);
+}
+
+static void
+nautilus_favorite_directory_set_files (NautilusFavoriteDirectory *self)
+{
+    GList *favorite_files;
+    NautilusFile *file;
+    GList *l;
+    GList *file_list;
+    FavoriteMonitor *monitor;
+    GList *monitor_list;
+
+    file_list = NULL;
+
+    favorite_files = nautilus_tag_manager_get_favorite_files (self->tag_manager);
+
+    for (l = favorite_files; l != NULL; l = l->next)
+    {
+        file = nautilus_file_get_by_uri ((gchar*) l->data);
+
+        g_signal_connect (file, "changed", G_CALLBACK (file_changed), self);
+
+        for (monitor_list = self->monitor_list; monitor_list; monitor_list = monitor_list->next)
+        {
+            monitor = monitor_list->data;
+
+            /* Add monitors */
+            nautilus_file_monitor_add (file, monitor, monitor->monitor_attributes);
+        }
+
+        file_list = g_list_prepend (file_list, file);
+    }
+
+    nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (self), file_list);
+
+    self->files = file_list;
+}
+
+static void
+nautilus_favorite_directory_finalize (GObject *object)
+{
+    NautilusFavoriteDirectory *self;
+
+    self = NAUTILUS_FAVORITE_DIRECTORY (object);
+
+    g_signal_handlers_disconnect_by_func (self->tag_manager,
+                                          on_favorites_files_changed,
+                                          self);
+
+    g_object_unref (self->tag_manager);
+    nautilus_file_list_free (self->files);
+
+    G_OBJECT_CLASS (nautilus_favorite_directory_parent_class)->finalize (object);
+}
+
+static void
+nautilus_favorite_directory_dispose (GObject *object)
+{
+    NautilusFavoriteDirectory *favorite;
+    GList *l;
+    GList *monitor_list;
+    FavoriteMonitor *monitor;
+    NautilusFile *file;
+
+    favorite = NAUTILUS_FAVORITE_DIRECTORY (object);
+
+    /* Remove file connections */
+    for (l = favorite->files; l != NULL; l = l->next)
+    {
+        file = l->data;
+
+        /* Disconnect change handler */
+        g_signal_handlers_disconnect_by_func (file, file_changed, favorite);
+
+        /* Remove monitors */
+        for (monitor_list = favorite->monitor_list; monitor_list;
+             monitor_list = monitor_list->next)
+        {
+            monitor = monitor_list->data;
+            nautilus_file_monitor_remove (file, monitor);
+        }
+    }
+
+    /* Remove search monitors */
+    if (favorite->monitor_list)
+    {
+        for (l = favorite->monitor_list; l != NULL; l = l->next)
+        {
+            favorite_monitor_destroy ((FavoriteMonitor*) l->data, favorite);
+        }
+
+        g_list_free (favorite->monitor_list);
+        favorite->monitor_list = NULL;
+    }
+
+    G_OBJECT_CLASS (nautilus_favorite_directory_parent_class)->dispose (object);
+}
+
+static void
+nautilus_favorite_directory_class_init (NautilusFavoriteDirectoryClass *klass)
+{
+    GObjectClass *oclass;
+    NautilusDirectoryClass *directory_class;
+
+    oclass = G_OBJECT_CLASS (klass);
+    directory_class = NAUTILUS_DIRECTORY_CLASS (klass);
+
+    oclass->finalize = nautilus_favorite_directory_finalize;
+    oclass->dispose = nautilus_favorite_directory_dispose;
+
+    directory_class->handles_location = real_handles_location;
+    directory_class->contains_file = real_contains_file;
+    directory_class->is_editable = real_is_editable;
+    directory_class->force_reload = real_force_reload;
+    directory_class->call_when_ready = real_call_when_ready;
+    directory_class->are_all_files_seen = real_are_all_files_seen;
+    directory_class->file_monitor_add = real_file_monitor_add;
+    directory_class->file_monitor_remove = real_monitor_remove;
+    directory_class->cancel_callback = real_cancel_callback;
+    directory_class->get_file_list = real_get_file_list;
+}
+
+NautilusFavoriteDirectory*
+nautilus_favorite_directory_new ()
+{
+    NautilusFavoriteDirectory *self;
+
+    self = g_object_new (NAUTILUS_TYPE_FAVORITE_DIRECTORY, NULL);
+
+    return self;
+}
+
+static void
+nautilus_favorite_directory_init (NautilusFavoriteDirectory *self)
+{
+    NautilusTagManager *tag_manager;
+
+    tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
+    g_signal_connect (tag_manager,
+                      "favorites-changed",
+                      (GCallback) on_favorites_files_changed,
+                      self);
+
+    self->tag_manager = tag_manager;
+
+    nautilus_favorite_directory_set_files (self);
+
+}
diff --git a/src/nautilus-favorite-directory.h b/src/nautilus-favorite-directory.h
new file mode 100644
index 0000000..3da343d
--- /dev/null
+++ b/src/nautilus-favorite-directory.h
@@ -0,0 +1,35 @@
+/* nautilus-favorite-directory.h
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_FAVORITE_DIRECTORY_H
+#define NAUTILUS_FAVORITE_DIRECTORY_H
+
+#include "nautilus-directory.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_FAVORITE_DIRECTORY_PROVIDER_NAME "favorite-directory-provider"
+
+#define NAUTILUS_TYPE_FAVORITE_DIRECTORY (nautilus_favorite_directory_get_type ())
+
+G_DECLARE_FINAL_TYPE (NautilusFavoriteDirectory, nautilus_favorite_directory, NAUTILUS, FAVORITE_DIRECTORY, 
NautilusDirectory);
+NautilusFavoriteDirectory* nautilus_favorite_directory_new ();
+
+G_END_DECLS
+
+#endif
diff --git a/src/nautilus-file-utilities.c b/src/nautilus-file-utilities.c
index 59add50..ee60430 100644
--- a/src/nautilus-file-utilities.c
+++ b/src/nautilus-file-utilities.c
@@ -322,7 +322,10 @@ nautilus_compute_title_for_location (GFile *location)
         {
             title = g_strdup (_("Other Locations"));
         }
-        else
+        else if (nautilus_file_is_favorite_location (file))
+        {
+            title = g_strdup (_("Favorite Files"));
+        }
         {
             title = nautilus_file_get_description (file);
 
@@ -593,6 +596,19 @@ nautilus_is_search_directory (GFile *dir)
 }
 
 gboolean
+nautilus_is_favorite_directory (GFile *dir)
+{
+    g_autofree gchar *uri = NULL;
+
+    uri = g_file_get_uri (dir);
+
+    if (g_strcmp0 (uri, "favorites:///") == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+gboolean
 nautilus_is_other_locations_directory (GFile *dir)
 {
     g_autofree gchar *uri = NULL;
diff --git a/src/nautilus-file-utilities.h b/src/nautilus-file-utilities.h
index 29ad7b3..c0d5529 100644
--- a/src/nautilus-file-utilities.h
+++ b/src/nautilus-file-utilities.h
@@ -46,6 +46,7 @@ gboolean nautilus_is_home_directory_file             (GFile *dir,
                                                      const char *filename);
 gboolean nautilus_is_in_system_dir                   (GFile *location);
 gboolean nautilus_is_search_directory                (GFile *dir);
+gboolean nautilus_is_favorite_directory              (GFile *dir);
 gboolean nautilus_is_other_locations_directory       (GFile *dir);
 GMount * nautilus_get_mounted_mount_for_root         (GFile *location);
 
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index daa4009..df2b543 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -40,6 +40,7 @@
 #include "nautilus-vfs-file.h"
 #include "nautilus-file-undo-operations.h"
 #include "nautilus-file-undo-manager.h"
+#include "nautilus-tag-manager.h"
 #include <eel/eel-debug.h>
 #include <eel/eel-glib-extensions.h>
 #include <eel/eel-gtk-extensions.h>
@@ -129,6 +130,7 @@ static NautilusSpeedTradeoffValue show_directory_item_count;
 static GQuark attribute_name_q,
               attribute_size_q,
               attribute_type_q,
+              attribute_favorite_q,
               attribute_detailed_type_q,
               attribute_modification_date_q,
               attribute_date_modified_q,
@@ -3659,6 +3661,42 @@ compare_by_type (NautilusFile *file_1,
     return result;
 }
 
+static int
+compare_by_favorite (NautilusFile *file_1,
+                     NautilusFile *file_2)
+{
+    NautilusTagManager *tag_manager;
+    g_autofree gchar *uri_1 = NULL;
+    g_autofree gchar *uri_2 = NULL;
+    gboolean file_1_is_favorite;
+    gboolean file_2_is_favorite;
+
+    tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
+    uri_1 = nautilus_file_get_uri (file_1);
+    uri_2 = nautilus_file_get_uri (file_2);
+
+    file_1_is_favorite = nautilus_tag_manager_file_is_favorite (tag_manager,
+                                                                uri_1);
+    file_2_is_favorite = nautilus_tag_manager_file_is_favorite (tag_manager,
+                                                                uri_2);
+
+
+    if ((file_1_is_favorite && file_2_is_favorite) ||
+        (!file_1_is_favorite && !file_2_is_favorite))
+    {
+        return 0;
+    }
+    else if (file_1_is_favorite && !file_2_is_favorite)
+    {
+        return -1;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
 static Knowledge
 get_search_relevance (NautilusFile *file,
                       gdouble      *relevance_out)
@@ -3862,6 +3900,16 @@ nautilus_file_compare_for_sort (NautilusFile         *file_1,
             }
             break;
 
+            case NAUTILUS_FILE_SORT_BY_FAVORITE:
+            {
+                result = compare_by_favorite (file_1, file_2);
+                if (result == 0)
+                {
+                    result = compare_by_full_path (file_1, file_2);
+                }
+            }
+            break;
+
             case NAUTILUS_FILE_SORT_BY_MTIME:
             {
                 result = compare_by_time (file_1, file_2, NAUTILUS_DATE_TYPE_MODIFIED);
@@ -3966,6 +4014,13 @@ nautilus_file_compare_for_sort_by_attribute_q   (NautilusFile *file_1,
                                                directories_first,
                                                reversed);
     }
+    else if (attribute == attribute_favorite_q)
+    {
+        return nautilus_file_compare_for_sort (file_1, file_2,
+                                               NAUTILUS_FILE_SORT_BY_FAVORITE,
+                                               directories_first,
+                                               reversed);
+    }
     else if (attribute == attribute_modification_date_q || attribute == attribute_date_modified_q || 
attribute == attribute_date_modified_with_time_q || attribute == attribute_date_modified_full_q)
     {
         return nautilus_file_compare_for_sort (file_1, file_2,
@@ -4557,6 +4612,11 @@ nautilus_file_peek_display_name (NautilusFile *file)
 char *
 nautilus_file_get_display_name (NautilusFile *file)
 {
+    if (nautilus_file_is_other_locations (file))
+        return g_strdup (_("Other Locations"));
+    if (nautilus_file_is_favorite_location (file))
+        return g_strdup (_("Favorite Files"));
+
     return g_strdup (nautilus_file_peek_display_name (file));
 }
 
@@ -8337,6 +8397,31 @@ nautilus_file_is_other_locations (NautilusFile *file)
 }
 
 /**
+ * nautilus_file_is_favorite_location
+ *
+ * Check if this file is the Favorite location.
+ * @file: NautilusFile representing the file in question.
+ *
+ * Returns: TRUE if @file is the Favorite location.
+ *
+ **/
+gboolean
+nautilus_file_is_favorite_location (NautilusFile *file)
+{
+    gboolean is_favorite_location;
+    gchar *uri;
+
+    g_assert (NAUTILUS_IS_FILE (file));
+
+    uri = nautilus_file_get_uri (file);
+    is_favorite_location = g_strcmp0 (uri, "favorites:///") == 0;
+
+    g_free (uri);
+
+    return is_favorite_location;
+}
+
+/**
  * nautilus_file_is_in_admin
  *
  * Check if this file is using admin backend.
@@ -9317,6 +9402,7 @@ nautilus_file_class_init (NautilusFileClass *class)
     attribute_name_q = g_quark_from_static_string ("name");
     attribute_size_q = g_quark_from_static_string ("size");
     attribute_type_q = g_quark_from_static_string ("type");
+    attribute_favorite_q = g_quark_from_static_string ("favorite");
     attribute_detailed_type_q = g_quark_from_static_string ("detailed_type");
     attribute_modification_date_q = g_quark_from_static_string ("modification_date");
     attribute_date_modified_q = g_quark_from_static_string ("date_modified");
diff --git a/src/nautilus-file.h b/src/nautilus-file.h
index c4d1780..210be64 100644
--- a/src/nautilus-file.h
+++ b/src/nautilus-file.h
@@ -55,6 +55,7 @@ typedef enum {
        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
        NAUTILUS_FILE_SORT_BY_SIZE,
        NAUTILUS_FILE_SORT_BY_TYPE,
+       NAUTILUS_FILE_SORT_BY_FAVORITE,
        NAUTILUS_FILE_SORT_BY_MTIME,
         NAUTILUS_FILE_SORT_BY_ATIME,
        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
@@ -218,6 +219,7 @@ gboolean                nautilus_file_is_in_recent                      (Nautilu
 gboolean                nautilus_file_is_in_admin                       (NautilusFile                   
*file);
 gboolean                nautilus_file_is_remote                         (NautilusFile                   
*file);
 gboolean                nautilus_file_is_other_locations                (NautilusFile                   
*file);
+gboolean                nautilus_file_is_favorite_location              (NautilusFile                   
*file);
 gboolean               nautilus_file_is_home                           (NautilusFile                   
*file);
 gboolean                nautilus_file_is_desktop_directory              (NautilusFile                   
*file);
 gboolean                nautilus_file_is_child_of_desktop_directory     (NautilusFile                   
*file);
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 3d56913..60360ab 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -31,6 +31,7 @@
 #ifdef ENABLE_TRACKER
 #include "nautilus-batch-rename-dialog.h"
 #include "nautilus-batch-rename-utilities.h"
+#include "nautilus-tags-dialog.h"
 #endif
 #include "nautilus-error-reporting.h"
 #include "nautilus-file-undo-manager.h"
@@ -44,6 +45,7 @@
 #include "nautilus-window.h"
 #include "nautilus-toolbar.h"
 #include "nautilus-view.h"
+#include "nautilus-tag-manager.h"
 
 #ifdef HAVE_X11_XF86KEYSYM_H
 #include <X11/XF86keysym.h>
@@ -72,6 +74,7 @@
 #include <libnautilus-extension/nautilus-menu-provider.h>
 #include "nautilus-clipboard.h"
 #include "nautilus-search-directory.h"
+#include "nautilus-favorite-directory.h"
 #include "nautilus-directory.h"
 #include "nautilus-dnd.h"
 #include "nautilus-file-attributes.h"
@@ -274,6 +277,9 @@ typedef struct
 
     gulong stop_signal_handler;
     gulong reload_signal_handler;
+
+    GCancellable *favorite_cancellable;
+    NautilusTagManager *tag_manager;
 } NautilusFilesViewPrivate;
 
 typedef struct
@@ -1580,6 +1586,85 @@ action_delete (GSimpleAction *action,
 }
 
 static void
+action_edit_tags (GSimpleAction *action,
+                  GVariant      *state,
+                  gpointer       user_data)
+{
+    NautilusFilesView *view;
+    GtkWidget *dialog;
+    NautilusWindow *window;
+    GList *selection;
+
+    view = NAUTILUS_FILES_VIEW (user_data);
+
+    window = nautilus_files_view_get_window (view);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+
+    dialog = nautilus_tags_dialog_new (selection, window);
+    gtk_widget_show (GTK_WIDGET (dialog));
+}
+
+void
+on_favorite_tags_updated (GObject      *object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+{
+    GTask *task;
+    NautilusFilesView *view;
+
+    view = NAUTILUS_FILES_VIEW (object);
+
+    if (NAUTILUS_IS_LIST_VIEW (view))
+    {
+        nautilus_list_view_redraw_tree_view (view);
+    }
+
+    task = user_data;
+    g_clear_object (&task);
+}
+
+static void
+action_star (GSimpleAction *action,
+             GVariant      *state,
+             gpointer       user_data)
+{
+    NautilusFilesView *view;
+    GList *selection;
+    NautilusFilesViewPrivate *priv;
+
+    view = NAUTILUS_FILES_VIEW (user_data);
+    priv = nautilus_files_view_get_instance_private (view);
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+
+    nautilus_tag_manager_star_files (priv->tag_manager,
+                                     G_OBJECT (view),
+                                     selection,
+                                     on_favorite_tags_updated,
+                                     priv->favorite_cancellable);
+}
+
+static void
+action_unstar (GSimpleAction *action,
+               GVariant      *state,
+               gpointer       user_data)
+{
+    NautilusFilesView *view;
+    GList *selection;
+    NautilusFilesViewPrivate *priv;
+
+    view = NAUTILUS_FILES_VIEW (user_data);
+    priv = nautilus_files_view_get_instance_private (view);
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+
+    nautilus_tag_manager_unstar_files (priv->tag_manager,
+                                       G_OBJECT (view),
+                                       selection,
+                                       on_favorite_tags_updated,
+                                       priv->favorite_cancellable);
+}
+
+static void
 action_restore_from_trash (GSimpleAction *action,
                            GVariant      *state,
                            gpointer       user_data)
@@ -3236,6 +3321,9 @@ nautilus_files_view_finalize (GObject *object)
     g_hash_table_destroy (priv->non_ready_files);
     g_hash_table_destroy (priv->pending_reveal);
 
+    g_cancellable_cancel (priv->favorite_cancellable);
+    g_clear_object (&priv->favorite_cancellable);
+
     G_OBJECT_CLASS (nautilus_files_view_parent_class)->finalize (object);
 }
 
@@ -3484,6 +3572,10 @@ nautilus_files_view_set_location (NautilusView *view,
         set_search_query_internal (files_view, previous_query, base_model);
         g_object_unref (previous_query);
     }
+    else if (NAUTILUS_IS_FAVORITE_DIRECTORY (directory))
+    {
+        load_directory (NAUTILUS_FILES_VIEW (view), directory);
+    }
     else
     {
         load_directory (NAUTILUS_FILES_VIEW (view), directory);
@@ -7009,6 +7101,9 @@ const GActionEntry view_entries[] =
     { "copy-to", action_copy_to},
     { "move-to-trash", action_move_to_trash},
     { "delete-from-trash", action_delete },
+    { "edit-tags", action_edit_tags},
+    { "star", action_star},
+    { "unstar", action_unstar},
     /* We separate the shortcut and the menu item since we want the shortcut
      * to always be available, but we don't want the menu item shown if not
      * completely necesary. Since the visibility of the menu item is based on
@@ -7401,6 +7496,9 @@ real_update_actions_state (NautilusFilesView *view)
     gboolean settings_show_create_link;
     gboolean settings_automatic_decompression;
     GDriveStartStopType start_stop_type;
+    gboolean show_star;
+    gboolean show_unstar;
+    gchar *uri;
 
     priv = nautilus_files_view_get_instance_private (view);
 
@@ -7741,6 +7839,33 @@ real_update_actions_state (NautilusFilesView *view)
     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
                                  !nautilus_files_view_is_empty (view));
 
+    show_star = (selection != NULL);
+    show_unstar = (selection != NULL);
+    for (l = selection; l != NULL; l = l->next)
+    {
+        file = NAUTILUS_FILE (l->data);
+        uri = nautilus_file_get_uri (file);
+
+        if (nautilus_tag_manager_file_is_favorite (priv->tag_manager, uri))
+        {
+            show_star = FALSE;
+        }
+        else
+        {
+            show_unstar = FALSE;
+        }
+
+        g_free (uri);
+    }
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
+                                         "star");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), show_star);
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
+                                         "unstar");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), show_unstar);
+
     nautilus_file_list_free (selection);
 }
 
@@ -9714,6 +9839,9 @@ nautilus_files_view_init (NautilusFilesView *view)
      * changed */
     nautilus_application_set_accelerator (app, "view.show-move-to-trash-shortcut-changed-dialog", 
"<control>Delete");
 
+    priv->favorite_cancellable = g_cancellable_new ();
+    priv->tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
     nautilus_profile_end (NULL);
 }
 
diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h
index cc3340a..4c3ebfe 100644
--- a/src/nautilus-files-view.h
+++ b/src/nautilus-files-view.h
@@ -338,6 +338,10 @@ void              nautilus_files_view_action_show_hidden_files   (NautilusFilesV
 GActionGroup *    nautilus_files_view_get_action_group           (NautilusFilesView      *view);
 GtkWidget*        nautilus_files_view_get_content_widget         (NautilusFilesView      *view);
 
+void              on_favorite_tags_updated                       (GObject      *object,
+                                                                  GAsyncResult *res,
+                                                                  gpointer      user_data);
+
 G_END_DECLS
 
 #endif /* NAUTILUS_FILES_VIEW_H */
diff --git a/src/nautilus-list-view-private.h b/src/nautilus-list-view-private.h
index e2a09a1..f0d1716 100644
--- a/src/nautilus-list-view-private.h
+++ b/src/nautilus-list-view-private.h
@@ -21,6 +21,7 @@
 #include "nautilus-list-model.h"
 #include "nautilus-tree-view-drag-dest.h"
 #include "nautilus-dnd.h"
+#include "nautilus-tag-manager.h"
 
 struct NautilusListViewDetails {
   GtkTreeView *tree_view;
@@ -64,5 +65,8 @@ struct NautilusListViewDetails {
   gulong clipboard_handler_id;
 
   GQuark last_sort_attr;
+
+  NautilusTagManager *tag_manager;
+  GCancellable *favorite_cancellable;
 };
 
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index 14a074a..13beaf5 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -32,6 +32,7 @@
 #include "nautilus-toolbar.h"
 #include "nautilus-list-view-dnd.h"
 #include "nautilus-view.h"
+#include "nautilus-tag-manager.h"
 
 #include <string.h>
 #include <eel/eel-vfs-extensions.h>
@@ -106,22 +107,22 @@ G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_FILES_VIEW);
 
 static const char *default_search_visible_columns[] =
 {
-    "name", "size", "where", NULL
+    "name", "size", "where", "favorite", NULL
 };
 
 static const char *default_search_columns_order[] =
 {
-    "name", "size", "where", NULL
+    "name", "size", "where", "favorite", NULL
 };
 
 static const char *default_recent_visible_columns[] =
 {
-    "name", "size", "where", NULL
+    "name", "size", "where", "favorite", NULL
 };
 
 static const char *default_recent_columns_order[] =
 {
-    "name", "size", "where", NULL
+    "name", "size", "where", "favorite", NULL
 };
 
 static const char *default_trash_visible_columns[] =
@@ -149,6 +150,7 @@ get_default_sort_order (NautilusFile *file,
         "type",
         "date_modified",
         "date_accessed",
+        "favorite",
         "trashed_on",
         NULL
     };
@@ -456,6 +458,51 @@ row_activated_callback (GtkTreeView       *treeview,
     activate_selected_items (view);
 }
 
+void
+nautilus_list_view_redraw_tree_view (NautilusFilesView *view)
+{
+    NautilusListView *list_view;
+
+    list_view = NAUTILUS_LIST_VIEW (view);
+
+    gtk_widget_queue_draw (GTK_WIDGET (list_view->details->tree_view));
+}
+
+static void
+on_star_cell_renderer_clicked (GtkTreePath      *path,
+                               NautilusListView *list_view)
+{
+    NautilusListModel *list_model;
+    NautilusFile *file;
+    g_autofree gchar *uri = NULL;
+    GList *selection;
+
+    list_model = list_view->details->model;
+
+    file = nautilus_list_model_file_for_path (list_model, path);
+    uri = nautilus_file_get_uri (file);
+    selection = g_list_prepend (NULL, file);
+
+    if (nautilus_tag_manager_file_is_favorite (list_view->details->tag_manager, uri))
+    {
+        nautilus_tag_manager_unstar_files (list_view->details->tag_manager,
+                                           G_OBJECT (list_view),
+                                           selection,
+                                           on_favorite_tags_updated,
+                                           list_view->details->favorite_cancellable);
+    }
+    else
+    {
+        nautilus_tag_manager_star_files (list_view->details->tag_manager,
+                                         G_OBJECT (list_view),
+                                         selection,
+                                         on_favorite_tags_updated,
+                                         list_view->details->favorite_cancellable);
+    }
+
+    nautilus_file_list_free (selection);
+}
+
 static gboolean
 button_press_callback (GtkWidget      *widget,
                        GdkEventButton *event,
@@ -702,6 +749,33 @@ button_press_callback (GtkWidget      *widget,
         }
     }
 
+    if (is_simple_click)
+    {
+        GtkTreeViewColumn *column;
+        gchar **visible_columns;
+        gint i;
+        gdouble m;
+
+        visible_columns = get_visible_columns (view);
+
+        for (i = 0; visible_columns[i] != NULL; i++)
+        {
+            column = g_hash_table_lookup (view->details->columns, visible_columns[i]);
+
+            if (g_strcmp0 (visible_columns[i], "favorite") == 0)
+            {
+                m = gtk_tree_view_column_get_width (column) / 2 +
+                    gtk_tree_view_column_get_x_offset (column);
+
+                if (event->x > m - 10 && event->x < m + 10)
+                {
+                    on_star_cell_renderer_clicked (path, view);
+                }
+            }
+
+        }
+    }
+
     gtk_tree_path_free (path);
 
     /* We chained to the default handler in this method, so never
@@ -1546,6 +1620,45 @@ apply_columns_settings (NautilusListView  *list_view,
 }
 
 static void
+favorite_cell_data_func (GtkTreeViewColumn *column,
+                         GtkCellRenderer   *renderer,
+                         GtkTreeModel      *model,
+                         GtkTreeIter       *iter,
+                         NautilusListView  *view)
+{
+    g_autofree gchar *text = NULL;
+    g_autofree gchar *uri = NULL;
+    NautilusFile *file;
+
+    gtk_tree_model_get (model, iter,
+                        view->details->file_name_column_num, &text,
+                        -1);
+
+    gtk_tree_model_get (GTK_TREE_MODEL (model),
+                        iter,
+                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
+                        -1);
+
+    uri = nautilus_file_get_uri (file);
+
+    if (nautilus_tag_manager_file_is_favorite (view->details->tag_manager, uri))
+    {
+        g_object_set (renderer,
+                      "icon-name", "starred-symbolic",
+                      NULL);
+    }
+    else
+    {
+        g_object_set (renderer,
+                      "icon-name", "non-starred-symbolic",
+                      NULL);
+
+    }
+
+    nautilus_file_unref (file);
+}
+
+static void
 filename_cell_data_func (GtkTreeViewColumn *column,
                          GtkCellRenderer   *renderer,
                          GtkTreeModel      *model,
@@ -2029,13 +2142,34 @@ create_and_set_up_tree_view (NautilusListView *view)
         }
         else
         {
-            /* We need to use libgd */
-            cell = gd_styled_text_renderer_new ();
-            /* FIXME: should be just dim-label.
-             * See https://bugzilla.gnome.org/show_bug.cgi?id=744397
-             */
-            gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (cell),
-                                               "nautilus-list-dim-label");
+            if (!g_strcmp0 (name, "favorite"))
+            {
+                cell = gtk_cell_renderer_pixbuf_new ();
+                g_object_set (cell,
+                              "icon-name", "non-starred-symbolic",
+                              "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+                              NULL);
+
+                column = gtk_tree_view_column_new_with_attributes (label,
+                                                                   cell,
+                                                                   NULL);
+            }
+            else
+            {
+                /* We need to use libgd */
+                cell = gd_styled_text_renderer_new ();
+                /* FIXME: should be just dim-label.
+                 * See https://bugzilla.gnome.org/show_bug.cgi?id=744397
+                 */
+                gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (cell),
+                                                   "nautilus-list-dim-label");
+
+                column = gtk_tree_view_column_new_with_attributes (label,
+                                                                   cell,
+                                                                   "text", column_num,
+                                                                   NULL);
+            }
+
 
             g_object_set (cell,
                           "xalign", xalign,
@@ -2049,10 +2183,7 @@ create_and_set_up_tree_view (NautilusListView *view)
             }
             view->details->cells = g_list_append (view->details->cells,
                                                   cell);
-            column = gtk_tree_view_column_new_with_attributes (label,
-                                                               cell,
-                                                               "text", column_num,
-                                                               NULL);
+
             gtk_tree_view_append_column (view->details->tree_view, column);
             gtk_tree_view_column_set_sort_column_id (column, column_num);
             g_hash_table_insert (view->details->columns,
@@ -2079,6 +2210,12 @@ create_and_set_up_tree_view (NautilusListView *view)
                                                          (GtkTreeCellDataFunc) 
trash_orig_path_cell_data_func,
                                                          view, NULL);
             }
+            else if (!strcmp (name, "favorite"))
+            {
+                gtk_tree_view_column_set_cell_data_func (column, cell,
+                                                         (GtkTreeCellDataFunc) favorite_cell_data_func,
+                                                         view, NULL);
+            }
         }
         g_free (name);
         g_free (label);
@@ -3170,6 +3307,18 @@ nautilus_list_view_get_zoom_level_percentage (NautilusFilesView *view)
     return (gfloat) icon_size / NAUTILUS_LIST_ICON_SIZE_STANDARD;
 }
 
+static gboolean
+nautilus_list_view_is_zoom_level_default (NautilusFilesView *view)
+{
+    NautilusListView *list_view;
+    guint icon_size;
+
+    list_view = NAUTILUS_LIST_VIEW (view);
+    icon_size = nautilus_list_model_get_icon_size_for_zoom_level (list_view->details->zoom_level);
+
+    return icon_size == NAUTILUS_LIST_ICON_SIZE_STANDARD;
+}
+
 static void
 nautilus_list_view_click_policy_changed (NautilusFilesView *directory_view)
 {
@@ -3355,6 +3504,9 @@ nautilus_list_view_finalize (GObject *object)
 
     g_free (list_view->details);
 
+    g_cancellable_cancel (list_view->details->favorite_cancellable);
+    g_clear_object (&list_view->details->favorite_cancellable);
+
     G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object);
 }
 
@@ -3560,6 +3712,7 @@ nautilus_list_view_class_init (NautilusListViewClass *class)
     nautilus_files_view_class->can_zoom_in = nautilus_list_view_can_zoom_in;
     nautilus_files_view_class->can_zoom_out = nautilus_list_view_can_zoom_out;
     nautilus_files_view_class->get_zoom_level_percentage = nautilus_list_view_get_zoom_level_percentage;
+    nautilus_files_view_class->is_zoom_level_default = nautilus_list_view_is_zoom_level_default;
     nautilus_files_view_class->click_policy_changed = nautilus_list_view_click_policy_changed;
     nautilus_files_view_class->clear = nautilus_list_view_clear;
     nautilus_files_view_class->file_changed = nautilus_list_view_file_changed;
@@ -3637,6 +3790,9 @@ nautilus_list_view_init (NautilusListView *list_view)
     /* Keep the action synced with the actual value, so the toolbar can poll it */
     g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW 
(list_view)),
                                         "zoom-to-level", g_variant_new_int32 (get_default_zoom_level ()));
+
+    list_view->details->tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+    list_view->details->favorite_cancellable = g_cancellable_new ();
 }
 
 NautilusFilesView *
diff --git a/src/nautilus-list-view.h b/src/nautilus-list-view.h
index f775431..cedeb02 100644
--- a/src/nautilus-list-view.h
+++ b/src/nautilus-list-view.h
@@ -53,4 +53,5 @@ typedef struct {
 GType nautilus_list_view_get_type (void);
 NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot);
 
+void nautilus_list_view_redraw_tree_view (NautilusFilesView *view);
 #endif /* NAUTILUS_LIST_VIEW_H */
diff --git a/src/nautilus-pathbar.c b/src/nautilus-pathbar.c
index 60817b4..24b1685 100644
--- a/src/nautilus-pathbar.c
+++ b/src/nautilus-pathbar.c
@@ -47,7 +47,8 @@ typedef enum
     OTHER_LOCATIONS_BUTTON,
     ROOT_BUTTON,
     HOME_BUTTON,
-    MOUNT_BUTTON
+    MOUNT_BUTTON,
+    FAVORITE_LOCATION_BUTTON
 } ButtonType;
 
 #define BUTTON_DATA(x) ((ButtonData *) (x))
@@ -331,6 +332,11 @@ get_dir_name (ButtonData *button_data)
             return _("Other Locations");
         }
 
+        case FAVORITE_LOCATION_BUTTON:
+        {
+            return _("Favorite Files");
+        }
+
         default:
             return button_data->dir_name;
     }
@@ -1723,6 +1729,10 @@ setup_button_type (ButtonData      *button_data,
 
         g_object_unref (mount);
     }
+    else if (g_strcmp0 (uri, "favorites:///") == 0)
+    {
+        button_data->type = FAVORITE_LOCATION_BUTTON;
+    }
     else
     {
         button_data->type = NORMAL_BUTTON;
diff --git a/src/nautilus-query.c b/src/nautilus-query.c
index 53771ce..15a01bf 100644
--- a/src/nautilus-query.c
+++ b/src/nautilus-query.c
@@ -42,6 +42,7 @@ struct _NautilusQuery
     GPtrArray *date_range;
     NautilusQuerySearchType search_type;
     NautilusQuerySearchContent search_content;
+    gboolean search_favorite;
 
     gboolean searching;
     gboolean recursive;
@@ -339,6 +340,7 @@ nautilus_query_init (NautilusQuery *query)
     query->location = g_file_new_for_path (g_get_home_dir ());
     query->search_type = g_settings_get_enum (nautilus_preferences, "search-filter-time-type");
     query->search_content = NAUTILUS_QUERY_SEARCH_CONTENT_SIMPLE;
+    query->search_favorite = FALSE;
     g_mutex_init (&query->prepared_words_mutex);
 }
 
@@ -541,6 +543,23 @@ nautilus_query_set_search_content (NautilusQuery              *query,
     }
 }
 
+gboolean
+nautilus_query_get_search_favorite (NautilusQuery *query)
+{
+    g_return_val_if_fail (NAUTILUS_IS_QUERY (query), FALSE);
+
+    return query->search_favorite;
+}
+
+void
+nautilus_query_set_search_favorite (NautilusQuery *query,
+                                    gboolean       search_favorite)
+{
+    g_return_if_fail (NAUTILUS_IS_QUERY (query));
+
+    query->search_favorite = search_favorite;
+}
+
 NautilusQuerySearchType
 nautilus_query_get_search_type (NautilusQuery *query)
 {
diff --git a/src/nautilus-query.h b/src/nautilus-query.h
index 2264f50..5367844 100644
--- a/src/nautilus-query.h
+++ b/src/nautilus-query.h
@@ -59,6 +59,10 @@ NautilusQuerySearchContent nautilus_query_get_search_content (NautilusQuery *que
 void                       nautilus_query_set_search_content (NautilusQuery              *query,
                                                               NautilusQuerySearchContent  content);
 
+gboolean nautilus_query_get_search_favorite (NautilusQuery *query);
+void     nautilus_query_set_search_favorite (NautilusQuery *query,
+                                             gboolean       search_favorite);
+
 NautilusQuerySearchType nautilus_query_get_search_type (NautilusQuery *query);
 void                    nautilus_query_set_search_type (NautilusQuery           *query,
                                                         NautilusQuerySearchType  type);
diff --git a/src/nautilus-search-directory.c b/src/nautilus-search-directory.c
index d51f4f8..e04d5cb 100644
--- a/src/nautilus-search-directory.c
+++ b/src/nautilus-search-directory.c
@@ -641,6 +641,7 @@ search_engine_hits_added (NautilusSearchEngine    *engine,
         g_signal_connect (file, "changed", G_CALLBACK (file_changed), search),
 
         file_list = g_list_prepend (file_list, file);
+
         g_hash_table_add (search->details->files_hash, file);
     }
 
diff --git a/src/nautilus-search-engine-model.c b/src/nautilus-search-engine-model.c
index b8f2a94..aa6e47e 100644
--- a/src/nautilus-search-engine-model.c
+++ b/src/nautilus-search-engine-model.c
@@ -27,6 +27,7 @@
 #include "nautilus-directory-private.h"
 #include "nautilus-file.h"
 #include "nautilus-ui-utilities.h"
+#include "nautilus-tag-manager.h"
 #define DEBUG_FLAG NAUTILUS_DEBUG_SEARCH
 #include "nautilus-debug.h"
 
@@ -137,6 +138,7 @@ model_directory_ready_cb (NautilusDirectory *directory,
     GDateTime *initial_date;
     GDateTime *end_date;
     GPtrArray *date_range;
+    NautilusTagManager *tag_manager;
 
     files = nautilus_directory_get_file_list (directory);
     mime_types = nautilus_query_get_mime_types (model->details->query);
@@ -189,12 +191,27 @@ model_directory_ready_cb (NautilusDirectory *directory,
             g_ptr_array_unref (date_range);
         }
 
+        if (nautilus_query_get_search_favorite (model->details->query))
+        {
+            tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
+            uri = nautilus_file_get_uri (file);
+
+            if (!nautilus_tag_manager_file_is_favorite (tag_manager, uri))
+            {
+                found = FALSE;
+            }
+
+            g_free (uri);
+        }
+
         if (found)
         {
             uri = nautilus_file_get_uri (file);
             hit = nautilus_search_hit_new (uri);
             nautilus_search_hit_set_fts_rank (hit, match);
             hits = g_list_prepend (hits, hit);
+
             g_free (uri);
         }
 
diff --git a/src/nautilus-search-engine-simple.c b/src/nautilus-search-engine-simple.c
index ea2dbe3..f0d74ea 100644
--- a/src/nautilus-search-engine-simple.c
+++ b/src/nautilus-search-engine-simple.c
@@ -24,6 +24,7 @@
 #include "nautilus-search-provider.h"
 #include "nautilus-search-engine-simple.h"
 #include "nautilus-ui-utilities.h"
+#include "nautilus-tag-manager.h"
 #define DEBUG_FLAG NAUTILUS_DEBUG_SEARCH
 #include "nautilus-debug.h"
 
@@ -222,7 +223,8 @@ visit_directory (GFile            *dir,
     GPtrArray *date_range;
     GDateTime *initial_date;
     GDateTime *end_date;
-
+    NautilusTagManager *tag_manager;
+    gchar *uri;
 
     enumerator = g_file_enumerate_children (dir,
                                             data->mime_types != NULL ?
@@ -299,11 +301,24 @@ visit_directory (GFile            *dir,
             g_ptr_array_unref (date_range);
         }
 
+        if (nautilus_query_get_search_favorite (data->query))
+        {
+            tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
+            uri = g_file_get_uri (child);
+
+            if (!nautilus_tag_manager_file_is_favorite (tag_manager, uri))
+            {
+                found = FALSE;
+            }
+
+            g_free (uri);
+        }
+
         if (found)
         {
             NautilusSearchHit *hit;
             GDateTime *date;
-            char *uri;
 
             uri = g_file_get_uri (child);
             hit = nautilus_search_hit_new (uri);
diff --git a/src/nautilus-search-engine-tracker.c b/src/nautilus-search-engine-tracker.c
index b868dbf..54bdeeb 100644
--- a/src/nautilus-search-engine-tracker.c
+++ b/src/nautilus-search-engine-tracker.c
@@ -351,6 +351,11 @@ nautilus_search_engine_tracker_start (NautilusSearchProvider *provider)
         g_string_append_printf (sparql, "; fts:match '\"%s\"*'", search_text);
     }
 
+    if (nautilus_query_get_search_favorite (tracker->details->query))
+    {
+        g_string_append_printf (sparql, "; nao:hasTag nao:predefined-tag-favorite");
+    }
+
     if (mime_count > 0)
     {
         g_string_append (sparql, "; nie:mimeType ?mime");
diff --git a/src/nautilus-search-engine.c b/src/nautilus-search-engine.c
index f2bc012..cc0bc32 100644
--- a/src/nautilus-search-engine.c
+++ b/src/nautilus-search-engine.c
@@ -106,6 +106,7 @@ search_engine_start_real (NautilusSearchEngine *engine)
     nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (priv->tracker));
     priv->providers_running++;
 #endif
+
     if (nautilus_search_engine_model_get_model (priv->model))
     {
         nautilus_search_provider_start (NAUTILUS_SEARCH_PROVIDER (priv->model));
diff --git a/src/nautilus-tag-manager.c b/src/nautilus-tag-manager.c
new file mode 100644
index 0000000..6825aae
--- /dev/null
+++ b/src/nautilus-tag-manager.c
@@ -0,0 +1,1399 @@
+/* nautilus-tag-manager.c
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-tag-manager.h"
+#include "nautilus-file.h"
+#include <tracker-sparql.h>
+
+#define GINT64_TO_POINTER(i) ((gpointer) (gint64) (i))
+#define GPOINTER_TO_GINT64(p) ((gint64) (p))
+
+struct _NautilusTagManager
+{
+    GObject object;
+
+    TrackerNotifier *notifier;
+    GError *notifier_error;
+
+    GQueue *all_tags;
+    GCancellable *all_tags_cancellable;
+
+    GHashTable *favorite_files;
+    GCancellable *favorite_files_cancellable;
+};
+
+G_DEFINE_TYPE (NautilusTagManager, nautilus_tag_manager, G_TYPE_OBJECT);
+
+static NautilusTagManager *tag_manager = NULL;
+
+typedef enum
+{
+    GET_ALL_TAGS,
+    GET_FAVORITE_FILES,
+    GET_SELECTION_TAGS,
+    GET_FILES_WITH_TAG,
+    GET_IDS_FOR_URLS
+} OperationType;
+
+typedef struct
+{
+    GTask *task;
+    GList *selection;
+    GHashTable *ids;
+    GObject *object;
+    GAsyncReadyCallback callback;
+    GCancellable *cancellable;
+} InsertTaskData;
+
+enum
+{
+    FAVORITES_CHANGED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static const gchar*
+nautilus_tag_manager_file_with_id_changed_url (GHashTable  *hash_table,
+                                               gint64       id,
+                                               const gchar *url)
+{
+    GHashTableIter iter;
+    gpointer key, value;
+
+    g_hash_table_iter_init (&iter, hash_table);
+    while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+        if ((gint64) value == id && g_strcmp0 (url, key) != 0)
+        {
+            return key;
+        }
+    }
+
+    return NULL;
+}
+
+gchar*
+parse_color_from_tag_id (const gchar *tag_id)
+{
+    gchar *color;
+
+    if (g_strrstr (tag_id, "org:gnome:nautilus:tag"))
+    {
+        color = g_strdup (g_strrstr (tag_id, ":") + 1);
+    }
+    else
+    {
+        color = g_strdup ("rgb(220,220,220)");
+    }
+
+    return color;
+}
+
+gboolean
+nautilus_tag_queue_has_tag (GQueue      *tag_queue,
+                            const gchar *tag_name)
+{
+    GList *l;
+    TagData *tag_data;
+
+    for (l = g_queue_peek_head_link (tag_queue); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        if (g_strcmp0 (tag_name, tag_data->name) == 0)
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+GQueue*
+nautilus_tag_copy_tag_queue (GQueue *queue)
+{
+    GQueue *result_queue;
+    GList *l;
+    TagData *tag_data, *new_tag_data;
+
+    result_queue = g_queue_new ();
+
+    for (l = g_queue_peek_head_link (queue); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        new_tag_data = g_new0 (TagData, 1);
+        new_tag_data->id = g_strdup (tag_data->id);
+        new_tag_data->name = g_strdup (tag_data->name);
+        new_tag_data->url = g_strdup (tag_data->url);
+
+        g_queue_push_tail (result_queue, new_tag_data);
+    }
+
+    return result_queue;
+}
+
+void
+destroy_insert_task_data (gpointer data)
+{
+    InsertTaskData *task_data;
+
+    task_data = data;
+
+    nautilus_file_list_free (task_data->selection);
+    g_hash_table_destroy (task_data->ids);
+    g_free (data);
+}
+
+void
+nautilus_tag_data_free (gpointer data)
+{
+    TagData *tag_data;
+
+    tag_data = data;
+
+    g_free (tag_data->id);
+    g_free (tag_data->name);
+    g_free (tag_data->url);
+    g_free (tag_data);
+}
+
+static void
+nautilus_tag_data_queue_free (gpointer data)
+{
+    GQueue *queue;
+
+    queue = data;
+
+    g_queue_free_full (queue, nautilus_tag_data_free);
+}
+
+static void
+destroy_url_queue (gpointer data)
+{
+    GQueue *queue;
+
+    queue = data;
+
+    g_queue_free_full (queue, g_free);
+}
+
+TagData*
+nautilus_tag_data_new (const gchar *id,
+                       const gchar *name,
+                       const gchar *url)
+{
+    TagData *data;
+
+    data = g_new0 (TagData, 1);
+
+    data->id = g_strdup (id);
+    data->name = g_strdup (name);
+    data->url = g_strdup (url);
+
+    return data;
+}
+
+static GString*
+add_selection_filter (GList   *selection,
+                      GString *query)
+{
+    NautilusFile *file;
+    GList *l;
+    gchar *uri;
+
+    g_string_append (query, " FILTER(?url IN (");
+
+    for (l = selection; l != NULL; l = l->next)
+    {
+        file = l->data;
+
+        uri = nautilus_file_get_uri (file);
+
+        g_string_append_printf (query, "'%s'", uri);
+
+        if (l->next != NULL)
+        {
+            g_string_append (query, ", ");
+        }
+
+        g_free (uri);
+    }
+
+    g_string_append (query, "))");
+
+    return query;
+}
+
+static void
+start_query_or_update (GString             *query,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data,
+                       gboolean             is_query,
+                       GCancellable        *cancellable)
+{
+    g_autoptr (GError) error = NULL;
+    TrackerSparqlConnection *connection;
+
+    connection = tracker_sparql_connection_get (cancellable, &error);
+    if (!connection)
+    {
+        if (error)
+        {
+            g_warning ("Error on getting connection: %s", error->message);
+        }
+
+        return;
+    }
+
+    if (is_query)
+    {
+        tracker_sparql_connection_query_async (connection,
+                                               query->str,
+                                               cancellable,
+                                               callback,
+                                               user_data);
+    }
+    else
+    {
+        tracker_sparql_connection_update_async (connection,
+                                                query->str,
+                                                G_PRIORITY_DEFAULT,
+                                                cancellable,
+                                                callback,
+                                                user_data);
+    }
+
+    g_object_unref (connection);
+}
+
+static void
+on_query_callback (GObject              *object,
+                   GAsyncResult         *result,
+                   gpointer              user_data,
+                   GAsyncReadyCallback   callback,
+                   OperationType         op_type,
+                   GCancellable         *cancellable)
+{
+    TrackerSparqlCursor *cursor;
+    g_autoptr (GError) error = NULL;
+    TrackerSparqlConnection *connection;
+    GTask *task;
+
+    task = user_data;
+
+    connection = TRACKER_SPARQL_CONNECTION (object);
+
+    cursor = tracker_sparql_connection_query_finish (connection,
+                                                     result,
+                                                     &error);
+
+    if (error != NULL)
+    {
+        if (error->code != G_IO_ERROR_CANCELLED)
+        {
+            if (op_type == GET_ALL_TAGS)
+            {
+                g_warning ("error while getting tags: %s\n", error->message);
+            }
+            else if (op_type == GET_SELECTION_TAGS)
+            {
+                g_task_return_pointer (task, g_task_get_task_data (task), nautilus_tag_data_queue_free);
+            }
+            else if (op_type == GET_FAVORITE_FILES)
+            {
+                g_warning ("Error on getting favorite files: %s", error->message);
+            }
+            else if (op_type == GET_IDS_FOR_URLS)
+            {
+                g_warning ("Error on getting id for url: %s", error->message);
+                g_task_return_pointer (task, g_task_get_task_data (task), destroy_insert_task_data);
+            }
+            else
+            {
+                g_warning ("Error on getting query callback: %s", error->message);
+            }
+        }
+    }
+    else
+    {
+        tracker_sparql_cursor_next_async (cursor,
+                                          cancellable,
+                                          callback,
+                                          user_data);
+    }
+}
+
+static void
+on_update_callback (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer     user_data)
+{
+    TrackerSparqlConnection *connection;
+    GError *error;
+    GTask *task;
+
+    task = user_data;
+
+    error = NULL;
+
+    connection = TRACKER_SPARQL_CONNECTION (object);
+
+    tracker_sparql_connection_update_finish (connection, result, &error);
+
+    if (error == NULL ||
+        (error != NULL && error->code != G_IO_ERROR_CANCELLED))
+    {
+        g_task_return_boolean (task, TRUE);
+    }
+    else if (error && error->code == G_IO_ERROR_CANCELLED)
+    {
+       g_error_free (error);
+    }
+    else
+    {
+        g_task_return_error (task, error);
+        g_warning ("error updating tags: %s", error->message);
+    }
+}
+
+static gboolean
+get_query_status (TrackerSparqlCursor *cursor,
+                  GAsyncResult        *result,
+                  OperationType        op_type,
+                  gpointer             user_data)
+{
+    gboolean success;
+    GTask *task;
+    g_autoptr (GError) error = NULL;
+
+    task = user_data;
+
+    success = tracker_sparql_cursor_next_finish (cursor, result, &error);
+
+    if (!success)
+    {
+        if (error)
+        {
+            g_warning ("Error on getting all tags cursor callback: %s", error->message);
+        }
+
+        g_clear_object (&cursor);
+
+        if (error == NULL ||
+            (error != NULL && error->code != G_IO_ERROR_CANCELLED))
+        {
+            if (op_type == GET_SELECTION_TAGS)
+            {
+                g_task_return_pointer (task, g_task_get_task_data (task), nautilus_tag_data_queue_free);
+
+            }
+            else if (op_type == GET_FILES_WITH_TAG)
+            {
+                g_task_return_pointer (task, g_task_get_task_data (task), destroy_url_queue);
+            }
+            else if (op_type == GET_IDS_FOR_URLS)
+            {
+                g_task_return_pointer (task, g_task_get_task_data (task), destroy_insert_task_data);
+            }
+        }
+    }
+
+    return success;
+}
+
+static void
+on_get_all_tags_cursor_callback (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+    TrackerSparqlCursor *cursor;
+    const gchar *id;
+    const gchar *name;
+    gboolean success;
+    NautilusTagManager *self;
+    TagData *tag_data;
+
+    cursor = TRACKER_SPARQL_CURSOR (object);
+
+    self = NAUTILUS_TAG_MANAGER (user_data);
+
+    success = get_query_status (cursor, result, GET_ALL_TAGS, NULL);
+    if (!success)
+    {
+        return;
+    }
+
+    id = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+    name = tracker_sparql_cursor_get_string (cursor, 1, NULL);
+
+    tag_data = nautilus_tag_data_new (id, name, NULL);
+
+    g_queue_push_tail (self->all_tags, tag_data);
+
+    tracker_sparql_cursor_next_async (cursor,
+                                      self->all_tags_cancellable,
+                                      on_get_all_tags_cursor_callback,
+                                      self);
+}
+
+static void
+on_get_all_tags_query_callback (GObject      *object,
+                                GAsyncResult *result,
+                                gpointer      user_data)
+{
+    NautilusTagManager *self;
+
+    self = NAUTILUS_TAG_MANAGER (user_data);
+
+    on_query_callback (object,
+                       result,
+                       user_data,
+                       on_get_all_tags_cursor_callback,
+                       GET_ALL_TAGS,
+                       self->all_tags_cancellable);
+}
+
+GQueue*
+nautilus_tag_manager_get_all_tags (NautilusTagManager *self)
+{
+    return self->all_tags;
+}
+
+GList*
+nautilus_tag_manager_get_favorite_files (NautilusTagManager *self)
+{
+    return g_hash_table_get_keys (self->favorite_files);
+}
+
+static void
+nautilus_tag_manager_query_all_tags (NautilusTagManager  *self,
+                                     GCancellable *cancellable)
+{
+    GString *query;
+
+    if (self->all_tags)
+    {
+        g_queue_free_full (self->all_tags, nautilus_tag_data_free);
+    }
+    self->all_tags = g_queue_new ();
+
+    self->all_tags_cancellable = cancellable;
+
+    query = g_string_new ("SELECT ?urn ?label WHERE { ?urn a nao:Tag ; nao:prefLabel ?label . } ORDER BY 
?label");
+
+    start_query_or_update (query,
+                           on_get_all_tags_query_callback,
+                           self,
+                           TRUE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+static void
+on_get_favorite_files_cursor_callback (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+    TrackerSparqlCursor *cursor;
+    const gchar *url;
+    gint64 id;
+    gboolean success;
+    NautilusTagManager *self;
+
+    cursor = TRACKER_SPARQL_CURSOR (object);
+
+    self = NAUTILUS_TAG_MANAGER (user_data);
+
+    success = get_query_status (cursor, result, GET_FAVORITE_FILES, NULL);
+    if (!success)
+    {
+        return;
+    }
+
+    url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+    id = tracker_sparql_cursor_get_integer (cursor, 1);
+
+    g_hash_table_insert (self->favorite_files,
+                         g_strdup (url),
+                         GINT64_TO_POINTER (id));
+    g_signal_emit_by_name (self, "favorites-changed", NULL);
+
+    tracker_sparql_cursor_next_async (cursor,
+                                      self->favorite_files_cancellable,
+                                      on_get_favorite_files_cursor_callback,
+                                      self);
+}
+
+static void
+on_get_favorite_files_query_callback (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+    NautilusTagManager *self;
+
+    self = NAUTILUS_TAG_MANAGER (user_data);
+
+    on_query_callback (object,
+                       result,
+                       user_data,
+                       on_get_favorite_files_cursor_callback,
+                       GET_FAVORITE_FILES,
+                       self->favorite_files_cancellable);
+}
+
+
+static void
+nautilus_tag_manager_query_favorite_files (NautilusTagManager *self,
+                                           GCancellable       *cancellable)
+{
+    GString *query;
+
+    self->favorite_files_cancellable = cancellable;
+
+    query = g_string_new ("SELECT ?url tracker:id(?urn) WHERE { ?urn nie:url ?url ; nao:hasTag 
nao:predefined-tag-favorite}");
+
+    start_query_or_update (query,
+                           on_get_favorite_files_query_callback,
+                           self,
+                           TRUE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+static void
+on_get_selection_tags_cursor_callback (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+    TrackerSparqlCursor *cursor;
+    const gchar *id;
+    const gchar *name;
+    const gchar *url;
+    gboolean success;
+    TagData *tag_data;
+    GTask *task;
+    GQueue *selection_tags;
+
+    cursor = TRACKER_SPARQL_CURSOR (object);
+
+    task = user_data;
+
+    selection_tags = g_task_get_task_data (task);
+
+    success = get_query_status (cursor, result, GET_SELECTION_TAGS, user_data);
+    if (!success)
+    {
+        return;
+    }
+
+    id = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+    name = tracker_sparql_cursor_get_string (cursor, 1, NULL);
+    url = tracker_sparql_cursor_get_string (cursor, 2, NULL);
+
+    tag_data = nautilus_tag_data_new (id, name, url);
+
+    g_queue_push_tail (selection_tags, tag_data);
+
+    tracker_sparql_cursor_next_async (cursor,
+                                      g_task_get_cancellable (task),
+                                      on_get_selection_tags_cursor_callback,
+                                      task);
+}
+
+static void
+on_get_selection_tags_query_callback (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+    GTask *task;
+
+    task = user_data;
+
+    on_query_callback (object,
+                       result,
+                       task,
+                       on_get_selection_tags_cursor_callback,
+                       GET_SELECTION_TAGS,
+                       g_task_get_cancellable (task));
+}
+
+gpointer
+nautilus_tag_manager_gpointer_task_finish (GObject      *source_object,
+                                           GAsyncResult *res,
+                                           GError      **error)
+{
+    g_return_val_if_fail (g_task_is_valid (res, source_object), FALSE);
+
+    return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+nautilus_tag_manager_get_selection_tags (GObject            *object,
+                                         GList              *selection,
+                                         GAsyncReadyCallback callback,
+                                         GCancellable       *cancellable)
+{
+    GString *query;
+    GQueue *selection_tags;
+    GTask *task;
+
+    selection_tags = g_queue_new ();
+
+    task = g_task_new (object, cancellable, callback, NULL);
+    g_task_set_task_data (task,
+                          selection_tags,
+                          nautilus_tag_data_queue_free);
+
+    query = g_string_new ("SELECT ?tag nao:prefLabel(?tag) ?url WHERE"
+                          "{ ?urn a nfo:FileDataObject ; nao:hasTag ?tag ; nie:url ?url .");
+
+    query = add_selection_filter (selection, query);
+
+    g_string_append (query, "} ORDER BY (?tag)");
+
+    start_query_or_update (query,
+                           on_get_selection_tags_query_callback,
+                           task,
+                           TRUE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+static void
+on_get_files_with_tag_cursor_callback (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+    TrackerSparqlCursor *cursor;
+    cursor = TRACKER_SPARQL_CURSOR (object);
+    gchar *url;
+    gboolean success;
+    GTask *task;
+    GQueue *files_with_tag;
+
+    task = user_data;
+    files_with_tag = g_task_get_task_data (task);
+
+    success = get_query_status (cursor, result, GET_FILES_WITH_TAG, task);
+    if (!success)
+    {
+        return;
+    }
+
+    url = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
+    g_queue_push_tail (files_with_tag, url);
+
+    tracker_sparql_cursor_next_async (cursor,
+                                      g_task_get_cancellable (task),
+                                      on_get_files_with_tag_cursor_callback,
+                                      task);
+}
+
+static void
+on_get_files_with_tag_query_callback (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+    GTask *task;
+
+    task = user_data;
+
+    on_query_callback (object,
+                       result,
+                       user_data,
+                       on_get_files_with_tag_cursor_callback,
+                       GET_FILES_WITH_TAG,
+                       g_task_get_cancellable (task));
+}
+
+void
+nautilus_tag_manager_get_files_with_tag (NautilusTagManager  *self,
+                                         GObject             *object,
+                                         const gchar         *tag_name,
+                                         GAsyncReadyCallback  callback,
+                                         GCancellable        *cancellable)
+{
+    GString *query;
+    GQueue *files_with_tag;
+    GError *error;
+    GTask *task;
+
+    files_with_tag = g_queue_new ();
+
+    task = g_task_new (object, cancellable, callback, NULL);
+    g_task_set_task_data (task,
+                          files_with_tag,
+                          nautilus_tag_data_queue_free);
+
+    if (!nautilus_tag_queue_has_tag (self->all_tags, tag_name))
+    {
+        error = g_error_new (G_IO_ERROR,
+                             G_IO_ERROR_INVALID_ARGUMENT,
+                             "this tag doesn't exist");
+
+        g_task_return_error (task, error);
+
+        return;
+    }
+
+    query = g_string_new ("SELECT ?url WHERE { ?urn a nfo:FileDataObject ;"
+                          " nie:url ?url ; nao:hasTag ?tag . ");
+
+    g_string_append_printf (query, "?tag nao:prefLabel '%s' }", tag_name);
+
+    start_query_or_update (query,
+                           on_get_files_with_tag_query_callback,
+                           task,
+                           TRUE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+void
+nautilus_tag_manager_remove_tag (NautilusTagManager  *self,
+                                 GObject             *object,
+                                 const gchar         *tag_name,
+                                 GAsyncReadyCallback  callback,
+                                 GCancellable        *cancellable)
+{
+    GString *query;
+    GError *error;
+    GList *l;
+    TagData *tag_data;
+    GTask *task;
+
+    task = g_task_new (object, cancellable, callback, NULL);
+
+    if (!nautilus_tag_queue_has_tag (self->all_tags, tag_name))
+    {
+        error = g_error_new (G_IO_ERROR,
+                             G_IO_ERROR_INVALID_ARGUMENT,
+                             "this tag doesn't exist");
+
+        g_task_return_error (task, error);
+
+        return;
+    }
+
+    query = g_string_new ("");
+    g_string_append_printf (query,
+                            "DELETE { ?urn nao:hasTag ?label } "
+                            "WHERE { ?urn nie:url ?f . "
+                            "?label nao:prefLabel '%s' }\n",
+                            tag_name);
+
+    for (l = g_queue_peek_head_link (self->all_tags); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        if (g_strcmp0 (tag_name, tag_data->name) == 0)
+        {
+            g_string_append_printf (query,
+                                    "DELETE { <%s> a rdfs:Resource }",
+                                    tag_data->id);
+
+            break;
+        }
+    }
+
+    start_query_or_update (query,
+                           on_update_callback,
+                           task,
+                           FALSE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+static GString*
+nautilus_tag_manager_delete_tag (NautilusTagManager  *self,
+                                 GList               *selection,
+                                 GString             *query,
+                                 TagData             *tag_data,
+                                 gboolean             favorite_tag)
+{
+
+    if (!favorite_tag)
+    {
+        if (!nautilus_tag_queue_has_tag (self->all_tags, tag_data->name))
+        {
+            return query;
+        }
+
+        g_string_append (query,
+                         "DELETE { ?urn nao:hasTag ?label } "
+                         "WHERE { ?urn a nfo:FileDataObject ; nie:url ?url . ");
+
+        g_string_append_printf (query,
+                                "?label nao:prefLabel '%s' . ",
+                                tag_data->name);
+    }
+    else
+    {
+        g_string_append (query,
+                         "DELETE { ?urn nao:hasTag nao:predefined-tag-favorite }"
+                         "WHERE { ?urn a nfo:FileDataObject ; nie:url ?url .");
+    }
+
+    query = add_selection_filter (selection, query);
+
+    g_string_append (query, "}\n");
+
+    return query;
+}
+
+static GString*
+nautilus_tag_manager_insert_tag (NautilusTagManager  *self,
+                                 GList               *selection,
+                                 GString             *query,
+                                 TagData             *tag_data,
+                                 gboolean             favorite_tag)
+{
+    g_autofree gchar *tag_color = NULL;
+
+    if (!favorite_tag)
+    {
+        tag_color = parse_color_from_tag_id (tag_data->id);
+
+        if (!nautilus_tag_queue_has_tag (self->all_tags, tag_data->name))
+        {
+            g_string_append_printf (query,
+                                    "INSERT DATA { <org:gnome:nautilus:tag:%s:%s> a nao:Tag ; nao:prefLabel 
'%s' }\n",
+                                    tag_data->name,
+                                    tag_color,
+                                    tag_data->name);
+        }
+
+        query = g_string_append (query,
+                                 "INSERT { ?urn nao:hasTag ?label }"
+                                 "WHERE { ?urn a nfo:FileDataObject ; nie:url ?url . ");
+
+        g_string_append_printf (query, "?label nao:prefLabel '%s'", tag_data->name);
+    }
+    else
+    {
+        g_string_append (query,
+                         "INSERT { ?urn nao:hasTag nao:predefined-tag-favorite }"
+                         "WHERE { ?urn a nfo:FileDataObject ; nie:url ?url .");
+    }
+
+    query = add_selection_filter (selection, query);
+
+    g_string_append (query, "}\n");
+
+    return query;
+}
+
+gboolean
+nautilus_tag_manager_update_tags_finish (GObject      *source_object,
+                                         GAsyncResult *res,
+                                         GError       **error)
+{
+    g_return_val_if_fail (g_task_is_valid (res, source_object), FALSE);
+
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+nautilus_tag_manager_update_tags (NautilusTagManager *self,
+                                  GObject            *object,
+                                  GList              *selection,
+                                  GQueue             *selection_tags,
+                                  GQueue             *new_selection_tags,
+                                  GAsyncReadyCallback callback,
+                                  GCancellable       *cancellable)
+{
+    GString *query;
+    GList *l;
+    TagData *tag_data;
+    gchar *current_tag_name;
+    GTask *task;
+
+    task = g_task_new (object, cancellable, callback, NULL);
+
+    query = g_string_new ("");
+
+    for (l = g_queue_peek_head_link (new_selection_tags); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        if (!nautilus_tag_queue_has_tag (selection_tags, tag_data->name))
+        {
+            query = nautilus_tag_manager_insert_tag (self,
+                                                     selection,
+                                                     query,
+                                                     tag_data,
+                                                     FALSE);
+        }
+    }
+
+    for (l = g_queue_peek_head_link (selection_tags); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        if (!nautilus_tag_queue_has_tag (new_selection_tags, tag_data->name) &&
+            tag_data->name != NULL)
+        {
+            query = nautilus_tag_manager_delete_tag (self,
+                                                     selection,
+                                                     query,
+                                                     tag_data,
+                                                     FALSE);
+        }
+
+        current_tag_name = tag_data->name;
+        while (TRUE)
+        {
+            if (l->next == NULL)
+            {
+                break;
+            }
+
+            tag_data = l->next->data;
+            if (g_strcmp0 (tag_data->name, current_tag_name) == 0)
+            {
+                l = l->next;
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+
+    start_query_or_update (query,
+                           on_update_callback,
+                           task,
+                           FALSE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+gboolean
+nautilus_tag_manager_file_is_favorite (NautilusTagManager *self,
+                                       const gchar        *file_name)
+{
+    return g_hash_table_contains (self->favorite_files, file_name);
+}
+
+static void
+on_get_file_ids_for_urls_cursor_callback (GObject      *object,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+    TrackerSparqlCursor *cursor;
+    GTask *task;
+    gint64 id;
+    const gchar *url;
+    gboolean success;
+    GList *l;
+    gchar *file_url;
+    InsertTaskData *data;
+
+    task = user_data;
+    data = g_task_get_task_data (task);
+
+    cursor = TRACKER_SPARQL_CURSOR (object);
+
+    success = get_query_status (cursor, result, GET_IDS_FOR_URLS, task);
+    if (!success)
+    {
+        return;
+    }
+
+    url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+    id = tracker_sparql_cursor_get_integer (cursor, 1);
+
+    for (l = data->selection; l != NULL; l = l->next)
+    {
+        file_url = nautilus_file_get_uri (NAUTILUS_FILE (l->data));
+
+        if (g_strcmp0 (file_url, url) == 0)
+        {
+            g_hash_table_insert (data->ids,
+                                 g_strdup (url),
+                                 GINT64_TO_POINTER (id));
+
+            g_free (file_url);
+
+            break;
+        }
+
+        g_free (file_url);
+    }
+
+    tracker_sparql_cursor_next_async (cursor,
+                                      g_task_get_cancellable (task),
+                                      on_get_file_ids_for_urls_cursor_callback,
+                                      task);
+
+}
+
+
+static void
+on_get_file_ids_for_urls_query_callback (GObject      *object,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+    GTask *task;
+
+    task = user_data;
+
+    on_query_callback (object,
+                       result,
+                       user_data,
+                       on_get_file_ids_for_urls_cursor_callback,
+                       GET_IDS_FOR_URLS,
+                       g_task_get_cancellable (task));
+}
+
+static void
+nautilus_tag_manager_get_file_ids_for_urls (GObject *object,
+                                            GList   *selection,
+                                            GTask   *task)
+{
+    GString *query;
+
+    query = g_string_new ("SELECT ?url tracker:id(?urn) WHERE { ?urn nie:url ?url; .");
+
+    query = add_selection_filter (selection, query);
+
+    g_string_append (query, "}\n");
+
+    start_query_or_update (query,
+                           on_get_file_ids_for_urls_query_callback,
+                           task,
+                           TRUE,
+                           g_task_get_cancellable (task));
+
+    g_string_free (query, TRUE);
+}
+
+static void
+on_star_file_callback (GObject      *object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+    NautilusTagManager *self;
+    GString *query;
+    InsertTaskData *data;
+    g_autoptr (GError) error = NULL;
+    GTask *task;
+    gchar* uri;
+    gint64 id;
+    GList *l;
+
+    self = NAUTILUS_TAG_MANAGER (object);
+
+    data = nautilus_tag_manager_gpointer_task_finish (object, res, &error);
+    task = user_data;
+    g_clear_object (&task);
+
+    task = g_task_new (data->object, data->cancellable, data->callback, NULL);
+
+    query = g_string_new ("");
+
+    query = nautilus_tag_manager_insert_tag (self,
+                                             data->selection,
+                                             query,
+                                             NULL,
+                                             TRUE);
+
+    for (l = data->selection; l != NULL; l = l->next)
+    {
+        uri = nautilus_file_get_uri (NAUTILUS_FILE (l->data));
+
+        if (g_hash_table_contains (data->ids, uri))
+        {
+            id = GPOINTER_TO_GINT64 (g_hash_table_lookup (data->ids, uri));
+            g_hash_table_insert (self->favorite_files,
+                                 nautilus_file_get_uri (NAUTILUS_FILE (l->data)),
+                                 GINT64_TO_POINTER (id));
+        }
+
+        g_free (uri);
+    }
+    g_signal_emit_by_name (self, "favorites-changed", NULL);
+
+    start_query_or_update (query,
+                           on_update_callback,
+                           task,
+                           FALSE,
+                           g_task_get_cancellable (task));
+
+    g_string_free (query, TRUE);
+}
+
+void
+nautilus_tag_manager_star_files (NautilusTagManager  *self,
+                                 GObject             *object,
+                                 GList               *selection,
+                                 GAsyncReadyCallback  callback,
+                                 GCancellable        *cancellable)
+{
+    GTask *task;
+    InsertTaskData *data;
+
+    data = g_new0 (InsertTaskData, 1);
+    data->selection = nautilus_file_list_copy (selection);
+    data->ids = g_hash_table_new_full (g_str_hash,
+                                       g_str_equal,
+                                       (GDestroyNotify) g_free,
+                                       NULL);
+    data->callback = callback;
+    data->object = object;
+    data->cancellable = cancellable;
+
+    task = g_task_new (self, cancellable, on_star_file_callback, NULL);
+    g_task_set_task_data (task,
+                          data,
+                          destroy_insert_task_data);
+
+    nautilus_tag_manager_get_file_ids_for_urls (G_OBJECT (self), selection, task);
+}
+
+void
+nautilus_tag_manager_unstar_files (NautilusTagManager  *self,
+                                   GObject             *object,
+                                   GList               *selection,
+                                   GAsyncReadyCallback  callback,
+                                   GCancellable        *cancellable)
+{
+    GString *query;
+    GTask *task;
+    NautilusFile *file;
+    gchar *uri = NULL;
+    GList *l;
+
+    file = NAUTILUS_FILE (selection->data);
+    uri = nautilus_file_get_uri (file);
+
+    task = g_task_new (object, cancellable, callback, NULL);
+
+    query = g_string_new ("");
+
+    query = nautilus_tag_manager_delete_tag (self,
+                                             selection,
+                                             query,
+                                             NULL,
+                                             TRUE);
+
+    for (l = selection; l != NULL; l = l->next)
+    {
+        uri = nautilus_file_get_uri (NAUTILUS_FILE (l->data));
+
+        g_hash_table_remove (self->favorite_files, uri);
+
+        g_free (uri);
+    }
+    g_signal_emit_by_name (self, "favorites-changed", NULL);
+
+    start_query_or_update (query,
+                           on_update_callback,
+                           task,
+                           FALSE,
+                           cancellable);
+
+    g_string_free (query, TRUE);
+}
+
+void
+on_tracker_notifier_events(TrackerNotifier *notifier,
+                           GPtrArray       *events,
+                           gpointer         user_data)
+{
+    TrackerNotifierEvent *event;
+    NautilusTagManager *self;
+    int i;
+    const gchar *location_uri;
+    const gchar *new_location_uri;
+    GError *error = NULL;
+    TrackerSparqlConnection *connection;
+    TrackerSparqlCursor *cursor;
+    GString *query;
+    gboolean query_has_results;
+
+    self = NAUTILUS_TAG_MANAGER (user_data);
+
+    for (i = 0; i < events->len; i++)
+    {
+        event = g_ptr_array_index (events, i);
+
+        location_uri = tracker_notifier_event_get_location (event);
+
+        query = g_string_new ("");
+        g_string_append_printf (query,
+                                "SELECT ?url WHERE { ?urn nie:url ?url; nao:hasTag 
nao:predefined-tag-favorite . FILTER (tracker:id(?urn) = %ld)}",
+                                tracker_notifier_event_get_id (event));
+
+        /* check if the file changed it's location and update hash table if so */
+        new_location_uri = nautilus_tag_manager_file_with_id_changed_url (self->favorite_files,
+                                                                          tracker_notifier_event_get_id 
(event),
+                                                                          location_uri);
+        if (new_location_uri)
+        {
+            g_hash_table_remove (self->favorite_files, new_location_uri);
+            g_hash_table_insert (self->favorite_files,
+                                 g_strdup (location_uri),
+                                 GINT64_TO_POINTER (tracker_notifier_event_get_id (event)));
+            g_signal_emit_by_name (self, "favorites-changed", NULL);
+        }
+
+        connection = tracker_sparql_connection_get (NULL, &error);
+
+        if (!connection)
+        {
+            g_printerr ("Couldn't obtain a direct connection to the Tracker store: %s",
+                        error ? error->message : "unknown error");
+            g_clear_error (&error);
+
+            return;
+        }
+
+        cursor = tracker_sparql_connection_query (connection,
+                                                  query->str,
+                                                  NULL,
+                                                  &error);
+
+        if (error)
+        {
+            g_printerr ("Couldn't query the Tracker Store: '%s'", error->message);
+
+            g_clear_error (&error);
+
+            return;
+        }
+
+        if (cursor)
+        {
+            query_has_results = tracker_sparql_cursor_next (cursor, NULL, &error);
+
+            /* if no results are found, then the file isn't marked as favorite.
+             * If needed, update the hashtable.
+             */
+            if (!query_has_results && location_uri && g_hash_table_contains (self->favorite_files, 
location_uri))
+            {
+                g_hash_table_remove (self->favorite_files, location_uri);
+                g_signal_emit_by_name (self, "favorites-changed", NULL);
+            }
+            else if (query_has_results && location_uri && !g_hash_table_contains (self->favorite_files, 
location_uri))
+            {
+                g_hash_table_insert (self->favorite_files,
+                                     g_strdup (location_uri),
+                                     GINT64_TO_POINTER (tracker_notifier_event_get_id (event)));
+                g_signal_emit_by_name (self, "favorites-changed", NULL);
+            }
+
+            g_object_unref (cursor);
+        }
+
+        g_object_unref (connection);
+
+        g_string_free (query, TRUE);
+    }
+
+}
+
+static void
+nautilus_tag_manager_finalize (GObject *object)
+{
+    NautilusTagManager *self;
+
+    self = NAUTILUS_TAG_MANAGER (object);
+
+    if (self->all_tags)
+    {
+        g_queue_free_full (self->all_tags, nautilus_tag_data_free);
+    }
+
+    g_signal_handlers_disconnect_by_func (self->notifier,
+                                          G_CALLBACK (on_tracker_notifier_events),
+                                          self);
+    g_clear_object (&self->notifier);
+
+    g_hash_table_destroy (self->favorite_files);
+
+    G_OBJECT_CLASS (nautilus_tag_manager_parent_class)->finalize (object);
+}
+
+static void
+nautilus_tag_manager_class_init (NautilusTagManagerClass *klass)
+{
+    GObjectClass *oclass;
+
+    oclass = G_OBJECT_CLASS (klass);
+
+    oclass->finalize = nautilus_tag_manager_finalize;
+
+    signals[FAVORITES_CHANGED] = g_signal_new ("favorites-changed",
+                                               NAUTILUS_TYPE_TAG_MANAGER,
+                                               G_SIGNAL_RUN_LAST,
+                                               0,
+                                               NULL,
+                                               NULL,
+                                               g_cclosure_marshal_VOID__VOID,
+                                               G_TYPE_NONE,
+                                               0);
+}
+
+NautilusTagManager* nautilus_tag_manager_new (GCancellable *cancellable_tags,
+                                              GCancellable *cancellable_notifier,
+                                              GCancellable *cancellable_favorite)
+{
+    //gchar *classes[] = { "nao:hasTag", NULL };
+
+    if (tag_manager != NULL)
+    {
+        return g_object_ref (tag_manager);
+    }
+
+    tag_manager = g_object_new (NAUTILUS_TYPE_TAG_MANAGER, NULL);
+    g_object_add_weak_pointer (G_OBJECT (tag_manager), (gpointer)&tag_manager);
+
+    nautilus_tag_manager_query_all_tags (tag_manager, cancellable_tags);
+
+    nautilus_tag_manager_query_favorite_files (tag_manager, cancellable_favorite);
+
+    tag_manager->notifier = tracker_notifier_new (NULL,
+                                                  TRACKER_NOTIFIER_FLAG_QUERY_LOCATION,
+                                                  cancellable_notifier,
+                                                  &tag_manager->notifier_error);
+
+    g_signal_connect (tag_manager->notifier,
+                      "events",
+                      G_CALLBACK (on_tracker_notifier_events),
+                      tag_manager);
+
+    return tag_manager;
+}
+
+static void
+nautilus_tag_manager_init (NautilusTagManager *self)
+{
+    self->favorite_files = g_hash_table_new_full (g_str_hash,
+                                                  g_str_equal,
+                                                  (GDestroyNotify) g_free,
+                                                  NULL);
+}
diff --git a/src/nautilus-tag-manager.h b/src/nautilus-tag-manager.h
new file mode 100644
index 0000000..3aa06ab
--- /dev/null
+++ b/src/nautilus-tag-manager.h
@@ -0,0 +1,113 @@
+/* nautilus-tag-manager.h
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_TAG_MANAGER_H
+#define NAUTILUS_TAG_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_TAG_MANAGER (nautilus_tag_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (NautilusTagManager, nautilus_tag_manager, NAUTILUS, TAG_MANAGER, GObject);
+
+typedef struct
+{
+    gchar *id;
+    gchar *name;
+    gchar *url;
+} TagData;
+
+NautilusTagManager* nautilus_tag_manager_new (GCancellable *cancellable_tags,
+                                              GCancellable *cancellable_notifier,
+                                              GCancellable *cancellable_favorite);
+
+GList* nautilus_tag_manager_get_favorite_files (NautilusTagManager *self);
+
+GQueue* nautilus_tag_manager_get_all_tags (NautilusTagManager *self);
+
+gpointer nautilus_tag_manager_gpointer_task_finish (GObject     *source_object,
+                                                    GAsyncResult *res,
+                                                    GError      **error);
+
+void nautilus_tag_manager_get_selection_tags (GObject            *object,
+                                              GList              *selection,
+                                              GAsyncReadyCallback callback,
+                                              GCancellable       *cancellable);
+
+void nautilus_tag_manager_get_files_with_tag (NautilusTagManager  *self,
+                                              GObject             *object,
+                                              const gchar         *tag_name,
+                                              GAsyncReadyCallback  callback,
+                                              GCancellable        *cancellable);
+
+void nautilus_tag_manager_remove_tag (NautilusTagManager  *self,
+                                      GObject             *object,
+                                      const gchar         *tag_name,
+                                      GAsyncReadyCallback  callback,
+                                      GCancellable        *cancellable);
+
+gboolean nautilus_tag_manager_update_tags_finish (GObject      *source_object,
+                                                  GAsyncResult *res,
+                                                  GError       **error);
+
+void nautilus_tag_manager_update_tags (NautilusTagManager *self,
+                                       GObject            *object,
+                                       GList              *selection,
+                                       GQueue             *selection_tags,
+                                       GQueue             *new_selection_tags,
+                                       GAsyncReadyCallback callback,
+                                       GCancellable       *cancellable);
+
+void nautilus_tag_manager_star_files (NautilusTagManager  *self,
+                                      GObject             *object,
+                                      GList               *selection,
+                                      GAsyncReadyCallback  callback,
+                                      GCancellable        *cancellable);
+
+void nautilus_tag_manager_unstar_files (NautilusTagManager  *self,
+                                        GObject             *object,
+                                        GList               *selection,
+                                        GAsyncReadyCallback  callback,
+                                        GCancellable        *cancellable);
+
+
+gboolean nautilus_tag_manager_file_is_favorite (NautilusTagManager *self,
+                                                const gchar        *file_name);
+
+GQueue* nautilus_tag_copy_tag_queue (GQueue *queue);
+
+gboolean nautilus_tag_queue_has_tag (GQueue      *selection_tags,
+                                     const gchar *tag_name);
+
+void nautilus_tag_data_free (gpointer data);
+
+gchar* parse_color_from_tag_id (const gchar *tag_id);
+
+TagData* nautilus_tag_data_new (const gchar *id,
+                                const gchar *name,
+                                const gchar *url);
+
+
+
+G_END_DECLS
+
+#endif
\ No newline at end of file
diff --git a/src/nautilus-tag-widget.c b/src/nautilus-tag-widget.c
new file mode 100644
index 0000000..73893ce
--- /dev/null
+++ b/src/nautilus-tag-widget.c
@@ -0,0 +1,304 @@
+/* nautilus-tag-widget.c
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-tag-widget.h"
+#include "nautilus-tag-manager.h"
+
+struct _NautilusTagWidget
+{
+    GtkEventBox parent;
+
+    GtkWidget *box;
+    GtkWidget *label;
+    GtkWidget *circle;
+
+    gboolean cursor_over;
+
+    gchar *color;
+    TagData *tag_data;
+};
+
+G_DEFINE_TYPE (NautilusTagWidget, nautilus_tag_widget, GTK_TYPE_EVENT_BOX);
+
+GtkWidget*
+nautilus_tag_widget_queue_get_tag_with_name (GQueue      *queue,
+                                             const gchar *tag_name)
+{
+    GList *l;
+    NautilusTagWidget *tag_widget;
+
+    for (l = g_queue_peek_head_link (queue); l != NULL; l = l->next)
+    {
+        tag_widget = NAUTILUS_TAG_WIDGET (l->data);
+
+        if (g_strcmp0 (tag_name, tag_widget->tag_data->name) == 0)
+        {
+            return GTK_WIDGET (tag_widget);
+        }
+    }
+
+    return NULL;
+}
+
+gboolean
+nautilus_tag_widget_queue_contains_tag (GQueue      *queue,
+                                        const gchar *tag_name)
+{
+    GList *l;
+    NautilusTagWidget *tag_widget;
+
+    for (l = g_queue_peek_head_link (queue); l != NULL; l = l->next)
+    {
+        tag_widget = NAUTILUS_TAG_WIDGET (l->data);
+
+        if (g_strcmp0 (tag_name, tag_widget->tag_data->name) == 0)
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+const gchar*
+nautilus_tag_widget_get_tag_name (NautilusTagWidget *self)
+{
+    return self->tag_data->name;
+}
+
+const gchar*
+nautilus_tag_widget_get_tag_id (NautilusTagWidget *self)
+{
+    return self->tag_data->id;
+}
+
+static gboolean
+paint_circle (GtkWidget *widget,
+              cairo_t   *cr,
+              gpointer   data)
+{
+    guint width, height;
+    GdkRGBA color;
+    GtkStyleContext *context;
+    NautilusTagWidget *tag_widget;
+    /*GtkIconInfo *info;
+    GdkPixbuf *pixbuf;
+    gint icon_size;
+    gint scale_factor;*/
+
+    tag_widget = NAUTILUS_TAG_WIDGET (data);
+
+    context = gtk_widget_get_style_context (widget);
+
+    width = gtk_widget_get_allocated_width (widget);
+    height = gtk_widget_get_allocated_height (widget);
+
+    gtk_render_background (context, cr, 0, 0, width, height);
+
+    cairo_arc (cr,
+               width / 2.0, height / 2.0,
+               MIN (width, height) / 2.0,
+               0, 2 * G_PI);
+
+    gtk_style_context_get_color (context,
+                                 gtk_style_context_get_state (context),
+                                 &color);
+
+
+    gdk_rgba_parse (&color, tag_widget->color);
+
+    gdk_cairo_set_source_rgba (cr, &color);
+
+    cairo_fill (cr);
+
+    if (tag_widget->cursor_over)
+    {
+
+        /*gtk_icon_size_lookup (GTK_ICON_SIZE_MENU,
+                              &icon_size, NULL);
+        scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (tag_widget->label));
+
+        info = gtk_icon_theme_lookup_icon_for_scale (gtk_icon_theme_get_default (),
+                                                     "window-close-symbolic",
+                                                     icon_size, scale_factor,
+                                                     GTK_ICON_LOOKUP_GENERIC_FALLBACK);
+
+        pixbuf = gtk_icon_info_load_symbolic_for_context (info, context, NULL, NULL);
+        g_print("%d %d\n", width, height);
+        width = 0;
+        height = 0;
+        gdk_cairo_set_source_pixbuf (cr, pixbuf, width / 2, height / 2);
+
+        cairo_paint (cr);
+
+        g_object_unref (info);
+        g_object_unref (pixbuf);*/
+
+        gtk_style_context_get_color (context,
+                                     gtk_style_context_get_state (context),
+                                     &color);
+
+        gdk_cairo_set_source_rgba (cr, &color);
+
+        cairo_move_to (cr,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 * -1 + width / 
2.0,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 + height / 
2.0);
+        cairo_line_to (cr,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 + width / 2.0,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 * -1 + height 
/ 2.0);
+
+        cairo_stroke (cr);
+
+        cairo_move_to (cr,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 + width / 2.0,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 + height / 
2.0);
+        cairo_line_to (cr,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 * -1 + width / 
2.0,
+                       (MIN (width, height) - MIN (width, height) * 0.25) / 2.0 * G_SQRT2 / 2 * -1 + height 
/ 2.0);
+
+        cairo_stroke (cr);
+    }
+
+    return FALSE;
+}
+
+gboolean
+on_leave_event (GtkWidget *widget,
+                GdkEvent  *event,
+                gpointer   user_data)
+{
+    NautilusTagWidget *self;
+
+    self = NAUTILUS_TAG_WIDGET (widget);
+
+    if (self->cursor_over == TRUE)
+    {
+        gtk_widget_queue_draw (self->circle);
+
+        gtk_label_set_label (GTK_LABEL (self->label), self->tag_data->name);
+    }
+
+    self->cursor_over = FALSE;
+
+    return FALSE;
+}
+
+gboolean
+on_motion_event (GtkWidget *widget,
+                 GdkEvent  *event,
+                 gpointer   user_data)
+{
+    NautilusTagWidget *self;
+    g_autofree gchar *markup = NULL;
+
+    self = NAUTILUS_TAG_WIDGET (widget);
+
+    if (self->cursor_over == FALSE)
+    {
+        gtk_widget_queue_draw (self->circle);
+
+        markup = g_markup_printf_escaped ("<u>%s</u>", self->tag_data->name);
+        gtk_label_set_label (GTK_LABEL (self->label), markup);
+    }
+
+    self->cursor_over = TRUE;
+
+    return FALSE;
+}
+
+
+static void
+nautilus_tag_widget_finalize (GObject *object)
+{
+    NautilusTagWidget *self;
+
+    self = NAUTILUS_TAG_WIDGET (object);
+
+    g_free (self->color);
+    nautilus_tag_data_free (self->tag_data);
+
+    G_OBJECT_CLASS (nautilus_tag_widget_parent_class)->finalize (object);
+}
+
+static void
+nautilus_tag_widget_class_init (NautilusTagWidgetClass *klass)
+{
+    //GtkWidgetClass *widget_class;
+    GObjectClass *oclass;
+
+    //widget_class = GTK_WIDGET_CLASS (klass);
+    oclass = G_OBJECT_CLASS (klass);
+
+    oclass->finalize = nautilus_tag_widget_finalize;
+}
+
+GtkWidget* nautilus_tag_widget_new (const gchar   *tag_label,
+                                    const gchar   *tag_id,
+                                    gboolean       can_close)
+{
+    NautilusTagWidget *self;
+    self = g_object_new (NAUTILUS_TYPE_TAG_WIDGET,
+                         NULL);
+
+    self->color = parse_color_from_tag_id (tag_id);
+
+    self->tag_data = nautilus_tag_data_new (tag_id, tag_label, NULL);
+
+    self->label = gtk_label_new (tag_label);
+    gtk_label_set_use_markup (GTK_LABEL (self->label), TRUE);
+
+    self->box = g_object_new (GTK_TYPE_BOX,
+                              "orientation",
+                              GTK_ORIENTATION_HORIZONTAL,
+                              "spacing",
+                              5,
+                              NULL);
+
+    gtk_widget_add_events (GTK_WIDGET (self), GDK_POINTER_MOTION_MASK);
+
+    if (can_close)
+    {
+        g_signal_connect (self,
+                          "motion-notify-event",
+                          G_CALLBACK (on_motion_event),
+                          NULL);
+
+        g_signal_connect (self,
+                          "leave-notify-event",
+                          G_CALLBACK (on_leave_event),
+                          NULL);
+    }
+
+    self->circle = gtk_drawing_area_new ();
+    gtk_widget_set_size_request (self->circle, 15, 15);
+    g_signal_connect (self->circle, "draw",
+                      G_CALLBACK (paint_circle), self);
+
+    gtk_box_pack_start (GTK_BOX (self->box), self->circle, FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (self->box), self->label, FALSE, FALSE, 0);
+
+    gtk_container_add (GTK_CONTAINER (self), self->box);
+
+    return GTK_WIDGET (self);
+}
+
+static void
+nautilus_tag_widget_init (NautilusTagWidget *self)
+{
+
+}
diff --git a/src/nautilus-tag-widget.h b/src/nautilus-tag-widget.h
new file mode 100644
index 0000000..01ea881
--- /dev/null
+++ b/src/nautilus-tag-widget.h
@@ -0,0 +1,51 @@
+/* nautilus-tag-widget.h
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_TAG_WIDGET_H
+#define NAUTILUS_TAG_WIDGET_H
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "nautilus-files-view.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_TAG_WIDGET (nautilus_tag_widget_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusTagWidget, nautilus_tag_widget, NAUTILUS, TAG_WIDGET, GtkEventBox);
+
+GtkWidget*    nautilus_tag_widget_new    (const gchar   *tag_label,
+                                          const gchar   *tag_id,
+                                          gboolean       can_close);
+
+const gchar*  nautilus_tag_widget_get_tag_name (NautilusTagWidget *self);
+
+const gchar*  nautilus_tag_widget_get_tag_id (NautilusTagWidget *self);
+
+GtkWidget* nautilus_tag_widget_queue_get_tag_with_name (GQueue      *queue,
+                                                        const gchar *tag_name);
+
+gboolean      nautilus_tag_widget_queue_contains_tag (GQueue      *queue,
+                                                      const gchar *tag_name);
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/nautilus-tags-dialog.c b/src/nautilus-tags-dialog.c
new file mode 100644
index 0000000..db194aa
--- /dev/null
+++ b/src/nautilus-tags-dialog.c
@@ -0,0 +1,620 @@
+/* nautilus-tags-dialog.c
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-tags-dialog.h"
+#include "nautilus-tag-manager.h"
+#include "nautilus-tag-widget.h"
+
+typedef enum
+{
+    NO_ACTION,
+    UPDATE_TAGS
+} ChosenAction;
+
+struct _NautilusTagsDialog
+{
+    GtkDialog parent;
+
+    NautilusWindow *window;
+    GList *selection;
+
+    GtkWidget *cancel_button;
+    GtkWidget *update_tags_button;
+    GtkWidget *tags_entry;
+    GtkWidget *color_button;
+    GtkWidget *tags_listbox;
+    GtkWidget *selection_tags_box;
+    GtkWidget *add_tag_button;
+
+    NautilusTagManager *tag_manager;
+    GQueue *all_tags;
+    GQueue *selection_tags;
+    GQueue *new_selection_tags;
+
+    /* queues that represent all the tags */
+    GQueue *list_box_rows;
+    GQueue *all_tags_widgets;
+    /* initilay it has tags widgets that the selection
+     * has, but updates when a tag is removed/added */
+    GQueue *selection_tags_widgets;
+
+    ChosenAction action;
+
+    GCancellable *cancellable_selection;
+    GCancellable *cancellable_update;
+    GCancellable *cancellable_get_files;
+};
+
+G_DEFINE_TYPE (NautilusTagsDialog, nautilus_tags_dialog, GTK_TYPE_DIALOG);
+
+static void on_selection_tags_obtained (GObject      *object,
+                                        GAsyncResult *res,
+                                        gpointer      user_data);
+
+static void on_tags_updated (GObject      *object,
+                             GAsyncResult *res,
+                             gpointer      user_data);
+
+static void
+update_tags (NautilusTagsDialog *dialog)
+{
+    GQueue *all_tags_check;
+    GList *l;
+    GList *l_check;
+    TagData *tag_data;
+    TagData *tag_data_check;
+
+    all_tags_check = nautilus_tag_manager_get_all_tags (dialog->tag_manager);
+
+    for (l = g_queue_peek_head_link (dialog->all_tags), l_check = g_queue_peek_head_link (all_tags_check);
+        l != NULL && l_check != NULL; l = l->next, l_check = l_check->next)
+    {
+        tag_data = l->data;
+        tag_data_check = l_check->data;
+
+        if (g_strcmp0 (tag_data->id, tag_data_check->id) != 0 ||
+            g_strcmp0 (tag_data->name, tag_data_check->name) != 0)
+        {
+            return;
+        }
+    }
+
+    nautilus_tag_manager_get_selection_tags (G_OBJECT (dialog),
+                                             dialog->selection,
+                                             on_selection_tags_obtained,
+                                             dialog->cancellable_selection);
+}
+
+static gint
+get_tag_position_in_listbox (NautilusTagsDialog *dialog,
+                             const gchar        *tag_name)
+{
+    GList *l;
+    gint index = 0;
+    g_autofree gchar *casefold_tag_name = NULL;
+    gchar *casefold_widget_tag_name;
+
+    casefold_tag_name = g_utf8_casefold (tag_name, -1);
+
+    for (l = g_queue_peek_head_link (dialog->all_tags_widgets), index = 0;
+        l != NULL;
+        l = l->next, index++)
+    {
+        casefold_widget_tag_name = g_utf8_casefold (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET 
(l->data)), -1);
+
+        if (g_strcmp0 (casefold_tag_name, casefold_widget_tag_name) < 0)
+        {
+            g_free (casefold_widget_tag_name);
+
+            return index;
+        }
+
+        g_free (casefold_widget_tag_name);
+    }
+
+    return index;
+}
+
+static gboolean
+on_tag_widget_button_press_event (GtkWidget *old_tag_widget,
+                                  GdkEvent  *event,
+                                  gpointer   user_data)
+{
+    NautilusTagsDialog *dialog;
+    gboolean got_button;
+    guint button;
+    GtkWidget *row;
+    GtkWidget *tag_widget;
+    gint index;
+
+    dialog = NAUTILUS_TAGS_DIALOG (user_data);
+
+    got_button = gdk_event_get_button (event, &button);
+
+    if (got_button && button == GDK_BUTTON_PRIMARY)
+    {
+        if (nautilus_tag_queue_has_tag (dialog->all_tags,
+            nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET (old_tag_widget))))
+        {
+            row = gtk_list_box_row_new ();
+
+            tag_widget = nautilus_tag_widget_new (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET 
(old_tag_widget)),
+                                                  nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET 
(old_tag_widget)),
+                                                  FALSE);
+
+            g_queue_push_tail (dialog->list_box_rows, row);
+
+            gtk_container_add (GTK_CONTAINER (row), tag_widget);
+            gtk_widget_show_all (row);
+
+            index = get_tag_position_in_listbox (dialog,
+                                                 nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET 
(old_tag_widget)));
+
+            gtk_list_box_insert (GTK_LIST_BOX (dialog->tags_listbox),
+                                 row,
+                                 index);
+
+            g_queue_push_nth (dialog->all_tags_widgets, tag_widget, index);
+        }
+
+        g_queue_remove (dialog->selection_tags_widgets, old_tag_widget);
+
+        gtk_widget_destroy (old_tag_widget);
+    }
+
+    return FALSE;
+}
+
+static void
+on_row_activated (GtkListBox    *box,
+                  GtkListBoxRow *row,
+                  gpointer       user_data)
+{
+    NautilusTagsDialog *dialog;
+
+    dialog = NAUTILUS_TAGS_DIALOG (user_data);
+
+    GtkWidget *old_tag_widget;
+    GtkWidget *tag_widget;
+
+    old_tag_widget = gtk_bin_get_child (GTK_BIN (row));
+
+    tag_widget = nautilus_tag_widget_new (nautilus_tag_widget_get_tag_name (NAUTILUS_TAG_WIDGET 
(old_tag_widget)),
+                                          nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET 
(old_tag_widget)),
+                                          TRUE);
+
+    g_signal_connect (tag_widget,
+                      "button-press-event",
+                      G_CALLBACK (on_tag_widget_button_press_event),
+                      dialog);
+
+    gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget);
+    gtk_widget_show_all (dialog->selection_tags_box);
+
+    g_queue_push_tail (dialog->selection_tags_widgets, tag_widget);
+
+    g_queue_remove (dialog->all_tags_widgets, old_tag_widget);
+    g_queue_remove (dialog->list_box_rows, row);
+    gtk_widget_destroy (GTK_WIDGET (row));
+}
+
+static void
+fill_selection_box (NautilusTagsDialog *dialog)
+{
+    GList *l;
+    GtkWidget *tag_widget;
+    TagData *tag_data;
+    gint index;
+    GtkListBoxRow *row;
+
+    dialog->selection_tags_widgets = g_queue_new ();
+
+    for (l = g_queue_peek_head_link (dialog->selection_tags); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        if (nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets,
+            tag_data->name) || tag_data->name == NULL)
+        {
+            continue;
+        }
+
+        if (nautilus_tag_widget_queue_contains_tag (dialog->all_tags_widgets,
+            tag_data->name))
+        {
+            tag_widget = nautilus_tag_widget_queue_get_tag_with_name (dialog->all_tags_widgets,
+                                                                      tag_data->name);
+
+            index = g_queue_index (dialog->all_tags_widgets, tag_widget);
+            g_queue_remove (dialog->all_tags_widgets, tag_widget);
+
+            row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(dialog->tags_listbox), index);
+            g_queue_remove (dialog->list_box_rows, row);
+            gtk_widget_destroy (GTK_WIDGET (row));
+        }
+
+        tag_widget = nautilus_tag_widget_new (tag_data->name, tag_data->id, TRUE);
+
+        g_signal_connect (tag_widget,
+                          "button-press-event",
+                          G_CALLBACK (on_tag_widget_button_press_event),
+                          dialog);
+
+        gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget);
+
+        g_queue_push_tail (dialog->selection_tags_widgets, tag_widget);
+    }
+
+    gtk_widget_show_all (dialog->selection_tags_box);
+
+
+    if (!nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets,
+        gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))) &&
+        g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), "") != 0)
+    {
+        gtk_widget_set_sensitive (dialog->add_tag_button, TRUE);
+    }
+}
+
+static void
+fill_list_box (NautilusTagsDialog *dialog)
+{
+    GList *l;
+    TagData *tag_data;
+    GtkWidget *row;
+    GtkWidget *tag_widget;
+
+    dialog->all_tags_widgets = g_queue_new ();
+
+    for (l = g_queue_peek_head_link (dialog->all_tags); l != NULL; l = l->next)
+    {
+        tag_data = l->data;
+
+        row = gtk_list_box_row_new ();
+
+        tag_widget = nautilus_tag_widget_new (tag_data->name, tag_data->id, FALSE);
+
+        g_queue_push_tail (dialog->list_box_rows, row);
+
+        gtk_container_add (GTK_CONTAINER (row), tag_widget);
+        gtk_widget_show_all (row);
+
+        gtk_container_add (GTK_CONTAINER (dialog->tags_listbox), row);
+
+        g_queue_push_tail (dialog->all_tags_widgets, tag_widget);
+    }
+}
+
+static void
+nautilus_tags_dialog_on_response (NautilusTagsDialog *dialog,
+                                  gint                response_id,
+                                  gpointer            user_data)
+{
+    if (response_id == GTK_RESPONSE_OK)
+    {
+        dialog->action = UPDATE_TAGS;
+
+        update_tags (dialog);
+    }
+    else
+    {
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+    }
+}
+
+static void
+on_selection_tags_obtained (GObject      *object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+    NautilusTagsDialog *dialog;
+    g_autoptr (GError) error = NULL;
+    TagData *tag_data;
+    GQueue *selection_tags_check;
+    GList *l_check;
+    GList *l;
+    TagData *tag_data_check;
+    NautilusTagWidget *tag_widget;
+    GTask *task;
+
+    dialog = NAUTILUS_TAGS_DIALOG (object);
+
+    if (dialog->action == NO_ACTION)
+    {
+        dialog->selection_tags = nautilus_tag_manager_gpointer_task_finish (object, res, &error);
+
+        fill_selection_box (dialog);
+    }
+    else
+    {
+        selection_tags_check = nautilus_tag_manager_gpointer_task_finish (object, res, &error);
+
+        for (l = g_queue_peek_head_link (dialog->selection_tags), l_check = g_queue_peek_head_link 
(selection_tags_check);
+            l != NULL && l_check != NULL; l = l->next, l_check = l_check->next)
+        {
+            tag_data = l->data;
+            tag_data_check = l_check->data;
+
+            if (g_strcmp0 (tag_data->id, tag_data_check->id) != 0 ||
+                g_strcmp0 (tag_data->name, tag_data_check->name) != 0)
+            {
+                return;
+            }
+        }
+
+        dialog->new_selection_tags = g_queue_new ();
+
+        for (l = g_queue_peek_head_link (dialog->selection_tags_widgets); l != NULL; l = l->next)
+        {
+            tag_widget = NAUTILUS_TAG_WIDGET (l->data);
+
+            tag_data = nautilus_tag_data_new (nautilus_tag_widget_get_tag_id (tag_widget),
+                                              nautilus_tag_widget_get_tag_name (tag_widget),
+                                              NULL);
+
+            g_queue_push_tail (dialog->new_selection_tags, tag_data);
+        }
+
+        nautilus_tag_manager_update_tags (dialog->tag_manager,
+                                          G_OBJECT (dialog),
+                                          dialog->selection,
+                                          dialog->selection_tags,
+                                          dialog->new_selection_tags,
+                                          on_tags_updated,
+                                          dialog->cancellable_update);
+    }
+
+    task = user_data;
+    g_clear_object (&task);
+}
+
+static void
+on_tags_updated (GObject      *object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+    NautilusTagsDialog *dialog;
+    g_autoptr (GError) error = NULL;
+    gboolean result;
+    GTask *task;
+
+    dialog = NAUTILUS_TAGS_DIALOG (object);
+
+    result = nautilus_tag_manager_update_tags_finish (object, res, &error);
+
+    task = user_data;
+    g_clear_object (&task);
+
+    if (!result)
+    {
+        g_warning ("something went wrong while updating tags");
+    }
+
+    gtk_widget_destroy (GTK_WIDGET (dialog));
+
+}
+
+static void
+on_tags_entry_activate (NautilusTagsDialog *dialog)
+{
+    if (gtk_widget_get_sensitive (dialog->add_tag_button))
+    {
+        g_signal_emit_by_name (dialog->add_tag_button, "clicked", dialog);
+    }
+}
+
+static void
+on_tags_entry_changed (NautilusTagsDialog *dialog)
+{
+    /* check if selection has this tag and update add button acordingly */
+
+    if (nautilus_tag_widget_queue_contains_tag (dialog->selection_tags_widgets,
+        gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))) ||
+        g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), "") == 0 ||
+        dialog->selection_tags_widgets == NULL)
+    {
+        gtk_widget_set_sensitive (dialog->add_tag_button, FALSE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive (dialog->add_tag_button, TRUE);
+    }
+}
+
+static void
+on_add_button_clicked (GtkButton *button,
+                       gpointer   user_data)
+{
+    NautilusTagsDialog *dialog;
+    g_autofree gchar *tag_id = NULL;
+    g_autofree gchar *color_rgb = NULL;
+    const gchar *const_tag_id;
+    GdkRGBA color;
+    GtkWidget *tag_widget;
+    GtkWidget *old_tag_widget;
+    GtkListBoxRow *row;
+    gint index;
+
+    dialog = NAUTILUS_TAGS_DIALOG (user_data);
+
+    /* if tag exists but it's not in selection: move from listbox to box */
+    if (nautilus_tag_queue_has_tag (dialog->all_tags,
+        gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry))))
+    {
+        old_tag_widget = nautilus_tag_widget_queue_get_tag_with_name (dialog->all_tags_widgets,
+                                                                      gtk_entry_get_text (GTK_ENTRY 
(dialog->tags_entry)));
+
+        const_tag_id = nautilus_tag_widget_get_tag_id (NAUTILUS_TAG_WIDGET (old_tag_widget));
+
+        tag_widget = nautilus_tag_widget_new (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)),
+                                              const_tag_id,
+                                              TRUE);
+
+        index = g_queue_index (dialog->all_tags_widgets, old_tag_widget);
+        g_queue_remove (dialog->all_tags_widgets, old_tag_widget);
+
+        row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(dialog->tags_listbox), index);
+        g_queue_remove (dialog->list_box_rows, row);
+        gtk_widget_destroy (GTK_WIDGET (row));
+    }
+    else
+    {
+        /* if tag doesn't exist and it's not in selection: create new widget for box */
+        gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog->color_button), &color);
+        color_rgb = gdk_rgba_to_string (&color);
+        tag_id = g_strdup_printf ("org:gnome:nautilus:tag:%s:%s",
+                                  gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)),
+                                  color_rgb);
+
+        tag_widget = nautilus_tag_widget_new (gtk_entry_get_text (GTK_ENTRY (dialog->tags_entry)), tag_id, 
TRUE);
+    }
+
+    g_signal_connect (tag_widget,
+                      "button-press-event",
+                      G_CALLBACK (on_tag_widget_button_press_event),
+                      dialog);
+
+    gtk_container_add (GTK_CONTAINER (dialog->selection_tags_box), tag_widget);
+
+    g_queue_push_tail (dialog->selection_tags_widgets, tag_widget);
+
+    gtk_widget_show_all (dialog->selection_tags_box);
+
+    gtk_entry_set_text (GTK_ENTRY (dialog->tags_entry), "");
+}
+
+static void
+nautilus_tags_dialog_finalize (GObject *object)
+{
+    NautilusTagsDialog *dialog;
+
+    dialog = NAUTILUS_TAGS_DIALOG (object);
+
+    g_cancellable_cancel (dialog->cancellable_selection);
+    g_clear_object (&dialog->cancellable_selection);
+
+    g_cancellable_cancel (dialog->cancellable_update);
+    g_clear_object (&dialog->cancellable_update);
+
+    g_cancellable_cancel (dialog->cancellable_get_files);
+    g_clear_object (&dialog->cancellable_get_files);
+
+    if (dialog->all_tags)
+    {
+        g_queue_free_full (dialog->all_tags, nautilus_tag_data_free);
+    }
+    if (dialog->selection_tags)
+    {
+        g_queue_free_full (dialog->selection_tags, nautilus_tag_data_free);
+    }
+    if (dialog->new_selection_tags)
+    {
+        g_queue_free_full (dialog->new_selection_tags, nautilus_tag_data_free);
+    }
+    if (dialog->selection_tags_widgets)
+    {
+        g_queue_free (dialog->selection_tags_widgets);
+    }
+    if (dialog->all_tags_widgets)
+    {
+        g_queue_free (dialog->all_tags_widgets);
+    }
+    if (dialog->list_box_rows)
+    {
+        g_queue_free (dialog->list_box_rows);
+    }
+
+    g_clear_object (&dialog->tag_manager);
+
+    G_OBJECT_CLASS (nautilus_tags_dialog_parent_class)->finalize (object);
+}
+
+static void
+nautilus_tags_dialog_class_init (NautilusTagsDialogClass *klass)
+{
+    GtkWidgetClass *widget_class;
+    GObjectClass *oclass;
+
+    widget_class = GTK_WIDGET_CLASS (klass);
+    oclass = G_OBJECT_CLASS (klass);
+
+    oclass->finalize = nautilus_tags_dialog_finalize;
+
+    gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-tags-dialog.ui");
+
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, cancel_button);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, update_tags_button);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, tags_entry);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, color_button);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, tags_listbox);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, selection_tags_box);
+    gtk_widget_class_bind_template_child (widget_class, NautilusTagsDialog, add_tag_button);
+
+    gtk_widget_class_bind_template_callback (widget_class, nautilus_tags_dialog_on_response);
+    gtk_widget_class_bind_template_callback (widget_class, on_tags_entry_activate);
+    gtk_widget_class_bind_template_callback (widget_class, on_tags_entry_changed);
+    gtk_widget_class_bind_template_callback (widget_class, on_add_button_clicked);
+}
+
+GtkWidget* nautilus_tags_dialog_new (GList          *selection,
+                                     NautilusWindow *window)
+{
+    NautilusTagsDialog *dialog;
+
+    dialog = g_object_new (NAUTILUS_TYPE_TAGS_DIALOG, "use-header-bar", TRUE, NULL);
+
+    dialog->window = window;
+    dialog->selection = selection;
+
+    gtk_window_set_transient_for (GTK_WINDOW (dialog),
+                                  GTK_WINDOW (window));
+
+    g_signal_connect (dialog->tags_listbox, "row-activated", G_CALLBACK (on_row_activated), dialog);
+
+    dialog->cancellable_selection = g_cancellable_new ();
+    dialog->cancellable_update = g_cancellable_new ();
+
+    dialog->tag_manager = nautilus_tag_manager_new (NULL, NULL, NULL);
+
+    dialog->all_tags = nautilus_tag_manager_get_all_tags (dialog->tag_manager);
+
+    fill_list_box (dialog);
+
+    dialog->action = NO_ACTION;
+
+    nautilus_tag_manager_get_selection_tags (G_OBJECT (dialog),
+                                             selection,
+                                             on_selection_tags_obtained,
+                                             dialog->cancellable_selection);
+
+    return GTK_WIDGET (dialog);
+}
+
+static void
+nautilus_tags_dialog_init (NautilusTagsDialog *self)
+{
+    GdkRGBA color;
+
+    gtk_widget_init_template (GTK_WIDGET (self));
+
+    self->list_box_rows = g_queue_new ();
+
+    gdk_rgba_parse (&color, "rgb(220,220,220)");
+    gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (self->color_button),
+                                &color);
+}
\ No newline at end of file
diff --git a/src/nautilus-tags-dialog.h b/src/nautilus-tags-dialog.h
new file mode 100644
index 0000000..1968bc6
--- /dev/null
+++ b/src/nautilus-tags-dialog.h
@@ -0,0 +1,39 @@
+/* nautilus-tags-dialog.h
+ *
+ * Copyright (C) 2017 Alexandru Pandelea <alexandru pandelea gmail com>
+ *
+ * This program 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 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_TAGS_DIALOG_H
+#define NAUTILUS_TAGS_DIALOG_H
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "nautilus-files-view.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_TAGS_DIALOG (nautilus_tags_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusTagsDialog, nautilus_tags_dialog, NAUTILUS, TAGS_DIALOG, GtkDialog);
+
+GtkWidget*    nautilus_tags_dialog_new    (GList           *selection,
+                                           NautilusWindow  *window);
+
+G_END_DECLS
+
+#endif
\ No newline at end of file
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index b2c6a82..0e6367a 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -1156,6 +1156,19 @@ places_sidebar_show_other_locations_with_flags (NautilusWindow     *window,
     g_object_unref (location);
 }
 
+static void
+places_sidebar_show_starred_location (NautilusWindow    *window,
+                                      GtkPlacesOpenFlags open_flags)
+{
+    GFile *location;
+
+    location = g_file_new_for_uri ("favorites:///");
+
+    open_location_cb (window, location, open_flags);
+
+    g_object_unref (location);
+}
+
 static GList *
 build_selection_list_from_gfile_list (GList *gfile_list)
 {
@@ -2967,6 +2980,7 @@ nautilus_window_class_init (NautilusWindowClass *class)
     gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_close);
 
     gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_other_locations_with_flags);
+    gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_starred_location);
 
     properties[PROP_DISABLE_CHROME] =
         g_param_spec_boolean ("disable-chrome",
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index 720bfa5..7f3a5e9 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -20,6 +20,7 @@
     <file>ui/nautilus-trash-is-empty.ui</file>
     <file>gtk/help-overlay.ui</file>
     <file>ui/nautilus-batch-rename-dialog.ui</file>
+    <file>ui/nautilus-tags-dialog.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesview.ui">../gtk/nautilusgtkplacesview.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesviewrow.ui">../gtk/nautilusgtkplacesviewrow.ui</file>
     <file alias="icons/thumbnail_frame.png">../../icons/thumbnail_frame.png</file>
diff --git a/src/resources/ui/nautilus-files-view-context-menus.ui 
b/src/resources/ui/nautilus-files-view-context-menus.ui
index 4a53de4..377b71f 100644
--- a/src/resources/ui/nautilus-files-view-context-menus.ui
+++ b/src/resources/ui/nautilus-files-view-context-menus.ui
@@ -253,6 +253,23 @@
     </section>
     <section>
       <item>
+        <attribute name="label" translatable="yes">Tags</attribute>
+        <attribute name="action">view.edit-tags</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Star</attribute>
+        <attribute name="action">view.star</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Unstar</attribute>
+        <attribute name="action">view.unstar</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
         <attribute name="label" translatable="yes">P_roperties</attribute>
         <attribute name="action">view.properties</attribute>
       </item>
diff --git a/src/resources/ui/nautilus-tags-dialog.ui b/src/resources/ui/nautilus-tags-dialog.ui
new file mode 100644
index 0000000..5139c7c
--- /dev/null
+++ b/src/resources/ui/nautilus-tags-dialog.ui
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="NautilusTagsDialog" parent="GtkDialog">
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="destroy_with_parent">True</property>
+    <signal name="response" handler="nautilus_tags_dialog_on_response"/>
+    <child type="action">
+      <object class="GtkButton" id="cancel_button">
+        <property name="label" translatable="yes">_Cancel</property>
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="use_underline">True</property>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="update_tags_button">
+        <property name="label" translatable="yes">_Update Tags</property>
+        <property name="visible">True</property>
+        <property name="use_underline">True</property>
+        <style>
+          <class name="suggested-action"/>
+        </style>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="ok">update_tags_button</action-widget>
+      <action-widget response="cancel">cancel_button</action-widget>
+    </action-widgets>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <property name="border-width">0</property>
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="margin">10</property>
+            <property name="row-spacing">6</property>
+            <property name="column-spacing">6</property>
+            <property name="hexpand">True</property>
+            <property name="row-homogeneous">False</property>
+            <property name="column-homogeneous">False</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolled_tags">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">False</property>
+                <property name="vexpand">True</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkBox" id="selection_tags_box">
+                        <property name="orientation">horizontal</property>
+                        <property name="visible">True</property>
+                        <property name="spacing">5</property>
+                        <property name="halign">start</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="orientation">horizontal</property>
+                <property name="spacing">0</property>
+                <property name="visible">True</property>
+                <property name="halign">center</property>
+                <child>
+                  <object class="GtkEntry" id="tags_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_focus">True</property>
+                    <property name="width_request">250</property>
+                    <property name="hexpand">True</property>
+                    <property name="activates-default">True</property>
+                    <signal name="activate" handler="on_tags_entry_activate" swapped="yes" />
+                    <signal name="changed" handler="on_tags_entry_changed" swapped="yes" />
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="add_tag_button">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="label" translatable="yes">Add</property>
+                    <signal name="clicked" handler="on_add_button_clicked" swapped="no" />
+                  </object>
+                </child>
+                <style>
+                  <class name="linked"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="orientation">horizontal</property>
+                <child>
+                  <object class="GtkLabel" id="color_label">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">New Tag Color: </property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkColorButton" id="color_button">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">2</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolled_window">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hexpand">False</property>
+            <property name="vexpand">True</property>
+            <property name="shadow_type">in</property>
+            <property name="max-content-height">200</property>
+            <property name="min-content-height">200</property>
+            <property name="max-content-width">150</property>
+            <property name="min-content-width">150</property>
+            <child>
+              <object class="GtkViewport">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkListBox" id="tags_listbox">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
\ No newline at end of file
diff --git a/src/resources/ui/nautilus-window.ui b/src/resources/ui/nautilus-window.ui
index 2fc5ffc..74e8c62 100644
--- a/src/resources/ui/nautilus-window.ui
+++ b/src/resources/ui/nautilus-window.ui
@@ -34,7 +34,9 @@
                     <property name="visible">True</property>
                     <property name="populate-all">True</property>
                     <property name="show-other-locations">True</property>
+                    <property name="show-starred-location">True</property>
                     <signal name="show-other-locations-with-flags" 
handler="places_sidebar_show_other_locations_with_flags" object="NautilusWindow" swapped="yes" />
+                    <signal name="show-starred-location" handler="places_sidebar_show_starred_location" 
object="NautilusWindow" swapped="yes" />
                   </object>
                   <packing>
                     <property name="pack_type">start</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]