[dasher] In two-button dynamic mode, add option to invert the sense of a



commit 7ef84213b013eb2f18ce344179488a32b1d8d15c
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Thu Jul 2 13:32:38 2009 +0100

    In two-button dynamic mode, add option to invert the sense of a
    double-click. (Includes partial implementation of RevertPresses)

 ChangeLog                                 |    2 +
 Src/DasherCore/ButtonMultiPress.cpp       |   46 ++++++++++++-------------
 Src/DasherCore/ButtonMultiPress.h         |    1 +
 Src/DasherCore/DynamicFilter.cpp          |    3 +-
 Src/DasherCore/DynamicFilter.h            |    2 +-
 Src/DasherCore/OneButtonDynamicFilter.cpp |    6 +--
 Src/DasherCore/OneButtonDynamicFilter.h   |    1 +
 Src/DasherCore/Parameters.h               |    8 ++--
 Src/DasherCore/TwoButtonDynamicFilter.cpp |   53 +++++++++++++++++++++++-----
 Src/DasherCore/TwoButtonDynamicFilter.h   |    8 ++++
 10 files changed, 85 insertions(+), 45 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 974263c..7ba826c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,8 @@
 	* Remove NodeManagerFactory class.
 	* Remove CControlManagerFactory class.
 	* Rename BP_DELAY_VIEW to BP_SMOOTH_OFFSET and tidy its handling.
+	* In two-button dynamic mode, add option to invert the sense of a
+	  double-click.
 
 2009-07-02  Patrick Welche <prlw1 cam ac uk>
 
diff --git a/Src/DasherCore/ButtonMultiPress.cpp b/Src/DasherCore/ButtonMultiPress.cpp
index 0f777ae..ace0652 100644
--- a/Src/DasherCore/ButtonMultiPress.cpp
+++ b/Src/DasherCore/ButtonMultiPress.cpp
@@ -30,34 +30,32 @@ void CButtonMultiPress::KeyDown(int iTime, int iId, CDasherView *pView, CDasherM
   if (m_bKeyDown) return;
       
   // Check for multiple clicks
-  if(iId == m_iQueueId) {
-    while((m_deQueueTimes.size() > 0) && (iTime - m_deQueueTimes.front()) > GetLongParameter(LP_MULTIPRESS_TIME))
-      m_deQueueTimes.pop_front();
-
-    //if that's the final press...  
-    if(m_deQueueTimes.size() == static_cast<unsigned int>(GetLongParameter(LP_MULTIPRESS_COUNT) - 1)) { 
-      //undo the preceding ones...
-      RevertPresses(GetLongParameter(LP_MULTIPRESS_COUNT) - 1);
-      //execute the event
-      Event(iTime, iId, 2, pModel, pUserLog);
-      m_deQueueTimes.clear();
-    }
-    else {
-      //not the final press; so record...
-    m_deQueueTimes.push_back(iTime);
-      //and process normally
-      //(this may clear the queue if it changes the state)
-      CDynamicFilter::KeyDown(iTime, iId, pView, pModel, pUserLog);
+  if(iId == m_iQueueId && m_deQueueTimes.size()) {
+    if ( (iTime - m_deQueueTimes.back()) > GetLongParameter(LP_MULTIPRESS_TIME) )
+      m_deQueueTimes.clear(); //and fall through to record+process normally, below
+    else
+    {
+      //previous presses should not be treated as such....
+      RevertPresses(m_deQueueTimes.size());
+      //...but should be combined with this one into a new event (type = #presses)
+      Event(iTime, iId, m_deQueueTimes.size()+1, pModel, pUserLog);
+      if (m_deQueueTimes.size() >= maxClickCount() - 1)
+	m_deQueueTimes.clear(); //final press
+      else //may still be more presses to come
+	m_deQueueTimes.push_back(iTime);
+      return; //we've called Event ourselves, so finished.
     }
   }
-  else {
-    //record as first press...
-    m_deQueueTimes.clear();
-    m_deQueueTimes.push_back(iTime);
+  else
+  {
+    m_deQueueTimes.clear(); //clear record of previous, different, button
     m_iQueueId = iId;
-    //...and process normally; if it changes the state, pause()/reverse()'ll clear the queue
-    CDynamicFilter::KeyDown(iTime, iId, pView, pModel, pUserLog);
   }
+   
+  //record press...
+  m_deQueueTimes.push_back(iTime);
+  //...and process normally; if it changes the state, pause()/reverse()'ll clear the queue
+  CDynamicFilter::KeyDown(iTime, iId, pView, pModel, pUserLog);
 }
 
 void CButtonMultiPress::pause()
diff --git a/Src/DasherCore/ButtonMultiPress.h b/Src/DasherCore/ButtonMultiPress.h
index cb172c8..faf4026 100644
--- a/Src/DasherCore/ButtonMultiPress.h
+++ b/Src/DasherCore/ButtonMultiPress.h
@@ -33,6 +33,7 @@ class CButtonMultiPress : public CDynamicFilter {
   virtual void KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
 
  protected:
+  virtual unsigned int maxClickCount()=0;
   virtual void reverse();
   virtual void pause();
   virtual void run(int iSubclassState);
diff --git a/Src/DasherCore/DynamicFilter.cpp b/Src/DasherCore/DynamicFilter.cpp
index 8c1416f..d83e449 100644
--- a/Src/DasherCore/DynamicFilter.cpp
+++ b/Src/DasherCore/DynamicFilter.cpp
@@ -120,8 +120,7 @@ void CDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pMod
 	ActionButton(iTime, iButton, iType, pModel, pUserLog);
       }
       break;
-    case 1: // Delibarate fallthrough
-    case 2: 
+    default: // _Any_ other kind of event - long, double, triple, ...
       if((iButton >= 2) && (iButton <= 4)) {
 	if(pUserLog)
 	  pUserLog->KeyDown(iButton, iType, 6);
diff --git a/Src/DasherCore/DynamicFilter.h b/Src/DasherCore/DynamicFilter.h
index e9a4646..db702b5 100644
--- a/Src/DasherCore/DynamicFilter.h
+++ b/Src/DasherCore/DynamicFilter.h
@@ -40,7 +40,7 @@ class CDynamicFilter : public CInputFilter {
 
  protected:
   virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog) = 0;
-  void Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
+  virtual void Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
 
   bool m_bKeyDown;
   bool m_bKeyHandled;
diff --git a/Src/DasherCore/OneButtonDynamicFilter.cpp b/Src/DasherCore/OneButtonDynamicFilter.cpp
index 7911747..b187c6a 100644
--- a/Src/DasherCore/OneButtonDynamicFilter.cpp
+++ b/Src/DasherCore/OneButtonDynamicFilter.cpp
@@ -27,10 +27,8 @@
 static SModuleSettings sSettings[] = {
   /* TRANSLATORS: The time for which a button must be held before it counts as a 'long' (rather than short) press. */
   {LP_HOLD_TIME, T_LONG, 100, 10000, 1000, 100, _("Long press time")},
-  /* TRANSLATORS: Multiple button presses are special (like a generalisation on double clicks) in some situations. This is the time in which the button must be pressed multiple times to count.*/
-  {LP_MULTIPRESS_TIME, T_LONG, 100, 10000, 1000, 100, _("Multiple press time")},
-  /* TRANSLATORS: Multiple button presses are special (like a generalisation on double clicks) in some situations. This is the number of times a button must be pressed to count as a multiple press.*/
-  {LP_MULTIPRESS_COUNT,T_LONG, 2, 10, 1, 1, _("Multiple press count")},
+  /* TRANSLATORS: Double-clicks are special in some situations (they cause us to start reversing). This is the time in which the button must be pressed twice to count.*/
+  {LP_MULTIPRESS_TIME, T_LONG, 100, 10000, 1000, 100, _("Double-press time")},
   /* TRANSLATORS: Backoff = reversing in Dasher to correct mistakes. This allows a single button to be dedicated to activating backoff, rather than using multiple presses of other buttons.*/
   {BP_BACKOFF_BUTTON,T_BOOL, -1, -1, -1, -1, _("Enable backoff button")},
   {BP_SLOW_START,T_BOOL, -1, -1, -1, -1, _("Slow startup")},
diff --git a/Src/DasherCore/OneButtonDynamicFilter.h b/Src/DasherCore/OneButtonDynamicFilter.h
index f79bcd9..4d43410 100644
--- a/Src/DasherCore/OneButtonDynamicFilter.h
+++ b/Src/DasherCore/OneButtonDynamicFilter.h
@@ -35,6 +35,7 @@ class COneButtonDynamicFilter : public CButtonMultiPress {
   virtual bool GetSettings(SModuleSettings **pSettings, int *iCount);
 
  private:
+  unsigned int maxClickCount() {return 2;} //double-click to reverse
   virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
   virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
   
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 68df370..ed0358d 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -44,7 +44,7 @@ enum {
   BP_COMPASSMODE, BP_SOCKET_INPUT_ENABLE, BP_SOCKET_DEBUG, 
   BP_OLD_STYLE_PUSH, BP_CIRCLE_START, BP_GLOBAL_KEYBOARD, 
   BP_SMOOTH_OFFSET, BP_CONVERSION_MODE, BP_PAUSE_OUTSIDE, BP_BACKOFF_BUTTON, 
-  BP_TWOBUTTON_REVERSE, BP_SLOW_START, BP_FIXED_MARKERS, END_OF_BPS
+  BP_TWOBUTTON_REVERSE, BP_2B_INVERT_DOUBLE, BP_SLOW_START, BP_FIXED_MARKERS, END_OF_BPS
 };
 
 enum { 
@@ -58,7 +58,7 @@ enum {
   LP_ZOOMSTEPS, LP_B, LP_S, LP_Z, LP_R, LP_RIGHTZOOM,
   LP_BOOSTFACTOR, LP_AUTOSPEED_SENSITIVITY, LP_SOCKET_PORT, LP_SOCKET_INPUT_X_MIN, LP_SOCKET_INPUT_X_MAX,
   LP_SOCKET_INPUT_Y_MIN, LP_SOCKET_INPUT_Y_MAX, LP_OX, LP_OY, LP_MAX_Y, LP_INPUT_FILTER, 
-  LP_CIRCLE_PERCENT, LP_TWO_BUTTON_OFFSET, LP_HOLD_TIME, LP_MULTIPRESS_TIME, LP_MULTIPRESS_COUNT, 
+  LP_CIRCLE_PERCENT, LP_TWO_BUTTON_OFFSET, LP_HOLD_TIME, LP_MULTIPRESS_TIME,
   LP_SLOW_START_TIME, LP_CONVERSION_ORDER, LP_CONVERSION_TYPE,
   LP_TWO_PUSH_OUTER, LP_TWO_PUSH_UP, LP_TWO_PUSH_DOWN, LP_TWO_PUSH_TOLERANCE,
   LP_DYNAMIC_BUTTON_LAG,
@@ -157,11 +157,12 @@ static bp_table boolparamtable[] = {
   {BP_OLD_STYLE_PUSH, "OldStylePush", PERS, false, "Old style node pushing algorithm"},
   {BP_CIRCLE_START, "CircleStart", PERS, false, "Start on circle mode"},
   {BP_GLOBAL_KEYBOARD, "GlobalKeyboard", PERS, false, "Whether to assume global control of the keyboard"},
-  {BP_SMOOTH_OFFSET, "DelayView", !PERS, false, "Smooth dynamic button mode jumps over several frames"},
+  {BP_SMOOTH_OFFSET, "DelayView", !PERS, false, "Smooth dynamic-button-mode jumps over several frames"},
   {BP_CONVERSION_MODE, "ConversionMode", !PERS, false, "Whether Dasher is operating in conversion (eg Japanese) mode"},
   {BP_PAUSE_OUTSIDE, "PauseOutside", PERS, false, "Whether to pause when pointer leaves canvas area"},
   {BP_BACKOFF_BUTTON, "BackoffButton", PERS, true, "Whether to enable the extra backoff button in dynamic mode"},
   {BP_TWOBUTTON_REVERSE, "TwoButtonReverse", PERS, false, "Reverse the up/down buttons in two button mode"},
+  {BP_2B_INVERT_DOUBLE, "TwoButtonInvertDouble", PERS, false, "Double-press acts as opposite button in two-button mode"},
   {BP_SLOW_START, "SlowStart", PERS, false, "Start at low speed and increase"},
   {BP_FIXED_MARKERS, "TwoPushMarkersFixed", PERS, false, "Two-push Dynamic Mode markers fixed to canvas"},
 };
@@ -212,7 +213,6 @@ static lp_table longparamtable[] = {
   {LP_TWO_BUTTON_OFFSET, "TwoButtonOffset", PERS, 1638, "Offset for two button dynamic mode"},
   {LP_HOLD_TIME, "HoldTime", PERS, 1000, "Time for which buttons must be held to count as long presses, in ms"},
   {LP_MULTIPRESS_TIME, "MultipressTime", PERS, 1000, "Time in which multiple presses must occur, in ms"},
-  {LP_MULTIPRESS_COUNT, "MultipressCount", PERS, 2, "Time in which multiple presses must occur to count"},
   {LP_SLOW_START_TIME, "SlowStartTime", PERS, 1000, "Time over which slow start occurs"},
   {LP_CONVERSION_ORDER, "ConversionOrder", PERS, 0, "Conversion ordering"},
   {LP_CONVERSION_TYPE, "ConversionType", PERS, 0, "Conversion type"},
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.cpp b/Src/DasherCore/TwoButtonDynamicFilter.cpp
index 8802f41..19beeb8 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.cpp
+++ b/Src/DasherCore/TwoButtonDynamicFilter.cpp
@@ -28,14 +28,16 @@ static SModuleSettings sSettings[] = {
   {LP_TWO_BUTTON_OFFSET, T_LONG, 1024, 2048, 2048, 100, _("Button offset")},
   /* TRANSLATORS: The time for which a button must be held before it counts as a 'long' (rather than short) press. */
   {LP_HOLD_TIME, T_LONG, 100, 10000, 1000, 100, _("Long press time")},
-  /* TRANSLATORS: Multiple button presses are special (like a generalisation on double clicks) in some situations. This is the time in which the button must be pressed multiple times to count.*/
-  {LP_MULTIPRESS_TIME, T_LONG, 100, 10000, 1000, 100, _("Multiple press time")},
-  /* TRANSLATORS: Multiple button presses are special (like a generalisation on double clicks) in some situations. This is the number of times a button must be pressed to count as a multiple press.*/
-  {LP_MULTIPRESS_COUNT,T_LONG, 2, 10, 1, 1, _("Multiple press count")}, 
+  /* TRANSLATORS: Multiple button presses are special (like a generalisation on double clicks) in some situations. This is the maximum time between two presses to count as _part_of_ a multi-press gesture
+  (potentially more than two presses). */
+  {LP_MULTIPRESS_TIME, T_LONG, 100, 10000, 1000, 100, _("Multiple press interval")},
   /* TRANSLATORS: Backoff = reversing in Dasher to correct mistakes. This allows a single button to be dedicated to activating backoff, rather than using multiple presses of other buttons, and another to be dedicated to starting and stopping. 'Button' in this context is a physical hardware device, not a UI element.*/
   {BP_BACKOFF_BUTTON,T_BOOL, -1, -1, -1, -1, _("Enable backoff and start/stop buttons")},
   /* TRANSLATORS: What is normally the up button becomes the down button etc. */
   {BP_TWOBUTTON_REVERSE,T_BOOL, -1, -1, -1, -1, _("Reverse up and down buttons")},
+  /* TRANSLATORS: Pushing the up/down button twice quickly has the same effect as pushing the other
+  button once; in this case, one must push three times (or push-and-hold) to reverse. */
+  {BP_2B_INVERT_DOUBLE, T_BOOL, -1, -1, -1, -1, _("Double-click is opposite up/down - triple to reverse")},
   {BP_SLOW_START,T_BOOL, -1, -1, -1, -1, _("Slow startup")},
   {LP_SLOW_START_TIME, T_LONG, 0, 10000, 1000, 100, _("Startup time")},
   {LP_DYNAMIC_BUTTON_LAG, T_LONG, 0, 1000, 1, 25, _("Lag before user actually pushes button (ms)")}, 
@@ -45,7 +47,7 @@ static SModuleSettings sSettings[] = {
 };
 
 CTwoButtonDynamicFilter::CTwoButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
-  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode"))
+  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode")), m_dMulSinceFirstPush(1.0)
 {
   //ensure that m_dLagMul is properly initialised
   Dasher::CParameterNotificationEvent oEvent(LP_DYNAMIC_BUTTON_LAG);
@@ -103,19 +105,50 @@ void CTwoButtonDynamicFilter::Deactivate() {
 
 void CTwoButtonDynamicFilter::run(int iState) {
   SetBoolParameter(BP_SMOOTH_OFFSET, true);
-  CDynamicFilter::run(iState);
+  CButtonMultiPress::run(iState);
 }
 
 void CTwoButtonDynamicFilter::pause() {
   SetBoolParameter(BP_SMOOTH_OFFSET, false);
-  CDynamicFilter::pause();
+  CButtonMultiPress::pause();
 }
 
 void CTwoButtonDynamicFilter::reverse() {
   //hmmmm. If we ever actually did Offset() while reversing,
   // we might want BP_SMOOTH_OFFSET on....
   SetBoolParameter(BP_SMOOTH_OFFSET, false);
-  CDynamicFilter::reverse();
+  CButtonMultiPress::reverse();
+}
+
+void CTwoButtonDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog)
+{
+  if (GetBoolParameter(BP_2B_INVERT_DOUBLE) && iType == 2 && iButton>=2 && iButton<=4)
+  { //double-press - treat as single-press of the other button....
+    iType = 0; //0=normal, 1=long press
+    iButton = (iButton == 2) ? 3 : 2;
+    m_dMulSinceFirstPush = exp(pModel->GetNats());
+  }
+  CDynamicFilter::Event(iTime, iButton, iType, pModel, pUserLog);
+}
+    
+void CTwoButtonDynamicFilter::ApplyOffset(CDasherModel *pModel, long lOffset)
+{
+  lOffset *= m_dMulSinceFirstPush; m_dMulSinceFirstPush = 1.0;
+  pModel->Offset(lOffset);
+  m_pModel = pModel;
+  m_lOffsetApplied = lOffset;
+  pModel->ResetNats();
+}
+
+void CTwoButtonDynamicFilter::RevertPresses(int iCount)
+{
+  //invert the *last* invocation of ApplyOffset.
+  //this'll handle reverting single clicks and (if BP_2B_INVERT_DOUBLE is on) double-clicks,
+  //but we'll get into trouble if the user e.g. double-presses the reverse button!
+
+  //correct for expansion since the first click, if any (if we've rendered any frames!)
+  m_pModel->Offset(-m_lOffsetApplied * exp(m_pModel->GetNats()));
+  m_lOffsetApplied = 0;
 }
 
 void CTwoButtonDynamicFilter::ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog) {
@@ -125,12 +158,12 @@ void CTwoButtonDynamicFilter::ActionButton(int iTime, int iButton, int iType, CD
     iFactor = -1;
 
   if(iButton == 2) {
-    pModel->Offset(iFactor * GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
+    ApplyOffset(pModel, iFactor * GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
     if(pUserLog)
       pUserLog->KeyDown(iButton, iType, 3);
   }
   else if((iButton == 3) || (iButton == 4)) {
-    pModel->Offset(iFactor * -GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
+    ApplyOffset(pModel, iFactor * -GetLongParameter(LP_TWO_BUTTON_OFFSET) * m_dLagMul);
     if(pUserLog)
       pUserLog->KeyDown(iButton, iType, 4);
   }
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.h b/Src/DasherCore/TwoButtonDynamicFilter.h
index 1362926..a1b818b 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.h
+++ b/Src/DasherCore/TwoButtonDynamicFilter.h
@@ -44,14 +44,22 @@ class CTwoButtonDynamicFilter : public CButtonMultiPress {
   virtual void HandleEvent(Dasher::CEvent *pEvent);
   
  protected:
+  void Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
   virtual void run(int iState);
   virtual void pause();
   virtual void reverse();
 
  private:
+  unsigned int maxClickCount() {return GetBoolParameter(BP_2B_INVERT_DOUBLE) ? 3 : 2;}
   virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
   virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
   double m_dLagMul;
+
+  void RevertPresses(int iCount);
+  void ApplyOffset(CDasherModel *pModel, long lOffset);
+  CDasherModel *m_pModel;
+  long m_lOffsetApplied;
+  double m_dMulSinceFirstPush;
 };
 /// @}
 



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