From a8ea3ba0548c30696245f74ed701ca9105f7ce5b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 18 Mar 2016 10:17:55 -0500 Subject: get event selection up-to-date with the spec, including showing in-progress events. add unit tests to cover event priority and display order. --- src/menu.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 18 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index d19ad73..0cd3f9b 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include extern "C" @@ -58,11 +60,90 @@ GMenuModel* Menu::menu_model() return G_MENU_MODEL(m_menu); } +/** + * To avoid a giant menu on the PC, and to avoid pushing lower menu items + * off-screen on the phone, the menu should show the + * next five calendar events, if any. + * + * The list might include multiple occurrences of the same event (bug 1515821). + */ +std::vector +Menu::get_display_appointments(const std::vector& appointments_in, + const DateTime& now, + unsigned int max_items) +{ + std::vector appointments; + std::copy_if(appointments_in.begin(), + appointments_in.end(), + std::back_inserter(appointments), + [now](const Appointment& a){return a.end >= now;}); + + if (appointments.size() > max_items) + { + const auto next_minute = now.add_full(0,0,0,0,1,-now.seconds()); + const auto start_of_day = now.start_of_day(); + const auto end_of_day = now.end_of_day(); + + /* + * If there are more than five, the events shown should be, in order of priority: + * 1. any events that start or end (bug 1329048) after the current minute today; + * 2. any full-day events that span all of today (bug 1302004); + * 3. any events that start or end tomorrow; + * 4. any events that start or end the day after tomorrow; and so on. + */ + auto compare = [next_minute, start_of_day, end_of_day]( + const Appointment& a, + const Appointment& b) + { + const bool a_later_today = (a.begin >= next_minute) || (a.end <= end_of_day); + const bool b_later_today = (b.begin >= next_minute) || (b.end <= end_of_day); + if (a_later_today != b_later_today) + return a_later_today; + + const bool a_full_day_today = (a.begin <= start_of_day) && (end_of_day <= a.end); + const bool b_full_day_today = (b.begin <= start_of_day) && (end_of_day <= b.end); + if (a_full_day_today != b_full_day_today) + return a_full_day_today; + + const bool a_after_today = (a.begin > end_of_day) || (a.end > end_of_day); + const bool b_after_today = (a.begin > end_of_day) || (a.end > end_of_day); + if (a_after_today != b_after_today) + return a_after_today; + if (a.begin != b.begin) + return a.begin < b.begin; + if (b.end != b.end) + return a.end < b.end; + + return false; + }; + std::sort(appointments.begin(), appointments.end(), compare); + appointments.resize(max_items); + } + + /* + * However, the display order should be the reverse: full-day events + * first (since they start first), part-day events afterward in + * chronological order. If multiple events have exactly the same start+end + * time, they should be sorted alphabetically. + */ + auto compare = [](const Appointment& a, const Appointment& b) + { + if (a.begin != b.begin) + return a.begin < b.begin; + + if (a.end != b.end) + return a.end < b.end; + + return a.summary < b.summary; + }; + std::sort(appointments.begin(), appointments.end(), compare); + return appointments; +} + /**** ***** ****/ - #define ALARM_ICON_NAME "alarm-clock" #define CALENDAR_ICON_NAME "calendar" @@ -150,25 +231,19 @@ protected: void update_upcoming() { - // The usual case is on desktop (and /only/ case on phone) - // is that we're looking at the current date and want to see - // "the next five calendar events, if any." - // - // However on the Desktop when the user clicks onto a different - // calendar date, show the next five calendar events starting - // from the beginning of that clicked day. - DateTime begin; + // The usual case is to show events germane to the current time. + // However when the user clicks onto a different calendar date, + // we pick events starting from the beginning of that clicked day. const auto now = m_state->clock->localtime(); const auto calendar_day = m_state->calendar_month->month().get(); - if ((profile() == Desktop) && !DateTime::is_same_day(now, calendar_day)) - begin = calendar_day.start_of_day(); - else - begin = now.start_of_minute(); - - std::vector upcoming; - for(const auto& a : m_state->calendar_upcoming->appointments().get()) - if (begin <= a.begin) - upcoming.push_back(a); + const auto begin = DateTime::is_same_day(now, calendar_day) + ? now.start_of_minute() + : calendar_day.start_of_day(); + + auto upcoming = get_display_appointments( + m_state->calendar_upcoming->appointments().get(), + begin + ); if (m_upcoming != upcoming) { -- cgit v1.2.3 From 7fde2181c74fada11d537e2f09ccbb8e5745ae8c Mon Sep 17 00:00:00 2001 From: Renato Araujo Oliveira Filho Date: Mon, 21 Mar 2016 14:32:39 -0300 Subject: Make sure that the ocurrence time is used to build the url to launch external application. --- include/datetime/actions-live.h | 4 ++-- include/datetime/actions.h | 4 ++-- src/actions-live.cpp | 8 ++++---- src/actions.cpp | 29 +++++++++++++++-------------- src/menu.cpp | 7 +++++-- tests/actions-mock.h | 6 ++++-- tests/test-actions.cpp | 4 ++-- tests/test-live-actions.cpp | 11 ++++++----- 8 files changed, 40 insertions(+), 33 deletions(-) (limited to 'src/menu.cpp') diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 2c348c6..1f84659 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -41,12 +41,12 @@ public: bool desktop_has_calendar_app() const override; void desktop_open_alarm_app() override; - void desktop_open_appointment(const Appointment&) override; + void desktop_open_appointment(const Appointment&, const DateTime&) override; void desktop_open_calendar_app(const DateTime&) override; void desktop_open_settings_app() override; void phone_open_alarm_app() override; - void phone_open_appointment(const Appointment&) override; + void phone_open_appointment(const Appointment&, const DateTime &) override; void phone_open_calendar_app(const DateTime&) override; void phone_open_settings_app() override; diff --git a/include/datetime/actions.h b/include/datetime/actions.h index 47931ac..ea163e4 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -45,12 +45,12 @@ public: virtual bool desktop_has_calendar_app() const =0; virtual void desktop_open_alarm_app() =0; - virtual void desktop_open_appointment(const Appointment&) =0; + virtual void desktop_open_appointment(const Appointment&, const DateTime&) =0; virtual void desktop_open_calendar_app(const DateTime&) =0; virtual void desktop_open_settings_app() =0; virtual void phone_open_alarm_app() =0; - virtual void phone_open_appointment(const Appointment&) =0; + virtual void phone_open_appointment(const Appointment&, const DateTime&) =0; virtual void phone_open_calendar_app(const DateTime&) =0; virtual void phone_open_settings_app() =0; diff --git a/src/actions-live.cpp b/src/actions-live.cpp index 4fe2f39..231fb33 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -127,9 +127,9 @@ void LiveActions::desktop_open_alarm_app() execute_command("evolution -c calendar"); } -void LiveActions::desktop_open_appointment(const Appointment& appt) +void LiveActions::desktop_open_appointment(const Appointment&, const DateTime& date) { - desktop_open_calendar_app(appt.begin); + desktop_open_calendar_app(date); } void LiveActions::desktop_open_calendar_app(const DateTime& dt) @@ -148,7 +148,7 @@ void LiveActions::phone_open_alarm_app() dispatch_url("appid://com.ubuntu.clock/clock/current-user-version"); } -void LiveActions::phone_open_appointment(const Appointment& appt) +void LiveActions::phone_open_appointment(const Appointment& appt, const DateTime& date) { if (!appt.activation_url.empty()) @@ -163,7 +163,7 @@ void LiveActions::phone_open_appointment(const Appointment& appt) case Appointment::EVENT: default: - phone_open_calendar_app(appt.begin); + phone_open_calendar_app(date); break; } } diff --git a/src/actions.cpp b/src/actions.cpp index 93629a0..ea68d3e 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -48,13 +48,8 @@ DateTime datetime_from_timet_variant(GVariant* v) return DateTime::NowLocal(); } -bool lookup_appointment_by_uid_variant(const std::shared_ptr& state, GVariant* vuid, Appointment& setme) +bool lookup_appointment_by_uid(const std::shared_ptr& state, const gchar* uid, Appointment& setme) { - g_return_val_if_fail(vuid != nullptr, false); - g_return_val_if_fail(g_variant_type_equal(G_VARIANT_TYPE_STRING,g_variant_get_type(vuid)), false); - const auto uid = g_variant_get_string(vuid, nullptr); - g_return_val_if_fail(uid && *uid, false); - for(const auto& appt : state->calendar_upcoming->appointments().get()) { if (appt.uid == uid) @@ -67,12 +62,15 @@ bool lookup_appointment_by_uid_variant(const std::shared_ptr& state, GVar return false; } -void on_desktop_appointment_activated (GSimpleAction*, GVariant *vuid, gpointer gself) +void on_desktop_appointment_activated (GSimpleAction*, GVariant *vdata, gpointer gself) { auto self = static_cast(gself); Appointment appt; - if (lookup_appointment_by_uid_variant(self->state(), vuid, appt)) - self->desktop_open_appointment(appt); + const gchar* uid = nullptr; + gint64 time = 0; + g_variant_get(vdata, "(&sx)", &uid, &time); + if (lookup_appointment_by_uid(self->state(), uid, appt)) + self->desktop_open_appointment(appt, DateTime::Local(time)); } void on_desktop_alarm_activated (GSimpleAction*, GVariant*, gpointer gself) { @@ -88,12 +86,15 @@ void on_desktop_settings_activated (GSimpleAction*, GVariant*, gpointer gself) static_cast(gself)->desktop_open_settings_app(); } -void on_phone_appointment_activated (GSimpleAction*, GVariant *vuid, gpointer gself) +void on_phone_appointment_activated (GSimpleAction*, GVariant *vdata, gpointer gself) { auto self = static_cast(gself); Appointment appt; - if (lookup_appointment_by_uid_variant(self->state(), vuid, appt)) - self->phone_open_appointment(appt); + const gchar* uid = nullptr; + gint64 time = 0; + g_variant_get(vdata, "(&sx)", &uid, &time); + if (lookup_appointment_by_uid(self->state(), uid, appt)) + self->phone_open_appointment(appt, DateTime::Local(time)); } void on_phone_alarm_activated (GSimpleAction*, GVariant*, gpointer gself) { @@ -198,12 +199,12 @@ Actions::Actions(const std::shared_ptr& state): { GActionEntry entries[] = { - { "desktop.open-appointment", on_desktop_appointment_activated, "s", nullptr }, + { "desktop.open-appointment", on_desktop_appointment_activated, "(sx)", nullptr }, { "desktop.open-alarm-app", on_desktop_alarm_activated }, { "desktop.open-calendar-app", on_desktop_calendar_activated, "x", nullptr }, { "desktop.open-settings-app", on_desktop_settings_activated }, - { "phone.open-appointment", on_phone_appointment_activated, "s", nullptr }, + { "phone.open-appointment", on_phone_appointment_activated, "(sx)", nullptr }, { "phone.open-alarm-app", on_phone_alarm_activated }, { "phone.open-calendar-app", on_phone_calendar_activated, "x", nullptr }, { "phone.open-settings-app", on_phone_settings_activated }, diff --git a/src/menu.cpp b/src/menu.cpp index 0cd3f9b..b1ac75c 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -414,9 +414,12 @@ private: if (!appt.color.empty()) g_menu_item_set_attribute (menu_item, "x-ayatana-color", "s", appt.color.c_str()); - if (action_name != nullptr) + if (action_name != nullptr) { g_menu_item_set_action_and_target_value (menu_item, action_name, - g_variant_new_string (appt.uid.c_str())); + g_variant_new ("(sx)", + appt.uid.c_str(), + unix_time)); + } g_menu_append_item (menu, menu_item); g_object_unref (menu_item); diff --git a/tests/actions-mock.h b/tests/actions-mock.h index 59a0912..346a8f6 100644 --- a/tests/actions-mock.h +++ b/tests/actions-mock.h @@ -57,8 +57,9 @@ public: void desktop_open_alarm_app() { m_history.push_back(DesktopOpenAlarmApp); } - void desktop_open_appointment(const Appointment& appt) { + void desktop_open_appointment(const Appointment& appt, const DateTime& dt) { m_appt = appt; + m_date_time = dt; m_history.push_back(DesktopOpenAppt); } void desktop_open_calendar_app(const DateTime& dt) { @@ -72,8 +73,9 @@ public: void phone_open_alarm_app() { m_history.push_back(PhoneOpenAlarmApp); } - void phone_open_appointment(const Appointment& appt) { + void phone_open_appointment(const Appointment& appt, const DateTime& dt) { m_appt = appt; + m_date_time = dt; m_history.push_back(PhoneOpenAppt); } void phone_open_calendar_app(const DateTime& dt) { diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp index aa608a8..96da7cc 100644 --- a/tests/test-actions.cpp +++ b/tests/test-actions.cpp @@ -116,7 +116,7 @@ protected: m_mock_state->mock_range_planner->appointments().set(appointments); // activate the action - auto v = g_variant_new_string(appointments[0].uid.c_str()); + auto v = g_variant_new("(sx)", appointments[0].uid.c_str(), 0); g_action_group_activate_action(action_group, action_name, v); // test the results @@ -134,7 +134,7 @@ protected: EXPECT_TRUE(m_mock_actions->history().empty()); // activate the action - v = g_variant_new_string("this-uid-is-not-one-that-we-have"); + v = g_variant_new("(sx)", "this-uid-is-not-one-that-we-have", 0); g_action_group_activate_action(action_group, action_name, v); // test the results diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp index 3f79d7d..e7cb1a2 100644 --- a/tests/test-live-actions.cpp +++ b/tests/test-live-actions.cpp @@ -64,7 +64,7 @@ TEST_F(TimedateFixture, DesktopOpenAppointment) Appointment a; a.uid = "some-uid"; a.begin = DateTime::NowLocal(); - m_actions->desktop_open_appointment(a); + m_actions->desktop_open_appointment(a, a.begin); const std::string expected_substr = "evolution \"calendar:///?startdate="; EXPECT_NE(m_live_actions->last_cmd.find(expected_substr), std::string::npos); } @@ -106,12 +106,13 @@ TEST_F(TimedateFixture, PhoneOpenAppointment) a.source_uid = "source-uid"; a.begin = DateTime::NowLocal(); a.type = Appointment::EVENT; - m_actions->phone_open_appointment(a); - const std::string appointment_app_url = "calendar://eventid=source-uid/event-uid"; + auto ocurrenceDate = DateTime::Local(2014, 1, 1, 0, 0, 0); + m_actions->phone_open_appointment(a, ocurrenceDate); + const std::string appointment_app_url = ocurrenceDate.to_timezone("UTC").format("calendar://startdate=%Y-%m-%dT%H:%M:%S+00:00"); EXPECT_EQ(appointment_app_url, m_live_actions->last_url); a.type = Appointment::UBUNTU_ALARM; - m_actions->phone_open_appointment(a); + m_actions->phone_open_appointment(a, a.begin); EXPECT_EQ(clock_app_url, m_live_actions->last_url); } @@ -119,7 +120,7 @@ TEST_F(TimedateFixture, PhoneOpenCalendarApp) { auto now = DateTime::NowLocal(); m_actions->phone_open_calendar_app(now); - const std::string expected = now.format("calendar:///?startdate=%Y%m%dT%H%M%SZ"); + const std::string expected = now.to_timezone("UTC").format("calendar://startdate=%Y-%m-%dT%H:%M:%S+00:00"); EXPECT_EQ(expected, m_live_actions->last_url); } -- cgit v1.2.3 From b704508b415ec9400e38f6a47b109e0ae3b2d1fd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 17:29:04 -0500 Subject: revert r426 & r427 to remove the calendar from the phone profile --- src/menu.cpp | 4 ++-- tests/test-menus.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index b1ac75c..f9b6485 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -319,7 +319,7 @@ private: GMenuModel* create_calendar_section(Profile profile) { const bool show_calendar = m_state->settings->show_calendar.get() && - ((profile == Desktop) || (profile == DesktopGreeter) || (profile == Phone)); + ((profile == Desktop) || (profile == DesktopGreeter)); auto menu = g_menu_new(); const char * action_name; @@ -464,7 +464,7 @@ private: const auto now = m_state->clock->localtime(); - if (profile == Desktop || profile == Phone) + if (profile == Desktop) { for(const auto& location : m_state->locations->locations.get()) { diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index 8650201..73592d0 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -102,7 +102,7 @@ protected: else expected_action = nullptr; - const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter) || (profile == Menu::Phone)) + const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter)) && (m_state->settings->show_calendar.get()); // get the calendar section @@ -420,7 +420,7 @@ protected: void InspectLocations(GMenuModel* menu_model, Menu::Profile profile) { - const bool locations_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); + const bool locations_expected = profile == Menu::Desktop; // when there aren't any locations, confirm the menu is empty const std::vector empty; -- cgit v1.2.3 From 63e38ce5a62731310013918a3ef16b1c81a68dd0 Mon Sep 17 00:00:00 2001 From: Lukáš Tinkl Date: Thu, 16 Jun 2016 18:55:27 +0200 Subject: re-enable the calendar component in phone mode too --- src/menu.cpp | 4 ++-- tests/test-menus.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index f9b6485..b1ac75c 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -319,7 +319,7 @@ private: GMenuModel* create_calendar_section(Profile profile) { const bool show_calendar = m_state->settings->show_calendar.get() && - ((profile == Desktop) || (profile == DesktopGreeter)); + ((profile == Desktop) || (profile == DesktopGreeter) || (profile == Phone)); auto menu = g_menu_new(); const char * action_name; @@ -464,7 +464,7 @@ private: const auto now = m_state->clock->localtime(); - if (profile == Desktop) + if (profile == Desktop || profile == Phone) { for(const auto& location : m_state->locations->locations.get()) { diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index 73592d0..8650201 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -102,7 +102,7 @@ protected: else expected_action = nullptr; - const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter)) + const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter) || (profile == Menu::Phone)) && (m_state->settings->show_calendar.get()); // get the calendar section @@ -420,7 +420,7 @@ protected: void InspectLocations(GMenuModel* menu_model, Menu::Profile profile) { - const bool locations_expected = profile == Menu::Desktop; + const bool locations_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); // when there aren't any locations, confirm the menu is empty const std::vector empty; -- cgit v1.2.3