[california/wip/728838-transitions: 1/3] Refactoring and functionality in place



commit b06d47b4ab8f275abb702bdc9129d4071ffe9042
Author: Jim Nelson <jim yorba org>
Date:   Fri Apr 25 15:40:10 2014 -0700

    Refactoring and functionality in place
    
    Startup time is too slow.

 src/Makefile.am                                    |    4 +-
 src/calendar/calendar-month-of-year.vala           |   14 ++
 src/calendar/calendar-month-span.vala              |  143 ++++++++++++
 src/calendar/calendar-week-span.vala               |    2 +-
 src/host/host-main-window.vala                     |    4 +-
 src/view/month/month-cell.vala                     |    8 +-
 src/view/month/month-controller.vala               |  239 ++++++++++++++++++++
 .../{month-controllable.vala => month-grid.vala}   |  205 +++++------------
 src/view/view-controllable.vala                    |    6 +
 9 files changed, 474 insertions(+), 151 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 1e8ebda..c41eeeb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,6 +59,7 @@ california_VALASOURCES = \
        calendar/calendar-first-of-week.vala \
        calendar/calendar-month.vala \
        calendar/calendar-month-of-year.vala \
+       calendar/calendar-month-span.vala \
        calendar/calendar-olson-zone.vala \
        calendar/calendar-span.vala \
        calendar/calendar-system.vala \
@@ -124,7 +125,8 @@ california_VALASOURCES = \
        \
        view/month/month.vala \
        view/month/month-cell.vala \
-       view/month/month-controllable.vala \
+       view/month/month-controller.vala \
+       view/month/month-grid.vala \
        \
        $(NULL)
 
diff --git a/src/calendar/calendar-month-of-year.vala b/src/calendar/calendar-month-of-year.vala
index 7eaa491..65d297f 100644
--- a/src/calendar/calendar-month-of-year.vala
+++ b/src/calendar/calendar-month-of-year.vala
@@ -84,6 +84,20 @@ public class MonthOfYear : DateSpan {
         return start_date.adjust(quantity, DateUnit.MONTH).month_of_year();
     }
     
+    /**
+     * Returns the chronological next { link MonthOfYear}.
+     */
+    public MonthOfYear next() {
+        return adjust(1);
+    }
+    
+    /**
+     * Returns the chronological prior { link MonthOfYear}.
+     */
+    public MonthOfYear previous() {
+        return adjust(-1);
+    }
+    
     public override string to_string() {
         return "%s %s".printf(month.to_string(), year.to_string());
     }
diff --git a/src/calendar/calendar-month-span.vala b/src/calendar/calendar-month-span.vala
new file mode 100644
index 0000000..506dbc8
--- /dev/null
+++ b/src/calendar/calendar-month-span.vala
@@ -0,0 +1,143 @@
+/* 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.Calendar {
+
+/**
+ * An immutable representation of a span of { link MonthOfYear}s.
+ *
+ * This class provides methods that can turn a { link DateSpan} an iteration of MonthOfYear objects.
+ * Partial months are included; the caller needs to do their own clamping if they want to avoid
+ * days outside of the DateSpan.
+ */
+
+public class MonthSpan : BaseObject, Collection.SimpleIterable<MonthOfYear>, Span<MonthOfYear>,
+    Gee.Comparable<MonthSpan>, Gee.Hashable<MonthSpan> {
+    private class MonthSpanIterator : BaseObject, Collection.SimpleIterator<MonthOfYear> {
+        public MonthOfYear first;
+        public MonthOfYear last;
+        public MonthOfYear? current = null;
+        
+        public MonthSpanIterator(MonthSpan owner) {
+            first = owner.start();
+            last = owner.end();
+        }
+        
+        public new MonthOfYear get() {
+            return current;
+        }
+        
+        public bool next() {
+            if (current == null)
+                current = first;
+            else if (current.start_date.compare_to(last.start_date) < 0)
+                current = current.adjust(1);
+            else
+                return false;
+            
+            return true;
+        }
+        
+        public override string to_string() {
+            return "MonthSpanIterator %s::%s".printf(first.to_string(), last.to_string());
+        }
+    }
+    
+    /**
+     * The { link DateSpan} of the { link MonthOfYear}s.
+     */
+    public DateSpan dates { get; private set; }
+    
+    /**
+     * inheritDoc
+     */
+    public Date start_date { owned get { return dates.start_date; } }
+    
+    /**
+     * inheritDoc
+     */
+    public Date end_date { owned get { return dates.end_date; } }
+    
+    /**
+     * Create a span of { link MonthOfYear}s corresponding to the { link DateSpan}.
+     */
+    public MonthSpan(DateSpan dates) {
+        this.dates = dates;
+    }
+    
+    /**
+     * Create a span of { link MonthOfYear}s corresponding to the start and end months.
+     */
+    public MonthSpan.from_months(MonthOfYear start, MonthOfYear end) {
+        dates = new DateSpan(start.start_date, end.end_date);
+    }
+    
+    /**
+     * Create an arbitrary span of { link MonthOfYear}s starting from the specified starting month.
+     */
+    public MonthSpan.count(MonthOfYear start, int count) {
+        dates = new DateSpan(start.start_date, start.adjust(count).end_date);
+    }
+    
+    /**
+     * inheritDoc
+     */
+    public MonthOfYear start() {
+        return dates.start_date.month_of_year();
+    }
+    
+    /**
+     * inheritDoc
+     */
+    public MonthOfYear end() {
+        return dates.end_date.month_of_year();
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public bool contains(Date date) {
+        return dates.contains(date);
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public bool has(MonthOfYear month) {
+        return (start().compare_to(month) <= 0) && (end().compare_to(month) >= 0);
+    }
+    
+    /**
+     * Returns an Iterator for each { link MonthOfYear} (full and partial) in the { link MonthSpan}.
+     */
+    public Collection.SimpleIterator<MonthOfYear> iterator() {
+        return new MonthSpanIterator(this);
+    }
+    
+    /**
+     * Compares two { link MonthSpan}s by their { link start_date}.
+     */
+    public int compare_to(MonthSpan other) {
+        return start_date.compare_to(other.start_date);
+    }
+    
+    public bool equal_to(MonthSpan other) {
+        if (this == other)
+            return true;
+        
+        return start_date.equal_to(other.start_date) && end_date.equal_to(other.end_date);
+    }
+    
+    public uint hash() {
+        return start_date.hash() ^ end_date.hash();
+    }
+    
+    public override string to_string() {
+        return "months of %s".printf(dates.to_string());
+    }
+}
+
+}
diff --git a/src/calendar/calendar-week-span.vala b/src/calendar/calendar-week-span.vala
index 91093bc..16a54b8 100644
--- a/src/calendar/calendar-week-span.vala
+++ b/src/calendar/calendar-week-span.vala
@@ -47,7 +47,7 @@ public class WeekSpan : BaseObject, Collection.SimpleIterable<Week>, Span<Week>,
     }
     
     /**
-     * The { link DateSpan} of thw { link Week}s.
+     * The { link DateSpan} of the { link Week}s.
      */
     public DateSpan dates { get; private set; }
     
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index 1d2922e..aa8459c 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -36,7 +36,7 @@ public class MainWindow : Gtk.ApplicationWindow {
     public Calendar.FirstOfWeek first_of_week { get; set; }
     
     private View.Controllable current_view;
-    private View.Month.Controllable month_view = new View.Month.Controllable();
+    private View.Month.Controller month_view = new View.Month.Controller();
     private Gtk.Button quick_add_button;
     
     public MainWindow(Application app) {
@@ -109,7 +109,7 @@ public class MainWindow : Gtk.ApplicationWindow {
 #if ENABLE_UNITY
         layout.pack_start(headerbar, false, true, 0);
 #endif
-        layout.pack_end(month_view, true, true, 0);
+        layout.pack_end(month_view.get_container(), true, true, 0);
         
         // current host bindings and signals
         current_view.request_create_timed_event.connect(on_request_create_timed_event);
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 2ee1959..637ef47 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -35,7 +35,7 @@ public class Cell : Gtk.EventBox {
         POINTED
     }
     
-    public weak Controllable owner { get; private set; }
+    public weak Grid owner { get; private set; }
     public int row { get; private set; }
     public int col { get; private set; }
     
@@ -88,7 +88,7 @@ public class Cell : Gtk.EventBox {
     
     private Gtk.DrawingArea canvas = new Gtk.DrawingArea();
     
-    public Cell(Controllable owner, int row, int col) {
+    public Cell(Grid owner, int row, int col) {
         this.owner = owner;
         this.row = row;
         this.col = col;
@@ -395,13 +395,13 @@ public class Cell : Gtk.EventBox {
         }
         
         // only draw bottom line if not on the bottom row
-        if (row < Controllable.ROWS - 1) {
+        if (row < Grid.ROWS - 1) {
             ctx.move_to(0, height);
             ctx.line_to(width, height);
         }
         
         // only draw right line if not on the right-most column
-        if (col < Controllable.COLS - 1) {
+        if (col < Grid.COLS - 1) {
             ctx.move_to(width, 0);
             ctx.line_to(width, height);
         }
diff --git a/src/view/month/month-controller.vala b/src/view/month/month-controller.vala
new file mode 100644
index 0000000..daa4668
--- /dev/null
+++ b/src/view/month/month-controller.vala
@@ -0,0 +1,239 @@
+/* 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.View.Month {
+
+/**
+ * The { link View.Controllable} for a Month View of the user's calendars.
+ *
+ * The Controller holds a GtkStack of { link Grid}s which it "flips" back and forth through as
+ * the user navigates the calendar.
+ */
+
+public class Controller : BaseObject, View.Controllable {
+    public const string PROP_MONTH_OF_YEAR = "month-of-year";
+    public const string PROP_SHOW_OUTSIDE_MONTH = "show-outside-month";
+    
+    // Slower than default to make more apparent to user what's occurring
+    private const int TRANSITION_DURATION_MSEC = 500;
+    
+    /**
+     * The month and year being displayed.
+     *
+     * Defaults to the current month and year.
+     */
+    public Calendar.MonthOfYear month_of_year { get; private set; }
+    
+    /**
+     * @inheritDoc
+     */
+    public Calendar.FirstOfWeek first_of_week { get; set; }
+    
+    /**
+     * Show days outside the current month.
+     */
+    public bool show_outside_month { get; set; default = true; }
+    
+    /**
+     * @inheritDoc
+     */
+    public string current_label { get; protected set; }
+    
+    /**
+     * @inheritDoc
+     */
+    public bool is_viewing_today { get; protected set; }
+    
+    /**
+     * @inheritDoc
+     */
+    public Calendar.Date default_date { get; protected set; }
+    
+    private Gtk.Grid master_grid = new Gtk.Grid();
+    private Gtk.Stack stack = new Gtk.Stack();
+    private Gee.HashMap<Calendar.MonthOfYear, Grid> month_grids = new Gee.HashMap<Calendar.MonthOfYear, 
Grid>();
+    
+    public Controller() {
+        master_grid.column_homogeneous = true;
+        master_grid.column_spacing = 0;
+        master_grid.row_homogeneous = false;
+        master_grid.row_spacing = 0;
+        master_grid.expand = true;
+        
+        stack.transition_duration = TRANSITION_DURATION_MSEC;
+        
+        // insert labels for days of the week across top of master grid
+        for (int col = 0; col < Grid.COLS; col++) {
+            Gtk.Label dow_label = new Gtk.Label(null);
+            dow_label.margin_top = 2;
+            dow_label.margin_bottom = 2;
+            
+            // update label if first-of-week changes
+            int dow_col = col + Calendar.DayOfWeek.MIN;
+            notify[PROP_FIRST_OF_WEEK].connect(() => {
+                Calendar.DayOfWeek dow = Calendar.DayOfWeek.for_checked(dow_col, first_of_week);
+                dow_label.label = dow.abbrev_name;
+            });
+            
+            master_grid.attach(dow_label, col, 0, 1, 1);
+        }
+        
+        // the stack is what flips between the month grids (it's inserted empty here, changes to
+        // first_of_week are what fill the stack with Grids and select which to display)
+        master_grid.attach(stack, 0, 1, Grid.COLS, 1);
+        
+        notify[PROP_MONTH_OF_YEAR].connect(on_month_of_year_changed);
+        Calendar.System.instance.today_changed.connect(on_today_changed);
+        
+        // update now that signal handlers are in place
+        month_of_year = Calendar.System.today.month_of_year();
+        first_of_week = Calendar.FirstOfWeek.SUNDAY;
+    }
+    
+    ~Controller() {
+        Calendar.System.instance.today_changed.disconnect(on_today_changed);
+    }
+    
+    // Creates a new Grid for the MonthOfYear, storing locally and adding to the GtkStack.  Will
+    // reuse existing Grids whenever possible.
+    private Grid create_month_grid(Calendar.MonthOfYear month_of_year) {
+        if (month_grids.has_key(month_of_year))
+            return month_grids.get(month_of_year);
+        
+        Grid month_grid = new Grid(this, month_of_year);
+        month_grid.show_all();
+        
+        // add to local store and to the GtkStack itself
+        month_grids.set(month_of_year, month_grid);
+        stack.add_named(month_grid, month_grid.id);
+        
+        return month_grid;
+    }
+    
+    // Performs Grid caching by ensuring that Grids are available for the current, next, and
+    // previous month and that Grids outside that range are dropped.  The current chronological
+    // month is never discarded.
+    private void update_month_grid_cache() {
+        Calendar.MonthOfYear next_month = month_of_year.next();
+        Calendar.MonthOfYear prev_month = month_of_year.previous();
+        Calendar.MonthSpan cache_span = new Calendar.MonthSpan.from_months(prev_month, next_month);
+        
+        // drop anything outside three-month range, other than current chronological month
+        Gee.MapIterator<Calendar.MonthOfYear, Grid> iter = month_grids.map_iterator();
+        while (iter.next()) {
+            Calendar.MonthOfYear grid_moy = iter.get_key();
+            if (grid_moy.equal_to(Calendar.System.today.month_of_year()))
+                continue;
+            
+            if (cache_span.has(grid_moy))
+                continue;
+            
+            // remove from GtkStack and local storage
+            stack.remove(iter.get_value());
+            iter.unset();
+        }
+        
+        // ensure three-months worth of grids are available
+        create_month_grid(month_of_year);
+        create_month_grid(next_month);
+        create_month_grid(prev_month);
+    }
+    
+    private unowned Grid get_current_month_grid() {
+        return (Grid) stack.get_visible_child();
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public void next() {
+        month_of_year = month_of_year.next();
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public void prev() {
+        month_of_year = month_of_year.previous();
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public Gtk.Widget today() {
+        // since changing the date is expensive in terms of adding/removing subscriptions, only
+        // update the property if it's actually different
+        Calendar.MonthOfYear now = Calendar.System.today.month_of_year();
+        if (!now.equal_to(month_of_year))
+            month_of_year = now;
+        
+        Cell? cell = get_current_month_grid().get_cell_for_date(Calendar.System.today);
+        assert(cell != null);
+        
+        return cell;
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public void unselect_all() {
+        get_current_month_grid().unselect_all();
+    }
+    
+    /**
+     * @inheritDoc
+     */
+    public Gtk.Widget get_container() {
+        return master_grid;
+    }
+    
+    private void update_is_viewing_today() {
+        is_viewing_today = month_of_year.equal_to(Calendar.System.today.month_of_year());
+    }
+    
+    private void on_today_changed() {
+        // don't update view but indicate if it's still in view
+        update_is_viewing_today();
+    }
+    
+    private void on_month_of_year_changed() {
+        current_label = month_of_year.full_name;
+        update_is_viewing_today();
+        
+        // default date is first of month unless displaying current month, in which case it's
+        // current date
+        try {
+            default_date = is_viewing_today ? Calendar.System.today
+                : month_of_year.date_for(month_of_year.first_day_of_month());
+        } catch (CalendarError calerr) {
+            // this should always work
+            error("Unable to set default date for %s: %s", month_of_year.to_string(), calerr.message);
+        }
+        
+        // update the cache to store current month and neighbors
+        update_month_grid_cache();
+        
+        // set up transition to give appearance of moving chronologically through the pages of
+        // a calendar
+        Calendar.MonthOfYear current_moy = get_current_month_grid().month_of_year;
+        int compare = month_of_year.compare_to(current_moy);
+        if (compare < 0)
+            stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT;
+        else if (compare > 0)
+            stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT;
+        else
+            return;
+        
+        stack.set_visible_child(month_grids.get(month_of_year));
+    }
+    
+    public override string to_string() {
+        return "Month.Controller for %s".printf(month_of_year.to_string());
+    }
+}
+
+}
+
diff --git a/src/view/month/month-controllable.vala b/src/view/month/month-grid.vala
similarity index 74%
rename from src/view/month/month-controllable.vala
rename to src/view/month/month-grid.vala
index cf5b34f..c76803b 100644
--- a/src/view/month/month-controllable.vala
+++ b/src/view/month/month-grid.vala
@@ -7,42 +7,38 @@
 namespace California.View.Month {
 
 /**
- * A Gtk.Grid widget that displays a month's worth of days as cells.
- *
- * @see Cell
+ * A Gtk.Grid of { link Cell}s, each representing a particular { link Calendar.Date}.
  */
 
-public class Controllable : Gtk.Grid, View.Controllable {
+public class Grid : Gtk.Grid {
+    public const string PROP_MONTH_OF_YEAR = "month-of-year";
+    public const string PROP_WINDOW = "window";
+    public const string PROP_FIRST_OF_WEEK = "first-of-week";
+    
     // days of the week
     public const int COLS = Calendar.DayOfWeek.COUNT;
     // calendar weeks to be displayed at any one time
     public const int ROWS = 6;
     
-    // day of week labels are stored in the -1 row
-    private const int DOW_ROW = -1;
-    
-    public const string PROP_MONTH_OF_YEAR = "month-of-year";
-    public const string PROP_SHOW_OUTSIDE_MONTH = "show-outside-month";
-    
     // Delegate for walking only Cells in the Grid.  Return true to keep iterating.
     private delegate bool CellCallback(Cell cell);
     
     /**
-     * The month and year being displayed.
-     *
-     * Defaults to the current month and year.
+     * { link Month.Controller} that created and holds this { link Grid}.
      */
-    public Calendar.MonthOfYear month_of_year { get; private set; }
+    public weak Controller owner { get; private set; }
     
     /**
-     * @inheritDoc
+     * { link MonthOfYear} this { link Grid} represents.
+     *
+     * This is immutable; Grids are not designed to be re-used for other months.
      */
-    public Calendar.FirstOfWeek first_of_week { get; set; }
+    public Calendar.MonthOfYear month_of_year { get; private set; }
     
     /**
-     * Show days outside the current month.
+     * The first day of the week, as defined by this { link Grid}'s { link Controller}.
      */
-    public bool show_outside_month { get; set; default = true; }
+    public Calendar.FirstOfWeek first_of_week { get; private set; }
     
     /**
      * The span of dates being displayed.
@@ -50,29 +46,25 @@ public class Controllable : Gtk.Grid, View.Controllable {
     public Calendar.DateSpan window { get; private set; }
     
     /**
-     * @inheritDoc
-     */
-    public string current_label { get; protected set; }
-    
-    /**
-     * @inheritDoc
-     */
-    public bool is_viewing_today { get; protected set; }
-    
-    /**
-     * @inheritDoc
+     * The name (id) of the { link Grid}.
+     *
+     * This is used when the Grid is added to Gtk.Stack.
      */
-    public Calendar.Date default_date { get; protected set; }
+    public string id { get { return month_of_year.full_name; } }
     
     private Gee.HashMap<Calendar.Date, Cell> date_to_cell = new Gee.HashMap<Calendar.Date, Cell>();
-    private Backing.CalendarSubscriptionManager? subscriptions = null;
+    private Backing.CalendarSubscriptionManager subscriptions;
     private Gdk.EventType button_press_type = Gdk.EventType.NOTHING;
     private Gdk.Point button_press_point = Gdk.Point();
     
-    public Controllable() {
+    public Grid(Controller owner, Calendar.MonthOfYear month_of_year) {
+        this.owner = owner;
+        this.month_of_year = month_of_year;
+        first_of_week = owner.first_of_week;
+        
         column_homogeneous = true;
         column_spacing = 0;
-        row_homogeneous = false;
+        row_homogeneous = true;
         row_spacing = 0;
         
         // prep the grid with a fixed number of rows and columns
@@ -82,20 +74,9 @@ public class Controllable : Gtk.Grid, View.Controllable {
         for (int col = 0; col < COLS; col++)
             insert_column(0);
         
-        // pre-add grid elements for days of the week along the top row (using -1 as the row so the
-        // remainder of grid is "naturally" zero-based rows)
-        for (int col = 0; col < COLS; col++) {
-            Gtk.Label dow_cell = new Gtk.Label(null);
-            dow_cell.margin_top = 2;
-            dow_cell.margin_bottom = 2;
-            
-            attach(dow_cell, col, DOW_ROW, 1, 1);
-        }
-        
         // pre-add grid elements for every cell, which are updated when the MonthYear changes
         for (int row = 0; row < ROWS; row++) {
             for (int col = 0; col < COLS; col++) {
-                // mouse events are enabled in Cell's constructor, not here
                 Cell cell = new Cell(this, row, col);
                 cell.expand = true;
                 cell.events |= Gdk.EventMask.BUTTON_PRESS_MASK & Gdk.EventMask.BUTTON1_MOTION_MASK;
@@ -107,58 +88,18 @@ public class Controllable : Gtk.Grid, View.Controllable {
             }
         }
         
-        notify[PROP_MONTH_OF_YEAR].connect(on_month_of_year_changed);
-        notify[PROP_FIRST_OF_WEEK].connect(update_first_of_week);
-        notify[PROP_SHOW_OUTSIDE_MONTH].connect(update_cells);
-        Calendar.System.instance.today_changed.connect(on_today_changed);
-        
-        // update now that signal handlers are in place
-        month_of_year = Calendar.System.today.month_of_year();
-        first_of_week = Calendar.FirstOfWeek.SUNDAY;
-    }
-    
-    ~Controllable() {
-        Calendar.System.instance.today_changed.disconnect(on_today_changed);
-    }
-    
-    /**
-     * @inheritDoc
-     */
-    public void next() {
-        month_of_year = month_of_year.adjust(1);
-    }
-    
-    /**
-     * @inheritDoc
-     */
-    public void prev() {
-        month_of_year = month_of_year.adjust(-1);
-    }
-    
-    /**
-     * @inheritDoc
-     */
-    public Gtk.Widget today() {
-        // since changing the date is expensive in terms of adding/removing subscriptions, only
-        // update the property if it's actually different
-        Calendar.MonthOfYear now = Calendar.System.today.month_of_year();
-        if (!now.equal_to(month_of_year))
-            month_of_year = now;
-        
-        assert(date_to_cell.has_key(Calendar.System.today));
+        // update all the Cells by assigning them Dates ... this also updates the window, which
+        // is necessary for subscriptions
+        update_cells();
+        update_subscriptions();
         
-        return date_to_cell.get(Calendar.System.today);
+        owner.notify[View.Controllable.PROP_FIRST_OF_WEEK].connect(update_first_of_week);
+        owner.notify[Controller.PROP_SHOW_OUTSIDE_MONTH].connect(update_cells);
     }
     
-    /**
-     * @inheritDoc
-     */
-    public void unselect_all() {
-        foreach_cell((cell) => {
-            cell.selected = false;
-            
-            return true;
-        });
+    ~Grid() {
+        owner.notify[View.Controllable.PROP_FIRST_OF_WEEK].disconnect(update_first_of_week);
+        owner.notify[Controller.PROP_SHOW_OUTSIDE_MONTH].disconnect(update_cells);
     }
     
     private Cell get_cell(int row, int col) {
@@ -201,14 +142,14 @@ public class Controllable : Gtk.Grid, View.Controllable {
     
     private void update_week(int row, Calendar.Week week) {
         foreach (Calendar.Date date in week) {
-            int col = date.day_of_week.ordinal(first_of_week) - 1;
+            int col = date.day_of_week.ordinal(owner.first_of_week) - 1;
             
             Cell cell = get_cell(row, col);
             
             // if the date is in the month or configured to show days outside the month, set
             // the cell to show that date; otherwise, it'll be cleared
             cell.clear();
-            cell.date = (date in month_of_year) || show_outside_month ? date : null;
+            cell.date = (date in month_of_year) || owner.show_outside_month ? date : null;
             
             // add to map for quick lookups
             date_to_cell.set(date, cell);
@@ -221,7 +162,7 @@ public class Controllable : Gtk.Grid, View.Controllable {
         
         // create a WeekSpan for the first week of the month to the last displayed week (not all
         // months will fill all displayed weeks, but some will)
-        Calendar.WeekSpan span = new Calendar.WeekSpan.count(month_of_year.weeks(first_of_week).start(),
+        Calendar.WeekSpan span = new 
Calendar.WeekSpan.count(month_of_year.weeks(owner.first_of_week).start(),
             ROWS - 1);
         
         // fill in weeks of the displayed month
@@ -233,53 +174,12 @@ public class Controllable : Gtk.Grid, View.Controllable {
         window = span.to_date_span();
     }
     
-    private void update_first_of_week() {
-        // set label text in day of week row
-        int col = 0;
-        foreach (Calendar.DayOfWeek dow in Calendar.DayOfWeek.iterator(first_of_week)) {
-            Gtk.Label dow_cell = (Gtk.Label) get_child_at(col++, DOW_ROW);
-            dow_cell.label = dow.abbrev_name;
-        }
-        
-        // requires updating all the cells as well, since all dates have to be shifted
-        update_cells();
-        update_subscription();
-    }
-    
-    private void update_is_viewing_today() {
-        is_viewing_today = month_of_year.equal_to(Calendar.System.today.month_of_year());
-    }
-    
-    private void on_today_changed() {
-        // don't update view but indicate if it's still in view
-        update_is_viewing_today();
-    }
-    
-    private void on_month_of_year_changed() {
-        current_label = month_of_year.full_name;
-        update_is_viewing_today();
-        
-        // default date is first of month unless displaying current month, in which case it's
-        // current date
-        try {
-            default_date = is_viewing_today ? Calendar.System.today
-                : month_of_year.date_for(month_of_year.first_day_of_month());
-        } catch (CalendarError calerr) {
-            // this should always work
-            error("Unable to set default date for %s: %s", month_of_year.to_string(), calerr.message);
-        }
-        
-        update_cells();
-        update_subscription();
-    }
-    
-    private void update_subscription() {
+    private void update_subscriptions() {
         // convert DateSpan window into an ExactTimeSpan, which is what the subscription wants
         Calendar.ExactTimeSpan time_window = new Calendar.ExactTimeSpan.from_date_span(window,
             Calendar.Timezone.local);
         
         // create new subscription manager, subscribe to its signals, and let them drive
-        subscriptions = null;
         subscriptions = new Backing.CalendarSubscriptionManager(time_window);
         subscriptions.calendar_added.connect(on_calendar_added);
         subscriptions.calendar_removed.connect(on_calendar_removed);
@@ -289,6 +189,14 @@ public class Controllable : Gtk.Grid, View.Controllable {
         subscriptions.start();
     }
     
+    private void update_first_of_week() {
+        first_of_week = owner.first_of_week;
+        
+        // requires updating all the cells as well, since all dates have to be shifted
+        update_cells();
+        update_subscriptions();
+    }
+    
     private void on_calendar_added(Backing.CalendarSource calendar) {
         calendar.notify[Backing.Source.PROP_VISIBLE].connect(on_calendar_visibility_changed);
         calendar.notify[Backing.Source.PROP_COLOR].connect(queue_draw);
@@ -334,14 +242,21 @@ public class Controllable : Gtk.Grid, View.Controllable {
         }
     }
     
+    public void unselect_all() {
+        foreach_cell((cell) => {
+            cell.selected = false;
+            
+            return true;
+        });
+    }
+    
     private bool on_cell_button_event(Gtk.Widget widget, Gdk.EventButton event) {
         // only interested in left-clicks
         if (event.button != 1)
             return false;
         
         // NOTE: widget is the *pressed* widget, even for "release" events, no matter where the release
-        // occurs ... this signal handler is fired from Cells, never the GtkLabels across the top
-        // of the grid
+        // occurs
         Cell press_cell = (Cell) widget;
         
         switch (event.type) {
@@ -411,12 +326,12 @@ public class Controllable : Gtk.Grid, View.Controllable {
         if (press_cell == release_cell) {
             Component.Event? event = release_cell.get_event_at(release_point);
             if (event != null) {
-                request_display_event(event, release_cell, release_point);
+                owner.request_display_event(event, release_cell, release_point);
                 stop_propagation = true;
             }
         } else if (press_cell.date != null && release_cell.date != null) {
             // create multi-day event
-            request_create_all_day_event(new Calendar.DateSpan(press_cell.date, release_cell.date),
+            owner.request_create_all_day_event(new Calendar.DateSpan(press_cell.date, release_cell.date),
                 release_cell, release_point);
             stop_propagation = true;
         } else {
@@ -453,7 +368,7 @@ public class Controllable : Gtk.Grid, View.Controllable {
         
         Calendar.ExactTime end = start.adjust_time(1, Calendar.TimeUnit.HOUR);
         
-        request_create_timed_event(new Calendar.ExactTimeSpan(start, end), release_cell, release_point);
+        owner.request_create_timed_event(new Calendar.ExactTimeSpan(start, end), release_cell, 
release_point);
         
         // stop propagation
         return true;
@@ -490,6 +405,10 @@ public class Controllable : Gtk.Grid, View.Controllable {
         
         return true;
     }
+    
+    public string to_string() {
+        return "Month.Grid for %s".printf(month_of_year.to_string());
+    }
 }
 
 }
diff --git a/src/view/view-controllable.vala b/src/view/view-controllable.vala
index 0346bf9..db5dc44 100644
--- a/src/view/view-controllable.vala
+++ b/src/view/view-controllable.vala
@@ -65,6 +65,12 @@ public interface Controllable : Object {
         Gdk.Point? for_location);
     
     /**
+     * Returns the Gtk.Widget container that should be used to display the { link Controllable}'s
+     * contents.
+     */
+    public abstract Gtk.Widget get_container();
+    
+    /**
      * Move forward one calendar unit.
      */
     public abstract void next();


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