aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/datetime/date-time.h2
-rw-r--r--include/datetime/menu.h6
-rw-r--r--src/date-time.cpp10
-rw-r--r--src/menu.cpp111
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/print-to.h10
6 files changed, 122 insertions, 18 deletions
diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h
index fc83388..845716d 100644
--- a/include/datetime/date-time.h
+++ b/include/datetime/date-time.h
@@ -68,7 +68,9 @@ public:
int64_t to_unix() const;
bool operator<(const DateTime& that) const;
+ bool operator>(const DateTime& that) const;
bool operator<=(const DateTime& that) const;
+ bool operator>=(const DateTime& that) const;
bool operator!=(const DateTime& that) const;
bool operator==(const DateTime& that) const;
int64_t operator- (const DateTime& that) const;
diff --git a/include/datetime/menu.h b/include/datetime/menu.h
index acd9ed8..0074ea5 100644
--- a/include/datetime/menu.h
+++ b/include/datetime/menu.h
@@ -21,6 +21,7 @@
#define INDICATOR_DATETIME_MENU_H
#include <datetime/actions.h>
+#include <datetime/appointment.h>
#include <datetime/state.h>
#include <memory> // std::shared_ptr
@@ -49,6 +50,11 @@ public:
Profile profile() const;
GMenuModel* menu_model();
+ static std::vector<Appointment> get_display_appointments(
+ const std::vector<Appointment>&,
+ const DateTime& start,
+ unsigned int max_items=5);
+
protected:
Menu (Profile profile_in, const std::string& name_in);
virtual ~Menu() =default;
diff --git a/src/date-time.cpp b/src/date-time.cpp
index 169426c..911fb7a 100644
--- a/src/date-time.cpp
+++ b/src/date-time.cpp
@@ -245,11 +245,21 @@ bool DateTime::operator<(const DateTime& that) const
return g_date_time_compare(get(), that.get()) < 0;
}
+bool DateTime::operator>(const DateTime& that) const
+{
+ return g_date_time_compare(get(), that.get()) > 0;
+}
+
bool DateTime::operator<=(const DateTime& that) const
{
return g_date_time_compare(get(), that.get()) <= 0;
}
+bool DateTime::operator>=(const DateTime& that) const
+{
+ return g_date_time_compare(get(), that.get()) >= 0;
+}
+
bool DateTime::operator!=(const DateTime& that) const
{
// return true if this isn't set, or if it's not equal
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 <datetime/state.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include <algorithm>
+#include <iterator>
#include <vector>
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<Appointment>
+Menu::get_display_appointments(const std::vector<Appointment>& appointments_in,
+ const DateTime& now,
+ unsigned int max_items)
+{
+ std::vector<Appointment> 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<Appointment> 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)
{
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index bec0010..aa69ca1 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -59,6 +59,7 @@ add_test_by_name(test-exporter)
add_test_by_name(test-formatter)
add_test_by_name(test-live-actions)
add_test_by_name(test-locations)
+add_test_by_name(test-menu-appointments)
add_test_by_name(test-menus)
add_test_by_name(test-planner)
add_test_by_name(test-settings)
diff --git a/tests/print-to.h b/tests/print-to.h
index 19367ac..652da52 100644
--- a/tests/print-to.h
+++ b/tests/print-to.h
@@ -21,6 +21,7 @@
#define INDICATOR_DATETIME_TESTS_PRINT_TO
#include <algorithm>
+#include <vector>
#include <datetime/appointment.h>
@@ -71,6 +72,15 @@ PrintTo(const Appointment& appointment, std::ostream* os)
*os << '}';
}
+void
+PrintTo(const std::vector<Appointment>& appointments, std::ostream* os)
+{
+ *os << '{';
+ for (const auto& appointment : appointments)
+ PrintTo(appointment, os);
+ *os << '}';
+}
+
} // namespace datetime
} // namespace indicator
} // namespace ayatana