diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2015-04-09 13:08:24 -0500 |
---|---|---|
committer | Charles Kerr <charles.kerr@canonical.com> | 2015-04-09 13:08:24 -0500 |
commit | 8d282df37c72ff27a006ff928f1b151d98aa0e45 (patch) | |
tree | 89d5ecc46c7e674e753e907f48d8e8acd4935d1a /src | |
parent | d5002c6b27cf0e00828555a10e89778008d6cd01 (diff) | |
parent | 49f49d4c18c2cc77a1a305c93a74e9e8ec903526 (diff) | |
download | ayatana-indicator-datetime-8d282df37c72ff27a006ff928f1b151d98aa0e45.tar.gz ayatana-indicator-datetime-8d282df37c72ff27a006ff928f1b151d98aa0e45.tar.bz2 ayatana-indicator-datetime-8d282df37c72ff27a006ff928f1b151d98aa0e45.zip |
sync with trunk
Diffstat (limited to 'src')
-rw-r--r-- | src/actions-live.cpp | 19 | ||||
-rw-r--r-- | src/actions.cpp | 4 | ||||
-rw-r--r-- | src/alarm-queue-simple.cpp | 215 | ||||
-rw-r--r-- | src/appointment.cpp | 12 | ||||
-rw-r--r-- | src/awake.cpp | 63 | ||||
-rw-r--r-- | src/date-time.cpp | 38 | ||||
-rw-r--r-- | src/engine-eds.cpp | 189 | ||||
-rw-r--r-- | src/main.cpp | 12 | ||||
-rw-r--r-- | src/planner-snooze.cpp | 16 | ||||
-rw-r--r-- | src/snap.cpp | 16 |
10 files changed, 395 insertions, 189 deletions
diff --git a/src/actions-live.cpp b/src/actions-live.cpp index 4d1f770..3cbfb78 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -135,12 +135,19 @@ void LiveActions::phone_open_alarm_app() void LiveActions::phone_open_appointment(const Appointment& appt) { - if (!appt.url.empty()) - dispatch_url(appt.url); - else if (appt.is_ubuntu_alarm()) - phone_open_alarm_app(); - else - phone_open_calendar_app(DateTime::NowLocal()); + if (!appt.activation_url.empty()) + { + dispatch_url(appt.activation_url); + } + else switch (appt.type) + { + case Appointment::UBUNTU_ALARM: + phone_open_alarm_app(); + break; + + default: + phone_open_calendar_app(appt.begin); + } } void LiveActions::phone_open_calendar_app(const DateTime&) diff --git a/src/actions.cpp b/src/actions.cpp index 839c9cd..930e100 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -43,7 +43,7 @@ DateTime datetime_from_timet_variant(GVariant* v) t = g_variant_get_int64(v); if (t != 0) - return DateTime(t); + return DateTime::Local(t); else return DateTime::NowLocal(); } @@ -143,7 +143,7 @@ void on_calendar_activated(GSimpleAction * /*action*/, g_return_if_fail(t != 0); - auto dt = DateTime(t).start_of_day(); + auto dt = DateTime::Local(t).start_of_day(); static_cast<Actions*>(gself)->set_calendar_date(dt); } diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp index f45e61a..921e8d2 100644 --- a/src/alarm-queue-simple.cpp +++ b/src/alarm-queue-simple.cpp @@ -20,134 +20,165 @@ #include <datetime/alarm-queue-simple.h> #include <cmath> +#include <set> namespace unity { namespace indicator { namespace datetime { /*** -**** Public API +**** ***/ -SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr<Clock>& clock, - const std::shared_ptr<Planner>& planner, - const std::shared_ptr<WakeupTimer>& timer): - m_clock(clock), - m_planner(planner), - m_timer(timer), - m_datetime(clock->localtime()) +class SimpleAlarmQueue::Impl { - m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ - g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); - requeue(); - }); - - m_clock->minute_changed.connect([=]{ - const auto now = m_clock->localtime(); - constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; - const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; - m_datetime = now; - if (clock_jumped) { - g_debug("AlarmQueue %p calling requeue() due to clock skew", this); +public: + + Impl(const std::shared_ptr<Clock>& clock, + const std::shared_ptr<Planner>& planner, + const std::shared_ptr<WakeupTimer>& timer): + m_clock{clock}, + m_planner{planner}, + m_timer{timer}, + m_datetime{clock->localtime()} + { + m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ + g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); requeue(); - } - }); + }); + + m_clock->minute_changed.connect([this]{ + const auto now = m_clock->localtime(); + constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; + const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; + m_datetime = now; + if (clock_jumped) { + g_debug("AlarmQueue %p calling requeue() due to clock skew", this); + requeue(); + } + }); + + m_timer->timeout().connect([this](){ + g_debug("AlarmQueue %p calling requeue() due to timeout", this); + requeue(); + }); - m_timer->timeout().connect([this](){ - g_debug("AlarmQueue %p calling requeue() due to timeout", this); requeue(); - }); + } - requeue(); -} + ~Impl() + { + } -SimpleAlarmQueue::~SimpleAlarmQueue() -{ -} + core::Signal<const Appointment&, const Alarm&>& alarm_reached() + { + return m_alarm_reached; + } -core::Signal<const Appointment&>& SimpleAlarmQueue::alarm_reached() -{ - return m_alarm_reached; -} +private: -/*** -**** -***/ + void requeue() + { + const auto appointments = m_planner->appointments().get(); + const Alarm* alarm; + + // kick any current alarms + for (const auto& appointment : appointments) + { + if ((alarm = appointment_get_current_alarm(appointment))) + { + m_triggered.insert(std::make_pair(appointment.uid, alarm->time)); + m_alarm_reached(appointment, *alarm); + } + } -void SimpleAlarmQueue::requeue() -{ - // kick any current alarms - for (auto current : find_current_alarms()) + // idle until the next alarm + if ((alarm = find_next_alarm(appointments))) + { + g_debug ("setting timer to wake up for next appointment '%s' at %s", + alarm->text.c_str(), + alarm->time.format("%F %T").c_str()); + + m_timer->set_wakeup_time(alarm->time); + } + } + + bool already_triggered (const Appointment& appt, const Alarm& alarm) const { - const std::pair<std::string,DateTime> trig {current.uid, current.begin}; - m_triggered.insert(trig); - m_alarm_reached(current); + const std::pair<const std::string&,const DateTime&> key{appt.uid, alarm.time}; + return m_triggered.count(key) != 0; } - // idle until the next alarm - Appointment next; - if (find_next_alarm(next)) + // return the next Alarm (if any) that will kick now or in the future + const Alarm* find_next_alarm(const std::vector<Appointment>& appointments) const { - g_debug ("setting timer to wake up for next appointment '%s' at %s", - next.summary.c_str(), - next.begin.format("%F %T").c_str()); + const Alarm* best {}; + const auto now = m_clock->localtime(); + const auto beginning_of_minute = now.start_of_minute(); - m_timer->set_wakeup_time(next.begin); - } -} + g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); -// find the next alarm that will kick now or in the future -bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const -{ - bool found = false; - Appointment tmp; - const auto now = m_clock->localtime(); - const auto beginning_of_minute = now.start_of_minute(); + for(const auto& appointment : appointments) + { + for(const auto& alarm : appointment.alarms) + { + if (already_triggered(appointment, alarm)) + continue; - const auto appointments = m_planner->appointments().get(); - g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); + if (alarm.time < beginning_of_minute) // has this one already passed? + continue; - for(const auto& walk : appointments) - { - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; - if (m_triggered.count(trig)) - continue; + if (best && (best->time < alarm.time)) // do we already have a better match? + continue; - if (walk.begin < beginning_of_minute) // has this one already passed? - continue; + best = &alarm; + } + } - if (found && (tmp.begin < walk.begin)) // do we already have a better match? - continue; + return best; + } + + // return the Appointment's current Alarm (if any) + const Alarm* appointment_get_current_alarm(const Appointment& appointment) const + { + const auto now = m_clock->localtime(); - tmp = walk; - found = true; + for (const auto& alarm : appointment.alarms) + if (!already_triggered(appointment, alarm) && DateTime::is_same_minute(now, alarm.time)) + return &alarm; + + return nullptr; } - if (found) - setme = tmp; - return found; -} + std::set<std::pair<std::string,DateTime>> m_triggered; + const std::shared_ptr<Clock> m_clock; + const std::shared_ptr<Planner> m_planner; + const std::shared_ptr<WakeupTimer> m_timer; + core::Signal<const Appointment&, const Alarm&> m_alarm_reached; + DateTime m_datetime; +}; -// find the alarm(s) that should kick right now -std::vector<Appointment> SimpleAlarmQueue::find_current_alarms() const -{ - std::vector<Appointment> appointments; +/*** +**** Public API +***/ - const auto now = m_clock->localtime(); - for(const auto& walk : m_planner->appointments().get()) - { - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; - if (m_triggered.count(trig)) // did we already use this one? - continue; - if (!DateTime::is_same_minute(now, walk.begin)) - continue; +SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr<Clock>& clock, + const std::shared_ptr<Planner>& planner, + const std::shared_ptr<WakeupTimer>& timer): + impl{new Impl{clock, planner, timer}} +{ +} - appointments.push_back(walk); - } +SimpleAlarmQueue::~SimpleAlarmQueue() +{ +} - return appointments; +core::Signal<const Appointment&, const Alarm&>& +SimpleAlarmQueue::alarm_reached() +{ + return impl->alarm_reached(); } /*** diff --git a/src/appointment.cpp b/src/appointment.cpp index ae71459..1edd93c 100644 --- a/src/appointment.cpp +++ b/src/appointment.cpp @@ -27,16 +27,22 @@ namespace datetime { ***** ****/ +bool Alarm::operator==(const Alarm& that) const +{ + return (text==that.text) + && (audio_url==that.audio_url) + && (this->time==that.time); +} + bool Appointment::operator==(const Appointment& that) const { return (type==that.type) && (uid==that.uid) && (color==that.color) && (summary==that.summary) - && (url==that.url) - && (audio_url==that.audio_url) && (begin==that.begin) - && (end==that.end); + && (end==that.end) + && (alarms==that.alarms); } /**** diff --git a/src/awake.cpp b/src/awake.cpp index 57358ab..e1bec6c 100644 --- a/src/awake.cpp +++ b/src/awake.cpp @@ -48,10 +48,16 @@ public: g_cancellable_cancel (m_cancellable); g_object_unref (m_cancellable); + if (m_display_on_timer) + { + g_source_remove (m_display_on_timer); + m_display_on_timer = 0; + } + if (m_system_bus != nullptr) { unforce_awake (); - unforce_screen (); + remove_display_on_request (); g_object_unref (m_system_bus); } } @@ -106,7 +112,7 @@ private: G_DBUS_CALL_FLAGS_NONE, -1, self->m_cancellable, - on_force_screen_response, + on_keep_display_on_response, self); g_object_unref (system_bus); @@ -146,9 +152,9 @@ private: } } - static void on_force_screen_response (GObject * connection, - GAsyncResult * res, - gpointer gself) + static void on_keep_display_on_response (GObject * connection, + GAsyncResult * res, + gpointer gself) { GError * error; GVariant * args; @@ -171,14 +177,29 @@ private: { auto self = static_cast<Impl*>(gself); - self->m_screen_cookie = NO_SCREEN_COOKIE; - g_variant_get (args, "(i)", &self->m_screen_cookie); - g_debug ("m_screen_cookie is now '%d'", self->m_screen_cookie); + self->m_display_on_cookie = NO_DISPLAY_ON_COOKIE; + g_variant_get (args, "(i)", &self->m_display_on_cookie); + g_debug ("m_display_on_cookie is now '%d'", self->m_display_on_cookie); + + self->m_display_on_timer = g_timeout_add_seconds (self->m_display_on_seconds, + on_display_on_timer, + gself); g_variant_unref (args); } } + static gboolean on_display_on_timer (gpointer gself) + { + auto self = static_cast<Impl*>(gself); + + self->m_display_on_timer = 0; + self->remove_display_on_request(); + + return G_SOURCE_REMOVE; + } + + void unforce_awake () { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); @@ -202,18 +223,18 @@ private: } } - void unforce_screen () + void remove_display_on_request () { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); - if (m_screen_cookie != NO_SCREEN_COOKIE) + if (m_display_on_cookie != NO_DISPLAY_ON_COOKIE) { g_dbus_connection_call (m_system_bus, BUS_SCREEN_NAME, BUS_SCREEN_PATH, BUS_SCREEN_INTERFACE, "removeDisplayOnRequest", - g_variant_new("(i)", m_screen_cookie), + g_variant_new("(i)", m_display_on_cookie), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, @@ -221,7 +242,7 @@ private: nullptr, nullptr); - m_screen_cookie = NO_SCREEN_COOKIE; + m_display_on_cookie = NO_DISPLAY_ON_COOKIE; } } @@ -229,9 +250,21 @@ private: GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; char * m_awake_cookie = nullptr; - int32_t m_screen_cookie = NO_SCREEN_COOKIE; - static constexpr int32_t NO_SCREEN_COOKIE { std::numeric_limits<int32_t>::min() }; + /** + * As described by bug #1434637, alarms should have the display turn on, + * dim, and turn off "just like it would if you'd woken it up yourself". + * USC may be adding an intent-based bus API to handle this use case, + * e.g. turnDisplayOnTemporarily(intent), but there's no timeframe for it. + * + * Until that's avaialble, we can get close to Design's specs by + * requesting a display-on cookie and then releasing the cookie + * a moment later. */ + const guint m_display_on_seconds = 1; + guint m_display_on_timer = 0; + int32_t m_display_on_cookie = NO_DISPLAY_ON_COOKIE; + + static constexpr int32_t NO_DISPLAY_ON_COOKIE { std::numeric_limits<int32_t>::min() }; }; /*** @@ -239,7 +272,7 @@ private: ***/ Awake::Awake(const std::string& app_name): - impl(new Impl (app_name)) + impl(new Impl(app_name)) { } diff --git a/src/date-time.cpp b/src/date-time.cpp index 689688c..4930bf6 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -55,13 +55,23 @@ DateTime& DateTime::operator=(const DateTime& that) return *this; } -DateTime::DateTime(time_t t) +DateTime& DateTime::operator+=(const std::chrono::minutes& minutes) { - auto gtz = g_time_zone_new_local(); - auto gdt = g_date_time_new_from_unix_local(t); + return (*this = add_full(0, 0, 0, 0, minutes.count(), 0)); +} + +DateTime& DateTime::operator+=(const std::chrono::seconds& seconds) +{ + return (*this = add_full(0, 0, 0, 0, 0, seconds.count())); +} + +DateTime::DateTime(GTimeZone* gtz, time_t t) +{ + auto utc = g_date_time_new_from_unix_utc(t); + auto gdt = g_date_time_to_timezone (utc, gtz); reset(gtz, gdt); - g_time_zone_unref(gtz); g_date_time_unref(gdt); + g_date_time_unref(utc); } DateTime DateTime::NowLocal() @@ -74,6 +84,16 @@ DateTime DateTime::NowLocal() return dt; } +DateTime DateTime::Local(time_t t) +{ + auto gtz = g_time_zone_new_local(); + auto gdt = g_date_time_new_from_unix_local(t); + DateTime dt(gtz, gdt); + g_time_zone_unref(gtz); + g_date_time_unref(gdt); + return dt; +} + DateTime DateTime::Local(int year, int month, int day, int hour, int minute, double seconds) { auto gtz = g_time_zone_new_local(); @@ -244,10 +264,12 @@ bool DateTime::is_same_day(const DateTime& a, const DateTime& b) if (!a.m_dt || !b.m_dt) return false; - const auto adt = a.get(); - const auto bdt = b.get(); - return (g_date_time_get_year(adt) == g_date_time_get_year(bdt)) - && (g_date_time_get_day_of_year(adt) == g_date_time_get_day_of_year(bdt)); + int ay, am, ad; + int by, bm, bd; + g_date_time_get_ymd(a.get(), &ay, &am, &ad); + g_date_time_get_ymd(b.get(), &by, &bm, &bd); + + return (ay==by) && (am==bm) && (ad==bd); } bool DateTime::is_same_minute(const DateTime& a, const DateTime& b) diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index a47ab4a..b91fd71 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -25,6 +25,7 @@ #include <libedataserver/libedataserver.h> #include <algorithm> // std::sort() +#include <array> #include <ctime> // time() #include <map> #include <set> @@ -128,12 +129,18 @@ public: auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); g_debug("calling e_cal_client_generate_instances for %p", (void*)client); + auto subtask = new AppointmentSubtask(main_task, + client, + color, + default_timezone, + begin_timet, + end_timet); e_cal_client_generate_instances(client, begin_timet, end_timet, m_cancellable, my_get_appointments_foreach, - new AppointmentSubtask (main_task, client, color), + subtask, [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); } } @@ -411,14 +418,70 @@ private: 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) + icaltimezone* default_timezone; + time_t begin; + time_t end; + + AppointmentSubtask(const std::shared_ptr<Task>& task_in, + ECalClient* client_in, + const char* color_in, + icaltimezone* default_tz, + time_t begin_, + time_t end_): + task(task_in), + client(client_in), + default_timezone(default_tz), + begin(begin_), + end(end_) { if (color_in) color = color_in; } }; + static std::string get_alarm_text(ECalComponentAlarm * alarm) + { + std::string ret; + + ECalComponentAlarmAction action; + e_cal_component_alarm_get_action(alarm, &action); + if (action == E_CAL_COMPONENT_ALARM_DISPLAY) + { + ECalComponentText text {}; + e_cal_component_alarm_get_description(alarm, &text); + if (text.value) + ret = text.value; + } + + return ret; + } + + static std::string get_alarm_sound_url(ECalComponentAlarm * alarm) + { + std::string ret; + + ECalComponentAlarmAction action; + e_cal_component_alarm_get_action(alarm, &action); + if (action == E_CAL_COMPONENT_ALARM_AUDIO) + { + icalattach* attach = nullptr; + e_cal_component_alarm_get_attach(alarm, &attach); + if (attach != nullptr) + { + if (icalattach_get_is_url (attach)) + { + const char* url = icalattach_get_url(attach); + if (url != nullptr) + ret = url; + } + + icalattach_unref(attach); + } + } + + return ret; + } + static gboolean my_get_appointments_foreach(ECalComponent* component, time_t begin, @@ -436,11 +499,16 @@ private: auto status = ICAL_STATUS_NONE; e_cal_component_get_status(component, &status); - const auto begin_dt = DateTime(begin); - const auto end_dt = DateTime(end); + // get the timezone we want to use for generated Appointments/Alarms + const char * location = icaltimezone_get_location(subtask->default_timezone); + auto gtz = g_time_zone_new(location); + g_debug("timezone abbreviation is %s", g_time_zone_get_abbreviation (gtz, 0)); + + const DateTime begin_dt { gtz, begin }; + const DateTime end_dt { gtz, end }; g_debug ("got appointment from %s to %s, uid %s status %d", - begin_dt.format("%F %T").c_str(), - end_dt.format("%F %T").c_str(), + begin_dt.format("%F %T %z").c_str(), + end_dt.format("%F %T %z").c_str(), uid, (int)status); @@ -463,10 +531,10 @@ private: (status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) { + constexpr std::array<ECalComponentAlarmAction,1> omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 Appointment appointment; - ECalComponentText text; - text.value = nullptr; + ECalComponentText text {}; e_cal_component_get_summary(component, &text); if (text.value) appointment.summary = text.value; @@ -495,49 +563,80 @@ private: appointment.uid = uid; appointment.type = type; - // Look through all of this component's alarms - // for DISPLAY or AUDIO url attachments. - // If we find any, use them for appointment.url and audio_sound - auto alarm_uids = e_cal_component_get_alarm_uids(component); - for(auto walk=alarm_uids; appointment.url.empty() && walk!=nullptr; walk=walk->next) + icalcomponent * icc = e_cal_component_get_icalcomponent(component); + g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak + + auto e_alarms = e_cal_util_generate_alarms_for_comp(component, + subtask->begin, + subtask->end, + const_cast<ECalComponentAlarmAction*>(omit.data()), + e_cal_client_resolve_tzid_cb, + subtask->client, + subtask->default_timezone); + + std::map<DateTime,Alarm> alarms; + + if (e_alarms != nullptr) { - auto alarm = e_cal_component_get_alarm(component, static_cast<const char*>(walk->data)); + for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) + { + auto ai = static_cast<ECalComponentAlarmInstance*>(l->data); + auto a = e_cal_component_get_alarm(component, ai->auid); + + if (a != nullptr) + { + const DateTime alarm_begin{gtz, ai->trigger}; + auto& alarm = alarms[alarm_begin]; + + if (alarm.text.empty()) + alarm.text = get_alarm_text(a); + if (alarm.audio_url.empty()) + alarm.audio_url = get_alarm_sound_url(a); + if (!alarm.time.is_set()) + alarm.time = alarm_begin; - ECalComponentAlarmAction action; - e_cal_component_alarm_get_action(alarm, &action); - if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) || (action == E_CAL_COMPONENT_ALARM_AUDIO)) + e_cal_component_alarm_free(a); + } + } + + e_cal_component_alarms_free(e_alarms); + } + // Hm, no alarm triggers? + // That's a bug in alarms created by some versions of ubuntu-ui-toolkit. + // If that's what's happening here, let's handle those alarms anyway + // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S + else if (appointment.is_ubuntu_alarm()) + { + Alarm tmp; + tmp.time = appointment.begin; + + auto auids = e_cal_component_get_alarm_uids(component); + for(auto l=auids; l!=nullptr; l=l->next) { - icalattach* attach = nullptr; - e_cal_component_alarm_get_attach(alarm, &attach); - if (attach != nullptr) + const auto auid = static_cast<const char*>(l->data); + auto a = e_cal_component_get_alarm(component, auid); + if (a != nullptr) { - if (icalattach_get_is_url (attach)) - { - const char* url = icalattach_get_url(attach); - if (url != nullptr) - { - if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) && appointment.url.empty()) - { - appointment.url = url; - } - else if ((action == E_CAL_COMPONENT_ALARM_AUDIO) && appointment.audio_url.empty()) - { - appointment.audio_url = url; - } - } - } - - icalattach_unref(attach); + if (tmp.text.empty()) + tmp.text = get_alarm_text(a); + if (tmp.audio_url.empty()) + tmp.audio_url = get_alarm_sound_url(a); + e_cal_component_alarm_free(a); } } + cal_obj_uid_list_free(auids); - e_cal_component_alarm_free(alarm); + alarms[tmp.time] = tmp; } - cal_obj_uid_list_free(alarm_uids); - g_debug("adding appointment '%s' '%s'", appointment.summary.c_str(), appointment.url.c_str()); + appointment.alarms.reserve(alarms.size()); + for (const auto& it : alarms) + appointment.alarms.push_back(it.second); + subtask->task->appointments.push_back(appointment); } + + g_time_zone_unref(gtz); } return G_SOURCE_CONTINUE; @@ -611,10 +710,10 @@ private: std::set<ESource*> m_sources; std::map<ESource*,ECalClient*> m_clients; std::map<ESource*,ECalClientView*> m_views; - GCancellable* m_cancellable = nullptr; - ESourceRegistry* m_source_registry = nullptr; - guint m_rebuild_tag = 0; - time_t m_rebuild_deadline = 0; + GCancellable* m_cancellable {}; + ESourceRegistry* m_source_registry {}; + guint m_rebuild_tag {}; + time_t m_rebuild_deadline {}; }; /*** diff --git a/src/main.cpp b/src/main.cpp index 9aa502c..907d49f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -138,11 +138,13 @@ main(int /*argc*/, char** /*argv*/) auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service"); std::unique_ptr<Snap> snap (new Snap(notification_engine, state->settings)); auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone_); - auto on_snooze = [snooze_planner](const Appointment& a) {snooze_planner->add(a);}; - auto on_ok = [](const Appointment&){}; - auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& a) { - (*snap)(a, on_snooze, on_ok); - engine->disable_ubuntu_alarm(a); + auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm& alarm) { + snooze_planner->add(appointment, alarm); + }; + auto on_ok = [](const Appointment&, const Alarm&){}; + auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment, const Alarm& alarm) { + (*snap)(appointment, alarm, on_snooze, on_ok); + engine->disable_ubuntu_alarm(appointment); }; alarm_queue->alarm_reached().connect(on_alarm_reached); diff --git a/src/planner-snooze.cpp b/src/planner-snooze.cpp index 29d5f06..e4062d2 100644 --- a/src/planner-snooze.cpp +++ b/src/planner-snooze.cpp @@ -51,14 +51,18 @@ public: return m_appointments; } - void add(const Appointment& appt_in) + void add(const Appointment& appt_in, const Alarm& alarm) { + // make a copy of the appointment with only this alarm Appointment appt = appt_in; + appt.alarms.clear(); + appt.alarms.push_back(alarm); // reschedule the alarm to go off N minutes from now - const auto alarm_duration_secs = appt.end - appt.begin; - appt.begin = m_clock->localtime().add_full(0,0,0,0,m_settings->snooze_duration.get(),0); - appt.end = appt.begin.add_full(0,0,0,0,0,alarm_duration_secs); + const auto offset = std::chrono::minutes(m_settings->snooze_duration.get()); + appt.begin += offset; + appt.end += offset; + appt.alarms[0].time += offset; // give it a new ID gchar* uid = e_uid_new(); @@ -95,9 +99,9 @@ SnoozePlanner::~SnoozePlanner() } void -SnoozePlanner::add(const Appointment& appointment) +SnoozePlanner::add(const Appointment& appointment, const Alarm& alarm) { - impl->add(appointment); + impl->add(appointment, alarm); } core::Property<std::vector<Appointment>>& diff --git a/src/snap.cpp b/src/snap.cpp index e655d2d..ae0a62a 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -79,6 +79,7 @@ public: } void operator()(const Appointment& appointment, + const Alarm& alarm, appointment_func snooze, appointment_func ok) { @@ -96,7 +97,7 @@ public: if (appointment.is_ubuntu_alarm() || !silent_mode()) { // create the sound. const auto role = appointment.is_ubuntu_alarm() ? "alarm" : "alert"; - const auto uri = get_alarm_uri(appointment, m_settings); + const auto uri = get_alarm_uri(alarm, m_settings); const auto volume = m_settings->alarm_volume.get(); const bool loop = interactive; sound = std::make_shared<uin::Sound>(role, uri, volume, loop); @@ -140,12 +141,12 @@ public: // add 'sound', 'haptic', and 'awake' objects to the capture so // they stay alive until the closed callback is called; i.e., // for the lifespan of the notficiation - b.set_closed_callback([appointment, snooze, ok, sound, awake, haptic] + b.set_closed_callback([appointment, alarm, snooze, ok, sound, awake, haptic] (const std::string& action){ if (action == "snooze") - snooze(appointment); + snooze(appointment, alarm); else - ok(appointment); + ok(appointment, alarm); }); const auto key = m_engine->show(b); @@ -180,12 +181,12 @@ private: && (accounts_service_sound_get_silent_mode(m_accounts_service_sound_proxy)); } - std::string get_alarm_uri(const Appointment& appointment, + std::string get_alarm_uri(const Alarm& alarm, const std::shared_ptr<const Settings>& settings) const { const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; - const std::string candidates[] = { appointment.audio_url, + const std::string candidates[] = { alarm.audio_url, settings->alarm_sound.get(), FALLBACK }; @@ -236,10 +237,11 @@ Snap::~Snap() void Snap::operator()(const Appointment& appointment, + const Alarm& alarm, appointment_func show, appointment_func ok) { - (*impl)(appointment, show, ok); + (*impl)(appointment, alarm, show, ok); } /*** |