[california/wip/725778-allday] Ensure that if a multi-day event changes lines, other affected cells are notified
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/725778-allday] Ensure that if a multi-day event changes lines, other affected cells are notified
- Date: Fri, 25 Apr 2014 01:29:34 +0000 (UTC)
commit 777906ce5de7b333422d680a9663d31231df977c
Author: Jim Nelson <jim yorba org>
Date: Thu Apr 24 18:29:25 2014 -0700
Ensure that if a multi-day event changes lines, other affected cells are notified
src/Makefile.am | 1 +
src/calendar/calendar-date-span.vala | 15 +++++
src/collection/collection-iterable.vala | 9 +++
src/tests/tests-calendar-date.vala | 67 ++++++++++++++++++++++++
src/tests/tests.vala | 1 +
src/view/month/month-cell.vala | 86 +++++++++++++++++++++++++++----
6 files changed, 169 insertions(+), 10 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 57105f0..1e8ebda 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -99,6 +99,7 @@ california_VALASOURCES = \
manager/manager-window.vala \
\
tests/tests.vala \
+ tests/tests-calendar-date.vala \
tests/tests-quick-add.vala \
\
toolkit/toolkit.vala \
diff --git a/src/calendar/calendar-date-span.vala b/src/calendar/calendar-date-span.vala
index 8bc1336..a7ffc29 100644
--- a/src/calendar/calendar-date-span.vala
+++ b/src/calendar/calendar-date-span.vala
@@ -188,6 +188,21 @@ public class DateSpan : BaseObject, Collection.SimpleIterable<Date>, Span<Date>,
}
/**
+ * Returns a { link DateSpan} with starting and ending points within the boundary specified
+ * (inclusive).
+ *
+ * If this DateSpan is within the clamped dates, this object may be returned.
+ *
+ * This method will not expand a DateSpan to meet the clamp range.
+ */
+ public DateSpan clamp(DateSpan span) {
+ Date new_start = (start_date.compare_to(span.start_date) < 0) ? span.start_date : start_date;
+ Date new_end = (end_date.compare_to(span.end_date) > 0) ? span.end_date : end_date;
+
+ return new DateSpan(new_start, new_end);
+ }
+
+ /**
* Compares two { link DateSpan}s by their { link start_date}.
*/
public int compare_to(DateSpan other) {
diff --git a/src/collection/collection-iterable.vala b/src/collection/collection-iterable.vala
index 1bd9cbf..2276d8e 100644
--- a/src/collection/collection-iterable.vala
+++ b/src/collection/collection-iterable.vala
@@ -131,6 +131,15 @@ public class Iterable<G> : BaseObject {
return false;
}
+ public bool contains_any(Gee.Collection<G> c) {
+ foreach (G g in this) {
+ if (c.contains(g))
+ return true;
+ }
+
+ return false;
+ }
+
public bool all(owned Gee.Predicate<G> f) {
foreach (G g in this) {
if (!f(g))
diff --git a/src/tests/tests-calendar-date.vala b/src/tests/tests-calendar-date.vala
new file mode 100644
index 0000000..2620dd9
--- /dev/null
+++ b/src/tests/tests-calendar-date.vala
@@ -0,0 +1,67 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+namespace California.Tests {
+
+private class CalendarDate : UnitTest.Harness {
+ public CalendarDate() {
+ add_case("clamp-start", clamp_start);
+ add_case("clamp-end", clamp_end);
+ add_case("clamp-both", clamp_both);
+ add_case("clamp-neither", clamp_neither);
+ }
+
+ protected override void setup() throws Error {
+ Calendar.init();
+ }
+
+ protected override void teardown() {
+ Calendar.terminate();
+ }
+
+ private Calendar.Date from_today(int days) {
+ return Calendar.System.today.adjust(days, Calendar.DateUnit.DAY);
+ }
+
+ private Calendar.DateSpan span_from_today(int start_days, int end_days) {
+ return new Calendar.DateSpan(from_today(start_days), from_today(end_days));
+ }
+
+ private bool clamp_start() throws Error {
+ Calendar.DateSpan span = span_from_today(0, 5);
+ Calendar.DateSpan clamp = span_from_today(1, 5);
+ Calendar.DateSpan adj = span.clamp(clamp);
+
+ return adj.start_date.equal_to(clamp.start_date) && adj.end_date.equal_to(span.end_date);
+ }
+
+ private bool clamp_end() throws Error {
+ Calendar.DateSpan span = span_from_today(0, 5);
+ Calendar.DateSpan clamp = span_from_today(0, 4);
+ Calendar.DateSpan adj = span.clamp(clamp);
+
+ return adj.start_date.equal_to(span.start_date) && adj.end_date.equal_to(clamp.end_date);
+ }
+
+ private bool clamp_both() throws Error {
+ Calendar.DateSpan span = span_from_today(0, 5);
+ Calendar.DateSpan clamp = span_from_today(1, 4);
+ Calendar.DateSpan adj = span.clamp(clamp);
+
+ return adj.start_date.equal_to(clamp.start_date) && adj.end_date.equal_to(clamp.end_date);
+ }
+
+ private bool clamp_neither() throws Error {
+ Calendar.DateSpan span = span_from_today(0, 5);
+ Calendar.DateSpan clamp = span_from_today(-1, 6);
+ Calendar.DateSpan adj = span.clamp(clamp);
+
+ return adj.start_date.equal_to(span.start_date) && adj.end_date.equal_to(span.end_date);
+ }
+}
+
+}
+
diff --git a/src/tests/tests.vala b/src/tests/tests.vala
index 327609f..0684618 100644
--- a/src/tests/tests.vala
+++ b/src/tests/tests.vala
@@ -8,6 +8,7 @@ namespace California.Tests {
public int run(string[] args) {
UnitTest.Harness.register(new QuickAdd());
+ UnitTest.Harness.register(new CalendarDate());
return UnitTest.Harness.exec_all();
}
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 5ac7f3e..2ee1959 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -71,7 +71,7 @@ public class Cell : Gtk.EventBox {
}
}
- private Gee.TreeSet<Component.Event> sorted_events = new Gee.TreeSet<Component.Event>();
+ private Gee.TreeSet<Component.Event> sorted_events = new
Gee.TreeSet<Component.Event>(all_day_comparator);
private Gee.HashMap<int, Component.Event> line_to_event = new Gee.HashMap<int, Component.Event>();
// TODO: We may need to get these colors from the theme
@@ -129,6 +129,28 @@ public class Cell : Gtk.EventBox {
line_font = null;
}
+ // this comparator uses the standard Event comparator with one exception: if both Events are
+ // all-day, it sorts the one(s) with the furthest out end dates to the top, to ensure they are
+ // at the top of the drawn lines and prevent gaps and skips in the connected bars
+ private static int all_day_comparator(Component.Event a, Component.Event b) {
+ if (a == b)
+ return 0;
+
+ if (!a.is_all_day || !b.is_all_day)
+ return a.compare_to(b);
+
+ int compare = a.date_span.start_date.compare_to(b.date_span.start_date);
+ if (compare != 0)
+ return compare;
+
+ compare = b.date_span.end_date.compare_to(a.date_span.end_date);
+ if (compare != 0)
+ return compare;
+
+ // to stabilize
+ return a.compare_to(b);
+ }
+
/**
* Returns true if the point at x,y is within the { link Cell}'s width and height.
*/
@@ -182,14 +204,29 @@ public class Cell : Gtk.EventBox {
queue_draw();
}
+ /**
+ * Called by { link Controllable} when a calendar's visibility has changed.
+ *
+ * This causes event line numbers to be reassigned and thie { link Cell} redrawn, if the
+ * calendar in question has any events in this date.
+ */
public void notify_calendar_visibility_changed(Backing.CalendarSource calendar_source) {
- if (traverse<Component.Event>(sorted_events).any((event) => event.calendar_source ==
calendar_source)) {
- // found one
- assign_line_numbers();
- queue_draw();
-
+ if (!traverse<Component.Event>(sorted_events).any((event) => event.calendar_source ==
calendar_source))
return;
- }
+
+ // found one
+ assign_line_numbers();
+ queue_draw();
+ }
+
+ // Called internally by other Cells when (a) they're in charge of assigning a multi-day event
+ // its line number for the week and (b) that line number has changed.
+ private void notify_assigned_line_number_changed(Gee.Collection<Component.Event> events) {
+ if (!traverse<Component.Event>(sorted_events).contains_any(events))
+ return;
+
+ assign_line_numbers();
+ queue_draw();
}
// each event gets a line of the cell to draw in; this clears all assigned line numbers and
@@ -198,12 +235,18 @@ public class Cell : Gtk.EventBox {
// this cell is responsible for assigning a line number to it, which the other cells of the
// same week will honor (so a continuous line can be drawn)
private void assign_line_numbers() {
- line_to_event.clear();
+ Gee.HashMap<int, Component.Event> old_line_to_event = line_to_event;
+ line_to_event = new Gee.HashMap<int, Component.Event>();
+
+ // track each event whose line number this cell is responsible for assigning that gets
+ // reassigned because of this
+ Gee.ArrayList<Component.Event> reassigned = new Gee.ArrayList<Component.Event>();
foreach (Component.Event event in sorted_events) {
if (!event.calendar_source.visible)
continue;
+ bool all_day_assigned_here = false;
if (event.is_all_day) {
// get the first day of this week the event exists in ... if not the current cell's
// date, get the assigned line number from the first day of this week the event
@@ -220,16 +263,37 @@ public class Cell : Gtk.EventBox {
continue;
}
+ } else {
+ // only worried about multi-day events being reassigned, as that's what effects
+ // other cells
+ all_day_assigned_here = event.date_span.duration() > 1;
}
}
// otherwise, a timed event, a single-day event, or a multi-day event which starts here,
// so assign
- assign_line_number(-1, event);
+ int assigned = assign_line_number(-1, event);
+
+ // if this cell assigns the line number and the event is not new and the number has changed,
+ // inform all the other cells following this day's in the current week
+ if (all_day_assigned_here && old_line_to_event.values.contains(event) &&
old_line_to_event.get(assigned) != event)
+ reassigned.add(event);
+ }
+
+ if (reassigned.size > 0) {
+ // only need to tell cells following this day's in the current week about the reassignment
+ Calendar.Week this_week = date.week_of(owner.first_of_week);
+ Calendar.DateSpan span = new Calendar.DateSpan(date.next(), this_week.end_date).clamp(this_week);
+
+ foreach (Calendar.Date span_date in span) {
+ Cell? cell = owner.get_cell_for_date(span_date);
+ if (cell != null && cell != this)
+ cell.notify_assigned_line_number_changed(reassigned);
+ }
}
}
- private void assign_line_number(int force_line_number, Component.Event event) {
+ private int assign_line_number(int force_line_number, Component.Event event) {
// kinda dumb, but this prevents holes appearing in lines where, due to the shape of the
// all-day events, could be filled
int line_number = 0;
@@ -241,6 +305,8 @@ public class Cell : Gtk.EventBox {
}
line_to_event.set(line_number, event);
+
+ return line_number;
}
public bool has_events() {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]