diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2014-01-14 23:07:10 -0600 |
---|---|---|
committer | Charles Kerr <charles.kerr@canonical.com> | 2014-01-14 23:07:10 -0600 |
commit | ee64bb2698adfe27e55615a8856b0e2c78ad8469 (patch) | |
tree | b8fb6e1440ac700f7f51e5c765303abb315c6399 | |
parent | 3b8833efe6ab21387b6f73b4a4ef757445801623 (diff) | |
download | ayatana-indicator-datetime-ee64bb2698adfe27e55615a8856b0e2c78ad8469.tar.gz ayatana-indicator-datetime-ee64bb2698adfe27e55615a8856b0e2c78ad8469.tar.bz2 ayatana-indicator-datetime-ee64bb2698adfe27e55615a8856b0e2c78ad8469.zip |
Function: add fully-tested ActionGroups, per-profile Menus, state object.
Form: Add code annotations/comments. Remove dead code. Use Mir style guide.
Todo: GSettings toggles, sync with new dbus-test-runner API, get GNOME Panel building again
69 files changed, 3962 insertions, 2008 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdd4a3..da97a6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ add_custom_target (cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2 ## set (CC_WARNING_ARGS " -Wall -Wshadow -Wextra -Wunused -Wformat=2 -Wno-missing-field-initializers") -set (CXX_WARNING_ARGS " -Wall -Wextra -pedantic") +set (CXX_WARNING_ARGS " -Wall -Wextra -pedantic -Wno-missing-field-initializers") include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h new file mode 100644 index 0000000..2059a4d --- /dev/null +++ b/include/datetime/actions-live.h @@ -0,0 +1,57 @@ +/* + * 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_ACTIONS_LIVE_H +#define INDICATOR_DATETIME_ACTIONS_LIVE_H + +#include <datetime/actions.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Production implentation of the Actions interface. + * + * Delegates URLs, sets the timezone via org.freedesktop.timedate1, etc. + * + * @see MockActions + */ +class LiveActions: public Actions +{ +public: + LiveActions(std::shared_ptr<State>& state): Actions(state) {} + ~LiveActions() =default; + + void open_desktop_settings(); + void open_phone_settings(); + void open_phone_clock(); + void open_phone_planner(); + void open_planner_at(const DateTime&); + void open_calendar_at(const DateTime&); + void open_appointment(const std::string& uid); + void set_location(const std::string& zone, const std::string& name); + void set_calendar_date(const DateTime&); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_H diff --git a/include/datetime/actions.h b/include/datetime/actions.h new file mode 100644 index 0000000..ed45c3c --- /dev/null +++ b/include/datetime/actions.h @@ -0,0 +1,70 @@ +/* + * 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_ACTIONS_H +#define INDICATOR_DATETIME_ACTIONS_H + +#include <datetime/date-time.h> +#include <datetime/state.h> + +#include <memory> // shared_ptr +#include <string> + +#include <gio/gio.h> // GSimpleActionGroup + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * Interface for all the actions that can be activated by users via the menu. + */ +class Actions +{ +public: + virtual void open_desktop_settings() =0; + virtual void open_phone_settings() =0; + virtual void open_phone_clock_app() =0; + virtual void open_planner() =0; + virtual void open_planner_at(const DateTime&) =0; + virtual void open_appointment(const std::string& uid) =0; + virtual void set_location(const std::string& zone, const std::string& name)=0; + virtual void set_calendar_date(const DateTime&) =0; + + GActionGroup* action_group() { return G_ACTION_GROUP(m_actions); } + std::shared_ptr<State> state() { return m_state; } + +protected: + Actions(std::shared_ptr<State>& state); + virtual ~Actions(); + +private: + std::shared_ptr<State> m_state; + GSimpleActionGroup* m_actions = nullptr; + + // we've got raw pointers in here, so disable copying + Actions(const Actions&) =delete; + Actions& operator=(const Actions&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_H diff --git a/include/datetime/appointment.h b/include/datetime/appointment.h index 098b0d0..e034c08 100644 --- a/include/datetime/appointment.h +++ b/include/datetime/appointment.h @@ -28,7 +28,9 @@ namespace indicator { namespace datetime { /** - * PODS representing a calendar appointment + * \brief Plain Old Data Structure that represents a calendar appointment. + * + * @see Planner */ struct Appointment { diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 814b29a..19a859b 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -30,30 +30,28 @@ namespace datetime { **** ***/ +/** + * \brief A clock that uses a client-provided time instead of the system time. + */ class MockClock: public Clock { public: + MockClock(const DateTime& dt): m_localtime(dt) {} + ~MockClock() =default; - MockClock(GDateTime * dt) { setLocaltime(dt); } - - ~MockClock() { - g_clear_pointer(&localtime_, g_date_time_unref); - } - - GDateTime* localtime() const { - g_assert (localtime_ != nullptr); - return g_date_time_ref(localtime_); - } + DateTime localtime() const { return m_localtime; } - void setLocaltime(GDateTime* dt) { - g_clear_pointer(&localtime_, g_date_time_unref); - localtime_ = g_date_time_ref(dt); + void set_localtime(const DateTime& dt) { + const auto old_day = m_localtime.day_of_year(); + m_localtime = dt; skewDetected(); + const auto new_day = m_localtime.day_of_year(); + if (old_day != new_day) + dateChanged(); } private: - - GDateTime * localtime_ = nullptr; + DateTime m_localtime; }; } // namespace datetime diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 978be27..c8e6c16 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -20,12 +20,12 @@ #ifndef INDICATOR_DATETIME_CLOCK_H #define INDICATOR_DATETIME_CLOCK_H +#include <datetime/date-time.h> #include <datetime/timezones.h> #include <core/property.h> #include <core/signal.h> -#include <glib.h> #include <gio/gio.h> #include <set> @@ -45,9 +45,10 @@ class Clock { public: virtual ~Clock(); - virtual GDateTime* localtime() const = 0; - core::Property<std::set<std::string> > timezones; + virtual DateTime localtime() const =0; + core::Property<std::set<std::string>> timezones; core::Signal<> skewDetected; + core::Signal<> dateChanged; protected: Clock(); @@ -56,12 +57,13 @@ private: static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_system_bus = nullptr; + unsigned int m_sleep_subscription_id = 0; + + // we've got raw pointers in here, so disable copying Clock(const Clock&) =delete; Clock& operator=(const Clock&) =delete; - - GCancellable * cancellable_ = nullptr; - GDBusConnection * system_bus_ = nullptr; - unsigned int sleep_subscription_id_ = 0; }; /*** @@ -71,16 +73,17 @@ private: /** * \brief A live clock that provides the actual system time. * - * Adds another clock skew detection test: wakes up every - * skewTestIntervalSec seconds to see how much time has passed - * since the last time it checked. + * This subclass also adds another clock skew detection test: + * it wakes up every skewTestIntervalSec seconds to see how + * much time has passed since the last wakeup. If the answer + * isn't what it expected, the skewDetected signal is triggered. */ class LiveClock: public Clock { public: LiveClock (const std::shared_ptr<Timezones>& zones); virtual ~LiveClock(); - virtual GDateTime* localtime() const; + virtual DateTime localtime() const; core::Property<unsigned int> skewTestIntervalSec; private: diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index bbcd00a..5b9421f 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -22,6 +22,7 @@ #include <glib.h> // GDateTime +#include <ctime> // time_t #include <memory> // std::shared_ptr namespace unity { @@ -29,61 +30,111 @@ namespace indicator { namespace datetime { /** - * C++ wrapper class for GDateTime + * \brief A simple C++ wrapper for GDateTime to simplify ownership/refcounts */ class DateTime { public: - GDateTime* get() const - { - return dt_.get(); + explicit DateTime(GDateTime* in=nullptr) { reset(in); } + + explicit DateTime(time_t t) { reset(g_date_time_new_from_unix_local(t)); } + + static DateTime NowLocal() { + GDateTime * gdt = g_date_time_new_now_local(); + DateTime dt(gdt); + g_date_time_unref(gdt); + return dt; + } + + DateTime to_timezone(const std::string& zone) const { + auto gtz = g_time_zone_new(zone.c_str()); + auto gdt = g_date_time_to_timezone(get(), gtz); + DateTime dt(gdt); + g_time_zone_unref(gtz); + g_date_time_unref(gdt); + return dt; } - GDateTime* operator()() const - { + + GDateTime* get() const { + g_assert(m_dt); + return m_dt.get(); + } + + GDateTime* operator()() const { return get(); } - void set (GDateTime* in) { - auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; - dt_ = std::shared_ptr<GDateTime>(g_date_time_ref(in), deleter); + + std::string format(const std::string& fmt) const { + auto str = g_date_time_format(get(), fmt.c_str()); + std::string ret = str; + g_free(str); + return ret; + } + + int day_of_month() const { return g_date_time_get_day_of_month(get()); } + + int64_t to_unix() const { return g_date_time_to_unix(get()); } + + int day_of_year() const { return m_dt ? g_date_time_get_day_of_year(get()) : -1; } + + void reset(GDateTime* in=nullptr) { + if (in) { + auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; + m_dt = std::shared_ptr<GDateTime>(g_date_time_ref(in), deleter); + g_assert(m_dt); + } else { + m_dt.reset(); + } } - DateTime& operator=(GDateTime* in) - { - set (in); + DateTime& operator=(GDateTime* in) { + reset(in); return *this; } - DateTime& operator=(const DateTime& in) - { - set (in.get()); + DateTime& operator=(const DateTime& in) { + m_dt = in.m_dt; return *this; } - bool operator<(const DateTime& that) const - { - return g_date_time_compare (get(), that.get()) < 0; + gint64 difference(const DateTime& that) const { + const auto dt = get(); + const auto tdt = that.get(); + + gint64 ret; + if (dt && tdt) + ret = g_date_time_difference(dt, tdt); + else if (dt) + ret = to_unix(); + else if (tdt) + ret = that.to_unix(); + else + ret = 0; + return ret; + } + + bool operator<(const DateTime& that) const { + return g_date_time_compare(get(), that.get()) < 0; } - bool operator!=(const DateTime& that) const - { - return !(*this == that); + bool operator!=(const DateTime& that) const { + // return true if this isn't set, or if it's not equal + return (!m_dt) || !(*this == that); } - bool operator==(const DateTime& that) const - { + bool operator==(const DateTime& that) const { GDateTime * dt = get(); GDateTime * tdt = that.get(); if (!dt && !tdt) return true; if (!dt || !tdt) return false; - return g_date_time_compare (get(), that.get()) == 0; + return g_date_time_compare(get(), that.get()) == 0; } private: - - std::shared_ptr<GDateTime> dt_; + std::shared_ptr<GDateTime> m_dt; }; } // namespace datetime diff --git a/include/datetime/dbus-shared.h b/include/datetime/dbus-shared.h index 24319e3..c5ff6ab 100644 --- a/include/datetime/dbus-shared.h +++ b/include/datetime/dbus-shared.h @@ -1,24 +1,25 @@ /* -An indicator to show date and time information. + * 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: + * Ted Gould <ted@canonical.com> + * Charles Kerr <charles.kerr@canonical.com> + */ -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould <ted@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/>. -*/ #define BUS_NAME "com.canonical.indicator.datetime" + #define BUS_PATH "/com/canonical/indicator/datetime" diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 66dc212..09ed035 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -37,7 +37,26 @@ class DateTime; ***/ /** - * \brief Provides the right time format strings based on the profile and user's settings + * \brief Provide the strftime() format strings + * + * This mission's been moved out into its own class because there are + * a lot of options and edge cases: + * + * - The default time format can change based on the locale. + * + * - The user's settings can change or completely override the format string. + * + * - The time formats are different on the Phone and Desktop profiles. + * + * - The time format string in the Locations' menuitems uses (mostly) + * the same time format as the header, except for some changes. + * + * - The 'current time' format string in the Locations' menuitems also + * prepends the string 'Yesterday' or 'Today' if it differs from the + * local time, so Formatter needs to have a Clock for its state. + * + * So the Formatter monitors system settings, the current timezone, etc. + * and upate its time format properties appropriately. */ class Formatter { @@ -51,7 +70,8 @@ public: /** \brief Signal to denote when the relativeFormat has changed. When this is emitted, clients will want to rebuild their - menuitems that contain relative time strings */ + menuitems that contain relative time strings + (ie, the Appointments and Locations menuitems) */ core::Signal<> relativeFormatChanged; /** \brief Generate a relative time format for some time (or time range) @@ -60,19 +80,18 @@ public: std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; protected: - Formatter(const std::shared_ptr<Clock>&); virtual ~Formatter(); /** \brief Returns true if the current locale prefers 12h display instead of 24h */ static bool is_locale_12h(); - static const char * getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); + static const char* getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); /** \brief Translate the string based on LC_TIME instead of LC_MESSAGES. The intent of this is to let users set LC_TIME to override their other locale settings when generating time format string */ - static const char * T_(const char * fmt); + static const char* T_(const char * fmt); private: diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index d343fe3..eaabf73 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -32,8 +32,8 @@ namespace datetime { class Timezones; /** - * \brief An ordered list of Location objects found from - * the system timezone and from the user's GSettings + * \brief Settings implentation which builds its list from the + * user's GSettings and from the Timezones passed in the ctor. */ class SettingsLocations: public Locations { @@ -42,11 +42,12 @@ 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); + 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_; + std::unique_ptr<GSettings,std::function<void(GSettings*)>> m_settings; + std::shared_ptr<Timezones> m_timezones; private: static void onSettingsChanged (gpointer gself); diff --git a/include/datetime/locations.h b/include/datetime/locations.h index a06d1cc..ee67615 100644 --- a/include/datetime/locations.h +++ b/include/datetime/locations.h @@ -20,6 +20,8 @@ #ifndef INDICATOR_DATETIME_LOCATIONS_H #define INDICATOR_DATETIME_LOCATIONS_H +#include <datetime/date-time.h> + #include <core/property.h> #include <string> @@ -31,28 +33,42 @@ namespace datetime { /** * \brief A physical place and its timezone; eg, "America/Chicago" + "Oklahoma City" + * + * @see Locations */ -struct Location +class Location { - /** timezone; eg, "America/Chicago" */ - std::string zone; - - /* human-readable location name; eg, "Oklahoma City" */ - std::string name; +public: + const std::string& zone() const { return m_zone; } - /** offset from UTC in microseconds */ - int64_t offset = 0; + const std::string& name() const { return m_name; } bool operator== (const Location& that) const { - return (name == that.name) && (zone == that.zone) && (offset == that.offset); + return (name() == that.name()) && + (zone() == that.zone()) && + (m_offset == that.m_offset); } Location (const std::string& zone, const std::string& name); + +private: + + /** timezone; eg, "America/Chicago" */ + std::string m_zone; + + /* human-readable location name; eg, "Oklahoma City" */ + std::string m_name; + + /** offset from UTC in microseconds */ + int64_t m_offset = 0; }; /** - * A container for an ordered list of Locations. + * Container which holds an ordered list of Locations + * + * @see Location + * @see State */ class Locations { @@ -61,7 +77,7 @@ public: virtual ~Locations() =default; /** \brief an ordered list of Location items */ - core::Property<std::vector<Location> > locations; + core::Property<std::vector<Location>> locations; }; } // namespace datetime diff --git a/include/datetime/menu.h b/include/datetime/menu.h new file mode 100644 index 0000000..0bc3781 --- /dev/null +++ b/include/datetime/menu.h @@ -0,0 +1,82 @@ +/* + * 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_MENU_H +#define INDICATOR_DATETIME_MENU_H + +#include <datetime/actions.h> +#include <datetime/state.h> + +#include <memory> // std::shared_ptr +#include <vector> + +#include <gio/gio.h> // GMenuModel + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A menu for a specific profile; eg, Desktop or Phone. + * + * @see MenuFactory + */ +class Menu +{ +public: + enum Profile { Desktop, DesktopGreeter, Phone, PhoneGreeter, NUM_PROFILES }; + enum Section { Calendar, Appointments, Locations, Settings, NUM_SECTIONS }; + const std::string& name() const { return m_name; } + Profile profile() const { return m_profile; } + GMenuModel* menu_model() { return G_MENU_MODEL(m_menu); } + +protected: + Menu (Profile profile_in, const std::string& name_in): m_profile(profile_in), m_name(name_in) {} + virtual ~Menu() =default; + GMenu* m_menu = nullptr; + +private: + const Profile m_profile; + const std::string m_name; + + // we've got raw pointers in here, so disable copying + Menu(const Menu&) =delete; + Menu& operator=(const Menu&) =delete; +}; + +/** + * \brief Builds a Menu for a given state and profile + */ +class MenuFactory +{ +public: + MenuFactory (std::shared_ptr<Actions>& actions, std::shared_ptr<State>& state); + std::shared_ptr<Menu> buildMenu(Menu::Profile profile); + std::shared_ptr<State> state() { return m_state; } + +private: + std::shared_ptr<Actions> m_actions; + std::shared_ptr<State> m_state; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_MENU_H diff --git a/include/datetime/planner-eds.h b/include/datetime/planner-eds.h index 43f222e..f3abce0 100644 --- a/include/datetime/planner-eds.h +++ b/include/datetime/planner-eds.h @@ -39,7 +39,7 @@ public: private: class Impl; - std::unique_ptr<Impl> impl_; + std::unique_ptr<Impl> p; }; } // namespace datetime diff --git a/include/datetime/planner-mock.h b/include/datetime/planner-mock.h new file mode 100644 index 0000000..bb3ff53 --- /dev/null +++ b/include/datetime/planner-mock.h @@ -0,0 +1,44 @@ +/* + * 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_PLANNER_MOCK_H +#define INDICATOR_DATETIME_PLANNER_MOCK_H + +#include <datetime/planner.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Planner which does nothing on its own and requires + * its client to set its appointments property. + */ +class MockPlanner: public Planner +{ +public: + MockPlanner() =default; + virtual ~MockPlanner() =default; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_MOCK_H diff --git a/include/datetime/planner.h b/include/datetime/planner.h index 4d27a2b..198b6fa 100644 --- a/include/datetime/planner.h +++ b/include/datetime/planner.h @@ -33,6 +33,9 @@ namespace datetime { /** * \brief Simple appointment book + * + * @see EdsPlanner + * @see State */ class Planner { diff --git a/include/datetime/service.h b/include/datetime/service.h new file mode 100644 index 0000000..c7171b7 --- /dev/null +++ b/include/datetime/service.h @@ -0,0 +1,72 @@ +/* + * 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_EXPORTER_H +#define INDICATOR_DATETIME_EXPORTER_H + +#include <datetime/menu.h> + +#include <core/signal.h> + +#include <gio/gio.h> // GActionGroup + +#include <memory> // std::shared_ptr +#include <vector> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Exports actions and menus to DBus. + */ +class Service +{ +public: + Service() =default; + ~Service(); + + core::Signal<> name_lost; + + void publish (GActionGroup* actions, std::vector<std::shared_ptr<Menu>>& menus); + +private: + static void on_bus_acquired(GDBusConnection*, const gchar *name, gpointer gthis); + void on_bus_acquired(GDBusConnection*, const gchar *name); + + static void on_name_lost(GDBusConnection*, const gchar *name, gpointer gthis); + void on_name_lost(GDBusConnection*, const gchar *name); + + std::set<guint> m_exported_menu_ids; + guint m_own_id = 0; + guint m_exported_actions_id = 0; + GDBusConnection * m_dbus_connection = nullptr; + GActionGroup* m_actions = nullptr; + std::vector<std::shared_ptr<Menu>> m_menus; + + // we've got raw pointers and gsignal tags in here, so disable copying + Service(const Service&) =delete; + Service& operator=(const Service&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_EXPORTER_H diff --git a/include/datetime/settings-live.h b/include/datetime/settings-live.h new file mode 100644 index 0000000..44e27b0 --- /dev/null +++ b/include/datetime/settings-live.h @@ -0,0 +1,57 @@ +/* + * 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_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include <datetime/settings.h> // parent class + +#include <gio/gio.h> // GSettings + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief #Settings implementation which uses GSettings. + */ +class LiveSettings: public Settings +{ +public: + LiveSettings(); + virtual ~LiveSettings(); + +private: + void update_show_clock(); + + void update_key(const std::string& key); + static void on_changed(GSettings*, gchar*, gpointer); + + GSettings* m_settings; + + // we've got a raw pointer here, so disable copying + LiveSettings(const LiveSettings&) =delete; + LiveSettings& operator=(const LiveSettings&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H diff --git a/include/datetime/settings-shared.h b/include/datetime/settings-shared.h index 896db95..17a8ef0 100644 --- a/include/datetime/settings-shared.h +++ b/include/datetime/settings-shared.h @@ -18,8 +18,8 @@ * Charles Kerr <charles.kerr@canonical.com> */ -#ifndef __DATETIME_SETTINGS_SHARED_H__ -#define __DATETIME_SETTINGS_SHARED_H__ +#ifndef INDICATOR_DATETIME_SETTINGS_SHARED +#define INDICATOR_DATETIME_SETTINGS_SHARED typedef enum { @@ -46,4 +46,4 @@ TimeFormatMode; #define SETTINGS_LOCATIONS_S "locations" #define SETTINGS_TIMEZONE_NAME_S "timezone-name" -#endif /* __DATETIME_SETTINGS_SHARED_H__ */ +#endif // INDICATOR_DATETIME_SETTINGS_SHARED diff --git a/include/datetime/settings.h b/include/datetime/settings.h new file mode 100644 index 0000000..3d4bc33 --- /dev/null +++ b/include/datetime/settings.h @@ -0,0 +1,63 @@ +/* + * 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_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include <datetime/settings-shared.h> + +#include <core/property.h> + +#include <vector> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Interface that represents user-configurable settings. + * + * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml + * For more information. + */ +class Settings +{ +public: + Settings() =default; + virtual ~Settings() =default; + + core::Property<TimeFormatMode> time_format_mode; + core::Property<bool> show_clock; + core::Property<bool> show_day; + core::Property<bool> show_year; + core::Property<bool> show_seconds; + core::Property<std::string> custom_time_format; + core::Property<bool> show_calendar; + core::Property<bool> show_events; + core::Property<bool> show_locations; + core::Property<bool> show_auto_detected_location; + core::Property<std::vector<std::string>> locations; + core::Property<std::string> timezone_name; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_H diff --git a/include/datetime/state.h b/include/datetime/state.h new file mode 100644 index 0000000..a82bb4b --- /dev/null +++ b/include/datetime/state.h @@ -0,0 +1,68 @@ +/* + * 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_STATE_H +#define INDICATOR_DATETIME_STATE_H + +#include <datetime/clock.h> +#include <datetime/timezones.h> +#include <datetime/planner.h> +#include <datetime/locations.h> + +#include <core/property.h> + +#include <memory> // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Aggregates all the classes that represent the backend state. + * + * This is where the app comes together. It's a model that aggregates + * all of the backend appointments/alarms, locations, timezones, + * system time, and so on. The "view" code (ie, the Menus) need to + * respond to Signals from the State and update themselves accordingly. + * + * @see Menu + * @see MenuFactory + * @see Timezones + * @see Clock + * @see Planner + * @see Locations + */ +struct State +{ + std::shared_ptr<Timezones> timezones; + std::shared_ptr<Clock> clock; + std::shared_ptr<Planner> planner; + std::shared_ptr<Locations> locations; + + core::Property<bool> show_events; + core::Property<bool> show_clock; + core::Property<DateTime> calendar_day; + core::Property<bool> show_week_numbers; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_STATE_H diff --git a/include/datetime/timezone-file.h b/include/datetime/timezone-file.h index 39a3d83..7f47df6 100644 --- a/include/datetime/timezone-file.h +++ b/include/datetime/timezone-file.h @@ -43,15 +43,18 @@ public: private: void setFilename(const std::string& filename); - void clear(); static void onFileChanged(gpointer gself); + void clear(); void reload(); - std::string filename_; - GFileMonitor * monitor_ = nullptr; - unsigned long monitor_handler_id_ = 0; -}; + std::string m_filename; + GFileMonitor * m_monitor = nullptr; + unsigned long m_monitor_handler_id = 0; + // we have raw pointers and glib tags in here, so disable copying + FileTimezone(const FileTimezone&) =delete; + FileTimezone& operator=(const FileTimezone&) =delete; +}; } // namespace datetime } // namespace indicator diff --git a/include/datetime/timezone-geoclue.h b/include/datetime/timezone-geoclue.h index 382b3cc..4a5b726 100644 --- a/include/datetime/timezone-geoclue.h +++ b/include/datetime/timezone-geoclue.h @@ -50,10 +50,14 @@ private: void setTimezoneFromAddressVariant (GVariant*); static GVariant * call_finish (GObject*, GAsyncResult*); - GCancellable * cancellable_ = nullptr; - GDBusConnection * connection_ = nullptr; - std::string client_object_path_; - guint signal_subscription_ = 0; + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_connection = nullptr; + std::string m_client_object_path; + guint m_signal_subscription = 0; + + // we've got pointers and gsignal tags in here, so don't allow copying + GeoclueTimezone(const GeoclueTimezone&) =delete; + GeoclueTimezone& operator=(const GeoclueTimezone&) =delete; }; diff --git a/include/datetime/timezone.h b/include/datetime/timezone.h index 218e219..ffa5a84 100644 --- a/include/datetime/timezone.h +++ b/include/datetime/timezone.h @@ -28,11 +28,11 @@ namespace unity { namespace indicator { namespace datetime { -/** \brief Base class for objects that use various means to detect the system's timezone */ +/** \brief Base a timezone, such as "America/Chicago". */ class Timezone { protected: - Timezone() {} + Timezone() =default; public: //virtual ~Timezone() {} diff --git a/include/datetime/timezones-live.h b/include/datetime/timezones-live.h index 3075bd8..5f2cb3e 100644 --- a/include/datetime/timezones-live.h +++ b/include/datetime/timezones-live.h @@ -31,7 +31,7 @@ namespace indicator { namespace datetime { /** - * \brief Timezones object that uses a #FileTimezone and #GeoclueTimezone + * \brief #Timezones object that uses a #FileTimezone and #GeoclueTimezone * to detect what timezone we're in */ class LiveTimezones: public Timezones @@ -40,13 +40,14 @@ public: LiveTimezones(const std::string& filename); /** \brief Whether or not to track location by IP address */ - core::Property<bool> geolocationEnabled = core::Property<bool>(false); + core::Property<bool> geolocation_enabled = core::Property<bool>(false); private: - FileTimezone file_; - std::shared_ptr<GeoclueTimezone> geo_; - void updateGeolocation(); - void updateTimezones(); + void update_geolocation(); + void update_timezones(); + + FileTimezone m_file; + std::shared_ptr<GeoclueTimezone> m_geo; }; } // namespace datetime diff --git a/include/datetime/timezones.h b/include/datetime/timezones.h index 0b97683..10c4e97 100644 --- a/include/datetime/timezones.h +++ b/include/datetime/timezones.h @@ -28,13 +28,11 @@ namespace unity { namespace indicator { namespace datetime { -/** \brief Aggregates one or more timezone detectors and decides which to use */ +/** \brief Aggregates one or more timezone detectors and decides which to give precedence to */ class Timezones { public: - Timezones() =default; - virtual ~Timezones() =default; /** diff --git a/include/datetime/utils.h b/include/datetime/utils.h index bd2e132..fbc80d7 100644 --- a/include/datetime/utils.h +++ b/include/datetime/utils.h @@ -20,8 +20,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __DATETIME_UTILS_H__ -#define __DATETIME_UTILS_H__ +#ifndef INDICATOR_DATETIME_UTILS_H +#define INDICATOR_DATETIME_UTILS_H #include <glib.h> #include <gio/gio.h> /* GSettings */ @@ -37,31 +37,9 @@ void split_settings_location (const char * location, gchar * get_current_zone_name (const char * location, GSettings * settings); -#if 0 -gchar* join_date_and_time_format_strings (const char * date_fmt, - const char * time_fmt); -/*** -**** -***/ - -const gchar * get_terse_time_format_string (GDateTime * time); - -const gchar * get_terse_header_time_format_string (void); - -const gchar * get_full_time_format_string (GSettings * settings); - -gchar * generate_terse_format_string_at_time (GDateTime * now, - GDateTime * time); - -gchar * generate_full_format_string (gboolean show_day, - gboolean show_date, - gboolean show_year, - GSettings * settings); -#endif - gchar * generate_full_format_string_at_time (GDateTime * now, GDateTime * time); G_END_DECLS -#endif +#endif /* INDICATOR_DATETIME_UTILS_H */ diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 4ac3289..c165326 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -2,6 +2,8 @@ set (PANEL_LIB "indicator-datetime") add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") + add_library (${PANEL_LIB} SHARED datetime-prefs.c datetime-prefs-locations.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c847ff..976adc3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,13 +12,18 @@ if (BUILD_PANEL) endif () add_library (${SERVICE_LIB} STATIC + actions.cpp + actions-live.cpp clock.cpp clock-live.cpp formatter.cpp formatter-desktop.cpp locations.cpp locations-settings.cpp + menu.cpp planner-eds.cpp + service.cpp + settings-live.cpp timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp @@ -27,9 +32,9 @@ include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) -#add_executable (${SERVICE_EXEC} main.c) -#target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) -#install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) +add_executable (${SERVICE_EXEC} main.cpp) +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) # common properties #set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} diff --git a/src/actions-live.cpp b/src/actions-live.cpp new file mode 100644 index 0000000..08e1466 --- /dev/null +++ b/src/actions-live.cpp @@ -0,0 +1,84 @@ +/* + * 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/actions-live.h> + +#include <glib.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +void LiveActions::open_desktop_settings() +{ + g_message ("%s", G_STRFUNC); +} + +void LiveActions::open_phone_settings() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_clock() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_planner() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_planner_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_calendar_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_appointment(const std::string& uid) +{ + g_message("%s - %s", G_STRFUNC, uid.c_str()); +} + +void LiveActions::set_location(const std::string& zone, const std::string& name) +{ + g_message("%s - %s %s", G_STRFUNC, zone.c_str(), name.c_str()); +} + +void LiveActions::set_calendar_date(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/actions.cpp b/src/actions.cpp new file mode 100644 index 0000000..4efe950 --- /dev/null +++ b/src/actions.cpp @@ -0,0 +1,215 @@ +/* + * 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/actions.h> +#include <datetime/utils.h> // split_settings_location() + +#include <glib.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +namespace +{ + +void on_desktop_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_desktop_settings(); +} + +void on_phone_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_phone_settings(); +} + +void on_phone_clock_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_phone_clock_app(); +} + +void on_activate_appointment(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto uid = g_variant_get_string(param, nullptr); + auto self = static_cast<Actions*>(gself); + + g_return_if_fail(uid && *uid); + + // find url of the upcoming appointment with this uid + for (auto& appt : self->state()->planner->upcoming.get()) + { + if (appt.uid == uid) + { + const auto url = appt.url; + g_debug("%s: uid[%s] -> url[%s]", G_STRFUNC, uid, url.c_str()); + self->open_appointment(url); + break; + } + } +} + +void on_activate_planner(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto at = g_variant_get_int64(param); + auto self = static_cast<Actions*>(gself); + + if (at) + { + auto gdt = g_date_time_new_from_unix_local(at); + self->open_planner_at(DateTime(gdt)); + g_date_time_unref(gdt); + } + else // no time specified... + { + self->open_planner(); + } +} + +void on_set_location(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + char * zone; + char * name; + split_settings_location(g_variant_get_string(param, nullptr), &zone, &name); + static_cast<Actions*>(gself)->set_location(zone, name); + g_free(name); + g_free(zone); +} + +static void on_calendar_activated(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) +{ + const time_t t = g_variant_get_int64(state); + + g_return_if_fail(t != 0); + + static_cast<Actions*>(gself)->set_calendar_date(DateTime(t)); +} + +GVariant* create_default_header_state() +{ + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string("accessible-desc")); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string("label")); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string("title")); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(true)); + return g_variant_builder_end(&b); +} + +GVariant* create_calendar_state(std::shared_ptr<State>& state) +{ + gboolean days[32] = { 0 }; + for(const auto& appt : state->planner->thisMonth.get()) + days[appt.begin.day_of_month()] = true; + + GVariantBuilder day_builder; + g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); + for (int i=0; i<G_N_ELEMENTS(days); i++) + if (days[i]) + g_variant_builder_add(&day_builder, "i", i); + + GVariantBuilder dict_builder; + g_variant_builder_init(&dict_builder, G_VARIANT_TYPE_DICTIONARY); + + auto key = "appointment-days"; + auto v = g_variant_builder_end(&day_builder); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + key = "calendar-day"; + v = g_variant_new_int64(state->calendar_day.get().to_unix()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + key = "show-week-numbers"; + v = g_variant_new_boolean(state->show_week_numbers.get()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + return g_variant_builder_end(&dict_builder); +} +} // anonymous namespace + +/*** +**** +***/ + +Actions::Actions(std::shared_ptr<State>& state): + m_state(state), + m_actions(g_simple_action_group_new()) +{ + GActionEntry entries[] = { + { "activate-desktop-settings", on_desktop_settings_activated }, + { "activate-phone-settings", on_phone_settings_activated }, + { "activate-phone-clock-app", on_phone_clock_activated }, + { "activate-appointment", on_activate_appointment, "s", nullptr }, + { "activate-planner", on_activate_planner, "x", nullptr }, + { "set-location", on_set_location, "s" } + }; + + g_action_map_add_action_entries(G_ACTION_MAP(m_actions), + entries, + G_N_ELEMENTS(entries), + this); + + // add the header actions + auto gam = G_ACTION_MAP(m_actions); + auto v = create_default_header_state(); + auto a = g_simple_action_new_stateful("desktop-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("desktop-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + + // add the calendar action + v = create_calendar_state(state); + a = g_simple_action_new_stateful("calendar", G_VARIANT_TYPE_INT64, v); + g_action_map_add_action(gam, G_ACTION(a)); + g_signal_connect(a, "activate", G_CALLBACK(on_calendar_activated), this); + //m_calendar_action = a; + + // FIXME: rebuild the calendar state when show-week-number changes +} + +Actions::~Actions() +{ + g_clear_object(&m_actions); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 80e91a3..1fadfe8 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -28,53 +28,56 @@ class LiveClock::Impl public: Impl(LiveClock& owner, const std::shared_ptr<Timezones>& tzd): - owner_(owner), - timezones_(tzd) + m_owner(owner), + m_timezones(tzd) { - if (timezones_) + if (m_timezones) { - timezones_->timezone.changed().connect ([this](const std::string& z) {setTimezone(z);}); - setTimezone(timezones_->timezone.get()); + m_timezones->timezone.changed().connect([this](const std::string& z) {setTimezone(z);}); + setTimezone(m_timezones->timezone.get()); } - owner_.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); - setInterval(owner_.skewTestIntervalSec.get()); + m_owner.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); + setInterval(m_owner.skewTestIntervalSec.get()); } ~Impl() { clearTimer(); - g_clear_pointer (&timezone_, g_time_zone_unref); + g_clear_pointer(&m_timezone, g_time_zone_unref); } - GDateTime* localtime() const + DateTime localtime() const { - g_assert (timezone_ != nullptr); + g_assert(m_timezone != nullptr); - return g_date_time_new_now (timezone_); + auto gdt = g_date_time_new_now(m_timezone); + DateTime ret(gdt); + g_date_time_unref(gdt); + return ret; } private: - void setTimezone (const std::string& str) + void setTimezone(const std::string& str) { - g_clear_pointer (&timezone_, g_time_zone_unref); - timezone_= g_time_zone_new (str.c_str()); - owner_.skewDetected(); + g_clear_pointer(&m_timezone, g_time_zone_unref); + m_timezone= g_time_zone_new(str.c_str()); + m_owner.skewDetected(); } private: void clearTimer() { - if (skew_timeout_id_) + if (m_skew_timeout_id) { - g_source_remove(skew_timeout_id_); - skew_timeout_id_ = 0; + g_source_remove(m_skew_timeout_id); + m_skew_timeout_id = 0; } - g_clear_pointer(&prev_datetime_, g_date_time_unref); + m_prev_datetime.reset(); } void setInterval(unsigned int seconds) @@ -83,50 +86,53 @@ private: if (seconds > 0) { - prev_datetime_ = owner_.localtime(); - skew_timeout_id_ = g_timeout_add_seconds(seconds, onTimerPulse, this); + m_prev_datetime = localtime(); + m_skew_timeout_id = g_timeout_add_seconds(seconds, onTimerPulse, this); } } static gboolean onTimerPulse(gpointer gself) { - auto self = static_cast<Impl*>(gself); + static_cast<Impl*>(gself)->onTimerPulse(); + return G_SOURCE_CONTINUE; + } + void onTimerPulse() + { // check to see if too much time passed since the last check */ - GDateTime * now = self->owner_.localtime(); - const GTimeSpan diff = g_date_time_difference(now, self->prev_datetime_); + const auto now = localtime(); + const auto diff = now.difference (m_prev_datetime); const GTimeSpan fuzz = 5; - const GTimeSpan max = (self->owner_.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; + const GTimeSpan max = (m_owner.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; if (abs(diff) > max) - self->owner_.skewDetected(); + m_owner.skewDetected(); - // update prev_datetime - g_clear_pointer(&self->prev_datetime_, g_date_time_unref); - self->prev_datetime_ = now; + // check to see if the day has changed + if (now.day_of_year() != m_prev_datetime.day_of_year()) + m_owner.dateChanged(); - return G_SOURCE_CONTINUE; + // update m_prev_datetime + m_prev_datetime = now; } protected: - LiveClock& owner_; - GTimeZone * timezone_ = nullptr; - std::shared_ptr<Timezones> timezones_; + LiveClock& m_owner; + GTimeZone * m_timezone = nullptr; + std::shared_ptr<Timezones> m_timezones; - GDateTime * prev_datetime_ = nullptr; - unsigned int skew_timeout_id_ = 0; - unsigned int sleep_subscription_id_ = 0; + DateTime m_prev_datetime; + unsigned int m_skew_timeout_id = 0; }; LiveClock::LiveClock(const std::shared_ptr<Timezones>& tzd): - p (new Impl (*this, tzd)) + p(new Impl(*this, tzd)) { } LiveClock::~LiveClock() =default; -GDateTime * -LiveClock::localtime() const +DateTime LiveClock::localtime() const { return p->localtime(); } diff --git a/src/clock.cpp b/src/clock.cpp index 4a75ceb..f3f5d70 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -31,9 +31,9 @@ namespace datetime { ***/ Clock::Clock(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SYSTEM, cancellable_, onSystemBusReady, this); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); timezones.changed().connect([this](const std::set<std::string>& timezones){ g_message ("timezones changed... new count is %d", (int)timezones.size()); @@ -43,13 +43,13 @@ Clock::Clock(): Clock::~Clock() { - g_cancellable_cancel(cancellable_); - g_clear_object(&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (sleep_subscription_id_) - g_dbus_connection_signal_unsubscribe(system_bus_ , sleep_subscription_id_); + if (m_sleep_subscription_id) + g_dbus_connection_signal_unsubscribe(m_system_bus, m_sleep_subscription_id); - g_clear_object(&system_bus_); + g_clear_object(&m_system_bus); } void @@ -61,9 +61,9 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) { auto self = static_cast<Clock*>(gself); - self->system_bus_ = system_bus; + self->m_system_bus = system_bus; - self->sleep_subscription_id_ = g_dbus_connection_signal_subscribe( + self->m_sleep_subscription_id = g_dbus_connection_signal_subscribe( system_bus, nullptr, "org.freedesktop.login1.Manager", // interface @@ -78,13 +78,13 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) } void -Clock::onPrepareForSleep(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters G_GNUC_UNUSED, - gpointer gself) +Clock::onPrepareForSleep(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { static_cast<Clock*>(gself)->skewDetected(); } diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 8390967..3f942f4 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -35,165 +35,167 @@ class DesktopFormatter::Impl { public: - Impl (DesktopFormatter * owner, const std::shared_ptr<Clock>& clock): - owner_(owner), - clock_(clock), - settings_(g_settings_new(SETTINGS_INTERFACE)) - { - const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, - "changed::" SETTINGS_SHOW_DAY_S, - "changed::" SETTINGS_SHOW_DATE_S, - "changed::" SETTINGS_SHOW_YEAR_S }; - for (guint i=0, n=G_N_ELEMENTS(keys); i<n; i++) - g_signal_connect(settings_, keys[i], G_CALLBACK(onSettingsChanged), this); - - rebuildHeaderFormat(); - } +Impl(DesktopFormatter * owner, const std::shared_ptr<Clock>& clock): + m_owner(owner), + m_clock(clock), + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, + "changed::" SETTINGS_SHOW_DAY_S, + "changed::" SETTINGS_SHOW_DATE_S, + "changed::" SETTINGS_SHOW_YEAR_S }; + for(const auto& key : keys) + g_signal_connect(m_settings, key, G_CALLBACK(onSettingsChanged), this); + + rebuildHeaderFormat(); +} - ~Impl() - { - g_signal_handlers_disconnect_by_data (settings_, this); - g_object_unref (settings_); - } +~Impl() +{ + g_signal_handlers_disconnect_by_data(m_settings, this); + g_object_unref(m_settings); +} private: - static void onSettingsChanged (GSettings * changed G_GNUC_UNUSED, - const gchar * key G_GNUC_UNUSED, - gpointer gself) - { - static_cast<Impl*>(gself)->rebuildHeaderFormat(); - } +static void onSettingsChanged(GSettings * /*changed*/, + const gchar * /*key*/, + gpointer gself) +{ + static_cast<Impl*>(gself)->rebuildHeaderFormat(); +} - void rebuildHeaderFormat() - { - gchar * fmt = getHeaderLabelFormatString (settings_); - owner_->headerFormat.set(fmt); - g_free (fmt); - } +void rebuildHeaderFormat() +{ + auto fmt = getHeaderLabelFormatString(m_settings); + m_owner->headerFormat.set(fmt); + g_free(fmt); +} private: - gchar* getHeaderLabelFormatString (GSettings * s) const - { - char * fmt; - const TimeFormatMode mode = (TimeFormatMode) g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S); - - if (mode == TIME_FORMAT_MODE_CUSTOM) - { - fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S); - } - else - { - const bool show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S); - const bool show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S); - const bool show_year = show_date && g_settings_get_boolean (s, SETTINGS_SHOW_YEAR_S); - const char * date_fmt = getDateFormat (show_day, show_date, show_year); - const char * time_fmt = getFullTimeFormatString (s); - fmt = joinDateAndTimeFormatStrings (date_fmt, time_fmt); - } - - return fmt; - } +gchar* getHeaderLabelFormatString(GSettings* s) const +{ + char * fmt; + const auto mode = g_settings_get_enum(s, SETTINGS_TIME_FORMAT_S); - const gchar* T_(const gchar* in) const + if (mode == TIME_FORMAT_MODE_CUSTOM) { - return owner_->T_(in); + fmt = g_settings_get_string(s, SETTINGS_CUSTOM_TIME_FORMAT_S); } - - const gchar* getDateFormat (bool show_day, bool show_date, bool show_year) const + else { - const char * fmt; - - if (show_day && show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ - fmt = T_("%a %b %e %Y"); - else if (show_day && show_date) - fmt = T_("%a %b %e"); - else if (show_day && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ - fmt = T_("%a %Y"); - else if (show_day) - /* TRANSLATORS: a strftime(3) format showing the weekday. */ - fmt = T_("%a"); - else if (show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the date and year */ - fmt = T_("%b %e %Y"); - else if (show_date) - /* TRANSLATORS: a strftime(3) format showing the date */ - fmt = T_("%b %e"); - else if (show_year) - /* TRANSLATORS: a strftime(3) format showing the year */ - fmt = T_("%Y"); - else - fmt = nullptr; - - return fmt; + const auto show_day = g_settings_get_boolean(s, SETTINGS_SHOW_DAY_S); + const auto show_date = g_settings_get_boolean(s, SETTINGS_SHOW_DATE_S); + const auto show_year = show_date && g_settings_get_boolean(s, SETTINGS_SHOW_YEAR_S); + const auto date_fmt = getDateFormat(show_day, show_date, show_year); + const auto time_fmt = getFullTimeFormatString(s); + fmt = joinDateAndTimeFormatStrings(date_fmt, time_fmt); } - const gchar * getFullTimeFormatString (GSettings * settings) const - { - const bool show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); + return fmt; +} + +const gchar* T_(const gchar* in) const +{ + return m_owner->T_(in); +} - bool twelvehour; - switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; +const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const +{ + const char * fmt; + + if (show_day && show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ + fmt = T_("%a %b %e %Y"); + else if (show_day && show_date) + /* TRANSLATORS: a strftime(3) format showing the weekday and date */ + fmt = T_("%a %b %e"); + else if (show_day && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ + fmt = T_("%a %Y"); + else if (show_day) + /* TRANSLATORS: a strftime(3) format showing the weekday. */ + fmt = T_("%a"); + else if (show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the date and year */ + fmt = T_("%b %e %Y"); + else if (show_date) + /* TRANSLATORS: a strftime(3) format showing the date */ + fmt = T_("%b %e"); + else if (show_year) + /* TRANSLATORS: a strftime(3) format showing the year */ + fmt = T_("%Y"); + else + fmt = nullptr; + + return fmt; +} - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = FALSE; - break; +const gchar* getFullTimeFormatString(GSettings* settings) const +{ + auto show_seconds = g_settings_get_boolean(settings, SETTINGS_SHOW_SECONDS_S); + + bool twelvehour; + switch (g_settings_get_enum(settings, SETTINGS_TIME_FORMAT_S)) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; - default: - twelvehour = TRUE; - break; - } + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = false; + break; - return owner_->getDefaultHeaderTimeFormat (twelvehour, show_seconds); + default: + twelvehour = true; + break; } - gchar* joinDateAndTimeFormatStrings (const char * date_string, const char * time_string) const + return m_owner->getDefaultHeaderTimeFormat(twelvehour, show_seconds); +} + +gchar* joinDateAndTimeFormatStrings(const char* date_string, + const char* time_string) const +{ + gchar * str; + + if (date_string && time_string) + { + /* TRANSLATORS: This is a format string passed to strftime to + * combine the date and the time. The value of "%s\u2003%s" + * will result in a string like this in US English 12-hour time: + * 'Fri Jul 16 11:50 AM'. The space in between date and time is + * a Unicode en space (E28082 in UTF-8 hex). */ + str = g_strdup_printf("%s\u2003%s", date_string, time_string); + } + else if (date_string) + { + str = g_strdup(date_string); + } + else // time_string { - gchar * str; - - if (date_string && time_string) - { - /* TRANSLATORS: This is a format string passed to strftime to combine the - * date and the time. The value of "%s\u2003%s" will result in a - * string like this in US English 12-hour time: 'Fri Jul 16 11:50 AM'. - * The space in between date and time is a Unicode en space - * (E28082 in UTF-8 hex). */ - str = g_strdup_printf ("%s\u2003%s", date_string, time_string); - } - else if (date_string) - { - str = g_strdup (date_string); - } - else // time_string - { - str = g_strdup (time_string); - } - - return str; + str = g_strdup(time_string); } + return str; +} + private: - DesktopFormatter * const owner_; - std::shared_ptr<Clock> clock_; - GSettings * settings_; +DesktopFormatter * const m_owner; +std::shared_ptr<Clock> m_clock; +GSettings * m_settings; }; /*** **** ***/ -DesktopFormatter::DesktopFormatter (const std::shared_ptr<Clock>& clock): +DesktopFormatter::DesktopFormatter(const std::shared_ptr<Clock>& clock): Formatter(clock), p(new Impl(this, clock)) { diff --git a/src/formatter.cpp b/src/formatter.cpp index 4e2f582..1f26cc7 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -30,16 +30,16 @@ namespace { -void clearTimer (guint& tag) +void clearTimer(guint& tag) { if (tag) { - g_source_remove (tag); + g_source_remove(tag); tag = 0; } } -guint calculate_milliseconds_until_next_minute (GDateTime * now) +guint calculate_milliseconds_until_next_minute(GDateTime * now) { GDateTime * next; GDateTime * start_of_next; @@ -47,12 +47,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) guint interval_msec; next = g_date_time_add_minutes(now, 1); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), - 0.1); + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), + 0.1); interval_usec = g_date_time_difference(start_of_next, now); interval_msec = (interval_usec + 999) / 1000; @@ -62,12 +62,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) return interval_msec; } -gint calculate_milliseconds_until_next_second (GDateTime * now) +gint calculate_milliseconds_until_next_second(GDateTime * now) { gint interval_usec; guint interval_msec; - interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now); + interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond(now); interval_msec = (interval_usec + 999) / 1000; return interval_msec; } @@ -94,7 +94,7 @@ gint calculate_milliseconds_until_next_second (GDateTime * now) * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45. */ -guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) +guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) { char * str; gint minute; @@ -106,23 +106,22 @@ guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) minute = g_date_time_get_minute(now); minute = 15 - (minute % 15); next = g_date_time_add_minutes(now, minute); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), 0.1); str = g_date_time_format(start_of_next, "%F %T"); - g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); - g_free (str); + g_debug("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); + g_free(str); diff = g_date_time_difference(start_of_next, now); seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND; g_date_time_unref(start_of_next); g_date_time_unref(next); - return seconds; } } // anonymous namespace @@ -137,11 +136,12 @@ class Formatter::Impl { public: - Impl (Formatter* owner, const std::shared_ptr<Clock>& clock): - owner_(owner), - clock_(clock) + Impl(Formatter* owner, const std::shared_ptr<Clock>& clock): + m_owner(owner), + m_clock(clock) { - owner_->headerFormat.changed().connect([this](const std::string& fmt G_GNUC_UNUSED){updateHeader();}); + m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){updateHeader();}); + m_clock->skewDetected.connect([this](){updateHeader();}); updateHeader(); restartRelativeTimer(); @@ -149,30 +149,25 @@ public: ~Impl() { - clearTimer(header_timer_); + clearTimer(m_header_timer); } private: void updateHeader() { - GDateTime * now = clock_->localtime(); - const time_t unix = g_date_time_to_unix (now); - struct tm tm; - localtime_r (&unix, &tm); - char str[512]; - strftime (str, sizeof(str), owner_->headerFormat.get().c_str(), &tm); - owner_->header.set (str); - g_date_time_unref (now); + const auto fmt = m_owner->headerFormat.get(); + const auto str = m_clock->localtime().format(fmt); + m_owner->header.set(str); restartHeaderTimer(); } void restartHeaderTimer() { - clearTimer(header_timer_); + clearTimer(m_header_timer); - const std::string fmt = owner_->headerFormat.get(); + const std::string fmt = m_owner->headerFormat.get(); const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) || (fmt.find("%S") != std::string::npos) || (fmt.find("%T") != std::string::npos) @@ -180,17 +175,17 @@ private: || (fmt.find("%c") != std::string::npos); guint interval_msec; - GDateTime * now = clock_->localtime(); + const auto now = m_clock->localtime(); + auto str = now.format("%F %T"); if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second (now); + interval_msec = calculate_milliseconds_until_next_second(now.get()); else - interval_msec = calculate_milliseconds_until_next_minute (now); - g_date_time_unref (now); + interval_msec = calculate_milliseconds_until_next_minute(now.get()); interval_msec += 50; // add a small margin to ensure the callback // fires /after/ next is reached - header_timer_ = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + m_header_timer = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); } static gboolean onHeaderTimer(gpointer gself) @@ -203,29 +198,28 @@ private: void restartRelativeTimer() { - clearTimer(relative_timer_); + clearTimer(m_relative_timer); - GDateTime * now = clock_->localtime(); - const guint seconds = calculate_seconds_until_next_fifteen_minutes(now); - relative_timer_ = g_timeout_add_seconds(seconds, onRelativeTimer, this); - g_date_time_unref(now); + const auto now = m_clock->localtime(); + const auto seconds = calculate_seconds_until_next_fifteen_minutes(now.get()); + m_relative_timer = g_timeout_add_seconds(seconds, onRelativeTimer, this); } static gboolean onRelativeTimer(gpointer gself) { auto self = static_cast<Formatter::Impl*>(gself); - self->owner_->relativeFormatChanged(); + self->m_owner->relativeFormatChanged(); self->restartRelativeTimer(); return G_SOURCE_REMOVE; } private: - Formatter * const owner_; - guint header_timer_ = 0; - guint relative_timer_ = 0; + Formatter * const m_owner; + guint m_header_timer = 0; + guint m_relative_timer = 0; public: - std::shared_ptr<Clock> clock_; + std::shared_ptr<Clock> m_clock; }; /*** @@ -233,7 +227,7 @@ public: ***/ Formatter::Formatter(const std::shared_ptr<Clock>& clock): - p (new Formatter::Impl(this, clock)) + p(new Formatter::Impl(this, clock)) { } @@ -244,12 +238,11 @@ Formatter::~Formatter() bool Formatter::is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + const auto t_fmt = nl_langinfo(T_FMT); - for (i=0; formats_24h[i]; ++i) - if (strstr (t_fmt, formats_24h[i])) + for (const auto& needle : formats_24h) + if (strstr(t_fmt, needle)) return false; return true; @@ -272,7 +265,7 @@ Formatter::T_(const char *msg) */ char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const char *time_locale = setlocale (LC_TIME, nullptr); + const char *time_locale = setlocale(LC_TIME, nullptr); char *language = g_strdup(g_getenv("LANGUAGE")); const char *rv; @@ -329,15 +322,15 @@ typedef enum } date_proximity_t; -date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) +date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) { date_proximity_t prox = DATE_PROXIMITY_FAR; gint now_year, now_month, now_day; gint time_year, time_month, time_day; // does it happen today? - g_date_time_get_ymd (now, &now_year, &now_month, &now_day); - g_date_time_get_ymd (time, &time_year, &time_month, &time_day); + g_date_time_get_ymd(now, &now_year, &now_month, &now_day); + g_date_time_get_ymd(time, &time_year, &time_month, &time_day); if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) prox = DATE_PROXIMITY_TODAY; @@ -347,12 +340,12 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * tomorrow; gint tom_year, tom_month, tom_day; - tomorrow = g_date_time_add_days (now, 1); - g_date_time_get_ymd (tomorrow, &tom_year, &tom_month, &tom_day); + tomorrow = g_date_time_add_days(now, 1); + g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day); if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) prox = DATE_PROXIMITY_TOMORROW; - g_date_time_unref (tomorrow); + g_date_time_unref(tomorrow); } // does it happen this week? @@ -361,17 +354,17 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * week; GDateTime * week_bound; - week = g_date_time_add_days (now, 6); - week_bound = g_date_time_new_local (g_date_time_get_year(week), - g_date_time_get_month (week), - g_date_time_get_day_of_month(week), - 23, 59, 59.9); + week = g_date_time_add_days(now, 6); + week_bound = g_date_time_new_local(g_date_time_get_year(week), + g_date_time_get_month(week), + g_date_time_get_day_of_month(week), + 23, 59, 59.9); - if (g_date_time_compare (time, week_bound) <= 0) + if (g_date_time_compare(time, week_bound) <= 0) prox = DATE_PROXIMITY_WEEK; - g_date_time_unref (week_bound); - g_date_time_unref (week); + g_date_time_unref(week_bound); + g_date_time_unref(week); } return prox; @@ -398,12 +391,12 @@ std::string Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const { std::string ret; - GDateTime * now = p->clock_->localtime(); + auto now = p->m_clock->localtime().get(); if (then != nullptr) { - const bool full_day = then_end && (g_date_time_difference (then_end, then) >= G_TIME_SPAN_DAY); - const auto prox = getDateProximity (now, then); + const bool full_day = then_end && (g_date_time_difference(then_end, then) >= G_TIME_SPAN_DAY); + const auto prox = getDateProximity(now, then); if (full_day) { @@ -440,14 +433,13 @@ Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const then the time should be followed by its timezone. */ if ((then_end != nullptr) && (!full_day) && - ((g_date_time_get_utc_offset (now) != g_date_time_get_utc_offset (then)))) + ((g_date_time_get_utc_offset(now) != g_date_time_get_utc_offset(then)))) { ret += ' '; - ret += g_date_time_get_timezone_abbreviation (then); + ret += g_date_time_get_timezone_abbreviation(then); } } - g_date_time_unref (now); return ret; } diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index ed8f998..646a360 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,16 +29,16 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations (const std::string& schemaId, - const std::shared_ptr<Timezones>& timezones): - timezones_(timezones) +SettingsLocations::SettingsLocations(const std::string& schemaId, + const std::shared_ptr<Timezones>& timezones): + m_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); + auto deleter = [](GSettings* s){g_object_unref(s);}; + m_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); + for (const auto& key : keys) + g_signal_connect_swapped(m_settings.get(), key, G_CALLBACK(onSettingsChanged), this); timezones->timezone.changed().connect([this](const std::string&){reload();}); timezones->timezones.changed().connect([this](const std::set<std::string>&){reload();}); @@ -47,7 +47,7 @@ SettingsLocations::SettingsLocations (const std::string& schemaId, } void -SettingsLocations::onSettingsChanged (gpointer gself) +SettingsLocations::onSettingsChanged(gpointer gself) { static_cast<SettingsLocations*>(gself)->reload(); } @@ -56,48 +56,49 @@ void SettingsLocations::reload() { std::vector<Location> v; + auto settings = m_settings.get(); // add the primary timezone first - std::string zone = timezones_->timezone.get(); + auto zone = m_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); + gchar * name = get_current_zone_name(zone.c_str(), settings); + Location l(zone, name); + v.push_back(l); + g_free(name); } // add the other detected timezones - for (const auto& zone : timezones_->timezones.get()) + for (const auto& zone : m_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); + gchar * name = get_current_zone_name(zone.c_str(), settings); + 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)) + if (g_settings_get_boolean(settings, SETTINGS_SHOW_LOCATIONS_S)) { - gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + gchar ** user_locations = g_settings_get_strv(settings, 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); + 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); + g_strfreev(user_locations); } - locations.set (v); + locations.set(v); } } // namespace datetime diff --git a/src/locations.cpp b/src/locations.cpp index 5c3c52b..d6ab73a 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -26,16 +26,39 @@ namespace indicator { namespace datetime { Location::Location(const std::string& zone_, const std::string& name_): - zone(zone_), - name(name_) + m_zone(zone_), + m_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); + auto gzone = g_time_zone_new (zone().c_str()); + auto gtime = g_date_time_new_now (gzone); + m_offset = g_date_time_get_utc_offset (gtime); g_date_time_unref (gtime); g_time_zone_unref (gzone); } +#if 0 +DateTime Location::localtime(const DateTime& reference_point) const +{ +GDateTime * g_date_time_to_timezone (GDateTime *datetime, + GTimeZone *tz); + auto gzone = g_time_zone_new(zone().c_str()); + const auto gtime = reference_point.get(); + auto glocal = g_date_time_new (gzone, + g_date_time_get_year(gtime), + g_date_time_get_month(gtime), + g_date_time_get_day_of_month(gtime), + g_date_time_get_hour(gtime), + g_date_time_get_minute(gtime), + g_date_time_get_seconds(gtime)); + DateTime local(glocal); + g_date_time_unref(glocal); + g_message("reference: %zu", (size_t)reference_point.to_unix(), (size_t)local.to_unix()); + //g_date_time_unref(gtime); + g_time_zone_unref(gzone); + return local; +} +#endif + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 868d41b..0000000 --- a/src/main.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 <locale.h> -#include <stdlib.h> /* exit() */ - -#include <glib/gi18n.h> -#include <gio/gio.h> -#include <libnotify/notify.h> - -#include "clock-live.h" -#include "planner-eds.h" -#include "service.h" - -/*** -**** -***/ - -static void -on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) -{ - g_message ("exiting: service couldn't acquire or lost ownership of busname"); - - g_main_loop_quit ((GMainLoop*)loop); -} - -int -main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) -{ - IndicatorDatetimeClock * clock; - IndicatorDatetimePlanner * planner; - IndicatorDatetimeService * service; - GMainLoop * loop; - - /* Work around a deadlock in glib's type initialization. It can be - * removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is - * fixed. - */ - g_type_ensure (G_TYPE_DBUS_CONNECTION); - - /* boilerplate i18n */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - /* init libnotify */ - if (!notify_init ("indicator-datetime-service")) - g_critical ("libnotify initialization failed"); - - /* create the service */ - clock = indicator_datetime_clock_live_new (); - planner = indicator_datetime_planner_eds_new (); - service = indicator_datetime_service_new (clock, planner); - - /* run */ - loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST, - G_CALLBACK(on_name_lost), loop); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - /* cleanup */ - g_object_unref (service); - g_object_unref (planner); - g_object_unref (clock); - return 0; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c246296 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,81 @@ +/* + * 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 <datetime/clock.h> +#include <datetime/formatter.h> +#include <datetime/locations-settings.h> +#include <datetime/menu.h> +#include <datetime/planner-eds.h> +#include <datetime/service.h> +#include <datetime/settings-shared.h> +#include <datetime/state.h> +#include <datetime/timezones-live.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <libnotify/notify.h> + +#include <iostream> + +#include <locale.h> +#include <stdlib.h> /* exit() */ + +using namespace unity::indicator::datetime; + +int +main(int /*argc*/, char** /*argv*/) +{ + // Work around a deadlock in glib's type initialization. + // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. + g_type_ensure(G_TYPE_DBUS_CONNECTION); + + // boilerplate i18n + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain(GETTEXT_PACKAGE); + + // init libnotify + if(!notify_init("indicator-datetime-service")) + g_critical("libnotify initialization failed"); + + // build the menu factory + GActionGroup * actions = G_ACTION_GROUP(g_simple_action_group_new()); + std::shared_ptr<Timezones> timezones(new LiveTimezones(TIMEZONE_FILE)); + std::shared_ptr<Clock> clock(new LiveClock(timezones)); + std::shared_ptr<Planner> planner(new PlannerEds); + std::shared_ptr<Locations> locations(new SettingsLocations(SETTINGS_INTERFACE, timezones)); + planner->time = clock->localtime(); + MenuFactory factory(actions, timezones, clock, planner, locations); + + // create the menus + std::vector<std::shared_ptr<Menu>> menus; + menus.push_back(factory.buildMenu(MenuFactory::Desktop)); + + // export them + auto loop = g_main_loop_new(nullptr, false); + Service service; + service.name_lost.connect([loop](){ + g_message("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit(loop); + }); + service.publish(actions, menus); + g_main_loop_run(loop); + g_main_loop_unref(loop); + return 0; +} diff --git a/src/menu.cpp b/src/menu.cpp new file mode 100644 index 0000000..76306cc --- /dev/null +++ b/src/menu.cpp @@ -0,0 +1,518 @@ +/* + * 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/menu.h> + +#include <datetime/clock.h> +#include <datetime/formatter.h> +#include <datetime/locations.h> +#include <datetime/planner.h> +#include <datetime/state.h> +#include <datetime/timezones.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +#define ALARM_CLOCK_ICON_NAME "alarm-clock" +#define CALENDAR_ICON_NAME "calendar" + +class MenuImpl: public Menu +{ +protected: + MenuImpl(const Menu::Profile profile_in, + const std::string& name_in, + std::shared_ptr<State>& state, + std::shared_ptr<Actions>& actions, + std::shared_ptr<Formatter> formatter): + Menu(profile_in, name_in), + m_state(state), + m_actions(actions), + m_formatter(formatter) + { + // initialize the menu + create_gmenu(); + for (int i=0; i<NUM_SECTIONS; i++) + update_section(Section(i)); + + // listen for state changes so we can update the menu accordingly + m_formatter->header.changed().connect([this](const std::string&){ + update_header(); + }); + m_formatter->headerFormat.changed().connect([this](const std::string&){ + update_section(Locations); // need to update x-canonical-time-format + }); + m_formatter->relativeFormatChanged.connect([this](){ + update_section(Appointments); // uses formatter.getRelativeFormat() + update_section(Locations); // uses formatter.getRelativeFormat() + }); + m_state->show_clock.changed().connect([this](bool){ + update_header(); // update header's label + update_section(Locations); // locations' relative time may have changed + }); + m_state->show_events.changed().connect([this](bool){ + update_section(Appointments); // showing events got toggled + }); + m_state->planner->upcoming.changed().connect([this](const std::vector<Appointment>&){ + update_section(Appointments); // "upcoming" is the list of Appointments we show + }); + m_state->clock->dateChanged.connect([this](){ + update_section(Calendar); // need to update the Date menuitem + update_section(Locations); // locations' relative time may have changed + }); + m_state->locations->locations.changed().connect([this](const std::vector<Location>&) { + update_section(Locations); // "locations" is the list of Locations we show + }); + } + + virtual ~MenuImpl() + { + g_clear_object(&m_menu); + g_clear_pointer(&m_serialized_alarm_icon, g_variant_unref); + g_clear_pointer(&m_serialized_calendar_icon, g_variant_unref); + } + + virtual GVariant* create_header_state() =0; + + void update_header() + { + auto action_group = m_actions->action_group(); + auto action_name = name() + "-header"; + auto state = create_header_state(); + g_action_group_change_action_state(action_group, action_name.c_str(), state); + } + + std::shared_ptr<State> m_state; + std::shared_ptr<Actions> m_actions; + std::shared_ptr<Formatter> m_formatter; + GMenu* m_submenu = nullptr; + + GVariant* get_serialized_alarm_icon() + { + if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(ALARM_CLOCK_ICON_NAME); + m_serialized_alarm_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_alarm_icon; + } + +private: + + GVariant* get_serialized_calendar_icon() + { + if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(CALENDAR_ICON_NAME); + m_serialized_calendar_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_calendar_icon; + } + + void create_gmenu() + { + g_assert(m_submenu == nullptr); + + m_submenu = g_menu_new(); + + // build placeholders for the sections + for(int i=0; i<NUM_SECTIONS; i++) + { + GMenuItem * item = g_menu_item_new(nullptr, nullptr); + g_menu_append_item(m_submenu, item); + g_object_unref(item); + } + + // add submenu to the header + const auto detailed_action = std::string("indicator.") + name() + "-header"; + auto header = g_menu_item_new(nullptr, detailed_action.c_str()); + g_menu_item_set_attribute(header, "x-canonical-type", "s", + "com.canonical.indicator.root"); + g_menu_item_set_submenu(header, G_MENU_MODEL(m_submenu)); + g_object_unref(m_submenu); + + // add header to the menu + m_menu = g_menu_new(); + g_menu_append_item(m_menu, header); + g_object_unref(header); + } + + GMenuModel* create_calendar_section(Profile profile) + { + const bool allow_activation = (profile == Desktop) + || (profile == Phone); + const bool show_calendar = (profile == Desktop) + || (profile == DesktopGreeter); + auto menu = g_menu_new(); + + // add a menuitem that shows the current date + auto label = m_state->clock->localtime().format(_("%A, %e %B %Y")); + auto item = g_menu_item_new (label.c_str(), nullptr); + auto v = get_serialized_calendar_icon(); + g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, v); + if (allow_activation) + { + v = g_variant_new_int64(0); + const char* action = "indicator.activate-planner"; + g_menu_item_set_action_and_target_value (item, action, v); + } + g_menu_append_item(menu, item); + g_object_unref(item); + + // add calendar + if (show_calendar) + { + item = g_menu_item_new ("[calendar]", NULL); + v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value (item, "indicator.calendar", v); + g_menu_item_set_attribute (item, "x-canonical-type", + "s", "com.canonical.indicator.calendar"); + if (allow_activation) + { + g_menu_item_set_attribute (item, "activation-action", + "s", "indicator.activate-planner"); + } + g_menu_append_item (menu, item); + g_object_unref (item); + } + + return G_MENU_MODEL(menu); + } + + void add_appointments(GMenu* menu, Profile profile) + { + int n = 0; + const int MAX_APPTS = 5; + std::set<std::string> added; + + for (const auto& appt : m_state->planner->upcoming.get()) + { + if (n++ >= MAX_APPTS) + break; + + if (added.count(appt.uid)) + continue; + + added.insert(appt.uid); + + GDateTime* begin = appt.begin(); + GDateTime* end = appt.end(); + auto fmt = m_formatter->getRelativeFormat(begin, end); + auto unix_time = g_date_time_to_unix(begin); + + auto menu_item = g_menu_item_new (appt.summary.c_str(), nullptr); + g_menu_item_set_attribute (menu_item, "x-canonical-time", "x", unix_time); + g_menu_item_set_attribute (menu_item, "x-canonical-time-format", "s", fmt.c_str()); + + if (appt.has_alarms) + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.alarm"); + g_menu_item_set_attribute_value(menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + } + else + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.appointment"); + + if (!appt.color.empty()) + g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt.color.c_str()); + } + + if (profile == Phone) + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-appointment", + g_variant_new_string (appt.uid.c_str())); + else + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-planner", + g_variant_new_int64 (unix_time)); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + } + } + + GMenuModel* create_appointments_section(Profile profile) + { + auto menu = g_menu_new(); + + if (((profile==Phone) || (profile==Desktop)) && m_state->show_events.get()) + { + add_appointments (menu, profile); + + // add the 'Add Event…' menuitem + auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); + const gchar* action_name = "indicator.activate_planner"; + auto v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value(menu_item, action_name, v); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_locations_section(Profile profile) + { + GMenu* menu = g_menu_new(); + + if (profile == Desktop) + { + const auto now = m_state->clock->localtime(); + + for(const auto& location : m_state->locations->locations.get()) + { + const auto& zone = location.zone(); + const auto& name = location.name(); + const auto zone_now = now.to_timezone(zone); + const auto fmt = m_formatter->getRelativeFormat(zone_now.get()); + auto detailed_action = g_strdup_printf("indicator.set-location::%s %s", zone.c_str(), name.c_str()); + auto i = g_menu_item_new (name.c_str(), detailed_action); + g_menu_item_set_attribute(i, "x-canonical-type", "s", "com.canonical.indicator.location"); + g_menu_item_set_attribute(i, "x-canonical-timezone", "s", zone.c_str()); + g_menu_item_set_attribute(i, "x-canonical-time-format", "s", fmt.c_str()); + g_menu_append_item (menu, i); + g_object_unref(i); + g_free(detailed_action); + } + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_settings_section(Profile profile) + { + auto menu = g_menu_new(); + + if (profile == Desktop) + { + g_menu_append (menu, _("Date & Time Settings…"), "indicator.activate-desktop-settings"); + } + else if (profile == Phone) + { + g_menu_append (menu, _("Time & Date settings…"), "indicator.activate-phone-settings"); + } + + return G_MENU_MODEL (menu); + } + + void update_section(Section section) + { + GMenuModel * model; + const auto p = profile(); + + switch (section) + { + case Calendar: model = create_calendar_section(p); break; + case Appointments: model = create_appointments_section(p); break; + case Locations: model = create_locations_section(p); break; + case Settings: model = create_settings_section(p); break; + default: g_warn_if_reached(); + } + + if (model) + { + g_menu_remove(m_submenu, section); + g_menu_insert_section(m_submenu, section, nullptr, model); + g_object_unref(model); + } + } + +//private: + GVariant * m_serialized_alarm_icon = nullptr; + GVariant * m_serialized_calendar_icon = nullptr; + +}; // class MenuImpl + + + +/*** +**** +***/ + +class DesktopBaseMenu: public MenuImpl +{ +protected: + DesktopBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr<Formatter>(new DesktopFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + const auto visible = m_state->show_clock.get(); + const auto title = _("Date and Time"); + auto label = g_variant_new_string(m_formatter->header.get().c_str()); + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", label); + g_variant_builder_add(&b, "{sv}", "label", label); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(title)); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(visible)); + return g_variant_builder_end(&b); + } +}; + +class DesktopMenu: public DesktopBaseMenu +{ +public: + DesktopMenu(std::shared_ptr<State>& state_, std::shared_ptr<Actions>& actions_): + DesktopBaseMenu(Desktop,"desktop", state_, actions_) {} +}; + +class DesktopGreeterMenu: public DesktopBaseMenu +{ +public: + DesktopGreeterMenu(std::shared_ptr<State>& state_, std::shared_ptr<Actions>& actions_): + DesktopBaseMenu(DesktopGreeter,"desktop-greeter", state_, actions_) {} +}; + +class PhoneBaseMenu: public MenuImpl +{ +protected: + PhoneBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr<Formatter>(new PhoneFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + // are there alarms? + bool has_alarms = false; + for(const auto& appointment : m_state->planner->upcoming.get()) + if((has_alarms = appointment.has_alarms)) + break; + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string (_("Upcoming"))); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); + if (has_alarms) + { + auto label = m_formatter->header.get(); + auto a11y = g_strdup_printf(_("%s (has alarms)"), label.c_str()); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(label.c_str())); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_take_string(a11y)); + g_variant_builder_add(&b, "{sv}", "icon", get_serialized_alarm_icon()); + } + else + { + auto v = g_variant_new_string(m_formatter->header.get().c_str()); + g_variant_builder_add(&b, "{sv}", "label", v); + g_variant_builder_add(&b, "{sv}", "accessible-desc", v); + } + return g_variant_builder_end (&b); + } +}; + +class PhoneMenu: public PhoneBaseMenu +{ +public: + PhoneMenu(std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + PhoneBaseMenu(Phone, "phone", state_, actions_) {} +}; + +class PhoneGreeterMenu: public PhoneBaseMenu +{ +public: + PhoneGreeterMenu(std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + PhoneBaseMenu(PhoneGreeter, "phone-greeter", state_, actions_) {} +}; + +/**** +***** +****/ + +MenuFactory::MenuFactory(std::shared_ptr<Actions>& actions_, + std::shared_ptr<State>& state_): + m_actions(actions_), + m_state(state_) +{ +} + +std::shared_ptr<Menu> +MenuFactory::buildMenu(Menu::Profile profile) +{ + std::shared_ptr<Menu> menu; + m_state->show_clock.set (true); // FIXME + + //std::shared_ptr<State> state(new State); + //state->clock = clock; + //state->planner = planner; + //state->locations = locations; + + switch (profile) + { + case Menu::Desktop: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopMenu(m_state, m_actions)); + break; + + case Menu::DesktopGreeter: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopGreeterMenu(m_state, m_actions)); + break; + + case Menu::Phone: + m_state->show_events.set(true); // FIXME + menu.reset(new PhoneMenu(m_state, m_actions)); + break; + + case Menu::PhoneGreeter: + m_state->show_events.set(false); // FIXME + menu.reset(new PhoneGreeterMenu(m_state, m_actions)); + break; + + default: + g_warn_if_reached(); + break; + } + + return menu; +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 804d98e..6abaf3e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -34,21 +34,21 @@ namespace datetime { ***** ****/ -G_DEFINE_QUARK ("source-client", source_client) +G_DEFINE_QUARK("source-client", source_client) class PlannerEds::Impl { public: - Impl (PlannerEds& owner): - owner_(owner), - cancellable_(g_cancellable_new()) + Impl(PlannerEds& owner): + m_owner(owner), + m_cancellable(g_cancellable_new()) { - e_source_registry_new (cancellable_, on_source_registry_ready, this); + e_source_registry_new(m_cancellable, on_source_registry_ready, this); - owner_.time.changed().connect([this](const DateTime& dt) { - g_message ("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); + m_owner.time.changed().connect([this](const DateTime& dt) { + g_message("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); rebuildSoon(); }); @@ -57,46 +57,46 @@ public: ~Impl() { - g_cancellable_cancel (cancellable_); - g_clear_object (&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (rebuild_tag_) - g_source_remove (rebuild_tag_); + if (m_rebuild_tag) + g_source_remove(m_rebuild_tag); - if (source_registry_) - g_signal_handlers_disconnect_by_data (source_registry_, this); - g_clear_object (&source_registry_); + if (m_source_registry) + g_signal_handlers_disconnect_by_data(m_source_registry, this); + g_clear_object(&m_source_registry); } private: - static void on_source_registry_ready (GObject* /*source*/, GAsyncResult* res, gpointer gself) + static void on_source_registry_ready(GObject* /*source*/, GAsyncResult* res, gpointer gself) { - GError * error = NULL; - auto r = e_source_registry_new_finish (res, &error); - if (error != NULL) + GError * error = nullptr; + auto r = e_source_registry_new_finish(res, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot show EDS appointments: %s", error->message); - g_error_free (error); + g_error_free(error); } else { auto self = static_cast<Impl*>(gself); - g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self); - g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self); - g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self); - g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self); - g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self); + g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), self); + g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), self); + g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), self); + g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), self); + g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), self); - self->source_registry_ = r; + self->m_source_registry = r; - GList* sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); + GList* sources = e_source_registry_list_sources(r, E_SOURCE_EXTENSION_CALENDAR); for (auto l=sources; l!=nullptr; l=l->next) - on_source_added (r, E_SOURCE(l->data), gself); - g_list_free_full (sources, g_object_unref); + on_source_added(r, E_SOURCE(l->data), gself); + g_list_free_full(sources, g_object_unref); } } @@ -104,74 +104,74 @@ private: { auto self = static_cast<PlannerEds::Impl*>(gself); - self->sources_.insert(E_SOURCE(g_object_ref(source))); + self->m_sources.insert(E_SOURCE(g_object_ref(source))); if (e_source_get_enabled(source)) on_source_enabled(registry, source, gself); } - static void on_source_enabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { auto self = static_cast<PlannerEds::Impl*>(gself); - e_cal_client_connect (source, - E_CAL_CLIENT_SOURCE_TYPE_EVENTS, - self->cancellable_, - on_client_connected, - gself); + e_cal_client_connect(source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + self->m_cancellable, + on_client_connected, + gself); } - static void on_client_connected (GObject* /*source*/, GAsyncResult * res, gpointer gself) + static void on_client_connected(GObject* /*source*/, GAsyncResult * res, gpointer gself) { GError * error = nullptr; - EClient * client = e_cal_client_connect_finish (res, &error); + EClient * client = e_cal_client_connect_finish(res, &error); if (error) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot connect to EDS source: %s", error->message); - g_error_free (error); + g_error_free(error); } else { // we've got a new connected ECalClient, so store it & notify clients - g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)), - source_client_quark(), - client, - g_object_unref); + g_object_set_qdata_full(G_OBJECT(e_client_get_source(client)), + source_client_quark(), + client, + g_object_unref); - g_message ("client connected; calling rebuildSoon()"); + g_message("client connected; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } } - static void on_source_disabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { gpointer e_cal_client; // if this source has a connected ECalClient, remove it & notify clients - if ((e_cal_client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) + if ((e_cal_client = g_object_steal_qdata(G_OBJECT(source), source_client_quark()))) { - g_object_unref (e_cal_client); + g_object_unref(e_cal_client); - g_message ("source disabled; calling rebuildSoon()"); + g_message("source disabled; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } } - static void on_source_removed (ESourceRegistry* registry, ESource* source, gpointer gself) + static void on_source_removed(ESourceRegistry* registry, ESource* source, gpointer gself) { auto self = static_cast<PlannerEds::Impl*>(gself); - on_source_disabled (registry, source, gself); + on_source_disabled(registry, source, gself); - self->sources_.erase (source); - g_object_unref (source); + self->m_sources.erase(source); + g_object_unref(source); } - static void on_source_changed (ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) { - g_message ("source changed; calling rebuildSoon()"); + g_message("source changed; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } @@ -181,44 +181,44 @@ private: struct Task { - Impl* impl_; - appointment_func func_; - std::vector<Appointment> appointments_; - Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + Impl* p; + appointment_func func; + std::vector<Appointment> appointments; + Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} }; struct AppointmentSubtask { - std::shared_ptr<Task> task_; - ECalClient * client_; - std::string color_; - AppointmentSubtask (const std::shared_ptr<Task>& task, ECalClient* client, const char* color): - task_(task), client_(client), color_(color) {} + std::shared_ptr<Task> task; + ECalClient* client; + std::string color; + AppointmentSubtask(const std::shared_ptr<Task>& task_in, ECalClient* client_in, const char* color_in): + task(task_in), client(client_in), color(color_in) {} }; void rebuildSoon() { const static guint ARBITRARY_INTERVAL_SECS = 2; - if (rebuild_tag_ == 0) - rebuild_tag_ = g_timeout_add_seconds (ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); + if (m_rebuild_tag == 0) + m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); } - static gboolean rebuildNowStatic (gpointer gself) + static gboolean rebuildNowStatic(gpointer gself) { auto self = static_cast<Impl*>(gself); - self->rebuild_tag_ = 0; + self->m_rebuild_tag = 0; self->rebuildNow(); return G_SOURCE_REMOVE; } void rebuildNow() { - GDateTime* calendar_date = owner_.time.get().get(); + auto calendar_date = m_owner.time.get().get(); GDateTime* begin; GDateTime* end; int y, m, d; - g_message ("in rebuildNow"); + g_message("in rebuildNow"); // get all the appointments in the calendar month g_date_time_get_ymd(calendar_date, &y, &m, &d); @@ -227,7 +227,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { - g_message ("got %d appointments in this calendar month", (int)appointments.size()); + g_message("got %d appointments in this calendar month", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -239,7 +239,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { - g_message ("got %d upcoming appointments", (int)appointments.size()); + g_message("got %d upcoming appointments", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -251,8 +251,8 @@ private: { const auto begin = g_date_time_to_unix(begin_dt); const auto end = g_date_time_to_unix(end_dt); - g_message ("getting all appointments from [%s ... %s]", g_date_time_format (begin_dt, "%F %T"), - g_date_time_format (end_dt, "%F %T")); + g_message("getting all appointments from [%s ... %s]", g_date_time_format(begin_dt, "%F %T"), + g_date_time_format(end_dt, "%F %T")); /** *** init the default timezone @@ -260,75 +260,76 @@ private: icaltimezone * default_timezone = nullptr; - const auto tz = g_date_time_get_timezone_abbreviation (owner_.time.get().get()); - g_message ("%s tz is %s", G_STRLOC, tz); + const auto tz = g_date_time_get_timezone_abbreviation(m_owner.time.get().get()); + g_message("%s tz is %s", G_STRLOC, tz); if (tz && *tz) { - default_timezone = icaltimezone_get_builtin_timezone (tz); + default_timezone = icaltimezone_get_builtin_timezone(tz); if (default_timezone == nullptr) // maybe str is a tzid? - default_timezone = icaltimezone_get_builtin_timezone_from_tzid (tz); + default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); - g_debug ("default_timezone is %p", default_timezone); + g_debug("default_timezone is %p", default_timezone); } /** *** walk through the sources to build the appointment list **/ - std::shared_ptr<Task> main_task (new Task(this, func), [](Task* task){ + std::shared_ptr<Task> main_task(new Task(this, func), [](Task* task){ g_message("time to delete task %p", task); - task->func_(task->appointments_); + task->func(task->appointments); }); - for (auto& source : sources_) + for (auto& source : m_sources) { - auto client = E_CAL_CLIENT (g_object_get_qdata (G_OBJECT(source), source_client_quark())); + auto client = E_CAL_CLIENT(g_object_get_qdata(G_OBJECT(source), source_client_quark())); if (client == nullptr) continue; if (default_timezone != nullptr) - e_cal_client_set_default_timezone (client, default_timezone); + e_cal_client_set_default_timezone(client, default_timezone); // start a new subtask to enumerate all the components in this client. - auto extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); - const auto color = e_source_selectable_get_color (E_SOURCE_SELECTABLE(extension)); - g_message ("calling e_cal_client_generate_instances for %p", client); - e_cal_client_generate_instances (client, - begin, - end, - cancellable_, - my_get_appointments_foreach, - new AppointmentSubtask (main_task, client, color), - [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); + auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); + const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); + g_message("calling e_cal_client_generate_instances for %p", client); + e_cal_client_generate_instances(client, + begin, + end, + m_cancellable, + my_get_appointments_foreach, + new AppointmentSubtask (main_task, client, color), + [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); } } struct UrlSubtask { - std::shared_ptr<Task> task_; - Appointment appointment_; - UrlSubtask (const std::shared_ptr<Task>& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + std::shared_ptr<Task> task; + Appointment appointment; + UrlSubtask(const std::shared_ptr<Task>& task_in, const Appointment& appointment_in): + task(task_in), appointment(appointment_in) {} }; static gboolean - my_get_appointments_foreach (ECalComponent* component, - time_t begin, - time_t end, - gpointer gsubtask) + my_get_appointments_foreach(ECalComponent* component, + time_t begin, + time_t end, + gpointer gsubtask) { const auto vtype = e_cal_component_get_vtype(component); auto subtask = static_cast<AppointmentSubtask*>(gsubtask); if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) { - const gchar* uid = NULL; - e_cal_component_get_uid (component, &uid); + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); auto status = ICAL_STATUS_NONE; - e_cal_component_get_status (component, &status); + e_cal_component_get_status(component, &status); - if ((uid != NULL) && + if ((uid != nullptr) && (status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) { @@ -339,78 +340,78 @@ private: however, since design only allows daily recurrence, that's all we support here. */ GSList * recur_list; - e_cal_component_get_rrule_list (component, &recur_list); - for (auto l=recur_list; l!=NULL; l=l->next) + e_cal_component_get_rrule_list(component, &recur_list); + for (auto l=recur_list; l!=nullptr; l=l->next) { const auto recur = static_cast<struct icalrecurrencetype*>(l->data); appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) && (recur->interval == 1)); } - e_cal_component_free_recur_list (recur_list); + e_cal_component_free_recur_list(recur_list); ECalComponentText text; text.value = ""; - e_cal_component_get_summary (component, &text); + e_cal_component_get_summary(component, &text); - appointment.begin = g_date_time_new_from_unix_local (begin); - appointment.end = g_date_time_new_from_unix_local (end); - appointment.color = subtask->color_; + appointment.begin = g_date_time_new_from_unix_local(begin); + appointment.end = g_date_time_new_from_unix_local(end); + appointment.color = subtask->color; appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; appointment.summary = text.value; appointment.uid = uid; - GList * alarm_uids = e_cal_component_get_alarm_uids (component); + GList * alarm_uids = e_cal_component_get_alarm_uids(component); appointment.has_alarms = alarm_uids != nullptr; - cal_obj_uid_list_free (alarm_uids); - - e_cal_client_get_attachment_uris (subtask->client_, - uid, - NULL, - subtask->task_->impl_->cancellable_, - on_appointment_uris_ready, - new UrlSubtask(subtask->task_, appointment)); + cal_obj_uid_list_free(alarm_uids); + + e_cal_client_get_attachment_uris(subtask->client, + uid, + nullptr, + subtask->task->p->m_cancellable, + on_appointment_uris_ready, + new UrlSubtask(subtask->task, appointment)); } } return G_SOURCE_CONTINUE; } - static void on_appointment_uris_ready (GObject* client, GAsyncResult* res, gpointer gsubtask) + static void on_appointment_uris_ready(GObject* client, GAsyncResult* res, gpointer gsubtask) { auto subtask = static_cast<UrlSubtask*>(gsubtask); GSList * uris = nullptr; GError * error = nullptr; - e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); - if (error != NULL) + e_cal_client_get_attachment_uris_finish(E_CAL_CLIENT(client), res, &uris, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Error getting appointment uris: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Error getting appointment uris: %s", error->message); - g_error_free (error); + g_error_free(error); } - else if (uris != NULL) + else if (uris != nullptr) { - subtask->appointment_.url = (const char*) uris->data; // copy the first URL - g_debug ("found url '%s' for appointment '%s'", subtask->appointment_.url.c_str(), subtask->appointment_.summary.c_str()); - e_client_util_free_string_slist (uris); + subtask->appointment.url = (const char*) uris->data; // copy the first URL + g_debug("found url '%s' for appointment '%s'", subtask->appointment.url.c_str(), subtask->appointment.summary.c_str()); + e_client_util_free_string_slist(uris); } - g_message ("adding appointment '%s' '%s'", subtask->appointment_.summary.c_str(), subtask->appointment_.url.c_str()); - subtask->task_->appointments_.push_back (subtask->appointment_); + g_message("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); + subtask->task->appointments.push_back(subtask->appointment); delete subtask; } private: - PlannerEds& owner_; - std::set<ESource*> sources_; - GCancellable * cancellable_ = nullptr; - ESourceRegistry * source_registry_ = nullptr; - guint rebuild_tag_ = 0; + PlannerEds& m_owner; + std::set<ESource*> m_sources; + GCancellable * m_cancellable = nullptr; + ESourceRegistry * m_source_registry = nullptr; + guint m_rebuild_tag = 0; }; -PlannerEds::PlannerEds(): impl_(new Impl(*this)) {} +PlannerEds::PlannerEds(): p(new Impl(*this)) {} PlannerEds::~PlannerEds() =default; diff --git a/src/planner-eds.h b/src/planner-eds.h deleted file mode 100644 index dea9371..0000000 --- a/src/planner-eds.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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/>. - */ - -#ifndef __INDICATOR_DATETIME_PLANNER_EDS__H__ -#define __INDICATOR_DATETIME_PLANNER_EDS__H__ - -#include "planner.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER_EDS (indicator_datetime_planner_eds_get_type()) -#define INDICATOR_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEds)) -#define INDICATOR_DATETIME_PLANNER_EDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEdsClass)) -#define INDICATOR_IS_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS)) - -typedef struct _IndicatorDatetimePlannerEds IndicatorDatetimePlannerEds; -typedef struct _IndicatorDatetimePlannerEdsPriv IndicatorDatetimePlannerEdsPriv; -typedef struct _IndicatorDatetimePlannerEdsClass IndicatorDatetimePlannerEdsClass; - -GType indicator_datetime_planner_eds_get_type (void); - -/** - * An IndicatorDatetimePlanner which uses Evolution Data Server - * to get its list of appointments. - */ -struct _IndicatorDatetimePlannerEds -{ - /*< private >*/ - IndicatorDatetimePlanner parent; - IndicatorDatetimePlannerEdsPriv * priv; -}; - -struct _IndicatorDatetimePlannerEdsClass -{ - IndicatorDatetimePlannerClass parent_class; -}; - -IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER_EDS__H__ */ diff --git a/src/planner.h b/src/planner.h deleted file mode 100644 index ffe8937..0000000 --- a/src/planner.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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/>. - */ - -#ifndef __INDICATOR_DATETIME_PLANNER__H__ -#define __INDICATOR_DATETIME_PLANNER__H__ - -#include <glib.h> -#include <glib-object.h> /* parent class */ -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER (indicator_datetime_planner_get_type()) -#define INDICATOR_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlanner)) -#define INDICATOR_DATETIME_PLANNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_DATETIME_PLANNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_IS_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER)) - -typedef struct _IndicatorDatetimePlanner IndicatorDatetimePlanner; -typedef struct _IndicatorDatetimePlannerPriv IndicatorDatetimePlannerPriv; -typedef struct _IndicatorDatetimePlannerClass IndicatorDatetimePlannerClass; - -GType indicator_datetime_planner_get_type (void); - -struct IndicatorDatetimeAppt -{ - gchar * color; - gchar * summary; - gchar * url; - gchar * uid; - GDateTime * begin; - GDateTime * end; - gboolean is_event; - gboolean is_daily; - gboolean has_alarms; -}; - -/** - * Abstract Base Class for objects that provides appointments and events. - * - * These will be listed in the appointments section of indicator-datetime's menu. - */ -struct _IndicatorDatetimePlanner -{ - /*< private >*/ - GObject parent; - IndicatorDatetimePlannerPriv * priv; -}; - -struct _IndicatorDatetimePlannerClass -{ - GObjectClass parent_class; - - /* signals */ - - void (*appointments_changed) (IndicatorDatetimePlanner * self); - - /* virtual functions */ - - void (*get_appointments) (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - - GSList* (*get_appointments_finish) (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - - - gboolean (*is_configured) (IndicatorDatetimePlanner * self); - void (*activate) (IndicatorDatetimePlanner * self); - void (*activate_time) (IndicatorDatetimePlanner * self, GDateTime *); -}; - -/*** -**** -***/ - -void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt); - -/** - * Get a list of appointments, sorted by start time. - */ -void indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - -/** - * Finishes the async call begun with indicator_datetime_planner_get_appointments() - * - * To free the list properly, use indicator_datetime_planner_free_appointments() - * - * Return value: (element-type IndicatorDatetimeAppt) - * (transfer full): - * list of appointments - */ -GSList * indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - -/** - * Convenience function for freeing a GSList of IndicatorDatetimeAppt. - * - * Equivalent to g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free); - */ -void indicator_datetime_planner_free_appointments (GSList *); - - -/** - * Returns false if the planner's backend is not configured. - * - * This can be used on startup to determine whether or not to use this planner. - */ -gboolean indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor. - */ -void indicator_datetime_planner_activate (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor, - * with an added hint of the specific time that the user would like to edit. - */ -void indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time); - -/** - * Set the timezone. - * - * This is used as a default timezone if the backend's events don't provide their own. - */ -void indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone); - -const char * indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self); - - -/** - * Emits the "appointments-changed" signal. This should only be called by subclasses. - */ -void indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER__H__ */ diff --git a/src/service.cpp b/src/service.cpp new file mode 100644 index 0000000..0671c61 --- /dev/null +++ b/src/service.cpp @@ -0,0 +1,140 @@ +/* + * 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/service.h> +#include <datetime/dbus-shared.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +Service::~Service() +{ + if (m_dbus_connection != nullptr) + { + for(auto& id : m_exported_menu_ids) + g_dbus_connection_unexport_menu_model(m_dbus_connection, id); + + if (m_exported_actions_id) + g_dbus_connection_unexport_action_group(m_dbus_connection, m_exported_actions_id); + } + + if (m_own_id) + g_bus_unown_name(m_own_id); + + g_clear_object(&m_dbus_connection); +} + +/*** +**** +***/ + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("bus acquired: %s", name); + static_cast<Service*>(gthis)->on_bus_acquired(connection, name); +} + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) +{ + m_dbus_connection = static_cast<GDBusConnection*>(g_object_ref(G_OBJECT(connection))); + + // export the actions + GError * error = nullptr; + const auto id = g_dbus_connection_export_action_group(m_dbus_connection, BUS_PATH, m_actions, &error); + if (id) + { + m_exported_actions_id = id; + } + else + { + g_warning("cannot export action group: %s", error->message); + g_clear_error(&error); + } + + // export the menus + for(auto& menu : m_menus) + { + const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto id = g_dbus_connection_export_menu_model(m_dbus_connection, path.c_str(), menu->menu_model(), &error); + if (id) + { + m_exported_menu_ids.insert(id); + } + else + { + g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + g_clear_error(&error); + } + } +} + +/*** +**** +***/ + +void +Service::on_name_lost(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("name lost: %s", name); + static_cast<Service*>(gthis)->on_name_lost(connection, name); +} + +void +Service::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) +{ + name_lost(); +} + +/*** +**** +***/ + +void +Service::publish(GActionGroup* actions, std::vector<std::shared_ptr<Menu>>& menus) +{ + m_actions = actions; + m_menus = menus; + m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + nullptr, + on_name_lost, + this, + nullptr); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/src/settings-live.cpp b/src/settings-live.cpp new file mode 100644 index 0000000..74085a9 --- /dev/null +++ b/src/settings-live.cpp @@ -0,0 +1,293 @@ +/* + * 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/settings-live.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +LiveSettings::~LiveSettings() +{ + g_clear_object(&m_settings); +} + +LiveSettings::LiveSettings(): + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + g_signal_connect (m_settings, "changed", G_CALLBACK(on_changed), this); + + update_show_clock(); +} + +void LiveSettings::update_show_clock() +{ + auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S); + show_clock.set(val); +} + +void LiveSettings::on_changed(GSettings* /*settings*/, + gchar* key, + gpointer gself) +{ + static_cast<LiveSettings*>(gself)->update_key(key); +} + +void LiveSettings::update_key(const std::string& key) +{ + if (key == SETTINGS_SHOW_CLOCK_S) + { + update_show_clock(); + } +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#if 0 + else if (!g_strcmp0(key, TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_SECONDS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DAY_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DATE_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_YEAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CUSTOM_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CALENDAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_WEEK_NUMBERS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_EVENTS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_LOCATIONS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIMEZONE_NAME_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DETECTED_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_LOCATIONS_S)) + { + } +#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" +#define SETTINGS_LOCATIONS_S "locations" +#define SETTINGS_TIMEZONE_NAME_S "timezone-name" + zzz + "show-clock" + +void user_function (GSettings *settings, + gchar *key, + gpointer user_data) : Has Details + + for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++) + { + g_string_printf (gstr, "changed::%s", header_settings[i]); + g_signal_connect_swapped (p->settings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + + + const char * const calendar_settings[] = { + SETTINGS_SHOW_CALENDAR_S, + SETTINGS_SHOW_WEEK_NUMBERS_S + }; + const char * const appointment_settings[] = { + SETTINGS_SHOW_EVENTS_S, + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S + }; + const char * const location_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S, + SETTINGS_SHOW_LOCATIONS_S, + SETTINGS_LOCATIONS_S, + SETTINGS_SHOW_DETECTED_S, + SETTINGS_TIMEZONE_NAME_S + }; + const char * const time_format_string_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S + }; + + + /*** + **** Listen for settings changes + ***/ + + for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++) + { + g_string_printf (gstr, "changed::%s", header_settings[i]); + g_signal_connect_swapped (p->settings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + +} + + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include <datetime/settings.h> // parent class + +#include <glib.h> // GSettings + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief #Settings implementation which uses GSettings. + */ +class LiveSettings +{ +public: + LiveSettings(); + virtual ~LiveSettings(); + +private: + GSettings* m_settings; + + // we've got a raw pointer here, so disable copying + LiveSettings(const LiveSettings&) =delete; + LiveSettings& operator=(const LiveSettings&) =delete; +}; + + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H +/* + * Copyright 2010 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: + * Ted Gould <ted@canonical.com> + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_SHARED +#define INDICATOR_DATETIME_SETTINGS_SHARED + +typedef enum +{ + TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM +} +TimeFormatMode; + + +#endif // INDICATOR_DATETIME_SETTINGS_SHARED +/* + * 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_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include <datetime/settings-shared.h> + +#include <core/property.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Interface that represents user-configurable settings. + * + * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml + * For more information. + */ +class Settings +{ +public: + Settings() =default; + virtual ~Settings =default; + + core::Property<TimeFormatMode> time_format_mode; + core::Property<bool> show_clock; + core::Property<bool> show_day; + core::Property<bool> show_year; + core::Property<bool> show_seconds; + core::Property<std::string> custom_time_format; + core::Property<bool> show_calendar; + core::Property<bool> show_events; + core::Property<bool> show_locations; + core::Property<bool> show_auto_detected_location; + core::Property<std::vector<std::string>> locations; + core::Property<std::string> timezone_name; +}; + +#endif // INDICATOR_DATETIME_SETTINGS_H +#endif diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp index f1d0dca..3a0c240 100644 --- a/src/timezone-file.cpp +++ b/src/timezone-file.cpp @@ -26,12 +26,12 @@ namespace datetime { void FileTimezone::clear() { - if (monitor_handler_id_) - g_signal_handler_disconnect(monitor_, monitor_handler_id_); + if (m_monitor_handler_id) + g_signal_handler_disconnect(m_monitor, m_monitor_handler_id); - g_clear_object (&monitor_); + g_clear_object (&m_monitor); - filename_.clear(); + m_filename.clear(); } void @@ -39,11 +39,11 @@ FileTimezone::setFilename(const std::string& filename) { clear(); - filename_ = filename; + m_filename = filename; auto file = g_file_new_for_path(filename.c_str()); GError * err = nullptr; - monitor_ = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); + m_monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); g_object_unref(file); if (err) { @@ -52,7 +52,7 @@ FileTimezone::setFilename(const std::string& filename) } else { - monitor_handler_id_ = g_signal_connect_swapped(monitor_, "changed", G_CALLBACK(onFileChanged), this); + m_monitor_handler_id = g_signal_connect_swapped(m_monitor, "changed", G_CALLBACK(onFileChanged), this); g_debug("%s Monitoring timezone file '%s'", G_STRLOC, filename.c_str()); } @@ -71,9 +71,9 @@ FileTimezone::reload() GError * err = nullptr; gchar * str = nullptr; - if (!g_file_get_contents(filename_.c_str(), &str, nullptr, &err)) + if (!g_file_get_contents(m_filename.c_str(), &str, nullptr, &err)) { - g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename_.c_str(), err->message); + g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, m_filename.c_str(), err->message); g_error_free(err); } else diff --git a/src/timezone-geoclue.cpp b/src/timezone-geoclue.cpp index f6d1f5e..b8847a4 100644 --- a/src/timezone-geoclue.cpp +++ b/src/timezone-geoclue.cpp @@ -27,20 +27,20 @@ namespace datetime { GeoclueTimezone::GeoclueTimezone(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SESSION, cancellable_, on_bus_got, this); + g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_got, this); } GeoclueTimezone::~GeoclueTimezone() { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); + g_cancellable_cancel(m_cancellable); + g_object_unref(m_cancellable); - if (signal_subscription_) - g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_); + if (m_signal_subscription) + g_dbus_connection_signal_unsubscribe(m_connection, m_signal_subscription); - g_object_unref(connection_); + g_object_unref(m_connection); } /*** @@ -48,7 +48,9 @@ GeoclueTimezone::~GeoclueTimezone() ***/ void -GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_bus_got(GObject* /*source*/, + GAsyncResult* res, + gpointer gself) { GError * error; GDBusConnection * connection; @@ -66,9 +68,9 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, { auto self = static_cast<GeoclueTimezone*>(gself); - self->connection_ = connection; + self->m_connection = connection; - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, "/org/freedesktop/Geoclue/Master", "org.freedesktop.Geoclue.Master", @@ -77,7 +79,7 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, G_VARIANT_TYPE("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_client_created, self); } @@ -93,51 +95,51 @@ GeoclueTimezone::on_client_created(GObject * source, GAsyncResult * res, gpointe auto self = static_cast<GeoclueTimezone*>(gself); GVariant * child = g_variant_get_child_value(result, 0); - self->client_object_path_ = g_variant_get_string(child, nullptr); + self->m_client_object_path = g_variant_get_string(child, nullptr); g_variant_unref(child); g_variant_unref(result); - self->signal_subscription_ = g_dbus_connection_signal_subscribe( - self->connection_, + self->m_signal_subscription = g_dbus_connection_signal_subscribe( + self->m_connection, GEOCLUE_BUS_NAME, "org.freedesktop.Geoclue.Address", // inteface "AddressChanged", // signal name - self->client_object_path_.c_str(), // object path + self->m_client_object_path.c_str(), // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_address_changed, self, nullptr); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "SetRequirements", g_variant_new("(iibi)", 2, 0, FALSE, 1023), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_requirements_set, self); } } void -GeoclueTimezone::on_address_changed(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters, - gpointer gself) +GeoclueTimezone::on_address_changed(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* parameters, + gpointer gself) { static_cast<GeoclueTimezone*>(gself)->setTimezoneFromAddressVariant(parameters); } void -GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_requirements_set(GObject* source, GAsyncResult* res, gpointer gself) { GVariant * result; @@ -145,16 +147,16 @@ GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpoin { auto self = static_cast<GeoclueTimezone*>(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "AddressStart", nullptr, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_started, self); @@ -171,16 +173,16 @@ GeoclueTimezone::on_address_started(GObject * source, GAsyncResult * res, gpoint { auto self = static_cast<GeoclueTimezone*>(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_got, self); diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp index cb5e2bc..dc14021 100644 --- a/src/timezones-live.cpp +++ b/src/timezones-live.cpp @@ -24,37 +24,35 @@ namespace unity { namespace indicator { namespace datetime { -LiveTimezones::LiveTimezones (const std::string& filename): - file_ (filename) +LiveTimezones::LiveTimezones(const std::string& filename): + m_file(filename) { - file_.timezone.changed().connect([this](const std::string&){updateTimezones();}); + m_file.timezone.changed().connect([this](const std::string&){update_timezones();}); - geolocationEnabled.changed().connect([this](bool){updateGeolocation();}); - updateGeolocation(); + geolocation_enabled.changed().connect([this](bool){update_geolocation();}); + update_geolocation(); - updateTimezones(); + update_timezones(); } -void -LiveTimezones::updateGeolocation() +void LiveTimezones::update_geolocation() { - geo_.reset(); + m_geo.reset(); - if (geolocationEnabled.get()) + if(geolocation_enabled.get()) { - GeoclueTimezone * geo = new GeoclueTimezone(); - geo->timezone.changed().connect([this](const std::string&){updateTimezones();}); - geo_.reset(geo); + auto geo = new GeoclueTimezone(); + geo->timezone.changed().connect([this](const std::string&){update_timezones();}); + m_geo.reset(geo); } } -void -LiveTimezones::updateTimezones() +void LiveTimezones::update_timezones() { - const std::string a = file_.timezone.get(); - const std::string b = geo_ ? geo_->timezone.get() : ""; + const auto a = m_file.timezone.get(); + const auto b = m_geo ? m_geo->timezone.get() : ""; - timezone.set (a.empty() ? b : a); + timezone.set(a.empty() ? b : a); std::set<std::string> zones; if (!a.empty()) diff --git a/src/utils.cpp b/src/utils.cpp index 42e034e..acd9796 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,104 +37,101 @@ with this program. If not, see <http://www.gnu.org/licenses/>. /* Check the system locale setting to see if the format is 24-hour time or 12-hour time */ gboolean -is_locale_12h (void) +is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + const char *t_fmt = nl_langinfo(T_FMT); - for (i = 0; formats_24h[i]; ++i) { - if (strstr (t_fmt, formats_24h[i])) { - return FALSE; - } - } + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + for(const auto& format : formats_24h) + if(strstr(t_fmt, format) != nullptr) + return false; - return TRUE; + return true; } void -split_settings_location (const gchar * location, gchar ** zone, gchar ** name) +split_settings_location(const gchar* location, gchar** zone, gchar** name) { - gchar * location_dup; - gchar * first; + auto location_dup = g_strdup(location); + g_strstrip(location_dup); - location_dup = g_strdup (location); - g_strstrip (location_dup); + gchar* first; + if((first = strchr(location_dup, ' '))) + *first = '\0'; - if ((first = strchr (location_dup, ' '))) - *first = '\0'; - - if (zone != NULL) + if(zone) { - *zone = location_dup; + *zone = location_dup; } - if (name != NULL) + if(name != nullptr) { - gchar * after = first ? g_strstrip (first + 1) : NULL; + gchar* after = first ? g_strstrip(first + 1) : nullptr; - if (after && *after) + if(after && *after) { - *name = g_strdup (after); + *name = g_strdup(after); } - else /* make the name from zone */ + else // make the name from zone { - gchar * chr = strrchr (location_dup, '/'); - after = g_strdup (chr ? chr + 1 : location_dup); + gchar * chr = strrchr(location_dup, '/'); + after = g_strdup(chr ? chr + 1 : location_dup); - /* replace underscores with spaces */ - for (chr=after; chr && *chr; chr++) - if (*chr == '_') - *chr = ' '; + // replace underscores with spaces + for(chr=after; chr && *chr; chr++) + if(*chr == '_') + *chr = ' '; - *name = after; + *name = after; } } } -gchar * -get_current_zone_name (const gchar * location, GSettings * settings) +gchar* +get_current_zone_name(const gchar* location, GSettings* settings) { - gchar * new_zone, * new_name; - gchar * tz_name; - gchar * old_zone, * old_name; - gchar * rv; - - split_settings_location (location, &new_zone, &new_name); - - tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S); - split_settings_location (tz_name, &old_zone, &old_name); - g_free (tz_name); - - /* new_name is always just a sanitized version of a timezone. - old_name is potentially a saved "pretty" version of a timezone name from - geonames. So we prefer to use it if available and the zones match. */ - - if (g_strcmp0 (old_zone, new_zone) == 0) { - rv = old_name; - old_name = NULL; - } - else { - rv = new_name; - new_name = NULL; - } - - g_free (new_zone); - g_free (old_zone); - g_free (new_name); - g_free (old_name); - - return rv; + gchar* new_zone; + gchar* new_name; + split_settings_location(location, &new_zone, &new_name); + + auto tz_name = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + gchar* old_zone; + gchar* old_name; + split_settings_location(tz_name, &old_zone, &old_name); + g_free(tz_name); + + /* new_name is always just a sanitized version of a timezone. + old_name is potentially a saved "pretty" version of a timezone name from + geonames. So we prefer to use it if available and the zones match. */ + + gchar* rv; + if (g_strcmp0(old_zone, new_zone) == 0) + { + rv = old_name; + old_name = nullptr; + } + else + { + rv = new_name; + new_name = nullptr; + } + + g_free(new_zone); + g_free(old_zone); + g_free(new_name); + g_free(old_name); + return rv; } gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) { - using unity::indicator::datetime::Clock; - using unity::indicator::datetime::MockClock; - using unity::indicator::datetime::DesktopFormatter; - - std::shared_ptr<Clock> clock(new MockClock(now)); - DesktopFormatter formatter(clock); - return g_strdup (formatter.getRelativeFormat(then).c_str()); + using unity::indicator::datetime::Clock; + using unity::indicator::datetime::DateTime; + using unity::indicator::datetime::MockClock; + using unity::indicator::datetime::DesktopFormatter; + + std::shared_ptr<Clock> clock(new MockClock(DateTime(now))); + DesktopFormatter formatter(clock); + return g_strdup(formatter.getRelativeFormat(then).c_str()); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a424858..fb55f5a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,12 +32,6 @@ include_directories (${DBUSTEST_INCLUDE_DIRS}) add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") -# test-core -set (TEST_NAME test-core) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - # test-timezone-file set (TEST_NAME test-timezone-file) add_executable (${TEST_NAME} ${TEST_NAME}.cc) @@ -76,18 +70,31 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-planner-eds -set (TEST_NAME test-planner-eds) +# test-locations +set (TEST_NAME test-locations) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-locations -set (TEST_NAME test-locations) +# test-actions +set (TEST_NAME test-actions) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-menus +set (TEST_NAME test-menus) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-desktop +#set (TEST_NAME test-desktop) +#add_executable (${TEST_NAME} ${TEST_NAME}.cc) +#add_test (${TEST_NAME} ${TEST_NAME}) +#add_dependencies (${TEST_NAME} libindicatordatetimeservice) +#target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) diff --git a/tests/Makefile.am.strings b/tests/Makefile.am.strings deleted file mode 100644 index 4a89e8f..0000000 --- a/tests/Makefile.am.strings +++ /dev/null @@ -1,38 +0,0 @@ -TESTS += \ - test-ellipsis \ - test-space-ellipsis \ - test-ascii-quotes - -##### -# Tests for there being proper ellipsis instead of three periods in a row -##### -test-ellipsis: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid.*\.\.\.\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Ellipsis found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -##### -# Tests for there being a space before an ellipsis -##### -test-space-ellipsis: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid.* …\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Space before ellipsis found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -##### -# Tests for ASCII quote types -##### -test-ascii-quotes: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid \\\".*'.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII apostrophe found in user visible strings\" >&2 && exit 1" >> $@ - @echo "grep -c -e \"^msgid \\\".*\\\".*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII quote found in user visible strings\" >&2 && exit 1" >> $@ - @echo "grep -c -e \"^msgid \\\".*\\\`.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII backtick found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -CLEANFILES += $(TESTS) diff --git a/tests/actions-mock.h b/tests/actions-mock.h new file mode 100644 index 0000000..112900b --- /dev/null +++ b/tests/actions-mock.h @@ -0,0 +1,88 @@ +/* + * 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_ACTIONS_MOCK_H +#define INDICATOR_DATETIME_ACTIONS_MOCK_H + +#include <datetime/actions.h> + +#include <set> + +namespace unity { +namespace indicator { +namespace datetime { + +class MockActions: public Actions +{ +public: + MockActions(std::shared_ptr<State>& state_in): Actions(state_in) {} + ~MockActions() =default; + + enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp, + OpenPlanner, OpenPlannerAt, OpenAppointment, + SetLocation, SetCalendarDate }; + const std::vector<Action>& history() const { return m_history; } + const DateTime& date_time() const { return m_date_time; } + const std::string& zone() const { return m_zone; } + const std::string& name() const { return m_name; } + const std::string& url() const { return m_url; } + void clear() { m_history.clear(); m_zone.clear(); m_name.clear(); } + + void open_desktop_settings() { m_history.push_back(OpenDesktopSettings); } + + void open_phone_settings() { m_history.push_back(OpenPhoneSettings); } + + void open_phone_clock_app() { m_history.push_back(OpenPhoneClockApp); } + + void open_planner() { m_history.push_back(OpenPlanner); } + + void open_planner_at(const DateTime& date_time_) { + m_history.push_back(OpenPlannerAt); + m_date_time = date_time_; + } + + void set_location(const std::string& zone_, const std::string& name_) { + m_history.push_back(SetLocation); + m_zone = zone_; + m_name = name_; + } + + void open_appointment(const std::string& url_) { + m_history.push_back(OpenAppointment); + m_url = url_; + } + + void set_calendar_date(const DateTime& date_time_) { + m_history.push_back(SetCalendarDate); + m_date_time = date_time_; + } + +private: + std::string m_url; + std::string m_zone; + std::string m_name; + DateTime m_date_time; + std::vector<Action> m_history; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_MOCK_H diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 043b7e3..3f517d4 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -25,6 +25,8 @@ #include <gtest/gtest.h> +#include <locale.h> // setlocale() + class GlibFixture : public ::testing::Test { private: @@ -35,96 +37,101 @@ class GlibFixture : public ::testing::Test std::map<GLogLevelFlags,int> logCounts; - void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED) + void testLogCount(GLogLevelFlags log_level, int /*expected*/) { #if 0 - EXPECT_EQ (expected, logCounts[log_level]); + EXPECT_EQ(expected, logCounts[log_level]); #endif - logCounts.erase (log_level); + logCounts.erase(log_level); } private: - static void default_log_handler (const gchar * log_domain, - GLogLevelFlags log_level, - const gchar * message, - gpointer self) + static void default_log_handler(const gchar * log_domain, + GLogLevelFlags log_level, + const gchar * message, + gpointer self) { - g_print ("%s - %d - %s\n", log_domain, (int)log_level, message); + g_print("%s - %d - %s\n", log_domain, (int)log_level, message); static_cast<GlibFixture*>(self)->logCounts[log_level]++; } protected: - virtual void SetUp () + virtual void SetUp() { - loop = g_main_loop_new (NULL, FALSE); + setlocale(LC_ALL, ""); + + loop = g_main_loop_new(nullptr, false); - //g_log_set_default_handler (default_log_handler, this); + //g_log_set_default_handler(default_log_handler, this); // only use local, temporary settings - g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); - g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); - g_debug ("SCHEMA_DIR is %s", SCHEMA_DIR); + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); + + g_unsetenv("DISPLAY"); + } virtual void TearDown() { #if 0 // confirm there aren't any unexpected log messages - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_ERROR]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_CRITICAL]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_WARNING]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_MESSAGE]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_INFO]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_ERROR]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_CRITICAL]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_WARNING]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_MESSAGE]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_INFO]); #endif // revert to glib's log handler - //g_log_set_default_handler (realLogHandler, this); + //g_log_set_default_handler(realLogHandler, this); - g_clear_pointer (&loop, g_main_loop_unref); + g_clear_pointer(&loop, g_main_loop_unref); } private: static gboolean - wait_for_signal__timeout (gpointer name) + wait_for_signal__timeout(gpointer name) { - g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); return G_SOURCE_REMOVE; } static gboolean - wait_msec__timeout (gpointer loop) + wait_msec__timeout(gpointer loop) { - g_main_loop_quit (static_cast<GMainLoop*>(loop)); + g_main_loop_quit(static_cast<GMainLoop*>(loop)); return G_SOURCE_CONTINUE; } protected: /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal (gpointer o, const gchar * signal, const int timeout_seconds=5) + void wait_for_signal(gpointer o, const gchar * signal, const int timeout_seconds=5) { // wait for the signal or for timeout, whichever comes first - const auto handler_id = g_signal_connect_swapped (o, signal, - G_CALLBACK(g_main_loop_quit), - loop); - const auto timeout_id = g_timeout_add_seconds (timeout_seconds, - wait_for_signal__timeout, - loop); - g_main_loop_run (loop); - g_source_remove (timeout_id); - g_signal_handler_disconnect (o, handler_id); + const auto handler_id = g_signal_connect_swapped(o, signal, + G_CALLBACK(g_main_loop_quit), + loop); + const auto timeout_id = g_timeout_add_seconds(timeout_seconds, + wait_for_signal__timeout, + loop); + g_main_loop_run(loop); + g_source_remove(timeout_id); + g_signal_handler_disconnect(o, handler_id); } /* convenience func to loop for N msec */ - void wait_msec (int msec=50) + void wait_msec(int msec=50) { - const auto id = g_timeout_add (msec, wait_msec__timeout, loop); - g_main_loop_run (loop); - g_source_remove (id); + const auto id = g_timeout_add(msec, wait_msec__timeout, loop); + g_main_loop_run(loop); + g_source_remove(id); } GMainLoop * loop; diff --git a/tests/planner-mock.c b/tests/planner-mock.c index e67ad7e..df5413e 100644 --- a/tests/planner-mock.c +++ b/tests/planner-mock.c @@ -39,7 +39,7 @@ G_DEFINE_TYPE (IndicatorDatetimePlannerMock, static void my_get_appointments (IndicatorDatetimePlanner * planner, GDateTime * begin_datetime, - GDateTime * end_datetime G_GNUC_UNUSED, + GDateTime * /*end_datetime*/, GAsyncReadyCallback callback, gpointer user_data) { @@ -88,34 +88,34 @@ my_get_appointments (IndicatorDatetimePlanner * planner, } static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GAsyncResult * res, - GError ** error) +my_get_appointments_finish (IndicatorDatetimePlanner* /*self*/, + GAsyncResult* res, + GError** error) { - return g_task_propagate_pointer (G_TASK(res), error); + return g_task_propagate_pointer(G_TASK(res), error); } static gboolean -my_is_configured (IndicatorDatetimePlanner * planner) +my_is_configured(IndicatorDatetimePlanner* planner) { IndicatorDatetimePlannerMock * self; - self = INDICATOR_DATETIME_PLANNER_MOCK (planner); + self = INDICATOR_DATETIME_PLANNER_MOCK(planner); return self->priv->is_configured; } static void -my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED) +my_activate(IndicatorDatetimePlanner* /*self*/) { - g_message ("%s %s", G_STRLOC, G_STRFUNC); + g_message("%s %s", G_STRLOC, G_STRFUNC); } static void -my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GDateTime * activate_time) +my_activate_time(IndicatorDatetimePlanner* /*self*/, + GDateTime* activate_time) { - gchar * str = g_date_time_format (activate_time, "%F %T"); - g_message ("%s %s: %s", G_STRLOC, G_STRFUNC, str); - g_free (str); + gchar * str = g_date_time_format(activate_time, "%F %T"); + g_message("%s %s: %s", G_STRLOC, G_STRFUNC, str); + g_free(str); } /*** @@ -123,9 +123,9 @@ my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, ***/ static void -my_dispose (GObject * o) +my_dispose(GObject * o) { - G_OBJECT_CLASS (indicator_datetime_planner_mock_parent_class)->dispose (o); + G_OBJECT_CLASS(indicator_datetime_planner_mock_parent_class)->dispose(o); } /*** @@ -133,7 +133,7 @@ my_dispose (GObject * o) ***/ static void -indicator_datetime_planner_mock_class_init (IndicatorDatetimePlannerMockClass * klass) +indicator_datetime_planner_mock_class_init(IndicatorDatetimePlannerMockClass* klass) { GObjectClass * object_class; IndicatorDatetimePlannerClass * planner_class; diff --git a/tests/state-fixture.h b/tests/state-fixture.h new file mode 100644 index 0000000..0286ea9 --- /dev/null +++ b/tests/state-fixture.h @@ -0,0 +1,75 @@ +/* + * 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 "glib-fixture.h" +#include "actions-mock.h" + +#include <datetime/clock-mock.h> +#include <datetime/formatter.h> +#include <datetime/locations.h> +#include <datetime/menu.h> +#include <datetime/planner-mock.h> +#include <datetime/service.h> +#include <datetime/state.h> +#include <datetime/timezones.h> + +using namespace unity::indicator::datetime; + +class StateFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + std::shared_ptr<MockClock> m_clock; + std::shared_ptr<State> m_state; + std::shared_ptr<MockActions> m_mock_actions; + std::shared_ptr<Actions> m_actions; + + virtual void SetUp() + { + super::SetUp(); + + // first, build a mock backend state + const DateTime now = DateTime::NowLocal(); + m_clock.reset(new MockClock(now)); + m_state.reset(new State); + m_state->timezones.reset(new Timezones); + m_state->clock = std::dynamic_pointer_cast<Clock>(m_clock); + m_state->planner.reset(new MockPlanner); + m_state->planner->time = now; + m_state->locations.reset(new Locations); + m_state->calendar_day = now; + + // build the actions on top of the state + m_mock_actions.reset(new MockActions(m_state)); + m_actions = std::dynamic_pointer_cast<Actions>(m_mock_actions); + } + + virtual void TearDown() + { + m_actions.reset(); + m_mock_actions.reset(); + m_state.reset(); + m_clock.reset(); + + super::TearDown(); + } +}; + diff --git a/tests/test-actions.cc b/tests/test-actions.cc new file mode 100644 index 0000000..4329608 --- /dev/null +++ b/tests/test-actions.cc @@ -0,0 +1,173 @@ +/* + * 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/actions.h> + +#include "state-fixture.h" + +using namespace unity::indicator::datetime; + +typedef StateFixture ActionsFixture; + +TEST_F(ActionsFixture, ActionsExist) +{ + EXPECT_TRUE(m_actions != nullptr); + + const char* names[] = { "desktop-header", + "calendar", + "set-location", + "activate-planner", + "activate-appointment", + "activate-phone-settings", + "activate-phone-clock-app", + "activate-desktop-settings" }; + for(const auto& name: names) + { + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), name)); + } +} + +TEST_F(ActionsFixture, ActivateDesktopSettings) +{ + const auto action_name = "activate-desktop-settings"; + const auto expected_action = MockActions::OpenDesktopSettings; + + auto action_group = m_actions->action_group(); + auto history = m_mock_actions->history(); + EXPECT_EQ(0, history.size()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneSettings) +{ + const auto action_name = "activate-phone-settings"; + const auto expected_action = MockActions::OpenPhoneSettings; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneClockApp) +{ + const auto action_name = "activate-phone-clock-app"; + const auto expected_action = MockActions::OpenPhoneClockApp; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlanner) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto expected_action = MockActions::OpenPlanner; + auto v = g_variant_new_int64(0); + g_action_group_activate_action(action_group, action_name, v); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlannerAt) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto now = DateTime::NowLocal(); + auto v = g_variant_new_int64(now.to_unix()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenPlannerAt; + EXPECT_EQ(std::vector<MockActions::Action>({a}), m_mock_actions->history()); + EXPECT_EQ(now.to_unix(), m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, SetLocation) +{ + const auto action_name = "set-location"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string("America/Chicago Oklahoma City"); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetLocation; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ("America/Chicago", m_mock_actions->zone()); + EXPECT_EQ("Oklahoma City", m_mock_actions->name()); +} + +TEST_F(ActionsFixture, SetCalendarDate) +{ + const auto action_name = "calendar"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto unix = m_state->clock->localtime().to_unix(); + auto v = g_variant_new_int64(unix); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetCalendarDate; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ(unix, m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, OpenAppointment) +{ + Appointment appt; + appt.uid = "some arbitrary uid"; + appt.url = "http://www.canonical.com/"; + m_state->planner->upcoming.set(std::vector<Appointment>({appt})); + + const auto action_name = "activate-appointment"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string(appt.uid.c_str()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenAppointment; + ASSERT_EQ(1, m_mock_actions->history().size()); + ASSERT_EQ(a, m_mock_actions->history()[0]); + EXPECT_EQ(appt.url, m_mock_actions->url()); +} + diff --git a/tests/test-clock.cc b/tests/test-clock.cc index a0b4360..7d3a35e 100644 --- a/tests/test-clock.cc +++ b/tests/test-clock.cc @@ -37,27 +37,27 @@ class ClockFixture: public GlibFixture typedef GlibFixture super; static void - on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + on_bus_opened(GObject* /*object*/, GAsyncResult* res, gpointer gself) { auto self = static_cast<ClockFixture*>(gself); GError * err = 0; - self->system_bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); + self->system_bus = g_bus_get_finish(res, &err); + g_assert_no_error(err); - g_main_loop_quit (self->loop); + g_main_loop_quit(self->loop); } static void - on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + on_bus_closed(GObject* /*object*/, GAsyncResult* res, gpointer gself) { auto self = static_cast<ClockFixture*>(gself); GError * err = 0; - g_dbus_connection_close_finish (self->system_bus, res, &err); - g_assert_no_error (err); + g_dbus_connection_close_finish(self->system_bus, res, &err); + g_assert_no_error(err); - g_main_loop_quit (self->loop); + g_main_loop_quit(self->loop); } protected: @@ -65,41 +65,41 @@ class ClockFixture: public GlibFixture GTestDBus * test_dbus; GDBusConnection * system_bus; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); - g_debug ("test_dbus's address is %s", address); + test_dbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(test_dbus); + const char * address = g_test_dbus_get_bus_address(test_dbus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); + g_debug("test_dbus's address is %s", address); // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); + g_bus_get(G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run(loop); } - virtual void TearDown () + virtual void TearDown() { // close the system bus - g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); - g_main_loop_run (loop); - g_clear_object (&system_bus); + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&system_bus); // tear down the test dbus - g_test_dbus_down (test_dbus); - g_clear_object (&test_dbus); + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); - super::TearDown (); + super::TearDown(); } public: - void emitPrepareForSleep () + void emitPrepareForSleep() { - g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), + g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), NULL, "/org/freedesktop/login1", // object path "org.freedesktop.login1.Manager", // interface @@ -115,93 +115,91 @@ class ClockFixture: public GlibFixture #define TIMEZONE_FILE (SANDBOX"/timezone") -TEST_F (ClockFixture, HelloFixture) +TEST_F(ClockFixture, HelloFixture) { - std::shared_ptr<Timezones> zones (new Timezones); + std::shared_ptr<Timezones> zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); + LiveClock clock(zones); #if 0 - GTimeZone * tz_nyc = g_time_zone_new (file_timezone.c_str()); - GDateTime * now_nyc = g_date_time_new_now (tz_nyc); + GTimeZone * tz_nyc = g_time_zone_new(file_timezone.c_str()); + GDateTime * now_nyc = g_date_time_new_now(tz_nyc); GDateTime * now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_nyc); - g_time_zone_unref (tz_nyc); + EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); + EXPECT_LE(abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); + g_date_time_unref(now); + g_date_time_unref(now_nyc); + g_time_zone_unref(tz_nyc); /// change the timezones! clock.skewDetected.connect([this](){ g_main_loop_quit(loop); }); file_timezone = "America/Los_Angeles"; - g_idle_add ([](gpointer str){ + g_idle_add([](gpointer str){ set_file(static_cast<const char*>(str)); return G_SOURCE_REMOVE; }, const_cast<char*>(file_timezone.c_str())); - g_main_loop_run (loop); + g_main_loop_run(loop); - GTimeZone * tz_la = g_time_zone_new (file_timezone.c_str()); - GDateTime * now_la = g_date_time_new_now (tz_la); + GTimeZone * tz_la = g_time_zone_new(file_timezone.c_str()); + GDateTime * now_la = g_date_time_new_now(tz_la); now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_la); - g_time_zone_unref (tz_la); + EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); + EXPECT_LE(abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); + g_date_time_unref(now); + g_date_time_unref(now_la); + g_time_zone_unref(tz_la); #endif } -TEST_F (ClockFixture, TimezoneChangeTriggersSkew) +TEST_F(ClockFixture, TimezoneChangeTriggersSkew) { - std::shared_ptr<Timezones> zones (new Timezones); + std::shared_ptr<Timezones> zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); + LiveClock clock(zones); //std::string file_timezone = "America/New_York"; - //set_file (file_timezone); - //std::shared_ptr<TimezoneDetector> detector (new TimezoneDetector(TIMEZONE_FILE)); - //LiveClock clock (detector); - - GTimeZone * tz_nyc = g_time_zone_new ("America/New_York"); - GDateTime * now_nyc = g_date_time_new_now (tz_nyc); - GDateTime * now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_nyc); - g_time_zone_unref (tz_nyc); + //set_file(file_timezone); + //std::shared_ptr<TimezoneDetector> detector(new TimezoneDetector(TIMEZONE_FILE)); + //LiveClock clock(detector); + + auto tz_nyc = g_time_zone_new("America/New_York"); + auto now_nyc = g_date_time_new_now(tz_nyc); + auto now = clock.localtime(); + EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_nyc,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_nyc); + g_time_zone_unref(tz_nyc); /// change the timezones! clock.skewDetected.connect([this](){ - g_main_loop_quit(loop); - }); - g_idle_add ([](gpointer gs){ - static_cast<Timezones*>(gs)->timezone.set("America/Los_Angeles"); - return G_SOURCE_REMOVE; - }, zones.get()); - g_main_loop_run (loop); - - GTimeZone * tz_la = g_time_zone_new ("America/Los_Angeles"); - GDateTime * now_la = g_date_time_new_now (tz_la); + g_main_loop_quit(loop); + }); + g_idle_add([](gpointer gs){ + static_cast<Timezones*>(gs)->timezone.set("America/Los_Angeles"); + return G_SOURCE_REMOVE; + }, zones.get()); + g_main_loop_run(loop); + + auto tz_la = g_time_zone_new("America/Los_Angeles"); + auto now_la = g_date_time_new_now(tz_la); now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_la); - g_time_zone_unref (tz_la); + EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_la,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_la); + g_time_zone_unref(tz_la); } /** * Confirm that a "PrepareForSleep" event wil trigger a skew event */ -TEST_F (ClockFixture, SleepTriggersSkew) +TEST_F(ClockFixture, SleepTriggersSkew) { - std::shared_ptr<Timezones> zones (new Timezones); + std::shared_ptr<Timezones> zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); - wait_msec (500); // wait for the bus to set up + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up bool skewed = false; clock.skewDetected.connect([&skewed, this](){ @@ -210,12 +208,12 @@ TEST_F (ClockFixture, SleepTriggersSkew) return G_SOURCE_REMOVE; }); - g_idle_add ([](gpointer gself){ - static_cast<ClockFixture*>(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; + g_idle_add([](gpointer gself){ + static_cast<ClockFixture*>(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; }, this); - wait_msec (1000); + wait_msec(1000); EXPECT_TRUE(skewed); } @@ -223,12 +221,12 @@ TEST_F (ClockFixture, SleepTriggersSkew) * Confirm that normal time passing doesn't trigger a skew event. * that idling changing the clock's time triggers a skew event */ -TEST_F (ClockFixture, IdleDoesNotTriggerSkew) +TEST_F(ClockFixture, IdleDoesNotTriggerSkew) { - std::shared_ptr<Timezones> zones (new Timezones); + std::shared_ptr<Timezones> zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); - wait_msec (500); // wait for the bus to set up + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up bool skewed = false; clock.skewDetected.connect([&skewed](){ @@ -239,6 +237,6 @@ TEST_F (ClockFixture, IdleDoesNotTriggerSkew) const unsigned int intervalSec = 4; clock.skewTestIntervalSec.set(intervalSec); - wait_msec (intervalSec * 2.5 * 1000); - EXPECT_FALSE (skewed); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); } diff --git a/tests/test-core.cc b/tests/test-core.cc deleted file mode 100644 index 7ed38a9..0000000 --- a/tests/test-core.cc +++ /dev/null @@ -1,148 +0,0 @@ - -/* - * 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 <condition_variable> -#include <mutex> -#include <queue> -#include <thread> - -#include <langinfo.h> -#include <locale.h> - -#include <glib/gi18n.h> - -#include <core/connection.h> -#include <core/signal.h> -#include <core/property.h> - -#include "glib-fixture.h" - -/*** -**** -***/ - -class CoreFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp () - { - super::SetUp (); - } - - virtual void TearDown () - { - super::TearDown (); - } -}; - -namespace -{ -struct EventLoop -{ - typedef std::function<void()> Handler; - - void stop() - { - stop_requested = true; - } - - void run() - { - while (!stop_requested) - { - std::unique_lock<std::mutex> ul(guard); - wait_condition.wait_for( - ul, - std::chrono::milliseconds{500}, - [this]() { return handlers.size() > 0; }); - - std::cerr << "handlers.size() is " << handlers.size() << std::endl; - while (handlers.size() > 0) - { - std::cerr << "gaba begin" << std::endl; - handlers.front()(); - std::cerr << "gaba end" << std::endl; - handlers.pop(); - } - } - } - - void dispatch(const Handler& h) - { -std::cerr << "in dispatch" << std::endl; - std::lock_guard<std::mutex> lg(guard); - handlers.push(h); - } - - bool stop_requested = false; - std::queue<Handler> handlers; - std::mutex guard; - std::condition_variable wait_condition; -}; -} - - -TEST_F (CoreFixture, HelloWorld) -{ - // We instantiate an event loop and run it on a different thread than the main one. - EventLoop dispatcher; - std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }}; - std::thread::id dispatcher_thread_id = dispatcher_thread.get_id(); - - // The signal that we want to dispatch via the event loop. - core::Signal<int, double> s; - - static const int expected_invocation_count = 10000; - - // Setup the connection. For each invocation we check that the id of the - // thread the handler is being called upon equals the thread that the - // event loop is running upon. - auto connection = s.connect( - [&dispatcher, dispatcher_thread_id](int value, double d) - { - std::cerr << "this is the lambda" << std::endl; - EXPECT_EQ(dispatcher_thread_id, - std::this_thread::get_id()); - - std::cout << d << std::endl; - - if (value == expected_invocation_count) - dispatcher.stop(); - }); - - // Route the connection via the dispatcher - connection.dispatch_via( - std::bind( - &EventLoop::dispatch, - std::ref(dispatcher), - std::placeholders::_1)); - - // Invoke the signal from the main thread. - for (unsigned int i = 1; i <= expected_invocation_count; i++) - s(i, 42.); - - if (dispatcher_thread.joinable()) - dispatcher_thread.join(); -} diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h new file mode 100644 index 0000000..fc7ab5a --- /dev/null +++ b/tests/test-dbus-fixture.h @@ -0,0 +1,100 @@ +/* + * 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 "glib-fixture.h" + +/*** +**** +***/ + +class TestDBusFixture: public GlibFixture +{ + public: + + TestDBusFixture() {} + + TestDBusFixture(const std::vector<std::string>& service_dirs_in): service_dirs(service_dirs_in) {} + + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast<TestDBusFixture*>(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast<TestDBusFixture*>(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus; + GDBusConnection * system_bus; + const std::vector<std::string> service_dirs; + + virtual void SetUp () + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + for (const auto& dir : service_dirs) + g_test_dbus_add_service_dir (test_dbus, dir.c_str()); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown () + { + // close the system bus + g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); + g_main_loop_run (loop); + g_clear_object (&system_bus); + + // tear down the test dbus + g_test_dbus_down (test_dbus); + g_clear_object (&test_dbus); + + super::TearDown (); + } +}; diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc index 641338b..42c828c 100644 --- a/tests/test-formatter.cc +++ b/tests/test-formatter.cc @@ -18,9 +18,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "clock-mock.h" #include "glib-fixture.h" +#include <datetime/clock-mock.h> #include <datetime/formatter.h> #include <datetime/settings-shared.h> @@ -30,9 +30,10 @@ #include <locale.h> using unity::indicator::datetime::Clock; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::DesktopFormatter; using unity::indicator::datetime::MockClock; using unity::indicator::datetime::PhoneFormatter; -using unity::indicator::datetime::DesktopFormatter; /*** **** @@ -94,7 +95,8 @@ class FormatterFixture: public GlibFixture TEST_F (FormatterFixture, TestPhoneHeader) { GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr<MockClock> mock (new MockClock(now)); + std::shared_ptr<MockClock> mock (new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr<Clock> clock = std::dynamic_pointer_cast<Clock>(mock); // test the default value in a 24h locale @@ -146,8 +148,9 @@ TEST_F (FormatterFixture, TestDesktopHeader) { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } }; - GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr<MockClock> mock (new MockClock(now)); + GDateTime * now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr<MockClock> mock(new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr<Clock> clock = std::dynamic_pointer_cast<Clock>(mock); for (int i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) @@ -174,44 +177,44 @@ TEST_F (FormatterFixture, TestDesktopHeader) */ TEST_F (FormatterFixture, TestUpcomingTimes) { - GDateTime * a = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + auto a = g_date_time_new_local (2020, 10, 31, 18, 30, 59); struct { gboolean is_12h; GDateTime * now; GDateTime * then; - GDateTime * then_end; const char * expected_format_string; } test_cases[] = { - { true, g_date_time_ref(a), g_date_time_ref(a), NULL, "%l:%M %p" }, // identical time - { true, g_date_time_ref(a), g_date_time_add_hours(a,1), NULL, "%l:%M %p" }, // later today - { true, g_date_time_ref(a), g_date_time_add_days(a,1), NULL, "Tomorrow" EM_SPACE "%l:%M %p" }, // tomorrow - { true, g_date_time_ref(a), g_date_time_add_days(a,2), NULL, "%a" EM_SPACE "%l:%M %p" }, - { true, g_date_time_ref(a), g_date_time_add_days(a,6), NULL, "%a" EM_SPACE "%l:%M %p" }, - { true, g_date_time_ref(a), g_date_time_add_days(a,7), NULL, "%a %d %b" EM_SPACE "%l:%M %p" }, // over one week away - - { false, g_date_time_ref(a), g_date_time_ref(a), NULL, "%H:%M" }, // identical time - { false, g_date_time_ref(a), g_date_time_add_hours(a,1), NULL, "%H:%M" }, // later today - { false, g_date_time_ref(a), g_date_time_add_days(a,1), NULL, "Tomorrow" EM_SPACE "%H:%M" }, // tomorrow - { false, g_date_time_ref(a), g_date_time_add_days(a,2), NULL, "%a" EM_SPACE "%H:%M" }, - { false, g_date_time_ref(a), g_date_time_add_days(a,6), NULL, "%a" EM_SPACE "%H:%M" }, - { false, g_date_time_ref(a), g_date_time_add_days(a,7), NULL, "%a %d %b" EM_SPACE "%H:%M" } // over one week away + { true, g_date_time_ref(a), g_date_time_ref(a), "%l:%M %p" }, // identical time + { true, g_date_time_ref(a), g_date_time_add_hours(a,1), "%l:%M %p" }, // later today + { true, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%l:%M %p" }, // tomorrow + { true, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%l:%M %p" }, + { true, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%l:%M %p" }, + { true, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%l:%M %p" }, // over one week away + + { false, g_date_time_ref(a), g_date_time_ref(a), "%H:%M" }, // identical time + { false, g_date_time_ref(a), g_date_time_add_hours(a,1), "%H:%M" }, // later today + { false, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%H:%M" }, // tomorrow + { false, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%H:%M" }, + { false, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%H:%M" }, + { false, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%H:%M" } // over one week away }; for (int i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) { if (test_cases[i].is_12h ? Set12hLocale() : Set24hLocale()) { - std::shared_ptr<MockClock> mock (new MockClock(test_cases[i].now)); + DateTime tmp(test_cases[i].now); + tmp.get(); + std::shared_ptr<MockClock> mock (new MockClock(tmp));//DateTime(test_cases[i].now))); std::shared_ptr<Clock> clock = std::dynamic_pointer_cast<Clock>(mock); DesktopFormatter f (clock); - std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + std::string fmt = f.getRelativeFormat (test_cases[i].then); ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); g_clear_pointer (&test_cases[i].now, g_date_time_unref); g_clear_pointer (&test_cases[i].then, g_date_time_unref); - g_clear_pointer (&test_cases[i].then_end, g_date_time_unref); } } @@ -224,11 +227,11 @@ TEST_F (FormatterFixture, TestUpcomingTimes) */ TEST_F (FormatterFixture, TestEventTimes) { - GDateTime * day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - GDateTime * day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - GDateTime * day_end = g_date_time_add_days (day_begin, 1); - GDateTime * tomorrow_begin = g_date_time_add_days (day_begin, 1); - GDateTime * tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); + auto day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + auto day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + auto day_end = g_date_time_add_days (day_begin, 1); + auto tomorrow_begin = g_date_time_add_days (day_begin, 1); + auto tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); struct { bool is_12h; @@ -247,7 +250,7 @@ TEST_F (FormatterFixture, TestEventTimes) { if (test_cases[i].is_12h ? Set12hLocale() : Set24hLocale()) { - std::shared_ptr<MockClock> mock (new MockClock(test_cases[i].now)); + std::shared_ptr<MockClock> mock (new MockClock(DateTime(test_cases[i].now))); std::shared_ptr<Clock> clock = std::dynamic_pointer_cast<Clock>(mock); DesktopFormatter f (clock); diff --git a/tests/test-indicator.cc b/tests/test-indicator.cc deleted file mode 100644 index 2480c94..0000000 --- a/tests/test-indicator.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2012 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 <gtest/gtest.h> - -#include <glib-object.h> - -/*** -**** -***/ - -namespace -{ - void ensure_glib_initialized () - { - static bool initialized = false; - - if (G_UNLIKELY(!initialized)) - { - initialized = true; - g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); - } - } -} - -/*** -**** -***/ - -class IndicatorTest : public ::testing::Test -{ - private: - - guint log_handler_id; - - int log_count_ipower_actual; - - static void log_count_func (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) - { - reinterpret_cast<IndicatorTest*>(user_data)->log_count_ipower_actual++; - } - - protected: - - int log_count_ipower_expected; - - protected: - - virtual void SetUp() - { - const GLogLevelFlags flags = GLogLevelFlags(G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING); - log_handler_id = g_log_set_handler ("Indicator-Power", flags, log_count_func, this); - log_count_ipower_expected = 0; - log_count_ipower_actual = 0; - - ensure_glib_initialized (); - } - - virtual void TearDown() - { - ASSERT_EQ (log_count_ipower_expected, log_count_ipower_actual); - g_log_remove_handler ("Indicator-Power", log_handler_id); - } -}; - -/*** -**** -***/ - -TEST_F(IndicatorTest, HelloWorld) -{ - ASSERT_TRUE (TRUE); -} diff --git a/tests/test-locations.cc b/tests/test-locations.cc index c833bed..edaac69 100644 --- a/tests/test-locations.cc +++ b/tests/test-locations.cc @@ -19,9 +19,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "clock-mock.h" #include "glib-fixture.h" +#include <datetime/clock-mock.h> #include <datetime/locations.h> #include <datetime/locations-settings.h> #include <datetime/settings-shared.h> @@ -53,120 +53,120 @@ class LocationsFixture: public GlibFixture const std::string nyc = "America/New_York"; const std::string chicago = "America/Chicago"; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); - settings = g_settings_new (SETTINGS_INTERFACE); + 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); + 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 })); + timezones.reset(new Timezones); + timezones->timezone.set(chicago); + timezones->timezones.set(std::set<std::string>({ nyc, chicago })); } - virtual void TearDown () + virtual void TearDown() { - //timezones.reset (nullptr); - g_clear_object (&settings); + //timezones.reset(nullptr); + g_clear_object(&settings); - super::TearDown (); + super::TearDown(); } }; -TEST_F (LocationsFixture, Timezones) +TEST_F(LocationsFixture, Timezones) { - g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, false); + g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, false); - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + 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); + 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) +TEST_F(LocationsFixture, SettingsLocations) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + 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); + 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) +TEST_F(LocationsFixture, ChangeLocationStrings) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + 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_main_loop_quit(loop); }); - g_idle_add ([](gpointer gsettings){ + 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); + g_settings_set_strv(static_cast<GSettings*>(gsettings), SETTINGS_LOCATIONS_S, strv); return G_SOURCE_REMOVE; }, settings); - g_main_loop_run (loop); + g_main_loop_run(loop); - EXPECT_TRUE (locations_changed); + 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); + 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) +TEST_F(LocationsFixture, ChangeLocationVisibility) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + 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_main_loop_quit(loop); }); - g_idle_add ([](gpointer gsettings){ - g_settings_set_boolean (static_cast<GSettings*>(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + 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); + g_main_loop_run(loop); - EXPECT_TRUE (locations_changed); + 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); + 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); } diff --git a/tests/test-menus.cc b/tests/test-menus.cc new file mode 100644 index 0000000..88e4706 --- /dev/null +++ b/tests/test-menus.cc @@ -0,0 +1,406 @@ +/* + * 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 "actions-mock.h" +#include "state-fixture.h" + +#include <datetime/clock-mock.h> +#include <datetime/formatter.h> +#include <datetime/locations.h> +#include <datetime/menu.h> +#include <datetime/planner-mock.h> +#include <datetime/service.h> +#include <datetime/state.h> +#include <datetime/timezones.h> + +#include <gio/gio.h> + +using namespace unity::indicator::datetime; + +class MenuFixture: public StateFixture +{ +private: + typedef StateFixture super; + +protected: + std::shared_ptr<MenuFactory> m_menu_factory; + std::vector<std::shared_ptr<Menu>> m_menus; + + virtual void SetUp() + { + super::SetUp(); + + // build the menus on top of the actions and state + m_menu_factory.reset(new MenuFactory(m_actions, m_state)); + for(int i=0; i<Menu::NUM_PROFILES; i++) + m_menus.push_back(m_menu_factory->buildMenu(Menu::Profile(i))); + } + + virtual void TearDown() + { + m_menus.clear(); + m_menu_factory.reset(); + + super::TearDown(); + } + + void InspectHeader(GMenuModel* menu_model, const std::string& name) + { + // check that there's a header menuitem + EXPECT_EQ(1,g_menu_model_get_n_items(menu_model)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(menu_model, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.root", str); + g_clear_pointer(&str, g_free); + g_menu_model_get_item_attribute(menu_model, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + const auto action_name = name + "-header"; + EXPECT_EQ(std::string("indicator.")+action_name, str); + g_clear_pointer(&str, g_free); + + // check the header + auto dict = g_action_group_get_action_state(m_actions->action_group(), action_name.c_str()); + EXPECT_TRUE(dict != nullptr); + EXPECT_TRUE(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); + auto v = g_variant_lookup_value(dict, "accessible-desc", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "label", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "title", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "visible", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + g_variant_unref(dict); + } + + void InspectCalendar(GMenuModel* menu_model, Menu::Profile profile) + { + gchar* str = nullptr; + const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); + const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); + + // get the calendar section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + + // should be one or two items: a date label and maybe a calendar + ASSERT_TRUE(section != nullptr); + auto n_expected = calendar_expected ? 2 : 1; + EXPECT_EQ(n_expected, g_menu_model_get_n_items(section)); + + // look at the date menuitem + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + const auto now = m_state->clock->localtime(); + EXPECT_EQ(now.format("%A, %e %B %Y"), str); + + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + + // look at the calendar menuitem + if (calendar_expected) + { + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_STREQ("indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, "activation-action", "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + + // now change the clock and see if the date label changes appropriately + + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + m_clock->set_localtime(tomorrow); + wait_msec(); + + section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + EXPECT_EQ(tomorrow.format("%A, %e %B %Y"), str); + g_clear_pointer(&str, g_free); + g_clear_object(§ion); + + // cleanup + g_object_unref(submenu); + } + + void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) + { + const bool appointments_expected = (profile == Menu::Desktop) + || (profile == Menu::Phone); + + // get the Appointments section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + + // there shouldn't be any menuitems when "show events" is false + m_state->show_events.set(false); + wait_msec(); + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + std::vector<Appointment> appointments; + m_state->show_events.set(true); + m_state->planner->upcoming.set(appointments); + wait_msec(); + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + int expected_n = appointments_expected ? 1 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // try adding a few appointments and see if the menu updates itself + + const auto now = m_state->clock->localtime(); + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + const auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + + Appointment a1; // an alarm clock appointment + a1.color = "red"; + a1.summary = "Alarm"; + a1.summary = "http://www.example.com/"; + a1.uid = "example"; + a1.has_alarms = true; + a1.begin = a1.end = tomorrow; + appointments.push_back(a1); + + Appointment a2; // a non-alarm appointment + a2.color = "green"; + a2.summary = "Other Text"; + a2.summary = "http://www.monkey.com/"; + a2.uid = "monkey"; + a2.has_alarms = false; + a2.begin = a2.end = tomorrow; + appointments.push_back(a2); + + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + expected_n = appointments_expected ? 3 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + if (appointments_expected) + { + gchar * str = nullptr; + + // test the alarm + // - confirm it has an x-canonical-type of "alarm" + g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.alarm", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm it has a serialized icon attribute + auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); + EXPECT_TRUE(v != nullptr); + auto icon = g_icon_deserialize(v); + EXPECT_TRUE(icon != nullptr); + g_clear_object(&icon); + g_clear_pointer(&v, g_variant_unref); + + // test the appointment + // - confirm it has an x-canonical-type of "appointment" + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.appointment", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm its color matches the one we fed the appointments vector + g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); + EXPECT_EQ(a2.color, str); + g_clear_pointer(&str, g_free); + } + g_clear_object(§ion); + + g_object_unref(submenu); + } + + void CompareLocationsTo(GMenuModel* menu_model, const std::vector<Location>& locations) + { + // get the Locations section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Locations, G_MENU_LINK_SECTION); + + // confirm that section's menuitems mirror the "locations" vector + const auto n = locations.size(); + ASSERT_EQ(n, g_menu_model_get_n_items(section)); + for (guint i=0; i<n; i++) + { + gchar* str = nullptr; + + // confirm that the x-canonical-type is right + g_menu_model_get_item_attribute(section, i, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.location", str); + g_clear_pointer(&str, g_free); + + // confirm that the timezones match the ones in the vector + g_menu_model_get_item_attribute(section, i, "x-canonical-timezone", "s", &str); + EXPECT_EQ(locations[i].zone(), str); + g_clear_pointer(&str, g_free); + + // confirm that x-canonical-time-format has some kind of time format string + g_menu_model_get_item_attribute(section, i, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str && (strchr(str,'%')!=nullptr)); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + g_clear_object(&submenu); + } + + void InspectLocations(GMenuModel* menu_model, Menu::Profile profile) + { + const bool locations_expected = profile == Menu::Desktop; + + // when there aren't any locations, confirm the menu is empty + const std::vector<Location> empty; + m_state->locations->locations.set(empty); + wait_msec(); + CompareLocationsTo(menu_model, empty); + + // add some locations and confirm the menu picked up our changes + Location l1 ("America/Chicago", "Dallas"); + Location l2 ("America/Arizona", "Phoenix"); + std::vector<Location> locations({l1, l2}); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + + // now remove one of the locations... + locations.pop_back(); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + } + + void InspectSettings(GMenuModel* menu_model, Menu::Profile profile) + { + std::string expected_action; + + if (profile == Menu::Desktop) + expected_action = "indicator.activate-desktop-settings"; + else if (profile == Menu::Phone) + expected_action = "indicator.activate-phone-settings"; + + // get the Settings section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Settings, G_MENU_LINK_SECTION); + + if (expected_action.empty()) + { + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + } + else + { + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_EQ(expected_action, str); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + g_object_unref(submenu); + } +}; + + +TEST_F(MenuFixture, HelloWorld) +{ + EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); + for (int i=0; i<Menu::NUM_PROFILES; i++) + { + EXPECT_TRUE(m_menus[i] != false); + EXPECT_TRUE(m_menus[i]->menu_model() != nullptr); + EXPECT_EQ(i, m_menus[i]->profile()); + } + EXPECT_EQ(m_menus[Menu::Desktop]->name(), "desktop"); +} + +TEST_F(MenuFixture, Header) +{ + for(auto& menu : m_menus) + InspectHeader(menu->menu_model(), menu->name()); +} + +TEST_F(MenuFixture, Sections) +{ + for(auto& menu : m_menus) + { + // check that the header has a submenu + auto menu_model = menu->menu_model(); + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + EXPECT_TRUE(submenu != nullptr); + EXPECT_EQ(Menu::NUM_SECTIONS, g_menu_model_get_n_items(submenu)); + g_object_unref(submenu); + } +} + +TEST_F(MenuFixture, Calendar) +{ + for(auto& menu : m_menus) + InspectCalendar(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Appointments) +{ + for(auto& menu : m_menus) + InspectAppointments(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Locations) +{ + for(auto& menu : m_menus) + InspectLocations(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Settings) +{ + for(auto& menu : m_menus) + InspectSettings(menu->menu_model(), menu->profile()); +} + + diff --git a/tests/test-planner-eds.cc b/tests/test-planner-eds.cc deleted file mode 100644 index 986a45e..0000000 --- a/tests/test-planner-eds.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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/planner-eds.h> - -#include <glib/gi18n.h> - -#include <langinfo.h> -#include <locale.h> - -using unity::indicator::datetime::Appointment; -using unity::indicator::datetime::DateTime; -using unity::indicator::datetime::PlannerEds; - -/*** -**** -***/ - -class PlannerEdsFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp () - { - super::SetUp (); - } - - virtual void TearDown () - { - super::TearDown (); - } -}; - -/*** -**** -***/ - -TEST_F (PlannerEdsFixture, HelloWorld) -{ - DateTime dt; - dt = g_date_time_new_now_local(); - - PlannerEds planner; - planner.time.set (dt); - g_main_loop_run (loop); -} - diff --git a/tests/test-planner.cc b/tests/test-planner.cc index 8c74366..3072aea 100644 --- a/tests/test-planner.cc +++ b/tests/test-planner.cc @@ -17,18 +17,20 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "clock-mock.h" #include "glib-fixture.h" #include <datetime/appointment.h> +#include <datetime/clock-mock.h> +#include <datetime/date-time.h> #include <datetime/planner.h> - -#include <glib/gi18n.h> +#include <datetime/planner-eds.h> #include <langinfo.h> #include <locale.h> using unity::indicator::datetime::Appointment; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::PlannerEds; /*** **** @@ -36,33 +38,48 @@ using unity::indicator::datetime::Appointment; typedef GlibFixture PlannerFixture; +TEST_F(PlannerFixture, EDS) +{ + PlannerEds planner; + wait_msec(100); + + auto now = g_date_time_new_now_local(); + planner.time.set(DateTime(now)); + wait_msec(2500); + + std::vector<Appointment> thisMonth = planner.thisMonth.get(); + std::cerr << thisMonth.size() << " appointments this month" << std::endl; + for(const auto& a : thisMonth) + std::cerr << a.summary << std::endl; +} + -TEST_F (PlannerFixture, HelloWorld) +TEST_F(PlannerFixture, HelloWorld) { - GDateTime * halloween = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - GDateTime * christmas = g_date_time_new_local (2020, 12, 25, 0, 0, 0); + auto halloween = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + auto christmas = g_date_time_new_local(2020, 12, 25, 0, 0, 0); Appointment a; a.summary = "Test"; a.begin = halloween; - a.end = g_date_time_add_hours (halloween, 1); + a.end = g_date_time_add_hours(halloween, 1); const Appointment b = a; a.summary = "Foo"; - EXPECT_EQ (a.summary, "Foo"); - EXPECT_EQ (b.summary, "Test"); - EXPECT_EQ (0, g_date_time_compare (a.begin(), b.begin())); - EXPECT_EQ (0, g_date_time_compare (a.end(), b.end())); + EXPECT_EQ(a.summary, "Foo"); + EXPECT_EQ(b.summary, "Test"); + EXPECT_EQ(0, g_date_time_compare(a.begin(), b.begin())); + EXPECT_EQ(0, g_date_time_compare(a.end(), b.end())); Appointment c; c.begin = christmas; - c.end = g_date_time_add_hours (christmas, 1); + c.end = g_date_time_add_hours(christmas, 1); Appointment d; d = c; - EXPECT_EQ (0, g_date_time_compare (c.begin(), d.begin())); - EXPECT_EQ (0, g_date_time_compare (c.end(), d.end())); + EXPECT_EQ(0, g_date_time_compare(c.begin(), d.begin())); + EXPECT_EQ(0, g_date_time_compare(c.end(), d.end())); a = d; - EXPECT_EQ (0, g_date_time_compare (d.begin(), a.begin())); - EXPECT_EQ (0, g_date_time_compare (d.end(), a.end())); + EXPECT_EQ(0, g_date_time_compare(d.begin(), a.begin())); + EXPECT_EQ(0, g_date_time_compare(d.end(), a.end())); } diff --git a/tests/test-skew.cc b/tests/test-skew.cc deleted file mode 100644 index 90c0164..0000000 --- a/tests/test-skew.cc +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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 "glib-fixture.h" - -#include "Clock.h" -#include "MockClock.h" - -/*** -**** -***/ - -using unity::indicator::datetime::Clock; -using unity::indicator::datetime::MockClock; -using unity::indicator::datetime::SkewDetector; - -class SkewFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - static void - on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) - { - auto self = static_cast<SkewFixture*>(gself); - - GError * err = 0; - self->system_bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - static void - on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) - { - auto self = static_cast<SkewFixture*>(gself); - - GError * err = 0; - g_dbus_connection_close_finish (self->system_bus, res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - protected: - - std::shared_ptr<Clock> mockClock; - GTestDBus * test_dbus; - GDBusConnection * system_bus; - - virtual void SetUp () - { - super::SetUp (); - - // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); - g_debug ("test_dbus's address is %s", address); - - // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); - - // create a clock - GDateTime * now = g_date_time_new_now_local (); - mockClock.reset (new MockClock (now)); - g_date_time_unref (now); - } - - virtual void TearDown () - { - mockClock.reset(); - - // close the system bus - g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); - g_main_loop_run (loop); - g_clear_object (&system_bus); - - // tear down the test dbus - g_test_dbus_down (test_dbus); - g_clear_object (&test_dbus); - - super::TearDown (); - } - - public: - - void emitPrepareForSleep () - { - g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), - NULL, - "/org/freedesktop/login1", // object path - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - g_variant_new("(b)", FALSE), - NULL); - } -}; - - -/** - * A simple "hello world" style test. - */ -TEST_F (SkewFixture, CanInstantiate) -{ - SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock)); - wait_msec (500); // wait for the bus to set up -} - - -/** - * Confirm that changing the clock's timezone triggers a skew event - */ -TEST_F (SkewFixture, ChangingTimezonesTriggersEvent) -{ - SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add([](gpointer gclock){ - GDateTime * arbitrary = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - static_cast<MockClock*>(gclock)->setLocaltime (arbitrary); - g_date_time_unref (arbitrary); - return G_SOURCE_REMOVE; - }, mockClock.get()); - - wait_msec (1000); - - EXPECT_TRUE (skewed); - GDateTime * expected = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - GDateTime * actual = mockClock->localtime(); - EXPECT_EQ (0, g_date_time_compare (expected, actual)); - g_date_time_unref (actual); - g_date_time_unref (expected); -} - -/** - * Confirm that a "PrepareForSleep" event wil trigger a skew event - */ -TEST_F (SkewFixture, PrepareForSleep) -{ - SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add ([](gpointer gself){ - static_cast<SkewFixture*>(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; - }, this); - - wait_msec (1000); - EXPECT_TRUE(skewed); -} - - -/** - * Confirm that normal time passing doesn't trigger a skew event. - * that idling changing the clock's time triggers a skew event - */ -TEST_F (SkewFixture, IdleDoesNotTriggerEvent) -{ - SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - //abort(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 4; - skew.intervalSec.set(intervalSec); - wait_msec (intervalSec * 2.5 * 1000); - EXPECT_FALSE (skewed); -} diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc index 510d12c..453b353 100644 --- a/tests/test-timezone-file.cc +++ b/tests/test-timezone-file.cc @@ -52,25 +52,25 @@ class TimezoneFixture: public GlibFixture protected: - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); } - virtual void TearDown () + virtual void TearDown() { - super::TearDown (); + super::TearDown(); } public: /* convenience func to set the timezone file */ - void set_file (const std::string& text) + void set_file(const std::string& text) { - FILE * fp = fopen (TIMEZONE_FILE, "w+"); - fprintf (fp, "%s\n", text.c_str()); - fclose (fp); - sync (); + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); } }; @@ -78,56 +78,56 @@ class TimezoneFixture: public GlibFixture /** * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist */ -TEST_F (TimezoneFixture, NoFile) +TEST_F(TimezoneFixture, NoFile) { - remove (TIMEZONE_FILE); - ASSERT_FALSE (g_file_test (TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + remove(TIMEZONE_FILE); + ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); - FileTimezone tz (TIMEZONE_FILE); - testLogCount (G_LOG_LEVEL_WARNING, 1); + FileTimezone tz(TIMEZONE_FILE); + testLogCount(G_LOG_LEVEL_WARNING, 1); } /** * Test that timezone-file picks up the initial value */ -TEST_F (TimezoneFixture, InitialValue) +TEST_F(TimezoneFixture, InitialValue) { const std::string expected_timezone = "America/Chicago"; - set_file (expected_timezone); - FileTimezone tz (TIMEZONE_FILE); - ASSERT_EQ (expected_timezone, tz.timezone.get()); + set_file(expected_timezone); + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(expected_timezone, tz.timezone.get()); } /** * Test that clearing the timezone results in an empty string */ -TEST_F (TimezoneFixture, ChangedValue) +TEST_F(TimezoneFixture, ChangedValue) { const std::string initial_timezone = "America/Chicago"; const std::string changed_timezone = "America/New_York"; - set_file (initial_timezone); + set_file(initial_timezone); - FileTimezone tz (TIMEZONE_FILE); - ASSERT_EQ (initial_timezone, tz.timezone.get()); + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(initial_timezone, tz.timezone.get()); bool changed = false; auto connection = tz.timezone.changed().connect( [&changed, this](const std::string& s){ - g_message ("timezone changed to %s", s.c_str()); + g_message("timezone changed to %s", s.c_str()); changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gself){ - static_cast<TimezoneFixture*>(gself)->set_file ("America/New_York"); - // static_cast<FileTimezone*>(gtz)->timezone.set ("America/New_York"); + g_idle_add([](gpointer gself){ + static_cast<TimezoneFixture*>(gself)->set_file("America/New_York"); + // static_cast<FileTimezone*>(gtz)->timezone.set("America/New_York"); return G_SOURCE_REMOVE; }, this);//&tz); - g_main_loop_run (loop); + g_main_loop_run(loop); - ASSERT_TRUE (changed); - ASSERT_EQ (changed_timezone, tz.timezone.get()); + ASSERT_TRUE(changed); + ASSERT_EQ(changed_timezone, tz.timezone.get()); } diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc index a577fbd..4bf08a7 100644 --- a/tests/test-timezone-geoclue.cc +++ b/tests/test-timezone-geoclue.cc @@ -46,21 +46,21 @@ namespace const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} }; - gboolean emit_address_changed_idle (gpointer gdata) + gboolean emit_address_changed_idle(gpointer gdata) { auto data = static_cast<EmitAddressChangedData*>(gdata); GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal (data->mock, data->obj_client, + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, "org.freedesktop.Geoclue.Address", "AddressChanged", G_VARIANT_TYPE("(ia{ss}(idd))"), - g_variant_new_parsed ("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), + g_variant_new_parsed("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), &error); if (error) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free(error); } delete data; @@ -69,29 +69,29 @@ namespace } #endif -TEST_F (TimezoneGeoclueFixture, ChangeDetected) +TEST_F(TimezoneGeoclueFixture, ChangeDetected) { // const std::string timezone_1 = "America/Denver"; const std::string timezone_2 = "America/Chicago"; GeoclueTimezone tz; - wait_msec (500); // wait for the bus to get set up - EXPECT_EQ (timezone_1, tz.timezone.get()); + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_1, tz.timezone.get()); // start listening for a timezone change, then change the timezone bool changed = false; auto connection = tz.timezone.changed().connect( [&changed, this](const std::string& s){ - g_debug ("timezone changed to %s", s.c_str()); + g_debug("timezone changed to %s", s.c_str()); changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - setGeoclueTimezoneOnIdle (timezone_2); - //g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); - g_main_loop_run (loop); - EXPECT_EQ (timezone_2, tz.timezone.get()); + setGeoclueTimezoneOnIdle(timezone_2); + //g_timeout_add(50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); + g_main_loop_run(loop); + EXPECT_EQ(timezone_2, tz.timezone.get()); } diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc index cda53a6..d3c8e3a 100644 --- a/tests/test-timezones.cc +++ b/tests/test-timezones.cc @@ -33,33 +33,34 @@ typedef GeoclueFixture TimezonesFixture; namespace { /* convenience func to set the timezone file */ - void set_file (const std::string& text) + void set_file(const std::string& text) { - FILE * fp = fopen (TIMEZONE_FILE, "w+"); - fprintf (fp, "%s\n", text.c_str()); - fclose (fp); - sync (); + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); } } -TEST_F (TimezonesFixture, ManagerTest) +TEST_F(TimezonesFixture, ManagerTest) { std::string timezone_file = "America/New_York"; std::string timezone_geo = "America/Denver"; - set_file (timezone_file); - LiveTimezones z (TIMEZONE_FILE); - wait_msec (500); // wait for the bus to get set up - EXPECT_EQ (timezone_file, z.timezone.get()); - std::set<std::string> zones = z.timezones.get(); - EXPECT_EQ (1, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); + set_file(timezone_file); + LiveTimezones z(TIMEZONE_FILE); + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_file, z.timezone.get()); + auto zones = z.timezones.get(); + //std::set<std::string> zones = z.timezones.get(); + EXPECT_EQ(1, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); bool zone_changed = false; auto zone_connection = z.timezone.changed().connect([&zone_changed, this](const std::string&) { zone_changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); // start listening for a timezone change, then change the timezone @@ -67,24 +68,24 @@ TEST_F (TimezonesFixture, ManagerTest) auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set<std::string>& timezones) { zones_changed = true; zones = timezones; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gz) { + g_idle_add([](gpointer gz) { auto az = static_cast<LiveTimezones*>(gz); - g_message ("geolocation was %d", (int)az->geolocationEnabled.get()); - g_message ("turning geolocation on"); + g_message("geolocation was %d", (int)az->geolocationEnabled.get()); + g_message("turning geolocation on"); az->geolocationEnabled.set(true); return G_SOURCE_REMOVE; }, &z); // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property - g_main_loop_run (loop); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + g_main_loop_run(loop); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); zones_changed = false; // now tweak the geoclue value... the geoclue-detected timezone should change, @@ -92,28 +93,28 @@ TEST_F (TimezonesFixture, ManagerTest) zone_changed = false; zones_changed = false; timezone_geo = "America/Chicago"; - setGeoclueTimezoneOnIdle (timezone_geo); - g_main_loop_run (loop); - EXPECT_FALSE (zone_changed); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + setGeoclueTimezoneOnIdle(timezone_geo); + g_main_loop_run(loop); + EXPECT_FALSE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); // now set the file value... this should change both the primary property and set property zone_changed = false; zones_changed = false; timezone_file = "America/Los_Angeles"; - EXPECT_EQ (0, zones.count(timezone_file)); - g_idle_add ([](gpointer str) {set_file(static_cast<const char*>(str)); return G_SOURCE_REMOVE;}, const_cast<char*>(timezone_file.c_str())); - g_main_loop_run (loop); - EXPECT_TRUE (zone_changed); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + EXPECT_EQ(0, zones.count(timezone_file)); + g_idle_add([](gpointer str) {set_file(static_cast<const char*>(str)); return G_SOURCE_REMOVE;}, const_cast<char*>(timezone_file.c_str())); + g_main_loop_run(loop); + EXPECT_TRUE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); diff --git a/tests/test-utils.cc b/tests/test-utils.cc index d0f277b..8246396 100644 --- a/tests/test-utils.cc +++ b/tests/test-utils.cc @@ -1,21 +1,21 @@ /* -Copyright 2012 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/>. -*/ + * 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 <gtest/gtest.h> @@ -27,36 +27,33 @@ with this program. If not, see <http://www.gnu.org/licenses/>. **** ***/ -TEST (UtilsTest, SplitSettingsLocation) +TEST(UtilsTest, SplitSettingsLocation) { - guint i; - guint n; - - struct { - const char * location; - const char * expected_zone; - const char * expected_name; - } test_cases[] = { - { "America/Chicago Chicago", "America/Chicago", "Chicago" }, - { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, - { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { "UTC UTC", "UTC", "UTC" } - }; - - for (i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) + struct { + const char * location; + const char * expected_zone; + const char * expected_name; + } test_cases[] = { + { "America/Chicago Chicago", "America/Chicago", "Chicago" }, + { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, + { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, + { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, + { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, + { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, + { "UTC UTC", "UTC", "UTC" } + }; + + for(guint i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) { - char * zone = NULL; - char * name = NULL; + char * zone = NULL; + char * name = NULL; - split_settings_location (test_cases[i].location, &zone, &name); - ASSERT_STREQ (test_cases[i].expected_zone, zone); - ASSERT_STREQ (test_cases[i].expected_name, name); + split_settings_location(test_cases[i].location, &zone, &name); + ASSERT_STREQ(test_cases[i].expected_zone, zone); + ASSERT_STREQ(test_cases[i].expected_name, name); - g_free (zone); - g_free (name); + g_free(zone); + g_free(name); } } @@ -66,47 +63,43 @@ TEST (UtilsTest, SplitSettingsLocation) #define EM_SPACE "\xE2\x80\x82" -TEST (UtilsTest, GenerateTerseFormatString) +TEST(UtilsTest, GenerateTerseFormatString) { - guint i; - guint n; - GDateTime * arbitrary_day = g_date_time_new_local (2013, 6, 25, 12, 34, 56); - GDateTime * on_the_hour = g_date_time_new_local (2013, 6, 25, 12, 0, 0); - - struct { - GDateTime * now; - GDateTime * time; - const char * expected_format_string; - } test_cases[] = { - { g_date_time_ref(arbitrary_day), g_date_time_ref(arbitrary_day), "%l:%M %p" }, /* identical time */ - { g_date_time_ref(arbitrary_day), g_date_time_add_hours(arbitrary_day,1), "%l:%M %p" }, /* later today */ - { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,1), "Tomorrow" EM_SPACE "%l:%M %p" }, /* tomorrow */ - { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,2), "%a" EM_SPACE "%l:%M %p" }, - { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,6), "%a" EM_SPACE "%l:%M %p" }, - { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,7), "%d %b" EM_SPACE "%l:%M %p" }, /* over one week away */ - - { g_date_time_ref(on_the_hour), g_date_time_ref(on_the_hour), "%l %p" }, /* identical time */ - { g_date_time_ref(on_the_hour), g_date_time_add_hours(on_the_hour,1), "%l %p" }, /* later today */ - { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,1), "Tomorrow" EM_SPACE "%l %p" }, /* tomorrow */ - { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,2), "%a" EM_SPACE "%l %p" }, - { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,6), "%a" EM_SPACE "%l %p" }, - { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,7), "%d %b" EM_SPACE "%l %p" }, /* over one week away */ - }; - - for (i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) + auto arbitrary_day = g_date_time_new_local(2013, 6, 25, 12, 34, 56); + auto on_the_hour = g_date_time_new_local(2013, 6, 25, 12, 0, 0); + + struct { + GDateTime * now; + GDateTime * time; + const char * expected_format_string; + } test_cases[] = { + { g_date_time_ref(arbitrary_day), g_date_time_ref(arbitrary_day), "%l:%M %p" }, /* identical time */ + { g_date_time_ref(arbitrary_day), g_date_time_add_hours(arbitrary_day,1), "%l:%M %p" }, /* later today */ + { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,1), "Tomorrow" EM_SPACE "%l:%M %p" }, /* tomorrow */ + { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,2), "%a" EM_SPACE "%l:%M %p" }, + { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,6), "%a" EM_SPACE "%l:%M %p" }, + { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,7), "%d %b" EM_SPACE "%l:%M %p" }, /* over one week away */ + + { g_date_time_ref(on_the_hour), g_date_time_ref(on_the_hour), "%l %p" }, /* identical time */ + { g_date_time_ref(on_the_hour), g_date_time_add_hours(on_the_hour,1), "%l %p" }, /* later today */ + { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,1), "Tomorrow" EM_SPACE "%l %p" }, /* tomorrow */ + { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,2), "%a" EM_SPACE "%l %p" }, + { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,6), "%a" EM_SPACE "%l %p" }, + { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,7), "%d %b" EM_SPACE "%l %p" }, /* over one week away */ + }; + + for(guint i=0, n=G_N_ELEMENTS(test_cases); i<n; i++) { - char * format_string; - - format_string = generate_terse_format_string_at_time (test_cases[i].now, - test_cases[i].time); + auto format_string = generate_terse_format_string_at_time(test_cases[i].now, + test_cases[i].time); - ASSERT_STREQ (test_cases[i].expected_format_string, format_string); + ASSERT_STREQ(test_cases[i].expected_format_string, format_string); - g_free (format_string); - g_date_time_unref (test_cases[i].now); - g_date_time_unref (test_cases[i].time); + g_free(format_string); + g_date_time_unref(test_cases[i].now); + g_date_time_unref(test_cases[i].time); } - g_date_time_unref (arbitrary_day); - g_date_time_unref (on_the_hour); + g_date_time_unref(arbitrary_day); + g_date_time_unref(on_the_hour); } |