[dasher: 22/28] TwoPushDynamicFilter: change params; release time opt; guides w/ FrameSpeedMul



commit 5ca645851aecd001c818bacbd0976367f2f817e7
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Sun Oct 2 11:12:04 2011 +0100

    TwoPushDynamicFilter: change params; release time opt; guides w/ FrameSpeedMul
    
    LP_TWO_PUSH_UP and LP_TWO_PUSH_DOWN (inner posns) -> LP_TWO_PUSH_LONG (gap)
     and LP_TWO_PUSH_SHORT (%age), as removes interdependencies between params
     and matches Android.
    
    Add BP_TWO_PUSH_RELEASE_TIME.
    
    Recompute guide areas according to FrameSpeedMul, caching by effective bitrate.
    
    Move Long-press detection into ButtonMultiPress so TwoPushDynamicFilter doesn't

 Src/DasherCore/ButtonMultiPress.cpp     |   23 +++-
 Src/DasherCore/ButtonMultiPress.h       |   16 ++-
 Src/DasherCore/DynamicButtons.cpp       |   24 +---
 Src/DasherCore/DynamicButtons.h         |   26 +++--
 Src/DasherCore/Parameters.cpp           |    5 +-
 Src/DasherCore/Parameters.h             |    4 +-
 Src/DasherCore/TwoPushDynamicFilter.cpp |  206 ++++++++++++++-----------------
 Src/DasherCore/TwoPushDynamicFilter.h   |   18 +++-
 8 files changed, 166 insertions(+), 156 deletions(-)
---
diff --git a/Src/DasherCore/ButtonMultiPress.cpp b/Src/DasherCore/ButtonMultiPress.cpp
index ec68f79..7df57fb 100644
--- a/Src/DasherCore/ButtonMultiPress.cpp
+++ b/Src/DasherCore/ButtonMultiPress.cpp
@@ -27,6 +27,14 @@ CButtonMultiPress::CButtonMultiPress(CSettingsUser *pCreator, CDasherInterfaceBa
   : CDynamicButtons(pCreator, pInterface, pFramerate, iID, szName) {
 }
 
+void CButtonMultiPress::Timer(unsigned long iTime, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CExpansionPolicy **pol) {
+  if(m_bKeyDown && !m_bKeyHandled && ((iTime - m_iKeyDownTime) > GetLongParameter(LP_HOLD_TIME))) {
+    ButtonEvent(iTime, m_iHeldId, 1, pModel);
+    m_bKeyHandled = true;
+  }
+  CDynamicButtons::Timer(iTime,pView,pInput,pModel,pol);
+}
+
 void CButtonMultiPress::KeyDown(unsigned long iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
 
   if (m_bKeyDown) return;
@@ -47,9 +55,7 @@ void CButtonMultiPress::KeyDown(unsigned long iTime, int iId, CDasherView *pView
 	m_deQueueTimes.push_back(iTime);
       return; //we've called Event ourselves, so finished.
     }
-  }
-  else
-  {
+  } else {
     m_deQueueTimes.clear(); //clear record of previous, different, button
     m_iQueueId = iId;
   }
@@ -58,10 +64,17 @@ void CButtonMultiPress::KeyDown(unsigned long iTime, int iId, CDasherView *pView
   m_deQueueTimes.push_back(iTime);
   // ... and process normally; if it changes the state, pause()/reverse()'ll clear the queue
   CDynamicButtons::KeyDown(iTime, iId, pView, pInput, pModel);
+  
+  // Store the key down time so that long presses can be determined
+  // TODO: This is going to cause problems if multiple buttons are
+  // held down at once
+  m_iKeyDownTime = iTime;
+  
+  m_bKeyHandled = false;
 }
 
-void CButtonMultiPress::pause()
-{
+
+void CButtonMultiPress::pause() {
   CDynamicButtons::pause();
   m_deQueueTimes.clear();
 }
diff --git a/Src/DasherCore/ButtonMultiPress.h b/Src/DasherCore/ButtonMultiPress.h
index f1fa348..2f46a3e 100644
--- a/Src/DasherCore/ButtonMultiPress.h
+++ b/Src/DasherCore/ButtonMultiPress.h
@@ -26,16 +26,17 @@
 namespace Dasher {
 /// \ingroup InputFilter
 /// @{
-/// DynamicButtons filter which detects multiple presses of the same button
-/// occurring in a short space of time - up to maxClickCount() consecutive presses,
+/// DynamicButtons filter which detects long and multiple presses - the latter of the
+/// same button, up to maxClickCount() consecutive presses,
 /// with a gap of up to LP_MULTIPRESS_TIME ms between the start of _each_pair_ of 
-/// presses. Such multi-presses are passed onto the standard ButtonEvent method,
-/// with iType equal to the number of presses, for subclasses to decide how to respond.
+/// presses. Long- and multi-presses are passed onto the standard ButtonEvent method,
+/// with iType 1 or to the number of presses, respectively, for subclasses to decide how to respond.
 class CButtonMultiPress : public CDynamicButtons {
  public:
   CButtonMultiPress(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, CFrameRate *pFramerate, ModuleID_t iID, const char *szName);
 
-  virtual void KeyDown(unsigned long iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel);
+  void Timer(unsigned long iTime, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CExpansionPolicy **pol);
+  void KeyDown(unsigned long iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel);
 
   void pause();
  protected:
@@ -48,6 +49,11 @@ class CButtonMultiPress : public CDynamicButtons {
 
   int m_iQueueId;
   std::deque<unsigned long> m_deQueueTimes;
+
+  ///Whether a long-press has been handled (in Timer) - as the key
+  /// may still be down (and the press becoming ever-longer)!
+  bool m_bKeyHandled;
+  unsigned long m_iKeyDownTime;
  };
 }
 
diff --git a/Src/DasherCore/DynamicButtons.cpp b/Src/DasherCore/DynamicButtons.cpp
index 241e834..83c75fd 100644
--- a/Src/DasherCore/DynamicButtons.cpp
+++ b/Src/DasherCore/DynamicButtons.cpp
@@ -30,16 +30,10 @@ CDynamicButtons::CDynamicButtons(CSettingsUser *pCreator, CDasherInterfaceBase *
   pause();
 }
 
-void CDynamicButtons::Timer(unsigned long iTime, CDasherView *pDasherView, CDasherInput *pInput, CDasherModel *m_pDasherModel, CExpansionPolicy **pol)
-{
-  if(m_bKeyDown && !m_bKeyHandled && ((iTime - m_iKeyDownTime) > GetLongParameter(LP_HOLD_TIME))) {
-    ButtonEvent(iTime, m_iHeldId, 1, m_pDasherModel);
-    m_bKeyHandled = true;
-    //return true; //ACL although that's what old DynamicButtons did, surely we should progress normally?
-  }
+void CDynamicButtons::Timer(unsigned long iTime, CDasherView *pDasherView, CDasherInput *pInput, CDasherModel *pModel, CExpansionPolicy **pol) {
   if (isPaused()) return;
   if (isReversing()) {
-    OneStepTowards(m_pDasherModel, 41943,2048, iTime, FrameSpeedMul(m_pDasherModel, iTime));
+    OneStepTowards(pModel, 41943,2048, iTime, FrameSpeedMul(pModel, iTime));
   } else {
     //moving forwards. Check auto speed control...
     if (GetBoolParameter(BP_AUTO_SPEEDCONTROL) && m_uSpeedControlTime < iTime) {
@@ -47,7 +41,7 @@ void CDynamicButtons::Timer(unsigned long iTime, CDasherView *pDasherView, CDash
           SetLongParameter(LP_MAX_BITRATE, GetLongParameter(LP_MAX_BITRATE) * (1.0 + GetLongParameter(LP_DYNAMIC_SPEED_INC)/100.0));
         m_uSpeedControlTime = iTime + 1000*GetLongParameter(LP_DYNAMIC_SPEED_FREQ);
     }
-    TimerImpl(iTime, pDasherView, m_pDasherModel, pol);
+    TimerImpl(iTime, pDasherView, pModel, pol);
   }
 }
 
@@ -61,15 +55,9 @@ void CDynamicButtons::KeyDown(unsigned long iTime, int iId, CDasherView *pView,
 
   // Pass the basic key down event to the handler
   ButtonEvent(iTime, iId, 0, pModel);
-    
-  // Store the key down time so that long presses can be determined
-  // TODO: This is going to cause problems if multiple buttons are
-  // held down at once
-  m_iKeyDownTime = iTime;
 
   m_iHeldId = iId;
   m_bKeyDown = true;
-  m_bKeyHandled = false;
 }
 
 void CDynamicButtons::KeyUp(unsigned long iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
@@ -126,7 +114,7 @@ void CDynamicButtons::pause() {
 
 void CDynamicButtons::reverse(unsigned long iTime) {
   m_bForwards=false;
-  CDynamicFilter::run(iTime);
+  if (isPaused()) CDynamicFilter::run(iTime);
   if (GetBoolParameter(BP_AUTO_SPEEDCONTROL)) {
     //treat reversing as a sign of distress --> slow down!
     SetLongParameter(LP_MAX_BITRATE, GetLongParameter(LP_MAX_BITRATE) *
@@ -136,9 +124,7 @@ void CDynamicButtons::reverse(unsigned long iTime) {
 
 void CDynamicButtons::run(unsigned long iTime) {
   m_bForwards=true;
-  if (!isPaused()) return;
-  //wasn't running previously
-  CDynamicFilter::run(iTime);
+  if (isPaused()) CDynamicFilter::run(iTime); //wasn't running previously
   m_uSpeedControlTime = 0; //will be set in Timer()
 }
 
diff --git a/Src/DasherCore/DynamicButtons.h b/Src/DasherCore/DynamicButtons.h
index f308324..f6d0386 100644
--- a/Src/DasherCore/DynamicButtons.h
+++ b/Src/DasherCore/DynamicButtons.h
@@ -26,7 +26,9 @@
 /// \ingroup InputFilter
 /// @{
 namespace Dasher {
-///filter with three states: paused, reversing, running. Hold any button down to reverse.
+///filter with three states: paused, reversing, running. Button 1 is dedicated reverse
+/// button (subclasses may also call reverse());  when reversing, any key pauses,
+/// then any key restarts.
 class CDynamicButtons : public CDynamicFilter {
  public:
   CDynamicButtons(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, CFrameRate *pFramerate, ModuleID_t iID, const char *szName);
@@ -53,8 +55,17 @@ class CDynamicButtons : public CDynamicFilter {
   /// \param iType 0=normal press, 1=long press; see also CButtonMultiPress.
   virtual void ActionButton(unsigned long iTime, int iButton, int iType, CDasherModel *pModel) = 0;
 
+  ///Whether a key (any that we might respond to) is held down.
+  /// If so, m_iHeldId identifies the key in question. We need this
+  /// not just for detecting long-presses etc. (in subclasses), and
+  /// ignoring presses of other keys while the first is down, but also
+  /// simply to filter out key-repeat events (=multiple keydown without a keyup)
   bool m_bKeyDown;
-  bool m_bKeyHandled;
+
+  ///if m_bKeyDown is true, identifies the key that was first pressed
+  /// that is currently still held down.
+  int m_iHeldId;
+
   bool m_bDecorationChanged;
   bool isReversing() {return !isPaused() && !m_bForwards;}
   bool isRunning() {return !isPaused() && m_bForwards;}
@@ -67,12 +78,11 @@ class CDynamicButtons : public CDynamicFilter {
   ///Subclasses should all this (rather than pModel->Offset()) to offset the model
   /// (it also stores the model, to abort the offset upon pause if necessary)
   void ApplyOffset(CDasherModel *pModel, int iOffset);
-  private:
-    bool m_bForwards;
-    int m_iHeldId;
-    unsigned long m_iKeyDownTime;
-    unsigned long m_uSpeedControlTime;
-    CDasherModel *m_pModel;
+
+private:
+  bool m_bForwards;
+  unsigned long m_uSpeedControlTime;
+  CDasherModel *m_pModel;
 };
 }
 #endif
diff --git a/Src/DasherCore/Parameters.cpp b/Src/DasherCore/Parameters.cpp
index 1175dec..e882306 100644
--- a/Src/DasherCore/Parameters.cpp
+++ b/Src/DasherCore/Parameters.cpp
@@ -58,6 +58,7 @@ const bp_table boolparamtable[] = {
   {BP_CONTROL_MODE_HAS_COPY, "ControlHasCopy", true, "Provide copy-to-clipboard actions in Control Mode (if platforms supports)"},
   {BP_CONTROL_MODE_HAS_SPEECH, "ControlHasSpeech", true, "Provide speech actions in Control Mode (if platform supports)"},
   {BP_GAME_HELP_DRAW_PATH, "GameDrawPath", true, "When we give help, show the shortest path to the target sentence"},
+  {BP_TWO_PUSH_RELEASE_TIME, "TwoPushReleaseTime", false, _("Use push and release times of single press rather than push times of two presses")},
 };
 
 const lp_table longparamtable[] = {
@@ -122,8 +123,8 @@ const lp_table longparamtable[] = {
   {LP_CONVERSION_ORDER, "ConversionOrder", 0, "Conversion ordering"},
   {LP_CONVERSION_TYPE, "ConversionType", 0, "Conversion type"},
   {LP_TWO_PUSH_OUTER, "TwoPushOuter", 1792, "Offset for one button dynamic mode outer marker"},
-  {LP_TWO_PUSH_UP, "TwoPushUp", 1536, "Offset to up marker in one button dynamic"},
-  {LP_TWO_PUSH_DOWN, "TwoPushDown", 1280, "Offset to down marker in one button dynamic"},
+  {LP_TWO_PUSH_LONG, "TwoPushLong", 512, _("Distance between down markers (long gap)")},
+  {LP_TWO_PUSH_SHORT, "TwoPushShort", 80, _("Distance between up markers, as %age of long gap")},
   {LP_TWO_PUSH_TOLERANCE, "TwoPushTolerance", 100, "Tolerance of two-push-mode pushes, in ms"},
   {LP_DYNAMIC_BUTTON_LAG, "DynamicButtonLag", 50, "Lag of pushes in dynamic button mode (ms)"},
   {LP_STATIC1B_TIME, "Static1BTime", 2000, "Time for static-1B mode to scan from top to bottom (ms)"},
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 506f3c0..fd4be5b 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -42,7 +42,7 @@ enum {
   BP_TWOBUTTON_REVERSE, BP_2B_INVERT_DOUBLE, BP_SLOW_START,
   BP_COPY_ALL_ON_STOP, BP_SPEAK_ALL_ON_STOP, BP_SPEAK_WORDS,
   BP_CONTROL_MODE_HAS_HALT, BP_CONTROL_MODE_HAS_EDIT, BP_CONTROL_MODE_HAS_COPY, BP_CONTROL_MODE_HAS_SPEECH,
-  BP_GAME_HELP_DRAW_PATH,
+  BP_GAME_HELP_DRAW_PATH, BP_TWO_PUSH_RELEASE_TIME,
   END_OF_BPS
 };
 
@@ -60,7 +60,7 @@ enum {
   LP_SOCKET_INPUT_Y_MIN, LP_SOCKET_INPUT_Y_MAX, LP_INPUT_FILTER, 
   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_TWO_PUSH_OUTER, LP_TWO_PUSH_LONG, LP_TWO_PUSH_SHORT, LP_TWO_PUSH_TOLERANCE,
   LP_DYNAMIC_BUTTON_LAG, LP_STATIC1B_TIME, LP_STATIC1B_ZOOM,
   LP_DEMO_SPRING, LP_DEMO_NOISE_MEM, LP_DEMO_NOISE_MAG, LP_MAXZOOM, 
   LP_DYNAMIC_SPEED_INC, LP_DYNAMIC_SPEED_FREQ, LP_DYNAMIC_SPEED_DEC,
diff --git a/Src/DasherCore/TwoPushDynamicFilter.cpp b/Src/DasherCore/TwoPushDynamicFilter.cpp
index 413e267..a84dba9 100644
--- a/Src/DasherCore/TwoPushDynamicFilter.cpp
+++ b/Src/DasherCore/TwoPushDynamicFilter.cpp
@@ -28,11 +28,10 @@ using namespace Dasher;
 
 static SModuleSettings sSettings[] = {
   {LP_TWO_PUSH_OUTER, T_LONG, 1024, 2048, 2048, 128, _("Offset for outer (second) button")},
-  {LP_TWO_PUSH_UP, T_LONG, 257, 2047, 2048/*divisor*/, 128/*step*/, _("Distance for 1st button UP")},
-  {LP_TWO_PUSH_DOWN, T_LONG, 256, 2046, 2048, 128, _("Distance for 1st button DOWN")},
+  {LP_TWO_PUSH_LONG, T_LONG, 128, 1024, 2048/*divisor*/, 128/*step*/, _("Distance between down markers (long gap)")},
+  {LP_TWO_PUSH_SHORT, T_LONG, 10, 90, 100, 1, _("Distance between up markers, as %age of long gap")},
   {LP_TWO_PUSH_TOLERANCE, T_LONG, 50, 1000, 1, 10, _("Tolerance for inaccurate timing of button pushes (in ms)")},
-  /* 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")},
+  {BP_TWO_PUSH_RELEASE_TIME, T_BOOL, -1, -1, -1, -1, _("Use push and release times of single press rather than push times of two presses")},
   /* 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")},
   {BP_SLOW_START,T_BOOL, -1, -1, -1, -1, _("Slow startup")},
@@ -62,35 +61,41 @@ void GuideLine(CDasherView *pView, const myint iDasherY, const int iColour)
   pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
 
   pScreen->Polyline(p, 2, 3, iColour);
-}  
+}
 
-bool CTwoPushDynamicFilter::DecorateView(CDasherView *pView, CDasherInput *pInput)
-{  
-  //outer guides (yellow rects)
-  for (int i=0; i<2; i++)
-  {
-    screenint x1, y1, x2, y2;
-    CDasherScreen *pScreen(pView->Screen());
-  
-    pView->Dasher2Screen(-100, m_aaiGuideAreas[i][0], x1, y1);
-    pView->Dasher2Screen(-1000, m_aaiGuideAreas[i][1], x2, y2);
-  
-    pScreen->DrawRectangle(x1, y1, x2, y2, 62/*pale yellow*/, -1, 0);
+long CTwoPushDynamicFilter::downDist() {
+  return GetLongParameter(LP_TWO_PUSH_OUTER) - GetLongParameter(LP_TWO_PUSH_LONG);
+}
+
+long CTwoPushDynamicFilter::upDist() {
+  return GetLongParameter(LP_TWO_PUSH_OUTER) - (GetLongParameter(LP_TWO_PUSH_LONG) * GetLongParameter(LP_TWO_PUSH_SHORT))/100;
+}
+
+bool CTwoPushDynamicFilter::DecorateView(CDasherView *pView, CDasherInput *pInput) {
+  if (isRunning()) {
+    //outer guides (yellow rects)
+    for (int i=0; i<2; i++) {
+      screenint x1, y1, x2, y2;
+      CDasherScreen *pScreen(pView->Screen());
+    
+      pView->Dasher2Screen(-100, m_aaiGuideAreas[i][0], x1, y1);
+      pView->Dasher2Screen(-1000, m_aaiGuideAreas[i][1], x2, y2);
+    
+      pScreen->DrawRectangle(x1, y1, x2, y2, 62/*pale yellow*/, -1, 0);
+    }
   }
 
-  //inner guides (red lines)
-  GuideLine(pView, 2048 - GetLongParameter(LP_TWO_PUSH_UP), 1);
-  GuideLine(pView, 2048 + GetLongParameter(LP_TWO_PUSH_DOWN), 1);
+  //inner guides (red lines).
+  GuideLine(pView, 2048 - upDist(), 1);
+  GuideLine(pView, 2048 + downDist(), 1);
 
   //outer guides (at center of rects) - red lines
   GuideLine(pView, 2048 - GetLongParameter(LP_TWO_PUSH_OUTER), 1);
   GuideLine(pView, 2048 + GetLongParameter(LP_TWO_PUSH_OUTER), 1);
 
   //moving markers - green if active, else yellow
-  if (m_bDecorationChanged && isRunning() && m_dNatsSinceFirstPush > -std::numeric_limits<double>::infinity())
-  {
-    for (int i = 0; i < 2; i++)
-    {
+  if (m_bDecorationChanged && isRunning() && m_dNatsSinceFirstPush > -std::numeric_limits<double>::infinity()) {
+    for (int i = 0; i < 2; i++) {
       GuideLine(pView, m_aiMarker[i], (i == m_iActiveMarker) ? 240 : 61/*orange*/);
     }
   }
@@ -100,75 +105,52 @@ bool CTwoPushDynamicFilter::DecorateView(CDasherView *pView, CDasherInput *pInpu
 }
 
 void CTwoPushDynamicFilter::HandleEvent(int iParameter) {
-  switch (iParameter)
-  {
-    case LP_TWO_PUSH_OUTER:
-      if (GetLongParameter(LP_TWO_PUSH_OUTER)<=GetLongParameter(LP_TWO_PUSH_UP)) {
-        //two_push_outer being moved down; force two_push_up down too
-        SetLongParameter(LP_TWO_PUSH_UP, GetLongParameter(LP_TWO_PUSH_OUTER)-1);
-        return; // as HandleEvent on latter will execute same code (below)
-      }
-      //deliberate fallthrough
-    case LP_TWO_PUSH_UP:
-      if (GetLongParameter(LP_TWO_PUSH_UP)>=GetLongParameter(LP_TWO_PUSH_OUTER)) {
-        //two_push_up must have changed, or we'd have caught this in previous check for two_push_outer
-        SetLongParameter(LP_TWO_PUSH_OUTER, GetLongParameter(LP_TWO_PUSH_UP)+1);
-        return;
-      }
-      if (GetLongParameter(LP_TWO_PUSH_UP)<=GetLongParameter(LP_TWO_PUSH_DOWN)) {
-        SetLongParameter(LP_TWO_PUSH_DOWN, GetLongParameter(LP_TWO_PUSH_UP)-1);
-        return;
-      }
-      //deliberate fallthrough
-    case LP_TWO_PUSH_DOWN:
-      if (GetLongParameter(LP_TWO_PUSH_DOWN)>=GetLongParameter(LP_TWO_PUSH_UP)) {
-        SetLongParameter(LP_TWO_PUSH_UP, GetLongParameter(LP_TWO_PUSH_DOWN)+1);
-        return;
-      }
-    {
-  //TODO, that means short gap at the top - allow other way around also?
-
-double dOuter = GetLongParameter(LP_TWO_PUSH_OUTER);
-m_dLogUpMul = log(dOuter / (double)GetLongParameter(LP_TWO_PUSH_UP));
-m_dLogDownMul = log(dOuter / (double)GetLongParameter(LP_TWO_PUSH_DOWN));
-//cout << "bitsUp " << m_dLogUpMul << " bitsDown " << m_dLogDownMul << "\n";
-    } //and fallthrough
-    case LP_TWO_PUSH_TOLERANCE: //deliberate fallthrough
-    case LP_MAX_BITRATE:
-    {
-      //dPressBits just measures the number of bits which would be output in the
-      // tolerance time, at full (100%) speed; note it does not take account of
-      // the SpeedMul (viscosity) of the node under the cursor (or Slow Start, etc.)
-      // - iow, when we are moving slowly for such a reason, we'll be proportionately
-      // _more_ tolerant of user inaccuracy in button pushing...
-double dPressBits = GetLongParameter(LP_MAX_BITRATE)/100.0 * (double) GetLongParameter(LP_TWO_PUSH_TOLERANCE) / 1000.0;
-//cout << "Max Bitrate changed - now " << dMaxRate << " user accuracy " << dPressBits;
-m_dMinShortTwoPushTime = m_dLogUpMul - dPressBits;
-//cout << "bits; minShort " << m_dMinShortTwoPushTime;
-m_dMaxShortTwoPushTime = m_dLogUpMul + dPressBits;
-m_dMinLongTwoPushTime = m_dLogDownMul - dPressBits;
-if (m_dMaxShortTwoPushTime > m_dMinLongTwoPushTime)
-        m_dMaxShortTwoPushTime = m_dMinLongTwoPushTime = (m_dLogUpMul + m_dLogDownMul)/2.0;
-m_dMaxLongTwoPushTime = m_dLogDownMul + dPressBits;
-//TODO, what requirements do we actually need to make to ensure sanity (specifically, that computed m_aiTarget's are in range)?
-//cout << " maxShort " << m_dMaxShortTwoPushTime << " minLong " << m_dMinLongTwoPushTime << " maxLong " << m_dMaxLongTwoPushTime << "\n";
-m_bDecorationChanged = true;
-   }  //and fallthrough again
-   case LP_DYNAMIC_BUTTON_LAG:
-     m_dLagBits = GetLongParameter(LP_MAX_BITRATE)/100.0 * GetLongParameter(LP_DYNAMIC_BUTTON_LAG)/1000.0;
-//cout << " lag (" << m_dLagBits[0] << ", " << m_dLagBits[1] << ", " << m_dLagBits[2] << ", " << m_dLagBits[3] << ")";
-      //these areas should really be calculated using short/long push-times modified by the
-      // current FrameSpeedMul, which we'd have to do every frame. For now I'm not, so the guide
-      // areas will be wrong when the speed multiplier is other than 1.0. TODO reconsider, esp.
-      // wrt. possibly memoizing exp() in CDynamicFilter?
-     m_aaiGuideAreas[0][0] = 2048 - GetLongParameter(LP_TWO_PUSH_UP)*exp(m_dMaxShortTwoPushTime);
-     m_aaiGuideAreas[0][1] = 2048 - GetLongParameter(LP_TWO_PUSH_UP)*exp(m_dMinShortTwoPushTime);
-     m_aaiGuideAreas[1][0] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN)*exp(m_dMinLongTwoPushTime);
-     m_aaiGuideAreas[1][1] = 2048 + GetLongParameter(LP_TWO_PUSH_DOWN)*exp(m_dMaxLongTwoPushTime);
-     break;
+  switch (iParameter) {
+  case LP_TWO_PUSH_OUTER: //fallthrough
+  case LP_TWO_PUSH_LONG: //fallthrough
+  case LP_TWO_PUSH_SHORT: {
+    //TODO, short gap always at the top - allow other way around also?
+    double dOuter = GetLongParameter(LP_TWO_PUSH_OUTER);
+    m_dLogUpMul = log(dOuter / upDist());
+    m_dLogDownMul = log(dOuter / downDist());
+//cout << "bitsUp " << m_dLogUpMul << " bitsDown " << m_dLogDownMul << std::endl;
+  } //and fallthrough
+  case LP_TWO_PUSH_TOLERANCE: //fallthrough
+  case LP_DYNAMIC_BUTTON_LAG:
+    //recompute rest in Timer
+    m_dLastBitRate=-numeric_limits<double>::infinity();
   }
 }
 
+void CTwoPushDynamicFilter::updateBitrate(double dBitrate) {
+  if (dBitrate==m_dLastBitRate) return;
+  m_dLastBitRate = dBitrate;
+
+  double dPressBits = dBitrate * (double) GetLongParameter(LP_TWO_PUSH_TOLERANCE) / 1000.0;
+//cout << "Max Bitrate changed - now " << dBitrate << " user accuracy " << dPressBits;
+  m_dMinShortTwoPushTime = m_dLogUpMul - dPressBits;
+  m_dMaxShortTwoPushTime = m_dLogUpMul + dPressBits;
+  m_dMinLongTwoPushTime = m_dLogDownMul - dPressBits;
+  if (m_dMaxShortTwoPushTime > m_dMinLongTwoPushTime)
+    m_dMaxShortTwoPushTime = m_dMinLongTwoPushTime = (m_dLogUpMul + m_dLogDownMul)/2.0;
+  m_dMaxLongTwoPushTime = m_dLogDownMul + dPressBits;
+  //TODO, what requirements do we actually need to make to ensure sanity (specifically, that computed m_aiTarget's are in range)?
+//cout << "bits; minShort " << m_dMinShortTwoPushTime << " maxShort " << m_dMaxShortTwoPushTime << " minLong " << m_dMinLongTwoPushTime << " maxLong " << m_dMaxLongTwoPushTime << std::endl;
+  m_bDecorationChanged = true;
+
+  m_dLagBits = dBitrate * GetLongParameter(LP_DYNAMIC_BUTTON_LAG)/1000.0;
+
+  const long down(downDist()), up(upDist());
+  
+  //the boundaries of the guide areas (around the outer markers) are
+  // then computed from the number of bits _since_ the inner marker:
+  m_aaiGuideAreas[0][0] = 2048 - up*exp(m_dMaxShortTwoPushTime);
+  m_aaiGuideAreas[0][1] = 2048 - up*exp(m_dMinShortTwoPushTime);
+  m_aaiGuideAreas[1][0] = 2048 + down*exp(m_dMinLongTwoPushTime);
+  m_aaiGuideAreas[1][1] = 2048 + down*exp(m_dMaxLongTwoPushTime);
+//cout << "Short " << m_aaiGuideAreas[0][0] << " to " << m_aaiGuideAreas[0][1] << ", Long " << m_aaiGuideAreas[1][0] << " to " << m_aaiGuideAreas[1][1];
+}
+
 void CTwoPushDynamicFilter::KeyDown(unsigned long Time, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
   if (iId == 100 && !GetBoolParameter(BP_BACKOFF_BUTTON))
     //mouse click - will be ignored by superclass method.
@@ -182,6 +164,11 @@ void CTwoPushDynamicFilter::KeyUp(unsigned long Time, int iId, CDasherView *pVie
     //mouse click - will be ignored by superclass method.
     //simulate press of button 2...
     iId=2;
+  if (GetBoolParameter(BP_TWO_PUSH_RELEASE_TIME)
+      && isRunning() && iId==m_iHeldId
+      && m_dNatsSinceFirstPush!=-numeric_limits<double>::infinity())
+    ActionButton(Time, iId, 0, pModel);
+  //just records that the key has been released
   CDynamicButtons::KeyUp(Time, iId, pView, pInput, pModel);
 }
 
@@ -194,19 +181,16 @@ void CTwoPushDynamicFilter::ActionButton(unsigned long iTime, int iButton, int i
     reverse(iTime);
     return;
   }
-  if (m_dNatsSinceFirstPush == -std::numeric_limits<double>::infinity()) //no button pushed (recently)
-  {
+  if (m_dNatsSinceFirstPush == -std::numeric_limits<double>::infinity()) {
+    //no button pushed (recently)
     m_dNatsSinceFirstPush = pModel->GetNats();
     //note, could be negative if overall reversed since last ResetNats (Offset)
 //cout << "First push - got " << m_dNatsSinceFirstPush << std::endl;
-  }
-  else
-  {
-//cout << "Second push - event type " << iType << " logGrowth " << pModel->GetNats() << "\n";
+  } else {
+//cout << "Second push - event type " << iType << " logGrowth " << pModel->GetNats() << std::endl;
     if (m_iActiveMarker == -1)
       reverse(iTime);
-    else
-    {
+    else {
       ApplyOffset(pModel,m_aiTarget[m_iActiveMarker]);
       pModel->ResetNats();
       //don't really have to reset there, but seems as good a place as any
@@ -215,8 +199,7 @@ void CTwoPushDynamicFilter::ActionButton(unsigned long iTime, int iButton, int i
   }
 }
 
-bool doSet(int &var, const int val)
-{
+bool doSet(int &var, const int val) {
   if (var == val) return false;
   var = val;
   return true;
@@ -225,10 +208,11 @@ bool doSet(int &var, const int val)
 void CTwoPushDynamicFilter::TimerImpl(unsigned long iTime, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, CExpansionPolicy **pol) {
   DASHER_ASSERT(isRunning());
   const double dSpeedMul(FrameSpeedMul(m_pDasherModel, iTime));
-  if (m_dNatsSinceFirstPush > -std::numeric_limits<double>::infinity()) // first button has been pushed
-  {
+  updateBitrate(GetLongParameter(LP_MAX_BITRATE)*dSpeedMul/100.0);
+  if (m_dNatsSinceFirstPush > -std::numeric_limits<double>::infinity()) {
+    // first button has been pushed
     double dLogGrowth(m_pDasherModel->GetNats() - m_dNatsSinceFirstPush), dOuter(GetLongParameter(LP_TWO_PUSH_OUTER)),
-           dUp(GetLongParameter(LP_TWO_PUSH_UP)), dDown(GetLongParameter(LP_TWO_PUSH_DOWN));
+           dUp(upDist()), dDown(downDist());
     
     //to move to point currently at outer marker: set m_aiTarget to dOuter==exp( log(dOuter/dUp) ) * dUp
               // (note that m_dLogUpMul has already been set to log(dOuter/dUp)...)
@@ -242,17 +226,15 @@ void CTwoPushDynamicFilter::TimerImpl(unsigned long iTime, CDasherView *m_pDashe
     double dDownDist = exp( dDownBits ) * dDown;
     // (note it's actually slightly more complicated even than that, we have to add in m_dLagBits too!)
     
-    m_aiTarget[0] = dUpDist * exp(m_dLagBits * dSpeedMul);
-    m_aiTarget[1] = -dDownDist * exp(m_dLagBits * dSpeedMul);
-    m_bDecorationChanged |= doSet(m_aiMarker[0], 2048 - exp(m_dLagBits*dSpeedMul + dLogGrowth) * dUp);
-    m_bDecorationChanged |= doSet(m_aiMarker[1], 2048 + exp(m_dLagBits*dSpeedMul + dLogGrowth) * dDown);
-    if (dLogGrowth > m_dMaxLongTwoPushTime)
-    {
-//cout << " growth " << dLogGrowth << " - reversing\n";
+    m_aiTarget[0] = dUpDist * exp(m_dLagBits);
+    m_aiTarget[1] = -dDownDist * exp(m_dLagBits);
+    m_bDecorationChanged |= doSet(m_aiMarker[0], 2048 - exp(m_dLagBits + dLogGrowth) * dUp);
+    m_bDecorationChanged |= doSet(m_aiMarker[1], 2048 + exp(m_dLagBits + dLogGrowth) * dDown);
+    if (dLogGrowth > m_dMaxLongTwoPushTime) {
+//cout << " growth " << dLogGrowth << " - reversing" << std::endl;
       //button pushed, but then waited too long.
       reverse(iTime);
-    }
-    else if (dLogGrowth >= m_dMinShortTwoPushTime && dLogGrowth <= m_dMaxShortTwoPushTime)
+    } else if (dLogGrowth >= m_dMinShortTwoPushTime && dLogGrowth <= m_dMaxShortTwoPushTime)
       m_bDecorationChanged |= doSet(m_iActiveMarker, 0 /*up*/);
     else if (dLogGrowth >= m_dMinLongTwoPushTime)
       m_bDecorationChanged |= doSet(m_iActiveMarker, 1 /*down*/);
diff --git a/Src/DasherCore/TwoPushDynamicFilter.h b/Src/DasherCore/TwoPushDynamicFilter.h
index f893e8f..8b55c59 100644
--- a/Src/DasherCore/TwoPushDynamicFilter.h
+++ b/Src/DasherCore/TwoPushDynamicFilter.h
@@ -25,7 +25,16 @@
 namespace Dasher {
 /// \ingroup InputFilter
 /// @{
-class CTwoPushDynamicFilter : public CDynamicButtons, public CSettingsObserver /*long push, but do our own "multi-push" detection*/ {
+  ///Dynamic filter in which user has two gestures, both performed with a single
+  /// button: one gesture for up and one for down. According to BP_TWO_PUSH_RELEASE_TIME,
+  /// the two gestures are either (a) two pushes with a short gap between, vs
+  /// (b) two pushes with a long gap between; or else, (a) a short vs (b) a long press.
+  ///Note we do not detect long or multiple presses in the usual way (CButtonMultiPress):
+  /// for users capable of making long presses, we suggest turning BP_TWO_PUSH_RELEASE_TIME
+  /// on (and reversing can be achieved by making a press of ambiguous length -
+  /// too short, too long, or inbetween); otherwise, with BP_TWO_PRESS_RELEASE_TIME
+  /// off, reversing can be achieved by making just a single press, and then waiting.
+  class CTwoPushDynamicFilter : public CDynamicButtons, public CSettingsObserver {
  public:
   CTwoPushDynamicFilter(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, CFrameRate *pFramerate);
   
@@ -35,7 +44,7 @@ class CTwoPushDynamicFilter : public CDynamicButtons, public CSettingsObserver /
   virtual bool GetMinWidth(int &iMinWidth);
   virtual bool GetSettings(SModuleSettings **pSettings, int *iCount);
 
-  //override to get mouse clicks / taps back again...
+  //override to get mouse clicks / taps back again if BACKOFF_BUTTON off...
   virtual void KeyDown(unsigned long Time, int iId, CDasherView *pDasherView, CDasherInput *pInput, CDasherModel *pModel);
   virtual void KeyUp(unsigned long Time, int iId, CDasherView *pDasherView, CDasherInput *pInput, CDasherModel *pModel);
 
@@ -48,6 +57,9 @@ class CTwoPushDynamicFilter : public CDynamicButtons, public CSettingsObserver /
   virtual void run(unsigned long iTime);
 
  private:
+  void updateBitrate(double dBitrate);
+  long upDist();
+  long downDist();
   double m_dLogUpMul, m_dLogDownMul, m_dLagBits;
   double m_dMinShortTwoPushTime, m_dMaxShortTwoPushTime,
     m_dMinLongTwoPushTime, m_dMaxLongTwoPushTime;
@@ -55,7 +67,7 @@ class CTwoPushDynamicFilter : public CDynamicButtons, public CSettingsObserver /
   int m_iActiveMarker;
   int m_aiTarget[2];
   int m_aaiGuideAreas[2][2];
-  
+  double m_dLastBitRate;
   double m_dNatsSinceFirstPush;
 };
 }



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