[dasher] - split DasherButtons into one class per style.



commit 1893bb976103dd931e7899756bac3c4d9b76a5d3
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Tue Aug 11 19:16:45 2009 +0200

    - split DasherButtons into one class per style.
    - add option for scanning menu
    DasherButtons is now an abstract superclass; subclasses AlternatingDirectMode,
    CompassMode and ButtonMode (which does both Direct and Menu modes).

 ChangeLog                                   |    4 +
 Src/DasherCore/AlternatingDirectMode.cpp    |  123 ++++++++
 Src/DasherCore/AlternatingDirectMode.h      |   46 +++
 Src/DasherCore/ButtonMode.cpp               |  216 ++++++++++++++
 Src/DasherCore/ButtonMode.h                 |   43 +++
 Src/DasherCore/CompassMode.cpp              |  142 ++++++++++
 Src/DasherCore/CompassMode.h                |   43 +++
 Src/DasherCore/DasherButtons.cpp            |  406 ++-------------------------
 Src/DasherCore/DasherButtons.h              |   40 +--
 Src/DasherCore/DasherInterfaceBase.cpp      |   14 +-
 Src/DasherCore/Makefile.am                  |    6 +
 Src/DasherCore/Parameters.h                 |    4 +-
 Src/MacOSX/Dasher.xcodeproj/project.pbxproj |   26 ++
 13 files changed, 699 insertions(+), 414 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 84b0e82..08c306b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-08-11  Alan Lawrence <acl33 inf phy cam ac uk>
+
+	* Split DasherButtons into one class per style; add option for scanning menu
+
 2009-08-10  Alan Lawrence <acl33 inf phy cam ac uk>
 
 	* Remove unused GetRenderCount methods
diff --git a/Src/DasherCore/AlternatingDirectMode.cpp b/Src/DasherCore/AlternatingDirectMode.cpp
new file mode 100644
index 0000000..1e57622
--- /dev/null
+++ b/Src/DasherCore/AlternatingDirectMode.cpp
@@ -0,0 +1,123 @@
+// DasherButtons.cpp, build a set of boxes for Button Dasher.
+// Copyright 2005, Chris Ball and David MacKay.  GPL.
+
+// Idea - should back off button always just undo the previous 'forwards' button?
+
+#include "../Common/Common.h"
+
+
+#include "AlternatingDirectMode.h"
+#include "DasherScreen.h"
+#include <valarray>
+#include <iostream>
+
+// Track memory leaks on Windows to the line that new'd the memory
+#ifdef _WIN32
+#ifdef _DEBUG_MEMLEAKS
+#define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+#endif
+
+using namespace Dasher;
+
+static SModuleSettings sSettings[] = {
+  /* TRANSLATORS: The number of time steps over which to perform the zooming motion in button mode. */
+  {LP_ZOOMSTEPS, T_LONG, 1, 63, 1, 1, _("Zoom steps")},
+  /* TRANSLATORS: Intercept keyboard events for 'special' keys even when the Dasher window doesn't have keyboard focus.*/
+  {BP_GLOBAL_KEYBOARD, T_BOOL, -1, -1, -1, -1, _("Global keyboard grab")}
+};
+
+CAlternatingDirectMode::CAlternatingDirectMode(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
+  : CDasherButtons(pEventHandler, pSettingsStore, pInterface, false/*menu*/, 12, _("Alternating Direct Mode")) {}
+
+void CAlternatingDirectMode::SetupBoxes()
+{
+  int iDasherY(GetLongParameter(LP_MAX_Y));
+
+  m_pBoxes = new SBoxInfo[m_iNumBoxes = 5];
+
+  // Fast boxes
+
+  m_pBoxes[0].iTop = 0;
+  m_pBoxes[0].iBottom = 1000;
+  m_pBoxes[1].iTop = 3096;
+  m_pBoxes[1].iBottom = 4096;
+
+  // Slow boxes
+
+  m_pBoxes[2].iTop = 0;
+  m_pBoxes[2].iBottom = 3096;
+  m_pBoxes[3].iTop = 1000;
+  m_pBoxes[3].iBottom = 4096; 
+
+  m_pBoxes[0].iDisplayTop = m_pBoxes[0].iTop; 
+  m_pBoxes[0].iDisplayBottom = m_pBoxes[0].iBottom;
+  m_pBoxes[1].iDisplayTop = m_pBoxes[1].iTop; 
+  m_pBoxes[1].iDisplayBottom = m_pBoxes[1].iBottom;
+  m_pBoxes[2].iDisplayTop = m_pBoxes[2].iTop; 
+  m_pBoxes[2].iDisplayBottom = m_pBoxes[2].iBottom;
+  m_pBoxes[3].iDisplayTop = m_pBoxes[3].iTop; 
+  m_pBoxes[3].iDisplayBottom = m_pBoxes[3].iBottom;
+
+  m_pBoxes[m_iNumBoxes-1].iDisplayTop = 0;
+  m_pBoxes[m_iNumBoxes-1].iDisplayBottom = iDasherY;
+  
+  m_pBoxes[m_iNumBoxes-1].iTop = int(- iDasherY / 2);
+  m_pBoxes[m_iNumBoxes-1].iBottom = int(iDasherY * 1.5);
+
+  m_iLastBox = -1;
+}
+
+bool CAlternatingDirectMode::DecorateView(CDasherView *pView) {
+
+  if(m_iLastBox == 1) {
+    NewDrawGoTo(pView, m_pBoxes[2].iDisplayTop, m_pBoxes[2].iDisplayBottom, false);
+    NewDrawGoTo(pView, m_pBoxes[1].iDisplayTop, m_pBoxes[3].iDisplayBottom, false);
+    NewDrawGoTo(pView, m_pBoxes[4].iDisplayTop, m_pBoxes[4].iDisplayBottom, false);
+  }
+  else {
+    NewDrawGoTo(pView, m_pBoxes[0].iDisplayTop, m_pBoxes[0].iDisplayBottom, false);
+    NewDrawGoTo(pView, m_pBoxes[3].iDisplayTop, m_pBoxes[1].iDisplayBottom, false);
+    NewDrawGoTo(pView, m_pBoxes[4].iDisplayTop, m_pBoxes[4].iDisplayBottom, false);
+  }
+
+  bool bRV(m_bDecorationChanged);
+  m_bDecorationChanged = false;
+  return bRV;
+}
+ 
+
+void CAlternatingDirectMode::DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog) {
+
+  switch(iId) {
+  case 2:
+    if(m_iLastBox == 1)
+      pModel->ScheduleZoom((m_pBoxes[2].iBottom - m_pBoxes[2].iTop)/2, (m_pBoxes[2].iBottom + m_pBoxes[2].iTop)/2);
+    else
+      pModel->ScheduleZoom((m_pBoxes[0].iBottom - m_pBoxes[0].iTop)/2, (m_pBoxes[0].iBottom + m_pBoxes[0].iTop)/2);
+    m_iLastBox = 1;
+    break; 
+  case 3:
+  case 4:
+    if(m_iLastBox == 2)
+      pModel->ScheduleZoom((m_pBoxes[3].iBottom - m_pBoxes[3].iTop)/2, (m_pBoxes[3].iBottom + m_pBoxes[3].iTop)/2);
+    else
+      pModel->ScheduleZoom((m_pBoxes[1].iBottom - m_pBoxes[1].iTop)/2, (m_pBoxes[1].iBottom + m_pBoxes[1].iTop)/2);
+    m_iLastBox = 2;
+    break;
+  case 1:
+    pModel->ScheduleZoom((m_pBoxes[4].iBottom - m_pBoxes[4].iTop)/2, (m_pBoxes[4].iBottom + m_pBoxes[4].iTop)/2);
+    break;
+  }
+
+}
+
+bool CAlternatingDirectMode::GetSettings(SModuleSettings **pSettings, int *iCount) {
+  *pSettings = sSettings;
+  *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
+
+  return true;
+};
diff --git a/Src/DasherCore/AlternatingDirectMode.h b/Src/DasherCore/AlternatingDirectMode.h
new file mode 100644
index 0000000..71c364c
--- /dev/null
+++ b/Src/DasherCore/AlternatingDirectMode.h
@@ -0,0 +1,46 @@
+
+// DasherButtons.h 
+// Copyright 2005 by Chris Ball
+
+#ifndef __ALTERNATING_DIRECT_MODE_H__
+#define __ALTERNATING_DIRECT_MODE_H__
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include "Alphabet/Alphabet.h"
+#include <algorithm>
+#include "DasherComponent.h"
+#include "Event.h"
+#include "DasherButtons.h"
+
+
+using namespace std;
+namespace Dasher {
+/// \ingroup Input
+/// @{
+
+//TODO maybe some kind of scanning/menu option here, too, tho slightly more complicated than for direct/menu mode?
+
+  class CAlternatingDirectMode : public CDasherButtons
+{
+ public:
+  CAlternatingDirectMode(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, CDasherInterfaceBase *pInterface);
+  
+  bool DecorateView(CDasherView *pView);
+
+  bool GetSettings(SModuleSettings **pSettings, int *iCount);
+
+ protected:
+  void SetupBoxes();
+  
+ private:
+  void DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
+    
+  int m_iLastBox;
+};
+}
+/// @}
+
+#endif
diff --git a/Src/DasherCore/ButtonMode.cpp b/Src/DasherCore/ButtonMode.cpp
new file mode 100644
index 0000000..23b6a8e
--- /dev/null
+++ b/Src/DasherCore/ButtonMode.cpp
@@ -0,0 +1,216 @@
+// DasherButtons.cpp, build a set of boxes for Button Dasher.
+// Copyright 2005, Chris Ball and David MacKay.  GPL.
+
+// Idea - should back off button always just undo the previous 'forwards' button?
+
+#include "../Common/Common.h"
+
+
+#include "ButtonMode.h"
+#include "DasherScreen.h"
+#include <valarray>
+#include <iostream>
+
+// Track memory leaks on Windows to the line that new'd the memory
+#ifdef _WIN32
+#ifdef _DEBUG_MEMLEAKS
+#define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+#endif
+
+using namespace Dasher;
+
+static SModuleSettings sSettings[] = {
+  /* TRANSLATORS: The number of time steps over which to perform the zooming motion in button mode. */
+  {LP_ZOOMSTEPS, T_LONG, 1, 63, 1, 1, _("Zoom steps")},
+  {LP_BUTTON_SCAN_TIME, T_LONG, 0, 2000, 1, 100, _("Scan time in menu mode (0 for don't scan)")},
+  {LP_B, T_LONG, 2, 10, 1, 1, _("Number of boxes")},
+  {LP_S, T_LONG, 0, 256, 1, 1, _("Safety margin")},
+  /* TRANSLATORS: The boxes (zoom targets) in button mode can either be the same size, or different sizes - this is the extent to which the sizes are allowed to differ from each other. */
+  /* XXX PRLW: 128 log(2) = 89, where 2 is the ratio of adjacent boxes
+   * however the code seems to use ratio = (129/127)^-r, instead of
+   * ratio = exp(r/128) used in the design document
+   */
+  {LP_R, T_LONG, -89, 89, 1, 10, _("Box non-uniformity")},
+  /* TRANSLATORS: Intercept keyboard events for 'special' keys even when the Dasher window doesn't have keyboard focus.*/
+  {BP_GLOBAL_KEYBOARD, T_BOOL, -1, -1, -1, -1, _("Global keyboard grab")}
+};
+
+// FIX iStyle == 0
+
+CButtonMode::CButtonMode(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, bool bMenu, int iID, const char *szName)
+: CDasherButtons(pEventHandler, pSettingsStore, pInterface, bMenu, iID, szName) {}
+
+void CButtonMode::SetupBoxes()
+{
+  int iDasherY(GetLongParameter(LP_MAX_Y));
+
+  int iForwardBoxes(GetLongParameter(LP_B));
+  m_pBoxes = new SBoxInfo[m_iNumBoxes = iForwardBoxes+1];
+
+  // Calculate the sizes of non-uniform boxes using standard
+  // geometric progression results
+
+  double dRatio;
+  double dNorm;
+  
+  // FIXME - implement this using DJCM's integer method?
+  // See ~mackay/dasher/buttons/
+  dRatio = pow(129/127.0, -static_cast<double>(GetLongParameter(LP_R)));
+
+  if(m_bMenu) {
+
+    double dMaxSize;
+    if(dRatio == 1.0)
+      dMaxSize = iDasherY / static_cast<double>(iForwardBoxes);
+    else
+      dMaxSize = ((dRatio - 1)/(pow(dRatio, iForwardBoxes) - 1)) * iDasherY; 
+    
+    double dMin(0.0);
+    double dMax;
+    
+    for(int i(0); i < m_iNumBoxes - 1; ++i) { // One button reserved for backoff
+      dMax = dMin + dMaxSize * pow(dRatio, i);
+
+//       m_pBoxes[i].iDisplayTop = (i * iDasherY) / (m_iNumBoxes - 1);
+//       m_pBoxes[i].iDisplayBottom = ((i+1) * iDasherY) / (m_iNumBoxes - 1);
+
+      m_pBoxes[i].iDisplayTop = static_cast<int>(dMin);
+      m_pBoxes[i].iDisplayBottom = static_cast<int>(dMax);
+
+      m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
+      m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
+
+      dMin = dMax;
+    }
+
+  }
+  else {      
+    if(iForwardBoxes == 2+1) { // Special case for two forwards buttons
+      dNorm = 1+dRatio;
+
+      m_pBoxes[0].iDisplayTop = 0;
+      m_pBoxes[0].iDisplayBottom = int( (1 / dNorm) * iDasherY );
+
+      m_pBoxes[1].iDisplayTop = int( (1 / dNorm) * iDasherY );
+      m_pBoxes[1].iDisplayBottom = iDasherY;
+    }
+    else {
+      bool bEven(iForwardBoxes % 2 == 0);
+
+      int iGeometricTerms;
+
+      if(bEven)
+        iGeometricTerms = iForwardBoxes / 2;
+      else
+        iGeometricTerms = (1+iForwardBoxes) / 2;
+
+      double dMaxSize;
+
+      if(dRatio == 1.0) {
+        dMaxSize = iDasherY / iForwardBoxes;
+      }
+      else {
+        if(bEven)
+          dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1));
+        else
+          dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1) - (dRatio - 1));
+      }
+
+      double dMin;
+      double dMax;
+
+      if(bEven)
+        dMin = iDasherY / 2;
+      else
+        dMin = (iDasherY - dMaxSize)/2;
+
+      int iUpBase;
+      int iDownBase;
+
+      if(bEven) {
+        iUpBase = iForwardBoxes / 2;
+        iDownBase = iUpBase - 1;
+      }
+      else {
+        iUpBase = (iForwardBoxes - 1)/ 2;
+        iDownBase = iUpBase;
+      }
+
+      for(int i(0); i < iGeometricTerms; ++i) { // One button reserved for backoff
+        dMax = dMin + dMaxSize * pow(dRatio, i);
+
+        m_pBoxes[iUpBase + i].iDisplayTop = int(dMin);
+        m_pBoxes[iUpBase + i].iDisplayBottom = int(dMax);
+
+        m_pBoxes[iDownBase - i].iDisplayTop = int(iDasherY - dMax);
+        m_pBoxes[iDownBase - i].iDisplayBottom = int(iDasherY - dMin);
+
+        dMin = dMax;
+      }
+    }
+  }
+
+  for(int i(0); i < m_iNumBoxes - 1; ++i) {
+    m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
+    m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
+  }
+  
+  m_pBoxes[m_iNumBoxes-1].iDisplayTop = 0;
+  m_pBoxes[m_iNumBoxes-1].iDisplayBottom = iDasherY;
+  
+  m_pBoxes[m_iNumBoxes-1].iTop = int(- iDasherY / 2);
+  m_pBoxes[m_iNumBoxes-1].iBottom = int(iDasherY * 1.5);
+}
+
+bool CButtonMode::DecorateView(CDasherView *pView) {
+  for(int i(0); i < m_iNumBoxes; ++i) {
+    if(i != iActiveBox)
+      NewDrawGoTo(pView, m_pBoxes[i].iDisplayTop, m_pBoxes[i].iDisplayBottom, false);
+  }
+  NewDrawGoTo(pView, m_pBoxes[iActiveBox].iDisplayTop, m_pBoxes[iActiveBox].iDisplayBottom, m_bMenu || m_bHighlight);
+
+  bool bRV(m_bDecorationChanged);
+  m_bDecorationChanged = false;
+  return bRV;
+}
+
+bool CButtonMode::Timer(int Time, CDasherView *pView, CDasherModel *pModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) {
+  bool m_bOldHighlight(m_bHighlight);
+  m_bHighlight = (Time - m_iLastTime < 200);
+  
+  if(m_bOldHighlight != m_bHighlight)
+    m_bDecorationChanged = true;  
+
+  return CDasherButtons::Timer(Time, pView, pModel, pAdded, pNumDeleted);
+}
+
+void CButtonMode::DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog) {
+  CDasherButtons::DirectKeyDown(iTime, iId, pView, pModel, pUserLog);
+ if (iId!=100) m_iLastTime = iTime;
+}
+
+void CButtonMode::HandleEvent(Dasher::CEvent * pEvent) {
+  if(pEvent->m_iEventType == 1) {
+    Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
+
+    switch (pEvt->m_iParameter) {
+    case LP_B:
+    case LP_R:
+      // Delibarate fallthrough
+      delete[] m_pBoxes;
+      SetupBoxes();
+      break;
+    }
+  }
+}
+
+bool CButtonMode::GetSettings(SModuleSettings **pSettings, int *iCount) {
+  *pSettings = sSettings;
+  *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
+
+  return true;
+};
\ No newline at end of file
diff --git a/Src/DasherCore/ButtonMode.h b/Src/DasherCore/ButtonMode.h
new file mode 100644
index 0000000..3b720f3
--- /dev/null
+++ b/Src/DasherCore/ButtonMode.h
@@ -0,0 +1,43 @@
+
+// ButtonMode.h 
+// Copyright 2009 by Alan Lawrence
+
+#ifndef __BUTTON_MODE_H__
+#define __BUTTON_MODE_H__
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include "Alphabet/Alphabet.h"
+#include <algorithm>
+#include "DasherComponent.h"
+#include "Event.h"
+#include "DasherButtons.h"
+
+using namespace std;
+namespace Dasher {
+/// \ingroup Input
+/// @{
+/// Handles the "menu mode" and "direct mode" input filters, according to the bMenu constructor parameter.
+class CButtonMode : public CDasherButtons
+{
+ public:
+  CButtonMode(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, CDasherInterfaceBase *pInterface, bool bMenu, int iID, const char *szName);
+
+  virtual void HandleEvent(Dasher::CEvent * pEvent);
+  bool Timer(int Time, CDasherView *pView, CDasherModel *pModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
+  bool DecorateView(CDasherView *pView);
+
+  bool GetSettings(SModuleSettings **pSettings, int *iCount);
+ protected: 
+  void SetupBoxes();
+  void DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
+ private:
+  bool m_bHighlight;
+  int m_iLastTime;
+};
+}
+/// @}
+
+#endif
diff --git a/Src/DasherCore/CompassMode.cpp b/Src/DasherCore/CompassMode.cpp
new file mode 100644
index 0000000..f6b5ccb
--- /dev/null
+++ b/Src/DasherCore/CompassMode.cpp
@@ -0,0 +1,142 @@
+// DasherButtons.cpp, build a set of boxes for Button Dasher.
+// Copyright 2005, Chris Ball and David MacKay.  GPL.
+
+// Idea - should back off button always just undo the previous 'forwards' button?
+
+#include "../Common/Common.h"
+
+
+#include "CompassMode.h"
+#include "DasherScreen.h"
+#include <valarray>
+#include <iostream>
+
+// Track memory leaks on Windows to the line that new'd the memory
+#ifdef _WIN32
+#ifdef _DEBUG_MEMLEAKS
+#define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+#endif
+
+using namespace Dasher;
+
+static SModuleSettings sSettings[] = {
+  /* TRANSLATORS: The number of time steps over which to perform the zooming motion in button mode. */
+  {LP_ZOOMSTEPS, T_LONG, 1, 63, 1, 1, _("Zoom steps")},
+  /* TRANSLATORS: The zoom factor per press when moving to the right in compass mode. */
+  {LP_RIGHTZOOM, T_LONG, 1024, 10240, 1024, 1024, _("Right zoom")},
+  /* TRANSLATORS: Intercept keyboard events for 'special' keys even when the Dasher window doesn't have keyboard focus.*/
+  {BP_GLOBAL_KEYBOARD, T_BOOL, -1, -1, -1, -1, _("Global keyboard grab")}
+};
+
+// FIX iStyle == 2
+
+CCompassMode::CCompassMode(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
+  : CDasherButtons(pEventHandler, pSettingsStore, pInterface, false /*bMenu*/, 13, _("Compass Mode")) {}
+
+void CCompassMode::SetupBoxes()
+{  
+  int iDasherY(GetLongParameter(LP_MAX_Y));
+
+  m_pBoxes = new SBoxInfo[m_iNumBoxes = 4];
+
+  iTargetWidth = iDasherY * 1024 / GetLongParameter(LP_RIGHTZOOM);
+
+  // FIXME - need to relate these to cross-hair position as stored in the parameters
+
+  // Not sure whether this is at all the right algorithm here - need to check
+
+  m_pBoxes[1].iTop = (2048 - iTargetWidth / 2);
+  m_pBoxes[1].iBottom = 4096 - m_pBoxes[1].iTop;
+
+  // Make this the inverse of the right zoom option
+
+  m_pBoxes[3].iTop = -2048 *  m_pBoxes[1].iTop / (2048 -  m_pBoxes[1].iTop);
+  m_pBoxes[3].iBottom = 4096 - m_pBoxes[3].iTop;
+
+  m_pBoxes[0].iTop = -iTargetWidth;
+  m_pBoxes[0].iBottom = iDasherY - iTargetWidth;
+  m_pBoxes[2].iTop = iTargetWidth;
+  m_pBoxes[2].iBottom = iDasherY + iTargetWidth;
+
+  m_pBoxes[0].iDisplayTop = m_pBoxes[0].iTop; 
+  m_pBoxes[0].iDisplayBottom = m_pBoxes[0].iBottom;
+  m_pBoxes[1].iDisplayTop = m_pBoxes[1].iTop; 
+  m_pBoxes[1].iDisplayBottom = m_pBoxes[1].iBottom;
+  m_pBoxes[2].iDisplayTop = m_pBoxes[2].iTop; 
+  m_pBoxes[2].iDisplayBottom = m_pBoxes[2].iBottom;
+  m_pBoxes[3].iDisplayTop = m_pBoxes[3].iTop; 
+  m_pBoxes[3].iDisplayBottom = m_pBoxes[3].iBottom;
+}
+
+bool CCompassMode::DecorateView(CDasherView *pView) {
+  CDasherScreen *pScreen(pView->Screen());
+
+  int iPos(2048 - iTargetWidth / 2);
+
+  bool bFirst(true);
+
+  while(iPos >= 0) {
+    CDasherScreen::point p[2];
+
+    myint iDasherX;
+    myint iDasherY;
+
+    iDasherX = -100;
+    iDasherY = iPos;
+
+    pView->Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
+
+    iDasherX = -1000;
+    iDasherY = iPos;
+    
+    pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
+
+    if(bFirst)
+      pScreen->Polyline(p, 2, 1, 1);
+    else
+      pScreen->Polyline(p, 2, 1, 2);
+
+    iDasherX = -100;
+    iDasherY = 4096 - iPos;
+
+    pView->Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
+
+    iDasherX = -1000;
+    iDasherY = 4096 - iPos;
+    
+    pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
+
+    if(bFirst)
+      pScreen->Polyline(p, 2, 1, 1);
+    else
+      pScreen->Polyline(p, 2, 1, 2);
+
+    iPos -= iTargetWidth;
+    bFirst = false;
+  }
+
+  bool bRV(m_bDecorationChanged);
+  m_bDecorationChanged = false;
+  return bRV;
+}
+ 
+void CCompassMode::HandleEvent(Dasher::CEvent * pEvent) {
+  if(pEvent->m_iEventType == 1) {
+    Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
+    if (pEvt->m_iParameter == LP_RIGHTZOOM) {
+      delete[] m_pBoxes;
+      SetupBoxes();
+    }
+  }
+}
+
+bool CCompassMode::GetSettings(SModuleSettings **pSettings, int *iCount) {
+  *pSettings = sSettings;
+  *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
+
+  return true;
+};
\ No newline at end of file
diff --git a/Src/DasherCore/CompassMode.h b/Src/DasherCore/CompassMode.h
new file mode 100644
index 0000000..f982e41
--- /dev/null
+++ b/Src/DasherCore/CompassMode.h
@@ -0,0 +1,43 @@
+
+// DasherButtons.h 
+// Copyright 2005 by Chris Ball
+
+#ifndef __COMPASS_MODE_H__
+#define __COMPASS_MODE_H__
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include "Alphabet/Alphabet.h"
+#include <algorithm>
+#include "DasherComponent.h"
+#include "Event.h"
+#include "DasherButtons.h"
+
+
+using namespace std;
+namespace Dasher {
+/// \ingroup Input
+/// @{
+class CCompassMode : public CDasherButtons
+{
+ public:
+  CCompassMode(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, CDasherInterfaceBase *pInterface);
+
+  virtual void HandleEvent(Dasher::CEvent * pEvent);
+  
+  bool DecorateView(CDasherView *pView);
+
+  bool GetSettings(SModuleSettings **pSettings, int *iCount);
+
+ protected:
+  void SetupBoxes();
+  
+ private: 
+  int iTargetWidth;
+};
+}
+/// @}
+
+#endif
diff --git a/Src/DasherCore/DasherButtons.cpp b/Src/DasherCore/DasherButtons.cpp
index 0a00969..8bed265 100644
--- a/Src/DasherCore/DasherButtons.cpp
+++ b/Src/DasherCore/DasherButtons.cpp
@@ -23,328 +23,22 @@ static char THIS_FILE[] = __FILE__;
 
 using namespace Dasher;
 
-static SModuleSettings sSettings[] = {
-  /* TRANSLATORS: The number of time steps over which to perform the zooming motion in button mode. */
-  {LP_ZOOMSTEPS, T_LONG, 1, 63, 1, 1, _("Zoom steps")},
-  /* TRANSLATORS: The zoom factor per press when moving to the right in compass mode. */
-  {LP_RIGHTZOOM, T_LONG, 1024, 10240, 1024, 1024, _("Right zoom")},
-  {LP_B, T_LONG, 2, 10, 1, 1, _("Number of boxes")},
-  {LP_S, T_LONG, 0, 256, 1, 1, _("Safety margin")},
-  /* TRANSLATORS: The boxes (zoom targets) in button mode can either be the same size, or different sizes - this is the extent to which the sizes are allowed to differ from each other. */
-  /* XXX PRLW: 128 log(2) = 89, where 2 is the ratio of adjacent boxes
-   * however the code seems to use ratio = (129/127)^-r, instead of
-   * ratio = exp(r/128) used in the design document
-   */
-  {LP_R, T_LONG, -89, 89, 1, 10, _("Box non-uniformity")},
-  /* TRANSLATORS: Intercept keyboard events for 'special' keys even when the Dasher window doesn't have keyboard focus.*/
-  {BP_GLOBAL_KEYBOARD, T_BOOL, -1, -1, -1, -1, _("Global keyboard grab")}
-};
-
 // FIXME - should compass mode be made a separate class?
 
-CDasherButtons::CDasherButtons(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, int iNumBoxes, int iStyle, bool bMenu, ModuleID_t iID, const char *szName)
-  : CInputFilter(pEventHandler, pSettingsStore, pInterface, iID, 1, szName) {
-
-  m_pBoxes = 0;
-
-  m_iNumBoxes = iNumBoxes;
-  m_iStyle = iStyle;
-  m_bMenu = bMenu;
-
-  m_pSettingsStore = pSettingsStore;
-
-  m_bDecorationChanged = true;
-
-  SetupBoxes();
-}
+CDasherButtons::CDasherButtons(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, bool bMenu, ModuleID_t iID, const char *szName)
+  : CInputFilter(pEventHandler, pSettingsStore, pInterface, iID, 1, szName), m_bMenu(bMenu), m_bDecorationChanged(true), m_pBoxes(NULL), iActiveBox(0) {}
 
 CDasherButtons::~CDasherButtons()
 {
   delete[] m_pBoxes;
-}
-
-void CDasherButtons::SetupBoxes()
-{
-  
-  int iDasherY(GetLongParameter(LP_MAX_Y));
-
-
-  if(m_iStyle == 3) {
-    // Alternating direct mode
-
-    m_pBoxes = new SBoxInfo[5];
-
-    // Fast boxes
-
-    m_pBoxes[0].iTop = 0;
-    m_pBoxes[0].iBottom = 1000;
-    m_pBoxes[1].iTop = 3096;
-    m_pBoxes[1].iBottom = 4096;
-
-    // Slow boxes
-
-    m_pBoxes[2].iTop = 0;
-    m_pBoxes[2].iBottom = 3096;
-    m_pBoxes[3].iTop = 1000;
-    m_pBoxes[3].iBottom = 4096; 
-
-    m_pBoxes[0].iDisplayTop = m_pBoxes[0].iTop; 
-    m_pBoxes[0].iDisplayBottom = m_pBoxes[0].iBottom;
-    m_pBoxes[1].iDisplayTop = m_pBoxes[1].iTop; 
-    m_pBoxes[1].iDisplayBottom = m_pBoxes[1].iBottom;
-    m_pBoxes[2].iDisplayTop = m_pBoxes[2].iTop; 
-    m_pBoxes[2].iDisplayBottom = m_pBoxes[2].iBottom;
-    m_pBoxes[3].iDisplayTop = m_pBoxes[3].iTop; 
-    m_pBoxes[3].iDisplayBottom = m_pBoxes[3].iBottom;
-
-    m_iNumBoxes = 5; 
-    m_pBoxes[m_iNumBoxes-1].iDisplayTop = 0;
-    m_pBoxes[m_iNumBoxes-1].iDisplayBottom = iDasherY;
-    
-    m_pBoxes[m_iNumBoxes-1].iTop = int(- iDasherY / 2);
-    m_pBoxes[m_iNumBoxes-1].iBottom = int(iDasherY * 1.5);
-
-    m_iLastBox = -1;
-    
-  }
-  else if(m_iStyle == 2) { // Compass mode
-    m_pBoxes = new SBoxInfo[4];
-
-    iTargetWidth = iDasherY * 1024 / GetLongParameter(LP_RIGHTZOOM);
-
-    // FIXME - need to relate these to cross-hair position as stored in the parameters
-
-    // Not sure whether this is at all the right algorithm here - need to check
-
-    m_pBoxes[1].iTop = (2048 - iTargetWidth / 2);
-    m_pBoxes[1].iBottom = 4096 - m_pBoxes[1].iTop;
-
-    // Make this the inverse of the right zoom option
-
-    m_pBoxes[3].iTop = -2048 *  m_pBoxes[1].iTop / (2048 -  m_pBoxes[1].iTop);
-    m_pBoxes[3].iBottom = 4096 - m_pBoxes[3].iTop;
-
-    m_pBoxes[0].iTop = -iTargetWidth;
-    m_pBoxes[0].iBottom = iDasherY - iTargetWidth;
-    m_pBoxes[2].iTop = iTargetWidth;
-    m_pBoxes[2].iBottom = iDasherY + iTargetWidth;
-
-    m_pBoxes[0].iDisplayTop = m_pBoxes[0].iTop; 
-    m_pBoxes[0].iDisplayBottom = m_pBoxes[0].iBottom;
-    m_pBoxes[1].iDisplayTop = m_pBoxes[1].iTop; 
-    m_pBoxes[1].iDisplayBottom = m_pBoxes[1].iBottom;
-    m_pBoxes[2].iDisplayTop = m_pBoxes[2].iTop; 
-    m_pBoxes[2].iDisplayBottom = m_pBoxes[2].iBottom;
-    m_pBoxes[3].iDisplayTop = m_pBoxes[3].iTop; 
-    m_pBoxes[3].iDisplayBottom = m_pBoxes[3].iBottom;
-
-  }
-  else {
-
-    if((m_iStyle == 1) || (m_iStyle == 0)) 
-      m_iNumBoxes = GetLongParameter(LP_B) + 1; // One extra box for backoff
-    
-    if(m_pBoxes) {
-      delete[] m_pBoxes;
-      m_pBoxes = 0;
-    }
-
-    m_pBoxes = new SBoxInfo[m_iNumBoxes];
-    int iForwardBoxes(m_iNumBoxes - 1);
-
-    // Calculate the sizes of non-uniform boxes using standard
-    // geometric progression results
-
-    double dRatio;
-    double dNorm;
-    
-    // FIXME - implement this using DJCM's integer method?
-    // See ~mackay/dasher/buttons/
-    dRatio = pow(129/127.0, -static_cast<double>(GetLongParameter(LP_R)));
-
-    if(m_bMenu) {
-
-      double dMaxSize;
-      if(dRatio == 1.0)
-        dMaxSize = iDasherY / static_cast<double>(iForwardBoxes);
-      else
-        dMaxSize = ((dRatio - 1)/(pow(dRatio, iForwardBoxes) - 1)) * iDasherY; 
-      
-      double dMin(0.0);
-      double dMax;
-      
-      for(int i(0); i < m_iNumBoxes - 1; ++i) { // One button reserved for backoff
-        dMax = dMin + dMaxSize * pow(dRatio, i);
-
-//       m_pBoxes[i].iDisplayTop = (i * iDasherY) / (m_iNumBoxes - 1);
-//       m_pBoxes[i].iDisplayBottom = ((i+1) * iDasherY) / (m_iNumBoxes - 1);
-
-        m_pBoxes[i].iDisplayTop = static_cast<int>(dMin);
-        m_pBoxes[i].iDisplayBottom = static_cast<int>(dMax);
-
-        m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
-        m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
-
-        dMin = dMax;
-      }
-
-    }
-    else {      
-      if(m_iNumBoxes == 2+1) { // Special case for two forwards buttons
-        dNorm = 1+dRatio;
-
-        m_pBoxes[0].iDisplayTop = 0;
-        m_pBoxes[0].iDisplayBottom = int( (1 / dNorm) * iDasherY );
-
-        m_pBoxes[1].iDisplayTop = int( (1 / dNorm) * iDasherY );
-        m_pBoxes[1].iDisplayBottom = iDasherY;
-      }
-      else {
-        int iForwardsButtons(m_iNumBoxes - 1);
-        bool bEven(iForwardsButtons % 2 == 0);
-
-        int iGeometricTerms;
-
-        if(bEven)
-          iGeometricTerms = iForwardsButtons / 2;
-        else
-          iGeometricTerms = (1+iForwardsButtons) / 2;
-
-        double dMaxSize;
-
-        if(dRatio == 1.0) {
-          dMaxSize = iDasherY / iForwardsButtons;
-        }
-        else {
-          if(bEven)
-            dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1));
-          else
-            dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1) - (dRatio - 1));
-        }
-
-        double dMin;
-        double dMax;
+} 
 
-        if(bEven)
-          dMin = iDasherY / 2;
-        else
-          dMin = (iDasherY - dMaxSize)/2;
-
-        int iUpBase;
-        int iDownBase;
-
-        if(bEven) {
-          iUpBase = iForwardsButtons / 2;
-          iDownBase = iUpBase - 1;
-        }
-        else {
-          iUpBase = (iForwardsButtons - 1)/ 2;
-          iDownBase = iUpBase;
-        }
-
-        for(int i(0); i < iGeometricTerms; ++i) { // One button reserved for backoff
-          dMax = dMin + dMaxSize * pow(dRatio, i);
-
-          m_pBoxes[iUpBase + i].iDisplayTop = int(dMin);
-          m_pBoxes[iUpBase + i].iDisplayBottom = int(dMax);
-
-          m_pBoxes[iDownBase - i].iDisplayTop = int(iDasherY - dMax);
-          m_pBoxes[iDownBase - i].iDisplayBottom = int(iDasherY - dMin);
-
-          dMin = dMax;
-        }
-      }
-    }
-
-    for(int i(0); i < m_iNumBoxes - 1; ++i) {
-      m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
-      m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
-    }
-    
-    m_pBoxes[m_iNumBoxes-1].iDisplayTop = 0;
-    m_pBoxes[m_iNumBoxes-1].iDisplayBottom = iDasherY;
-    
-    m_pBoxes[m_iNumBoxes-1].iTop = int(- iDasherY / 2);
-    m_pBoxes[m_iNumBoxes-1].iBottom = int(iDasherY * 1.5);
-  }
- 
-  iActiveBox = 0;
-  m_bDecorationChanged = true;
-}
-
-bool CDasherButtons::DecorateView(CDasherView *pView) {
-  if(m_iStyle == 2) {
-    CDasherScreen *pScreen(pView->Screen());
-
-    int iPos(2048 - iTargetWidth / 2);
-
-    bool bFirst(true);
-
-    while(iPos >= 0) {
-      CDasherScreen::point p[2];
-
-      myint iDasherX;
-      myint iDasherY;
-
-      iDasherX = -100;
-      iDasherY = iPos;
-
-      pView->Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
-
-      iDasherX = -1000;
-      iDasherY = iPos;
-      
-      pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
-
-      if(bFirst)
-        pScreen->Polyline(p, 2, 1, 1);
-      else
-        pScreen->Polyline(p, 2, 1, 2);
-
-      iDasherX = -100;
-      iDasherY = 4096 - iPos;
-
-      pView->Dasher2Screen(iDasherX, iDasherY, p[0].x, p[0].y);
-
-      iDasherX = -1000;
-      iDasherY = 4096 - iPos;
-      
-      pView->Dasher2Screen(iDasherX, iDasherY, p[1].x, p[1].y);
-
-      if(bFirst)
-        pScreen->Polyline(p, 2, 1, 1);
-      else
-        pScreen->Polyline(p, 2, 1, 2);
-
-      iPos -= iTargetWidth;
-      bFirst = false;
-    }
-  }
-  else if(m_iStyle == 3) {
-    if(m_iLastBox == 1) {
-      NewDrawGoTo(pView, m_pBoxes[2].iDisplayTop, m_pBoxes[2].iDisplayBottom, false);
-      NewDrawGoTo(pView, m_pBoxes[1].iDisplayTop, m_pBoxes[3].iDisplayBottom, false);
-      NewDrawGoTo(pView, m_pBoxes[4].iDisplayTop, m_pBoxes[4].iDisplayBottom, false);
-    }
-    else {
-      NewDrawGoTo(pView, m_pBoxes[0].iDisplayTop, m_pBoxes[0].iDisplayBottom, false);
-      NewDrawGoTo(pView, m_pBoxes[3].iDisplayTop, m_pBoxes[1].iDisplayBottom, false);
-      NewDrawGoTo(pView, m_pBoxes[4].iDisplayTop, m_pBoxes[4].iDisplayBottom, false);
-    }
-  }
-  else {
-    for(int i(0); i < m_iNumBoxes; ++i) {
-      if(i != iActiveBox)
-        NewDrawGoTo(pView, m_pBoxes[i].iDisplayTop, m_pBoxes[i].iDisplayBottom, false);
-    }
-    NewDrawGoTo(pView, m_pBoxes[iActiveBox].iDisplayTop, m_pBoxes[iActiveBox].iDisplayBottom, m_bMenu || m_bHighlight);
-  }
-
-  bool bRV(m_bDecorationChanged);
-  m_bDecorationChanged = false;
-  return bRV;
+void CDasherButtons::Activate() {
+  //ick - can't do this at construction time! This should get called before anything
+  // which depends on it, tho...
+  if (!m_pBoxes) SetupBoxes();
+  m_iScanTime = std::numeric_limits<int>::min();
 }
- 
 
 void CDasherButtons::KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog) {
 
@@ -367,53 +61,33 @@ void CDasherButtons::KeyDown(int iTime, int iId, CDasherView *pView, CDasherMode
     }
   }
   else {
-    if(m_iStyle == 3) {
-      switch(iId) {
-      case 2:
-        if(m_iLastBox == 1)
-          pModel->ScheduleZoom((m_pBoxes[2].iBottom - m_pBoxes[2].iTop)/2, (m_pBoxes[2].iBottom + m_pBoxes[2].iTop)/2);
-        else
-          pModel->ScheduleZoom((m_pBoxes[0].iBottom - m_pBoxes[0].iTop)/2, (m_pBoxes[0].iBottom + m_pBoxes[0].iTop)/2);
-        m_iLastBox = 1;
-        break; 
-      case 3:
-      case 4:
-        if(m_iLastBox == 2)
-          pModel->ScheduleZoom((m_pBoxes[3].iBottom - m_pBoxes[3].iTop)/2, (m_pBoxes[3].iBottom + m_pBoxes[3].iTop)/2);
-        else
-          pModel->ScheduleZoom((m_pBoxes[1].iBottom - m_pBoxes[1].iTop)/2, (m_pBoxes[1].iBottom + m_pBoxes[1].iTop)/2);
-        m_iLastBox = 2;
-        break;
-      case 1:
-        pModel->ScheduleZoom((m_pBoxes[4].iBottom - m_pBoxes[4].iTop)/2, (m_pBoxes[4].iBottom + m_pBoxes[4].iTop)/2);
-        break;
-      }
-    }
-    else {
-      if(iId == 100) // Ignore mouse events
-        return;
-      if(iId == 1)
-        iActiveBox = m_iNumBoxes - 1;
-      else if(iId <= m_iNumBoxes) 
-        iActiveBox = iId-2;
-      else
-        iActiveBox = m_iNumBoxes-2;
-
-      m_iLastTime = iTime;
-
-      pModel->ScheduleZoom((m_pBoxes[iActiveBox].iBottom - m_pBoxes[iActiveBox].iTop)/2, (m_pBoxes[iActiveBox].iBottom + m_pBoxes[iActiveBox].iTop)/2);
-    }
+    DirectKeyDown(iTime, iId, pView, pModel, pUserLog);
   }
 
 }
 
-bool CDasherButtons::Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) {
-  bool m_bOldHighlight(m_bHighlight);
-  m_bHighlight = (Time - m_iLastTime < 200);
+void CDasherButtons::DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog) {
+  if(iId == 100) // Ignore mouse events
+  return;
+  if(iId == 1)
+  iActiveBox = m_iNumBoxes - 1;
+  else if(iId <= m_iNumBoxes) 
+  iActiveBox = iId-2;
+  else
+  iActiveBox = m_iNumBoxes-2;
+
+  pModel->ScheduleZoom((m_pBoxes[iActiveBox].iBottom - m_pBoxes[iActiveBox].iTop)/2, (m_pBoxes[iActiveBox].iBottom + m_pBoxes[iActiveBox].iTop)/2);
+}
 
-  if(m_bOldHighlight != m_bHighlight)
+bool CDasherButtons::Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) {
+  if (m_bMenu && GetLongParameter(LP_BUTTON_SCAN_TIME) &&
+      Time - m_iScanTime > GetLongParameter(LP_BUTTON_SCAN_TIME)) {
+    m_iScanTime = Time;
     m_bDecorationChanged = true;
-
+    ++iActiveBox;
+    if(iActiveBox == m_iNumBoxes)
+      iActiveBox = 0;
+  }
   // TODO: This is a bit of a hack to make joystick mode work
   myint iDasherX;
   myint iDasherY;
@@ -424,28 +98,6 @@ bool CDasherButtons::Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m
   return m_pDasherModel->NextScheduledStep(Time, pAdded, pNumDeleted);
 }
 
-void CDasherButtons::HandleEvent(Dasher::CEvent * pEvent) {
-  if(pEvent->m_iEventType == 1) {
-    Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
-
-    switch (pEvt->m_iParameter) {
-    case LP_B:
-    case LP_RIGHTZOOM:
-    case LP_R:
-      // Delibarate fallthrough
-      SetupBoxes();
-      break;
-    }
-  }
-}
-
-bool CDasherButtons::GetSettings(SModuleSettings **pSettings, int *iCount) {
-  *pSettings = sSettings;
-  *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
-
-  return true;
-};
-
 void CDasherButtons::NewDrawGoTo(CDasherView *pView, myint iDasherMin, myint iDasherMax, bool bActive) {
    myint iHeight(iDasherMax - iDasherMin);
 
diff --git a/Src/DasherCore/DasherButtons.h b/Src/DasherCore/DasherButtons.h
index d7c05f2..5573ddb 100644
--- a/Src/DasherCore/DasherButtons.h
+++ b/Src/DasherCore/DasherButtons.h
@@ -23,20 +23,16 @@ namespace Dasher {
 class CDasherButtons : public CInputFilter
 {
  public:
-  CDasherButtons(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, CDasherInterfaceBase *pInterface, int iNumBoxes, int iStyle, bool bMenu, ModuleID_t iID, const char *szName);
-
+  CDasherButtons(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, CDasherInterfaceBase *pInterface, bool bMenu, ModuleID_t iID, const char *szName);
 
   ~CDasherButtons();
 
-  virtual void HandleEvent(Dasher::CEvent * pEvent);
-  
-  bool DecorateView(CDasherView *pView);
+  virtual bool DecorateView(CDasherView *pView)=0;
   
   void KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
   bool Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
-
-  void SetupBoxes();
-
+  void Activate();
+  
   struct SBoxInfo {
     int iTop;
     int iBottom;
@@ -44,30 +40,16 @@ class CDasherButtons : public CInputFilter
     int iDisplayBottom;
   };
 
-  bool GetSettings(SModuleSettings **pSettings, int *iCount);
-
+ protected:
+  virtual void SetupBoxes()=0;
   void NewDrawGoTo(CDasherView *pView, myint iDasherMin, myint iDasherMax, bool bActive);
-
-    
- private:
-  CSettingsStore*  m_pSettingsStore;
-  
-  SBoxInfo *m_pBoxes;
-
-  int m_iNumBoxes;
-  int m_iStyle;
   bool m_bMenu;
-  
-  int iActiveBox;
-
-  int m_iLastBox;
-
-  int iTargetWidth;
-
-  int m_iLastTime;
-  bool m_bHighlight;
   bool m_bDecorationChanged;
-
+  SBoxInfo *m_pBoxes;
+  int m_iNumBoxes, iActiveBox;
+  int m_iScanTime;
+  
+  virtual void DirectKeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
 };
 }
 /// @}
diff --git a/Src/DasherCore/DasherInterfaceBase.cpp b/Src/DasherCore/DasherInterfaceBase.cpp
index 8544bc6..42ce44c 100644
--- a/Src/DasherCore/DasherInterfaceBase.cpp
+++ b/Src/DasherCore/DasherInterfaceBase.cpp
@@ -40,9 +40,12 @@
 #include "DasherGameMode.h"
 
 // Input filters
+#include "AlternatingDirectMode.h"
+#include "ButtonMode.h"
 #include "ClickFilter.h" 
+#include "CompassMode.h"
 #include "DefaultFilter.h"
-#include "DasherButtons.h"
+
 #include "EyetrackerFilter.h"
 #include "OneButtonFilter.h"
 #include "OneButtonDynamicFilter.h"
@@ -950,13 +953,12 @@ void CDasherInterfaceBase::CreateModules() {
   RegisterModule(new CTwoButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
   RegisterModule(new CTwoPushDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
   // TODO: specialist factory for button mode
-  RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 5, 1, true,8, _("Menu Mode")));
-  RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 0, false,10, _("Direct Mode")));
+  RegisterModule(new CButtonMode(m_pEventHandler, m_pSettingsStore, this, true, 8, _("Menu Mode")));
+  RegisterModule(new CButtonMode(m_pEventHandler, m_pSettingsStore, this, false,10, _("Direct Mode")));
   //  RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 0, false,11, "Buttons 3"));
-  RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 3, false,12, _("Alternating Direct Mode")));
-  RegisterModule(new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 2, false,13, _("Compass Mode")));
+  RegisterModule(new CAlternatingDirectMode(m_pEventHandler, m_pSettingsStore, this));
+  RegisterModule(new CCompassMode(m_pEventHandler, m_pSettingsStore, this));
   RegisterModule(new CStylusFilter(m_pEventHandler, m_pSettingsStore, this, 15, _("Stylus Control")));
-
 }
 
 void CDasherInterfaceBase::GetPermittedValues(int iParameter, std::vector<std::string> &vList) {
diff --git a/Src/DasherCore/Makefile.am b/Src/DasherCore/Makefile.am
index aa4fc5e..f447bb0 100644
--- a/Src/DasherCore/Makefile.am
+++ b/Src/DasherCore/Makefile.am
@@ -15,6 +15,8 @@ libdashercore_a_SOURCES = \
                 Alphabet/AlphabetMap.cpp \
                 Alphabet/AlphabetMap.h \
                 Alphabet/GroupInfo.h \
+		AlternatingDirectMode.cpp \
+		AlternatingDirectMode.h \
 		ActionButton.cpp \
 		ActionButton.h \
 		AlphabetManager.cpp \
@@ -25,6 +27,8 @@ libdashercore_a_SOURCES = \
 		AutoSpeedControl.h \
 		BasicLog.cpp \
 		BasicLog.h \
+		ButtonMode.cpp \
+		ButtonMode.h \
 		ButtonMultiPress.h \
 		ButtonMultiPress.cpp \
 		CircleStartHandler.cpp \
@@ -33,6 +37,8 @@ libdashercore_a_SOURCES = \
 		ClickFilter.h \
 		ColourIO.cpp \
 		ColourIO.h \
+		CompassMode.cpp \
+		CompassMode.h \
 		ControlManager.cpp \
 		ControlManager.h \
 		ConversionHelper.cpp \
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 90446b9..12a0c30 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -55,7 +55,7 @@ enum {
   LP_LM_UPDATE_EXCLUSION, LP_LM_ALPHA, LP_LM_BETA,
   LP_LM_MIXTURE, LP_MOUSE_POS_BOX, LP_NORMALIZATION, LP_LINE_WIDTH, 
   LP_LM_WORD_ALPHA, LP_USER_LOG_LEVEL_MASK, 
-  LP_ZOOMSTEPS, LP_B, LP_S, LP_Z, LP_R, LP_RIGHTZOOM,
+  LP_ZOOMSTEPS, LP_B, LP_S, LP_BUTTON_SCAN_TIME, LP_R, LP_RIGHTZOOM,
   LP_NODE_BUDGET,
   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, 
@@ -195,7 +195,7 @@ static lp_table longparamtable[] = {
   {LP_ZOOMSTEPS, "Zoomsteps", PERS, 32, "Integerised ratio of zoom size for click/button mode, denom 64."},
   {LP_B, "ButtonMenuBoxes", PERS, 4, "Number of boxes for button menu mode"},
   {LP_S, "ButtonMenuSafety", PERS, 25, "Safety parameter for button mode, in percent."},
-  {LP_Z, "ButtonMenuBackwardsBox", PERS, 1, "Number of back-up boxes for button menu mode"},
+  {LP_BUTTON_SCAN_TIME, "ButtonMenuScanTime", PERS, 0, "Scanning time in menu mode (0 = don't scan), in ms"},
   {LP_R, "ButtonModeNonuniformity", PERS, 0, "Button mode box non-uniformity"},
   {LP_RIGHTZOOM, "ButtonCompassModeRightZoom", PERS, 5120, "Zoomfactor (*1024) for compass mode"},
   {LP_NODE_BUDGET, "NodeBudget", PERS, 3000, "Target (min) number of node objects to maintain"},
diff --git a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
index dc98680..726ad71 100755
--- a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
+++ b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
@@ -355,6 +355,12 @@
 		19F8C7FA0C858E9900276B4F /* TrainingHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 19F8C7F80C858E9900276B4F /* TrainingHelper.h */; };
 		3306E0220FFD1CE60017324C /* PPMPYLanguageModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3306E0200FFD1CE60017324C /* PPMPYLanguageModel.cpp */; };
 		3306E0230FFD1CE60017324C /* PPMPYLanguageModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 3306E0210FFD1CE60017324C /* PPMPYLanguageModel.h */; };
+		33135351102C6D8E00E28220 /* AlternatingDirectMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33135349102C6D8E00E28220 /* AlternatingDirectMode.cpp */; };
+		33135352102C6D8E00E28220 /* AlternatingDirectMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3313534A102C6D8E00E28220 /* AlternatingDirectMode.h */; };
+		33135353102C6D8E00E28220 /* CompassMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3313534B102C6D8E00E28220 /* CompassMode.cpp */; };
+		33135354102C6D8E00E28220 /* CompassMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3313534C102C6D8E00E28220 /* CompassMode.h */; };
+		33135355102C6D8E00E28220 /* ButtonMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3313534D102C6D8E00E28220 /* ButtonMode.cpp */; };
+		33135356102C6D8E00E28220 /* ButtonMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3313534E102C6D8E00E28220 /* ButtonMode.h */; };
 		3306E1F70FFE6CAD0017324C /* Trainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3306E1F50FFE6CAD0017324C /* Trainer.cpp */; };
 		3306E1F80FFE6CAD0017324C /* Trainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3306E1F60FFE6CAD0017324C /* Trainer.h */; };
 		3306E33D0FFFB9880017324C /* MandarinAlphMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3306E33B0FFFB9880017324C /* MandarinAlphMgr.cpp */; };
@@ -776,6 +782,12 @@
 		29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
 		3306E0200FFD1CE60017324C /* PPMPYLanguageModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PPMPYLanguageModel.cpp; sourceTree = "<group>"; };
 		3306E0210FFD1CE60017324C /* PPMPYLanguageModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PPMPYLanguageModel.h; sourceTree = "<group>"; };
+		33135349102C6D8E00E28220 /* AlternatingDirectMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AlternatingDirectMode.cpp; sourceTree = "<group>"; };
+		3313534A102C6D8E00E28220 /* AlternatingDirectMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlternatingDirectMode.h; sourceTree = "<group>"; };
+		3313534B102C6D8E00E28220 /* CompassMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompassMode.cpp; sourceTree = "<group>"; };
+		3313534C102C6D8E00E28220 /* CompassMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompassMode.h; sourceTree = "<group>"; };
+		3313534D102C6D8E00E28220 /* ButtonMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ButtonMode.cpp; sourceTree = "<group>"; };
+		3313534E102C6D8E00E28220 /* ButtonMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButtonMode.h; sourceTree = "<group>"; };
 		3306E1F50FFE6CAD0017324C /* Trainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Trainer.cpp; sourceTree = "<group>"; };
 		3306E1F60FFE6CAD0017324C /* Trainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Trainer.h; sourceTree = "<group>"; };
 		3306E33B0FFFB9880017324C /* MandarinAlphMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MandarinAlphMgr.cpp; sourceTree = "<group>"; };
@@ -923,6 +935,13 @@
 		1948BDF40C226CFC001DFA32 /* DasherCore */ = {
 			isa = PBXGroup;
 			children = (
+				33135349102C6D8E00E28220 /* AlternatingDirectMode.cpp */,
+				3313534A102C6D8E00E28220 /* AlternatingDirectMode.h */,
+				3313534B102C6D8E00E28220 /* CompassMode.cpp */,
+				3313534C102C6D8E00E28220 /* CompassMode.h */,
+				3313534D102C6D8E00E28220 /* ButtonMode.cpp */,
+				3313534E102C6D8E00E28220 /* ButtonMode.h */,
+ 				336C2D12101A31EE00CCD70D /* NodeQueue.h */,
 				3306E1F50FFE6CAD0017324C /* Trainer.cpp */,
 				3306E1F60FFE6CAD0017324C /* Trainer.h */,
 				33FC93420FEFA2FB00A9F08D /* FrameRate.cpp */,
@@ -1516,6 +1535,10 @@
 				3306E0230FFD1CE60017324C /* PPMPYLanguageModel.h in Headers */,
 				3306E1F80FFE6CAD0017324C /* Trainer.h in Headers */,
 				3306E33E0FFFB9880017324C /* MandarinAlphMgr.h in Headers */,
+ 				336C2D13101A31EE00CCD70D /* NodeQueue.h in Headers */,
+				33135352102C6D8E00E28220 /* AlternatingDirectMode.h in Headers */,
+				33135354102C6D8E00E28220 /* CompassMode.h in Headers */,
+				33135356102C6D8E00E28220 /* ButtonMode.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1866,6 +1889,9 @@
 				3306E33D0FFFB9880017324C /* MandarinAlphMgr.cpp in Sources */,
 				335DB122100B3606006DB155 /* PinYinConversionHelper.cpp in Sources */,
 				335901B41009E5A900821255 /* ConversionHelper.cpp in Sources */,
+				33135351102C6D8E00E28220 /* AlternatingDirectMode.cpp in Sources */,
+				33135353102C6D8E00E28220 /* CompassMode.cpp in Sources */,
+				33135355102C6D8E00E28220 /* ButtonMode.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};



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