[gnome-documents] all: introduce read support for favorite resources



commit 817af54597371747e1d8b3bf6019fd030db3e361
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Thu Aug 25 16:13:47 2011 -0400

    all: introduce read support for favorite resources
    
    Together with a list of categories on the sidebar; the Favorites
    category is the first we're going to implement. Right now only reading
    is supported, writing support coming soon.

 src/Makefile-js.am  |    1 +
 src/application.js  |    2 +
 src/categories.js   |   85 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/docFactory.js   |   27 ++++++++++++++-
 src/global.js       |    7 ++--
 src/lib/gd-utils.c  |   31 ++++++++++++++++++
 src/lib/gd-utils.h  |    8 +++++
 src/sidebar.js      |   88 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/trackerModel.js |   24 +++++++++-----
 9 files changed, 255 insertions(+), 18 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index efa6c44..e5e4f55 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -1,6 +1,7 @@
 jsdir = $(pkgdatadir)/js/
 dist_js_DATA = \
     application.js \
+    categories.js \
     docFactory.js \
     filterController.js \
     gDataMiner.js \
diff --git a/src/application.js b/src/application.js
index d2330fb..b5c6aae 100644
--- a/src/application.js
+++ b/src/application.js
@@ -30,6 +30,7 @@ const Gtk = imports.gi.Gtk;
 const GLib = imports.gi.GLib;
 const Tracker = imports.gi.Tracker;
 
+const Categories = imports.categories;
 const FilterController = imports.filterController;
 const Format = imports.format;
 const Global = imports.global;
@@ -108,6 +109,7 @@ Application.prototype = {
         Global.settings = new Gio.Settings({ schema: 'org.gnome.documents' });
         Global.offsetController = new OffsetController.OffsetController();
         Global.filterController = new FilterController.FilterController();
+        Global.categoryManager = new Categories.CategoryManager();
 
         // connect to tracker
         Tracker.SparqlConnection.get_async(null, Lang.bind(this,
diff --git a/src/categories.js b/src/categories.js
new file mode 100644
index 0000000..43191b3
--- /dev/null
+++ b/src/categories.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Gnome Documents 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.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const _ = imports.gettext.gettext;
+
+function Category(id, name, icon) {
+    this._init(id, name, icon);
+};
+
+Category.prototype = {
+    _init: function(id, name, icon) {
+        this.id = id;
+        this.name = name;
+        this.icon = icon;
+    },
+
+    getFilter: function() {
+        if (this.id == 'favorites')
+            return '{ ?urn nao:hasTag nao:predefined-tag-favorite }';
+
+        return '';
+    }
+};
+
+function CategoryManager() {
+    this._init();
+};
+
+CategoryManager.prototype = {
+    _init: function() {
+        this.categories = [];
+
+        this.categories.push(new Category('recent', _("New and Recent"), ''));
+
+        this.categories.push(new Category('favorites', _("Favorites"), 'emblem-favorite-symbolic'));
+        this.categories.push(new Category('private', _("Private"), 'channel-secure-symbolic'));
+        this.categories.push(new Category('shared', _("Shared with you"), 'emblem-shared-symbolic'));
+
+        this.setActiveCategoryId('recent');
+    },
+
+    setActiveCategoryId: function(id) {
+        let matched = this.categories.filter(Lang.bind(this,
+            function(category) {
+                return (category.id == id);
+            }));
+
+        if (!matched.length)
+            return;
+
+        this.activeCategory = matched[0];
+
+        this.emit('active-category-changed');
+    },
+
+    getActiveCategoryId: function() {
+        return this.activeCategory.id;
+    },
+
+    getActiveCategoryFilter: function() {
+        return this.activeCategory.getFilter();
+    }
+};
+Signals.addSignalMethods(CategoryManager.prototype);
diff --git a/src/docFactory.js b/src/docFactory.js
index 87ccee0..4652ae0 100644
--- a/src/docFactory.js
+++ b/src/docFactory.js
@@ -43,6 +43,7 @@ DocCommon.prototype = {
         this.mtime = cursor.get_string(TrackerModel.TrackerColumns.MTIME)[0];
         this.resourceUrn = cursor.get_string(TrackerModel.TrackerColumns.RESOURCE_URN)[0];
 
+        this._favorite = cursor.get_boolean(TrackerModel.TrackerColumns.FAVORITE);
         this._type = cursor.get_string(TrackerModel.TrackerColumns.TYPE)[0];
         this.pixbuf = Utils.pixbufFromRdfType(this._type);
 
@@ -60,6 +61,28 @@ DocCommon.prototype = {
 
     refreshIcon: function() {
         this.pixbuf = Utils.pixbufFromRdfType(this._type);
+        this.checkEmblemsAndUpdateIcon();
+    },
+
+    checkEmblemsAndUpdateIcon: function() {
+        if (this._favorite) {
+            let emblemIcon = new Gio.ThemedIcon({ name: 'emblem-favorite' });
+            let emblem = new Gio.Emblem({ icon: emblemIcon });
+            let emblemedIcon = new Gio.EmblemedIcon({ gicon: this.pixbuf });
+            emblemedIcon.add_emblem(emblem);
+
+            let theme = Gtk.IconTheme.get_default();
+
+            try {
+                let iconInfo = theme.lookup_by_gicon(emblemedIcon,
+                                                     this.pixbuf.get_width(),
+                                                     Gtk.IconLookupFlags.FORCE_SIZE);
+                this.pixbuf = iconInfo.load_icon();
+            } catch (e) {
+                log('Unable to render the emblem: ' + e.toString());
+            }
+        }
+
         this.emit('icon-updated');
     },
 
@@ -133,7 +156,7 @@ LocalDocument.prototype = {
         }
 
         if (haveNewIcon)
-            this.emit('icon-updated');
+            this.checkEmblemsAndUpdateIcon();
     },
 
     _onQueueThumbnailJob: function(object, res) {
@@ -166,7 +189,7 @@ LocalDocument.prototype = {
                                                        Utils.getIconSize(),
                                                        Utils.getIconSize());
 
-            this.emit('icon-updated');
+            this.checkEmblemsAndUpdateIcon();
         }
     }
 };
diff --git a/src/global.js b/src/global.js
index 769ed7f..077aff2 100644
--- a/src/global.js
+++ b/src/global.js
@@ -20,9 +20,10 @@
  */
 
 let application = null;
-let sourceManager = null;
+let categoryManager = null;
 let connection = null;
-let settings = null;
+let model = null;
 let offsetController = null;
 let selectionController = null;
-let model = null;
+let settings = null;
+let sourceManager = null;
diff --git a/src/lib/gd-utils.c b/src/lib/gd-utils.c
index f79da9d..3fd56d8 100644
--- a/src/lib/gd-utils.c
+++ b/src/lib/gd-utils.c
@@ -107,6 +107,37 @@ gd_sources_store_set (GtkListStore *store,
                       -1);
 }
 
+/**
+ * gd_create_sidebar_store:
+ *
+ * Returns: (transfer full):
+ */
+GtkListStore *
+gd_create_sidebar_store (void)
+{
+  return gtk_list_store_new (4,
+                             G_TYPE_STRING, // ID
+                             G_TYPE_STRING, // NAME
+                             G_TYPE_STRING, // ICON
+                             G_TYPE_BOOLEAN); // HEADING
+}
+
+void
+gd_sidebar_store_set (GtkListStore *store,
+                      GtkTreeIter *iter,
+                      const gchar *id,
+                      const gchar *name,
+                      const gchar *icon_name,
+                      gboolean heading)
+{
+  gtk_list_store_set (store, iter,
+                      0, id,
+                      1, name,
+                      2, icon_name,
+                      3, heading,
+                      -1);
+}
+
 #define ATTRIBUTES_FOR_THUMBNAIL \
   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," \
   G_FILE_ATTRIBUTE_TIME_MODIFIED
diff --git a/src/lib/gd-utils.h b/src/lib/gd-utils.h
index 3214051..3f6d525 100644
--- a/src/lib/gd-utils.h
+++ b/src/lib/gd-utils.h
@@ -47,6 +47,14 @@ void gd_sources_store_set (GtkListStore *store,
                            const gchar *name,
                            gboolean heading);
 
+GtkListStore * gd_create_sidebar_store (void);
+void gd_sidebar_store_set (GtkListStore *store,
+                           GtkTreeIter *iter,
+                           const gchar *id,
+                           const gchar *name,
+                           const gchar *icon_name,
+                           gboolean heading);
+
 void gd_queue_thumbnail_job_for_file_async (GFile *file,
                                             GAsyncReadyCallback callback,
                                             gpointer user_data);
diff --git a/src/sidebar.js b/src/sidebar.js
index d379aeb..c81a021 100644
--- a/src/sidebar.js
+++ b/src/sidebar.js
@@ -35,17 +35,91 @@ const _SIDEBAR_WIDTH_REQUEST = 240;
 const _SIDEBAR_SOURCES_PAGE = 0;
 const _SIDEBAR_MAIN_PAGE = 1;
 
+const SidebarModelColumns = {
+    ID: 0,
+    NAME: 1,
+    ICON: 2,
+    HEADING: 3
+};
+
+function SidebarModel() {
+    this._init();
+};
+
+SidebarModel.prototype = {
+    _init: function() {
+        let iter = null;
+
+        this.model = Gd.create_sidebar_store();
+        this._categoryManager = Global.categoryManager;
+
+        let categories = this._categoryManager.categories;
+        categories.forEach(Lang.bind(this,
+            function(category) {
+                iter = this.model.append();
+                Gd.sidebar_store_set(this.model, iter,
+                                     category.id, category.name, category.icon, false);
+            }));
+    }
+};
+
 function SidebarView() {
     this._init();
 };
 
 SidebarView.prototype = {
     _init: function() {
+        this._model = new SidebarModel();
+        this._treeModel = this._model.model;
+        this._categoryManager = Global.categoryManager;
+
+        this._treeView = new Gtk.TreeView({ headers_visible: false,
+                                            vexpand: true });
+        Gd.gtk_tree_view_set_activate_on_single_click(this._treeView, true);
+        this._treeView.set_model(this._treeModel);
+
+        this.widget = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER });
+        this.widget.add(this._treeView);
+
+        this._treeView.connect('row-activated', Lang.bind(this,
+            function(view, path) {
+                let iter = this._treeModel.get_iter(path)[1];
+                let id = this._treeModel.get_value(iter, SidebarModelColumns.ID);
+
+                this._categoryManager.setActiveCategoryId(id);
+            }));
+
+        let col = new Gtk.TreeViewColumn();
+        this._treeView.append_column(col);
+
+        this._rendererIcon = new Gtk.CellRendererPixbuf({ xpad: 4 });
+        col.pack_start(this._rendererIcon, false);
+        col.add_attribute(this._rendererIcon,
+                          'icon-name', SidebarModelColumns.ICON);
+
+
+        this._rendererText = new Gtk.CellRendererText();
+        col.pack_start(this._rendererText, true);
+        col.add_attribute(this._rendererText,
+                          'text', SidebarModelColumns.NAME);
+
+        this.widget.show_all();
+    }
+};
+
+function SidebarMainPage() {
+    this._init();
+};
+
+SidebarMainPage.prototype = {
+    _init: function() {
         this.widget = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
-                                    border_width: 6,
-                                    width_request: _SIDEBAR_WIDTH_REQUEST,
-                                    column_homogeneous: true });
+                                     border_width: 6,
+                                     width_request: _SIDEBAR_WIDTH_REQUEST,
+                                     column_homogeneous: true,
+                                     column_spacing: 12 });
 
+        // sources button
         let buttonContent = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
                                            row_spacing: 6 });
         // FIXME: setting yalign here seems wrong, but why are those not aligned
@@ -60,6 +134,10 @@ SidebarView.prototype = {
         this.widget.add(this._sourcesButton);
         this._sourcesButton.connect('clicked', Lang.bind(this, this._onSourcesButtonClicked));
 
+        // actual view
+        this._sidebarView = new SidebarView();
+        this.widget.add(this._sidebarView.widget);
+
         this.widget.show_all();
     },
 
@@ -67,7 +145,7 @@ SidebarView.prototype = {
         this.emit('sources-button-clicked');
     }
 };
-Signals.addSignalMethods(SidebarView.prototype);
+Signals.addSignalMethods(SidebarMainPage.prototype);
 
 function Sidebar() {
     this._init();
@@ -85,7 +163,7 @@ Sidebar.prototype = {
         this._sourceView = new Sources.SourceView();
         this.widget.insert_page(this._sourceView.widget, null, _SIDEBAR_SOURCES_PAGE);
 
-        this._sidebarView = new SidebarView();
+        this._sidebarView = new SidebarMainPage();
         this.widget.insert_page(this._sidebarView.widget, null, _SIDEBAR_MAIN_PAGE);
         this._sidebarView.connect('sources-button-clicked',
                                   Lang.bind(this, this._onSourcesButtonClicked));
diff --git a/src/trackerModel.js b/src/trackerModel.js
index 77838bc..a06c074 100644
--- a/src/trackerModel.js
+++ b/src/trackerModel.js
@@ -57,7 +57,8 @@ const TrackerColumns = {
     TOTAL_COUNT: 5,
     IDENTIFIER: 6,
     TYPE: 7,
-    RESOURCE_URN: 8
+    RESOURCE_URN: 8,
+    FAVORITE: 9
 };
 
 function QueryBuilder() {
@@ -72,8 +73,7 @@ QueryBuilder.prototype = {
         let filter =
             ('fn:contains ' +
              '(fn:lower-case (tracker:coalesce(nie:title(%s), nfo:fileName(%s))), ' +
-             '"%s") ' +
-             '&& ').format(subject, subject, Global.filterController.getFilter());
+             '"%s")').format(subject, subject, Global.filterController.getFilter());
 
         return filter;
     },
@@ -82,8 +82,10 @@ QueryBuilder.prototype = {
         let sparql = 'FILTER ((';
 
         sparql += this._buildFilterSearch(subject);
+        sparql += ') && (';
         sparql += Global.sourceManager.getActiveSourceFilter(subject);
-        sparql += ')) ';
+
+        sparql += '))';
 
         return sparql;
     },
@@ -116,14 +118,16 @@ QueryBuilder.prototype = {
              'tracker:coalesce(nie:title(?urn), nfo:fileName(?urn)) ' + // title
              'tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher)) ' + // author
              'tracker:coalesce(nfo:fileLastModified(?urn), nie:contentLastModified(?urn)) AS ?mtime ' + // mtime
-             this._buildTotalCounter() +
-             'nao:identifier(?urn) ' +
-             'rdf:type(?urn) ' +
-             'nie:dataSource(?urn) ' +
+             this._buildTotalCounter() + // totalCount
+             'nao:identifier(?urn) ' + // identifier
+             'rdf:type(?urn) ' + // type
+             'nie:dataSource(?urn) ' + // resource URN
+             '( EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite } )' + // favorite
              'WHERE { ' +
              this._buildTypeFilter('?urn') +
              'OPTIONAL { ?urn nco:creator ?creator . } ' +
              'OPTIONAL { ?urn nco:publisher ?publisher . } ' +
+             Global.categoryManager.getActiveCategoryFilter() +
              this._buildFilterString('?urn') +
              ' } ' +
              'ORDER BY DESC (?mtime)' +
@@ -156,6 +160,10 @@ TrackerModel.prototype = {
         this._sourceManager.connect('active-source-changed',
                                     Lang.bind(this, this._refresh));
 
+        this._categoryManager = Global.categoryManager;
+        this._categoryManager.connect('active-category-changed',
+                                      Lang.bind(this, this._refresh));
+
         this._offsetController = Global.offsetController;
         this._offsetController.connect('offset-changed',
                                        Lang.bind(this, this._performCurrentQuery));



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