[california/wip/734698-agenda] Allow for moving the Agenda chron window back and forth, expand



commit b5dab5261c8444a947a9d228cfbdb90981b38936
Author: Jim Nelson <jim yorba org>
Date:   Mon Dec 8 14:43:02 2014 -0800

    Allow for moving the Agenda chron window back and forth, expand

 .../backing-calendar-subscription-manager.vala     |    5 +
 src/calendar/calendar-span.vala                    |   38 ++++++++++
 src/toolkit/toolkit-listbox-model.vala             |    9 ++
 src/view/agenda/agenda-controller.vala             |   78 ++++++++++++++++++--
 src/view/agenda/agenda-event-row.vala              |    2 +-
 5 files changed, 123 insertions(+), 9 deletions(-)
---
diff --git a/src/backing/backing-calendar-subscription-manager.vala 
b/src/backing/backing-calendar-subscription-manager.vala
index b86c78d..68fa314 100644
--- a/src/backing/backing-calendar-subscription-manager.vala
+++ b/src/backing/backing-calendar-subscription-manager.vala
@@ -126,6 +126,11 @@ public class CalendarSubscriptionManager : BaseObject {
      * are unguaranteed if called while start_async() is executing.
      *
      * If expanded_time is within the current window, nothing happens.
+     *
+     * TODO: Currently the subscription manager will expand the range by creating a new
+     * { link CalendarSubscription} for the new dates.  This can be inefficient when dealing with
+     * lots of small ranges.  A better solution would be to create wider ranges and filter out
+     * events outside of the specified window.
      */
     public async void expand_window_async(Calendar.ExactTime expanded_time) {
         if (expanded_time in window)
diff --git a/src/calendar/calendar-span.vala b/src/calendar/calendar-span.vala
index 30d2997..e5f2355 100644
--- a/src/calendar/calendar-span.vala
+++ b/src/calendar/calendar-span.vala
@@ -194,6 +194,44 @@ public abstract class Span : BaseObject {
     }
     
     /**
+     * Returns a { link DateSpan} that represents this { link Span} with the { link start_date}
+     * set to the supplied { link Date}.
+     *
+     * If the new start_date is the same or later than the { link end_date}, a one-day Span is
+     * returned that matches the supplied Date.
+     *
+     * If the new start date is outside the range of this Span, a DateSpan for this Span is
+     * returned, i.e. this is just like calling { link to_date_span}.
+     *
+     * @see reduce_from_end
+     */
+    public DateSpan reduce_from_start(Calendar.Date new_start_date) {
+        if (!has_date(new_start_date))
+            return to_date_span();
+        
+        return new DateSpan(new_start_date, end_date);
+    }
+    
+    /**
+     * Returns a { link DateSpan} that represents this { link Span} with the { link end_date}
+     * set to the supplied { link Date}.
+     *
+     * If the new end_date is the same or earlier than the { link start_date}, a one-day Span is
+     * returned that matches the supplied Date.
+     *
+     * If the new end date is outside the range of this Span, a DateSpan for this Span is
+     * returned, i.e. this is just like calling { link to_date_span}.
+     *
+     * @see reduce_from_start
+     */
+    public DateSpan reduce_from_end(Calendar.Date new_end_date) {
+        if (!has_date(new_end_date))
+            return to_date_span();
+        
+        return new DateSpan(start_date, new_end_date);
+    }
+    
+    /**
      * True if the { link Span} contains the specified { link Date}.
      */
     public bool has_date(Date date) {
diff --git a/src/toolkit/toolkit-listbox-model.vala b/src/toolkit/toolkit-listbox-model.vala
index 8595bd6..10d92a3 100644
--- a/src/toolkit/toolkit-listbox-model.vala
+++ b/src/toolkit/toolkit-listbox-model.vala
@@ -126,9 +126,18 @@ public class ListBoxModel<G> : BaseObject {
         // item -> Gtk.ListBoxRow, with MutableWidget support
         Gtk.ListBoxRow row = new Gtk.ListBoxRow();
         Gtk.Widget widget = model_presentation(item);
+        
+        // allow for external callers to make the ListBoxRow visible via their supplied widget's
+        // visibility flag ... this is necessary because setting the presentation widget to invisible
+        // leaves the row's visible and taking up a little space for border and margin and such
+        widget.bind_property("visible", row, "visible",
+            BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
+        
+        // if widget is mutable, watch for that
         MutableWidget? mutable = widget as MutableWidget;
         if (mutable != null)
             mutable.mutated.connect(() => { row.changed(); });
+        
         row.add(widget);
         
         // mappings
diff --git a/src/view/agenda/agenda-controller.vala b/src/view/agenda/agenda-controller.vala
index 66e0aba..3b85944 100644
--- a/src/view/agenda/agenda-controller.vala
+++ b/src/view/agenda/agenda-controller.vala
@@ -108,13 +108,14 @@ public class Controller : BaseObject, View.Controllable {
      * @inheritDoc
      */
     public void next() {
+        reduce_subscriptions_start(current_span.start_date.adjust_by(1, Calendar.DateUnit.DAY));
     }
     
     /**
      * @inheritDoc
      */
     public void previous() {
-        expand_subscriptions(current_span.start_date.adjust(-1));
+        expand_subscriptions(current_span.start_date.adjust_by(-1, Calendar.DateUnit.DAY));
     }
     
     /**
@@ -156,10 +157,28 @@ public class Controller : BaseObject, View.Controllable {
         expand_subscriptions(current_span.end_date.adjust_by(1, Calendar.DateUnit.MONTH));
     }
     
+    private Iterable<DateRow> traverse_date_rows() {
+        return traverse<Calendar.Date>(listbox_model.all())
+            .map_nonnull<DateRow>(date => listbox_model.get_widget_for_item(date) as DateRow);
+    }
+    
+    // Make existing DateRow widgets visible depending on if they're in the current_span; don't
+    // remove them to allow them to continue to receive event notifications in case the window is
+    // widened again to show them
+    private void show_hide_date_rows() {
+        traverse_date_rows()
+            .iterate(date_row => date_row.visible = date_row.date in current_span);
+    }
+    
+    private void clear_date_rows() {
+        traverse_date_rows()
+            .iterate(date_row => listbox_model.remove(date_row.date));
+    }
+    
     private void reset_subscriptions(Calendar.DateSpan new_span) {
         current_span = new_span;
         
-        listbox_model.clear();
+        clear_date_rows();
         
         subscriptions = new Backing.CalendarSubscriptionManager(
             current_span.to_exact_time_span(Calendar.Timezone.local));
@@ -177,8 +196,50 @@ public class Controller : BaseObject, View.Controllable {
     private void expand_subscriptions(Calendar.Date expansion) {
         current_span = current_span.expand(expansion);
         
-        subscriptions.expand_window_async.begin(
-            expansion.to_exact_time_span(Calendar.Timezone.local).start_exact_time);
+        // make previously invisible widgets (due to window reduction) visible again if in new span
+        show_hide_date_rows();
+        
+        // to avoid adding a lot of little expansions (which is expensive), add them a month at a
+        // time ... first check if subscription expansion even necessary, and if so, on which ends
+        // of the span ... first, convert to DateSpan
+        Calendar.DateSpan sub_span = new Calendar.DateSpan.from_exact_time_span(
+            subscriptions.window.to_timezone(Calendar.Timezone.local));
+        
+        bool expanded = false;
+        
+        // if necessary, walk the subscription start date back one month from requested date
+        if (!(current_span.start_date in sub_span)) {
+            Calendar.Date new_sub_start = sub_span.start_date.adjust_by(-1, Calendar.DateUnit.MONTH);
+            if (current_span.start_date.compare_to(new_sub_start) < 0)
+                new_sub_start = current_span.start_date;
+            
+            subscriptions.expand_window_async.begin(
+                new_sub_start.to_exact_time_span(Calendar.Timezone.local).start_exact_time);
+            expanded = true;
+        }
+        
+        // do the same for the subscription end date
+        if (!(current_span.end_date in sub_span)) {
+            Calendar.Date new_sub_end = sub_span.end_date.adjust_by(1, Calendar.DateUnit.MONTH);
+            if (current_span.end_date.compare_to(new_sub_end) > 0)
+                new_sub_end = current_span.end_date;
+            
+            subscriptions.expand_window_async.begin(
+                new_sub_end.to_exact_time_span(Calendar.Timezone.local).end_exact_time);
+            expanded = true;
+        }
+        
+        if (expanded)
+            debug("Agenda subscription window expanded to %s", subscriptions.window.to_string());
+        
+        update_view_details();
+    }
+    
+    private void reduce_subscriptions_start(Calendar.Date new_start) {
+        current_span = current_span.reduce_from_start(new_start);
+        
+        // make previously invisible widgets (due to window reduction) visible again if in new span
+        show_hide_date_rows();
         
         update_view_details();
     }
@@ -203,10 +264,8 @@ public class Controller : BaseObject, View.Controllable {
     private void on_calendar_visibility_changed(Object o, ParamSpec pspec) {
         Backing.CalendarSource calendar = (Backing.CalendarSource) o;
         
-        foreach (Calendar.Date date in listbox_model.all()) {
-            DateRow date_row = (DateRow) listbox_model.get_widget_for_item(date);
-            date_row.notify_calendar_visibility_changed(calendar);
-        }
+        traverse_date_rows()
+            .iterate(date_row => date_row.notify_calendar_visibility_changed(calendar));
     }
     
     private void on_instance_added_or_altered(Component.Instance instance) {
@@ -221,6 +280,9 @@ public class Controller : BaseObject, View.Controllable {
             
             DateRow date_row = (DateRow) listbox_model.get_widget_for_item(date);
             date_row.add_event(event);
+            
+            // possible to be notified of Event outside of current_span; see reduce_subscriptions()
+            date_row.visible = date in current_span;
         }
     }
     
diff --git a/src/view/agenda/agenda-event-row.vala b/src/view/agenda/agenda-event-row.vala
index 6580510..af1d4cc 100644
--- a/src/view/agenda/agenda-event-row.vala
+++ b/src/view/agenda/agenda-event-row.vala
@@ -7,7 +7,7 @@
 namespace California.View.Agenda {
 
 [GtkTemplate (ui = "/org/yorba/california/rc/view-agenda-event-row.ui")]
-private class EventRow : Gtk.Box {
+private class EventRow : Gtk.Box, Toolkit.MutableWidget {
     private const Calendar.WallTime.PrettyFlag TIME_PRETTY_FLAGS = Calendar.WallTime.PrettyFlag.NONE;
     
     private static Gtk.SizeGroup time_label_size_group;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]