[california/wip/725783-time] Further work, button handling much improved here



commit 6df229c52a636433010cb6c5f3a6ca6ccfcc6ecf
Author: Jim Nelson <jim yorba org>
Date:   Fri Jul 25 16:33:09 2014 -0700

    Further work, button handling much improved here

 src/Makefile.am                           |    1 +
 src/host/host-event-time-settings.vala    |  154 ++++++++++++++++++++++++-
 src/rc/event-time-settings.ui             |  175 ++++++++++++++++++++++-------
 src/toolkit/toolkit-button-connector.vala |  148 +++++++++----------------
 src/toolkit/toolkit-button-event.vala     |    4 +-
 src/view/week/week-grid.vala              |   22 ++--
 6 files changed, 350 insertions(+), 154 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index fdd41d1..dd14329 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -122,6 +122,7 @@ california_VALASOURCES = \
        toolkit/toolkit-combo-box-text-model.vala \
        toolkit/toolkit-deck.vala \
        toolkit/toolkit-deck-window.vala \
+       toolkit/toolkit-editable-filter.vala \
        toolkit/toolkit-editable-label.vala \
        toolkit/toolkit-entry-clear-text-connector.vala \
        toolkit/toolkit-event-connector.vala \
diff --git a/src/host/host-event-time-settings.vala b/src/host/host-event-time-settings.vala
index 8ff9b91..f6e0e72 100644
--- a/src/host/host-event-time-settings.vala
+++ b/src/host/host-event-time-settings.vala
@@ -10,17 +10,56 @@ namespace California.Host {
 public class EventTimeSettings : Gtk.Box, Toolkit.Card {
     public const string ID = "CaliforniaHostEventTimeSettings";
     
+    private class TimeControls {
+        public Gtk.EventBox hour_up;
+        public Gtk.EventBox hour_down;
+        public Gtk.EventBox minutes_up;
+        public Gtk.EventBox minutes_down;
+        public Gtk.EventBox meridiem_up;
+        public Gtk.EventBox meridiem_down;
+        public Gtk.Entry hour_entry;
+        public Gtk.Entry minutes_entry;
+        public Gtk.Label meridiem_label;
+    }
+    
     [GtkChild]
     private Gtk.Calendar from_calendar;
     
     [GtkChild]
+    private Gtk.Calendar to_calendar;
+    
+    [GtkChild]
     private Gtk.Entry from_hour_entry;
     
     [GtkChild]
+    private Gtk.Label from_colon_label;
+    
+    [GtkChild]
     private Gtk.Entry from_minutes_entry;
     
     [GtkChild]
-    private Gtk.Entry from_meridiem;
+    private Gtk.Label from_meridiem_label;
+    
+    [GtkChild]
+    private Gtk.EventBox from_hour_up;
+    
+    [GtkChild]
+    private Gtk.EventBox from_hour_down;
+    
+    [GtkChild]
+    private Gtk.EventBox from_minutes_up;
+    
+    [GtkChild]
+    private Gtk.EventBox from_minutes_down;
+    
+    [GtkChild]
+    private Gtk.EventBox from_meridiem_up;
+    
+    [GtkChild]
+    private Gtk.EventBox from_meridiem_down;
+    
+    [GtkChild]
+    private Gtk.CheckButton all_day_checkbutton;
     
     public string card_id { get { return ID; } }
     public string? title { get { return null; } }
@@ -28,22 +67,129 @@ public class EventTimeSettings : Gtk.Box, Toolkit.Card {
     public Gtk.Widget? initial_focus { get { return null; } }
     
     private new Component.Event? event = null;
+    private Calendar.WallTime? start_time = null;
+    private TimeControls from_controls = new TimeControls();
+    public Toolkit.ButtonConnector button_connector = new Toolkit.ButtonConnector();
     
     public EventTimeSettings() {
+        from_controls.hour_up = from_hour_up;
+        from_controls.hour_down = from_hour_down;
+        from_controls.minutes_up = from_minutes_up;
+        from_controls.minutes_down = from_minutes_down;
+        from_controls.meridiem_up = from_meridiem_up;
+        from_controls.meridiem_down = from_meridiem_down;
+        from_controls.hour_entry = from_hour_entry;
+        from_controls.minutes_entry = from_minutes_entry;
+        from_controls.meridiem_label = from_meridiem_label;
+        
+        button_connector.connect_to(from_hour_up);
+        button_connector.connect_to(from_hour_down);
+        button_connector.connect_to(from_minutes_up);
+        button_connector.connect_to(from_minutes_down);
+        button_connector.connect_to(from_meridiem_up);
+        button_connector.connect_to(from_meridiem_down);
+        
+        button_connector.clicked.connect(on_time_adjustment_clicked);
+        
+        bind_all_day_to_time_controls(iterate<Gtk.Widget>(
+            from_hour_up, from_hour_down,
+            from_minutes_up, from_minutes_down,
+            from_meridiem_up, from_meridiem_down,
+            from_hour_entry, from_colon_label, from_minutes_entry, from_meridiem_label).to_array_list());
+    }
+    
+    private void bind_all_day_to_time_controls(Gee.List<Gtk.Widget> time_widgets) {
+        foreach (Gtk.Widget time_widget in time_widgets) {
+            all_day_checkbutton.bind_property("active", time_widget, "sensitive",
+                BindingFlags.SYNC_CREATE | BindingFlags.INVERT_BOOLEAN);
+        }
     }
     
     public void jumped_to(Toolkit.Card? from, Toolkit.Card.Jump reason, Value? message) {
         event = (Component.Event) message;
         
+        start_time = event.is_all_day
+            ? Calendar.System.now.to_wall_time()
+            : event.exact_time_span.start_exact_time.to_wall_time();
+        
         init_controls();
     }
     
     private void init_controls() {
         Calendar.DateSpan event_span = event.get_event_date_span(Calendar.Timezone.local);
+        init_calendar(from_calendar, event_span.start_date);
+        init_calendar(to_calendar, event_span.end_date);
         
-        from_calendar.day = event_span.start_date.day_of_month.value;
-        from_calendar.month = event_span.start_date.month.value - 1;
-        from_calendar.year = event_span.start_date.year.value;
+        all_day_checkbutton.active = event.is_all_day;
+        init_time_controls(from_controls, start_time);
+    }
+    
+    private void init_calendar(Gtk.Calendar calendar, Calendar.Date date) {
+        calendar.day = date.day_of_month.value;
+        calendar.month = date.month.value - 1;
+        calendar.year = date.year.value;
+    }
+    
+    private void init_time_controls(TimeControls controls, Calendar.WallTime wall_time) {
+        controls.hour_entry.text = "%d".printf(wall_time.12hour);
+        controls.minutes_entry.text = "%02d".printf(wall_time.minute);
+        controls.meridiem_label.label = wall_time.is_pm ? Calendar.FMT_PM : Calendar.FMT_AM;
+    }
+    
+    private bool on_time_adjustment_clicked(Toolkit.ButtonEvent details) {
+        // ignored late "guaranteed" clicks and clicks from anything but primary button
+        if (details.button != Toolkit.Button.PRIMARY)
+            return Toolkit.PROPAGATE;
+        
+        int amount;
+        Calendar.TimeUnit time_unit;
+        if (!adjust_time_controls(from_controls, details, out amount, out time_unit))
+            return Toolkit.PROPAGATE;
+        
+        start_time = start_time.adjust(amount, time_unit, null);
+        init_time_controls(from_controls, start_time);
+        
+        return Toolkit.STOP;
+    }
+    
+    private bool adjust_time_controls(TimeControls controls, Toolkit.ButtonEvent details, out int amount,
+        out Calendar.TimeUnit time_unit) {
+        if (details.widget == controls.hour_up) {
+            amount = 1;
+            time_unit = Calendar.TimeUnit.HOUR;
+        } else if (details.widget == controls.hour_down) {
+            amount = -1;
+            time_unit = Calendar.TimeUnit.HOUR;
+        } else if (details.widget == controls.minutes_up) {
+            amount = 1;
+            time_unit = Calendar.TimeUnit.MINUTE;
+        } else if (details.widget == controls.minutes_down) {
+            amount = -1;
+            time_unit = Calendar.TimeUnit.MINUTE;
+        } else if (details.widget == controls.meridiem_up) {
+            amount = 12;
+            time_unit = Calendar.TimeUnit.HOUR;
+        } else if (details.widget == controls.meridiem_down) {
+            amount = -12;
+            time_unit = Calendar.TimeUnit.HOUR;
+        } else {
+            amount = 0;
+            time_unit = Calendar.TimeUnit.HOUR;
+            
+            return false;
+        }
+        
+        return true;
+    }
+    
+    [GtkCallback]
+    private void on_cancel_button_clicked() {
+        jump_back();
+    }
+    
+    [GtkCallback]
+    private void on_ok_button_clicked() {
+        jump_back();
     }
 }
 
diff --git a/src/rc/event-time-settings.ui b/src/rc/event-time-settings.ui
index 2885e8b..0dac5af 100644
--- a/src/rc/event-time-settings.ui
+++ b/src/rc/event-time-settings.ui
@@ -70,6 +70,7 @@
                     <property name="vexpand">False</property>
                     <property name="max_length">2</property>
                     <property name="width_chars">2</property>
+                    <property name="xalign">1</property>
                     <property name="input_purpose">digits</property>
                   </object>
                   <packing>
@@ -80,7 +81,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkLabel" id="label2">
+                  <object class="GtkLabel" id="from_colon_label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="halign">center</property>
@@ -113,104 +114,144 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkEntry" id="from_meridiem">
+                  <object class="GtkEventBox" id="from_hour_up">
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="halign">start</property>
-                    <property name="hexpand">False</property>
-                    <property name="max_length">2</property>
-                    <property name="width_chars">2</property>
-                    <property name="input_purpose">alpha</property>
+                    <property name="can_focus">False</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_hour_up_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">up</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
-                    <property name="left_attach">3</property>
-                    <property name="top_attach">1</property>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_hour_up">
+                  <object class="GtkEventBox" id="from_hour_down">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">up</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_hour_down_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">down</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
                     <property name="left_attach">0</property>
-                    <property name="top_attach">0</property>
+                    <property name="top_attach">2</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_minutes_up">
+                  <object class="GtkLabel" id="from_meridiem_label">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">up</property>
+                    <property name="label">am</property>
+                    <property name="width_chars">3</property>
+                    <property name="max_width_chars">2</property>
                   </object>
                   <packing>
-                    <property name="left_attach">2</property>
-                    <property name="top_attach">0</property>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">1</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_meridiem_up">
+                  <object class="GtkEventBox" id="from_minutes_up">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">up</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_minutes_up_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">up</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
-                    <property name="left_attach">3</property>
+                    <property name="left_attach">2</property>
                     <property name="top_attach">0</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_hour_down">
+                  <object class="GtkEventBox" id="from_minutes_down">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">down</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_minutes_down_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">down</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
-                    <property name="left_attach">0</property>
+                    <property name="left_attach">2</property>
                     <property name="top_attach">2</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_minutes_down">
+                  <object class="GtkEventBox" id="from_meridiem_up">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">down</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_meridiem_up_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">up</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
-                    <property name="left_attach">2</property>
-                    <property name="top_attach">2</property>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">0</property>
                     <property name="width">1</property>
                     <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkArrow" id="from_meridiem_down">
+                  <object class="GtkEventBox" id="from_meridiem_down">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="hexpand">False</property>
-                    <property name="arrow_type">down</property>
+                    <property name="visible_window">False</property>
+                    <child>
+                      <object class="GtkArrow" id="from_meridiem_down_arrow">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">False</property>
+                        <property name="arrow_type">down</property>
+                      </object>
+                    </child>
                   </object>
                   <packing>
                     <property name="left_attach">3</property>
@@ -243,6 +284,8 @@
           <object class="GtkLabel" id="to_label">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <property name="margin_left">4</property>
+            <property name="margin_right">4</property>
             <property name="label" translatable="yes" comments="As in &quot;From 9pm to 
10pm&quot;">to</property>
             <attributes>
               <attribute name="weight" value="bold"/>
@@ -310,5 +353,55 @@
         <property name="position">2</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkButtonBox" id="buttonbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">end</property>
+        <property name="margin_top">8</property>
+        <property name="spacing">8</property>
+        <property name="homogeneous">True</property>
+        <property name="layout_style">start</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">_Cancel</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+            <signal name="clicked" handler="on_cancel_button_clicked" 
object="CaliforniaHostEventTimeSettings" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="ok_button">
+            <property name="label" translatable="yes">_OK</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+            <signal name="clicked" handler="on_ok_button_clicked" object="CaliforniaHostEventTimeSettings" 
swapped="no"/>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="pack_type">end</property>
+        <property name="position">3</property>
+      </packing>
+    </child>
   </template>
 </interface>
diff --git a/src/toolkit/toolkit-button-connector.vala b/src/toolkit/toolkit-button-connector.vala
index 0549f57..a91f943 100644
--- a/src/toolkit/toolkit-button-connector.vala
+++ b/src/toolkit/toolkit-button-connector.vala
@@ -20,46 +20,12 @@ namespace California.Toolkit {
  */
 
 public class ButtonConnector : EventConnector {
-    // GDK reports 250ms is used to determine if a click is a double-click (and another 250ms for
-    // triple-click), so pause just a little more than that to determine if all the clicking is
-    // done
-    private const int CLICK_DETERMINATION_DELAY_MSEC = 255;
-    
-    // The actual ButtonEvent, with some useful functionality for release timeouts
-    private class InternalButtonEvent : ButtonEvent {
-        private Scheduled? scheduled_timeout = null;
-        
-        public signal void release_timeout();
-        
-        public InternalButtonEvent(Gtk.Widget widget, Gdk.EventButton event) {
-            base (widget, event);
-        }
-        
-        public override void update_press(Gtk.Widget widget, Gdk.EventButton press_event) {
-            base.update_press(widget, press_event);
-            
-            if (scheduled_timeout != null)
-                scheduled_timeout.cancel();
-        }
-        
-        public override void update_release(Gtk.Widget widget, Gdk.EventButton release_event) {
-            base.update_release(widget, release_event);
-            
-            scheduled_timeout = new Scheduled.once_after_msec(CLICK_DETERMINATION_DELAY_MSEC,
-                on_timeout, Priority.LOW);
-        }
-        
-        private void on_timeout() {
-            release_timeout();
-        }
-    }
-    
-    private Gee.HashMap<Gtk.Widget, InternalButtonEvent> primary_states = new Gee.HashMap<
-        Gtk.Widget, InternalButtonEvent>();
-    private Gee.HashMap<Gtk.Widget, InternalButtonEvent> secondary_states = new Gee.HashMap<
-        Gtk.Widget, InternalButtonEvent>();
-    private Gee.HashMap<Gtk.Widget, InternalButtonEvent> tertiary_states = new Gee.HashMap<
-        Gtk.Widget, InternalButtonEvent>();
+    private Gee.HashMap<Gtk.Widget, ButtonEvent> primary_states = new Gee.HashMap<
+        Gtk.Widget, ButtonEvent>();
+    private Gee.HashMap<Gtk.Widget, ButtonEvent> secondary_states = new Gee.HashMap<
+        Gtk.Widget, ButtonEvent>();
+    private Gee.HashMap<Gtk.Widget, ButtonEvent> tertiary_states = new Gee.HashMap<
+        Gtk.Widget, ButtonEvent>();
     
     /**
      * The "raw" "button-pressed" signal received by { link ButtonConnector}.
@@ -80,33 +46,26 @@ public class ButtonConnector : EventConnector {
     /**
      * Fired when a button is pressed and released once.
      *
-     * The "guaranteed" flag is important to distinguish here.  If set, that indicates a timeout
-     * has occurred and the user did not follow the click with a second or third.  If not set,
-     * this was fired immediately after the user released the button and it is unknown if the user
-     * intends to follow it with more clicks.
-     *
-     * Because no timeout has occurred, unguaranteed clicks can be processed immediately if they
-     * occur on a widget or location where double- and triple-clicks are meaningless.
-     *
-     * NOTE: This means "clicked" (and { link double_clicked} and { link triple_clicked} will be
-     * fired ''twice'', once unguaranteed, once guaranteed.  To prevent double-processing, handlers
-     * should always check the flag.
+     * Note that this can be fired after { link double_clicked} and { link triple_clicked}.  That
+     * indicates that the user double- or triple-clicked ''and'' the other signal handlers did not
+     * return { link Toolkit.STOP}, indicating the event was unhandled or unabsorbed by the signal
+     * handlers.  If either returns STOP, "clicked" will not fire.
      */
-    public signal void clicked(ButtonEvent details, bool guaranteed);
+    public signal bool clicked(ButtonEvent details);
     
     /**
      * Fired when a button is pressed and released twice in succession.
      *
-     * See { link clicked} for an explanation of the { link guaranteed} flag.
+     * See { link clicked} for an explanation of signal firing order.
      */
-    public signal void double_clicked(ButtonEvent details, bool guaranteed);
+    public signal bool double_clicked(ButtonEvent details);
     
     /**
      * Fired when a button is pressed and released thrice in succession.
      *
-     * See { link clicked} for an explanation of the { link guaranteed} flag.
+     * See { link clicked} for an explanation of signal firing order.
      */
-    public signal void triple_clicked(ButtonEvent details, bool guaranteed);
+    public signal bool triple_clicked(ButtonEvent details);
     
     /**
      * Create a new { link ButtonConnector} for monitoring (mouse) button events from Gtk.Widgets.
@@ -119,7 +78,7 @@ public class ButtonConnector : EventConnector {
      * Subclasses may override this method to hook into this event before or after the signal
      * has fired.
      *
-     * @return { link EVENT_STOP} or { link EVENT_PROPAGATE}.
+     * @return { link STOP} or { link PROPAGATE}.
      */
     protected virtual bool notify_pressed(Gtk.Widget widget, Button button, Gdk.Point point,
         Gdk.EventType event_type) {
@@ -130,7 +89,7 @@ public class ButtonConnector : EventConnector {
      * Subclasses may override this method to hook into this event before or after the signal
      * has fired.
      *
-     * @return { link EVENT_STOP} or { link EVENT_PROPAGATE}.
+     * @return { link STOP} or { link PROPAGATE}.
      */
     protected virtual bool notify_released(Gtk.Widget widget, Button button, Gdk.Point point,
         Gdk.EventType event_type) {
@@ -141,24 +100,24 @@ public class ButtonConnector : EventConnector {
      * Subclasses may override this method to hook into this event before or after the signal
      * has fired.
      */
-    protected virtual void notify_clicked(ButtonEvent details, bool guaranteed) {
-        clicked(details, guaranteed);
+    protected virtual bool notify_clicked(ButtonEvent details) {
+        return clicked(details);
     }
     
     /**
      * Subclasses may override this method to hook into this event before or after the signal
      * has fired.
      */
-    protected virtual void notify_double_clicked(ButtonEvent details, bool guaranteed) {
-        double_clicked(details, guaranteed);
+    protected virtual bool notify_double_clicked(ButtonEvent details) {
+        return double_clicked(details);
     }
     
     /**
      * Subclasses may override this method to hook into this event before or after the signal
      * has fired.
      */
-    protected virtual void notify_triple_clicked(ButtonEvent details, bool guaranteed) {
-        triple_clicked(details, guaranteed);
+    protected virtual bool notify_triple_clicked(ButtonEvent details) {
+        return triple_clicked(details);
     }
     
     protected override void connect_signals(Gtk.Widget widget) {
@@ -182,7 +141,7 @@ public class ButtonConnector : EventConnector {
         tertiary_states.unset(widget);
     }
     
-    private Gee.HashMap<Gtk.Widget, InternalButtonEvent>? get_states_map(Button button) {
+    private Gee.HashMap<Gtk.Widget, ButtonEvent>? get_states_map(Button button) {
         switch (button) {
             case Button.PRIMARY:
                 return primary_states;
@@ -208,7 +167,7 @@ public class ButtonConnector : EventConnector {
     }
     
     private bool process_button_event(Gtk.Widget widget, Gdk.EventButton event,
-        Button button, Gee.HashMap<Gtk.Widget, InternalButtonEvent>? button_states) {
+        Button button, Gee.HashMap<Gtk.Widget, ButtonEvent>? button_states) {
         switch(event.type) {
             case Gdk.EventType.BUTTON_PRESS:
             case Gdk.EventType.2BUTTON_PRESS:
@@ -227,10 +186,9 @@ public class ButtonConnector : EventConnector {
                 // previous press (possible for multiple press events to arrive back-to-back
                 // when double- and triple-clicking)
                 if (button_states != null) {
-                    InternalButtonEvent? details = button_states.get(widget);
+                    ButtonEvent? details = button_states.get(widget);
                     if (details == null) {
-                        details = new InternalButtonEvent(widget, event);
-                        details.release_timeout.connect(on_release_timeout);
+                        details = new ButtonEvent(widget, event);
                         button_states.set(widget, details);
                     } else {
                         details.update_press(widget, event);
@@ -251,26 +209,11 @@ public class ButtonConnector : EventConnector {
                 
                 // update saved state (if any) with release info and start timer
                 if (button_states != null) {
-                    InternalButtonEvent? details = button_states.get(widget);
+                    ButtonEvent? details = button_states.get(widget);
                     if (details != null) {
-                        // fire "unguaranteed" clicked signals now (with button release) rather than
-                        // wait for timeout using the current value of press_type before the details
-                        // are updated
-                        switch (details.press_type) {
-                            case Gdk.EventType.BUTTON_PRESS:
-                                notify_clicked(details, false);
-                            break;
-                            
-                            case Gdk.EventType.2BUTTON_PRESS:
-                                notify_double_clicked(details, false);
-                            break;
-                            
-                            case Gdk.EventType.3BUTTON_PRESS:
-                                notify_triple_clicked(details, false);
-                            break;
-                        }
-                        
                         details.update_release(widget, event);
+                        
+                        return synthesize_click(details);
                     }
                 }
             break;
@@ -279,27 +222,38 @@ public class ButtonConnector : EventConnector {
         return Toolkit.PROPAGATE;
     }
     
-    private void on_release_timeout(InternalButtonEvent details) {
-        // release button timed-out, meaning it's time to evaluate where the sequence stands and
-        // notify subscribers
+    private bool synthesize_click(ButtonEvent details) {
+        // use Accept flags to indicate which signal(s) to fire
+        bool result = Toolkit.PROPAGATE;
         switch (details.press_type) {
             case Gdk.EventType.BUTTON_PRESS:
-                notify_clicked(details, true);
+                result = notify_clicked(details);
             break;
             
             case Gdk.EventType.2BUTTON_PRESS:
-                notify_double_clicked(details, true);
+                result = notify_double_clicked(details);
+                if (result != Toolkit.STOP)
+                    result = notify_clicked(details);
             break;
             
             case Gdk.EventType.3BUTTON_PRESS:
-                notify_triple_clicked(details, true);
+                result = notify_triple_clicked(details);
+                if (result != Toolkit.STOP) {
+                    result = notify_double_clicked(details);
+                    if (result != Toolkit.STOP)
+                        result = notify_clicked(details);
+                }
             break;
         }
         
-        // drop state, now finished with it
-        Gee.HashMap<Gtk.Widget, InternalButtonEvent>? states_map = get_states_map(details.button);
-        if (states_map != null)
-            states_map.unset(details.widget);
+        if (result == Toolkit.STOP) {
+            // drop event entirely
+            Gee.HashMap<Gtk.Widget, ButtonEvent>? states_map = get_states_map(details.button);
+            if (states_map != null)
+                states_map.unset(details.widget);
+        }
+        
+        return result;
     }
 }
 
diff --git a/src/toolkit/toolkit-button-event.vala b/src/toolkit/toolkit-button-event.vala
index 775267e..25319a0 100644
--- a/src/toolkit/toolkit-button-event.vala
+++ b/src/toolkit/toolkit-button-event.vala
@@ -106,7 +106,7 @@ public class ButtonEvent : BaseObject {
     }
     
     // Update state with the next button press
-    internal virtual void update_press(Gtk.Widget widget, Gdk.EventButton press_event) {
+    internal void update_press(Gtk.Widget widget, Gdk.EventButton press_event) {
         assert(this.widget == widget);
         assert(Button.from_button_event(press_event) == button);
         
@@ -116,7 +116,7 @@ public class ButtonEvent : BaseObject {
     }
     
     // Update state with the next button release and start the release timer
-    internal virtual void update_release(Gtk.Widget widget, Gdk.EventButton release_event) {
+    internal void update_release(Gtk.Widget widget, Gdk.EventButton release_event) {
         assert(this.widget == widget);
         assert(Button.from_button_event(release_event) == button);
         
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index 1920b6c..ea82fdc 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -318,28 +318,28 @@ internal class Grid : Gtk.Box {
         return date_to_all_day.get(cell_date);
     }
     
-    private void on_instance_container_clicked(Toolkit.ButtonEvent details, bool guaranteed) {
-        // only interested in unguaranteed clicks on the primary mouse button
-        if (details.button != Toolkit.Button.PRIMARY || guaranteed)
-            return;
+    private bool on_instance_container_clicked(Toolkit.ButtonEvent details) {
+        if (details.button != Toolkit.Button.PRIMARY)
+            return Toolkit.PROPAGATE;
         
         Common.InstanceContainer instance_container = (Common.InstanceContainer) details.widget;
         
         Component.Event? event = instance_container.get_event_at(details.press_point);
         if (event != null)
             owner.request_display_event(event, instance_container, details.press_point);
+        
+        return Toolkit.STOP;
     }
     
-    private void on_instance_container_double_clicked(Toolkit.ButtonEvent details, bool guaranteed) {
-        // only interested in unguaranteed double-clicks on the primary mouse button
-        if (details.button != Toolkit.Button.PRIMARY || guaranteed)
-            return;
+    private bool on_instance_container_double_clicked(Toolkit.ButtonEvent details) {
+        if (details.button != Toolkit.Button.PRIMARY)
+            return Toolkit.PROPAGATE;
         
         Common.InstanceContainer instance_container = (Common.InstanceContainer) details.widget;
         
         // if an event is at this location, don't process
         if (instance_container.get_event_at(details.press_point) != null)
-            return;
+            return Toolkit.PROPAGATE;
         
         // if a DayPane, use double-click to determine rounded time of the event's start
         DayPane? day_pane = instance_container as DayPane;
@@ -355,12 +355,14 @@ internal class Grid : Gtk.Box {
                 new Calendar.ExactTimeSpan(start_time, start_time.adjust_time(1, Calendar.TimeUnit.HOUR)),
                 day_pane, details.press_point);
             
-            return;
+            return Toolkit.STOP;
         }
         
         // otherwise, an all-day-cell, so request an all-day event
         owner.request_create_all_day_event(instance_container.contained_span, instance_container,
             details.press_point);
+        
+        return Toolkit.STOP;
     }
     
     private void on_day_pane_motion(Toolkit.MotionEvent details) {



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