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 --- src/clock-live.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/clock.cpp | 98 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 src/clock-live.cpp create mode 100644 src/clock.cpp (limited to 'src') 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 -- 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 --- src/timezone-file.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/timezone-file.cpp (limited to 'src') 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 -- 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 --- src/timezone-geoclue.cpp | 248 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 src/timezone-geoclue.cpp (limited to 'src') 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 + -- 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 --- src/formatter-desktop.cpp | 210 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/formatter-desktop.cpp (limited to 'src') 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 -- 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 --- src/planner-eds.cpp | 419 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 src/planner-eds.cpp (limited to 'src') 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 -- 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 --- src/locations-settings.cpp | 105 +++++++++++++++++++++++++++++++++++++++++++++ src/locations.cpp | 41 ++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/locations-settings.cpp create mode 100644 src/locations.cpp (limited to 'src') 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 -- 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 --- 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 ----- 21 files changed, 693 insertions(+), 2936 deletions(-) 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 (limited to 'src') 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 -- 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 --- 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 ++++++------ 21 files changed, 1926 insertions(+), 876 deletions(-) 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 (limited to 'src') 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()); } -- cgit v1.2.3 From bcff13b6bce18604472e5954eb5ab7af4bb43b0f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 15:10:39 -0600 Subject: Implement Settings, a properties-cpp wrapper around GSettings --- src/settings-live.cpp | 410 +++++++++++++++++++++++--------------------------- 1 file changed, 187 insertions(+), 223 deletions(-) (limited to 'src') 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 -- 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. --- src/formatter-desktop.cpp | 186 ++++++++++++++++++---------------------------- src/formatter.cpp | 49 ++++++------ 2 files changed, 92 insertions(+), 143 deletions(-) (limited to 'src') 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 -- 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. --- src/locations-settings.cpp | 50 +++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'src') 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); -- 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. --- src/actions.cpp | 4 ++-- src/menu.cpp | 20 +++++--------------- 2 files changed, 7 insertions(+), 17 deletions(-) (limited to 'src') 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; -- 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. --- src/utils.cpp | 89 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 36 deletions(-) (limited to 'src') 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()); } -- cgit v1.2.3 From 40689ea36c360cb6fb42048f5d93303237745b86 Mon Sep 17 00:00:00 2001 From: Charles Kerr 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 --- src/timezones-live.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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 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 --- src/actions-live.cpp | 9 ++------- src/actions.cpp | 6 +++--- 2 files changed, 5 insertions(+), 10 deletions(-) (limited to 'src') 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()) { -- 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 --- src/actions-live.cpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 13 deletions(-) (limited to 'src') 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); +} /*** **** -- 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 --- src/CMakeLists.txt | 4 +- src/date-time.cpp | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/date-time.cpp (limited to 'src') 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 --- src/main.cpp | 46 +++++++++++-------------------------------- src/state-live.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 34 deletions(-) create mode 100644 src/state-live.cpp (limited to 'src') 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 --- src/exporter.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 6 +-- src/planner-eds.cpp | 2 +- src/service.cpp | 140 -------------------------------------------------- 4 files changed, 148 insertions(+), 144 deletions(-) create mode 100644 src/exporter.cpp delete mode 100644 src/service.cpp (limited to 'src') 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 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(+) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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 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(-) (limited to 'src') 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(-) (limited to 'src') 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. --- src/state-live.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') 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); -- 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(-) (limited to 'src') 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 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 --- src/clock-live.cpp | 1 + src/clock.cpp | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'src') 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 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. --- src/clock-live.cpp | 113 +++++++++++++++++++++++++++++++---------------------- src/clock.cpp | 2 +- src/date-time.cpp | 65 ++++++++++++------------------ src/formatter.cpp | 106 ++++++++++++++++++++++--------------------------- 4 files changed, 139 insertions(+), 147 deletions(-) (limited to 'src') 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: -- 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 --- src/actions-live.cpp | 5 ----- src/actions.cpp | 30 ++++++++++++++++++++++++++++-- src/state-live.cpp | 1 - 3 files changed, 28 insertions(+), 8 deletions(-) (limited to 'src') 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(); } /*** -- 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 --- src/CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src') 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}") - - -- 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(-) (limited to 'src') 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 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(-) (limited to 'src') 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 --- src/CMakeLists.txt | 3 +- src/utils.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.cpp | 154 --------------------------- 3 files changed, 309 insertions(+), 155 deletions(-) create mode 100644 src/utils.c delete mode 100644 src/utils.cpp (limited to 'src') 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. --- src/formatter-desktop.cpp | 1 + src/formatter.cpp | 172 ++-------------------------------------------- 2 files changed, 6 insertions(+), 167 deletions(-) (limited to 'src') 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 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(-) (limited to 'src') 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(+) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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 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 ++ 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src') 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); -- 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 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') 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 -- 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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); -- 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 (limited to 'src') 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 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') 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); } -- 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 --- src/actions.cpp | 11 +++++++++++ src/locations.cpp | 10 ++++++++++ src/menu.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) (limited to 'src') 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 --- 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 ++++++++++++++ 7 files changed, 99 insertions(+) create mode 100644 src/appointment.cpp (limited to 'src') 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() --- src/CMakeLists.txt | 1 - src/main.cpp | 21 +++++++++++++++++--- src/state-live.cpp | 56 ------------------------------------------------------ 3 files changed, 18 insertions(+), 60 deletions(-) delete mode 100644 src/state-live.cpp (limited to 'src') 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. --- src/actions.cpp | 6 +++--- src/menu.cpp | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'src') 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& --- src/exporter.cpp | 4 ++-- src/menu.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') 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. --- 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 ++- 6 files changed, 24 insertions(+), 23 deletions(-) (limited to 'src') 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 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() --- 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 +- 7 files changed, 32 insertions(+), 32 deletions(-) (limited to 'src') 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); -- 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 ----------------------- 1 file changed, 23 deletions(-) (limited to 'src') 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 -- cgit v1.2.3