diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/actions-live.cpp | 84 | ||||
-rw-r--r-- | src/actions.cpp | 215 | ||||
-rw-r--r-- | src/clock-live.cpp | 84 | ||||
-rw-r--r-- | src/clock.cpp | 32 | ||||
-rw-r--r-- | src/formatter-desktop.cpp | 258 | ||||
-rw-r--r-- | src/formatter.cpp | 146 | ||||
-rw-r--r-- | src/locations-settings.cpp | 59 | ||||
-rw-r--r-- | src/locations.cpp | 33 | ||||
-rw-r--r-- | src/main.c | 83 | ||||
-rw-r--r-- | src/main.cpp | 81 | ||||
-rw-r--r-- | src/menu.cpp | 518 | ||||
-rw-r--r-- | src/planner-eds.cpp | 283 | ||||
-rw-r--r-- | src/planner-eds.h | 58 | ||||
-rw-r--r-- | src/planner.h | 167 | ||||
-rw-r--r-- | src/service.cpp | 140 | ||||
-rw-r--r-- | src/settings-live.cpp | 293 | ||||
-rw-r--r-- | src/timezone-file.cpp | 18 | ||||
-rw-r--r-- | src/timezone-geoclue.cpp | 66 | ||||
-rw-r--r-- | src/timezones-live.cpp | 34 | ||||
-rw-r--r-- | src/utils.cpp | 139 |
21 files changed, 1926 insertions, 876 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c847ff..976adc3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,13 +12,18 @@ if (BUILD_PANEL) endif () add_library (${SERVICE_LIB} STATIC + actions.cpp + actions-live.cpp clock.cpp clock-live.cpp formatter.cpp formatter-desktop.cpp locations.cpp locations-settings.cpp + menu.cpp planner-eds.cpp + service.cpp + settings-live.cpp timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp @@ -27,9 +32,9 @@ include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) -#add_executable (${SERVICE_EXEC} main.c) -#target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) -#install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) +add_executable (${SERVICE_EXEC} main.cpp) +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) # common properties #set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} diff --git a/src/actions-live.cpp b/src/actions-live.cpp new file mode 100644 index 0000000..08e1466 --- /dev/null +++ b/src/actions-live.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/actions-live.h> + +#include <glib.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +void LiveActions::open_desktop_settings() +{ + g_message ("%s", G_STRFUNC); +} + +void LiveActions::open_phone_settings() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_clock() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_planner() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_planner_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_calendar_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_appointment(const std::string& uid) +{ + g_message("%s - %s", G_STRFUNC, uid.c_str()); +} + +void LiveActions::set_location(const std::string& zone, const std::string& name) +{ + g_message("%s - %s %s", G_STRFUNC, zone.c_str(), name.c_str()); +} + +void LiveActions::set_calendar_date(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/actions.cpp b/src/actions.cpp new file mode 100644 index 0000000..4efe950 --- /dev/null +++ b/src/actions.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/actions.h> +#include <datetime/utils.h> // split_settings_location() + +#include <glib.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +namespace +{ + +void on_desktop_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_desktop_settings(); +} + +void on_phone_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_phone_settings(); +} + +void on_phone_clock_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast<Actions*>(gself)->open_phone_clock_app(); +} + +void on_activate_appointment(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto uid = g_variant_get_string(param, nullptr); + auto self = static_cast<Actions*>(gself); + + g_return_if_fail(uid && *uid); + + // find url of the upcoming appointment with this uid + for (auto& appt : self->state()->planner->upcoming.get()) + { + if (appt.uid == uid) + { + const auto url = appt.url; + g_debug("%s: uid[%s] -> url[%s]", G_STRFUNC, uid, url.c_str()); + self->open_appointment(url); + break; + } + } +} + +void on_activate_planner(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto at = g_variant_get_int64(param); + auto self = static_cast<Actions*>(gself); + + if (at) + { + auto gdt = g_date_time_new_from_unix_local(at); + self->open_planner_at(DateTime(gdt)); + g_date_time_unref(gdt); + } + else // no time specified... + { + self->open_planner(); + } +} + +void on_set_location(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + char * zone; + char * name; + split_settings_location(g_variant_get_string(param, nullptr), &zone, &name); + static_cast<Actions*>(gself)->set_location(zone, name); + g_free(name); + g_free(zone); +} + +static void on_calendar_activated(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) +{ + const time_t t = g_variant_get_int64(state); + + g_return_if_fail(t != 0); + + static_cast<Actions*>(gself)->set_calendar_date(DateTime(t)); +} + +GVariant* create_default_header_state() +{ + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string("accessible-desc")); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string("label")); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string("title")); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(true)); + return g_variant_builder_end(&b); +} + +GVariant* create_calendar_state(std::shared_ptr<State>& state) +{ + gboolean days[32] = { 0 }; + for(const auto& appt : state->planner->thisMonth.get()) + days[appt.begin.day_of_month()] = true; + + GVariantBuilder day_builder; + g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); + for (int i=0; i<G_N_ELEMENTS(days); i++) + if (days[i]) + g_variant_builder_add(&day_builder, "i", i); + + GVariantBuilder dict_builder; + g_variant_builder_init(&dict_builder, G_VARIANT_TYPE_DICTIONARY); + + auto key = "appointment-days"; + auto v = g_variant_builder_end(&day_builder); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + key = "calendar-day"; + v = g_variant_new_int64(state->calendar_day.get().to_unix()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + key = "show-week-numbers"; + v = g_variant_new_boolean(state->show_week_numbers.get()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + return g_variant_builder_end(&dict_builder); +} +} // anonymous namespace + +/*** +**** +***/ + +Actions::Actions(std::shared_ptr<State>& state): + m_state(state), + m_actions(g_simple_action_group_new()) +{ + GActionEntry entries[] = { + { "activate-desktop-settings", on_desktop_settings_activated }, + { "activate-phone-settings", on_phone_settings_activated }, + { "activate-phone-clock-app", on_phone_clock_activated }, + { "activate-appointment", on_activate_appointment, "s", nullptr }, + { "activate-planner", on_activate_planner, "x", nullptr }, + { "set-location", on_set_location, "s" } + }; + + g_action_map_add_action_entries(G_ACTION_MAP(m_actions), + entries, + G_N_ELEMENTS(entries), + this); + + // add the header actions + auto gam = G_ACTION_MAP(m_actions); + auto v = create_default_header_state(); + auto a = g_simple_action_new_stateful("desktop-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("desktop-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + + // add the calendar action + v = create_calendar_state(state); + a = g_simple_action_new_stateful("calendar", G_VARIANT_TYPE_INT64, v); + g_action_map_add_action(gam, G_ACTION(a)); + g_signal_connect(a, "activate", G_CALLBACK(on_calendar_activated), this); + //m_calendar_action = a; + + // FIXME: rebuild the calendar state when show-week-number changes +} + +Actions::~Actions() +{ + g_clear_object(&m_actions); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 80e91a3..1fadfe8 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -28,53 +28,56 @@ class LiveClock::Impl public: Impl(LiveClock& owner, const std::shared_ptr<Timezones>& tzd): - owner_(owner), - timezones_(tzd) + m_owner(owner), + m_timezones(tzd) { - if (timezones_) + if (m_timezones) { - timezones_->timezone.changed().connect ([this](const std::string& z) {setTimezone(z);}); - setTimezone(timezones_->timezone.get()); + m_timezones->timezone.changed().connect([this](const std::string& z) {setTimezone(z);}); + setTimezone(m_timezones->timezone.get()); } - owner_.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); - setInterval(owner_.skewTestIntervalSec.get()); + m_owner.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); + setInterval(m_owner.skewTestIntervalSec.get()); } ~Impl() { clearTimer(); - g_clear_pointer (&timezone_, g_time_zone_unref); + g_clear_pointer(&m_timezone, g_time_zone_unref); } - GDateTime* localtime() const + DateTime localtime() const { - g_assert (timezone_ != nullptr); + g_assert(m_timezone != nullptr); - return g_date_time_new_now (timezone_); + auto gdt = g_date_time_new_now(m_timezone); + DateTime ret(gdt); + g_date_time_unref(gdt); + return ret; } private: - void setTimezone (const std::string& str) + void setTimezone(const std::string& str) { - g_clear_pointer (&timezone_, g_time_zone_unref); - timezone_= g_time_zone_new (str.c_str()); - owner_.skewDetected(); + g_clear_pointer(&m_timezone, g_time_zone_unref); + m_timezone= g_time_zone_new(str.c_str()); + m_owner.skewDetected(); } private: void clearTimer() { - if (skew_timeout_id_) + if (m_skew_timeout_id) { - g_source_remove(skew_timeout_id_); - skew_timeout_id_ = 0; + g_source_remove(m_skew_timeout_id); + m_skew_timeout_id = 0; } - g_clear_pointer(&prev_datetime_, g_date_time_unref); + m_prev_datetime.reset(); } void setInterval(unsigned int seconds) @@ -83,50 +86,53 @@ private: if (seconds > 0) { - prev_datetime_ = owner_.localtime(); - skew_timeout_id_ = g_timeout_add_seconds(seconds, onTimerPulse, this); + m_prev_datetime = localtime(); + m_skew_timeout_id = g_timeout_add_seconds(seconds, onTimerPulse, this); } } static gboolean onTimerPulse(gpointer gself) { - auto self = static_cast<Impl*>(gself); + static_cast<Impl*>(gself)->onTimerPulse(); + return G_SOURCE_CONTINUE; + } + void onTimerPulse() + { // check to see if too much time passed since the last check */ - GDateTime * now = self->owner_.localtime(); - const GTimeSpan diff = g_date_time_difference(now, self->prev_datetime_); + const auto now = localtime(); + const auto diff = now.difference (m_prev_datetime); const GTimeSpan fuzz = 5; - const GTimeSpan max = (self->owner_.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; + const GTimeSpan max = (m_owner.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; if (abs(diff) > max) - self->owner_.skewDetected(); + m_owner.skewDetected(); - // update prev_datetime - g_clear_pointer(&self->prev_datetime_, g_date_time_unref); - self->prev_datetime_ = now; + // check to see if the day has changed + if (now.day_of_year() != m_prev_datetime.day_of_year()) + m_owner.dateChanged(); - return G_SOURCE_CONTINUE; + // update m_prev_datetime + m_prev_datetime = now; } protected: - LiveClock& owner_; - GTimeZone * timezone_ = nullptr; - std::shared_ptr<Timezones> timezones_; + LiveClock& m_owner; + GTimeZone * m_timezone = nullptr; + std::shared_ptr<Timezones> m_timezones; - GDateTime * prev_datetime_ = nullptr; - unsigned int skew_timeout_id_ = 0; - unsigned int sleep_subscription_id_ = 0; + DateTime m_prev_datetime; + unsigned int m_skew_timeout_id = 0; }; LiveClock::LiveClock(const std::shared_ptr<Timezones>& tzd): - p (new Impl (*this, tzd)) + p(new Impl(*this, tzd)) { } LiveClock::~LiveClock() =default; -GDateTime * -LiveClock::localtime() const +DateTime LiveClock::localtime() const { return p->localtime(); } diff --git a/src/clock.cpp b/src/clock.cpp index 4a75ceb..f3f5d70 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -31,9 +31,9 @@ namespace datetime { ***/ Clock::Clock(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SYSTEM, cancellable_, onSystemBusReady, this); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); timezones.changed().connect([this](const std::set<std::string>& timezones){ g_message ("timezones changed... new count is %d", (int)timezones.size()); @@ -43,13 +43,13 @@ Clock::Clock(): Clock::~Clock() { - g_cancellable_cancel(cancellable_); - g_clear_object(&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (sleep_subscription_id_) - g_dbus_connection_signal_unsubscribe(system_bus_ , sleep_subscription_id_); + if (m_sleep_subscription_id) + g_dbus_connection_signal_unsubscribe(m_system_bus, m_sleep_subscription_id); - g_clear_object(&system_bus_); + g_clear_object(&m_system_bus); } void @@ -61,9 +61,9 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) { auto self = static_cast<Clock*>(gself); - self->system_bus_ = system_bus; + self->m_system_bus = system_bus; - self->sleep_subscription_id_ = g_dbus_connection_signal_subscribe( + self->m_sleep_subscription_id = g_dbus_connection_signal_subscribe( system_bus, nullptr, "org.freedesktop.login1.Manager", // interface @@ -78,13 +78,13 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) } void -Clock::onPrepareForSleep(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters G_GNUC_UNUSED, - gpointer gself) +Clock::onPrepareForSleep(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { static_cast<Clock*>(gself)->skewDetected(); } diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 8390967..3f942f4 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -35,165 +35,167 @@ class DesktopFormatter::Impl { public: - Impl (DesktopFormatter * owner, const std::shared_ptr<Clock>& clock): - owner_(owner), - clock_(clock), - settings_(g_settings_new(SETTINGS_INTERFACE)) - { - const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, - "changed::" SETTINGS_SHOW_DAY_S, - "changed::" SETTINGS_SHOW_DATE_S, - "changed::" SETTINGS_SHOW_YEAR_S }; - for (guint i=0, n=G_N_ELEMENTS(keys); i<n; i++) - g_signal_connect(settings_, keys[i], G_CALLBACK(onSettingsChanged), this); - - rebuildHeaderFormat(); - } +Impl(DesktopFormatter * owner, const std::shared_ptr<Clock>& clock): + m_owner(owner), + m_clock(clock), + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, + "changed::" SETTINGS_SHOW_DAY_S, + "changed::" SETTINGS_SHOW_DATE_S, + "changed::" SETTINGS_SHOW_YEAR_S }; + for(const auto& key : keys) + g_signal_connect(m_settings, key, G_CALLBACK(onSettingsChanged), this); + + rebuildHeaderFormat(); +} - ~Impl() - { - g_signal_handlers_disconnect_by_data (settings_, this); - g_object_unref (settings_); - } +~Impl() +{ + g_signal_handlers_disconnect_by_data(m_settings, this); + g_object_unref(m_settings); +} private: - static void onSettingsChanged (GSettings * changed G_GNUC_UNUSED, - const gchar * key G_GNUC_UNUSED, - gpointer gself) - { - static_cast<Impl*>(gself)->rebuildHeaderFormat(); - } +static void onSettingsChanged(GSettings * /*changed*/, + const gchar * /*key*/, + gpointer gself) +{ + static_cast<Impl*>(gself)->rebuildHeaderFormat(); +} - void rebuildHeaderFormat() - { - gchar * fmt = getHeaderLabelFormatString (settings_); - owner_->headerFormat.set(fmt); - g_free (fmt); - } +void rebuildHeaderFormat() +{ + auto fmt = getHeaderLabelFormatString(m_settings); + m_owner->headerFormat.set(fmt); + g_free(fmt); +} private: - gchar* getHeaderLabelFormatString (GSettings * s) const - { - char * fmt; - const TimeFormatMode mode = (TimeFormatMode) g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S); - - if (mode == TIME_FORMAT_MODE_CUSTOM) - { - fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S); - } - else - { - const bool show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S); - const bool show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S); - const bool show_year = show_date && g_settings_get_boolean (s, SETTINGS_SHOW_YEAR_S); - const char * date_fmt = getDateFormat (show_day, show_date, show_year); - const char * time_fmt = getFullTimeFormatString (s); - fmt = joinDateAndTimeFormatStrings (date_fmt, time_fmt); - } - - return fmt; - } +gchar* getHeaderLabelFormatString(GSettings* s) const +{ + char * fmt; + const auto mode = g_settings_get_enum(s, SETTINGS_TIME_FORMAT_S); - const gchar* T_(const gchar* in) const + if (mode == TIME_FORMAT_MODE_CUSTOM) { - return owner_->T_(in); + fmt = g_settings_get_string(s, SETTINGS_CUSTOM_TIME_FORMAT_S); } - - const gchar* getDateFormat (bool show_day, bool show_date, bool show_year) const + else { - const char * fmt; - - if (show_day && show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ - fmt = T_("%a %b %e %Y"); - else if (show_day && show_date) - fmt = T_("%a %b %e"); - else if (show_day && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ - fmt = T_("%a %Y"); - else if (show_day) - /* TRANSLATORS: a strftime(3) format showing the weekday. */ - fmt = T_("%a"); - else if (show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the date and year */ - fmt = T_("%b %e %Y"); - else if (show_date) - /* TRANSLATORS: a strftime(3) format showing the date */ - fmt = T_("%b %e"); - else if (show_year) - /* TRANSLATORS: a strftime(3) format showing the year */ - fmt = T_("%Y"); - else - fmt = nullptr; - - return fmt; + const auto show_day = g_settings_get_boolean(s, SETTINGS_SHOW_DAY_S); + const auto show_date = g_settings_get_boolean(s, SETTINGS_SHOW_DATE_S); + const auto show_year = show_date && g_settings_get_boolean(s, SETTINGS_SHOW_YEAR_S); + const auto date_fmt = getDateFormat(show_day, show_date, show_year); + const auto time_fmt = getFullTimeFormatString(s); + fmt = joinDateAndTimeFormatStrings(date_fmt, time_fmt); } - const gchar * getFullTimeFormatString (GSettings * settings) const - { - const bool show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); + return fmt; +} + +const gchar* T_(const gchar* in) const +{ + return m_owner->T_(in); +} - bool twelvehour; - switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; +const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const +{ + const char * fmt; + + if (show_day && show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ + fmt = T_("%a %b %e %Y"); + else if (show_day && show_date) + /* TRANSLATORS: a strftime(3) format showing the weekday and date */ + fmt = T_("%a %b %e"); + else if (show_day && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ + fmt = T_("%a %Y"); + else if (show_day) + /* TRANSLATORS: a strftime(3) format showing the weekday. */ + fmt = T_("%a"); + else if (show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the date and year */ + fmt = T_("%b %e %Y"); + else if (show_date) + /* TRANSLATORS: a strftime(3) format showing the date */ + fmt = T_("%b %e"); + else if (show_year) + /* TRANSLATORS: a strftime(3) format showing the year */ + fmt = T_("%Y"); + else + fmt = nullptr; + + return fmt; +} - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = FALSE; - break; +const gchar* getFullTimeFormatString(GSettings* settings) const +{ + auto show_seconds = g_settings_get_boolean(settings, SETTINGS_SHOW_SECONDS_S); + + bool twelvehour; + switch (g_settings_get_enum(settings, SETTINGS_TIME_FORMAT_S)) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; - default: - twelvehour = TRUE; - break; - } + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = false; + break; - return owner_->getDefaultHeaderTimeFormat (twelvehour, show_seconds); + default: + twelvehour = true; + break; } - gchar* joinDateAndTimeFormatStrings (const char * date_string, const char * time_string) const + return m_owner->getDefaultHeaderTimeFormat(twelvehour, show_seconds); +} + +gchar* joinDateAndTimeFormatStrings(const char* date_string, + const char* time_string) const +{ + gchar * str; + + if (date_string && time_string) + { + /* TRANSLATORS: This is a format string passed to strftime to + * combine the date and the time. The value of "%s\u2003%s" + * will result in a string like this in US English 12-hour time: + * 'Fri Jul 16 11:50 AM'. The space in between date and time is + * a Unicode en space (E28082 in UTF-8 hex). */ + str = g_strdup_printf("%s\u2003%s", date_string, time_string); + } + else if (date_string) + { + str = g_strdup(date_string); + } + else // time_string { - gchar * str; - - if (date_string && time_string) - { - /* TRANSLATORS: This is a format string passed to strftime to combine the - * date and the time. The value of "%s\u2003%s" will result in a - * string like this in US English 12-hour time: 'Fri Jul 16 11:50 AM'. - * The space in between date and time is a Unicode en space - * (E28082 in UTF-8 hex). */ - str = g_strdup_printf ("%s\u2003%s", date_string, time_string); - } - else if (date_string) - { - str = g_strdup (date_string); - } - else // time_string - { - str = g_strdup (time_string); - } - - return str; + str = g_strdup(time_string); } + return str; +} + private: - DesktopFormatter * const owner_; - std::shared_ptr<Clock> clock_; - GSettings * settings_; +DesktopFormatter * const m_owner; +std::shared_ptr<Clock> m_clock; +GSettings * m_settings; }; /*** **** ***/ -DesktopFormatter::DesktopFormatter (const std::shared_ptr<Clock>& clock): +DesktopFormatter::DesktopFormatter(const std::shared_ptr<Clock>& clock): Formatter(clock), p(new Impl(this, clock)) { diff --git a/src/formatter.cpp b/src/formatter.cpp index 4e2f582..1f26cc7 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -30,16 +30,16 @@ namespace { -void clearTimer (guint& tag) +void clearTimer(guint& tag) { if (tag) { - g_source_remove (tag); + g_source_remove(tag); tag = 0; } } -guint calculate_milliseconds_until_next_minute (GDateTime * now) +guint calculate_milliseconds_until_next_minute(GDateTime * now) { GDateTime * next; GDateTime * start_of_next; @@ -47,12 +47,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) guint interval_msec; next = g_date_time_add_minutes(now, 1); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), - 0.1); + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), + 0.1); interval_usec = g_date_time_difference(start_of_next, now); interval_msec = (interval_usec + 999) / 1000; @@ -62,12 +62,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) return interval_msec; } -gint calculate_milliseconds_until_next_second (GDateTime * now) +gint calculate_milliseconds_until_next_second(GDateTime * now) { gint interval_usec; guint interval_msec; - interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now); + interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond(now); interval_msec = (interval_usec + 999) / 1000; return interval_msec; } @@ -94,7 +94,7 @@ gint calculate_milliseconds_until_next_second (GDateTime * now) * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45. */ -guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) +guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) { char * str; gint minute; @@ -106,23 +106,22 @@ guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) minute = g_date_time_get_minute(now); minute = 15 - (minute % 15); next = g_date_time_add_minutes(now, minute); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), 0.1); str = g_date_time_format(start_of_next, "%F %T"); - g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); - g_free (str); + g_debug("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); + g_free(str); diff = g_date_time_difference(start_of_next, now); seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND; g_date_time_unref(start_of_next); g_date_time_unref(next); - return seconds; } } // anonymous namespace @@ -137,11 +136,12 @@ class Formatter::Impl { public: - Impl (Formatter* owner, const std::shared_ptr<Clock>& clock): - owner_(owner), - clock_(clock) + Impl(Formatter* owner, const std::shared_ptr<Clock>& clock): + m_owner(owner), + m_clock(clock) { - owner_->headerFormat.changed().connect([this](const std::string& fmt G_GNUC_UNUSED){updateHeader();}); + m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){updateHeader();}); + m_clock->skewDetected.connect([this](){updateHeader();}); updateHeader(); restartRelativeTimer(); @@ -149,30 +149,25 @@ public: ~Impl() { - clearTimer(header_timer_); + clearTimer(m_header_timer); } private: void updateHeader() { - GDateTime * now = clock_->localtime(); - const time_t unix = g_date_time_to_unix (now); - struct tm tm; - localtime_r (&unix, &tm); - char str[512]; - strftime (str, sizeof(str), owner_->headerFormat.get().c_str(), &tm); - owner_->header.set (str); - g_date_time_unref (now); + const auto fmt = m_owner->headerFormat.get(); + const auto str = m_clock->localtime().format(fmt); + m_owner->header.set(str); restartHeaderTimer(); } void restartHeaderTimer() { - clearTimer(header_timer_); + clearTimer(m_header_timer); - const std::string fmt = owner_->headerFormat.get(); + const std::string fmt = m_owner->headerFormat.get(); const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) || (fmt.find("%S") != std::string::npos) || (fmt.find("%T") != std::string::npos) @@ -180,17 +175,17 @@ private: || (fmt.find("%c") != std::string::npos); guint interval_msec; - GDateTime * now = clock_->localtime(); + const auto now = m_clock->localtime(); + auto str = now.format("%F %T"); if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second (now); + interval_msec = calculate_milliseconds_until_next_second(now.get()); else - interval_msec = calculate_milliseconds_until_next_minute (now); - g_date_time_unref (now); + interval_msec = calculate_milliseconds_until_next_minute(now.get()); interval_msec += 50; // add a small margin to ensure the callback // fires /after/ next is reached - header_timer_ = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + m_header_timer = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); } static gboolean onHeaderTimer(gpointer gself) @@ -203,29 +198,28 @@ private: void restartRelativeTimer() { - clearTimer(relative_timer_); + clearTimer(m_relative_timer); - GDateTime * now = clock_->localtime(); - const guint seconds = calculate_seconds_until_next_fifteen_minutes(now); - relative_timer_ = g_timeout_add_seconds(seconds, onRelativeTimer, this); - g_date_time_unref(now); + const auto now = m_clock->localtime(); + const auto seconds = calculate_seconds_until_next_fifteen_minutes(now.get()); + m_relative_timer = g_timeout_add_seconds(seconds, onRelativeTimer, this); } static gboolean onRelativeTimer(gpointer gself) { auto self = static_cast<Formatter::Impl*>(gself); - self->owner_->relativeFormatChanged(); + self->m_owner->relativeFormatChanged(); self->restartRelativeTimer(); return G_SOURCE_REMOVE; } private: - Formatter * const owner_; - guint header_timer_ = 0; - guint relative_timer_ = 0; + Formatter * const m_owner; + guint m_header_timer = 0; + guint m_relative_timer = 0; public: - std::shared_ptr<Clock> clock_; + std::shared_ptr<Clock> m_clock; }; /*** @@ -233,7 +227,7 @@ public: ***/ Formatter::Formatter(const std::shared_ptr<Clock>& clock): - p (new Formatter::Impl(this, clock)) + p(new Formatter::Impl(this, clock)) { } @@ -244,12 +238,11 @@ Formatter::~Formatter() bool Formatter::is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + const auto t_fmt = nl_langinfo(T_FMT); - for (i=0; formats_24h[i]; ++i) - if (strstr (t_fmt, formats_24h[i])) + for (const auto& needle : formats_24h) + if (strstr(t_fmt, needle)) return false; return true; @@ -272,7 +265,7 @@ Formatter::T_(const char *msg) */ char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const char *time_locale = setlocale (LC_TIME, nullptr); + const char *time_locale = setlocale(LC_TIME, nullptr); char *language = g_strdup(g_getenv("LANGUAGE")); const char *rv; @@ -329,15 +322,15 @@ typedef enum } date_proximity_t; -date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) +date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) { date_proximity_t prox = DATE_PROXIMITY_FAR; gint now_year, now_month, now_day; gint time_year, time_month, time_day; // does it happen today? - g_date_time_get_ymd (now, &now_year, &now_month, &now_day); - g_date_time_get_ymd (time, &time_year, &time_month, &time_day); + g_date_time_get_ymd(now, &now_year, &now_month, &now_day); + g_date_time_get_ymd(time, &time_year, &time_month, &time_day); if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) prox = DATE_PROXIMITY_TODAY; @@ -347,12 +340,12 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * tomorrow; gint tom_year, tom_month, tom_day; - tomorrow = g_date_time_add_days (now, 1); - g_date_time_get_ymd (tomorrow, &tom_year, &tom_month, &tom_day); + tomorrow = g_date_time_add_days(now, 1); + g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day); if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) prox = DATE_PROXIMITY_TOMORROW; - g_date_time_unref (tomorrow); + g_date_time_unref(tomorrow); } // does it happen this week? @@ -361,17 +354,17 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * week; GDateTime * week_bound; - week = g_date_time_add_days (now, 6); - week_bound = g_date_time_new_local (g_date_time_get_year(week), - g_date_time_get_month (week), - g_date_time_get_day_of_month(week), - 23, 59, 59.9); + week = g_date_time_add_days(now, 6); + week_bound = g_date_time_new_local(g_date_time_get_year(week), + g_date_time_get_month(week), + g_date_time_get_day_of_month(week), + 23, 59, 59.9); - if (g_date_time_compare (time, week_bound) <= 0) + if (g_date_time_compare(time, week_bound) <= 0) prox = DATE_PROXIMITY_WEEK; - g_date_time_unref (week_bound); - g_date_time_unref (week); + g_date_time_unref(week_bound); + g_date_time_unref(week); } return prox; @@ -398,12 +391,12 @@ std::string Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const { std::string ret; - GDateTime * now = p->clock_->localtime(); + auto now = p->m_clock->localtime().get(); if (then != nullptr) { - const bool full_day = then_end && (g_date_time_difference (then_end, then) >= G_TIME_SPAN_DAY); - const auto prox = getDateProximity (now, then); + const bool full_day = then_end && (g_date_time_difference(then_end, then) >= G_TIME_SPAN_DAY); + const auto prox = getDateProximity(now, then); if (full_day) { @@ -440,14 +433,13 @@ Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const then the time should be followed by its timezone. */ if ((then_end != nullptr) && (!full_day) && - ((g_date_time_get_utc_offset (now) != g_date_time_get_utc_offset (then)))) + ((g_date_time_get_utc_offset(now) != g_date_time_get_utc_offset(then)))) { ret += ' '; - ret += g_date_time_get_timezone_abbreviation (then); + ret += g_date_time_get_timezone_abbreviation(then); } } - g_date_time_unref (now); return ret; } diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index ed8f998..646a360 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,16 +29,16 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations (const std::string& schemaId, - const std::shared_ptr<Timezones>& timezones): - timezones_(timezones) +SettingsLocations::SettingsLocations(const std::string& schemaId, + const std::shared_ptr<Timezones>& timezones): + m_timezones(timezones) { - auto deleter = [&](GSettings* s){g_object_unref(s);}; - settings_ = std::unique_ptr<GSettings,std::function<void(GSettings*)>>(g_settings_new(schemaId.c_str()), deleter); + auto deleter = [](GSettings* s){g_object_unref(s);}; + m_settings = std::unique_ptr<GSettings,std::function<void(GSettings*)>>(g_settings_new(schemaId.c_str()), deleter); const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, "changed::" SETTINGS_SHOW_LOCATIONS_S }; - for (int i=0, n=G_N_ELEMENTS(keys); i<n; i++) - g_signal_connect_swapped (settings_.get(), keys[i], G_CALLBACK(onSettingsChanged), this); + for (const auto& key : keys) + g_signal_connect_swapped(m_settings.get(), key, G_CALLBACK(onSettingsChanged), this); timezones->timezone.changed().connect([this](const std::string&){reload();}); timezones->timezones.changed().connect([this](const std::set<std::string>&){reload();}); @@ -47,7 +47,7 @@ SettingsLocations::SettingsLocations (const std::string& schemaId, } void -SettingsLocations::onSettingsChanged (gpointer gself) +SettingsLocations::onSettingsChanged(gpointer gself) { static_cast<SettingsLocations*>(gself)->reload(); } @@ -56,48 +56,49 @@ void SettingsLocations::reload() { std::vector<Location> v; + auto settings = m_settings.get(); // add the primary timezone first - std::string zone = timezones_->timezone.get(); + auto zone = m_timezones->timezone.get(); if (!zone.empty()) { - gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); - Location l (zone, name); - v.push_back (l); - g_free (name); + gchar * name = get_current_zone_name(zone.c_str(), settings); + Location l(zone, name); + v.push_back(l); + g_free(name); } // add the other detected timezones - for (const auto& zone : timezones_->timezones.get()) + for (const auto& zone : m_timezones->timezones.get()) { - gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); - Location l (zone, name); - if (std::find (v.begin(), v.end(), l) == v.end()) - v.push_back (l); - g_free (name); + gchar * name = get_current_zone_name(zone.c_str(), settings); + Location l(zone, name); + if (std::find(v.begin(), v.end(), l) == v.end()) + v.push_back(l); + g_free(name); } // maybe add the user-specified locations - if (g_settings_get_boolean (settings_.get(), SETTINGS_SHOW_LOCATIONS_S)) + if (g_settings_get_boolean(settings, SETTINGS_SHOW_LOCATIONS_S)) { - gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + gchar ** user_locations = g_settings_get_strv(settings, SETTINGS_LOCATIONS_S); for (int i=0; user_locations[i]; i++) { gchar * zone; gchar * name; - split_settings_location (user_locations[i], &zone, &name); - Location l (zone, name); - if (std::find (v.begin(), v.end(), l) == v.end()) - v.push_back (l); - g_free (name); - g_free (zone); + split_settings_location(user_locations[i], &zone, &name); + Location l(zone, name); + if (std::find(v.begin(), v.end(), l) == v.end()) + v.push_back(l); + g_free(name); + g_free(zone); } - g_strfreev (user_locations); + g_strfreev(user_locations); } - locations.set (v); + locations.set(v); } } // namespace datetime diff --git a/src/locations.cpp b/src/locations.cpp index 5c3c52b..d6ab73a 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -26,16 +26,39 @@ namespace indicator { namespace datetime { Location::Location(const std::string& zone_, const std::string& name_): - zone(zone_), - name(name_) + m_zone(zone_), + m_name(name_) { - GTimeZone * gzone = g_time_zone_new (zone.c_str()); - GDateTime * gtime = g_date_time_new_now (gzone); - offset = g_date_time_get_utc_offset (gtime); + auto gzone = g_time_zone_new (zone().c_str()); + auto gtime = g_date_time_new_now (gzone); + m_offset = g_date_time_get_utc_offset (gtime); g_date_time_unref (gtime); g_time_zone_unref (gzone); } +#if 0 +DateTime Location::localtime(const DateTime& reference_point) const +{ +GDateTime * g_date_time_to_timezone (GDateTime *datetime, + GTimeZone *tz); + auto gzone = g_time_zone_new(zone().c_str()); + const auto gtime = reference_point.get(); + auto glocal = g_date_time_new (gzone, + g_date_time_get_year(gtime), + g_date_time_get_month(gtime), + g_date_time_get_day_of_month(gtime), + g_date_time_get_hour(gtime), + g_date_time_get_minute(gtime), + g_date_time_get_seconds(gtime)); + DateTime local(glocal); + g_date_time_unref(glocal); + g_message("reference: %zu", (size_t)reference_point.to_unix(), (size_t)local.to_unix()); + //g_date_time_unref(gtime); + g_time_zone_unref(gzone); + return local; +} +#endif + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 868d41b..0000000 --- a/src/main.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <locale.h> -#include <stdlib.h> /* exit() */ - -#include <glib/gi18n.h> -#include <gio/gio.h> -#include <libnotify/notify.h> - -#include "clock-live.h" -#include "planner-eds.h" -#include "service.h" - -/*** -**** -***/ - -static void -on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) -{ - g_message ("exiting: service couldn't acquire or lost ownership of busname"); - - g_main_loop_quit ((GMainLoop*)loop); -} - -int -main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) -{ - IndicatorDatetimeClock * clock; - IndicatorDatetimePlanner * planner; - IndicatorDatetimeService * service; - GMainLoop * loop; - - /* Work around a deadlock in glib's type initialization. It can be - * removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is - * fixed. - */ - g_type_ensure (G_TYPE_DBUS_CONNECTION); - - /* boilerplate i18n */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - /* init libnotify */ - if (!notify_init ("indicator-datetime-service")) - g_critical ("libnotify initialization failed"); - - /* create the service */ - clock = indicator_datetime_clock_live_new (); - planner = indicator_datetime_planner_eds_new (); - service = indicator_datetime_service_new (clock, planner); - - /* run */ - loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST, - G_CALLBACK(on_name_lost), loop); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - /* cleanup */ - g_object_unref (service); - g_object_unref (planner); - g_object_unref (clock); - return 0; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c246296 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <datetime/clock.h> +#include <datetime/formatter.h> +#include <datetime/locations-settings.h> +#include <datetime/menu.h> +#include <datetime/planner-eds.h> +#include <datetime/service.h> +#include <datetime/settings-shared.h> +#include <datetime/state.h> +#include <datetime/timezones-live.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <libnotify/notify.h> + +#include <iostream> + +#include <locale.h> +#include <stdlib.h> /* exit() */ + +using namespace unity::indicator::datetime; + +int +main(int /*argc*/, char** /*argv*/) +{ + // Work around a deadlock in glib's type initialization. + // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. + g_type_ensure(G_TYPE_DBUS_CONNECTION); + + // boilerplate i18n + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain(GETTEXT_PACKAGE); + + // init libnotify + if(!notify_init("indicator-datetime-service")) + g_critical("libnotify initialization failed"); + + // build the menu factory + GActionGroup * actions = G_ACTION_GROUP(g_simple_action_group_new()); + std::shared_ptr<Timezones> timezones(new LiveTimezones(TIMEZONE_FILE)); + std::shared_ptr<Clock> clock(new LiveClock(timezones)); + std::shared_ptr<Planner> planner(new PlannerEds); + std::shared_ptr<Locations> locations(new SettingsLocations(SETTINGS_INTERFACE, timezones)); + planner->time = clock->localtime(); + MenuFactory factory(actions, timezones, clock, planner, locations); + + // create the menus + std::vector<std::shared_ptr<Menu>> menus; + menus.push_back(factory.buildMenu(MenuFactory::Desktop)); + + // export them + auto loop = g_main_loop_new(nullptr, false); + Service service; + service.name_lost.connect([loop](){ + g_message("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit(loop); + }); + service.publish(actions, menus); + g_main_loop_run(loop); + g_main_loop_unref(loop); + return 0; +} diff --git a/src/menu.cpp b/src/menu.cpp new file mode 100644 index 0000000..76306cc --- /dev/null +++ b/src/menu.cpp @@ -0,0 +1,518 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/menu.h> + +#include <datetime/clock.h> +#include <datetime/formatter.h> +#include <datetime/locations.h> +#include <datetime/planner.h> +#include <datetime/state.h> +#include <datetime/timezones.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +#define ALARM_CLOCK_ICON_NAME "alarm-clock" +#define CALENDAR_ICON_NAME "calendar" + +class MenuImpl: public Menu +{ +protected: + MenuImpl(const Menu::Profile profile_in, + const std::string& name_in, + std::shared_ptr<State>& state, + std::shared_ptr<Actions>& actions, + std::shared_ptr<Formatter> formatter): + Menu(profile_in, name_in), + m_state(state), + m_actions(actions), + m_formatter(formatter) + { + // initialize the menu + create_gmenu(); + for (int i=0; i<NUM_SECTIONS; i++) + update_section(Section(i)); + + // listen for state changes so we can update the menu accordingly + m_formatter->header.changed().connect([this](const std::string&){ + update_header(); + }); + m_formatter->headerFormat.changed().connect([this](const std::string&){ + update_section(Locations); // need to update x-canonical-time-format + }); + m_formatter->relativeFormatChanged.connect([this](){ + update_section(Appointments); // uses formatter.getRelativeFormat() + update_section(Locations); // uses formatter.getRelativeFormat() + }); + m_state->show_clock.changed().connect([this](bool){ + update_header(); // update header's label + update_section(Locations); // locations' relative time may have changed + }); + m_state->show_events.changed().connect([this](bool){ + update_section(Appointments); // showing events got toggled + }); + m_state->planner->upcoming.changed().connect([this](const std::vector<Appointment>&){ + update_section(Appointments); // "upcoming" is the list of Appointments we show + }); + m_state->clock->dateChanged.connect([this](){ + update_section(Calendar); // need to update the Date menuitem + update_section(Locations); // locations' relative time may have changed + }); + m_state->locations->locations.changed().connect([this](const std::vector<Location>&) { + update_section(Locations); // "locations" is the list of Locations we show + }); + } + + virtual ~MenuImpl() + { + g_clear_object(&m_menu); + g_clear_pointer(&m_serialized_alarm_icon, g_variant_unref); + g_clear_pointer(&m_serialized_calendar_icon, g_variant_unref); + } + + virtual GVariant* create_header_state() =0; + + void update_header() + { + auto action_group = m_actions->action_group(); + auto action_name = name() + "-header"; + auto state = create_header_state(); + g_action_group_change_action_state(action_group, action_name.c_str(), state); + } + + std::shared_ptr<State> m_state; + std::shared_ptr<Actions> m_actions; + std::shared_ptr<Formatter> m_formatter; + GMenu* m_submenu = nullptr; + + GVariant* get_serialized_alarm_icon() + { + if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(ALARM_CLOCK_ICON_NAME); + m_serialized_alarm_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_alarm_icon; + } + +private: + + GVariant* get_serialized_calendar_icon() + { + if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(CALENDAR_ICON_NAME); + m_serialized_calendar_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_calendar_icon; + } + + void create_gmenu() + { + g_assert(m_submenu == nullptr); + + m_submenu = g_menu_new(); + + // build placeholders for the sections + for(int i=0; i<NUM_SECTIONS; i++) + { + GMenuItem * item = g_menu_item_new(nullptr, nullptr); + g_menu_append_item(m_submenu, item); + g_object_unref(item); + } + + // add submenu to the header + const auto detailed_action = std::string("indicator.") + name() + "-header"; + auto header = g_menu_item_new(nullptr, detailed_action.c_str()); + g_menu_item_set_attribute(header, "x-canonical-type", "s", + "com.canonical.indicator.root"); + g_menu_item_set_submenu(header, G_MENU_MODEL(m_submenu)); + g_object_unref(m_submenu); + + // add header to the menu + m_menu = g_menu_new(); + g_menu_append_item(m_menu, header); + g_object_unref(header); + } + + GMenuModel* create_calendar_section(Profile profile) + { + const bool allow_activation = (profile == Desktop) + || (profile == Phone); + const bool show_calendar = (profile == Desktop) + || (profile == DesktopGreeter); + auto menu = g_menu_new(); + + // add a menuitem that shows the current date + auto label = m_state->clock->localtime().format(_("%A, %e %B %Y")); + auto item = g_menu_item_new (label.c_str(), nullptr); + auto v = get_serialized_calendar_icon(); + g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, v); + if (allow_activation) + { + v = g_variant_new_int64(0); + const char* action = "indicator.activate-planner"; + g_menu_item_set_action_and_target_value (item, action, v); + } + g_menu_append_item(menu, item); + g_object_unref(item); + + // add calendar + if (show_calendar) + { + item = g_menu_item_new ("[calendar]", NULL); + v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value (item, "indicator.calendar", v); + g_menu_item_set_attribute (item, "x-canonical-type", + "s", "com.canonical.indicator.calendar"); + if (allow_activation) + { + g_menu_item_set_attribute (item, "activation-action", + "s", "indicator.activate-planner"); + } + g_menu_append_item (menu, item); + g_object_unref (item); + } + + return G_MENU_MODEL(menu); + } + + void add_appointments(GMenu* menu, Profile profile) + { + int n = 0; + const int MAX_APPTS = 5; + std::set<std::string> added; + + for (const auto& appt : m_state->planner->upcoming.get()) + { + if (n++ >= MAX_APPTS) + break; + + if (added.count(appt.uid)) + continue; + + added.insert(appt.uid); + + GDateTime* begin = appt.begin(); + GDateTime* end = appt.end(); + auto fmt = m_formatter->getRelativeFormat(begin, end); + auto unix_time = g_date_time_to_unix(begin); + + auto menu_item = g_menu_item_new (appt.summary.c_str(), nullptr); + g_menu_item_set_attribute (menu_item, "x-canonical-time", "x", unix_time); + g_menu_item_set_attribute (menu_item, "x-canonical-time-format", "s", fmt.c_str()); + + if (appt.has_alarms) + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.alarm"); + g_menu_item_set_attribute_value(menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + } + else + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.appointment"); + + if (!appt.color.empty()) + g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt.color.c_str()); + } + + if (profile == Phone) + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-appointment", + g_variant_new_string (appt.uid.c_str())); + else + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-planner", + g_variant_new_int64 (unix_time)); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + } + } + + GMenuModel* create_appointments_section(Profile profile) + { + auto menu = g_menu_new(); + + if (((profile==Phone) || (profile==Desktop)) && m_state->show_events.get()) + { + add_appointments (menu, profile); + + // add the 'Add Event…' menuitem + auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); + const gchar* action_name = "indicator.activate_planner"; + auto v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value(menu_item, action_name, v); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_locations_section(Profile profile) + { + GMenu* menu = g_menu_new(); + + if (profile == Desktop) + { + const auto now = m_state->clock->localtime(); + + for(const auto& location : m_state->locations->locations.get()) + { + const auto& zone = location.zone(); + const auto& name = location.name(); + const auto zone_now = now.to_timezone(zone); + const auto fmt = m_formatter->getRelativeFormat(zone_now.get()); + auto detailed_action = g_strdup_printf("indicator.set-location::%s %s", zone.c_str(), name.c_str()); + auto i = g_menu_item_new (name.c_str(), detailed_action); + g_menu_item_set_attribute(i, "x-canonical-type", "s", "com.canonical.indicator.location"); + g_menu_item_set_attribute(i, "x-canonical-timezone", "s", zone.c_str()); + g_menu_item_set_attribute(i, "x-canonical-time-format", "s", fmt.c_str()); + g_menu_append_item (menu, i); + g_object_unref(i); + g_free(detailed_action); + } + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_settings_section(Profile profile) + { + auto menu = g_menu_new(); + + if (profile == Desktop) + { + g_menu_append (menu, _("Date & Time Settings…"), "indicator.activate-desktop-settings"); + } + else if (profile == Phone) + { + g_menu_append (menu, _("Time & Date settings…"), "indicator.activate-phone-settings"); + } + + return G_MENU_MODEL (menu); + } + + void update_section(Section section) + { + GMenuModel * model; + const auto p = profile(); + + switch (section) + { + case Calendar: model = create_calendar_section(p); break; + case Appointments: model = create_appointments_section(p); break; + case Locations: model = create_locations_section(p); break; + case Settings: model = create_settings_section(p); break; + default: g_warn_if_reached(); + } + + if (model) + { + g_menu_remove(m_submenu, section); + g_menu_insert_section(m_submenu, section, nullptr, model); + g_object_unref(model); + } + } + +//private: + GVariant * m_serialized_alarm_icon = nullptr; + GVariant * m_serialized_calendar_icon = nullptr; + +}; // class MenuImpl + + + +/*** +**** +***/ + +class DesktopBaseMenu: public MenuImpl +{ +protected: + DesktopBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr<Formatter>(new DesktopFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + const auto visible = m_state->show_clock.get(); + const auto title = _("Date and Time"); + auto label = g_variant_new_string(m_formatter->header.get().c_str()); + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", label); + g_variant_builder_add(&b, "{sv}", "label", label); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(title)); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(visible)); + return g_variant_builder_end(&b); + } +}; + +class DesktopMenu: public DesktopBaseMenu +{ +public: + DesktopMenu(std::shared_ptr<State>& state_, std::shared_ptr<Actions>& actions_): + DesktopBaseMenu(Desktop,"desktop", state_, actions_) {} +}; + +class DesktopGreeterMenu: public DesktopBaseMenu +{ +public: + DesktopGreeterMenu(std::shared_ptr<State>& state_, std::shared_ptr<Actions>& actions_): + DesktopBaseMenu(DesktopGreeter,"desktop-greeter", state_, actions_) {} +}; + +class PhoneBaseMenu: public MenuImpl +{ +protected: + PhoneBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr<Formatter>(new PhoneFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + // are there alarms? + bool has_alarms = false; + for(const auto& appointment : m_state->planner->upcoming.get()) + if((has_alarms = appointment.has_alarms)) + break; + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string (_("Upcoming"))); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); + if (has_alarms) + { + auto label = m_formatter->header.get(); + auto a11y = g_strdup_printf(_("%s (has alarms)"), label.c_str()); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(label.c_str())); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_take_string(a11y)); + g_variant_builder_add(&b, "{sv}", "icon", get_serialized_alarm_icon()); + } + else + { + auto v = g_variant_new_string(m_formatter->header.get().c_str()); + g_variant_builder_add(&b, "{sv}", "label", v); + g_variant_builder_add(&b, "{sv}", "accessible-desc", v); + } + return g_variant_builder_end (&b); + } +}; + +class PhoneMenu: public PhoneBaseMenu +{ +public: + PhoneMenu(std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + PhoneBaseMenu(Phone, "phone", state_, actions_) {} +}; + +class PhoneGreeterMenu: public PhoneBaseMenu +{ +public: + PhoneGreeterMenu(std::shared_ptr<State>& state_, + std::shared_ptr<Actions>& actions_): + PhoneBaseMenu(PhoneGreeter, "phone-greeter", state_, actions_) {} +}; + +/**** +***** +****/ + +MenuFactory::MenuFactory(std::shared_ptr<Actions>& actions_, + std::shared_ptr<State>& state_): + m_actions(actions_), + m_state(state_) +{ +} + +std::shared_ptr<Menu> +MenuFactory::buildMenu(Menu::Profile profile) +{ + std::shared_ptr<Menu> menu; + m_state->show_clock.set (true); // FIXME + + //std::shared_ptr<State> state(new State); + //state->clock = clock; + //state->planner = planner; + //state->locations = locations; + + switch (profile) + { + case Menu::Desktop: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopMenu(m_state, m_actions)); + break; + + case Menu::DesktopGreeter: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopGreeterMenu(m_state, m_actions)); + break; + + case Menu::Phone: + m_state->show_events.set(true); // FIXME + menu.reset(new PhoneMenu(m_state, m_actions)); + break; + + case Menu::PhoneGreeter: + m_state->show_events.set(false); // FIXME + menu.reset(new PhoneGreeterMenu(m_state, m_actions)); + break; + + default: + g_warn_if_reached(); + break; + } + + return menu; +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 804d98e..6abaf3e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -34,21 +34,21 @@ namespace datetime { ***** ****/ -G_DEFINE_QUARK ("source-client", source_client) +G_DEFINE_QUARK("source-client", source_client) class PlannerEds::Impl { public: - Impl (PlannerEds& owner): - owner_(owner), - cancellable_(g_cancellable_new()) + Impl(PlannerEds& owner): + m_owner(owner), + m_cancellable(g_cancellable_new()) { - e_source_registry_new (cancellable_, on_source_registry_ready, this); + e_source_registry_new(m_cancellable, on_source_registry_ready, this); - owner_.time.changed().connect([this](const DateTime& dt) { - g_message ("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); + m_owner.time.changed().connect([this](const DateTime& dt) { + g_message("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); rebuildSoon(); }); @@ -57,46 +57,46 @@ public: ~Impl() { - g_cancellable_cancel (cancellable_); - g_clear_object (&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (rebuild_tag_) - g_source_remove (rebuild_tag_); + if (m_rebuild_tag) + g_source_remove(m_rebuild_tag); - if (source_registry_) - g_signal_handlers_disconnect_by_data (source_registry_, this); - g_clear_object (&source_registry_); + if (m_source_registry) + g_signal_handlers_disconnect_by_data(m_source_registry, this); + g_clear_object(&m_source_registry); } private: - static void on_source_registry_ready (GObject* /*source*/, GAsyncResult* res, gpointer gself) + static void on_source_registry_ready(GObject* /*source*/, GAsyncResult* res, gpointer gself) { - GError * error = NULL; - auto r = e_source_registry_new_finish (res, &error); - if (error != NULL) + GError * error = nullptr; + auto r = e_source_registry_new_finish(res, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot show EDS appointments: %s", error->message); - g_error_free (error); + g_error_free(error); } else { auto self = static_cast<Impl*>(gself); - g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self); - g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self); - g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self); - g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self); - g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self); + g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), self); + g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), self); + g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), self); + g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), self); + g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), self); - self->source_registry_ = r; + self->m_source_registry = r; - GList* sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); + GList* sources = e_source_registry_list_sources(r, E_SOURCE_EXTENSION_CALENDAR); for (auto l=sources; l!=nullptr; l=l->next) - on_source_added (r, E_SOURCE(l->data), gself); - g_list_free_full (sources, g_object_unref); + on_source_added(r, E_SOURCE(l->data), gself); + g_list_free_full(sources, g_object_unref); } } @@ -104,74 +104,74 @@ private: { auto self = static_cast<PlannerEds::Impl*>(gself); - self->sources_.insert(E_SOURCE(g_object_ref(source))); + self->m_sources.insert(E_SOURCE(g_object_ref(source))); if (e_source_get_enabled(source)) on_source_enabled(registry, source, gself); } - static void on_source_enabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { auto self = static_cast<PlannerEds::Impl*>(gself); - e_cal_client_connect (source, - E_CAL_CLIENT_SOURCE_TYPE_EVENTS, - self->cancellable_, - on_client_connected, - gself); + e_cal_client_connect(source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + self->m_cancellable, + on_client_connected, + gself); } - static void on_client_connected (GObject* /*source*/, GAsyncResult * res, gpointer gself) + static void on_client_connected(GObject* /*source*/, GAsyncResult * res, gpointer gself) { GError * error = nullptr; - EClient * client = e_cal_client_connect_finish (res, &error); + EClient * client = e_cal_client_connect_finish(res, &error); if (error) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot connect to EDS source: %s", error->message); - g_error_free (error); + g_error_free(error); } else { // we've got a new connected ECalClient, so store it & notify clients - g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)), - source_client_quark(), - client, - g_object_unref); + g_object_set_qdata_full(G_OBJECT(e_client_get_source(client)), + source_client_quark(), + client, + g_object_unref); - g_message ("client connected; calling rebuildSoon()"); + g_message("client connected; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } } - static void on_source_disabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { gpointer e_cal_client; // if this source has a connected ECalClient, remove it & notify clients - if ((e_cal_client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) + if ((e_cal_client = g_object_steal_qdata(G_OBJECT(source), source_client_quark()))) { - g_object_unref (e_cal_client); + g_object_unref(e_cal_client); - g_message ("source disabled; calling rebuildSoon()"); + g_message("source disabled; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } } - static void on_source_removed (ESourceRegistry* registry, ESource* source, gpointer gself) + static void on_source_removed(ESourceRegistry* registry, ESource* source, gpointer gself) { auto self = static_cast<PlannerEds::Impl*>(gself); - on_source_disabled (registry, source, gself); + on_source_disabled(registry, source, gself); - self->sources_.erase (source); - g_object_unref (source); + self->m_sources.erase(source); + g_object_unref(source); } - static void on_source_changed (ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) { - g_message ("source changed; calling rebuildSoon()"); + g_message("source changed; calling rebuildSoon()"); static_cast<Impl*>(gself)->rebuildSoon(); } @@ -181,44 +181,44 @@ private: struct Task { - Impl* impl_; - appointment_func func_; - std::vector<Appointment> appointments_; - Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + Impl* p; + appointment_func func; + std::vector<Appointment> appointments; + Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} }; struct AppointmentSubtask { - std::shared_ptr<Task> task_; - ECalClient * client_; - std::string color_; - AppointmentSubtask (const std::shared_ptr<Task>& task, ECalClient* client, const char* color): - task_(task), client_(client), color_(color) {} + std::shared_ptr<Task> task; + ECalClient* client; + std::string color; + AppointmentSubtask(const std::shared_ptr<Task>& task_in, ECalClient* client_in, const char* color_in): + task(task_in), client(client_in), color(color_in) {} }; void rebuildSoon() { const static guint ARBITRARY_INTERVAL_SECS = 2; - if (rebuild_tag_ == 0) - rebuild_tag_ = g_timeout_add_seconds (ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); + if (m_rebuild_tag == 0) + m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); } - static gboolean rebuildNowStatic (gpointer gself) + static gboolean rebuildNowStatic(gpointer gself) { auto self = static_cast<Impl*>(gself); - self->rebuild_tag_ = 0; + self->m_rebuild_tag = 0; self->rebuildNow(); return G_SOURCE_REMOVE; } void rebuildNow() { - GDateTime* calendar_date = owner_.time.get().get(); + auto calendar_date = m_owner.time.get().get(); GDateTime* begin; GDateTime* end; int y, m, d; - g_message ("in rebuildNow"); + g_message("in rebuildNow"); // get all the appointments in the calendar month g_date_time_get_ymd(calendar_date, &y, &m, &d); @@ -227,7 +227,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { - g_message ("got %d appointments in this calendar month", (int)appointments.size()); + g_message("got %d appointments in this calendar month", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -239,7 +239,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { - g_message ("got %d upcoming appointments", (int)appointments.size()); + g_message("got %d upcoming appointments", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -251,8 +251,8 @@ private: { const auto begin = g_date_time_to_unix(begin_dt); const auto end = g_date_time_to_unix(end_dt); - g_message ("getting all appointments from [%s ... %s]", g_date_time_format (begin_dt, "%F %T"), - g_date_time_format (end_dt, "%F %T")); + g_message("getting all appointments from [%s ... %s]", g_date_time_format(begin_dt, "%F %T"), + g_date_time_format(end_dt, "%F %T")); /** *** init the default timezone @@ -260,75 +260,76 @@ private: icaltimezone * default_timezone = nullptr; - const auto tz = g_date_time_get_timezone_abbreviation (owner_.time.get().get()); - g_message ("%s tz is %s", G_STRLOC, tz); + const auto tz = g_date_time_get_timezone_abbreviation(m_owner.time.get().get()); + g_message("%s tz is %s", G_STRLOC, tz); if (tz && *tz) { - default_timezone = icaltimezone_get_builtin_timezone (tz); + default_timezone = icaltimezone_get_builtin_timezone(tz); if (default_timezone == nullptr) // maybe str is a tzid? - default_timezone = icaltimezone_get_builtin_timezone_from_tzid (tz); + default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); - g_debug ("default_timezone is %p", default_timezone); + g_debug("default_timezone is %p", default_timezone); } /** *** walk through the sources to build the appointment list **/ - std::shared_ptr<Task> main_task (new Task(this, func), [](Task* task){ + std::shared_ptr<Task> main_task(new Task(this, func), [](Task* task){ g_message("time to delete task %p", task); - task->func_(task->appointments_); + task->func(task->appointments); }); - for (auto& source : sources_) + for (auto& source : m_sources) { - auto client = E_CAL_CLIENT (g_object_get_qdata (G_OBJECT(source), source_client_quark())); + auto client = E_CAL_CLIENT(g_object_get_qdata(G_OBJECT(source), source_client_quark())); if (client == nullptr) continue; if (default_timezone != nullptr) - e_cal_client_set_default_timezone (client, default_timezone); + e_cal_client_set_default_timezone(client, default_timezone); // start a new subtask to enumerate all the components in this client. - auto extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); - const auto color = e_source_selectable_get_color (E_SOURCE_SELECTABLE(extension)); - g_message ("calling e_cal_client_generate_instances for %p", client); - e_cal_client_generate_instances (client, - begin, - end, - cancellable_, - my_get_appointments_foreach, - new AppointmentSubtask (main_task, client, color), - [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); + auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); + const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); + g_message("calling e_cal_client_generate_instances for %p", client); + e_cal_client_generate_instances(client, + begin, + end, + m_cancellable, + my_get_appointments_foreach, + new AppointmentSubtask (main_task, client, color), + [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); } } struct UrlSubtask { - std::shared_ptr<Task> task_; - Appointment appointment_; - UrlSubtask (const std::shared_ptr<Task>& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + std::shared_ptr<Task> task; + Appointment appointment; + UrlSubtask(const std::shared_ptr<Task>& task_in, const Appointment& appointment_in): + task(task_in), appointment(appointment_in) {} }; static gboolean - my_get_appointments_foreach (ECalComponent* component, - time_t begin, - time_t end, - gpointer gsubtask) + my_get_appointments_foreach(ECalComponent* component, + time_t begin, + time_t end, + gpointer gsubtask) { const auto vtype = e_cal_component_get_vtype(component); auto subtask = static_cast<AppointmentSubtask*>(gsubtask); if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) { - const gchar* uid = NULL; - e_cal_component_get_uid (component, &uid); + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); auto status = ICAL_STATUS_NONE; - e_cal_component_get_status (component, &status); + e_cal_component_get_status(component, &status); - if ((uid != NULL) && + if ((uid != nullptr) && (status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) { @@ -339,78 +340,78 @@ private: however, since design only allows daily recurrence, that's all we support here. */ GSList * recur_list; - e_cal_component_get_rrule_list (component, &recur_list); - for (auto l=recur_list; l!=NULL; l=l->next) + e_cal_component_get_rrule_list(component, &recur_list); + for (auto l=recur_list; l!=nullptr; l=l->next) { const auto recur = static_cast<struct icalrecurrencetype*>(l->data); appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) && (recur->interval == 1)); } - e_cal_component_free_recur_list (recur_list); + e_cal_component_free_recur_list(recur_list); ECalComponentText text; text.value = ""; - e_cal_component_get_summary (component, &text); + e_cal_component_get_summary(component, &text); - appointment.begin = g_date_time_new_from_unix_local (begin); - appointment.end = g_date_time_new_from_unix_local (end); - appointment.color = subtask->color_; + appointment.begin = g_date_time_new_from_unix_local(begin); + appointment.end = g_date_time_new_from_unix_local(end); + appointment.color = subtask->color; appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; appointment.summary = text.value; appointment.uid = uid; - GList * alarm_uids = e_cal_component_get_alarm_uids (component); + GList * alarm_uids = e_cal_component_get_alarm_uids(component); appointment.has_alarms = alarm_uids != nullptr; - cal_obj_uid_list_free (alarm_uids); - - e_cal_client_get_attachment_uris (subtask->client_, - uid, - NULL, - subtask->task_->impl_->cancellable_, - on_appointment_uris_ready, - new UrlSubtask(subtask->task_, appointment)); + cal_obj_uid_list_free(alarm_uids); + + e_cal_client_get_attachment_uris(subtask->client, + uid, + nullptr, + subtask->task->p->m_cancellable, + on_appointment_uris_ready, + new UrlSubtask(subtask->task, appointment)); } } return G_SOURCE_CONTINUE; } - static void on_appointment_uris_ready (GObject* client, GAsyncResult* res, gpointer gsubtask) + static void on_appointment_uris_ready(GObject* client, GAsyncResult* res, gpointer gsubtask) { auto subtask = static_cast<UrlSubtask*>(gsubtask); GSList * uris = nullptr; GError * error = nullptr; - e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); - if (error != NULL) + e_cal_client_get_attachment_uris_finish(E_CAL_CLIENT(client), res, &uris, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Error getting appointment uris: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Error getting appointment uris: %s", error->message); - g_error_free (error); + g_error_free(error); } - else if (uris != NULL) + else if (uris != nullptr) { - subtask->appointment_.url = (const char*) uris->data; // copy the first URL - g_debug ("found url '%s' for appointment '%s'", subtask->appointment_.url.c_str(), subtask->appointment_.summary.c_str()); - e_client_util_free_string_slist (uris); + subtask->appointment.url = (const char*) uris->data; // copy the first URL + g_debug("found url '%s' for appointment '%s'", subtask->appointment.url.c_str(), subtask->appointment.summary.c_str()); + e_client_util_free_string_slist(uris); } - g_message ("adding appointment '%s' '%s'", subtask->appointment_.summary.c_str(), subtask->appointment_.url.c_str()); - subtask->task_->appointments_.push_back (subtask->appointment_); + g_message("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); + subtask->task->appointments.push_back(subtask->appointment); delete subtask; } private: - PlannerEds& owner_; - std::set<ESource*> sources_; - GCancellable * cancellable_ = nullptr; - ESourceRegistry * source_registry_ = nullptr; - guint rebuild_tag_ = 0; + PlannerEds& m_owner; + std::set<ESource*> m_sources; + GCancellable * m_cancellable = nullptr; + ESourceRegistry * m_source_registry = nullptr; + guint m_rebuild_tag = 0; }; -PlannerEds::PlannerEds(): impl_(new Impl(*this)) {} +PlannerEds::PlannerEds(): p(new Impl(*this)) {} PlannerEds::~PlannerEds() =default; diff --git a/src/planner-eds.h b/src/planner-eds.h deleted file mode 100644 index dea9371..0000000 --- a/src/planner-eds.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __INDICATOR_DATETIME_PLANNER_EDS__H__ -#define __INDICATOR_DATETIME_PLANNER_EDS__H__ - -#include "planner.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER_EDS (indicator_datetime_planner_eds_get_type()) -#define INDICATOR_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEds)) -#define INDICATOR_DATETIME_PLANNER_EDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEdsClass)) -#define INDICATOR_IS_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS)) - -typedef struct _IndicatorDatetimePlannerEds IndicatorDatetimePlannerEds; -typedef struct _IndicatorDatetimePlannerEdsPriv IndicatorDatetimePlannerEdsPriv; -typedef struct _IndicatorDatetimePlannerEdsClass IndicatorDatetimePlannerEdsClass; - -GType indicator_datetime_planner_eds_get_type (void); - -/** - * An IndicatorDatetimePlanner which uses Evolution Data Server - * to get its list of appointments. - */ -struct _IndicatorDatetimePlannerEds -{ - /*< private >*/ - IndicatorDatetimePlanner parent; - IndicatorDatetimePlannerEdsPriv * priv; -}; - -struct _IndicatorDatetimePlannerEdsClass -{ - IndicatorDatetimePlannerClass parent_class; -}; - -IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER_EDS__H__ */ diff --git a/src/planner.h b/src/planner.h deleted file mode 100644 index ffe8937..0000000 --- a/src/planner.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __INDICATOR_DATETIME_PLANNER__H__ -#define __INDICATOR_DATETIME_PLANNER__H__ - -#include <glib.h> -#include <glib-object.h> /* parent class */ -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER (indicator_datetime_planner_get_type()) -#define INDICATOR_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlanner)) -#define INDICATOR_DATETIME_PLANNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_DATETIME_PLANNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_IS_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER)) - -typedef struct _IndicatorDatetimePlanner IndicatorDatetimePlanner; -typedef struct _IndicatorDatetimePlannerPriv IndicatorDatetimePlannerPriv; -typedef struct _IndicatorDatetimePlannerClass IndicatorDatetimePlannerClass; - -GType indicator_datetime_planner_get_type (void); - -struct IndicatorDatetimeAppt -{ - gchar * color; - gchar * summary; - gchar * url; - gchar * uid; - GDateTime * begin; - GDateTime * end; - gboolean is_event; - gboolean is_daily; - gboolean has_alarms; -}; - -/** - * Abstract Base Class for objects that provides appointments and events. - * - * These will be listed in the appointments section of indicator-datetime's menu. - */ -struct _IndicatorDatetimePlanner -{ - /*< private >*/ - GObject parent; - IndicatorDatetimePlannerPriv * priv; -}; - -struct _IndicatorDatetimePlannerClass -{ - GObjectClass parent_class; - - /* signals */ - - void (*appointments_changed) (IndicatorDatetimePlanner * self); - - /* virtual functions */ - - void (*get_appointments) (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - - GSList* (*get_appointments_finish) (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - - - gboolean (*is_configured) (IndicatorDatetimePlanner * self); - void (*activate) (IndicatorDatetimePlanner * self); - void (*activate_time) (IndicatorDatetimePlanner * self, GDateTime *); -}; - -/*** -**** -***/ - -void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt); - -/** - * Get a list of appointments, sorted by start time. - */ -void indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - -/** - * Finishes the async call begun with indicator_datetime_planner_get_appointments() - * - * To free the list properly, use indicator_datetime_planner_free_appointments() - * - * Return value: (element-type IndicatorDatetimeAppt) - * (transfer full): - * list of appointments - */ -GSList * indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - -/** - * Convenience function for freeing a GSList of IndicatorDatetimeAppt. - * - * Equivalent to g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free); - */ -void indicator_datetime_planner_free_appointments (GSList *); - - -/** - * Returns false if the planner's backend is not configured. - * - * This can be used on startup to determine whether or not to use this planner. - */ -gboolean indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor. - */ -void indicator_datetime_planner_activate (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor, - * with an added hint of the specific time that the user would like to edit. - */ -void indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time); - -/** - * Set the timezone. - * - * This is used as a default timezone if the backend's events don't provide their own. - */ -void indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone); - -const char * indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self); - - -/** - * Emits the "appointments-changed" signal. This should only be called by subclasses. - */ -void indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER__H__ */ diff --git a/src/service.cpp b/src/service.cpp new file mode 100644 index 0000000..0671c61 --- /dev/null +++ b/src/service.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/service.h> +#include <datetime/dbus-shared.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +Service::~Service() +{ + if (m_dbus_connection != nullptr) + { + for(auto& id : m_exported_menu_ids) + g_dbus_connection_unexport_menu_model(m_dbus_connection, id); + + if (m_exported_actions_id) + g_dbus_connection_unexport_action_group(m_dbus_connection, m_exported_actions_id); + } + + if (m_own_id) + g_bus_unown_name(m_own_id); + + g_clear_object(&m_dbus_connection); +} + +/*** +**** +***/ + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("bus acquired: %s", name); + static_cast<Service*>(gthis)->on_bus_acquired(connection, name); +} + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) +{ + m_dbus_connection = static_cast<GDBusConnection*>(g_object_ref(G_OBJECT(connection))); + + // export the actions + GError * error = nullptr; + const auto id = g_dbus_connection_export_action_group(m_dbus_connection, BUS_PATH, m_actions, &error); + if (id) + { + m_exported_actions_id = id; + } + else + { + g_warning("cannot export action group: %s", error->message); + g_clear_error(&error); + } + + // export the menus + for(auto& menu : m_menus) + { + const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto id = g_dbus_connection_export_menu_model(m_dbus_connection, path.c_str(), menu->menu_model(), &error); + if (id) + { + m_exported_menu_ids.insert(id); + } + else + { + g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + g_clear_error(&error); + } + } +} + +/*** +**** +***/ + +void +Service::on_name_lost(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("name lost: %s", name); + static_cast<Service*>(gthis)->on_name_lost(connection, name); +} + +void +Service::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) +{ + name_lost(); +} + +/*** +**** +***/ + +void +Service::publish(GActionGroup* actions, std::vector<std::shared_ptr<Menu>>& menus) +{ + m_actions = actions; + m_menus = menus; + m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + nullptr, + on_name_lost, + this, + nullptr); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/src/settings-live.cpp b/src/settings-live.cpp new file mode 100644 index 0000000..74085a9 --- /dev/null +++ b/src/settings-live.cpp @@ -0,0 +1,293 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <datetime/settings-live.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +LiveSettings::~LiveSettings() +{ + g_clear_object(&m_settings); +} + +LiveSettings::LiveSettings(): + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + g_signal_connect (m_settings, "changed", G_CALLBACK(on_changed), this); + + update_show_clock(); +} + +void LiveSettings::update_show_clock() +{ + auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S); + show_clock.set(val); +} + +void LiveSettings::on_changed(GSettings* /*settings*/, + gchar* key, + gpointer gself) +{ + static_cast<LiveSettings*>(gself)->update_key(key); +} + +void LiveSettings::update_key(const std::string& key) +{ + if (key == SETTINGS_SHOW_CLOCK_S) + { + update_show_clock(); + } +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#if 0 + else if (!g_strcmp0(key, TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_SECONDS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DAY_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DATE_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_YEAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CUSTOM_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CALENDAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_WEEK_NUMBERS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_EVENTS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_LOCATIONS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIMEZONE_NAME_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DETECTED_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_LOCATIONS_S)) + { + } +#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" +#define SETTINGS_LOCATIONS_S "locations" +#define SETTINGS_TIMEZONE_NAME_S "timezone-name" + zzz + "show-clock" + +void user_function (GSettings *settings, + gchar *key, + gpointer user_data) : Has Details + + for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++) + { + g_string_printf (gstr, "changed::%s", header_settings[i]); + g_signal_connect_swapped (p->settings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + + + const char * const calendar_settings[] = { + SETTINGS_SHOW_CALENDAR_S, + SETTINGS_SHOW_WEEK_NUMBERS_S + }; + const char * const appointment_settings[] = { + SETTINGS_SHOW_EVENTS_S, + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S + }; + const char * const location_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S, + SETTINGS_SHOW_LOCATIONS_S, + SETTINGS_LOCATIONS_S, + SETTINGS_SHOW_DETECTED_S, + SETTINGS_TIMEZONE_NAME_S + }; + const char * const time_format_string_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S + }; + + + /*** + **** Listen for settings changes + ***/ + + for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++) + { + g_string_printf (gstr, "changed::%s", header_settings[i]); + g_signal_connect_swapped (p->settings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + +} + + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include <datetime/settings.h> // parent class + +#include <glib.h> // GSettings + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief #Settings implementation which uses GSettings. + */ +class LiveSettings +{ +public: + LiveSettings(); + virtual ~LiveSettings(); + +private: + GSettings* m_settings; + + // we've got a raw pointer here, so disable copying + LiveSettings(const LiveSettings&) =delete; + LiveSettings& operator=(const LiveSettings&) =delete; +}; + + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H +/* + * Copyright 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Ted Gould <ted@canonical.com> + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_SHARED +#define INDICATOR_DATETIME_SETTINGS_SHARED + +typedef enum +{ + TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM +} +TimeFormatMode; + + +#endif // INDICATOR_DATETIME_SETTINGS_SHARED +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include <datetime/settings-shared.h> + +#include <core/property.h> + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Interface that represents user-configurable settings. + * + * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml + * For more information. + */ +class Settings +{ +public: + Settings() =default; + virtual ~Settings =default; + + core::Property<TimeFormatMode> time_format_mode; + core::Property<bool> show_clock; + core::Property<bool> show_day; + core::Property<bool> show_year; + core::Property<bool> show_seconds; + core::Property<std::string> custom_time_format; + core::Property<bool> show_calendar; + core::Property<bool> show_events; + core::Property<bool> show_locations; + core::Property<bool> show_auto_detected_location; + core::Property<std::vector<std::string>> locations; + core::Property<std::string> timezone_name; +}; + +#endif // INDICATOR_DATETIME_SETTINGS_H +#endif diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp index f1d0dca..3a0c240 100644 --- a/src/timezone-file.cpp +++ b/src/timezone-file.cpp @@ -26,12 +26,12 @@ namespace datetime { void FileTimezone::clear() { - if (monitor_handler_id_) - g_signal_handler_disconnect(monitor_, monitor_handler_id_); + if (m_monitor_handler_id) + g_signal_handler_disconnect(m_monitor, m_monitor_handler_id); - g_clear_object (&monitor_); + g_clear_object (&m_monitor); - filename_.clear(); + m_filename.clear(); } void @@ -39,11 +39,11 @@ FileTimezone::setFilename(const std::string& filename) { clear(); - filename_ = filename; + m_filename = filename; auto file = g_file_new_for_path(filename.c_str()); GError * err = nullptr; - monitor_ = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); + m_monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); g_object_unref(file); if (err) { @@ -52,7 +52,7 @@ FileTimezone::setFilename(const std::string& filename) } else { - monitor_handler_id_ = g_signal_connect_swapped(monitor_, "changed", G_CALLBACK(onFileChanged), this); + m_monitor_handler_id = g_signal_connect_swapped(m_monitor, "changed", G_CALLBACK(onFileChanged), this); g_debug("%s Monitoring timezone file '%s'", G_STRLOC, filename.c_str()); } @@ -71,9 +71,9 @@ FileTimezone::reload() GError * err = nullptr; gchar * str = nullptr; - if (!g_file_get_contents(filename_.c_str(), &str, nullptr, &err)) + if (!g_file_get_contents(m_filename.c_str(), &str, nullptr, &err)) { - g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename_.c_str(), err->message); + g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, m_filename.c_str(), err->message); g_error_free(err); } else diff --git a/src/timezone-geoclue.cpp b/src/timezone-geoclue.cpp index f6d1f5e..b8847a4 100644 --- a/src/timezone-geoclue.cpp +++ b/src/timezone-geoclue.cpp @@ -27,20 +27,20 @@ namespace datetime { GeoclueTimezone::GeoclueTimezone(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SESSION, cancellable_, on_bus_got, this); + g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_got, this); } GeoclueTimezone::~GeoclueTimezone() { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); + g_cancellable_cancel(m_cancellable); + g_object_unref(m_cancellable); - if (signal_subscription_) - g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_); + if (m_signal_subscription) + g_dbus_connection_signal_unsubscribe(m_connection, m_signal_subscription); - g_object_unref(connection_); + g_object_unref(m_connection); } /*** @@ -48,7 +48,9 @@ GeoclueTimezone::~GeoclueTimezone() ***/ void -GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_bus_got(GObject* /*source*/, + GAsyncResult* res, + gpointer gself) { GError * error; GDBusConnection * connection; @@ -66,9 +68,9 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, { auto self = static_cast<GeoclueTimezone*>(gself); - self->connection_ = connection; + self->m_connection = connection; - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, "/org/freedesktop/Geoclue/Master", "org.freedesktop.Geoclue.Master", @@ -77,7 +79,7 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, G_VARIANT_TYPE("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_client_created, self); } @@ -93,51 +95,51 @@ GeoclueTimezone::on_client_created(GObject * source, GAsyncResult * res, gpointe auto self = static_cast<GeoclueTimezone*>(gself); GVariant * child = g_variant_get_child_value(result, 0); - self->client_object_path_ = g_variant_get_string(child, nullptr); + self->m_client_object_path = g_variant_get_string(child, nullptr); g_variant_unref(child); g_variant_unref(result); - self->signal_subscription_ = g_dbus_connection_signal_subscribe( - self->connection_, + self->m_signal_subscription = g_dbus_connection_signal_subscribe( + self->m_connection, GEOCLUE_BUS_NAME, "org.freedesktop.Geoclue.Address", // inteface "AddressChanged", // signal name - self->client_object_path_.c_str(), // object path + self->m_client_object_path.c_str(), // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_address_changed, self, nullptr); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "SetRequirements", g_variant_new("(iibi)", 2, 0, FALSE, 1023), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_requirements_set, self); } } void -GeoclueTimezone::on_address_changed(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters, - gpointer gself) +GeoclueTimezone::on_address_changed(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* parameters, + gpointer gself) { static_cast<GeoclueTimezone*>(gself)->setTimezoneFromAddressVariant(parameters); } void -GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_requirements_set(GObject* source, GAsyncResult* res, gpointer gself) { GVariant * result; @@ -145,16 +147,16 @@ GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpoin { auto self = static_cast<GeoclueTimezone*>(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "AddressStart", nullptr, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_started, self); @@ -171,16 +173,16 @@ GeoclueTimezone::on_address_started(GObject * source, GAsyncResult * res, gpoint { auto self = static_cast<GeoclueTimezone*>(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_got, self); diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp index cb5e2bc..dc14021 100644 --- a/src/timezones-live.cpp +++ b/src/timezones-live.cpp @@ -24,37 +24,35 @@ namespace unity { namespace indicator { namespace datetime { -LiveTimezones::LiveTimezones (const std::string& filename): - file_ (filename) +LiveTimezones::LiveTimezones(const std::string& filename): + m_file(filename) { - file_.timezone.changed().connect([this](const std::string&){updateTimezones();}); + m_file.timezone.changed().connect([this](const std::string&){update_timezones();}); - geolocationEnabled.changed().connect([this](bool){updateGeolocation();}); - updateGeolocation(); + geolocation_enabled.changed().connect([this](bool){update_geolocation();}); + update_geolocation(); - updateTimezones(); + update_timezones(); } -void -LiveTimezones::updateGeolocation() +void LiveTimezones::update_geolocation() { - geo_.reset(); + m_geo.reset(); - if (geolocationEnabled.get()) + if(geolocation_enabled.get()) { - GeoclueTimezone * geo = new GeoclueTimezone(); - geo->timezone.changed().connect([this](const std::string&){updateTimezones();}); - geo_.reset(geo); + auto geo = new GeoclueTimezone(); + geo->timezone.changed().connect([this](const std::string&){update_timezones();}); + m_geo.reset(geo); } } -void -LiveTimezones::updateTimezones() +void LiveTimezones::update_timezones() { - const std::string a = file_.timezone.get(); - const std::string b = geo_ ? geo_->timezone.get() : ""; + const auto a = m_file.timezone.get(); + const auto b = m_geo ? m_geo->timezone.get() : ""; - timezone.set (a.empty() ? b : a); + timezone.set(a.empty() ? b : a); std::set<std::string> zones; if (!a.empty()) diff --git a/src/utils.cpp b/src/utils.cpp index 42e034e..acd9796 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,104 +37,101 @@ with this program. If not, see <http://www.gnu.org/licenses/>. /* Check the system locale setting to see if the format is 24-hour time or 12-hour time */ gboolean -is_locale_12h (void) +is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + const char *t_fmt = nl_langinfo(T_FMT); - for (i = 0; formats_24h[i]; ++i) { - if (strstr (t_fmt, formats_24h[i])) { - return FALSE; - } - } + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + for(const auto& format : formats_24h) + if(strstr(t_fmt, format) != nullptr) + return false; - return TRUE; + return true; } void -split_settings_location (const gchar * location, gchar ** zone, gchar ** name) +split_settings_location(const gchar* location, gchar** zone, gchar** name) { - gchar * location_dup; - gchar * first; + auto location_dup = g_strdup(location); + g_strstrip(location_dup); - location_dup = g_strdup (location); - g_strstrip (location_dup); + gchar* first; + if((first = strchr(location_dup, ' '))) + *first = '\0'; - if ((first = strchr (location_dup, ' '))) - *first = '\0'; - - if (zone != NULL) + if(zone) { - *zone = location_dup; + *zone = location_dup; } - if (name != NULL) + if(name != nullptr) { - gchar * after = first ? g_strstrip (first + 1) : NULL; + gchar* after = first ? g_strstrip(first + 1) : nullptr; - if (after && *after) + if(after && *after) { - *name = g_strdup (after); + *name = g_strdup(after); } - else /* make the name from zone */ + else // make the name from zone { - gchar * chr = strrchr (location_dup, '/'); - after = g_strdup (chr ? chr + 1 : location_dup); + gchar * chr = strrchr(location_dup, '/'); + after = g_strdup(chr ? chr + 1 : location_dup); - /* replace underscores with spaces */ - for (chr=after; chr && *chr; chr++) - if (*chr == '_') - *chr = ' '; + // replace underscores with spaces + for(chr=after; chr && *chr; chr++) + if(*chr == '_') + *chr = ' '; - *name = after; + *name = after; } } } -gchar * -get_current_zone_name (const gchar * location, GSettings * settings) +gchar* +get_current_zone_name(const gchar* location, GSettings* settings) { - gchar * new_zone, * new_name; - gchar * tz_name; - gchar * old_zone, * old_name; - gchar * rv; - - split_settings_location (location, &new_zone, &new_name); - - tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S); - split_settings_location (tz_name, &old_zone, &old_name); - g_free (tz_name); - - /* new_name is always just a sanitized version of a timezone. - old_name is potentially a saved "pretty" version of a timezone name from - geonames. So we prefer to use it if available and the zones match. */ - - if (g_strcmp0 (old_zone, new_zone) == 0) { - rv = old_name; - old_name = NULL; - } - else { - rv = new_name; - new_name = NULL; - } - - g_free (new_zone); - g_free (old_zone); - g_free (new_name); - g_free (old_name); - - return rv; + gchar* new_zone; + gchar* new_name; + split_settings_location(location, &new_zone, &new_name); + + auto tz_name = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + gchar* old_zone; + gchar* old_name; + split_settings_location(tz_name, &old_zone, &old_name); + g_free(tz_name); + + /* new_name is always just a sanitized version of a timezone. + old_name is potentially a saved "pretty" version of a timezone name from + geonames. So we prefer to use it if available and the zones match. */ + + gchar* rv; + if (g_strcmp0(old_zone, new_zone) == 0) + { + rv = old_name; + old_name = nullptr; + } + else + { + rv = new_name; + new_name = nullptr; + } + + g_free(new_zone); + g_free(old_zone); + g_free(new_name); + g_free(old_name); + return rv; } gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) { - using unity::indicator::datetime::Clock; - using unity::indicator::datetime::MockClock; - using unity::indicator::datetime::DesktopFormatter; - - std::shared_ptr<Clock> clock(new MockClock(now)); - DesktopFormatter formatter(clock); - return g_strdup (formatter.getRelativeFormat(then).c_str()); + using unity::indicator::datetime::Clock; + using unity::indicator::datetime::DateTime; + using unity::indicator::datetime::MockClock; + using unity::indicator::datetime::DesktopFormatter; + + std::shared_ptr<Clock> clock(new MockClock(DateTime(now))); + DesktopFormatter formatter(clock); + return g_strdup(formatter.getRelativeFormat(then).c_str()); } |