From c834e012f5e189c0222f5e4165fe34b8c1e5b1c8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 21:57:19 -0600 Subject: add cxx warnings, include/ into the include dirs --- CMakeLists.txt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ee5cf9..1fdd4a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ pkg_check_modules (SERVICE_DEPS REQUIRED libnotify>=0.7.6 url-dispatcher-1>=1 json-glib-1.0>=0.16.2) -include_directories (${SERVICE_INCLUDE_DIRS}) +include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) pkg_check_modules (PANEL_DEPS glib-2.0>=2.36 @@ -75,12 +75,14 @@ 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") -include_directories (${CMAKE_CURRENT_SOURCE_DIR}/src) -include_directories (${CMAKE_CURRENT_BINARY_DIR}/src) +include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) # testing & coverage if (${enable_tests}) + pkg_check_modules (DBUSTEST REQUIRED dbustest-1>=14.04.0) set (GTEST_SOURCE_DIR /usr/src/gtest/src) set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..) set (GTEST_LIBS -lpthread) @@ -91,13 +93,14 @@ if (${enable_tests}) endif () # actually build things -add_subdirectory (src) +add_subdirectory(include) +add_subdirectory(src) if (BUILD_PANEL) - add_subdirectory (panel) + add_subdirectory(panel) endif () -add_subdirectory (data) -add_subdirectory (po) +add_subdirectory(data) +add_subdirectory(po) if (${enable_tests}) - add_subdirectory (tests) + add_subdirectory(tests) endif () -- cgit v1.2.3 From 060bc69d8bdfe9860a71109ecf92810beb99f4da Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 21:59:43 -0600 Subject: add clock + tests --- include/datetime/clock-mock.h | 63 +++++++++++ include/datetime/clock.h | 99 +++++++++++++++++ src/clock-live.cpp | 137 ++++++++++++++++++++++++ src/clock.cpp | 98 +++++++++++++++++ tests/test-clock.cc | 244 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 641 insertions(+) create mode 100644 include/datetime/clock-mock.h create mode 100644 include/datetime/clock.h create mode 100644 src/clock-live.cpp create mode 100644 src/clock.cpp create mode 100644 tests/test-clock.cc diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h new file mode 100644 index 0000000..814b29a --- /dev/null +++ b/include/datetime/clock-mock.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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_CLOCK_MOCK_H +#define INDICATOR_DATETIME_CLOCK_MOCK_H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class MockClock: public Clock +{ +public: + + 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_); + } + + void setLocaltime(GDateTime* dt) { + g_clear_pointer(&localtime_, g_date_time_unref); + localtime_ = g_date_time_ref(dt); + skewDetected(); + } + +private: + + GDateTime * localtime_ = nullptr; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_CLOCK_MOCK_H diff --git a/include/datetime/clock.h b/include/datetime/clock.h new file mode 100644 index 0000000..978be27 --- /dev/null +++ b/include/datetime/clock.h @@ -0,0 +1,99 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_CLOCK_H +#define INDICATOR_DATETIME_CLOCK_H + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A clock. + * + * Provides a signal to notify when clock skew is detected, such as + * when the timezone changes or when the system resumes from sleep. + */ +class Clock +{ +public: + virtual ~Clock(); + virtual GDateTime* localtime() const = 0; + core::Property > timezones; + core::Signal<> skewDetected; + +protected: + Clock(); + +private: + static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); + static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + + Clock(const Clock&) =delete; + Clock& operator=(const Clock&) =delete; + + GCancellable * cancellable_ = nullptr; + GDBusConnection * system_bus_ = nullptr; + unsigned int sleep_subscription_id_ = 0; +}; + +/*** +**** +***/ + +/** + * \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. + */ +class LiveClock: public Clock +{ +public: + LiveClock (const std::shared_ptr& zones); + virtual ~LiveClock(); + virtual GDateTime* localtime() const; + core::Property skewTestIntervalSec; + +private: + class Impl; + std::unique_ptr p; +}; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_CLOCK_H diff --git a/src/clock-live.cpp b/src/clock-live.cpp new file mode 100644 index 0000000..80e91a3 --- /dev/null +++ b/src/clock-live.cpp @@ -0,0 +1,137 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class LiveClock::Impl +{ +public: + + Impl(LiveClock& owner, const std::shared_ptr& tzd): + owner_(owner), + timezones_(tzd) + { + if (timezones_) + { + timezones_->timezone.changed().connect ([this](const std::string& z) {setTimezone(z);}); + setTimezone(timezones_->timezone.get()); + } + + owner_.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); + setInterval(owner_.skewTestIntervalSec.get()); + } + + ~Impl() + { + clearTimer(); + + g_clear_pointer (&timezone_, g_time_zone_unref); + } + + GDateTime* localtime() const + { + g_assert (timezone_ != nullptr); + + return g_date_time_new_now (timezone_); + } + +private: + + void setTimezone (const std::string& str) + { + g_clear_pointer (&timezone_, g_time_zone_unref); + timezone_= g_time_zone_new (str.c_str()); + owner_.skewDetected(); + } + +private: + + void clearTimer() + { + if (skew_timeout_id_) + { + g_source_remove(skew_timeout_id_); + skew_timeout_id_ = 0; + } + + g_clear_pointer(&prev_datetime_, g_date_time_unref); + } + + void setInterval(unsigned int seconds) + { + clearTimer(); + + if (seconds > 0) + { + prev_datetime_ = owner_.localtime(); + skew_timeout_id_ = g_timeout_add_seconds(seconds, onTimerPulse, this); + } + } + + static gboolean onTimerPulse(gpointer gself) + { + auto self = static_cast(gself); + + // 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 GTimeSpan fuzz = 5; + const GTimeSpan max = (self->owner_.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; + if (abs(diff) > max) + self->owner_.skewDetected(); + + // update prev_datetime + g_clear_pointer(&self->prev_datetime_, g_date_time_unref); + self->prev_datetime_ = now; + + return G_SOURCE_CONTINUE; + } + +protected: + + LiveClock& owner_; + GTimeZone * timezone_ = nullptr; + std::shared_ptr timezones_; + + GDateTime * prev_datetime_ = nullptr; + unsigned int skew_timeout_id_ = 0; + unsigned int sleep_subscription_id_ = 0; +}; + +LiveClock::LiveClock(const std::shared_ptr& tzd): + p (new Impl (*this, tzd)) +{ +} + +LiveClock::~LiveClock() =default; + +GDateTime * +LiveClock::localtime() const +{ + return p->localtime(); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/src/clock.cpp b/src/clock.cpp new file mode 100644 index 0000000..4a75ceb --- /dev/null +++ b/src/clock.cpp @@ -0,0 +1,98 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +Clock::Clock(): + cancellable_(g_cancellable_new()) +{ + g_bus_get(G_BUS_TYPE_SYSTEM, cancellable_, onSystemBusReady, this); + + timezones.changed().connect([this](const std::set& timezones){ + g_message ("timezones changed... new count is %d", (int)timezones.size()); + skewDetected(); + }); +} + +Clock::~Clock() +{ + g_cancellable_cancel(cancellable_); + g_clear_object(&cancellable_); + + if (sleep_subscription_id_) + g_dbus_connection_signal_unsubscribe(system_bus_ , sleep_subscription_id_); + + g_clear_object(&system_bus_); +} + +void +Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) +{ + GDBusConnection * system_bus; + + if ((system_bus = g_bus_get_finish(res, nullptr))) + { + auto self = static_cast(gself); + + self->system_bus_ = system_bus; + + self->sleep_subscription_id_ = g_dbus_connection_signal_subscribe( + system_bus, + nullptr, + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + "/org/freedesktop/login1", // object path + nullptr, // arg0 + G_DBUS_SIGNAL_FLAGS_NONE, + onPrepareForSleep, + self, + nullptr); + } +} + +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) +{ + static_cast(gself)->skewDetected(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-clock.cc b/tests/test-clock.cc new file mode 100644 index 0000000..a0b4360 --- /dev/null +++ b/tests/test-clock.cc @@ -0,0 +1,244 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include "glib-fixture.h" + +/*** +**** +***/ + +using unity::indicator::datetime::Clock; +using unity::indicator::datetime::LiveClock; +using unity::indicator::datetime::Timezones; + +class ClockFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(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(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; + + 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); + } + + 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 (); + } + + 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); + } +}; + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +TEST_F (ClockFixture, HelloFixture) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + 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); + 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); + + /// change the timezones! + clock.skewDetected.connect([this](){ + g_main_loop_quit(loop); + }); + file_timezone = "America/Los_Angeles"; + g_idle_add ([](gpointer str){ + set_file(static_cast(str)); + return G_SOURCE_REMOVE; + }, const_cast(file_timezone.c_str())); + 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); + 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); +#endif +} + + +TEST_F (ClockFixture, TimezoneChangeTriggersSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + //std::string file_timezone = "America/New_York"; + //set_file (file_timezone); + //std::shared_ptr 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); + + /// change the timezones! + clock.skewDetected.connect([this](){ + g_main_loop_quit(loop); + }); + g_idle_add ([](gpointer gs){ + static_cast(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); + 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); +} + +/** + * Confirm that a "PrepareForSleep" event wil trigger a skew event + */ +TEST_F (ClockFixture, SleepTriggersSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add ([](gpointer gself){ + static_cast(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 (ClockFixture, IdleDoesNotTriggerSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 4; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec (intervalSec * 2.5 * 1000); + EXPECT_FALSE (skewed); +} -- cgit v1.2.3 From 36d8e8ee2d9f2b7967cef6c74c6f779263727eb9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:00:32 -0600 Subject: add timezone-file + tests --- include/datetime/timezone-file.h | 60 ++++++++++++++++++ include/datetime/timezone.h | 46 ++++++++++++++ src/timezone-file.cpp | 89 ++++++++++++++++++++++++++ tests/test-timezone-file.cc | 133 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+) create mode 100644 include/datetime/timezone-file.h create mode 100644 include/datetime/timezone.h create mode 100644 src/timezone-file.cpp create mode 100644 tests/test-timezone-file.cc diff --git a/include/datetime/timezone-file.h b/include/datetime/timezone-file.h new file mode 100644 index 0000000..39a3d83 --- /dev/null +++ b/include/datetime/timezone-file.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_FILE_TIMEZONE_H +#define INDICATOR_DATETIME_FILE_TIMEZONE_H + +#include // base class + +#include // std::string + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Timezone that gets its information from monitoring a file, such as /etc/timezone + */ +class FileTimezone: public Timezone +{ +public: + FileTimezone() {} + FileTimezone(const std::string& filename) { setFilename(filename); } + ~FileTimezone() {clear();} + +private: + void setFilename(const std::string& filename); + void clear(); + static void onFileChanged(gpointer gself); + void reload(); + + std::string filename_; + GFileMonitor * monitor_ = nullptr; + unsigned long monitor_handler_id_ = 0; +}; + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_FILE_TIMEZONE_H diff --git a/include/datetime/timezone.h b/include/datetime/timezone.h new file mode 100644 index 0000000..218e219 --- /dev/null +++ b/include/datetime/timezone.h @@ -0,0 +1,46 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_TIMEZONE_H +#define INDICATOR_DATETIME_TIMEZONE_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** \brief Base class for objects that use various means to detect the system's timezone */ +class Timezone +{ +protected: + Timezone() {} + +public: + //virtual ~Timezone() {} + core::Property timezone; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_TIMEZONE_H diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp new file mode 100644 index 0000000..f1d0dca --- /dev/null +++ b/src/timezone-file.cpp @@ -0,0 +1,89 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +void +FileTimezone::clear() +{ + if (monitor_handler_id_) + g_signal_handler_disconnect(monitor_, monitor_handler_id_); + + g_clear_object (&monitor_); + + filename_.clear(); +} + +void +FileTimezone::setFilename(const std::string& filename) +{ + clear(); + + 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); + g_object_unref(file); + if (err) + { + g_warning("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message); + g_error_free(err); + } + else + { + monitor_handler_id_ = g_signal_connect_swapped(monitor_, "changed", G_CALLBACK(onFileChanged), this); + g_debug("%s Monitoring timezone file '%s'", G_STRLOC, filename.c_str()); + } + + reload(); +} + +void +FileTimezone::onFileChanged(gpointer gself) +{ + static_cast(gself)->reload(); +} + +void +FileTimezone::reload() +{ + GError * err = nullptr; + gchar * str = nullptr; + + if (!g_file_get_contents(filename_.c_str(), &str, nullptr, &err)) + { + g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename_.c_str(), err->message); + g_error_free(err); + } + else + { + g_strstrip(str); + timezone.set(str); + g_free(str); + } +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc new file mode 100644 index 0000000..510d12c --- /dev/null +++ b/tests/test-timezone-file.cc @@ -0,0 +1,133 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include // fopen() +//#include // chmod() +#include // sync() + +using unity::indicator::datetime::FileTimezone; + + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +class TimezoneFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp () + { + super::SetUp (); + } + + virtual void TearDown () + { + super::TearDown (); + } + + public: + + /* convenience func to set the timezone file */ + void set_file (const std::string& text) + { + FILE * fp = fopen (TIMEZONE_FILE, "w+"); + fprintf (fp, "%s\n", text.c_str()); + fclose (fp); + sync (); + } +}; + + +/** + * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist + */ +TEST_F (TimezoneFixture, NoFile) +{ + remove (TIMEZONE_FILE); + ASSERT_FALSE (g_file_test (TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + + FileTimezone tz (TIMEZONE_FILE); + testLogCount (G_LOG_LEVEL_WARNING, 1); +} + + +/** + * Test that timezone-file picks up the initial value + */ +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()); +} + + +/** + * Test that clearing the timezone results in an empty string + */ +TEST_F (TimezoneFixture, ChangedValue) +{ + const std::string initial_timezone = "America/Chicago"; + const std::string changed_timezone = "America/New_York"; + set_file (initial_timezone); + + 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()); + changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gself){ + static_cast(gself)->set_file ("America/New_York"); + // static_cast(gtz)->timezone.set ("America/New_York"); + return G_SOURCE_REMOVE; + }, this);//&tz); + + g_main_loop_run (loop); + + ASSERT_TRUE (changed); + ASSERT_EQ (changed_timezone, tz.timezone.get()); +} -- cgit v1.2.3 From a1cb4d7802e6e024c842f2a4fa41b91b67162698 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:00:51 -0600 Subject: add timezone-geoclue + tests --- include/datetime/timezone-geoclue.h | 65 ++++++++++ src/timezone-geoclue.cpp | 248 ++++++++++++++++++++++++++++++++++++ tests/test-timezone-geoclue.cc | 97 ++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 include/datetime/timezone-geoclue.h create mode 100644 src/timezone-geoclue.cpp create mode 100644 tests/test-timezone-geoclue.cc diff --git a/include/datetime/timezone-geoclue.h b/include/datetime/timezone-geoclue.h new file mode 100644 index 0000000..382b3cc --- /dev/null +++ b/include/datetime/timezone-geoclue.h @@ -0,0 +1,65 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H +#define INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H + +#include // base class + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Timezone that gets its information from asking GeoClue + */ +class GeoclueTimezone: public Timezone +{ +public: + GeoclueTimezone(); + ~GeoclueTimezone(); + +private: + static void on_bus_got (GObject*, GAsyncResult*, gpointer); + static void on_client_created (GObject*, GAsyncResult*, gpointer); + static void on_address_changed (GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + static void on_requirements_set (GObject*, GAsyncResult*, gpointer); + static void on_address_started (GObject*, GAsyncResult*, gpointer); + static void on_address_got (GObject*, GAsyncResult*, gpointer); + void setTimezoneFromAddressVariant (GVariant*); + static GVariant * call_finish (GObject*, GAsyncResult*); + + GCancellable * cancellable_ = nullptr; + GDBusConnection * connection_ = nullptr; + std::string client_object_path_; + guint signal_subscription_ = 0; +}; + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H + diff --git a/src/timezone-geoclue.cpp b/src/timezone-geoclue.cpp new file mode 100644 index 0000000..f6d1f5e --- /dev/null +++ b/src/timezone-geoclue.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include + +#define GEOCLUE_BUS_NAME "org.freedesktop.Geoclue.Master" + +namespace unity { +namespace indicator { +namespace datetime { + + +GeoclueTimezone::GeoclueTimezone(): + cancellable_(g_cancellable_new()) +{ + g_bus_get(G_BUS_TYPE_SESSION, cancellable_, on_bus_got, this); +} + +GeoclueTimezone::~GeoclueTimezone() +{ + g_cancellable_cancel(cancellable_); + g_object_unref(cancellable_); + + if (signal_subscription_) + g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_); + + g_object_unref(connection_); +} + +/*** +**** +***/ + +void +GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * error; + GDBusConnection * connection; + + error = nullptr; + connection = g_bus_get_finish(res, &error); + if (error) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Couldn't get bus: %s", error->message); + + g_error_free(error); + } + else + { + auto self = static_cast(gself); + + self->connection_ = connection; + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + "/org/freedesktop/Geoclue/Master", + "org.freedesktop.Geoclue.Master", + "Create", + nullptr, // parameters + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_client_created, + self); + } +} + +void +GeoclueTimezone::on_client_created(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + GVariant * child = g_variant_get_child_value(result, 0); + self->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_, + GEOCLUE_BUS_NAME, + "org.freedesktop.Geoclue.Address", // inteface + "AddressChanged", // signal name + self->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_, + GEOCLUE_BUS_NAME, + self->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_, + 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) +{ + static_cast(gself)->setTimezoneFromAddressVariant(parameters); +} + +void +GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + self->client_object_path_.c_str(), + "org.freedesktop.Geoclue.MasterClient", + "AddressStart", + nullptr, + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_address_started, + self); + + g_variant_unref(result); + } +} + +void +GeoclueTimezone::on_address_started(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + self->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_, + on_address_got, + self); + + g_variant_unref(result); + } +} + +void +GeoclueTimezone::on_address_got(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + static_cast(gself)->setTimezoneFromAddressVariant(result); + g_variant_unref(result); + } +} + +void +GeoclueTimezone::setTimezoneFromAddressVariant(GVariant * variant) +{ + g_return_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("(ia{ss}(idd))"))); + + const gchar * timezone_string = nullptr; + GVariant * dict = g_variant_get_child_value(variant, 1); + if (dict) + { + if (g_variant_lookup(dict, "timezone", "&s", &timezone_string)) + timezone.set(timezone_string); + + g_variant_unref(dict); + } +} + +GVariant* +GeoclueTimezone::call_finish(GObject * source, GAsyncResult * res) +{ + GError * error; + GVariant * result; + + error = nullptr; + result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); + + if (error) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("AddressStart() failed: %s", error->message); + + g_error_free(error); + + g_clear_pointer(&result, g_variant_unref); + } + + return result; +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc new file mode 100644 index 0000000..a577fbd --- /dev/null +++ b/tests/test-timezone-geoclue.cc @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "geoclue-fixture.h" + +#include + +//#include + +using unity::indicator::datetime::GeoclueTimezone; + +/*** +**** +***/ + +class TimezoneGeoclueFixture : public GeoclueFixture +{ +}; + +#if 0 +namespace +{ + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + gboolean emit_address_changed_idle (gpointer gdata) + { + auto data = static_cast(gdata); + + GError * error = nullptr; + 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))"), + &error); + if (error) + { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + delete data; + return G_SOURCE_REMOVE; + } +} +#endif + +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()); + + // 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()); + changed = true; + 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()); +} + + -- cgit v1.2.3 From 172246c997d7b20d7e98fc25a7b23f4a79a0f6a6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:02:06 -0600 Subject: add formatter + tests --- include/datetime/formatter.h | 118 +++++++++++++++++++ src/formatter-desktop.cpp | 210 +++++++++++++++++++++++++++++++++ tests/test-formatter.cc | 270 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 598 insertions(+) create mode 100644 include/datetime/formatter.h create mode 100644 src/formatter-desktop.cpp create mode 100644 tests/test-formatter.cc diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h new file mode 100644 index 0000000..66dc212 --- /dev/null +++ b/include/datetime/formatter.h @@ -0,0 +1,118 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_FORMATTER_H +#define INDICATOR_DATETIME_FORMATTER_H + +#include +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class Clock; +class DateTime; + +/*** +**** +***/ + +/** + * \brief Provides the right time format strings based on the profile and user's settings + */ +class Formatter +{ +public: + + /** \brief The time format string for the menu header */ + core::Property headerFormat; + + /** \brief The time string for the menu header. (eg, the headerFormat + the clock's time */ + core::Property header; + + /** \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 */ + core::Signal<> relativeFormatChanged; + + /** \brief Generate a relative time format for some time (or time range) + from the current clock's value. For example, a full-day interval + starting at the end of the current clock's day yields "Tomorrow" */ + std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; + +protected: + + Formatter(const std::shared_ptr&); + 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); + + /** \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); + +private: + + Formatter(const Formatter&) =delete; + Formatter& operator=(const Formatter&) =delete; + + class Impl; + std::unique_ptr p; +}; + + +/** + * \brief A Formatter for the Desktop and DesktopGreeter profiles. + */ +class DesktopFormatter: public Formatter +{ +public: + DesktopFormatter(const std::shared_ptr&); + ~DesktopFormatter(); + +private: + class Impl; + friend Impl; + std::unique_ptr p; +}; + + +/** + * \brief A Formatter for Phone and PhoneGreeter profiles. + */ +class PhoneFormatter: public Formatter +{ +public: + PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { + headerFormat.set(getDefaultHeaderTimeFormat(is_locale_12h(), false)); + } +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_CLOCK_H diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp new file mode 100644 index 0000000..8390967 --- /dev/null +++ b/src/formatter-desktop.cpp @@ -0,0 +1,210 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class DesktopFormatter::Impl +{ +public: + + Impl (DesktopFormatter * owner, const std::shared_ptr& 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(gself)->rebuildHeaderFormat(); + } + + void rebuildHeaderFormat() + { + gchar * fmt = getHeaderLabelFormatString (settings_); + 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; + } + + const gchar* T_(const gchar* in) const + { + return owner_->T_(in); + } + + 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) + 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 gchar * getFullTimeFormatString (GSettings * settings) const + { + const bool 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; + + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = FALSE; + break; + + default: + twelvehour = TRUE; + break; + } + + return 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 + { + str = g_strdup (time_string); + } + + return str; + } + +private: + + DesktopFormatter * const owner_; + std::shared_ptr clock_; + GSettings * settings_; +}; + +/*** +**** +***/ + +DesktopFormatter::DesktopFormatter (const std::shared_ptr& clock): + Formatter(clock), + p(new Impl(this, clock)) +{ +} + +DesktopFormatter::~DesktopFormatter() = default; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc new file mode 100644 index 0000000..641338b --- /dev/null +++ b/tests/test-formatter.cc @@ -0,0 +1,270 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Clock; +using unity::indicator::datetime::MockClock; +using unity::indicator::datetime::PhoneFormatter; +using unity::indicator::datetime::DesktopFormatter; + +/*** +**** +***/ + +class FormatterFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + gchar * original_locale = nullptr; + + protected: + + GSettings * settings = nullptr; + + virtual void SetUp () + { + super::SetUp (); + + settings = g_settings_new (SETTINGS_INTERFACE); + + original_locale = g_strdup (setlocale (LC_TIME, NULL)); + } + + virtual void TearDown () + { + g_clear_object (&settings); + + setlocale (LC_TIME, original_locale); + g_clear_pointer (&original_locale, g_free); + + super::TearDown (); + } + + bool SetLocale (const char * expected_locale, const char * name) + { + setlocale (LC_TIME, expected_locale); + const char * actual_locale = setlocale (LC_TIME, NULL); + if (!g_strcmp0 (expected_locale, actual_locale)) + { + return true; + } + else + { + g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + return false; + } + } + + inline bool Set24hLocale () { return SetLocale ("C", "24h"); } + inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } +}; + + +/** + * Test the phone header format + */ +TEST_F (FormatterFixture, TestPhoneHeader) +{ + GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + std::shared_ptr mock (new MockClock(now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + + // test the default value in a 24h locale + if (Set24hLocale ()) + { + PhoneFormatter formatter (clock); + EXPECT_EQ (std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ (std::string("18:30"), formatter.header.get()); + } + + // test the default value in a 12h locale + if (Set12hLocale ()) + { + PhoneFormatter formatter (clock); + EXPECT_EQ (std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ (std::string(" 6:30 PM"), formatter.header.get()); + } +} + +#define EM_SPACE "\u2003" + +/** + * Test the default values of the desktop header format + */ +TEST_F (FormatterFixture, TestDesktopHeader) +{ + struct { + bool is_12h; + bool show_day; + bool show_date; + bool show_year; + const char * expected_format_string; + } test_cases[] = { + { false, false, false, false, "%H:%M" }, + { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false + { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, + { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, + { false, true, false, false, "%a" EM_SPACE "%H:%M" }, + { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false + { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, + { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, + { true, false, false, false, "%l:%M %p" }, + { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, + { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, + { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, + { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, + { 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 mock (new MockClock(now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + + for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + DesktopFormatter f (clock); + + std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + 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); + } + } + + g_date_time_unref (a); +} + + +/** + * Test the default values of the desktop header format + */ +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); + + struct { + bool is_12h; + GDateTime * now; + GDateTime * then; + GDateTime * then_end; + const char * expected_format_string; + } test_cases[] = { + { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, + { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } + }; + + for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + DesktopFormatter f (clock); + + std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + 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); + } + } + + g_date_time_unref (tomorrow_end); + g_date_time_unref (tomorrow_begin); + g_date_time_unref (day_end); + g_date_time_unref (day_begin); + g_date_time_unref (day); +} + + -- cgit v1.2.3 From 38b878589ffb08d2272169d2529703e887933be9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:03:12 -0600 Subject: add planner + tests --- include/datetime/planner-eds.h | 49 +++++ include/datetime/planner.h | 71 +++++++ src/planner-eds.cpp | 419 +++++++++++++++++++++++++++++++++++++++++ tests/test-planner-eds.cc | 70 +++++++ tests/test-planner.cc | 68 +++++++ 5 files changed, 677 insertions(+) create mode 100644 include/datetime/planner-eds.h create mode 100644 include/datetime/planner.h create mode 100644 src/planner-eds.cpp create mode 100644 tests/test-planner-eds.cc create mode 100644 tests/test-planner.cc diff --git a/include/datetime/planner-eds.h b/include/datetime/planner-eds.h new file mode 100644 index 0000000..43f222e --- /dev/null +++ b/include/datetime/planner-eds.h @@ -0,0 +1,49 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_EDS_H +#define INDICATOR_DATETIME_PLANNER_EDS_H + +#include + +#include // unique_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Planner which uses EDS as its backend + */ +class PlannerEds: public Planner +{ +public: + PlannerEds(); + virtual ~PlannerEds(); + +private: + class Impl; + std::unique_ptr impl_; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_EDS_H diff --git a/include/datetime/planner.h b/include/datetime/planner.h new file mode 100644 index 0000000..4d27a2b --- /dev/null +++ b/include/datetime/planner.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_H +#define INDICATOR_DATETIME_PLANNER_H + +#include +#include + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Simple appointment book + */ +class Planner +{ +public: + virtual ~Planner() =default; + + /** + * \brief Timestamp used to determine the appointments in the `upcoming' and `thisMonth' properties. + * Setting this value will cause the planner to re-query its backend and + * update the `upcoming' and `thisMonth' properties. + */ + core::Property time; + + /** + * \brief The next few appointments that follow the time specified in the time property. + */ + core::Property> upcoming; + + /** + * \brief The appointments that occur in the same month as the time property + */ + core::Property> thisMonth; + +protected: + Planner() =default; + +private: + Planner(const Planner&) =delete; + Planner& operator=(const Planner&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_H diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp new file mode 100644 index 0000000..804d98e --- /dev/null +++ b/src/planner-eds.cpp @@ -0,0 +1,419 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +#include +#include +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +G_DEFINE_QUARK ("source-client", source_client) + + +class PlannerEds::Impl +{ +public: + + Impl (PlannerEds& owner): + owner_(owner), + cancellable_(g_cancellable_new()) + { + e_source_registry_new (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")); + rebuildSoon(); + }); + + rebuildSoon(); + } + + ~Impl() + { + g_cancellable_cancel (cancellable_); + g_clear_object (&cancellable_); + + if (rebuild_tag_) + g_source_remove (rebuild_tag_); + + if (source_registry_) + g_signal_handlers_disconnect_by_data (source_registry_, this); + g_clear_object (&source_registry_); + } + +private: + + 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) + { + 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); + } + else + { + auto self = static_cast(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); + + self->source_registry_ = r; + + 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); + } + } + + static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + self->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) + { + auto self = static_cast(gself); + + e_cal_client_connect (source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + self->cancellable_, + on_client_connected, + gself); + } + + static void on_client_connected (GObject* /*source*/, GAsyncResult * res, gpointer gself) + { + GError * error = nullptr; + 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); + + 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_message ("client connected; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + } + + 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()))) + { + g_object_unref (e_cal_client); + + g_message ("source disabled; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + } + + static void on_source_removed (ESourceRegistry* registry, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + on_source_disabled (registry, source, gself); + + self->sources_.erase (source); + g_object_unref (source); + } + + static void on_source_changed (ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + { + g_message ("source changed; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + +private: + + typedef std::function&)> appointment_func; + + struct Task + { + Impl* impl_; + appointment_func func_; + std::vector appointments_; + Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + }; + + struct AppointmentSubtask + { + std::shared_ptr task_; + ECalClient * client_; + std::string color_; + AppointmentSubtask (const std::shared_ptr& task, ECalClient* client, const char* color): + task_(task), client_(client), color_(color) {} + }; + + void rebuildSoon() + { + const static guint ARBITRARY_INTERVAL_SECS = 2; + + if (rebuild_tag_ == 0) + rebuild_tag_ = g_timeout_add_seconds (ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); + } + + static gboolean rebuildNowStatic (gpointer gself) + { + auto self = static_cast(gself); + self->rebuild_tag_ = 0; + self->rebuildNow(); + return G_SOURCE_REMOVE; + } + + void rebuildNow() + { + GDateTime* calendar_date = owner_.time.get().get(); + GDateTime* begin; + GDateTime* end; + int y, m, d; + g_message ("in rebuildNow"); + + // get all the appointments in the calendar month + g_date_time_get_ymd(calendar_date, &y, &m, &d); + begin = g_date_time_new_local(y, m, 1, 0, 0, 0.1); + end = g_date_time_new_local(y, m, g_date_get_days_in_month(GDateMonth(m),GDateYear(y)), 23, 59, 59.9); + if (begin && end) + { + getAppointments(begin, end, [this](const std::vector& appointments) { + g_message ("got %d appointments in this calendar month", (int)appointments.size()); + }); + } + g_clear_pointer(&begin, g_date_time_unref); + g_clear_pointer(&end, g_date_time_unref); + + // get the upcoming appointments + begin = g_date_time_ref(calendar_date); + end = g_date_time_add_months(begin, 1); + if (begin && end) + { + getAppointments(begin, end, [this](const std::vector& appointments) { + g_message ("got %d upcoming appointments", (int)appointments.size()); + }); + } + g_clear_pointer(&begin, g_date_time_unref); + g_clear_pointer(&end, g_date_time_unref); + g_clear_pointer(&calendar_date, g_date_time_unref); + } + + void getAppointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) + { + 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")); + + /** + *** init the default timezone + **/ + + 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); + if (tz && *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); + + g_debug ("default_timezone is %p", default_timezone); + } + + /** + *** walk through the sources to build the appointment list + **/ + + std::shared_ptr main_task (new Task(this, func), [](Task* task){ + g_message("time to delete task %p", task); + task->func_(task->appointments_); + }); + + for (auto& source : sources_) + { + 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); + + // 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(g);}); + } + } + + struct UrlSubtask + { + std::shared_ptr task_; + Appointment appointment_; + UrlSubtask (const std::shared_ptr& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + }; + + static gboolean + 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(gsubtask); + + if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) + { + const gchar* uid = NULL; + e_cal_component_get_uid (component, &uid); + + auto status = ICAL_STATUS_NONE; + e_cal_component_get_status (component, &status); + + if ((uid != NULL) && + (status != ICAL_STATUS_COMPLETED) && + (status != ICAL_STATUS_CANCELLED)) + { + Appointment appointment; + + /* Determine whether this is a recurring event. + NB: icalrecurrencetype supports complex recurrence patterns; + 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) + { + const auto recur = static_cast(l->data); + appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) + && (recur->interval == 1)); + } + e_cal_component_free_recur_list (recur_list); + + ECalComponentText text; + text.value = ""; + 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.is_event = vtype == E_CAL_COMPONENT_EVENT; + appointment.summary = text.value; + appointment.uid = uid; + + 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)); + } + } + + return G_SOURCE_CONTINUE; + } + + static void on_appointment_uris_ready (GObject* client, GAsyncResult* res, gpointer gsubtask) + { + auto subtask = static_cast(gsubtask); + + GSList * uris = nullptr; + GError * error = nullptr; + e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); + if (error != NULL) + { + 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); + } + else if (uris != NULL) + { + 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_); + delete subtask; + } + +private: + + PlannerEds& owner_; + std::set sources_; + GCancellable * cancellable_ = nullptr; + ESourceRegistry * source_registry_ = nullptr; + guint rebuild_tag_ = 0; +}; + +PlannerEds::PlannerEds(): impl_(new Impl(*this)) {} + +PlannerEds::~PlannerEds() =default; + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-planner-eds.cc b/tests/test-planner-eds.cc new file mode 100644 index 0000000..986a45e --- /dev/null +++ b/tests/test-planner-eds.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include + +#include + +#include +#include + +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 new file mode 100644 index 0000000..8c74366 --- /dev/null +++ b/tests/test-planner.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Appointment; + +/*** +**** +***/ + +typedef GlibFixture PlannerFixture; + + +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); + + Appointment a; + a.summary = "Test"; + a.begin = halloween; + 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())); + + Appointment c; + c.begin = christmas; + 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())); + a = d; + EXPECT_EQ (0, g_date_time_compare (d.begin(), a.begin())); + EXPECT_EQ (0, g_date_time_compare (d.end(), a.end())); +} + -- cgit v1.2.3 From db7b874bbf1a98f85ffe511be230164c40fe080d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:03:43 -0600 Subject: add locations + tests --- include/datetime/locations-settings.h | 60 ++++++++++++ include/datetime/locations.h | 71 ++++++++++++++ src/locations-settings.cpp | 105 +++++++++++++++++++++ src/locations.cpp | 41 ++++++++ tests/test-locations.cc | 172 ++++++++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+) create mode 100644 include/datetime/locations-settings.h create mode 100644 include/datetime/locations.h create mode 100644 src/locations-settings.cpp create mode 100644 src/locations.cpp create mode 100644 tests/test-locations.cc diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h new file mode 100644 index 0000000..d343fe3 --- /dev/null +++ b/include/datetime/locations-settings.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_LOCATIONS_H +#define INDICATOR_DATETIME_SETTINGS_LOCATIONS_H + +#include // base class + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class Timezones; + +/** + * \brief An ordered list of Location objects found from + * the system timezone and from the user's GSettings + */ +class SettingsLocations: public Locations +{ +public: + /** + * @param[in] schemaId the settings schema to load + * @param[in] timezones the timezones to always show first in the list + */ + SettingsLocations (const std::string& schemaId, const std::shared_ptr& timezones); + +protected: + std::unique_ptr> settings_; + std::shared_ptr timezones_; + +private: + static void onSettingsChanged (gpointer gself); + void reload(); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LOCATIONS_H diff --git a/include/datetime/locations.h b/include/datetime/locations.h new file mode 100644 index 0000000..a06d1cc --- /dev/null +++ b/include/datetime/locations.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_LOCATIONS_H +#define INDICATOR_DATETIME_LOCATIONS_H + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A physical place and its timezone; eg, "America/Chicago" + "Oklahoma City" + */ +struct Location +{ + /** timezone; eg, "America/Chicago" */ + std::string zone; + + /* human-readable location name; eg, "Oklahoma City" */ + std::string name; + + /** offset from UTC in microseconds */ + int64_t offset = 0; + + bool operator== (const Location& that) const + { + return (name == that.name) && (zone == that.zone) && (offset == that.offset); + } + + Location (const std::string& zone, const std::string& name); +}; + +/** + * A container for an ordered list of Locations. + */ +class Locations +{ +public: + Locations() =default; + virtual ~Locations() =default; + + /** \brief an ordered list of Location items */ + core::Property > locations; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_LOCATIONS_H diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp new file mode 100644 index 0000000..ed8f998 --- /dev/null +++ b/src/locations-settings.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include + +#include // std::find() + +namespace unity { +namespace indicator { +namespace datetime { + +SettingsLocations::SettingsLocations (const std::string& schemaId, + const std::shared_ptr& timezones): + timezones_(timezones) +{ + auto deleter = [&](GSettings* s){g_object_unref(s);}; + settings_ = std::unique_ptr>(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); itimezone.changed().connect([this](const std::string&){reload();}); + timezones->timezones.changed().connect([this](const std::set&){reload();}); + + reload(); +} + +void +SettingsLocations::onSettingsChanged (gpointer gself) +{ + static_cast(gself)->reload(); +} + +void +SettingsLocations::reload() +{ + std::vector v; + + // add the primary timezone first + std::string zone = timezones_->timezone.get(); + if (!zone.empty()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + v.push_back (l); + g_free (name); + } + + // add the other detected timezones + for (const auto& zone : timezones_->timezones.get()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + } + + // maybe add the user-specified locations + if (g_settings_get_boolean (settings_.get(), SETTINGS_SHOW_LOCATIONS_S)) + { + gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + + for (int i=0; user_locations[i]; i++) + { + gchar * zone; + gchar * name; + split_settings_location (user_locations[i], &zone, &name); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + g_free (zone); + } + + g_strfreev (user_locations); + } + + locations.set (v); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/locations.cpp b/src/locations.cpp new file mode 100644 index 0000000..5c3c52b --- /dev/null +++ b/src/locations.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +Location::Location(const std::string& zone_, const std::string& name_): + zone(zone_), + name(name_) +{ + GTimeZone * gzone = g_time_zone_new (zone.c_str()); + GDateTime * gtime = g_date_time_new_now (gzone); + offset = g_date_time_get_utc_offset (gtime); + g_date_time_unref (gtime); + g_time_zone_unref (gzone); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-locations.cc b/tests/test-locations.cc new file mode 100644 index 0000000..c833bed --- /dev/null +++ b/tests/test-locations.cc @@ -0,0 +1,172 @@ + + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Location; +using unity::indicator::datetime::Locations; +using unity::indicator::datetime::SettingsLocations; +using unity::indicator::datetime::Timezones; + +/*** +**** +***/ + +class LocationsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + GSettings * settings = nullptr; + std::shared_ptr timezones; + const std::string nyc = "America/New_York"; + const std::string chicago = "America/Chicago"; + + virtual void SetUp () + { + super::SetUp (); + + settings = g_settings_new (SETTINGS_INTERFACE); + const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (settings, SETTINGS_LOCATIONS_S, location_strv); + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, true); + + timezones.reset (new Timezones); + timezones->timezone.set (chicago); + timezones->timezones.set (std::set({ nyc, chicago })); + } + + virtual void TearDown () + { + //timezones.reset (nullptr); + g_clear_object (&settings); + + super::TearDown (); + } +}; + +TEST_F (LocationsFixture, Timezones) +{ + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, false); + + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + std::vector l = locations.locations.get(); + EXPECT_EQ (2, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); +} + +TEST_F (LocationsFixture, SettingsLocations) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + std::vector l = locations.locations.get(); + EXPECT_EQ (7, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("Oklahoma City", l[3].name); + EXPECT_EQ ("America/Chicago", l[3].zone); + EXPECT_EQ ("Toronto", l[4].name); + EXPECT_EQ ("America/Toronto", l[4].zone); + EXPECT_EQ ("London", l[5].name); + EXPECT_EQ ("Europe/London", l[5].zone); + EXPECT_EQ ("Berlin", l[6].name); + EXPECT_EQ ("Europe/Berlin", l[6].zone); +} + +TEST_F (LocationsFixture, ChangeLocationStrings) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector l = locations.locations.get(); + EXPECT_EQ (5, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("London", l[3].name); + EXPECT_EQ ("Europe/London", l[3].zone); + EXPECT_EQ ("Berlin", l[4].name); + EXPECT_EQ ("Europe/Berlin", l[4].zone); + locations_changed = false; +} + +TEST_F (LocationsFixture, ChangeLocationVisibility) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + g_settings_set_boolean (static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector 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); +} -- cgit v1.2.3 From 2690929aeaef44f2b9c818a7e033ce0ef676fbcd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:06:05 -0600 Subject: add local copy of tvoss' cpp-properties. this will be removed when cpp-properties-dev lands in 14.04 --- include/core/CMakeLists.txt | 2 + include/core/connection.h | 162 ++++++++++++++++++++++++ include/core/property.h | 181 +++++++++++++++++++++++++++ include/core/signal.h | 297 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 642 insertions(+) create mode 100644 include/core/CMakeLists.txt create mode 100644 include/core/connection.h create mode 100644 include/core/property.h create mode 100644 include/core/signal.h diff --git a/include/core/CMakeLists.txt b/include/core/CMakeLists.txt new file mode 100644 index 0000000..139597f --- /dev/null +++ b/include/core/CMakeLists.txt @@ -0,0 +1,2 @@ + + diff --git a/include/core/connection.h b/include/core/connection.h new file mode 100644 index 0000000..37b1355 --- /dev/null +++ b/include/core/connection.h @@ -0,0 +1,162 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef COM_UBUNTU_CONNECTION_H_ +#define COM_UBUNTU_CONNECTION_H_ + +#include +#include +#include + +namespace core +{ +/** + * @brief The Connection class models a signal-slot connection. + */ +class Connection +{ +public: + typedef std::function&)> Dispatcher; + + /** + * @brief Checks if this instance corresponds to an active signal-slot connection. + * @return true iff the instance corresponds to an active signal-slot connection. + */ + inline bool is_connected() const + { + return (d->disconnector ? true : false); + } + + /** + * @brief End a signal-slot connection. + */ + inline void disconnect() + { + d->disconnect(); + } + + /** + * @brief Installs a dispatcher for this signal-slot connection. + * @param dispatcher The dispatcher to be used for signal emissions. + */ + inline void dispatch_via(const Dispatcher& dispatcher) + { + if (d->dispatcher_installer) + d->dispatcher_installer(dispatcher); + } + +private: + typedef std::function Disconnector; + typedef std::function DispatcherInstaller; + + template friend class Signal; + + inline Connection(const Disconnector& disconnector, + const DispatcherInstaller& installer) + : d(std::make_shared(disconnector, installer)) + { + } + + inline void reset() + { + d->reset(); + } + + struct Private + { + Private(const Connection::Disconnector& disconnector_, + const Connection::DispatcherInstaller& dispatcher_installer_) + : disconnector(disconnector_), + dispatcher_installer(dispatcher_installer_) + { + } + + inline void reset() + { + std::lock_guard lg(guard); + reset_locked(); + } + + inline void reset_locked() + { + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + disconnector = empty_disconnector; + dispatcher_installer = empty_dispatcher_installer; + } + + inline void disconnect() + { + static const Connection::Disconnector empty_disconnector{}; + + std::lock_guard lg(guard); + + if (disconnector) + disconnector(); + + reset_locked(); + } + + std::mutex guard; + Connection::Disconnector disconnector; + Connection::DispatcherInstaller dispatcher_installer; + }; + + // The whole class is implicitly shared and we thus forward our complete + // shared state to a private structure that is lifetime-managed by a shared_ptr. + std::shared_ptr d; +}; + +/** + * @brief Scoped helper class to map signal-slot connection mgmt. to RAII. + */ +class ScopedConnection +{ +public: + /** + * @brief Constructs an instance for an existing signal-slot connection. + * @param c The existing signal-slot connection. + */ + inline ScopedConnection(const Connection& c) : connection(c) + { + } + + ScopedConnection(const ScopedConnection&) = delete; + + /** + * @brief Disconnects the signal-slot connection. + */ + inline ~ScopedConnection() noexcept(true) + { + try + { + connection.disconnect(); + } catch(...) + { + } + } + + ScopedConnection& operator=(const ScopedConnection&) = delete; + bool operator==(const ScopedConnection&) = delete; + +private: + Connection connection; +}; +} + +#endif // COM_UBUNTU_CONNECTION_H_ diff --git a/include/core/property.h b/include/core/property.h new file mode 100644 index 0000000..996ba8a --- /dev/null +++ b/include/core/property.h @@ -0,0 +1,181 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef CORE_PROPERTY_H_ +#define CORE_PROPERTY_H_ + +#include + +#include + +namespace core +{ +/** + * @brief A very simple, templated class that allows for uniform declaration of get-able/set-able/observable members. + * @tparam The type of the value contained within the property. + */ +template +class Property +{ + public: + /** + * @brief ValueType refers to the type of the contained value. + */ + typedef T ValueType; + + /** + * @brief Property creates a new instance of property and initializes the contained value. + * @param t The initial value, defaults to Property::default_value(). + */ + inline explicit Property(const T& t = T{}) : value{t} + { + } + + /** + * @brief Copy c'tor, only copies the contained value, not the changed signal and its connections. + * @param rhs + */ + inline Property(const Property& rhs) : value{rhs.value} + { + } + + inline virtual ~Property() = default; + + /** + * @brief Assignment operator, only assigns to the contained value. + * @param rhs The right-hand-side, raw value to assign to this property. + */ + inline Property& operator=(const T& rhs) + { + set(rhs); + return *this; + } + + /** + * @brief Assignment operator, only assigns to the contained value, not the changed signal and its connections. + * @param rhs The right-hand-side property to assign from. + */ + inline Property& operator=(const Property& rhs) + { + set(rhs.value); + return *this; + } + + /** + * @brief Explicit casting operator to the contained value type. + * @return A non-mutable reference to the contained value. + */ + inline operator const T&() const + { + return get(); + } + + /** + * @brief Provides access to a pointer to the contained value. + */ + inline const T* operator->() const + { + return &get(); + } + + /** + * @brief operator == checks if the value of a property and a raw value are equal. + * @param lhs Non-mutable reference to a property. + * @param rhs Non-mutable reference to a raw value. + * @return True iff the value contained in lhs equals rhs. + */ + friend inline bool operator==(const Property& lhs, const T& rhs) + { + return lhs.get() == rhs; + } + + /** + * @brief operator == checks if the value of two properties are equal. + * @param lhs Non-mutable reference to a property. + * @param rhs Non-mutable reference to a property. + * @return True iff the value contained in lhs equals the value contained in rhs. + */ + friend inline bool operator==(const Property& lhs, const Property& rhs) + { + return lhs.get() == rhs.get(); + } + + /** + * @brief Set the contained value to the provided value. Notify observers of the change. + * @param [in] new_value The new value to assign to this property. + * @post get() == new_value; + */ + inline virtual void set(const T& new_value) + { + if (value != new_value) + { + value = new_value; + signal_changed(value); + } + } + + /** + * @brief Access the value contained within this property. + * @return A non-mutable reference to the property value. + */ + inline virtual const T& get() const + { + return value; + } + + /** + * @brief Access to the changed signal, allows observers to subscribe to change notifications. + * @return A non-mutable reference to the changed signal. + */ + inline const Signal& changed() const + { + return signal_changed; + } + + /** + * @brief Provides in-place update facilities. + * + * The provided update_functor is applied to the contained value. If the update functor + * returns true, indicating that the value has been changed, the changed signal is emitted. + * + * @param update_functor The update function to be applied to the contained value. + * @return true iff application of the update functor has been successful. + */ + inline virtual bool update(const std::function& update_functor) + { + if (update_functor(mutable_get())) + { + signal_changed(value); + return true; + } + + return false; + } + + protected: + inline T& mutable_get() const + { + return value; + } + + private: + mutable T value; + Signal signal_changed; +}; +} + +#endif // CORE_PROPERTY_H_ diff --git a/include/core/signal.h b/include/core/signal.h new file mode 100644 index 0000000..be2984b --- /dev/null +++ b/include/core/signal.h @@ -0,0 +1,297 @@ +/* + * Copyright © 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored by: Thomas Voß + */ +#ifndef COM_UBUNTU_SIGNAL_H_ +#define COM_UBUNTU_SIGNAL_H_ + +#include + +#include +#include +#include +#include +#include + +namespace core +{ +/** + * @brief A signal class that observers can subscribe to. + * @tparam Arguments List of argument types passed on to observers when the signal is emitted. + */ +template +class Signal +{ +public: + /** + * @brief Slot is the function type that observers have to provide to connect to this signal. + */ + typedef std::function Slot; + +private: + struct SlotWrapper + { + void operator()(Arguments... args) + { + dispatcher(std::bind(slot, args...)); + } + + Slot slot; + Connection::Dispatcher dispatcher; + Connection connection; + }; + +public: + /** + * @brief Signal constructs a new instance. Never throws. + */ + inline Signal() noexcept(true) : d(new Private()) + { + } + + inline ~Signal() + { + std::lock_guard lg(d->guard); + for (auto slot : d->slots) + slot.connection.reset(); + } + + // Copy construction, assignment and equality comparison are disabled. + Signal(const Signal&) = delete; + Signal& operator=(const Signal&) = delete; + bool operator==(const Signal&) const = delete; + + /** + * @brief Connects the provided slot to this signal instance. + * + * Calling this method is thread-safe and synchronized with any + * other connect, signal emission or disconnect calls. + * + * @param slot The function to be called when the signal is emitted. + * @return A connection object corresponding to the signal-slot connection. + */ + inline Connection connect(const Slot& slot) const + { + // Helpers to initialize an invalid connection. + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + // The default dispatcher immediately executes the function object + // provided as argument on whatever thread is currently running. + static const Connection::Dispatcher default_dispatcher + = [](const std::function& handler) { handler(); }; + + Connection conn{empty_disconnector, empty_dispatcher_installer}; + + std::lock_guard lg(d->guard); + + auto result = d->slots.insert( + d->slots.end(), + SlotWrapper{slot, default_dispatcher, conn}); + + // We implicitly share our internal state with the connection here + // by passing in our private bits contained in 'd' to the std::bind call. + // This admittedly uncommon approach allows us to cleanly manage connection + // and signal lifetimes without the need to mark everything as mutable. + conn.d->disconnector = std::bind( + &Private::disconnect_slot_for_iterator, + d, + result); + conn.d->dispatcher_installer = std::bind( + &Private::install_dispatcher_for_iterator, + d, + std::placeholders::_1, + result); + + return conn; + } + + /** + * @brief operator () emits the signal with the provided parameters. + * + * Please note that signal emissions might not be delivered immediately to + * registered slots, depending on whether the respective connection is dispatched + * via a queueing dispatcher. For that reason, the lifetime of the arguments has to + * exceed the scope of the call to this operator and its surrounding scope. + * + * @param args The arguments to be passed on to registered slots. + */ + inline void operator()(Arguments... args) + { + std::lock_guard lg(d->guard); + for(auto slot : d->slots) + { + slot(args...); + } + } + +private: + struct Private + { + typedef std::list SlotContainer; + + inline void disconnect_slot_for_iterator(typename SlotContainer::iterator it) + { + std::lock_guard lg(guard); + slots.erase(it); + } + + inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, + typename SlotContainer::iterator it) + { + std::lock_guard lg(guard); + it->dispatcher = dispatcher; + } + + std::mutex guard; + SlotContainer slots; + }; + std::shared_ptr d; +}; + +/** + * @brief A signal class that observers can subscribe to, + * template specialization for signals without arguments. + */ +template<> +class Signal +{ +public: + /** + * @brief Slot is the function type that observers have to provide to connect to this signal. + */ + typedef std::function Slot; + +private: + struct SlotWrapper + { + void operator()() + { + dispatcher(slot); + } + + Slot slot; + Connection::Dispatcher dispatcher; + Connection connection; + }; + +public: + /** + * @brief Signal constructs a new instance. Never throws. + */ + inline Signal() noexcept(true) : d(new Private()) + { + } + + inline ~Signal() + { + std::lock_guard lg(d->guard); + for (auto slot : d->slots) + slot.connection.reset(); + } + + // Copy construction, assignment and equality comparison are disabled. + Signal(const Signal&) = delete; + Signal& operator=(const Signal&) = delete; + bool operator==(const Signal&) const = delete; + + /** + * @brief Connects the provided slot to this signal instance. + * + * Calling this method is thread-safe and synchronized with any + * other connect, signal emission or disconnect calls. + * + * @param slot The function to be called when the signal is emitted. + * @return A connection object corresponding to the signal-slot connection. + */ + inline Connection connect(const Slot& slot) const + { + // Helpers to initialize an invalid connection. + static const Connection::Disconnector empty_disconnector{}; + static const Connection::DispatcherInstaller empty_dispatcher_installer{}; + + // The default dispatcher immediately executes the function object + // provided as argument on whatever thread is currently running. + static const Connection::Dispatcher default_dispatcher + = [](const std::function& handler) { handler(); }; + + Connection conn{empty_disconnector, empty_dispatcher_installer}; + + std::lock_guard lg(d->guard); + + auto result = d->slots.insert( + d->slots.end(), + SlotWrapper{slot, default_dispatcher, conn}); + + // We implicitly share our internal state with the connection here + // by passing in our private bits contained in 'd' to the std::bind call. + // This admittedly uncommon approach allows us to cleanly manage connection + // and signal lifetimes without the need to mark everything as mutable. + conn.d->disconnector = std::bind( + &Private::disconnect_slot_for_iterator, + d, + result); + conn.d->dispatcher_installer = std::bind( + &Private::install_dispatcher_for_iterator, + d, + std::placeholders::_1, + result); + + return conn; + } + + /** + * @brief operator () emits the signal. + * + * Please note that signal emissions might not be delivered immediately to + * registered slots, depending on whether the respective connection is dispatched + * via a queueing dispatcher. + */ + inline void operator()() + { + std::lock_guard lg(d->guard); + for(auto slot : d->slots) + { + slot(); + } + } + +private: + struct Private + { + typedef std::list SlotContainer; + + inline void disconnect_slot_for_iterator(typename SlotContainer::iterator it) + { + std::lock_guard lg(guard); + slots.erase(it); + } + + inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, + typename SlotContainer::iterator it) + { + std::lock_guard lg(guard); + it->dispatcher = dispatcher; + } + + std::mutex guard; + SlotContainer slots; + }; + std::shared_ptr d; +}; +} + +#endif // COM_UBUNTU_SIGNAL_H_ -- cgit v1.2.3 From 0e11a8ac5e0e90ae1b576287e13011593dcdc88c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:08:08 -0600 Subject: add other datetime header files --- include/datetime/CMakeLists.txt | 2 + include/datetime/appointment.h | 63 ++++++++++++++++++++++++++ include/datetime/date-time.h | 93 ++++++++++++++++++++++++++++++++++++++ include/datetime/dbus-shared.h | 24 ++++++++++ include/datetime/settings-shared.h | 49 ++++++++++++++++++++ include/datetime/timezones-live.h | 56 +++++++++++++++++++++++ include/datetime/timezones.h | 56 +++++++++++++++++++++++ include/datetime/utils.h | 67 +++++++++++++++++++++++++++ 8 files changed, 410 insertions(+) create mode 100644 include/datetime/CMakeLists.txt create mode 100644 include/datetime/appointment.h create mode 100644 include/datetime/date-time.h create mode 100644 include/datetime/dbus-shared.h create mode 100644 include/datetime/settings-shared.h create mode 100644 include/datetime/timezones-live.h create mode 100644 include/datetime/timezones.h create mode 100644 include/datetime/utils.h diff --git a/include/datetime/CMakeLists.txt b/include/datetime/CMakeLists.txt new file mode 100644 index 0000000..139597f --- /dev/null +++ b/include/datetime/CMakeLists.txt @@ -0,0 +1,2 @@ + + diff --git a/include/datetime/appointment.h b/include/datetime/appointment.h new file mode 100644 index 0000000..098b0d0 --- /dev/null +++ b/include/datetime/appointment.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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_APPOINTMENT_H +#define INDICATOR_DATETIME_APPOINTMENT_H + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * PODS representing a calendar appointment + */ +struct Appointment +{ +public: + std::string color; + std::string summary; + std::string url; + std::string uid; + bool is_event = false; + bool is_daily = false; + bool has_alarms = false; + DateTime begin; + DateTime end; + + bool operator== (const Appointment& that) const + { + return (color==that.color) && + (summary==that.summary) && + (url==that.url) && + (uid==that.uid) && + (is_event==that.is_event) && + (has_alarms==that.has_alarms) && + (begin==that.begin) && + (end==that.end); + } +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_APPOINTMENT_H diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h new file mode 100644 index 0000000..bbcd00a --- /dev/null +++ b/include/datetime/date-time.h @@ -0,0 +1,93 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_DATETIME_H +#define INDICATOR_DATETIME_DATETIME_H + +#include // GDateTime + +#include // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * C++ wrapper class for GDateTime + */ +class DateTime +{ +public: + + GDateTime* get() const + { + return dt_.get(); + } + + GDateTime* operator()() const + { + return get(); + } + + void set (GDateTime* in) { + auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; + dt_ = std::shared_ptr(g_date_time_ref(in), deleter); + } + + DateTime& operator=(GDateTime* in) + { + set (in); + return *this; + } + + DateTime& operator=(const DateTime& in) + { + set (in.get()); + return *this; + } + + 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 + { + 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; + } + +private: + + std::shared_ptr dt_; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_DATETIME_H diff --git a/include/datetime/dbus-shared.h b/include/datetime/dbus-shared.h new file mode 100644 index 0000000..24319e3 --- /dev/null +++ b/include/datetime/dbus-shared.h @@ -0,0 +1,24 @@ +/* +An indicator to show date and time information. + +Copyright 2010 Canonical Ltd. + +Authors: + Ted Gould + +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 . +*/ + +#define BUS_NAME "com.canonical.indicator.datetime" +#define BUS_PATH "/com/canonical/indicator/datetime" + diff --git a/include/datetime/settings-shared.h b/include/datetime/settings-shared.h new file mode 100644 index 0000000..896db95 --- /dev/null +++ b/include/datetime/settings-shared.h @@ -0,0 +1,49 @@ +/* + * 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 . + * + * Authors: + * Ted Gould + * Charles Kerr + */ + +#ifndef __DATETIME_SETTINGS_SHARED_H__ +#define __DATETIME_SETTINGS_SHARED_H__ + +typedef enum +{ + TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM +} +TimeFormatMode; + +#define SETTINGS_INTERFACE "com.canonical.indicator.datetime" +#define SETTINGS_SHOW_CLOCK_S "show-clock" +#define SETTINGS_TIME_FORMAT_S "time-format" +#define SETTINGS_SHOW_SECONDS_S "show-seconds" +#define SETTINGS_SHOW_DAY_S "show-day" +#define SETTINGS_SHOW_DATE_S "show-date" +#define SETTINGS_SHOW_YEAR_S "show-year" +#define SETTINGS_CUSTOM_TIME_FORMAT_S "custom-time-format" +#define SETTINGS_SHOW_CALENDAR_S "show-calendar" +#define SETTINGS_SHOW_WEEK_NUMBERS_S "show-week-numbers" +#define SETTINGS_SHOW_EVENTS_S "show-events" +#define SETTINGS_SHOW_LOCATIONS_S "show-locations" +#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" +#define SETTINGS_LOCATIONS_S "locations" +#define SETTINGS_TIMEZONE_NAME_S "timezone-name" + +#endif /* __DATETIME_SETTINGS_SHARED_H__ */ diff --git a/include/datetime/timezones-live.h b/include/datetime/timezones-live.h new file mode 100644 index 0000000..3075bd8 --- /dev/null +++ b/include/datetime/timezones-live.h @@ -0,0 +1,56 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_LIVE_TIMEZONES_H +#define INDICATOR_DATETIME_LIVE_TIMEZONES_H + +#include // base class +#include // aggregated +#include // aggregated + +#include // shared_ptr<> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Timezones object that uses a #FileTimezone and #GeoclueTimezone + * to detect what timezone we're in + */ +class LiveTimezones: public Timezones +{ +public: + LiveTimezones(const std::string& filename); + + /** \brief Whether or not to track location by IP address */ + core::Property geolocationEnabled = core::Property(false); + +private: + FileTimezone file_; + std::shared_ptr geo_; + void updateGeolocation(); + void updateTimezones(); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_LIVE_TIMEZONES_H diff --git a/include/datetime/timezones.h b/include/datetime/timezones.h new file mode 100644 index 0000000..0b97683 --- /dev/null +++ b/include/datetime/timezones.h @@ -0,0 +1,56 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_TIMEZONES_H +#define INDICATOR_DATETIME_TIMEZONES_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** \brief Aggregates one or more timezone detectors and decides which to use */ +class Timezones +{ +public: + + Timezones() =default; + + virtual ~Timezones() =default; + + /** + * \brief the current timezone + */ + core::Property timezone; + + /** + * \brief all the detected timezones. + * The count is >1 iff the detection mechamisms disagree. + */ + core::Property > timezones; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_TIMEZONES_H diff --git a/include/datetime/utils.h b/include/datetime/utils.h new file mode 100644 index 0000000..bd2e132 --- /dev/null +++ b/include/datetime/utils.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- + +A dialog for setting time and date preferences. + +Copyright 2010 Canonical Ltd. + +Authors: + Michael Terry + +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 . +*/ + +#ifndef __DATETIME_UTILS_H__ +#define __DATETIME_UTILS_H__ + +#include +#include /* GSettings */ + +G_BEGIN_DECLS + +gboolean is_locale_12h (void); + +void split_settings_location (const char * location, + char ** zone, + char ** name); + +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 -- cgit v1.2.3 From 81c3d4ca5ee8f43e3996bec3be02c566a5e33a4c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:10:11 -0600 Subject: initial cut for syncing panel with c++ changes. this is broken. --- panel/CMakeLists.txt | 6 +++--- panel/datetime-prefs-locations.c | 22 +++++++++++----------- panel/datetime-prefs.c | 39 +++++++++++++++++++++------------------ 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index b3fcc7b..4ac3289 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -6,9 +6,9 @@ add_library (${PANEL_LIB} SHARED datetime-prefs.c datetime-prefs-locations.c datetime-prefs-locations.h - ${CMAKE_SOURCE_DIR}/src/utils.c - ${CMAKE_SOURCE_DIR}/src/utils.h - ${CMAKE_SOURCE_DIR}/src/settings-shared.h) + ${CMAKE_SOURCE_DIR}/src/utils.cpp + ${CMAKE_SOURCE_DIR}/include/datetime/utils.h + ${CMAKE_SOURCE_DIR}/include/datetime/settings-shared.h) include_directories (${PANEL_DEPS_INCLUDE_DIRS}) diff --git a/panel/datetime-prefs-locations.c b/panel/datetime-prefs-locations.c index 54ab8f4..b79c014 100644 --- a/panel/datetime-prefs-locations.c +++ b/panel/datetime-prefs-locations.c @@ -24,15 +24,18 @@ with this program. If not, see . #include "config.h" #endif -#include -#include /* time_t */ +#include "datetime-prefs-locations.h" + +#include +#include + +#include + #include #include -#include -#include "datetime-prefs-locations.h" -#include "settings-shared.h" -#include "utils.h" +#include +#include /* time_t */ #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/datetime-dialog.ui" @@ -310,7 +313,7 @@ handle_edit (GtkCellRendererText * renderer G_GNUC_UNUSED, gtk_list_store_set (store, &iter, COL_VISIBLE_NAME, new_text, - COL_ICON, correct ? NULL : GTK_STOCK_DIALOG_ERROR, + COL_ICON, correct ? NULL : "dialog-error", -1); } } @@ -425,7 +428,6 @@ update_times (GtkWidget * dlg) g_signal_handlers_block_by_func (store, save_when_idle, dlg); - GSettings * settings = g_settings_new (SETTINGS_INTERFACE); GtkTreeIter iter; if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { GDateTime * now = g_date_time_new_now_local (); @@ -437,7 +439,7 @@ update_times (GtkWidget * dlg) if (strzone && *strzone) { GTimeZone * tz = g_time_zone_new (strzone); GDateTime * now_tz = g_date_time_to_timezone (now, tz); - gchar * format = generate_full_format_string_at_time (now, now_tz, settings); + gchar * format = generate_full_format_string_at_time (now, now_tz); gchar * time_str = g_date_time_format (now_tz, format); gchar * old_time_str; @@ -456,8 +458,6 @@ update_times (GtkWidget * dlg) g_date_time_unref (now); } - g_object_unref (settings); - g_signal_handlers_unblock_by_func (store, save_when_idle, dlg); return TRUE; diff --git a/panel/datetime-prefs.c b/panel/datetime-prefs.c index 55456ac..90f9e79 100644 --- a/panel/datetime-prefs.c +++ b/panel/datetime-prefs.c @@ -25,22 +25,25 @@ with this program. If not, see . #include "config.h" #endif -#include -#include -#include -#include -#include -#include -#include +#include "datetime-prefs-locations.h" + +#include +#include +#include + #include #include #include #include -#include "dbus-shared.h" -#include "settings-shared.h" -#include "utils.h" -#include "datetime-prefs-locations.h" +#include +#include +#include + +#include +#include +#include +#include #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/datetime-dialog.ui" @@ -221,8 +224,8 @@ sync_entry (IndicatorDatetimePanel * self, const gchar * location) gtk_entry_set_text (GTK_ENTRY (self->priv->tz_entry), name); g_free (name); - gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->tz_entry), - GTK_ENTRY_ICON_SECONDARY, NULL); + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (self->priv->tz_entry), + GTK_ENTRY_ICON_SECONDARY, NULL); } static void @@ -287,10 +290,10 @@ proxy_ready (GObject *object G_GNUC_UNUSED, { if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) { - const gchar *timezone = g_variant_get_string (value, NULL); + const gchar * str = g_variant_get_string (value, NULL); - cc_timezone_map_set_timezone (priv->tzmap, timezone); - sync_entry (self, timezone); + cc_timezone_map_set_timezone (priv->tzmap, str); + sync_entry (self, str); g_signal_connect (priv->tzmap, "location-changed", G_CALLBACK (tz_changed), self); } g_variant_unref (value); @@ -637,8 +640,8 @@ entry_focus_out (GtkEntry * entry, g_free (name); g_free (zone); - gtk_entry_set_icon_from_stock (entry, GTK_ENTRY_ICON_SECONDARY, - correct ? NULL : GTK_STOCK_DIALOG_ERROR); + gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, + correct ? NULL : "dialog-error"); gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, _("You need to choose a location to change the time zone.")); gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, FALSE); -- cgit v1.2.3 From 3b8833efe6ab21387b6f73b4a4ef757445801623 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:10:18 -0600 Subject: add geoclue, glib test fixtures --- include/CMakeLists.txt | 2 + src/CMakeLists.txt | 50 ++-- src/clock-live.c | 278 --------------------- src/clock-live.h | 73 ------ src/clock.c | 110 -------- src/clock.h | 76 ------ src/dbus-shared.h | 24 -- src/formatter.cpp | 462 ++++++++++++++++++++++++++++++++++ src/planner-eds.c | 653 ------------------------------------------------ src/planner.c | 281 --------------------- src/service.h | 84 ------- src/settings-shared.h | 50 ---- src/timezone-file.c | 212 ---------------- src/timezone-file.h | 58 ----- src/timezone-geoclue.c | 227 ----------------- src/timezone-geoclue.h | 57 ----- src/timezone.c | 134 ---------- src/timezone.h | 72 ------ src/timezones-live.cpp | 69 +++++ src/utils.c | 453 --------------------------------- src/utils.cpp | 140 +++++++++++ src/utils.h | 66 ----- tests/CMakeLists.txt | 76 +++++- tests/geoclue-fixture.h | 142 +++++++++++ tests/glib-fixture.h | 131 ++++++++++ tests/test-core.cc | 148 +++++++++++ tests/test-skew.cc | 209 ++++++++++++++++ tests/test-timezones.cc | 122 +++++++++ 28 files changed, 1520 insertions(+), 2939 deletions(-) create mode 100644 include/CMakeLists.txt delete mode 100644 src/clock-live.c delete mode 100644 src/clock-live.h delete mode 100644 src/clock.c delete mode 100644 src/clock.h delete mode 100644 src/dbus-shared.h create mode 100644 src/formatter.cpp delete mode 100644 src/planner-eds.c delete mode 100644 src/planner.c delete mode 100644 src/service.h delete mode 100644 src/settings-shared.h delete mode 100644 src/timezone-file.c delete mode 100644 src/timezone-file.h delete mode 100644 src/timezone-geoclue.c delete mode 100644 src/timezone-geoclue.h delete mode 100644 src/timezone.c delete mode 100644 src/timezone.h create mode 100644 src/timezones-live.cpp delete mode 100644 src/utils.c create mode 100644 src/utils.cpp delete mode 100644 src/utils.h create mode 100644 tests/geoclue-fixture.h create mode 100644 tests/glib-fixture.h create mode 100644 tests/test-core.cc create mode 100644 tests/test-skew.cc create mode 100644 tests/test-timezones.cc diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..cfef04b --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(core) +add_subdirectory(datetime) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d51385..2c847ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,8 @@ -set (SERVICE_LIB "libindicatordatetimeservice") +set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") + add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") @@ -10,36 +12,28 @@ if (BUILD_PANEL) endif () add_library (${SERVICE_LIB} STATIC - clock.c - clock.h - clock-live.c - clock-live.h - planner.c - planner.h - planner-eds.c - planner-eds.h - service.c - service.h - timezone.c - timezone.h - timezone-file.c - timezone-file.h - timezone-geoclue.c - timezone-geoclue.h - utils.c - utils.h - dbus-shared.h - settings-shared.h) -include_directories (${SERVICE_DEPS_INCLUDE_DIRS}) + clock.cpp + clock-live.cpp + formatter.cpp + formatter-desktop.cpp + locations.cpp + locations-settings.cpp + planner-eds.cpp + timezone-file.cpp + timezone-geoclue.cpp + timezones-live.cpp + utils.cpp) +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.c) +#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} - APPEND_STRING PROPERTY COMPILE_FLAGS - " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") +#set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} +# APPEND_STRING PROPERTY COMPILE_FLAGS +# " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") diff --git a/src/clock-live.c b/src/clock-live.c deleted file mode 100644 index 2a375fd..0000000 --- a/src/clock-live.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include -#include - -#include "clock-live.h" -#include "settings-shared.h" -#include "timezone-file.h" -#include "timezone-geoclue.h" - -/*** -**** private struct -***/ - -struct _IndicatorDatetimeClockLivePriv -{ - GSettings * settings; - - GSList * timezones; /* IndicatorDatetimeTimezone */ - gchar ** timezones_strv; - GTimeZone * localtime_zone; -}; - -typedef IndicatorDatetimeClockLivePriv priv_t; - -/*** -**** GObject boilerplate -***/ - -static void indicator_datetime_clock_interface_init ( - IndicatorDatetimeClockInterface * iface); - -G_DEFINE_TYPE_WITH_CODE ( - IndicatorDatetimeClockLive, - indicator_datetime_clock_live, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_DATETIME_CLOCK, - indicator_datetime_clock_interface_init)) - -/*** -**** Timezones -***/ - -static void -on_current_timezone_changed (IndicatorDatetimeClockLive * self) -{ - priv_t * p = self->priv; - - /* Invalidate the timezone information. - These fields will be lazily regenerated by rebuild_timezones() */ - g_clear_pointer (&p->timezones_strv, g_strfreev); - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - - indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self)); -} - -static void -set_detect_location_enabled (IndicatorDatetimeClockLive * self, gboolean enabled) -{ - GSList * l; - priv_t * p = self->priv; - gboolean changed = FALSE; - - /* clear out the old timezone objects */ - if (p->timezones != NULL) - { - for (l=p->timezones; l!=NULL; l=l->next) - { - g_signal_handlers_disconnect_by_func (l->data, on_current_timezone_changed, self); - g_object_unref (l->data); - } - - g_slist_free (p->timezones); - p->timezones = NULL; - changed = TRUE; - } - - /* maybe add new timezone objects */ - if (enabled) - { - p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_geoclue_new ()); - p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_file_new (TIMEZONE_FILE)); - - for (l=p->timezones; l!=NULL; l=l->next) - { - g_signal_connect_swapped (l->data, "notify::timezone", - G_CALLBACK(on_current_timezone_changed), self); - } - - changed = TRUE; - } - - if (changed) - on_current_timezone_changed (self); -} - -/* When the 'auto-detect timezone' boolean setting changes, - start or stop watching geoclue and /etc/timezone */ -static void -on_detect_location_changed (IndicatorDatetimeClockLive * self) -{ - const gboolean enabled = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_DETECTED_S); - set_detect_location_enabled (self, enabled); -} - -/*** -**** IndicatorDatetimeClock virtual functions -***/ - -static void -rebuild_timezones (IndicatorDatetimeClockLive * self) -{ - priv_t * p; - GHashTable * hash; - GSList * l; - int i; - GHashTableIter iter; - gpointer key; - - p = self->priv; - - /* Build a hashtable of timezone strings. - This will weed out duplicates. */ - hash = g_hash_table_new (g_str_hash, g_str_equal); - for (l=p->timezones; l!=NULL; l=l->next) - { - const gchar * tz = indicator_datetime_timezone_get_timezone (l->data); - if (tz && *tz) - g_hash_table_add (hash, (gpointer) tz); - } - - /* rebuild p->timezone_strv */ - g_strfreev (p->timezones_strv); - p->timezones_strv = g_new0 (gchar*, g_hash_table_size(hash) + 1); - i = 0; - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, &key, NULL)) - p->timezones_strv[i++] = g_strdup (key); - - /* rebuild localtime_zone */ - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - p->localtime_zone = g_time_zone_new (p->timezones_strv ? p->timezones_strv[0] : NULL); - - /* cleanup */ - g_hash_table_unref (hash); -} - -static const gchar ** -my_get_timezones (IndicatorDatetimeClock * clock) -{ - IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock); - priv_t * p = self->priv; - - if (G_UNLIKELY (p->timezones_strv == NULL)) - rebuild_timezones (self); - - return (const gchar **) p->timezones_strv; -} - -static GDateTime * -my_get_localtime (IndicatorDatetimeClock * clock) -{ - IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock); - priv_t * p = self->priv; - - if (G_UNLIKELY (p->localtime_zone == NULL)) - rebuild_timezones (self); - - return g_date_time_new_now (p->localtime_zone); -} - -/*** -**** GObject virtual functions -***/ - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimeClockLive * self; - priv_t * p; - - self = INDICATOR_DATETIME_CLOCK_LIVE(o); - p = self->priv; - - if (p->settings != NULL) - { - g_signal_handlers_disconnect_by_data (p->settings, self); - g_clear_object (&p->settings); - } - - set_detect_location_enabled (self, FALSE); - - G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeClockLive * self; - priv_t * p; - - self = INDICATOR_DATETIME_CLOCK_LIVE(o); - p = self->priv; - - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - g_strfreev (p->timezones_strv); - - G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_clock_live_class_init (IndicatorDatetimeClockLiveClass * klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - - g_type_class_add_private (klass, - sizeof (IndicatorDatetimeClockLivePriv)); -} - -static void -indicator_datetime_clock_interface_init (IndicatorDatetimeClockInterface * iface) -{ - iface->get_localtime = my_get_localtime; - iface->get_timezones = my_get_timezones; -} - -static void -indicator_datetime_clock_live_init (IndicatorDatetimeClockLive * self) -{ - IndicatorDatetimeClockLivePriv * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, - IndicatorDatetimeClockLivePriv); - self->priv = p; - - p->settings = g_settings_new (SETTINGS_INTERFACE); - g_signal_connect_swapped (p->settings, "changed::" SETTINGS_SHOW_DETECTED_S, - G_CALLBACK(on_detect_location_changed), self); - - on_detect_location_changed (self); -} - -/*** -**** Public API -***/ - -IndicatorDatetimeClock * -indicator_datetime_clock_live_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_CLOCK_LIVE, NULL); - - return INDICATOR_DATETIME_CLOCK (o); -} diff --git a/src/clock-live.h b/src/clock-live.h deleted file mode 100644 index 4425f5b..0000000 --- a/src/clock-live.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_CLOCK_LIVE__H__ -#define __INDICATOR_DATETIME_CLOCK_LIVE__H__ - -#include /* parent class */ - -#include "clock.h" - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_CLOCK_LIVE \ - (indicator_datetime_clock_live_get_type()) - -#define INDICATOR_DATETIME_CLOCK_LIVE(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \ - IndicatorDatetimeClockLive)) - -#define INDICATOR_DATETIME_CLOCK_LIVE_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \ - IndicatorDatetimeClockLiveClass)) - -#define INDICATOR_IS_DATETIME_CLOCK_LIVE(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE)) - -typedef struct _IndicatorDatetimeClockLive - IndicatorDatetimeClockLive; -typedef struct _IndicatorDatetimeClockLivePriv - IndicatorDatetimeClockLivePriv; -typedef struct _IndicatorDatetimeClockLiveClass - IndicatorDatetimeClockLiveClass; - -/** - * An IndicatorDatetimeClock which gives live clock times - * from timezones determined by geoclue and /etc/timezone - */ -struct _IndicatorDatetimeClockLive -{ - GObject parent_instance; - - IndicatorDatetimeClockLivePriv * priv; -}; - -struct _IndicatorDatetimeClockLiveClass -{ - GObjectClass parent_class; -}; - -IndicatorDatetimeClock * indicator_datetime_clock_live_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_CLOCK_LIVE__H__ */ diff --git a/src/clock.c b/src/clock.c deleted file mode 100644 index 2c2fec2..0000000 --- a/src/clock.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "clock.h" - -enum -{ - SIGNAL_CHANGED, - SIGNAL_LAST -}; - -static guint signals[SIGNAL_LAST] = { 0 }; - -G_DEFINE_INTERFACE (IndicatorDatetimeClock, - indicator_datetime_clock, - G_TYPE_OBJECT); - -static void -indicator_datetime_clock_default_init (IndicatorDatetimeClockInterface * klass) -{ - signals[SIGNAL_CHANGED] = g_signal_new ( - "changed", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (IndicatorDatetimeClockInterface, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -/*** -**** PUBLIC API -***/ - -/** - * Get a strv of timezones. - * - * Return value: (element-type char*) - * (transfer full): - * array of timezone strings - */ -const gchar ** -indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * self) -{ - const gchar ** timezones; - IndicatorDatetimeClockInterface * iface; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL); - iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self); - - if (iface->get_timezones != NULL) - timezones = iface->get_timezones (self); - else - timezones = NULL; - - return timezones; -} - -/** - * Get the current time. - * - * Return value: (element-type GDateTime*) - * (transfer full): - * the current time. - */ -GDateTime * -indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * self) -{ - GDateTime * now; - IndicatorDatetimeClockInterface * iface; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL); - iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self); - - if (iface->get_localtime != NULL) - now = iface->get_localtime (self); - else - now = NULL; - - return now; -} - -/** - * Emits the "changed" signal. - * - * This should only be called by subclasses. - */ -void -indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_CLOCK (self)); - - g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL); -} diff --git a/src/clock.h b/src/clock.h deleted file mode 100644 index 40cdf1c..0000000 --- a/src/clock.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_CLOCK__H__ -#define __INDICATOR_DATETIME_CLOCK__H__ - -#include - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_CLOCK \ - (indicator_datetime_clock_get_type ()) - -#define INDICATOR_DATETIME_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - INDICATOR_TYPE_DATETIME_CLOCK, \ - IndicatorDatetimeClock)) - -#define INDICATOR_IS_DATETIME_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DATETIME_CLOCK)) - -#define INDICATOR_DATETIME_CLOCK_GET_INTERFACE(inst) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ - INDICATOR_TYPE_DATETIME_CLOCK, \ - IndicatorDatetimeClockInterface)) - -typedef struct _IndicatorDatetimeClock - IndicatorDatetimeClock; - -typedef struct _IndicatorDatetimeClockInterface - IndicatorDatetimeClockInterface; - -struct _IndicatorDatetimeClockInterface -{ - GTypeInterface parent_iface; - - /* signals */ - void (*changed) (IndicatorDatetimeClock * self); - - /* virtual functions */ - const gchar** (*get_timezones) (IndicatorDatetimeClock * self); - GDateTime* (*get_localtime) (IndicatorDatetimeClock * self); -}; - -GType indicator_datetime_clock_get_type (void); - -/*** -**** -***/ - -const gchar ** indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * clock); - -GDateTime * indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * clock); - -void indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * clock); - - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_CLOCK__H__ */ diff --git a/src/dbus-shared.h b/src/dbus-shared.h deleted file mode 100644 index 24319e3..0000000 --- a/src/dbus-shared.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -An indicator to show date and time information. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#define BUS_NAME "com.canonical.indicator.datetime" -#define BUS_PATH "/com/canonical/indicator/datetime" - diff --git a/src/formatter.cpp b/src/formatter.cpp new file mode 100644 index 0000000..4e2f582 --- /dev/null +++ b/src/formatter.cpp @@ -0,0 +1,462 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +#include +#include + +#include // setlocale() +#include // nl_langinfo() +#include // strstr() + +namespace +{ +void clearTimer (guint& tag) +{ + if (tag) + { + g_source_remove (tag); + tag = 0; + } +} + +guint calculate_milliseconds_until_next_minute (GDateTime * now) +{ + GDateTime * next; + GDateTime * start_of_next; + GTimeSpan interval_usec; + 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); + + interval_usec = g_date_time_difference(start_of_next, now); + interval_msec = (interval_usec + 999) / 1000; + + g_date_time_unref(start_of_next); + g_date_time_unref(next); + return interval_msec; +} + +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_msec = (interval_usec + 999) / 1000; + return interval_msec; +} + +/* + * We periodically rebuild the sections that have time format strings + * that are dependent on the current time: + * + * 1. appointment menuitems' time format strings depend on the + * current time; for example, they don't show the day of week + * if the appointment is today. + * + * 2. location menuitems' time format strings depend on the + * current time; for example, they don't show the day of the week + * if the local date and location date are the same. + * + * 3. the "local date" menuitem in the calendar section is, + * obviously, dependent on the local time. + * + * In short, we want to update whenever the number of days between two zone + * might have changed. We do that by updating when either zone's day changes. + * + * Since not all UTC offsets are evenly divisible by hours + * (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) +{ + char * str; + gint minute; + guint seconds; + GTimeSpan diff; + GDateTime * next; + GDateTime * start_of_next; + + 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), + 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); + + 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 + + + +namespace unity { +namespace indicator { +namespace datetime { + +class Formatter::Impl +{ +public: + + Impl (Formatter* owner, const std::shared_ptr& clock): + owner_(owner), + clock_(clock) + { + owner_->headerFormat.changed().connect([this](const std::string& fmt G_GNUC_UNUSED){updateHeader();}); + updateHeader(); + + restartRelativeTimer(); + } + + ~Impl() + { + clearTimer(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); + + restartHeaderTimer(); + } + + void restartHeaderTimer() + { + clearTimer(header_timer_); + + const std::string fmt = 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) + || (fmt.find("%X") != std::string::npos) + || (fmt.find("%c") != std::string::npos); + + guint interval_msec; + GDateTime * now = clock_->localtime(); + if (header_shows_seconds) + interval_msec = calculate_milliseconds_until_next_second (now); + else + interval_msec = calculate_milliseconds_until_next_minute (now); + g_date_time_unref (now); + + 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); + } + + static gboolean onHeaderTimer(gpointer gself) + { + static_cast(gself)->updateHeader(); + return G_SOURCE_REMOVE; + } + +private: + + void restartRelativeTimer() + { + clearTimer(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); + } + + static gboolean onRelativeTimer(gpointer gself) + { + auto self = static_cast(gself); + self->owner_->relativeFormatChanged(); + self->restartRelativeTimer(); + return G_SOURCE_REMOVE; + } + +private: + Formatter * const owner_; + guint header_timer_ = 0; + guint relative_timer_ = 0; + +public: + std::shared_ptr clock_; +}; + +/*** +**** +***/ + +Formatter::Formatter(const std::shared_ptr& clock): + p (new Formatter::Impl(this, clock)) +{ +} + +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; + + for (i=0; formats_24h[i]; ++i) + if (strstr (t_fmt, formats_24h[i])) + return false; + + return true; +} + +const char * +Formatter::T_(const char *msg) +{ + /* General strategy here is to make sure LANGUAGE is empty (since that + trumps all LC_* vars) and then to temporarily swap LC_TIME and + LC_MESSAGES. Then have gettext translate msg. + + We strdup the strings because the setlocale & *env functions do not + guarantee anything about the storage used for the string, and thus + the string may not be portably safe after multiple calls. + + Note that while you might think g_dcgettext would do the trick here, + that actually looks in /usr/share/locale/XX/LC_TIME, not the + LC_MESSAGES directory, so we won't find any translation there. + */ + + char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); + const char *time_locale = setlocale (LC_TIME, nullptr); + char *language = g_strdup(g_getenv("LANGUAGE")); + const char *rv; + + if (language) + g_unsetenv("LANGUAGE"); + setlocale(LC_MESSAGES, time_locale); + + /* Get the LC_TIME version */ + rv = _(msg); + + /* Put everything back the way it was */ + setlocale(LC_MESSAGES, message_locale); + if (language) + g_setenv("LANGUAGE", language, TRUE); + + g_free(message_locale); + g_free(language); + return rv; +} + +const char * +Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) +{ + const char * fmt; + + if (twelvehour && show_seconds) + /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ + fmt = T_("%l:%M:%S %p"); + else if (twelvehour) + /* TRANSLATORS: a strftime(3) format for 12hr time */ + fmt = T_("%l:%M %p"); + else if (show_seconds) + /* TRANSLATORS: a strftime(3) format for 24hr time w/seconds */ + fmt = T_("%H:%M:%S"); + else + /* TRANSLATORS: a strftime(3) format for 24hr time */ + fmt = T_("%H:%M"); + + return fmt; +} + +/*** +**** +***/ + +namespace +{ +typedef enum +{ + DATE_PROXIMITY_TODAY, + DATE_PROXIMITY_TOMORROW, + DATE_PROXIMITY_WEEK, + DATE_PROXIMITY_FAR +} +date_proximity_t; + +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); + if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) + prox = DATE_PROXIMITY_TODAY; + + // does it happen tomorrow? + if (prox == DATE_PROXIMITY_FAR) + { + 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); + if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) + prox = DATE_PROXIMITY_TOMORROW; + + g_date_time_unref (tomorrow); + } + + // does it happen this week? + if (prox == DATE_PROXIMITY_FAR) + { + 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); + + if (g_date_time_compare (time, week_bound) <= 0) + prox = DATE_PROXIMITY_WEEK; + + g_date_time_unref (week_bound); + g_date_time_unref (week); + } + + return prox; +} +} // anonymous namespace + +/** + * _ a time today should be shown as just the time (e.g. “3:55 PM”) + * _ a full-day event today should be shown as “Today” + * _ a time any other day this week should be shown as the short version of the + * day and time (e.g. “Wed 3:55 PM”) + * _ a full-day event tomorrow should be shown as “Tomorrow” + * _ a full-day event another day this week should be shown as the + * weekday (e.g. “Friday”) + * _ a time after this week should be shown as the short version of the day, + * date, and time (e.g. “Wed 21 Apr 3:55 PM”) + * _ a full-day event after this week should be shown as the short version of + * the day and date (e.g. “Wed 21 Apr”). + * _ in addition, when presenting the times of upcoming events, the time should + * be followed by the timezone if it is different from the one the computer + * is currently set to. For example, “Wed 3:55 PM UTC−5”. + */ +std::string +Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const +{ + std::string ret; + GDateTime * now = p->clock_->localtime(); + + 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); + + if (full_day) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = _("Today"); break; + case DATE_PROXIMITY_TOMORROW: ret = _("Tomorrow"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%A"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b"); break; + } + } + else if (is_locale_12h()) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = T_("%l:%M %p"); break; + case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%l:%M %p"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%l:%M %p"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%l:%M %p"); break; + } + } + else + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = T_("%H:%M"); break; + case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%H:%M"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%H:%M"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%H:%M"); break; + } + } + + /* if it's an appointment in a different timezone (and doesn't run for a full day) + 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)))) + { + ret += ' '; + ret += g_date_time_get_timezone_abbreviation (then); + } + } + + g_date_time_unref (now); + return ret; +} + +/*** +**** +***/ + +} // namespace datetime + +} // namespace indicator + +} // namespace unity diff --git a/src/planner-eds.c b/src/planner-eds.c deleted file mode 100644 index cc2b8c5..0000000 --- a/src/planner-eds.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include -#include -#include -#include - -#include "planner-eds.h" - -struct _IndicatorDatetimePlannerEdsPriv -{ - GSList * sources; - GCancellable * cancellable; - ESourceRegistry * source_registry; -}; - -typedef IndicatorDatetimePlannerEdsPriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimePlannerEds, - indicator_datetime_planner_eds, - INDICATOR_TYPE_DATETIME_PLANNER) - -G_DEFINE_QUARK ("source-client", source_client) - -/*** -**** -**** my_get_appointments() helpers -**** -***/ - -/* whole-task data that all the subtasks can see */ -struct appointment_task_data -{ - /* a ref to the planner's cancellable */ - GCancellable * cancellable; - - /* how many subtasks are still running on */ - int subtask_count; - - /* the list of appointments to be returned */ - GSList * appointments; -}; - -static struct appointment_task_data * -appointment_task_data_new (GCancellable * cancellable) -{ - struct appointment_task_data * data; - - data = g_slice_new0 (struct appointment_task_data); - data->cancellable = g_object_ref (cancellable); - return data; -} - -static void -appointment_task_data_free (gpointer gdata) -{ - struct appointment_task_data * data = gdata; - - g_object_unref (data->cancellable); - - g_slice_free (struct appointment_task_data, data); -} - -static void -appointment_task_done (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - g_task_return_pointer (task, data->appointments, NULL); - g_object_unref (task); -} - -static void -appointment_task_decrement_subtasks (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - if (g_atomic_int_dec_and_test (&data->subtask_count)) - appointment_task_done (task); -} - -static void -appointment_task_increment_subtasks (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - g_atomic_int_inc (&data->subtask_count); -} - -/** -*** get-the-appointment's-uri subtasks -**/ - -struct appointment_uri_subtask_data -{ - /* The parent task */ - GTask * task; - - /* The appointment whose uri we're looking for. - This pointer is owned by the Task and isn't reffed/unreffed by the subtask */ - struct IndicatorDatetimeAppt * appt; -}; - -static void -appointment_uri_subtask_done (struct appointment_uri_subtask_data * subdata) -{ - GTask * task = subdata->task; - - /* free the subtask data */ - g_slice_free (struct appointment_uri_subtask_data, subdata); - - appointment_task_decrement_subtasks (task); -} - -static struct appointment_uri_subtask_data * -appointment_uri_subtask_data_new (GTask * task, struct IndicatorDatetimeAppt * appt) -{ - struct appointment_uri_subtask_data * subdata; - - appointment_task_increment_subtasks (task); - - subdata = g_slice_new0 (struct appointment_uri_subtask_data); - subdata->task = task; - subdata->appt = appt; - return subdata; -} - -static void -on_appointment_uris_ready (GObject * client, - GAsyncResult * res, - gpointer gsubdata) -{ - GSList * uris; - GError * error; - struct appointment_uri_subtask_data * subdata = gsubdata; - - uris = NULL; - error = NULL; - e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); - if (error != NULL) - { - 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); - } - else if (uris != NULL) - { - struct IndicatorDatetimeAppt * appt = subdata->appt; - appt->url = g_strdup (uris->data); /* copy the first URL */ - g_debug ("found url '%s' for appointment '%s'", appt->url, appt->summary); - e_client_util_free_string_slist (uris); - } - - appointment_uri_subtask_done (subdata); -} - -/** -*** enumerate-the-components subtasks -**/ - -/* data struct for the enumerate-components subtask */ -struct appointment_component_subtask_data -{ - /* The parent task */ - GTask * task; - - /* The client we're walking through. The subtask owns a ref to this */ - ECalClient * client; - - /* The appointment's color coding. The subtask owns this string */ - gchar * color; -}; - -static void -on_appointment_component_subtask_done (gpointer gsubdata) -{ - struct appointment_component_subtask_data * subdata = gsubdata; - GTask * task = subdata->task; - - /* free the subtask data */ - g_free (subdata->color); - g_object_unref (subdata->client); - g_slice_free (struct appointment_component_subtask_data, subdata); - - appointment_task_decrement_subtasks (task); -} - -static struct appointment_component_subtask_data * -appointment_component_subtask_data_new (GTask * task, ECalClient * client, const gchar * color) -{ - struct appointment_component_subtask_data * subdata; - - appointment_task_increment_subtasks (task); - - subdata = g_slice_new0 (struct appointment_component_subtask_data); - subdata->task = task; - subdata->client = g_object_ref (client); - subdata->color = g_strdup (color); - return subdata; -} - -static gboolean -my_get_appointments_foreach (ECalComponent * component, - time_t begin, - time_t end, - gpointer gsubdata) -{ - const ECalComponentVType vtype = e_cal_component_get_vtype (component); - struct appointment_component_subtask_data * subdata = gsubdata; - struct appointment_task_data * data = g_task_get_task_data (subdata->task); - - if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) - { - const gchar * uid = NULL; - icalproperty_status status = 0; - - e_cal_component_get_uid (component, &uid); - e_cal_component_get_status (component, &status); - - if ((uid != NULL) && - (status != ICAL_STATUS_COMPLETED) && - (status != ICAL_STATUS_CANCELLED)) - { - GList * alarm_uids; - GSList * l; - GSList * recur_list; - ECalComponentText text; - struct IndicatorDatetimeAppt * appt; - struct appointment_uri_subtask_data * uri_subdata; - - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - - /* Determine whether this is a recurring event. - NB: icalrecurrencetype supports complex recurrence patterns; - however, since design only allows daily recurrence, - that's all we support here. */ - e_cal_component_get_rrule_list (component, &recur_list); - for (l=recur_list; l!=NULL; l=l->next) - { - const struct icalrecurrencetype * recur = l->data; - appt->is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) - && (recur->interval == 1)); - } - e_cal_component_free_recur_list (recur_list); - - text.value = ""; - e_cal_component_get_summary (component, &text); - - appt->begin = g_date_time_new_from_unix_local (begin); - appt->end = g_date_time_new_from_unix_local (end); - appt->color = g_strdup (subdata->color); - appt->is_event = vtype == E_CAL_COMPONENT_EVENT; - appt->summary = g_strdup (text.value); - appt->uid = g_strdup (uid); - - alarm_uids = e_cal_component_get_alarm_uids (component); - appt->has_alarms = alarm_uids != NULL; - cal_obj_uid_list_free (alarm_uids); - - data->appointments = g_slist_prepend (data->appointments, appt); - - /* start a new subtask to get the associated URIs */ - uri_subdata = appointment_uri_subtask_data_new (subdata->task, appt); - e_cal_client_get_attachment_uris (subdata->client, - uid, - NULL, - data->cancellable, - on_appointment_uris_ready, - uri_subdata); - } - } - - return G_SOURCE_CONTINUE; -} - -/*** -**** IndicatorDatetimePlanner virtual funcs -***/ - -static void -my_get_appointments (IndicatorDatetimePlanner * planner, - GDateTime * begin_datetime, - GDateTime * end_datetime, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSList * l; - priv_t * p; - const char * str; - icaltimezone * default_timezone; - const int64_t begin = g_date_time_to_unix (begin_datetime); - const int64_t end = g_date_time_to_unix (end_datetime); - GTask * task; - gboolean subtasks_added; - - p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv; - - /** - *** init the default timezone - **/ - - default_timezone = NULL; - - if ((str = indicator_datetime_planner_get_timezone (planner))) - { - default_timezone = icaltimezone_get_builtin_timezone (str); - - if (default_timezone == NULL) /* maybe str is a tzid? */ - default_timezone = icaltimezone_get_builtin_timezone_from_tzid (str); - } - - /** - *** walk through the sources to build the appointment list - **/ - - task = g_task_new (planner, p->cancellable, callback, user_data); - g_task_set_task_data (task, - appointment_task_data_new (p->cancellable), - appointment_task_data_free); - - subtasks_added = FALSE; - for (l=p->sources; l!=NULL; l=l->next) - { - ESource * source; - ECalClient * client; - const char * color; - struct appointment_component_subtask_data * subdata; - - source = l->data; - client = g_object_get_qdata (l->data, source_client_quark()); - if (client == NULL) - continue; - - if (default_timezone != NULL) - e_cal_client_set_default_timezone (client, default_timezone); - - /* start a new subtask to enumerate all the components in this client. */ - color = e_source_selectable_get_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR)); - subdata = appointment_component_subtask_data_new (task, client, color); - subtasks_added = TRUE; - e_cal_client_generate_instances (client, - begin, - end, - p->cancellable, - my_get_appointments_foreach, - subdata, - on_appointment_component_subtask_done); - } - - if (!subtasks_added) - appointment_task_done (task); -} - -static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GAsyncResult * res, - GError ** error) -{ - return g_task_propagate_pointer (G_TASK(res), error); -} - -static gboolean -my_is_configured (IndicatorDatetimePlanner * planner) -{ - IndicatorDatetimePlannerEds * self; - - /* confirm that it's installed... */ - gchar *evo = g_find_program_in_path ("evolution"); - if (evo == NULL) - return FALSE; - - g_debug ("found calendar app: '%s'", evo); - g_free (evo); - - /* see if there are any calendar sources */ - self = INDICATOR_DATETIME_PLANNER_EDS (planner); - return self->priv->sources != NULL; -} - -static void -my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED) -{ - GError * error = NULL; - const char * const command = "evolution -c calendar"; - - if (!g_spawn_command_line_async (command, &error)) - { - g_warning ("Unable to start %s: %s", command, error->message); - g_error_free (error); - } -} - -static void -my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GDateTime * activate_time) -{ - gchar * isodate; - gchar * command; - GError * err; - - isodate = g_date_time_format (activate_time, "%Y%m%d"); - command = g_strdup_printf ("evolution \"calendar:///?startdate=%s\"", isodate); - err = 0; - if (!g_spawn_command_line_async (command, &err)) - { - g_warning ("Unable to start %s: %s", command, err->message); - g_error_free (err); - } - - g_free (command); - g_free (isodate); -} - -/*** -**** Source / Client Wrangling -***/ - -static void -on_client_connected (GObject * unused G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * error; - EClient * client; - - error = NULL; - client = e_cal_client_connect_finish (res, &error); - if (error != NULL) - { - 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); - } - 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); - - indicator_datetime_planner_emit_appointments_changed (gself); - } -} - -static void -on_source_enabled (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - e_cal_client_connect (source, - E_CAL_CLIENT_SOURCE_TYPE_EVENTS, - p->cancellable, - on_client_connected, - self); -} - -static void -on_source_added (ESourceRegistry * registry, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - p->sources = g_slist_prepend (p->sources, g_object_ref(source)); - - if (e_source_get_enabled (source)) - on_source_enabled (registry, source, gself); -} - -static void -on_source_disabled (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source, - gpointer gself) -{ - ECalClient * client; - - /* If this source has a connected ECalClient, remove it & notify clients */ - if ((client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) - { - g_object_unref (client); - indicator_datetime_planner_emit_appointments_changed (gself); - } -} - -static void -on_source_removed (ESourceRegistry * registry, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - on_source_disabled (registry, source, gself); - - p->sources = g_slist_remove (p->sources, source); - g_object_unref (source); -} - -static void -on_source_changed (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source G_GNUC_UNUSED, - gpointer gself) -{ - indicator_datetime_planner_emit_appointments_changed (gself); -} - -static void -on_source_registry_ready (GObject * source_object G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * error; - ESourceRegistry * r; - - error = NULL; - r = e_source_registry_new_finish (res, &error); - if (error != NULL) - { - 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); - } - else - { - IndicatorDatetimePlannerEds * self; - priv_t * p; - GList * l; - GList * sources; - - self = INDICATOR_DATETIME_PLANNER_EDS (gself); - p = self->priv; - - 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); - - p->source_registry = r; - - sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); - for (l=sources; l!=NULL; l=l->next) - on_source_added (r, l->data, self); - g_list_free_full (sources, g_object_unref); - } -} - -/*** -**** GObject virtual funcs -***/ - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o); - priv_t * p = self->priv; - - if (p->cancellable != NULL) - { - g_cancellable_cancel (p->cancellable); - g_clear_object (&p->cancellable); - } - - if (p->source_registry != NULL) - { - g_signal_handlers_disconnect_by_func (p->source_registry, - indicator_datetime_planner_emit_appointments_changed, - self); - - g_clear_object (&self->priv->source_registry); - } - - G_OBJECT_CLASS (indicator_datetime_planner_eds_parent_class)->dispose (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * klass) -{ - GObjectClass * object_class; - IndicatorDatetimePlannerClass * planner_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass); - planner_class->is_configured = my_is_configured; - planner_class->activate = my_activate; - planner_class->activate_time = my_activate_time; - planner_class->get_appointments = my_get_appointments; - planner_class->get_appointments_finish = my_get_appointments_finish; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv)); -} - -static void -indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER_EDS, - IndicatorDatetimePlannerEdsPriv); - - self->priv = p; - - p->cancellable = g_cancellable_new (); - - e_source_registry_new (p->cancellable, - on_source_registry_ready, - self); -} - -/*** -**** Public -***/ - -IndicatorDatetimePlanner * -indicator_datetime_planner_eds_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_EDS, NULL); - - return INDICATOR_DATETIME_PLANNER (o); -} diff --git a/src/planner.c b/src/planner.c deleted file mode 100644 index 9b9a77f..0000000 --- a/src/planner.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "planner.h" - -/** -*** Signals Boilerplate -**/ - -enum -{ - SIGNAL_APPTS_CHANGED, - SIGNAL_LAST -}; - -static guint signals[SIGNAL_LAST] = { 0 }; - -/** -*** Properties Boilerplate -**/ - -enum -{ - PROP_0, - PROP_TIMEZONE, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -/** -*** GObject Boilerplate -**/ - -G_DEFINE_TYPE (IndicatorDatetimePlanner, - indicator_datetime_planner, - G_TYPE_OBJECT) - -struct _IndicatorDatetimePlannerPriv -{ - char * timezone; -}; - -/*** -**** GObjectClass virtual funcs -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); - - switch (property_id) - { - case PROP_TIMEZONE: - g_value_set_string (value, self->priv->timezone); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_set_property (GObject * o, - guint property_id, - const GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); - - switch (property_id) - { - case PROP_TIMEZONE: - indicator_datetime_planner_set_timezone (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER(o); - - g_free (self->priv->timezone); - - G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->finalize (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_class_init (IndicatorDatetimePlannerClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerPriv)); - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = my_finalize; - object_class->get_property = my_get_property; - object_class->set_property = my_set_property; - - klass->get_appointments = NULL; - - signals[SIGNAL_APPTS_CHANGED] = g_signal_new ("appointments-changed", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (IndicatorDatetimePlannerClass, appointments_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", - "Timezone", - "Default timezone for the EDS appointments", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_planner_init (IndicatorDatetimePlanner * self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER, - IndicatorDatetimePlannerPriv); -} - -/*** -**** Public API -***/ - -void -indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - g_signal_emit (self, signals[SIGNAL_APPTS_CHANGED], 0, NULL); -} - -static gint -compare_appointments_by_start_time (gconstpointer ga, gconstpointer gb) -{ - const struct IndicatorDatetimeAppt * a = ga; - const struct IndicatorDatetimeAppt * b = gb; - - return g_date_time_compare (a->begin, b->begin); -} - -void -indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data) -{ - IndicatorDatetimePlannerClass * klass; - - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - g_return_val_if_fail (begin != NULL, NULL); - g_return_val_if_fail (end != NULL, NULL); - - klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self); - g_return_if_fail (klass->get_appointments != NULL); - klass->get_appointments (self, begin, end, callback, user_data); -} - -GSList * -indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error) -{ - IndicatorDatetimePlannerClass * klass; - GSList * appointments; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); - - klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self); - g_return_val_if_fail (klass->get_appointments_finish != NULL, NULL); - appointments = klass->get_appointments_finish (self, res, error); - return g_slist_sort (appointments, compare_appointments_by_start_time); -} - -void -indicator_datetime_planner_free_appointments (GSList * l) -{ - g_slist_free_full (l, (GDestroyNotify)indicator_datetime_appt_free); -} - -gboolean -indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), FALSE); - - return INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->is_configured (self); -} - -void -indicator_datetime_planner_activate (IndicatorDatetimePlanner * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate (self); -} - -void -indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate_time (self, time); -} - -void -indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - g_free (self->priv->timezone); - self->priv->timezone = g_strdup (timezone); - g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); -} - -const char * -indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); - - return self->priv->timezone; -} - -/*** -**** -***/ - -void -indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt) -{ - if (appt != NULL) - { - g_date_time_unref (appt->end); - g_date_time_unref (appt->begin); - g_free (appt->color); - g_free (appt->summary); - g_free (appt->url); - g_free (appt->uid); - g_slice_free (struct IndicatorDatetimeAppt, appt); - } -} - diff --git a/src/service.h b/src/service.h deleted file mode 100644 index d38db72..0000000 --- a/src/service.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_SERVICE_H__ -#define __INDICATOR_DATETIME_SERVICE_H__ - -#include -#include - -#include "clock.h" -#include "planner.h" - -G_BEGIN_DECLS - -/* standard GObject macros */ -#define INDICATOR_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeService)) -#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type()) -#define INDICATOR_IS_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_SERVICE)) - -typedef struct _IndicatorDatetimeService IndicatorDatetimeService; -typedef struct _IndicatorDatetimeServiceClass IndicatorDatetimeServiceClass; -typedef struct _IndicatorDatetimeServicePrivate IndicatorDatetimeServicePrivate; - -/* signal keys */ -#define INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST "name-lost" - -/** - * The Indicator Datetime Service. - */ -struct _IndicatorDatetimeService -{ - /*< private >*/ - GObject parent; - IndicatorDatetimeServicePrivate * priv; -}; - -struct _IndicatorDatetimeServiceClass -{ - GObjectClass parent_class; - - /* signals */ - - void (* name_lost)(IndicatorDatetimeService * self); -}; - -/*** -**** -***/ - -GType indicator_datetime_service_get_type (void); - -IndicatorDatetimeService * indicator_datetime_service_new (IndicatorDatetimeClock * clock, - IndicatorDatetimePlanner * planner); - -void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, - GDateTime * date); - -void indicator_datetime_service_set_planner (IndicatorDatetimeService * self, - IndicatorDatetimePlanner * planner); - - -void indicator_datetime_service_set_clock (IndicatorDatetimeService * self, - IndicatorDatetimeClock * clock); - - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_SERVICE_H__ */ diff --git a/src/settings-shared.h b/src/settings-shared.h deleted file mode 100644 index 4615fe8..0000000 --- a/src/settings-shared.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -An indicator to show date and time information. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ - -#ifndef __DATETIME_SETTINGS_SHARED_H__ -#define __DATETIME_SETTINGS_SHARED_H__ - -typedef enum -{ - TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM -} -TimeFormatMode; - -#define SETTINGS_INTERFACE "com.canonical.indicator.datetime" -#define SETTINGS_SHOW_CLOCK_S "show-clock" -#define SETTINGS_TIME_FORMAT_S "time-format" -#define SETTINGS_SHOW_SECONDS_S "show-seconds" -#define SETTINGS_SHOW_DAY_S "show-day" -#define SETTINGS_SHOW_DATE_S "show-date" -#define SETTINGS_SHOW_YEAR_S "show-year" -#define SETTINGS_CUSTOM_TIME_FORMAT_S "custom-time-format" -#define SETTINGS_SHOW_CALENDAR_S "show-calendar" -#define SETTINGS_SHOW_WEEK_NUMBERS_S "show-week-numbers" -#define SETTINGS_SHOW_EVENTS_S "show-events" -#define SETTINGS_SHOW_LOCATIONS_S "show-locations" -#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" -#define SETTINGS_LOCATIONS_S "locations" -#define SETTINGS_TIMEZONE_NAME_S "timezone-name" - -#endif diff --git a/src/timezone-file.c b/src/timezone-file.c deleted file mode 100644 index ddc4256..0000000 --- a/src/timezone-file.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include /* GFile, GFileMonitor */ - -#include "timezone-file.h" - -enum -{ - PROP_0, - PROP_FILENAME, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -struct _IndicatorDatetimeTimezoneFilePriv -{ - gchar * filename; - GFileMonitor * monitor; -}; - -typedef IndicatorDatetimeTimezoneFilePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeTimezoneFile, - indicator_datetime_timezone_file, - INDICATOR_TYPE_DATETIME_TIMEZONE) - -/*** -**** -***/ - -static void -reload (IndicatorDatetimeTimezoneFile * self) -{ - priv_t * p = self->priv; - - GError * err = NULL; - gchar * timezone = NULL; - - if (!g_file_get_contents (p->filename, &timezone, NULL, &err)) - { - g_warning ("%s Unable to read timezone file '%s': %s", G_STRLOC, p->filename, err->message); - g_error_free (err); - } - else - { - g_strstrip (timezone); - indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone); - g_free (timezone); - } -} - -static void -set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename) -{ - GError * err; - GFile * file; - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - g_free (p->filename); - - p->filename = g_strdup (filename); - err = NULL; - file = g_file_new_for_path (p->filename); - p->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &err); - g_object_unref (file); - if (err != NULL) - { - g_warning ("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message); - g_error_free (err); - } - else - { - g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); - g_debug ("%s Monitoring timezone file '%s'", G_STRLOC, p->filename); - } - - reload (self); -} - -/*** -**** GObjectClass funcs -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - g_value_set_string (value, self->priv->filename); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_set_property (GObject * o, - guint property_id, - const GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - set_filename (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - - G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - priv_t * p = self->priv; - - g_free (p->filename); - - G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->finalize (o); -} - -/*** -**** -***/ - -static void -indicator_datetime_timezone_file_class_init (IndicatorDatetimeTimezoneFileClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - object_class->set_property = my_set_property; - object_class->get_property = my_get_property; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneFilePriv)); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_FILENAME] = g_param_spec_string ("filename", - "Filename", - "Filename to monitor for TZ changes", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_timezone_file_init (IndicatorDatetimeTimezoneFile * self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, - IndicatorDatetimeTimezoneFilePriv); -} - -/*** -**** Public -***/ - -IndicatorDatetimeTimezone * -indicator_datetime_timezone_file_new (const char * filename) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, "filename", filename, NULL); - - return INDICATOR_DATETIME_TIMEZONE (o); -} diff --git a/src/timezone-file.h b/src/timezone-file.h deleted file mode 100644 index b02abe1..0000000 --- a/src/timezone-file.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE_FILE__H__ -#define __INDICATOR_DATETIME_TIMEZONE_FILE__H__ - -#include "timezone.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE_FILE (indicator_datetime_timezone_file_get_type()) -#define INDICATOR_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFile)) -#define INDICATOR_DATETIME_TIMEZONE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFileClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE)) - -typedef struct _IndicatorDatetimeTimezoneFile IndicatorDatetimeTimezoneFile; -typedef struct _IndicatorDatetimeTimezoneFilePriv IndicatorDatetimeTimezoneFilePriv; -typedef struct _IndicatorDatetimeTimezoneFileClass IndicatorDatetimeTimezoneFileClass; - -GType indicator_datetime_timezone_file_get_type (void); - -/** - * An IndicatorDatetimeTimezone which uses a local file, - * such as /etc/timezone, to determine the timezone. - */ -struct _IndicatorDatetimeTimezoneFile -{ - /*< private >*/ - IndicatorDatetimeTimezone parent; - IndicatorDatetimeTimezoneFilePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneFileClass -{ - IndicatorDatetimeTimezoneClass parent_class; -}; - -IndicatorDatetimeTimezone * indicator_datetime_timezone_file_new (const char * filename); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE_FILE__H__ */ diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c deleted file mode 100644 index ac23b93..0000000 --- a/src/timezone-geoclue.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include -#include - -#include "timezone-geoclue.h" - -struct _IndicatorDatetimeTimezoneGeocluePriv -{ - GeoclueMaster * master; - GeoclueMasterClient * client; - GeoclueAddress * address; -}; - -typedef IndicatorDatetimeTimezoneGeocluePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeTimezoneGeoclue, - indicator_datetime_timezone_geoclue, - INDICATOR_TYPE_DATETIME_TIMEZONE) - -static void geo_restart (IndicatorDatetimeTimezoneGeoclue * self); - -/*** -**** -***/ - -static void -on_address_changed (GeoclueAddress * address G_GNUC_UNUSED, - int timestamp G_GNUC_UNUSED, - GHashTable * addy_data, - GeoclueAccuracy * accuracy G_GNUC_UNUSED, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); - const char * timezone = g_hash_table_lookup (addy_data, "timezone"); - indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone); - } -} - -/* The signal doesn't have the parameter for an error, so it ends up needing - a NULL inserted. */ -static void -on_address_changed_sig (GeoclueAddress * address G_GNUC_UNUSED, - int timestamp G_GNUC_UNUSED, - GHashTable * addy_data, - GeoclueAccuracy * accuracy G_GNUC_UNUSED, - gpointer gself) -{ - on_address_changed(address, timestamp, addy_data, accuracy, NULL, gself); -} - -static void -on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED, - GeoclueAddress * address, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - priv_t * p = INDICATOR_DATETIME_TIMEZONE_GEOCLUE(gself)->priv; - - g_assert (p->address == NULL); - p->address = g_object_ref (address); - - geoclue_address_get_address_async (address, on_address_changed, gself); - g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed_sig), gself); - } -} - -static void -on_requirements_set (GeoclueMasterClient * master G_GNUC_UNUSED, - GError * error, - gpointer user_data G_GNUC_UNUSED) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } -} - -static void -on_client_created (GeoclueMaster * master G_GNUC_UNUSED, - GeoclueMasterClient * client, - gchar * path, - GError * error, - gpointer gself) -{ - g_debug ("Created Geoclue client at: %s", path); - - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); - priv_t * p = self->priv; - - g_clear_object (&p->client); - p->client = g_object_ref (client); - g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself); - - geoclue_master_client_set_requirements_async (p->client, - GEOCLUE_ACCURACY_LEVEL_REGION, - 0, - FALSE, - GEOCLUE_RESOURCE_ALL, - on_requirements_set, - NULL); - - geoclue_master_client_create_address_async (p->client, on_address_created, gself); - } -} - -static void -geo_start (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p = self->priv; - - g_assert (p->master == NULL); - p->master = geoclue_master_get_default (); - geoclue_master_create_client_async (p->master, on_client_created, self); -} - -static void -geo_stop (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p = self->priv; - - if (p->address != NULL) - { - g_signal_handlers_disconnect_by_func (p->address, on_address_changed_sig, self); - g_clear_object (&p->address); - } - - if (p->client != NULL) - { - g_signal_handlers_disconnect_by_func (p->client, geo_restart, self); - g_clear_object (&p->client); - } - - g_clear_object (&p->master); -} - -static void -geo_restart (IndicatorDatetimeTimezoneGeoclue * self) -{ - geo_stop (self); - geo_start (self); -} - -/*** -**** -***/ - -static void -my_dispose (GObject * o) -{ - geo_stop (INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o)); - - G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->dispose (o); -} - -static void -indicator_datetime_timezone_geoclue_class_init (IndicatorDatetimeTimezoneGeoclueClass * klass) -{ - GObjectClass * object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneGeocluePriv)); -} - -static void -indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, - IndicatorDatetimeTimezoneGeocluePriv); - - self->priv = p; - - geo_start (self); -} - -/*** -**** Public -***/ - -IndicatorDatetimeTimezone * -indicator_datetime_timezone_geoclue_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, NULL); - - return INDICATOR_DATETIME_TIMEZONE (o); -} diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h deleted file mode 100644 index 059bd81..0000000 --- a/src/timezone-geoclue.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ -#define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ - -#include "timezone.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE (indicator_datetime_timezone_geoclue_get_type()) -#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclue)) -#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclueClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE)) - -typedef struct _IndicatorDatetimeTimezoneGeoclue IndicatorDatetimeTimezoneGeoclue; -typedef struct _IndicatorDatetimeTimezoneGeocluePriv IndicatorDatetimeTimezoneGeocluePriv; -typedef struct _IndicatorDatetimeTimezoneGeoclueClass IndicatorDatetimeTimezoneGeoclueClass; - -GType indicator_datetime_timezone_geoclue_get_type (void); - -/** - * An IndicatorDatetimeTimezone which uses GeoClue to determine the timezone. - */ -struct _IndicatorDatetimeTimezoneGeoclue -{ - /*< private >*/ - IndicatorDatetimeTimezone parent; - IndicatorDatetimeTimezoneGeocluePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneGeoclueClass -{ - IndicatorDatetimeTimezoneClass parent_class; -}; - -IndicatorDatetimeTimezone * indicator_datetime_timezone_geoclue_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ */ diff --git a/src/timezone.c b/src/timezone.c deleted file mode 100644 index 4f8addc..0000000 --- a/src/timezone.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "timezone.h" - -G_DEFINE_TYPE (IndicatorDatetimeTimezone, - indicator_datetime_timezone, - G_TYPE_OBJECT) - -enum -{ - PROP_0, - PROP_TIMEZONE, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0, }; - -struct _IndicatorDatetimeTimezonePriv -{ - GString * timezone; -}; - -typedef struct _IndicatorDatetimeTimezonePriv priv_t; - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezone * self = INDICATOR_DATETIME_TIMEZONE (o); - - switch (property_id) - { - case PROP_TIMEZONE: - g_value_set_string (value, indicator_datetime_timezone_get_timezone (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_finalize (GObject * o) -{ - priv_t * p = INDICATOR_DATETIME_TIMEZONE(o)->priv; - - g_string_free (p->timezone, TRUE); - - G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->finalize (o); -} - -static void -/* cppcheck-suppress unusedFunction */ -indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezonePriv)); - - object_class = G_OBJECT_CLASS (klass); - object_class->get_property = my_get_property; - object_class->finalize = my_finalize; - - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", - "Timezone", - "Timezone", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE, - IndicatorDatetimeTimezonePriv); - - p->timezone = g_string_new (NULL); - - self->priv = p; -} - -/*** -**** -***/ - -const char * -indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self), NULL); - - return self->priv->timezone->str; -} - -void -indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone * self, - const char * timezone) -{ - priv_t * p = self->priv; - - if (g_strcmp0 (p->timezone->str, timezone)) - { - if (timezone != NULL) - g_string_assign (p->timezone, timezone); - else - g_string_set_size (p->timezone, 0); - g_debug ("%s new timezone set: '%s'", G_STRLOC, p->timezone->str); - g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); - } -} diff --git a/src/timezone.h b/src/timezone.h deleted file mode 100644 index fa6593d..0000000 --- a/src/timezone.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE__H__ -#define __INDICATOR_DATETIME_TIMEZONE__H__ - -#include -#include /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE (indicator_datetime_timezone_get_type()) -#define INDICATOR_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezone)) -#define INDICATOR_DATETIME_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) -#define INDICATOR_DATETIME_TIMEZONE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE)) - -typedef struct _IndicatorDatetimeTimezone IndicatorDatetimeTimezone; -typedef struct _IndicatorDatetimeTimezonePriv IndicatorDatetimeTimezonePriv; -typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass; - -GType indicator_datetime_timezone_get_type (void); - -/** - * Abstract Base Class for objects that provide a timezone. - * - * We use this in datetime to determine the user's current timezone - * for display in the 'locations' section of the datetime indicator. - * - * This class has a 'timezone' property that clients can watch - * for change notifications. - */ -struct _IndicatorDatetimeTimezone -{ - /*< private >*/ - GObject parent; - IndicatorDatetimeTimezonePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneClass -{ - GObjectClass parent_class; -}; - -/*** -**** -***/ - -const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *); - -void indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone *, - const char * new_timezone); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE__H__ */ diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp new file mode 100644 index 0000000..cb5e2bc --- /dev/null +++ b/src/timezones-live.cpp @@ -0,0 +1,69 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +LiveTimezones::LiveTimezones (const std::string& filename): + file_ (filename) +{ + file_.timezone.changed().connect([this](const std::string&){updateTimezones();}); + + geolocationEnabled.changed().connect([this](bool){updateGeolocation();}); + updateGeolocation(); + + updateTimezones(); +} + +void +LiveTimezones::updateGeolocation() +{ + geo_.reset(); + + if (geolocationEnabled.get()) + { + GeoclueTimezone * geo = new GeoclueTimezone(); + geo->timezone.changed().connect([this](const std::string&){updateTimezones();}); + geo_.reset(geo); + } +} + +void +LiveTimezones::updateTimezones() +{ + const std::string a = file_.timezone.get(); + const std::string b = geo_ ? geo_->timezone.get() : ""; + + timezone.set (a.empty() ? b : a); + + std::set zones; + if (!a.empty()) + zones.insert(a); + if (!b.empty()) + zones.insert(b); + timezones.set(zones); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 5539c5c..0000000 --- a/src/utils.c +++ /dev/null @@ -1,453 +0,0 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -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 . -*/ - -#include -#include -#include -#include -#include -#include "utils.h" -#include "settings-shared.h" - -/* Check the system locale setting to see if the format is 24-hour - time or 12-hour time */ -gboolean -is_locale_12h (void) -{ - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; - - for (i = 0; formats_24h[i]; ++i) { - if (strstr (t_fmt, formats_24h[i])) { - return FALSE; - } - } - - return TRUE; -} - -void -split_settings_location (const gchar * location, gchar ** zone, gchar ** name) -{ - gchar * location_dup; - gchar * first; - - location_dup = g_strdup (location); - g_strstrip (location_dup); - - if ((first = strchr (location_dup, ' '))) - *first = '\0'; - - if (zone != NULL) - { - *zone = location_dup; - } - - if (name != NULL) - { - gchar * after = first ? g_strstrip (first + 1) : NULL; - - if (after && *after) - { - *name = g_strdup (after); - } - else /* make the name from zone */ - { - 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 = ' '; - - *name = after; - } - } -} - -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; -} - -/* Translate msg according to the locale specified by LC_TIME */ -static const char * -T_(const char *msg) -{ - /* General strategy here is to make sure LANGUAGE is empty (since that - trumps all LC_* vars) and then to temporarily swap LC_TIME and - LC_MESSAGES. Then have gettext translate msg. - - We strdup the strings because the setlocale & *env functions do not - guarantee anything about the storage used for the string, and thus - the string may not be portably safe after multiple calls. - - Note that while you might think g_dcgettext would do the trick here, - that actually looks in /usr/share/locale/XX/LC_TIME, not the - LC_MESSAGES directory, so we won't find any translation there. - */ - char *message_locale = g_strdup(setlocale(LC_MESSAGES, NULL)); - const char *time_locale = setlocale (LC_TIME, NULL); - char *language = g_strdup(g_getenv("LANGUAGE")); - const char *rv; - if (language) - g_unsetenv("LANGUAGE"); - setlocale(LC_MESSAGES, time_locale); - - /* Get the LC_TIME version */ - rv = _(msg); - - /* Put everything back the way it was */ - setlocale(LC_MESSAGES, message_locale); - if (language) - g_setenv("LANGUAGE", language, TRUE); - g_free(message_locale); - g_free(language); - return rv; -} - -gchar * -join_date_and_time_format_strings (const char * date_string, - const char * 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\xE2\x80\x82%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 (T_("%s\xE2\x80\x82%s"), date_string, time_string); - } - else if (date_string) - { - str = g_strdup_printf (T_("%s"), date_string); - } - else /* time_string */ - { - str = g_strdup_printf (T_("%s"), time_string); - } - - return str; -} - -/*** -**** -***/ - -typedef enum -{ - DATE_PROXIMITY_TODAY, - DATE_PROXIMITY_TOMORROW, - DATE_PROXIMITY_WEEK, - DATE_PROXIMITY_FAR -} -date_proximity_t; - -static date_proximity_t -get_date_proximity (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); - if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) - prox = DATE_PROXIMITY_TODAY; - - /* does it happen tomorrow? */ - if (prox == DATE_PROXIMITY_FAR) - { - 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); - if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) - prox = DATE_PROXIMITY_TOMORROW; - - g_date_time_unref (tomorrow); - } - - /* does it happen this week? */ - if (prox == DATE_PROXIMITY_FAR) - { - 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); - - if (g_date_time_compare (time, week_bound) <= 0) - prox = DATE_PROXIMITY_WEEK; - - g_date_time_unref (week_bound); - g_date_time_unref (week); - } - - return prox; -} - - -/* - * "Terse" time & date format strings - * - * Used on the phone menu where space is at a premium, these strings - * express the time and date in as brief a form as possible. - * - * Examples from spec: - * 1. "Daily 6:30 AM" - * 2. "5:07 PM" (note date is omitted; today's date is implicit) - * 3. "Daily 12 PM" (note minutes are omitted for on-the-hour times) - * 4. "Tomorrow 7 AM" (note "Tomorrow" is used instead of a day of week) - */ - -static const gchar * -get_terse_date_format_string (date_proximity_t proximity) -{ - const gchar * fmt; - - switch (proximity) - { - case DATE_PROXIMITY_TODAY: - /* 'Today' is implicit in the terse case, so no string needed */ - fmt = NULL; - break; - - case DATE_PROXIMITY_TOMORROW: - fmt = T_("Tomorrow"); - break; - - case DATE_PROXIMITY_WEEK: - /* a strftime(3) fmt string for abbreviated day of week */ - fmt = T_("%a"); - break; - - default: - /* a strftime(3) fmt string for day-of-month and abbreviated month */ - fmt = T_("%d %b"); - break; - } - - return fmt; -} - -const gchar* -get_terse_header_time_format_string (void) -{ - /* a strftime(3) fmt string for a H:MM 12 hour time, eg "6:59 PM" */ - return T_("%l:%M %p"); -} - -const gchar * -get_terse_time_format_string (GDateTime * time) -{ - const gchar * fmt; - - if (g_date_time_get_minute (time) != 0) - { - fmt = get_terse_header_time_format_string (); - } - else - { - /* a strftime(3) fmt string for a 12 hour on-the-hour time, eg "7 PM" */ - fmt = T_("%l %p"); - } - - return fmt; -} - -gchar * -generate_terse_format_string_at_time (GDateTime * now, GDateTime * time) -{ - const date_proximity_t prox = get_date_proximity (now, time); - const gchar * date_fmt = get_terse_date_format_string (prox); - const gchar * time_fmt = get_terse_time_format_string (time); - return join_date_and_time_format_strings (date_fmt, time_fmt); -} - -/*** -**** FULL -***/ - -static const gchar * -get_full_date_format_string (gboolean show_day, gboolean show_date, gboolean show_year) -{ - 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 = NULL; - - return fmt; -} - - -/* - * "Full" time & date format strings - * - * These are used on the desktop menu & header and honors the - * GSettings entries for 12/24hr mode and whether or not to show seconds. - * - */ - -const gchar * -get_full_time_format_string (GSettings * settings) -{ - gboolean twelvehour; - gboolean show_seconds; - const gchar * fmt; - - g_return_val_if_fail (settings != NULL, NULL); - - show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); - - switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; - - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = FALSE; - break; - - default: - twelvehour = TRUE; - break; - } - - if (twelvehour && show_seconds) - /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ - fmt = T_("%l:%M:%S %p"); - else if (twelvehour) - /* TRANSLATORS: a strftime(3) format for 12hr time */ - fmt = T_("%l:%M %p"); - else if (show_seconds) - /* TRANSLATORS: a strftime(3) format for 24hr time w/seconds */ - fmt = T_("%H:%M:%S"); - else - /* TRANSLATORS: a strftime(3) format for 24hr time */ - fmt = T_("%H:%M"); - - return fmt; -} - -gchar * -generate_full_format_string (gboolean show_day, gboolean show_date, gboolean show_year, GSettings * settings) -{ - const gchar * date_fmt = get_full_date_format_string (show_day, show_date, show_year); - const gchar * time_fmt = get_full_time_format_string (settings); - return join_date_and_time_format_strings (date_fmt, time_fmt); -} - -gchar * -generate_full_format_string_at_time (GDateTime * now, GDateTime * time, GSettings * settings) -{ - gboolean show_day; - gboolean show_date; - - g_return_val_if_fail (now != NULL, NULL); - g_return_val_if_fail (time != NULL, NULL); - g_return_val_if_fail (settings != NULL, NULL); - - switch (get_date_proximity (now, time)) - { - case DATE_PROXIMITY_TODAY: - show_day = FALSE; - show_date = FALSE; - break; - - case DATE_PROXIMITY_TOMORROW: - case DATE_PROXIMITY_WEEK: - show_day = FALSE; - show_date = TRUE; - break; - - default: - show_day = TRUE; - show_date = TRUE; - break; - } - - return generate_full_format_string (show_day, show_date, FALSE, settings); -} - diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..42e034e --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- + +A dialog for setting time and date preferences. + +Copyright 2010 Canonical Ltd. + +Authors: + Michael Terry + +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 . +*/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* Check the system locale setting to see if the format is 24-hour + time or 12-hour time */ +gboolean +is_locale_12h (void) +{ + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; + const char *t_fmt = nl_langinfo (T_FMT); + int i; + + for (i = 0; formats_24h[i]; ++i) { + if (strstr (t_fmt, formats_24h[i])) { + return FALSE; + } + } + + return TRUE; +} + +void +split_settings_location (const gchar * location, gchar ** zone, gchar ** name) +{ + gchar * location_dup; + gchar * first; + + location_dup = g_strdup (location); + g_strstrip (location_dup); + + if ((first = strchr (location_dup, ' '))) + *first = '\0'; + + if (zone != NULL) + { + *zone = location_dup; + } + + if (name != NULL) + { + gchar * after = first ? g_strstrip (first + 1) : NULL; + + if (after && *after) + { + *name = g_strdup (after); + } + else /* make the name from zone */ + { + 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 = ' '; + + *name = after; + } + } +} + +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* 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(new MockClock(now)); + DesktopFormatter formatter(clock); + return g_strdup (formatter.getRelativeFormat(then).c_str()); +} + diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 5eacce5..0000000 --- a/src/utils.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -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 . -*/ - -#ifndef __DATETIME_UTILS_H__ -#define __DATETIME_UTILS_H__ - -#include -#include /* GSettings */ - -G_BEGIN_DECLS - -gboolean is_locale_12h (void); - -void split_settings_location (const char * location, - char ** zone, - char ** name); - -gchar * get_current_zone_name (const char * location, - GSettings * settings); - -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); - -gchar * generate_full_format_string_at_time (GDateTime * now, - GDateTime * time, - GSettings * settings); - -G_END_DECLS - -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 682896b..a424858 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,12 @@ +# build libgtest +add_library (gtest STATIC + ${GTEST_SOURCE_DIR}/gtest-all.cc + ${GTEST_SOURCE_DIR}/gtest_main.cc) +set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR}) +set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w) + +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -g ${CC_WARNING_ARGS}") + # build the necessary schemas set_directory_properties (PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled) @@ -12,12 +21,73 @@ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compil OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE) add_custom_command (OUTPUT gschemas.compiled - DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.session.gschema.xml + DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.gschema.xml COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) -# look for hearder in our src dir, and also in the directories where we autogenerate files... +# look for headers in our src dir, and also in the directories where we autogenerate files... include_directories (${CMAKE_SOURCE_DIR}/src) -include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS}) +include_directories (${CMAKE_CURRENT_BINARY_DIR}) +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) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-timezone-geoclue +set (TEST_NAME test-timezone-geoclue) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) + +# test-timezones +set (TEST_NAME test-timezones) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) + +# test-clock +set (TEST_NAME test-clock) +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-formatter +set (TEST_NAME test-formatter) +add_executable (${TEST_NAME} test-formatter.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-planner +set (TEST_NAME test-planner) +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-planner-eds +set (TEST_NAME test-planner-eds) +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) +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/geoclue-fixture.h b/tests/geoclue-fixture.h new file mode 100644 index 0000000..890204a --- /dev/null +++ b/tests/geoclue-fixture.h @@ -0,0 +1,142 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include + +class GeoclueFixture : public GlibFixture +{ + private: + + typedef GlibFixture super; + + GDBusConnection * bus = nullptr; + + protected: + + DbusTestService * service = nullptr; + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_master = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + const std::string timezone_1 = "America/Denver"; + + void SetUp () + { + super::SetUp(); + + GError * error = nullptr; + const gchar * const client_path = "/org/freedesktop/Geoclue/Master/client0"; + GString * gstr = g_string_new (nullptr); + + service = dbus_test_service_new (nullptr); + mock = dbus_test_dbus_mock_new ("org.freedesktop.Geoclue.Master"); + + obj_master = dbus_test_dbus_mock_get_object (mock, + "/org/freedesktop/Geoclue/Master", + "org.freedesktop.Geoclue.Master", + nullptr); + g_string_printf (gstr, "ret = '%s'", client_path); + dbus_test_dbus_mock_object_add_method (mock, obj_master, nullptr, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); + + obj_client = dbus_test_dbus_mock_get_object (mock, client_path, "org.freedesktop.Geoclue.MasterClient", nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "AddressStart", nullptr, nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue", "AddReference", nullptr, nullptr, "", &error); + g_string_printf (gstr, "ret = (1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", timezone_1.c_str()); + dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); + + dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); + dbus_test_service_start_tasks(service); + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_set_exit_on_close (bus, FALSE); + g_object_add_weak_pointer (G_OBJECT(bus), (gpointer*)&bus); + + g_string_free (gstr, TRUE); + } + + virtual void TearDown () + { + g_clear_object (&mock); + g_clear_object (&service); + g_object_unref (bus); + + unsigned int cleartry = 0; + while (bus != nullptr && cleartry < 10) + { + wait_msec (100); + cleartry++; + } + + // I've looked and can't find where this extra ref is coming from. + // is there an unbalanced ref to the bus in the test harness?! + while (bus != NULL) + { + g_object_unref (bus); + wait_msec (1000); + } + + super::TearDown (); + } + +private: + + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + static gboolean emit_address_changed_idle (gpointer gdata) + { + auto data = static_cast(gdata); + auto fmt = g_strdup_printf ("(1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", data->timezone.c_str()); + + GError * error = nullptr; + 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 (fmt), + &error); + if (error) + { + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + g_free (fmt); + delete data; + return G_SOURCE_REMOVE; + } + +public: + + void setGeoclueTimezoneOnIdle (const std::string& newZone) + { + g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, newZone.c_str())); + } + +}; + diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h new file mode 100644 index 0000000..043b7e3 --- /dev/null +++ b/tests/glib-fixture.h @@ -0,0 +1,131 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include + +#include +#include +#include + +#include + +class GlibFixture : public ::testing::Test +{ + private: + + //GLogFunc realLogHandler; + + protected: + + std::map logCounts; + + void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED) + { +#if 0 + EXPECT_EQ (expected, logCounts[log_level]); +#endif + + logCounts.erase (log_level); + } + + private: + + 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); + static_cast(self)->logCounts[log_level]++; + } + + protected: + + virtual void SetUp () + { + loop = g_main_loop_new (NULL, FALSE); + + //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); + } + + 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]); +#endif + + // revert to glib's log handler + //g_log_set_default_handler (realLogHandler, this); + + g_clear_pointer (&loop, g_main_loop_unref); + } + + private: + + static gboolean + wait_for_signal__timeout (gpointer 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) + { + g_main_loop_quit (static_cast(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) + { + // 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); + } + + /* convenience func to loop for N msec */ + 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); + } + + GMainLoop * loop; +}; diff --git a/tests/test-core.cc b/tests/test-core.cc new file mode 100644 index 0000000..7ed38a9 --- /dev/null +++ b/tests/test-core.cc @@ -0,0 +1,148 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#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 Handler; + + void stop() + { + stop_requested = true; + } + + void run() + { + while (!stop_requested) + { + std::unique_lock 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 lg(guard); + handlers.push(h); + } + + bool stop_requested = false; + std::queue 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 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-skew.cc b/tests/test-skew.cc new file mode 100644 index 0000000..90c0164 --- /dev/null +++ b/tests/test-skew.cc @@ -0,0 +1,209 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#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(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(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 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(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(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(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(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(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(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-timezones.cc b/tests/test-timezones.cc new file mode 100644 index 0000000..cda53a6 --- /dev/null +++ b/tests/test-timezones.cc @@ -0,0 +1,122 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include "geoclue-fixture.h" + +#include + +#include // fopen() +#include // sync() + +using unity::indicator::datetime::LiveTimezones; + +typedef GeoclueFixture TimezonesFixture; + +#define TIMEZONE_FILE (SANDBOX "/timezone") + +namespace +{ + /* convenience func to set the timezone file */ + void set_file (const std::string& text) + { + FILE * fp = fopen (TIMEZONE_FILE, "w+"); + fprintf (fp, "%s\n", text.c_str()); + fclose (fp); + sync (); + } +} + + +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 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); + }); + + // start listening for a timezone change, then change the timezone + bool zones_changed = false; + auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { + zones_changed = true; + zones = timezones; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gz) { + auto az = static_cast(gz); + 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)); + zones_changed = false; + + // now tweak the geoclue value... the geoclue-detected timezone should change, + // causing the 'timezones' property to change + 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)); + + // 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(str)); return G_SOURCE_REMOVE;}, const_cast(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)); + + + +} + + -- cgit v1.2.3 From ee64bb2698adfe27e55615a8856b0e2c78ad8469 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 14 Jan 2014 23:07:10 -0600 Subject: 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 --- CMakeLists.txt | 2 +- include/datetime/actions-live.h | 57 ++++ include/datetime/actions.h | 70 +++++ include/datetime/appointment.h | 4 +- include/datetime/clock-mock.h | 28 +- include/datetime/clock.h | 25 +- include/datetime/date-time.h | 103 +++++-- include/datetime/dbus-shared.h | 37 +-- include/datetime/formatter.h | 29 +- include/datetime/locations-settings.h | 11 +- include/datetime/locations.h | 38 ++- include/datetime/menu.h | 82 ++++++ include/datetime/planner-eds.h | 2 +- include/datetime/planner-mock.h | 44 +++ include/datetime/planner.h | 3 + include/datetime/service.h | 72 +++++ include/datetime/settings-live.h | 57 ++++ include/datetime/settings-shared.h | 6 +- include/datetime/settings.h | 63 +++++ include/datetime/state.h | 68 +++++ include/datetime/timezone-file.h | 13 +- include/datetime/timezone-geoclue.h | 12 +- include/datetime/timezone.h | 4 +- include/datetime/timezones-live.h | 13 +- include/datetime/timezones.h | 4 +- include/datetime/utils.h | 28 +- panel/CMakeLists.txt | 2 + src/CMakeLists.txt | 11 +- src/actions-live.cpp | 84 ++++++ src/actions.cpp | 215 ++++++++++++++ src/clock-live.cpp | 84 +++--- src/clock.cpp | 32 +-- src/formatter-desktop.cpp | 258 ++++++++--------- src/formatter.cpp | 146 +++++----- src/locations-settings.cpp | 59 ++-- src/locations.cpp | 33 ++- src/main.c | 83 ------ src/main.cpp | 81 ++++++ src/menu.cpp | 518 ++++++++++++++++++++++++++++++++++ src/planner-eds.cpp | 283 ++++++++++--------- src/planner-eds.h | 58 ---- src/planner.h | 167 ----------- src/service.cpp | 140 +++++++++ src/settings-live.cpp | 293 +++++++++++++++++++ src/timezone-file.cpp | 18 +- src/timezone-geoclue.cpp | 66 ++--- src/timezones-live.cpp | 34 ++- src/utils.cpp | 139 +++++---- tests/CMakeLists.txt | 27 +- tests/Makefile.am.strings | 38 --- tests/actions-mock.h | 88 ++++++ tests/glib-fixture.h | 85 +++--- tests/planner-mock.c | 34 +-- tests/state-fixture.h | 75 +++++ tests/test-actions.cc | 173 ++++++++++++ tests/test-clock.cc | 176 ++++++------ tests/test-core.cc | 148 ---------- tests/test-dbus-fixture.h | 100 +++++++ tests/test-formatter.cc | 61 ++-- tests/test-indicator.cc | 92 ------ tests/test-locations.cc | 136 ++++----- tests/test-menus.cc | 406 ++++++++++++++++++++++++++ tests/test-planner-eds.cc | 70 ----- tests/test-planner.cc | 49 ++-- tests/test-skew.cc | 209 -------------- tests/test-timezone-file.cc | 60 ++-- tests/test-timezone-geoclue.cc | 28 +- tests/test-timezones.cc | 83 +++--- tests/test-utils.cc | 153 +++++----- 69 files changed, 3962 insertions(+), 2008 deletions(-) create mode 100644 include/datetime/actions-live.h create mode 100644 include/datetime/actions.h create mode 100644 include/datetime/menu.h create mode 100644 include/datetime/planner-mock.h create mode 100644 include/datetime/service.h create mode 100644 include/datetime/settings-live.h create mode 100644 include/datetime/settings.h create mode 100644 include/datetime/state.h create mode 100644 src/actions-live.cpp create mode 100644 src/actions.cpp delete mode 100644 src/main.c create mode 100644 src/main.cpp create mode 100644 src/menu.cpp delete mode 100644 src/planner-eds.h delete mode 100644 src/planner.h create mode 100644 src/service.cpp create mode 100644 src/settings-live.cpp delete mode 100644 tests/Makefile.am.strings create mode 100644 tests/actions-mock.h create mode 100644 tests/state-fixture.h create mode 100644 tests/test-actions.cc delete mode 100644 tests/test-core.cc create mode 100644 tests/test-dbus-fixture.h delete mode 100644 tests/test-indicator.cc create mode 100644 tests/test-menus.cc delete mode 100644 tests/test-planner-eds.cc delete mode 100644 tests/test-skew.cc 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_LIVE_H +#define INDICATOR_DATETIME_ACTIONS_LIVE_H + +#include + +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): 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_H +#define INDICATOR_DATETIME_ACTIONS_H + +#include +#include + +#include // shared_ptr +#include + +#include // 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() { return m_state; } + +protected: + Actions(std::shared_ptr& state); + virtual ~Actions(); + +private: + std::shared_ptr 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 #include #include #include -#include #include #include @@ -45,9 +45,10 @@ class Clock { public: virtual ~Clock(); - virtual GDateTime* localtime() const = 0; - core::Property > timezones; + virtual DateTime localtime() const =0; + core::Property> 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& zones); virtual ~LiveClock(); - virtual GDateTime* localtime() const; + virtual DateTime localtime() const; core::Property 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 // GDateTime +#include // time_t #include // 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(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(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 dt_; + std::shared_ptr 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 . + * + * Authors: + * Ted Gould + * Charles Kerr + */ -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -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 . -*/ #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&); 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); + SettingsLocations (const std::string& schemaId, + const std::shared_ptr& timezones); protected: - std::unique_ptr> settings_; - std::shared_ptr timezones_; + std::unique_ptr> m_settings; + std::shared_ptr 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 + #include #include @@ -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 > locations; + core::Property> 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_MENU_H +#define INDICATOR_DATETIME_MENU_H + +#include +#include + +#include // std::shared_ptr +#include + +#include // 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, std::shared_ptr& state); + std::shared_ptr buildMenu(Menu::Profile profile); + std::shared_ptr state() { return m_state; } + +private: + std::shared_ptr m_actions; + std::shared_ptr 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_; + std::unique_ptr 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H +#define INDICATOR_DATETIME_PLANNER_MOCK_H + +#include + +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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_EXPORTER_H +#define INDICATOR_DATETIME_EXPORTER_H + +#include + +#include + +#include // GActionGroup + +#include // std::shared_ptr +#include + +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>& 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 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> 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include // parent class + +#include // 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 */ -#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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include + +#include + +#include + +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 time_format_mode; + core::Property show_clock; + core::Property show_day; + core::Property show_year; + core::Property show_seconds; + core::Property custom_time_format; + core::Property show_calendar; + core::Property show_events; + core::Property show_locations; + core::Property show_auto_detected_location; + core::Property> locations; + core::Property 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_STATE_H +#define INDICATOR_DATETIME_STATE_H + +#include +#include +#include +#include + +#include + +#include // 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; + std::shared_ptr clock; + std::shared_ptr planner; + std::shared_ptr locations; + + core::Property show_events; + core::Property show_clock; + core::Property calendar_day; + core::Property 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 geolocationEnabled = core::Property(false); + core::Property geolocation_enabled = core::Property(false); private: - FileTimezone file_; - std::shared_ptr geo_; - void updateGeolocation(); - void updateTimezones(); + void update_geolocation(); + void update_timezones(); + + FileTimezone m_file; + std::shared_ptr 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 . */ -#ifndef __DATETIME_UTILS_H__ -#define __DATETIME_UTILS_H__ +#ifndef INDICATOR_DATETIME_UTILS_H +#define INDICATOR_DATETIME_UTILS_H #include #include /* 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include // split_settings_location() + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +namespace +{ + +void on_desktop_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(gself)->open_desktop_settings(); +} + +void on_phone_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(gself)->open_phone_settings(); +} + +void on_phone_clock_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(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(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(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(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(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) +{ + 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; icalendar_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): + 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& 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(gself); + static_cast(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_; + LiveClock& m_owner; + GTimeZone * m_timezone = nullptr; + std::shared_ptr 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& 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& 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(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(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): - 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& 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(gself)->rebuildHeaderFormat(); - } +static void onSettingsChanged(GSettings * /*changed*/, + const gchar * /*key*/, + gpointer gself) +{ + static_cast(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_; - GSettings * settings_; +DesktopFormatter * const m_owner; +std::shared_ptr m_clock; +GSettings * m_settings; }; /*** **** ***/ -DesktopFormatter::DesktopFormatter (const std::shared_ptr& clock): +DesktopFormatter::DesktopFormatter(const std::shared_ptr& 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): - owner_(owner), - clock_(clock) + Impl(Formatter* owner, const std::shared_ptr& 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(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_; + std::shared_ptr m_clock; }; /*** @@ -233,7 +227,7 @@ public: ***/ Formatter::Formatter(const std::shared_ptr& 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) +SettingsLocations::SettingsLocations(const std::string& schemaId, + const std::shared_ptr& timezones): + m_timezones(timezones) { - auto deleter = [&](GSettings* s){g_object_unref(s);}; - settings_ = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); + auto deleter = [](GSettings* s){g_object_unref(s);}; + m_settings = std::unique_ptr>(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); itimezone.changed().connect([this](const std::string&){reload();}); timezones->timezones.changed().connect([this](const std::set&){reload();}); @@ -47,7 +47,7 @@ SettingsLocations::SettingsLocations (const std::string& schemaId, } void -SettingsLocations::onSettingsChanged (gpointer gself) +SettingsLocations::onSettingsChanged(gpointer gself) { static_cast(gself)->reload(); } @@ -56,48 +56,49 @@ void SettingsLocations::reload() { std::vector 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 - * - * 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 . - */ - -#include -#include /* exit() */ - -#include -#include -#include - -#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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include /* 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(new LiveTimezones(TIMEZONE_FILE)); + std::shared_ptr clock(new LiveClock(timezones)); + std::shared_ptr planner(new PlannerEds); + std::shared_ptr locations(new SettingsLocations(SETTINGS_INTERFACE, timezones)); + planner->time = clock->localtime(); + MenuFactory factory(actions, timezones, clock, planner, locations); + + // create the menus + std::vector> 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +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, + std::shared_ptr& actions, + std::shared_ptr formatter): + Menu(profile_in, name_in), + m_state(state), + m_actions(actions), + m_formatter(formatter) + { + // initialize the menu + create_gmenu(); + for (int i=0; iheader.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&){ + 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&) { + 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 m_state; + std::shared_ptr m_actions; + std::shared_ptr 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; iclock->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 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_, + std::shared_ptr& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr(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_, std::shared_ptr& actions_): + DesktopBaseMenu(Desktop,"desktop", state_, actions_) {} +}; + +class DesktopGreeterMenu: public DesktopBaseMenu +{ +public: + DesktopGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): + DesktopBaseMenu(DesktopGreeter,"desktop-greeter", state_, actions_) {} +}; + +class PhoneBaseMenu: public MenuImpl +{ +protected: + PhoneBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr& state_, + std::shared_ptr& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr(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_, + std::shared_ptr& actions_): + PhoneBaseMenu(Phone, "phone", state_, actions_) {} +}; + +class PhoneGreeterMenu: public PhoneBaseMenu +{ +public: + PhoneGreeterMenu(std::shared_ptr& state_, + std::shared_ptr& actions_): + PhoneBaseMenu(PhoneGreeter, "phone-greeter", state_, actions_) {} +}; + +/**** +***** +****/ + +MenuFactory::MenuFactory(std::shared_ptr& actions_, + std::shared_ptr& state_): + m_actions(actions_), + m_state(state_) +{ +} + +std::shared_ptr +MenuFactory::buildMenu(Menu::Profile profile) +{ + std::shared_ptr menu; + m_state->show_clock.set (true); // FIXME + + //std::shared_ptr 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(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(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(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(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(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(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(gself)->rebuildSoon(); } @@ -181,44 +181,44 @@ private: struct Task { - Impl* impl_; - appointment_func func_; - std::vector appointments_; - Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + Impl* p; + appointment_func func; + std::vector appointments; + Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} }; struct AppointmentSubtask { - std::shared_ptr task_; - ECalClient * client_; - std::string color_; - AppointmentSubtask (const std::shared_ptr& task, ECalClient* client, const char* color): - task_(task), client_(client), color_(color) {} + std::shared_ptr task; + ECalClient* client; + std::string color; + AppointmentSubtask(const std::shared_ptr& 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(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& 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& 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 main_task (new Task(this, func), [](Task* task){ + std::shared_ptr 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(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(g);}); } } struct UrlSubtask { - std::shared_ptr task_; - Appointment appointment_; - UrlSubtask (const std::shared_ptr& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + std::shared_ptr task; + Appointment appointment; + UrlSubtask(const std::shared_ptr& 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(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(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(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 sources_; - GCancellable * cancellable_ = nullptr; - ESourceRegistry * source_registry_ = nullptr; - guint rebuild_tag_ = 0; + PlannerEds& m_owner; + std::set 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 - * - * 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 . - */ - -#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 - * - * 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 . - */ - -#ifndef __INDICATOR_DATETIME_PLANNER__H__ -#define __INDICATOR_DATETIME_PLANNER__H__ - -#include -#include /* parent class */ -#include - -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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include + +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(gthis)->on_bus_acquired(connection, name); +} + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) +{ + m_dbus_connection = static_cast(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(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>& 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +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(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); isettings, 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); isettings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + +} + + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include // parent class + +#include // 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 . + * + * Authors: + * Ted Gould + * Charles Kerr + */ + +#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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include + +#include + +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 time_format_mode; + core::Property show_clock; + core::Property show_day; + core::Property show_year; + core::Property show_seconds; + core::Property custom_time_format; + core::Property show_calendar; + core::Property show_events; + core::Property show_locations; + core::Property show_auto_detected_location; + core::Property> locations; + core::Property 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(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(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(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(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(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 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 . /* 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(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(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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_MOCK_H +#define INDICATOR_DATETIME_ACTIONS_MOCK_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class MockActions: public Actions +{ +public: + MockActions(std::shared_ptr& state_in): Actions(state_in) {} + ~MockActions() =default; + + enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp, + OpenPlanner, OpenPlannerAt, OpenAppointment, + SetLocation, SetCalendarDate }; + const std::vector& 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 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 +#include // setlocale() + class GlibFixture : public ::testing::Test { private: @@ -35,96 +37,101 @@ class GlibFixture : public ::testing::Test std::map 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(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(loop)); + g_main_loop_quit(static_cast(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 . + * + * Authors: + * Charles Kerr + */ + +#include "glib-fixture.h" +#include "actions-mock.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace unity::indicator::datetime; + +class StateFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + std::shared_ptr m_clock; + std::shared_ptr m_state; + std::shared_ptr m_mock_actions; + std::shared_ptr 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(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(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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#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({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({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(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(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 zones (new Timezones); + std::shared_ptr 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(str)); return G_SOURCE_REMOVE; }, const_cast(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 zones (new Timezones); + std::shared_ptr 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 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 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(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(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 zones (new Timezones); + std::shared_ptr 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(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; + g_idle_add([](gpointer gself){ + static_cast(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 zones (new Timezones); + std::shared_ptr 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 - * - * 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 . - */ - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#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 Handler; - - void stop() - { - stop_requested = true; - } - - void run() - { - while (!stop_requested) - { - std::unique_lock 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 lg(guard); - handlers.push(h); - } - - bool stop_requested = false; - std::queue 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 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 . + * + * Authors: + * Charles Kerr + */ + +#include "glib-fixture.h" + +/*** +**** +***/ + +class TestDBusFixture: public GlibFixture +{ + public: + + TestDBusFixture() {} + + TestDBusFixture(const std::vector& 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(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(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 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 . */ -#include "clock-mock.h" #include "glib-fixture.h" +#include #include #include @@ -30,9 +30,10 @@ #include 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 mock (new MockClock(now)); + std::shared_ptr mock (new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr clock = std::dynamic_pointer_cast(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 mock (new MockClock(now)); + GDateTime * now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr mock(new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr clock = std::dynamic_pointer_cast(mock); for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + DateTime tmp(test_cases[i].now); + tmp.get(); + std::shared_ptr mock (new MockClock(tmp));//DateTime(test_cases[i].now))); std::shared_ptr clock = std::dynamic_pointer_cast(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 mock (new MockClock(test_cases[i].now)); + std::shared_ptr mock (new MockClock(DateTime(test_cases[i].now))); std::shared_ptr clock = std::dynamic_pointer_cast(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 - -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 . -*/ - -#include - -#include - -/*** -**** -***/ - -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(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 . */ -#include "clock-mock.h" #include "glib-fixture.h" +#include #include #include #include @@ -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({ nyc, chicago })); + timezones.reset(new Timezones); + timezones->timezone.set(chicago); + timezones->timezones.set(std::set({ 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 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 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&){ 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), SETTINGS_LOCATIONS_S, strv); + g_settings_set_strv(static_cast(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 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&){ 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), SETTINGS_SHOW_LOCATIONS_S, false); + g_idle_add([](gpointer gsettings){ + g_settings_set_boolean(static_cast(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 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 . + * + * Authors: + * Charles Kerr + */ + + +#include "actions-mock.h" +#include "state-fixture.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace unity::indicator::datetime; + +class MenuFixture: public StateFixture +{ +private: + typedef StateFixture super; + +protected: + std::shared_ptr m_menu_factory; + std::vector> 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; ibuildMenu(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 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& 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 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 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; imenu_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 - * - * 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 . - */ - -#include "clock-mock.h" -#include "glib-fixture.h" - -#include - -#include - -#include -#include - -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 . */ -#include "clock-mock.h" #include "glib-fixture.h" #include +#include +#include #include - -#include +#include #include #include 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 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 - * - * 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 . - */ - -#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(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(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 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(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(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(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(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(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(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(gself)->set_file ("America/New_York"); - // static_cast(gtz)->timezone.set ("America/New_York"); + g_idle_add([](gpointer gself){ + static_cast(gself)->set_file("America/New_York"); + // static_cast(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(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 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 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& 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(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(str)); return G_SOURCE_REMOVE;}, const_cast(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(str)); return G_SOURCE_REMOVE;}, const_cast(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 - -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 . -*/ + * 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 . + * + * Authors: + * Charles Kerr + */ #include @@ -27,36 +27,33 @@ with this program. If not, see . **** ***/ -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 Date: Thu, 16 Jan 2014 15:10:39 -0600 Subject: Implement Settings, a properties-cpp wrapper around GSettings --- include/datetime/settings-live.h | 19 +- include/datetime/settings.h | 16 +- src/settings-live.cpp | 410 ++++++++++++++++++--------------------- tests/CMakeLists.txt | 14 ++ tests/test-settings.cc | 197 +++++++++++++++++++ 5 files changed, 423 insertions(+), 233 deletions(-) create mode 100644 tests/test-settings.cc diff --git a/include/datetime/settings-live.h b/include/datetime/settings-live.h index 44e27b0..202c998 100644 --- a/include/datetime/settings-live.h +++ b/include/datetime/settings-live.h @@ -38,10 +38,23 @@ public: virtual ~LiveSettings(); private: - void update_show_clock(); - - void update_key(const std::string& key); static void on_changed(GSettings*, gchar*, gpointer); + void update_key(const std::string& key); + + void update_custom_time_format(); + void update_locations(); + void update_show_calendar(); + void update_show_clock(); + void update_show_date(); + void update_show_day(); + void update_show_detected_locations(); + void update_show_events(); + void update_show_locations(); + void update_show_seconds(); + void update_show_week_numbers(); + void update_show_year(); + void update_time_format_mode(); + void update_timezone_name(); GSettings* m_settings; diff --git a/include/datetime/settings.h b/include/datetime/settings.h index 3d4bc33..418bc33 100644 --- a/include/datetime/settings.h +++ b/include/datetime/settings.h @@ -42,17 +42,19 @@ public: Settings() =default; virtual ~Settings() =default; - core::Property time_format_mode; - core::Property show_clock; - core::Property show_day; - core::Property show_year; - core::Property show_seconds; core::Property custom_time_format; + core::Property> locations; core::Property show_calendar; + core::Property show_clock; + core::Property show_date; + core::Property show_day; + core::Property show_detected_location; core::Property show_events; core::Property show_locations; - core::Property show_auto_detected_location; - core::Property> locations; + core::Property show_seconds; + core::Property show_week_numbers; + core::Property show_year; + core::Property time_format_mode; core::Property timezone_name; }; diff --git a/src/settings-live.cpp b/src/settings-live.cpp index 74085a9..2305c93 100644 --- a/src/settings-live.cpp +++ b/src/settings-live.cpp @@ -37,257 +37,221 @@ LiveSettings::LiveSettings(): { g_signal_connect (m_settings, "changed", G_CALLBACK(on_changed), this); + // init the Properties from the GSettings backend + update_custom_time_format(); + update_locations(); + update_show_calendar(); update_show_clock(); + update_show_date(); + update_show_day(); + update_show_detected_locations(); + update_show_events(); + update_show_locations(); + update_show_seconds(); + update_show_week_numbers(); + update_show_year(); + update_time_format_mode(); + update_timezone_name(); + + // now listen for clients to change the properties s.t. we can sync update GSettings + + custom_time_format.changed().connect([this](const std::string& value){ + g_settings_set_string(m_settings, SETTINGS_CUSTOM_TIME_FORMAT_S, value.c_str()); + }); + + locations.changed().connect([this](const std::vector& value){ + const int n = value.size(); + gchar** strv = g_new0(gchar*, n+1); + for(int i=0; i(value[i].c_str()); + g_settings_set_strv(m_settings, SETTINGS_LOCATIONS_S, strv); + g_free(strv); + }); + + show_calendar.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_CALENDAR_S, value); + }); + + show_clock.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_CLOCK_S, value); + }); + + show_date.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DATE_S, value); + }); + + show_day.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DAY_S, value); + }); + + show_detected_location.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DETECTED_S, value); + }); + + show_events.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_EVENTS_S, value); + }); + + show_locations.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S, value); + }); + + show_seconds.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_SECONDS_S, value); + }); + + show_week_numbers.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_WEEK_NUMBERS_S, value); + }); + + show_year.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_YEAR_S, value); + }); + + time_format_mode.changed().connect([this](TimeFormatMode value){ + g_settings_set_enum(m_settings, SETTINGS_TIME_FORMAT_S, gint(value)); + }); + + timezone_name.changed().connect([this](const std::string& value){ + g_settings_set_string(m_settings, SETTINGS_TIMEZONE_NAME_S, value.c_str()); + }); } -void LiveSettings::update_show_clock() +/*** +**** +***/ + +void LiveSettings::update_custom_time_format() { - auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S); - show_clock.set(val); + auto val = g_settings_get_string(m_settings, SETTINGS_CUSTOM_TIME_FORMAT_S); + custom_time_format.set(val); + g_free(val); } -void LiveSettings::on_changed(GSettings* /*settings*/, - gchar* key, - gpointer gself) +void LiveSettings::update_locations() { - static_cast(gself)->update_key(key); + auto strv = g_settings_get_strv(m_settings, SETTINGS_LOCATIONS_S); + std::vector l; + for(int i=0; strv && strv[i]; i++) + l.push_back(strv[i]); + g_strfreev(strv); + locations.set(l); } -void LiveSettings::update_key(const std::string& key) +void LiveSettings::update_show_calendar() { - if (key == SETTINGS_SHOW_CLOCK_S) - { - update_show_clock(); - } + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CALENDAR_S); + show_calendar.set(val); } -/*** -**** -***/ - -} // 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); isettings, 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); isettings, gstr->str, - G_CALLBACK(rebuild_header_soon), self); - } - +void LiveSettings::update_show_clock() +{ + show_clock.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S)); } +void LiveSettings::update_show_date() +{ + show_date.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_DATE_S)); +} -#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H -#define INDICATOR_DATETIME_SETTINGS_LIVE_H - -#include // parent class - -#include // GSettings - -namespace unity { -namespace indicator { -namespace datetime { - -/** - * \brief #Settings implementation which uses GSettings. - */ -class LiveSettings +void LiveSettings::update_show_day() { -public: - LiveSettings(); - virtual ~LiveSettings(); + show_day.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_DAY_S)); +} -private: - GSettings* m_settings; +void LiveSettings::update_show_detected_locations() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_DETECTED_S); + show_detected_location.set(val); +} - // we've got a raw pointer here, so disable copying - LiveSettings(const LiveSettings&) =delete; - LiveSettings& operator=(const LiveSettings&) =delete; -}; +void LiveSettings::update_show_events() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_EVENTS_S); + show_events.set(val); +} +void LiveSettings::update_show_locations() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S); + show_locations.set(val); +} -#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 . - * - * Authors: - * Ted Gould - * Charles Kerr - */ +void LiveSettings::update_show_seconds() +{ + show_seconds.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_SECONDS_S)); +} -#ifndef INDICATOR_DATETIME_SETTINGS_SHARED -#define INDICATOR_DATETIME_SETTINGS_SHARED +void LiveSettings::update_show_week_numbers() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_WEEK_NUMBERS_S); + show_week_numbers.set(val); +} -typedef enum +void LiveSettings::update_show_year() { - TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM + show_year.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_YEAR_S)); } -TimeFormatMode; +void LiveSettings::update_time_format_mode() +{ + time_format_mode.set((TimeFormatMode)g_settings_get_enum(m_settings, SETTINGS_TIME_FORMAT_S)); +} -#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 . - * - * Authors: - * Charles Kerr - */ +void LiveSettings::update_timezone_name() +{ + auto val = g_settings_get_string(m_settings, SETTINGS_TIMEZONE_NAME_S); + timezone_name.set(val); + g_free(val); +} -#ifndef INDICATOR_DATETIME_SETTINGS_H -#define INDICATOR_DATETIME_SETTINGS_H +/*** +**** +***/ -#include +void LiveSettings::on_changed(GSettings* /*settings*/, + gchar* key, + gpointer gself) +{ + static_cast(gself)->update_key(key); +} -#include +void LiveSettings::update_key(const std::string& key) +{ + if (key == SETTINGS_SHOW_CLOCK_S) + update_show_clock(); + else if (key == SETTINGS_LOCATIONS_S) + update_locations(); + else if (key == SETTINGS_TIME_FORMAT_S) + update_time_format_mode(); + else if (key == SETTINGS_SHOW_SECONDS_S) + update_show_seconds(); + else if (key == SETTINGS_SHOW_DAY_S) + update_show_day(); + else if (key == SETTINGS_SHOW_DATE_S) + update_show_date(); + else if (key == SETTINGS_SHOW_YEAR_S) + update_show_year(); + else if (key == SETTINGS_CUSTOM_TIME_FORMAT_S) + update_custom_time_format(); + else if (key == SETTINGS_SHOW_CALENDAR_S) + update_show_calendar(); + else if (key == SETTINGS_SHOW_WEEK_NUMBERS_S) + update_show_week_numbers(); + else if (key == SETTINGS_SHOW_EVENTS_S) + update_show_events(); + else if (key == SETTINGS_SHOW_LOCATIONS_S) + update_show_locations(); + else if (key == SETTINGS_SHOW_DETECTED_S) + update_show_detected_locations(); + else if (key == SETTINGS_TIMEZONE_NAME_S) + update_timezone_name(); +} -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 time_format_mode; - core::Property show_clock; - core::Property show_day; - core::Property show_year; - core::Property show_seconds; - core::Property custom_time_format; - core::Property show_calendar; - core::Property show_events; - core::Property show_locations; - core::Property show_auto_detected_location; - core::Property> locations; - core::Property timezone_name; -}; - -#endif // INDICATOR_DATETIME_SETTINGS_H -#endif +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb55f5a..a908801 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -91,6 +91,20 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-utils +set (TEST_NAME test-utils) +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-utils +set (TEST_NAME test-settings) +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) diff --git a/tests/test-settings.cc b/tests/test-settings.cc new file mode 100644 index 0000000..9e52197 --- /dev/null +++ b/tests/test-settings.cc @@ -0,0 +1,197 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class SettingsFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + + std::shared_ptr m_live; + std::shared_ptr m_settings; + GSettings * m_gsettings; + + virtual void SetUp() + { + super::SetUp(); + + m_gsettings = g_settings_new(SETTINGS_INTERFACE); + m_live.reset(new LiveSettings); + m_settings = std::dynamic_pointer_cast(m_live); + } + + virtual void TearDown() + { + g_clear_object(&m_gsettings); + m_settings.reset(); + m_live.reset(); + + super::TearDown(); + } + + void TestBoolProperty(core::Property& property, const gchar* key) + { + EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); + g_settings_set_boolean(m_gsettings, key, false); + EXPECT_EQ(false, property.get()); + g_settings_set_boolean(m_gsettings, key, true); + EXPECT_EQ(true, property.get()); + + property.set(false); + EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + property.set(true); + EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + } + + void TestStringProperty(core::Property& property, const gchar* key) + { + gchar* tmp; + std::string str; + + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(tmp, property.get()); + g_clear_pointer(&tmp, g_free); + + str = "a"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "b"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "a"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + + str = "b"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + } +}; + +/*** +**** +***/ + +TEST_F(SettingsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(SettingsFixture, BoolProperties) +{ + TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); + TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); + TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); + TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); + TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); + TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); + TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); + TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); + TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); + TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); +} + +TEST_F(SettingsFixture, StringProperties) +{ + TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); + TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); +} + +TEST_F(SettingsFixture, TimeFormatMode) +{ + const auto key = SETTINGS_TIME_FORMAT_S; + const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM }; + + for(const auto& mode : modes) + { + g_settings_set_enum(m_gsettings, key, mode); + EXPECT_EQ(mode, m_settings->time_format_mode.get()); + } + + for(const auto& mode : modes) + { + m_settings->time_format_mode.set(mode); + EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); + } +} + +namespace +{ + std::vector strv_to_vector(const gchar** strv) + { + std::vector v; + for(int i=0; strv && strv[i]; i++) + v.push_back(strv[i]); + return v; + } +}; + +TEST_F(SettingsFixture, Locations) +{ + const auto key = SETTINGS_LOCATIONS_S; + + const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; + const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; + const std::vector av = strv_to_vector(astrv); + const std::vector bv = strv_to_vector(bstrv); + + g_settings_set_strv(m_gsettings, key, astrv); + EXPECT_EQ(av, m_settings->locations.get()); + g_settings_set_strv(m_gsettings, key, bstrv); + EXPECT_EQ(bv, m_settings->locations.get()); + + m_settings->locations.set(av); + auto tmp = g_settings_get_strv(m_gsettings, key); + auto vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(av, vtmp); + + m_settings->locations.set(bv); + tmp = g_settings_get_strv(m_gsettings, key); + vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(bv, vtmp); +} + +#if 0 + core::Property> locations; + core::Property time_format_mode; + +#endif -- cgit v1.2.3 From 78d0a231c12c159d1130ec080efab472f59851af Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:42:36 -0600 Subject: update DesktopFormatter class to use the "Settings" class instead of using GSettings directly. --- include/datetime/formatter.h | 19 +++-- src/formatter-desktop.cpp | 186 +++++++++++++++++-------------------------- src/formatter.cpp | 49 +++++------- tests/test-formatter.cc | 177 +++++++++++++++++++--------------------- 4 files changed, 186 insertions(+), 245 deletions(-) diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 09ed035..d8736c7 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -23,8 +23,14 @@ #include #include +#include +#include + #include +#include +#include + namespace unity { namespace indicator { namespace datetime { @@ -109,13 +115,16 @@ private: class DesktopFormatter: public Formatter { public: - DesktopFormatter(const std::shared_ptr&); - ~DesktopFormatter(); + DesktopFormatter(const std::shared_ptr&, const std::shared_ptr&); private: - class Impl; - friend Impl; - std::unique_ptr p; + std::shared_ptr m_settings; + + void rebuildHeaderFormat(); + const gchar* getFullTimeFormatString() const; + std::string getHeaderLabelFormatString() const; + const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const; + }; diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 3f942f4..5efdf8b 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -18,10 +18,6 @@ */ #include -#include - -#include -#include namespace unity { namespace indicator { @@ -31,80 +27,108 @@ namespace datetime { **** ***/ -class DesktopFormatter::Impl +namespace { -public: -Impl(DesktopFormatter * owner, const std::shared_ptr& clock): - m_owner(owner), - m_clock(clock), - m_settings(g_settings_new(SETTINGS_INTERFACE)) +std::string joinDateAndTimeFormatStrings(const char* date_string, + const char* time_string) { - 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); + std::string str; - rebuildHeaderFormat(); -} + 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 = date_string; + str += "\u2003"; + str += time_string; + } + else if (date_string) + { + str = date_string; + } + else // time_string + { + str = time_string; + } -~Impl() -{ - g_signal_handlers_disconnect_by_data(m_settings, this); - g_object_unref(m_settings); + return str; } +} // unnamed namespace -private: +/*** +**** +***/ -static void onSettingsChanged(GSettings * /*changed*/, - const gchar * /*key*/, - gpointer gself) +DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_in, + const std::shared_ptr& settings_in): + Formatter(clock_in), + m_settings(settings_in) { - static_cast(gself)->rebuildHeaderFormat(); + m_settings->show_day.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_date.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_year.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_seconds.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->time_format_mode.changed().connect([this](TimeFormatMode){rebuildHeaderFormat();}); + m_settings->custom_time_format.changed().connect([this](const std::string&){rebuildHeaderFormat();}); + + rebuildHeaderFormat(); } -void rebuildHeaderFormat() +void DesktopFormatter::rebuildHeaderFormat() { - auto fmt = getHeaderLabelFormatString(m_settings); - m_owner->headerFormat.set(fmt); - g_free(fmt); + headerFormat.set(getHeaderLabelFormatString()); } -private: - -gchar* getHeaderLabelFormatString(GSettings* s) const +std::string DesktopFormatter::getHeaderLabelFormatString() const { - char * fmt; - const auto mode = g_settings_get_enum(s, SETTINGS_TIME_FORMAT_S); + std::string fmt; + const auto mode = m_settings->time_format_mode.get(); if (mode == TIME_FORMAT_MODE_CUSTOM) { - fmt = g_settings_get_string(s, SETTINGS_CUSTOM_TIME_FORMAT_S); + fmt = m_settings->custom_time_format.get(); } else { - 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 show_day = m_settings->show_day.get(); + const auto show_date = m_settings->show_date.get(); + const auto show_year = show_date && m_settings->show_year.get(); const auto date_fmt = getDateFormat(show_day, show_date, show_year); - const auto time_fmt = getFullTimeFormatString(s); + const auto time_fmt = getFullTimeFormatString(); fmt = joinDateAndTimeFormatStrings(date_fmt, time_fmt); } return fmt; } -const gchar* T_(const gchar* in) const +const gchar* DesktopFormatter::getFullTimeFormatString() const { - return m_owner->T_(in); + const auto show_seconds = m_settings->show_seconds.get(); + + bool twelvehour; + switch (m_settings->time_format_mode.get()) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; + + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = false; + break; + + default: + twelvehour = true; + break; + } + + return getDefaultHeaderTimeFormat(twelvehour, show_seconds); } -const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const +const gchar* DesktopFormatter::getDateFormat(bool show_day, bool show_date, bool show_year) const { const char * fmt; @@ -135,74 +159,6 @@ const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const return fmt; } -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; - - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = false; - break; - - default: - twelvehour = true; - break; - } - - 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 - { - str = g_strdup(time_string); - } - - return str; -} - -private: - -DesktopFormatter * const m_owner; -std::shared_ptr m_clock; -GSettings * m_settings; -}; - -/*** -**** -***/ - -DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock): - Formatter(clock), - p(new Impl(this, clock)) -{ -} - -DesktopFormatter::~DesktopFormatter() = default; - /*** **** ***/ diff --git a/src/formatter.cpp b/src/formatter.cpp index 1f26cc7..88a64df 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -124,7 +124,7 @@ guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) g_date_time_unref(next); return seconds; } -} // anonymous namespace +} // unnamed namespace @@ -167,7 +167,7 @@ private: { clearTimer(m_header_timer); - const std::string fmt = m_owner->headerFormat.get(); + const auto 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) @@ -214,7 +214,7 @@ private: } private: - Formatter * const m_owner; + Formatter* const m_owner; guint m_header_timer = 0; guint m_relative_timer = 0; @@ -248,7 +248,7 @@ Formatter::is_locale_12h() return true; } -const char * +const char* Formatter::T_(const char *msg) { /* General strategy here is to make sure LANGUAGE is empty (since that @@ -264,17 +264,16 @@ Formatter::T_(const char *msg) LC_MESSAGES directory, so we won't find any translation there. */ - char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const char *time_locale = setlocale(LC_TIME, nullptr); - char *language = g_strdup(g_getenv("LANGUAGE")); - const char *rv; + auto message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); + const auto time_locale = setlocale(LC_TIME, nullptr); + auto language = g_strdup(g_getenv("LANGUAGE")); if (language) g_unsetenv("LANGUAGE"); setlocale(LC_MESSAGES, time_locale); /* Get the LC_TIME version */ - rv = _(msg); + const auto rv = _(msg); /* Put everything back the way it was */ setlocale(LC_MESSAGES, message_locale); @@ -286,10 +285,10 @@ Formatter::T_(const char *msg) return rv; } -const char * +const char* Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) { - const char * fmt; + const char* fmt; if (twelvehour && show_seconds) /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ @@ -322,9 +321,9 @@ 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; + auto prox = DATE_PROXIMITY_FAR; gint now_year, now_month, now_day; gint time_year, time_month, time_day; @@ -337,10 +336,9 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) // does it happen tomorrow? if (prox == DATE_PROXIMITY_FAR) { - GDateTime * tomorrow; - gint tom_year, tom_month, tom_day; + auto tomorrow = g_date_time_add_days(now, 1); - tomorrow = g_date_time_add_days(now, 1); + gint tom_year, tom_month, tom_day; 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; @@ -351,14 +349,11 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) // does it happen this week? if (prox == DATE_PROXIMITY_FAR) { - 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); + auto week = g_date_time_add_days(now, 6); + auto 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) prox = DATE_PROXIMITY_WEEK; @@ -369,7 +364,7 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) return prox; } -} // anonymous namespace +} // unnamed namespace /** * _ a time today should be shown as just the time (e.g. “3:55 PM”) @@ -391,7 +386,7 @@ std::string Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const { std::string ret; - auto now = p->m_clock->localtime().get(); + const auto now = p->m_clock->localtime().get(); if (then != nullptr) { @@ -448,7 +443,5 @@ Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const ***/ } // namespace datetime - } // namespace indicator - } // namespace unity diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc index 42c828c..9950453 100644 --- a/tests/test-formatter.cc +++ b/tests/test-formatter.cc @@ -22,18 +22,14 @@ #include #include -#include +#include #include #include #include -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 namespace unity::indicator::datetime; /*** **** @@ -44,75 +40,73 @@ class FormatterFixture: public GlibFixture private: typedef GlibFixture super; - gchar * original_locale = nullptr; + gchar* m_original_locale = nullptr; protected: - GSettings * settings = nullptr; + std::shared_ptr m_settings; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); - settings = g_settings_new (SETTINGS_INTERFACE); - - original_locale = g_strdup (setlocale (LC_TIME, NULL)); + m_settings.reset(new Settings); + m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); } - virtual void TearDown () + virtual void TearDown() { - g_clear_object (&settings); + m_settings.reset(); - setlocale (LC_TIME, original_locale); - g_clear_pointer (&original_locale, g_free); + setlocale(LC_TIME, m_original_locale); + g_clear_pointer(&m_original_locale, g_free); - super::TearDown (); + super::TearDown(); } - bool SetLocale (const char * expected_locale, const char * name) + bool SetLocale(const char* expected_locale, const char* name) { - setlocale (LC_TIME, expected_locale); - const char * actual_locale = setlocale (LC_TIME, NULL); - if (!g_strcmp0 (expected_locale, actual_locale)) + setlocale(LC_TIME, expected_locale); + const auto actual_locale = setlocale(LC_TIME, nullptr); + if (!g_strcmp0(expected_locale, actual_locale)) { return true; } else { - g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); return false; } } - inline bool Set24hLocale () { return SetLocale ("C", "24h"); } - inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } + inline bool Set24hLocale() { return SetLocale("C", "24h"); } + inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } }; /** * Test the phone header format */ -TEST_F (FormatterFixture, TestPhoneHeader) +TEST_F(FormatterFixture, TestPhoneHeader) { - GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr mock (new MockClock(DateTime(now))); + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); g_date_time_unref(now); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); // test the default value in a 24h locale - if (Set24hLocale ()) + if(Set24hLocale()) { - PhoneFormatter formatter (clock); - EXPECT_EQ (std::string("%H:%M"), formatter.headerFormat.get()); - EXPECT_EQ (std::string("18:30"), formatter.header.get()); + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("18:30"), formatter.header.get()); } // test the default value in a 12h locale - if (Set12hLocale ()) + if(Set12hLocale()) { - PhoneFormatter formatter (clock); - EXPECT_EQ (std::string("%l:%M %p"), formatter.headerFormat.get()); - EXPECT_EQ (std::string(" 6:30 PM"), formatter.header.get()); + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); } } @@ -121,14 +115,14 @@ TEST_F (FormatterFixture, TestPhoneHeader) /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestDesktopHeader) +TEST_F(FormatterFixture, TestDesktopHeader) { struct { bool is_12h; bool show_day; bool show_date; bool show_year; - const char * expected_format_string; + const char* expected_format_string; } test_cases[] = { { false, false, false, false, "%H:%M" }, { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false @@ -148,26 +142,21 @@ 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 mock(new MockClock(DateTime(now))); + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); g_date_time_unref(now); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - for (int i=0, n=G_N_ELEMENTS(test_cases); ishow_day.set(test_case.show_day); + m_settings->show_date.set(test_case.show_date); + m_settings->show_year.set(test_case.show_year); - ASSERT_STREQ (test_cases[i].expected_format_string, f.headerFormat.get().c_str()); - - g_settings_reset (settings, SETTINGS_SHOW_DAY_S); - g_settings_reset (settings, SETTINGS_SHOW_DATE_S); - g_settings_reset (settings, SETTINGS_SHOW_YEAR_S); + ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); } } } @@ -175,15 +164,15 @@ TEST_F (FormatterFixture, TestDesktopHeader) /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestUpcomingTimes) +TEST_F(FormatterFixture, TestUpcomingTimes) { - auto 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; - const char * expected_format_string; + GDateTime* now; + GDateTime* then; + const char* expected_format_string; } test_cases[] = { { 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 @@ -200,45 +189,42 @@ TEST_F (FormatterFixture, TestUpcomingTimes) { 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 mock (new MockClock(tmp));//DateTime(test_cases[i].now))); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - DesktopFormatter f (clock); + std::shared_ptr clock (new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); - std::string fmt = f.getRelativeFormat (test_cases[i].then); - ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + const auto fmt = f.getRelativeFormat(test_case.then); + ASSERT_EQ(test_case.expected_format_string, fmt); - 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_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); } } - g_date_time_unref (a); + g_date_time_unref(a); } /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestEventTimes) +TEST_F(FormatterFixture, TestEventTimes) { - 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); + 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; - GDateTime * now; - GDateTime * then; - GDateTime * then_end; - const char * expected_format_string; + GDateTime* now; + GDateTime* then; + GDateTime* then_end; + const char* expected_format_string; } test_cases[] = { { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, @@ -246,28 +232,25 @@ TEST_F (FormatterFixture, TestEventTimes) { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } }; - for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(DateTime(test_cases[i].now))); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - DesktopFormatter f (clock); + std::shared_ptr clock(new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); - std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); - ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + ASSERT_STREQ(test_case.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); + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + g_clear_pointer(&test_case.then_end, g_date_time_unref); } } - g_date_time_unref (tomorrow_end); - g_date_time_unref (tomorrow_begin); - g_date_time_unref (day_end); - g_date_time_unref (day_begin); - g_date_time_unref (day); + g_date_time_unref(tomorrow_end); + g_date_time_unref(tomorrow_begin); + g_date_time_unref(day_end); + g_date_time_unref(day_begin); + g_date_time_unref(day); } - - -- cgit v1.2.3 From 9c81a4d60d06b1f33001602cd0cde9844c9233a6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:44:06 -0600 Subject: update SettingsLocations class to use the "Settings" class instead of using GSettings directly. --- include/datetime/locations-settings.h | 18 ++--- src/locations-settings.cpp | 50 +++++-------- tests/test-locations.cc | 133 +++++++++++++++++----------------- 3 files changed, 89 insertions(+), 112 deletions(-) diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index eaabf73..4072736 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -22,18 +22,15 @@ #include // base class -#include -#include +#include +#include namespace unity { namespace indicator { namespace datetime { -class Timezones; - /** - * \brief Settings implentation which builds its list from the - * user's GSettings and from the Timezones passed in the ctor. + * \brief #Locations implementation which builds its list from the #Settings. */ class SettingsLocations: public Locations { @@ -42,15 +39,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, + SettingsLocations (const std::shared_ptr& settings, const std::shared_ptr& timezones); -protected: - std::unique_ptr> m_settings; - std::shared_ptr m_timezones; - private: - static void onSettingsChanged (gpointer gself); + std::shared_ptr m_settings; + std::shared_ptr m_timezones; void reload(); }; diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index 646a360..9b90bc0 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,49 +29,39 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations(const std::string& schemaId, +SettingsLocations::SettingsLocations(const std::shared_ptr& settings, const std::shared_ptr& timezones): + m_settings(settings), m_timezones(timezones) { - auto deleter = [](GSettings* s){g_object_unref(s);}; - m_settings = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); - const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, - "changed::" SETTINGS_SHOW_LOCATIONS_S }; - 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&){reload();}); + m_settings->locations.changed().connect([this](const std::vector&){reload();}); + m_settings->show_locations.changed().connect([this](bool){reload();}); + m_timezones->timezone.changed().connect([this](const std::string&){reload();}); + m_timezones->timezones.changed().connect([this](const std::set&){reload();}); reload(); } -void -SettingsLocations::onSettingsChanged(gpointer gself) -{ - static_cast(gself)->reload(); -} - void SettingsLocations::reload() { std::vector v; - auto settings = m_settings.get(); + const std::string timezone_name = m_settings->timezone_name.get(); // add the primary timezone first auto zone = m_timezones->timezone.get(); if (!zone.empty()) { - gchar * name = get_current_zone_name(zone.c_str(), settings); + gchar * name = get_beautified_timezone_name(zone.c_str(), timezone_name.c_str()); Location l(zone, name); v.push_back(l); g_free(name); } // add the other detected timezones - for (const auto& zone : m_timezones->timezones.get()) + for(const auto& zone : m_timezones->timezones.get()) { - gchar * name = get_current_zone_name(zone.c_str(), settings); + gchar * name = get_beautified_timezone_name(zone.c_str(), timezone_name.c_str()); Location l(zone, name); if (std::find(v.begin(), v.end(), l) == v.end()) v.push_back(l); @@ -79,23 +69,19 @@ SettingsLocations::reload() } // maybe add the user-specified locations - if (g_settings_get_boolean(settings, SETTINGS_SHOW_LOCATIONS_S)) + if (m_settings->show_locations.get()) { - gchar ** user_locations = g_settings_get_strv(settings, SETTINGS_LOCATIONS_S); - - for (int i=0; user_locations[i]; i++) + for(const auto& locstr : m_settings->locations.get()) { - 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); + gchar* zone; + gchar* name; + split_settings_location(locstr.c_str(), &zone, &name); + Location loc(zone, name); + if (std::find(v.begin(), v.end(), loc) == v.end()) + v.push_back(loc); g_free(name); g_free(zone); } - - g_strfreev(user_locations); } locations.set(v); diff --git a/tests/test-locations.cc b/tests/test-locations.cc index edaac69..65adbc7 100644 --- a/tests/test-locations.cc +++ b/tests/test-locations.cc @@ -21,18 +21,11 @@ #include "glib-fixture.h" -#include -#include #include -#include - -#include - -#include -#include using unity::indicator::datetime::Location; using unity::indicator::datetime::Locations; +using unity::indicator::datetime::Settings; using unity::indicator::datetime::SettingsLocations; using unity::indicator::datetime::Timezones; @@ -48,8 +41,9 @@ class LocationsFixture: public GlibFixture protected: - GSettings * settings = nullptr; - std::shared_ptr timezones; + //GSettings * settings = nullptr; + std::shared_ptr m_settings; + std::shared_ptr m_timezones; const std::string nyc = "America/New_York"; const std::string chicago = "America/Chicago"; @@ -57,20 +51,24 @@ class LocationsFixture: public GlibFixture { super::SetUp(); - settings = g_settings_new(SETTINGS_INTERFACE); - const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv(settings, SETTINGS_LOCATIONS_S, location_strv); - g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, true); - - timezones.reset(new Timezones); - timezones->timezone.set(chicago); - timezones->timezones.set(std::set({ nyc, chicago })); + m_settings.reset(new Settings); + m_settings->show_locations.set(true); + m_settings->locations.set({"America/Los_Angeles Oakland", + "America/Chicago Chicago", + "America/Chicago Oklahoma City", + "America/Toronto Toronto", + "Europe/London London", + "Europe/Berlin Berlin"}); + + m_timezones.reset(new Timezones); + m_timezones->timezone.set(chicago); + m_timezones->timezones.set(std::set({ nyc, chicago })); } virtual void TearDown() { - //timezones.reset(nullptr); - g_clear_object(&settings); + m_timezones.reset(); + m_settings.reset(); super::TearDown(); } @@ -78,42 +76,42 @@ class LocationsFixture: public GlibFixture TEST_F(LocationsFixture, Timezones) { - g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, false); + m_settings->show_locations.set(false); - SettingsLocations locations(SETTINGS_INTERFACE, timezones); - std::vector l = locations.locations.get(); + SettingsLocations locations(m_settings, m_timezones); + const auto 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_STREQ("Chicago", l[0].name().c_str()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); } TEST_F(LocationsFixture, SettingsLocations) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); - std::vector l = locations.locations.get(); + const auto 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("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("Oklahoma City", l[3].name()); + EXPECT_EQ("America/Chicago", l[3].zone()); + EXPECT_EQ("Toronto", l[4].name()); + EXPECT_EQ("America/Toronto", l[4].zone()); + EXPECT_EQ("London", l[5].name()); + EXPECT_EQ("Europe/London", l[5].zone()); + EXPECT_EQ("Berlin", l[6].name()); + EXPECT_EQ("Europe/Berlin", l[6].zone()); } TEST_F(LocationsFixture, ChangeLocationStrings) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ @@ -121,33 +119,32 @@ TEST_F(LocationsFixture, ChangeLocationStrings) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gsettings){ - const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv(static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); + g_idle_add([](gpointer settings){ + static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); return G_SOURCE_REMOVE; - }, settings); + }, m_settings.get()); g_main_loop_run(loop); EXPECT_TRUE(locations_changed); - std::vector l = locations.locations.get(); + const auto 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("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("London", l[3].name()); + EXPECT_EQ("Europe/London", l[3].zone()); + EXPECT_EQ("Berlin", l[4].name()); + EXPECT_EQ("Europe/Berlin", l[4].zone()); locations_changed = false; } TEST_F(LocationsFixture, ChangeLocationVisibility) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ @@ -155,18 +152,18 @@ TEST_F(LocationsFixture, ChangeLocationVisibility) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gsettings){ - g_settings_set_boolean(static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + g_idle_add([](gpointer settings){ + static_cast(settings)->show_locations.set(false); return G_SOURCE_REMOVE; - }, settings); + }, m_settings.get()); g_main_loop_run(loop); EXPECT_TRUE(locations_changed); - std::vector l = locations.locations.get(); + const auto 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("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); } -- cgit v1.2.3 From a2b5c79157fa8db36d94786de1b86b756308912d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:45:40 -0600 Subject: Plug the Settings object into the State container s.t. menus and actions can update themselves when the user's settings change. --- include/datetime/state.h | 14 +++++++------- src/actions.cpp | 4 ++-- src/menu.cpp | 20 +++++--------------- tests/test-menus.cc | 4 ++-- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/include/datetime/state.h b/include/datetime/state.h index a82bb4b..e735b6f 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -21,9 +21,10 @@ #define INDICATOR_DATETIME_STATE_H #include -#include -#include #include +#include +#include +#include #include @@ -47,18 +48,17 @@ namespace datetime { * @see Clock * @see Planner * @see Locations + * @see Settings */ struct State { - std::shared_ptr timezones; std::shared_ptr clock; - std::shared_ptr planner; std::shared_ptr locations; + std::shared_ptr planner; + std::shared_ptr settings; + std::shared_ptr timezones; - core::Property show_events; - core::Property show_clock; core::Property calendar_day; - core::Property show_week_numbers; }; } // namespace datetime diff --git a/src/actions.cpp b/src/actions.cpp index 4efe950..52ee1eb 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -154,12 +154,12 @@ GVariant* create_calendar_state(std::shared_ptr& state) g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "show-week-numbers"; - v = g_variant_new_boolean(state->show_week_numbers.get()); + v = g_variant_new_boolean(state->settings->show_week_numbers.get()); g_variant_builder_add(&dict_builder, "{sv}", key, v); return g_variant_builder_end(&dict_builder); } -} // anonymous namespace +} // unnamed namespace /*** **** diff --git a/src/menu.cpp b/src/menu.cpp index 76306cc..c81f185 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -69,11 +69,11 @@ protected: update_section(Appointments); // uses formatter.getRelativeFormat() update_section(Locations); // uses formatter.getRelativeFormat() }); - m_state->show_clock.changed().connect([this](bool){ + m_state->settings->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){ + m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); m_state->planner->upcoming.changed().connect([this](const std::vector&){ @@ -261,7 +261,7 @@ private: { auto menu = g_menu_new(); - if (((profile==Phone) || (profile==Desktop)) && m_state->show_events.get()) + if (((profile==Phone) || (profile==Desktop)) && m_state->settings->show_events.get()) { add_appointments (menu, profile); @@ -363,14 +363,14 @@ protected: std::shared_ptr& state_, std::shared_ptr& actions_): MenuImpl(profile_, name_, state_, actions_, - std::shared_ptr(new DesktopFormatter(state_->clock))) + std::shared_ptr(new DesktopFormatter(state_->clock, state_->settings))) { update_header(); } GVariant* create_header_state() { - const auto visible = m_state->show_clock.get(); + const auto visible = m_state->settings->show_clock.get(); const auto title = _("Date and Time"); auto label = g_variant_new_string(m_formatter->header.get().c_str()); @@ -472,32 +472,22 @@ std::shared_ptr MenuFactory::buildMenu(Menu::Profile profile) { std::shared_ptr menu; - m_state->show_clock.set (true); // FIXME - - //std::shared_ptr 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; diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 88e4706..0f86b0d 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -170,7 +170,7 @@ protected: 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); + m_state->settings->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)); @@ -179,7 +179,7 @@ protected: // when "show_events" is true, // there should be an "add event" button even if there aren't any appointments std::vector appointments; - m_state->show_events.set(true); + m_state->settings->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); -- cgit v1.2.3 From 4cc19729c540ffba163d5c9a53b9352fe61fe8af Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 17:28:37 -0600 Subject: in utils.c, make a version of get_timezone_name() that doesn't require a GSettings argument. Update utils tests. --- include/datetime/utils.h | 5 ++- src/utils.cpp | 89 ++++++++++++++++++++++++++++-------------------- tests/state-fixture.h | 1 + tests/test-utils.cc | 79 +++++++++++++----------------------------- 4 files changed, 82 insertions(+), 92 deletions(-) diff --git a/include/datetime/utils.h b/include/datetime/utils.h index fbc80d7..10e881f 100644 --- a/include/datetime/utils.h +++ b/include/datetime/utils.h @@ -34,9 +34,12 @@ void split_settings_location (const char * location, char ** zone, char ** name); -gchar * get_current_zone_name (const char * location, +gchar * get_timezone_name (const char * timezone, GSettings * settings); +gchar * get_beautified_timezone_name (const char * timezone, + const char * saved_location); + gchar * generate_full_format_string_at_time (GDateTime * now, GDateTime * time); diff --git a/src/utils.cpp b/src/utils.cpp index acd9796..bef8c2e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -25,10 +25,9 @@ with this program. If not, see . #include #include #include -#include +#include -#include -#include +#include #include #include @@ -53,16 +52,15 @@ void split_settings_location(const gchar* location, gchar** zone, gchar** name) { auto location_dup = g_strdup(location); - g_strstrip(location_dup); + if(location_dup != nullptr) + g_strstrip(location_dup); gchar* first; - if((first = strchr(location_dup, ' '))) + if(location_dup && (first = strchr(location_dup, ' '))) *first = '\0'; if(zone) - { *zone = location_dup; - } if(name != nullptr) { @@ -72,7 +70,7 @@ split_settings_location(const gchar* location, gchar** zone, gchar** name) { *name = g_strdup(after); } - else // make the name from zone + else if (location_dup) // make the name from zone { gchar * chr = strrchr(location_dup, '/'); after = g_strdup(chr ? chr + 1 : location_dup); @@ -84,54 +82,73 @@ split_settings_location(const gchar* location, gchar** zone, gchar** name) *name = after; } + else + { + *name = nullptr; + } } } +/** + * Our Locations come from two places: (1) direct user input and (2) ones + * guessed by the system, such as from geoclue or timedate1. + * + * Since the latter only have a timezone (eg, "America/Chicago") and the + * former have a descriptive name provided by the end user (eg, + * "America/Chicago Oklahoma City"), this function tries to make a + * more human-readable name by using the user-provided name if the guessed + * timezone matches the last one the user manually clicked on. + * + * In the example above, this allows the menuitem for the system-guessed + * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks + * on the "Oklahoma City" menuitem. + */ gchar* -get_current_zone_name(const gchar* location, GSettings* settings) +get_beautified_timezone_name(const char* timezone, const char* saved_location) { - 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); + gchar* zone; + gchar* name; + split_settings_location(timezone, &zone, &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* saved_zone; + gchar* saved_name; + split_settings_location(saved_location, &saved_zone, &saved_name); gchar* rv; - if (g_strcmp0(old_zone, new_zone) == 0) + if (g_strcmp0(zone, saved_zone) == 0) { - rv = old_name; - old_name = nullptr; + rv = saved_name; + saved_name = nullptr; } else { - rv = new_name; - new_name = nullptr; + rv = name; + name = nullptr; } - g_free(new_zone); - g_free(old_zone); - g_free(new_name); - g_free(old_name); + g_free(zone); + g_free(name); + g_free(saved_zone); + g_free(saved_name); return rv; } -gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) +gchar* +get_timezone_name(const gchar* timezone, GSettings* settings) { - using unity::indicator::datetime::Clock; - using unity::indicator::datetime::DateTime; - using unity::indicator::datetime::MockClock; - using unity::indicator::datetime::DesktopFormatter; + auto saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + auto rv = get_beautified_timezone_name(timezone, saved_location); + g_free(saved_location); + return rv; +} +using namespace unity::indicator::datetime; + +gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) +{ std::shared_ptr clock(new MockClock(DateTime(now))); - DesktopFormatter formatter(clock); + std::shared_ptr settings(new LiveSettings); + DesktopFormatter formatter(clock, settings); return g_strdup(formatter.getRelativeFormat(then).c_str()); } diff --git a/tests/state-fixture.h b/tests/state-fixture.h index 0286ea9..3c6ecd5 100644 --- a/tests/state-fixture.h +++ b/tests/state-fixture.h @@ -50,6 +50,7 @@ protected: const DateTime now = DateTime::NowLocal(); m_clock.reset(new MockClock(now)); m_state.reset(new State); + m_state->settings.reset(new Settings); m_state->timezones.reset(new Timezones); m_state->clock = std::dynamic_pointer_cast(m_clock); m_state->planner.reset(new MockPlanner); diff --git a/tests/test-utils.cc b/tests/test-utils.cc index 8246396..2fe6a2e 100644 --- a/tests/test-utils.cc +++ b/tests/test-utils.cc @@ -17,22 +17,16 @@ * Charles Kerr */ -#include - -#include - -#include "utils.h" +#include -/*** -**** -***/ +#include TEST(UtilsTest, SplitSettingsLocation) { struct { - const char * location; - const char * expected_zone; - const char * expected_name; + 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" }, @@ -43,63 +37,38 @@ TEST(UtilsTest, SplitSettingsLocation) { "UTC UTC", "UTC", "UTC" } }; - for(guint i=0, n=G_N_ELEMENTS(test_cases); i Date: Thu, 16 Jan 2014 17:42:43 -0600 Subject: update timezones-live to use Settings to tell when the user has enabled/disabled GeoClue lookups --- include/datetime/timezones-live.h | 13 ++++++------- src/timezones-live.cpp | 12 ++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/datetime/timezones-live.h b/include/datetime/timezones-live.h index 5f2cb3e..286c967 100644 --- a/include/datetime/timezones-live.h +++ b/include/datetime/timezones-live.h @@ -20,9 +20,10 @@ #ifndef INDICATOR_DATETIME_LIVE_TIMEZONES_H #define INDICATOR_DATETIME_LIVE_TIMEZONES_H -#include // base class -#include // aggregated -#include // aggregated +#include +#include +#include +#include #include // shared_ptr<> @@ -37,16 +38,14 @@ namespace datetime { class LiveTimezones: public Timezones { public: - LiveTimezones(const std::string& filename); - - /** \brief Whether or not to track location by IP address */ - core::Property geolocation_enabled = core::Property(false); + LiveTimezones(std::shared_ptr& settings, const std::string& filename); private: void update_geolocation(); void update_timezones(); FileTimezone m_file; + std::shared_ptr m_settings; std::shared_ptr m_geo; }; diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp index dc14021..baac05d 100644 --- a/src/timezones-live.cpp +++ b/src/timezones-live.cpp @@ -18,18 +18,20 @@ */ #include + #include namespace unity { namespace indicator { namespace datetime { -LiveTimezones::LiveTimezones(const std::string& filename): - m_file(filename) +LiveTimezones::LiveTimezones(std::shared_ptr& settings, const std::string& filename): + m_file(filename), + m_settings(settings) { m_file.timezone.changed().connect([this](const std::string&){update_timezones();}); - geolocation_enabled.changed().connect([this](bool){update_geolocation();}); + m_settings->show_detected_location.changed().connect([this](bool){update_geolocation();}); update_geolocation(); update_timezones(); @@ -37,9 +39,11 @@ LiveTimezones::LiveTimezones(const std::string& filename): void LiveTimezones::update_geolocation() { + // clear the previous pointer, if any m_geo.reset(); - if(geolocation_enabled.get()) + // if location detection is enabled, turn on GeoClue + if(m_settings->show_detected_location.get()) { auto geo = new GeoclueTimezone(); geo->timezone.changed().connect([this](const std::string&){update_timezones();}); -- cgit v1.2.3 From 5e7433aabbe4b14327202ea1e9318bb8fd92d625 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 21:21:13 -0600 Subject: fix minor -Wshadow warnings --- src/utils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index bef8c2e..0b586f4 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -104,11 +104,11 @@ split_settings_location(const gchar* location, gchar** zone, gchar** name) * on the "Oklahoma City" menuitem. */ gchar* -get_beautified_timezone_name(const char* timezone, const char* saved_location) +get_beautified_timezone_name(const char* timezone_, const char* saved_location) { gchar* zone; gchar* name; - split_settings_location(timezone, &zone, &name); + split_settings_location(timezone_, &zone, &name); gchar* saved_zone; gchar* saved_name; @@ -134,10 +134,10 @@ get_beautified_timezone_name(const char* timezone, const char* saved_location) } gchar* -get_timezone_name(const gchar* timezone, GSettings* settings) +get_timezone_name(const gchar* timezone_, GSettings* settings) { auto saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); - auto rv = get_beautified_timezone_name(timezone, saved_location); + auto rv = get_beautified_timezone_name(timezone_, saved_location); g_free(saved_location); return rv; } -- cgit v1.2.3 From 06a5132025ba2ab43de9d1d583a81d8e0b326da8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 21:21:41 -0600 Subject: sync with new State class changes --- src/main.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c246296..7bd6f9d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,13 +17,14 @@ * with this program. If not, see . */ +#include #include #include #include #include #include #include -#include +#include #include #include @@ -54,18 +55,28 @@ main(int /*argc*/, char** /*argv*/) 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(new LiveTimezones(TIMEZONE_FILE)); + // build the state + std::shared_ptr settings(new LiveSettings); + std::shared_ptr timezones(new LiveTimezones(settings, TIMEZONE_FILE)); std::shared_ptr clock(new LiveClock(timezones)); std::shared_ptr planner(new PlannerEds); - std::shared_ptr locations(new SettingsLocations(SETTINGS_INTERFACE, timezones)); planner->time = clock->localtime(); - MenuFactory factory(actions, timezones, clock, planner, locations); + std::shared_ptr locations(new SettingsLocations(settings, timezones)); + std::shared_ptr state(new State); + state->settings = settings; + state->timezones = timezones; + state->clock = clock; + state->locations = locations; + state->planner = planner; + state->calendar_day = clock->localtime(); + + // build the menu factory + std::shared_ptr actions(new LiveActions(state)); + MenuFactory factory(actions, state); // create the menus std::vector> menus; - menus.push_back(factory.buildMenu(MenuFactory::Desktop)); + menus.push_back(factory.buildMenu(Menu::Desktop)); // export them auto loop = g_main_loop_new(nullptr, false); @@ -74,7 +85,7 @@ main(int /*argc*/, char** /*argv*/) g_message("exiting: service couldn't acquire or lost ownership of busname"); g_main_loop_quit(loop); }); - service.publish(actions, menus); + //service.publish(actions, menus); g_main_loop_run(loop); g_main_loop_unref(loop); return 0; -- cgit v1.2.3 From 83dcdd840483c9183fcd500b0f63d1d011da90bf Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 21:21:55 -0600 Subject: fix minor -Wpedantic warnings --- src/planner-eds.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 6abaf3e..275a29e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -269,7 +269,7 @@ private: if (default_timezone == nullptr) // maybe str is a tzid? default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); - g_debug("default_timezone is %p", default_timezone); + g_debug("default_timezone is %p", (void*)default_timezone); } /** @@ -277,7 +277,7 @@ private: **/ std::shared_ptr main_task(new Task(this, func), [](Task* task){ - g_message("time to delete task %p", task); + g_message("time to delete task %p", (void*)task); task->func(task->appointments); }); @@ -293,7 +293,7 @@ private: // 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); + g_message("calling e_cal_client_generate_instances for %p", (void*)client); e_cal_client_generate_instances(client, begin, end, -- cgit v1.2.3 From 6f2e235e660094f6ec27a0a099428ac3fe852ff7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 21:22:52 -0600 Subject: sync with utils --- panel/datetime-prefs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel/datetime-prefs.c b/panel/datetime-prefs.c index 90f9e79..3e7fc3c 100644 --- a/panel/datetime-prefs.c +++ b/panel/datetime-prefs.c @@ -220,7 +220,7 @@ toggle_ntp (GtkWidget * radio, GParamSpec * pspec G_GNUC_UNUSED, IndicatorDateti static void sync_entry (IndicatorDatetimePanel * self, const gchar * location) { - gchar * name = get_current_zone_name (location, self->priv->settings); + gchar * name = get_timezone_name (location, self->priv->settings); gtk_entry_set_text (GTK_ENTRY (self->priv->tz_entry), name); g_free (name); @@ -635,7 +635,7 @@ entry_focus_out (GtkEntry * entry, gchar * zone; g_object_get (location, "zone", &zone, NULL); - gchar * name = get_current_zone_name (zone, self->priv->settings); + gchar * name = get_timezone_name (zone, self->priv->settings); gboolean correct = (g_strcmp0 (gtk_entry_get_text (entry), name) == 0); g_free (name); g_free (zone); -- cgit v1.2.3 From 2e9d3bb48946ccebf49cff64ab5de67e5714e1e3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 21:23:57 -0600 Subject: get timezone, clock tests running again with Settings & State --- include/datetime/actions-live.h | 7 +- include/datetime/actions.h | 2 +- src/actions-live.cpp | 9 +-- src/actions.cpp | 6 +- tests/geoclue-fixture.h | 48 +++++++----- tests/test-clock.cc | 168 +++++++++------------------------------- tests/test-dbus-fixture.h | 14 ++-- tests/test-timezones.cc | 21 ++--- 8 files changed, 91 insertions(+), 184 deletions(-) diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 2059a4d..3d04660 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -36,15 +36,14 @@ namespace datetime { class LiveActions: public Actions { public: - LiveActions(std::shared_ptr& state): Actions(state) {} + LiveActions(const std::shared_ptr& state): Actions(state) {} ~LiveActions() =default; void open_desktop_settings(); void open_phone_settings(); - void open_phone_clock(); - void open_phone_planner(); + void open_phone_clock_app(); + void open_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&); diff --git a/include/datetime/actions.h b/include/datetime/actions.h index ed45c3c..a4c6017 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -51,7 +51,7 @@ public: std::shared_ptr state() { return m_state; } protected: - Actions(std::shared_ptr& state); + Actions(const std::shared_ptr& state); virtual ~Actions(); private: diff --git a/src/actions-live.cpp b/src/actions-live.cpp index 08e1466..c699aff 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -39,12 +39,12 @@ void LiveActions::open_phone_settings() g_message("%s", G_STRFUNC); } -void LiveActions::open_phone_clock() +void LiveActions::open_phone_clock_app() { g_message("%s", G_STRFUNC); } -void LiveActions::open_phone_planner() +void LiveActions::open_planner() { g_message("%s", G_STRFUNC); } @@ -54,11 +54,6 @@ 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()); diff --git a/src/actions.cpp b/src/actions.cpp index 52ee1eb..0df7d53 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -130,7 +130,7 @@ GVariant* create_default_header_state() return g_variant_builder_end(&b); } -GVariant* create_calendar_state(std::shared_ptr& state) +GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; for(const auto& appt : state->planner->thisMonth.get()) @@ -138,7 +138,7 @@ GVariant* create_calendar_state(std::shared_ptr& state) GVariantBuilder day_builder; g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); - for (int i=0; i& state) **** ***/ -Actions::Actions(std::shared_ptr& state): +Actions::Actions(const std::shared_ptr& state): m_state(state), m_actions(g_simple_action_group_new()) { diff --git a/tests/geoclue-fixture.h b/tests/geoclue-fixture.h index 890204a..7e29018 100644 --- a/tests/geoclue-fixture.h +++ b/tests/geoclue-fixture.h @@ -33,8 +33,10 @@ class GeoclueFixture : public GlibFixture DbusTestService * service = nullptr; DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_master = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; + DbusTestDbusMockObject * obj_geo = nullptr; + DbusTestDbusMockObject * obj_geo_m = nullptr; + DbusTestDbusMockObject * obj_geo_mc = nullptr; + DbusTestDbusMockObject * obj_geo_addr = nullptr; const std::string timezone_1 = "America/Denver"; void SetUp () @@ -42,25 +44,31 @@ class GeoclueFixture : public GlibFixture super::SetUp(); GError * error = nullptr; - const gchar * const client_path = "/org/freedesktop/Geoclue/Master/client0"; + const auto master_path = "/org/freedesktop/Geoclue/Master"; + const auto client_path = "/org/freedesktop/Geoclue/Master/client0"; GString * gstr = g_string_new (nullptr); service = dbus_test_service_new (nullptr); mock = dbus_test_dbus_mock_new ("org.freedesktop.Geoclue.Master"); - obj_master = dbus_test_dbus_mock_get_object (mock, - "/org/freedesktop/Geoclue/Master", - "org.freedesktop.Geoclue.Master", - nullptr); + auto interface = "org.freedesktop.Geoclue.Master"; + obj_geo_m = dbus_test_dbus_mock_get_object (mock, master_path, interface, nullptr); g_string_printf (gstr, "ret = '%s'", client_path); - dbus_test_dbus_mock_object_add_method (mock, obj_master, nullptr, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_m, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); - obj_client = dbus_test_dbus_mock_get_object (mock, client_path, "org.freedesktop.Geoclue.MasterClient", nullptr); - dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); - dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "AddressStart", nullptr, nullptr, "", &error); - dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue", "AddReference", nullptr, nullptr, "", &error); + interface = "org.freedesktop.Geoclue.MasterClient"; + obj_geo_mc = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_mc, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_mc, "AddressStart", nullptr, nullptr, "", &error); + + interface = "org.freedesktop.Geoclue"; + obj_geo = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo, "AddReference", nullptr, nullptr, "", &error); g_string_printf (gstr, "ret = (1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", timezone_1.c_str()); - dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); + + interface = "org.freedesktop.Geoclue.Address"; + obj_geo_addr = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_addr, "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); dbus_test_service_start_tasks(service); @@ -101,11 +109,11 @@ private: struct EmitAddressChangedData { DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; + DbusTestDbusMockObject * obj_geo_addr = nullptr; std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + EmitAddressChangedData(DbusTestDbusMock* mock_, + DbusTestDbusMockObject* obj_geo_addr_, + const std::string& timezone_): mock(mock_), obj_geo_addr(obj_geo_addr_), timezone(timezone_) {} }; static gboolean emit_address_changed_idle (gpointer gdata) @@ -114,8 +122,8 @@ private: auto fmt = g_strdup_printf ("(1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", data->timezone.c_str()); GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, - "org.freedesktop.Geoclue.Address", + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_geo_addr, + //"org.freedesktop.Geoclue.Address", "AddressChanged", G_VARIANT_TYPE("(ia{ss}(idd))"), g_variant_new_parsed (fmt), @@ -135,7 +143,7 @@ public: void setGeoclueTimezoneOnIdle (const std::string& newZone) { - g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, newZone.c_str())); + g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_geo_addr, newZone.c_str())); } }; diff --git a/tests/test-clock.cc b/tests/test-clock.cc index 7d3a35e..142ccad 100644 --- a/tests/test-clock.cc +++ b/tests/test-clock.cc @@ -20,95 +20,56 @@ #include #include -#include "glib-fixture.h" +#include "test-dbus-fixture.h" /*** **** ***/ -using unity::indicator::datetime::Clock; -using unity::indicator::datetime::LiveClock; -using unity::indicator::datetime::Timezones; +using namespace unity::indicator::datetime; -class ClockFixture: public GlibFixture +class ClockFixture: public TestDBusFixture { private: - - typedef GlibFixture super; - - static void - on_bus_opened(GObject* /*object*/, GAsyncResult* res, gpointer gself) - { - auto self = static_cast(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(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; - - 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); - } - - 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(); - } + typedef TestDBusFixture super; 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); + NULL, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + NULL); } }; +/** + * 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) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 3; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); +} + /*** **** ***/ @@ -120,37 +81,6 @@ TEST_F(ClockFixture, HelloFixture) std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); 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); - 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); - - /// change the timezones! - clock.skewDetected.connect([this](){ - g_main_loop_quit(loop); - }); - file_timezone = "America/Los_Angeles"; - g_idle_add([](gpointer str){ - set_file(static_cast(str)); - return G_SOURCE_REMOVE; - }, const_cast(file_timezone.c_str())); - 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); - 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); -#endif } @@ -159,10 +89,6 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); - //std::string file_timezone = "America/New_York"; - //set_file(file_timezone); - //std::shared_ptr 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); @@ -213,30 +139,6 @@ TEST_F(ClockFixture, SleepTriggersSkew) return G_SOURCE_REMOVE; }, this); - wait_msec(1000); + g_main_loop_run(loop); 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(ClockFixture, IdleDoesNotTriggerSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 4; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); -} diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h index fc7ab5a..db06be2 100644 --- a/tests/test-dbus-fixture.h +++ b/tests/test-dbus-fixture.h @@ -86,15 +86,17 @@ class TestDBusFixture: public GlibFixture virtual void TearDown () { + wait_msec(); + // 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(); } }; diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc index d3c8e3a..3f02761 100644 --- a/tests/test-timezones.cc +++ b/tests/test-timezones.cc @@ -19,12 +19,15 @@ #include "geoclue-fixture.h" +#include #include +#include // std::shared_ptr + #include // fopen() #include // sync() -using unity::indicator::datetime::LiveTimezones; +using namespace unity::indicator::datetime; typedef GeoclueFixture TimezonesFixture; @@ -49,7 +52,8 @@ TEST_F(TimezonesFixture, ManagerTest) std::string timezone_geo = "America/Denver"; set_file(timezone_file); - LiveTimezones z(TIMEZONE_FILE); + std::shared_ptr settings(new Settings); + LiveTimezones z(settings, 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(); @@ -71,13 +75,13 @@ TEST_F(TimezonesFixture, ManagerTest) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gz) { - auto az = static_cast(gz); - g_message("geolocation was %d", (int)az->geolocationEnabled.get()); + g_idle_add([](gpointer s_in) { + auto s = static_cast(s_in); + g_message("geolocation was %d", (int)s->show_detected_location.get()); g_message("turning geolocation on"); - az->geolocationEnabled.set(true); + s->show_detected_location.set(true); return G_SOURCE_REMOVE; - }, &z); + }, settings.get()); // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property g_main_loop_run(loop); @@ -115,9 +119,6 @@ TEST_F(TimezonesFixture, ManagerTest) EXPECT_EQ(2, zones.size()); EXPECT_EQ(1, zones.count(timezone_file)); EXPECT_EQ(1, zones.count(timezone_geo)); - - - } -- cgit v1.2.3 From 8910c3c25d9c3c77aaa987fdc366cd77f63d376c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 21 Jan 2014 23:27:45 -0600 Subject: Extract the MockState logic from state-fixture.h so that it can be reused for testing LiveActions --- tests/state-fixture.h | 30 +++++++----------------------- tests/state-mock.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 tests/state-mock.h diff --git a/tests/state-fixture.h b/tests/state-fixture.h index 3c6ecd5..7d8358e 100644 --- a/tests/state-fixture.h +++ b/tests/state-fixture.h @@ -18,16 +18,9 @@ */ #include "glib-fixture.h" -#include "actions-mock.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "actions-mock.h" +#include "state-mock.h" using namespace unity::indicator::datetime; @@ -37,7 +30,7 @@ private: typedef GlibFixture super; protected: - std::shared_ptr m_clock; + std::shared_ptr m_mock_state; std::shared_ptr m_state; std::shared_ptr m_mock_actions; std::shared_ptr m_actions; @@ -46,19 +39,9 @@ protected: { 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->settings.reset(new Settings); - m_state->timezones.reset(new Timezones); - m_state->clock = std::dynamic_pointer_cast(m_clock); - m_state->planner.reset(new MockPlanner); - m_state->planner->time = now; - m_state->locations.reset(new Locations); - m_state->calendar_day = now; + m_mock_state.reset(new MockState); + m_state = std::dynamic_pointer_cast(m_mock_state); - // build the actions on top of the state m_mock_actions.reset(new MockActions(m_state)); m_actions = std::dynamic_pointer_cast(m_mock_actions); } @@ -67,8 +50,9 @@ protected: { m_actions.reset(); m_mock_actions.reset(); + m_state.reset(); - m_clock.reset(); + m_mock_state.reset(); super::TearDown(); } diff --git a/tests/state-mock.h b/tests/state-mock.h new file mode 100644 index 0000000..ae1217b --- /dev/null +++ b/tests/state-mock.h @@ -0,0 +1,49 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace unity::indicator::datetime; + +class MockState: public State +{ +public: + std::shared_ptr mock_clock; + + MockState() + { + const DateTime now = DateTime::NowLocal(); + mock_clock.reset(new MockClock(now)); + settings.reset(new Settings); + timezones.reset(new Timezones); + clock = std::dynamic_pointer_cast(mock_clock); + planner.reset(new MockPlanner); + planner->time = now; + locations.reset(new Locations); + calendar_day = now; + } +}; + -- cgit v1.2.3 From 74f8897902c99180e721d616614a9962c819d90b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 21 Jan 2014 23:29:19 -0600 Subject: add LiveActions implementation and unit tests --- include/datetime/actions-live.h | 6 +- src/actions-live.cpp | 153 ++++++++++++++++++++++++++++++++++++---- tests/CMakeLists.txt | 9 ++- tests/test-menus.cc | 2 +- 4 files changed, 154 insertions(+), 16 deletions(-) diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 3d04660..1947e6e 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -36,7 +36,7 @@ namespace datetime { class LiveActions: public Actions { public: - LiveActions(const std::shared_ptr& state): Actions(state) {} + LiveActions(const std::shared_ptr& state_in): Actions(state_in) {} ~LiveActions() =default; void open_desktop_settings(); @@ -47,6 +47,10 @@ public: void open_appointment(const std::string& uid); void set_location(const std::string& zone, const std::string& name); void set_calendar_date(const DateTime&); + +protected: + virtual void execute_command(const std::string& command); + virtual void dispatch_url(const std::string& url); }; } // namespace datetime diff --git a/src/actions-live.cpp b/src/actions-live.cpp index c699aff..e4f5e9f 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -19,6 +19,8 @@ #include +#include + #include namespace unity { @@ -29,46 +31,171 @@ namespace datetime { **** ***/ +void LiveActions::execute_command(const std::string& cmdstr) +{ + const auto cmd = cmdstr.c_str(); + g_debug("Issuing command '%s'", cmd); + + GError* error = nullptr; + if (!g_spawn_command_line_async(cmd, &error)) + { + g_warning("Unable to start \"%s\": %s", cmd, error->message); + g_error_free(error); + } +} + +void LiveActions::dispatch_url(const std::string& url) +{ + url_dispatch_send(url.c_str(), nullptr, nullptr); +} + +/*** +**** +***/ + void LiveActions::open_desktop_settings() { - g_message ("%s", G_STRFUNC); +#ifdef HAVE_CCPANEL + execute_command("gnome-control-center indicator-datetime"); +#else + execute_command("gnome-control-center datetime"); +#endif +} + +void LiveActions::open_planner() +{ + execute_command("evolution -c calendar"); } void LiveActions::open_phone_settings() { - g_message("%s", G_STRFUNC); + dispatch_url("settings:///system/time-date"); } void LiveActions::open_phone_clock_app() { - g_message("%s", G_STRFUNC); + dispatch_url("appid://com.ubuntu.clock/clock/current-user-version"); } -void LiveActions::open_planner() +void LiveActions::open_planner_at(const DateTime& dt) { - g_message("%s", G_STRFUNC); + auto cmd = dt.format("evolution \"calendar:///?startdate=%Y%m%d\""); + execute_command(cmd.c_str()); } -void LiveActions::open_planner_at(const DateTime&) +void LiveActions::open_appointment(const std::string& uid) { - g_message("%s", G_STRFUNC); + for(const auto& appt : state()->planner->upcoming.get()) + { + if(appt.uid != uid) + continue; + + if (!appt.url.empty()) + dispatch_url(appt.url); + break; + } } -void LiveActions::open_appointment(const std::string& uid) +void LiveActions::set_calendar_date(const DateTime& dt) { - g_message("%s - %s", G_STRFUNC, uid.c_str()); + state()->calendar_day.set(dt); } -void LiveActions::set_location(const std::string& zone, const std::string& name) +/*** +**** +***/ + +namespace +{ + +struct setlocation_data { - g_message("%s - %s %s", G_STRFUNC, zone.c_str(), name.c_str()); + std::string tzid; + std::string name; + std::shared_ptr settings; +}; + +static void +on_datetime1_set_timezone_response(GObject * object, + GAsyncResult * res, + gpointer gdata) +{ + GError* err = nullptr; + auto response = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &err); + auto data = static_cast(gdata); + + if (err != nullptr) + { + if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Could not set new timezone: %s", err->message); + + g_error_free(err); + } + else + { + data->settings->timezone_name.set(data->tzid + " " + data->name); + g_variant_unref(response); + } + + delete data; } -void LiveActions::set_calendar_date(const DateTime&) +static void +on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) { - g_message("%s", G_STRFUNC); + auto data = static_cast(gdata); + + GError * err = nullptr; + auto proxy = g_dbus_proxy_new_for_bus_finish(res, &err); + if (err != NULL) + { + if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Could not grab DBus proxy for timedated: %s", err->message); + + g_error_free(err); + + delete data; + } + else + { + g_dbus_proxy_call(proxy, + "SetTimezone", + g_variant_new ("(sb)", data->tzid.c_str(), TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + on_datetime1_set_timezone_response, + data); + + g_object_unref (proxy); + } } +} // unnamed namespace + + +void LiveActions::set_location(const std::string& tzid, const std::string& name) +{ + g_return_if_fail(!tzid.empty()); + g_return_if_fail(!name.empty()); + + auto data = new struct setlocation_data; + data->tzid = tzid; + data->name = name; + data->settings = state()->settings; + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + nullptr, + on_datetime1_proxy_ready, + data); +} /*** **** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a908801..9bc1d75 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -84,6 +84,13 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-live-actions +set (TEST_NAME test-live-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) @@ -98,7 +105,7 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-utils +# test-settings set (TEST_NAME test-settings) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 0f86b0d..95ac5c2 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -148,7 +148,7 @@ protected: 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); + m_mock_state->mock_clock->set_localtime(tomorrow); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); -- cgit v1.2.3 From 74fcc162abf2224bd74e564978afb338252c29ce Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:15:34 -0600 Subject: Fix GDateTime leak in DateTime::DateTime(time_t) --- include/datetime/date-time.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index 5b9421f..33f8b40 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -38,7 +38,11 @@ public: explicit DateTime(GDateTime* in=nullptr) { reset(in); } - explicit DateTime(time_t t) { reset(g_date_time_new_from_unix_local(t)); } + explicit DateTime(time_t t) { + GDateTime * gdt = g_date_time_new_from_unix_local(t); + reset(gdt); + g_date_time_unref(gdt); + } static DateTime NowLocal() { GDateTime * gdt = g_date_time_new_now_local(); -- cgit v1.2.3 From 931e448b916453139e2c043161088851a1b47b42 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:33:22 -0600 Subject: remove unnecessary #includes --- tests/state-mock.h | 3 --- tests/test-menus.cc | 4 ---- 2 files changed, 7 deletions(-) diff --git a/tests/state-mock.h b/tests/state-mock.h index ae1217b..2104aa0 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -18,11 +18,8 @@ */ #include -#include #include -#include #include -#include #include #include diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 95ac5c2..fe1e86e 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -22,13 +22,9 @@ #include "state-fixture.h" #include -#include #include #include -#include -#include #include -#include #include -- cgit v1.2.3 From 2b857f6ca1c9c67a023fb9ad2eb6ec97f59e4335 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:34:25 -0600 Subject: move DateTime's impl from the header to a cc file --- include/datetime/date-time.h | 121 ++++++--------------------------- src/CMakeLists.txt | 4 +- src/date-time.cpp | 157 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 102 deletions(-) create mode 100644 src/date-time.cpp diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index 33f8b40..145e34e 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -35,107 +35,26 @@ namespace datetime { class DateTime { public: - - explicit DateTime(GDateTime* in=nullptr) { reset(in); } - - explicit DateTime(time_t t) { - GDateTime * gdt = g_date_time_new_from_unix_local(t); - reset(gdt); - g_date_time_unref(gdt); - } - - 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* get() const { - g_assert(m_dt); - return m_dt.get(); - } - - GDateTime* operator()() const { - return get(); - } - - - 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(g_date_time_ref(in), deleter); - g_assert(m_dt); - } else { - m_dt.reset(); - } - } - - DateTime& operator=(GDateTime* in) { - reset(in); - return *this; - } - - DateTime& operator=(const DateTime& in) { - m_dt = in.m_dt; - return *this; - } - - 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 true if this isn't set, or if it's not equal - return (!m_dt) || !(*this == that); - } - - 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; - } + static DateTime NowLocal(); + explicit DateTime(time_t t); + explicit DateTime(GDateTime* in=nullptr) {reset(in);} + DateTime& operator=(GDateTime* in) {reset(in); return *this;} + DateTime& operator=(const DateTime& in) {m_dt=in.m_dt; return *this; } + DateTime to_timezone(const std::string& zone) const; + void reset(GDateTime* in=nullptr); + + GDateTime* get() const; + GDateTime* operator()() const {return get();} + + std::string format(const std::string& fmt) const; + int day_of_month() const; + int64_t to_unix() const; + int day_of_year() const; + gint64 difference(const DateTime& that) const; + + bool operator<(const DateTime& that) const; + bool operator!=(const DateTime& that) const; + bool operator==(const DateTime& that) const; private: std::shared_ptr m_dt; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 976adc3..a90fcbf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,14 +16,16 @@ add_library (${SERVICE_LIB} STATIC actions-live.cpp clock.cpp clock-live.cpp + date-time.cpp + exporter.cpp formatter.cpp formatter-desktop.cpp locations.cpp locations-settings.cpp menu.cpp planner-eds.cpp - service.cpp settings-live.cpp + state-live.cpp timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp diff --git a/src/date-time.cpp b/src/date-time.cpp new file mode 100644 index 0000000..3842ac0 --- /dev/null +++ b/src/date-time.cpp @@ -0,0 +1,157 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +DateTime::DateTime(time_t t) +{ + GDateTime * gdt = g_date_time_new_from_unix_local(t); + reset(gdt); + g_date_time_unref(gdt); +} + +DateTime DateTime::NowLocal() +{ + GDateTime * gdt = g_date_time_new_now_local(); + DateTime dt(gdt); + g_date_time_unref(gdt); + return dt; +} + +DateTime 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* DateTime::get() const +{ + g_assert(m_dt); + return m_dt.get(); +} + +std::string DateTime::format(const std::string& fmt) const +{ + const auto str = g_date_time_format(get(), fmt.c_str()); + std::string ret = str; + g_free(str); + return ret; +} + +int DateTime::day_of_month() const +{ + return g_date_time_get_day_of_month(get()); +} + +int64_t DateTime::to_unix() const +{ + return g_date_time_to_unix(get()); +} + +int DateTime::day_of_year() const +{ + return m_dt ? g_date_time_get_day_of_year(get()) : -1; +} + +void DateTime::reset(GDateTime* in) +{ + if (in) + { + auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; + m_dt = std::shared_ptr(g_date_time_ref(in), deleter); + g_assert(m_dt); + } + else + { + m_dt.reset(); + } +} + +#if 0 +DateTime& DateTime::operator=(GDateTime* in) +{ + reset(in); + return *this; +} + +DateTime& DateTime::operator=(const DateTime& in) +{ + m_dt = in.m_dt; + return *this; +} +#endif + +gint64 DateTime::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 DateTime::operator<(const DateTime& that) const +{ + return g_date_time_compare(get(), that.get()) < 0; +} + +bool DateTime::operator!=(const DateTime& that) const +{ + // return true if this isn't set, or if it's not equal + return (!m_dt) || !(*this == that); +} + +bool DateTime::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; +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity -- cgit v1.2.3 From d611929649cc4ef6b04ad9c453f14c85e1108842 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:36:07 -0600 Subject: extract LiveState to its own State subclass to make main()'s flow easier to follow --- include/datetime/state-live.h | 49 ++++++++++++++++++++++++++++++++++++ src/main.cpp | 46 +++++++++------------------------- src/state-live.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 34 deletions(-) create mode 100644 include/datetime/state-live.h create mode 100644 src/state-live.cpp diff --git a/include/datetime/state-live.h b/include/datetime/state-live.h new file mode 100644 index 0000000..2b93722 --- /dev/null +++ b/include/datetime/state-live.h @@ -0,0 +1,49 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_STATE_LIVE_H +#define INDICATOR_DATETIME_STATE_LIVE_H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class LiveState: public State +{ +public: + LiveState(); + virtual ~LiveState() =default; +}; + +/*** +**** +***/ + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H diff --git a/src/main.cpp b/src/main.cpp index 7bd6f9d..6a56163 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,24 +18,16 @@ */ #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include +#include -#include +#include // bindtextdomain() #include #include -#include - #include -#include /* exit() */ +#include // exit() using namespace unity::indicator::datetime; @@ -46,7 +38,7 @@ main(int /*argc*/, char** /*argv*/) // 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 + // init i18n setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain(GETTEXT_PACKAGE); @@ -55,22 +47,8 @@ main(int /*argc*/, char** /*argv*/) if(!notify_init("indicator-datetime-service")) g_critical("libnotify initialization failed"); - // build the state - std::shared_ptr settings(new LiveSettings); - std::shared_ptr timezones(new LiveTimezones(settings, TIMEZONE_FILE)); - std::shared_ptr clock(new LiveClock(timezones)); - std::shared_ptr planner(new PlannerEds); - planner->time = clock->localtime(); - std::shared_ptr locations(new SettingsLocations(settings, timezones)); - std::shared_ptr state(new State); - state->settings = settings; - state->timezones = timezones; - state->clock = clock; - state->locations = locations; - state->planner = planner; - state->calendar_day = clock->localtime(); - - // build the menu factory + // build the state and menu factory + std::shared_ptr state(new LiveState); std::shared_ptr actions(new LiveActions(state)); MenuFactory factory(actions, state); @@ -80,12 +58,12 @@ main(int /*argc*/, char** /*argv*/) // 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); + Exporter exporter; + exporter.name_lost.connect([loop](){ + g_message("%s exiting; failed/lost bus ownership", GETTEXT_PACKAGE); + g_main_loop_quit(loop); }); - //service.publish(actions, menus); + exporter.publish(actions, menus); g_main_loop_run(loop); g_main_loop_unref(loop); return 0; diff --git a/src/state-live.cpp b/src/state-live.cpp new file mode 100644 index 0000000..f4690b3 --- /dev/null +++ b/src/state-live.cpp @@ -0,0 +1,58 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +LiveState::LiveState() +{ + std::shared_ptr live_settings(new LiveSettings); + std::shared_ptr live_timezones(new LiveTimezones(live_settings, TIMEZONE_FILE)); + std::shared_ptr live_clock(new LiveClock(live_timezones)); + + settings = live_settings; + timezones = live_timezones; + clock = live_clock; + locations.reset(new SettingsLocations(live_settings, live_timezones)); + planner.reset(new PlannerEds); + planner->time = clock->localtime(); + calendar_day = clock->localtime(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity -- cgit v1.2.3 From f0fee18c0baf7ef0fb27351db716ee3708c021c6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:41:16 -0600 Subject: copyediting: rename Service as Exporter & tweak comments --- include/datetime/exporter.h | 74 +++++++++++++++++++++++ include/datetime/service.h | 72 ---------------------- src/exporter.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 6 +- src/planner-eds.cpp | 2 +- src/service.cpp | 140 ------------------------------------------ 6 files changed, 222 insertions(+), 216 deletions(-) create mode 100644 include/datetime/exporter.h delete mode 100644 include/datetime/service.h create mode 100644 src/exporter.cpp delete mode 100644 src/service.cpp diff --git a/include/datetime/exporter.h b/include/datetime/exporter.h new file mode 100644 index 0000000..a32b941 --- /dev/null +++ b/include/datetime/exporter.h @@ -0,0 +1,74 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_EXPORTER_H +#define INDICATOR_DATETIME_EXPORTER_H + +#include +#include + +#include + +#include // GActionGroup + +#include // std::shared_ptr +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Exports actions and menus to DBus. + */ +class Exporter +{ +public: + Exporter() =default; + ~Exporter(); + + core::Signal<> name_lost; + + void publish(std::shared_ptr& actions, + std::vector>& 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 m_exported_menu_ids; + guint m_own_id = 0; + guint m_exported_actions_id = 0; + GDBusConnection * m_dbus_connection = nullptr; + std::shared_ptr m_actions; + std::vector> m_menus; + + // we've got raw pointers and gsignal tags in here, so disable copying + Exporter(const Exporter&) =delete; + Exporter& operator=(const Exporter&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_EXPORTER_H diff --git a/include/datetime/service.h b/include/datetime/service.h deleted file mode 100644 index c7171b7..0000000 --- a/include/datetime/service.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_EXPORTER_H -#define INDICATOR_DATETIME_EXPORTER_H - -#include - -#include - -#include // GActionGroup - -#include // std::shared_ptr -#include - -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>& 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 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> 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/src/exporter.cpp b/src/exporter.cpp new file mode 100644 index 0000000..aa021f3 --- /dev/null +++ b/src/exporter.cpp @@ -0,0 +1,144 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +Exporter::~Exporter() +{ + 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 +Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("bus acquired: %s", name); + static_cast(gthis)->on_bus_acquired(connection, name); +} + +void +Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) +{ + m_dbus_connection = static_cast(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->action_group(), + &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 +Exporter::on_name_lost(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("name lost: %s", name); + static_cast(gthis)->on_name_lost(connection, name); +} + +void +Exporter::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) +{ + name_lost(); +} + +/*** +**** +***/ + +void +Exporter::publish(std::shared_ptr& actions, + std::vector>& 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/main.cpp b/src/main.cpp index 6a56163..50d5241 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,7 +38,7 @@ main(int /*argc*/, char** /*argv*/) // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. g_type_ensure(G_TYPE_DBUS_CONNECTION); - // init i18n + // boilerplate i18n setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain(GETTEXT_PACKAGE); @@ -47,7 +47,7 @@ main(int /*argc*/, char** /*argv*/) if(!notify_init("indicator-datetime-service")) g_critical("libnotify initialization failed"); - // build the state and menu factory + // build the state and actions for the MenuFactory to use std::shared_ptr state(new LiveState); std::shared_ptr actions(new LiveActions(state)); MenuFactory factory(actions, state); @@ -56,7 +56,7 @@ main(int /*argc*/, char** /*argv*/) std::vector> menus; menus.push_back(factory.buildMenu(Menu::Desktop)); - // export them + // export them & run until we lose the busname auto loop = g_main_loop_new(nullptr, false); Exporter exporter; exporter.name_lost.connect([loop](){ diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 275a29e..b3f751a 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -48,7 +48,7 @@ public: e_source_registry_new(m_cancellable, on_source_registry_ready, this); 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")); + g_message("planner's datetime property changed to %s; calling rebuildSoon()", dt.format("%F %T").c_str()); rebuildSoon(); }); diff --git a/src/service.cpp b/src/service.cpp deleted file mode 100644 index 0671c61..0000000 --- a/src/service.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include -#include - -#include -#include - -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(gthis)->on_bus_acquired(connection, name); -} - -void -Service::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) -{ - m_dbus_connection = static_cast(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(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>& 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 - -- cgit v1.2.3 From 6d515551ef24c92fa9a56e9533c4175e2b4f7bbd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 01:23:38 -0600 Subject: add unit tests for the menu/action exporter --- tests/CMakeLists.txt | 7 +++ tests/test-exporter.cc | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/test-exporter.cc diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9bc1d75..ff4a8a5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,6 +112,13 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-exporter +set (TEST_NAME test-exporter) +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) diff --git a/tests/test-exporter.cc b/tests/test-exporter.cc new file mode 100644 index 0000000..ea62cd3 --- /dev/null +++ b/tests/test-exporter.cc @@ -0,0 +1,128 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include "actions-mock.h" +#include "state-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include +#include + +using namespace unity::indicator::datetime; + +class ExporterFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_closed(GObject * object, + GAsyncResult * res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + +protected: + + GTestDBus* bus = nullptr; + + void SetUp() + { + super::SetUp(); + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); + } + + void TearDown() + { + GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +TEST_F(ExporterFixture, HelloWorld) +{ + // confirms that the Test DBus SetUp() and TearDown() works +} + +TEST_F(ExporterFixture, Publish) +{ + std::shared_ptr state(new MockState); + std::shared_ptr actions(new MockActions(state)); + std::vector> menus; + + Exporter exporter; + exporter.publish(actions, menus); + wait_msec(); + + auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); + auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + + // wait for the exported ActionGroup to be populated + if (g_strv_length(names_strv) == 0) + { + g_strfreev(names_strv); + wait_for_signal(exported, "action-added"); + names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + } + + // convert it to a std::set for easy prodding + std::set names; + for(int i=0; names_strv && names_strv[i]; i++) + names.insert(names_strv[i]); + + // confirm the actions that we expect + EXPECT_EQ(1, names.count("activate-appointment")); + EXPECT_EQ(1, names.count("activate-desktop-settings")); + EXPECT_EQ(1, names.count("activate-phone-clock-app")); + EXPECT_EQ(1, names.count("activate-phone-settings")); + EXPECT_EQ(1, names.count("activate-planner")); + EXPECT_EQ(1, names.count("calendar")); + EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop-header")); + EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone-header")); + EXPECT_EQ(1, names.count("set-location")); + + // cleanup + g_strfreev(names_strv); + g_clear_object(&exported); + g_clear_object(&connection); +} -- cgit v1.2.3 From f1027f7cd2f846211a46b072965331fb0eb69fec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 01:34:04 -0600 Subject: fold repetitious unit test rules into CMake functions --- tests/CMakeLists.txt | 122 ++++++++++++--------------------------------------- 1 file changed, 29 insertions(+), 93 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ff4a8a5..c8dd6aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,97 +32,33 @@ include_directories (${DBUSTEST_INCLUDE_DIRS}) add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") -# test-timezone-file -set (TEST_NAME test-timezone-file) -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-geoclue -set (TEST_NAME test-timezone-geoclue) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) - -# test-timezones -set (TEST_NAME test-timezones) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) - -# test-clock -set (TEST_NAME test-clock) -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-formatter -set (TEST_NAME test-formatter) -add_executable (${TEST_NAME} test-formatter.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-planner -set (TEST_NAME test-planner) -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) -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-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-live-actions -set (TEST_NAME test-live-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-utils -set (TEST_NAME test-utils) -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-settings -set (TEST_NAME test-settings) -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-exporter -set (TEST_NAME test-exporter) -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}) +function(add_test_by_name name) + set (TEST_NAME ${name}) + 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}) +endfunction() +add_test_by_name(test-actions) +add_test_by_name(test-clock) +add_test_by_name(test-exporter) +add_test_by_name(test-formatter) +add_test_by_name(test-live-actions) +add_test_by_name(test-locations) +add_test_by_name(test-menus) +add_test_by_name(test-planner) +add_test_by_name(test-settings) +add_test_by_name(test-timezone-file) +add_test_by_name(test-utils) + + +function(add_dbusmock_test_by_name name) + set (TEST_NAME ${name}) + 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} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) +endfunction() +add_dbusmock_test_by_name(test-timezone-geoclue) +add_dbusmock_test_by_name(test-timezones) -- cgit v1.2.3 From 0ec1731c28ee208eab98f3ff53bc63cedb527b75 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 07:57:21 -0600 Subject: in PlannerEds, wire in planner.thisMonth and planner.upcoming --- src/planner-eds.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index b3f751a..f7a1d17 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -228,6 +228,7 @@ private: { getAppointments(begin, end, [this](const std::vector& appointments) { g_message("got %d appointments in this calendar month", (int)appointments.size()); + m_owner.thisMonth.set(appointments); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -240,6 +241,7 @@ private: { getAppointments(begin, end, [this](const std::vector& appointments) { g_message("got %d upcoming appointments", (int)appointments.size()); + m_owner.upcoming.set(appointments); }); } g_clear_pointer(&begin, g_date_time_unref); -- cgit v1.2.3 From bf30d05fa9220b3369734c74197ac149c3290af7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 07:59:05 -0600 Subject: in PlannerEds, replace g_message() console messages with g_debug() --- src/planner-eds.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index f7a1d17..54332ce 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -48,7 +48,7 @@ public: e_source_registry_new(m_cancellable, on_source_registry_ready, this); m_owner.time.changed().connect([this](const DateTime& dt) { - g_message("planner's datetime property changed to %s; calling rebuildSoon()", dt.format("%F %T").c_str()); + g_debug("planner's datetime property changed to %s; calling rebuildSoon()", dt.format("%F %T").c_str()); rebuildSoon(); }); @@ -140,7 +140,7 @@ private: client, g_object_unref); - g_message("client connected; calling rebuildSoon()"); + g_debug("client connected; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } } @@ -154,7 +154,7 @@ private: { g_object_unref(e_cal_client); - g_message("source disabled; calling rebuildSoon()"); + g_debug("source disabled; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } } @@ -171,7 +171,7 @@ private: static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) { - g_message("source changed; calling rebuildSoon()"); + g_debug("source changed; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } @@ -218,7 +218,6 @@ private: GDateTime* begin; GDateTime* end; int y, m, d; - g_message("in rebuildNow"); // get all the appointments in the calendar month g_date_time_get_ymd(calendar_date, &y, &m, &d); @@ -227,7 +226,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector& appointments) { - g_message("got %d appointments in this calendar month", (int)appointments.size()); + g_debug("got %d appointments in this calendar month", (int)appointments.size()); m_owner.thisMonth.set(appointments); }); } @@ -240,7 +239,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector& appointments) { - g_message("got %d upcoming appointments", (int)appointments.size()); + g_debug("got %d upcoming appointments", (int)appointments.size()); m_owner.upcoming.set(appointments); }); } @@ -253,7 +252,7 @@ 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_debug("getting all appointments from [%s ... %s]", g_date_time_format(begin_dt, "%F %T"), g_date_time_format(end_dt, "%F %T")); /** @@ -263,7 +262,7 @@ private: icaltimezone * default_timezone = nullptr; const auto tz = g_date_time_get_timezone_abbreviation(m_owner.time.get().get()); - g_message("%s tz is %s", G_STRLOC, tz); + g_debug("%s tz is %s", G_STRLOC, tz); if (tz && *tz) { default_timezone = icaltimezone_get_builtin_timezone(tz); @@ -279,7 +278,7 @@ private: **/ std::shared_ptr main_task(new Task(this, func), [](Task* task){ - g_message("time to delete task %p", (void*)task); + g_debug("time to delete task %p", (void*)task); task->func(task->appointments); }); @@ -295,7 +294,7 @@ private: // 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", (void*)client); + g_debug("calling e_cal_client_generate_instances for %p", (void*)client); e_cal_client_generate_instances(client, begin, end, @@ -399,7 +398,7 @@ private: e_client_util_free_string_slist(uris); } - g_message("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); + g_debug("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); subtask->task->appointments.push_back(subtask->appointment); delete subtask; } -- cgit v1.2.3 From 6e447b7bdb1273048dbaf9ead0eea629e73042e3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 08:08:44 -0600 Subject: plug in the greeter menus --- src/exporter.cpp | 4 +++- src/main.cpp | 3 ++- src/menu.cpp | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/exporter.cpp b/src/exporter.cpp index aa021f3..8103b5b 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -85,13 +85,15 @@ Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) { 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); +g_message ("path %s id %d", path.c_str(), (int)id); if (id) { m_exported_menu_ids.insert(id); } else { - g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + if (error != nullptr) + g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); g_clear_error(&error); } } diff --git a/src/main.cpp b/src/main.cpp index 50d5241..2c4f160 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,8 @@ main(int /*argc*/, char** /*argv*/) // create the menus std::vector> menus; - menus.push_back(factory.buildMenu(Menu::Desktop)); + for(int i=0, n=Menu::NUM_PROFILES; i& state_, std::shared_ptr& actions_): - DesktopBaseMenu(DesktopGreeter,"desktop-greeter", state_, actions_) {} + DesktopBaseMenu(DesktopGreeter,"desktop_greeter", state_, actions_) {} }; class PhoneBaseMenu: public MenuImpl @@ -454,7 +454,7 @@ class PhoneGreeterMenu: public PhoneBaseMenu public: PhoneGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): - PhoneBaseMenu(PhoneGreeter, "phone-greeter", state_, actions_) {} + PhoneBaseMenu(PhoneGreeter, "phone_greeter", state_, actions_) {} }; /**** -- cgit v1.2.3 From 56e036200e389f74064836ea2e3254a8b9bc267f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 08:35:54 -0600 Subject: the clock icon should match the one used by the clock app, so use click to ask the clock app which icon it's using --- src/exporter.cpp | 1 - src/menu.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/exporter.cpp b/src/exporter.cpp index 8103b5b..86e3670 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -85,7 +85,6 @@ Exporter::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) { 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); -g_message ("path %s id %d", path.c_str(), (int)id); if (id) { m_exported_menu_ids.insert(id); diff --git a/src/menu.cpp b/src/menu.cpp index 43e07ae..5b19d92 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include @@ -53,6 +55,9 @@ protected: m_actions(actions), m_formatter(formatter) { + // preload the alarm icon from click + m_serialized_alarm_icon = create_alarm_icon(); + // initialize the menu create_gmenu(); for (int i=0; i m_formatter; GMenu* m_submenu = nullptr; - GVariant* get_serialized_alarm_icon() + GVariant* get_serialized_alarm_icon() { return m_serialized_alarm_icon; } + +private: + + /* try to get the clock app's filename from click. (/$pkgdir/$icon) */ + static GVariant* create_alarm_icon() { - if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) + GVariant* serialized = nullptr; + gchar* icon_filename = nullptr; + gchar* standard_error = nullptr; + gchar* pkgdir = nullptr; + + g_spawn_command_line_sync("click pkgdir com.ubuntu.clock", &pkgdir, &standard_error, nullptr, nullptr); + g_clear_pointer(&standard_error, g_free); + if (pkgdir != 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); + gchar* manifest = nullptr; + g_strstrip(pkgdir); + g_spawn_command_line_sync("click info com.ubuntu.clock", &manifest, &standard_error, nullptr, nullptr); + g_clear_pointer(&standard_error, g_free); + if (manifest != nullptr) + { + JsonParser* parser = json_parser_new(); + if (json_parser_load_from_data(parser, manifest, -1, nullptr)) + { + JsonNode* root = json_parser_get_root(parser); /* transfer-none */ + if ((root != nullptr) && (JSON_NODE_TYPE(root) == JSON_NODE_OBJECT)) + { + JsonObject* o = json_node_get_object(root); /* transfer-none */ + const gchar* icon_name = json_object_get_string_member(o, "icon"); + if (icon_name != nullptr) + icon_filename = g_build_filename(pkgdir, icon_name, nullptr); + } + } + g_object_unref(parser); + g_free(manifest); + } + g_free(pkgdir); } - return m_serialized_alarm_icon; - } + if (icon_filename != nullptr) + { + GFile* file = g_file_new_for_path(icon_filename); + GIcon* icon = g_file_icon_new(file); -private: + serialized = g_icon_serialize(icon); + + g_object_unref(icon); + g_object_unref(file); + g_free(icon_filename); + } + + return serialized; + } GVariant* get_serialized_calendar_icon() { -- cgit v1.2.3 From 9415a7cea370352141ec4e1cd140137e9215fded Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:04:30 -0600 Subject: add a broad overview of the codebase to make life easier for reviewers. --- README | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/README b/README index b31db05..6bd0edb 100644 --- a/README +++ b/README @@ -68,3 +68,56 @@ CUSTOM MENUITEMS - x-canonical-time-format s strftime format string + +CODE +==== + +Model + + The app's model is represented by the "State" class, and "Menu" objects + are the corresponding views. "State" is a simple container for various + properties, and menus connect to those properties' changed() signals to + know when the view needs to be refreshed. + + As one can see in main.c, the app's very simple flow is to instantiate + a state and its properties, build menus that correspond to the state, + and export the menus on DBus. + + Because State is a simple aggregate of its components (such as a "Clock" + or "Planner" object to get the current time and upcoming appointments, + respectively), one can plug in live components for production and mock + components for unit tests. The entire backend can be mix-and-matched by + adding the desired test-or-production components. + + Start with: + include/datetime/state.h + include/datetime/clock.h + include/datetime/locations.h + include/datetime/planner.h + include/datetime/settings.h + include/datetime/timezones.h + + Implementations: + include/datetime/settings-live.h + include/datetime/locations-settings.h + include/datetime/planner-eds.h + include/datetime/timezones-live.h + +View + + Menu is a mostly-opaque class to wrap GMenu code. Its subclasses contain + the per-profile logic of which sections/menuitems to show and which to hide. + Menus are instantiated via the MenuFactory, which takes a state and profile. + + Actions is a mostly-opaque class to wrap our GActionGroup. Its subclasses + contain the code that actually executed when an action is triggered (ie, + LiveActions for production and MockActions for testing). + + Exporter exports the Actions and Menus onto the DBus, and also emits a + signal if/when the busname is lost so indicator-datetime-service knows + when to exit. + + include/datetime/menu.h + include/datetime/actions.h + include/datetime/exporter.h + -- cgit v1.2.3 From dd41db685c518acab1c8e3676f4292f66e4d0476 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:06:01 -0600 Subject: copyediting: rename the test/ directory's .cc files to .cpp for consistency with src/ --- tests/CMakeLists.txt | 4 +- tests/test-actions.cc | 173 ----------------- tests/test-actions.cpp | 173 +++++++++++++++++ tests/test-clock.cc | 144 -------------- tests/test-clock.cpp | 144 ++++++++++++++ tests/test-exporter.cc | 128 ------------- tests/test-exporter.cpp | 128 +++++++++++++ tests/test-formatter.cc | 256 ------------------------- tests/test-formatter.cpp | 256 +++++++++++++++++++++++++ tests/test-live-actions.cpp | 289 +++++++++++++++++++++++++++++ tests/test-locations.cc | 169 ----------------- tests/test-locations.cpp | 169 +++++++++++++++++ tests/test-menus.cc | 402 ---------------------------------------- tests/test-menus.cpp | 402 ++++++++++++++++++++++++++++++++++++++++ tests/test-planner.cc | 85 --------- tests/test-planner.cpp | 85 +++++++++ tests/test-settings.cc | 197 -------------------- tests/test-settings.cpp | 197 ++++++++++++++++++++ tests/test-timezone-file.cc | 133 ------------- tests/test-timezone-file.cpp | 133 +++++++++++++ tests/test-timezone-geoclue.cc | 97 ---------- tests/test-timezone-geoclue.cpp | 97 ++++++++++ tests/test-timezones.cc | 124 ------------- tests/test-timezones.cpp | 124 +++++++++++++ tests/test-utils.cc | 74 -------- tests/test-utils.cpp | 74 ++++++++ 26 files changed, 2273 insertions(+), 1984 deletions(-) delete mode 100644 tests/test-actions.cc create mode 100644 tests/test-actions.cpp delete mode 100644 tests/test-clock.cc create mode 100644 tests/test-clock.cpp delete mode 100644 tests/test-exporter.cc create mode 100644 tests/test-exporter.cpp delete mode 100644 tests/test-formatter.cc create mode 100644 tests/test-formatter.cpp create mode 100644 tests/test-live-actions.cpp delete mode 100644 tests/test-locations.cc create mode 100644 tests/test-locations.cpp delete mode 100644 tests/test-menus.cc create mode 100644 tests/test-menus.cpp delete mode 100644 tests/test-planner.cc create mode 100644 tests/test-planner.cpp delete mode 100644 tests/test-settings.cc create mode 100644 tests/test-settings.cpp delete mode 100644 tests/test-timezone-file.cc create mode 100644 tests/test-timezone-file.cpp delete mode 100644 tests/test-timezone-geoclue.cc create mode 100644 tests/test-timezone-geoclue.cpp delete mode 100644 tests/test-timezones.cc create mode 100644 tests/test-timezones.cpp delete mode 100644 tests/test-utils.cc create mode 100644 tests/test-utils.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c8dd6aa..c909e0e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,7 +35,7 @@ add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") function(add_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) @@ -55,7 +55,7 @@ add_test_by_name(test-utils) function(add_dbusmock_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) diff --git a/tests/test-actions.cc b/tests/test-actions.cc deleted file mode 100644 index 4329608..0000000 --- a/tests/test-actions.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include - -#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({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({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-actions.cpp b/tests/test-actions.cpp new file mode 100644 index 0000000..4329608 --- /dev/null +++ b/tests/test-actions.cpp @@ -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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#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({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({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 deleted file mode 100644 index 142ccad..0000000 --- a/tests/test-clock.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include -#include - -#include "test-dbus-fixture.h" - -/*** -**** -***/ - -using namespace unity::indicator::datetime; - -class ClockFixture: public TestDBusFixture -{ - private: - typedef TestDBusFixture super; - - 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); - } -}; - -/** - * 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) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 3; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); -} - -/*** -**** -***/ - -#define TIMEZONE_FILE (SANDBOX"/timezone") - -TEST_F(ClockFixture, HelloFixture) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); -} - - -TEST_F(ClockFixture, TimezoneChangeTriggersSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - - 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(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.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) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add([](gpointer gself){ - static_cast(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; - }, this); - - g_main_loop_run(loop); - EXPECT_TRUE(skewed); -} diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp new file mode 100644 index 0000000..142ccad --- /dev/null +++ b/tests/test-clock.cpp @@ -0,0 +1,144 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include "test-dbus-fixture.h" + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class ClockFixture: public TestDBusFixture +{ + private: + typedef TestDBusFixture super; + + 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); + } +}; + +/** + * 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) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 3; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); +} + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +TEST_F(ClockFixture, HelloFixture) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); +} + + +TEST_F(ClockFixture, TimezoneChangeTriggersSkew) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + + 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(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.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) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add([](gpointer gself){ + static_cast(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; + }, this); + + g_main_loop_run(loop); + EXPECT_TRUE(skewed); +} diff --git a/tests/test-exporter.cc b/tests/test-exporter.cc deleted file mode 100644 index ea62cd3..0000000 --- a/tests/test-exporter.cc +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include "actions-mock.h" -#include "state-mock.h" -#include "glib-fixture.h" - -#include -#include -#include - -#include -#include - -using namespace unity::indicator::datetime; - -class ExporterFixture: public GlibFixture -{ -private: - - typedef GlibFixture super; - - static void on_bus_closed(GObject * object, - GAsyncResult * res, - gpointer gself) - { - auto self = static_cast(gself); - GError* err = nullptr; - g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); - g_assert_no_error(err); - g_main_loop_quit(self->loop); - } - -protected: - - GTestDBus* bus = nullptr; - - void SetUp() - { - super::SetUp(); - - // bring up the test bus - bus = g_test_dbus_new(G_TEST_DBUS_NONE); - g_test_dbus_up(bus); - const auto address = g_test_dbus_get_bus_address(bus); - g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); - g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); - } - - void TearDown() - { - GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - g_dbus_connection_close(connection, nullptr, on_bus_closed, this); - g_main_loop_run(loop); - g_clear_object(&connection); - g_test_dbus_down(bus); - g_clear_object(&bus); - - super::TearDown(); - } -}; - -TEST_F(ExporterFixture, HelloWorld) -{ - // confirms that the Test DBus SetUp() and TearDown() works -} - -TEST_F(ExporterFixture, Publish) -{ - std::shared_ptr state(new MockState); - std::shared_ptr actions(new MockActions(state)); - std::vector> menus; - - Exporter exporter; - exporter.publish(actions, menus); - wait_msec(); - - auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); - auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); - - // wait for the exported ActionGroup to be populated - if (g_strv_length(names_strv) == 0) - { - g_strfreev(names_strv); - wait_for_signal(exported, "action-added"); - names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); - } - - // convert it to a std::set for easy prodding - std::set names; - for(int i=0; names_strv && names_strv[i]; i++) - names.insert(names_strv[i]); - - // confirm the actions that we expect - EXPECT_EQ(1, names.count("activate-appointment")); - EXPECT_EQ(1, names.count("activate-desktop-settings")); - EXPECT_EQ(1, names.count("activate-phone-clock-app")); - EXPECT_EQ(1, names.count("activate-phone-settings")); - EXPECT_EQ(1, names.count("activate-planner")); - EXPECT_EQ(1, names.count("calendar")); - EXPECT_EQ(1, names.count("desktop-greeter-header")); - EXPECT_EQ(1, names.count("desktop-header")); - EXPECT_EQ(1, names.count("phone-greeter-header")); - EXPECT_EQ(1, names.count("phone-header")); - EXPECT_EQ(1, names.count("set-location")); - - // cleanup - g_strfreev(names_strv); - g_clear_object(&exported); - g_clear_object(&connection); -} diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp new file mode 100644 index 0000000..ea62cd3 --- /dev/null +++ b/tests/test-exporter.cpp @@ -0,0 +1,128 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include "actions-mock.h" +#include "state-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include +#include + +using namespace unity::indicator::datetime; + +class ExporterFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_closed(GObject * object, + GAsyncResult * res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + +protected: + + GTestDBus* bus = nullptr; + + void SetUp() + { + super::SetUp(); + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); + } + + void TearDown() + { + GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +TEST_F(ExporterFixture, HelloWorld) +{ + // confirms that the Test DBus SetUp() and TearDown() works +} + +TEST_F(ExporterFixture, Publish) +{ + std::shared_ptr state(new MockState); + std::shared_ptr actions(new MockActions(state)); + std::vector> menus; + + Exporter exporter; + exporter.publish(actions, menus); + wait_msec(); + + auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); + auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + + // wait for the exported ActionGroup to be populated + if (g_strv_length(names_strv) == 0) + { + g_strfreev(names_strv); + wait_for_signal(exported, "action-added"); + names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + } + + // convert it to a std::set for easy prodding + std::set names; + for(int i=0; names_strv && names_strv[i]; i++) + names.insert(names_strv[i]); + + // confirm the actions that we expect + EXPECT_EQ(1, names.count("activate-appointment")); + EXPECT_EQ(1, names.count("activate-desktop-settings")); + EXPECT_EQ(1, names.count("activate-phone-clock-app")); + EXPECT_EQ(1, names.count("activate-phone-settings")); + EXPECT_EQ(1, names.count("activate-planner")); + EXPECT_EQ(1, names.count("calendar")); + EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop-header")); + EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone-header")); + EXPECT_EQ(1, names.count("set-location")); + + // cleanup + g_strfreev(names_strv); + g_clear_object(&exported); + g_clear_object(&connection); +} diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc deleted file mode 100644 index 9950453..0000000 --- a/tests/test-formatter.cc +++ /dev/null @@ -1,256 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "glib-fixture.h" - -#include -#include -#include - -#include - -#include -#include - -using namespace unity::indicator::datetime; - -/*** -**** -***/ - -class FormatterFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - gchar* m_original_locale = nullptr; - - protected: - - std::shared_ptr m_settings; - - virtual void SetUp() - { - super::SetUp(); - - m_settings.reset(new Settings); - m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); - } - - virtual void TearDown() - { - m_settings.reset(); - - setlocale(LC_TIME, m_original_locale); - g_clear_pointer(&m_original_locale, g_free); - - super::TearDown(); - } - - bool SetLocale(const char* expected_locale, const char* name) - { - setlocale(LC_TIME, expected_locale); - const auto actual_locale = setlocale(LC_TIME, nullptr); - if (!g_strcmp0(expected_locale, actual_locale)) - { - return true; - } - else - { - g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); - return false; - } - } - - inline bool Set24hLocale() { return SetLocale("C", "24h"); } - inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } -}; - - -/** - * Test the phone header format - */ -TEST_F(FormatterFixture, TestPhoneHeader) -{ - auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - std::shared_ptr clock(new MockClock(DateTime(now))); - g_date_time_unref(now); - - // test the default value in a 24h locale - if(Set24hLocale()) - { - PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); - EXPECT_EQ(std::string("18:30"), formatter.header.get()); - } - - // test the default value in a 12h locale - if(Set12hLocale()) - { - PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); - EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); - } -} - -#define EM_SPACE "\u2003" - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestDesktopHeader) -{ - struct { - bool is_12h; - bool show_day; - bool show_date; - bool show_year; - const char* expected_format_string; - } test_cases[] = { - { false, false, false, false, "%H:%M" }, - { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false - { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, - { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, - { false, true, false, false, "%a" EM_SPACE "%H:%M" }, - { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false - { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, - { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, - { true, false, false, false, "%l:%M %p" }, - { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false - { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, - { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, - { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, - { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false - { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, - { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } - }; - - auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - std::shared_ptr clock(new MockClock(DateTime(now))); - g_date_time_unref(now); - - for(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - DesktopFormatter f(clock, m_settings); - - m_settings->show_day.set(test_case.show_day); - m_settings->show_date.set(test_case.show_date); - m_settings->show_year.set(test_case.show_year); - - ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); - } - } -} - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestUpcomingTimes) -{ - auto a = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - - struct { - gboolean is_12h; - GDateTime* now; - GDateTime* then; - const char* expected_format_string; - } test_cases[] = { - { 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(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - std::shared_ptr clock (new MockClock(DateTime(test_case.now))); - DesktopFormatter f(clock, m_settings); - - const auto fmt = f.getRelativeFormat(test_case.then); - ASSERT_EQ(test_case.expected_format_string, fmt); - - g_clear_pointer(&test_case.now, g_date_time_unref); - g_clear_pointer(&test_case.then, g_date_time_unref); - } - } - - g_date_time_unref(a); -} - - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestEventTimes) -{ - 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; - GDateTime* now; - GDateTime* then; - GDateTime* then_end; - const char* expected_format_string; - } test_cases[] = { - { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, - { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, - { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, - { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } - }; - - for(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - std::shared_ptr clock(new MockClock(DateTime(test_case.now))); - DesktopFormatter f(clock, m_settings); - - const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); - ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); - - g_clear_pointer(&test_case.now, g_date_time_unref); - g_clear_pointer(&test_case.then, g_date_time_unref); - g_clear_pointer(&test_case.then_end, g_date_time_unref); - } - } - - g_date_time_unref(tomorrow_end); - g_date_time_unref(tomorrow_begin); - g_date_time_unref(day_end); - g_date_time_unref(day_begin); - g_date_time_unref(day); -} diff --git a/tests/test-formatter.cpp b/tests/test-formatter.cpp new file mode 100644 index 0000000..9950453 --- /dev/null +++ b/tests/test-formatter.cpp @@ -0,0 +1,256 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include +#include +#include + +#include + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class FormatterFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + gchar* m_original_locale = nullptr; + + protected: + + std::shared_ptr m_settings; + + virtual void SetUp() + { + super::SetUp(); + + m_settings.reset(new Settings); + m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); + } + + virtual void TearDown() + { + m_settings.reset(); + + setlocale(LC_TIME, m_original_locale); + g_clear_pointer(&m_original_locale, g_free); + + super::TearDown(); + } + + bool SetLocale(const char* expected_locale, const char* name) + { + setlocale(LC_TIME, expected_locale); + const auto actual_locale = setlocale(LC_TIME, nullptr); + if (!g_strcmp0(expected_locale, actual_locale)) + { + return true; + } + else + { + g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + return false; + } + } + + inline bool Set24hLocale() { return SetLocale("C", "24h"); } + inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } +}; + + +/** + * Test the phone header format + */ +TEST_F(FormatterFixture, TestPhoneHeader) +{ + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); + g_date_time_unref(now); + + // test the default value in a 24h locale + if(Set24hLocale()) + { + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("18:30"), formatter.header.get()); + } + + // test the default value in a 12h locale + if(Set12hLocale()) + { + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); + } +} + +#define EM_SPACE "\u2003" + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestDesktopHeader) +{ + struct { + bool is_12h; + bool show_day; + bool show_date; + bool show_year; + const char* expected_format_string; + } test_cases[] = { + { false, false, false, false, "%H:%M" }, + { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false + { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, + { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, + { false, true, false, false, "%a" EM_SPACE "%H:%M" }, + { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false + { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, + { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, + { true, false, false, false, "%l:%M %p" }, + { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, + { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, + { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, + { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, + { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } + }; + + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); + g_date_time_unref(now); + + for(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + DesktopFormatter f(clock, m_settings); + + m_settings->show_day.set(test_case.show_day); + m_settings->show_date.set(test_case.show_date); + m_settings->show_year.set(test_case.show_year); + + ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); + } + } +} + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestUpcomingTimes) +{ + auto a = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + + struct { + gboolean is_12h; + GDateTime* now; + GDateTime* then; + const char* expected_format_string; + } test_cases[] = { + { 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(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + std::shared_ptr clock (new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); + + const auto fmt = f.getRelativeFormat(test_case.then); + ASSERT_EQ(test_case.expected_format_string, fmt); + + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + } + } + + g_date_time_unref(a); +} + + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestEventTimes) +{ + 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; + GDateTime* now; + GDateTime* then; + GDateTime* then_end; + const char* expected_format_string; + } test_cases[] = { + { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, + { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } + }; + + for(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + std::shared_ptr clock(new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); + + const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); + + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + g_clear_pointer(&test_case.then_end, g_date_time_unref); + } + } + + g_date_time_unref(tomorrow_end); + g_date_time_unref(tomorrow_begin); + g_date_time_unref(day_end); + g_date_time_unref(day_begin); + g_date_time_unref(day); +} diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp new file mode 100644 index 0000000..d3d7720 --- /dev/null +++ b/tests/test-live-actions.cpp @@ -0,0 +1,289 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include "state-mock.h" +#include "glib-fixture.h" + +/*** +**** +***/ + +class MockLiveActions: public LiveActions +{ +public: + std::string last_cmd; + std::string last_url; + MockLiveActions(const std::shared_ptr& state_in): LiveActions(state_in) {} + virtual ~MockLiveActions() {} + +protected: + void dispatch_url(const std::string& url) { last_url = url; } + void execute_command(const std::string& cmd) { last_cmd = cmd; } +}; + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class LiveActionsFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_acquired(GDBusConnection* conn, + const gchar* name, + gpointer gself) + { + auto self = static_cast(gself); + g_debug("bus acquired: %s, connection is %p", name, conn); + + // Set up a mock GSD. + // All it really does is wait for calls to GetDevice and + // returns the get_devices_retval variant + static const GDBusInterfaceVTable vtable = { + timedate1_handle_method_call, + nullptr, /* GetProperty */ + nullptr, /* SetProperty */ + }; + + self->connection = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(conn))); + + GError* error = nullptr; + self->object_register_id = g_dbus_connection_register_object( + conn, + "/org/freedesktop/timedate1", + self->node_info->interfaces[0], + &vtable, + self, + nullptr, + &error); + g_assert_no_error(error); + } + + static void on_name_acquired(GDBusConnection* /*conn*/, + const gchar* /*name*/, + gpointer gself) + { + auto self = static_cast(gself); + self->name_acquired = true; + g_main_loop_quit(self->loop); + } + + static void on_name_lost(GDBusConnection* /*conn*/, + const gchar* /*name*/, + gpointer gself) + { + auto self = static_cast(gself); + self->name_acquired = false; + } + + static void on_bus_closed(GObject* /*object*/, + GAsyncResult* res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(self->connection, res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + + static void + timedate1_handle_method_call(GDBusConnection * /*connection*/, + const gchar * /*sender*/, + const gchar * /*object_path*/, + const gchar * /*interface_name*/, + const gchar * method_name, + GVariant * parameters, + GDBusMethodInvocation * invocation, + gpointer gself) + { + g_assert(!g_strcmp0(method_name, "SetTimezone")); + g_assert(g_variant_is_of_type(parameters, G_VARIANT_TYPE_TUPLE)); + g_assert(2 == g_variant_n_children(parameters)); + + auto child = g_variant_get_child_value(parameters, 0); + g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING)); + auto self = static_cast(gself); + self->attempted_tzid = g_variant_get_string(child, nullptr); + g_variant_unref(child); + + g_dbus_method_invocation_return_value(invocation, nullptr); + g_main_loop_quit(self->loop); + } + +protected: + + std::shared_ptr m_mock_state; + std::shared_ptr m_state; + std::shared_ptr m_live_actions; + std::shared_ptr m_actions; + + bool name_acquired; + std::string attempted_tzid; + + GTestDBus* bus; + guint own_name; + GDBusConnection* connection; + GDBusNodeInfo* node_info; + int object_register_id; + + void SetUp() + { + super::SetUp(); + + name_acquired = false; + attempted_tzid.clear(); + connection = nullptr; + node_info = nullptr; + object_register_id = 0; + own_name = 0; + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + 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); + + // parse the org.freedesktop.timedate1 interface + const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + ""; + node_info = g_dbus_node_info_new_for_xml(introspection_xml, nullptr); + ASSERT_TRUE(node_info != nullptr); + ASSERT_TRUE(node_info->interfaces != nullptr); + ASSERT_TRUE(node_info->interfaces[0] != nullptr); + ASSERT_TRUE(node_info->interfaces[1] == nullptr); + ASSERT_STREQ("org.freedesktop.timedate1", node_info->interfaces[0]->name); + + // own the bus + own_name = g_bus_own_name(G_BUS_TYPE_SYSTEM, + "org.freedesktop.timedate1", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, on_name_acquired, on_name_lost, + this, nullptr); + ASSERT_TRUE(object_register_id == 0); + ASSERT_FALSE(name_acquired); + ASSERT_TRUE(connection == nullptr); + g_main_loop_run(loop); + ASSERT_TRUE(object_register_id != 0); + ASSERT_TRUE(name_acquired); + ASSERT_TRUE(G_IS_DBUS_CONNECTION(connection)); + + // create the State and Actions + m_mock_state.reset(new MockState); + m_mock_state->settings.reset(new Settings); + m_state = std::dynamic_pointer_cast(m_mock_state); + m_live_actions.reset(new MockLiveActions(m_state)); + m_actions = std::dynamic_pointer_cast(m_live_actions); + } + + void TearDown() + { + m_actions.reset(); + m_live_actions.reset(); + m_state.reset(); + m_mock_state.reset(); + + g_dbus_connection_unregister_object(connection, object_register_id); + g_dbus_node_info_unref(node_info); + g_bus_unown_name(own_name); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +/*** +**** +***/ + +TEST_F(LiveActionsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(LiveActionsFixture, SetLocation) +{ + const std::string tzid = "America/Chicago"; + const std::string name = "Oklahoma City"; + const std::string expected = tzid + " " + name; + + EXPECT_NE(expected, m_state->settings->timezone_name.get()); + + m_actions->set_location(tzid, name); + g_main_loop_run(loop); + EXPECT_EQ(attempted_tzid, tzid); + wait_msec(); + + EXPECT_EQ(expected, m_state->settings->timezone_name.get()); +} + +TEST_F(LiveActionsFixture, OpenDesktopSettings) +{ + m_actions->open_desktop_settings(); + const std::string expected_substr = "control-center"; + EXPECT_NE(m_live_actions->last_cmd.find(expected_substr), std::string::npos); +} + +TEST_F(LiveActionsFixture, OpenPlanner) +{ + m_actions->open_planner(); + const std::string expected = "evolution -c calendar"; + EXPECT_EQ(expected, m_live_actions->last_cmd); +} + +TEST_F(LiveActionsFixture, OpenPhoneSettings) +{ + m_actions->open_phone_settings(); + const std::string expected = "settings:///system/time-date"; + EXPECT_EQ(expected, m_live_actions->last_url); +} + +TEST_F(LiveActionsFixture, OpenPhoneClockApp) +{ + m_actions->open_phone_clock_app(); + const std::string expected = "appid://com.ubuntu.clock/clock/current-user-version"; + EXPECT_EQ(expected, m_live_actions->last_url); +} + +TEST_F(LiveActionsFixture, OpenPlannerAt) +{ + const auto now = DateTime::NowLocal(); + m_actions->open_planner_at(now); + const std::string expected = now.format("evolution \"calendar:///?startdate=%Y%m%d\""); + EXPECT_EQ(expected, m_live_actions->last_cmd); +} diff --git a/tests/test-locations.cc b/tests/test-locations.cc deleted file mode 100644 index 65adbc7..0000000 --- a/tests/test-locations.cc +++ /dev/null @@ -1,169 +0,0 @@ - - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "glib-fixture.h" - -#include - -using unity::indicator::datetime::Location; -using unity::indicator::datetime::Locations; -using unity::indicator::datetime::Settings; -using unity::indicator::datetime::SettingsLocations; -using unity::indicator::datetime::Timezones; - -/*** -**** -***/ - -class LocationsFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - //GSettings * settings = nullptr; - std::shared_ptr m_settings; - std::shared_ptr m_timezones; - const std::string nyc = "America/New_York"; - const std::string chicago = "America/Chicago"; - - virtual void SetUp() - { - super::SetUp(); - - m_settings.reset(new Settings); - m_settings->show_locations.set(true); - m_settings->locations.set({"America/Los_Angeles Oakland", - "America/Chicago Chicago", - "America/Chicago Oklahoma City", - "America/Toronto Toronto", - "Europe/London London", - "Europe/Berlin Berlin"}); - - m_timezones.reset(new Timezones); - m_timezones->timezone.set(chicago); - m_timezones->timezones.set(std::set({ nyc, chicago })); - } - - virtual void TearDown() - { - m_timezones.reset(); - m_settings.reset(); - - super::TearDown(); - } -}; - -TEST_F(LocationsFixture, Timezones) -{ - m_settings->show_locations.set(false); - - SettingsLocations locations(m_settings, m_timezones); - const auto l = locations.locations.get(); - EXPECT_EQ(2, l.size()); - EXPECT_STREQ("Chicago", l[0].name().c_str()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); -} - -TEST_F(LocationsFixture, SettingsLocations) -{ - SettingsLocations locations(m_settings, m_timezones); - - const auto l = locations.locations.get(); - EXPECT_EQ(7, l.size()); - EXPECT_EQ("Chicago", l[0].name()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); - EXPECT_EQ("Oakland", l[2].name()); - EXPECT_EQ("America/Los_Angeles", l[2].zone()); - EXPECT_EQ("Oklahoma City", l[3].name()); - EXPECT_EQ("America/Chicago", l[3].zone()); - EXPECT_EQ("Toronto", l[4].name()); - EXPECT_EQ("America/Toronto", l[4].zone()); - EXPECT_EQ("London", l[5].name()); - EXPECT_EQ("Europe/London", l[5].zone()); - EXPECT_EQ("Berlin", l[6].name()); - EXPECT_EQ("Europe/Berlin", l[6].zone()); -} - -TEST_F(LocationsFixture, ChangeLocationStrings) -{ - SettingsLocations locations(m_settings, m_timezones); - - bool locations_changed = false; - locations.locations.changed().connect([&locations_changed, this](const std::vector&){ - locations_changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer settings){ - static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); - return G_SOURCE_REMOVE; - }, m_settings.get()); - - g_main_loop_run(loop); - - EXPECT_TRUE(locations_changed); - const auto l = locations.locations.get(); - EXPECT_EQ(5, l.size()); - EXPECT_EQ("Chicago", l[0].name()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); - EXPECT_EQ("Oakland", l[2].name()); - EXPECT_EQ("America/Los_Angeles", l[2].zone()); - EXPECT_EQ("London", l[3].name()); - EXPECT_EQ("Europe/London", l[3].zone()); - EXPECT_EQ("Berlin", l[4].name()); - EXPECT_EQ("Europe/Berlin", l[4].zone()); - locations_changed = false; -} - -TEST_F(LocationsFixture, ChangeLocationVisibility) -{ - SettingsLocations locations(m_settings, m_timezones); - - bool locations_changed = false; - locations.locations.changed().connect([&locations_changed, this](const std::vector&){ - locations_changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer settings){ - static_cast(settings)->show_locations.set(false); - return G_SOURCE_REMOVE; - }, m_settings.get()); - - g_main_loop_run(loop); - - EXPECT_TRUE(locations_changed); - const auto 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()); -} diff --git a/tests/test-locations.cpp b/tests/test-locations.cpp new file mode 100644 index 0000000..65adbc7 --- /dev/null +++ b/tests/test-locations.cpp @@ -0,0 +1,169 @@ + + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include + +using unity::indicator::datetime::Location; +using unity::indicator::datetime::Locations; +using unity::indicator::datetime::Settings; +using unity::indicator::datetime::SettingsLocations; +using unity::indicator::datetime::Timezones; + +/*** +**** +***/ + +class LocationsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + //GSettings * settings = nullptr; + std::shared_ptr m_settings; + std::shared_ptr m_timezones; + const std::string nyc = "America/New_York"; + const std::string chicago = "America/Chicago"; + + virtual void SetUp() + { + super::SetUp(); + + m_settings.reset(new Settings); + m_settings->show_locations.set(true); + m_settings->locations.set({"America/Los_Angeles Oakland", + "America/Chicago Chicago", + "America/Chicago Oklahoma City", + "America/Toronto Toronto", + "Europe/London London", + "Europe/Berlin Berlin"}); + + m_timezones.reset(new Timezones); + m_timezones->timezone.set(chicago); + m_timezones->timezones.set(std::set({ nyc, chicago })); + } + + virtual void TearDown() + { + m_timezones.reset(); + m_settings.reset(); + + super::TearDown(); + } +}; + +TEST_F(LocationsFixture, Timezones) +{ + m_settings->show_locations.set(false); + + SettingsLocations locations(m_settings, m_timezones); + const auto l = locations.locations.get(); + EXPECT_EQ(2, l.size()); + EXPECT_STREQ("Chicago", l[0].name().c_str()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); +} + +TEST_F(LocationsFixture, SettingsLocations) +{ + SettingsLocations locations(m_settings, m_timezones); + + const auto l = locations.locations.get(); + EXPECT_EQ(7, l.size()); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("Oklahoma City", l[3].name()); + EXPECT_EQ("America/Chicago", l[3].zone()); + EXPECT_EQ("Toronto", l[4].name()); + EXPECT_EQ("America/Toronto", l[4].zone()); + EXPECT_EQ("London", l[5].name()); + EXPECT_EQ("Europe/London", l[5].zone()); + EXPECT_EQ("Berlin", l[6].name()); + EXPECT_EQ("Europe/Berlin", l[6].zone()); +} + +TEST_F(LocationsFixture, ChangeLocationStrings) +{ + SettingsLocations locations(m_settings, m_timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer settings){ + static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); + return G_SOURCE_REMOVE; + }, m_settings.get()); + + g_main_loop_run(loop); + + EXPECT_TRUE(locations_changed); + const auto l = locations.locations.get(); + EXPECT_EQ(5, l.size()); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("London", l[3].name()); + EXPECT_EQ("Europe/London", l[3].zone()); + EXPECT_EQ("Berlin", l[4].name()); + EXPECT_EQ("Europe/Berlin", l[4].zone()); + locations_changed = false; +} + +TEST_F(LocationsFixture, ChangeLocationVisibility) +{ + SettingsLocations locations(m_settings, m_timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer settings){ + static_cast(settings)->show_locations.set(false); + return G_SOURCE_REMOVE; + }, m_settings.get()); + + g_main_loop_run(loop); + + EXPECT_TRUE(locations_changed); + const auto 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()); +} diff --git a/tests/test-menus.cc b/tests/test-menus.cc deleted file mode 100644 index fe1e86e..0000000 --- a/tests/test-menus.cc +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - - -#include "actions-mock.h" -#include "state-fixture.h" - -#include -#include -#include -#include - -#include - -using namespace unity::indicator::datetime; - -class MenuFixture: public StateFixture -{ -private: - typedef StateFixture super; - -protected: - std::shared_ptr m_menu_factory; - std::vector> 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; ibuildMenu(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_mock_state->mock_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->settings->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 appointments; - m_state->settings->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& 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 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 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; imenu_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-menus.cpp b/tests/test-menus.cpp new file mode 100644 index 0000000..fe1e86e --- /dev/null +++ b/tests/test-menus.cpp @@ -0,0 +1,402 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + + +#include "actions-mock.h" +#include "state-fixture.h" + +#include +#include +#include +#include + +#include + +using namespace unity::indicator::datetime; + +class MenuFixture: public StateFixture +{ +private: + typedef StateFixture super; + +protected: + std::shared_ptr m_menu_factory; + std::vector> 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; ibuildMenu(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_mock_state->mock_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->settings->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 appointments; + m_state->settings->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& 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 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 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; imenu_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.cc b/tests/test-planner.cc deleted file mode 100644 index 3072aea..0000000 --- a/tests/test-planner.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "glib-fixture.h" - -#include -#include -#include -#include -#include - -#include -#include - -using unity::indicator::datetime::Appointment; -using unity::indicator::datetime::DateTime; -using unity::indicator::datetime::PlannerEds; - -/*** -**** -***/ - -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 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) -{ - 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); - 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())); - - Appointment c; - c.begin = christmas; - 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())); - a = d; - 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-planner.cpp b/tests/test-planner.cpp new file mode 100644 index 0000000..3072aea --- /dev/null +++ b/tests/test-planner.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include +#include +#include +#include +#include + +#include +#include + +using unity::indicator::datetime::Appointment; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::PlannerEds; + +/*** +**** +***/ + +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 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) +{ + 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); + 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())); + + Appointment c; + c.begin = christmas; + 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())); + a = d; + 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-settings.cc b/tests/test-settings.cc deleted file mode 100644 index 9e52197..0000000 --- a/tests/test-settings.cc +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "glib-fixture.h" - -#include -#include - -using namespace unity::indicator::datetime; - -/*** -**** -***/ - -class SettingsFixture: public GlibFixture -{ -private: - typedef GlibFixture super; - -protected: - - std::shared_ptr m_live; - std::shared_ptr m_settings; - GSettings * m_gsettings; - - virtual void SetUp() - { - super::SetUp(); - - m_gsettings = g_settings_new(SETTINGS_INTERFACE); - m_live.reset(new LiveSettings); - m_settings = std::dynamic_pointer_cast(m_live); - } - - virtual void TearDown() - { - g_clear_object(&m_gsettings); - m_settings.reset(); - m_live.reset(); - - super::TearDown(); - } - - void TestBoolProperty(core::Property& property, const gchar* key) - { - EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); - g_settings_set_boolean(m_gsettings, key, false); - EXPECT_EQ(false, property.get()); - g_settings_set_boolean(m_gsettings, key, true); - EXPECT_EQ(true, property.get()); - - property.set(false); - EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); - property.set(true); - EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); - } - - void TestStringProperty(core::Property& property, const gchar* key) - { - gchar* tmp; - std::string str; - - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(tmp, property.get()); - g_clear_pointer(&tmp, g_free); - - str = "a"; - g_settings_set_string(m_gsettings, key, str.c_str()); - EXPECT_EQ(str, property.get()); - - str = "b"; - g_settings_set_string(m_gsettings, key, str.c_str()); - EXPECT_EQ(str, property.get()); - - str = "a"; - property.set(str); - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(str, tmp); - g_clear_pointer(&tmp, g_free); - - str = "b"; - property.set(str); - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(str, tmp); - g_clear_pointer(&tmp, g_free); - } -}; - -/*** -**** -***/ - -TEST_F(SettingsFixture, HelloWorld) -{ - EXPECT_TRUE(true); -} - -TEST_F(SettingsFixture, BoolProperties) -{ - TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); - TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); - TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); - TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); - TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); - TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); - TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); - TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); - TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); - TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); -} - -TEST_F(SettingsFixture, StringProperties) -{ - TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); - TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); -} - -TEST_F(SettingsFixture, TimeFormatMode) -{ - const auto key = SETTINGS_TIME_FORMAT_S; - const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM }; - - for(const auto& mode : modes) - { - g_settings_set_enum(m_gsettings, key, mode); - EXPECT_EQ(mode, m_settings->time_format_mode.get()); - } - - for(const auto& mode : modes) - { - m_settings->time_format_mode.set(mode); - EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); - } -} - -namespace -{ - std::vector strv_to_vector(const gchar** strv) - { - std::vector v; - for(int i=0; strv && strv[i]; i++) - v.push_back(strv[i]); - return v; - } -}; - -TEST_F(SettingsFixture, Locations) -{ - const auto key = SETTINGS_LOCATIONS_S; - - const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; - const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; - const std::vector av = strv_to_vector(astrv); - const std::vector bv = strv_to_vector(bstrv); - - g_settings_set_strv(m_gsettings, key, astrv); - EXPECT_EQ(av, m_settings->locations.get()); - g_settings_set_strv(m_gsettings, key, bstrv); - EXPECT_EQ(bv, m_settings->locations.get()); - - m_settings->locations.set(av); - auto tmp = g_settings_get_strv(m_gsettings, key); - auto vtmp = strv_to_vector((const gchar**)tmp); - g_strfreev(tmp); - EXPECT_EQ(av, vtmp); - - m_settings->locations.set(bv); - tmp = g_settings_get_strv(m_gsettings, key); - vtmp = strv_to_vector((const gchar**)tmp); - g_strfreev(tmp); - EXPECT_EQ(bv, vtmp); -} - -#if 0 - core::Property> locations; - core::Property time_format_mode; - -#endif diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp new file mode 100644 index 0000000..9e52197 --- /dev/null +++ b/tests/test-settings.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class SettingsFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + + std::shared_ptr m_live; + std::shared_ptr m_settings; + GSettings * m_gsettings; + + virtual void SetUp() + { + super::SetUp(); + + m_gsettings = g_settings_new(SETTINGS_INTERFACE); + m_live.reset(new LiveSettings); + m_settings = std::dynamic_pointer_cast(m_live); + } + + virtual void TearDown() + { + g_clear_object(&m_gsettings); + m_settings.reset(); + m_live.reset(); + + super::TearDown(); + } + + void TestBoolProperty(core::Property& property, const gchar* key) + { + EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); + g_settings_set_boolean(m_gsettings, key, false); + EXPECT_EQ(false, property.get()); + g_settings_set_boolean(m_gsettings, key, true); + EXPECT_EQ(true, property.get()); + + property.set(false); + EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + property.set(true); + EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + } + + void TestStringProperty(core::Property& property, const gchar* key) + { + gchar* tmp; + std::string str; + + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(tmp, property.get()); + g_clear_pointer(&tmp, g_free); + + str = "a"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "b"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "a"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + + str = "b"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + } +}; + +/*** +**** +***/ + +TEST_F(SettingsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(SettingsFixture, BoolProperties) +{ + TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); + TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); + TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); + TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); + TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); + TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); + TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); + TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); + TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); + TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); +} + +TEST_F(SettingsFixture, StringProperties) +{ + TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); + TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); +} + +TEST_F(SettingsFixture, TimeFormatMode) +{ + const auto key = SETTINGS_TIME_FORMAT_S; + const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM }; + + for(const auto& mode : modes) + { + g_settings_set_enum(m_gsettings, key, mode); + EXPECT_EQ(mode, m_settings->time_format_mode.get()); + } + + for(const auto& mode : modes) + { + m_settings->time_format_mode.set(mode); + EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); + } +} + +namespace +{ + std::vector strv_to_vector(const gchar** strv) + { + std::vector v; + for(int i=0; strv && strv[i]; i++) + v.push_back(strv[i]); + return v; + } +}; + +TEST_F(SettingsFixture, Locations) +{ + const auto key = SETTINGS_LOCATIONS_S; + + const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; + const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; + const std::vector av = strv_to_vector(astrv); + const std::vector bv = strv_to_vector(bstrv); + + g_settings_set_strv(m_gsettings, key, astrv); + EXPECT_EQ(av, m_settings->locations.get()); + g_settings_set_strv(m_gsettings, key, bstrv); + EXPECT_EQ(bv, m_settings->locations.get()); + + m_settings->locations.set(av); + auto tmp = g_settings_get_strv(m_gsettings, key); + auto vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(av, vtmp); + + m_settings->locations.set(bv); + tmp = g_settings_get_strv(m_gsettings, key); + vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(bv, vtmp); +} + +#if 0 + core::Property> locations; + core::Property time_format_mode; + +#endif diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc deleted file mode 100644 index 453b353..0000000 --- a/tests/test-timezone-file.cc +++ /dev/null @@ -1,133 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "glib-fixture.h" - -#include - -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include - -#include // fopen() -//#include // chmod() -#include // sync() - -using unity::indicator::datetime::FileTimezone; - - -/*** -**** -***/ - -#define TIMEZONE_FILE (SANDBOX"/timezone") - -class TimezoneFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp() - { - super::SetUp(); - } - - virtual void TearDown() - { - super::TearDown(); - } - - public: - - /* convenience func to set the timezone file */ - void set_file(const std::string& text) - { - auto fp = fopen(TIMEZONE_FILE, "w+"); - fprintf(fp, "%s\n", text.c_str()); - fclose(fp); - sync(); - } -}; - - -/** - * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist - */ -TEST_F(TimezoneFixture, NoFile) -{ - remove(TIMEZONE_FILE); - ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); - - FileTimezone tz(TIMEZONE_FILE); - testLogCount(G_LOG_LEVEL_WARNING, 1); -} - - -/** - * Test that timezone-file picks up the initial value - */ -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()); -} - - -/** - * Test that clearing the timezone results in an empty string - */ -TEST_F(TimezoneFixture, ChangedValue) -{ - const std::string initial_timezone = "America/Chicago"; - const std::string changed_timezone = "America/New_York"; - set_file(initial_timezone); - - 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()); - changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer gself){ - static_cast(gself)->set_file("America/New_York"); - // static_cast(gtz)->timezone.set("America/New_York"); - return G_SOURCE_REMOVE; - }, this);//&tz); - - g_main_loop_run(loop); - - ASSERT_TRUE(changed); - ASSERT_EQ(changed_timezone, tz.timezone.get()); -} diff --git a/tests/test-timezone-file.cpp b/tests/test-timezone-file.cpp new file mode 100644 index 0000000..453b353 --- /dev/null +++ b/tests/test-timezone-file.cpp @@ -0,0 +1,133 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "glib-fixture.h" + +#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include // fopen() +//#include // chmod() +#include // sync() + +using unity::indicator::datetime::FileTimezone; + + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +class TimezoneFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp() + { + super::SetUp(); + } + + virtual void TearDown() + { + super::TearDown(); + } + + public: + + /* convenience func to set the timezone file */ + void set_file(const std::string& text) + { + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); + } +}; + + +/** + * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist + */ +TEST_F(TimezoneFixture, NoFile) +{ + remove(TIMEZONE_FILE); + ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + + FileTimezone tz(TIMEZONE_FILE); + testLogCount(G_LOG_LEVEL_WARNING, 1); +} + + +/** + * Test that timezone-file picks up the initial value + */ +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()); +} + + +/** + * Test that clearing the timezone results in an empty string + */ +TEST_F(TimezoneFixture, ChangedValue) +{ + const std::string initial_timezone = "America/Chicago"; + const std::string changed_timezone = "America/New_York"; + set_file(initial_timezone); + + 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()); + changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer gself){ + static_cast(gself)->set_file("America/New_York"); + // static_cast(gtz)->timezone.set("America/New_York"); + return G_SOURCE_REMOVE; + }, this);//&tz); + + g_main_loop_run(loop); + + ASSERT_TRUE(changed); + ASSERT_EQ(changed_timezone, tz.timezone.get()); +} diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc deleted file mode 100644 index 4bf08a7..0000000 --- a/tests/test-timezone-geoclue.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "geoclue-fixture.h" - -#include - -//#include - -using unity::indicator::datetime::GeoclueTimezone; - -/*** -**** -***/ - -class TimezoneGeoclueFixture : public GeoclueFixture -{ -}; - -#if 0 -namespace -{ - struct EmitAddressChangedData - { - DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; - std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} - }; - - gboolean emit_address_changed_idle(gpointer gdata) - { - auto data = static_cast(gdata); - - GError * error = nullptr; - 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))"), - &error); - if (error) - { - g_warning("%s: %s", G_STRFUNC, error->message); - g_error_free(error); - } - - delete data; - return G_SOURCE_REMOVE; - } -} -#endif - -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()); - - // 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()); - changed = true; - 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()); -} - - diff --git a/tests/test-timezone-geoclue.cpp b/tests/test-timezone-geoclue.cpp new file mode 100644 index 0000000..4bf08a7 --- /dev/null +++ b/tests/test-timezone-geoclue.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#include "geoclue-fixture.h" + +#include + +//#include + +using unity::indicator::datetime::GeoclueTimezone; + +/*** +**** +***/ + +class TimezoneGeoclueFixture : public GeoclueFixture +{ +}; + +#if 0 +namespace +{ + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + gboolean emit_address_changed_idle(gpointer gdata) + { + auto data = static_cast(gdata); + + GError * error = nullptr; + 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))"), + &error); + if (error) + { + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free(error); + } + + delete data; + return G_SOURCE_REMOVE; + } +} +#endif + +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()); + + // 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()); + changed = true; + 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()); +} + + diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc deleted file mode 100644 index 3f02761..0000000 --- a/tests/test-timezones.cc +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include "geoclue-fixture.h" - -#include -#include - -#include // std::shared_ptr - -#include // fopen() -#include // sync() - -using namespace unity::indicator::datetime; - -typedef GeoclueFixture TimezonesFixture; - -#define TIMEZONE_FILE (SANDBOX "/timezone") - -namespace -{ - /* convenience func to set the timezone file */ - void set_file(const std::string& text) - { - auto fp = fopen(TIMEZONE_FILE, "w+"); - fprintf(fp, "%s\n", text.c_str()); - fclose(fp); - sync(); - } -} - - -TEST_F(TimezonesFixture, ManagerTest) -{ - std::string timezone_file = "America/New_York"; - std::string timezone_geo = "America/Denver"; - - set_file(timezone_file); - std::shared_ptr settings(new Settings); - LiveTimezones z(settings, 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 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); - }); - - // start listening for a timezone change, then change the timezone - bool zones_changed = false; - auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { - zones_changed = true; - zones = timezones; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer s_in) { - auto s = static_cast(s_in); - g_message("geolocation was %d", (int)s->show_detected_location.get()); - g_message("turning geolocation on"); - s->show_detected_location.set(true); - return G_SOURCE_REMOVE; - }, settings.get()); - - // 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)); - zones_changed = false; - - // now tweak the geoclue value... the geoclue-detected timezone should change, - // causing the 'timezones' property to change - 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)); - - // 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(str)); return G_SOURCE_REMOVE;}, const_cast(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-timezones.cpp b/tests/test-timezones.cpp new file mode 100644 index 0000000..3f02761 --- /dev/null +++ b/tests/test-timezones.cpp @@ -0,0 +1,124 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include "geoclue-fixture.h" + +#include +#include + +#include // std::shared_ptr + +#include // fopen() +#include // sync() + +using namespace unity::indicator::datetime; + +typedef GeoclueFixture TimezonesFixture; + +#define TIMEZONE_FILE (SANDBOX "/timezone") + +namespace +{ + /* convenience func to set the timezone file */ + void set_file(const std::string& text) + { + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); + } +} + + +TEST_F(TimezonesFixture, ManagerTest) +{ + std::string timezone_file = "America/New_York"; + std::string timezone_geo = "America/Denver"; + + set_file(timezone_file); + std::shared_ptr settings(new Settings); + LiveTimezones z(settings, 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 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); + }); + + // start listening for a timezone change, then change the timezone + bool zones_changed = false; + auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { + zones_changed = true; + zones = timezones; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer s_in) { + auto s = static_cast(s_in); + g_message("geolocation was %d", (int)s->show_detected_location.get()); + g_message("turning geolocation on"); + s->show_detected_location.set(true); + return G_SOURCE_REMOVE; + }, settings.get()); + + // 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)); + zones_changed = false; + + // now tweak the geoclue value... the geoclue-detected timezone should change, + // causing the 'timezones' property to change + 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)); + + // 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(str)); return G_SOURCE_REMOVE;}, const_cast(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 deleted file mode 100644 index 2fe6a2e..0000000 --- a/tests/test-utils.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include - -#include - -TEST(UtilsTest, SplitSettingsLocation) -{ - 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(const auto& test_case : test_cases) - { - char * zone = nullptr; - char * name = nullptr; - - split_settings_location(test_case.location, &zone, &name); - ASSERT_STREQ(test_case.expected_zone, zone); - ASSERT_STREQ(test_case.expected_name, name); - - g_free(zone); - g_free(name); - } -} - -TEST(UtilsTest, BeautifulTimezoneName) -{ - struct { - const char* timezone; - const char* location; - const char* expected_name; - } test_cases[] = { - { "America/Chicago", NULL, "Chicago" }, - { "America/Chicago", "America/Chicago", "Chicago" }, - { "America/Chicago", "America/Chigago Chicago", "Chicago" }, - { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, - { "America/Chicago", "Europe/London London", "Chicago" } - }; - - for(const auto& test_case : test_cases) - { - auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); - EXPECT_STREQ(test_case.expected_name, name); - g_free(name); - } -} diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp new file mode 100644 index 0000000..2fe6a2e --- /dev/null +++ b/tests/test-utils.cpp @@ -0,0 +1,74 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +TEST(UtilsTest, SplitSettingsLocation) +{ + 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(const auto& test_case : test_cases) + { + char * zone = nullptr; + char * name = nullptr; + + split_settings_location(test_case.location, &zone, &name); + ASSERT_STREQ(test_case.expected_zone, zone); + ASSERT_STREQ(test_case.expected_name, name); + + g_free(zone); + g_free(name); + } +} + +TEST(UtilsTest, BeautifulTimezoneName) +{ + struct { + const char* timezone; + const char* location; + const char* expected_name; + } test_cases[] = { + { "America/Chicago", NULL, "Chicago" }, + { "America/Chicago", "America/Chicago", "Chicago" }, + { "America/Chicago", "America/Chigago Chicago", "Chicago" }, + { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, + { "America/Chicago", "Europe/London London", "Chicago" } + }; + + for(const auto& test_case : test_cases) + { + auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); + EXPECT_STREQ(test_case.expected_name, name); + g_free(name); + } +} -- cgit v1.2.3 From 572377478193fab7189e817f08487a17543ee32b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:16:32 -0600 Subject: silence a minor compiler warning in test-settings --- tests/test-settings.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index 9e52197..df01cd7 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -61,14 +61,14 @@ protected: { EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); g_settings_set_boolean(m_gsettings, key, false); - EXPECT_EQ(false, property.get()); + EXPECT_FALSE(property.get()); g_settings_set_boolean(m_gsettings, key, true); - EXPECT_EQ(true, property.get()); + EXPECT_TRUE(property.get()); property.set(false); - EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + EXPECT_FALSE(g_settings_get_boolean(m_gsettings, key)); property.set(true); - EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + EXPECT_TRUE(g_settings_get_boolean(m_gsettings, key)); } void TestStringProperty(core::Property& property, const gchar* key) -- cgit v1.2.3 From b4a6c506a38e5e9d364a3909eb4aeeed91ed36c8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:53:27 -0600 Subject: copyediting: omit unnecessary #includes --- src/menu.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index 5b19d92..503d47c 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -19,12 +19,8 @@ #include -#include #include -#include -#include #include -#include #include -- cgit v1.2.3 From 47ab9adad438b137b5e7e9b33ce503e184e019f8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:57:52 -0600 Subject: follow-up to r326, if we can't get the alarm icon from click, use a sane fallback icon --- src/menu.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index 503d47c..9e76d4b 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -35,7 +35,7 @@ namespace datetime { ***** ****/ -#define ALARM_CLOCK_ICON_NAME "alarm-clock" +#define FALLBACK_ALARM_CLOCK_ICON_NAME "clock" #define CALENDAR_ICON_NAME "calendar" class MenuImpl: public Menu @@ -163,8 +163,15 @@ private: g_free(icon_filename); } + if (serialized == nullptr) + { + auto i = g_themed_icon_new_with_default_fallbacks(FALLBACK_ALARM_CLOCK_ICON_NAME); + serialized = g_icon_serialize(i); + g_object_unref(i); + } + return serialized; - } + } GVariant* get_serialized_calendar_icon() { -- cgit v1.2.3 From 2db8f6e7c4b7148377800400c72bb2e59b793d3a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:01:20 -0600 Subject: remove the State.timezones property. Timezones is a helper class for LiveClock and doesn't need to be public in State. --- include/datetime/state.h | 11 ++++++++++- src/state-live.cpp | 1 - tests/state-mock.h | 3 --- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/datetime/state.h b/include/datetime/state.h index e735b6f..b14908e 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -52,11 +52,20 @@ namespace datetime { */ struct State { + /** \brief The current time. Used by the header, by the date menuitem, + and by the locations for relative timestamp */ std::shared_ptr clock; + + /** \brief The locations to be displayed in the Locations + section of the #Menu */ std::shared_ptr locations; + + /** \brief The appointments to be displayed in the Calendar and + Appointments sections of the #Menu */ std::shared_ptr planner; + + /** \brief Configuration options that modify the view */ std::shared_ptr settings; - std::shared_ptr timezones; core::Property calendar_day; }; diff --git a/src/state-live.cpp b/src/state-live.cpp index f4690b3..8ee663b 100644 --- a/src/state-live.cpp +++ b/src/state-live.cpp @@ -41,7 +41,6 @@ LiveState::LiveState() std::shared_ptr live_clock(new LiveClock(live_timezones)); settings = live_settings; - timezones = live_timezones; clock = live_clock; locations.reset(new SettingsLocations(live_settings, live_timezones)); planner.reset(new PlannerEds); diff --git a/tests/state-mock.h b/tests/state-mock.h index 2104aa0..f2e58e6 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -18,10 +18,8 @@ */ #include -#include #include #include -#include using namespace unity::indicator::datetime; @@ -35,7 +33,6 @@ public: const DateTime now = DateTime::NowLocal(); mock_clock.reset(new MockClock(now)); settings.reset(new Settings); - timezones.reset(new Timezones); clock = std::dynamic_pointer_cast(mock_clock); planner.reset(new MockPlanner); planner->time = now; -- cgit v1.2.3 From 039c43dc5d0bee82c69c4ef8beee246940570096 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:01:57 -0600 Subject: fix r325 regression found by test-menus --- src/actions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 0df7d53..1d80c99 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -188,11 +188,11 @@ Actions::Actions(const std::shared_ptr& state): 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); + 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); + a = g_simple_action_new_stateful("phone_greeter-header", nullptr, v); g_action_map_add_action(gam, G_ACTION(a)); // add the calendar action -- cgit v1.2.3 From 0f6a8f9bcf57e0c77b9c8d7f83d93dd1be178d41 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:03:57 -0600 Subject: copyediting: slightly better header comments/docs --- include/datetime/actions-live.h | 2 +- include/datetime/actions.h | 6 +++++- include/datetime/clock.h | 2 +- include/datetime/formatter.h | 3 +-- include/datetime/locations-settings.h | 4 ++-- include/datetime/menu.h | 4 ++++ include/datetime/planner-mock.h | 4 ++-- include/datetime/planner.h | 2 ++ include/datetime/settings.h | 2 +- 9 files changed, 19 insertions(+), 10 deletions(-) diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 1947e6e..949222d 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -27,7 +27,7 @@ namespace indicator { namespace datetime { /** - * \brief Production implentation of the Actions interface. + * \brief Production implementation of the Actions interface. * * Delegates URLs, sets the timezone via org.freedesktop.timedate1, etc. * diff --git a/include/datetime/actions.h b/include/datetime/actions.h index a4c6017..18286dc 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -33,7 +33,11 @@ namespace indicator { namespace datetime { /** - * Interface for all the actions that can be activated by users via the menu. + * \brief Interface for all the actions that can be activated by users. + * + * This is a simple C++ wrapper around our GActionGroup that gets exported + * onto the bus. Subclasses implement the actual code that should be run + * when a particular action is triggered. */ class Actions { diff --git a/include/datetime/clock.h b/include/datetime/clock.h index c8e6c16..ffaf4f8 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -61,7 +61,7 @@ private: GDBusConnection * m_system_bus = nullptr; unsigned int m_sleep_subscription_id = 0; - // we've got raw pointers in here, so disable copying + // we've got raw pointers and GSignal tags in here, so disable copying Clock(const Clock&) =delete; Clock& operator=(const Clock&) =delete; }; diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index d8736c7..86fa64a 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -45,8 +45,7 @@ class DateTime; /** * \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: + * This is a simple goal, but getting there has a lot of options and edge cases: * * - The default time format can change based on the locale. * diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index 4072736..d01cbb5 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -36,8 +36,8 @@ class SettingsLocations: public Locations { public: /** - * @param[in] schemaId the settings schema to load - * @param[in] timezones the timezones to always show first in the list + * @param[in] settings the #Settings whose locations property is to be used + * @param[in] timezones the #Timezones to always show first in the list */ SettingsLocations (const std::shared_ptr& settings, const std::shared_ptr& timezones); diff --git a/include/datetime/menu.h b/include/datetime/menu.h index 0bc3781..fcd709f 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -36,6 +36,7 @@ namespace datetime { * \brief A menu for a specific profile; eg, Desktop or Phone. * * @see MenuFactory + * @see Exporter */ class Menu { @@ -62,6 +63,9 @@ private: /** * \brief Builds a Menu for a given state and profile + * + * @see Menu + * @see Exporter */ class MenuFactory { diff --git a/include/datetime/planner-mock.h b/include/datetime/planner-mock.h index bb3ff53..44d30c7 100644 --- a/include/datetime/planner-mock.h +++ b/include/datetime/planner-mock.h @@ -27,8 +27,8 @@ namespace indicator { namespace datetime { /** - * \brief Planner which does nothing on its own and requires - * its client to set its appointments property. + * \brief Planner which does nothing on its own. + * It requires its client must set its appointments property. */ class MockPlanner: public Planner { diff --git a/include/datetime/planner.h b/include/datetime/planner.h index 198b6fa..a8f9941 100644 --- a/include/datetime/planner.h +++ b/include/datetime/planner.h @@ -63,6 +63,8 @@ protected: Planner() =default; private: + + // disable copying Planner(const Planner&) =delete; Planner& operator=(const Planner&) =delete; }; diff --git a/include/datetime/settings.h b/include/datetime/settings.h index 418bc33..ce234d9 100644 --- a/include/datetime/settings.h +++ b/include/datetime/settings.h @@ -34,7 +34,7 @@ namespace datetime { * \brief Interface that represents user-configurable settings. * * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml - * For more information. + * for more information on specific properties. */ class Settings { -- cgit v1.2.3 From 063ff0ef0a7761e713b7b36e220e0e2bfa442326 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:14:56 -0600 Subject: sync test-exporter with the greeter profile's header names that changed in r325 --- tests/test-exporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index ea62cd3..aa9e89d 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -115,9 +115,9 @@ TEST_F(ExporterFixture, Publish) EXPECT_EQ(1, names.count("activate-phone-settings")); EXPECT_EQ(1, names.count("activate-planner")); EXPECT_EQ(1, names.count("calendar")); - EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop_greeter-header")); EXPECT_EQ(1, names.count("desktop-header")); - EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone_greeter-header")); EXPECT_EQ(1, names.count("phone-header")); EXPECT_EQ(1, names.count("set-location")); -- cgit v1.2.3 From 08b1cfd6bc97cae18b916d97a03781986e7421fe Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:15:56 -0600 Subject: move planner-mock.h from include/datetime/ to tests/ because it's only needed by the tests. --- include/datetime/planner-mock.h | 44 ---------------------------------- tests/planner-mock.h | 52 +++++++++++++++-------------------------- tests/state-mock.h | 3 ++- 3 files changed, 21 insertions(+), 78 deletions(-) delete mode 100644 include/datetime/planner-mock.h diff --git a/include/datetime/planner-mock.h b/include/datetime/planner-mock.h deleted file mode 100644 index 44d30c7..0000000 --- a/include/datetime/planner-mock.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H -#define INDICATOR_DATETIME_PLANNER_MOCK_H - -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/** - * \brief Planner which does nothing on its own. - * It requires its client must 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/tests/planner-mock.h b/tests/planner-mock.h index 8d7d7c2..44d30c7 100644 --- a/tests/planner-mock.h +++ b/tests/planner-mock.h @@ -1,9 +1,6 @@ /* * Copyright 2013 Canonical Ltd. * - * Authors: - * Charles Kerr - * * 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. @@ -15,44 +12,33 @@ * * You should have received a copy of the GNU General Public License along * with this program. If not, see . + * + * Authors: + * Charles Kerr */ -#ifndef __INDICATOR_DATETIME_PLANNER_MOCK__H__ -#define __INDICATOR_DATETIME_PLANNER_MOCK__H__ - -#include "planner.h" /* parent class */ +#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H +#define INDICATOR_DATETIME_PLANNER_MOCK_H -G_BEGIN_DECLS +#include -#define INDICATOR_TYPE_DATETIME_PLANNER_MOCK (indicator_datetime_planner_mock_get_type()) -#define INDICATOR_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMock)) -#define INDICATOR_DATETIME_PLANNER_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMockClass)) -#define INDICATOR_IS_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK)) - -typedef struct _IndicatorDatetimePlannerMock IndicatorDatetimePlannerMock; -typedef struct _IndicatorDatetimePlannerMockPriv IndicatorDatetimePlannerMockPriv; -typedef struct _IndicatorDatetimePlannerMockClass IndicatorDatetimePlannerMockClass; - -GType indicator_datetime_planner_mock_get_type (void); +namespace unity { +namespace indicator { +namespace datetime { /** - * An IndicatorDatetimePlanner which uses Evolution Data Server - * to get its list of appointments. + * \brief Planner which does nothing on its own. + * It requires its client must set its appointments property. */ -struct _IndicatorDatetimePlannerMock -{ - /*< private >*/ - IndicatorDatetimePlanner parent; - IndicatorDatetimePlannerMockPriv * priv; -}; - -struct _IndicatorDatetimePlannerMockClass +class MockPlanner: public Planner { - IndicatorDatetimePlannerClass parent_class; +public: + MockPlanner() =default; + virtual ~MockPlanner() =default; }; -IndicatorDatetimePlanner * indicator_datetime_planner_mock_new (void); - -G_END_DECLS +} // namespace datetime +} // namespace indicator +} // namespace unity -#endif /* __INDICATOR_DATETIME_PLANNER_MOCK__H__ */ +#endif // INDICATOR_DATETIME_PLANNER_MOCK_H diff --git a/tests/state-mock.h b/tests/state-mock.h index f2e58e6..64ab08b 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -17,8 +17,9 @@ * Charles Kerr */ +#include "planner-mock.h" + #include -#include #include using namespace unity::indicator::datetime; -- cgit v1.2.3 From 9ea8a269f3c984901e721e8bb0c796942bffb082 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 11:29:10 -0600 Subject: Remove the Timezones property from Clock; it's only needed by the subclass LiveClock --- include/datetime/clock.h | 9 ++++----- include/datetime/timezones.h | 7 ++++++- src/clock-live.cpp | 1 + src/clock.cpp | 5 ----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/datetime/clock.h b/include/datetime/clock.h index ffaf4f8..9e87082 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -21,15 +21,13 @@ #define INDICATOR_DATETIME_CLOCK_H #include -#include #include #include -#include +#include // GDBusConnection -#include -#include +#include // std::shared_ptr, std::unique_ptr namespace unity { namespace indicator { @@ -46,7 +44,6 @@ class Clock public: virtual ~Clock(); virtual DateTime localtime() const =0; - core::Property> timezones; core::Signal<> skewDetected; core::Signal<> dateChanged; @@ -70,6 +67,8 @@ private: **** ***/ +class Timezones; + /** * \brief A live clock that provides the actual system time. * diff --git a/include/datetime/timezones.h b/include/datetime/timezones.h index 10c4e97..d2842af 100644 --- a/include/datetime/timezones.h +++ b/include/datetime/timezones.h @@ -28,7 +28,12 @@ namespace unity { namespace indicator { namespace datetime { -/** \brief Aggregates one or more timezone detectors and decides which to give precedence to */ +/** + * \brief Helper class which aggregates one or more timezones + * + * @see LiveClock + * @see SettingsLocations + */ class Timezones { public: diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 1fadfe8..25623ae 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -18,6 +18,7 @@ */ #include +#include namespace unity { namespace indicator { diff --git a/src/clock.cpp b/src/clock.cpp index f3f5d70..7c3b8c7 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -34,11 +34,6 @@ Clock::Clock(): m_cancellable(g_cancellable_new()) { g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); - - timezones.changed().connect([this](const std::set& timezones){ - g_message ("timezones changed... new count is %d", (int)timezones.size()); - skewDetected(); - }); } Clock::~Clock() -- cgit v1.2.3 From e2fd6e620ce7f18c2cbf7c7b353d7cb5f86dce11 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 11:56:49 -0600 Subject: remove the local snapshot of properties-cpp and add a dependency for it in debian/control and CMakeLists.txt --- CMakeLists.txt | 1 + debian/control | 1 + include/CMakeLists.txt | 1 - include/core/CMakeLists.txt | 2 - include/core/connection.h | 162 ------------------------ include/core/property.h | 181 --------------------------- include/core/signal.h | 297 -------------------------------------------- 7 files changed, 2 insertions(+), 643 deletions(-) delete mode 100644 include/core/CMakeLists.txt delete mode 100644 include/core/connection.h delete mode 100644 include/core/property.h delete mode 100644 include/core/signal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da97a6d..bf34ebb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ pkg_check_modules (SERVICE_DEPS REQUIRED libedataserver-1.2>=3.5 libnotify>=0.7.6 url-dispatcher-1>=1 + properties-cpp>=0.0.1 json-glib-1.0>=0.16.2) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) diff --git a/debian/control b/debian/control index 357b099..3dc61c6 100644 --- a/debian/control +++ b/debian/control @@ -25,6 +25,7 @@ Build-Depends: cmake, libgnome-control-center-dev, libtimezonemap1-dev, liburl-dispatcher1-dev, + libproperties-cpp-dev, Standards-Version: 3.9.3 Homepage: https://launchpad.net/indicator-datetime # If you aren't a member of ~indicator-applet-developers but need to upload diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index cfef04b..486e9c7 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,2 +1 @@ -add_subdirectory(core) add_subdirectory(datetime) diff --git a/include/core/CMakeLists.txt b/include/core/CMakeLists.txt deleted file mode 100644 index 139597f..0000000 --- a/include/core/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/include/core/connection.h b/include/core/connection.h deleted file mode 100644 index 37b1355..0000000 --- a/include/core/connection.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright © 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser 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 warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Authored by: Thomas Voß - */ -#ifndef COM_UBUNTU_CONNECTION_H_ -#define COM_UBUNTU_CONNECTION_H_ - -#include -#include -#include - -namespace core -{ -/** - * @brief The Connection class models a signal-slot connection. - */ -class Connection -{ -public: - typedef std::function&)> Dispatcher; - - /** - * @brief Checks if this instance corresponds to an active signal-slot connection. - * @return true iff the instance corresponds to an active signal-slot connection. - */ - inline bool is_connected() const - { - return (d->disconnector ? true : false); - } - - /** - * @brief End a signal-slot connection. - */ - inline void disconnect() - { - d->disconnect(); - } - - /** - * @brief Installs a dispatcher for this signal-slot connection. - * @param dispatcher The dispatcher to be used for signal emissions. - */ - inline void dispatch_via(const Dispatcher& dispatcher) - { - if (d->dispatcher_installer) - d->dispatcher_installer(dispatcher); - } - -private: - typedef std::function Disconnector; - typedef std::function DispatcherInstaller; - - template friend class Signal; - - inline Connection(const Disconnector& disconnector, - const DispatcherInstaller& installer) - : d(std::make_shared(disconnector, installer)) - { - } - - inline void reset() - { - d->reset(); - } - - struct Private - { - Private(const Connection::Disconnector& disconnector_, - const Connection::DispatcherInstaller& dispatcher_installer_) - : disconnector(disconnector_), - dispatcher_installer(dispatcher_installer_) - { - } - - inline void reset() - { - std::lock_guard lg(guard); - reset_locked(); - } - - inline void reset_locked() - { - static const Connection::Disconnector empty_disconnector{}; - static const Connection::DispatcherInstaller empty_dispatcher_installer{}; - - disconnector = empty_disconnector; - dispatcher_installer = empty_dispatcher_installer; - } - - inline void disconnect() - { - static const Connection::Disconnector empty_disconnector{}; - - std::lock_guard lg(guard); - - if (disconnector) - disconnector(); - - reset_locked(); - } - - std::mutex guard; - Connection::Disconnector disconnector; - Connection::DispatcherInstaller dispatcher_installer; - }; - - // The whole class is implicitly shared and we thus forward our complete - // shared state to a private structure that is lifetime-managed by a shared_ptr. - std::shared_ptr d; -}; - -/** - * @brief Scoped helper class to map signal-slot connection mgmt. to RAII. - */ -class ScopedConnection -{ -public: - /** - * @brief Constructs an instance for an existing signal-slot connection. - * @param c The existing signal-slot connection. - */ - inline ScopedConnection(const Connection& c) : connection(c) - { - } - - ScopedConnection(const ScopedConnection&) = delete; - - /** - * @brief Disconnects the signal-slot connection. - */ - inline ~ScopedConnection() noexcept(true) - { - try - { - connection.disconnect(); - } catch(...) - { - } - } - - ScopedConnection& operator=(const ScopedConnection&) = delete; - bool operator==(const ScopedConnection&) = delete; - -private: - Connection connection; -}; -} - -#endif // COM_UBUNTU_CONNECTION_H_ diff --git a/include/core/property.h b/include/core/property.h deleted file mode 100644 index 996ba8a..0000000 --- a/include/core/property.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright © 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser 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 warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Authored by: Thomas Voß - */ -#ifndef CORE_PROPERTY_H_ -#define CORE_PROPERTY_H_ - -#include - -#include - -namespace core -{ -/** - * @brief A very simple, templated class that allows for uniform declaration of get-able/set-able/observable members. - * @tparam The type of the value contained within the property. - */ -template -class Property -{ - public: - /** - * @brief ValueType refers to the type of the contained value. - */ - typedef T ValueType; - - /** - * @brief Property creates a new instance of property and initializes the contained value. - * @param t The initial value, defaults to Property::default_value(). - */ - inline explicit Property(const T& t = T{}) : value{t} - { - } - - /** - * @brief Copy c'tor, only copies the contained value, not the changed signal and its connections. - * @param rhs - */ - inline Property(const Property& rhs) : value{rhs.value} - { - } - - inline virtual ~Property() = default; - - /** - * @brief Assignment operator, only assigns to the contained value. - * @param rhs The right-hand-side, raw value to assign to this property. - */ - inline Property& operator=(const T& rhs) - { - set(rhs); - return *this; - } - - /** - * @brief Assignment operator, only assigns to the contained value, not the changed signal and its connections. - * @param rhs The right-hand-side property to assign from. - */ - inline Property& operator=(const Property& rhs) - { - set(rhs.value); - return *this; - } - - /** - * @brief Explicit casting operator to the contained value type. - * @return A non-mutable reference to the contained value. - */ - inline operator const T&() const - { - return get(); - } - - /** - * @brief Provides access to a pointer to the contained value. - */ - inline const T* operator->() const - { - return &get(); - } - - /** - * @brief operator == checks if the value of a property and a raw value are equal. - * @param lhs Non-mutable reference to a property. - * @param rhs Non-mutable reference to a raw value. - * @return True iff the value contained in lhs equals rhs. - */ - friend inline bool operator==(const Property& lhs, const T& rhs) - { - return lhs.get() == rhs; - } - - /** - * @brief operator == checks if the value of two properties are equal. - * @param lhs Non-mutable reference to a property. - * @param rhs Non-mutable reference to a property. - * @return True iff the value contained in lhs equals the value contained in rhs. - */ - friend inline bool operator==(const Property& lhs, const Property& rhs) - { - return lhs.get() == rhs.get(); - } - - /** - * @brief Set the contained value to the provided value. Notify observers of the change. - * @param [in] new_value The new value to assign to this property. - * @post get() == new_value; - */ - inline virtual void set(const T& new_value) - { - if (value != new_value) - { - value = new_value; - signal_changed(value); - } - } - - /** - * @brief Access the value contained within this property. - * @return A non-mutable reference to the property value. - */ - inline virtual const T& get() const - { - return value; - } - - /** - * @brief Access to the changed signal, allows observers to subscribe to change notifications. - * @return A non-mutable reference to the changed signal. - */ - inline const Signal& changed() const - { - return signal_changed; - } - - /** - * @brief Provides in-place update facilities. - * - * The provided update_functor is applied to the contained value. If the update functor - * returns true, indicating that the value has been changed, the changed signal is emitted. - * - * @param update_functor The update function to be applied to the contained value. - * @return true iff application of the update functor has been successful. - */ - inline virtual bool update(const std::function& update_functor) - { - if (update_functor(mutable_get())) - { - signal_changed(value); - return true; - } - - return false; - } - - protected: - inline T& mutable_get() const - { - return value; - } - - private: - mutable T value; - Signal signal_changed; -}; -} - -#endif // CORE_PROPERTY_H_ diff --git a/include/core/signal.h b/include/core/signal.h deleted file mode 100644 index be2984b..0000000 --- a/include/core/signal.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright © 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser 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 warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Authored by: Thomas Voß - */ -#ifndef COM_UBUNTU_SIGNAL_H_ -#define COM_UBUNTU_SIGNAL_H_ - -#include - -#include -#include -#include -#include -#include - -namespace core -{ -/** - * @brief A signal class that observers can subscribe to. - * @tparam Arguments List of argument types passed on to observers when the signal is emitted. - */ -template -class Signal -{ -public: - /** - * @brief Slot is the function type that observers have to provide to connect to this signal. - */ - typedef std::function Slot; - -private: - struct SlotWrapper - { - void operator()(Arguments... args) - { - dispatcher(std::bind(slot, args...)); - } - - Slot slot; - Connection::Dispatcher dispatcher; - Connection connection; - }; - -public: - /** - * @brief Signal constructs a new instance. Never throws. - */ - inline Signal() noexcept(true) : d(new Private()) - { - } - - inline ~Signal() - { - std::lock_guard lg(d->guard); - for (auto slot : d->slots) - slot.connection.reset(); - } - - // Copy construction, assignment and equality comparison are disabled. - Signal(const Signal&) = delete; - Signal& operator=(const Signal&) = delete; - bool operator==(const Signal&) const = delete; - - /** - * @brief Connects the provided slot to this signal instance. - * - * Calling this method is thread-safe and synchronized with any - * other connect, signal emission or disconnect calls. - * - * @param slot The function to be called when the signal is emitted. - * @return A connection object corresponding to the signal-slot connection. - */ - inline Connection connect(const Slot& slot) const - { - // Helpers to initialize an invalid connection. - static const Connection::Disconnector empty_disconnector{}; - static const Connection::DispatcherInstaller empty_dispatcher_installer{}; - - // The default dispatcher immediately executes the function object - // provided as argument on whatever thread is currently running. - static const Connection::Dispatcher default_dispatcher - = [](const std::function& handler) { handler(); }; - - Connection conn{empty_disconnector, empty_dispatcher_installer}; - - std::lock_guard lg(d->guard); - - auto result = d->slots.insert( - d->slots.end(), - SlotWrapper{slot, default_dispatcher, conn}); - - // We implicitly share our internal state with the connection here - // by passing in our private bits contained in 'd' to the std::bind call. - // This admittedly uncommon approach allows us to cleanly manage connection - // and signal lifetimes without the need to mark everything as mutable. - conn.d->disconnector = std::bind( - &Private::disconnect_slot_for_iterator, - d, - result); - conn.d->dispatcher_installer = std::bind( - &Private::install_dispatcher_for_iterator, - d, - std::placeholders::_1, - result); - - return conn; - } - - /** - * @brief operator () emits the signal with the provided parameters. - * - * Please note that signal emissions might not be delivered immediately to - * registered slots, depending on whether the respective connection is dispatched - * via a queueing dispatcher. For that reason, the lifetime of the arguments has to - * exceed the scope of the call to this operator and its surrounding scope. - * - * @param args The arguments to be passed on to registered slots. - */ - inline void operator()(Arguments... args) - { - std::lock_guard lg(d->guard); - for(auto slot : d->slots) - { - slot(args...); - } - } - -private: - struct Private - { - typedef std::list SlotContainer; - - inline void disconnect_slot_for_iterator(typename SlotContainer::iterator it) - { - std::lock_guard lg(guard); - slots.erase(it); - } - - inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, - typename SlotContainer::iterator it) - { - std::lock_guard lg(guard); - it->dispatcher = dispatcher; - } - - std::mutex guard; - SlotContainer slots; - }; - std::shared_ptr d; -}; - -/** - * @brief A signal class that observers can subscribe to, - * template specialization for signals without arguments. - */ -template<> -class Signal -{ -public: - /** - * @brief Slot is the function type that observers have to provide to connect to this signal. - */ - typedef std::function Slot; - -private: - struct SlotWrapper - { - void operator()() - { - dispatcher(slot); - } - - Slot slot; - Connection::Dispatcher dispatcher; - Connection connection; - }; - -public: - /** - * @brief Signal constructs a new instance. Never throws. - */ - inline Signal() noexcept(true) : d(new Private()) - { - } - - inline ~Signal() - { - std::lock_guard lg(d->guard); - for (auto slot : d->slots) - slot.connection.reset(); - } - - // Copy construction, assignment and equality comparison are disabled. - Signal(const Signal&) = delete; - Signal& operator=(const Signal&) = delete; - bool operator==(const Signal&) const = delete; - - /** - * @brief Connects the provided slot to this signal instance. - * - * Calling this method is thread-safe and synchronized with any - * other connect, signal emission or disconnect calls. - * - * @param slot The function to be called when the signal is emitted. - * @return A connection object corresponding to the signal-slot connection. - */ - inline Connection connect(const Slot& slot) const - { - // Helpers to initialize an invalid connection. - static const Connection::Disconnector empty_disconnector{}; - static const Connection::DispatcherInstaller empty_dispatcher_installer{}; - - // The default dispatcher immediately executes the function object - // provided as argument on whatever thread is currently running. - static const Connection::Dispatcher default_dispatcher - = [](const std::function& handler) { handler(); }; - - Connection conn{empty_disconnector, empty_dispatcher_installer}; - - std::lock_guard lg(d->guard); - - auto result = d->slots.insert( - d->slots.end(), - SlotWrapper{slot, default_dispatcher, conn}); - - // We implicitly share our internal state with the connection here - // by passing in our private bits contained in 'd' to the std::bind call. - // This admittedly uncommon approach allows us to cleanly manage connection - // and signal lifetimes without the need to mark everything as mutable. - conn.d->disconnector = std::bind( - &Private::disconnect_slot_for_iterator, - d, - result); - conn.d->dispatcher_installer = std::bind( - &Private::install_dispatcher_for_iterator, - d, - std::placeholders::_1, - result); - - return conn; - } - - /** - * @brief operator () emits the signal. - * - * Please note that signal emissions might not be delivered immediately to - * registered slots, depending on whether the respective connection is dispatched - * via a queueing dispatcher. - */ - inline void operator()() - { - std::lock_guard lg(d->guard); - for(auto slot : d->slots) - { - slot(); - } - } - -private: - struct Private - { - typedef std::list SlotContainer; - - inline void disconnect_slot_for_iterator(typename SlotContainer::iterator it) - { - std::lock_guard lg(guard); - slots.erase(it); - } - - inline void install_dispatcher_for_iterator(const Connection::Dispatcher& dispatcher, - typename SlotContainer::iterator it) - { - std::lock_guard lg(guard); - it->dispatcher = dispatcher; - } - - std::mutex guard; - SlotContainer slots; - }; - std::shared_ptr d; -}; -} - -#endif // COM_UBUNTU_SIGNAL_H_ -- cgit v1.2.3 From aad7e86a109aeec75b3772cda20478363f966745 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 14:28:20 -0600 Subject: Alarms is going to need to know when the clock's minute changes. We already have a timer for that in Formatter, so move it from there to Clock and add a corresponding public signal Clock.minuteChanged that both Formatter and Alarms can use. Sync unit tests. --- include/datetime/clock-mock.h | 8 +-- include/datetime/clock.h | 20 ++++---- include/datetime/date-time.h | 5 +- src/clock-live.cpp | 113 ++++++++++++++++++++++++------------------ src/clock.cpp | 2 +- src/date-time.cpp | 65 ++++++++++-------------- src/formatter.cpp | 106 ++++++++++++++++++--------------------- tests/test-clock.cpp | 32 ++++++------ 8 files changed, 169 insertions(+), 182 deletions(-) diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 19a859b..27926ff 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -42,11 +42,11 @@ public: DateTime localtime() const { return m_localtime; } void set_localtime(const DateTime& dt) { - const auto old_day = m_localtime.day_of_year(); + const auto old = m_localtime; m_localtime = dt; - skewDetected(); - const auto new_day = m_localtime.day_of_year(); - if (old_day != new_day) + if (!DateTime::is_same_minute(old, m_localtime)) + minuteChanged(); + if (!DateTime::is_same_day(old, m_localtime)) dateChanged(); } diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 9e87082..b3e3538 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -35,21 +35,25 @@ namespace datetime { /** * \brief A clock. - * - * Provides a signal to notify when clock skew is detected, such as - * when the timezone changes or when the system resumes from sleep. */ class Clock { public: virtual ~Clock(); virtual DateTime localtime() const =0; - core::Signal<> skewDetected; + + /** \brief A signal which fires when the clock's minute changes */ + core::Signal<> minuteChanged; + + /** \brief A signal which fires when the clock's date changes */ core::Signal<> dateChanged; protected: Clock(); + /** \brief Compares old and new times, emits minuteChanged() or dateChanged() signals if appropriate */ + void maybe_emit (const DateTime& a, const DateTime& b); + private: static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); @@ -70,12 +74,7 @@ private: class Timezones; /** - * \brief A live clock that provides the actual system time. - * - * 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. + * \brief A live #Clock that provides the actual system time. */ class LiveClock: public Clock { @@ -83,7 +82,6 @@ public: LiveClock (const std::shared_ptr& zones); virtual ~LiveClock(); virtual DateTime localtime() const; - core::Property skewTestIntervalSec; private: class Impl; diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index 145e34e..c2429eb 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -49,13 +49,14 @@ public: std::string format(const std::string& fmt) const; int day_of_month() const; int64_t to_unix() const; - int day_of_year() const; - gint64 difference(const DateTime& that) const; bool operator<(const DateTime& that) const; bool operator!=(const DateTime& that) const; bool operator==(const DateTime& that) const; + static bool is_same_day(const DateTime& a, const DateTime& b); + static bool is_same_minute(const DateTime& a, const DateTime& b); + private: std::shared_ptr m_dt; }; diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 25623ae..69ebda7 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -24,6 +24,37 @@ namespace unity { namespace indicator { namespace datetime { +/*** +**** +***/ + +namespace +{ + +void clearTimer(guint& tag) +{ + if (tag) + { + g_source_remove(tag); + tag = 0; + } +} + +guint calculate_milliseconds_until_next_minute(const DateTime& now) +{ + auto next = g_date_time_add_minutes(now.get(), 1); + auto start_of_next = g_date_time_add_seconds (next, -g_date_time_get_seconds(next)); + const auto interval_usec = g_date_time_difference(start_of_next, now.get()); + const guint interval_msec = (interval_usec + 999) / 1000; + g_date_time_unref(start_of_next); + g_date_time_unref(next); + g_assert (interval_msec <= 60000); + return interval_msec; +} + +} // unnamed namespace + + class LiveClock::Impl { public: @@ -38,13 +69,12 @@ public: setTimezone(m_timezones->timezone.get()); } - m_owner.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); - setInterval(m_owner.skewTestIntervalSec.get()); + restart_minute_timer(); } ~Impl() { - clearTimer(); + clearTimer(m_timer); g_clear_pointer(&m_timezone, g_time_zone_unref); } @@ -64,70 +94,55 @@ private: void setTimezone(const std::string& str) { 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 (m_skew_timeout_id) - { - g_source_remove(m_skew_timeout_id); - m_skew_timeout_id = 0; - } - - m_prev_datetime.reset(); + m_timezone = g_time_zone_new(str.c_str()); + m_owner.minuteChanged(); } - void setInterval(unsigned int seconds) - { - clearTimer(); - - if (seconds > 0) - { - m_prev_datetime = localtime(); - m_skew_timeout_id = g_timeout_add_seconds(seconds, onTimerPulse, this); - } - } + /*** + **** + ***/ - static gboolean onTimerPulse(gpointer gself) + void restart_minute_timer() { - static_cast(gself)->onTimerPulse(); - return G_SOURCE_CONTINUE; - } + clearTimer(m_timer); - void onTimerPulse() - { - // check to see if too much time passed since the last check */ + // maybe emit change signals const auto now = localtime(); - const auto diff = now.difference (m_prev_datetime); - const GTimeSpan fuzz = 5; - const GTimeSpan max = (m_owner.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; - if (abs(diff) > max) - m_owner.skewDetected(); - - // check to see if the day has changed - if (now.day_of_year() != m_prev_datetime.day_of_year()) + if (!DateTime::is_same_minute(m_prev_datetime, now)) + m_owner.minuteChanged(); + if (!DateTime::is_same_day(m_prev_datetime, now)) m_owner.dateChanged(); - // update m_prev_datetime + // queue up a timer to fire at the next minute m_prev_datetime = now; + auto interval_msec = calculate_milliseconds_until_next_minute(now); + interval_msec += 50; // add a small margin to ensure the callback + // fires /after/ next is reached + m_timer = g_timeout_add_full(G_PRIORITY_HIGH, + interval_msec, + on_minute_timer_reached, + this, + nullptr); + } + + static gboolean on_minute_timer_reached(gpointer gself) + { + static_cast(gself)->restart_minute_timer(); + return G_SOURCE_REMOVE; } protected: LiveClock& m_owner; - GTimeZone * m_timezone = nullptr; + GTimeZone* m_timezone = nullptr; std::shared_ptr m_timezones; DateTime m_prev_datetime; - unsigned int m_skew_timeout_id = 0; + unsigned int m_timer = 0; }; LiveClock::LiveClock(const std::shared_ptr& tzd): - p(new Impl(*this, tzd)) + p(new Impl(*this, tzd)) { } @@ -138,6 +153,10 @@ DateTime LiveClock::localtime() const return p->localtime(); } +/*** +**** +***/ + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/clock.cpp b/src/clock.cpp index 7c3b8c7..d5293cc 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -81,7 +81,7 @@ Clock::onPrepareForSleep(GDBusConnection* /*connection*/, GVariant* /*parameters*/, gpointer gself) { - static_cast(gself)->skewDetected(); + static_cast(gself)->minuteChanged(); } /*** diff --git a/src/date-time.cpp b/src/date-time.cpp index 3842ac0..40c638f 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -76,11 +76,6 @@ int64_t DateTime::to_unix() const return g_date_time_to_unix(get()); } -int DateTime::day_of_year() const -{ - return m_dt ? g_date_time_get_day_of_year(get()) : -1; -} - void DateTime::reset(GDateTime* in) { if (in) @@ -95,39 +90,6 @@ void DateTime::reset(GDateTime* in) } } -#if 0 -DateTime& DateTime::operator=(GDateTime* in) -{ - reset(in); - return *this; -} - -DateTime& DateTime::operator=(const DateTime& in) -{ - m_dt = in.m_dt; - return *this; -} -#endif - -gint64 DateTime::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 DateTime::operator<(const DateTime& that) const { return g_date_time_compare(get(), that.get()) < 0; @@ -141,13 +103,36 @@ bool DateTime::operator!=(const DateTime& that) const bool DateTime::operator==(const DateTime& that) const { - GDateTime * dt = get(); - GDateTime * tdt = that.get(); + auto dt = get(); + auto tdt = that.get(); if (!dt && !tdt) return true; if (!dt || !tdt) return false; return g_date_time_compare(get(), that.get()) == 0; } +bool DateTime::is_same_day(const DateTime& a, const DateTime& b) +{ + // it's meaningless to compare uninitialized dates + if (!a.m_dt || !b.m_dt) + return false; + + const auto adt = a.get(); + const auto bdt = b.get(); + return (g_date_time_get_year(adt) == g_date_time_get_year(bdt)) + && (g_date_time_get_day_of_year(adt) == g_date_time_get_day_of_year(bdt)); +} + +bool DateTime::is_same_minute(const DateTime& a, const DateTime& b) +{ + if (!is_same_day(a,b)) + return false; + + const auto adt = a.get(); + const auto bdt = b.get(); + return (g_date_time_get_hour(adt) == g_date_time_get_hour(bdt)) + && (g_date_time_get_minute(adt) == g_date_time_get_minute(bdt)); +} + /*** **** ***/ diff --git a/src/formatter.cpp b/src/formatter.cpp index 88a64df..a271ba0 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -28,9 +28,18 @@ #include // nl_langinfo() #include // strstr() +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + namespace { -void clearTimer(guint& tag) + +void clear_timer(guint& tag) { if (tag) { @@ -39,35 +48,12 @@ void clearTimer(guint& tag) } } -guint calculate_milliseconds_until_next_minute(GDateTime * now) -{ - GDateTime * next; - GDateTime * start_of_next; - GTimeSpan interval_usec; - 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); - - interval_usec = g_date_time_difference(start_of_next, now); - interval_msec = (interval_usec + 999) / 1000; - - g_date_time_unref(start_of_next); - g_date_time_unref(next); - return interval_msec; -} - -gint calculate_milliseconds_until_next_second(GDateTime * now) +gint calculate_milliseconds_until_next_second(const DateTime& 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.get()); interval_msec = (interval_usec + 999) / 1000; return interval_msec; } @@ -127,11 +113,6 @@ guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) } // unnamed namespace - -namespace unity { -namespace indicator { -namespace datetime { - class Formatter::Impl { public: @@ -140,57 +121,64 @@ public: m_owner(owner), m_clock(clock) { - m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){updateHeader();}); - m_clock->skewDetected.connect([this](){updateHeader();}); - updateHeader(); + m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){update_header();}); + m_clock->minuteChanged.connect([this](){update_header();}); + update_header(); restartRelativeTimer(); } ~Impl() { - clearTimer(m_header_timer); + clear_timer(m_header_seconds_timer); + clear_timer(m_relative_timer); } private: - void updateHeader() + static bool format_shows_seconds(const std::string& fmt) { + return (fmt.find("%s") != std::string::npos) + || (fmt.find("%S") != std::string::npos) + || (fmt.find("%T") != std::string::npos) + || (fmt.find("%X") != std::string::npos) + || (fmt.find("%c") != std::string::npos); + } + + void update_header() + { + // update the header property const auto fmt = m_owner->headerFormat.get(); const auto str = m_clock->localtime().format(fmt); m_owner->header.set(str); - restartHeaderTimer(); + // if the header needs to show seconds, set a timer. + if (format_shows_seconds(fmt)) + start_header_timer(); + else + clear_timer(m_header_seconds_timer); } - void restartHeaderTimer() + // we've got a header format that shows seconds, + // so we need to update it every second + void start_header_timer() { - clearTimer(m_header_timer); + clear_timer(m_header_seconds_timer); - const auto 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) - || (fmt.find("%X") != std::string::npos) - || (fmt.find("%c") != std::string::npos); - - guint interval_msec; const auto now = m_clock->localtime(); - auto str = now.format("%F %T"); - if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second(now.get()); - else - interval_msec = calculate_milliseconds_until_next_minute(now.get()); - + auto interval_msec = calculate_milliseconds_until_next_second(now); interval_msec += 50; // add a small margin to ensure the callback // fires /after/ next is reached - - m_header_timer = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + m_header_seconds_timer = g_timeout_add_full(G_PRIORITY_HIGH, + interval_msec, + on_header_timer, + this, + nullptr); } - static gboolean onHeaderTimer(gpointer gself) + static gboolean on_header_timer(gpointer gself) { - static_cast(gself)->updateHeader(); + static_cast(gself)->update_header(); return G_SOURCE_REMOVE; } @@ -198,7 +186,7 @@ private: void restartRelativeTimer() { - clearTimer(m_relative_timer); + clear_timer(m_relative_timer); const auto now = m_clock->localtime(); const auto seconds = calculate_seconds_until_next_fifteen_minutes(now.get()); @@ -215,7 +203,7 @@ private: private: Formatter* const m_owner; - guint m_header_timer = 0; + guint m_header_seconds_timer = 0; guint m_relative_timer = 0; public: diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index 142ccad..4271374 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -46,28 +46,24 @@ class ClockFixture: public TestDBusFixture } }; -/** - * 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, MinuteChangedSignalShouldTriggerOncePerMinute) { + // start up a live clock std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); wait_msec(500); // wait for the bus to set up - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 3; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); + // count how many times clock.minuteChanged() is emitted over the next minute + const DateTime now = clock.localtime(); + const auto gnow = now.get(); + auto gthen = g_date_time_add_minutes(gnow, 1); + int count = 0; + clock.minuteChanged.connect([&count](){count++;}); + const auto msec = g_date_time_difference(gthen,gnow) / 1000; + wait_msec(msec); + EXPECT_EQ(1, count); + g_date_time_unref(gthen); } /*** @@ -99,7 +95,7 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_time_zone_unref(tz_nyc); /// change the timezones! - clock.skewDetected.connect([this](){ + clock.minuteChanged.connect([this](){ g_main_loop_quit(loop); }); g_idle_add([](gpointer gs){ @@ -128,7 +124,7 @@ TEST_F(ClockFixture, SleepTriggersSkew) wait_msec(500); // wait for the bus to set up bool skewed = false; - clock.skewDetected.connect([&skewed, this](){ + clock.minuteChanged.connect([&skewed, this](){ skewed = true; g_main_loop_quit(loop); return G_SOURCE_REMOVE; -- cgit v1.2.3 From 7b09a0ff5652bdca7c8d8e046d2af6a696f94147 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 25 Jan 2014 16:54:41 -0600 Subject: sync the exported calendar state with the #State backend --- include/datetime/actions.h | 4 +- include/datetime/state.h | 2 - src/actions-live.cpp | 5 -- src/actions.cpp | 30 +++++++++++- src/state-live.cpp | 1 - tests/actions-mock.h | 8 +--- tests/state-mock.h | 1 - tests/test-actions.cpp | 19 +++++--- tests/test-live-actions.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 157 insertions(+), 27 deletions(-) diff --git a/include/datetime/actions.h b/include/datetime/actions.h index 18286dc..3686b95 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -49,8 +49,7 @@ public: 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; - + void set_calendar_date(const DateTime&); GActionGroup* action_group() { return G_ACTION_GROUP(m_actions); } std::shared_ptr state() { return m_state; } @@ -61,6 +60,7 @@ protected: private: std::shared_ptr m_state; GSimpleActionGroup* m_actions = nullptr; + void update_calendar_state(); // we've got raw pointers in here, so disable copying Actions(const Actions&) =delete; diff --git a/include/datetime/state.h b/include/datetime/state.h index b14908e..414be32 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -66,8 +66,6 @@ struct State /** \brief Configuration options that modify the view */ std::shared_ptr settings; - - core::Property calendar_day; }; } // namespace datetime diff --git a/src/actions-live.cpp b/src/actions-live.cpp index e4f5e9f..d5f7180 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -96,11 +96,6 @@ void LiveActions::open_appointment(const std::string& uid) } } -void LiveActions::set_calendar_date(const DateTime& dt) -{ - state()->calendar_day.set(dt); -} - /*** **** ***/ diff --git a/src/actions.cpp b/src/actions.cpp index 1d80c99..a6a7c0b 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -150,7 +150,7 @@ GVariant* create_calendar_state(const std::shared_ptr& state) g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "calendar-day"; - v = g_variant_new_int64(state->calendar_day.get().to_unix()); + v = g_variant_new_int64(state->planner->time.get().to_unix()); g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "show-week-numbers"; @@ -200,7 +200,20 @@ Actions::Actions(const std::shared_ptr& 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; + + /// + /// Keep our GActionGroup's action's states in sync with m_state + /// + + m_state->planner->time.changed().connect([this](const DateTime&){ + update_calendar_state(); + }); + m_state->planner->thisMonth.changed().connect([this](const std::vector&){ + update_calendar_state(); + }); + m_state->settings->show_week_numbers.changed().connect([this](bool){ + update_calendar_state(); + }); // FIXME: rebuild the calendar state when show-week-number changes } @@ -210,6 +223,19 @@ Actions::~Actions() g_clear_object(&m_actions); } +void Actions::update_calendar_state() +{ + g_action_group_change_action_state(action_group(), + "calendar", + create_calendar_state(m_state)); +} + +void Actions::set_calendar_date(const DateTime& date) +{ + m_state->planner->time.set(date); +} + + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/state-live.cpp b/src/state-live.cpp index 8ee663b..fe1e6cd 100644 --- a/src/state-live.cpp +++ b/src/state-live.cpp @@ -45,7 +45,6 @@ LiveState::LiveState() locations.reset(new SettingsLocations(live_settings, live_timezones)); planner.reset(new PlannerEds); planner->time = clock->localtime(); - calendar_day = clock->localtime(); } /*** diff --git a/tests/actions-mock.h b/tests/actions-mock.h index 112900b..da93cb9 100644 --- a/tests/actions-mock.h +++ b/tests/actions-mock.h @@ -35,8 +35,7 @@ public: ~MockActions() =default; enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp, - OpenPlanner, OpenPlannerAt, OpenAppointment, - SetLocation, SetCalendarDate }; + OpenPlanner, OpenPlannerAt, OpenAppointment, SetLocation }; const std::vector& history() const { return m_history; } const DateTime& date_time() const { return m_date_time; } const std::string& zone() const { return m_zone; } @@ -68,11 +67,6 @@ public: 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; diff --git a/tests/state-mock.h b/tests/state-mock.h index 64ab08b..721b82f 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -38,7 +38,6 @@ public: planner.reset(new MockPlanner); planner->time = now; locations.reset(new Locations); - calendar_day = now; } }; diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp index 4329608..c30d1fb 100644 --- a/tests/test-actions.cpp +++ b/tests/test-actions.cpp @@ -137,18 +137,23 @@ TEST_F(ActionsFixture, SetLocation) TEST_F(ActionsFixture, SetCalendarDate) { + // confirm that such an action exists 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()); + // pick an arbitrary DateTime... + auto tmp = g_date_time_new_local(2010, 1, 2, 3, 4, 5); + const auto now = DateTime(tmp); + g_date_time_unref(tmp); + + // confirm that Planner.time gets changed to that date when we + // activate the 'calendar' action with that date's time_t as the arg + EXPECT_NE (now, m_state->planner->time.get()); + auto v = g_variant_new_int64(now.to_unix()); + g_action_group_activate_action (action_group, action_name, v); + EXPECT_EQ (now, m_state->planner->time.get()); } TEST_F(ActionsFixture, OpenAppointment) diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp index d3d7720..562b358 100644 --- a/tests/test-live-actions.cpp +++ b/tests/test-live-actions.cpp @@ -287,3 +287,117 @@ TEST_F(LiveActionsFixture, OpenPlannerAt) const std::string expected = now.format("evolution \"calendar:///?startdate=%Y%m%d\""); EXPECT_EQ(expected, m_live_actions->last_cmd); } + +TEST_F(LiveActionsFixture, CalendarState) +{ + // init the clock + auto tmp = g_date_time_new_local (2014, 1, 1, 0, 0, 0); + const DateTime now (tmp); + g_date_time_unref (tmp); + m_mock_state->mock_clock->set_localtime (now); + m_state->planner->time.set(now); + + /// + /// Test the default calendar state. + /// + + auto action_group = m_actions->action_group(); + auto calendar_state = g_action_group_get_action_state (action_group, "calendar"); + EXPECT_TRUE (calendar_state != nullptr); + EXPECT_TRUE (g_variant_is_of_type (calendar_state, G_VARIANT_TYPE_DICTIONARY)); + + // there's nothing in the planner yet, so appointment-days should be an empty array + auto v = g_variant_lookup_value (calendar_state, "appointment-days", G_VARIANT_TYPE_ARRAY); + EXPECT_TRUE (v != nullptr); + EXPECT_EQ (0, g_variant_n_children (v)); + g_clear_pointer (&v, g_variant_unref); + + // calendar-day should be in sync with m_state->calendar_day + v = g_variant_lookup_value (calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE (v != nullptr); + EXPECT_EQ (m_state->planner->time.get().to_unix(), g_variant_get_int64(v)); + g_clear_pointer (&v, g_variant_unref); + + // show-week-numbers should be false because MockSettings defaults everything to 0 + v = g_variant_lookup_value (calendar_state, "show-week-numbers", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE (v != nullptr); + EXPECT_FALSE (g_variant_get_boolean (v)); + g_clear_pointer (&v, g_variant_unref); + + // cleanup this step + g_clear_pointer (&calendar_state, g_variant_unref); + + + /// + /// Now add appointments to the planner and confirm that the state keeps in sync + /// + + auto tomorrow = g_date_time_add_days (now.get(), 1); + auto tomorrow_begin = g_date_time_add_full (tomorrow, 0, 0, 0, + -g_date_time_get_hour(tomorrow), + -g_date_time_get_minute(tomorrow), + -g_date_time_get_seconds(tomorrow)); + auto tomorrow_end = g_date_time_add_full (tomorrow_begin, 0, 0, 1, 0, 0, -1); + Appointment a1; + a1.color = "green"; + a1.summary = "write unit tests"; + a1.url = "http://www.ubuntu.com/"; + a1.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; + a1.begin = tomorrow_begin; + a1.end = tomorrow_end; + + auto next_begin = g_date_time_add_days (tomorrow_begin, 1); + auto next_end = g_date_time_add_full (next_begin, 0, 0, 1, 0, 0, -1); + Appointment a2; + a2.color = "orange"; + a2.summary = "code review"; + a2.url = "http://www.ubuntu.com/"; + a2.uid = "2756ff7de3745bbffd65d2e4779c37c7ca60d843"; + a2.begin = next_begin; + a2.end = next_end; + + m_state->planner->thisMonth.set(std::vector({a1, a2})); + + /// + /// Now test the calendar state again. + /// The thisMonth field should now contain the appointments we just added. + /// + + calendar_state = g_action_group_get_action_state (action_group, "calendar"); + v = g_variant_lookup_value (calendar_state, "appointment-days", G_VARIANT_TYPE_ARRAY); + EXPECT_TRUE (v != nullptr); + int i; + g_variant_get_child (v, 0, "i", &i); + EXPECT_EQ (g_date_time_get_day_of_month(a1.begin.get()), i); + g_variant_get_child (v, 1, "i", &i); + EXPECT_EQ (g_date_time_get_day_of_month(a2.begin.get()), i); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + + // cleanup this step + g_date_time_unref (next_end); + g_date_time_unref (next_begin); + g_date_time_unref (tomorrow_end); + g_date_time_unref (tomorrow_begin); + g_date_time_unref (tomorrow); + + /// + /// Confirm that the action state's dictionary + /// keeps in sync with settings.show_week_numbers + /// + + auto b = m_state->settings->show_week_numbers.get(); + for (i=0; i<2; i++) + { + b = !b; + m_state->settings->show_week_numbers.set(b); + + calendar_state = g_action_group_get_action_state (action_group, "calendar"); + v = g_variant_lookup_value (calendar_state, "show-week-numbers", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(b, g_variant_get_boolean(v)); + + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + } +} -- cgit v1.2.3 From c1ffb9cd6082acb7aa7e820a7ed852adc7ae648d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:24:20 -0600 Subject: re-enable coverage-html reports --- cmake/GCov.cmake | 3 ++- src/CMakeLists.txt | 9 +-------- trim-lcov.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) create mode 100755 trim-lcov.py diff --git a/cmake/GCov.cmake b/cmake/GCov.cmake index 8df10ee..81c0c40 100644 --- a/cmake/GCov.cmake +++ b/cmake/GCov.cmake @@ -29,7 +29,8 @@ if (CMAKE_BUILD_TYPE MATCHES coverage) WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND "${CMAKE_CTEST_COMMAND}" --force-new-ctest-process --verbose COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture | ${CMAKE_SOURCE_DIR}/trim-lcov.py > dconf-lcov.info - COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details dconf-lcov.info + COMMAND "${LCOV_EXECUTABLE}" -r dconf-lcov.info /usr/include/\\* -o nosys-lcov.info + COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details nosys-lcov.info COMMAND ${CMAKE_COMMAND} -E echo "" COMMAND ${CMAKE_COMMAND} -E echo "file://${CMAKE_BINARY_DIR}/lcov-html/index.html" COMMAND ${CMAKE_COMMAND} -E echo "") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a90fcbf..b8c62fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") @@ -33,14 +33,7 @@ add_library (${SERVICE_LIB} STATIC include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) - 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} -# APPEND_STRING PROPERTY COMPILE_FLAGS -# " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") - - diff --git a/trim-lcov.py b/trim-lcov.py new file mode 100755 index 0000000..78613d3 --- /dev/null +++ b/trim-lcov.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +# This script removes branch and/or line coverage data for lines that +# contain a particular substring. +# +# In the interest of "fairness" it removes all branch or coverage data +# when a match is found -- not just negative data. It is therefore +# likely that running this script will actually reduce the total number +# of lines and branches that are marked as covered (in absolute terms). +# +# This script intentionally avoids checking for errors. Any exceptions +# will trigger make to fail. +# +# Author: Ryan Lortie + +import sys + +line_suppress = ['g_assert_not_reached'] +branch_suppress = ['g_assert', 'g_return_if_fail', 'g_clear_object', 'g_clear_pointer', 'g_return_val_if_fail', 'G_DEFINE_TYPE'] + +def check_suppress(suppressions, source, data): + line, _, rest = data.partition(',') + line = int(line) - 1 + + assert line < len(source) + + for suppression in suppressions: + if suppression in source[line]: + return True + + return False + +source = [] +for line in sys.stdin: + line = line[:-1] + + keyword, _, rest = line.partition(':') + + # Source file + if keyword == 'SF': + source = file(rest).readlines() + + # Branch coverage data + elif keyword == 'BRDA': + if check_suppress(branch_suppress, source, rest): + continue + + # Line coverage data + elif keyword == 'DA': + if check_suppress(line_suppress, source, rest): + continue + + print line -- cgit v1.2.3 From a268daf59f5b3a8ccd99f2b821a4c7cbcca3ccbc Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:25:07 -0600 Subject: remove code duplication between formatter.cpp and utils.cpp --- src/formatter.cpp | 10 ++-------- src/utils.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/formatter.cpp b/src/formatter.cpp index a271ba0..989781a 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -226,14 +227,7 @@ Formatter::~Formatter() bool Formatter::is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; - const auto t_fmt = nl_langinfo(T_FMT); - - for (const auto& needle : formats_24h) - if (strstr(t_fmt, needle)) - return false; - - return true; + return ::is_locale_12h(); } const char* diff --git a/src/utils.cpp b/src/utils.cpp index 0b586f4..e97b654 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -38,11 +38,11 @@ with this program. If not, see . gboolean is_locale_12h() { - const char *t_fmt = nl_langinfo(T_FMT); - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; - for(const auto& format : formats_24h) - if(strstr(t_fmt, format) != nullptr) + const auto t_fmt = nl_langinfo(T_FMT); + + for (const auto& needle : formats_24h) + if (strstr(t_fmt, needle) != nullptr) return false; return true; -- cgit v1.2.3 From 1c547710eb14d7805e47205f7c62fb7b0d0f3756 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:26:02 -0600 Subject: add unit tests for utils.cpp's get_timezone_name() --- tests/test-utils.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp index 2fe6a2e..036c13f 100644 --- a/tests/test-utils.cpp +++ b/tests/test-utils.cpp @@ -17,6 +17,7 @@ * Charles Kerr */ +#include #include #include @@ -51,24 +52,47 @@ TEST(UtilsTest, SplitSettingsLocation) } } -TEST(UtilsTest, BeautifulTimezoneName) +namespace { struct { const char* timezone; const char* location; const char* expected_name; - } test_cases[] = { + } beautify_timezone_test_cases[] = { { "America/Chicago", NULL, "Chicago" }, { "America/Chicago", "America/Chicago", "Chicago" }, { "America/Chicago", "America/Chigago Chicago", "Chicago" }, { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, { "America/Chicago", "Europe/London London", "Chicago" } }; - - for(const auto& test_case : test_cases) +} + +TEST(UtilsTest, BeautifulTimezoneName) +{ + for(const auto& test_case : beautify_timezone_test_cases) { auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); EXPECT_STREQ(test_case.expected_name, name); g_free(name); } } + + +TEST(UtilsTest, GetTimezonename) +{ + // set up a local GSettings + 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); + auto settings = g_settings_new(SETTINGS_INTERFACE); + + for(const auto& test_case : beautify_timezone_test_cases) + { + g_settings_set_string(settings, SETTINGS_TIMEZONE_NAME_S, test_case.location); + auto name = get_timezone_name (test_case.timezone, settings); + EXPECT_STREQ(test_case.expected_name, name); + g_free(name); + } + + g_clear_object(&settings); +} -- cgit v1.2.3 From f320760f20b282e9b2695477ab602d4041a3ada8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:26:52 -0600 Subject: add test coverage for Exporter's name_lost signal --- tests/test-exporter.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index aa9e89d..104fb4b 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -36,17 +36,6 @@ private: typedef GlibFixture super; - static void on_bus_closed(GObject * object, - GAsyncResult * res, - gpointer gself) - { - auto self = static_cast(gself); - GError* err = nullptr; - g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); - g_assert_no_error(err); - g_main_loop_quit(self->loop); - } - protected: GTestDBus* bus = nullptr; @@ -65,9 +54,11 @@ protected: void TearDown() { - GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - g_dbus_connection_close(connection, nullptr, on_bus_closed, this); - g_main_loop_run(loop); + GError * error = nullptr; + GDBusConnection* connection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error); + if(!g_dbus_connection_is_closed(connection)) + g_dbus_connection_close_sync(connection, nullptr, &error); + g_assert_no_error(error); g_clear_object(&connection); g_test_dbus_down(bus); g_clear_object(&bus); @@ -87,6 +78,10 @@ TEST_F(ExporterFixture, Publish) std::shared_ptr actions(new MockActions(state)); std::vector> menus; + MenuFactory menu_factory (actions, state); + for(int i=0; i Date: Tue, 28 Jan 2014 10:39:44 -0600 Subject: add libdbustest1-dev to debian/control's Build-Depends to pick up dbus-test-runner's dbusmock interface for the geoclue unit tests. --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index 1788381..d28f086 100644 --- a/debian/control +++ b/debian/control @@ -29,6 +29,7 @@ Build-Depends: cmake, libtimezonemap1-dev, liburl-dispatcher1-dev, libproperties-cpp-dev, + libdbustest1-dev, locales, Standards-Version: 3.9.3 Homepage: https://launchpad.net/indicator-datetime -- cgit v1.2.3 From 1e90a7ec0e834dc28434f4db7b4339c8492c602b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 11:41:01 -0600 Subject: update POTFILES.in --- po/POTFILES.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index acc8916..8ee157e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,6 +1,6 @@ -src/service.c -src/settings-shared.h -src/utils.c +src/formatter.cpp +src/formatter-desktop.cpp +src/menu.cpp panel/datetime-prefs.c panel/datetime-prefs-locations.c [type: gettext/glade]data/datetime-dialog.ui -- cgit v1.2.3 From e57a2e9b860db9776d94356a6fc38e193c801f65 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 11:50:50 -0600 Subject: silence compiler warning --- src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu.cpp b/src/menu.cpp index 9e76d4b..390a06f 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -381,7 +381,7 @@ private: 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(); + default: model = nullptr; g_warn_if_reached(); } if (model) -- cgit v1.2.3 From ee9f4c7ef822a101cea8d12c565d8a8a93d9caf5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 16:26:45 -0600 Subject: make utils.cpp's generate_full_format_string_at_time() a standalone function so that the panels can use the utils functions without a libindicatordatetime dependency --- include/datetime/utils.h | 50 ++++--- panel-gnome/CMakeLists.txt | 3 +- panel-unity/CMakeLists.txt | 3 +- panel/datetime-prefs-locations.c | 2 +- src/CMakeLists.txt | 3 +- src/utils.c | 307 +++++++++++++++++++++++++++++++++++++++ src/utils.cpp | 154 -------------------- 7 files changed, 342 insertions(+), 180 deletions(-) create mode 100644 src/utils.c delete mode 100644 src/utils.cpp diff --git a/include/datetime/utils.h b/include/datetime/utils.h index 10e881f..7cac9fd 100644 --- a/include/datetime/utils.h +++ b/include/datetime/utils.h @@ -1,24 +1,22 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -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 . -*/ +/* + * Copyright 2010, 2014 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 . + * + * Authors: + * Michael Terry + * Charles Kerr + */ #ifndef INDICATOR_DATETIME_UTILS_H #define INDICATOR_DATETIME_UTILS_H @@ -28,6 +26,7 @@ with this program. If not, see . G_BEGIN_DECLS +/** \brief Returns true if the current locale prefers 12h display instead of 24h */ gboolean is_locale_12h (void); void split_settings_location (const char * location, @@ -41,7 +40,14 @@ gchar * get_beautified_timezone_name (const char * timezone, const char * saved_location); gchar * generate_full_format_string_at_time (GDateTime * now, - GDateTime * time); + GDateTime * then_begin, + GDateTime * then_end); + +/** \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 */ +const char* T_ (const char * msg); + G_END_DECLS diff --git a/panel-gnome/CMakeLists.txt b/panel-gnome/CMakeLists.txt index 1135cd4..82c511d 100644 --- a/panel-gnome/CMakeLists.txt +++ b/panel-gnome/CMakeLists.txt @@ -3,13 +3,14 @@ set (PANEL_LIB "gnome-indicator-datetime") add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}") +SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g") SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") add_library (${PANEL_LIB} SHARED ${CMAKE_SOURCE_DIR}/panel/datetime-prefs.c ${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.c ${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.h - ${CMAKE_SOURCE_DIR}/src/utils.cpp + ${CMAKE_SOURCE_DIR}/src/utils.c ${CMAKE_SOURCE_DIR}/include/datetime/utils.h ${CMAKE_SOURCE_DIR}/include/datetime/settings-shared.h) set_property (TARGET ${PANEL_LIB} PROPERTY OUTPUT_NAME indicator-datetime) diff --git a/panel-unity/CMakeLists.txt b/panel-unity/CMakeLists.txt index 150034c..c8ebde0 100644 --- a/panel-unity/CMakeLists.txt +++ b/panel-unity/CMakeLists.txt @@ -2,13 +2,14 @@ set (PANEL_LIB "unity-indicator-datetime") add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}") +SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g") SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") add_library (${PANEL_LIB} SHARED ${CMAKE_SOURCE_DIR}/panel/datetime-prefs.c ${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.c ${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.h - ${CMAKE_SOURCE_DIR}/src/utils.cpp + ${CMAKE_SOURCE_DIR}/src/utils.c ${CMAKE_SOURCE_DIR}/include/datetime/utils.h ${CMAKE_SOURCE_DIR}/include/datetime/settings-shared.h) set_property (TARGET ${PANEL_LIB} PROPERTY OUTPUT_NAME indicator-datetime) diff --git a/panel/datetime-prefs-locations.c b/panel/datetime-prefs-locations.c index b79c014..0437eb4 100644 --- a/panel/datetime-prefs-locations.c +++ b/panel/datetime-prefs-locations.c @@ -439,7 +439,7 @@ update_times (GtkWidget * dlg) if (strzone && *strzone) { GTimeZone * tz = g_time_zone_new (strzone); GDateTime * now_tz = g_date_time_to_timezone (now, tz); - gchar * format = generate_full_format_string_at_time (now, now_tz); + gchar * format = generate_full_format_string_at_time (now, now_tz, NULL); gchar * time_str = g_date_time_format (now_tz, format); gchar * old_time_str; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8c62fd..eb716d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") +SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") add_definitions (-DTIMEZONE_FILE="/etc/timezone" @@ -29,7 +30,7 @@ add_library (${SERVICE_LIB} STATIC timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp - utils.cpp) + utils.c) include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..f4eb53f --- /dev/null +++ b/src/utils.c @@ -0,0 +1,307 @@ +/* + * Copyright 2010, 2014 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 . + * + * Authors: + * Michael Terry + * Charles Kerr + */ + + +#include +#include + +#include +#include + +#include +#include +#include + +/* Check the system locale setting to see if the format is 24-hour + time or 12-hour time */ +gboolean +is_locale_12h(void) +{ + int i; + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; + const char* t_fmt = nl_langinfo(T_FMT); + + for (i=0; formats_24h[i]!=NULL; i++) + if (strstr(t_fmt, formats_24h[i]) != NULL) + return FALSE; + + return TRUE; +} + +void +split_settings_location(const gchar* location, gchar** zone, gchar** name) +{ + gchar* location_dup = g_strdup(location); + if(location_dup != NULL) + g_strstrip(location_dup); + + gchar* first; + if(location_dup && (first = strchr(location_dup, ' '))) + *first = '\0'; + + if(zone) + *zone = location_dup; + + if(name != NULL) + { + gchar* after = first ? g_strstrip(first + 1) : NULL; + + if(after && *after) + { + *name = g_strdup(after); + } + else if (location_dup) // make the name from zone + { + 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 = ' '; + + *name = after; + } + else + { + *name = NULL; + } + } +} + +/** + * Our Locations come from two places: (1) direct user input and (2) ones + * guessed by the system, such as from geoclue or timedate1. + * + * Since the latter only have a timezone (eg, "America/Chicago") and the + * former have a descriptive name provided by the end user (eg, + * "America/Chicago Oklahoma City"), this function tries to make a + * more human-readable name by using the user-provided name if the guessed + * timezone matches the last one the user manually clicked on. + * + * In the example above, this allows the menuitem for the system-guessed + * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks + * on the "Oklahoma City" menuitem. + */ +gchar* +get_beautified_timezone_name(const char* timezone_, const char* saved_location) +{ + gchar* zone; + gchar* name; + split_settings_location(timezone_, &zone, &name); + + gchar* saved_zone; + gchar* saved_name; + split_settings_location(saved_location, &saved_zone, &saved_name); + + gchar* rv; + if (g_strcmp0(zone, saved_zone) == 0) + { + rv = saved_name; + saved_name = NULL; + } + else + { + rv = name; + name = NULL; + } + + g_free(zone); + g_free(name); + g_free(saved_zone); + g_free(saved_name); + return rv; +} + +gchar* +get_timezone_name(const gchar* timezone_, GSettings* settings) +{ + gchar* saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + gchar* rv = get_beautified_timezone_name(timezone_, saved_location); + g_free(saved_location); + return rv; +} + +/*** +**** +***/ + +typedef enum +{ + DATE_PROXIMITY_TODAY, + DATE_PROXIMITY_TOMORROW, + DATE_PROXIMITY_WEEK, + DATE_PROXIMITY_FAR +} +date_proximity_t; + +static 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); + if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) + prox = DATE_PROXIMITY_TODAY; + + // does it happen tomorrow? + if (prox == DATE_PROXIMITY_FAR) + { + GDateTime* tomorrow = g_date_time_add_days(now, 1); + + gint tom_year, tom_month, tom_day; + 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); + } + + // does it happen this week? + if (prox == DATE_PROXIMITY_FAR) + { + GDateTime* week = g_date_time_add_days(now, 6); + GDateTime* 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) + prox = DATE_PROXIMITY_WEEK; + + g_date_time_unref(week_bound); + g_date_time_unref(week); + } + + return prox; +} + +const char* +T_(const char *msg) +{ + /* General strategy here is to make sure LANGUAGE is empty (since that + trumps all LC_* vars) and then to temporarily swap LC_TIME and + LC_MESSAGES. Then have gettext translate msg. + + We strdup the strings because the setlocale & *env functions do not + guarantee anything about the storage used for the string, and thus + the string may not be portably safe after multiple calls. + + Note that while you might think g_dcgettext would do the trick here, + that actually looks in /usr/share/locale/XX/LC_TIME, not the + LC_MESSAGES directory, so we won't find any translation there. + */ + + gchar* message_locale = g_strdup(setlocale(LC_MESSAGES, NULL)); + const char* time_locale = setlocale(LC_TIME, NULL); + gchar* language = g_strdup(g_getenv("LANGUAGE")); + + if (language) + g_unsetenv("LANGUAGE"); + setlocale(LC_MESSAGES, time_locale); + + /* Get the LC_TIME version */ + const char* rv = _(msg); + + /* Put everything back the way it was */ + setlocale(LC_MESSAGES, message_locale); + if (language) + g_setenv("LANGUAGE", language, TRUE); + + g_free(message_locale); + g_free(language); + return rv; +} + + +/** + * _ a time today should be shown as just the time (e.g. “3:55 PM”) + * _ a full-day event today should be shown as “Today” + * _ a time any other day this week should be shown as the short version of the + * day and time (e.g. “Wed 3:55 PM”) + * _ a full-day event tomorrow should be shown as “Tomorrow” + * _ a full-day event another day this week should be shown as the + * weekday (e.g. “Friday”) + * _ a time after this week should be shown as the short version of the day, + * date, and time (e.g. “Wed 21 Apr 3:55 PM”) + * _ a full-day event after this week should be shown as the short version of + * the day and date (e.g. “Wed 21 Apr”). + * _ in addition, when presenting the times of upcoming events, the time should + * be followed by the timezone if it is different from the one the computer + * is currently set to. For example, “Wed 3:55 PM UTC−5”. + */ +char* generate_full_format_string_at_time (GDateTime* now, + GDateTime* then, + GDateTime* then_end) +{ + GString* ret = g_string_new (NULL); + + if (then != NULL) + { + const gboolean full_day = then_end && (g_date_time_difference(then_end, then) >= G_TIME_SPAN_DAY); + const date_proximity_t prox = getDateProximity(now, then); + + if (full_day) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("Today")); break; + case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow")); break; + case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%A")); break; + case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b")); break; + } + } + else if (is_locale_12h()) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("%l:%M %p")); break; + case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow\u2003%l:%M %p")); break; + case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%a\u2003%l:%M %p")); break; + case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b\u2003%l:%M %p")); break; + } + } + else + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("%H:%M")); break; + case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow\u2003%H:%M")); break; + case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%a\u2003%H:%M")); break; + case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b\u2003%H:%M")); break; + } + } + + /* if it's an appointment in a different timezone (and doesn't run for a full day) + then the time should be followed by its timezone. */ + if ((then_end != NULL) && + (!full_day) && + ((g_date_time_get_utc_offset(now) != g_date_time_get_utc_offset(then)))) + { + g_string_append_printf (ret, " %s", g_date_time_get_timezone_abbreviation(then)); + } + } + + return g_string_free (ret, FALSE); +} diff --git a/src/utils.cpp b/src/utils.cpp deleted file mode 100644 index e97b654..0000000 --- a/src/utils.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -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 . -*/ - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -/* Check the system locale setting to see if the format is 24-hour - time or 12-hour time */ -gboolean -is_locale_12h() -{ - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; - const auto t_fmt = nl_langinfo(T_FMT); - - for (const auto& needle : formats_24h) - if (strstr(t_fmt, needle) != nullptr) - return false; - - return true; -} - -void -split_settings_location(const gchar* location, gchar** zone, gchar** name) -{ - auto location_dup = g_strdup(location); - if(location_dup != nullptr) - g_strstrip(location_dup); - - gchar* first; - if(location_dup && (first = strchr(location_dup, ' '))) - *first = '\0'; - - if(zone) - *zone = location_dup; - - if(name != nullptr) - { - gchar* after = first ? g_strstrip(first + 1) : nullptr; - - if(after && *after) - { - *name = g_strdup(after); - } - else if (location_dup) // make the name from zone - { - 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 = ' '; - - *name = after; - } - else - { - *name = nullptr; - } - } -} - -/** - * Our Locations come from two places: (1) direct user input and (2) ones - * guessed by the system, such as from geoclue or timedate1. - * - * Since the latter only have a timezone (eg, "America/Chicago") and the - * former have a descriptive name provided by the end user (eg, - * "America/Chicago Oklahoma City"), this function tries to make a - * more human-readable name by using the user-provided name if the guessed - * timezone matches the last one the user manually clicked on. - * - * In the example above, this allows the menuitem for the system-guessed - * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks - * on the "Oklahoma City" menuitem. - */ -gchar* -get_beautified_timezone_name(const char* timezone_, const char* saved_location) -{ - gchar* zone; - gchar* name; - split_settings_location(timezone_, &zone, &name); - - gchar* saved_zone; - gchar* saved_name; - split_settings_location(saved_location, &saved_zone, &saved_name); - - gchar* rv; - if (g_strcmp0(zone, saved_zone) == 0) - { - rv = saved_name; - saved_name = nullptr; - } - else - { - rv = name; - name = nullptr; - } - - g_free(zone); - g_free(name); - g_free(saved_zone); - g_free(saved_name); - return rv; -} - -gchar* -get_timezone_name(const gchar* timezone_, GSettings* settings) -{ - auto saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); - auto rv = get_beautified_timezone_name(timezone_, saved_location); - g_free(saved_location); - return rv; -} - -using namespace unity::indicator::datetime; - -gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) -{ - std::shared_ptr clock(new MockClock(DateTime(now))); - std::shared_ptr settings(new LiveSettings); - DesktopFormatter formatter(clock, settings); - return g_strdup(formatter.getRelativeFormat(then).c_str()); -} - -- cgit v1.2.3 From fd92ac35f12176225e910f1d25f57acf16a35cda Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 16:28:40 -0600 Subject: cleanup from previous commit: since Formatter code was migrated to utils.c so that it could be used standalone by the panels, there's now code duplication between Utils and Formatter. Remove code duplication s.t. Formatter uses the Utils code as well. --- include/datetime/formatter.h | 9 +-- src/formatter-desktop.cpp | 1 + src/formatter.cpp | 172 ++----------------------------------------- 3 files changed, 7 insertions(+), 175 deletions(-) diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 86fa64a..3de109e 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -25,6 +25,7 @@ #include #include +#include // is_locale_12h() #include @@ -88,16 +89,8 @@ protected: Formatter(const std::shared_ptr&); 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); - /** \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); - private: Formatter(const Formatter&) =delete; diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 5efdf8b..d542ec4 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -18,6 +18,7 @@ */ #include +#include // T_() namespace unity { namespace indicator { diff --git a/src/formatter.cpp b/src/formatter.cpp index 989781a..a15c7f8 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include // T_() #include #include @@ -224,49 +224,6 @@ Formatter::~Formatter() { } -bool -Formatter::is_locale_12h() -{ - return ::is_locale_12h(); -} - -const char* -Formatter::T_(const char *msg) -{ - /* General strategy here is to make sure LANGUAGE is empty (since that - trumps all LC_* vars) and then to temporarily swap LC_TIME and - LC_MESSAGES. Then have gettext translate msg. - - We strdup the strings because the setlocale & *env functions do not - guarantee anything about the storage used for the string, and thus - the string may not be portably safe after multiple calls. - - Note that while you might think g_dcgettext would do the trick here, - that actually looks in /usr/share/locale/XX/LC_TIME, not the - LC_MESSAGES directory, so we won't find any translation there. - */ - - auto message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const auto time_locale = setlocale(LC_TIME, nullptr); - auto language = g_strdup(g_getenv("LANGUAGE")); - - if (language) - g_unsetenv("LANGUAGE"); - setlocale(LC_MESSAGES, time_locale); - - /* Get the LC_TIME version */ - const auto rv = _(msg); - - /* Put everything back the way it was */ - setlocale(LC_MESSAGES, message_locale); - if (language) - g_setenv("LANGUAGE", language, TRUE); - - g_free(message_locale); - g_free(language); - return rv; -} - const char* Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) { @@ -292,131 +249,12 @@ Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) **** ***/ -namespace -{ -typedef enum -{ - DATE_PROXIMITY_TODAY, - DATE_PROXIMITY_TOMORROW, - DATE_PROXIMITY_WEEK, - DATE_PROXIMITY_FAR -} -date_proximity_t; - -date_proximity_t getDateProximity(GDateTime* now, GDateTime* time) -{ - auto 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); - if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) - prox = DATE_PROXIMITY_TODAY; - - // does it happen tomorrow? - if (prox == DATE_PROXIMITY_FAR) - { - auto tomorrow = g_date_time_add_days(now, 1); - - gint tom_year, tom_month, tom_day; - 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); - } - - // does it happen this week? - if (prox == DATE_PROXIMITY_FAR) - { - auto week = g_date_time_add_days(now, 6); - auto 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) - prox = DATE_PROXIMITY_WEEK; - - g_date_time_unref(week_bound); - g_date_time_unref(week); - } - - return prox; -} -} // unnamed namespace - -/** - * _ a time today should be shown as just the time (e.g. “3:55 PM”) - * _ a full-day event today should be shown as “Today” - * _ a time any other day this week should be shown as the short version of the - * day and time (e.g. “Wed 3:55 PM”) - * _ a full-day event tomorrow should be shown as “Tomorrow” - * _ a full-day event another day this week should be shown as the - * weekday (e.g. “Friday”) - * _ a time after this week should be shown as the short version of the day, - * date, and time (e.g. “Wed 21 Apr 3:55 PM”) - * _ a full-day event after this week should be shown as the short version of - * the day and date (e.g. “Wed 21 Apr”). - * _ in addition, when presenting the times of upcoming events, the time should - * be followed by the timezone if it is different from the one the computer - * is currently set to. For example, “Wed 3:55 PM UTC−5”. - */ std::string -Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const +Formatter::getRelativeFormat(GDateTime* then_begin, GDateTime* then_end) const { - std::string ret; - const 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); - - if (full_day) - { - switch (prox) - { - case DATE_PROXIMITY_TODAY: ret = _("Today"); break; - case DATE_PROXIMITY_TOMORROW: ret = _("Tomorrow"); break; - case DATE_PROXIMITY_WEEK: ret = T_("%A"); break; - case DATE_PROXIMITY_FAR: ret = T_("%a %d %b"); break; - } - } - else if (is_locale_12h()) - { - switch (prox) - { - case DATE_PROXIMITY_TODAY: ret = T_("%l:%M %p"); break; - case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%l:%M %p"); break; - case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%l:%M %p"); break; - case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%l:%M %p"); break; - } - } - else - { - switch (prox) - { - case DATE_PROXIMITY_TODAY: ret = T_("%H:%M"); break; - case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%H:%M"); break; - case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%H:%M"); break; - case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%H:%M"); break; - } - } - - /* if it's an appointment in a different timezone (and doesn't run for a full day) - 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)))) - { - ret += ' '; - ret += g_date_time_get_timezone_abbreviation(then); - } - } - + auto cstr = generate_full_format_string_at_time (p->m_clock->localtime().get(), then_begin, then_end); + const std::string ret = cstr; + g_free (cstr); return ret; } -- cgit v1.2.3 From 793c6fb6550b8a0c95df695aed70dc1777e501df Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 17:02:34 -0600 Subject: fix bootstrap error in unit tests by ensuring we always use the local gschema instead of the one preinstalled on the system --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c909e0e..c86b4bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,7 +35,7 @@ add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") function(add_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) @@ -55,7 +55,7 @@ add_test_by_name(test-utils) function(add_dbusmock_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) -- cgit v1.2.3 From 416e13070bc73824999ad430cb9f264192c76296 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 18:37:14 -0600 Subject: fix free-memory-read bug found by valgrind testing --- src/planner-eds.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 54332ce..db5d1ba 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -214,7 +214,7 @@ private: void rebuildNow() { - auto calendar_date = m_owner.time.get().get(); + const auto calendar_date = m_owner.time.get().get(); GDateTime* begin; GDateTime* end; int y, m, d; @@ -245,7 +245,6 @@ private: } g_clear_pointer(&begin, g_date_time_unref); g_clear_pointer(&end, g_date_time_unref); - g_clear_pointer(&calendar_date, g_date_time_unref); } void getAppointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) -- cgit v1.2.3 From 65b58035b31bde014bc206ae23a6fac83e9bf3b9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 18:40:13 -0600 Subject: fix Task leak found by valgrind testing --- src/planner-eds.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index db5d1ba..1fb0bd1 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -279,6 +279,7 @@ private: std::shared_ptr main_task(new Task(this, func), [](Task* task){ g_debug("time to delete task %p", (void*)task); task->func(task->appointments); + delete task; }); for (auto& source : m_sources) -- cgit v1.2.3 From ae3ac73a1a8e5a8da7aa0e4f3a3031ba0ec2f192 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 18:51:21 -0600 Subject: fix GDateTime leak found by valgrind testing --- src/planner-eds.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 1fb0bd1..df62360 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -353,9 +353,9 @@ private: ECalComponentText text; text.value = ""; 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.begin = DateTime(begin); + appointment.end = DateTime(end); appointment.color = subtask->color; appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; appointment.summary = text.value; -- cgit v1.2.3 From a82d0fd7cbad9bff96fafae17b2922e1e9d99972 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 18:51:56 -0600 Subject: fix g_date_time_format() leak found by valgrind testing --- src/planner-eds.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index df62360..98cfe0a 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -251,8 +251,12 @@ private: { const auto begin = g_date_time_to_unix(begin_dt); const auto end = g_date_time_to_unix(end_dt); - g_debug("getting all appointments from [%s ... %s]", g_date_time_format(begin_dt, "%F %T"), - g_date_time_format(end_dt, "%F %T")); + + auto begin_str = g_date_time_format(begin_dt, "%F %T"); + auto end_str = g_date_time_format(end_dt, "%F %T"); + g_debug("getting all appointments from [%s ... %s]", begin_str, end_str); + g_free(begin_str); + g_free(end_str); /** *** init the default timezone -- cgit v1.2.3 From c3ee494eefcabd48eb0d2cb8ea8cf8cb1600331a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 21:15:39 -0600 Subject: ensure the unit tests' default locale supports utf-8 --- tests/glib-fixture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 3f517d4..1914b8c 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -61,7 +61,7 @@ class GlibFixture : public ::testing::Test virtual void SetUp() { - setlocale(LC_ALL, ""); + setlocale(LC_ALL, "C.UTF-8"); loop = g_main_loop_new(nullptr, false); -- cgit v1.2.3 From 81d4a2586eb9ff910b6df45569ad8574573a60eb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 21:38:57 -0600 Subject: add dbus-test-runner and python3-dbusmock to Build-Depends; they're needed by the unit tests. --- debian/control | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/control b/debian/control index d28f086..175684f 100644 --- a/debian/control +++ b/debian/control @@ -5,6 +5,8 @@ Maintainer: Ubuntu Desktop Team # language-pack-en-base is for the unit tests s.t. we can test in 12h and 24h locales Build-Depends: cmake, dbus, + dbus-test-runner, + python3-dbusmock, debhelper (>= 9), dh-translations, intltool (>= 0.35.0), -- cgit v1.2.3 From 9045a274a93bb96be458e563bebbceca1426da96 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 21:42:05 -0600 Subject: comment out unit tests that require a version of dbus-test-runner that hasn't landed yet. --- tests/CMakeLists.txt | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c86b4bb..3dcd151 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,12 +53,15 @@ add_test_by_name(test-timezone-file) add_test_by_name(test-utils) -function(add_dbusmock_test_by_name name) - set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) - add_test (${TEST_NAME} ${TEST_NAME}) - add_dependencies (${TEST_NAME} libindicatordatetimeservice) - target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) -endfunction() -add_dbusmock_test_by_name(test-timezone-geoclue) -add_dbusmock_test_by_name(test-timezones) +# disabling the timezone unit tests because they require +# https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 +# which hasn't landed yet. These can be re-enabled as soon as that lands. +#function(add_dbusmock_test_by_name name) +# set (TEST_NAME ${name}) +# add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) +# add_test (${TEST_NAME} ${TEST_NAME}) +# add_dependencies (${TEST_NAME} libindicatordatetimeservice) +# target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) +#endfunction() +#add_dbusmock_test_by_name(test-timezone-geoclue) +#add_dbusmock_test_by_name(test-timezones) -- cgit v1.2.3 From f07f97ef53522abdce52cf3c7b583c0d6d47aa40 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 12:50:59 -0600 Subject: sync with lp:~larsu/indicator-datetime/reset-date and add corresponding unit tests. --- src/actions.cpp | 20 ++++++++++++++++--- src/menu.cpp | 2 ++ tests/test-actions.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index a6a7c0b..acf8fd4 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -108,9 +108,22 @@ void on_set_location(GSimpleAction * /*action*/, g_free(zone); } -static void on_calendar_activated(GSimpleAction * /*action*/, - GVariant * state, - gpointer gself) +void on_calendar_active_changed(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) +{ + // reset the date when the menu is shown + if (g_variant_get_boolean(state)) + { + auto self = static_cast(gself); + + self->set_calendar_date(self->state()->clock->localtime()); + } +} + +void on_calendar_activated(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) { const time_t t = g_variant_get_int64(state); @@ -175,6 +188,7 @@ Actions::Actions(const std::shared_ptr& state): { "activate-phone-clock-app", on_phone_clock_activated }, { "activate-appointment", on_activate_appointment, "s", nullptr }, { "activate-planner", on_activate_planner, "x", nullptr }, + { "calendar-active", nullptr, nullptr, "false", on_calendar_active_changed }, { "set-location", on_set_location, "s" } }; diff --git a/src/menu.cpp b/src/menu.cpp index 390a06f..b0ba79d 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -204,6 +204,8 @@ private: 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_attribute(header, "submenu-action", "s", + "indicator.calendar-active"); g_menu_item_set_submenu(header, G_MENU_MODEL(m_submenu)); g_object_unref(m_submenu); diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp index c30d1fb..1865cfd 100644 --- a/tests/test-actions.cpp +++ b/tests/test-actions.cpp @@ -156,6 +156,60 @@ TEST_F(ActionsFixture, SetCalendarDate) EXPECT_EQ (now, m_state->planner->time.get()); } +TEST_F(ActionsFixture, ActivatingTheCalendarResetsItsDate) +{ + // Confirm that the GActions exist + auto action_group = m_actions->action_group(); + EXPECT_TRUE(g_action_group_has_action(action_group, "calendar")); + EXPECT_TRUE(g_action_group_has_action(action_group, "calendar-active")); + + /// + /// Prerequisite for the test: move calendar-date away from today + /// + + // move calendar-date a week into the future... + const auto now = m_state->clock->localtime(); + auto next_week = g_date_time_add_weeks(now.get(), 1); + const auto next_week_unix = g_date_time_to_unix(next_week); + g_action_group_activate_action (action_group, "calendar", g_variant_new_int64(next_week_unix)); + + // confirm the planner and calendar action state moved a week into the future + // but that m_state->clock is unchanged + EXPECT_EQ(next_week_unix, m_state->planner->time.get().to_unix()); + EXPECT_EQ(now, m_state->clock->localtime()); + auto calendar_state = g_action_group_get_action_state(action_group, "calendar"); + EXPECT_TRUE(calendar_state != nullptr); + EXPECT_TRUE(g_variant_is_of_type(calendar_state, G_VARIANT_TYPE_DICTIONARY)); + auto v = g_variant_lookup_value(calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(next_week_unix, g_variant_get_int64(v)); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + + /// + /// Now the actual test. + /// We set the state of 'calendar-active' to true, which should reset the calendar date. + /// This is so the calendar always starts on today's date when the indicator's menu is pulled down. + /// + + // change the state... + g_action_group_change_action_state(action_group, "calendar-active", g_variant_new_boolean(true)); + + // confirm the planner and calendar action state were reset back to m_state->clock's time + EXPECT_EQ(now.to_unix(), m_state->planner->time.get().to_unix()); + EXPECT_EQ(now, m_state->clock->localtime()); + calendar_state = g_action_group_get_action_state(action_group, "calendar"); + EXPECT_TRUE(calendar_state != nullptr); + EXPECT_TRUE(g_variant_is_of_type(calendar_state, G_VARIANT_TYPE_DICTIONARY)); + v = g_variant_lookup_value(calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(now.to_unix(), g_variant_get_int64(v)); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + +} + + TEST_F(ActionsFixture, OpenAppointment) { Appointment appt; -- cgit v1.2.3 From a64c84b920f3e6ea4b2872f3d709ceab3eb3b700 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 15:37:21 -0600 Subject: sync with lp:~seb128/indicator-datetime/use-correct-gtkbuilder-location --- panel/datetime-prefs-locations.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/panel/datetime-prefs-locations.c b/panel/datetime-prefs-locations.c index 0437eb4..8cd81fb 100644 --- a/panel/datetime-prefs-locations.c +++ b/panel/datetime-prefs-locations.c @@ -37,7 +37,11 @@ with this program. If not, see . #include #include /* time_t */ -#define DATETIME_DIALOG_UI_FILE PKGDATADIR "/datetime-dialog.ui" +#if USE_UNITY + #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/unity-control-center/datetime-dialog.ui" +#else + #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/gnome-control-center/datetime-dialog.ui" +#endif #define COL_NAME 0 #define COL_TIME 1 -- cgit v1.2.3 From 6b2c01ed6063bd7fd12e192668b738a075dc3a24 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 16:10:50 -0600 Subject: fix instant calendar toggle issue reported in testing by seb128; add a test for this regression --- src/menu.cpp | 7 +++++-- tests/test-menus.cpp | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index b0ba79d..40a94fa 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -74,6 +74,9 @@ protected: update_header(); // update header's label update_section(Locations); // locations' relative time may have changed }); + m_state->settings->show_calendar.changed().connect([this](bool){ + update_section(Calendar); + }); m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); @@ -219,8 +222,8 @@ private: { const bool allow_activation = (profile == Desktop) || (profile == Phone); - const bool show_calendar = (profile == Desktop) - || (profile == DesktopGreeter); + const bool show_calendar = m_state->settings->show_calendar.get() && + ((profile == Desktop) || (profile == DesktopGreeter)); auto menu = g_menu_new(); // add a menuitem that shows the current date diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index fe1e86e..27248ad 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -93,7 +93,8 @@ protected: { gchar* str = nullptr; const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); - const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); + const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter)) + && (m_state->settings->show_calendar.get()); // get the calendar section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); @@ -377,6 +378,11 @@ TEST_F(MenuFixture, Sections) TEST_F(MenuFixture, Calendar) { + m_state->settings->show_calendar.set(true); + for(auto& menu : m_menus) + InspectCalendar(menu->menu_model(), menu->profile()); + + m_state->settings->show_calendar.set(false); for(auto& menu : m_menus) InspectCalendar(menu->menu_model(), menu->profile()); } -- cgit v1.2.3 From 131f7512975e3e0e1363cb787834abee5b79b4e5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 16:26:25 -0600 Subject: =?UTF-8?q?fix=20disabled=20'Add=20Event=E2=80=A6'=20menuitem=20is?= =?UTF-8?q?sue=20reported=20in=20testing=20by=20seb128;=20add=20a=20test?= =?UTF-8?q?=20for=20this=20regression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/menu.cpp | 2 +- tests/test-menus.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/menu.cpp b/src/menu.cpp index 40a94fa..44da7b7 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -321,7 +321,7 @@ private: // add the 'Add Event…' menuitem auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); - const gchar* action_name = "indicator.activate_planner"; + 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); diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index 27248ad..e9dd7df 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -182,6 +182,14 @@ protected: 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)); + if (appointments_expected) + { + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + EXPECT_STREQ("indicator.activate-planner", action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), "activate-planner")); + g_free(action); + } g_clear_object(§ion); // try adding a few appointments and see if the menu updates itself -- cgit v1.2.3 From c0c764e032787251d1990d2949b2044d87b6db38 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 16:32:46 -0600 Subject: remove unused old file --- src/service.c | 2432 --------------------------------------------------------- 1 file changed, 2432 deletions(-) delete mode 100644 src/service.c diff --git a/src/service.c b/src/service.c deleted file mode 100644 index 7176ef1..0000000 --- a/src/service.c +++ /dev/null @@ -1,2432 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * Ted Gould - * - * 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 . - */ - -#include /* strstr() */ - -#include -#include -#include -#include -#include - -#include "dbus-shared.h" -#include "service.h" -#include "settings-shared.h" -#include "utils.h" - -#define SKEW_CHECK_INTERVAL_SEC 10 -#define SKEW_DIFF_THRESHOLD_USEC ((SKEW_CHECK_INTERVAL_SEC+5) * G_USEC_PER_SEC) -#define ALARM_CLOCK_ICON_NAME "alarm-clock" - -G_DEFINE_TYPE (IndicatorDatetimeService, - indicator_datetime_service, - G_TYPE_OBJECT) - -enum -{ - SIGNAL_NAME_LOST, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -enum -{ - PROP_0, - PROP_CLOCK, - PROP_PLANNER, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -enum -{ - SECTION_HEADER = (1<<0), - SECTION_CALENDAR = (1<<1), - SECTION_APPOINTMENTS = (1<<2), - SECTION_LOCATIONS = (1<<3), - SECTION_SETTINGS = (1<<4), -}; - -enum -{ - PROFILE_PHONE, - PROFILE_DESKTOP, - PROFILE_GREETER, - N_PROFILES -}; - -static const char * const menu_names[N_PROFILES] = -{ - "phone", - "desktop", - "desktop_greeter" -}; - -struct ProfileMenuInfo -{ - /* the root level -- the header is the only child of this */ - GMenu * menu; - - /* parent of the sections. This is the header's submenu */ - GMenu * submenu; - - guint export_id; -}; - -struct _IndicatorDatetimeServicePrivate -{ - GCancellable * cancellable; - - GSettings * settings; - - IndicatorDatetimeClock * clock; - IndicatorDatetimePlanner * planner; - - gchar * header_label_format_string; - - guint own_id; - guint actions_export_id; - GDBusConnection * conn; - - guint rebuild_id; - int rebuild_flags; - struct ProfileMenuInfo menus[N_PROFILES]; - - GDateTime * skew_time; - guint skew_timer; - - guint header_timer; - guint timezone_timer; - guint alarm_timer; - - /* Which year/month to show in the calendar, - and which day should get the cursor. - This value is reflected in the calendar action's state */ - GDateTime * calendar_date; - - GSimpleActionGroup * actions; - GSimpleAction * phone_header_action; - GSimpleAction * desktop_header_action; - GSimpleAction * calendar_action; - - GDBusProxy * login1_manager; - - /* all the appointments in the selected calendar_date's month. - Used when populating the 'appointment-days' entry in - create_calendar_state() */ - GSList * calendar_appointments; - - /* appointments over the next few weeks. - Used when building SECTION_APPOINTMENTS */ - GSList * upcoming_appointments; - - /* serialized icon cache */ - GVariant * alarm_icon_serialized; - GVariant * calendar_icon_serialized; - GVariant * clock_app_icon_serialized; -}; - -typedef IndicatorDatetimeServicePrivate priv_t; - -/*** -**** -***/ - -static void -indicator_clear_timer (guint * tag) -{ - if (*tag) - { - g_source_remove (*tag); - *tag = 0; - } -} - -static inline GDateTime * -indicator_datetime_service_get_localtime (IndicatorDatetimeService * self) -{ - return indicator_datetime_clock_get_localtime (self->priv->clock); -} - -/*** -**** -***/ - -static void rebuild_now (IndicatorDatetimeService * self, int section); -static void rebuild_soon (IndicatorDatetimeService * self, int section); - -static inline void -rebuild_header_soon (IndicatorDatetimeService * self) -{ - g_clear_pointer (&self->priv->header_label_format_string, g_free); - - rebuild_soon (self, SECTION_HEADER); -} - -static inline void -rebuild_calendar_section_soon (IndicatorDatetimeService * self) -{ - rebuild_soon (self, SECTION_CALENDAR); -} - -static inline void -rebuild_appointments_section_soon (IndicatorDatetimeService * self) -{ - rebuild_soon (self, SECTION_APPOINTMENTS); -} - -static inline void -rebuild_locations_section_soon (IndicatorDatetimeService * self) -{ - rebuild_soon (self, SECTION_LOCATIONS); -} - -static inline void -rebuild_settings_section_soon (IndicatorDatetimeService * self) -{ - rebuild_soon (self, SECTION_SETTINGS); -} - -/*** -**** TIMEZONE TIMER -***/ - -/* - * Periodically rebuild the sections that have time format strings - * that are dependent on the current time: - * - * 1. appointment menuitems' time format strings depend on the - * current time; for example, they don't show the day of week - * if the appointment is today. - * - * 2. location menuitems' time format strings depend on the - * current time; for example, they don't show the day of the week - * if the local date and location date are the same. - * - * 3. the "local date" menuitem in the calendar section is, - * obviously, dependent on the local time. - * - * In short, we want to update whenever the number of days between two zone - * might have changed. We do that by updating when either zone's day changes. - * - * Since not all UTC offsets are evenly divisible by hours - * (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. - */ - -static guint -calculate_seconds_until_next_fifteen_minutes (GDateTime * now) -{ - char * str; - gint minute; - guint seconds; - GTimeSpan diff; - GDateTime * next; - GDateTime * start_of_next; - - 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), - 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); - - 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; -} - -static void start_timezone_timer (IndicatorDatetimeService * self); - -static gboolean -on_timezone_timer (gpointer gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - - rebuild_soon (self, SECTION_CALENDAR | - SECTION_APPOINTMENTS | - SECTION_LOCATIONS); - - /* Restarting the timer to recalculate the interval. This helps us to hit - our marks despite clock skew, suspend+resume, leap seconds, etc */ - start_timezone_timer (self); - return G_SOURCE_REMOVE; -} - -static void -start_timezone_timer (IndicatorDatetimeService * self) -{ - GDateTime * now; - guint seconds; - priv_t * p = self->priv; - - indicator_clear_timer (&p->timezone_timer); - - now = indicator_datetime_service_get_localtime (self); - seconds = calculate_seconds_until_next_fifteen_minutes (now); - p->timezone_timer = g_timeout_add_seconds (seconds, on_timezone_timer, self); - g_date_time_unref (now); -} - -/*** -**** HEADER TIMER -***/ - -/* - * This is to periodically rebuild the header's action's state. - * - * If the label shows seconds, update when we reach the next second. - * Otherwise, update when we reach the next minute. - */ - -static guint -calculate_milliseconds_until_next_minute (GDateTime * now) -{ - GDateTime * next; - GDateTime * start_of_next; - GTimeSpan interval_usec; - 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); - - interval_usec = g_date_time_difference (start_of_next, now); - interval_msec = (interval_usec + 999) / 1000; - - g_date_time_unref (start_of_next); - g_date_time_unref (next); - - return interval_msec; -} - -static 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_msec = (interval_usec + 999) / 1000; - - return interval_msec; -} - -static void start_header_timer (IndicatorDatetimeService * self); - -static gboolean -on_header_timer (gpointer gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - - rebuild_now (self, SECTION_HEADER); - - /* Restarting the timer to recalculate the interval. This helps us to hit - our marks despite clock skew, suspend+resume, leap seconds, etc */ - start_header_timer (self); - return G_SOURCE_REMOVE; -} - -static const char * get_header_label_format_string (IndicatorDatetimeService *); - -static void -start_header_timer (IndicatorDatetimeService * self) -{ - guint interval_msec; - gboolean header_shows_seconds = FALSE; - priv_t * p = self->priv; - GDateTime * now = indicator_datetime_service_get_localtime (self); - - indicator_clear_timer (&p->header_timer); - - if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S)) - { - const char * fmt = get_header_label_format_string (self); - header_shows_seconds = fmt && (strstr(fmt,"%s") || strstr(fmt,"%S") || - strstr(fmt,"%T") || strstr(fmt,"%X") || - strstr(fmt,"%c")); - } - - if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second (now); - else - interval_msec = calculate_milliseconds_until_next_minute (now); - - interval_msec += 50; /* add a small margin to ensure the callback - fires /after/ next is reached */ - - p->header_timer = g_timeout_add_full (G_PRIORITY_HIGH, - interval_msec, - on_header_timer, - self, - NULL); - - g_date_time_unref (now); -} - -/*** -**** ALARMS -***/ - -static void set_alarm_timer (IndicatorDatetimeService * self); - -static gboolean -appointment_has_alarm_url (const struct IndicatorDatetimeAppt * appt) -{ - return (appt->has_alarms) && - (appt->url != NULL) && - (g_str_has_prefix (appt->url, "alarm:///")); -} - -static gboolean -datetimes_have_the_same_minute (GDateTime * a G_GNUC_UNUSED, GDateTime * b G_GNUC_UNUSED) -{ - int ay, am, ad; - int by, bm, bd; - - g_date_time_get_ymd (a, &ay, &am, &ad); - g_date_time_get_ymd (b, &by, &bm, &bd); - - return (ay == by) && - (am == bm) && - (ad == bd) && - (g_date_time_get_hour (a) == g_date_time_get_hour (b)) && - (g_date_time_get_minute (a) == g_date_time_get_minute (b)); -} - -static void -dispatch_alarm_url (const struct IndicatorDatetimeAppt * appt) -{ - gchar * str; - - g_return_if_fail (appt != NULL); - g_return_if_fail (appointment_has_alarm_url (appt)); - - str = g_date_time_format (appt->begin, "%F %T"); - g_debug ("dispatching url \"%s\" for appointment \"%s\", which begins at %s", - appt->url, appt->summary, str); - g_free (str); - - url_dispatch_send (appt->url, NULL, NULL); -} - -static void -on_snap_decided (NotifyNotification * notification G_GNUC_UNUSED, - char * action, - gpointer gurl) -{ - g_debug ("%s: %s", G_STRFUNC, action); - - if (!g_strcmp0 (action, "show")) - { - const gchar * url = gurl; - g_debug ("dispatching url '%s'", url); - url_dispatch_send (url, NULL, NULL); - } -} - -static void -show_snap_decision_for_alarm (const struct IndicatorDatetimeAppt * appt) -{ - gchar * title; - const gchar * body; - const gchar * icon_name; - NotifyNotification * nn; - GError * error; - - title = g_date_time_format (appt->begin, - get_terse_time_format_string (appt->begin)); - body = appt->summary; - icon_name = ALARM_CLOCK_ICON_NAME; - g_debug ("creating a snap decision with title '%s', body '%s', icon '%s'", - title, body, icon_name); - - nn = notify_notification_new (title, body, icon_name); - notify_notification_set_hint_string (nn, - "x-canonical-snap-decisions", - "true"); - notify_notification_set_hint_string (nn, - "x-canonical-private-button-tint", - "true"); - notify_notification_add_action (nn, "show", _("Show"), - on_snap_decided, g_strdup(appt->url), g_free); - notify_notification_add_action (nn, "dismiss", _("Dismiss"), - on_snap_decided, NULL, NULL); - - error = NULL; - notify_notification_show (nn, &error); - if (error != NULL) - { - g_warning ("Unable to show alarm '%s' popup: %s", body, error->message); - g_error_free (error); - dispatch_alarm_url (appt); - } - - g_free (title); -} - -static void update_appointment_lists (IndicatorDatetimeService * self); - -static gboolean -on_alarm_timer (gpointer gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - GDateTime * now; - GSList * l; - - /* If there are any alarms at the current time, show a snap decision */ - now = indicator_datetime_service_get_localtime (self); - for (l=self->priv->upcoming_appointments; l!=NULL; l=l->next) - { - const struct IndicatorDatetimeAppt * appt = l->data; - - if (appointment_has_alarm_url (appt)) - if (datetimes_have_the_same_minute (now, appt->begin)) - show_snap_decision_for_alarm (appt); - } - g_date_time_unref (now); - - /* rebuild the alarm list asynchronously. - set_upcoming_appointments() will update the alarm timer when this - async call is done, so no need to restart the timer here... */ - update_appointment_lists (self); - - return G_SOURCE_REMOVE; -} - -/* if there are upcoming alarms, set the alarm timer to the nearest one. - otherwise, unset the alarm timer. */ -static void -set_alarm_timer (IndicatorDatetimeService * self) -{ - priv_t * p; - GDateTime * now; - GDateTime * alarm_time; - GSList * l; - - p = self->priv; - indicator_clear_timer (&p->alarm_timer); - - now = indicator_datetime_service_get_localtime (self); - - /* find the time of the next alarm on our calendar */ - alarm_time = NULL; - for (l=p->upcoming_appointments; l!=NULL; l=l->next) - { - const struct IndicatorDatetimeAppt * appt = l->data; - - if (appointment_has_alarm_url (appt)) - if (g_date_time_compare (appt->begin, now) > 0) - if (!alarm_time || g_date_time_compare (alarm_time, appt->begin) > 0) - alarm_time = appt->begin; - } - - /* if there's an upcoming alarm, set a timer to wake up at that time */ - if (alarm_time != NULL) - { - GTimeSpan interval_msec; - gchar * str; - GDateTime * then; - - interval_msec = g_date_time_difference (alarm_time, now); - interval_msec += G_USEC_PER_SEC; /* fire a moment after alarm_time */ - interval_msec /= 1000; /* convert from usec to msec */ - - str = g_date_time_format (alarm_time, "%F %T"); - g_debug ("%s is the next alarm time", str); - g_free (str); - then = g_date_time_add_seconds (now, interval_msec/1000); - str = g_date_time_format (then, "%F %T"); - g_debug ("%s is when we'll wake up for it", str); - g_free (str); - g_date_time_unref (then); - - p->alarm_timer = g_timeout_add_full (G_PRIORITY_HIGH, - (guint) interval_msec, - on_alarm_timer, - self, - NULL); - } - - g_date_time_unref (now); -} - -/*** -**** -***/ - -/** - * General purpose handler for rebuilding sections and restarting their timers - * when time jumps for whatever reason: - * - * - clock skew - * - laptop suspend + resume - * - geoclue detects that we've changed timezones - * - Unity is running inside a TARDIS - */ -static void -on_local_time_jumped (IndicatorDatetimeService * self) -{ - g_debug ("%s %s", G_STRLOC, G_STRFUNC); - - /* these calls accomplish two things: - 1. rebuild the necessary states / menuitems when time jumps - 2. restart the timers so their new wait interval is correct */ - - on_header_timer (self); - on_timezone_timer (self); -} - -static gboolean -skew_timer_func (gpointer gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - GDateTime * now = indicator_datetime_service_get_localtime (self); - priv_t * p = self->priv; - - /* check for clock skew: has too much time passed since the last check? */ - if (p->skew_time != NULL) - { - const GTimeSpan diff = g_date_time_difference (now, p->skew_time); - - if (diff > SKEW_DIFF_THRESHOLD_USEC) - on_local_time_jumped (self); - } - - g_clear_pointer (&p->skew_time, g_date_time_unref); - p->skew_time = now; - return G_SOURCE_CONTINUE; -} - -/*** -**** -**** HEADER SECTION -**** -***/ - -static const gchar * -get_header_label_format_string (IndicatorDatetimeService * self) -{ - priv_t * p = self->priv; - - if (p->header_label_format_string == NULL) - { - char * fmt; - GSettings * s = p->settings; - const TimeFormatMode mode = 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 - { - gboolean show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S); - gboolean show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S); - gboolean show_year = show_date && g_settings_get_boolean (s, SETTINGS_SHOW_YEAR_S); - fmt = generate_full_format_string (show_day, show_date, show_year, s); - } - - p->header_label_format_string = fmt; - } - - return p->header_label_format_string; -} - -static GVariant * -create_desktop_header_state (IndicatorDatetimeService * self) -{ - priv_t * p = self->priv; - GVariantBuilder b; - const gchar * fmt; - gchar * str; - gboolean visible; - GDateTime * now; - GVariant * label_variant; - - visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_CLOCK_S); - - /* build the time string for the label & a11y */ - fmt = get_header_label_format_string (self); - now = indicator_datetime_service_get_localtime (self); - str = g_date_time_format (now, fmt); - if (str == NULL) - { - str = g_strdup_printf (_("Unsupported date format “%s”"), fmt); - g_warning ("%s", str); - } - - label_variant = g_variant_new_take_string (str); - g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&b, "{sv}", "accessible-desc", label_variant); - g_variant_builder_add (&b, "{sv}", "label", label_variant); - g_variant_builder_add (&b, "{sv}", "title", g_variant_new_string (_("Date and Time"))); - g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (visible)); - - /* cleanup */ - g_date_time_unref (now); - return g_variant_builder_end (&b); -} - -static gboolean -service_has_alarms (IndicatorDatetimeService * self); - -static GVariant * -create_phone_header_state (IndicatorDatetimeService * self) -{ - priv_t * p = self->priv; - const gboolean has_alarms = service_has_alarms (self); - GVariantBuilder b; - GDateTime * now; - const gchar * fmt; - - 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) - g_variant_builder_add (&b, "{sv}", "icon", p->alarm_icon_serialized); - - /* label, a11y */ - now = indicator_datetime_service_get_localtime (self); - fmt = get_terse_header_time_format_string (); - if (has_alarms) - { - gchar * label = g_date_time_format (now, fmt); - gchar * a11y = g_strdup_printf (_("%s (has alarms)"), label); - g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (label)); - g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_take_string (a11y)); - } - else - { - GVariant * v = g_variant_new_take_string (g_date_time_format (now, fmt)); - g_variant_builder_add (&b, "{sv}", "label", v); - g_variant_builder_add (&b, "{sv}", "accessible-desc", v); - } - - g_date_time_unref (now); - return g_variant_builder_end (&b); -} - - -/*** -**** -**** CALENDAR SECTION -**** -***/ - -static GDateTime * -get_calendar_date (IndicatorDatetimeService * self) -{ - GDateTime * date; - priv_t * p = self->priv; - - if (p->calendar_date) - date = g_date_time_ref (p->calendar_date); - else - date = indicator_datetime_service_get_localtime (self); - - return date; -} - -static GVariant * -create_calendar_state (IndicatorDatetimeService * self) -{ - guint i; - const char * key; - gboolean days[32] = { 0 }; - GVariantBuilder dict_builder; - GVariantBuilder day_builder; - GDateTime * date; - GSList * l; - gboolean b; - priv_t * p = self->priv; - - g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_DICTIONARY); - - key = "appointment-days"; - for (l=p->calendar_appointments; l!=NULL; l=l->next) - { - const struct IndicatorDatetimeAppt * appt = l->data; - days[g_date_time_get_day_of_month (appt->begin)] = TRUE; - } - g_variant_builder_init (&day_builder, G_VARIANT_TYPE("ai")); - for (i=0; isettings, SETTINGS_SHOW_WEEK_NUMBERS_S); - g_variant_builder_add (&dict_builder, "{sv}", key, g_variant_new_boolean (b)); - - return g_variant_builder_end (&dict_builder); -} - -static void -update_calendar_action_state (IndicatorDatetimeService * self) -{ - g_simple_action_set_state (self->priv->calendar_action, - create_calendar_state (self)); -} - -static GMenuModel * -create_calendar_section (IndicatorDatetimeService * self, int profile) -{ - const gboolean allow_activation = (profile == PROFILE_PHONE) || (profile == PROFILE_DESKTOP); - GMenu * menu; - GDateTime * now; - char * label; - GMenuItem * item; - - menu = g_menu_new (); - - /* add a menuitem that shows the current date & time */ - now = indicator_datetime_service_get_localtime (self); - label = g_date_time_format (now, _("%A, %e %B %Y")); - item = g_menu_item_new (label, NULL); - g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, self->priv->calendar_icon_serialized); - if (allow_activation) - g_menu_item_set_action_and_target_value (item, "indicator.activate-planner", g_variant_new_int64(0)); - g_menu_append_item (menu, item); - g_object_unref (item); - g_free (label); - g_date_time_unref (now); - - /* add a menuitem that shows the current date & time */ - if ((profile == PROFILE_DESKTOP) || (profile == PROFILE_GREETER)) - { - item = g_menu_item_new ("[calendar]", NULL); - g_menu_item_set_action_and_target_value (item, "indicator.calendar", g_variant_new_int64(0)); - 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); -} - -/*** -**** -**** APPOINTMENTS SECTION -**** -***/ - -static gboolean -service_has_alarms (IndicatorDatetimeService * self) -{ - gboolean has_alarms = FALSE; - GSList * appts; - GSList * l; - - appts = self->priv->upcoming_appointments; - for (l=appts; l!=NULL; l=l->next) - { - struct IndicatorDatetimeAppt * appt = l->data; - if ((has_alarms = appt->has_alarms)) - break; - } - - return has_alarms; -} - -static char * -get_appointment_time_format (struct IndicatorDatetimeAppt * appt, - GDateTime * now, - GSettings * settings, - gboolean terse) -{ - char * fmt; - gboolean full_day = g_date_time_difference (appt->end, appt->begin) == G_TIME_SPAN_DAY; - - if (appt->is_daily) - { - const char * time_fmt = terse ? get_terse_time_format_string (appt->begin) - : get_full_time_format_string (settings); - fmt = join_date_and_time_format_strings (_("Daily"), time_fmt); - } - else if (full_day) - { - /* TRANSLATORS: a strftime(3) format showing full day events. - * "%A" means a full text day (Wednesday), "%a" means abbreviated (Wed). */ - fmt = g_strdup (_("%A")); - } - else - { - fmt = terse ? generate_terse_format_string_at_time (now, appt->begin) - : generate_full_format_string_at_time (now, appt->begin, settings); - } - - return fmt; -} - -static void -add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean phone) -{ - const int MAX_APPTS = 5; - GDateTime * now; - GHashTable * added; - GSList * appts; - GSList * l; - int i; - - now = indicator_datetime_service_get_localtime (self); - - added = g_hash_table_new (g_str_hash, g_str_equal); - - /* build appointment menuitems */ - appts = self->priv->upcoming_appointments; - for (l=appts, i=0; l!=NULL && inext, i++) - { - struct IndicatorDatetimeAppt * appt = l->data; - char * fmt; - gint64 unix_time; - GMenuItem * menu_item; - - if (g_hash_table_contains (added, appt->uid)) - continue; - - g_hash_table_add (added, appt->uid); - - fmt = get_appointment_time_format (appt, now, self->priv->settings, phone); - unix_time = g_date_time_to_unix (appt->begin); - - menu_item = g_menu_item_new (appt->summary, NULL); - - if (appt->has_alarms) - g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, - self->priv->alarm_icon_serialized); - else if (appt->color != NULL) - g_menu_item_set_attribute (menu_item, "x-canonical-color", - "s", appt->color); - - 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); - g_menu_item_set_attribute (menu_item, "x-canonical-type", - "s", appt->has_alarms ? "com.canonical.indicator.alarm" - : "com.canonical.indicator.appointment"); - - if (phone) - g_menu_item_set_action_and_target_value (menu_item, - "indicator.activate-appointment", - g_variant_new_string (appt->uid)); - 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); - g_free (fmt); - } - - /* cleanup */ - g_hash_table_unref (added); - g_date_time_unref (now); -} - - -/* try to extract the clock app's filename from click. (/$pkgdir/$icon) */ -static GVariant * -get_clock_app_icon (void) -{ - GVariant * serialized = NULL; - gchar * icon_filename = NULL; - gchar * pkgdir; - - pkgdir = NULL; - g_spawn_command_line_sync ("click pkgdir com.ubuntu.clock", &pkgdir, NULL, NULL, NULL); - if (pkgdir != NULL) - { - gchar * manifest = NULL; - g_strstrip (pkgdir); - g_spawn_command_line_sync ("click info com.ubuntu.clock", &manifest, NULL, NULL, NULL); - if (manifest != NULL) - { - JsonParser * parser = json_parser_new (); - if (json_parser_load_from_data (parser, manifest, -1, NULL)) - { - JsonNode * root = json_parser_get_root (parser); /* transfer-none */ - if ((root != NULL) && (JSON_NODE_TYPE(root) == JSON_NODE_OBJECT)) - { - JsonObject * o = json_node_get_object (root); /* transfer-none */ - const gchar * icon_name = json_object_get_string_member (o, "icon"); - if (icon_name != NULL) - icon_filename = g_build_filename (pkgdir, icon_name, NULL); - } - } - g_object_unref (parser); - g_free (manifest); - } - g_free (pkgdir); - } - - if (icon_filename != NULL) - { - GFile * file = g_file_new_for_path (icon_filename); - GIcon * icon = g_file_icon_new (file); - - serialized = g_icon_serialize (icon); - - g_object_unref (icon); - g_object_unref (file); - g_free (icon_filename); - } - - return serialized; -} - -static GMenuModel * -create_phone_appointments_section (IndicatorDatetimeService * self) -{ - priv_t * p = self->priv; - GMenu * menu = g_menu_new (); - GMenuItem * menu_item; - - menu_item = g_menu_item_new (_("Clock"), "indicator.activate-phone-clock-app"); - if (p->clock_app_icon_serialized != NULL) - g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, p->clock_app_icon_serialized); - g_menu_append_item (menu, menu_item); - g_object_unref (menu_item); - - add_appointments (self, menu, TRUE); - - return G_MENU_MODEL (menu); -} - -static GMenuModel * -create_desktop_appointments_section (IndicatorDatetimeService * self) -{ - GMenu * menu = g_menu_new (); - - if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_EVENTS_S)) - { - GMenuItem * menu_item; - - add_appointments (self, menu, FALSE); - - /* add the 'Add Event…' menuitem */ - menu_item = g_menu_item_new (_("Add Event…"), NULL); - g_menu_item_set_action_and_target_value (menu_item, - "indicator.activate-planner", - g_variant_new_int64 (0)); - g_menu_append_item (menu, menu_item); - g_object_unref (menu_item); - } - - return G_MENU_MODEL (menu); -} - -/*** -**** -**** LOCATIONS SECTION -**** -***/ - - -/* A temp struct used by create_locations_section() - for pruning duplicates and sorting. */ -struct TimeLocation -{ - GTimeSpan offset; - gchar * zone; - gchar * name; - gboolean visible; - GDateTime * local_time; -}; - -static void -time_location_free (struct TimeLocation * loc) -{ - g_date_time_unref (loc->local_time); - g_free (loc->name); - g_free (loc->zone); - g_slice_free (struct TimeLocation, loc); -} - -static struct TimeLocation* -time_location_new (const char * zone, - const char * name, - gboolean visible) -{ - struct TimeLocation * loc = g_slice_new (struct TimeLocation); - GTimeZone * tz = g_time_zone_new (zone); - loc->zone = g_strdup (zone); - loc->name = g_strdup (name); - loc->visible = visible; - loc->local_time = g_date_time_new_now (tz); - loc->offset = g_date_time_get_utc_offset (loc->local_time); - g_time_zone_unref (tz); - return loc; -} - -static int -time_location_compare (const struct TimeLocation * a, - const struct TimeLocation * b) -{ - int ret = 0; - - if (!ret && (a->offset != b->offset)) /* primary key */ - ret = (a->offset < b->offset) ? -1 : 1; - - if (!ret) - ret = g_strcmp0 (a->name, b->name); /* secondary key */ - - if (!ret) - ret = a->visible - b->visible; /* tertiary key */ - - return ret; -} - -static GSList* -locations_add (GSList * locations, - const char * zone, - const char * name, - gboolean visible) -{ - struct TimeLocation * loc = time_location_new (zone, name, visible); - - if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare)) - { - g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name); - time_location_free (loc); - } - else - { - g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name); - locations = g_slist_append (locations, loc); - } - - return locations; -} - -static GMenuModel * -create_locations_section (IndicatorDatetimeService * self) -{ - guint i; - GMenu * menu; - GSList * l; - GSList * locations = NULL; - gchar ** user_locations; - const gchar ** detected_timezones; - priv_t * p = self->priv; - GDateTime * now = indicator_datetime_service_get_localtime (self); - - menu = g_menu_new (); - - /*** - **** Build a list of locations to add, omitting duplicates - ***/ - - detected_timezones = indicator_datetime_clock_get_timezones (p->clock); - for (i=0; detected_timezones && detected_timezones[i]; i++) - { - const char * tz = detected_timezones[i]; - gchar * name = get_current_zone_name (tz, p->settings); - locations = locations_add (locations, tz, name, TRUE); - g_free (name); - } - - /* maybe add the user-specified locations */ - user_locations = g_settings_get_strv (p->settings, SETTINGS_LOCATIONS_S); - if (user_locations != NULL) - { - const gboolean visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_LOCATIONS_S); - - for (i=0; user_locations[i] != NULL; i++) - { - gchar * zone; - gchar * name; - split_settings_location (user_locations[i], &zone, &name); - locations = locations_add (locations, zone, name, visible); - g_free (name); - g_free (zone); - } - - g_strfreev (user_locations); - user_locations = NULL; - } - - /* now build menuitems for all the locations */ - for (l=locations; l!=NULL; l=l->next) - { - struct TimeLocation * loc = l->data; - if (loc->visible) - { - char * detailed_action; - char * fmt; - GMenuItem * menu_item; - - detailed_action = g_strdup_printf ("indicator.set-location::%s %s", - loc->zone, - loc->name); - fmt = generate_full_format_string_at_time (now, loc->local_time, p->settings); - - menu_item = g_menu_item_new (loc->name, detailed_action); - g_menu_item_set_attribute (menu_item, "x-canonical-type", - "s", "com.canonical.indicator.location"); - g_menu_item_set_attribute (menu_item, "x-canonical-timezone", - "s", loc->zone); - g_menu_item_set_attribute (menu_item, "x-canonical-time-format", - "s", fmt); - g_menu_append_item (menu, menu_item); - - g_object_unref (menu_item); - g_free (fmt); - g_free (detailed_action); - } - } - - g_date_time_unref (now); - g_slist_free_full (locations, (GDestroyNotify)time_location_free); - return G_MENU_MODEL (menu); -} - -/*** -**** SET LOCATION -***/ - -struct setlocation_data -{ - IndicatorDatetimeService * service; - char * timezone_id; - char * name; -}; - -static void -setlocation_data_free (struct setlocation_data * data) -{ - g_free (data->timezone_id); - g_free (data->name); - g_slice_free (struct setlocation_data, data); -} - -static void -on_datetime1_set_timezone_response (GObject * object, - GAsyncResult * res, - gpointer gdata) -{ - GError * err; - GVariant * answers; - struct setlocation_data * data = gdata; - - err = NULL; - answers = g_dbus_proxy_call_finish (G_DBUS_PROXY(object), res, &err); - if (err != NULL) - { - if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Could not set new timezone: %s", err->message); - - g_error_free (err); - } - else - { - char * timezone_name = g_strdup_printf ("%s %s", - data->timezone_id, - data->name); - - g_settings_set_string (data->service->priv->settings, - SETTINGS_TIMEZONE_NAME_S, - timezone_name); - - g_free (timezone_name); - g_variant_unref (answers); - } - - setlocation_data_free (data); -} - -static void -on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gdata) -{ - GError * err; - GDBusProxy * proxy; - struct setlocation_data * data = gdata; - - err = NULL; - proxy = g_dbus_proxy_new_for_bus_finish (res, &err); - if (err != NULL) - { - if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Could not grab DBus proxy for timedated: %s", err->message); - - g_error_free (err); - setlocation_data_free (data); - } - else - { - g_dbus_proxy_call (proxy, - "SetTimezone", - g_variant_new ("(sb)", data->timezone_id, TRUE), - G_DBUS_CALL_FLAGS_NONE, - -1, - data->service->priv->cancellable, - on_datetime1_set_timezone_response, - data); - - g_object_unref (proxy); - } -} - -static void -indicator_datetime_service_set_location (IndicatorDatetimeService * self, - const char * timezone_id, - const char * name) -{ - priv_t * p = self->priv; - struct setlocation_data * data; - - g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self)); - g_return_if_fail (name && *name); - g_return_if_fail (timezone_id && *timezone_id); - - data = g_slice_new0 (struct setlocation_data); - data->timezone_id = g_strdup (timezone_id); - data->name = g_strdup (name); - data->service = self; - - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - p->cancellable, - on_datetime1_proxy_ready, - data); -} - -static void -on_set_location (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param, - gpointer gself) -{ - char * zone; - char * name; - IndicatorDatetimeService * self; - - self = INDICATOR_DATETIME_SERVICE (gself); - split_settings_location (g_variant_get_string (param, NULL), &zone, &name); - indicator_datetime_service_set_location (self, zone, name); - - g_free (name); - g_free (zone); -} - -static void -on_calendar_active_changed (GSimpleAction *action G_GNUC_UNUSED, - GVariant *state, - gpointer user_data) -{ - IndicatorDatetimeService *self = user_data; - - /* reset the date when the menu is shown */ - if (g_variant_get_boolean (state) && self->priv->calendar_date) - { - g_clear_pointer (&self->priv->calendar_date, g_date_time_unref); - update_calendar_action_state (self); - } -} - -/*** -**** -***/ - -static GMenuModel * -create_desktop_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED) -{ - GMenu * menu = g_menu_new (); - g_menu_append (menu, _("Date & Time Settings…"), "indicator.activate-desktop-settings"); - return G_MENU_MODEL (menu); -} - -static GMenuModel * -create_phone_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED) -{ - GMenu * menu = g_menu_new (); - g_menu_append (menu, _("Time & Date settings…"), "indicator.activate-phone-settings"); - return G_MENU_MODEL (menu); -} - -static void -create_menu (IndicatorDatetimeService * self, int profile) -{ - GMenu * menu; - GMenu * submenu; - GMenuItem * header; - GMenuModel * sections[16]; - const gchar * header_action; - int i; - int n = 0; - - g_assert (0<=profile && profilepriv->menus[profile].menu == NULL); - - switch (profile) - { - case PROFILE_PHONE: - sections[n++] = create_calendar_section (self, profile); - sections[n++] = create_phone_appointments_section (self); - sections[n++] = create_phone_settings_section (self); - header_action = "indicator.phone-header"; - break; - - case PROFILE_DESKTOP: - sections[n++] = create_calendar_section (self, profile); - sections[n++] = create_desktop_appointments_section (self); - sections[n++] = create_locations_section (self); - sections[n++] = create_desktop_settings_section (self); - header_action = "indicator.desktop-header"; - break; - - case PROFILE_GREETER: - sections[n++] = create_calendar_section (self, profile); - header_action = "indicator.desktop-header"; - break; - } - - /* add sections to the submenu */ - - submenu = g_menu_new (); - - for (i=0; ipriv->menus[profile].menu = menu; - self->priv->menus[profile].submenu = submenu; -} - -/*** -**** GActions -***/ - -/* Run a particular program based on an activation */ -static void -execute_command (const gchar * cmd) -{ - GError * err = NULL; - - g_debug ("Issuing command '%s'", cmd); - - if (!g_spawn_command_line_async (cmd, &err)) - { - g_warning ("Unable to start \"%s\": %s", cmd, err->message); - g_error_free (err); - } -} - -static void -on_desktop_settings_activated (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param G_GNUC_UNUSED, - gpointer gself G_GNUC_UNUSED) -{ - gchar *path; - - path = g_find_program_in_path ("unity-control-center"); - if (path != NULL && g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "Unity") == 0) - { - execute_command ("unity-control-center datetime"); - } - else - { -#ifdef HAVE_CCPANEL - execute_command ("gnome-control-center indicator-datetime"); -#else - execute_command ("gnome-control-center datetime"); -#endif - } - - g_free (path); -} - -static void -on_phone_settings_activated (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param G_GNUC_UNUSED, - gpointer gself G_GNUC_UNUSED) -{ - url_dispatch_send ("settings:///system/time-date", NULL, NULL); -} - -static void -on_activate_appointment (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param, - gpointer gself) -{ - priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv; - const gchar * uid = g_variant_get_string (param, NULL); - - if (uid != NULL) - { - const struct IndicatorDatetimeAppt * appt; - GSList * l; - - /* find the appointment that matches that uid */ - for (l=p->upcoming_appointments, appt=NULL; l && !appt; l=l->next) - { - const struct IndicatorDatetimeAppt * tmp = l->data; - if (!g_strcmp0 (uid, tmp->uid)) - appt = tmp; - } - - /* if that appointment's an alarm, dispatch its url */ - g_debug ("%s: uri '%s'; matching appt is %p", G_STRFUNC, uid, (void*)appt); - if (appt && appointment_has_alarm_url (appt)) - dispatch_alarm_url (appt); - } -} - -static void -on_phone_clock_activated (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param G_GNUC_UNUSED, - gpointer gself G_GNUC_UNUSED) -{ - const char * url = "appid://com.ubuntu.clock/clock/current-user-version"; - url_dispatch_send (url, NULL, NULL); -} - -static void -on_activate_planner (GSimpleAction * a G_GNUC_UNUSED, - GVariant * param, - gpointer gself) -{ - priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv; - - if (p->planner != NULL) - { - const gint64 t = g_variant_get_int64 (param); - if (t) - { - GDateTime * date_time = g_date_time_new_from_unix_local (t); - indicator_datetime_planner_activate_time (p->planner, date_time); - g_date_time_unref (date_time); - } - else /* no time specified... */ - { - indicator_datetime_planner_activate (p->planner); - } - } -} - -static void -on_calendar_action_activated (GSimpleAction * action G_GNUC_UNUSED, - GVariant * state, - gpointer gself) -{ - gint64 unix_time; - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - - if ((unix_time = g_variant_get_int64 (state))) - { - GDateTime * date = g_date_time_new_from_unix_local (unix_time); - indicator_datetime_service_set_calendar_date (self, date); - g_date_time_unref (date); - } - else /* unset */ - { - indicator_datetime_service_set_calendar_date (self, NULL); - } -} - - -static void -init_gactions (IndicatorDatetimeService * self) -{ - GSimpleAction * a; - priv_t * p = self->priv; - - 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-planner", on_activate_planner, "x", NULL }, - { "activate-appointment", on_activate_appointment, "s", NULL }, - { "set-location", on_set_location, "s" }, - { "calendar-active", NULL, NULL, "false", on_calendar_active_changed } - }; - - p->actions = g_simple_action_group_new (); - - g_action_map_add_action_entries (G_ACTION_MAP(p->actions), - entries, - G_N_ELEMENTS(entries), - self); - - /* add the header actions */ - - a = g_simple_action_new_stateful ("desktop-header", NULL, - create_desktop_header_state (self)); - g_action_map_add_action (G_ACTION_MAP(p->actions), G_ACTION(a)); - p->desktop_header_action = a; - - a = g_simple_action_new_stateful ("phone-header", NULL, - create_phone_header_state (self)); - g_action_map_add_action (G_ACTION_MAP(p->actions), G_ACTION(a)); - p->phone_header_action = a; - - /* add the calendar action */ - a = g_simple_action_new_stateful ("calendar", - G_VARIANT_TYPE_INT64, - create_calendar_state (self)); - g_action_map_add_action (G_ACTION_MAP(p->actions), G_ACTION(a)); - g_signal_connect (a, "activate", - G_CALLBACK(on_calendar_action_activated), self); - p->calendar_action = a; - - rebuild_now (self, SECTION_HEADER); -} - -/*** -**** -***/ - -/** - * A small helper function for rebuild_now(). - * - removes the previous section - * - adds and unrefs the new section - */ -static void -rebuild_section (GMenu * parent, int pos, GMenuModel * new_section) -{ - g_menu_remove (parent, pos); - g_menu_insert_section (parent, pos, NULL, new_section); - g_object_unref (new_section); -} - -static void -rebuild_now (IndicatorDatetimeService * self, int sections) -{ - priv_t * p = self->priv; - struct ProfileMenuInfo * phone = &p->menus[PROFILE_PHONE]; - struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP]; - struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER]; - - if (p->actions == NULL) - return; - - if (sections & SECTION_HEADER) - { - g_simple_action_set_state (p->desktop_header_action, - create_desktop_header_state (self)); - g_simple_action_set_state (p->phone_header_action, - create_phone_header_state (self)); - } - - if (sections & SECTION_CALENDAR) - { - rebuild_section (phone->submenu, 0, create_calendar_section(self, PROFILE_PHONE)); - rebuild_section (desktop->submenu, 0, create_calendar_section(self, PROFILE_DESKTOP)); - rebuild_section (greeter->submenu, 0, create_calendar_section(self, PROFILE_GREETER)); - } - - if (sections & SECTION_APPOINTMENTS) - { - rebuild_section (phone->submenu, 1, create_phone_appointments_section (self)); - rebuild_section (desktop->submenu, 1, create_desktop_appointments_section (self)); - } - - if (sections & SECTION_LOCATIONS) - { - rebuild_section (desktop->submenu, 2, create_locations_section (self)); - } - - if (sections & SECTION_SETTINGS) - { - rebuild_section (phone->submenu, 2, create_phone_settings_section (self)); - rebuild_section (desktop->submenu, 3, create_desktop_settings_section (self)); - } -} - -static int -rebuild_timeout_func (IndicatorDatetimeService * self) -{ - priv_t * p = self->priv; - rebuild_now (self, p->rebuild_flags); - p->rebuild_flags = 0; - p->rebuild_id = 0; - return G_SOURCE_REMOVE; -} - -static void -rebuild_soon (IndicatorDatetimeService * self, int section) -{ - priv_t * p = self->priv; - - p->rebuild_flags |= section; - - if (p->rebuild_id == 0) - { - /* Change events seem to come over the bus in small bursts. This msec - value is an arbitrary number that tries to be large enough to fold - multiple events into a single rebuild, but small enough that the - user won't notice any lag. */ - static const int REBUILD_INTERVAL_MSEC = 500; - - p->rebuild_id = g_timeout_add (REBUILD_INTERVAL_MSEC, - (GSourceFunc)rebuild_timeout_func, - self); - } -} - -/*** -**** org.freedesktop.login1.Manager -***/ - -static void -on_login1_manager_signal (GDBusProxy * proxy G_GNUC_UNUSED, - gchar * sender_name G_GNUC_UNUSED, - gchar * signal_name, - GVariant * parameters, - gpointer gself) -{ - if (!g_strcmp0 (signal_name, "PrepareForSleep")) - { - gboolean sleeping = FALSE; - g_variant_get (parameters, "(b)", &sleeping); - if (!sleeping) - on_local_time_jumped (INDICATOR_DATETIME_SERVICE (gself)); - } -} - -static void -on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * err; - GDBusProxy * proxy; - - err = NULL; - proxy = g_dbus_proxy_new_for_bus_finish (res, &err); - - if (err != NULL) - { - if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Could not grab DBus proxy for logind: %s", err->message); - - g_error_free (err); - } - else - { - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - self->priv->login1_manager = proxy; - g_signal_connect (proxy, "g-signal", - G_CALLBACK(on_login1_manager_signal), self); - } -} - -/*** -**** Appointments -***/ - -static void -set_calendar_appointments (IndicatorDatetimeService * self, - GSList * appointments) -{ - priv_t * p = self->priv; - - /* repopulate the list */ - indicator_datetime_planner_free_appointments (p->calendar_appointments); - p->calendar_appointments = appointments; - - /* sync the menus/actions */ - update_calendar_action_state (self); - rebuild_calendar_section_soon (self); -} - -static void -on_calendar_appointments_ready (GObject * source, - GAsyncResult * res, - gpointer self) -{ - IndicatorDatetimePlanner * planner; - GError * error; - GSList * appointments; - - planner = INDICATOR_DATETIME_PLANNER (source); - error = NULL; - appointments = indicator_datetime_planner_get_appointments_finish (planner, - res, - &error); - - if (error != NULL) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("can't get this month's appointments: %s", error->message); - - g_error_free (error); - } - else - { - set_calendar_appointments (INDICATOR_DATETIME_SERVICE (self), - appointments); - } -} - -static void -set_upcoming_appointments (IndicatorDatetimeService * self, - GSList * appointments) -{ - priv_t * p = self->priv; - - /* repopulate the list */ - indicator_datetime_planner_free_appointments (p->upcoming_appointments); - p->upcoming_appointments = appointments; - - /* sync the menus/actions */ - rebuild_appointments_section_soon (self); - - /* alarm timer is keyed off of the next alarm time, - so it needs to be rebuilt when the appointment list changes */ - set_alarm_timer (self); -} - -static void -on_upcoming_appointments_ready (GObject * source, - GAsyncResult * res, - gpointer self) -{ - IndicatorDatetimePlanner * planner; - GError * error; - GSList * appointments; - - planner = INDICATOR_DATETIME_PLANNER (source); - error = NULL; - appointments = indicator_datetime_planner_get_appointments_finish (planner, - res, - &error); - - if (error != NULL) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("can't get upcoming appointments: %s", error->message); - - g_error_free (error); - } - else - { - set_upcoming_appointments (INDICATOR_DATETIME_SERVICE (self), - appointments); - } -} - -static void -update_appointment_lists (IndicatorDatetimeService * self) -{ - IndicatorDatetimePlanner * planner; - GDateTime * calendar_date; - GDateTime * begin; - GDateTime * end; - int y, m, d; - - planner = self->priv->planner; - calendar_date = get_calendar_date (self); - - /* get all the appointments in the calendar month */ - g_date_time_get_ymd (calendar_date, &y, &m, &d); - begin = g_date_time_new_local (y, m, 1, 0, 0, 0.1); - end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y), 23, 59, 59.9); - if (begin && end) - indicator_datetime_planner_get_appointments (planner, begin, end, - on_calendar_appointments_ready, - self); - g_clear_pointer (&begin, g_date_time_unref); - g_clear_pointer (&end, g_date_time_unref); - - /* get the upcoming appointments */ - begin = g_date_time_ref (calendar_date); - end = g_date_time_add_months (begin, 1); - if (begin && end) - indicator_datetime_planner_get_appointments (planner, begin, end, - on_upcoming_appointments_ready, - self); - g_clear_pointer (&begin, g_date_time_unref); - g_clear_pointer (&end, g_date_time_unref); - g_clear_pointer (&calendar_date, g_date_time_unref); -} - - -/*** -**** GDBus -***/ - -static void -on_bus_acquired (GDBusConnection * connection, - const gchar * name, - gpointer gself) -{ - int i; - guint id; - GError * err = NULL; - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(gself); - priv_t * p = self->priv; - - g_debug ("bus acquired: %s", name); - - p->conn = g_object_ref (G_OBJECT (connection)); - - /* export the actions */ - if ((id = g_dbus_connection_export_action_group (connection, - BUS_PATH, - G_ACTION_GROUP (p->actions), - &err))) - { - p->actions_export_id = id; - } - else - { - g_warning ("cannot export action group: %s", err->message); - g_clear_error (&err); - } - - /* export the menus */ - for (i=0; imenus[i]; - - if ((id = g_dbus_connection_export_menu_model (connection, - path, - G_MENU_MODEL (menu->menu), - &err))) - { - menu->export_id = id; - } - else - { - g_warning ("cannot export %s menu: %s", menu_names[i], err->message); - g_clear_error (&err); - } - - g_free (path); - } -} - -static void -unexport (IndicatorDatetimeService * self) -{ - int i; - priv_t * p = self->priv; - - /* unexport the menus */ - for (i=0; ipriv->menus[i].export_id; - - if (*id) - { - g_dbus_connection_unexport_menu_model (p->conn, *id); - *id = 0; - } - } - - /* unexport the actions */ - if (p->actions_export_id) - { - g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id); - p->actions_export_id = 0; - } -} - -static void -on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, - const gchar * name, - gpointer gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - - g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name); - - unexport (self); - - g_signal_emit (self, signals[SIGNAL_NAME_LOST], 0, NULL); -} - - -/*** -**** GObject virtual functions -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o); - - switch (property_id) - { - case PROP_CLOCK: - g_value_set_object (value, self->priv->clock); - break; - - case PROP_PLANNER: - g_value_set_object (value, self->priv->planner); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_set_property (GObject * o, - guint property_id, - const GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o); - - switch (property_id) - { - case PROP_CLOCK: - indicator_datetime_service_set_clock (self, g_value_get_object (value)); - break; - - case PROP_PLANNER: - indicator_datetime_service_set_planner (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - - -static void -my_dispose (GObject * o) -{ - int i; - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o); - priv_t * p = self->priv; - - if (p->own_id) - { - g_bus_unown_name (p->own_id); - p->own_id = 0; - } - - unexport (self); - - if (p->cancellable != NULL) - { - g_cancellable_cancel (p->cancellable); - g_clear_object (&p->cancellable); - } - - indicator_datetime_service_set_clock (self, NULL); - indicator_datetime_service_set_planner (self, NULL); - - if (p->login1_manager != NULL) - { - g_signal_handlers_disconnect_by_data (p->login1_manager, self); - g_clear_object (&p->login1_manager); - } - - indicator_clear_timer (&p->skew_timer); - indicator_clear_timer (&p->rebuild_id); - indicator_clear_timer (&p->timezone_timer); - indicator_clear_timer (&p->header_timer); - indicator_clear_timer (&p->alarm_timer); - - if (p->settings != NULL) - { - g_signal_handlers_disconnect_by_data (p->settings, self); - g_clear_object (&p->settings); - } - - g_clear_object (&p->actions); - - for (i=0; imenus[i].menu); - - g_clear_object (&p->calendar_action); - g_clear_object (&p->desktop_header_action); - g_clear_object (&p->phone_header_action); - g_clear_object (&p->conn); - - /* clear the serialized icon cache */ - g_clear_pointer (&p->alarm_icon_serialized, g_variant_unref); - g_clear_pointer (&p->calendar_icon_serialized, g_variant_unref); - g_clear_pointer (&p->clock_app_icon_serialized, g_variant_unref); - - G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o); - priv_t * p = self->priv; - - g_free (p->header_label_format_string); - g_clear_pointer (&p->skew_time, g_date_time_unref); - g_clear_pointer (&p->calendar_date, g_date_time_unref); - - G_OBJECT_CLASS (indicator_datetime_service_parent_class)->finalize (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_service_init (IndicatorDatetimeService * self) -{ - GIcon * icon; - priv_t * p; - - /* init the priv pointer */ - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_SERVICE, - IndicatorDatetimeServicePrivate); - self->priv = p; - - p->cancellable = g_cancellable_new (); - - p->settings = g_settings_new (SETTINGS_INTERFACE); - - /* build the serialized icon cache */ - - icon = g_themed_icon_new_with_default_fallbacks (ALARM_CLOCK_ICON_NAME); - p->alarm_icon_serialized = g_icon_serialize (icon); - g_object_unref (icon); - - icon = g_themed_icon_new_with_default_fallbacks ("calendar"); - p->calendar_icon_serialized = g_icon_serialize (icon); - g_object_unref (icon); - - p->clock_app_icon_serialized = get_clock_app_icon (); -} - -static void -my_constructed (GObject * gself) -{ - IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself); - priv_t * p = self->priv; - GString * gstr = g_string_new (NULL); - guint i, n; - - /* these are the settings that affect the - contents of the respective sections */ - const char * const header_settings[] = { - SETTINGS_SHOW_CLOCK_S, - SETTINGS_TIME_FORMAT_S, - SETTINGS_SHOW_SECONDS_S, - SETTINGS_SHOW_DAY_S, - SETTINGS_SHOW_DATE_S, - SETTINGS_SHOW_YEAR_S, - SETTINGS_CUSTOM_TIME_FORMAT_S - }; - 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); isettings, gstr->str, - G_CALLBACK(rebuild_header_soon), self); - } - - for (i=0, n=G_N_ELEMENTS(calendar_settings); isettings, gstr->str, - G_CALLBACK(rebuild_calendar_section_soon), self); - } - - for (i=0, n=G_N_ELEMENTS(appointment_settings); isettings, gstr->str, - G_CALLBACK(rebuild_appointments_section_soon), self); - } - - for (i=0, n=G_N_ELEMENTS(location_settings); isettings, gstr->str, - G_CALLBACK(rebuild_locations_section_soon), self); - } - - /* The keys in time_format_string_settings affect the time format strings we build. - When these change, we need to rebuild everything that has a time format string. */ - for (i=0, n=G_N_ELEMENTS(time_format_string_settings); isettings, gstr->str, - G_CALLBACK(on_local_time_jumped), self); - } - - init_gactions (self); - - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - p->cancellable, - on_login1_manager_proxy_ready, - self); - - p->skew_timer = g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC, - skew_timer_func, - self); - - p->own_id = g_bus_own_name (G_BUS_TYPE_SESSION, - BUS_NAME, - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, - on_bus_acquired, - NULL, - on_name_lost, - self, - NULL); - - on_local_time_jumped (self); - - set_alarm_timer (self); - - for (i=0; idispose = my_dispose; - object_class->finalize = my_finalize; - object_class->constructed = my_constructed; - object_class->get_property = my_get_property; - object_class->set_property = my_set_property; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeServicePrivate)); - - signals[SIGNAL_NAME_LOST] = g_signal_new ( - INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST, - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (IndicatorDatetimeServiceClass, name_lost), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_CLOCK] = g_param_spec_object ("clock", - "Clock", - "The clock", - INDICATOR_TYPE_DATETIME_CLOCK, - flags); - - properties[PROP_PLANNER] = g_param_spec_object ("planner", - "Planner", - "The appointment provider", - INDICATOR_TYPE_DATETIME_PLANNER, - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -/*** -**** Public API -***/ - -IndicatorDatetimeService * -indicator_datetime_service_new (IndicatorDatetimeClock * clock, - IndicatorDatetimePlanner * planner) -{ - GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, - "clock", clock, - "planner", planner, - NULL); - - return INDICATOR_DATETIME_SERVICE (o); -} - -void -indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, - GDateTime * date) -{ - gboolean dirty; - priv_t * p = self->priv; - - dirty = !date || !p->calendar_date || g_date_time_compare (date, p->calendar_date); - - /* update calendar_date */ - g_clear_pointer (&p->calendar_date, g_date_time_unref); - if (date != NULL) - p->calendar_date = g_date_time_ref (date); - - /* sync the menuitems and action states */ - if (dirty) - update_appointment_lists (self); -} - -static void -on_clock_changed (IndicatorDatetimeService * self) -{ - on_local_time_jumped (self); -} - -void -indicator_datetime_service_set_clock (IndicatorDatetimeService * self, - IndicatorDatetimeClock * clock) -{ - priv_t * p; - - g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self)); - g_return_if_fail ((clock == NULL) || INDICATOR_IS_DATETIME_CLOCK (clock)); - - p = self->priv; - - /* clear the old clock */ - - if (p->clock != NULL) - { - g_signal_handlers_disconnect_by_data (p->clock, self); - g_clear_object (&p->clock); - } - - /* set the new clock */ - - if (clock != NULL) - { - p->clock = g_object_ref (clock); - - g_signal_connect_swapped (p->clock, "changed", - G_CALLBACK(on_clock_changed), self); - on_clock_changed (self); - } -} - -void -indicator_datetime_service_set_planner (IndicatorDatetimeService * self, - IndicatorDatetimePlanner * planner) -{ - priv_t * p; - - g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self)); - g_return_if_fail ((planner == NULL) || INDICATOR_IS_DATETIME_PLANNER (planner)); - - p = self->priv; - - /* clear the old planner & appointments */ - - if (p->planner != NULL) - { - g_signal_handlers_disconnect_by_data (p->planner, self); - g_clear_object (&p->planner); - } - - g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments); - g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments); - - /* set the new planner & begin fetching appointments from it */ - - if (planner != NULL) - { - p->planner = g_object_ref (planner); - - g_signal_connect_swapped (p->planner, "appointments-changed", - G_CALLBACK(update_appointment_lists), self); - - update_appointment_lists (self); - } -} -- cgit v1.2.3 From 9752ca1cd3e75e1245ebb8dcb4719503e332a352 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 10:57:19 -0600 Subject: fix 'clock app' menuitem on phone profile reported by seb128 and update the corresponding unit tests --- src/menu.cpp | 17 +++- tests/test-menus.cpp | 242 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 188 insertions(+), 71 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index 44da7b7..d0756cc 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -293,10 +293,10 @@ private: 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 (!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, @@ -315,7 +315,7 @@ private: { auto menu = g_menu_new(); - if (((profile==Phone) || (profile==Desktop)) && m_state->settings->show_events.get()) + if ((profile==Desktop) && m_state->settings->show_events.get()) { add_appointments (menu, profile); @@ -327,6 +327,15 @@ private: g_menu_append_item(menu, menu_item); g_object_unref(menu_item); } + else if (profile==Phone) + { + auto menu_item = g_menu_item_new (_("Clock"), "indicator.activate-phone-clock-app"); + g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + + add_appointments (menu, profile); + } return G_MENU_MODEL(menu); } diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index e9dd7df..73d6036 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -158,42 +158,20 @@ protected: g_object_unref(submenu); } - void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) - { - const bool appointments_expected = (profile == Menu::Desktop) - || (profile == Menu::Phone); +private: + void InspectEmptySection(GMenuModel* menu_model, Menu::Section section) + { // get the Appointments section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto menu_section = g_menu_model_get_item_link(submenu, section, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(menu_section)); + g_clear_object(&menu_section); + g_clear_object(&submenu); + } - // there shouldn't be any menuitems when "show events" is false - m_state->settings->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 appointments; - m_state->settings->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)); - if (appointments_expected) - { - gchar* action = nullptr; - EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); - EXPECT_STREQ("indicator.activate-planner", action); - EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), "activate-planner")); - g_free(action); - } - g_clear_object(§ion); - - // try adding a few appointments and see if the menu updates itself - + std::vector build_some_appointments() + { const auto now = m_state->clock->localtime(); auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); const auto tomorrow = DateTime(gdt_tomorrow); @@ -206,7 +184,6 @@ protected: 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"; @@ -215,52 +192,183 @@ protected: 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 + return std::vector({a1, a2}); + } - 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) + void InspectAppointmentMenuItem(GMenuModel* section, + int index, + const Appointment& appt) + { + // confirm it has the right x-canonical-type + gchar * str = nullptr; + g_menu_model_get_item_attribute(section, index, "x-canonical-type", "s", &str); + if (appt.has_alarms) + EXPECT_STREQ("com.canonical.indicator.alarm", str); + else + 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, index, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + + // confirm the color hint, if it exists, + // is in the x-canonical-color attribute + if (appt.color.empty()) { - gchar * str = nullptr; + EXPECT_FALSE(g_menu_model_get_item_attribute(section, + index, + "x-canonical-color", + "s", + &str)); + } + else + { + EXPECT_TRUE(g_menu_model_get_item_attribute(section, + index, + "x-canonical-color", + "s", + &str)); + EXPECT_EQ(appt.color, str); + } + g_clear_pointer(&str, g_free); - // 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); + // confirm that alarms have an icon + if (appt.has_alarms) + { + auto v = g_menu_model_get_item_attribute_value(section, + index, + 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); } + } + + void InspectAppointmentMenuItems(GMenuModel* section, + int first_appt_index, + const std::vector& appointments) + { + // try adding a few appointments and see if the menu updates itself + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + //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::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(appointments.size()+1, g_menu_model_get_n_items(section)); + + for (int i=0, n=appointments.size(); isettings->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); - g_object_unref(submenu); + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + std::vector appointments; + m_state->settings->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); + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + const char* expected_action = "activate-planner"; + EXPECT_EQ(std::string("indicator.")+expected_action, action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); + g_free(action); + g_clear_object(§ion); + + // try adding a few appointments and see if the menu updates itself + appointments = build_some_appointments(); + 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); + EXPECT_EQ(3, g_menu_model_get_n_items(section)); + InspectAppointmentMenuItems(section, 0, appointments); + g_clear_object(§ion); + + // cleanup + g_clear_object(&submenu); + } + + void InspectPhoneAppointments(GMenuModel* menu_model) + { + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + + // clear all the appointments + std::vector appointments; + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + // check that there's a "clock app" menuitem even when there are no appointments + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + const char* expected_action = "activate-phone-clock-app"; + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + EXPECT_EQ(std::string("indicator.")+expected_action, action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); + g_free(action); + g_clear_object(§ion); + + // add some appointments and test them + appointments = build_some_appointments(); + 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); + EXPECT_EQ(3, g_menu_model_get_n_items(section)); + InspectAppointmentMenuItems(section, 1, appointments); + g_clear_object(§ion); + + // cleanup + g_clear_object(&submenu); + } + +protected: + + void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) + { + switch (profile) + { + case Menu::Desktop: + InspectDesktopAppointments(menu_model); + break; + + case Menu::DesktopGreeter: + InspectEmptySection(menu_model, Menu::Appointments); + break; + + case Menu::Phone: + InspectPhoneAppointments(menu_model); + break; + + case Menu::PhoneGreeter: + InspectEmptySection(menu_model, Menu::Appointments); + break; + + default: + g_warn_if_reached(); + break; + } } void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) -- cgit v1.2.3 From d2caa37e18191c31d866dd3042b676c135bae50d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 12:45:50 -0600 Subject: as per review, don't inline getters --- include/datetime/actions.h | 4 ++-- include/datetime/locations.h | 4 ++-- include/datetime/menu.h | 8 ++++---- src/actions.cpp | 11 +++++++++++ src/locations.cpp | 10 ++++++++++ src/menu.cpp | 26 ++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/include/datetime/actions.h b/include/datetime/actions.h index 3686b95..6817b4c 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -50,8 +50,8 @@ public: virtual void open_appointment(const std::string& uid) =0; virtual void set_location(const std::string& zone, const std::string& name)=0; void set_calendar_date(const DateTime&); - GActionGroup* action_group() { return G_ACTION_GROUP(m_actions); } - std::shared_ptr state() { return m_state; } + GActionGroup* action_group(); + std::shared_ptr state(); protected: Actions(const std::shared_ptr& state); diff --git a/include/datetime/locations.h b/include/datetime/locations.h index ee67615..fc776bb 100644 --- a/include/datetime/locations.h +++ b/include/datetime/locations.h @@ -39,9 +39,9 @@ namespace datetime { class Location { public: - const std::string& zone() const { return m_zone; } + const std::string& zone() const; - const std::string& name() const { return m_name; } + const std::string& name() const; bool operator== (const Location& that) const { diff --git a/include/datetime/menu.h b/include/datetime/menu.h index fcd709f..f097b02 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -43,9 +43,9 @@ 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); } + const std::string& name() const; + Profile profile() const; + GMenuModel* menu_model(); protected: Menu (Profile profile_in, const std::string& name_in): m_profile(profile_in), m_name(name_in) {} @@ -72,7 +72,7 @@ class MenuFactory public: MenuFactory (std::shared_ptr& actions, std::shared_ptr& state); std::shared_ptr buildMenu(Menu::Profile profile); - std::shared_ptr state() { return m_state; } + std::shared_ptr state(); private: std::shared_ptr m_actions; diff --git a/src/actions.cpp b/src/actions.cpp index acf8fd4..6ee3896 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -249,6 +249,17 @@ void Actions::set_calendar_date(const DateTime& date) m_state->planner->time.set(date); } +GActionGroup* Actions::action_group() +{ + return G_ACTION_GROUP(m_actions); +} + +std::shared_ptr Actions::state() +{ + return m_state; +} + + } // namespace datetime } // namespace indicator diff --git a/src/locations.cpp b/src/locations.cpp index d6ab73a..8d1a086 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -25,6 +25,16 @@ namespace unity { namespace indicator { namespace datetime { +const std::string& Location::zone() const +{ + return m_zone; +} + +const std::string& Location::name() const +{ + return m_name; +} + Location::Location(const std::string& zone_, const std::string& name_): m_zone(zone_), m_name(name_) diff --git a/src/menu.cpp b/src/menu.cpp index d0756cc..4bb4fb6 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -35,6 +35,27 @@ namespace datetime { ***** ****/ +const std::string& Menu::name() const +{ + return m_name; +} + +Menu::Profile Menu::profile() const +{ + return m_profile; +} + +GMenuModel* Menu::menu_model() +{ + return G_MENU_MODEL(m_menu); +} + + +/**** +***** +****/ + + #define FALLBACK_ALARM_CLOCK_ICON_NAME "clock" #define CALENDAR_ICON_NAME "calendar" @@ -531,6 +552,11 @@ MenuFactory::MenuFactory(std::shared_ptr& actions_, { } +std::shared_ptr MenuFactory::state() +{ + return m_state; +} + std::shared_ptr MenuFactory::buildMenu(Menu::Profile profile) { -- cgit v1.2.3 From cc2ad2ad457313cb87e4483bf0278f670a5a5cea Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 13:00:22 -0600 Subject: as per review, don't inline unless there are performance issues --- include/datetime/actions-live.h | 2 +- include/datetime/appointment.h | 12 +--------- include/datetime/date-time.h | 6 ++--- include/datetime/locations.h | 12 ++-------- include/datetime/menu.h | 2 +- include/datetime/timezone-file.h | 6 ++--- include/datetime/timezone.h | 1 - src/CMakeLists.txt | 1 + src/actions-live.cpp | 5 +++++ src/appointment.cpp | 48 ++++++++++++++++++++++++++++++++++++++++ src/date-time.cpp | 17 ++++++++++++++ src/locations.cpp | 8 +++++++ src/menu.cpp | 6 +++++ src/timezone-file.cpp | 14 ++++++++++++ 14 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 src/appointment.cpp diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 949222d..3607836 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -36,7 +36,7 @@ namespace datetime { class LiveActions: public Actions { public: - LiveActions(const std::shared_ptr& state_in): Actions(state_in) {} + LiveActions(const std::shared_ptr& state_in); ~LiveActions() =default; void open_desktop_settings(); diff --git a/include/datetime/appointment.h b/include/datetime/appointment.h index e034c08..a5283c9 100644 --- a/include/datetime/appointment.h +++ b/include/datetime/appointment.h @@ -45,17 +45,7 @@ public: DateTime begin; DateTime end; - bool operator== (const Appointment& that) const - { - return (color==that.color) && - (summary==that.summary) && - (url==that.url) && - (uid==that.uid) && - (is_event==that.is_event) && - (has_alarms==that.has_alarms) && - (begin==that.begin) && - (end==that.end); - } + bool operator== (const Appointment& that) const; }; } // namespace datetime diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index c2429eb..2ad7856 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -37,9 +37,9 @@ class DateTime public: static DateTime NowLocal(); explicit DateTime(time_t t); - explicit DateTime(GDateTime* in=nullptr) {reset(in);} - DateTime& operator=(GDateTime* in) {reset(in); return *this;} - DateTime& operator=(const DateTime& in) {m_dt=in.m_dt; return *this; } + explicit DateTime(GDateTime* in=nullptr); + DateTime& operator=(GDateTime* in); + DateTime& operator=(const DateTime& in); DateTime to_timezone(const std::string& zone) const; void reset(GDateTime* in=nullptr); diff --git a/include/datetime/locations.h b/include/datetime/locations.h index fc776bb..b840436 100644 --- a/include/datetime/locations.h +++ b/include/datetime/locations.h @@ -39,18 +39,10 @@ namespace datetime { class Location { public: + Location (const std::string& zone, const std::string& name); const std::string& zone() const; - const std::string& name() const; - - bool operator== (const Location& that) const - { - return (name() == that.name()) && - (zone() == that.zone()) && - (m_offset == that.m_offset); - } - - Location (const std::string& zone, const std::string& name); + bool operator== (const Location& that) const; private: diff --git a/include/datetime/menu.h b/include/datetime/menu.h index f097b02..526cd9f 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -48,7 +48,7 @@ public: GMenuModel* menu_model(); protected: - Menu (Profile profile_in, const std::string& name_in): m_profile(profile_in), m_name(name_in) {} + Menu (Profile profile_in, const std::string& name_in); virtual ~Menu() =default; GMenu* m_menu = nullptr; diff --git a/include/datetime/timezone-file.h b/include/datetime/timezone-file.h index 7f47df6..d77aaae 100644 --- a/include/datetime/timezone-file.h +++ b/include/datetime/timezone-file.h @@ -37,9 +37,9 @@ namespace datetime { class FileTimezone: public Timezone { public: - FileTimezone() {} - FileTimezone(const std::string& filename) { setFilename(filename); } - ~FileTimezone() {clear();} + FileTimezone(); + FileTimezone(const std::string& filename); + ~FileTimezone(); private: void setFilename(const std::string& filename); diff --git a/include/datetime/timezone.h b/include/datetime/timezone.h index ffa5a84..7d2ace8 100644 --- a/include/datetime/timezone.h +++ b/include/datetime/timezone.h @@ -35,7 +35,6 @@ protected: Timezone() =default; public: - //virtual ~Timezone() {} core::Property timezone; }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb716d4..d5236ad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ endif () add_library (${SERVICE_LIB} STATIC actions.cpp actions-live.cpp + appointment.cpp clock.cpp clock-live.cpp date-time.cpp diff --git a/src/actions-live.cpp b/src/actions-live.cpp index afd83f4..c0fd8ff 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -31,6 +31,11 @@ namespace datetime { **** ***/ +LiveActions::LiveActions(const std::shared_ptr& state_in): + Actions(state_in) +{ +} + void LiveActions::execute_command(const std::string& cmdstr) { const auto cmd = cmdstr.c_str(); diff --git a/src/appointment.cpp b/src/appointment.cpp new file mode 100644 index 0000000..6e742c3 --- /dev/null +++ b/src/appointment.cpp @@ -0,0 +1,48 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +bool Appointment::operator==(const Appointment& that) const +{ + return (color==that.color) + && (summary==that.summary) + && (url==that.url) + && (uid==that.uid) + && (is_event==that.is_event) + && (has_alarms==that.has_alarms) + && (begin==that.begin) + && (end==that.end); +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/date-time.cpp b/src/date-time.cpp index 40c638f..a634c5e 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -27,6 +27,23 @@ namespace datetime { **** ***/ +DateTime::DateTime(GDateTime* gdt) +{ + reset(gdt); +} + +DateTime& DateTime::operator=(GDateTime* gdt) +{ + reset(gdt); + return *this; +} + +DateTime& DateTime::operator=(const DateTime& that) +{ + m_dt = that.m_dt; + return *this; +} + DateTime::DateTime(time_t t) { GDateTime * gdt = g_date_time_new_from_unix_local(t); diff --git a/src/locations.cpp b/src/locations.cpp index 8d1a086..c59f988 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -35,6 +35,14 @@ const std::string& Location::name() const return m_name; } +bool Location::operator== (const Location& that) const +{ + return (m_name == that.m_name) + && (m_zone == that.m_zone) + && (m_offset == that.m_offset); +} + + Location::Location(const std::string& zone_, const std::string& name_): m_zone(zone_), m_name(name_) diff --git a/src/menu.cpp b/src/menu.cpp index 4bb4fb6..b263520 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -35,6 +35,12 @@ namespace datetime { ***** ****/ +Menu::Menu (Profile profile_in, const std::string& name_in): + m_profile(profile_in), + m_name(name_in) +{ +} + const std::string& Menu::name() const { return m_name; diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp index 3a0c240..76737b4 100644 --- a/src/timezone-file.cpp +++ b/src/timezone-file.cpp @@ -23,6 +23,20 @@ namespace unity { namespace indicator { namespace datetime { +FileTimezone::FileTimezone() +{ +} + +FileTimezone::FileTimezone(const std::string& filename) +{ + setFilename(filename); +} + +FileTimezone::~FileTimezone() +{ + clear(); +} + void FileTimezone::clear() { -- cgit v1.2.3 From fee34f529e85e97cb439ea9fbbb210cffd51a6cf Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 13:06:33 -0600 Subject: as per review, make explicit the dependency injection that was implicit in main() --- include/datetime/state-live.h | 49 ------------------------------------- src/CMakeLists.txt | 1 - src/main.cpp | 21 +++++++++++++--- src/state-live.cpp | 56 ------------------------------------------- 4 files changed, 18 insertions(+), 109 deletions(-) delete mode 100644 include/datetime/state-live.h delete mode 100644 src/state-live.cpp diff --git a/include/datetime/state-live.h b/include/datetime/state-live.h deleted file mode 100644 index 2b93722..0000000 --- a/include/datetime/state-live.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_STATE_LIVE_H -#define INDICATOR_DATETIME_STATE_LIVE_H - -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -class LiveState: public State -{ -public: - LiveState(); - virtual ~LiveState() =default; -}; - -/*** -**** -***/ - - -} // namespace datetime -} // namespace indicator -} // namespace unity - -#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5236ad..810e299 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,6 @@ add_library (${SERVICE_LIB} STATIC menu.cpp planner-eds.cpp settings-live.cpp - state-live.cpp timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp diff --git a/src/main.cpp b/src/main.cpp index 2c4f160..1534777 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,10 +17,17 @@ * with this program. If not, see . */ + + #include +#include #include +#include #include -#include +#include +#include +#include +#include #include // bindtextdomain() #include @@ -47,8 +54,16 @@ main(int /*argc*/, char** /*argv*/) if(!notify_init("indicator-datetime-service")) g_critical("libnotify initialization failed"); - // build the state and actions for the MenuFactory to use - std::shared_ptr state(new LiveState); + // build the state, actions, and menufactory + std::shared_ptr state(new State); + std::shared_ptr live_settings(new LiveSettings); + std::shared_ptr live_timezones(new LiveTimezones(live_settings, TIMEZONE_FILE)); + std::shared_ptr live_clock(new LiveClock(live_timezones)); + state->settings = live_settings; + state->clock = live_clock; + state->locations.reset(new SettingsLocations(live_settings, live_timezones)); + state->planner.reset(new PlannerEds); + state->planner->time = live_clock->localtime(); std::shared_ptr actions(new LiveActions(state)); MenuFactory factory(actions, state); diff --git a/src/state-live.cpp b/src/state-live.cpp deleted file mode 100644 index fe1e6cd..0000000 --- a/src/state-live.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include - -#include -#include -#include -#include -#include -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -LiveState::LiveState() -{ - std::shared_ptr live_settings(new LiveSettings); - std::shared_ptr live_timezones(new LiveTimezones(live_settings, TIMEZONE_FILE)); - std::shared_ptr live_clock(new LiveClock(live_timezones)); - - settings = live_settings; - clock = live_clock; - locations.reset(new SettingsLocations(live_settings, live_timezones)); - planner.reset(new PlannerEds); - planner->time = clock->localtime(); -} - -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity -- cgit v1.2.3 From b56293e7b4fb4b253da17119ad153990744dac3b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 13:17:11 -0600 Subject: as per review, constify getters where possible. This isn't always possible due to system APIs asking for non-const pointers. --- include/datetime/actions.h | 2 +- include/datetime/menu.h | 1 - src/actions.cpp | 6 +++--- src/menu.cpp | 5 ----- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/include/datetime/actions.h b/include/datetime/actions.h index 6817b4c..99e78f5 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -51,7 +51,7 @@ public: virtual void set_location(const std::string& zone, const std::string& name)=0; void set_calendar_date(const DateTime&); GActionGroup* action_group(); - std::shared_ptr state(); + const std::shared_ptr state() const; protected: Actions(const std::shared_ptr& state); diff --git a/include/datetime/menu.h b/include/datetime/menu.h index 526cd9f..5821e33 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -72,7 +72,6 @@ class MenuFactory public: MenuFactory (std::shared_ptr& actions, std::shared_ptr& state); std::shared_ptr buildMenu(Menu::Profile profile); - std::shared_ptr state(); private: std::shared_ptr m_actions; diff --git a/src/actions.cpp b/src/actions.cpp index 6ee3896..cdeb77f 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -65,7 +65,7 @@ void on_activate_appointment(GSimpleAction * /*action*/, g_return_if_fail(uid && *uid); // find url of the upcoming appointment with this uid - for (auto& appt : self->state()->planner->upcoming.get()) + for (const auto& appt : self->state()->planner->upcoming.get()) { if (appt.uid == uid) { @@ -146,7 +146,7 @@ GVariant* create_default_header_state() GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; - for(const auto& appt : state->planner->thisMonth.get()) + for (const auto& appt : state->planner->thisMonth.get()) days[appt.begin.day_of_month()] = true; GVariantBuilder day_builder; @@ -254,7 +254,7 @@ GActionGroup* Actions::action_group() return G_ACTION_GROUP(m_actions); } -std::shared_ptr Actions::state() +const std::shared_ptr Actions::state() const { return m_state; } diff --git a/src/menu.cpp b/src/menu.cpp index b263520..696ed2b 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -558,11 +558,6 @@ MenuFactory::MenuFactory(std::shared_ptr& actions_, { } -std::shared_ptr MenuFactory::state() -{ - return m_state; -} - std::shared_ptr MenuFactory::buildMenu(Menu::Profile profile) { -- cgit v1.2.3 From 0f384f4c9607b785d7df4da8566fe2b869ef11e4 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 13:28:48 -0600 Subject: as per review, there were a few places that accidentally passed a shared_ptr& instead of a const shared_ptr& --- include/datetime/exporter.h | 4 ++-- include/datetime/menu.h | 2 +- src/exporter.cpp | 4 ++-- src/menu.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/datetime/exporter.h b/include/datetime/exporter.h index a32b941..c228cc1 100644 --- a/include/datetime/exporter.h +++ b/include/datetime/exporter.h @@ -45,8 +45,8 @@ public: core::Signal<> name_lost; - void publish(std::shared_ptr& actions, - std::vector>& menus); + void publish(const std::shared_ptr& actions, + const std::vector>& menus); private: static void on_bus_acquired(GDBusConnection*, const gchar *name, gpointer gthis); diff --git a/include/datetime/menu.h b/include/datetime/menu.h index 5821e33..a95be10 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -70,7 +70,7 @@ private: class MenuFactory { public: - MenuFactory (std::shared_ptr& actions, std::shared_ptr& state); + MenuFactory (const std::shared_ptr& actions, const std::shared_ptr& state); std::shared_ptr buildMenu(Menu::Profile profile); private: diff --git a/src/exporter.cpp b/src/exporter.cpp index 86e3670..ccd6e5c 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -120,8 +120,8 @@ Exporter::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) ***/ void -Exporter::publish(std::shared_ptr& actions, - std::vector>& menus) +Exporter::publish(const std::shared_ptr& actions, + const std::vector>& menus) { m_actions = actions; m_menus = menus; diff --git a/src/menu.cpp b/src/menu.cpp index 696ed2b..42265c9 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -551,8 +551,8 @@ public: ***** ****/ -MenuFactory::MenuFactory(std::shared_ptr& actions_, - std::shared_ptr& state_): +MenuFactory::MenuFactory(const std::shared_ptr& actions_, + const std::shared_ptr& state_): m_actions(actions_), m_state(state_) { -- cgit v1.2.3 From 197309468247c893bdaa37ef47a98db695b2ea78 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 13:44:12 -0600 Subject: following on the review comment covered in the last commit, use shared_ptr instead of shared_ptr where possible. --- include/datetime/clock.h | 2 +- include/datetime/formatter.h | 8 ++++---- include/datetime/locations-settings.h | 8 ++++---- include/datetime/menu.h | 4 ++-- include/datetime/timezones-live.h | 4 ++-- src/clock-live.cpp | 6 +++--- src/formatter-desktop.cpp | 4 ++-- src/formatter.cpp | 6 +++--- src/locations-settings.cpp | 4 ++-- src/menu.cpp | 24 ++++++++++++------------ src/timezones-live.cpp | 3 ++- 11 files changed, 37 insertions(+), 36 deletions(-) diff --git a/include/datetime/clock.h b/include/datetime/clock.h index b3e3538..4a9db8f 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -79,7 +79,7 @@ class Timezones; class LiveClock: public Clock { public: - LiveClock (const std::shared_ptr& zones); + LiveClock (const std::shared_ptr& zones); virtual ~LiveClock(); virtual DateTime localtime() const; diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 3de109e..f323858 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -86,7 +86,7 @@ public: std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; protected: - Formatter(const std::shared_ptr&); + Formatter(const std::shared_ptr&); virtual ~Formatter(); static const char* getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); @@ -107,10 +107,10 @@ private: class DesktopFormatter: public Formatter { public: - DesktopFormatter(const std::shared_ptr&, const std::shared_ptr&); + DesktopFormatter(const std::shared_ptr&, const std::shared_ptr&); private: - std::shared_ptr m_settings; + std::shared_ptr m_settings; void rebuildHeaderFormat(); const gchar* getFullTimeFormatString() const; @@ -126,7 +126,7 @@ private: class PhoneFormatter: public Formatter { public: - PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { + PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { headerFormat.set(getDefaultHeaderTimeFormat(is_locale_12h(), false)); } }; diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index d01cbb5..8757f43 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -39,12 +39,12 @@ public: * @param[in] settings the #Settings whose locations property is to be used * @param[in] timezones the #Timezones to always show first in the list */ - SettingsLocations (const std::shared_ptr& settings, - const std::shared_ptr& timezones); + SettingsLocations (const std::shared_ptr& settings, + const std::shared_ptr& timezones); private: - std::shared_ptr m_settings; - std::shared_ptr m_timezones; + std::shared_ptr m_settings; + std::shared_ptr m_timezones; void reload(); }; diff --git a/include/datetime/menu.h b/include/datetime/menu.h index a95be10..7b351c3 100644 --- a/include/datetime/menu.h +++ b/include/datetime/menu.h @@ -70,12 +70,12 @@ private: class MenuFactory { public: - MenuFactory (const std::shared_ptr& actions, const std::shared_ptr& state); + MenuFactory (const std::shared_ptr& actions, const std::shared_ptr& state); std::shared_ptr buildMenu(Menu::Profile profile); private: std::shared_ptr m_actions; - std::shared_ptr m_state; + std::shared_ptr m_state; }; } // namespace datetime diff --git a/include/datetime/timezones-live.h b/include/datetime/timezones-live.h index 286c967..ca4ef31 100644 --- a/include/datetime/timezones-live.h +++ b/include/datetime/timezones-live.h @@ -38,14 +38,14 @@ namespace datetime { class LiveTimezones: public Timezones { public: - LiveTimezones(std::shared_ptr& settings, const std::string& filename); + LiveTimezones(const std::shared_ptr& settings, const std::string& filename); private: void update_geolocation(); void update_timezones(); FileTimezone m_file; - std::shared_ptr m_settings; + std::shared_ptr m_settings; std::shared_ptr m_geo; }; diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 69ebda7..7c9db40 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -59,7 +59,7 @@ class LiveClock::Impl { public: - Impl(LiveClock& owner, const std::shared_ptr& tzd): + Impl(LiveClock& owner, const std::shared_ptr& tzd): m_owner(owner), m_timezones(tzd) { @@ -135,13 +135,13 @@ protected: LiveClock& m_owner; GTimeZone* m_timezone = nullptr; - std::shared_ptr m_timezones; + std::shared_ptr m_timezones; DateTime m_prev_datetime; unsigned int m_timer = 0; }; -LiveClock::LiveClock(const std::shared_ptr& tzd): +LiveClock::LiveClock(const std::shared_ptr& tzd): p(new Impl(*this, tzd)) { } diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index d542ec4..9a098c6 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -64,8 +64,8 @@ std::string joinDateAndTimeFormatStrings(const char* date_string, **** ***/ -DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_in, - const std::shared_ptr& settings_in): +DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_in, + const std::shared_ptr& settings_in): Formatter(clock_in), m_settings(settings_in) { diff --git a/src/formatter.cpp b/src/formatter.cpp index a15c7f8..638eac4 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -118,7 +118,7 @@ class Formatter::Impl { public: - Impl(Formatter* owner, const std::shared_ptr& clock): + Impl(Formatter* owner, const std::shared_ptr& clock): m_owner(owner), m_clock(clock) { @@ -208,14 +208,14 @@ private: guint m_relative_timer = 0; public: - std::shared_ptr m_clock; + std::shared_ptr m_clock; }; /*** **** ***/ -Formatter::Formatter(const std::shared_ptr& clock): +Formatter::Formatter(const std::shared_ptr& clock): p(new Formatter::Impl(this, clock)) { } diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index 9b90bc0..ef3085f 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,8 +29,8 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations(const std::shared_ptr& settings, - const std::shared_ptr& timezones): +SettingsLocations::SettingsLocations(const std::shared_ptr& settings, + const std::shared_ptr& timezones): m_settings(settings), m_timezones(timezones) { diff --git a/src/menu.cpp b/src/menu.cpp index 42265c9..e92d398 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -70,9 +70,9 @@ class MenuImpl: public Menu protected: MenuImpl(const Menu::Profile profile_in, const std::string& name_in, - std::shared_ptr& state, + std::shared_ptr& state, std::shared_ptr& actions, - std::shared_ptr formatter): + std::shared_ptr formatter): Menu(profile_in, name_in), m_state(state), m_actions(actions), @@ -136,9 +136,9 @@ protected: g_action_group_change_action_state(action_group, action_name.c_str(), state); } - std::shared_ptr m_state; + std::shared_ptr m_state; std::shared_ptr m_actions; - std::shared_ptr m_formatter; + std::shared_ptr m_formatter; GMenu* m_submenu = nullptr; GVariant* get_serialized_alarm_icon() { return m_serialized_alarm_icon; } @@ -450,10 +450,10 @@ class DesktopBaseMenu: public MenuImpl protected: DesktopBaseMenu(Menu::Profile profile_, const std::string& name_, - std::shared_ptr& state_, + std::shared_ptr& state_, std::shared_ptr& actions_): MenuImpl(profile_, name_, state_, actions_, - std::shared_ptr(new DesktopFormatter(state_->clock, state_->settings))) + std::shared_ptr(new DesktopFormatter(state_->clock, state_->settings))) { update_header(); } @@ -477,14 +477,14 @@ protected: class DesktopMenu: public DesktopBaseMenu { public: - DesktopMenu(std::shared_ptr& state_, std::shared_ptr& actions_): + DesktopMenu(std::shared_ptr& state_, std::shared_ptr& actions_): DesktopBaseMenu(Desktop,"desktop", state_, actions_) {} }; class DesktopGreeterMenu: public DesktopBaseMenu { public: - DesktopGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): + DesktopGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): DesktopBaseMenu(DesktopGreeter,"desktop_greeter", state_, actions_) {} }; @@ -493,7 +493,7 @@ class PhoneBaseMenu: public MenuImpl protected: PhoneBaseMenu(Menu::Profile profile_, const std::string& name_, - std::shared_ptr& state_, + std::shared_ptr& state_, std::shared_ptr& actions_): MenuImpl(profile_, name_, state_, actions_, std::shared_ptr(new PhoneFormatter(state_->clock))) @@ -534,7 +534,7 @@ protected: class PhoneMenu: public PhoneBaseMenu { public: - PhoneMenu(std::shared_ptr& state_, + PhoneMenu(std::shared_ptr& state_, std::shared_ptr& actions_): PhoneBaseMenu(Phone, "phone", state_, actions_) {} }; @@ -542,7 +542,7 @@ public: class PhoneGreeterMenu: public PhoneBaseMenu { public: - PhoneGreeterMenu(std::shared_ptr& state_, + PhoneGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): PhoneBaseMenu(PhoneGreeter, "phone_greeter", state_, actions_) {} }; @@ -552,7 +552,7 @@ public: ****/ MenuFactory::MenuFactory(const std::shared_ptr& actions_, - const std::shared_ptr& state_): + const std::shared_ptr& state_): m_actions(actions_), m_state(state_) { diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp index baac05d..4902b76 100644 --- a/src/timezones-live.cpp +++ b/src/timezones-live.cpp @@ -25,7 +25,8 @@ namespace unity { namespace indicator { namespace datetime { -LiveTimezones::LiveTimezones(std::shared_ptr& settings, const std::string& filename): +LiveTimezones::LiveTimezones(const std::shared_ptr& settings, + const std::string& filename): m_file(filename), m_settings(settings) { -- cgit v1.2.3 From 8564861a5026561d94310cd60ed77e3986f64246 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 14:24:17 -0600 Subject: remove dead files --- tests/planner-mock.c | 178 ------------------------------------------------ tests/test-formatter.cc | 98 -------------------------- 2 files changed, 276 deletions(-) delete mode 100644 tests/planner-mock.c delete mode 100644 tests/test-formatter.cc diff --git a/tests/planner-mock.c b/tests/planner-mock.c deleted file mode 100644 index df5413e..0000000 --- a/tests/planner-mock.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include "config.h" - -#include "planner-mock.h" - -struct _IndicatorDatetimePlannerMockPriv -{ - gboolean is_configured; -}; - -typedef IndicatorDatetimePlannerMockPriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimePlannerMock, - indicator_datetime_planner_mock, - INDICATOR_TYPE_DATETIME_PLANNER) - -/*** -**** IndicatorDatetimePlanner virtual funcs -***/ - -static void -my_get_appointments (IndicatorDatetimePlanner * planner, - GDateTime * begin_datetime, - GDateTime * /*end_datetime*/, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask * task; - GSList * appointments; - struct IndicatorDatetimeAppt * appt; - struct IndicatorDatetimeAppt * prev; - - task = g_task_new (planner, NULL, callback, user_data); - - /** - *** Build the appointments list - **/ - - appointments = NULL; - - /* add a daily appointment that occurs at the beginning of the next minute */ - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - appt->is_daily = TRUE; - appt->begin = g_date_time_add_seconds (begin_datetime, 60-g_date_time_get_seconds(begin_datetime)); - appt->end = g_date_time_add_minutes (appt->begin, 1); - appt->color = g_strdup ("#00FF00"); - appt->is_event = TRUE; - appt->summary = g_strdup ("Daily alarm"); - appt->uid = g_strdup ("this uid isn't very random."); - appt->has_alarms = TRUE; - appt->url = g_strdup ("alarm:///some-alarm-info-goes-here"); - appointments = g_slist_prepend (appointments, appt); - prev = appt; - - /* and add one for a minute later that has an alarm uri */ - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - appt->is_daily = TRUE; - appt->begin = g_date_time_add_minutes (prev->end, 1); - appt->end = g_date_time_add_minutes (appt->begin, 1); - appt->color = g_strdup ("#0000FF"); - appt->is_event = TRUE; - appt->summary = g_strdup ("Second Daily alarm"); - appt->uid = g_strdup ("this uid isn't very random either."); - appt->has_alarms = FALSE; - appointments = g_slist_prepend (appointments, appt); - - /* done */ - g_task_return_pointer (task, appointments, NULL); - g_object_unref (task); -} - -static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner* /*self*/, - GAsyncResult* res, - GError** error) -{ - return g_task_propagate_pointer(G_TASK(res), error); -} - -static gboolean -my_is_configured(IndicatorDatetimePlanner* planner) -{ - IndicatorDatetimePlannerMock * self; - self = INDICATOR_DATETIME_PLANNER_MOCK(planner); - return self->priv->is_configured; -} - -static void -my_activate(IndicatorDatetimePlanner* /*self*/) -{ - g_message("%s %s", G_STRLOC, G_STRFUNC); -} - -static void -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); -} - -/*** -**** GObject virtual funcs -***/ - -static void -my_dispose(GObject * o) -{ - G_OBJECT_CLASS(indicator_datetime_planner_mock_parent_class)->dispose(o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_mock_class_init(IndicatorDatetimePlannerMockClass* klass) -{ - GObjectClass * object_class; - IndicatorDatetimePlannerClass * planner_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass); - planner_class->is_configured = my_is_configured; - planner_class->activate = my_activate; - planner_class->activate_time = my_activate_time; - planner_class->get_appointments = my_get_appointments; - planner_class->get_appointments_finish = my_get_appointments_finish; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerMockPriv)); -} - -static void -indicator_datetime_planner_mock_init (IndicatorDatetimePlannerMock * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER_MOCK, - IndicatorDatetimePlannerMockPriv); - - p->is_configured = TRUE; - - self->priv = p; -} - -/*** -**** Public -***/ - -IndicatorDatetimePlanner * -indicator_datetime_planner_mock_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_MOCK, NULL); - - return INDICATOR_DATETIME_PLANNER (o); -} diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc deleted file mode 100644 index 6a408ab..0000000 --- a/tests/test-formatter.cc +++ /dev/null @@ -1,98 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#include -#include - -#include - -#include "utils.h" - -#include "glib-fixture.h" - -/*** -**** -***/ - -class FormatterFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - gchar * original_locale = nullptr; - - protected: - - virtual void SetUp () - { - super::SetUp (); - - original_locale = g_strdup (setlocale (LC_TIME, NULL)); - } - - virtual void TearDown () - { - setlocale (LC_TIME, original_locale); - g_clear_pointer (&original_locale, g_free); - - super::TearDown (); - } - - bool SetLocale (const char * expected_locale, const char * name) - { - setlocale (LC_TIME, expected_locale); - const char * actual_locale = setlocale (LC_TIME, NULL); - if (!g_strcmp0 (expected_locale, actual_locale)) - { - return true; - } - else - { - g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); - return false; - } - } - - inline bool Set24hLocale () { return SetLocale ("C", "24h"); } - inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } -}; - - -/** - * Test the phone header format - */ -TEST_F (FormatterFixture, TestPhoneHeader) -{ - // test the default value in a 24h locale - if (Set24hLocale ()) - { - const gchar * format = get_terse_header_time_format_string (); - ASSERT_NE (nullptr, format); - ASSERT_STREQ ("%H:%M", format); - } - - // test the default value in a 12h locale - if (Set12hLocale ()) - { - const gchar * format = get_terse_header_time_format_string (); - ASSERT_NE (nullptr, format); - ASSERT_STREQ ("%l:%M %p", format); - } -} -- cgit v1.2.3 From a7a09a5ca5012fb1c48f259d2587542316e7349b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 18:33:14 -0600 Subject: copyediting: as per review, use name_of_thing() instead of get_name_of_thing() or getNameOfThing() --- include/datetime/clock-mock.h | 4 ++-- include/datetime/clock.h | 10 +++++----- include/datetime/formatter.h | 12 ++++++------ include/datetime/planner.h | 6 +++--- src/actions.cpp | 4 ++-- src/clock-live.cpp | 6 +++--- src/clock.cpp | 22 +++++++++++----------- src/formatter-desktop.cpp | 4 ++-- src/formatter.cpp | 12 ++++++------ src/menu.cpp | 14 +++++++------- src/planner-eds.cpp | 2 +- tests/test-clock.cpp | 8 ++++---- tests/test-formatter.cpp | 10 +++++----- tests/test-live-actions.cpp | 4 ++-- tests/test-planner.cpp | 6 +++--- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 27926ff..fb9b52f 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -45,9 +45,9 @@ public: const auto old = m_localtime; m_localtime = dt; if (!DateTime::is_same_minute(old, m_localtime)) - minuteChanged(); + minute_changed(); if (!DateTime::is_same_day(old, m_localtime)) - dateChanged(); + date_changed(); } private: diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 4a9db8f..1d488d1 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -43,20 +43,20 @@ public: virtual DateTime localtime() const =0; /** \brief A signal which fires when the clock's minute changes */ - core::Signal<> minuteChanged; + core::Signal<> minute_changed; /** \brief A signal which fires when the clock's date changes */ - core::Signal<> dateChanged; + core::Signal<> date_changed; protected: Clock(); - /** \brief Compares old and new times, emits minuteChanged() or dateChanged() signals if appropriate */ + /** \brief Compares old and new times, emits minute_changed() or date_changed() signals if appropriate */ void maybe_emit (const DateTime& a, const DateTime& b); private: - static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); - static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + static void on_system_bus_ready(GObject*, GAsyncResult*, gpointer); + static void on_prepare_for_sleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index f323858..0d695e2 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -69,27 +69,27 @@ class Formatter public: /** \brief The time format string for the menu header */ - core::Property headerFormat; + core::Property header_format; - /** \brief The time string for the menu header. (eg, the headerFormat + the clock's time */ + /** \brief The time string for the menu header. (eg, the header_format + the clock's time */ core::Property header; /** \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 (ie, the Appointments and Locations menuitems) */ - core::Signal<> relativeFormatChanged; + core::Signal<> relative_format_changed; /** \brief Generate a relative time format for some time (or time range) from the current clock's value. For example, a full-day interval starting at the end of the current clock's day yields "Tomorrow" */ - std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; + std::string relative_format(GDateTime* then, GDateTime* then_end=nullptr) const; protected: Formatter(const std::shared_ptr&); virtual ~Formatter(); - static const char* getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); + static const char* default_header_time_format(bool twelvehour, bool show_seconds); private: @@ -127,7 +127,7 @@ class PhoneFormatter: public Formatter { public: PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { - headerFormat.set(getDefaultHeaderTimeFormat(is_locale_12h(), false)); + header_format.set(default_header_time_format(is_locale_12h(), false)); } }; diff --git a/include/datetime/planner.h b/include/datetime/planner.h index a8f9941..376a31f 100644 --- a/include/datetime/planner.h +++ b/include/datetime/planner.h @@ -43,9 +43,9 @@ public: virtual ~Planner() =default; /** - * \brief Timestamp used to determine the appointments in the `upcoming' and `thisMonth' properties. + * \brief Timestamp used to determine the appointments in the `upcoming' and `this_month' properties. * Setting this value will cause the planner to re-query its backend and - * update the `upcoming' and `thisMonth' properties. + * update the `upcoming' and `this_month' properties. */ core::Property time; @@ -57,7 +57,7 @@ public: /** * \brief The appointments that occur in the same month as the time property */ - core::Property> thisMonth; + core::Property> this_month; protected: Planner() =default; diff --git a/src/actions.cpp b/src/actions.cpp index cdeb77f..d6fa698 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -146,7 +146,7 @@ GVariant* create_default_header_state() GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; - for (const auto& appt : state->planner->thisMonth.get()) + for (const auto& appt : state->planner->this_month.get()) days[appt.begin.day_of_month()] = true; GVariantBuilder day_builder; @@ -222,7 +222,7 @@ Actions::Actions(const std::shared_ptr& state): m_state->planner->time.changed().connect([this](const DateTime&){ update_calendar_state(); }); - m_state->planner->thisMonth.changed().connect([this](const std::vector&){ + m_state->planner->this_month.changed().connect([this](const std::vector&){ update_calendar_state(); }); m_state->settings->show_week_numbers.changed().connect([this](bool){ diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 7c9db40..21a18a3 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -95,7 +95,7 @@ private: { g_clear_pointer(&m_timezone, g_time_zone_unref); m_timezone = g_time_zone_new(str.c_str()); - m_owner.minuteChanged(); + m_owner.minute_changed(); } /*** @@ -109,9 +109,9 @@ private: // maybe emit change signals const auto now = localtime(); if (!DateTime::is_same_minute(m_prev_datetime, now)) - m_owner.minuteChanged(); + m_owner.minute_changed(); if (!DateTime::is_same_day(m_prev_datetime, now)) - m_owner.dateChanged(); + m_owner.date_changed(); // queue up a timer to fire at the next minute m_prev_datetime = now; diff --git a/src/clock.cpp b/src/clock.cpp index d5293cc..f41a0cc 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -33,7 +33,7 @@ namespace datetime { Clock::Clock(): m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); } Clock::~Clock() @@ -48,7 +48,7 @@ Clock::~Clock() } void -Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) +Clock::on_system_bus_ready(GObject*, GAsyncResult * res, gpointer gself) { GDBusConnection * system_bus; @@ -66,22 +66,22 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) "/org/freedesktop/login1", // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, - onPrepareForSleep, + on_prepare_for_sleep, self, nullptr); } } void -Clock::onPrepareForSleep(GDBusConnection* /*connection*/, - const gchar* /*sender_name*/, - const gchar* /*object_path*/, - const gchar* /*interface_name*/, - const gchar* /*signal_name*/, - GVariant* /*parameters*/, - gpointer gself) +Clock::on_prepare_for_sleep(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { - static_cast(gself)->minuteChanged(); + static_cast(gself)->minute_changed(); } /*** diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 9a098c6..336d2d3 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -81,7 +81,7 @@ DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_ void DesktopFormatter::rebuildHeaderFormat() { - headerFormat.set(getHeaderLabelFormatString()); + header_format.set(getHeaderLabelFormatString()); } std::string DesktopFormatter::getHeaderLabelFormatString() const @@ -126,7 +126,7 @@ const gchar* DesktopFormatter::getFullTimeFormatString() const break; } - return getDefaultHeaderTimeFormat(twelvehour, show_seconds); + return default_header_time_format(twelvehour, show_seconds); } const gchar* DesktopFormatter::getDateFormat(bool show_day, bool show_date, bool show_year) const diff --git a/src/formatter.cpp b/src/formatter.cpp index 638eac4..9aa9bbb 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -122,8 +122,8 @@ public: m_owner(owner), m_clock(clock) { - m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){update_header();}); - m_clock->minuteChanged.connect([this](){update_header();}); + m_owner->header_format.changed().connect([this](const std::string& /*fmt*/){update_header();}); + m_clock->minute_changed.connect([this](){update_header();}); update_header(); restartRelativeTimer(); @@ -149,7 +149,7 @@ private: void update_header() { // update the header property - const auto fmt = m_owner->headerFormat.get(); + const auto fmt = m_owner->header_format.get(); const auto str = m_clock->localtime().format(fmt); m_owner->header.set(str); @@ -197,7 +197,7 @@ private: static gboolean onRelativeTimer(gpointer gself) { auto self = static_cast(gself); - self->m_owner->relativeFormatChanged(); + self->m_owner->relative_format_changed(); self->restartRelativeTimer(); return G_SOURCE_REMOVE; } @@ -225,7 +225,7 @@ Formatter::~Formatter() } const char* -Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) +Formatter::default_header_time_format(bool twelvehour, bool show_seconds) { const char* fmt; @@ -250,7 +250,7 @@ Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) ***/ std::string -Formatter::getRelativeFormat(GDateTime* then_begin, GDateTime* then_end) const +Formatter::relative_format(GDateTime* then_begin, GDateTime* then_end) const { auto cstr = generate_full_format_string_at_time (p->m_clock->localtime().get(), then_begin, then_end); const std::string ret = cstr; diff --git a/src/menu.cpp b/src/menu.cpp index e92d398..91f7dd2 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -90,12 +90,12 @@ protected: m_formatter->header.changed().connect([this](const std::string&){ update_header(); }); - m_formatter->headerFormat.changed().connect([this](const std::string&){ + m_formatter->header_format.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_formatter->relative_format_changed.connect([this](){ + update_section(Appointments); // uses formatter.relative_format() + update_section(Locations); // uses formatter.relative_format() }); m_state->settings->show_clock.changed().connect([this](bool){ update_header(); // update header's label @@ -110,7 +110,7 @@ protected: m_state->planner->upcoming.changed().connect([this](const std::vector&){ update_section(Appointments); // "upcoming" is the list of Appointments we show }); - m_state->clock->dateChanged.connect([this](){ + m_state->clock->date_changed.connect([this](){ update_section(Calendar); // need to update the Date menuitem update_section(Locations); // locations' relative time may have changed }); @@ -305,7 +305,7 @@ private: GDateTime* begin = appt.begin(); GDateTime* end = appt.end(); - auto fmt = m_formatter->getRelativeFormat(begin, end); + auto fmt = m_formatter->relative_format(begin, end); auto unix_time = g_date_time_to_unix(begin); auto menu_item = g_menu_item_new (appt.summary.c_str(), nullptr); @@ -380,7 +380,7 @@ private: 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()); + const auto fmt = m_formatter->relative_format(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"); diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 98cfe0a..cb42d6e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -227,7 +227,7 @@ private: { getAppointments(begin, end, [this](const std::vector& appointments) { g_debug("got %d appointments in this calendar month", (int)appointments.size()); - m_owner.thisMonth.set(appointments); + m_owner.this_month.set(appointments); }); } g_clear_pointer(&begin, g_date_time_unref); diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index 4271374..4287e1c 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -54,12 +54,12 @@ TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) LiveClock clock(zones); wait_msec(500); // wait for the bus to set up - // count how many times clock.minuteChanged() is emitted over the next minute + // count how many times clock.minute_changed() is emitted over the next minute const DateTime now = clock.localtime(); const auto gnow = now.get(); auto gthen = g_date_time_add_minutes(gnow, 1); int count = 0; - clock.minuteChanged.connect([&count](){count++;}); + clock.minute_changed.connect([&count](){count++;}); const auto msec = g_date_time_difference(gthen,gnow) / 1000; wait_msec(msec); EXPECT_EQ(1, count); @@ -95,7 +95,7 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_time_zone_unref(tz_nyc); /// change the timezones! - clock.minuteChanged.connect([this](){ + clock.minute_changed.connect([this](){ g_main_loop_quit(loop); }); g_idle_add([](gpointer gs){ @@ -124,7 +124,7 @@ TEST_F(ClockFixture, SleepTriggersSkew) wait_msec(500); // wait for the bus to set up bool skewed = false; - clock.minuteChanged.connect([&skewed, this](){ + clock.minute_changed.connect([&skewed, this](){ skewed = true; g_main_loop_quit(loop); return G_SOURCE_REMOVE; diff --git a/tests/test-formatter.cpp b/tests/test-formatter.cpp index 9950453..01df4f2 100644 --- a/tests/test-formatter.cpp +++ b/tests/test-formatter.cpp @@ -97,7 +97,7 @@ TEST_F(FormatterFixture, TestPhoneHeader) if(Set24hLocale()) { PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("%H:%M"), formatter.header_format.get()); EXPECT_EQ(std::string("18:30"), formatter.header.get()); } @@ -105,7 +105,7 @@ TEST_F(FormatterFixture, TestPhoneHeader) if(Set12hLocale()) { PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("%l:%M %p"), formatter.header_format.get()); EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); } } @@ -156,7 +156,7 @@ TEST_F(FormatterFixture, TestDesktopHeader) m_settings->show_date.set(test_case.show_date); m_settings->show_year.set(test_case.show_year); - ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); + ASSERT_STREQ(test_case.expected_format_string, f.header_format.get().c_str()); } } } @@ -196,7 +196,7 @@ TEST_F(FormatterFixture, TestUpcomingTimes) std::shared_ptr clock (new MockClock(DateTime(test_case.now))); DesktopFormatter f(clock, m_settings); - const auto fmt = f.getRelativeFormat(test_case.then); + const auto fmt = f.relative_format(test_case.then); ASSERT_EQ(test_case.expected_format_string, fmt); g_clear_pointer(&test_case.now, g_date_time_unref); @@ -239,7 +239,7 @@ TEST_F(FormatterFixture, TestEventTimes) std::shared_ptr clock(new MockClock(DateTime(test_case.now))); DesktopFormatter f(clock, m_settings); - const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + const auto fmt = f.relative_format(test_case.then, test_case.then_end); ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); g_clear_pointer(&test_case.now, g_date_time_unref); diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp index 562b358..eab8596 100644 --- a/tests/test-live-actions.cpp +++ b/tests/test-live-actions.cpp @@ -356,11 +356,11 @@ TEST_F(LiveActionsFixture, CalendarState) a2.begin = next_begin; a2.end = next_end; - m_state->planner->thisMonth.set(std::vector({a1, a2})); + m_state->planner->this_month.set(std::vector({a1, a2})); /// /// Now test the calendar state again. - /// The thisMonth field should now contain the appointments we just added. + /// The this_month field should now contain the appointments we just added. /// calendar_state = g_action_group_get_action_state (action_group, "calendar"); diff --git a/tests/test-planner.cpp b/tests/test-planner.cpp index 3072aea..b476ee8 100644 --- a/tests/test-planner.cpp +++ b/tests/test-planner.cpp @@ -47,9 +47,9 @@ TEST_F(PlannerFixture, EDS) planner.time.set(DateTime(now)); wait_msec(2500); - std::vector thisMonth = planner.thisMonth.get(); - std::cerr << thisMonth.size() << " appointments this month" << std::endl; - for(const auto& a : thisMonth) + std::vector this_month = planner.this_month.get(); + std::cerr << this_month.size() << " appointments this month" << std::endl; + for(const auto& a : this_month) std::cerr << a.summary << std::endl; } -- cgit v1.2.3 From 271b0fbf8b14a4f7a8f47de0e3a8751bd50676c3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 18:40:13 -0600 Subject: copyediting: remove dead '#if 0''ed code --- src/locations.cpp | 23 ---------------- tests/test-settings.cpp | 6 ----- tests/test-timezone-geoclue.cpp | 59 ++++------------------------------------- 3 files changed, 5 insertions(+), 83 deletions(-) diff --git a/src/locations.cpp b/src/locations.cpp index c59f988..0690acd 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -54,29 +54,6 @@ Location::Location(const std::string& zone_, const std::string& name_): 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/tests/test-settings.cpp b/tests/test-settings.cpp index df01cd7..980e7fa 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -189,9 +189,3 @@ TEST_F(SettingsFixture, Locations) g_strfreev(tmp); EXPECT_EQ(bv, vtmp); } - -#if 0 - core::Property> locations; - core::Property time_format_mode; - -#endif diff --git a/tests/test-timezone-geoclue.cpp b/tests/test-timezone-geoclue.cpp index 4bf08a7..3cc1393 100644 --- a/tests/test-timezone-geoclue.cpp +++ b/tests/test-timezone-geoclue.cpp @@ -21,64 +21,17 @@ #include -//#include - using unity::indicator::datetime::GeoclueTimezone; -/*** -**** -***/ - -class TimezoneGeoclueFixture : public GeoclueFixture -{ -}; - -#if 0 -namespace +// This test looks small because the interesting +// work is all happening in GeoclueFixture... +TEST_F(GeoclueFixture, ChangeDetected) { - struct EmitAddressChangedData - { - DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; - std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} - }; - - gboolean emit_address_changed_idle(gpointer gdata) - { - auto data = static_cast(gdata); - - GError * error = nullptr; - 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))"), - &error); - if (error) - { - g_warning("%s: %s", G_STRFUNC, error->message); - g_error_free(error); - } - - delete data; - return G_SOURCE_REMOVE; - } -} -#endif - -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()); - // start listening for a timezone change, then change the timezone + // Start listening for a timezone change, then change the timezone. bool changed = false; auto connection = tz.timezone.changed().connect( @@ -88,10 +41,8 @@ TEST_F(TimezoneGeoclueFixture, ChangeDetected) g_main_loop_quit(loop); }); + const std::string timezone_2 = "America/Chicago"; 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()); } - - -- cgit v1.2.3