[gnome-weather] Rewrite as a JS application



commit 260211d1fd232cca21387c92257c872909dff13d
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sat Feb 23 01:33:09 2013 +0100

    Rewrite as a JS application
    
    JS is the language of choice for GNOME, and with this I can evaluate
    the new gjs Package framework on a real application.

 .gitmodules                               |    3 +
 Makefile.am                               |    4 +-
 autogen.sh                                |    6 ++
 configure.ac                              |   11 ++-
 data/Makefile.am                          |    6 +-
 src/ui/custom.css => data/application.css |    0
 libgd                                     |    1 +
 po/POTFILES.in                            |   13 ++--
 src/Makefile.am                           |   39 ++++------
 src/app.py                                |   90 --------------------
 src/conditions.js                         |   66 +++++++++++++++
 src/conditions.py                         |   57 -------------
 src/forecast.js                           |  126 +++++++++++++++++++++++++++++
 src/forecast.py                           |  106 ------------------------
 src/gnome-weather.in                      |   67 +---------------
 src/main.js                               |   62 ++++++++++++++
 src/strings.js                            |   62 ++++++++++++++
 src/strings.py                            |   58 -------------
 src/util.js                               |   56 +++++++++++++
 src/util.py                               |    5 -
 src/view.js                               |  113 ++++++++++++++++++++++++++
 src/widget.py                             |  126 -----------------------------
 src/window.js                             |  113 ++++++++++++++++++++++++++
 23 files changed, 644 insertions(+), 546 deletions(-)
---
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..bfd964e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "libgd"]
+       path = libgd
+       url = git://git.gnome.org/libgd
diff --git a/Makefile.am b/Makefile.am
index 09d6f10..0c2d064 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
-ACLOCAL_AMFLAGS = -I m4
+ACLOCAL_AMFLAGS = -I m4 -I libgd
 
-SUBDIRS = data src po
+SUBDIRS = po data libgd src
diff --git a/autogen.sh b/autogen.sh
index b39f842..ea09943 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -4,6 +4,7 @@
 srcdir=`dirname $0`
 test -z "$srcdir" && srcdir=.
 
+ACLOCAL_FLAGS="-I libgd ${ACLOCAL_FLAGS}"
 PKG_NAME="gnome-weather"
 
 test -f $srcdir/configure.ac || {
@@ -17,4 +18,9 @@ which gnome-autogen.sh || {
     echo "your OS vendor's package manager)."
     exit 1
 }
+
+(cd "$srcdir" ;
+test -d m4 || mkdir m4/ ;
+git submodule update --init --recursive ;
+)
 . gnome-autogen.sh
diff --git a/configure.ac b/configure.ac
index d9b1edb..1cffcb3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([config])
 
-AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign tar-ustar])
+AM_INIT_AUTOMAKE([1.11 dist-xz foreign tar-ustar -Wno-portability])
 
 m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
 
@@ -17,20 +17,21 @@ IT_PROG_INTLTOOL(0.26)
 
 AC_PROG_CC
 AM_PROG_CC_C_O
-LT_INIT
+LT_INIT([disable-static])
 
-PKG_PROG_PKG_CONFIG([0.22])
+LIBGD_INIT([header-bar revealer gir])
 
-PKG_CHECK_MODULES([GWEATHER], [gtk+-3.0 clutter-1.0 gdk-pixbuf-2.0 gio-2.0 gweather-3.0 >= 3.5.0])
+PKG_PROG_PKG_CONFIG([0.22])
 
 GLIB_GSETTINGS
 GOBJECT_INTROSPECTION_REQUIRE([1.30.0])
 
-AM_PATH_PYTHON([3.0])
+AC_PATH_PROG([GJS],[gjs])
 
 AC_CONFIG_FILES([
   Makefile
   data/Makefile
+  libgd/Makefile
   src/Makefile
   po/Makefile.in
 ])
diff --git a/data/Makefile.am b/data/Makefile.am
index 339398b..60cbca3 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,8 +1,10 @@
-appsdir = $(datadir)/applications
+cssdir = $(pkgdatadir)
+css_DATA = application.css
 
+appsdir = $(datadir)/applications
 apps_DATA = gnome-weather.desktop
 
 @INTLTOOL_DESKTOP_RULE@
 
-dist_noinst_DATA = gnome-weather.desktop.in
+EXTRA_DIST = gnome-weather.desktop.in
 CLEANFILES = $(apps_DATA)
diff --git a/src/ui/custom.css b/data/application.css
similarity index 100%
rename from src/ui/custom.css
rename to data/application.css
diff --git a/libgd b/libgd
new file mode 160000
index 0000000..006f5c8
--- /dev/null
+++ b/libgd
@@ -0,0 +1 @@
+Subproject commit 006f5c8619e98394c03e755ec1b72e2d5d908e35
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7eb67ac..926646b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,7 +1,8 @@
 data/gnome-weather.desktop.in
-src/gnome-weather.in
-src/app.py
-src/widget.py
-src/conditions.py
-src/forecast.py
-src/strings.py
+src/conditions.js
+src/forecast.js
+src/main.js
+src/strings.js
+src/util.js
+src/view.js
+src/window.js
diff --git a/src/Makefile.am b/src/Makefile.am
index 76c35d3..aab4d64 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,30 +1,21 @@
 NULL =
 
-bin_SCRIPTS = gnome-weather
+nodist_bin_SCRIPTS = gnome-weather
 
-gnome-weather: gnome-weather.in Makefile
-       $(AM_V_GEN) sed -e "s|[ ]PYTHON@|$(PYTHON)|g"   \
-                       -e "s|[ ]pkgdatadir@|$(pkgdatadir)|g" \
-                       -e "s|[ ]pkglibdir@|$(pkglibdir)|g" \
-                       -e "s|[ ]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-                       -e "s|[ ]localedir@|$(datadir)/locale|g" \
-                       $< > $@
-       @chmod +x $@
-
-pydir = $(pkglibdir)
-dist_py_PYTHON = \
-       __init__.py     \
-       app.py          \
-       util.py         \
-       widget.py       \
-       forecast.py     \
-       conditions.py   \
-       strings.py      \
+jsdir = $(pkgdatadir)
+dist_js_DATA = \
+       conditions.js   \
+       forecast.js     \
+       main.js         \
+       strings.js      \
+       util.js         \
+       view.js         \
+       window.js       \
        $(NULL)
 
-pkguidir = $(pkgdatadir)/ui
-dist_pkgui_DATA = \
-       ui/custom.css
+gnome-weather: $(top_builddir)/config.status gnome-weather.in
+       $(AM_V_GEN) cd $(top_builddir) && $(SHELL) ./config.status --file $(subdir)/$@
+       @chmod +x $@
 
-dist_noinst_DATA = gnome-weather.in
-CLEANFILES = gnome-weather
+EXTRA_DIST = gnome-weather.in
+DISTCLEANFILES = gnome-weather
diff --git a/src/conditions.js b/src/conditions.js
new file mode 100644
index 0000000..fa5726f
--- /dev/null
+++ b/src/conditions.js
@@ -0,0 +1,66 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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 ConditionsSidebar = new Lang.Class({
+    Name: 'ConditionsSidebar',
+    Extends: Gtk.Grid,
+
+    _init: function(params) {
+        params = Params.fill(params, { orientation: Gtk.Orientation.VERTICAL });
+        this.parent(params);
+        this.get_style_context().add_class('white-background');
+
+        let title = new Gtk.Label({ label: '<b>' + _("Current conditions:") + '</b>',
+                                    use_markup: true,
+                                    xalign: 0.0,
+                                    wrap: true });
+        this.add(title);
+
+        this._conditions = new Gtk.Label({ xalign: 0.0, wrap: true });
+        this.add(this._conditions);
+
+        this._temperature = new Gtk.Label({ xalign: 0.0, wrap: true });
+        this.add(this._temperature);
+
+        this._wind = new Gtk.Label({ xalign: 0.0, wrap: true });
+        this.add(this._wind);
+
+        this._attribution = new Gtk.Label({ use_markup: true,
+                                            xalign: 0.0,
+                                            wrap: true,
+                                            max_width_chars: 30 });
+        this.add(this._attribution);
+        this.show_all();
+    },
+
+    update: function(info) {
+        this._conditions.label = info.get_weather_summary();
+        this._temperature.label = _("Temperature: ") +
+            info.get_temp_summary();
+        this._wind.label = _("Wind: ") + info.get_wind();
+
+        let attr = info.get_attribution();
+        if (attr) {
+            this._attribution.label = attr;
+            this._attribution.show();
+        } else {
+            this._attribution.hide();
+        }
+    }
+});
+
diff --git a/src/forecast.js b/src/forecast.js
new file mode 100644
index 0000000..c23d851
--- /dev/null
+++ b/src/forecast.js
@@ -0,0 +1,126 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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 Strings = imports.strings;
+const Util = imports.util;
+
+// In microseconds
+const ONE_DAY = 24*3600*1000*1000;
+const SIX_HOURS = 6*3600*1000*1000;
+
+const ForecastBox = new Lang.Class({
+    Name: 'ForecastBox',
+    Extends: Gtk.Grid,
+
+    _init: function(params) {
+        params = Params.fill({ orientation: Gtk.Orientation.HORIZONTAL,
+                               column_spacing: 24,
+                               column_homogeneous: true });
+        this.parent(params);
+    },
+
+    update: function(infos) {
+        let dates = infos.map(function(i) {
+            let [ok, date] = i.get_value_update();
+            return GLib.DateTime.new_from_unix_local(date);
+        });
+
+        let subday = this._hasSubdayResolution(dates);
+
+        let current;
+        let n = 0;
+        // limit to 5 infos max
+        for (let i = 0; i < dates.length && n < 5; i++) {
+            let info = infos[i];
+
+            // only show forecasts if they're separated by
+            // at least 6 hours
+            let [ok, date] = info.get_value_update();
+            let datetime = GLib.DateTime.new_from_unix_local(date);
+            if (current && datetime.difference(current) < SIX_HOURS)
+                continue;
+
+            let text = '<b>' + this._getDate(datetime, subday) + '</b>';
+            let label = new Gtk.Label({ label: text,
+                                        use_markup: true,
+                                        visible: true });
+            this.attach(label, n, 0, 1, 1);
+
+            let image = new Gtk.Image({ icon_name: info.get_icon_name(),
+                                        icon_size: Gtk.IconSize.DIALOG,
+                                        visible: true });
+            this.attach(image, n, 1, 1, 1);
+
+            let temperature = new Gtk.Label({ label: this._getTemperature(info),
+                                              visible: true });
+            this.attach(temperature, n, 2, 1, 1);
+
+            current = datetime;
+            n++;
+        }
+    },
+
+    clear: function() {
+        this.foreach(function(w) { w.destroy(); });
+    },
+
+    _hasSubdayResolution: function(dates) {
+        if (dates.length == 1)
+            return false;
+
+        if (dates[1].difference(dates[0]) < ONE_DAY)
+            return true;
+        else
+            return false;
+    },
+
+    _getDate: function(datetime, subday) {
+        let now = GLib.DateTime.new_now_local();
+        let tomorrow = now.add_days(1);
+
+        if (Util.arrayEqual(now.get_ymd(),
+                            datetime.get_ymd())) {
+            if (subday)
+                return Strings.formatToday(datetime);
+            else
+                return _("Today");
+        } else if (Util.arrayEqual(tomorrow.get_ymd(),
+                                   datetime.get_ymd())) {
+            if (subday)
+                return Strings.formatTomorrow(datetime);
+            else
+                return _("Tomorrow");
+        } else {
+            if (subday)
+                return Strings.formatDayPart(datetime);
+            else
+                return datetime.format('%A');
+        }
+    },
+
+    _getTemperature: function(info) {
+        let [ok1, ] = info.get_value_temp_min(GWeather.TemperatureUnit.DEFAULT);
+        let [ok2, ] = info.get_value_temp_max(GWeather.TemperatureUnit.DEFAULT);
+
+        if (ok1 && ok2)
+            return _("%s / %s").format(info.get_temp_min(), info.get_temp_max());
+        else
+            return info.get_temp_summary();
+    }
+});
+
diff --git a/src/gnome-weather.in b/src/gnome-weather.in
old mode 100644
new mode 100755
index d09a392..ba55453
--- a/src/gnome-weather.in
+++ b/src/gnome-weather.in
@@ -1,66 +1,3 @@
-#! PYTHON@
+#!/bin/sh
+exec @GJS@ -c 'imports.package.start({ name: "@PACKAGE_NAME@", version: "@PACKAGE_VERSION@", prefix: 
"@prefix@" })'
 
-# Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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
-
-import sys, os, re
-import gettext
-
-def add_to_envvar(var, value):
-    if var in os.environ:
-        os.environ[var] += ':' + value
-    else:
-        os.environ[var] = value
-
-# HACK!
-# We need to remove python2 directories that are added
-# by jhbuild, or we end up loading pygobject for python 2!
-
-newpath = []
-for p in sys.path:
-    is_python2 = False
-    for d in p.split('/'):
-        if d.startswith('python2'):
-            is_python2 = True
-            break
-    if not is_python2:
-        newpath.append(p)
-
-sys.path = newpath
-
-gettext.textdomain('@GETTEXT_PACKAGE@')
-
-if os.path.exists('./src'):
-   # running from source tree
-   gettext.bindtextdomain('@GETTEXT_PACKAGE@', './po')
-   pkgdatadir = './src'
-   pkglibdir = './src'
-else:
-   gettext.bindtextdomain('@GETTEXT_PACKAGE@', '@localedir@')
-   pkgdatadir = '@pkgdatadir@'
-   pkglibdir = '@pkglibdir@'
-
-sys.path.append(pkglibdir)
-from gi.repository import GLib
-from app import GnomeWeatherApp
-
-class PathConfig:
-      PKGLIBDIR = pkglibdir
-      PKGDATADIR = pkgdatadir
-      UIDIR = pkgdatadir + '/ui'
-
-GLib.set_prgname('gnome-weather')
-sys.exit(GnomeWeatherApp(PathConfig()).run(sys.argv))
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..f09b17e
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,62 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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
+
+pkg.initSubmodule('libgd');
+pkg.initGettext();
+pkg.initFormat();
+pkg.require({ 'Clutter': '1.0',
+              'Gd': '1.0',
+              'Gdk': '3.0',
+              'GLib': '2.0',
+              'GObject': '2.0',
+              'Gtk': '3.0',
+              'GtkClutter': '1.0',
+              'GWeather': '3.0',
+              'Lang': '1.0',
+              'Mainloop': '1.0',
+              'Params': '1.0' });
+
+const Util = imports.util;
+const Window = imports.window;
+
+const Application = new Lang.Class({
+    Name: 'WeatherApplication',
+    Extends: Gtk.Application,
+
+    _init: function() {
+        this.parent({ application_id: 'org.gnome.Weather.Application' });
+        GLib.set_application_name(_("Weather"));
+    },
+
+    vfunc_startup: function() {
+        GtkClutter.init(null);
+        this.parent();
+
+        Util.loadStyleSheet();
+
+        this.world = GWeather.Location.new_world(false);
+    },
+
+    vfunc_activate: function() {
+        (new Window.MainWindow({ application: this })).show();
+    }
+});
+
+function main(argv) {
+    return (new Application()).run(argv);
+}
diff --git a/src/strings.js b/src/strings.js
new file mode 100644
index 0000000..905bc4e
--- /dev/null
+++ b/src/strings.js
@@ -0,0 +1,62 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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 Gettext = imports.gettext;
+
+const DAY_PARTS = [
+    [N_("Monday night"), N_("Monday morning"), N_("Monday afternoon"), N_("Monday evening")],
+    [N_("Tuesday night"), N_("Tuesday morning"), N_("Tuesday afternoon"), N_("Tuesday evening")],
+    [N_("Wednesday night"), N_("Wednesday morning"), N_("Wednesday afternoon"), N_("Wednesday evening")],
+    [N_("Thursday night"), N_("Thursday morning"), N_("Thursday afternoon"), N_("Thursday evening")],
+    [N_("Friday night"), N_("Friday morning"), N_("Friday afternoon"), N_("Friday evening")],
+    [N_("Saturday night"), N_("Saturday morning"), N_("Saturday afternoon"), N_("Saturday evening")],
+    [N_("Sunday night"), N_("Sunday morning"), N_("Sunday afternoon"), N_("Sunday evening")],
+];
+
+const TODAY_PARTS = [N_("This night"), N_("This morning"), N_("This afternoon"), N_("This evening")];
+const TOMORROW_PARTS = [N_("Tomorrow night"), N_("Tomorrow morning"), N_("Tomorrow afternoon"), N_("Tomorrow 
evening")];
+
+function _getDatetimePart(datetime) {
+    let h = datetime.get_hour();
+
+    if (h < 6 || h >= 21)
+        return 0;
+    else if (h < 12)
+        return 1;
+    else if (h < 18)
+        return 2;
+    else
+        return 3;
+}
+
+function formatToday(datetime) {
+    let part = _getDatetimePart(datetime);
+    return Gettext.gettext(TODAY_PARTS[part]);
+}
+
+function formatTomorrow(datetime) {
+    let part = _getDatetimePart(datetime);
+    return Gettext.gettext(TOMORROW_PARTS[part]);
+}
+
+function formatDayPart(datetime) {
+    let day = datetime.get_day_of_week() - 1;
+    let part = _getDatetimePart(datetime);
+
+    return Gettext.gettext(DAY_PARTS[day][part]);
+}
diff --git a/src/util.js b/src/util.js
new file mode 100644
index 0000000..7c9ca4d
--- /dev/null
+++ b/src/util.js
@@ -0,0 +1,56 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+//
+// Redistribution and use in source and binary forms, with or without
+//  modification, are permitted provided that the following conditions are met:
+//   * Redistributions of source code must retain the above copyright
+//     notice, this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright
+//     notice, this list of conditions and the following disclaimer in the
+//     documentation and/or other materials provided with the distribution.
+//   * Neither the name of the GNOME Foundation nor the
+//     names of its contributors may be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+function loadUI(file) {
+    file = file || 'main.ui';
+
+    let ui = new Gtk.Builder();
+    ui.add_from_file(GLib.build_filenamev([pkg.pkgdatadir,
+                                           file]));
+    return ui;
+}
+
+function loadStyleSheet(file) {
+    file = file || 'application.css';
+
+    let provider = new Gtk.CssProvider();
+    provider.load_from_path(GLib.build_filenamev([pkg.pkgdatadir,
+                                                  file]));
+    Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
+                                             provider,
+                                             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+}
+
+function arrayEqual(one, two) {
+    if (one.length != two.length)
+        return false;
+
+    for (let i = 0; i < one.length; i++)
+        if (one[i] != two[i])
+            return false;
+
+    return true;
+}
diff --git a/src/view.js b/src/view.js
new file mode 100644
index 0000000..6a10927
--- /dev/null
+++ b/src/view.js
@@ -0,0 +1,113 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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 Conditions = imports.conditions;
+const Forecast = imports.forecast;
+const Util = imports.util;
+
+const WeatherView = new Lang.Class({
+    Name: 'WeatherView',
+    Extends: GtkClutter.Embed,
+
+    _init: function(params) {
+        let filtered = Params.filter(params, { info: null });
+        params = Params.fill(params, { use_layout_size: true });
+        this.parent(params);
+
+        let iconBin = new GtkClutter.Actor();
+        iconBin.add_constraint(new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.BOTH,
+                                                             factor: 0.5,
+                                                             source: this.get_stage() }));
+        this.get_stage().add_actor(iconBin);
+
+        this._icon = new Gtk.Image({ pixel_size: 256,
+                                     visible: true });
+        iconBin.get_widget().get_style_context().add_class('white-background');
+        iconBin.get_widget().add(this._icon);
+
+        let currentBox = new GtkClutter.Actor({ margin_right:15,
+                                                margin_top:15 });
+        currentBox.add_constraint(new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.X_AXIS,
+                                                                factor: 1.0,
+                                                                source: this.get_stage() }));
+        currentBox.add_constraint(new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.Y_AXIS,
+                                                                factor: 0.0,
+                                                                source: this.get_stage() }));
+        this.get_stage().add_actor(currentBox);
+        this._conditions = new Conditions.ConditionsSidebar();
+        currentBox.get_widget().add(this._conditions);
+        currentBox.get_widget().get_style_context().add_class('white-background');
+
+        let forecastsBin = new GtkClutter.Actor({ margin_bottom: 10,
+                                                  margin_left: 5,
+                                                  margin_right: 5 });
+        forecastsBin.add_constraint(new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.Y_AXIS,
+                                                                  factor: 1.0,
+                                                                  source: this.get_stage() }));
+        forecastsBin.add_constraint(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.WIDTH,
+                                                                 source: this.get_stage() }));
+        this.get_stage().add_actor(forecastsBin);
+        this._forecasts = new Forecast.ForecastBox();
+        forecastsBin.get_widget().add(this._forecasts)
+        forecastsBin.get_widget().get_style_context().add_class('white-background')
+
+        let [ok, color] = Clutter.Color.from_string('#a0a0a0');
+        this._overlay = new Clutter.Actor({ background_color: color });
+        this._overlay.add_constraint(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.ALL,
+                                                                  source: this.get_stage() }));
+        this._overlay.show();
+        this.get_stage().add_actor(this._overlay);
+
+        this._info = filtered.info;
+        this._updateId = this._info.connect('updated',
+                                            Lang.bind(this, this._onUpdate));
+    },
+
+    vfunc_destroy: function() {
+        if (this._updateId) {
+            this._info.disconnect(this._updateId);
+            this._updateId = 0;
+        }
+
+        this.parent();
+    },
+
+    beginUpdate: function() {
+        this._forecasts.clear();
+        this._overlay.show();
+        this._overlay.opacity = 255;
+    },
+
+    _onUpdate: function(info) {
+        this._overlay.save_easing_state();
+        this._overlay.opacity = 0;
+        this._overlay.restore_easing_state();
+
+        this._conditions.update(info);
+
+        this._icon.icon_name = info.get_icon_name();
+
+        let forecasts = info.get_forecast_list();
+        if (forecasts.length > 0) {
+            this._forecasts.update(forecasts);
+            this._forecasts.show();
+        } else {
+            this._forecasts.hide();
+        }
+    }
+});
diff --git a/src/window.js b/src/window.js
new file mode 100644
index 0000000..9643dca
--- /dev/null
+++ b/src/window.js
@@ -0,0 +1,113 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni 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 View = imports.view;
+
+function makeTitle(location) {
+    let city = location;
+    if (location.get_level() == GWeather.LocationLevel.WEATHER_STATION)
+        city = location.get_parent();
+
+    let country = city.get_parent();
+    while (country &&
+           country.get_level() > GWeather.LocationLevel.COUNTRY)
+        country = country.get_parent();
+
+    if (country)
+        return _("%s, %s").format(city.get_name(), country.get_name());
+    else
+        return city.get_name();
+}
+
+const MainWindow = new Lang.Class({
+    Name: 'MainWindow',
+    Extends: Gtk.ApplicationWindow,
+    Properties: {
+        'location': GObject.ParamSpec.boxed('location', 'Location', '',
+                                            GObject.ParamFlags.READABLE |
+                                            GObject.ParamFlags.WRITABLE,
+                                            GWeather.Location),
+    },
+
+    _init: function(params) {
+        params = Params.fill(params, { default_width: 700,
+                                       default_height: 500 });
+        this.parent(params);
+
+        this._world = this.application.world;
+        this._info = new GWeather.Info({ world: this._world,
+                                         forecast_type: GWeather.ForecastType.LIST,
+                                         enabled_providers: GWeather.Provider.METAR |
+                                                            GWeather.Provider.YR_NO });
+        this._location = this._info.get_location();
+
+        let grid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL });
+
+        this._header = new Gd.HeaderBar({ title: makeTitle(this._location),
+                                          hexpand: true });
+        this._search = new Gd.HeaderToggleButton({ symbolic_icon_name: 'edit-find-symbolic' });
+        this._header.pack_end(this._search);
+        grid.add(this._header);
+
+        this._locationEntry = new GWeather.LocationEntry({ top: this._world,
+                                                           location: this._location,
+                                                           width_request: 500,
+                                                           halign: Gtk.Align.CENTER });
+        this._locationEntry.bind_property('location', this, 'location',
+                                          GObject.BindingFlags.DEFAULT);
+
+        let toolbar = new Gtk.Toolbar({ hexpand: true });
+        toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
+        let item = new Gtk.ToolItem();
+        item.set_expand(true);
+        item.add(this._locationEntry);
+        toolbar.insert(item, 0);
+
+        let revealer = new Gd.Revealer({ reveal_child: false,
+                                         child: toolbar });
+        this._search.bind_property('active', revealer, 'reveal-child',
+                                   GObject.BindingFlags.DEFAULT);
+        grid.add(revealer);
+
+        this._view = new View.WeatherView({ info: this._info,
+                                            hexpand: true,
+                                            vexpand: true });
+        grid.add(this._view);
+
+        this.add(grid);
+        grid.show_all();
+    },
+
+    get location() {
+        return this._location;
+    },
+
+    set location(l) {
+        this._location = l;
+        this._info.location = l;
+        this._header.title = makeTitle(l);
+        this.update();
+
+        this.notify('location');
+    },
+
+    update: function() {
+        this._view.beginUpdate();
+        this._info.update();
+    },
+});


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