[tempo] all: make WeatherLoader a global model-like object
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tempo] all: make WeatherLoader a global model-like object
- Date: Sat, 24 Sep 2011 01:36:16 +0000 (UTC)
commit 16d4d36fe3700bf2bc7151b0ae31d72ae9a957ff
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Fri Sep 23 21:32:32 2011 -0400
all: make WeatherLoader a global model-like object
WeatherLoader can now emit three signals:
- changed in case the weather has been successfully loaded (from cache
or remotely)
- error in case the weather has failed to remotely refresh
- loading in case the weather is pending a remote refresh
Use those signals from the window to cleanup the loading code.
tempo/main.js | 14 +++-
tempo/mainWindow.js | 185 ++++++++++++++++++++++-------------------------
tempo/weatherLoader.js | 103 +++++++++++++++++----------
3 files changed, 163 insertions(+), 139 deletions(-)
---
diff --git a/tempo/main.js b/tempo/main.js
index 4fd4225..722098f 100644
--- a/tempo/main.js
+++ b/tempo/main.js
@@ -18,6 +18,7 @@
*/
const Gettext = imports.gettext;
+const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
@@ -26,6 +27,10 @@ const _ = imports.gettext.gettext;
const Format = imports.format;
const MainWindow = imports.mainWindow;
+const WeatherLoader = imports.weatherLoader;
+
+let settings = null;
+let loader = null;
function start() {
Gettext.bindtextdomain('tempo', Path.LOCALE_DIR);
@@ -35,14 +40,17 @@ function start() {
GLib.set_prgname('tempo');
Gtk.init(null, null);
- let settings = Gtk.Settings.get_default();
- settings.gtk_application_prefer_dark_theme = true;
+ let gtkSettings = Gtk.Settings.get_default();
+ gtkSettings.gtk_application_prefer_dark_theme = true;
let provider = new Gtk.CssProvider();
provider.load_from_path(Path.STYLE_DIR + "gtk-style.css");
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
provider, 600);
+ settings = Gio.Settings.new('org.gnome.tempo');
+ loader = new WeatherLoader.WeatherLoader(settings.get_string('last-location'));
let mainWindow = new MainWindow.MainWindow();
+
Gtk.main();
-}
\ No newline at end of file
+}
diff --git a/tempo/mainWindow.js b/tempo/mainWindow.js
index dbc7ec3..58ba729 100644
--- a/tempo/mainWindow.js
+++ b/tempo/mainWindow.js
@@ -18,7 +18,7 @@
*/
const ForecastBox = imports.forecastBox;
-const WeatherLoader = imports.weatherLoader;
+const Main = imports.main;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
@@ -30,12 +30,14 @@ const Path = imports.path;
const Tempo = imports.gi.Tempo;
const _ = imports.gettext.gettext;
-function MainWindow(args) {
+const _CONFIGURE_ID_TIMEOUT = 100; // msecs
+
+function MainWindow() {
this._init();
}
MainWindow.prototype = {
- _init : function(args) {
+ _init: function() {
this._configureId = 0;
this._gtkWindow = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL,
@@ -43,7 +45,6 @@ MainWindow.prototype = {
has_resize_grip: false,
resizable: false,
window_position: Gtk.WindowPosition.CENTER });
-
Tempo.ensure_rounded_corners(this._gtkWindow);
this._gtkWindow.connect('delete-event',
@@ -53,20 +54,13 @@ MainWindow.prototype = {
this._gtkWindow.connect('configure-event',
Lang.bind(this, this._onConfigureEvent));
- this._settings = Gio.Settings.new('org.gnome.tempo');
- this._lastLocation = this._settings.get_string('last-location');
-
- let position = this._settings.get_value('window-position');
-
- if (position.n_children() == 2) {
- let x = position.get_child_value(0);
- let y = position.get_child_value(1);
-
- this._gtkWindow.move(x.get_int32(),
- y.get_int32());
- }
-
+ // build an event box to trap clicks on the window
this._eventBox = new Gtk.EventBox();
+ this._gtkWindow.add(this._eventBox);
+ this._eventBox.connect('button-press-event',
+ Lang.bind(this, this._onButtonPressEvent));
+
+ // build the UI
this._mainGrid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
row_spacing: 12,
margin_left: 12,
@@ -74,17 +68,8 @@ MainWindow.prototype = {
margin_top: 6,
margin_bottom: 6 });
this._eventBox.add(this._mainGrid);
- this._eventBox.connect('button-press-event', Lang.bind(this,
- function(widget, event) {
- let rootCoords = event.get_root_coords();
- this._gtkWindow.begin_move_drag(1,
- rootCoords[1],
- rootCoords[2],
- event.get_time());
-
- return false;
- }));
+ // decoGrid holds the pseudo window decorations
this._decoGrid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL });
this._mainGrid.add(this._decoGrid);
@@ -94,7 +79,7 @@ MainWindow.prototype = {
this._decoGrid.add(this._cityLabel);
this._settingsButton =
- new Gtk.Button({ image: new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
+ new Gtk.Button({ child: new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
icon_name: 'system-run-symbolic',
halign: Gtk.Align.END }),
relief: Gtk.ReliefStyle.NONE });
@@ -102,17 +87,6 @@ MainWindow.prototype = {
Lang.bind(this, this._showSettings));
this._decoGrid.add(this._settingsButton);
- this._settingsSpinner = new Gtk.Spinner();
- this._settingsSpinner.set_size_request(16, 16);
- this._settingsSpinner.start();
- this._decoGrid.add(this._settingsSpinner);
- this._settingsSpinner.show();
-
- this._settingsButton.connect('notify::visible', Lang.bind(this,
- function(){
- this._settingsSpinner.set_visible(!this._settingsButton.get_visible());
- }));
-
this._quitButton =
new Gtk.Button({ image: new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
icon_name: 'window-close-symbolic',
@@ -122,6 +96,7 @@ MainWindow.prototype = {
Lang.bind(this, this._quitApp));
this._decoGrid.add(this._quitButton);
+ // boxGrid holds the primary information
this._boxGrid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
column_spacing: 16 });
this._mainGrid.add(this._boxGrid);
@@ -134,6 +109,7 @@ MainWindow.prototype = {
name: 'tempo-degree-label' });
this._boxGrid.add(this._degreeLabel);
+ // infoGrid holds the right side of the primary information
this._infoGrid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
row_spacing: 3 });
this._boxGrid.add(this._infoGrid);
@@ -152,11 +128,13 @@ MainWindow.prototype = {
name: 'tempo-detail-label' });
this._infoGrid.add(this._windLabel);
+ // separator between the primary and secondary sections
let separator = new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL,
hexpand: true,
name: 'tempo-separator' });
this._mainGrid.add(separator);
+ // forecastGrid holds the bottom secondary information
this._forecastGrid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
hexpand: true,
halign: Gtk.Align.CENTER,
@@ -166,44 +144,50 @@ MainWindow.prototype = {
this._mainGrid.add(this._forecastGrid);
this._createForecastGrid();
- this._spinnerBox = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
- row_spacing: 12,
- halign: Gtk.Align.CENTER,
- valign: Gtk.Align.CENTER });
- this._spinnerBox.set_border_width(18);
+ // connect to the loader signals and see if we can populate
+ // something from the cache already
+ Main.loader.connect('changed', Lang.bind(this, this._onLoaderChanged));
+ Main.loader.connect('loading', Lang.bind(this, this._onLoaderLoading));
+ Main.loader.connect('error', Lang.bind(this, this._onLoaderError));
+ this._onLoaderChanged(Main.loader);
- this._spinner = new Gtk.Spinner();
- this._spinner.set_size_request (48, 48);
- this._spinner.start();
- this._spinnerBox.add(this._spinner);
+ // show all
+ this._gtkWindow.show_all();
- let label = new Gtk.Label ({ label: _("Loading...")});
- this._spinnerBox.add(label);
+ // apply the last saved window position
+ let position = Main.settings.get_value('window-position');
+ if (position.n_children() == 2) {
+ let x = position.get_child_value(0);
+ let y = position.get_child_value(1);
- this._gtkWindow.show();
- this._startLoader();
+ this._gtkWindow.move(x.get_int32(),
+ y.get_int32());
+ }
- Mainloop.timeout_add_seconds(this._settings.get_int('refresh-interval'), Lang.bind(this,
- function() {
- this._startLoader();
- return true;
- }));
+ if (Main.settings.get_string('last-location') == '') {
+ this._showSettings();
+ return;
+ }
},
_quitApp : function() {
+ // remove configure event handler if still there
if (this._configureId != 0) {
Mainloop.source_remove(this._configureId);
this._configureId = 0;
- this._saveWindowPosition();
}
+ // always save before quitting
+ this._saveWindowPosition();
+
Gtk.main_quit();
},
_saveWindowPosition: function() {
+ // GLib.Variant.new() can handle arrays just fine
let position = this._gtkWindow.get_position();
let variant = GLib.Variant.new ('ai', position);
- this._settings.set_value('window-position', variant);
+ Main.settings.set_value('window-position', variant);
},
_onConfigureEvent: function(widget, event) {
@@ -212,7 +196,7 @@ MainWindow.prototype = {
this._configureId = 0;
}
- this._configureId = Mainloop.timeout_add(100, Lang.bind(this,
+ this._configureId = Mainloop.timeout_add(_CONFIGURE_ID_TIMEOUT, Lang.bind(this,
function() {
this._saveWindowPosition();
return false;
@@ -223,6 +207,7 @@ MainWindow.prototype = {
let keyval = event.get_keyval()[1];
let state = event.get_state()[1];
+ // quit with Ctrl+Q
if ((keyval == Gdk.KEY_q) &&
((state & Gdk.ModifierType.CONTROL_MASK) != 0)) {
this._quitApp();
@@ -232,6 +217,17 @@ MainWindow.prototype = {
return false;
},
+ _onButtonPressEvent: function(widget, event) {
+ // drag on click
+ let rootCoords = event.get_root_coords();
+ this._gtkWindow.begin_move_drag(1,
+ rootCoords[1],
+ rootCoords[2],
+ event.get_time());
+
+ return false;
+ },
+
_onEntryChanged : function() {
this._okButton.set_sensitive((this._settingsEntry.get_text() != ""));
},
@@ -266,7 +262,7 @@ MainWindow.prototype = {
this._settingsEntry.connect('changed',
Lang.bind(this, this._onEntryChanged));
- this._settingsEntry.set_text(this._lastLocation);
+ this._settingsEntry.set_text(Main.settings.get_string('last-location'));
grid.add(this._settingsEntry);
content.pack_start(grid, true, true, 6);
@@ -282,11 +278,9 @@ MainWindow.prototype = {
if (id == Gtk.ResponseType.ACCEPT) {
let newLocation = this._settingsEntry.get_text();
- if (newLocation != this._lastLocation) {
- this._lastLocation = this._settingsEntry.get_text();
- this._settings.set_string('last-location', this._lastLocation);
-
- this._startLoader();
+ if (newLocation != Main.settings.get_string('last-location')) {
+ Main.settings.set_string('last-location', newLocation);
+ Main.loader.setLocation(newLocation);
}
}
@@ -314,42 +308,37 @@ MainWindow.prototype = {
}
},
- _startLoader : function() {
- if (this._lastLocation == "") {
- this._showSettings();
- return;
- }
+ _onLoaderError: function(loader) {
+ this._settingsButton.get_child().destroy();
+ this._settingsButton.child = new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
+ icon_name: 'dialog-warning-symbolic',
+ halign: Gtk.Align.END,
+ visible: true });
+ },
- this._loader = new WeatherLoader.WeatherLoader(this._lastLocation);
- this._loader.loadCache(Lang.bind(this, this._onWeatherRefreshed));
- this._loader.refreshWeather(Lang.bind(this, this._onWeatherRefreshed));
+ _onLoaderLoading: function(loader) {
+ let spinner = new Gtk.Spinner();
+ spinner.set_size_request(16, 16);
+ spinner.start();
+ spinner.show();
- if (this._eventBox.get_visible())
- this._settingsButton.hide();
+ this._settingsButton.get_child().destroy();
+ this._settingsButton.child = spinner;
},
- _onWeatherRefreshed : function(refreshed) {
- let weather = this._loader.googleXML;
-
- if (weather == null) {
- this._gtkWindow.add(this._spinnerBox);
- this._gtkWindow.show_all();
+ _onLoaderChanged : function(loader) {
+ let weather = loader.getXml();
+ let props = loader.getProps();
+ if (weather == null || props == null) {
return;
}
- if (!this._eventBox.get_visible()) {
- this._spinnerBox.destroy();
- this._gtkWindow.add(this._eventBox);
- this._gtkWindow.show_all();
- } else {
- this._settingsButton.show();
- }
-
- if (!refreshed)
- this._settingsButton.image = new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
- icon_name: 'dialog-warning-symbolic',
- halign: Gtk.Align.END });
+ this._settingsButton.get_child().destroy();
+ this._settingsButton.child = new Gtk.Image ({ icon_size: Gtk.IconSize.MENU,
+ icon_name: 'system-run-symbolic',
+ halign: Gtk.Align.END,
+ visible: true });
let iconName = this._getWeatherIcon(weather current_conditions icon data toString());
this._icon.icon_name = iconName;
@@ -369,12 +358,12 @@ MainWindow.prototype = {
this._degreeLabel.set_markup(temp + ' ' + tempUnit);
let state;
- if (this._loader.props['countrycode'] == 'US')
- state = this._loader.props['statecode'];
+ if (props['countrycode'] == 'US')
+ state = props['statecode'];
else
- state = this._loader.props['country'];
+ state = props['country'];
- let location = this._loader.props['city'] + ', ' + state;
+ let location = props['city'] + ', ' + state;
this._cityLabel.set_text(location);
let comment = weather current_conditions condition data toString();
diff --git a/tempo/weatherLoader.js b/tempo/weatherLoader.js
index c75d5ef..83d6200 100644
--- a/tempo/weatherLoader.js
+++ b/tempo/weatherLoader.js
@@ -19,6 +19,8 @@
const GOOGLE_URL = "http://www.google.com/ig/api?hl=%s&weather=%s";
+const Signals = imports.signals;
+
const Geocode = imports.gi.GeocodeGlib;
const GLib = imports.gi.GLib;
const Json = imports.gi.Json;
@@ -34,12 +36,33 @@ function WeatherLoader(location) {
WeatherLoader.prototype = {
_init : function(location) {
this._location = location;
- this.googleXML = null;
- this.props = null;
+ this._googleXml = null;
+ this._props = null;
this.loadCache();
},
+ setLocation: function(location) {
+ if (this._location == location)
+ return;
+
+ if (this._location == null ||
+ this._location == '')
+ return;
+
+ this._props = null;
+ this._location = location;
+ this._refreshWeather();
+ },
+
+ getXml: function() {
+ return this._googleXml;
+ },
+
+ getProps: function() {
+ return this._props;
+ },
+
loadCache: function(callback) {
let cachePath = GLib.get_user_cache_dir() + '/tempo/last_cache.xml';
@@ -48,46 +71,50 @@ WeatherLoader.prototype = {
let contentsStr = contents[1].toString();
if (contents[0]) {
- this.googleXML = new XML(contentsStr);
- this._resolveWeather(false, Lang.bind(this,
- function() {
- callback(true);
+ this._googleXml = new XML(contentsStr);
+
+ let geoCode = new Geocode.Object();
+ geoCode.add('location', this._location);
+
+ geoCode.resolve_async(null, Lang.bind(this,
+ function(obj, res) {
+ try {
+ let props = geoCode.resolve_finish(res);
+ this._props = props;
+
+ this.emit('changed');
+ } catch (e) {
+ log('Unable to resolve weather location ' + this._location + ': ' + e.toString());
+ }
}));
}
} catch (e) {
- Mainloop.idle_add(
- function() {
- callback(true);
- return false;
- });
+ log('Unable to load weather cache: ' + e.toString());
}
},
- _resolveWeather: function(refresh, callback) {
- this._callback = callback;
+ _refreshWeather: function(callback) {
+ this.emit('loading');
- let geoCode = new Geocode.Object();
+ if (!this._props) {
+ let geoCode = new Geocode.Object();
+ geoCode.add('location', this._location);
- geoCode.add('location', this._location);
- geoCode.resolve_async(null, Lang.bind(this,
- function(obj, res) {
- try {
- let props = geoCode.resolve_finish(res);
- this.props = props;
+ geoCode.resolve_async(null, Lang.bind(this,
+ function(obj, res) {
+ try {
+ let props = geoCode.resolve_finish(res);
+ this._props = props;
- if (refresh)
this._getWeather();
- else
- this._callback(true);
- } catch (e) {
- log('Unable to resolve weather location ' + this._location + ': ' + e.toString());
- this._callback(false);
- }
- }));
- },
-
- refreshWeather: function(callback) {
- this._resolveWeather(true, callback);
+ } catch (e) {
+ log('Unable to resolve weather location ' + this._location + ': ' + e.toString());
+ this.emit('error');
+ }
+ }));
+ } else {
+ this._getWeather();
+ }
},
_getLanguage : function() {
@@ -111,18 +138,17 @@ WeatherLoader.prototype = {
},
_onResponseReceived : function(session, message) {
- let cbval = true;
let utf8 = Weather.convert_response_to_utf8(message);
try {
- this.googleXML = new XML(utf8.substr(21)).*;
+ this._googleXml = new XML(utf8.substr(21)).*;
this._saveCache();
+
+ this.emit('changed');
} catch (e) {
log('Unable to parse the remote XML response: ' + e.toString());
- cbval = false;
+ this.emit('error');
}
-
- this._callback(cbval);
},
_saveCache: function() {
@@ -132,9 +158,10 @@ WeatherLoader.prototype = {
cachePath += '/last_cache.xml';
try {
- GLib.file_set_contents(cachePath, this.googleXML.toXMLString());
+ GLib.file_set_contents(cachePath, this._googleXml.toXMLString());
} catch (e) {
log('Unable to save cache file: ' + e.toString());
}
}
};
+Signals.addSignalMethods(WeatherLoader.prototype);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]