[dasher] iPhone: rewrite input filters + devices



commit 330d097fcd5a4e41d9b29c4dccde16f2054d8ad2
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Thu Dec 2 15:34:48 2010 +0000

    iPhone: rewrite input filters + devices
    
    Specialized touch & tilt filters (only), as per android
        -tilt switchable 1d/2d, hold-to-go/tap-to-start
        -touch uses 'undoubled' coords for tap
    Inputs obtain coords from EAGLView as reqd, rather than having pushed at them.
    GUI uses checkbox not highlight; inline tilt 'calibrate' button;
    module settings combines input filter, device, and iphone-specific opts

 Src/iPhone/Classes/CDasherInterfaceBridge.h  |   13 +-
 Src/iPhone/Classes/CDasherInterfaceBridge.mm |   49 ++----
 Src/iPhone/Classes/CDasherScreenBridge.h     |    2 +
 Src/iPhone/Classes/CDasherScreenBridge.mm    |    7 +
 Src/iPhone/Classes/DasherScreenCallbacks.h   |    2 +
 Src/iPhone/Classes/EAGLView.h                |    4 +
 Src/iPhone/Classes/EAGLView.mm               |   23 +--
 Src/iPhone/Classes/IPhoneFilters.cpp         |   81 ---------
 Src/iPhone/Classes/IPhoneFilters.h           |   64 ++++++--
 Src/iPhone/Classes/IPhoneFilters.mm          |  163 ++++++++++++++++++
 Src/iPhone/Classes/IPhoneInputs.h            |   58 ++-----
 Src/iPhone/Classes/IPhoneInputs.mm           |   62 +++++--
 Src/iPhone/Classes/InputMethodSelector.h     |    1 -
 Src/iPhone/Classes/InputMethodSelector.mm    |  234 +++++++++++++++-----------
 Src/iPhone/Classes/ParametersController.h    |    9 +-
 Src/iPhone/Classes/ParametersController.mm   |   55 ++++--
 Src/iPhone/Classes/PlainDragFilter.cpp       |   38 ----
 Src/iPhone/Classes/PlainDragFilter.h         |   29 ----
 Src/iPhone/Dasher.xcodeproj/project.pbxproj  |   14 +-
 19 files changed, 507 insertions(+), 401 deletions(-)
---
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.h b/Src/iPhone/Classes/CDasherInterfaceBridge.h
index 52f6f32..515da97 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.h
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.h
@@ -10,7 +10,6 @@
 #import "IPhoneInputs.h"
 #import "IPhoneFilters.h"
 #import "DefaultFilter.h"
-#import "PlainDragFilter.h"
 #import "StylusFilter.h"
 #import "Vec3.h"
 
@@ -23,7 +22,7 @@
 /// Implements the necessary abstract methods by bridging them into Objective C
 /// and sending messages onto the DasherAppDelegate.
 
-class CDasherInterfaceBridge : public CDasherInterfaceBase {
+class CDasherInterfaceBridge : public Dasher::CDasherInterfaceBase {
   
 public:
   
@@ -39,12 +38,10 @@ public:
   CDasherInterfaceBridge(DasherAppDelegate *aDasherApp);
   ~CDasherInterfaceBridge();
   
-  void ChangeScreen(CDasherScreen *NewScreen);
   //redefinitions to make public....
   void OnUIRealised();
   void NewFrame(unsigned long iTime, bool bForceRedraw);
   
-  void NotifyTouch(screenint x, screenint y);
   void SetTiltAxes(Vec3 main, float off, Vec3 slow, float off2);
   bool SupportsClipboard() {return true;}
   void CopyToClipboard(const std::string &strText);
@@ -72,12 +69,10 @@ private:
   
   DasherAppDelegate *dasherApp;   // objc counterpart
   
-  CPlainDragFilter *m_pPlainDragFilter;
-  CIPhone1DFilter *m_pOneDFilter;
-  CIPhonePolarFilter *m_pPolarFilter;
+  CIPhoneTiltFilter *m_pTiltFilter;
+  CIPhoneTouchFilter *m_pTouchFilter;
 	
-  CMixedInput *m_pMixDevice, *m_pReverseMix;
   CIPhoneMouseInput *m_pMouseDevice;
   CIPhoneTiltInput *m_pTiltDevice;
-	
+	UndoubledTouch *m_pUndoubledTouch;
 };
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.mm b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
index 378a779..9271040 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.mm
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
@@ -15,7 +15,9 @@
 #import "CalibrationController.h"
 #import "ControlManager.h"
 #import "../Common/Common.h"
-
+#import "ButtonMode.h"
+#import "TwoButtonDynamicFilter.h"
+#import "TwoPushDynamicFilter.h"
 #import <iostream>
 
 #import <fcntl.h>
@@ -31,42 +33,33 @@ CDasherInterfaceBridge::CDasherInterfaceBridge(DasherAppDelegate *aDasherApp) :
 
 void CDasherInterfaceBridge::CreateModules() {
 	//create the default set...a good idea?!?!
-	CDasherInterfaceBase::CreateModules();
 
+  RegisterModule(m_pUndoubledTouch = new UndoubledTouch(m_pEventHandler, m_pSettingsStore));
 	RegisterModule(m_pMouseDevice = 
 				new CIPhoneMouseInput(m_pEventHandler, m_pSettingsStore));
 	RegisterModule(m_pTiltDevice = 
 				new CIPhoneTiltInput(m_pEventHandler, m_pSettingsStore));
-	RegisterModule(m_pMixDevice =
-				   new CMixedInput(m_pEventHandler, m_pSettingsStore, m_pMouseDevice, m_pTiltDevice, MIXED_INPUT));
-	RegisterModule(m_pReverseMix =
-				   new CMixedInput(m_pEventHandler, m_pSettingsStore, m_pTiltDevice, m_pMouseDevice, REVERSE_MIX));
-	RegisterModule(m_pPlainDragFilter = new CPlainDragFilter(m_pEventHandler, m_pSettingsStore, this, 9, "Hold-down filter"));
-	RegisterModule(m_pOneDFilter =
-				   new CIPhone1DFilter(m_pEventHandler, m_pSettingsStore, this, 16));
-	RegisterModule(m_pPolarFilter = 
-				   new CIPhonePolarFilter(m_pEventHandler, m_pSettingsStore, this, 17));
-  CDasherModule *stylus = GetModuleByName("Stylus Control");
-  DASHER_ASSERT(stylus && stylus->GetType() == InputMethod);
-	SetDefaultInputMethod(static_cast<CInputFilter *>(stylus));
-	SetDefaultInputDevice(m_pMouseDevice);
+  SetDefaultInputDevice(m_pMouseDevice);
+                 
+  RegisterModule(new CButtonMode(m_pEventHandler, m_pSettingsStore, this, true, 9, "Menu Mode"));
+  RegisterModule(new CButtonMode(m_pEventHandler, m_pSettingsStore, this, false, 8, "Direct Mode"));
+  RegisterModule(new CTwoButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
+  RegisterModule(new CTwoPushDynamicFilter(m_pEventHandler, m_pSettingsStore, this));
+  
+	RegisterModule(m_pTiltFilter =
+				   new CIPhoneTiltFilter(m_pEventHandler, m_pSettingsStore, this, 16, m_pMouseDevice));
+	RegisterModule(m_pTouchFilter = 
+				   new CIPhoneTouchFilter(m_pEventHandler, m_pSettingsStore, this, 17, m_pUndoubledTouch, m_pTiltDevice));
+	SetDefaultInputMethod(m_pTouchFilter);
 }
 	
 CDasherInterfaceBridge::~CDasherInterfaceBridge() {
-  if (m_pMouseDevice)
-	delete m_pMouseDevice;
-  if (m_pTiltDevice)
+  delete m_pMouseDevice;
 	delete m_pTiltDevice;
-  if (m_pMixDevice)
-	delete m_pMixDevice;
+  delete m_pUndoubledTouch;
   //(ACL)registered input filters should be automatically free'd by the module mgr?
 }
 
-void CDasherInterfaceBridge::NotifyTouch(screenint x, screenint y)
-{
-	m_pMouseDevice->NotifyTouch(x, y);
-}
-
 void CDasherInterfaceBridge::SetTiltAxes(Vec3 main, float off, Vec3 slow, float off2)
 {
 	m_pTiltDevice->SetAxes(main, off, slow, off2);
@@ -78,12 +71,6 @@ void CDasherInterfaceBridge::SetupUI() {
 
 void CDasherInterfaceBridge::OnUIRealised() {CDasherInterfaceBase::OnUIRealised();}
 
-void CDasherInterfaceBridge::ChangeScreen(CDasherScreen *pScreen) {
-  CDasherInterfaceBase::ChangeScreen(pScreen);
-  m_pTiltDevice->SetScreenBounds(pScreen->GetWidth(), pScreen->GetHeight());
-  m_pMouseDevice->SetScreenBounds(pScreen->GetWidth(), pScreen->GetHeight());
-}
-
 void CDasherInterfaceBridge::SetupPaths() {
   NSString *systemDir = [NSString stringWithFormat:@"%@/", [[NSBundle mainBundle] bundlePath]];
   NSString *userDir = [NSString stringWithFormat:@"%@/", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
diff --git a/Src/iPhone/Classes/CDasherScreenBridge.h b/Src/iPhone/Classes/CDasherScreenBridge.h
index cd30023..9f89bd7 100644
--- a/Src/iPhone/Classes/CDasherScreenBridge.h
+++ b/Src/iPhone/Classes/CDasherScreenBridge.h
@@ -23,6 +23,8 @@ public:
   CDasherScreenBridge(id<DasherScreenCallbacks> dv);
   ~CDasherScreenBridge();
   
+  bool GetTouchCoords(screenint &iX, screenint &iY);
+  
   // CDasherScreen methods
     
   ///
diff --git a/Src/iPhone/Classes/CDasherScreenBridge.mm b/Src/iPhone/Classes/CDasherScreenBridge.mm
index 033d2e5..86bf200 100644
--- a/Src/iPhone/Classes/CDasherScreenBridge.mm
+++ b/Src/iPhone/Classes/CDasherScreenBridge.mm
@@ -19,6 +19,13 @@ CDasherScreenBridge::~CDasherScreenBridge() {
   dasherView = nil; // didn't retain it, so don't release it.
 }
 
+bool CDasherScreenBridge::GetTouchCoords(screenint &iX, screenint &iY) {
+  CGPoint p = dasherView.lastTouchCoords;
+  if (p.x==-1) return false;
+  iX=p.x; iY=p.y;
+  return true;
+}
+
 void CDasherScreenBridge::Blank() {
   [dasherView blankCallback];
 }
diff --git a/Src/iPhone/Classes/DasherScreenCallbacks.h b/Src/iPhone/Classes/DasherScreenCallbacks.h
index fbc7163..362dd72 100644
--- a/Src/iPhone/Classes/DasherScreenCallbacks.h
+++ b/Src/iPhone/Classes/DasherScreenCallbacks.h
@@ -24,4 +24,6 @@ typedef struct {
 -(void)setColourSchemeWithColourTable:(colour_t *)colourTable;
 -(int)boundsWidth;
 -(int)boundsHeight;
+
+ property (readonly,assign) CGPoint lastTouchCoords;
 @end
\ No newline at end of file
diff --git a/Src/iPhone/Classes/EAGLView.h b/Src/iPhone/Classes/EAGLView.h
index af848b1..832b0ba 100644
--- a/Src/iPhone/Classes/EAGLView.h
+++ b/Src/iPhone/Classes/EAGLView.h
@@ -37,8 +37,12 @@ Note that setting the view non-opaque will only work if the EAGL surface has an
 	GLuint mouseTex, boxesTex;
 	GLshort rectcoords[8];
 	GLfloat texcoords[8];
+  
+  CGPoint lastTouchCoords;
 }
 
+ property (readonly,assign) CGPoint lastTouchCoords;
+
 - (void)startAnimation;
 - (void)stopAnimation;
 - (void)drawView;
diff --git a/Src/iPhone/Classes/EAGLView.mm b/Src/iPhone/Classes/EAGLView.mm
index f0be848..e8e4a95 100644
--- a/Src/iPhone/Classes/EAGLView.mm
+++ b/Src/iPhone/Classes/EAGLView.mm
@@ -16,8 +16,6 @@
 // A class extension to declare private methods
 @interface EAGLView ()
 
- property (nonatomic, retain) EAGLContext *context;
-
 - (BOOL) createFramebuffer;
 - (void) destroyFramebuffer;
 
@@ -26,8 +24,7 @@
 
 @implementation EAGLView
 
- synthesize context;
-
+ synthesize lastTouchCoords;
 
 // You must implement this method
 + (Class)layerClass {
@@ -69,28 +66,24 @@
 
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 	NSAssert([touches count] == 1, @"Multitouch?!");
-	CGPoint p = [((UITouch *)[touches anyObject]) locationInView:self];
+	lastTouchCoords = [((UITouch *)[touches anyObject]) locationInView:self];
 	NSAssert(!anyDown,@"Touches began when already in progress - multitouch enabled?!?!\n");
 	anyDown = YES;
-	dasherApp.dasherInterface->NotifyTouch(p.x,p.y);
-	dasherApp.dasherInterface->KeyDown(get_time(), 100, true, p.x, p.y);
+	dasherApp.dasherInterface->KeyDown(get_time(), 100, true, lastTouchCoords.x, lastTouchCoords.y);
 }
 
 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
-	CGPoint p = [[touches anyObject] locationInView:self];
-	dasherApp.dasherInterface->NotifyTouch(p.x,p.y);
+	lastTouchCoords = [[touches anyObject] locationInView:self];
 }
 
 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 	NSAssert([touches count] == 1, @"Multitouch?!");
 	NSAssert(anyDown,@"Touches ended when not in progress - multitouch enabled?!?!\n");
-	UITouch *touch = [touches anyObject];
-	CGPoint p = [touch locationInView:self];
-	dasherApp.dasherInterface->NotifyTouch(p.x,p.y);
-	anyDown = NO;
-	dasherApp.dasherInterface->KeyUp(get_time(), 100, true, p.x, p.y);
+	lastTouchCoords = [(UITouch *)[touches anyObject] locationInView:self];
+	dasherApp.dasherInterface->KeyUp(get_time(), 100, true, lastTouchCoords.x, lastTouchCoords.y);
   //finished dealing with touch-up event. Finger is now officially off the screen...
-  dasherApp.dasherInterface->NotifyTouch(-1, -1);
+  lastTouchCoords.x = lastTouchCoords.y = -1;
+  anyDown = NO;
 }
 
 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
diff --git a/Src/iPhone/Classes/IPhoneFilters.h b/Src/iPhone/Classes/IPhoneFilters.h
index 439f0a8..f98225a 100644
--- a/Src/iPhone/Classes/IPhoneFilters.h
+++ b/Src/iPhone/Classes/IPhoneFilters.h
@@ -8,30 +8,68 @@
  */
 
 #include "OneDimensionalFilter.h"
+#include "StylusFilter.h"
+#include "IPhoneInputs.h"
 
 using namespace Dasher;
 
-#define ONE_D_FILTER "IPhone1D Filter"
-#define POLAR_FILTER "Polar Filter"
-class CIPhone1DFilter : public COneDimensionalFilter {
-public:
-	CIPhone1DFilter(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID);
+ class NSUserDefaultsObserver;
 
-	virtual bool Timer(int iTime, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted, CExpansionPolicy **pol);
+class IPhonePrefsObserver {
+public:
+  virtual void iPhonePrefsChanged(NSString *key)=0;
 protected:
-	virtual void ApplyTransform(myint &iDasherX, myint &iDasherY);
+  void ObserveKeys(NSString *key,...);
+  ~IPhonePrefsObserver();
 private:
-	int m_iSlow;
-	double m_dRad;
+  NSUserDefaultsObserver *obsvr;
 };
+#ifndef __IPHONE_FILTERS_MM__
+extern NSString *HOLD_TO_GO;
+extern NSString *TILT_1D;
+extern NSString *TILT_USE_TOUCH_X;
+extern NSString *TOUCH_USE_TILT_X;
+#endif
 
-class CIPhonePolarFilter : public COneDimensionalFilter {
+#define TILT_FILTER "IPhone Tilt Filter"
+#define TOUCH_FILTER "IPhone Touch Filter"
+class CIPhoneTiltFilter : public COneDimensionalFilter, private IPhonePrefsObserver {
 public:
-	CIPhonePolarFilter(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID);
-	
+	CIPhoneTiltFilter(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, CDasherInput *pTouch);
+  ///override to enable hold-to-go
 	virtual void KeyDown(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CUserLogBase *pUserLog);
+  ///override to enable hold-to-go
 	virtual void KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel);
+
+  ///respond to BP_DASHER_PAUSED by engaging wakelock (if !hold-to-go)
+  virtual void HandleEvent(CEvent *pEvent);
+  void iPhonePrefsChanged(NSString *key);
+  bool supportsPause();
 protected:
-	virtual void ApplyTransform(myint &iDasherX, myint &iDasherY);
+  ///Override to choose whether to apply 1D transform or not, and to get X coord from touch if appropriate
+	virtual void ApplyTransform(myint &iDasherX, myint &iDasherY, CDasherView *pView);
+  ///Never apply offset (just eyetracker remapping!) - otherwise would be done when operating in 2d mode
+  virtual void ApplyOffset(myint &iDasherX, myint &iDasherY);
+
+private:
+  CDasherInput *m_pTouch;
+  bool bHoldToGo, bUseTouchX, bTilt1D;
 };
+
+class CIPhoneTouchFilter : public CStylusFilter, private IPhonePrefsObserver {
+public:
+	CIPhoneTouchFilter(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, UndoubledTouch *pUndoubledTouch, CIPhoneTiltInput *pTilt);
+	
+	virtual void KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel);
+  
+  void ApplyTransform(myint &iDasherX, myint &iDasherY, CDasherView *pView);
+  void Activate();
+  void Deactivate();
+  void iPhonePrefsChanged(NSString *key);
+private:
+  UndoubledTouch *m_pUndoubledTouch;
+  CIPhoneTiltInput *m_pTilt;
+  bool bUseTiltX;
+};
+
 /// @}
\ No newline at end of file
diff --git a/Src/iPhone/Classes/IPhoneFilters.mm b/Src/iPhone/Classes/IPhoneFilters.mm
new file mode 100644
index 0000000..be386e9
--- /dev/null
+++ b/Src/iPhone/Classes/IPhoneFilters.mm
@@ -0,0 +1,163 @@
+/*
+ *  IPhoneFilters.cpp
+ *  Dasher
+ *
+ *  Created by Alan Lawrence on 29/04/2009.
+ *  Copyright 2009 Cavendish Laboratory. All rights reserved.
+ *
+ */
+#define __IPHONE_FILTERS_MM__
+#include "IPhoneFilters.h"
+
+#include "../Common/Common.h"
+#include "DasherInterfaceBase.h"
+#include "Event.h"
+#include "CDasherScreenBridge.h"
+#include <iostream>
+
+NSString *HOLD_TO_GO=@"HoldToGo";
+NSString *TILT_1D=@"Tilt1D";
+NSString * TILT_USE_TOUCH_X=@"UseTouchX";
+NSString * TOUCH_USE_TILT_X=@"UseTiltX";
+
+ interface NSUserDefaultsObserver : NSObject {
+  IPhonePrefsObserver *po;
+}
+-(id)initForPrefsObserver:(IPhonePrefsObserver *)po;
+-(void)stopObserving;
+-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
+ end
+
+ implementation NSUserDefaultsObserver
+
+-(id)initForPrefsObserver:(IPhonePrefsObserver *)_po {
+  if (self = [super init]) {
+    self->po = _po;
+  }
+  return self;
+}
+
+-(void)stopObserving {po=nil; [self autorelease];}
+
+-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+  NSAssert(object == [NSUserDefaults standardUserDefaults],@"Only observing user defaults?");
+  if (po) po->iPhonePrefsChanged(keyPath);
+  else [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:keyPath];
+}
+ end
+
+void IPhonePrefsObserver::ObserveKeys(NSString *key,...) {
+  va_list args;
+  va_start(args, key);
+  
+  obsvr = [[NSUserDefaultsObserver alloc] initForPrefsObserver:this];
+  while (key) {
+    [[NSUserDefaults standardUserDefaults] addObserver:obsvr forKeyPath:key options:0 context:nil];
+    iPhonePrefsChanged(key);
+    key=va_arg(args, NSString *);
+  }
+  va_end(args);
+}
+
+IPhonePrefsObserver::~IPhonePrefsObserver() {
+  [obsvr stopObserving];
+  [obsvr release];
+}
+
+CIPhoneTiltFilter::CIPhoneTiltFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, CDasherInput *pTouch)
+: COneDimensionalFilter(pEventHandler, pSettingsStore, pInterface, iID, TILT_FILTER), m_pTouch(pTouch) {
+  ObserveKeys(HOLD_TO_GO, TILT_USE_TOUCH_X, TILT_1D, nil);
+};
+			
+void CIPhoneTiltFilter::ApplyTransform(myint &iDasherX, myint &iDasherY, CDasherView *pView) {
+  if (bHoldToGo && bUseTouchX) {
+    myint temp;
+    m_pTouch->GetDasherCoords(iDasherX,temp,pView);
+  }
+  if (bTilt1D) {
+    COneDimensionalFilter::ApplyTransform(iDasherX, iDasherY, pView);
+    //that skips CDefaultFilter::ApplyTransform => no LP_TARGET_OFFSET/BP_AUTO_CALIBRATE or BP_REMAP_XTREME
+  } else {
+    CDefaultFilter::ApplyTransform(iDasherX, iDasherY, pView);
+  }
+}
+
+void CIPhoneTiltFilter::KeyDown(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CUserLogBase *pUserLog) {
+	if(iId == 100 && bHoldToGo)
+		m_pInterface->Unpause(iTime);
+  else COneDimensionalFilter::KeyDown(iTime, iId, pView, pInput, pModel, pUserLog);
+}
+
+void CIPhoneTiltFilter::KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
+	if(iId == 100 && bHoldToGo)
+		m_pInterface->Stop();
+  else COneDimensionalFilter::KeyUp(iTime, iId, pView, pInput, pModel);
+}
+
+bool CIPhoneTiltFilter::supportsPause() {
+  return !bHoldToGo;
+}
+
+void CIPhoneTiltFilter::ApplyOffset(myint &iDasherX, myint &iDasherY) {
+  //Do not apply LP_TARGET_OFFSET, or BP_AUTO_CALIBRATE
+}
+
+void CIPhoneTiltFilter::HandleEvent(CEvent *pEvent) {
+  if (pEvent->m_iEventType == EV_PARAM_NOTIFY
+      && static_cast<CParameterNotificationEvent *>(pEvent)->m_iParameter==BP_DASHER_PAUSED
+      && m_pInterface->GetActiveInputMethod()==this) {
+    [UIApplication sharedApplication].idleTimerDisabled=(!GetBoolParameter(BP_DASHER_PAUSED) && !bHoldToGo);
+  }
+}
+
+void CIPhoneTiltFilter::iPhonePrefsChanged(NSString *key) {
+  bool val = [[NSUserDefaults standardUserDefaults] boolForKey:key];
+  if ([key isEqualToString:HOLD_TO_GO])
+    bHoldToGo = val;
+  else if ([key isEqualToString:TILT_USE_TOUCH_X])
+    bUseTouchX = val;
+  else if ([key isEqualToString:TILT_1D])
+    bTilt1D = val;
+  //Hmmm, do we need to do anything _now_?
+}
+
+CIPhoneTouchFilter::CIPhoneTouchFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, UndoubledTouch *pUndoubledTouch, CIPhoneTiltInput *pTilt)
+: CStylusFilter(pEventHandler, pSettingsStore, pInterface, iID, TOUCH_FILTER), m_pUndoubledTouch(pUndoubledTouch), m_pTilt(pTilt) {
+  ObserveKeys(TOUCH_USE_TILT_X,nil);
+  
+};
+
+void CIPhoneTouchFilter::iPhonePrefsChanged(NSString *key) {
+  if ([key isEqualToString:TOUCH_USE_TILT_X]) {
+    if (m_pInterface->GetActiveInputMethod()==this) {
+      //current=new value of preference, should be different from what we have atm...
+      // (exception is at construction time - 
+      DASHER_ASSERT([[NSUserDefaults standardUserDefaults] boolForKey:TOUCH_USE_TILT_X] ^ bUseTiltX);
+      if (bUseTiltX)
+        m_pTilt->Deactivate(); //setting was on, so is being turned off
+      else 
+        m_pTilt->Activate();
+    }
+    bUseTiltX = [[NSUserDefaults standardUserDefaults] boolForKey:TOUCH_USE_TILT_X];
+  }
+}
+
+void CIPhoneTouchFilter::Activate() {
+  if (bUseTiltX) m_pTilt->Activate();
+}
+
+void CIPhoneTouchFilter::Deactivate() {
+  if (bUseTiltX) m_pTilt->Deactivate();
+}
+
+void CIPhoneTouchFilter::KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
+  CStylusFilter::KeyUp(iTime, iId, pView, m_pUndoubledTouch, pModel);
+}
+
+void CIPhoneTouchFilter::ApplyTransform(myint &iDasherX, myint &iDasherY, CDasherView *pView) {
+  if (bUseTiltX) {
+    myint temp;
+    m_pTilt->GetDasherCoords(iDasherX,temp,pView);
+  }
+  CStylusFilter::ApplyTransform(iDasherX, iDasherY, pView);
+}
\ No newline at end of file
diff --git a/Src/iPhone/Classes/IPhoneInputs.h b/Src/iPhone/Classes/IPhoneInputs.h
index ef00b1b..e1093af 100644
--- a/Src/iPhone/Classes/IPhoneInputs.h
+++ b/Src/iPhone/Classes/IPhoneInputs.h
@@ -14,32 +14,13 @@
 #import <deque>
 #import "Vec3.h"
 //A bit odd I admit, but we use the same string as is the default input device in Parameters.h...
+#define UNDOUBLED_TOUCH "Undoubled Touch"
 #define TOUCH_INPUT "Mouse Input"
 #define TILT_INPUT "Tilt Input"
-#define MIXED_INPUT "Mixed Inputs"
-#define REVERSE_MIX "Reverse Mix"
-namespace Dasher {
 
-class CIPhoneInput : public CScreenCoordInput {
-public:
-	CIPhoneInput(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, const char *name)
-	: CScreenCoordInput(pEventHandler, pSettingsStore, 0, 0, name) {};
-	void SetScreenBounds(int maxX, int maxY) {this->maxX=maxX; this->maxY=maxY;}
-		
-	virtual bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
-    if (m_iX==-1) return false;
-    iX = m_iX;
-		iY = m_iY;
-		
-		return true;
-	};
-	
-protected:
-	myint m_iX, m_iY;
-  int maxX, maxY;
-};
+namespace Dasher {
 
-class CIPhoneTiltInput : public CIPhoneInput {
+class CIPhoneTiltInput : public CScreenCoordInput {
 public:
 	CIPhoneTiltInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore); 
 	~CIPhoneTiltInput();
@@ -56,8 +37,11 @@ public:
 	
 	void Activate();
 	void Deactivate();
-	
+
+  bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
 private:
+  screenint m_iX, m_iY;
+  int maxX, maxY;
 	Vec3 main, slow;
 	float offset, slow_off;
 	SBTree *xTilts, *yTilts;
@@ -65,27 +49,19 @@ private:
 	id<UIAccelerometerDelegate> deleg;
 };
 
-class CIPhoneMouseInput : public CIPhoneInput {
+class UndoubledTouch : public CScreenCoordInput {
 public:
-	CIPhoneMouseInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
-
-	void NotifyTouch(screenint _iX, screenint _iY) {
-    if (GetBoolParameter(BP_DOUBLE_X)) _iX = std::min(2*_iX, maxX);
-		m_iX = _iX;
-		m_iY = _iY;
-	};
+  UndoubledTouch(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore);
+  bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
+protected:
+  UndoubledTouch(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, ModuleID_t iId, const char *szName);
 };
 
-class CMixedInput : public CDasherInput {
+class CIPhoneMouseInput : public UndoubledTouch {
 public:
-	CMixedInput(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore,
-				CDasherInput *pXinput, CDasherInput *pYinput, const char *name)
-	: CDasherInput(pEventHandler, pSettingsStore, 0, 0, name), m_pXinput(pXinput), m_pYinput(pYinput) {};
-	
-	virtual bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
-	void Activate();
-	void Deactivate();
-private:
-	CDasherInput *m_pXinput, *m_pYinput;
+	CIPhoneMouseInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
+  
+  bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
 };
 }
+
diff --git a/Src/iPhone/Classes/IPhoneInputs.mm b/Src/iPhone/Classes/IPhoneInputs.mm
index e42e564..51991fb 100644
--- a/Src/iPhone/Classes/IPhoneInputs.mm
+++ b/Src/iPhone/Classes/IPhoneInputs.mm
@@ -35,7 +35,7 @@ using namespace Dasher;
 @end
 
 CIPhoneTiltInput::CIPhoneTiltInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore) 
-	: CIPhoneInput(pEventHandler, pSettingsStore, TILT_INPUT) {
+	: CScreenCoordInput(pEventHandler, pSettingsStore, 8, TILT_INPUT) {
 	deleg = [[Accel alloc] initWithInput:this];	
 	xTilts = NULL;
 };
@@ -80,32 +80,60 @@ void CIPhoneTiltInput::NotifyTilt(float fx, float fy, float fz) {
 
 void CIPhoneTiltInput::Activate() {
   [[DasherAppDelegate theApp] setLandscapeSupported:NO];
-  [UIApplication sharedApplication].idleTimerDisabled = YES;
   UIAccelerometer*  theAccelerometer = [UIAccelerometer sharedAccelerometer];
   theAccelerometer.updateInterval = 0.01; //in secs
   theAccelerometer.delegate = deleg;
 }
 void CIPhoneTiltInput::Deactivate() {
   [[DasherAppDelegate theApp] setLandscapeSupported:YES];
-  [UIApplication sharedApplication].idleTimerDisabled = NO;
   [UIAccelerometer sharedAccelerometer].delegate = nil;
 }
 
-CIPhoneMouseInput::CIPhoneMouseInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore) 
-	: CIPhoneInput(pEventHandler, pSettingsStore, TOUCH_INPUT) {
-};
+bool CIPhoneTiltInput::GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
+  CDasherScreen *pScreen(pView->Screen());
+  maxX = pScreen->GetWidth();
+  maxY = pScreen->GetHeight();
+  
+  //could check we're active, but not bothering for now...
+  iX=m_iX; iY=m_iY;
+  return true;
+}
 
-bool CMixedInput::GetScreenCoords(screenint &iX, screenint &iY,CDasherView *pView) {
-  screenint temp;
-	if (!m_pYinput->GetScreenCoords(temp,iY,pView)) return false;
-  //got y; x coord stored into temp is not needed
-	return m_pXinput->GetScreenCoords(iX, temp,pView);
-};
+UndoubledTouch::UndoubledTouch(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore) : CScreenCoordInput(pEventHandler, pSettingsStore, 7, UNDOUBLED_TOUCH) {
+}
+
+UndoubledTouch::UndoubledTouch(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, ModuleID_t iId, const char *szName) : CScreenCoordInput(pEventHandler, pSettingsStore, iId, szName) {
+}
 
-void CMixedInput::Activate() {
-  m_pYinput->Activate(); m_pXinput->Activate();
+bool UndoubledTouch::GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
+  CDasherScreenBridge *sb(static_cast<CDasherScreenBridge *>(pView->Screen()));
+  return sb->GetTouchCoords(iX, iY);
 }
 
-void CMixedInput::Deactivate() {
-  m_pYinput->Deactivate(); m_pXinput->Deactivate();
-}
\ No newline at end of file
+
+CIPhoneMouseInput::CIPhoneMouseInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore) 
+	: UndoubledTouch(pEventHandler, pSettingsStore, 9, TOUCH_INPUT) {
+};
+
+bool CIPhoneMouseInput::GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
+  if (!UndoubledTouch::GetScreenCoords(iX,iY, pView)) return false;
+  CDasherScreen *scr(pView->Screen());
+  //double x/y
+  if (GetBoolParameter(BP_DOUBLE_X)) {
+    switch (GetLongParameter(LP_REAL_ORIENTATION)) {
+      case Opts::LeftToRight:
+        iX=min(iX*2, scr->GetWidth());
+        break;
+      case Opts::RightToLeft:
+        iX=max(iX*2-scr->GetWidth(), 0);
+        break;
+      case Opts::TopToBottom:
+        iY=min(iY*2, scr->GetHeight());
+        break;
+      case Opts::BottomToTop:
+        iY=max(iY*2 - scr->GetHeight(), 0);
+        break;
+    }
+  }
+  return true;
+}
diff --git a/Src/iPhone/Classes/InputMethodSelector.h b/Src/iPhone/Classes/InputMethodSelector.h
index 775be68..6242599 100644
--- a/Src/iPhone/Classes/InputMethodSelector.h
+++ b/Src/iPhone/Classes/InputMethodSelector.h
@@ -11,7 +11,6 @@
 
 @interface InputMethodSelector : UITableViewController {
 	NSIndexPath *selectedPath;
-  UIView *calibButton;
 }
 
 - (id)init;
diff --git a/Src/iPhone/Classes/InputMethodSelector.mm b/Src/iPhone/Classes/InputMethodSelector.mm
index 4a87746..b9b86a6 100644
--- a/Src/iPhone/Classes/InputMethodSelector.mm
+++ b/Src/iPhone/Classes/InputMethodSelector.mm
@@ -13,35 +13,35 @@
 #import "DasherAppDelegate.h"
 #import "CalibrationController.h"
 #import "ParametersController.h"
+#import "IPhoneFilters.h"
 
 typedef struct __FILTER_DESC__ {
 	NSString *title;
 	NSString *subTitle;
 	const char *deviceName;
 	const char *filterName;
+  ///Null-terminated list of NSUserDefaults keys for extra non-Core settings
+  NSString **iPhoneOpts;
 } SFilterDesc;
 
-SFilterDesc asTouchFilters[] = {
-	{@"Hybrid Mode", @"Tap or Hold", TOUCH_INPUT, "Stylus Control"},
-  {@"Boxes", @"Tap box to select", TOUCH_INPUT, "Direct Mode"},
-};
+NSString *touchSettings[] = {TOUCH_USE_TILT_X, NULL};
+NSString *tiltSettings[] = {HOLD_TO_GO, TILT_USE_TOUCH_X, TILT_1D, NULL};
 
-SFilterDesc asDynamicFilters[] = {
-  {@"Scanning", @"Tap screen when box highlighted", TOUCH_INPUT, "Menu Mode"},
-  {@"One Button Mode", @"Tap screen when cursor near", TOUCH_INPUT, "Static One Button Mode"},
-	{@"Dynamic 1B Mode", @"Tap anywhere - twice", TOUCH_INPUT, "Two-push Dynamic Mode (New One Button)"},
-  {@"Dynamic 2B Mode", @"Tap Top or Bottom", TOUCH_INPUT, "Two Button Dynamic Mode"},
+NSString *calibBtn=@"Calibrate...";//pointer equality used to mark cell for special-casing in cellForRowAtIndexPath: below
+
+SFilterDesc asNormalFilters[] = {
+	{@"Touch Control", @"Tap or Hold", TOUCH_INPUT, TOUCH_FILTER, touchSettings},
+  {@"Tilt Control", calibBtn, TILT_INPUT, TILT_FILTER, tiltSettings},
 };
 
-SFilterDesc asTiltFilters[] = {
-	{@"Full 2D",@"hold-to-go", TILT_INPUT, "Hold-down filter"},
-	{@"Single-axis",@"with slowdown & tap-to-start", TILT_INPUT, ONE_D_FILTER},
+SFilterDesc asBoxFilters[] = {
+  {@"Direct Mode", @"Tap box to select", TOUCH_INPUT, "Direct Mode", NULL},
+  {@"Scanning", @"Tap screen when box highlighted", TOUCH_INPUT, "Menu Mode", NULL},
 };
 
-SFilterDesc asMixedFilters[] = {
-	{@"(X,Y)", @"by (touch,tilt)", MIXED_INPUT, "Hold-down filter"},
-	{@"1D-mode", @"tilt for direction, X-touch for speed", MIXED_INPUT, POLAR_FILTER},
-	{@"(Y,X)", @"by (touch,tilt)", REVERSE_MIX, "Stylus Control"},
+SFilterDesc asDynamicFilters[] = {
+	{@"Dynamic 1B Mode", @"Tap anywhere - twice", TOUCH_INPUT, "Two-push Dynamic Mode (New One Button)", NULL},
+  {@"Dynamic 2B Mode", @"Tap Top or Bottom", TOUCH_INPUT, "Two Button Dynamic Mode", NULL},
 };
 
 typedef struct __SECTION_DESC__ {
@@ -51,23 +51,28 @@ typedef struct __SECTION_DESC__ {
 } SSectionDesc;
 	
 SSectionDesc allMeths[] = {
-{@"Touch Control", asTouchFilters, sizeof(asTouchFilters) / sizeof(asTouchFilters[0])},
-{@"Button Modes", asDynamicFilters, sizeof(asDynamicFilters) / sizeof(asDynamicFilters[0])},
-{@"Tilt Control", asTiltFilters, sizeof(asTiltFilters) / sizeof(asTiltFilters[0])},
-{@"Combined Touch & Tilt", asMixedFilters, sizeof(asMixedFilters) / sizeof(asMixedFilters[0])},
+{@"Normal Steering", asNormalFilters, sizeof(asNormalFilters) / sizeof(asNormalFilters[0])},
+{@"Box Modes", asBoxFilters, sizeof(asBoxFilters) / sizeof(asBoxFilters[0])},
+{@"Dynamic Modes", asDynamicFilters, sizeof(asDynamicFilters) / sizeof(asDynamicFilters[0])},
 };
 
 int numSections = sizeof(allMeths) / sizeof(allMeths[0]);
 
+ interface ExtraParametersController : ParametersController {
+  SModuleSettings *m_pSettings2;
+  int m_iCount2;
+  NSString **iPhonePrefKeys;
+}
+-(id)initForFilter:(SFilterDesc *)filtr;
+ end
+
+
 @interface InputMethodSelector ()
- property (retain) NSIndexPath *selectedPath;
-- (void)doSelect;
+-(UITableViewCellAccessoryType)accessoryTypeForFilter:(SFilterDesc *)filter;
 @end
 
 @implementation InputMethodSelector
 
- synthesize selectedPath;
-
 - (id)init {
   if (self = [super initWithStyle:UITableViewStyleGrouped]) {
 		self.tabBarItem.title = @"Control";
@@ -122,91 +127,73 @@ int numSections = sizeof(allMeths) / sizeof(allMeths[0]);
 #pragma mark Table view methods
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-  return numSections + 1; //add 1 for calibrate button
+  return numSections;
 }
 
-
 // Customize the number of rows in the table view.
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
-  return (section==numSections) ? 0 : allMeths[section].numFilters;
+  return allMeths[section].numFilters;
 }
 
 // Customize the appearance of table view cells.
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
-	DasherAppDelegate *app = [DasherAppDelegate theApp];
-    static NSString *CellIdentifier = @"Cell";
-    
-    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
-	UILabel *subText;
-	if (cell)
-		subText = [cell viewWithTag:1];
-	else {
-        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
-		subText = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
-		subText.tag = 1;
-		[cell addSubview:subText];
-		//cell.autoresizesSubviews = YES; //ineffective even if done
-		[subText setAdjustsFontSizeToFitWidth:YES];
+
+  SFilterDesc *filter = &allMeths[ [indexPath section] ].filters[ [indexPath row] ];
+  UITableViewCell *cell;
+  
+  if (filter->subTitle==calibBtn) {
+    static NSString *CalibCellId = @"CalibCell";
+    cell = [tableView dequeueReusableCellWithIdentifier:CalibCellId];
+    if (!cell) {
+      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CalibCellId] autorelease];
+      
+      UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+      [btn setTitle:calibBtn forState:UIControlStateNormal];
+      CGSize textSize = [calibBtn sizeWithFont:btn.titleLabel.font];
+      btn.frame = CGRectMake(140.0,9.0,textSize.width+10,textSize.height+6);
+      [btn addTarget:self action:@selector(calibrate) forControlEvents:UIControlEventTouchUpInside];
+      
+      [cell.contentView addSubview:btn];
     }
+  } else {
+    static NSString *CellId = @"Cell";
+    
+    cell = [tableView dequeueReusableCellWithIdentifier:CellId];
+    if (!cell)
+      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellId] autorelease];
+    cell.detailTextLabel.text = filter->subTitle;
+  }
 	
 	// Set up the cell...
-	SFilterDesc *filter = &allMeths[ [indexPath section] ].filters[ [indexPath row] ];
-	if (filter->deviceName == app.dasherInterface->GetStringParameter(SP_INPUT_DEVICE)
-		&& filter->filterName == app.dasherInterface->GetStringParameter(SP_INPUT_FILTER)
-    && (!selectedPath || [indexPath compare:selectedPath])) {
-      self.selectedPath = indexPath;
-      [self performSelectorOnMainThread:@selector(doSelect) withObject:nil waitUntilDone:NO];
-	}
+  cell.textLabel.text = filter->title;
+	CDasherInterfaceBase *intf = [DasherAppDelegate theApp].dasherInterface;
+  if (filter->deviceName == intf->GetStringParameter(SP_INPUT_DEVICE)
+      && filter->filterName == intf->GetStringParameter(SP_INPUT_FILTER)) {
+    //filter is currently selected...
+    DASHER_ASSERT(selectedPath==nil || [selectedPath isEqual:indexPath]);
+    selectedPath = indexPath;
+    cell.accessoryType = UITableViewCellAccessoryCheckmark;
+  } else {
+    cell.accessoryType = [self accessoryTypeForFilter:filter];
+  }
 	
-  UIButton *btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
-  cell.accessoryView = btn;
-  btn.tag = (int)filter;
-  [btn addTarget:self action:@selector(settings:) forControlEvents:UIControlEventTouchUpInside];
-  
-	cell.text = filter->title;
-	CGSize titleSize = [filter->title sizeWithFont:cell.font];
-	subText.frame = CGRectMake(titleSize.width + 30.0, 5.0, 245.0 - titleSize.width, 34.0);
-
-	subText.text = filter->subTitle;
+  return cell;
+}
 
-    return cell;
+-(UITableViewCellAccessoryType)accessoryTypeForFilter:(SFilterDesc *)filter {
+  SModuleSettings *sets; int count;
+  CDasherInterfaceBase *intf = [DasherAppDelegate theApp].dasherInterface;
+  if (intf->GetModuleSettings(filter->filterName, &sets, &count))
+    if (count>0) return UITableViewCellAccessoryDisclosureIndicator;
+  if (intf->GetModuleSettings(filter->deviceName, &sets, &count))
+    if (count>0) return UITableViewCellAccessoryDisclosureIndicator;
+  if (filter->iPhoneOpts && *(filter->iPhoneOpts))
+    return UITableViewCellAccessoryDisclosureIndicator;
+  return UITableViewCellAccessoryNone;
 }
 
 - (void)moduleSettingsDone {
   [self.navigationController popViewControllerAnimated:YES];
-  [self doSelect];
-}
-
-- (void)doSelect {
-  [self.tableView selectRowAtIndexPath:selectedPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
-}
-
-- (void)settings:(id)button {
-  UIButton *btn = (UIButton *)button;
-  SFilterDesc *desc = (SFilterDesc *)btn.tag;
-  CDasherModule *mod = [DasherAppDelegate theApp].dasherInterface->GetModuleByName(desc->filterName);
-  SModuleSettings *settings=NULL; int count=0;
-  if (mod->GetSettings(&settings, &count)) {
-    ParametersController *params = [[[ParametersController alloc] initWithTitle:NSStringFromStdString(desc->filterName) Settings:settings Count:count] autorelease];
-    [params setTarget:self Selector:@selector(moduleSettingsDone)];
-    [self.navigationController pushViewController:params animated:YES];
-  }
-}
-
-- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
-  if (section < numSections) return nil; //no special view => default, using title method (below)
-  if (!calibButton) {
-    calibButton = [[UIView alloc] initWithFrame:CGRectZero];
-    calibButton.backgroundColor = [UIColor clearColor];
-    calibButton.opaque = NO;
-    UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
-    [btn setTitle:@"Calibrate Tilting..." forState:UIControlStateNormal];
-    btn.font = [UIFont boldSystemFontOfSize:18.0];
-    btn.frame = CGRectMake(9.0,2.0,302.0,[self tableView:tableView heightForHeaderInSection:section]-4.0);
-    [btn addTarget:self action:@selector(calibrate) forControlEvents:UIControlEventTouchUpInside];
-    [calibButton addSubview:btn];
-  }
-  return calibButton;
 }
 
 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
@@ -223,12 +210,22 @@ int numSections = sizeof(allMeths) / sizeof(allMeths[0]);
 }
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-	//[tableView deselectRowAtIndexPath:indexPath animated:NO];
-	self.selectedPath = indexPath;
-	DasherAppDelegate *app = [DasherAppDelegate theApp];
+	[tableView deselectRowAtIndexPath:indexPath animated:NO];
+  //self->selectedPath is the PREVIOUSLY-SELECTED filter. Take away the checkmark...
+  [tableView cellForRowAtIndexPath:selectedPath].accessoryType = [self accessoryTypeForFilter: &allMeths[[selectedPath section]].filters[[selectedPath row]]];
+  //now give the NEWLY-SELECTED filter a checkmark
+  [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
+
+  //and record it as selected...
+	self->selectedPath = indexPath;
+	CDasherInterfaceBase *intf = [DasherAppDelegate theApp].dasherInterface;
 	SFilterDesc *filter = &allMeths[ [indexPath section] ].filters[ [indexPath row] ];
-	app.dasherInterface->SetStringParameter(SP_INPUT_DEVICE, filter->deviceName);
-	app.dasherInterface->SetStringParameter(SP_INPUT_FILTER, filter->filterName);
+	intf->SetStringParameter(SP_INPUT_DEVICE, filter->deviceName);
+	intf->SetStringParameter(SP_INPUT_FILTER, filter->filterName);
+  
+  ParametersController *params = [[[ExtraParametersController alloc] initForFilter:filter] autorelease];
+  [params setTarget:self Selector:@selector(moduleSettingsDone)];
+  [self.navigationController pushViewController:params animated:YES];
 }
 
 /*
@@ -275,6 +272,49 @@ int numSections = sizeof(allMeths) / sizeof(allMeths[0]);
     [super dealloc];
 }
 
+ end
+
 
+ implementation ExtraParametersController
+
+-(id)initForFilter:(SFilterDesc *)filter {
+  CDasherInterfaceBase *intf=[DasherAppDelegate theApp].dasherInterface;
+  SModuleSettings *settings; int count;
+  if (!intf->GetModuleSettings(filter->filterName, &settings, &count)) {
+    settings=NULL; count=0;
+  }
+  if (self = [super initWithTitle:NSStringFromStdString(filter->filterName) Settings:settings Count:count]) {
+    if (!intf->GetModuleSettings(filter->deviceName, &m_pSettings2, &m_iCount2)) {
+      m_pSettings2=NULL; m_iCount2=0;
+    }
+    iPhonePrefKeys = filter->iPhoneOpts;
+  }
+  return self;
+}
+
+-(int)layoutOptionsOn:(UIView *)view startingAtY:(int)y {
+  if (m_iCount)
+    y=[super layoutOptionsOn:view startingAtY:y];
+  else if (m_iCount2==0 && !iPhonePrefKeys)
+    return [self makeNoSettingsLabelOnView:view atY:y];
+  y=[self layoutModuleSettings:m_pSettings2 count:m_iCount2 onView:view startingAtY:y];
+  //finally, iPhone-specific keys...
+  NSUserDefaults *ud=[NSUserDefaults standardUserDefaults];
+  if (iPhonePrefKeys) {
+    for (NSString **key=iPhonePrefKeys; *key; key++) {
+      UISwitch *sw=[self makeSwitch:*key onView:view atY:&y];
+      sw.tag=(int)*key;
+      sw.on = [ud boolForKey:*key];
+      [sw addTarget:self action:@selector(boolUserDefChanged:) forControlEvents:UIControlEventValueChanged];
+    }
+  }
+  return y;
+}
+
+-(void)boolUserDefChanged:(id)uiswitch {
+  UISwitch *sw=(UISwitch *)uiswitch;
+  NSString *key = (NSString *)sw.tag;
+  [[NSUserDefaults standardUserDefaults] setBool:sw.on forKey:key];
+}
 @end
 
diff --git a/Src/iPhone/Classes/ParametersController.h b/Src/iPhone/Classes/ParametersController.h
index 1fb7a18..b7b4790 100644
--- a/Src/iPhone/Classes/ParametersController.h
+++ b/Src/iPhone/Classes/ParametersController.h
@@ -10,11 +10,16 @@
 #import "ModuleSettings.h"
 
 @interface ParametersController : UIViewController {
-  SModuleSettings *settings;
-  int count;
+  SModuleSettings *m_pSettings;
+  int m_iCount;
 }
 
 -(id)initWithTitle:(NSString *)title Settings:(SModuleSettings *)settings Count:(int)count;
 -(void)setTarget:(id)target Selector:(SEL)selector;
 
+///These are meant to be protected...
+-(int)layoutOptionsOn:(UIView *)view startingAtY:(int)y;
+-(int)layoutModuleSettings:(SModuleSettings *)settings count:(int)count onView:(UIView *)view startingAtY:(int)y;
+-(UISwitch *)makeSwitch:(NSString *)title onView:(UIView *)view atY:(int *)pY;
+-(int)makeNoSettingsLabelOnView:(UIView *)view atY:(int)y;
 @end
diff --git a/Src/iPhone/Classes/ParametersController.mm b/Src/iPhone/Classes/ParametersController.mm
index fce10d9..8c4ba4c 100644
--- a/Src/iPhone/Classes/ParametersController.mm
+++ b/Src/iPhone/Classes/ParametersController.mm
@@ -20,12 +20,12 @@ using namespace Dasher;
 
 @implementation ParametersController
 
--(id)initWithTitle:(NSString *)title Settings:(SModuleSettings *)_settings Count:(int)_count {
+-(id)initWithTitle:(NSString *)title Settings:(SModuleSettings *)settings Count:(int)count {
   if (self = [super init]) {
     self.title=title;
     self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleDone target:nil action:nil];
-    settings = _settings;
-    count = _count;
+    m_pSettings = settings;
+    m_iCount = count;
   }
   return self;
 }
@@ -37,27 +37,31 @@ using namespace Dasher;
 }
 
 - (void)loadView {
-  CDasherInterfaceBridge *intf = [DasherAppDelegate theApp].dasherInterface;
-  
   UIScrollView *view = [[[UIScrollView alloc] init] autorelease];
   self.view = view;
   view.backgroundColor = [UIColor whiteColor];
-    
-  int y=15;
+  
+  int y=[self layoutOptionsOn:view startingAtY:15];
+  [view setContentSize:CGSizeMake(320.0,y-15)];
+}
+
+-(int)layoutOptionsOn:(UIView *)view startingAtY:(int)y {
+  if (m_iCount==0) return [self makeNoSettingsLabelOnView:view atY:y];
+  return [self layoutModuleSettings:m_pSettings count:m_iCount onView:view startingAtY:y];
+}
+
+-(int)layoutModuleSettings:(SModuleSettings *)settings count:(int)count onView:(UIView *)view startingAtY:(int)y {
+  CDasherInterfaceBridge *intf = [DasherAppDelegate theApp].dasherInterface;
   for (int i=0; i<count; i++) {
     if (settings[i].iType == T_BOOL) {
-      UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10.0, y, 190.0, 20.0)] autorelease];
-      label.text = NSStringFromStdString(intf->GetSettingsStore()->GetParameterName(settings[i].iParameter));
-      UISwitch *sw = [[[UISwitch alloc] initWithFrame:CGRectMake(210.0, y, 100.0, 20.0)] autorelease];
-      [view addSubview:label];
-      [view addSubview:sw];
-      sw.on = intf->GetBoolParameter(sw.tag = settings[i].iParameter);
+      UISwitch *sw=[self makeSwitch:NSStringFromStdString(intf->GetSettingsStore()->GetParameterName(settings[i].iParameter)) onView:view atY:&y];
+      sw.tag = settings[i].iParameter;
+      sw.on = intf->GetBoolParameter(settings[i].iParameter);
       [sw addTarget:self action:@selector(boolParamChanged:) forControlEvents:UIControlEventValueChanged];
-      y += 50;
     } else if (settings[i].iType == T_LONG) {
       UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10.0, y, 300.0, 20.0)] autorelease];
       UISlider *slider = [[[UISlider alloc] initWithFrame:CGRectMake(10.0, y+20, 300.0, 20.0)] autorelease];
-      slider.tag = (int)label; label.tag=i;
+      slider.tag = (int)label; label.tag=(int)&settings[i];
       slider.minimumValue = settings[i].iMin; slider.maximumValue = settings[i].iMax;
       slider.value = intf->GetLongParameter(settings[i].iParameter);
       [slider addTarget:self action:@selector(longParamChanged:) forControlEvents:UIControlEventValueChanged];
@@ -66,7 +70,24 @@ using namespace Dasher;
       y += 70;
     }
   }
-  [view setContentSize:CGSizeMake(320.0,y-15)];
+  return y;
+}
+
+-(UISwitch *)makeSwitch:(NSString *)title onView:(UIView *)view atY:(int *)pY {
+  UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10.0, *pY, 190.0, 20.0)] autorelease];
+  label.text = title;
+  UISwitch *sw = [[[UISwitch alloc] initWithFrame:CGRectMake(210.0, *pY, 100.0, 20.0)] autorelease];
+  [view addSubview:label];
+  [view addSubview:sw];
+  *pY += 50;
+  return sw;
+}
+
+-(int)makeNoSettingsLabelOnView:(UIView *)view atY:(int)y {
+  UILabel *label=[[[UILabel alloc] initWithFrame:CGRectMake(10.0, y, 300.0, 20.0)] autorelease];
+  label.text=@"No Settings";
+  [view addSubview:label];
+  return y+50;
 }
 
 -(void)boolParamChanged:(id)uiswitch {
@@ -78,7 +99,7 @@ using namespace Dasher;
   CDasherInterfaceBridge *intf = [DasherAppDelegate theApp].dasherInterface;
   UISlider *slider = (UISlider *)uislider;
   UILabel *label = (UILabel *)slider.tag;
-  SModuleSettings *setting = &settings[label.tag];
+  SModuleSettings *setting = (SModuleSettings *)label.tag;
   long val = slider.value;
   if (!label.text || val != intf->GetLongParameter(setting->iParameter)) {
     intf->SetLongParameter(setting->iParameter, val);
diff --git a/Src/iPhone/Dasher.xcodeproj/project.pbxproj b/Src/iPhone/Dasher.xcodeproj/project.pbxproj
index c3b107e..fca3b5c 100755
--- a/Src/iPhone/Dasher.xcodeproj/project.pbxproj
+++ b/Src/iPhone/Dasher.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
 		331F29C80F7A9C270044EB9C /* alphabet.welsh.xml in Resources */ = {isa = PBXBuildFile; fileRef = 331F293C0F7A9C270044EB9C /* alphabet.welsh.xml */; };
 		331F29CA0F7A9C270044EB9C /* alphabet.xsl in Resources */ = {isa = PBXBuildFile; fileRef = 331F293E0F7A9C270044EB9C /* alphabet.xsl */; };
 		331F29CB0F7A9C270044EB9C /* alphabet.xsl.good in Resources */ = {isa = PBXBuildFile; fileRef = 331F293F0F7A9C270044EB9C /* alphabet.xsl.good */; };
+		3324F491129C119C00EE6A22 /* IPhoneFilters.mm in Sources */ = {isa = PBXBuildFile; fileRef = 332F32CC103C8D6E008448D7 /* IPhoneFilters.mm */; };
 		332BCAB40F71621400585DBD /* expat.dsp in Resources */ = {isa = PBXBuildFile; fileRef = 332BCA9A0F71621400585DBD /* expat.dsp */; };
 		332BCAB50F71621400585DBD /* expat_static.dsp in Resources */ = {isa = PBXBuildFile; fileRef = 332BCA9D0F71621400585DBD /* expat_static.dsp */; };
 		332BCAB60F71621400585DBD /* expatw.dsp in Resources */ = {isa = PBXBuildFile; fileRef = 332BCA9E0F71621400585DBD /* expatw.dsp */; };
@@ -60,8 +61,6 @@
 		332F32B4103C8A1E008448D7 /* ConversionHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 332F32AC103C8A1E008448D7 /* ConversionHelper.cpp */; };
 		332F32B5103C8A1E008448D7 /* MandarinAlphMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 332F32AD103C8A1E008448D7 /* MandarinAlphMgr.cpp */; };
 		332F32B6103C8A1E008448D7 /* Trainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 332F32AF103C8A1E008448D7 /* Trainer.cpp */; };
-		332F32CE103C8D6E008448D7 /* IPhoneFilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 332F32CC103C8D6E008448D7 /* IPhoneFilters.cpp */; };
-		332F331E103C8EFD008448D7 /* PlainDragFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3376EBFD0FC15A7300C4DC9F /* PlainDragFilter.cpp */; };
 		332F34A3103D8F54008448D7 /* colour.blue.xml in Resources */ = {isa = PBXBuildFile; fileRef = 332F3496103D8F54008448D7 /* colour.blue.xml */; };
 		332F34A4103D8F54008448D7 /* colour.dtd in Resources */ = {isa = PBXBuildFile; fileRef = 332F3497103D8F54008448D7 /* colour.dtd */; };
 		332F34A5103D8F54008448D7 /* colour.euroasian.xml in Resources */ = {isa = PBXBuildFile; fileRef = 332F3498103D8F54008448D7 /* colour.euroasian.xml */; };
@@ -368,7 +367,7 @@
 		332F32AE103C8A1E008448D7 /* MandarinAlphMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MandarinAlphMgr.h; sourceTree = "<group>"; };
 		332F32AF103C8A1E008448D7 /* Trainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Trainer.cpp; sourceTree = "<group>"; };
 		332F32B0103C8A1E008448D7 /* Trainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Trainer.h; sourceTree = "<group>"; };
-		332F32CC103C8D6E008448D7 /* IPhoneFilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IPhoneFilters.cpp; sourceTree = "<group>"; };
+		332F32CC103C8D6E008448D7 /* IPhoneFilters.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = IPhoneFilters.mm; sourceTree = "<group>"; };
 		332F32CD103C8D6E008448D7 /* IPhoneFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPhoneFilters.h; sourceTree = "<group>"; };
 		332F3496103D8F54008448D7 /* colour.blue.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = colour.blue.xml; sourceTree = "<group>"; };
 		332F3497103D8F54008448D7 /* colour.dtd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = colour.dtd; sourceTree = "<group>"; };
@@ -704,8 +703,6 @@
 		337691700F9CE8630083FEB2 /* StringParamController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringParamController.mm; sourceTree = "<group>"; };
 		337691840F9CEFC70083FEB2 /* InputMethodSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputMethodSelector.h; sourceTree = "<group>"; };
 		337691850F9CEFC70083FEB2 /* InputMethodSelector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMethodSelector.mm; sourceTree = "<group>"; };
-		3376EBFC0FC15A7300C4DC9F /* PlainDragFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlainDragFilter.h; sourceTree = "<group>"; };
-		3376EBFD0FC15A7300C4DC9F /* PlainDragFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlainDragFilter.cpp; sourceTree = "<group>"; };
 		337ECC1910DD5E0700D0C6A5 /* ExpansionPolicy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExpansionPolicy.cpp; sourceTree = "<group>"; };
 		337ECC1A10DD5E0700D0C6A5 /* ExpansionPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExpansionPolicy.h; sourceTree = "<group>"; };
 		339F8A310FF5088000282847 /* CalibrationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CalibrationController.h; sourceTree = "<group>"; };
@@ -780,7 +777,7 @@
 				333B5D4E100F5A93002041C8 /* TextView.mm */,
 				3330BD420FA885FD0035E952 /* IPhoneInputs.h */,
 				3330BDC50FA8BE360035E952 /* IPhoneInputs.mm */,
-				332F32CC103C8D6E008448D7 /* IPhoneFilters.cpp */,
+				332F32CC103C8D6E008448D7 /* IPhoneFilters.mm */,
 				332F32CD103C8D6E008448D7 /* IPhoneFilters.h */,
 				3330B80B0FA1FABB0035E952 /* Vec3.cpp */,
 				3330B7D00FA1198E0035E952 /* Vec3.h */,
@@ -805,8 +802,6 @@
 				3354AF4611ADBAFD006CF570 /* Actions.h */,
 				3354AF4711ADBAFD006CF570 /* Actions.mm */,
 				33EB48400F72A5680048E7C2 /* DasherScreenCallbacks.h */,
-				3376EBFC0FC15A7300C4DC9F /* PlainDragFilter.h */,
-				3376EBFD0FC15A7300C4DC9F /* PlainDragFilter.cpp */,
 				339F8A310FF5088000282847 /* CalibrationController.h */,
 				339F8A320FF5088000282847 /* CalibrationController.mm */,
 			);
@@ -1646,8 +1641,6 @@
 				332F32B4103C8A1E008448D7 /* ConversionHelper.cpp in Sources */,
 				332F32B5103C8A1E008448D7 /* MandarinAlphMgr.cpp in Sources */,
 				332F32B6103C8A1E008448D7 /* Trainer.cpp in Sources */,
-				332F32CE103C8D6E008448D7 /* IPhoneFilters.cpp in Sources */,
-				332F331E103C8EFD008448D7 /* PlainDragFilter.cpp in Sources */,
 				339F8A330FF5088000282847 /* CalibrationController.mm in Sources */,
 				333B5D4F100F5A93002041C8 /* TextView.mm in Sources */,
 				3334D4DF1014745F0077948A /* MiscSettings.mm in Sources */,
@@ -1728,6 +1721,7 @@
 				333F707A11A8AA66002E2BDF /* usenglish.c in Sources */,
 				3354AF4811ADBAFD006CF570 /* Actions.mm in Sources */,
 				337472D3121A976B001A858C /* AlphInfo.cpp in Sources */,
+				3324F491129C119C00EE6A22 /* IPhoneFilters.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};



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