diff options
-rw-r--r-- | include/datetime/locations-settings.h | 60 | ||||
-rw-r--r-- | include/datetime/locations.h | 71 | ||||
-rw-r--r-- | src/locations-settings.cpp | 105 | ||||
-rw-r--r-- | src/locations.cpp | 41 | ||||
-rw-r--r-- | tests/test-locations.cc | 172 |
5 files changed, 449 insertions, 0 deletions
diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h new file mode 100644 index 0000000..d343fe3 --- /dev/null +++ b/include/datetime/locations-settings.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_LOCATIONS_H +#define INDICATOR_DATETIME_SETTINGS_LOCATIONS_H + +#include <datetime/locations.h> // base class + +#include <glib.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +class Timezones; + +/** + * \brief An ordered list of Location objects found from + * the system timezone and from the user's GSettings + */ +class SettingsLocations: public Locations +{ +public: + /** + * @param[in] schemaId the settings schema to load + * @param[in] timezones the timezones to always show first in the list + */ + SettingsLocations (const std::string& schemaId, const std::shared_ptr<Timezones>& timezones); + +protected: + std::unique_ptr<GSettings,std::function<void(GSettings*)>> settings_; + std::shared_ptr<Timezones> timezones_; + +private: + static void onSettingsChanged (gpointer gself); + void reload(); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LOCATIONS_H diff --git a/include/datetime/locations.h b/include/datetime/locations.h new file mode 100644 index 0000000..a06d1cc --- /dev/null +++ b/include/datetime/locations.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DATETIME_LOCATIONS_H +#define INDICATOR_DATETIME_LOCATIONS_H + +#include <core/property.h> + +#include <string> +#include <vector> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A physical place and its timezone; eg, "America/Chicago" + "Oklahoma City" + */ +struct Location +{ + /** timezone; eg, "America/Chicago" */ + std::string zone; + + /* human-readable location name; eg, "Oklahoma City" */ + std::string name; + + /** offset from UTC in microseconds */ + int64_t offset = 0; + + bool operator== (const Location& that) const + { + return (name == that.name) && (zone == that.zone) && (offset == that.offset); + } + + Location (const std::string& zone, const std::string& name); +}; + +/** + * A container for an ordered list of Locations. + */ +class Locations +{ +public: + Locations() =default; + virtual ~Locations() =default; + + /** \brief an ordered list of Location items */ + core::Property<std::vector<Location> > locations; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_LOCATIONS_H diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp new file mode 100644 index 0000000..ed8f998 --- /dev/null +++ b/src/locations-settings.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/locations-settings.h> + +#include <datetime/settings-shared.h> +#include <datetime/timezones.h> +#include <datetime/utils.h> + +#include <algorithm> // std::find() + +namespace unity { +namespace indicator { +namespace datetime { + +SettingsLocations::SettingsLocations (const std::string& schemaId, + const std::shared_ptr<Timezones>& timezones): + timezones_(timezones) +{ + auto deleter = [&](GSettings* s){g_object_unref(s);}; + settings_ = std::unique_ptr<GSettings,std::function<void(GSettings*)>>(g_settings_new(schemaId.c_str()), deleter); + const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, + "changed::" SETTINGS_SHOW_LOCATIONS_S }; + for (int i=0, n=G_N_ELEMENTS(keys); i<n; i++) + g_signal_connect_swapped (settings_.get(), keys[i], G_CALLBACK(onSettingsChanged), this); + + timezones->timezone.changed().connect([this](const std::string&){reload();}); + timezones->timezones.changed().connect([this](const std::set<std::string>&){reload();}); + + reload(); +} + +void +SettingsLocations::onSettingsChanged (gpointer gself) +{ + static_cast<SettingsLocations*>(gself)->reload(); +} + +void +SettingsLocations::reload() +{ + std::vector<Location> v; + + // add the primary timezone first + std::string zone = timezones_->timezone.get(); + if (!zone.empty()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + v.push_back (l); + g_free (name); + } + + // add the other detected timezones + for (const auto& zone : timezones_->timezones.get()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + } + + // maybe add the user-specified locations + if (g_settings_get_boolean (settings_.get(), SETTINGS_SHOW_LOCATIONS_S)) + { + gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + + for (int i=0; user_locations[i]; i++) + { + gchar * zone; + gchar * name; + split_settings_location (user_locations[i], &zone, &name); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + g_free (zone); + } + + g_strfreev (user_locations); + } + + locations.set (v); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/locations.cpp b/src/locations.cpp new file mode 100644 index 0000000..5c3c52b --- /dev/null +++ b/src/locations.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/locations.h> + +#include <glib.h> + +namespace unity { +namespace indicator { +namespace datetime { + +Location::Location(const std::string& zone_, const std::string& name_): + zone(zone_), + name(name_) +{ + GTimeZone * gzone = g_time_zone_new (zone.c_str()); + GDateTime * gtime = g_date_time_new_now (gzone); + offset = g_date_time_get_utc_offset (gtime); + g_date_time_unref (gtime); + g_time_zone_unref (gzone); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-locations.cc b/tests/test-locations.cc new file mode 100644 index 0000000..c833bed --- /dev/null +++ b/tests/test-locations.cc @@ -0,0 +1,172 @@ + + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include <datetime/locations.h> +#include <datetime/locations-settings.h> +#include <datetime/settings-shared.h> + +#include <glib/gi18n.h> + +#include <langinfo.h> +#include <locale.h> + +using unity::indicator::datetime::Location; +using unity::indicator::datetime::Locations; +using unity::indicator::datetime::SettingsLocations; +using unity::indicator::datetime::Timezones; + +/*** +**** +***/ + +class LocationsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + GSettings * settings = nullptr; + std::shared_ptr<Timezones> timezones; + const std::string nyc = "America/New_York"; + const std::string chicago = "America/Chicago"; + + virtual void SetUp () + { + super::SetUp (); + + settings = g_settings_new (SETTINGS_INTERFACE); + const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (settings, SETTINGS_LOCATIONS_S, location_strv); + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, true); + + timezones.reset (new Timezones); + timezones->timezone.set (chicago); + timezones->timezones.set (std::set<std::string>({ nyc, chicago })); + } + + virtual void TearDown () + { + //timezones.reset (nullptr); + g_clear_object (&settings); + + super::TearDown (); + } +}; + +TEST_F (LocationsFixture, Timezones) +{ + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, false); + + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + std::vector<Location> l = locations.locations.get(); + EXPECT_EQ (2, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); +} + +TEST_F (LocationsFixture, SettingsLocations) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + std::vector<Location> l = locations.locations.get(); + EXPECT_EQ (7, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("Oklahoma City", l[3].name); + EXPECT_EQ ("America/Chicago", l[3].zone); + EXPECT_EQ ("Toronto", l[4].name); + EXPECT_EQ ("America/Toronto", l[4].zone); + EXPECT_EQ ("London", l[5].name); + EXPECT_EQ ("Europe/London", l[5].zone); + EXPECT_EQ ("Berlin", l[6].name); + EXPECT_EQ ("Europe/Berlin", l[6].zone); +} + +TEST_F (LocationsFixture, ChangeLocationStrings) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector<Location>&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (static_cast<GSettings*>(gsettings), SETTINGS_LOCATIONS_S, strv); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector<Location> l = locations.locations.get(); + EXPECT_EQ (5, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("London", l[3].name); + EXPECT_EQ ("Europe/London", l[3].zone); + EXPECT_EQ ("Berlin", l[4].name); + EXPECT_EQ ("Europe/Berlin", l[4].zone); + locations_changed = false; +} + +TEST_F (LocationsFixture, ChangeLocationVisibility) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector<Location>&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + g_settings_set_boolean (static_cast<GSettings*>(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector<Location> l = locations.locations.get(); + EXPECT_EQ (2, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); +} |