[tracker/nautilus-extension] Added a new Nautilus extension for managing tags.



commit b249428d6e18745f45bca84ba2edf7a021122697
Author: Debarshi Ray <debarshir src gnome org>
Date:   Wed Nov 18 14:46:43 2009 +0200

    Added a new Nautilus extension for managing tags.
    
    It provides a menu items and a property page for adding or removing
    tags, and attaching or detaching tags from files.
    
    This replaces the older (and broken) extension written in Python, and
    can be enabled using --enable-nautilus-extension.

 Makefile.am                                        |    2 +-
 configure.ac                                       |   46 ++
 extensions/Makefile.am                             |    7 +
 extensions/nautilus-extension/Makefile.am          |   28 +
 .../nautilus-extension/tracker-tags-add-dialog.c   |  131 +++++
 .../nautilus-extension/tracker-tags-add-dialog.h   |   53 ++
 .../nautilus-extension/tracker-tags-extension.c    |  305 +++++++++++
 extensions/nautilus-extension/tracker-tags-utils.c |  104 ++++
 extensions/nautilus-extension/tracker-tags-utils.h |   28 +
 extensions/nautilus-extension/tracker-tags-view.c  |  542 ++++++++++++++++++++
 extensions/nautilus-extension/tracker-tags-view.h  |   52 ++
 po/POTFILES.in                                     |    2 +
 python/nautilus/README                             |   15 -
 python/nautilus/tracker-tags-tab.py                |  195 -------
 14 files changed, 1299 insertions(+), 211 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index b120c48..2fb4f8d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
 include $(top_srcdir)/Makefile.decl
 
-SUBDIRS = data src docs po python utils
+SUBDIRS = data src docs extensions po python utils
 
 if HAVE_UNIT_TESTS
 SUBDIRS += tests
diff --git a/configure.ac b/configure.ac
index 29cf706..7945757 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1352,6 +1352,46 @@ fi
 
 AM_CONDITIONAL(HAVE_EVOLUTION_PLUGIN, test "$have_evolution_plugin" = "yes")
 
+####################################################################
+# Enable Nautilus extension support?
+####################################################################
+
+AC_ARG_ENABLE([nautilus-extension],
+              AS_HELP_STRING([--enable-nautilus-extension],
+                             [Enable the nautilus extension [[default=no]]]),
+              [enable_nautilus_extension=yes],
+              [enable_nautilus_extension=no])
+
+AM_CONDITIONAL([ENABLE_NAUTILUS_EXTENSION], [test "x$enable_nautilus_extension" != "xno"])
+
+AC_ARG_WITH([nautilus-extensions-dir],
+            AS_HELP_STRING([--with-nautilus-extensions-dir],
+                           [Path to Nautilus extensions directory]))
+
+NAUTILUS_EXTENSION_INSTALL_DIR="/dev/null"
+
+if test "x$enable_nautilus_extension" != "xno"; then
+   PKG_CHECK_MODULES([NAUTILUS_EXTENSION],
+                     [libnautilus-extension],
+                     [have_nautilus_extension=yes],
+                     [have_nautilus_extension=no])
+
+   if test "x$have_nautilus_extension" = "xyes"; then
+      if test "x$with_nautilus_extensions_dir" = "x"; then
+         nautilus_extensions_dir=`$PKG_CONFIG --variable=extensiondir libnautilus-extension`
+      else
+         nautilus_extensions_dir="$with_nautilus_extensions_dir"
+      fi
+      NAUTILUS_EXTENSION_INSTALL_DIR="$nautilus_extensions_dir"
+
+      AC_DEFINE([HAVE_NAUTILUS_EXTENSION], [], [Define if we have libnautilus-extension])
+   else
+      AC_MSG_ERROR([Couldn't find Nautilus extension requirements (libnautilus-extension).])
+   fi
+fi
+
+AC_SUBST([NAUTILUS_EXTENSION_INSTALL_DIR])
+
 
 ####################################################################
 # Include/Exclude functional tests
@@ -1443,6 +1483,8 @@ AC_CONFIG_FILES([
 	docs/reference/libtracker-common/version.xml
 	docs/reference/libtracker-miner/Makefile
 	docs/reference/libtracker-miner/version.xml
+	extensions/Makefile
+	extensions/nautilus-extension/Makefile
 	Makefile
 	po/Makefile.in
 	python/deskbar-handler/Makefile
@@ -1551,6 +1593,10 @@ Metadata Extractors:
 	Support MP3 album art:                  $selected_for_albumart
 	Support playlists (w/ Totem):           $have_playlist
 
+Extensions:
+
+	Nautilus extension:			$enable_nautilus_extension
+
 Plugins:
 
 	Evolution plugin    (data-push):	$enable_evolution_miner
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
new file mode 100644
index 0000000..7f011f6
--- /dev/null
+++ b/extensions/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS =
+
+if ENABLE_NAUTILUS_EXTENSION
+SUBDIRS += nautilus-extension
+endif
diff --git a/extensions/nautilus-extension/Makefile.am b/extensions/nautilus-extension/Makefile.am
new file mode 100644
index 0000000..cd98dc5
--- /dev/null
+++ b/extensions/nautilus-extension/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =											\
+	-I$(top_srcdir)/src									\
+	$(GCOV_CFLAGS)										\
+	$(WARN_CFLAGS)										\
+	$(NAUTILUS_EXTENSION_CFLAGS)								\
+	$(TRACKER_APPS_CFLAGS)
+
+nautilus_extensiondir = $(NAUTILUS_EXTENSION_INSTALL_DIR)
+
+nautilus_extension_LTLIBRARIES = libnautilus-tracker-tags.la
+
+libnautilus_tracker_tags_la_SOURCES =								\
+	tracker-tags-add-dialog.c								\
+	tracker-tags-add-dialog.h								\
+	tracker-tags-extension.c								\
+	tracker-tags-utils.c									\
+	tracker-tags-utils.h									\
+	tracker-tags-view.c									\
+	tracker-tags-view.h
+
+libnautilus_tracker_tags_la_LDFLAGS = -module -avoid-version
+libnautilus_tracker_tags_la_LIBADD =								\
+	$(top_builddir)/src/libtracker-client/libtracker-client- TRACKER_API_VERSION@.la	\
+	$(GCOV_LIBS)										\
+	$(NAUTILUS_EXTENSION_LIBS)								\
+	$(TRACKER_APPS_LIBS)
diff --git a/extensions/nautilus-extension/tracker-tags-add-dialog.c b/extensions/nautilus-extension/tracker-tags-add-dialog.c
new file mode 100644
index 0000000..6b0b442
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-add-dialog.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <glib/gi18n.h>
+
+#include "tracker-tags-add-dialog.h"
+
+struct _TrackerTagsAddDialogPrivate
+{
+	GtkWidget	*entry;
+};
+
+G_DEFINE_TYPE (TrackerTagsAddDialog, tracker_tags_add_dialog, GTK_TYPE_DIALOG)
+
+#define TRACKER_TAGS_ADD_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TRACKER_TYPE_TAGS_ADD_DIALOG, TrackerTagsAddDialogPrivate))
+
+static void
+tracker_tags_add_dialog_entry_changed_cb (GtkEditable *editable, gpointer user_data)
+{
+	GtkDialog *const add_dialog = GTK_DIALOG (user_data);
+	GtkEntry *const entry = GTK_ENTRY (editable);
+
+	if (0 == gtk_entry_get_text_length (entry))
+		gtk_dialog_set_response_sensitive (add_dialog, GTK_RESPONSE_OK, FALSE);
+	else
+		gtk_dialog_set_response_sensitive (add_dialog, GTK_RESPONSE_OK, TRUE);
+}
+
+void
+tracker_tags_add_dialog_register_type (GTypeModule *module)
+{
+	tracker_tags_add_dialog_get_type ();
+}
+
+static void
+tracker_tags_add_dialog_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (tracker_tags_add_dialog_parent_class)->finalize (object);
+}
+
+static void
+tracker_tags_add_dialog_response (GtkDialog *dialog, gint response_id)
+{
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+tracker_tags_add_dialog_class_init (TrackerTagsAddDialogClass *klass)
+{
+	GObjectClass *const gobject_class = G_OBJECT_CLASS (klass);
+	GtkDialogClass *const dialog_class = GTK_DIALOG_CLASS (klass);
+
+	gobject_class->finalize = tracker_tags_add_dialog_finalize;
+	dialog_class->response = tracker_tags_add_dialog_response;
+
+	g_type_class_add_private (gobject_class, sizeof (TrackerTagsAddDialogPrivate));
+
+	tracker_tags_add_dialog_parent_class = g_type_class_peek_parent (klass);
+}
+
+static void
+tracker_tags_add_dialog_init (TrackerTagsAddDialog *add_dialog)
+{
+	TrackerTagsAddDialogPrivate *const priv = TRACKER_TAGS_ADD_DIALOG_GET_PRIVATE (add_dialog);
+	GtkWidget *action_area;
+	GtkWidget *dialog;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *vbox;
+
+	add_dialog->priv = priv;
+
+	gtk_container_set_border_width (GTK_CONTAINER (add_dialog), 5);
+	gtk_window_set_destroy_with_parent (GTK_WINDOW (add_dialog), TRUE);
+	gtk_window_set_modal (GTK_WINDOW (add_dialog), TRUE);
+	gtk_window_set_title (GTK_WINDOW (add_dialog), N_("Add Tag"));
+	gtk_window_set_resizable (GTK_WINDOW (add_dialog), FALSE);
+	gtk_dialog_set_has_separator (GTK_DIALOG (add_dialog), FALSE);
+	gtk_dialog_add_button (GTK_DIALOG (add_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (add_dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (add_dialog), GTK_RESPONSE_OK);
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (add_dialog), GTK_RESPONSE_OK, FALSE);
+
+	action_area = gtk_dialog_get_action_area (GTK_DIALOG (add_dialog));
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+	gtk_box_set_spacing (GTK_BOX (action_area), 6);
+
+	vbox = gtk_dialog_get_content_area (GTK_DIALOG (add_dialog));
+	gtk_box_set_spacing (GTK_BOX (vbox), 2);
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+	label = gtk_label_new_with_mnemonic (N_("_Tag label:"));
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+	add_dialog->priv->entry = gtk_entry_new ();
+	gtk_box_pack_start (GTK_BOX (hbox), add_dialog->priv->entry, TRUE, TRUE, 0);
+	g_signal_connect (add_dialog->priv->entry, "changed", G_CALLBACK (tracker_tags_add_dialog_entry_changed_cb), add_dialog);
+
+	gtk_widget_show_all (vbox);
+}
+
+GtkWidget *
+tracker_tags_add_dialog_new (void)
+{
+	return GTK_WIDGET (g_object_new (TRACKER_TYPE_TAGS_ADD_DIALOG, NULL));
+}
+
+const gchar *
+tracker_tags_add_dialog_get_text (TrackerTagsAddDialog *add_dialog)
+{
+	return gtk_entry_get_text (GTK_ENTRY (add_dialog->priv->entry));
+}
diff --git a/extensions/nautilus-extension/tracker-tags-add-dialog.h b/extensions/nautilus-extension/tracker-tags-add-dialog.h
new file mode 100644
index 0000000..bc813e5
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-add-dialog.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef TRACKER_TAGS_ADD_DIALOG_H
+#define TRACKER_TAGS_ADD_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#define TRACKER_TYPE_TAGS_ADD_DIALOG (tracker_tags_add_dialog_get_type ())
+#define TRACKER_TAGS_ADD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_TAGS_ADD_DIALOG, TrackerTagsAddDialog))
+#define TRACKER_TAGS_ADD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_TAGS_ADD_DIALOG, TrackerTagsAddDialogClass))
+#define TRACKER_IS_TAGS_ADD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_TAGS_ADD_DIALOG))
+#define TRACKER_IS_TAGS_ADD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_TAGS_ADD_DIALOG))
+
+typedef struct _TrackerTagsAddDialogPrivate	TrackerTagsAddDialogPrivate;
+
+typedef struct _TrackerTagsAddDialog		TrackerTagsAddDialog;
+typedef struct _TrackerTagsAddDialogClass	TrackerTagsAddDialogClass;
+
+struct _TrackerTagsAddDialog
+{
+	GtkDialog parent;
+	TrackerTagsAddDialogPrivate *priv;
+};
+
+struct _TrackerTagsAddDialogClass
+{
+	GtkDialogClass parent;
+};
+
+GType	tracker_tags_add_dialog_get_type	(void);
+void	tracker_tags_add_dialog_register_type	(GTypeModule *module);
+
+GtkWidget	*tracker_tags_add_dialog_new		(void);
+const gchar	*tracker_tags_add_dialog_get_text	(TrackerTagsAddDialog *add_dialog);
+
+#endif /* TRACKER_TAGS_ADD_DIALOG_H */
diff --git a/extensions/nautilus-extension/tracker-tags-extension.c b/extensions/nautilus-extension/tracker-tags-extension.c
new file mode 100644
index 0000000..ccd462d
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-extension.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <gtk/gtk.h>
+
+#include <libnautilus-extension/nautilus-menu-provider.h>
+#include <libnautilus-extension/nautilus-property-page-provider.h>
+#include <libtracker-client/tracker.h>
+
+#include "tracker-tags-add-dialog.h"
+#include "tracker-tags-utils.h"
+#include "tracker-tags-view.h"
+
+#define TRACKER_TYPE_TAGS_PLUGIN (tracker_tags_plugin_get_type ())
+#define TRACKER_TAGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_TAGS_PLUGIN, TrackerTagsPlugin))
+#define TRACKER_TAGS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_TAGS_PLUGIN, TrackerTagsPluginClass))
+#define TRACKER_IS_TAGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_TAGS_PLUGIN))
+#define TRACKER_IS_TAGS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_TAGS_PLUGIN))
+
+typedef struct _TrackerTagsPluginPrivate	TrackerTagsPluginPrivate;
+
+typedef struct _TrackerTagsPlugin		TrackerTagsPlugin;
+typedef struct _TrackerTagsPluginClass		TrackerTagsPluginClass;
+
+struct _TrackerTagsPlugin
+{
+	GObject parent;
+	TrackerTagsPluginPrivate *priv;
+};
+
+struct _TrackerTagsPluginClass
+{
+	GObjectClass parent;
+};
+
+struct _TrackerTagsPluginPrivate
+{
+	TrackerClient	*tracker_client;
+};
+
+static void	tracker_tags_plugin_menu_provider_iface_init	(NautilusMenuProviderIface *iface);
+static void	tracker_tags_plugin_property_page_provider_iface_init	(NautilusPropertyPageProviderIface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (TrackerTagsPlugin, tracker_tags_plugin, G_TYPE_OBJECT, 0,
+				G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_MENU_PROVIDER,
+						       tracker_tags_plugin_menu_provider_iface_init)
+				G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER,
+						       tracker_tags_plugin_property_page_provider_iface_init));
+
+#define TRACKER_TAGS_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TRACKER_TYPE_TAGS_PLUGIN, TrackerTagsPluginPrivate))
+
+static void
+tracker_tags_plugin_update_finished (GError *error, gpointer user_data)
+{
+	if (NULL != error)
+	{
+		GtkWidget *error_dialog;
+
+		error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_NO_SEPARATOR, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, error->message);
+		g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+		gtk_dialog_run (GTK_DIALOG (error_dialog));
+		g_error_free (error);
+	}
+}
+
+static void
+tracker_tags_plugin_add_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+	TrackerTagsPlugin *const plugin = TRACKER_TAGS_PLUGIN (user_data);
+
+	switch (response_id)
+	{
+	case GTK_RESPONSE_CANCEL:
+	case GTK_RESPONSE_DELETE_EVENT:
+		break;
+
+	case GTK_RESPONSE_OK:
+	{
+		const gchar *query;
+		const gchar *tag_label;
+
+		tag_label = tracker_tags_add_dialog_get_text (TRACKER_TAGS_ADD_DIALOG (dialog));
+		query = tracker_tags_utils_add_query (tag_label);
+		tracker_resources_sparql_update_async (plugin->priv->tracker_client, query, tracker_tags_plugin_update_finished, NULL);
+		g_free ((gpointer) query);
+		break;
+	}
+
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+static void
+tracker_tags_plugin_add_activate_cb (NautilusMenuItem *menu_item, gpointer user_data)
+{
+	void **const arg = user_data;
+	TrackerTagsPlugin *const plugin = TRACKER_TAGS_PLUGIN (arg[0]);
+	GtkWindow *const window = GTK_WINDOW (arg[1]);
+	GtkWidget *add_dialog;
+
+	/* g_free (arg); */
+
+	add_dialog = tracker_tags_add_dialog_new ();
+	gtk_window_set_transient_for (GTK_WINDOW (add_dialog), window);
+	g_signal_connect (add_dialog, "response", G_CALLBACK (tracker_tags_plugin_add_dialog_response_cb), plugin);
+	gtk_widget_show_all (add_dialog);
+}
+
+static void
+tracker_tags_plugin_manage_activate_cb (NautilusMenuItem *menu_item, gpointer user_data)
+{
+	void **const arg = user_data;
+	GList *const files = arg[0];
+	GtkWindow *const window = GTK_WINDOW (arg[1]);
+	GtkWidget *action_area;
+	GtkWidget *manage_dialog;
+	GtkWidget *vbox;
+	GtkWidget *view;
+
+	/* g_free (arg); */
+
+	manage_dialog = gtk_dialog_new_with_buttons (N_("Manage Tags"), window, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_CLOSE, NULL);
+	gtk_container_set_border_width (GTK_CONTAINER (manage_dialog), 5);
+	gtk_window_set_default_size (GTK_WINDOW (manage_dialog), 250, 375);
+	gtk_window_set_resizable (GTK_WINDOW (manage_dialog), TRUE);
+	gtk_window_set_transient_for (GTK_WINDOW (manage_dialog), window);
+	g_signal_connect (manage_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+
+	action_area = gtk_dialog_get_action_area (GTK_DIALOG (manage_dialog));
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+
+	vbox = gtk_dialog_get_content_area (GTK_DIALOG (manage_dialog));
+	gtk_box_set_spacing (GTK_BOX (vbox), 2);
+
+	view = tracker_tags_view_new (files);
+	gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0);
+
+	gtk_widget_show_all (manage_dialog);
+}
+
+static GList *
+tracker_tags_plugin_get_background_items (NautilusMenuProvider	*provider,
+					  GtkWidget		*window,
+					  NautilusFileInfo	*current_folder)
+{
+	GList *menu_items = NULL;
+	NautilusMenuItem *menu_item;
+	void **arg;
+
+	if (NULL == current_folder)
+		return NULL;
+
+	menu_item = nautilus_menu_item_new ("tracker-tags-new", N_("Create Tag..."), N_("Create a new tag for this desktop"), NULL);
+	menu_items = g_list_append (menu_items, menu_item);
+	arg = g_new (void *, 2);
+	arg[0] = provider;
+	arg[1] = window;
+	g_signal_connect (menu_item, "activate", G_CALLBACK (tracker_tags_plugin_add_activate_cb), arg);
+
+	return menu_items;
+}
+
+static GList *
+tracker_tags_plugin_get_file_items (NautilusMenuProvider	*provider,
+				    GtkWidget			*window,
+				    GList			*files)
+{
+	GList *menu_items = NULL;
+	NautilusMenuItem *menu_item;
+	void **arg;
+
+	if (NULL == files)
+		return NULL;
+
+	menu_item = nautilus_menu_item_new ("tracker-tags-new", N_("Create Tag..."), N_("Create a new tag for this desktop"), NULL);
+	menu_items = g_list_append (menu_items, menu_item);
+	arg = g_new (void *, 2);
+	arg[0] = provider;
+	arg[1] = window;
+	g_signal_connect (menu_item, "activate", G_CALLBACK (tracker_tags_plugin_add_activate_cb), arg);
+
+	menu_item = nautilus_menu_item_new ("tracker-tags-manage", N_("Manage Tags..."), N_("Change the tags attached to the selected objects"), NULL);
+	menu_items = g_list_append (menu_items, menu_item);
+	arg = g_new (void *, 2);
+	arg[0] = g_list_copy (files);
+	arg[1] = window;
+	g_signal_connect (menu_item, "activate", G_CALLBACK (tracker_tags_plugin_manage_activate_cb), arg);
+
+	return menu_items;
+}
+
+static GList *
+tracker_tags_plugin_get_pages (NautilusPropertyPageProvider	*provider,
+			       GList				*files)
+{
+	GList *property_pages = NULL;
+	GtkWidget *label;
+	GtkWidget *view;
+	NautilusPropertyPage *property_page;
+
+	if (NULL == files)
+		return NULL;
+
+	label = gtk_label_new ("Tags");
+	view = tracker_tags_view_new (files);
+	property_page = nautilus_property_page_new ("tracker-tags", label, view);
+
+	property_pages = g_list_prepend (property_pages, property_page);
+	return property_pages;
+}
+
+static GList *
+tracker_tags_plugin_get_toolbar_items (NautilusMenuProvider	*provider,
+				       GtkWidget		*window,
+				       NautilusFileInfo		*current_folder)
+{
+	return NULL;
+}
+
+static void
+tracker_tags_plugin_menu_provider_iface_init (NautilusMenuProviderIface *iface)
+{
+        iface->get_file_items = tracker_tags_plugin_get_file_items;
+        iface->get_background_items = tracker_tags_plugin_get_background_items;
+        iface->get_toolbar_items = tracker_tags_plugin_get_toolbar_items;
+}
+
+static void
+tracker_tags_plugin_property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface)
+{
+	iface->get_pages = tracker_tags_plugin_get_pages;
+}
+
+static void
+tracker_tags_plugin_class_finalize (TrackerTagsPluginClass *klass)
+{
+}
+
+static void
+tracker_tags_plugin_finalize (GObject *object)
+{
+	TrackerTagsPlugin *const plugin = TRACKER_TAGS_PLUGIN (object);
+
+	tracker_disconnect (plugin->priv->tracker_client);
+	G_OBJECT_CLASS (tracker_tags_plugin_parent_class)->finalize (object);
+}
+
+static void
+tracker_tags_plugin_class_init (TrackerTagsPluginClass *klass)
+{
+	GObjectClass *const gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->finalize = tracker_tags_plugin_finalize;
+	g_type_class_add_private (gobject_class, sizeof (TrackerTagsPluginPrivate));
+}
+
+static void
+tracker_tags_plugin_init (TrackerTagsPlugin *self)
+{
+	TrackerTagsPluginPrivate *const priv = TRACKER_TAGS_PLUGIN_GET_PRIVATE (self);
+
+	self->priv = priv;
+	self->priv->tracker_client = tracker_connect (TRUE, G_MAXINT);
+}
+
+void
+nautilus_module_initialize (GTypeModule *module)
+{
+	tracker_tags_plugin_register_type (module);
+	tracker_tags_view_register_type (module);
+}
+
+void
+nautilus_module_shutdown (void)
+{
+}
+
+void
+nautilus_module_list_types (const GType **types,
+                            int          *num_types)
+{
+	static GType type_list[1];
+
+	type_list[0] = tracker_tags_plugin_type_id;
+	*types = type_list;
+	*num_types = G_N_ELEMENTS (type_list);
+}
diff --git a/extensions/nautilus-extension/tracker-tags-utils.c b/extensions/nautilus-extension/tracker-tags-utils.c
new file mode 100644
index 0000000..916a57c
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-utils.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <string.h>
+#include "tracker-tags-utils.h"
+
+/* Copied from src/tracker-utils/tracker-tags.c */
+static const gchar *
+get_escaped_sparql_string (const gchar *str)
+{
+	GString *sparql;
+
+	sparql = g_string_new ("");
+        g_string_append_c (sparql, '"');
+
+        while (*str != '\0') {
+                gsize len = strcspn (str, "\t\n\r\"\\");
+                g_string_append_len (sparql, str, len);
+                str += len;
+                switch (*str) {
+                case '\t':
+                        g_string_append (sparql, "\\t");
+                        break;
+                case '\n':
+                        g_string_append (sparql, "\\n");
+                        break;
+                case '\r':
+                        g_string_append (sparql, "\\r");
+                        break;
+                case '"':
+                        g_string_append (sparql, "\\\"");
+                        break;
+                case '\\':
+                        g_string_append (sparql, "\\\\");
+                        break;
+                default:
+                        continue;
+                }
+                str++;
+        }
+
+        g_string_append_c (sparql, '"');
+
+	return g_string_free (sparql, FALSE);
+}
+
+const gchar *
+tracker_tags_utils_add_query (const gchar *tag_label)
+{
+	const gchar *query;
+	const gchar *tag_label_escaped;
+
+	tag_label_escaped = get_escaped_sparql_string (tag_label);
+	query = g_strdup_printf ("INSERT { "
+				 "  _:tag a nao:Tag ;"
+				 "  nao:prefLabel %s ."
+				 "} "
+				 "WHERE {"
+				 "  OPTIONAL {"
+				 "     ?tag a nao:Tag ;"
+				 "     nao:prefLabel %s"
+				 "  } ."
+				 "  FILTER (!bound(?tag)) "
+				 "}",
+				 tag_label_escaped, tag_label_escaped);
+
+	g_free ((gpointer) tag_label_escaped);
+	return query;
+}
+
+const gchar *
+tracker_tags_utils_remove_query (const gchar *tag_label)
+{
+	const gchar *query;
+	const gchar *tag_label_escaped;
+
+	tag_label_escaped = get_escaped_sparql_string (tag_label);
+	query = g_strdup_printf ("DELETE { "
+				 "  ?tag a nao:Tag "
+				 "} "
+				 "WHERE {"
+				 "  ?tag nao:prefLabel %s "
+				 "}",
+				 tag_label_escaped);
+
+	g_free ((gpointer) tag_label_escaped);
+	return query;
+}
diff --git a/extensions/nautilus-extension/tracker-tags-utils.h b/extensions/nautilus-extension/tracker-tags-utils.h
new file mode 100644
index 0000000..8b1a599
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef TRACKER_TAGS_UTILS_H
+#define TRACKER_TAGS_UTILS_H
+
+#include <glib.h>
+
+const gchar	*tracker_tags_utils_add_query	(const gchar *tag_label);
+const gchar	*tracker_tags_utils_remove_query	(const gchar *tag_label);
+
+#endif /* TRACKER_TAGS_UTILS_H */
diff --git a/extensions/nautilus-extension/tracker-tags-view.c b/extensions/nautilus-extension/tracker-tags-view.c
new file mode 100644
index 0000000..654faea
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-view.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <string.h>
+
+#include <libnautilus-extension/nautilus-file-info.h>
+#include <libtracker-client/tracker.h>
+
+#include "tracker-tags-add-dialog.h"
+#include "tracker-tags-utils.h"
+#include "tracker-tags-view.h"
+
+enum
+{
+	COLUMN_SELECTED,
+	COLUMN_TAG_NAME,
+	N_COLUMNS
+};
+
+enum
+{
+	SELECTED_INCONSISTENT = -1,
+	SELECTED_FALSE = 0,
+	SELECTED_TRUE
+};
+
+struct _TrackerTagsViewPrivate
+{
+	TrackerClient	*tracker_client;
+	GList		*files;
+	GtkListStore	*list_store;
+	GtkWidget	*remove_button;
+	gchar		*selected_tag_label;
+};
+
+G_DEFINE_TYPE (TrackerTagsView, tracker_tags_view, GTK_TYPE_VBOX);
+
+#define TRACKER_TAGS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TRACKER_TYPE_TAGS_VIEW, TrackerTagsViewPrivate))
+
+/* Copied from src/tracker-utils/tracker-tags.c */
+static gchar *
+get_escaped_sparql_string (const gchar *str)
+{
+	GString *sparql;
+
+	sparql = g_string_new ("");
+        g_string_append_c (sparql, '"');
+
+        while (*str != '\0') {
+                gsize len = strcspn (str, "\t\n\r\"\\");
+                g_string_append_len (sparql, str, len);
+                str += len;
+                switch (*str) {
+                case '\t':
+                        g_string_append (sparql, "\\t");
+                        break;
+                case '\n':
+                        g_string_append (sparql, "\\n");
+                        break;
+                case '\r':
+                        g_string_append (sparql, "\\r");
+                        break;
+                case '"':
+                        g_string_append (sparql, "\\\"");
+                        break;
+                case '\\':
+                        g_string_append (sparql, "\\\\");
+                        break;
+                default:
+                        continue;
+                }
+                str++;
+        }
+
+        g_string_append_c (sparql, '"');
+
+	return g_string_free (sparql, FALSE);
+}
+
+/* Copied from src/tracker-utils/tracker-tags.c */
+static gchar *
+get_filter_string (GStrv        files,
+		   const gchar *tag)
+{
+	GString *filter;
+	gint i, len;
+
+	if (!files) {
+		return NULL;
+	}
+
+	len = g_strv_length (files);
+
+	if (len < 1) {
+		return NULL;
+	}
+
+	filter = g_string_new ("");
+
+	g_string_append_printf (filter, "FILTER (");
+
+	if (tag) {
+		g_string_append (filter, "(");
+	}
+
+	for (i = 0; i < len; i++) {
+		g_string_append_printf (filter, "?f = <%s>", files[i]);
+
+		if (i < len - 1) {
+			g_string_append (filter, " || ");
+		}
+	}
+
+	if (tag) {
+		g_string_append_printf (filter, ") && ?t = <%s>", tag);
+	}
+
+	g_string_append (filter, ")");
+
+	return g_string_free (filter, FALSE);
+}
+
+static void
+tracker_tags_view_check_foreach (gpointer data, gpointer user_data)
+{
+	const GStrv element = data;
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	GList *node;
+
+	for (node = view->priv->files; NULL != node; node = g_list_next (node))
+	{
+		gchar *uri;
+		gint cmp;
+
+		uri = nautilus_file_info_get_uri (NAUTILUS_FILE_INFO (node->data));
+		cmp = g_strcmp0 (element[0], uri);
+		g_free (uri);
+
+		if (0 == cmp)
+		{
+			view->priv->files = g_list_delete_link (view->priv->files, node);
+			break;
+		}
+	}
+}
+
+static void
+tracker_tags_view_query_each_tag_finished (GPtrArray *result, GError *error, gpointer user_data)
+{
+	void **const arg = user_data;
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (arg[0]);
+	GtkTreeIter *const iter = arg[1];
+	GList *files;
+	guint post_num;
+	guint pre_num;
+
+	g_free (arg);
+
+	if (NULL != error)
+	{
+		GtkWidget *error_dialog;
+
+		error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_NO_SEPARATOR, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, error->message);
+		g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+		gtk_dialog_run (GTK_DIALOG (error_dialog));
+		g_error_free (error);
+		goto end;
+	}
+
+	files = g_list_copy (view->priv->files);
+
+	pre_num = g_list_length (view->priv->files);
+	g_ptr_array_foreach (result, tracker_tags_view_check_foreach, view);
+	g_ptr_array_foreach (result, (GFunc) g_strfreev, NULL);
+	g_ptr_array_free (result, TRUE);
+	post_num = g_list_length (view->priv->files);
+
+	if (pre_num == post_num)
+		gtk_list_store_set (view->priv->list_store, iter, COLUMN_SELECTED, SELECTED_FALSE, -1);
+	else if (0 == post_num)
+		gtk_list_store_set (view->priv->list_store, iter, COLUMN_SELECTED, SELECTED_TRUE, -1);
+	else
+		gtk_list_store_set (view->priv->list_store, iter, COLUMN_SELECTED, SELECTED_INCONSISTENT, -1);
+
+	g_list_free (view->priv->files);
+	view->priv->files = files;
+
+end:
+	gtk_tree_iter_free (iter);
+}
+
+static void
+tracker_tags_view_append_foreach (gpointer data, gpointer user_data)
+{
+	const GStrv element = data;
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	GtkTreeIter iter;
+	gchar *query;
+	gchar *tag_escaped;
+	void **arg;
+
+	gtk_list_store_append (view->priv->list_store, &iter);
+	gtk_list_store_set (view->priv->list_store, &iter, COLUMN_SELECTED, SELECTED_FALSE, COLUMN_TAG_NAME, element[1], -1);
+
+	tag_escaped = get_escaped_sparql_string (element[0]);
+	query = g_strdup_printf ("SELECT ?f "
+				 "WHERE {"
+				 "  ?f a rdfs:Resource ;"
+				 "  nao:hasTag %s ."
+				 "}",
+				 tag_escaped);
+	g_free (tag_escaped);
+	arg = g_new (void *, 2);
+	arg[0] = view;
+	arg[1] = gtk_tree_iter_copy (&iter);
+	tracker_resources_sparql_query_async (view->priv->tracker_client, query, tracker_tags_view_query_each_tag_finished, arg);
+	g_free (query);
+}
+
+static void
+tracker_tags_view_query_all_tags_finished (GPtrArray *result, GError *error, gpointer user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+
+	if (NULL != error)
+	{
+		GtkWidget *error_dialog;
+
+		error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_NO_SEPARATOR, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, error->message);
+		g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+		gtk_dialog_run (GTK_DIALOG (error_dialog));
+		g_error_free (error);
+		return;
+	}
+
+	g_ptr_array_foreach (result, tracker_tags_view_append_foreach, view);
+	g_ptr_array_foreach (result, (GFunc) g_strfreev, NULL);
+	g_ptr_array_free (result, TRUE);
+}
+
+static void
+tracker_tags_view_toggle_cell_data_func (GtkTreeViewColumn	*column,
+					 GtkCellRenderer	*cell_renderer,
+					 GtkTreeModel		*tree_model,
+					 GtkTreeIter		*iter,
+					 gpointer		user_data)
+{
+	GValue inconsistent = {0};
+	gint selected;
+
+	gtk_tree_model_get (tree_model, iter, COLUMN_SELECTED, &selected, -1);
+	gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell_renderer), SELECTED_TRUE == selected);
+
+	g_value_init (&inconsistent, G_TYPE_BOOLEAN);
+	g_value_set_boolean (&inconsistent, SELECTED_INCONSISTENT == selected);
+	g_object_set_property (G_OBJECT (cell_renderer), "inconsistent", &inconsistent);
+}
+
+static void
+tracker_tags_view_update_finished (GError *error, gpointer user_data)
+{
+	if (NULL != error)
+	{
+		GtkWidget *error_dialog;
+
+		error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_NO_SEPARATOR, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, error->message);
+		g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+		gtk_dialog_run (GTK_DIALOG (error_dialog));
+		g_error_free (error);
+	}
+}
+
+static void
+tracker_tags_view_add_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+
+	switch (response_id)
+	{
+	case GTK_RESPONSE_CANCEL:
+	case GTK_RESPONSE_DELETE_EVENT:
+		break;
+
+	case GTK_RESPONSE_OK:
+	{
+		const gchar *query;
+		const gchar *tag_label;
+
+		tag_label = tracker_tags_add_dialog_get_text (TRACKER_TAGS_ADD_DIALOG (dialog));
+		query = tracker_tags_utils_add_query (tag_label);
+		tracker_resources_sparql_update_async (view->priv->tracker_client, query, tracker_tags_view_update_finished, NULL);
+		g_free ((gpointer) query);
+		break;
+	}
+
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+static void
+tracker_tags_view_add_clicked_cb (GtkButton *button, gpointer user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	GtkWidget *add_dialog;
+
+	add_dialog = tracker_tags_add_dialog_new ();
+	gtk_window_set_screen (GTK_WINDOW (add_dialog), gtk_widget_get_screen (GTK_WIDGET (view)));
+	g_signal_connect (add_dialog, "response", G_CALLBACK (tracker_tags_view_add_dialog_response_cb), view);
+	gtk_widget_show_all (add_dialog);
+}
+
+static void
+tracker_tags_view_copy_uri_foreach (gpointer data, gpointer user_data)
+{
+	NautilusFileInfo *const file_info = NAUTILUS_FILE_INFO (data);
+	GStrv *const arg = user_data;
+	gchar *uri;
+
+	uri = nautilus_file_info_get_uri (file_info);
+	(*arg)[0] = uri;
+	(*arg)++;
+}
+
+static void
+tracker_tags_view_remove_clicked_cb (GtkButton *button, gpointer user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	const gchar *query;
+
+	query = tracker_tags_utils_remove_query (view->priv->selected_tag_label);
+	tracker_resources_sparql_update_async (view->priv->tracker_client, query, tracker_tags_view_update_finished, NULL);
+	g_free ((gpointer) query);
+}
+
+static void
+tracker_tags_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	GStrv files;
+	GStrv arg;
+	GtkTreeIter iter;
+	GtkTreeModel *tree_model;
+	gchar *filter;
+	gchar *query;
+	gchar *tag_label_escaped;
+	gint selected;
+	guint num;
+
+	tree_model = gtk_tree_view_get_model (tree_view);
+	if (FALSE == gtk_tree_model_get_iter (tree_model, &iter, path))
+		return;
+
+	gtk_tree_model_get (tree_model, &iter, COLUMN_SELECTED, &selected, COLUMN_TAG_NAME, &view->priv->selected_tag_label, -1);
+	selected = (SELECTED_FALSE == selected) ? SELECTED_TRUE : SELECTED_FALSE;
+	gtk_list_store_set (view->priv->list_store, &iter, COLUMN_SELECTED, selected, -1);
+
+	tag_label_escaped = get_escaped_sparql_string (view->priv->selected_tag_label);
+
+	num = g_list_length (view->priv->files);
+	arg = files = g_new0 (char *, num + 1);
+	g_list_foreach (view->priv->files, tracker_tags_view_copy_uri_foreach, &arg);
+	filter = get_filter_string (files, NULL);
+
+	if (TRUE == selected)
+	{
+		query = g_strdup_printf ("INSERT { "
+					 "  ?urn nao:hasTag ?tag "
+					 "} "
+					 "WHERE {"
+					 "  ?urn nie:isStoredAs ?f ." /* NB: ?f is used in filter. */
+					 "  ?tag nao:prefLabel %s ."
+					 "  %s "
+					 "}",
+					 tag_label_escaped, filter);
+	}
+	else
+	{
+		query = g_strdup_printf ("DELETE { "
+					 "  ?urn nao:hasTag ?tag "
+					 "} "
+					 "WHERE { "
+					 "  ?urn nie:isStoredAs ?f ." /* NB: ?f is used in filter. */
+					 "  ?tag nao:prefLabel %s ."
+					 "  %s "
+					 "}",
+					 tag_label_escaped, filter);
+	}
+
+	g_strfreev (files);
+	g_free (tag_label_escaped);
+	tracker_resources_sparql_update_async (view->priv->tracker_client, query, tracker_tags_view_update_finished, NULL);
+	g_free (query);
+}
+
+static void
+tracker_tags_view_selection_changed_cb (GtkTreeSelection *tree_selection, gpointer *user_data)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (user_data);
+	GtkTreeIter iter;
+	GtkTreeModel *tree_model;
+
+	gtk_widget_set_sensitive (GTK_WIDGET (view->priv->remove_button), FALSE);
+	view->priv->selected_tag_label = NULL;
+	if (!gtk_tree_selection_get_selected (tree_selection, &tree_model, &iter))
+		return;
+
+	gtk_tree_model_get (tree_model, &iter, COLUMN_TAG_NAME, &view->priv->selected_tag_label, -1);
+	gtk_widget_set_sensitive (GTK_WIDGET (view->priv->remove_button), TRUE);
+}
+
+void
+tracker_tags_view_register_type (GTypeModule *module)
+{
+	tracker_tags_view_get_type ();
+}
+
+static void
+tracker_tags_view_finalize (GObject *object)
+{
+	TrackerTagsView *const view = TRACKER_TAGS_VIEW (object);
+
+	tracker_disconnect (view->priv->tracker_client);
+	g_list_free (view->priv->files);
+	G_OBJECT_CLASS (tracker_tags_view_parent_class)->finalize (object);
+}
+
+static void
+tracker_tags_view_class_init (TrackerTagsViewClass *klass)
+{
+	GObjectClass *const gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->finalize = tracker_tags_view_finalize;
+	g_type_class_add_private (gobject_class, sizeof (TrackerTagsViewPrivate));
+
+	tracker_tags_view_parent_class = g_type_class_peek_parent (klass);
+}
+
+static void
+tracker_tags_view_init (TrackerTagsView *view)
+{
+	TrackerTagsViewPrivate *const priv = TRACKER_TAGS_VIEW_GET_PRIVATE (view);
+	GtkCellRenderer *cell_renderer;
+	GtkTreeSelection *tree_selection;
+	GtkTreeViewColumn *column;
+	GtkWidget *button;
+	GtkWidget *button_box;
+	GtkWidget *scrolled_window;
+	GtkWidget *tree_view;
+
+	view->priv = priv;
+	view->priv->tracker_client = tracker_connect (TRUE, G_MAXINT);
+	view->priv->files = NULL;
+	view->priv->list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
+
+	gtk_container_set_border_width (GTK_CONTAINER (view), 6);
+	gtk_box_set_homogeneous (GTK_BOX (view), FALSE);
+	gtk_box_set_spacing (GTK_BOX (view), 0);
+
+	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_box_pack_start (GTK_BOX (view), scrolled_window, TRUE, TRUE, 6);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+
+	tree_view = gtk_tree_view_new ();
+	gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+	cell_renderer = gtk_cell_renderer_toggle_new ();
+	gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+	gtk_tree_view_column_set_cell_data_func (column, cell_renderer, tracker_tags_view_toggle_cell_data_func, NULL, NULL);
+	gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (cell_renderer), FALSE);
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+	cell_renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+	gtk_tree_view_column_add_attribute(column, cell_renderer, "text", COLUMN_TAG_NAME);
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (view->priv->list_store));
+
+	tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	gtk_tree_selection_set_mode (tree_selection, GTK_SELECTION_SINGLE);
+	g_signal_connect (tree_selection, "changed", G_CALLBACK (tracker_tags_view_selection_changed_cb), view);
+
+	g_signal_connect (tree_view, "row-activated", G_CALLBACK (tracker_tags_view_row_activated_cb), view);
+
+	button_box = gtk_hbutton_box_new ();
+	gtk_box_pack_start (GTK_BOX (view), button_box, FALSE, FALSE, 6);
+	gtk_box_set_spacing (GTK_BOX (button_box), 6);
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_ADD);
+        gtk_box_pack_start (GTK_BOX (button_box), button, FALSE, FALSE, 0);
+	g_signal_connect (button, "clicked", G_CALLBACK (tracker_tags_view_add_clicked_cb), view);
+
+	view->priv->remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+        gtk_box_pack_start (GTK_BOX (button_box), view->priv->remove_button, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive (GTK_WIDGET (view->priv->remove_button), FALSE);
+	g_signal_connect (view->priv->remove_button, "clicked", G_CALLBACK (tracker_tags_view_remove_clicked_cb), view);
+
+	view->priv->selected_tag_label = NULL;
+
+	tracker_resources_sparql_query_async (view->priv->tracker_client,
+					      "SELECT ?u ?t "
+					      "WHERE {"
+					      "  ?u a nao:Tag ;"
+					      "  nao:prefLabel ?t ."
+					      "}",
+					      tracker_tags_view_query_all_tags_finished, view);
+
+	gtk_widget_show_all (GTK_WIDGET (view));
+}
+
+GtkWidget *
+tracker_tags_view_new (GList *files)
+{
+	TrackerTagsView *self;
+
+	self = g_object_new (TRACKER_TYPE_TAGS_VIEW, NULL);
+	self->priv->files = g_list_copy (files);
+	return GTK_WIDGET (self);
+}
diff --git a/extensions/nautilus-extension/tracker-tags-view.h b/extensions/nautilus-extension/tracker-tags-view.h
new file mode 100644
index 0000000..b12810b
--- /dev/null
+++ b/extensions/nautilus-extension/tracker-tags-view.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009  Debarshi Ray <debarshir src gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef TRACKER_TAGS_VIEW_H
+#define TRACKER_TAGS_VIEW_H
+
+#include <gtk/gtk.h>
+
+#define TRACKER_TYPE_TAGS_VIEW (tracker_tags_view_get_type ())
+#define TRACKER_TAGS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_TAGS_VIEW, TrackerTagsView))
+#define TRACKER_TAGS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_TAGS_VIEW, TrackerTagsViewClass))
+#define TRACKER_IS_TAGS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_TAGS_VIEW))
+#define TRACKER_IS_TAGS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_TAGS_VIEW))
+
+typedef struct _TrackerTagsViewPrivate	TrackerTagsViewPrivate;
+
+typedef struct _TrackerTagsView		TrackerTagsView;
+typedef struct _TrackerTagsViewClass	TrackerTagsViewClass;
+
+struct _TrackerTagsView
+{
+	GtkVBox parent;
+	TrackerTagsViewPrivate *priv;
+};
+
+struct _TrackerTagsViewClass
+{
+	GtkVBoxClass parent;
+};
+
+GType	tracker_tags_view_get_type	(void);
+void	tracker_tags_view_register_type	(GTypeModule *module);
+
+GtkWidget	*tracker_tags_view_new	(GList *files);
+
+#endif /* TRACKER_TAGS_VIEW_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a4352c3..3a0494a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,8 @@ data/miners/tracker-miner-files.desktop.in
 data/tracker-miner-fs.desktop.in.in
 data/tracker-status-icon.desktop.in.in
 data/tracker-store.desktop.in.in
+extensions/nautilus-extension/tracker-tags-add-dialog.c
+extensions/nautilus-extension/tracker-tags-plugin.c
 python/deskbar-handler/tracker-handler.py
 python/deskbar-handler/tracker-handler-static.py
 python/deskbar-handler/tracker-module.py



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