[dasher] See previous commit log entry...



commit 8b0d7a0bc21da6359ceab087f600da226d08537f
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Jun 17 17:53:24 2009 +0100

    See previous commit log entry...

 ChangeLog                                   |    3 +
 Src/DasherCore/DynamicFilter.cpp            |  103 +++++++-------------------
 Src/DasherCore/DynamicFilter.h              |   40 +++++++----
 Src/DasherCore/Makefile.am                  |    2 +
 Src/DasherCore/OneButtonDynamicFilter.cpp   |    2 +-
 Src/DasherCore/OneButtonDynamicFilter.h     |    4 +-
 Src/DasherCore/TwoButtonDynamicFilter.cpp   |   16 ++--
 Src/DasherCore/TwoButtonDynamicFilter.h     |    6 +-
 Src/MacOSX/Dasher.xcodeproj/project.pbxproj |    8 ++
 9 files changed, 80 insertions(+), 104 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index a37b1ad..a9990af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,9 @@
 2009-06-17  Alan Lawrence <acl33 inf phy cam ac uk>
 
 	* Remove CDasherModel parameter to inputfilter constructor.
+	* Make DynamicFilter's states observable and changeable by
+	subclasses, and create a CButtonMultiPress subclass for
+	detection of multiple button-presses.
 
 2009-06-16  Alan Lawrence <acl33 inf phy cam ac uk>
 
diff --git a/Src/DasherCore/DynamicFilter.cpp b/Src/DasherCore/DynamicFilter.cpp
index 9f207af..8a7563c 100644
--- a/Src/DasherCore/DynamicFilter.cpp
+++ b/Src/DasherCore/DynamicFilter.cpp
@@ -22,25 +22,22 @@
 #include "DynamicFilter.h"
 
 CDynamicFilter::CDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, int iType, const char *szName)
-  : CInputFilter(pEventHandler, pSettingsStore, pInterface, iID, iType, szName) { 
-  m_iState = 0;
-    m_bDecorationChanged = true;
+  : CInputFilter(pEventHandler, pSettingsStore, pInterface, iID, iType, szName) {
+  m_bDecorationChanged = true;
   m_bKeyDown = false;
+  pause();
 }
 
-bool CDynamicFilter::Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) {
-  if(m_bKeyDown && !m_bKeyHandled && ((Time - m_iKeyDownTime) > GetLongParameter(LP_HOLD_TIME))) {
-    Event(Time, m_iHeldId, 1, m_pDasherModel, m_pUserLog);
+bool CDynamicFilter::Timer(int iTime, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted)
+{
+  if(m_bKeyDown && !m_bKeyHandled && ((iTime - m_iKeyDownTime) > GetLongParameter(LP_HOLD_TIME))) {
+    Event(iTime, m_iHeldId, 1, m_pDasherModel, m_pUserLog);
     m_bKeyHandled = true;
-    return true;
+    //return true; //ACL although that's what old DynamicFilter did, surely we should progress normally?
   }
-
-  if(m_iState == 2) //backing off
-    return m_pDasherModel->OneStepTowards(41943,2048, Time, pAdded, pNumDeleted);
-  else if(m_iState == 1)
-    return TimerImpl(Time, m_pDasherView, m_pDasherModel, pAdded, pNumDeleted);
-  else
-    return false;
+  if (isPaused()) return false;
+  if (isReversing()) return m_pDasherModel->OneStepTowards(41943,2048, iTime, pAdded, pNumDeleted);
+  return TimerImpl(iTime, m_pDasherView, m_pDasherModel, pAdded, pNumDeleted);
 }
 
 void CDynamicFilter::KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog) {
@@ -54,38 +51,12 @@ void CDynamicFilter::KeyDown(int iTime, int iId, CDasherView *pView, CDasherMode
     return;
 
   // Pass the basic key down event to the handler
-  // TODO: bit of a hack here
-
-  int iPreviousState = m_iState;
   Event(iTime, iId, 0, pModel, pUserLog);
-  bool bStateChanged = m_iState != iPreviousState;
     
   // Store the key down time so that long presses can be determined
   // TODO: This is going to cause problems if multiple buttons are
   // held down at once
   m_iKeyDownTime = iTime;
-  
-  // Check for multiple clicks
-  if(iId == m_iQueueId) {
-    while((m_deQueueTimes.size() > 0) && (iTime - m_deQueueTimes.front()) > GetLongParameter(LP_MULTIPRESS_TIME))
-      m_deQueueTimes.pop_front();
-
-    if(m_deQueueTimes.size() == static_cast<unsigned int>(GetLongParameter(LP_MULTIPRESS_COUNT) - 1)) { 
-      Event(iTime, iId, 2, pModel, pUserLog);
-      m_deQueueTimes.clear();
-    }
-    else {
-      if(!bStateChanged)
-	m_deQueueTimes.push_back(iTime);
-    }
-  }
-  else {
-    if(!bStateChanged) {
-      m_deQueueTimes.clear();
-      m_deQueueTimes.push_back(iTime);
-      m_iQueueId = iId;
-    }
-  }
 
   m_iHeldId = iId;
   m_bKeyDown = true;
@@ -93,44 +64,44 @@ void CDynamicFilter::KeyDown(int iTime, int iId, CDasherView *pView, CDasherMode
 }
 
 void CDynamicFilter::KeyUp(int iTime, int iId, CDasherView *pView, CDasherModel *pModel) {
-  m_bKeyDown = false;
+  if (iId == m_iHeldId) m_bKeyDown = false;
 }
 
 void CDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog) {
-  // Types:
+  // Types known at this point in inheritance hierarchy:
   // 0 = ordinary click
   // 1 = long click
-  // 2 = multiple click
-  
-  if(iType == 2)
-    RevertPresses(GetLongParameter(LP_MULTIPRESS_COUNT) - 1);
   
   // First sanity check - if Dasher is paused then jump to the
   // appropriate state
   if(GetBoolParameter(BP_DASHER_PAUSED))
-    m_iState = 0;
+    pause();
 
   // TODO: Check that state diagram implemented here is what we
   // decided upon
 
   // What happens next depends on the state:
-  switch(m_iState) {
-  case 0: // Any button when paused causes a restart
+  if (isPaused()) {
+  //Any button causes a restart
     if(pUserLog)
       pUserLog->KeyDown(iButton, iType, 1);
     m_pInterface->Unpause(iTime);
     SetBoolParameter(BP_DELAY_VIEW, true);
-    m_iState = 1;
-    m_deQueueTimes.clear();
-    break;
-  case 1:
+    run(0);
+  } else if (isReversing()) {
+    if(pUserLog)
+      pUserLog->KeyDown(iButton, iType, 2);
+    
+    pause();
+    m_pInterface->PauseAt(0, 0);
+  } else {
+    //examine event/button-press type
     switch(iType) {
     case 0:
       if((iButton == 0) || (iButton == 100)) {
 	if(pUserLog)
 	  pUserLog->KeyDown(iButton, iType, 2);
-	m_iState = 0;
-	m_deQueueTimes.clear();
+	pause();
 	SetBoolParameter(BP_DELAY_VIEW, false);
 	m_pInterface->PauseAt(0, 0);
       }
@@ -138,8 +109,7 @@ void CDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pMod
 	if(pUserLog)
 	  pUserLog->KeyDown(iButton, iType, 6);
 	SetBoolParameter(BP_DELAY_VIEW, false);
-	m_iState = 2;
-	m_deQueueTimes.clear();
+	reverse();
       }
       else {
 	ActionButton(iTime, iButton, iType, pModel, pUserLog);
@@ -151,8 +121,7 @@ void CDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pMod
 	if(pUserLog)
 	  pUserLog->KeyDown(iButton, iType, 6);
 	SetBoolParameter(BP_DELAY_VIEW, false);
-	m_iState = 2;
-	m_deQueueTimes.clear();
+	reverse(); //reversing!
        }
       else {
 	if(pUserLog)
@@ -160,21 +129,5 @@ void CDynamicFilter::Event(int iTime, int iButton, int iType, CDasherModel *pMod
       }
       break;
     }
-    break;
-  case 2:
-    if(pUserLog)
-      pUserLog->KeyDown(iButton, iType, 2);
-    
-    m_iState = 0;
-    m_deQueueTimes.clear();
-    m_pInterface->PauseAt(0, 0);
-    break;
   }
-
-  m_iLastButton = iButton;
-}
-
-bool CDynamicFilter::GetMinWidth(int &iMinWidth) {
-  iMinWidth = 1024;
-  return true;
 }
diff --git a/Src/DasherCore/DynamicFilter.h b/Src/DasherCore/DynamicFilter.h
index 7656047..86dd06e 100644
--- a/Src/DasherCore/DynamicFilter.h
+++ b/Src/DasherCore/DynamicFilter.h
@@ -25,36 +25,46 @@
 
 /// \ingroup InputFilter
 /// @{
+
+///filter with three states: paused, reversing, running. Hold any button down to reverse.
 class CDynamicFilter : public CInputFilter {
  public:
   CDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, int iType, const char *szName);
   
+  ///when reversing, backs off; when paused, does nothing; when running, delegates to TimerImpl
   virtual bool Timer(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted); 
 
-  virtual bool GetMinWidth(int &iMinWidth);
-
   virtual void KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
   virtual void KeyUp(int iTime, int iId, CDasherView *pView, CDasherModel *pModel);
 
- protected:
-  bool m_bDecorationChanged;
 
- private:
-  virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) = 0;
+ protected:
   virtual void ActionButton(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog) = 0;
   void Event(int iTime, int iButton, int iType, CDasherModel *pModel, CUserLogBase *pUserLog);
-  virtual void RevertPresses(int iCount) {};
 
   bool m_bKeyDown;
   bool m_bKeyHandled;
-  int m_iHeldId;
-  int m_iLastButton;
-  int m_iKeyDownTime;
-  int m_iState; // 0 = paused, 1 = running 2 = backing off
-  int m_iQueueId;
-  std::deque<int> m_deQueueTimes;
- 
-  CUserLogBase *m_pUserLog;
+  bool m_bDecorationChanged;
+  bool isPaused() {return m_iState == 0;}
+  bool isReversing() {return m_iState == 1;}
+  bool isRunning(int &iSubclassState)
+    {if (m_iState < 2) return false; iSubclassState = m_iState-2; return true;}
+  virtual void pause() {m_iState = 0;}
+  virtual void reverse() {m_iState = 1;}
+  virtual void run(int iSubclassState) {
+    DASHER_ASSERT(iSubclassState>=0);
+    m_iState = iSubclassState+2;
+  }
+
+  virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted) = 0;
+
+  private:
+    int m_iState; // 0 = paused, 1 = reversing, >=2 = running (extensible by subclasses)
+    int m_iHeldId;
+    int m_iKeyDownTime;
+  
+    CUserLogBase *m_pUserLog;
+
 };
 
 #endif
diff --git a/Src/DasherCore/Makefile.am b/Src/DasherCore/Makefile.am
index 539c293..5266d78 100644
--- a/Src/DasherCore/Makefile.am
+++ b/Src/DasherCore/Makefile.am
@@ -25,6 +25,8 @@ libdashercore_a_SOURCES = \
 		AutoSpeedControl.h \
 		BasicLog.cpp \
 		BasicLog.h \
+		ButtonMultiPress.h \
+		ButtonMultiPress.cpp \
 		CircleStartHandler.cpp \
 		CircleStartHandler.h \
 		ClickFilter.cpp \
diff --git a/Src/DasherCore/OneButtonDynamicFilter.cpp b/Src/DasherCore/OneButtonDynamicFilter.cpp
index 585cfc5..ab5fccc 100644
--- a/Src/DasherCore/OneButtonDynamicFilter.cpp
+++ b/Src/DasherCore/OneButtonDynamicFilter.cpp
@@ -36,7 +36,7 @@ static SModuleSettings sSettings[] = {
 };
 
 COneButtonDynamicFilter::COneButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
-  : CDynamicFilter(pEventHandler, pSettingsStore, pInterface, 6, 1, _("One Button Dynamic Mode")) {
+  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 6, 1, _("One Button Dynamic Mode")) {
   m_iTarget = 0;
 
   m_iTargetX = new int[2];
diff --git a/Src/DasherCore/OneButtonDynamicFilter.h b/Src/DasherCore/OneButtonDynamicFilter.h
index 953cb6c..6c7a3fb 100644
--- a/Src/DasherCore/OneButtonDynamicFilter.h
+++ b/Src/DasherCore/OneButtonDynamicFilter.h
@@ -21,11 +21,11 @@
 #ifndef __DYNAMIC_FILTER_H__
 #define __DYNAMIC_FILTER_H__
 
-#include "DynamicFilter.h"
+#include "ButtonMultiPress.h"
 
 /// \ingroup InputFilter
 /// @{
-class COneButtonDynamicFilter : public CDynamicFilter {
+class COneButtonDynamicFilter : public CButtonMultiPress {
  public:
   COneButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface);
   ~COneButtonDynamicFilter();
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.cpp b/Src/DasherCore/TwoButtonDynamicFilter.cpp
index 343167e..057f9b0 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.cpp
+++ b/Src/DasherCore/TwoButtonDynamicFilter.cpp
@@ -44,7 +44,7 @@ static SModuleSettings sSettings[] = {
 };
 
 CTwoButtonDynamicFilter::CTwoButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface)
-  : CDynamicFilter(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode")) { 
+  : CButtonMultiPress(pEventHandler, pSettingsStore, pInterface, 14, 1, _("Two Button Dynamic Mode")) { 
 
   m_iLastTime = -1;
 
@@ -151,19 +151,14 @@ void CTwoButtonDynamicFilter::AutoSpeedSample(int iTime, CDasherModel *pModel) {
 
   int iDiff(iTime - m_iLastTime);
   m_iLastTime = iTime;
-
   if(m_pTree) {
     int iMedian(m_pTree->GetOffset(m_pTree->GetCount() / 2));
-    
     if((iDiff <= 300) || (iDiff < (iMedian * GetLongParameter(LP_DYNAMIC_MEDIAN_FACTOR)) / 100)) {
       pModel->TriggerSlowdown();
     }
-  }
-  
-  if(!m_pTree)
-    m_pTree = new SBTree(iDiff);
-  else
     m_pTree->Add(iDiff);
+  }
+  else m_pTree = new SBTree(iDiff);  
 
   m_deOffsetQueue.push_back(iDiff);
 
@@ -272,3 +267,8 @@ void CTwoButtonDynamicFilter::RevertPresses(int iCount) {
   if(GetBoolParameter(BP_TWOBUTTON_SPEED))
     AutoSpeedUndo(iCount);
 }
+
+bool CTwoButtonDynamicFilter::GetMinWidth(int &iMinWidth) {
+  iMinWidth = 1024;
+  return true;
+}
diff --git a/Src/DasherCore/TwoButtonDynamicFilter.h b/Src/DasherCore/TwoButtonDynamicFilter.h
index 5981c51..0a227a7 100644
--- a/Src/DasherCore/TwoButtonDynamicFilter.h
+++ b/Src/DasherCore/TwoButtonDynamicFilter.h
@@ -21,13 +21,13 @@
 #ifndef __TWO_BUTTON_DYNAMIC_FILTER_H__
 #define __TWO_BUTTON_DYNAMIC_FILTER_H__
 
-#include "DynamicFilter.h"
+#include "ButtonMultiPress.h"
 
 #include <deque>
 
 /// \ingroup InputFilter
 /// @{
-class CTwoButtonDynamicFilter : public CDynamicFilter {
+class CTwoButtonDynamicFilter : public CButtonMultiPress {
  public:
   CTwoButtonDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface);
   ~CTwoButtonDynamicFilter();
@@ -40,7 +40,7 @@ class CTwoButtonDynamicFilter : public CDynamicFilter {
 
   virtual bool GetSettings(SModuleSettings **pSettings, int *iCount);
 
- 
+  virtual bool GetMinWidth(int &iMinWidth);
   
  private:
   virtual bool TimerImpl(int Time, CDasherView *m_pDasherView, CDasherModel *m_pDasherModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted);
diff --git a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
index 5db1189..cb0a6c3 100755
--- a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
+++ b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
@@ -358,6 +358,8 @@
 		19F8C7E60C858A2800276B4F /* I18n.h in Headers */ = {isa = PBXBuildFile; fileRef = 19F8C7E50C858A2800276B4F /* I18n.h */; };
 		19F8C7F90C858E9900276B4F /* TrainingHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19F8C7F70C858E9900276B4F /* TrainingHelper.cpp */; };
 		19F8C7FA0C858E9900276B4F /* TrainingHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 19F8C7F80C858E9900276B4F /* TrainingHelper.h */; };
+		33ABFEC60FC379EA00EA2BA5 /* ButtonMultiPress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33ABFEC40FC379EA00EA2BA5 /* ButtonMultiPress.cpp */; };
+		33ABFEC70FC379EA00EA2BA5 /* ButtonMultiPress.h in Headers */ = {isa = PBXBuildFile; fileRef = 33ABFEC50FC379EA00EA2BA5 /* ButtonMultiPress.h */; };
 		33E173C70F3E0B6400D19B38 /* Makefile.am in Resources */ = {isa = PBXBuildFile; fileRef = 33E173A70F3E0B6400D19B38 /* Makefile.am */; };
 		33E173C80F3E0B6400D19B38 /* training_albanian_SQ.txt in Resources */ = {isa = PBXBuildFile; fileRef = 33E173A80F3E0B6400D19B38 /* training_albanian_SQ.txt */; };
 		33E173C90F3E0B6400D19B38 /* training_albanian_SQ.txt.old in Resources */ = {isa = PBXBuildFile; fileRef = 33E173A90F3E0B6400D19B38 /* training_albanian_SQ.txt.old */; };
@@ -770,6 +772,8 @@
 		29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = "<group>"; };
 		29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
 		29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+		33ABFEC40FC379EA00EA2BA5 /* ButtonMultiPress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ButtonMultiPress.cpp; path = ../DasherCore/ButtonMultiPress.cpp; sourceTree = SOURCE_ROOT; };
+		33ABFEC50FC379EA00EA2BA5 /* ButtonMultiPress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ButtonMultiPress.h; path = ../DasherCore/ButtonMultiPress.h; sourceTree = SOURCE_ROOT; };
 		33E173A70F3E0B6400D19B38 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		33E173A80F3E0B6400D19B38 /* training_albanian_SQ.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_albanian_SQ.txt; sourceTree = "<group>"; };
 		33E173A90F3E0B6400D19B38 /* training_albanian_SQ.txt.old */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_albanian_SQ.txt.old; sourceTree = "<group>"; };
@@ -824,6 +828,8 @@
 		080E96DDFE201D6D7F000001 /* Mac OS X Classes */ = {
 			isa = PBXGroup;
 			children = (
+				33ABFEC40FC379EA00EA2BA5 /* ButtonMultiPress.cpp */,
+				33ABFEC50FC379EA00EA2BA5 /* ButtonMultiPress.h */,
 				193731A70C8586F20022CBC7 /* config.h */,
 				19C1AE810B130F18005C68D3 /* COSXMouseInput.h */,
 				191180E90B0FC91A001CB987 /* COSXSettingsStore.h */,
@@ -1490,6 +1496,7 @@
 				1988ABBE0C9FF97000D97977 /* GameStatistics.h in Headers */,
 				1988ABC00C9FF97000D97977 /* PinyinParser.h in Headers */,
 				33E91A780F55E60B00B5F513 /* KeyboardHelper.h in Headers */,
+				33ABFEC70FC379EA00EA2BA5 /* ButtonMultiPress.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1834,6 +1841,7 @@
 				1988ABBF0C9FF97000D97977 /* PinyinParser.cpp in Sources */,
 				1988ABCF0C9FFADA00D97977 /* GameLevel.cpp in Sources */,
 				33E91A770F55E60B00B5F513 /* KeyboardHelper.cpp in Sources */,
+				33ABFEC60FC379EA00EA2BA5 /* ButtonMultiPress.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};



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