[gnome-maps] Add store for recent and favorite places



commit 1057010f52c9cd6314ebe7f46f3d5e52bd7f5e0e
Author: Jonas Danielsson <jonas threetimestwo org>
Date:   Mon Jan 13 13:27:32 2014 +0100

    Add store for recent and favorite places
    
    This patch adds the PlaceStore object that extends GtkListStore.
    Recent places can be added to the store and be written to file using
    the JSON format.
    
    If adding a recent place exceeds the limit for recent places the
    oldest recent place will be removed.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=722102

 src/Makefile-js.am |    3 +-
 src/placeStore.js  |  189 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/utils.js       |   25 +++++++
 3 files changed, 216 insertions(+), 1 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index 134fbd1..a7055f5 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -13,7 +13,8 @@ dist_js_DATA = \
     geoclue.js \
     zoomControl.js \
     searchPopup.js \
-    contextMenu.js
+    contextMenu.js \
+    placeStore.js
 
 BUILT_SOURCES += \
     path.js \
diff --git a/src/placeStore.js b/src/placeStore.js
new file mode 100644
index 0000000..638a17d
--- /dev/null
+++ b/src/placeStore.js
@@ -0,0 +1,189 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * GNOME Maps 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 Maps 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 Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Jonas Danielsson <jonas threetimestwo org>
+ */
+
+const Geocode = imports.gi.GeocodeGlib;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const Gtk = imports.gi.Gtk;
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const Utils = imports.utils;
+const Application = imports.application;
+
+const _PLACES_STORE_FILE = 'maps-places.json';
+const _ICON_SIZE = 20;
+
+const PlaceType = {
+    ANY: -1,
+    RECENT: 0
+};
+
+const Columns = {
+    PLACE_ICON: 0,
+    PLACE: 1,
+    NAME: 2,
+    TYPE: 3,
+    ADDED: 4
+};
+
+const PlaceStore = new Lang.Class({
+    Name: 'PlaceStore',
+    Extends: Gtk.ListStore,
+
+    _init: function() {
+        this._dirty = false;
+        this.recentLimit = Application.settings.get('recent-places-limit');
+        this._numRecent = 0;
+        this.filename = GLib.build_filenamev([GLib.get_user_data_dir(),
+                                              _PLACES_STORE_FILE]);
+        this._typeTable = {};
+
+        this.parent();
+        this.set_column_types([GdkPixbuf.Pixbuf,
+                               GObject.TYPE_OBJECT,
+                               GObject.TYPE_STRING,
+                               GObject.TYPE_INT,
+                               GObject.TYPE_DOUBLE]);
+    },
+
+    addRecent: function(place) {
+        if (this._exists(place, PlaceType.RECENT))
+            return;
+
+        if (this._numRecent === this.recentLimit) {
+            // Since all we do is append, the oldest recent will be
+            // the first one we encounter.
+            this._removeIf((function(model, iter) {
+                let type = model.get_value(iter, Columns.TYPE);
+                return type === PlaceType.RECENT;
+            }), true);
+        }
+        this._addPlace(place, PlaceType.RECENT, new Date().getTime());
+        this._numRecent++;
+    },
+
+    load: function() {
+        if (!GLib.file_test(this.filename, GLib.FileTest.EXISTS))
+            return;
+
+        let buffer = Utils.readFile(this.filename);
+        if (buffer === null)
+            return;
+
+        try {
+            let jsonArray = JSON.parse(buffer);
+            jsonArray.forEach((function(obj) {
+                let location = new Geocode.Location({
+                    latitude:    obj.latitude,
+                    longitude:   obj.longitude,
+                    altitude:    obj.altitude,
+                    accuracy:    obj.accuracy,
+                    description: obj.name
+                });
+                let place = Geocode.Place.new_with_location(obj.name,
+                                                            obj.place_type,
+                                                            location);
+
+                this._addPlace(place, obj.type, obj.added);
+                if (obj.type === PlaceType.RECENT)
+                    this._numRecent++;
+            }).bind(this));
+        } catch (e) {
+            throw new Error('failed to parse places file');
+        }
+    },
+
+    store: function() {
+        if (!this._dirty)
+            return;
+
+        let jsonArray = [];
+        this.foreach(function(model, path, iter) {
+            let place    = model.get_value(iter, Columns.PLACE),
+                location = place.location,
+                type     = model.get_value(iter, Columns.TYPE),
+                added    = model.get_value(iter, Columns.ADDED);
+
+            jsonArray.push({
+                place_type: place.place_type,
+                name:       place.name,
+                latitude:   location.latitude,
+                longitude:  location.longitude,
+                altitude:   location.altitude,
+                accuracy:   location.accuracy,
+                type:       type,
+                added:      added
+            });
+        });
+
+        let buffer = JSON.stringify(jsonArray);
+        if (!Utils.writeFile(this.filename, buffer))
+            throw new Error('failed to write file');
+        else
+            this.dirty = false;
+    },
+
+    _addPlace: function(place, type, added) {
+        let iter = this.append();
+
+        this.set(iter,
+                 [Columns.PLACE,
+                  Columns.NAME,
+                  Columns.TYPE,
+                  Columns.ADDED],
+                 [place,
+                  place.name,
+                  type,
+                  added]);
+
+        if (place.icon !== null) {
+            Utils.load_icon(place.icon, _ICON_SIZE, (function(pixbuf) {
+                this.set(iter, [Columns.ICON], [pixbuf]);
+            }).bind(this));
+        }
+        this._typeTable[place.name] = type;
+        this._dirty = true;
+
+        try {
+            this.store();
+        } catch (e) {
+            Utils.debug(e);
+        }
+    },
+
+    _exists: function(place, type) {
+        return this._typeTable[place.name] === type;
+    },
+
+    _removeIf: function(evalFunc, stop) {
+        this.foreach((function(model, path, iter) {
+            if (evalFunc(model, iter)) {
+                this.remove(iter);
+                if (stop)
+                    return true;
+            }
+            return false;
+        }).bind(this));
+    }
+});
diff --git a/src/utils.js b/src/utils.js
index d7d17b4..631ca12 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -134,6 +134,31 @@ function getUIObject(res, ids) {
     return ret;
 }
 
+function readFile(filename) {
+    let status, buffer;
+    let file = Gio.File.new_for_path(filename);
+    try {
+        [status, buffer] = file.load_contents(null);
+    } catch (e) {
+        return null;
+    }
+    if (status)
+        return buffer;
+    else
+        return null;
+}
+
+function writeFile(filename, buffer) {
+    let file = Gio.File.new_for_path(filename);
+    let status;
+    try {
+        status = file.replace_contents(buffer, null, false, 0, null)[0];
+        return status;
+    } catch (e) {
+        return false;
+    }
+}
+
 function load_icon(icon, size, loadCompleteCallback) {
     if (icon instanceof Gio.FileIcon) {
         _load_file_icon(icon, loadCompleteCallback);


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