[gnome-applets] Add Tracker Search Bar applet
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-applets] Add Tracker Search Bar applet
- Date: Sat, 26 Sep 2015 04:31:04 +0000 (UTC)
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 <martyn at lanedo com>
+Jürg Billeter <juerg.billeter at codethink co uk>
+Philip Van Hoof <pvanhoof at gnome org>
+Carlos Garnacho <carlos at lanedo com>
+Mikael Ottela <mikael.ottela at ixonos com>
+Ivan Frade <ivan.frade at nokia com>
+Jamie McCracken <jamiemcc at gnome org>
+Adrien Bustany <abustany at gnome org>
+Aleksander Morgado <aleksander at lanedo com>
+Anders Aagaard <aagaande at gmail com>
+Anders Rune Jensen <anders iola dk>
+Baptiste Mille-Mathias <baptist millemathias gmail com>
+Christoph Laimburg <christoph laimburg at rolmail net>
+Dan Nicolaescu <dann at ics uci edu>
+Deji Akingunola <dakingun gmail com>
+Edward Duffy <eduffy at gmail com>
+Eskil Bylund <eskil at letterboxes org>
+Eugenio <me at eugesoftware com>
+Fabien VALLON <fabien at sonappart net>
+Gergan Penkov <gergan at gmail com>
+Halton Huo <halton huo at sun com>
+Jaime Frutos Morales <acidborg at gmail com>
+Jedy Wang <jedy wang at sun com>
+Jerry Tan <jerry tan at sun com>
+John Stowers <john.stowers at gmail com>
+Julien <julienc psychologie-fr org>
+Laurent Aguerreche <laurent.aguerreche at free fr>
+Luca Ferretti <elle.uca at libero it>
+Marcus Fritzsch <fritschy at googlemail com>
+Michael Biebl <mbiebl at gmail com>
+Michal Pryc <michal pryc at sun com>
+Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
+Nate Nielsen <nielsen at memberwewbs com>
+Neil Patel <njpatel at gmail com>
+Richard Quirk <quirky at zoom co uk>
+Saleem Abdulrasool <compnerd at gentoo org>
+Samuel Cormier-Iijima <sciyoshi at gmail com>
+Tobutaz <tobutaz gmail com>
+Tom <tpgww at onepost net>
+Tshepang Lekhonkhobe <tshepang at gmail com>
+Ulrik Mikaelsson <ulrik mikaelsson gmail com>
+</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]