[tracker/tracker-needle-model: 1/2] Move querying mechanism to a custom GtkTreeModel implementation
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/tracker-needle-model: 1/2] Move querying mechanism to a custom GtkTreeModel implementation
- Date: Thu, 17 Mar 2011 13:00:08 +0000 (UTC)
commit d943f05a3b02c1824ed43e4931eef9ddaab86edc
Author: Carlos Garnacho <carlosg gnome org>
Date: Thu Mar 17 13:34:14 2011 +0100
Move querying mechanism to a custom GtkTreeModel implementation
This model takes care of querying and queueing operations for incremental
loading.
src/tracker-needle/Makefile.am | 1 +
src/tracker-needle/tracker-needle.vala | 92 +++--
src/tracker-needle/tracker-query.vala | 289 +++++--------
src/tracker-needle/tracker-result-store.vala | 611 ++++++++++++++++++++++++++
src/tracker-needle/tracker-view.vala | 138 +++++--
5 files changed, 884 insertions(+), 247 deletions(-)
---
diff --git a/src/tracker-needle/Makefile.am b/src/tracker-needle/Makefile.am
index f3cdfde..b8e5aed 100644
--- a/src/tracker-needle/Makefile.am
+++ b/src/tracker-needle/Makefile.am
@@ -28,6 +28,7 @@ tracker_needle_SOURCES = \
tracker-cell-renderer-text.vala \
tracker-history.vala \
tracker-query.vala \
+ tracker-result-store.vala \
tracker-stats.vala \
tracker-taglist.vala \
tracker-utils.vala \
diff --git a/src/tracker-needle/tracker-needle.vala b/src/tracker-needle/tracker-needle.vala
index 0f8f54f..174643f 100644
--- a/src/tracker-needle/tracker-needle.vala
+++ b/src/tracker-needle/tracker-needle.vala
@@ -80,6 +80,19 @@ public class Tracker.Needle {
}
}
+ private void store_state_changed (GLib.Object object,
+ ParamSpec p) {
+ ResultStore store = (ResultStore) object;
+
+ if (store.active) {
+ spinner_shell.show_all ();
+ spinner.start ();
+ } else {
+ spinner_shell.hide ();
+ spinner.stop ();
+ }
+ }
+
private void setup_ui () {
var builder = new Gtk.Builder ();
@@ -154,17 +167,18 @@ public class Tracker.Needle {
sw_categories = new Tracker.View (Tracker.View.Display.CATEGORIES, null);
treeview = (TreeView) sw_categories.get_child ();
treeview.row_activated.connect (view_row_selected);
+ sw_categories.store.notify["active"].connect (store_state_changed);
view.pack_start (sw_categories, true, true, 0);
- sw_filelist = new Tracker.View (Tracker.View.Display.FILE_LIST, null);
- treeview = (TreeView) sw_filelist.get_child ();
- treeview.row_activated.connect (view_row_selected);
- view.pack_start (sw_filelist, true, true, 0);
+ // sw_filelist = new Tracker.View (Tracker.View.Display.FILE_LIST, null);
+ // treeview = (TreeView) sw_filelist.get_child ();
+ // treeview.row_activated.connect (view_row_selected);
+ // view.pack_start (sw_filelist, true, true, 0);
- sw_icons = new Tracker.View (Tracker.View.Display.FILE_ICONS, null);
- iconview = (IconView) sw_icons.get_child ();
- iconview.item_activated.connect (icon_item_selected);
- view.pack_start (sw_icons, true, true, 0);
+ // sw_icons = new Tracker.View (Tracker.View.Display.FILE_ICONS, null);
+ // iconview = (IconView) sw_icons.get_child ();
+ // iconview.item_activated.connect (icon_item_selected);
+ // view.pack_start (sw_icons, true, true, 0);
// Set up taglist
taglist = new Tracker.TagList ();
@@ -185,13 +199,13 @@ public class Tracker.Needle {
}
private ListStore? get_store_for_active_view () {
- if (view_icons.active) {
- return sw_icons.store;
- } else if (view_filelist.active) {
- return sw_filelist.store;
- } else if (view_categories.active) {
- return sw_categories.store;
- }
+ // if (view_icons.active) {
+ // return sw_icons.store;
+ // } else if (view_filelist.active) {
+ // return sw_filelist.store;
+ // } else if (view_categories.active) {
+ // return sw_categories.store;
+ // }
debug ("No views active to get store?!?!");
return null;
@@ -206,6 +220,7 @@ public class Tracker.Needle {
}
private async void search_simple (ListStore store) requires (store != null) {
+ /*
Tracker.Query query = new Tracker.Query ();
Tracker.Sparql.Cursor cursor = null;
@@ -216,9 +231,9 @@ public class Tracker.Needle {
try {
if (find_in_contents.active) {
- cursor = yield query.perform_async (query.Type.ALL);
+ cursor = yield query.perform_async (query.Type.ALL, null);
} else {
- cursor = yield query.perform_async (query.Type.ALL_ONLY_IN_TITLES);
+ cursor = yield query.perform_async (query.Type.ALL_ONLY_IN_TITLES, null);
}
if (cursor == null) {
@@ -281,9 +296,11 @@ public class Tracker.Needle {
}
search_finished (store);
+ */
}
- private async void search_detailed (ListStore store) requires (store != null) {
+ private async void search_detailed (ResultStore store) requires (store != null) {
+ /*
Tracker.Query.Type[] categories = {
Tracker.Query.Type.APPLICATIONS,
Tracker.Query.Type.MUSIC,
@@ -293,8 +310,10 @@ public class Tracker.Needle {
Tracker.Query.Type.IMAGES,
Tracker.Query.Type.FOLDERS
};
+
Tracker.Query query = new Tracker.Query ();
+
store.clear ();
debug ("Doing detailed search using store:%p", store);
@@ -311,8 +330,10 @@ public class Tracker.Needle {
query.limit = 1000;
query.criteria = search.get_text ();
+ print (search.get_text ());
+
try {
- cursor = yield query.perform_async (type);
+ cursor = yield query.perform_async (type, null);
if (cursor == null) {
search_finished (store);
@@ -429,17 +450,12 @@ public class Tracker.Needle {
}
search_finished (store);
+ */
}
- private void search_finished (ListStore? store) {
- // Hide spinner
- spinner.stop ();
- spinner_shell.hide ();
-
- TreeModel model = (TreeModel) store;
-
+ private void search_finished (ResultStore? store) {
// Check if we have any results, if we don't change the view
- if (model == null || model.iter_n_children (null) < 1) {
+ if (store == null || !store.has_results ()) {
sw_noresults.show ();
sw_icons.hide ();
sw_categories.hide ();
@@ -497,15 +513,10 @@ public class Tracker.Needle {
string str = search.get_text ();
string criteria = str.strip ();
- ListStore store = get_store_for_active_view ();
-
- if (criteria.length < 1) {
- if (store != null) {
- store.clear ();
- }
+ ResultStore store = null;
+ if (criteria.length < 3) {
search_finished (store);
-
return false;
}
@@ -516,30 +527,27 @@ public class Tracker.Needle {
if (view_icons.active) {
sw_icons.show ();
+ store = sw_icons.store;
} else {
sw_icons.hide ();
}
if (view_categories.active) {
sw_categories.show ();
+ store = sw_categories.store;
} else {
sw_categories.hide ();
}
if (view_filelist.active) {
sw_filelist.show ();
+ store = sw_filelist.store;
} else {
sw_filelist.hide ();
}
- // Show spinner
- spinner_shell.show_all ();
- spinner.start ();
-
- if (view_categories.active) {
- search_detailed (store);
- } else {
- search_simple (store);
+ if (store != null) {
+ store.search_term = search.get_text ();
}
return false;
diff --git a/src/tracker-needle/tracker-query.vala b/src/tracker-needle/tracker-query.vala
index b4daf40..97a167b 100644
--- a/src/tracker-needle/tracker-query.vala
+++ b/src/tracker-needle/tracker-query.vala
@@ -34,6 +34,87 @@ public class Tracker.Query {
FOLDERS
}
+ private string [] where_clauses = {
+ // ALL
+ "WHERE { ?u fts:match \"%s\" . ?u nfo:belongsToContainer ?c ; tracker:available true . }",
+
+ // ALL_ONLY_IN_TITLES
+ "WHERE { ?u a nfo:FileDataObject ; nfo:belongsToContainer ?c ; tracker:available true . FILTER(fn:contains(fn:lower-case(nfo:fileName(?u)), \"$criteria_escaped_down\")) }",
+ // CONTACTS
+ "",
+
+ // APPLICATIONS
+ "WHERE {
+ ?urn a nfo:Software ;
+ fts:match \"%s\"
+ }",
+
+ // MUSIC
+ "WHERE {
+ {
+ ?urn nmm:musicAlbum ?match
+ } UNION {
+ ?urn nmm:performer ?match
+ } UNION {
+ ?urn a nfo:Audio .
+ ?match a nfo:Audio
+ FILTER (?urn = ?match)
+ }
+ ?match fts:match \"%s\" .
+ ?urn nmm:performer [ nmm:artistName ?performer ] ;
+ nmm:musicAlbum [ nie:title ?album ] ;
+ nie:url ?tooltip
+ }",
+
+ // IMAGES
+ "WHERE {
+ ?urn a nfo:Image ;
+ nie:url ?tooltip ;
+ fts:match \"%s\"
+ }",
+
+ // VIDEOS
+ "WHERE {
+ ?urn a nfo:Video ;
+ nie:url ?tooltip ;
+ fts:match \"%s\" .
+ }",
+
+ // DOCUMENTS
+ "WHERE {
+ ?urn a nfo:Document ;
+ nie:url ?tooltip ;
+ fts:match \"%s\" .
+ OPTIONAL {
+ ?urn nco:creator ?creator .
+ }
+ OPTIONAL {
+ ?urn nco:publisher ?publisher .
+ }
+ }",
+
+ // MAIL
+ "WHERE {
+ ?urn a nmo:Email ;
+ nmo:from ?sender ;
+ nmo:to ?to ;
+ fts:match \"%s\" .
+ }",
+
+ // CALENDAR
+ "",
+
+ // FOLDERS
+ "WHERE {
+ ?urn a nfo:Folder ;
+ nie:url ?tooltip ;
+ fts:match \"%s\" .
+ OPTIONAL {
+ ?urn nfo:belongsToContainer ?parent .
+ }
+ }"
+ };
+
public string criteria { get; set; }
public uint offset { get; set; }
public uint limit { get; set; }
@@ -42,6 +123,7 @@ public class Tracker.Query {
private static Sparql.Connection connection;
public Query () {
+
try {
connection = Sparql.Connection.get ();
} catch (Sparql.Error ea) {
@@ -53,7 +135,39 @@ public class Tracker.Query {
}
}
- public async Sparql.Cursor? perform_async (Type query_type, Cancellable? cancellable = null) throws IOError
+ public async uint get_count_async (Type query_type,
+ Cancellable? cancellable = null) throws IOError
+ requires (connection != null) {
+ Sparql.Cursor cursor = null;
+
+ if (criteria == null || criteria.length < 1) {
+ warning ("Criteria was NULL or an empty string, no query performed");
+ return 0;
+ }
+
+ string criteria_escaped = Tracker.Sparql.escape_string (criteria);
+
+ query = "SELECT count(?urn) " + where_clauses[query_type].printf (criteria_escaped);
+
+ try {
+ cursor = yield connection.query_async (query, null);
+ yield cursor.next_async ();
+ } catch (Sparql.Error ea) {
+ warning ("Could not run Sparql count query: %s", ea.message);
+ } catch (GLib.IOError eb) {
+ warning ("Could not run Sparql count query: %s", eb.message);
+ } catch (GLib.DBusError ec) {
+ warning ("Could not run Sparql count query: %s", ec.message);
+ } catch (GLib.Error ge) {
+ warning ("Could not run Sparql count query: %s", ge.message);
+ }
+
+ return (uint) cursor.get_integer (0);
+ }
+
+ public async Sparql.Cursor? perform_async (Type query_type,
+ string [] ?args,
+ Cancellable? cancellable = null) throws IOError
requires (connection != null) {
Sparql.Cursor cursor = null;
@@ -68,178 +182,9 @@ public class Tracker.Query {
}
string criteria_escaped = Tracker.Sparql.escape_string (criteria);
- string unknown = _("Unknown");
-
- switch (query_type) {
- case Type.ALL:
- query = @"SELECT ?u nie:url(?u) tracker:coalesce(nie:title(?u), nfo:fileName(?u), \"$unknown\") nfo:fileLastModified(?u) nfo:fileSize(?u) nie:url(?c) WHERE { ?u fts:match \"$criteria_escaped\" . ?u nfo:belongsToContainer ?c ; tracker:available true . } ORDER BY DESC(fts:rank(?u)) OFFSET $offset LIMIT $limit";
- break;
-
- case Type.ALL_ONLY_IN_TITLES:
- string criteria_escaped_down = criteria_escaped.down();
-
- query = @"SELECT ?u nie:url(?u) tracker:coalesce(nfo:fileName(?u), \"$unknown\") nfo:fileLastModified(?u) nfo:fileSize(?u) nie:url(?c) WHERE { ?u a nfo:FileDataObject ; nfo:belongsToContainer ?c ; tracker:available true . FILTER(fn:contains(fn:lower-case(nfo:fileName(?u)), \"$criteria_escaped_down\")) } ORDER BY DESC(nfo:fileName(?u)) OFFSET $offset LIMIT $limit";
- break;
-
- case Type.APPLICATIONS:
- query = @"
- SELECT
- ?urn
- tracker:coalesce(nfo:softwareCmdLine(?urn), ?urn)
- tracker:coalesce(nie:title(?urn), nfo:fileName(?urn), \"$unknown\")
- nie:comment(?urn)
- WHERE {
- ?urn a nfo:Software ;
- fts:match \"$criteria_escaped\"
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nie:title(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- case Type.MUSIC:
- query = @"
- SELECT
- ?song
- nie:url(?song)
- tracker:coalesce(nie:title(?song), nfo:fileName(?song), \"$unknown\")
- fn:string-join((?performer, ?album), \" - \")
- nfo:duration(?song)
- ?tooltip
- WHERE {
- ?match fts:match \"$criteria_escaped\"
- {
- ?song nmm:musicAlbum ?match
- } UNION {
- ?song nmm:performer ?match
- } UNION {
- ?song a nfo:Audio .
- ?match a nfo:Audio
- FILTER (?song = ?match)
- }
- ?song nmm:performer [ nmm:artistName ?performer ] ;
- nmm:musicAlbum [ nie:title ?album ] ;
- nie:url ?tooltip
- }
- ORDER BY DESC(fts:rank(?song)) DESC(nie:title(?song))
- OFFSET $offset LIMIT $limit
- ";
- break;
- case Type.IMAGES:
- query = @"
- SELECT
- ?urn
- nie:url(?urn)
- tracker:coalesce(nie:title(?urn), nfo:fileName(?urn), \"$unknown\")
- fn:string-join((nfo:height(?urn), nfo:width(?urn)), \" x \")
- nfo:fileSize(?urn)
- ?tooltip
- WHERE {
- ?urn a nfo:Image ;
- nie:url ?tooltip ;
- fts:match \"$criteria_escaped\"
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nie:title(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- case Type.VIDEOS:
- query = @"
- SELECT
- ?urn
- nie:url(?urn)
- tracker:coalesce(nie:title(?urn), nfo:fileName(?urn), \"$unknown\")
- \"\"
- nfo:duration(?urn)
- ?tooltip
- WHERE {
- ?urn a nfo:Video ;
- nie:url ?tooltip ;
- fts:match \"$criteria_escaped\" .
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nie:title(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- case Type.DOCUMENTS:
-// fn:concat(nco:pageCount(?urn), \" pages\")
- string pages = _("Pages");
-
- query = @"
- SELECT
- ?urn
- nie:url(?urn)
- tracker:coalesce(nie:title(?urn), nfo:fileName(?urn), \"$unknown\")
- tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher), \"\")
- fn:concat(nfo:pageCount(?urn), \" $pages\")
- ?tooltip
- WHERE {
- ?urn a nfo:Document ;
- nie:url ?tooltip ;
- fts:match \"$criteria_escaped\" .
- OPTIONAL {
- ?urn nco:creator ?creator .
- }
- OPTIONAL {
- ?urn nco:publisher ?publisher .
- }
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nie:title(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- case Type.MAIL:
- string no_subject = _("No Subject");
- string to = _("To");
-
- query = @"
- SELECT
- ?urn
- nie:url(?urn)
- tracker:coalesce(nco:fullname(?sender), nco:nickname(?sender), nco:emailAddress(?sender), \"$unknown\")
- tracker:coalesce(nmo:messageSubject(?urn), \"$no_subject\")
- nmo:receivedDate(?urn)
- fn:concat(\"$to: \", tracker:coalesce(nco:fullname(?to), nco:nickname(?to), nco:emailAddress(?to), \"$unknown\"))
- WHERE {
- ?urn a nmo:Email ;
- nmo:from ?sender ;
- nmo:to ?to ;
- fts:match \"$criteria_escaped\" .
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nmo:messageSubject(?urn)) DESC(nmo:receivedDate(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- case Type.FOLDERS:
- query = @"
- SELECT
- ?urn
- nie:url(?urn)
- tracker:coalesce(nie:title(?urn), nfo:fileName(?urn), \"$unknown\")
- tracker:coalesce(nie:url(?parent), \"\")
- nfo:fileLastModified(?urn)
- ?tooltip
- WHERE {
- ?urn a nfo:Folder ;
- nie:url ?tooltip ;
- fts:match \"$criteria_escaped\" .
- OPTIONAL {
- ?urn nfo:belongsToContainer ?parent .
- }
- }
- ORDER BY DESC(fts:rank(?urn)) DESC(nie:title(?urn))
- OFFSET $offset LIMIT $limit
- ";
- break;
-
- default:
- assert_not_reached ();
- }
+ query = "SELECT " + string.joinv (" ", args) + " " + where_clauses[query_type].printf (criteria_escaped);
+ query += @" OFFSET $offset LIMIT $limit";
debug ("Running query: '%s'", query);
diff --git a/src/tracker-needle/tracker-result-store.vala b/src/tracker-needle/tracker-result-store.vala
new file mode 100644
index 0000000..1091421
--- /dev/null
+++ b/src/tracker-needle/tracker-result-store.vala
@@ -0,0 +1,611 @@
+//
+// Copyright 2010, Carlos Garnacho <carlos lanedo 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.
+//
+
+using Gtk;
+
+public class Tracker.ResultStore : Gtk.TreeModel, GLib.Object {
+ private GLib.Cancellable cancellable;
+
+ private struct ResultNode {
+ string [] values;
+ }
+ private struct CategoryNode {
+ Tracker.Query.Type type;
+ string [] args;
+ ResultNode [] results;
+ }
+ private CategoryNode [] categories;
+
+ private class Operation : GLib.Object {
+ public CategoryNode *node;
+ public int offset;
+ }
+ private GenericArray<Operation> running_operations;
+ private GenericArray<Operation> delayed_operations;
+
+ private int n_columns;
+
+ private Operation * find_operation (GenericArray<Operation> array,
+ CategoryNode *node,
+ int offset) {
+ Operation op;
+ int i;
+
+ for (i = 0; i < array.length; i++) {
+ op = array[i];
+
+ if (op.node == node &&
+ op.offset == offset) {
+ return op;
+ }
+ }
+
+ return null;
+ }
+
+ async void load_operation (Operation op,
+ Cancellable? cancellable) {
+ Tracker.Query query;
+ Sparql.Cursor cursor = null;
+ int i;
+
+ try {
+ cancellable.set_error_if_cancelled ();
+
+ query = new Tracker.Query ();
+ query.criteria = _search_term;
+ query.limit = 100;
+ query.offset = op.offset;
+
+ cursor = yield query.perform_async (op.node.type, op.node.args);
+
+ for (i = op.offset; i < op.offset + 100; i++) {
+ ResultNode *result;
+ TreeIter iter;
+ TreePath path;
+ bool b = false;
+ int j;
+
+ try {
+ b = yield cursor.next_async ();
+ } catch (GLib.Error ge) {
+ warning ("Could not fetch row: %s\n", ge.message);
+ }
+
+ if (!b) {
+ break;
+ }
+
+ result = &op.node.results[i];
+
+ for (j = 0; j < n_columns; j++) {
+ result.values[j] = cursor.get_string (j);
+ }
+
+ // Emit row-changed
+ iter = TreeIter ();
+ iter.user_data = op.node;
+ iter.user_data2 = result;
+ iter.user_data3 = i.to_pointer ();
+
+ path = this.get_path (iter);
+
+ row_changed (path, iter);
+ }
+
+ running_operations.remove (op);
+ } catch (GLib.IOError ie) {
+ warning ("Could not load items: %s\n", ie.message);
+ return;
+ }
+
+ if (delayed_operations.length > 0) {
+ Operation next_to_start;
+
+ // Take last added task from delayed queue and start it
+ next_to_start = delayed_operations[delayed_operations.length - 1];
+ delayed_operations.remove (next_to_start);
+ running_operations.add (next_to_start);
+
+ load_operation.begin (next_to_start, cancellable);
+ } else if (running_operations.length == 0) {
+ // finished processing
+ this.active = false;
+ }
+ }
+
+ private void add_operation (CategoryNode *cat,
+ int offset) {
+ Operation op = new Operation ();
+ Operation old;
+
+ op.node = cat;
+ op.offset = offset;
+
+ if (find_operation (running_operations, cat, offset) != null) {
+ // Operation already running
+ return;
+ }
+
+ // If the task is delayed, it will be either pushed
+ // to the running queue, or reordered to be processed
+ // next in the delayed queue.
+ old = find_operation (delayed_operations, cat, offset);
+
+ if (old != null) {
+ delayed_operations.remove (old);
+ }
+
+ this.active = true;
+
+ // Running queue is limited to 2 simultaneous queries,
+ // anything after that will be added to a different queue.
+ if (running_operations.length < 2) {
+ running_operations.add (op);
+
+ // Start the operation right away
+ load_operation.begin (op, cancellable);
+ } else {
+ // Reorder the operation if it was already there, else just add
+ delayed_operations.add (op);
+ }
+ }
+
+ async void load_category (CategoryNode *cat,
+ Cancellable? cancellable) {
+ uint count = 0;
+
+ try {
+ cancellable.set_error_if_cancelled ();
+
+ Tracker.Query query = new Tracker.Query ();
+ query.criteria = _search_term;
+
+ count = yield query.get_count_async (cat.type);
+ } catch (GLib.IOError ie) {
+ warning ("Could not get count: %s\n", ie.message);
+ return;
+ }
+
+ if (count != 0) {
+ int i;
+
+ Gtk.TreeIter iter;
+ Gtk.TreePath path;
+
+ cat.results.resize ((int) count);
+
+ iter = TreeIter ();
+ iter.user_data = cat;
+
+ for (i = 0; i < count; i++) {
+ ResultNode *res;
+
+ res = &cat.results[i];
+ res.values = new string[n_columns];
+
+ iter.user_data2 = res;
+ iter.user_data3 = i.to_pointer ();
+ path = this.get_path (iter);
+
+ row_inserted (path, iter);
+ }
+ }
+
+ if (running_operations.length == 0) {
+ this.active = false;
+ }
+ }
+
+ private void clear_results () {
+ int i, j;
+
+ print ("POSCLAROOO\n");
+
+ for (i = 0; i < categories.length; i++) {
+ CategoryNode *cat = &categories[i];
+ TreeIter iter;
+ TreePath path;
+
+ iter = TreeIter ();
+ iter.user_data = cat;
+
+ for (j = 0; j < cat.results.length; j++) {
+ iter.user_data2 = &cat.results[j];
+ iter.user_data3 = j.to_pointer ();
+ path = get_path (iter);
+ print ("removing = %s\n", path.to_string ());
+
+ row_deleted (path);
+ }
+
+ iter.user_data2 = null;
+ iter.user_data3 = null;
+ path = get_path (iter);
+
+ row_changed (path, iter);
+
+ cat.results.resize (0);
+ }
+ }
+
+ private string _search_term;
+ public string search_term {
+ get {
+ return _search_term;
+ }
+ set {
+ int i;
+
+ _search_term = value;
+
+ if (cancellable != null) {
+ cancellable.cancel ();
+ }
+
+ clear_results ();
+ this.active = true;
+
+ for (i = 0; i < categories.length; i++) {
+ load_category.begin (&categories[i], cancellable);
+ }
+ }
+ }
+
+ public bool active {
+ get;
+ private set;
+ }
+
+ private int find_nth_category_index (CategoryNode *node,
+ int n) {
+ int i;
+
+ if (node == null) {
+ // Count from the first one
+ return n;
+ }
+
+ for (i = 0; i < categories.length; i++) {
+ CategoryNode *cat;
+
+ cat = &categories[i];
+
+ if (cat == node) {
+ return i + n;
+ }
+ }
+
+ return -1;
+ }
+
+ private int filled_categories_count () {
+ int i, n = 0;
+
+ for (i = 0; i < categories.length; i++) {
+ CategoryNode *cat;
+
+ cat = &categories[i];
+
+ if (cat.results.length > 0) {
+ n++;
+ }
+ }
+
+ return n;
+ }
+
+ public GLib.Type get_column_type (int index_) {
+ return typeof (string);
+ }
+
+ public Gtk.TreeModelFlags get_flags () {
+ return Gtk.TreeModelFlags.ITERS_PERSIST;
+ }
+
+ public bool get_iter (out Gtk.TreeIter iter,
+ Gtk.TreePath path) {
+ unowned int [] indices = path.get_indices ();
+ CategoryNode *cat;
+
+ if (indices[0] >= categories.length) {
+ return false;
+ }
+
+ cat = &categories[indices[0]];
+ iter.user_data = cat;
+
+ if (path.get_depth () == 2) {
+ // it's a result
+ if (indices[1] >= cat.results.length) {
+ return false;
+ }
+
+ iter.user_data2 = &cat.results[indices[1]];
+ iter.user_data3 = indices[1].to_pointer ();
+ }
+
+ return true;
+ }
+
+ public int get_n_columns () {
+ return n_columns;
+ }
+
+ public Gtk.TreePath get_path (Gtk.TreeIter iter) {
+ TreePath path = new TreePath ();
+ CategoryNode *cat;
+ int i;
+
+ for (i = 0; i < categories.length; i++) {
+ cat = &categories[i];
+
+ if (cat == iter.user_data) {
+ path.append_index (i);
+ break;
+ }
+ }
+
+ if (iter.user_data2 != null) {
+ path.append_index ((int) iter.user_data3);
+ }
+
+ return path;
+ }
+
+ public void get_value (Gtk.TreeIter iter,
+ int column,
+ out GLib.Value value) {
+ CategoryNode *cat;
+
+ if (column > n_columns) {
+ value.init (typeof (string));
+ return;
+ }
+
+ cat = iter.user_data;
+ value.init (this.get_column_type (column));
+
+ if (iter.user_data2 == null) {
+ if (column == 2) {
+ switch (cat.type) {
+ case Tracker.Query.Type.APPLICATIONS:
+ value.set_string (_("Applications"));
+ break;
+ case Tracker.Query.Type.MUSIC:
+ value.set_string (_("Music"));
+ break;
+ case Tracker.Query.Type.IMAGES:
+ value.set_string (_("Images"));
+ break;
+ case Tracker.Query.Type.VIDEOS:
+ value.set_string (_("Videos"));
+ break;
+ case Tracker.Query.Type.DOCUMENTS:
+ value.set_string (_("Documents"));
+ break;
+ case Tracker.Query.Type.MAIL:
+ value.set_string (_("Mail"));
+ break;
+ case Tracker.Query.Type.FOLDERS:
+ value.set_string (_("Folders"));
+ break;
+ }
+ }
+ } else {
+ ResultNode *result;
+ int n_node;
+
+ result = iter.user_data2;
+ n_node = (int) iter.user_data3;
+
+ if (result.values[0] != null) {
+ value.set_string (result.values[column]);
+ } else {
+ n_node /= 100;
+ n_node *= 100;
+
+ add_operation (cat, n_node);
+ }
+ }
+ }
+
+ public bool iter_children (out Gtk.TreeIter iter,
+ Gtk.TreeIter? parent) {
+ CategoryNode *cat;
+
+ if (parent == null) {
+ int i;
+
+ if (categories.length == 0) {
+ return false;
+ }
+
+ i = find_nth_category_index (null, 0);
+ cat = &categories[i];
+ iter.user_data = cat;
+ return true;
+ }
+
+ if (parent.user_data2 != null) {
+ return false;
+ }
+
+ cat = parent.user_data;
+
+ iter.user_data = cat;
+ iter.user_data2 = &cat.results[0];
+ iter.user_data3 = 0.to_pointer ();
+
+ return true;
+ }
+
+ public bool iter_has_child (Gtk.TreeIter iter) {
+ if (iter.user_data2 == null) {
+ CategoryNode *cat;
+
+ cat = iter.user_data;
+ return (cat.results.length > 0);
+ }
+
+ return false;
+ }
+
+ public int iter_n_children (Gtk.TreeIter? iter) {
+ if (iter == null) {
+ return filled_categories_count ();
+ }
+
+ if (iter.user_data2 != null) {
+ // a result doesn't have children
+ return 0;
+ }
+
+ CategoryNode *cat = iter.user_data;
+
+ return cat.results.length;
+ }
+
+ public bool iter_next (ref Gtk.TreeIter iter) {
+ CategoryNode *cat;
+ int i;
+
+ cat = iter.user_data;
+
+ if (iter.user_data2 == null) {
+ i = find_nth_category_index (cat, 1);
+
+ if (i < 0 || i >= categories.length) {
+ return false;
+ }
+
+ iter.user_data = &categories[i];
+
+ return true;
+ } else {
+ // Result node
+ i = (int) iter.user_data3;
+ i++;
+
+ if (i >= cat.results.length) {
+ return false;
+ }
+
+ iter.user_data2 = &cat.results[i];
+ iter.user_data3 = i.to_pointer ();
+
+ return true;
+ }
+ }
+
+ public bool iter_nth_child (out Gtk.TreeIter iter,
+ Gtk.TreeIter? parent,
+ int n) {
+ CategoryNode *cat;
+
+ if (parent != null) {
+ cat = parent.user_data;
+
+ if (n >= cat.results.length) {
+ return false;
+ }
+
+ iter.user_data = cat;
+ iter.user_data2 = &cat.results[n];
+ iter.user_data3 = n.to_pointer ();
+ return true;
+ } else {
+ int index;
+
+ index = find_nth_category_index (null, n);
+
+ if (index < 0 ||
+ index >= categories.length) {
+ return false;
+ }
+
+ cat = &categories[index];
+ iter.user_data = cat;
+
+ return true;
+ }
+ }
+
+ public bool iter_parent (out Gtk.TreeIter iter,
+ Gtk.TreeIter child) {
+ if (child.user_data2 != null) {
+ // child within a category
+ iter.user_data = child.user_data;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void ref_node (Gtk.TreeIter iter) {
+ }
+
+ public void unref_node (Gtk.TreeIter iter) {
+ }
+
+ public ResultStore (int _n_columns) {
+ running_operations = new GenericArray<Operation?> ();
+ delayed_operations = new GenericArray<Operation?> ();
+ n_columns = _n_columns;
+ }
+
+ public void add_query (Tracker.Query.Type type, ...) {
+ var l = va_list ();
+ string str = null;
+ string [] args = null;
+ CategoryNode cat;
+ TreeIter iter;
+ TreePath path;
+
+ do {
+ str = l.arg ();
+
+ if (str != null) {
+ args += str;
+ }
+ } while (str != null);
+
+ if (args.length != n_columns) {
+ warning ("Arguments and number of columns doesn't match");
+ return;
+ }
+
+ cat = CategoryNode ();
+ cat.type = type;
+ cat.args = args;
+ cat.results = new ResultNode[0];
+
+ categories += cat;
+
+ iter = TreeIter ();
+ iter.user_data = &categories[categories.length - 1];
+ path = this.get_path (iter);
+
+ row_inserted (path, iter);
+ }
+
+ public bool has_results () {
+ return filled_categories_count () > 0;
+ }
+}
diff --git a/src/tracker-needle/tracker-view.vala b/src/tracker-needle/tracker-view.vala
index 814285d..27addbf 100644
--- a/src/tracker-needle/tracker-view.vala
+++ b/src/tracker-needle/tracker-view.vala
@@ -32,14 +32,14 @@ public class Tracker.View : ScrolledWindow {
private set;
}
- public ListStore store {
+ public ResultStore store {
get;
private set;
}
private Widget view = null;
- public View (Display? _display = Display.NO_RESULTS, ListStore? _store) {
+ public View (Display? _display = Display.NO_RESULTS, ResultStore? _store) {
set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
display = _display;
@@ -49,17 +49,59 @@ public class Tracker.View : ScrolledWindow {
debug ("using store:%p", store);
} else {
// Setup treeview
- store = new ListStore (10,
- typeof (Gdk.Pixbuf), // Icon small
- typeof (Gdk.Pixbuf), // Icon big
- typeof (string), // URN
- typeof (string), // URL
- typeof (string), // Title
- typeof (string), // Subtitle
- typeof (string), // Column 2
- typeof (string), // Column 3
- typeof (string), // Tooltip
- typeof (bool)); // Category hint
+ store = new ResultStore (6);
+
+ store.add_query (Tracker.Query.Type.APPLICATIONS,
+ "?urn",
+ "tracker:coalesce(nfo:softwareCmdLine(?urn), ?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "nie:comment(?urn)",
+ "\"\"",
+ "\"\"");
+
+ store.add_query (Tracker.Query.Type.IMAGES,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "fn:string-join((nfo:height(?urn), nfo:width(?urn)), \" x \")",
+ "nfo:fileSize(?urn)",
+ "nie:url(?urn)");
+ store.add_query (Tracker.Query.Type.MUSIC,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "fn:string-join((?performer, ?album), \" - \")",
+ "nfo:duration(?urn)",
+ "nie:url(?urn)");
+ store.add_query (Tracker.Query.Type.VIDEOS,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "\"\"",
+ "nfo:duration(?urn)",
+ "nie:url(?urn)");
+ store.add_query (Tracker.Query.Type.DOCUMENTS,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher))",
+ "fn:concat(nfo:pageCount(?urn), \" Pages\")",
+ "nie:url(?urn)");
+ store.add_query (Tracker.Query.Type.MAIL,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nco:fullname(?sender), nco:nickname(?sender), nco:emailAddress(?sender))",
+ "tracker:coalesce(nmo:messageSubject(?urn))",
+ "nmo:receivedDate(?urn)",
+ "fn:concat(\"To: \", tracker:coalesce(nco:fullname(?to), nco:nickname(?to), nco:emailAddress(?to)))");
+ store.add_query (Tracker.Query.Type.FOLDERS,
+ "?urn",
+ "nie:url(?urn)",
+ "tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))",
+ "tracker:coalesce(nie:url(?parent), \"\")",
+ "nfo:fileLastModified(?urn)",
+ "?tooltip");
+
debug ("Creating store:%p", store);
}
@@ -121,11 +163,13 @@ public class Tracker.View : ScrolledWindow {
tv.set_rules_hint (false);
tv.set_grid_lines (TreeViewGridLines.VERTICAL);
tv.set_headers_visible (true);
+ tv.set_fixed_height_mode (true);
var renderer1 = new CellRendererPixbuf ();
var renderer2 = new Tracker.CellRendererText ();
col = new TreeViewColumn ();
+ col.set_sizing (TreeViewColumnSizing.FIXED);
col.pack_start (renderer1, false);
col.add_attribute (renderer1, "pixbuf", 0);
renderer1.xpad = 5;
@@ -146,6 +190,7 @@ public class Tracker.View : ScrolledWindow {
var renderer3 = new Tracker.CellRendererText ();
col = new TreeViewColumn ();
+ col.set_sizing (TreeViewColumnSizing.FIXED);
col.pack_start (renderer3, true);
col.add_attribute (renderer3, "text", 6);
col.set_title (_("Last Changed"));
@@ -154,6 +199,7 @@ public class Tracker.View : ScrolledWindow {
var renderer4 = new Tracker.CellRendererText ();
col = new TreeViewColumn ();
+ col.set_sizing (TreeViewColumnSizing.FIXED);
col.pack_start (renderer4, true);
col.add_attribute (renderer4, "text", 7);
col.set_title (_("Size"));
@@ -168,49 +214,51 @@ public class Tracker.View : ScrolledWindow {
TreeView tv = (TreeView) view;
tv.set_model (store);
- tv.set_tooltip_column (8);
+ tv.set_tooltip_column (5);
tv.set_rules_hint (false);
tv.set_grid_lines (TreeViewGridLines.NONE);
tv.set_headers_visible (false);
var renderer1 = new CellRendererPixbuf ();
- var renderer2 = new Tracker.CellRendererText ();
+ var renderer2 = new Gtk.CellRendererText ();
col = new TreeViewColumn ();
- col.pack_start (renderer1, false);
- col.add_attribute (renderer1, "pixbuf", 0);
- renderer1.xpad = 5;
- renderer1.ypad = 5;
+ col.set_sizing (TreeViewColumnSizing.FIXED);
+ // col.pack_start (renderer1, false);
+ // col.add_attribute (renderer1, "pixbuf", 0);
+ // renderer1.xpad = 5;
+ // renderer1.ypad = 5;
col.pack_start (renderer2, true);
- col.add_attribute (renderer2, "text", 4);
- col.add_attribute (renderer2, "subtext", 5);
+ col.set_cell_data_func (renderer2, text_renderer_func);
+// col.add_attribute (renderer2, "text", 1); //4);
+// col.add_attribute (renderer2, "subtext", 5);
renderer2.ellipsize = Pango.EllipsizeMode.MIDDLE;
- renderer2.show_fixed_height = true;
+// renderer2.show_fixed_height = true;
col.set_title (_("Item"));
col.set_resizable (true);
col.set_expand (true);
col.set_sizing (TreeViewColumnSizing.AUTOSIZE);
- col.set_cell_data_func (renderer1, cell_renderer_func);
- col.set_cell_data_func (renderer2, cell_renderer_func);
+// col.set_cell_data_func (renderer1, cell_renderer_func);
tv.append_column (col);
-// var renderer3 = new Tracker.CellRendererText ();
+// var renderer3 = new Gtk.CellRendererText ();
// col = new TreeViewColumn ();
// col.pack_start (renderer3, true);
-// col.add_attribute (renderer3, "text", 6);
+// col.add_attribute (renderer3, "text", 3);
// col.set_title (_("Item Detail"));
// col.set_cell_data_func (renderer3, cell_renderer_func);
// tv.append_column (col);
- var renderer4 = new Tracker.CellRendererText ();
- col = new TreeViewColumn ();
- col.pack_start (renderer4, true);
- col.add_attribute (renderer4, "text", 7);
- col.set_title (_("Size"));
- col.set_cell_data_func (renderer4, cell_renderer_func);
- tv.append_column (col);
+// var renderer4 = new Tracker.CellRendererText ();
+// col = new TreeViewColumn ();
+// col.set_sizing (TreeViewColumnSizing.FIXED);
+// col.pack_start (renderer4, true);
+// col.add_attribute (renderer4, "text", 4);
+// col.set_title (_("Size"));
+// col.set_cell_data_func (renderer4, cell_renderer_func);
+// tv.append_column (col);
break;
}
@@ -256,5 +304,29 @@ public class Tracker.View : ScrolledWindow {
cell.set ("cell-background-gdk", null);
}
}
+
+ private void text_renderer_func (CellLayout cell_layout,
+ CellRenderer cell,
+ TreeModel tree_model,
+ TreeIter iter) {
+ string text, subtext;
+ string markup = null;
+
+ tree_model.get (iter, 2, out text, 3, out subtext, -1);
+
+ if (text != null) {
+ markup = Markup.escape_text (text);
+ }
+
+ if (subtext != null) {
+ markup += "\n<small><span color='grey'>%s</span></small>".printf (Markup.escape_text (subtext));
+ }
+
+ if (markup == null) {
+ markup = "<span color='grey'>%s</span>".printf (_("Loading..."));
+ }
+
+ cell.set ("markup", markup);
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]