[gnome-applets] Add Tracker Search Bar applet



commit 083c3a9a8ec6c0e02b705ffbdf9d54c5e4271c92
Author: Balló György <ballogyor gmail com>
Date:   Thu Aug 20 19:10:58 2015 +0200

    Add Tracker Search Bar applet
    
    It was part of the tracker project:
    https://git.gnome.org/browse/tracker/commit/?id=9d924ebb67c5efd1b97b0699db030bec38f3ae0e

 Makefile.am                                        |   10 +-
 configure.ac                                       |   32 +
 po/POTFILES.in                                     |    5 +
 po/POTFILES.skip                                   |    1 +
 tracker-search-bar/Makefile.am                     |   65 +
 .../org.gnome.panel.SearchBar.panel-applet.in.in   |   14 +
 ....gnome.panel.applet.SearchBarFactory.service.in |    3 +
 tracker-search-bar/tracker-aligned-window.c        |  332 +++++
 tracker-search-bar/tracker-aligned-window.h        |   56 +
 tracker-search-bar/tracker-applet.c                |  463 ++++++
 tracker-search-bar/tracker-applet.h                |   48 +
 tracker-search-bar/tracker-results-window.c        | 1533 ++++++++++++++++++++
 tracker-search-bar/tracker-results-window.h        |   53 +
 tracker-search-bar/tracker-search-bar-menu.xml     |    6 +
 tracker-search-bar/tracker-search-bar.ui           |   84 ++
 tracker-search-bar/tracker-utils.c                 |  159 ++
 tracker-search-bar/tracker-utils.h                 |   41 +
 17 files changed, 2903 insertions(+), 2 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a3ffebc..95dc9de 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,6 +36,10 @@ if BUILD_MINICOMMANDER_APPLET
 minicommander_applet_SUBDIR = mini-commander
 endif
 
+if HAVE_TRACKER_SEARCH_BAR
+tracker_search_bar_SUBDIR = tracker-search-bar
+endif
+
 always_built_SUBDIRS =  \
        charpick        \
        brightness      \
@@ -57,7 +61,8 @@ SUBDIRS = \
        $(accessx_status_SUBDIR)\
        $(invest_applet_SUBDIR) \
        $(cpufreq_SUBDIR) \
-       $(minicommander_applet_SUBDIR)
+       $(minicommander_applet_SUBDIR) \
+       $(tracker_search_bar_SUBDIR)
 
 DIST_SUBDIRS = \
        po              \
@@ -78,7 +83,8 @@ DIST_SUBDIRS = \
        invest-applet   \
        windowpicker    \
        inhibit         \
-       brightness
+       brightness      \
+       tracker-search-bar
 
 EXTRA_DIST = \
        MAINTAINERS                     \
diff --git a/configure.ac b/configure.ac
index 1a7ada7..b5b3c7d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -279,6 +279,15 @@ AC_SUBST(LIBGWEATHER_CFLAGS)
 AC_SUBST(LIBGWEATHER_LIBS)
 AM_CONDITIONAL(BUILD_LIBGWEATHER_APPLETS, $build_libgweather_applets)
 
+ dnl -- check for tracker-sparql (required for tracker-search-bar) ------------
+TRACKER_SEARCH_BAR_REQUIRED="tracker-sparql-1.0"
+
+PKG_CHECK_MODULES(TRACKER_SEARCH_BAR, [$TRACKER_SEARCH_BAR_REQUIRED],
+                  [have_tracker_search_bar=yes],
+                  [have_tracker_search_bar=no])
+AC_SUBST(TRACKER_SEARCH_BAR_CFLAGS)
+AC_SUBST(TRACKER_SEARCH_BAR_LIBS)
+
 dnl -- check for GSettings (required for gweather applet)
 GLIB_GSETTINGS
 
@@ -505,6 +514,27 @@ AM_CONDITIONAL(BUILD_CPUFREQ_SELECTOR, test x$enable_selector = xyes)
 AM_CONDITIONAL(CPUFREQ_SELECTOR_SUID, test x$suid = xyes)
 
 dnl ***************************************************************************
+dnl *** tracker-search-bar applet check                                     ***
+dnl ***************************************************************************
+
+AC_ARG_ENABLE([tracker-search-bar],
+              AS_HELP_STRING([--enable-tracker-search-bar],
+                             [enable tracker-search-bar[[default=auto]]]),,
+              [enable_tracker_search_bar=auto])
+
+if test "x$enable_tracker_search_bar" = "xyes" ; then
+   if test "x$have_tracker_search_bar" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find tracker-search-bar dependencies ($TRACKER_SEARCH_BAR_REQUIRED).])
+   fi
+else
+   if test "x$enable_tracker_search_bar" = "xno"; then
+      have_tracker_search_bar="no  (disabled)"
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_TRACKER_SEARCH_BAR, test "$have_tracker_search_bar" = "yes")
+
+dnl ***************************************************************************
 dnl *** invest-applet specific checks                                       ***
 dnl ***************************************************************************
 
@@ -730,6 +760,7 @@ netspeed/Makefile
 stickynotes/help/Makefile
 stickynotes/Makefile
 stickynotes/pixmaps/Makefile
+tracker-search-bar/Makefile
 trashapplet/help/Makefile
 trashapplet/Makefile
 trashapplet/src/Makefile
@@ -768,6 +799,7 @@ gnome-applets-$VERSION configure summary:
         - netspeed                     $build_gtop_applets
         - stickynotes                  $enable_stickynotes
         - trashapplet                  always
+        - tracker-search-bar:          $have_tracker_search_bar
         - windowpicker                 always
 
        Using DBUS:                     $HAVE_DBUS
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 312fdd4..3f7f75a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -106,6 +106,11 @@ stickynotes/stickynotes_callbacks.c
 [type: gettext/glade]stickynotes/stickynotes-note-menu.xml
 [type: gettext/ini]trashapplet/org.gnome.applets.TrashApplet.panel-applet.in.in
 stickynotes/org.gnome.gnome-applets.stickynotes.gschema.xml.in.in
+[type: gettext/ini]tracker-search-bar/org.gnome.panel.SearchBar.panel-applet.in.in
+tracker-search-bar/tracker-applet.c
+tracker-search-bar/tracker-results-window.c
+[type: gettext/glade]tracker-search-bar/tracker-search-bar.ui
+[type: gettext/glade]tracker-search-bar/tracker-search-bar-menu.xml
 trashapplet/src/trashapplet.c
 trashapplet/src/trash-empty.c
 [type: gettext/glade]trashapplet/trashapplet-menu.xml
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f22acd7..2a62ceb 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -27,4 +27,5 @@ netspeed/data/org.gnome.panel.Netspeed.panel-applet.in
 netspeed/data/org.gnome.gnome-applets.netspeed.gschema.xml.in
 stickynotes/org.gnome.applets.StickyNotesApplet.panel-applet.in
 stickynotes/org.gnome.gnome-applets.stickynotes.gschema.xml.in
+tracker-search-bar/org.gnome.panel.SearchBar.panel-applet.in
 trashapplet/org.gnome.applets.TrashApplet.panel-applet.in
diff --git a/tracker-search-bar/Makefile.am b/tracker-search-bar/Makefile.am
new file mode 100644
index 0000000..bdd5b76
--- /dev/null
+++ b/tracker-search-bar/Makefile.am
@@ -0,0 +1,65 @@
+AM_CPPFLAGS =                                  \
+       -I.                                     \
+       -I$(srcdir)                             \
+       $(GNOME_APPLETS_CFLAGS)                 \
+       $(TRACKER_SEARCH_BAR_CFLAGS)            \
+       $(WARN_CFLAGS)                          \
+       $(NULL)
+
+libexec_PROGRAMS = tracker-search-bar
+
+tracker_search_bar_SOURCES =                           \
+       tracker-applet.c                               \
+       tracker-applet.h                               \
+       tracker-aligned-window.c                       \
+       tracker-aligned-window.h                       \
+       tracker-results-window.c                       \
+       tracker-results-window.h                       \
+       tracker-utils.c                                \
+       tracker-utils.h
+
+tracker_search_bar_LDADD =                             \
+       $(GNOME_APPLETS_LIBS)                          \
+       $(TRACKER_SEARCH_BAR_LIBS)
+
+# Panel applet
+appletdir = $(LIBPANEL_APPLET_DIR)
+applet_in_files = org.gnome.panel.SearchBar.panel-applet.in
+applet_DATA = $(applet_in_files:.panel-applet.in=.panel-applet)
+
+$(applet_in_files): $(applet_in_files).in Makefile
+       $(AM_V_GEN)sed                                 \
+         -e "s|\ LIBEXECDIR\@|$(libexecdir)|"         \
+         -e "s|\ VERSION\@|$(PACKAGE_VERSION)|"       \
+         $< > $@
+
+%.panel-applet: %.panel-applet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) 
$(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+# Applet UIs and menu
+uidir = $(pkgdatadir)/ui
+ui_DATA =                                             \
+       tracker-search-bar.ui                         \
+       tracker-search-bar-menu.xml
+
+# DBus service file
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.gnome.panel.applet.SearchBarFactory.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+org.gnome.panel.applet.SearchBarFactory.service: $(service_in_files)
+       $(AM_V_GEN)sed                                \
+         -e "s|\ LIBEXECDIR\@|$(libexecdir)|"        \
+         $< > $@
+
+# Distribution details
+EXTRA_DIST = \
+       org.gnome.panel.SearchBar.panel-applet.in.in \
+       $(ui_DATA) \
+       $(service_in_files)
+
+CLEANFILES = \
+       $(applet_DATA) \
+       $(applet_DATA).in \
+       $(service_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/tracker-search-bar/org.gnome.panel.SearchBar.panel-applet.in.in 
b/tracker-search-bar/org.gnome.panel.SearchBar.panel-applet.in.in
new file mode 100644
index 0000000..54c5c6e
--- /dev/null
+++ b/tracker-search-bar/org.gnome.panel.SearchBar.panel-applet.in.in
@@ -0,0 +1,14 @@
+[Applet Factory]
+Id=SearchBarFactory
+Location= LIBEXECDIR@/tracker-search-bar
+Name=Search Bar
+Description=Find your data quickly using Tracker
+
+[SearchBar]
+_Name=Tracker Search Bar
+_Description=Find your data quickly using Tracker
+Icon=system-search
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-applets
+X-GNOME-Bugzilla-Component=Tracker Search Bar (tracker-search-bar)
+X-GNOME-Bugzilla-Version= VERSION@
diff --git a/tracker-search-bar/org.gnome.panel.applet.SearchBarFactory.service.in 
b/tracker-search-bar/org.gnome.panel.applet.SearchBarFactory.service.in
new file mode 100644
index 0000000..3c18523
--- /dev/null
+++ b/tracker-search-bar/org.gnome.panel.applet.SearchBarFactory.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.panel.applet.SearchBarFactory
+Exec= LIBEXECDIR@/tracker-search-bar
diff --git a/tracker-search-bar/tracker-aligned-window.c b/tracker-search-bar/tracker-aligned-window.c
new file mode 100644
index 0000000..f5d4071
--- /dev/null
+++ b/tracker-search-bar/tracker-aligned-window.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "tracker-aligned-window.h"
+
+#define TRACKER_ALIGNED_WINDOW_GET_PRIVATE(obj)         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), 
TRACKER_TYPE_ALIGNED_WINDOW, TrackerAlignedWindowPrivate))
+
+struct _TrackerAlignedWindowPrivate {
+       GtkWidget *align_widget;
+       guint motion_id;
+};
+
+enum {
+       PROP_0,
+       PROP_ALIGN_WIDGET
+};
+
+static void     tracker_aligned_window_finalize         (GObject              *object);
+static void     tracker_aligned_window_get_property     (GObject              *object,
+                                                         guint                 prop_id,
+                                                         GValue               *value,
+                                                         GParamSpec           *pspec);
+static void     tracker_aligned_window_set_property     (GObject              *object,
+                                                         guint                 prop_id,
+                                                         const GValue         *value,
+                                                         GParamSpec           *pspec);
+static void     tracker_aligned_window_realize          (GtkWidget            *widget);
+static void     tracker_aligned_window_show             (GtkWidget            *widget);
+static gboolean tracker_aligned_window_motion_notify_cb (GtkWidget            *widget,
+                                                         GdkEventMotion       *event,
+                                                         TrackerAlignedWindow *aligned_window);
+
+G_DEFINE_TYPE (TrackerAlignedWindow, tracker_aligned_window, GTK_TYPE_WINDOW);
+
+static void
+tracker_aligned_window_class_init (TrackerAlignedWindowClass *klass)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gobject_class->set_property = tracker_aligned_window_set_property;
+       gobject_class->get_property = tracker_aligned_window_get_property;
+       gobject_class->finalize = tracker_aligned_window_finalize;
+
+       widget_class->realize = tracker_aligned_window_realize;
+       widget_class->show = tracker_aligned_window_show;
+
+       g_object_class_install_property (gobject_class, PROP_ALIGN_WIDGET,
+                                        g_param_spec_object ("align-widget",
+                                                             "Align Widget",
+                                                             "The widget the window should align to",
+                                                             GTK_TYPE_WIDGET,
+                                                             G_PARAM_READWRITE));
+
+       g_type_class_add_private (klass, sizeof (TrackerAlignedWindowPrivate));
+}
+
+static void
+tracker_aligned_window_init (TrackerAlignedWindow *aligned_window)
+{
+       TrackerAlignedWindowPrivate *priv = TRACKER_ALIGNED_WINDOW_GET_PRIVATE (aligned_window);
+       GtkWindow *window = GTK_WINDOW (aligned_window);
+
+       aligned_window->private = priv;
+
+       priv->align_widget = NULL;
+       priv->motion_id = 0;
+
+       /* set window properties */
+       gtk_window_set_decorated (window, FALSE);
+       gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DOCK);
+}
+
+static void
+tracker_aligned_window_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+       TrackerAlignedWindow *aligned_window = TRACKER_ALIGNED_WINDOW (object);
+
+       switch (prop_id) {
+       case PROP_ALIGN_WIDGET:
+               g_value_set_object (value, aligned_window->private->align_widget);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+tracker_aligned_window_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+       TrackerAlignedWindow *aligned_window = TRACKER_ALIGNED_WINDOW (object);
+
+       switch (prop_id) {
+       case PROP_ALIGN_WIDGET:
+               tracker_aligned_window_set_widget (aligned_window,
+                                                  g_value_get_object (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+tracker_aligned_window_position (TrackerAlignedWindow *window)
+{
+       TrackerAlignedWindowPrivate *priv;
+       GtkWidget *align_widget;
+       gint our_width, our_height;
+       gint entry_x, entry_y, entry_width, entry_height;
+       gint x, y;
+       GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;
+
+       g_assert (TRACKER_IS_ALIGNED_WINDOW (window));
+       priv = window->private;
+
+       if (!priv->align_widget) {
+               return;
+       }
+
+       align_widget = priv->align_widget;
+
+       gdk_flush ();
+
+       gdk_window_get_geometry (gtk_widget_get_window (GTK_WIDGET (window)),
+                                NULL,
+                                NULL,
+                                &our_width,
+                                &our_height);
+
+       /* stick, skip taskbar and pager */
+       gtk_window_stick (GTK_WINDOW (window));
+       gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
+       gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
+
+       /* make sure the align_widget is realized before we do anything */
+       gtk_widget_realize (align_widget);
+
+       /* get the positional and dimensional attributes of the align widget */
+       gdk_window_get_origin (gtk_widget_get_window (align_widget),
+                              &entry_x,
+                              &entry_y);
+       gdk_window_get_geometry (gtk_widget_get_window (align_widget),
+                                NULL,
+                                NULL,
+                                &entry_width,
+                                &entry_height);
+
+       if (entry_x + our_width < gdk_screen_width ()) {
+               x = entry_x + 1;
+       } else {
+               x = entry_x + entry_width - our_width - 1;
+
+               gravity = GDK_GRAVITY_NORTH_EAST;
+       }
+
+       if (entry_y + entry_height + our_height < gdk_screen_height ()) {
+               y = entry_y + entry_height + 1;
+       } else {
+               y = entry_y - our_height + 1;
+
+               if (gravity == GDK_GRAVITY_NORTH_EAST) {
+                       gravity = GDK_GRAVITY_SOUTH_EAST;
+               } else {
+                       gravity = GDK_GRAVITY_SOUTH_WEST;
+               }
+       }
+
+       gtk_window_set_gravity (GTK_WINDOW (window), gravity);
+       gtk_window_move (GTK_WINDOW (window), x, y);
+}
+
+static void
+tracker_aligned_window_realize (GtkWidget *widget)
+{
+       GTK_WIDGET_CLASS (tracker_aligned_window_parent_class)->realize (widget);
+
+       tracker_aligned_window_position (TRACKER_ALIGNED_WINDOW (widget));
+}
+
+static void
+tracker_aligned_window_show (GtkWidget *widget)
+{
+       tracker_aligned_window_position (TRACKER_ALIGNED_WINDOW (widget));
+
+       GTK_WIDGET_CLASS (tracker_aligned_window_parent_class)->show (widget);
+}
+
+static void
+tracker_aligned_window_finalize (GObject *object)
+{
+       G_OBJECT_CLASS (tracker_aligned_window_parent_class)->finalize (object);
+}
+
+static gboolean
+tracker_aligned_window_motion_notify_cb (GtkWidget            *widget,
+                                         GdkEventMotion       *event,
+                                         TrackerAlignedWindow *aligned_window)
+{
+       GtkAllocation alloc;
+       GdkRectangle rect;
+
+       gtk_widget_get_allocation (GTK_WIDGET (aligned_window), &alloc);
+
+       rect.x = 0;
+       rect.y = 0;
+       rect.width = alloc.width;
+       rect.height = alloc.height;
+
+       gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (aligned_window)),
+                                   &rect,
+                                   FALSE);
+
+       return FALSE;
+}
+
+/**
+ * tracker_aligned_window_new:
+ * @align_widget: a #GtkWidget to which the window should align
+ *
+ * Creates a new window, aligned to a previously created widget.
+ *
+ * Return value: a new #TrackerAlignedWindow
+ */
+GtkWidget *
+tracker_aligned_window_new (GtkWidget *align_widget)
+{
+       return g_object_new (TRACKER_TYPE_ALIGNED_WINDOW,
+                            "align-widget", align_widget,
+                            NULL);
+}
+
+/**
+ * tracker_aligned_window_set_widget:
+ * @aligned_window: a #TrackerAlignedWindow
+ * @align_widget: the #GtkWidget @aligned_window should align to
+ *
+ * Sets @align_widget as the #GtkWidget to which @aligned_window should
+ * align.
+ *
+ * Note that @align_widget must have a #GdkWindow in order to
+ * #TrackerAlignedWindow to work.
+ */
+void
+tracker_aligned_window_set_widget (TrackerAlignedWindow *aligned_window,
+                                   GtkWidget          *align_widget)
+{
+       TrackerAlignedWindowPrivate *priv;
+
+       g_return_if_fail (TRACKER_IS_ALIGNED_WINDOW (aligned_window));
+       g_return_if_fail (GTK_IS_WIDGET (align_widget));
+
+#if 0
+       if (GTK_WIDGET_NO_WINDOW (align_widget))
+               {
+                       g_warning ("Attempting to set a widget of class '%s' as the "
+                                  "align widget, but widgets of this class does not "
+                                  "have a GdkWindow.",
+                                  g_type_name (G_OBJECT_TYPE (align_widget)));
+
+                       return;
+               }
+#endif
+
+       priv = TRACKER_ALIGNED_WINDOW_GET_PRIVATE (aligned_window);
+
+       if (priv->align_widget) {
+               g_signal_handler_disconnect (priv->align_widget, priv->motion_id);
+               priv->align_widget = NULL;
+       }
+
+       priv->align_widget = align_widget;
+
+       /* FIXME: This causes problems when the pointer goes out of the
+        * window after it is removed using escape.
+        *
+        * Probably fixable by watching for events somewhere and
+        * handling this better.
+        */
+       if (0) {
+               priv->motion_id = g_signal_connect (priv->align_widget, "motion-notify-event",
+                                                   G_CALLBACK (tracker_aligned_window_motion_notify_cb),
+                                                   aligned_window);
+       }
+}
+
+/**
+ * tracker_aligned_window_get_widget:
+ * @aligned_window: a #TrackerAlignedWindow
+ *
+ * Retrieves the #GtkWidget to which @aligned_window is aligned to.
+ *
+ * Return value: the align widget.
+ */
+GtkWidget *
+tracker_aligned_window_get_widget (TrackerAlignedWindow *aligned_window)
+{
+       g_return_val_if_fail (TRACKER_IS_ALIGNED_WINDOW (aligned_window), NULL);
+
+       return aligned_window->private->align_widget;
+}
diff --git a/tracker-search-bar/tracker-aligned-window.h b/tracker-search-bar/tracker-aligned-window.h
new file mode 100644
index 0000000..2d24e9d
--- /dev/null
+++ b/tracker-search-bar/tracker-aligned-window.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __TRACKER_ALIGNED_WINDOW_H__
+#define __TRACKER_ALIGNED_WINDOW_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_ALIGNED_WINDOW             (tracker_aligned_window_get_type ())
+#define TRACKER_ALIGNED_WINDOW(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
TRACKER_TYPE_ALIGNED_WINDOW, TrackerAlignedWindow))
+#define TRACKER_IS_ALIGNED_WINDOW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
TRACKER_TYPE_ALIGNED_WINDOW))
+#define TRACKER_ALIGNED_WINDOW_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
TRACKER_TYPE_ALIGNED_WINDOW, TrackerAlignedWindowClass))
+#define TRACKER_IS_ALIGNED_WINDOW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
TRACKER_TYPE_ALIGNED_WINDOW))
+#define TRACKER_ALIGNED_WINDOW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
TRACKER_TYPE_ALIGNED_WINDOW, TrackerAlignedWindowClass))
+
+typedef struct _TrackerAlignedWindow        TrackerAlignedWindow;
+typedef struct _TrackerAlignedWindowClass   TrackerAlignedWindowClass;
+typedef struct _TrackerAlignedWindowPrivate TrackerAlignedWindowPrivate;
+
+struct _TrackerAlignedWindow {
+       GtkWindow parent_instance;
+
+       TrackerAlignedWindowPrivate *private;
+};
+
+struct _TrackerAlignedWindowClass {
+       GtkWindowClass parent_class;
+};
+
+GType      tracker_aligned_window_get_type   (void) G_GNUC_CONST;
+GtkWidget *tracker_aligned_window_new        (GtkWidget            *align_widget);
+void       tracker_aligned_window_set_widget (TrackerAlignedWindow *aligned_window,
+                                              GtkWidget            *align_widget);
+GtkWidget *tracker_aligned_window_get_widget (TrackerAlignedWindow *aligned_window);
+
+G_END_DECLS
+
+#endif /* __TRACKER_ALIGNED_WINDOW_H__ */
diff --git a/tracker-search-bar/tracker-applet.c b/tracker-search-bar/tracker-applet.c
new file mode 100644
index 0000000..3b5fd62
--- /dev/null
+++ b/tracker-search-bar/tracker-applet.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <panel-applet.h>
+
+#include "tracker-applet.h"
+#include "tracker-results-window.h"
+
+static void applet_about_cb (GSimpleAction *action,
+                             GVariant *parameter,
+                             gpointer user_data);
+
+static const GActionEntry applet_menu_actions[] = {
+       { "about",
+         applet_about_cb,
+         NULL,
+         NULL,
+         NULL,
+       }
+};
+
+static void
+applet_about_cb (GSimpleAction *action,
+                 GVariant *parameter,
+                 gpointer user_data)
+{
+       TrackerApplet *applet = user_data;
+       GObject *object;
+       GtkWidget *dialog;
+
+       object = gtk_builder_get_object (applet->builder, "dialog_about");
+       g_return_if_fail (object != NULL);
+
+       dialog = GTK_WIDGET (object);
+
+       gtk_dialog_run (GTK_DIALOG(dialog));
+       gtk_widget_hide (dialog);
+}
+
+static gboolean
+applet_event_box_button_press_event_cb (GtkWidget      *widget,
+                                        GdkEventButton *event,
+                                        TrackerApplet  *applet)
+{
+       if (applet->results) {
+               gtk_widget_destroy (applet->results);
+               applet->results = NULL;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+applet_entry_start_search (TrackerApplet *applet)
+{
+       const gchar *text;
+
+       text = gtk_entry_get_text (GTK_ENTRY (applet->entry));
+
+        if (!text || !*text) {
+                gtk_widget_hide (applet->results);
+                return;
+        }
+
+       g_print ("Searching for: '%s'\n", text);
+
+       if (!applet->results) {
+               applet->results = tracker_results_window_new (applet->parent, text);
+       } else {
+               g_object_set (applet->results, "query", text, NULL);
+       }
+
+       if (!gtk_widget_get_visible (applet->results)) {
+               tracker_results_window_popup (TRACKER_RESULTS_WINDOW (applet->results));
+       }
+}
+
+static gboolean
+applet_entry_start_search_cb (gpointer user_data)
+{
+       TrackerApplet *applet;
+
+       applet = user_data;
+
+       applet->new_search_id = 0;
+
+       applet_entry_start_search (applet);
+
+       return FALSE;
+}
+
+static void
+applet_entry_activate_cb (GtkEntry      *entry,
+                          TrackerApplet *applet)
+{
+       if (applet->new_search_id) {
+               g_source_remove (applet->new_search_id);
+               applet->new_search_id = 0;
+       }
+
+       applet_entry_start_search (applet);
+}
+
+static gboolean
+applet_entry_button_press_event_cb (GtkWidget      *widget,
+                                    GdkEventButton *event,
+                                    TrackerApplet  *applet)
+{
+       panel_applet_request_focus (PANEL_APPLET (applet->parent), event->time);
+
+       return FALSE;
+}
+
+static void
+applet_entry_editable_changed_cb (GtkWidget     *widget,
+                                  TrackerApplet *applet)
+{
+        if (applet->new_search_id) {
+                g_source_remove (applet->new_search_id);
+        }
+
+        applet->new_search_id =
+                g_timeout_add (300,
+                               applet_entry_start_search_cb,
+                               applet);
+}
+
+static gboolean
+applet_entry_key_press_event_cb (GtkWidget     *widget,
+                                 GdkEventKey   *event,
+                                 TrackerApplet *applet)
+{
+       if (event->keyval == GDK_KEY_Escape) {
+               if (!applet->results) {
+                       return FALSE;
+               }
+
+               gtk_widget_destroy (applet->results);
+               applet->results = NULL;
+       } else if (event->keyval == GDK_KEY_Down) {
+               if (!applet->results) {
+                       return FALSE;
+               }
+
+               gtk_widget_grab_focus (applet->results);
+       }
+
+       return FALSE;
+}
+
+static gboolean
+applet_draw (gpointer user_data)
+{
+       TrackerApplet *applet = user_data;
+
+       if (applet->box) {
+               gtk_widget_destroy (applet->box);
+       }
+
+       switch (applet->orient) {
+       case GTK_ORIENTATION_VERTICAL:
+               applet->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+               break;
+       case GTK_ORIENTATION_HORIZONTAL:
+               applet->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       gtk_container_add (GTK_CONTAINER (PANEL_APPLET (applet->parent)), applet->box);
+       gtk_widget_show (applet->box);
+
+       applet->event_box = gtk_event_box_new ();
+       gtk_event_box_set_visible_window (GTK_EVENT_BOX (applet->event_box), FALSE);
+       gtk_widget_show (applet->event_box);
+       gtk_box_pack_start (GTK_BOX (applet->box), applet->event_box, FALSE, FALSE, 0);
+
+       g_signal_connect (applet->event_box,
+                         "button_press_event",
+                         G_CALLBACK (applet_event_box_button_press_event_cb), applet);
+
+       applet->image = gtk_image_new ();
+       gtk_container_add (GTK_CONTAINER (applet->event_box), applet->image);
+       gtk_image_set_from_icon_name (GTK_IMAGE (applet->image),
+                                     "edit-find",
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+       gtk_widget_show (applet->image);
+
+       applet->entry = gtk_entry_new ();
+       gtk_box_pack_start (GTK_BOX (applet->box), applet->entry, TRUE, TRUE, 0);
+       gtk_entry_set_width_chars (GTK_ENTRY (applet->entry), 12);
+       gtk_widget_show (applet->entry);
+
+       g_signal_connect (applet->entry,
+                         "activate",
+                         G_CALLBACK (applet_entry_activate_cb), applet);
+       g_signal_connect (applet->entry,
+                         "button_press_event",
+                         G_CALLBACK (applet_entry_button_press_event_cb), applet);
+        g_signal_connect (applet->entry,
+                          "changed",
+                          G_CALLBACK (applet_entry_editable_changed_cb), applet);
+       g_signal_connect (applet->entry,
+                         "key_press_event",
+                         G_CALLBACK (applet_entry_key_press_event_cb), applet);
+
+       applet->idle_draw_id = 0;
+
+       return FALSE;
+}
+
+static void
+applet_queue_draw (TrackerApplet *applet)
+{
+       if (!applet->idle_draw_id)
+               applet->idle_draw_id = g_idle_add (applet_draw, applet);
+}
+
+static void
+applet_change_orient_cb (GtkWidget         *widget,
+                         PanelAppletOrient  orient,
+                         gpointer           user_data)
+{
+       TrackerApplet *applet;
+       GtkAllocation alloc;
+       guint new_size;
+
+       applet = user_data;
+        new_size = applet->size;
+
+       gtk_widget_get_allocation (GTK_WIDGET (applet->parent), &alloc);
+
+       switch (orient) {
+       case PANEL_APPLET_ORIENT_LEFT:
+       case PANEL_APPLET_ORIENT_RIGHT:
+               applet->orient = GTK_ORIENTATION_VERTICAL;
+               new_size = alloc.width;
+               break;
+
+       case PANEL_APPLET_ORIENT_UP:
+       case PANEL_APPLET_ORIENT_DOWN:
+               applet->orient = GTK_ORIENTATION_HORIZONTAL;
+               new_size = alloc.height;
+               break;
+       }
+
+       if (new_size != applet->size) {
+               applet->size = new_size;
+       }
+
+       applet_queue_draw (applet);
+}
+
+static void
+applet_size_allocate_cb (GtkWidget     *widget,
+                         GtkAllocation *allocation,
+                         gpointer       user_data)
+{
+       TrackerApplet *applet;
+       PanelAppletOrient orient;
+       gint new_size;
+
+       applet = user_data;
+
+       orient = panel_applet_get_orient (PANEL_APPLET (widget));
+       if (orient == PANEL_APPLET_ORIENT_LEFT ||
+           orient == PANEL_APPLET_ORIENT_RIGHT) {
+               new_size = allocation->width;
+       } else {
+               new_size = allocation->height;
+       }
+
+       if (applet->size != new_size) {
+               applet->size = new_size;
+
+               gtk_image_set_pixel_size (GTK_IMAGE (applet->image), applet->size - 2);
+
+               /* re-scale the icon, if it was found */
+               if (applet->icon)       {
+                       GdkPixbuf *scaled;
+
+                       scaled = gdk_pixbuf_scale_simple (applet->icon,
+                                                         applet->size - 5,
+                                                         applet->size - 5,
+                                                         GDK_INTERP_BILINEAR);
+
+                       gtk_image_set_from_pixbuf (GTK_IMAGE (applet->image), scaled);
+                       g_object_unref (scaled);
+               }
+       }
+
+}
+
+#if 0
+
+static void
+applet_destroy_cb (BonoboObject  *object,
+                   TrackerApplet *applet)
+{
+       if (applet->builder) {
+               g_object_unref (applet->builder);
+               applet->builder = NULL;
+       }
+
+       if (applet->results) {
+               gtk_widget_destroy (applet->results);
+               applet->results = NULL;
+       }
+
+       if (applet->icon) {
+               g_object_unref (applet->icon);
+               applet->icon = NULL;
+       }
+
+       if (applet->idle_draw_id) {
+               g_source_remove (applet->idle_draw_id);
+               applet->idle_draw_id = 0;
+       }
+
+       if (applet->new_search_id) {
+               g_source_remove (applet->new_search_id);
+               applet->new_search_id = 0;
+       }
+
+       g_free (applet);
+}
+
+#endif
+
+static gboolean
+applet_new (PanelApplet *parent_applet)
+{
+       TrackerApplet *applet;
+       GError *error = NULL;
+       GtkBuilder *builder;
+       GSimpleActionGroup *action_group;
+       gchar *ui_path;
+
+       builder = gtk_builder_new ();
+       ui_path = g_build_filename (PKG_DATA_DIR,
+                                   "ui",
+                                   "tracker-search-bar.ui",
+                                   NULL);
+
+       if (gtk_builder_add_from_file (builder, ui_path, &error) == 0) {
+               g_printerr ("Could not load builder file, %s", error->message);
+               g_error_free (error);
+               g_free (ui_path);
+               g_object_unref (builder);
+
+               return FALSE;
+       }
+
+       g_print ("Added builder file:'%s'\n", ui_path);
+       g_free (ui_path);
+
+       applet = g_new0 (TrackerApplet, 1);
+
+       applet->parent = GTK_WIDGET (parent_applet);
+       applet->builder = builder;
+
+       applet->icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                "edit-find",
+                                                48,
+                                                0,
+                                                NULL);
+
+       applet_queue_draw (applet);
+
+       panel_applet_set_flags (PANEL_APPLET (applet->parent),
+                               PANEL_APPLET_EXPAND_MINOR);
+
+       action_group = g_simple_action_group_new ();
+       g_action_map_add_action_entries (G_ACTION_MAP (action_group),
+                                        applet_menu_actions,
+                                        G_N_ELEMENTS (applet_menu_actions),
+                                        applet);
+       ui_path = g_build_filename (PKG_DATA_DIR,
+                                   "ui",
+                                   "tracker-search-bar-menu.xml",
+                                   NULL);
+       panel_applet_setup_menu_from_file (PANEL_APPLET (applet->parent),
+                                          ui_path,
+                                          action_group,
+                                          GETTEXT_PACKAGE);
+       g_free (ui_path);
+
+       gtk_widget_insert_action_group (GTK_WIDGET (applet->parent), "tracker-search-bar",
+                                       G_ACTION_GROUP (action_group));
+
+       g_object_unref (action_group);
+
+       gtk_widget_show (applet->parent);
+
+       g_signal_connect (applet->parent, "size_allocate",
+                         G_CALLBACK (applet_size_allocate_cb), applet);
+       g_signal_connect (applet->parent, "change_orient",
+                         G_CALLBACK (applet_change_orient_cb), applet);
+
+#if 0
+       g_signal_connect (panel_applet_get_control (PANEL_APPLET (applet->parent)), "destroy",
+                         G_CALLBACK (applet_destroy_cb), applet);
+#endif
+
+       /* Initialise other modules */
+
+       return TRUE;
+}
+
+/*
+ * The entry point for this factory. If the OAFIID matches, create an instance
+ * of the applet.
+ */
+static gboolean
+applet_factory (PanelApplet *applet,
+                const gchar *iid,
+                gpointer     data)
+{
+       if (!strcmp (iid, "SearchBar")) {
+               g_print ("Creating applet\n");
+               return applet_new (applet);
+       }
+
+       return FALSE;
+}
+
+/*
+ * Generate the boilerplate to hook into GObject/Bonobo.
+ */
+PANEL_APPLET_OUT_PROCESS_FACTORY ("SearchBarFactory",
+                                  PANEL_TYPE_APPLET,
+                                  applet_factory,
+                                  NULL)
diff --git a/tracker-search-bar/tracker-applet.h b/tracker-search-bar/tracker-applet.h
new file mode 100644
index 0000000..bcfccc8
--- /dev/null
+++ b/tracker-search-bar/tracker-applet.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __TRACKER_APPLET_H__
+#define __TRACKER_APPLET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+       GtkBuilder *builder;
+
+       GtkWidget *results;
+       GtkWidget *parent;
+
+       GtkWidget *box;
+       GtkWidget *event_box;
+       GtkWidget *image;
+       GtkWidget *entry;
+
+       guint new_search_id;
+       guint idle_draw_id;
+
+       GtkOrientation orient;
+       GdkPixbuf *icon;
+       guint size;
+} TrackerApplet;
+
+G_END_DECLS
+
+#endif /* __TRACKER_APPLET_H__ */
diff --git a/tracker-search-bar/tracker-results-window.c b/tracker-search-bar/tracker-results-window.c
new file mode 100644
index 0000000..193d49a
--- /dev/null
+++ b/tracker-search-bar/tracker-results-window.c
@@ -0,0 +1,1533 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <panel-applet.h>
+
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "tracker-results-window.h"
+#include "tracker-aligned-window.h"
+#include "tracker-utils.h"
+
+#define MAX_ITEMS 10
+
+#define MUSIC_QUERY      \
+       "SELECT" \
+       "  ?uri ?title ?tooltip ?uri fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Audio ;" \
+       "  nie:url ?uri ; " \
+       "  nfo:fileName ?title ;" \
+       "  nfo:belongsToContainer ?tooltip ." \
+       "  ?urn fts:match \"%s*\" " \
+       "}" \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define IMAGE_QUERY      \
+       "SELECT" \
+       "  ?uri ?title ?tooltip ?uri fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Image ;" \
+       "  nie:url ?uri ; " \
+       "  nfo:fileName ?title ;" \
+       "  nfo:belongsToContainer ?tooltip ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define VIDEO_QUERY      \
+       "SELECT" \
+       "  ?uri ?title ?tooltip ?uri fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nmm:Video ;" \
+       "  nie:url ?uri ; " \
+       "  nfo:fileName ?title ;" \
+       "  nfo:belongsToContainer ?tooltip ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define DOCUMENT_QUERY   \
+       "SELECT" \
+       "  ?uri ?title ?tooltip ?uri fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Document ;" \
+       "  nie:url ?uri ; " \
+       "  nfo:fileName ?title ;" \
+       "  nfo:belongsToContainer ?tooltip ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define FOLDER_QUERY     \
+       "SELECT" \
+       "  ?uri ?title ?tooltip ?uri fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Folder ;" \
+       "  nie:url ?uri ; " \
+       "  nfo:fileName ?title ;" \
+       "  nfo:belongsToContainer ?tooltip ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define APP_QUERY        \
+       "SELECT" \
+       "  ?urn ?title ?tooltip ?link fts:rank(?urn) nfo:softwareIcon(?urn)" \
+       "WHERE {" \
+       "  ?urn a nfo:Software ;" \
+       "  nie:title ?title ;" \
+       "  nie:comment ?tooltip ;" \
+       "  nfo:softwareCmdLine ?link ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define TAGS_QUERY       \
+       "SELECT" \
+       "  ?urn ?title ?title ?urn fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nao:Tag ;" \
+       "  nao:prefLabel ?title ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+/* #define TAGS_QUERY                                   \ */
+/*      "SELECT"                                        \ */
+/*      "  ?urn ?title COUNT(?files) ?urn fts:rank(?urn) " \ */
+/*      "WHERE {"                                       \ */
+/*      "  ?urn a nao:Tag ;"                            \ */
+/*      "  nao:prefLabel ?title ."                      \ */
+/*      "  ?urn fts:match \"%s*\" ."                    \ */
+/*      "  ?files nao:hasTag ?urn "                     \ */
+/*      "} "                                            \ */
+/*      "GROUP BY ?urn "                                \ */
+/*      "ORDER BY DESC(fts:rank(?urn)) "                \ */
+/*      "OFFSET 0 LIMIT %d" */
+#define BOOKMARK_QUERY   \
+       "SELECT" \
+       "  ?urn ?title ?link ?link fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Bookmark ;" \
+       "  nie:title ?title ;" \
+       "  nie:links ?link ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define WEBSITE_QUERY    \
+       "SELECT" \
+       "  ?urn ?title ?link ?link fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nfo:Website ;" \
+       "  nie:title ?title ;" \
+       "  nie:links ?link ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+#define CONTACT_QUERY    \
+       "SELECT" \
+       "  ?urn ?title ?link ?link fts:rank(?urn) " \
+       "WHERE {" \
+       "  ?urn a nco:Contact ;" \
+       "  nco:fullname ?title ;" \
+       "  nco:hasEmailAddress ?link ." \
+       "  ?urn fts:match \"%s*\" " \
+       "} " \
+       "ORDER BY DESC(fts:rank(?urn)) " \
+       "OFFSET 0 LIMIT %d"
+
+#undef USE_SEPARATOR_FOR_SPACING
+
+#define TRACKER_RESULTS_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), 
TRACKER_TYPE_RESULTS_WINDOW, TrackerResultsWindowPrivate))
+
+typedef struct {
+       GtkWidget *frame;
+       GtkWidget *treeview;
+       GtkWidget *scrolled_window;
+       GObject *store;
+
+       GtkWidget *label;
+
+       GtkIconTheme *icon_theme;
+
+       TrackerSparqlConnection *connection;
+       GCancellable *cancellable;
+       gchar *query;
+
+       gboolean first_category_populated;
+
+       GList *search_queries;
+       gint queries_pending;
+       gint request_id;
+} TrackerResultsWindowPrivate;
+
+typedef enum {
+       CATEGORY_NONE                  = 1 << 0,
+       CATEGORY_CONTACT               = 1 << 1,
+       CATEGORY_TAG                   = 1 << 2,
+       CATEGORY_EMAIL_ADDRESS         = 1 << 3,
+       CATEGORY_DOCUMENT              = 1 << 4,
+       CATEGORY_APPLICATION           = 1 << 5,
+       CATEGORY_IMAGE                 = 1 << 6,
+       CATEGORY_AUDIO                 = 1 << 7,
+       CATEGORY_FOLDER                = 1 << 8,
+       CATEGORY_FONT                  = 1 << 9,
+       CATEGORY_VIDEO                 = 1 << 10,
+       CATEGORY_ARCHIVE               = 1 << 11,
+       CATEGORY_BOOKMARK              = 1 << 12,
+       CATEGORY_WEBSITE               = 1 << 13
+} TrackerCategory;
+
+typedef struct {
+       gchar *urn;
+       gchar *title;
+       gchar *tooltip;
+       gchar *link;
+       gchar *icon_name;
+       TrackerCategory category;
+} ItemData;
+
+typedef struct {
+       GCancellable *cancellable;
+       gint request_id;
+       TrackerCategory category;
+       TrackerResultsWindow *window;
+       GSList *results;
+} SearchQuery;
+
+struct FindCategory {
+       const gchar *category_str;
+       gboolean found;
+};
+
+static void     results_window_constructed          (GObject              *object);
+static void     results_window_finalize             (GObject              *object);
+static void     results_window_set_property         (GObject              *object,
+                                                     guint                 prop_id,
+                                                     const GValue         *value,
+                                                     GParamSpec           *pspec);
+static void     results_window_get_property         (GObject              *object,
+                                                     guint                 prop_id,
+                                                     GValue               *value,
+                                                     GParamSpec           *pspec);
+static gboolean results_window_key_press_event      (GtkWidget            *widget,
+                                                     GdkEventKey          *event);
+static gboolean results_window_button_press_event   (GtkWidget            *widget,
+                                                     GdkEventButton       *event);
+static void     results_window_get_preferred_width  (GtkWidget            *widget,
+                                                     gint                 *minimal_width,
+                                                     gint                 *natural_width);
+static void     results_window_get_preferred_height (GtkWidget            *widget,
+                                                     gint                 *minimal_height,
+                                                     gint                 *natural_height);
+static void     results_window_screen_changed       (GtkWidget            *widget,
+                                                     GdkScreen            *prev_screen);
+static void     model_set_up                        (TrackerResultsWindow *window);
+static void     search_get                          (TrackerResultsWindow *window,
+                                                     TrackerCategory       category);
+static void     search_start                        (TrackerResultsWindow *window);
+static void     search_query_free                   (SearchQuery          *sq);
+static gchar *  category_to_string                  (TrackerCategory       category);
+
+enum {
+       COL_CATEGORY_ID,
+       COL_IMAGE,
+       COL_IMAGE_REQUESTED,
+       COL_URN,
+       COL_TITLE,
+       COL_TOOLTIP,
+       COL_LINK,
+       COL_ICON_NAME,
+       COL_COUNT
+};
+
+enum {
+       PROP_0,
+       PROP_QUERY
+};
+
+G_DEFINE_TYPE (TrackerResultsWindow, tracker_results_window, TRACKER_TYPE_ALIGNED_WINDOW)
+
+static void
+tracker_results_window_class_init (TrackerResultsWindowClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->constructed = results_window_constructed;
+       object_class->finalize = results_window_finalize;
+       object_class->set_property = results_window_set_property;
+       object_class->get_property = results_window_get_property;
+
+       widget_class->key_press_event = results_window_key_press_event;
+       widget_class->button_press_event = results_window_button_press_event;
+       widget_class->get_preferred_width = results_window_get_preferred_width;
+       widget_class->get_preferred_height = results_window_get_preferred_height;
+       widget_class->screen_changed = results_window_screen_changed;
+
+       g_object_class_install_property (object_class,
+                                        PROP_QUERY,
+                                        g_param_spec_string ("query",
+                                                             "Query",
+                                                             "Query",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
+
+       g_type_class_add_private (object_class, sizeof (TrackerResultsWindowPrivate));
+}
+
+static gboolean
+launch_application_for_uri (GtkWidget   *widget,
+                            const gchar *uri)
+{
+       GdkAppLaunchContext *launch_context;
+       GdkScreen *screen;
+       GError *error = NULL;
+       gboolean success;
+
+       launch_context = gdk_app_launch_context_new ();
+
+       screen = gtk_widget_get_screen (widget);
+       gdk_app_launch_context_set_screen (launch_context, screen);
+
+       g_app_info_launch_default_for_uri (uri,
+                                          G_APP_LAUNCH_CONTEXT (launch_context),
+                                          &error);
+
+       if (error) {
+               g_critical ("Could not launch application for uri '%s': %s",
+                           uri, error->message);
+               g_error_free (error);
+               success = FALSE;
+       } else {
+               success = TRUE;
+       }
+
+       g_object_unref (launch_context);
+
+       return success;
+}
+
+static void
+tree_view_row_activated_cb (GtkTreeView       *treeview,
+                            GtkTreePath       *path,
+                            GtkTreeViewColumn *column,
+                            gpointer           user_data)
+{
+       TrackerResultsWindowPrivate *priv;
+       TrackerResultsWindow *window;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gchar *link;
+       gboolean success;
+
+       window = user_data;
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+       model = GTK_TREE_MODEL (priv->store);
+
+       if (!gtk_tree_model_get_iter (model, &iter, path)) {
+               return;
+       }
+
+       gtk_tree_model_get (model, &iter,
+                           COL_LINK, &link,
+                           -1);
+
+       if (!link) {
+               return;
+       }
+
+       if (tracker_regex_match (TRACKER_REGEX_ALL, link, NULL, NULL) > 0) {
+               success = launch_application_for_uri (GTK_WIDGET (window), link);
+       } else {
+               GError *error = NULL;
+
+               success = g_spawn_command_line_async (link, &error);
+
+               if (error) {
+                       g_critical ("Could not launch command line:'%s', %s",
+                                   link,
+                                   error->message);
+                       g_error_free (error);
+               }
+       }
+
+       if (success) {
+               gtk_widget_hide (GTK_WIDGET (window));
+       }
+
+       g_free (link);
+}
+
+static void
+tracker_results_window_init (TrackerResultsWindow *window)
+{
+       TrackerResultsWindowPrivate *priv;
+       GtkWidget *vbox;
+       GError *error = NULL;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+
+       priv->cancellable = g_cancellable_new ();
+       priv->connection = tracker_sparql_connection_get (priv->cancellable, &error);
+       if (error != NULL) {
+               g_warning ("Cannot connect to tracker: %s\n", error->message);
+               g_error_free (error);
+               g_object_unref (priv->cancellable);
+       }
+
+       priv->frame = gtk_frame_new (NULL);
+       gtk_container_add (GTK_CONTAINER (window), priv->frame);
+       gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), GTK_SHADOW_IN);
+       gtk_widget_set_size_request (priv->frame, 500, 500);
+       gtk_widget_show (priv->frame);
+
+       vbox = gtk_vbox_new (FALSE, 12);
+       gtk_container_add (GTK_CONTAINER (priv->frame), vbox);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+       gtk_widget_show (vbox);
+
+       priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       gtk_container_add (GTK_CONTAINER (vbox), priv->scrolled_window);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_AUTOMATIC);
+
+       priv->treeview = gtk_tree_view_new ();
+       gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->treeview);
+       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->treeview), FALSE);
+       g_signal_connect (priv->treeview, "row-activated",
+                         G_CALLBACK (tree_view_row_activated_cb), window);
+
+       priv->label = gtk_label_new (NULL);
+       gtk_widget_set_sensitive (priv->label, FALSE);
+       gtk_container_add (GTK_CONTAINER (vbox), priv->label);
+
+       priv->icon_theme = gtk_icon_theme_get_default ();
+
+       model_set_up (window);
+
+       gtk_widget_show_all (priv->scrolled_window);
+}
+
+static void
+results_window_constructed (GObject *object)
+{
+       TrackerResultsWindow *window;
+
+       window = TRACKER_RESULTS_WINDOW (object);
+
+       search_start (window);
+}
+
+static void
+results_window_finalize (GObject *object)
+{
+       TrackerResultsWindowPrivate *priv;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (object);
+
+       g_free (priv->query);
+
+       if (priv->cancellable) {
+               g_cancellable_cancel (priv->cancellable);
+               g_object_unref (priv->cancellable);
+       }
+
+       if (priv->connection) {
+               g_object_unref (priv->connection);
+       }
+
+       /* Clean up previous requests, this will call
+        * g_cancellable_cancel() on each query still running.
+        */
+       g_list_foreach (priv->search_queries, (GFunc) search_query_free, NULL);
+       g_list_free (priv->search_queries);
+
+       G_OBJECT_CLASS (tracker_results_window_parent_class)->finalize (object);
+}
+
+static void
+results_window_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+       TrackerResultsWindowPrivate *priv;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_QUERY:
+               /* Don't do the search_start() call if the window was
+                * just set up.
+                */
+               g_free (priv->query);
+               priv->query = g_value_dup_string (value);
+               search_start (TRACKER_RESULTS_WINDOW (object));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+results_window_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+       TrackerResultsWindowPrivate *priv;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_QUERY:
+               g_value_set_string (value, priv->query);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static gboolean
+results_window_key_press_event (GtkWidget   *widget,
+                                GdkEventKey *event)
+{
+       if (event->keyval == GDK_KEY_Escape) {
+               gtk_widget_hide (widget);
+
+               return TRUE;
+       }
+
+        if (event->keyval != GDK_KEY_Return &&
+            (*event->string != '\0' ||
+             event->keyval == GDK_KEY_BackSpace)) {
+                GtkWidget *entry;
+
+                entry = tracker_aligned_window_get_widget (TRACKER_ALIGNED_WINDOW (widget));
+                gtk_propagate_event (entry, (GdkEvent *) event);
+                return TRUE;
+        }
+
+       if (GTK_WIDGET_CLASS (tracker_results_window_parent_class)->key_press_event (widget, event)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+results_window_button_press_event (GtkWidget      *widget,
+                                   GdkEventButton *event)
+{
+       GtkAllocation alloc;
+
+       gtk_widget_get_allocation (widget, &alloc);
+
+       if (event->x < 0 || event->x > alloc.width ||
+           event->y < 0 || event->y > alloc.height) {
+               /* Click happened outside window, pop it down */
+               gtk_widget_hide (widget);
+               return TRUE;
+       }
+
+       if (GTK_WIDGET_CLASS (tracker_results_window_parent_class)->button_press_event &&
+           GTK_WIDGET_CLASS (tracker_results_window_parent_class)->button_press_event (widget, event)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+results_window_size_request (GtkWidget      *widget,
+                             GtkRequisition *requisition)
+{
+       GtkRequisition child_req;
+       guint border_width;
+
+       gtk_widget_size_request (gtk_bin_get_child (GTK_BIN (widget)), &child_req);
+       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+       requisition->width = child_req.width + (2 * border_width);
+       requisition->height = child_req.height + (2 * border_width);
+
+       if (gtk_widget_get_realized (widget)) {
+               GdkScreen *screen;
+               GdkRectangle monitor_geom;
+               guint monitor_num;
+
+               /* make it no larger than half the monitor size */
+               screen = gtk_widget_get_screen (widget);
+               monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
+
+               gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor_geom);
+
+               requisition->width = MIN (requisition->width, monitor_geom.width / 2);
+               requisition->height = MIN (requisition->height, monitor_geom.height / 2);
+       }
+}
+
+static void
+results_window_get_preferred_width  (GtkWidget *widget,
+                                     gint      *minimal_width,
+                                     gint      *natural_width)
+{
+       GtkRequisition requisition;
+
+       results_window_size_request (widget, &requisition);
+
+       *minimal_width = *natural_width = requisition.width;
+}
+
+static void
+results_window_get_preferred_height (GtkWidget *widget,
+                                     gint      *minimal_height,
+                                     gint      *natural_height)
+{
+       GtkRequisition requisition;
+
+       results_window_size_request (widget, &requisition);
+
+       *minimal_height = *natural_height = requisition.height;
+}
+
+static void
+results_window_screen_changed (GtkWidget *widget,
+                               GdkScreen *prev_screen)
+{
+       TrackerResultsWindowPrivate *priv;
+       GdkScreen *screen;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (widget);
+
+       if (priv->icon_theme) {
+               priv->icon_theme = NULL;
+       }
+
+       screen = gtk_widget_get_screen (widget);
+
+       if (screen) {
+               priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
+               /* FIXME: trigger the model to update icons */
+       }
+
+       GTK_WIDGET_CLASS (tracker_results_window_parent_class)->screen_changed (widget, prev_screen);
+}
+
+static ItemData *
+item_data_new (const gchar     *urn,
+               const gchar     *title,
+               const gchar     *tooltip,
+               const gchar     *link,
+               const gchar     *icon_name,
+               TrackerCategory  category)
+{
+       ItemData *id;
+
+       id = g_slice_new0 (ItemData);
+
+       id->urn = g_strdup (urn);
+       id->title = g_strdup (title);
+       id->tooltip = g_strdup (tooltip);
+       id->link = g_strdup (link);
+       id->icon_name = g_strdup (icon_name);
+       id->category = category;
+
+       return id;
+}
+
+static void
+item_data_free (ItemData *id)
+{
+       g_free (id->urn);
+       g_free (id->title);
+       g_free (id->tooltip);
+       g_free (id->link);
+       g_free (id->icon_name);
+
+       g_slice_free (ItemData, id);
+}
+
+static SearchQuery *
+search_query_new (gint                  request_id,
+                  TrackerCategory       category,
+                  TrackerResultsWindow *window)
+{
+       SearchQuery *sq;
+
+       sq = g_slice_new0 (SearchQuery);
+
+       sq->request_id = request_id;
+       sq->cancellable = g_cancellable_new ();
+       sq->category = category;
+       sq->window = window;
+       sq->results = NULL;
+
+       return sq;
+}
+
+static void
+search_query_free (SearchQuery *sq)
+{
+       if (sq->cancellable) {
+               g_cancellable_cancel (sq->cancellable);
+               g_object_unref (sq->cancellable);
+       }
+
+       g_slist_foreach (sq->results, (GFunc) item_data_free, NULL);
+       g_slist_free (sq->results);
+
+       g_slice_free (SearchQuery, sq);
+}
+
+static gchar *
+category_to_string (TrackerCategory category)
+{
+       switch (category) {
+       case CATEGORY_NONE: return _("Other");
+       case CATEGORY_CONTACT: return _("Contacts");
+       case CATEGORY_TAG: return _("Tags");
+       case CATEGORY_EMAIL_ADDRESS: return _("Email Addresses");
+       case CATEGORY_DOCUMENT: return _("Documents");
+       case CATEGORY_APPLICATION: return _("Applications");
+       case CATEGORY_IMAGE: return _("Images");
+       case CATEGORY_AUDIO: return _("Audio");
+       case CATEGORY_FOLDER: return _("Folders");
+       case CATEGORY_FONT: return _("Fonts");
+       case CATEGORY_VIDEO: return _("Videos");
+       case CATEGORY_ARCHIVE: return _("Archives");
+       case CATEGORY_BOOKMARK: return _("Bookmarks");
+       case CATEGORY_WEBSITE: return _("Links");
+       }
+
+       return _("Other");
+}
+
+inline static void
+category_from_string (const gchar *type,
+                      guint       *categories)
+{
+       if (g_str_has_suffix (type, "nao#Tag")) {
+               *categories |= CATEGORY_TAG;
+       }
+
+       if (g_str_has_suffix (type, "nfo#TextDocument") ||
+           g_str_has_suffix (type, "nfo#PaginatedTextDocument")) {
+               *categories |= CATEGORY_DOCUMENT;
+       }
+
+       if (g_str_has_suffix (type, "nco#Contact")) {
+               *categories |= CATEGORY_CONTACT;
+       }
+
+       if (g_str_has_suffix (type, "nco#EmailAddress")) {
+               *categories |= CATEGORY_EMAIL_ADDRESS;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Image") ||
+           g_str_has_suffix (type, "nfo#RosterImage") ||
+           g_str_has_suffix (type, "nfo#VectorImage") ||
+           g_str_has_suffix (type, "nfo#FilesystemImage")) {
+               *categories |= CATEGORY_IMAGE;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Audio") ||
+           g_str_has_suffix (type, "nmm#MusicPiece")) {
+               *categories |= CATEGORY_AUDIO;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Folder")) {
+               *categories |= CATEGORY_FOLDER;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Font")) {
+               *categories |= CATEGORY_FONT;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Video") ||
+           g_str_has_suffix (type, "nmm#Video")) {
+               *categories |= CATEGORY_VIDEO;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Archive")) {
+               *categories |= CATEGORY_ARCHIVE;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Bookmark")) {
+               *categories |= CATEGORY_BOOKMARK;
+       }
+
+       if (g_str_has_suffix (type, "nfo#Website")) {
+               *categories |= CATEGORY_WEBSITE;
+       }
+}
+
+static GdkPixbuf *
+pixbuf_get (TrackerResultsWindow *window,
+            const gchar          *uri,
+            const gchar          *icon_name,
+            TrackerCategory       category)
+{
+       TrackerResultsWindowPrivate *priv;
+       const gchar *attributes;
+       GFile *file;
+       GFileInfo *info;
+       GIcon *icon;
+       GdkPixbuf *pixbuf = NULL;
+       GError *error = NULL;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+       file = g_file_new_for_uri (uri);
+
+        if (category & CATEGORY_TAG) {
+                icon_name = GTK_STOCK_INDEX;
+        } else if (category & CATEGORY_BOOKMARK) {
+                icon_name = "user-bookmarks";
+        }
+
+       if (icon_name) {
+               if (strrchr (icon_name, '.') == NULL) {
+                       pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
+                                                          icon_name, 24,
+                                                          GTK_ICON_LOOKUP_USE_BUILTIN,
+                                                          &error);
+
+                       if (error) {
+                               g_printerr ("Couldn't get icon name '%s': %s\n",
+                                           icon_name, error->message);
+                               g_error_free (error);
+                       }
+               } else {
+                       const gchar * const *xdg_dirs;
+                       gchar *path;
+                       gint i;
+
+                       /* Icon name is actually some filename in $(sharedir)/icons */
+                       xdg_dirs = g_get_system_data_dirs ();
+
+                       for (i = 0; !pixbuf && xdg_dirs[i]; i++) {
+                               path = g_build_filename (xdg_dirs[i], "icons", icon_name, NULL);
+                               pixbuf = gdk_pixbuf_new_from_file_at_size (path, 24, 24, NULL);
+                               g_free (path);
+                       }
+               }
+        } else if (category & CATEGORY_IMAGE) {
+               gchar *path;
+
+               path = g_file_get_path (file);
+               pixbuf = gdk_pixbuf_new_from_file_at_size (path, 24, 24, &error);
+               g_free (path);
+
+               if (error) {
+                       g_printerr ("Couldn't get pixbuf for uri:'%s', %s\n",
+                                   uri,
+                                   error->message);
+                       g_clear_error (&error);
+               } else {
+                       g_object_unref (file);
+                       return pixbuf;
+               }
+       } else if (category &
+                  (CATEGORY_DOCUMENT |
+                   CATEGORY_IMAGE |
+                   CATEGORY_AUDIO |
+                   CATEGORY_FOLDER |
+                   CATEGORY_VIDEO |
+                   CATEGORY_ARCHIVE)) {
+               attributes =
+                       G_FILE_ATTRIBUTE_STANDARD_ICON;
+
+               info = g_file_query_info (file,
+                                         attributes,
+                                         G_FILE_QUERY_INFO_NONE,
+                                         NULL,
+                                         &error);
+
+               if (error) {
+                       g_printerr ("Couldn't get pixbuf for uri:'%s', %s\n",
+                                   uri,
+                                   error->message);
+                       g_object_unref (file);
+                       g_error_free (error);
+
+                       return NULL;
+               }
+
+               icon = g_file_info_get_icon (info);
+
+               if (icon && G_IS_THEMED_ICON (icon)) {
+                       GtkIconInfo *icon_info;
+                       const gchar **names;
+
+                       names = (const gchar**) g_themed_icon_get_names (G_THEMED_ICON (icon));
+                       icon_info = gtk_icon_theme_choose_icon (priv->icon_theme,
+                                                               names,
+                                                               24,
+                                                               GTK_ICON_LOOKUP_USE_BUILTIN);
+
+                       if (icon_info) {
+                               pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
+                               gtk_icon_info_free (icon_info);
+                       }
+               }
+
+               g_object_unref (info);
+       } else {
+               g_message ("No pixbuf could be retrieved for category %s (URI: %s)\n",
+                          category_to_string (category), uri);
+       }
+
+       g_object_unref (file);
+
+       return pixbuf;
+}
+
+static void
+model_category_cell_data_func (GtkTreeViewColumn    *tree_column,
+                               GtkCellRenderer      *cell,
+                               GtkTreeModel         *model,
+                               GtkTreeIter          *iter,
+                               TrackerResultsWindow *window)
+{
+       GtkTreePath *path;
+       GtkTreeIter prev_iter;
+       TrackerCategory category;
+       gboolean previous_path;
+       gboolean print = FALSE;
+
+       gtk_tree_model_get (model, iter, COL_CATEGORY_ID, &category, -1);
+
+       /* Get the previous iter */
+       path = gtk_tree_model_get_path (model, iter);
+
+       previous_path = gtk_tree_path_prev (path);
+
+       if (!previous_path) {
+               print = TRUE;
+       } else if (previous_path && gtk_tree_model_get_iter (model, &prev_iter, path)) {
+               TrackerCategory prev_category = CATEGORY_NONE;
+
+               gtk_tree_model_get (model, &prev_iter,
+                                   COL_CATEGORY_ID, &prev_category,
+                                   -1);
+
+               if (prev_category == CATEGORY_NONE) {
+                       print = TRUE;
+               }
+       }
+
+       g_object_set (cell,
+                     "text", print ? category_to_string (category) : "",
+                     "visible", print,
+                     NULL);
+
+       gtk_tree_path_free (path);
+}
+
+static void
+model_pixbuf_cell_data_func (GtkTreeViewColumn    *tree_column,
+                             GtkCellRenderer      *cell,
+                             GtkTreeModel         *model,
+                             GtkTreeIter          *iter,
+                             TrackerResultsWindow *window)
+{
+       GdkPixbuf *pixbuf = NULL;
+       gboolean requested = FALSE;
+
+       gtk_tree_model_get (model, iter, COL_IMAGE_REQUESTED, &requested, -1);
+
+       if (!requested) {
+               TrackerCategory category = CATEGORY_NONE;
+               gchar *urn, *icon_name;
+
+               gtk_tree_model_get (model, iter,
+                                   COL_CATEGORY_ID, &category,
+                                   COL_URN, &urn,
+                                   COL_ICON_NAME, &icon_name,
+                                   -1);
+
+               if (urn) {
+                       /* FIXME: Should use category */
+                       pixbuf = pixbuf_get (window, urn, icon_name, category);
+                       g_free (urn);
+                       g_free (icon_name);
+               }
+
+               /* Cache it in the store */
+               gtk_list_store_set (GTK_LIST_STORE (model), iter,
+                                   COL_IMAGE, pixbuf,
+                                   COL_IMAGE_REQUESTED, TRUE,
+                                   -1);
+       } else {
+               /* We do this because there may be no image for a file
+                * and we don't want to keep requesting the same
+                * file's image.
+                */
+               gtk_tree_model_get (model, iter, COL_IMAGE, &pixbuf, -1);
+       }
+
+       g_object_set (cell,
+                     "visible", TRUE,
+                     "pixbuf", pixbuf,
+                     NULL);
+
+       if (pixbuf) {
+               g_object_unref (pixbuf);
+       }
+}
+
+static gboolean
+model_separator_func (GtkTreeModel *model,
+                      GtkTreeIter  *iter,
+                      gpointer      user_data)
+{
+#ifdef USE_SEPARATOR_FOR_SPACING
+       gchar *urn;
+
+       gtk_tree_model_get (model, iter, COL_URN, &urn, -1);
+
+       if (!urn) {
+               return TRUE;
+       }
+
+       g_free (urn);
+
+       return FALSE;
+#else  /* USE_SEPARATOR_FOR_SPACING */
+       return FALSE;
+#endif /* USE_SEPARATOR_FOR_SPACING */
+}
+
+static gboolean
+model_selection_func (GtkTreeSelection *selection,
+                      GtkTreeModel     *model,
+                      GtkTreePath      *path,
+                      gboolean          path_currently_selected,
+                      gpointer          data)
+{
+       GtkTreeIter iter;
+       gchar *urn;
+
+       gtk_tree_model_get_iter (model, &iter, path);
+       gtk_tree_model_get (model, &iter, COL_URN, &urn, -1);
+
+       if (!urn) {
+               return FALSE;
+       }
+
+       g_free (urn);
+
+       return TRUE;
+}
+
+static void
+model_set_up (TrackerResultsWindow *window)
+{
+       TrackerResultsWindowPrivate *priv;
+       GtkTreeView *view;
+       GtkTreeViewColumn *column;
+       GtkTreeSelection *selection;
+       GtkListStore *store;
+       GtkCellRenderer *cell;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+       view = GTK_TREE_VIEW (priv->treeview);
+
+       /* View */
+       gtk_tree_view_set_enable_search (view, FALSE);
+
+       /* Store */
+       store = gtk_list_store_new (COL_COUNT,
+                                   G_TYPE_INT,            /* Category ID */
+                                   GDK_TYPE_PIXBUF,       /* Image */
+                                   G_TYPE_BOOLEAN,        /* Image requested */
+                                   G_TYPE_STRING,         /* URN */
+                                   G_TYPE_STRING,         /* Title */
+                                   G_TYPE_STRING,         /* Tooltip */
+                                   G_TYPE_STRING,         /* Link */
+                                   G_TYPE_STRING);        /* Icon name */
+
+       gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
+
+       gtk_tree_view_set_row_separator_func (view,
+                                             model_separator_func,
+                                             window,
+                                             NULL);
+
+       /* Selection */
+       selection = gtk_tree_view_get_selection (view);
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+       gtk_tree_selection_set_select_function (selection,
+                                               model_selection_func,
+                                               window,
+                                               NULL);
+
+       /* Column: Category */
+       column = gtk_tree_view_column_new ();
+       cell = gtk_cell_renderer_text_new ();
+
+       gtk_tree_view_column_pack_start (column, cell, FALSE);
+       gtk_tree_view_column_set_cell_data_func (column, cell,
+                                                (GtkTreeCellDataFunc)
+                                                model_category_cell_data_func,
+                                                window,
+                                                NULL);
+
+       gtk_tree_view_column_set_title (column, _("Category"));
+       gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+       gtk_tree_view_column_set_sort_column_id (column, COL_CATEGORY_ID);
+       gtk_tree_view_append_column (view, column);
+
+       /* Column: Icon + Title */
+       column = gtk_tree_view_column_new ();
+
+       cell = gtk_cell_renderer_pixbuf_new ();
+       gtk_tree_view_column_pack_start (column, cell, FALSE);
+       gtk_tree_view_column_set_cell_data_func (column, cell,
+                                                (GtkTreeCellDataFunc)
+                                                model_pixbuf_cell_data_func,
+                                                window,
+                                                NULL);
+        g_object_set (cell,
+                      "height", 24,
+                      "width", 24,
+                      NULL);
+
+       cell = gtk_cell_renderer_text_new ();
+       g_object_set (cell,
+                     "xpad", 4,
+                     "ypad", 1,
+                     NULL);
+       gtk_tree_view_column_pack_start (column, cell, TRUE);
+       gtk_tree_view_column_add_attribute (column, cell, "text", COL_TITLE);
+
+       gtk_tree_view_column_set_title (column, _("Title"));
+       gtk_tree_view_column_set_expand (column, TRUE);
+       gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+       gtk_tree_view_column_set_sort_column_id (column, COL_TITLE);
+       gtk_tree_view_append_column (view, column);
+
+       /* Sorting */
+       /* gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), */
+       /*                                    COL_CATEGORY_ID, */
+       /*                                    GTK_SORT_ASCENDING); */
+
+       /* Tooltips */
+       gtk_tree_view_set_tooltip_column (view, COL_TOOLTIP);
+
+       /* Save */
+       priv->store = G_OBJECT (store);
+}
+
+static void
+model_add (TrackerResultsWindow *window,
+           TrackerCategory       category,
+           const gchar          *urn,
+           const gchar          *title,
+           const gchar          *tooltip,
+           const gchar          *link,
+           const gchar          *icon_name)
+{
+       TrackerResultsWindowPrivate *priv;
+       GtkTreeIter iter;
+       GdkPixbuf *pixbuf;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+       pixbuf = NULL;
+
+       gtk_list_store_append (GTK_LIST_STORE (priv->store), &iter);
+       gtk_list_store_set (GTK_LIST_STORE (priv->store), &iter,
+                           COL_CATEGORY_ID, category,
+                           COL_IMAGE, pixbuf ? pixbuf : NULL,
+                           COL_URN, urn,
+                           COL_TITLE, title,
+                           COL_TOOLTIP, tooltip,
+                           COL_LINK, link,
+                           COL_ICON_NAME, icon_name,
+                           -1);
+
+       /* path = gtk_tree_model_get_path (GTK_TREE_MODEL (window->store), &iter); */
+       /* gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (window->treeview), tooltip, path); */
+       /* gtk_tree_path_free (path); */
+
+       /* gtk_tree_selection_select_iter (selection, &iter); */
+}
+
+static void
+search_window_ensure_not_blank (TrackerResultsWindow *window)
+{
+       TrackerResultsWindowPrivate *priv;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+
+       if (priv->queries_pending == 0) {
+               GtkTreeIter iter;
+
+               /* No more queries pending */
+               if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter)) {
+                       gchar *str;
+
+                       str = g_strdup_printf (_("No results found for “%s”"), priv->query);
+                       gtk_label_set_text (GTK_LABEL (priv->label), str);
+                       g_free (str);
+
+                       gtk_widget_hide (priv->scrolled_window);
+                       gtk_widget_show (priv->label);
+               } else {
+                       gtk_widget_show_all (priv->scrolled_window);
+                       gtk_widget_hide (priv->label);
+               }
+       }
+}
+
+inline static void
+search_get_foreach (SearchQuery         *sq,
+                   TrackerSparqlCursor *cursor)
+{
+       ItemData *id;
+       const gchar *urn, *title, *tooltip, *link, *rank, *icon_name;
+
+       urn = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+       title = tracker_sparql_cursor_get_string (cursor, 1, NULL);
+       tooltip = tracker_sparql_cursor_get_string (cursor, 2, NULL);
+       link = tracker_sparql_cursor_get_string (cursor, 3, NULL);
+       rank = tracker_sparql_cursor_get_string (cursor, 4, NULL);
+       icon_name = tracker_sparql_cursor_get_string (cursor, 5, NULL);
+
+       /* App queries don't return rank or belongs */
+       if (!rank) {
+               rank = "0.0";
+       }
+
+       if (icon_name && g_str_has_prefix (icon_name, "urn:theme-icon:")) {
+               icon_name += strlen ("urn:theme-icon:");
+       }
+
+       g_print ("urn:'%s' found (rank:'%s')\n", urn, rank);
+       g_print ("  title:'%s'\n", title);
+       g_print ("  tooltip:'%s'\n", tooltip);
+       g_print ("  link:'%s'\n", link);
+
+       id = item_data_new (urn, title, tooltip, link, icon_name, sq->category);
+       sq->results = g_slist_append (sq->results, id);
+
+       /* category_from_string (type, &id->categories); */
+       /* g_print ("  type:'%s', new categories:%d\n", type, id->categories); */
+}
+
+static void
+search_get_cb (GObject      *source_object,
+              GAsyncResult *res,
+              gpointer      user_data)
+{
+       TrackerSparqlCursor *cursor;
+       TrackerResultsWindow *window;
+       TrackerResultsWindowPrivate *priv;
+       SearchQuery *sq;
+       GError *error = NULL;
+
+       sq = user_data;
+       window = sq->window;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+       priv->queries_pending--;
+
+       cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (source_object),
+                                                        res,
+                                                        &error);
+
+       /* If request IDs don't match, data is no longer needed */
+       if (priv->request_id != sq->request_id) {
+               g_message ("Received data from request id:%d, now on request id:%d",
+                          sq->request_id,
+                          priv->request_id);
+
+               priv->search_queries = g_list_remove (priv->search_queries, sq);
+               search_query_free (sq);
+
+               /* We don't care about errors if we're not interested
+                * in the results.
+                */
+               g_clear_error (&error);
+
+               if (cursor) {
+                       g_object_unref (cursor);
+               }
+
+               return;
+       }
+
+       if (error) {
+               g_printerr ("Could not get search results, %s\n", error->message);
+               g_error_free (error);
+
+               if (cursor) {
+                       g_object_unref (cursor);
+               }
+
+               priv->search_queries = g_list_remove (priv->search_queries, sq);
+               search_query_free (sq);
+               search_window_ensure_not_blank (window);
+
+               return;
+       }
+
+       if (!cursor) {
+               g_print ("No results were found matching the query in category:'%s'\n",
+                        category_to_string (sq->category));
+       } else {
+               GSList *l;
+
+               g_print ("Results: category:'%s'\n",
+                        category_to_string (sq->category));
+
+               /* FIXME: make async */
+               while (tracker_sparql_cursor_next (cursor,
+                                                  priv->cancellable,
+                                                  &error)) {
+                       search_get_foreach (sq, cursor);
+               }
+
+               /* Add separator */
+               if (priv->first_category_populated && g_slist_length (sq->results) > 0) {
+                       model_add (window, CATEGORY_NONE, NULL, NULL, NULL, NULL, NULL);
+               } else {
+                       priv->first_category_populated = TRUE;
+               }
+
+               for (l = sq->results; l; l = l->next) {
+                       ItemData *id = l->data;
+
+                       model_add (window,
+                                  sq->category,
+                                  id->urn,
+                                  id->title,
+                                  id->tooltip,
+                                  id->link,
+                                  id->icon_name);
+               }
+
+               g_object_unref (cursor);
+       }
+
+       priv->search_queries = g_list_remove (priv->search_queries, sq);
+       search_query_free (sq);
+       search_window_ensure_not_blank (window);
+
+       if (priv->queries_pending < 1) {
+               g_print ("\n\n\n");
+       }
+}
+
+static void
+search_get (TrackerResultsWindow *window,
+            TrackerCategory       category)
+{
+       TrackerResultsWindowPrivate *priv;
+       SearchQuery *sq;
+       gchar *sparql;
+       const gchar *format;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+
+       if (!priv->connection) {
+               return;
+       }
+
+       switch (category) {
+       case CATEGORY_IMAGE:
+               format = IMAGE_QUERY;
+               break;
+       case CATEGORY_AUDIO:
+               format = MUSIC_QUERY;
+               break;
+       case CATEGORY_VIDEO:
+               format = VIDEO_QUERY;
+               break;
+       case CATEGORY_DOCUMENT:
+               format = DOCUMENT_QUERY;
+               break;
+       case CATEGORY_FOLDER:
+               format = FOLDER_QUERY;
+               break;
+       case CATEGORY_APPLICATION:
+               format = APP_QUERY;
+               break;
+       case CATEGORY_TAG:
+               format = TAGS_QUERY;
+               break;
+       case CATEGORY_BOOKMARK:
+               format = BOOKMARK_QUERY;
+               break;
+       case CATEGORY_WEBSITE:
+               format = WEBSITE_QUERY;
+               break;
+       case CATEGORY_CONTACT:
+               format = CONTACT_QUERY;
+               break;
+       default:
+               format = NULL;
+               break;
+       }
+
+       if (!format) {
+               return;
+       }
+
+       sq = search_query_new (priv->request_id, category, window);
+       priv->search_queries = g_list_prepend (priv->search_queries, sq);
+
+       sparql = g_strdup_printf (format, priv->query, MAX_ITEMS);
+       tracker_sparql_connection_query_async (priv->connection,
+                                              sparql,
+                                              sq->cancellable,
+                                              search_get_cb,
+                                              sq);
+       g_free (sparql);
+
+       priv->queries_pending++;
+}
+
+static void
+search_start (TrackerResultsWindow *window)
+{
+       TrackerResultsWindowPrivate *priv;
+       GtkTreeModel *model;
+       GtkListStore *store;
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+
+       /* Cancel current requests */
+       priv->request_id++;
+       g_message ("Incrementing request ID to %d", priv->request_id);
+
+       /* Clear current data */
+       g_message ("Clearing previous results");
+       model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
+       store = GTK_LIST_STORE (model);
+       gtk_list_store_clear (store);
+
+       if (!priv->query || strlen (priv->query) < 1) {
+               gtk_widget_show (priv->scrolled_window);
+               gtk_widget_hide (priv->label);
+               gtk_widget_hide (GTK_WIDGET (window));
+               return;
+       }
+
+       priv->first_category_populated = FALSE;
+
+       /* Clean up previous requests, this will call
+        * g_cancellable_cancel() on each query still running.
+        */
+       g_list_foreach (priv->search_queries, (GFunc) search_query_free, NULL);
+       g_list_free (priv->search_queries);
+
+       /* SPARQL requests */
+       search_get (window, CATEGORY_IMAGE);
+       search_get (window, CATEGORY_AUDIO);
+       search_get (window, CATEGORY_VIDEO);
+       search_get (window, CATEGORY_DOCUMENT);
+       search_get (window, CATEGORY_FOLDER);
+       search_get (window, CATEGORY_APPLICATION);
+       search_get (window, CATEGORY_TAG);
+       search_get (window, CATEGORY_BOOKMARK);
+       search_get (window, CATEGORY_WEBSITE);
+       search_get (window, CATEGORY_CONTACT);
+}
+
+static gboolean
+grab_popup_window (TrackerResultsWindow *window)
+{
+       GdkGrabStatus status;
+       GtkWidget *widget;
+       guint32 time;
+
+       widget = GTK_WIDGET (window);
+       time = gtk_get_current_event_time ();
+
+       /* Grab pointer */
+       status = gdk_pointer_grab (gtk_widget_get_window (widget),
+                                  TRUE,
+                                  GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK,
+                                  NULL, NULL,
+                                  time);
+
+       if (status == GDK_GRAB_SUCCESS) {
+               status = gdk_keyboard_grab (gtk_widget_get_window (widget), TRUE, time);
+       }
+
+       if (status == GDK_GRAB_SUCCESS) {
+               gtk_widget_grab_focus (widget);
+       } else if (status == GDK_GRAB_NOT_VIEWABLE) {
+               /* window is not viewable yet, retry */
+               return TRUE;
+       } else {
+               gtk_widget_hide (widget);
+       }
+
+       return FALSE;
+}
+
+GtkWidget *
+tracker_results_window_new (GtkWidget   *parent,
+                            const gchar *query)
+{
+       return g_object_new (TRACKER_TYPE_RESULTS_WINDOW,
+                            "align-widget", parent,
+                            "query", query,
+                            NULL);
+}
+
+void
+tracker_results_window_popup (TrackerResultsWindow *window)
+{
+        TrackerResultsWindowPrivate *priv;
+        GtkAdjustment *vadj, *hadj;
+
+        g_return_if_fail (TRACKER_IS_RESULTS_WINDOW (window));
+
+       priv = TRACKER_RESULTS_WINDOW_GET_PRIVATE (window);
+
+       gtk_widget_realize (GTK_WIDGET (window));
+       gtk_widget_show (GTK_WIDGET (window));
+
+        /* Force scroll to top-left */
+        vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolled_window));
+        gtk_adjustment_set_value (vadj, gtk_adjustment_get_lower (vadj));
+
+        hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolled_window));
+        gtk_adjustment_set_value (hadj, gtk_adjustment_get_lower (hadj));
+
+        g_idle_add ((GSourceFunc) grab_popup_window, window);
+}
diff --git a/tracker-search-bar/tracker-results-window.h b/tracker-search-bar/tracker-results-window.h
new file mode 100644
index 0000000..810bad5
--- /dev/null
+++ b/tracker-search-bar/tracker-results-window.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __TRACKER_RESULTS_WINDOW_H__
+#define __TRACKER_RESULTS_WINDOW_H__
+
+#include "tracker-aligned-window.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_RESULTS_WINDOW         (tracker_results_window_get_type())
+#define TRACKER_RESULTS_WINDOW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_RESULTS_WINDOW, 
TrackerResultsWindow))
+#define TRACKER_RESULTS_WINDOW_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_RESULTS_WINDOW, 
TrackerResultsWindowClass))
+#define TRACKER_IS_RESULTS_WINDOW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_RESULTS_WINDOW))
+#define TRACKER_IS_RESULTS_WINDOW_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    TRACKER_TYPE_RESULTS_WINDOW))
+#define TRACKER_RESULTS_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_RESULTS_WINDOW, 
TrackerResultsWindowClass))
+
+typedef struct TrackerResultsWindow TrackerResultsWindow;
+typedef struct TrackerResultsWindowClass TrackerResultsWindowClass;
+
+struct TrackerResultsWindow {
+       TrackerAlignedWindow parent_instance;
+};
+
+struct TrackerResultsWindowClass {
+       TrackerAlignedWindowClass parent_class;
+};
+
+GType       tracker_results_window_get_type (void) G_GNUC_CONST;
+
+GtkWidget * tracker_results_window_new      (GtkWidget   *parent,
+                                             const gchar *query);
+void        tracker_results_window_popup    (TrackerResultsWindow *window);
+
+G_END_DECLS
+
+#endif /* __TRACKER_RESULTS_WINDOW_H__ */
diff --git a/tracker-search-bar/tracker-search-bar-menu.xml b/tracker-search-bar/tracker-search-bar-menu.xml
new file mode 100644
index 0000000..de963eb
--- /dev/null
+++ b/tracker-search-bar/tracker-search-bar-menu.xml
@@ -0,0 +1,6 @@
+<section>
+       <item>
+               <attribute name="label" translatable="yes">_About</attribute>
+               <attribute name="action">tracker-search-bar.about</attribute>
+       </item>
+</section>
diff --git a/tracker-search-bar/tracker-search-bar.ui b/tracker-search-bar/tracker-search-bar.ui
new file mode 100644
index 0000000..ad8a0b6
--- /dev/null
+++ b/tracker-search-bar/tracker-search-bar.ui
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkAboutDialog" id="dialog_about">
+    <property name="border_width">5</property>
+    <property name="resizable">False</property>
+    <property name="type_hint">normal</property>
+    <property name="program_name">Search Bar</property>
+    <property name="version">0.7</property>
+    <property name="copyright" translatable="yes">Copyright Tracker Authors 2005-2010</property>
+    <property name="comments" translatable="yes">A search bar applet for finding content stored in 
Tracker</property>
+    <property name="license" translatable="yes">Tracker 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.
+
+Tracker 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 Tracker; if not, write to the 
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.</property>
+    <property name="authors">Martyn Russell &lt;martyn at lanedo com&gt;
+J&#xFC;rg Billeter &lt;juerg.billeter at codethink co uk&gt;
+Philip Van Hoof &lt;pvanhoof at gnome org&gt;
+Carlos Garnacho &lt;carlos at lanedo com&gt;
+Mikael Ottela &lt;mikael.ottela at ixonos com&gt;
+Ivan Frade &lt;ivan.frade at nokia com&gt;
+Jamie McCracken &lt;jamiemcc at gnome org&gt;
+Adrien Bustany &lt;abustany at gnome org&gt;
+Aleksander Morgado &lt;aleksander at lanedo com&gt;
+Anders Aagaard &lt;aagaande at gmail com&gt;
+Anders Rune Jensen &lt;anders iola dk&gt;
+Baptiste Mille-Mathias &lt;baptist millemathias gmail com&gt;
+Christoph Laimburg &lt;christoph laimburg at rolmail net&gt;
+Dan Nicolaescu &lt;dann at ics uci edu&gt;
+Deji Akingunola &lt;dakingun gmail com&gt;
+Edward Duffy &lt;eduffy at gmail com&gt;
+Eskil Bylund &lt;eskil at letterboxes org&gt;
+Eugenio &lt;me at eugesoftware com&gt;
+Fabien VALLON &lt;fabien at sonappart net&gt;
+Gergan Penkov &lt;gergan at gmail com&gt;
+Halton Huo &lt;halton huo at sun com&gt;
+Jaime Frutos Morales &lt;acidborg at gmail com&gt;
+Jedy Wang &lt;jedy wang at sun com&gt;
+Jerry Tan &lt;jerry tan at sun com&gt;
+John Stowers &lt;john.stowers at gmail com&gt;
+Julien &lt;julienc psychologie-fr org&gt;
+Laurent Aguerreche &lt;laurent.aguerreche at free fr&gt;
+Luca Ferretti &lt;elle.uca at libero it&gt;
+Marcus Fritzsch &lt;fritschy at googlemail com&gt;
+Michael Biebl &lt;mbiebl at gmail com&gt;
+Michal Pryc &lt;michal pryc at sun com&gt;
+Mikkel Kamstrup Erlandsen &lt;mikkel kamstrup gmail com&gt;
+Nate Nielsen  &lt;nielsen at memberwewbs com&gt;
+Neil Patel &lt;njpatel at gmail com&gt;
+Richard Quirk &lt;quirky at zoom co uk&gt;
+Saleem Abdulrasool &lt;compnerd at gentoo org&gt;
+Samuel Cormier-Iijima &lt;sciyoshi at gmail com&gt;
+Tobutaz &lt;tobutaz gmail com&gt;
+Tom &lt;tpgww at onepost net&gt;
+Tshepang Lekhonkhobe &lt;tshepang at gmail com&gt;
+Ulrik Mikaelsson &lt;ulrik mikaelsson gmail com&gt;
+</property>
+    <property name="logo_icon_name">edit-find</property>
+    <property name="wrap_license">True</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <placeholder/>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/tracker-search-bar/tracker-utils.c b/tracker-search-bar/tracker-utils.c
new file mode 100644
index 0000000..f3f8691
--- /dev/null
+++ b/tracker-search-bar/tracker-utils.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include <glib/gi18n.h>
+
+#include "tracker-utils.h"
+
+/*
+ * Regular Expression code to match urls.
+ */
+#define USERCHARS "-A-Za-z0-9"
+#define PASSCHARS "-A-Za-z0-9,?;.:/!%$^*&~\"#'"
+#define HOSTCHARS "-A-Za-z0-9"
+#define PATHCHARS "-A-Za-z0-9_$.+!*(),;:@&=?/~#%"
+#define SCHEME    "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)"
+#define USER      "[" USERCHARS "]+(:["PASSCHARS "]+)?"
+#define URLPATH   "/[" PATHCHARS "]*[^]'.}>) \t\r\n,\\\"]"
+
+static regex_t dingus[TRACKER_REGEX_ALL];
+
+static void
+regex_init (void)
+{
+       static gboolean  inited = FALSE;
+       const gchar     *expression;
+       gint             i;
+
+       if (inited) {
+               return;
+       }
+
+       for (i = 0; i < TRACKER_REGEX_ALL; i++) {
+               switch (i) {
+               case TRACKER_REGEX_AS_IS:
+                       expression =
+                               SCHEME "//(" USER "@)?[" HOSTCHARS ".]+"
+                               "(:[0-9]+)?(" URLPATH ")?";
+                       break;
+               case TRACKER_REGEX_BROWSER:
+                       expression =
+                               "(www|ftp)[" HOSTCHARS "]*\\.[" HOSTCHARS ".]+"
+                               "(:[0-9]+)?(" URLPATH ")?";
+                       break;
+               case TRACKER_REGEX_EMAIL:
+                       expression =
+                               "(mailto:)?[a-z0-9][a-z0-9 -]* [a-z0-9]"
+                               "[a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+";
+                       break;
+               case TRACKER_REGEX_OTHER:
+                       expression =
+                               "news:[-A-Z\\^_a-z{|}~!\"#$%&'()*+,./0-9;:=?`]+"
+                               "@[" HOSTCHARS ".]+(:[0-9]+)?";
+                       break;
+               default:
+                       /* Silence the compiler. */
+                       expression = NULL;
+                       continue;
+               }
+
+               memset (&dingus[i], 0, sizeof (regex_t));
+               regcomp (&dingus[i], expression, REG_EXTENDED | REG_ICASE);
+       }
+
+       inited = TRUE;
+}
+
+gint
+tracker_regex_match (TrackerRegExType  type,
+                     const gchar      *msg,
+                     GArray           *start,
+                     GArray           *end)
+{
+       regmatch_t matches[1];
+       gint       ret = 0;
+       gint       num_matches = 0;
+       gint       offset = 0;
+       gint       i;
+
+       g_return_val_if_fail (type <= TRACKER_REGEX_ALL, 0);
+
+       regex_init ();
+
+       while (!ret && type != TRACKER_REGEX_ALL) {
+               ret = regexec (&dingus[type], msg + offset, 1, matches, 0);
+               if (ret == 0) {
+                       gint s;
+
+                       num_matches++;
+
+                       s = matches[0].rm_so + offset;
+                       offset = matches[0].rm_eo + offset;
+
+                       if (start) {
+                               g_array_append_val (start, s);
+                       }
+
+                       if (end) {
+                               g_array_append_val (end, offset);
+                       }
+               }
+       }
+
+       if (type != TRACKER_REGEX_ALL) {
+               /* g_debug ("Found %d matches for regex type:%d", */
+               /*          num_matches, type); */
+               return num_matches;
+       }
+
+       /* If TRACKER_REGEX_ALL then we run ALL regex's on the string. */
+       for (i = 0; i < TRACKER_REGEX_ALL; i++, ret = 0) {
+               while (!ret) {
+                       ret = regexec (&dingus[i], msg + offset, 1, matches, 0);
+                       if (ret == 0) {
+                               gint s;
+
+                               num_matches++;
+
+                               s = matches[0].rm_so + offset;
+                               offset = matches[0].rm_eo + offset;
+
+                               if (start) {
+                                       g_array_append_val (start, s);
+                               }
+
+                               if (end) {
+                                       g_array_append_val (end, offset);
+                               }
+                       }
+               }
+       }
+
+       /* g_debug ("Found %d matches for ALL regex types", */
+       /*          num_matches); */
+
+       return num_matches;
+}
diff --git a/tracker-search-bar/tracker-utils.h b/tracker-search-bar/tracker-utils.h
new file mode 100644
index 0000000..f278723
--- /dev/null
+++ b/tracker-search-bar/tracker-utils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __TRACKER_UTILS_H__
+#define __TRACKER_UTILS_H__
+
+G_BEGIN_DECLS
+
+typedef enum {
+       TRACKER_REGEX_AS_IS,
+       TRACKER_REGEX_BROWSER,
+       TRACKER_REGEX_EMAIL,
+       TRACKER_REGEX_OTHER,
+       TRACKER_REGEX_ALL,
+} TrackerRegExType;
+
+/* Regular expressions */
+gint tracker_regex_match (TrackerRegExType  type,
+                          const gchar      *msg,
+                          GArray           *start,
+                          GArray           *end);
+
+G_END_DECLS
+
+#endif /* __TRACKER_UTILS_H__ */



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