[gnome-weather] Improve split between the model and view for the world module



commit 69ba00ac225efee9a4d4e29722d51b14d2d102ff
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Aug 3 15:05:51 2014 +0200

    Improve split between the model and view for the world module
    
    Split world.js into shared/world.js, which contains the model
    (a simple array of GWeatherInfos backed by GSettings), and app/world.js,
    which contains the view (a GtkPopover holding a GtkListBox).
    This will allow restoring the functionality of the search provider.

 data/places-popover.ui                             |   75 +----
 data/window.ui                                     |   75 ++++-
 src/app/main.js                                    |   70 +---
 src/app/window.js                                  |   43 ++-
 src/app/world.js                                   |  213 +++++++++++
 ...org.gnome.Weather.Application.src.gresource.xml |    1 +
 src/shared/world.js                                |  389 ++++----------------
 7 files changed, 411 insertions(+), 455 deletions(-)
---
diff --git a/data/places-popover.ui b/data/places-popover.ui
index 3a80f68..885ab40 100644
--- a/data/places-popover.ui
+++ b/data/places-popover.ui
@@ -198,6 +198,7 @@
                     <property name="can-focus">False</property>
                     <property name="hexpand">True</property>
                     <property name="vexpand">False</property>
+                    <property name="selection-mode">none</property>
                   </object>
                 </child>
               </object>
@@ -222,78 +223,4 @@
       </packing>
     </child>
   </object>
-  <object class="GtkGrid" id="initial-grid">
-    <property name="visible">True</property>
-    <property name="name">initial-grid</property>
-    <property name="can_focus">False</property>
-    <property name="orientation">vertical</property>
-    <property name="margin_top">25</property>
-    <property name="margin_bottom">25</property>
-    <property name="halign">center</property>
-    <property name="valign">center</property>
-    <property name="row_homogeneous">True</property>
-    <property name="vexpand">False</property>
-    <child>
-      <object class="GtkImage" id="mark-location-image">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="icon_name">mark-location-symbolic</property>
-        <property name="icon_size">6</property>
-        <property name="use_fallback">True</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
-        <property name="vexpand">False</property>
-      </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">0</property>
-        <property name="width">1</property>
-        <property name="height">1</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkLabel" id="search-location-label">
-        <property name="visible">True</property>
-        <property name="can-focus">False</property>
-        <property name="label">Search for a location</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
-        <property name="vexpand">False</property>
-      </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">1</property>
-        <property name="width">1</property>
-        <property name="height">1</property>
-      </packing>
-    </child>
-        <child>
-      <object class="GtkLabel" id="search-nereby-location-label">
-        <property name="visible">True</property>
-        <property name="can-focus">False</property>
-        <property name="label">To see weather information, enter the name of a city.</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
-        <property name="vexpand">False</property>
-      </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">2</property>
-        <property name="width">1</property>
-        <property name="height">1</property>
-      </packing>
-    </child>
-    <child>
-    <object class="GWeatherLocationEntry" id="initial-grid-location-entry">
-      <property name="visible">True</property>
-      <property name="can_focus">True</property>
-    </object>
-    <packing>
-      <property name="left_attach">0</property>
-      <property name="top_attach">3</property>
-      <property name="width">1</property>
-      <property name="height">1</property>
-    </packing>
-    </child>
-  </object>
 </interface>
diff --git a/data/window.ui b/data/window.ui
index e48d9b3..6cb5e7f 100644
--- a/data/window.ui
+++ b/data/window.ui
@@ -56,7 +56,80 @@
       <object class="GtkStack" id="main-stack">
         <property name="transition-type">crossfade</property>
         <child>
-          <placeholder/> <!-- world view -->
+          <object class="GtkGrid" id="initial-grid">
+            <property name="visible">True</property>
+            <property name="name">initial-grid</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="margin_top">25</property>
+            <property name="margin_bottom">25</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="row_homogeneous">True</property>
+            <property name="vexpand">False</property>
+            <child>
+              <object class="GtkImage" id="mark-location-image">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">mark-location-symbolic</property>
+                <property name="icon_size">6</property>
+                <property name="use_fallback">True</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="vexpand">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="search-location-label">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">Search for a location</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="vexpand">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="search-nereby-location-label">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label">To see weather information, enter the name of a city.</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="vexpand">False</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GWeatherLocationEntry" id="initial-grid-location-entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+          </object>
         </child>
         <child>
           <placeholder/> <!-- city view -->
diff --git a/src/app/main.js b/src/app/main.js
index 0a87ca8..30bd6d7 100644
--- a/src/app/main.js
+++ b/src/app/main.js
@@ -60,21 +60,7 @@ const Application = new Lang.Class({
         let location = this.world.deserialize(parameter.deep_unpack());
         let win = this._createWindow();
 
-        let info = this.model.getLocationInfo(location);
-        if (!info) {
-            this.model.addLocation(location, false);
-            info = this.model.getLocationInfo(location);
-        }
-
-        win.showInfo(info);
-    },
-
-    _onNewLocation: function(action, parameter) {
-        let win = this.get_active_window();
-        if (!win)
-            win = this._createWindow();
-
-        win.activate_action('new-location', null);
+        this.model.addNewLocation(location, false);
     },
 
     _initAppMenu: function() {
@@ -111,9 +97,7 @@ const Application = new Lang.Class({
                             activate: this._onQuit },
                           { name: 'show-location',
                             activate: this._onShowLocation,
-                            parameter_type: new GLib.VariantType('v') },
-                          { name: 'new-location',
-                            activate: this._onNewLocation }]);
+                            parameter_type: new GLib.VariantType('v') }]);
 
         let gwSettings = new Gio.Settings({ schema_id: 'org.gnome.GWeather' });
         this.add_action(gwSettings.create_action('temperature-unit'));
@@ -128,40 +112,26 @@ const Application = new Lang.Class({
         let win = new Window.MainWindow({ application: this });
         let notifyId;
 
-        if (!this.currentLocationController.autoLocation) {
-            if (this.model.loading) {
-                let timeoutId;
-                let model = this.model;
-
-                timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, function() {
-                    log('Timeout during model load, perhaps the network is not available?');
-                    model.disconnect(notifyId);
-                    win.show();
-                    return false;
-                });
-                notifyId = this.model.connect('notify::loading', function(model) {
-                    if (model.loading)
-                        return;
-
-                    model.disconnect(notifyId);
-                    GLib.source_remove(timeoutId);
-                    win.show();
-                });
-            } else {
+        if (this.model.loading) {
+            let timeoutId;
+            let model = this.model;
+
+            timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, function() {
+                log('Timeout during model load, perhaps the network is not available?');
+                model.disconnect(notifyId);
                 win.show();
-            }
-        } else {
-            if (this.model.loading) {
-                notifyId = this.model.connect('notify::loading', Lang.bind(this, function(model) {
-                    if (model.loading)
-                        return;
-
-                    model.disconnect(notifyId);
-                    win.show();
-                }));
-            } else {
+                return false;
+            });
+            notifyId = this.model.connect('notify::loading', function(model) {
+                if (model.loading)
+                    return;
+
+                model.disconnect(notifyId);
+                GLib.source_remove(timeoutId);
                 win.show();
-            }
+            });
+        } else {
+            win.show();
         }
 
         return win;
diff --git a/src/app/window.js b/src/app/window.js
index 444635c..19b82d7 100644
--- a/src/app/window.js
+++ b/src/app/window.js
@@ -23,10 +23,11 @@ const Lang = imports.lang;
 const City = imports.app.city;
 const Params = imports.misc.params;
 const World = imports.shared.world;
+const WorldView = imports.app.world;
 const Util = imports.misc.util;
 
 const Page = {
-    WORLD: 0,
+    SEARCH: 0,
     CITY: 1
 };
 
@@ -41,7 +42,7 @@ const MainWindow = new Lang.Class({
 
         this._world = this.application.world;
         this._currentInfo = null;
-        this._currentPage = Page.WORLD;
+        this._currentPage = Page.SEARCH;
         this._pageWidgets = [[],[]];
 
         Util.initActions(this,
@@ -62,20 +63,21 @@ const MainWindow = new Lang.Class({
         this._header.title = title;
         this._header.subtitle = subtitle;
 
-        this._worldView = new World.WorldContentView(this.application, { visible: true });
+        this._worldView = new WorldView.WorldContentView(this.application, { visible: true });
         this._worldView.hide();
 
         this._model = this._worldView.model;
         this._model.connect('show-info', Lang.bind(this, function(model, info) {
-            this.showInfo(info);
-        }));
-        this._model.connect('no-cityview', Lang.bind(this, function() {
-            for (let i = 0; i < this._pageWidgets[Page.WORLD].length; i++)
-                this._pageWidgets[Page.WORLD][i].show_all();
+            if (info)
+                this.showInfo(info);
+            else
+                this.showSearch(info);
         }));
 
-        this._initialGrid = this._worldView.initialGrid;
-        this._pageWidgets[Page.WORLD].push(this._initialGrid);
+        let initialGrid = builder.get_object('initial-grid');
+
+        let initialGridLocEntry = builder.get_object('initial-grid-location-entry');
+        initialGridLocEntry.connect('notify::location', Lang.bind(this, this._initialLocationChanged));
 
         let placesButton = builder.get_object('places-button');
         this._pageWidgets[Page.CITY].push(placesButton);
@@ -91,9 +93,7 @@ const MainWindow = new Lang.Class({
                                                 vexpand: true });
         this._stack.add(this._cityView);
 
-        this._stack.add(this._initialGrid);
-
-        this._stack.set_visible_child(this._initialGrid);
+        this._stack.set_visible_child(initialGrid);
 
         this.add(grid);
         grid.show_all();
@@ -101,15 +101,22 @@ const MainWindow = new Lang.Class({
         for (let i = 0; i < this._pageWidgets[Page.CITY].length; i++)
             this._pageWidgets[Page.CITY][i].hide();
 
-        this._model.fillCityView(this.application.currentLocationController.autoLocation);
+        let autoLocation = this.application.currentLocationController.autoLocation;
+        if (!autoLocation)
+            this._model.showRecent();
     },
 
     update: function() {
         this._cityView.update();
     },
 
+    _initialLocationChanged: function(entry) {
+        if (entry.location)
+            this._model.addNewLocation(entry.location, false);
+    },
+
     _getTitle: function() {
-        if (this._currentPage == Page.WORLD)
+        if (this._currentPage == Page.SEARCH)
             return [_("Select Location"), null];
 
         let location = this._cityView.info.location;
@@ -145,9 +152,14 @@ const MainWindow = new Lang.Class({
         this._header.subtitle = subtitle;
     },
 
+    showSearch: function() {
+        this._goToPage(Page.SEARCH);
+    },
+
     showInfo: function(info) {
         this._cityView.info = info;
         this._cityView.disconnectClock();
+
         let isCurrentLocation = false;
         let currentLocation = this.application.currentLocationController.currentLocation;
         if (currentLocation) {
@@ -159,6 +171,7 @@ const MainWindow = new Lang.Class({
             this._cityView.connectClock();
             this._cityView.infoPage.timeGrid.show();
         }
+
         this._stack.set_visible_child(this._cityView);
         this._goToPage(Page.CITY);
     },
diff --git a/src/app/world.js b/src/app/world.js
new file mode 100644
index 0000000..32edaff
--- /dev/null
+++ b/src/app/world.js
@@ -0,0 +1,213 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni gmail com>
+//               2014 Saurabh Patel <srp201201051 gmail com>
+//
+// Gnome Weather 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 Weather 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 Weather; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+const GWeather = imports.gi.GWeather;
+const Lang = imports.lang;
+
+const Params = imports.misc.params;
+const Util = imports.misc.util;
+
+
+const WorldContentView = new Lang.Class({
+    Name: 'WorldContentView',
+    Extends: Gtk.Popover,
+
+    _init: function(application, params) {
+        params = Params.fill(params, { hexpand: false, vexpand: false });
+        this.parent(params);
+
+        this.get_accessible().accessible_name = _("World view");
+
+        let builder = new Gtk.Builder();
+        builder.add_from_resource('/org/gnome/Weather/Application/places-popover.ui');
+
+        let grid = builder.get_object('popover-grid');
+        this.add(grid);
+
+        this.model = application.model;
+
+        this._listbox = builder.get_object('locations-list-box');
+        this._listbox.set_header_func(function (row, previous) {
+            let hasHeader = row.get_header() != null;
+            let shouldHaveHeader = previous != null;
+            if (hasHeader != shouldHaveHeader) {
+                if (shouldHaveHeader)
+                    row.set_header(new Gtk.Separator());
+                else
+                    row.set_header(null);
+            }
+        });
+
+        let locationEntry = builder.get_object('location-entry');
+        locationEntry.connect('notify::location', Lang.bind(this, this._locationChanged));
+
+        this.connect('show', Lang.bind(this, function() {
+            locationEntry.grab_focus();
+        }));
+
+        let autoLocStack = builder.get_object('auto-location-stack');
+        let autoLocSwitch = builder.get_object('auto-location-switch');
+        let currentLocationController = application.currentLocationController;
+
+        if(currentLocationController.autoLocation) {
+            autoLocStack.visible_child_name = 'locating-label';
+        } else {
+            autoLocStack.visible_child_name = 'auto-location-switch-grid';
+            autoLocSwitch.active = false;
+        }
+
+        let handlerId = autoLocSwitch.connect('notify::active', Lang.bind(this, function() {
+            currentLocationController.setAutoLocation(autoLocSwitch.active);
+
+            if (autoLocSwitch.active && !this.model.addedCurrentLocation)
+                autoLocStack.visible_child_name = 'locating-label';
+
+            this.hide();
+        }));
+
+        this._listbox.connect('row-activated', Lang.bind(this, function(listbox, row) {
+            this.hide();
+            this.model.showInfo(row._info);
+        }));
+
+        this.model.connect('current-location-changed', Lang.bind(this, function(model, location) {
+            autoLocStack.visible_child_name = 'auto-location-switch-grid';
+            if (location) {
+                GObject.signal_handler_block(autoLocSwitch, handlerId);
+                autoLocSwitch.active = true;
+                GObject.signal_handler_unblock(autoLocSwitch, handlerId);
+            } else {
+                GObject.signal_handler_block(autoLocSwitch, handlerId);
+                autoLocSwitch.active = false;
+                GObject.signal_handler_unblock(autoLocSwitch, handlerId);
+
+                autoLocSwitch.sensitive = false;
+            }
+        }));
+
+        let stackPopover = builder.get_object('popover-stack');
+        this.model.connect('revalidate', Lang.bind(this, function() {
+            this._listbox.invalidate_filter();
+
+            let children = this._listbox.get_children();
+            if (children.length == 1)
+                stackPopover.set_visible_child_name("search-grid");
+            else
+                stackPopover.set_visible_child_name("locations-grid");
+        }));
+
+        this._listbox.set_filter_func(Lang.bind(this, this._filterListbox, this.model));
+        this.model.connect('location-added', Lang.bind(this, this._onLocationAdded));
+        this.model.connect('location-removed', Lang.bind(this, this._onLocationRemoved));
+
+        this.model.load();
+    },
+
+    _filterListbox: function(row, model) {
+        return model.currentlyLoadedInfo == null ||
+            row._info != model.currentlyLoadedInfo;
+    },
+
+    _locationChanged: function(entry) {
+        if (entry.location) {
+            this.model.addNewLocation(entry.location, false);
+            this.hide();
+            entry.location = null;
+        }
+    },
+
+    _onLocationAdded: function(model, info, isCurrentLocation) {
+        let location = info.location;
+
+        let grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
+                                  column_spacing: 12,
+                                  margin: 12,
+                                  visible: true });
+
+        let name = location.get_city_name();
+        let locationGrid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
+                                          column_spacing: 12,
+                                          halign: Gtk.Align.START,
+                                          hexpand: true,
+                                          visible: true });
+        let locationLabel = new Gtk.Label({ label: name,
+                                            use_markup: true,
+                                            halign: Gtk.Align.START,
+                                            visible: true });
+        locationGrid.attach(locationLabel, 0, 0, 1, 1);
+        grid.attach(locationGrid, 0, 0, 1, 1);
+
+        let tempLabel = new Gtk.Label({ use_markup: true,
+                                        halign: Gtk.Align.END,
+                                        margin_start: 12,
+                                        visible: true });
+        grid.attach(tempLabel, 1, 0, 1, 1);
+
+        if (isCurrentLocation) {
+            let image = new Gtk.Image({ icon_size: Gtk.IconSize.LARGE_TOOLBAR,
+                                        icon_name: 'mark-location-symbolic',
+                                        use_fallback: true,
+                                        halign: Gtk.Align.START,
+                                        visible: true });
+            locationGrid.attach(image, 1, 0, 1, 1);
+        }
+
+        let image = new Gtk.Image({ icon_size: Gtk.IconSize.LARGE_TOOLBAR,
+                                    use_fallback: true,
+                                    halign: Gtk.Align.END,
+                                    visible: true });
+        grid.attach(image, 2, 0, 1, 1);
+
+        let row = new Gtk.ListBoxRow({ visible: true });
+        row.add(grid);
+        row._info = info;
+
+        if (isCurrentLocation) {
+            if (model.addedCurrentLocation) {
+                this._listbox.get_row_at_index(0).destroy();
+            }
+
+            this._listbox.insert(row, 0);
+        } else {
+            if (model.addedCurrentLocation)
+                this._listbox.insert(row, 1);
+            else
+                this._listbox.insert(row, 0);
+        }
+
+        info.connect('updated', Lang.bind(this, function(info) {
+            tempLabel.label = info.get_temp_summary();
+            image.icon_name = info.get_symbolic_icon_name();
+        }));
+    },
+
+    _onLocationRemoved: function(model, info) {
+        let rows = this._listbox.get_children();
+
+        for (let row of rows) {
+            if (row._info == info) {
+                row.destroy();
+                break;
+            }
+        }
+    },
+});
diff --git a/src/org.gnome.Weather.Application.src.gresource.xml 
b/src/org.gnome.Weather.Application.src.gresource.xml
index 33a1ebe..0d91f1c 100644
--- a/src/org.gnome.Weather.Application.src.gresource.xml
+++ b/src/org.gnome.Weather.Application.src.gresource.xml
@@ -7,6 +7,7 @@
     <file>app/weeklyForecast.js</file>
     <file>app/main.js</file>
     <file>app/window.js</file>
+    <file>app/world.js</file>
     <file>misc/params.js</file>
     <file>misc/util.js</file>
     <file>shared/strings.js</file>
diff --git a/src/shared/world.js b/src/shared/world.js
index 461c0f8..2ce4ee0 100644
--- a/src/shared/world.js
+++ b/src/shared/world.js
@@ -18,30 +18,21 @@
 
 const GLib = imports.gi.GLib;
 const GObject = imports.gi.GObject;
-const Gtk = imports.gi.Gtk;
 const GWeather = imports.gi.GWeather;
 const Lang = imports.lang;
 
 const Params = imports.misc.params;
 const Util = imports.misc.util;
 
-function getLabelFromRow(row) {
-    let grid = row.get_child();
-    let cityGrid = grid.get_child_at(0, 0);
-    let label = cityGrid.get_child_at(0, 0);
-    return label.get_text();
-}
-
 const WorldModel = new Lang.Class({
     Name: 'WorldModel',
     Extends: GObject.Object,
     Signals: {
-        'updated': { param_types: [ GWeather.Info ] },
-        'no-cityview': { param_types: [] },
         'show-info': { param_types: [ GWeather.Info ] },
         'current-location-changed': { param_types: [ GWeather.Location ] },
-        'validate-listbox': { param_types: [ GWeather.Location ] },
-        'update-listbox': { param_types: [ GWeather.Location, GWeather.Info, GObject.Boolean ] }
+        'revalidate': { param_types: [ GWeather.Location ] },
+        'location-added': { param_types: [ GWeather.Info, GObject.Boolean ] },
+        'location-removed': { param_types: [ GWeather.Info ] }
     },
     Properties: {
         'loading': GObject.ParamSpec.boolean('loading', '', '', GObject.ParamFlags.READABLE, false)
@@ -53,108 +44,62 @@ const WorldModel = new Lang.Class({
         this._world = world;
 
         this._settings = Util.getSettings('org.gnome.Weather.Application');
-
         this._providers = Util.getEnabledProviders();
 
         this._loadingCount = 0;
 
         this._infoList = [];
 
-        this._enableGtk = enableGtk;
-
-        this._currentLocationInfo = null;
-
         this.currentlyLoadedInfo = null;
-
         this.addedCurrentLocation = false;
+    },
 
-        this.numberOfLocations = 0;
+    get length() {
+        return this._infoList.length;
+    },
+
+    getAll: function() {
+        return [].concat(this._infoList);
     },
 
     currentLocationChanged: function(location) {
         if (location) {
-            this._newCurrentLocationInfo = new GWeather.Info({ location: location,
-                                                               enabled_providers: this._providers });
-            if (this._currentLocationInfo) {
-                if (!this._currentLocationInfo.location.equal(location)) {
-                    this._newCurrentLocationInfo.connect('updated', Lang.bind(this, function(info) {
-                        this._updateLoadingCount(-1);
-                        this.emit('updated', this._newCurrentLocationInfo);
-                    }));
-                    this.updateInfo(this._newCurrentLocationInfo);
-                    this._currentLocationInfo = this._newCurrentLocationInfo;
-                }
-            } else {
-                this._newCurrentLocationInfo.connect('updated', Lang.bind(this, function(info) {
-                    this._updateLoadingCount(-1);
-                    this.emit('updated', this._newCurrentLocationInfo);
-                }));
-                this.updateInfo(this._newCurrentLocationInfo);
-                this._currentLocationInfo = this._newCurrentLocationInfo;
-            }
+            this.addNewLocation(location, true);
+        } else {
+            if (!this.addedCurrentLocation)
+                this.showRecent();
         }
 
         this.emit('current-location-changed', location);
     },
 
-    rowActivated: function(listbox, row) {
-        let cityName = getLabelFromRow(row);
-        this.emit('show-info', this._infoList[cityName]);
-        this.currentlyLoadedInfo = this._infoList[cityName];
-        this.emit('validate-listbox', this.currentlyLoadedInfo.location);
+    showInfo: function(info) {
+        this.currentlyLoadedInfo = info;
+        this.emit('show-info', info);
+        if (info != null)
+            this.emit('revalidate', info.location);
     },
 
     showRecent: function(listbox) {
-        let row = listbox.get_row_at_index(0);
-        if (row) {
-            this.rowActivated(null, row);
-            return;
-        } else
-            this.emit('no-cityview');
-    },
-
-    _showCurrentLocation: function() {
-        if (this._currentLocationInfo) {
-            this.emit('show-info', this._currentLocationInfo);
-            this.currentlyLoadedInfo = this._currentLocationInfo;
-        }
+        if (this._infoList.length > 0)
+            this.showInfo(this._infoList[0]);
+        else
+            this.showInfo(null);
     },
 
-    fillListbox: function () {
+    load: function () {
         let locations = this._settings.get_value('locations').deep_unpack();
-        this.numberOfLocations = locations.length;
-        if (locations.length != 0) {
-            for (let i = 0; i < locations.length && i < 5; i++) {
-                let variant = locations[i];
-                let location = this._world.deserialize(variant);
-                let info = new GWeather.Info({ location: location,
-                                               enabled_providers: this._providers });
-
-                this._infoList[location.get_city_name()] = info;
-
-                info.connect('updated', Lang.bind(this, function(info) {
-                    this._updateLoadingCount(-1);
-                    this.emit('updated', info);
-                }));
-                this.updateInfo(info);
-                this.emit('update-listbox', location, info, false);
-            }
+
+        if (locations.length > 5) {
+            locations = locations.slice(0, 5);
+            this._settings.set_value('locations', new GLib.Variant('av', locations));
         }
-    },
 
-    fillCityView: function(autoLocation) {
-        let locations = this._settings.get_value('locations').deep_unpack();
+        for (let i = 0; i < locations.length; i++) {
+            let variant = locations[i];
+            let location = this._world.deserialize(variant);
 
-        if (!autoLocation) {
-            if (locations.length > 0) {
-                let variant = locations[locations.length-1];
-                let location = this._world.deserialize(variant);
-                this.emit('show-info', this._infoList[location.get_city_name()]);
-                this.currentlyLoadedInfo = this._infoList[location.get_city_name()];
-                this.emit('validate-listbox', this.currentlyLoadedInfo.location);
-            } else {
-                this.emit('no-cityview');
-            }
+            this._addLocationInternal(location, false);
         }
     },
 
@@ -168,6 +113,16 @@ const WorldModel = new Lang.Class({
     },
 
     updateInfo: function(info) {
+        if (info._loadingId)
+            return;
+
+        info._loadingId = info.connect('updated', Lang.bind(this, function(info) {
+            info.disconnect(info._loadingId);
+            info._loadingId = 0;
+
+            this._updateLoadingCount(-1);
+        }));
+
         info.update();
         this._updateLoadingCount(+1);
     },
@@ -177,249 +132,53 @@ const WorldModel = new Lang.Class({
     },
 
     addNewLocation: function(newLocation, isCurrentLocation) {
-        if (newLocation) {
-            let locations = this._settings.get_value('locations').deep_unpack();
-
-            if (!isCurrentLocation) {
-                for (let i = 0; i < locations.length; i++) {
-                    let location = this._world.deserialize(locations[i]);
-                    if (location.equal(newLocation)) {
-                        this.emit('show-info', this._infoList[location.get_city_name()]);
-                        this.currentlyLoadedInfo = this._infoList[location.get_city_name()];
-                        this.emit('validate-listbox', this.currentlyLoadedInfo.location);
-                        return;
-                    }
+        if (!isCurrentLocation) {
+            for (let info of this._infoList) {
+                let location = info.location;
+                if (location.equal(newLocation)) {
+                    this.showInfo(info);
+                    return;
                 }
-
-                locations.push(newLocation.serialize());
-
-                this.numberOfLocations = locations.length;
-
-                this._settings.set_value('locations', new GLib.Variant('av', locations));
             }
 
-            let info = new GWeather.Info({ location: newLocation,
-                                           enabled_providers: this._providers });
-
-            this._infoList[newLocation.get_city_name()] = info;
-
-            info.connect('updated', Lang.bind(this, function(info) {
-                this._updateLoadingCount(-1);
-                this.emit('updated', info);
-            }));
-            this.updateInfo(info);
-
-            this.emit('update-listbox', newLocation, info, isCurrentLocation);
-            if (!isCurrentLocation) {
-                this.emit('show-info', this._infoList[newLocation.get_city_name()]);
-                this.currentlyLoadedInfo = this._infoList[newLocation.get_city_name()];
-                this.emit('validate-listbox', this.currentlyLoadedInfo.location);
-            }
-            else {
-                if(!this.addedCurrentLocation) {
-                    this.emit('show-info', this._currentLocationInfo);
-                    this.currentlyLoadedInfo = this._currentLocationInfo;
-                    this.emit('validate-listbox', this.currentlyLoadedInfo.location);
-                }
-                this.addedCurrentLocation = true;
-            }
+            let locations = this._settings.get_value('locations').deep_unpack();
+            locations.push(newLocation.serialize());
+            this._settings.set_value('locations', new GLib.Variant('av', locations));
         }
-    }
-});
-
-const WorldContentView = new Lang.Class({
-    Name: 'WorldContentView',
-    Extends: Gtk.Popover,
-
-    _init: function(application, params) {
-        params = Params.fill(params, { hexpand: false, vexpand: false });
-        this.parent(params);
-
-        this.get_accessible().accessible_name = _("World view");
-
-        let builder = new Gtk.Builder();
-        builder.add_from_resource('/org/gnome/Weather/Application/places-popover.ui');
-
-        let grid = builder.get_object('popover-grid');
-        this.add(grid);
 
-        this.initialGrid = builder.get_object('initial-grid');
-
-        let stackPopover = builder.get_object('popover-stack');
-
-        this.model = application.model;
-
-        this._listbox = builder.get_object('locations-list-box');
-        this._listbox.set_header_func(function (row, previous) {
-            let hasHeader = row.get_header() != null;
-            let shouldHaveHeader = previous != null;
-            if (hasHeader != shouldHaveHeader) {
-                if (shouldHaveHeader)
-                    row.set_header(new Gtk.Separator());
-                else
-                    row.set_header(null);
-            }
-        });
-
-        let initialGridLocEntry = builder.get_object('initial-grid-location-entry');
-        initialGridLocEntry.connect('notify::location', Lang.bind(this, function(entry) {
-            this._locationChanged(entry);
-        }));
-
-        let locationEntry = builder.get_object('location-entry');
-        locationEntry.connect('notify::location', Lang.bind(this, function(entry) {
-            this._locationChanged(entry)
-        }));
-
-        this.connect('notify::visible', Lang.bind(this, function() {
-            this._listbox.set_selection_mode(0);
-            locationEntry.grab_focus();
-            this._listbox.set_selection_mode(1);
-        }));
-
-        let autoLocStack = builder.get_object('auto-location-stack');
-
-        let autoLocSwitch = builder.get_object('auto-location-switch');
-
-        let currentLocationController = application.currentLocationController;
-
-        let handlerId = autoLocSwitch.connect('notify::active', Lang.bind(this, function() {
-            currentLocationController.setAutoLocation(autoLocSwitch.get_active());
-
-            if (autoLocSwitch.get_active() && !this.model.addedCurrentLocation)
-                autoLocStack.set_visible_child_name('locating-label');
-
-            this.hide();
-        }));
+        this._addLocationInternal(newLocation, isCurrentLocation);
+    },
 
-        if(currentLocationController.autoLocation)
-            autoLocStack.set_visible_child_name('locating-label');
-        else {
-            autoLocStack.set_visible_child_name('auto-location-switch-grid');
-            GObject.signal_handler_block(autoLocSwitch, handlerId);
-            autoLocSwitch.set_active(false);
-            GObject.signal_handler_unblock(autoLocSwitch, handlerId);
+    _removeLocationInternal: function(oldInfo) {
+        if (oldInfo._loadingId) {
+            oldInfo.disconnect(oldInfo._loadingId);
+            oldInfo._loadingId = 0;
+            this._updateLoadingCount(-1);
         }
 
-        this._listbox.connect('row-activated', Lang.bind(this, function(listbox, row) {
-            this.hide();
-            this.model.rowActivated(listbox, row);
-        }));
-
-        this.model.connect('current-location-changed', Lang.bind(this, function(model, location) {
-            autoLocStack.set_visible_child_name('auto-location-switch-grid');
-            if (location) {
-                this.model.addNewLocation(location, true);
-
-                GObject.signal_handler_block(autoLocSwitch, handlerId);
-                autoLocSwitch.set_active(true);
-                GObject.signal_handler_unblock(autoLocSwitch, handlerId);
-            } else {
-                if (!this.model.addedCurrentLocation)
-                    this.model.showRecent(this._listbox);
-
-                GObject.signal_handler_block(autoLocSwitch, handlerId);
-                autoLocSwitch.set_active(false);
-                GObject.signal_handler_unblock(autoLocSwitch, handlerId);
-
-                autoLocSwitch.set_sensitive(false);
-            }
-        }));
-
-        this.model.connect('validate-listbox', Lang.bind(this, function() {
-            this._listbox.invalidate_filter();
-            let children = this._listbox.get_children();
-            if (children.length == 1) {
-                stackPopover.set_visible_child_name("search-grid");
-                return;
-            }
-            stackPopover.set_visible_child_name("locations-grid");
-        }));
-
-        this._listbox.set_filter_func(Lang.bind(this, this._filterListbox, this.model));
-
-        this.model.connect('update-listbox', Lang.bind(this, this._addLocationInternal));
-
-        this.model.fillListbox();
+        this.emit('location-removed', oldInfo);
     },
 
-    _filterListbox: function(row, model) {
-        if(model.currentlyLoadedInfo) {
-            let cityName = getLabelFromRow(row);
-            let loadedCity = model.currentlyLoadedInfo.location.get_city_name();
-            return (cityName != loadedCity);
-        }
-        return true;
-    },
+    _addLocationInternal: function(newLocation, isCurrentLocation) {
+        let info = new GWeather.Info({ location: newLocation,
+                                       enabled_providers: this._providers });
+        this._infoList.push(info);
+        this.updateInfo(info);
 
-    _locationChanged: function(entry) {
-        if (entry.location) {
-            this.model.addNewLocation(entry.location, false);
-            this.hide();
-            entry.location = null;
-        }
-    },
+        this.emit('location-added', info, isCurrentLocation);
 
-    _addLocationInternal: function(model, location, info, isCurrentLocation) {
-        let grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
-                                  column_spacing: 12,
-                                  margin: 12 });
-
-        let name = location.get_city_name();
-        let locationGrid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
-                                          column_spacing: 12,
-                                          halign: Gtk.Align.START,
-                                          hexpand: true,
-                                          visible: true });
-        let locationLabel = new Gtk.Label({ label: name,
-                                            use_markup: true,
-                                            halign: Gtk.Align.START,
-                                            visible: true });
-        locationGrid.attach(locationLabel, 0, 0, 1, 1);
-        grid.attach(locationGrid, 0, 0, 1, 1);
-
-        let tempLabel = new Gtk.Label({ use_markup: true,
-                                        halign: Gtk.Align.END,
-                                        margin_start: 12,
-                                        visible: true });
-        grid.attach(tempLabel, 1, 0, 1, 1);
+        if (this._infoList.length > 5) {
+            let oldInfo = this._infoList.pop();
+            this._removeLocationInternal(oldInfo);
+        }
 
         if (isCurrentLocation) {
-            let image = new Gtk.Image({ icon_size: Gtk.IconSize.LARGE_TOOLBAR,
-                                        icon_name: 'mark-location-symbolic',
-                                        use_fallback: true,
-                                        halign: Gtk.Align.START,
-                                        visible: true });
-            locationGrid.attach(image, 1, 0, 1, 1);
-        }
+            if(!this.addedCurrentLocation)
+                this.showInfo(info);
 
-        let image = new Gtk.Image({ icon_size: Gtk.IconSize.LARGE_TOOLBAR,
-                                    use_fallback: true,
-                                    halign: Gtk.Align.END,
-                                    visible: true });
-        grid.attach(image, 2, 0, 1, 1);
-
-        grid.show();
-        if(isCurrentLocation) {
-            if (model.addedCurrentLocation) {
-                let children = this._listbox.get_children();
-                children[0].destroy();
-            }
-            this._listbox.insert(grid, 0);
+            this.addedCurrentLocation = true;
         } else {
-            if (model.addedCurrentLocation)
-                this._listbox.insert(grid, 1);
-            else
-                this._listbox.insert(grid, 0);
+            this.showInfo(info);
         }
-        if (model.numberOfLocations > 5) {
-            let children = this._listbox.get_children();
-            children[children.length-1].destroy();
-        }
-
-        info.connect('updated', Lang.bind(this, function(info) {
-            tempLabel.label = info.get_temp_summary();
-            image.icon_name = info.get_symbolic_icon_name();
-        }));
-    },
+    }
 });



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