[california/wip/725778-allday] Event line numbers in cells are now assigned when added/removed
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/725778-allday] Event line numbers in cells are now assigned when added/removed
- Date: Thu, 24 Apr 2014 22:53:17 +0000 (UTC)
commit ffdd6e940706008c770626f1e008a2968863dc52
Author: Jim Nelson <jim yorba org>
Date: Thu Apr 24 15:52:28 2014 -0700
Event line numbers in cells are now assigned when added/removed
This allows for line numbers of events in cells to be known before
draw() is called, so any cell can know the assigned line for a
multi-day event and draw it in its own cell with the same line number.
src/component/component-event.vala | 5 +-
src/view/month/month-cell.vala | 148 +++++++++++++++++++++++---------
src/view/month/month-controllable.vala | 18 ++++-
3 files changed, 126 insertions(+), 45 deletions(-)
---
diff --git a/src/component/component-event.vala b/src/component/component-event.vala
index bd68069..3923383 100644
--- a/src/component/component-event.vala
+++ b/src/component/component-event.vala
@@ -347,11 +347,14 @@ public class Event : Instance, Gee.Comparable<Event> {
}
if (exact_time_span != null
+ && other_event.date_span != null
&& !new Calendar.DateSpan.from_exact_time_span(exact_time_span).equal_to(other_event.date_span))
{
return false;
}
- if (!date_span.equal_to(new Calendar.DateSpan.from_exact_time_span(other_event.exact_time_span))) {
+ if (date_span != null
+ && other_event.exact_time_span != null
+ && !date_span.equal_to(new Calendar.DateSpan.from_exact_time_span(other_event.exact_time_span)))
{
return false;
}
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 4b9a52c..1e3b68b 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -59,7 +59,7 @@ public class Cell : Gtk.EventBox {
}
}
- private Gee.TreeSet<Component.Event> days_events = new Gee.TreeSet<Component.Event>(all_day_comparator);
+ private Gee.TreeSet<Component.Event> sorted_events = new Gee.TreeSet<Component.Event>();
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
@@ -101,28 +101,6 @@ public class Cell : Gtk.EventBox {
Calendar.System.instance.today_changed.disconnect(on_today_changed);
}
- // 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);
- }
-
internal static void init() {
top_line_font = new Pango.FontDescription();
top_line_font.set_size(TOP_LINE_FONT_SIZE_PT * Pango.SCALE);
@@ -146,13 +124,27 @@ public class Cell : Gtk.EventBox {
return x >= 0 && x < get_allocated_width() && y >= 0 && y < get_allocated_height();
}
+ /**
+ * Returns the assigned line number for the event, -1 if not found in { link Cell}.
+ */
+ public int get_line_for_event(Component.Event event) {
+ Gee.MapIterator<int, Component.Event> iter = line_to_event.map_iterator();
+ while (iter.next()) {
+ if (iter.get_value().equal_to(event))
+ return iter.get_key();
+ }
+
+ return -1;
+ }
+
public void clear() {
date = null;
- days_events.clear();
+ sorted_events.clear();
+ line_to_event.clear();
}
public void add_event(Component.Event event) {
- if (!days_events.add(event))
+ if (!sorted_events.add(event))
return;
// subscribe to interesting mutable properties
@@ -160,22 +152,87 @@ public class Cell : Gtk.EventBox {
event.notify[Component.Event.PROP_DATE_SPAN].connect(on_span_updated);
event.notify[Component.Event.PROP_EXACT_TIME_SPAN].connect(on_span_updated);
+ assign_line_numbers();
+
queue_draw();
}
public void remove_event(Component.Event event) {
- if (!days_events.remove(event))
+ if (!sorted_events.remove(event))
return;
event.notify[Component.Event.PROP_SUMMARY].disconnect(queue_draw);
event.notify[Component.Event.PROP_DATE_SPAN].disconnect(on_span_updated);
event.notify[Component.Event.PROP_EXACT_TIME_SPAN].disconnect(on_span_updated);
+ assign_line_numbers();
+
queue_draw();
}
+ 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();
+
+ return;
+ }
+ }
+
+ // each event gets a line of the cell to draw in; this clears all assigned line numbers and
+ // re-assigns from the sorted set of events, making sure holes are filled where possible ...
+ // if an event starts in this cell or this cell is the first day of a week an event is in,
+ // 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();
+
+ foreach (Component.Event event in sorted_events) {
+ if (!event.calendar_source.visible)
+ continue;
+
+ 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
+ // exists in
+ Calendar.Date first_date = get_event_first_day_this_week(event);
+ if (!date.equal_to(first_date)) {
+ int event_line = -1;
+ Cell? cell = owner.get_cell_for_date(first_date);
+ if (cell != null)
+ event_line = cell.get_line_for_event(event);
+
+ if (event_line >= 0) {
+ assign_line_number(event_line, event);
+
+ continue;
+ }
+ }
+ }
+
+ // otherwise, a timed event, a single-day event, or a multi-day event which starts here,
+ // so assign
+ assign_line_number(-1, event);
+ }
+ }
+
+ private void 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;
+ if (force_line_number < 0) {
+ while (line_to_event.has_key(line_number))
+ line_number++;
+ } else {
+ line_number = force_line_number;
+ }
+
+ line_to_event.set(line_number, event);
+ }
+
public bool has_events() {
- return days_events.size > 0;
+ return sorted_events.size > 0;
}
private void on_24hr_changed() {
@@ -195,12 +252,14 @@ public class Cell : Gtk.EventBox {
Component.Event event = (Component.Event) object;
- // remove from cell if no longer in this day, otherwise remove and add again to days_events
+ // remove from cell if no longer in this day, otherwise remove and add again to sorted_events
// to re-sort
- if (!(date in event.get_event_date_span(Calendar.Timezone.local)))
+ if (!(date in event.get_event_date_span(Calendar.Timezone.local))) {
remove_event(event);
- else if (days_events.remove(event))
- days_events.add(event);
+ } else if (sorted_events.remove(event)) {
+ sorted_events.add(event);
+ assign_line_numbers();
+ }
queue_draw();
}
@@ -219,6 +278,17 @@ public class Cell : Gtk.EventBox {
return true;
}
+ // Returns the first day of this cell's calendar week that the event is in ... this could be
+ // the event's starting day or the first day of this week (i.e. Monday or Sunday)
+ private Calendar.Date get_event_first_day_this_week(Component.Event event) {
+ Calendar.Date event_start_date = event.get_event_date_span(Calendar.Timezone.local).start_date;
+
+ Calendar.Week cell_week = date.week_of(owner.first_of_week);
+ Calendar.Week event_start_week = event_start_date.week_of(owner.first_of_week);
+
+ return cell_week.equal_to(event_start_week) ? event_start_date : cell_week.start_date;
+ }
+
private bool on_draw(Cairo.Context ctx) {
// calculate extents if not already calculated;
if (line_height_px < 0 || top_line_height_px < 0)
@@ -266,15 +336,10 @@ public class Cell : Gtk.EventBox {
draw_line_of_text(ctx, -1, color, date.day_of_month.informal_number, false);
}
- // represents the line number being drawn (zero-based for remaining lines)
- int line_number = 0;
- line_to_event.clear();
-
- // draw all events in chronological order, all-day events first, storing lookup data
- // as the "lines" are drawn ... make sure to convert them all to local timezone
- foreach (Component.Event event in days_events) {
- if (!event.calendar_source.visible)
- continue;
+ // walk the assigned line numbers for each event and draw
+ Gee.MapIterator<int, Component.Event> iter = line_to_event.map_iterator();
+ while (iter.next()) {
+ Component.Event event = iter.get_value();
string text, tooltip_text;
if (event.is_all_day) {
@@ -292,9 +357,8 @@ public class Cell : Gtk.EventBox {
tooltip_text = text;
}
- Pango.Layout layout = draw_line_of_text(ctx, line_number, event.calendar_source.color_as_rgba(),
+ Pango.Layout layout = draw_line_of_text(ctx, iter.get_key(),
event.calendar_source.color_as_rgba(),
text, event.is_all_day);
- line_to_event.set(line_number++, event);
event.set_data<string?>(KEY_TOOLTIP, layout.is_ellipsized() ? tooltip_text : null);
}
diff --git a/src/view/month/month-controllable.vala b/src/view/month/month-controllable.vala
index 3707df8..cf5b34f 100644
--- a/src/view/month/month-controllable.vala
+++ b/src/view/month/month-controllable.vala
@@ -168,6 +168,10 @@ public class Controllable : Gtk.Grid, View.Controllable {
return (Cell) get_child_at(col, row);
}
+ internal Cell? get_cell_for_date(Calendar.Date date) {
+ return date_to_cell.get(date);
+ }
+
private void foreach_cell(CellCallback callback) {
foreach (unowned Gtk.Widget widget in get_children()) {
// watch for Gtk.Labels across the top
@@ -286,15 +290,25 @@ public class Controllable : Gtk.Grid, View.Controllable {
}
private void on_calendar_added(Backing.CalendarSource calendar) {
- calendar.notify[Backing.Source.PROP_VISIBLE].connect(queue_draw);
+ calendar.notify[Backing.Source.PROP_VISIBLE].connect(on_calendar_visibility_changed);
calendar.notify[Backing.Source.PROP_COLOR].connect(queue_draw);
}
private void on_calendar_removed(Backing.CalendarSource calendar) {
- calendar.notify[Backing.Source.PROP_VISIBLE].disconnect(queue_draw);
+ calendar.notify[Backing.Source.PROP_VISIBLE].disconnect(on_calendar_visibility_changed);
calendar.notify[Backing.Source.PROP_COLOR].disconnect(queue_draw);
}
+ private void on_calendar_visibility_changed(Object o, ParamSpec pspec) {
+ Backing.CalendarSource calendar = (Backing.CalendarSource) o;
+
+ foreach_cell((cell) => {
+ cell.notify_calendar_visibility_changed(calendar);
+
+ return true;
+ });
+ }
+
private void on_instance_added(Component.Instance instance) {
Component.Event? event = instance as Component.Event;
if (event == null)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]