[dasher: 4/11] Rewrite control mode! inc platform-independent speech & clipboard
- From: Patrick Welche <pwelche src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dasher: 4/11] Rewrite control mode! inc platform-independent speech & clipboard
- Date: Sat, 31 Jul 2010 14:07:31 +0000 (UTC)
commit 16dca8a699b27b6862ba705c966f86051a78bf5f
Author: Alan Lawrence <acl33 islay inf phy cam ac uk>
Date: Thu May 13 15:21:13 2010 +0100
Rewrite control mode! inc platform-independent speech & clipboard
...Controlled by BP_CONTROL_MODE_HAS_{SPEECH,COPY}.
*Also provided BP_SPEAK_ALL_ON_STOP, BP_COPY_ALL_ON_STOP, BP_SPEAK_WORDS.
*Stop and Pause in control mode according to CInputFilter::supportsPause(),
BP_CONTROL_MODE_HAS_HALT, and DasherInterface::hasStopTriggers.
*BP_CONTROL_MODE_HAS_EDIT allows turning off move/delete
Win32: Much code removed as equivalent now in DasherCore, but more TODO 2 build!
MacOSX also updated, but needs GUI for BP_*, & CTL_EDIT/DELETE handler (?!)
Gtk2 unchanged - old mechanism should still work, just not using new code...
(Note implementation of CDasherInterfaceBase::GetAllContext just throws)
iPhone: basic impl (no speech) with 'Control Mode' btn in misc settings
*textAtOffset now truncates desired region to lie within text, following Gtk2
*CTL_EDIT/DELETE search through textbox for alphabet space/para/default-ctx
*Also make textview first responder => displays cursor upon output/move/del!
Src/DasherCore/AlphabetManager.cpp | 26 +++---
Src/DasherCore/ControlManager.cpp | 107 ++++++++++++++++++-
Src/DasherCore/ControlManager.h | 38 ++++++-
Src/DasherCore/DasherInterfaceBase.cpp | 101 ++++++++++++++----
Src/DasherCore/DasherInterfaceBase.h | 54 +++++++++-
Src/DasherCore/DefaultFilter.h | 1 +
Src/DasherCore/DynamicFilter.h | 2 +
Src/DasherCore/InputFilter.h | 2 +
Src/DasherCore/NodeCreationManager.cpp | 43 +++-----
Src/DasherCore/NodeCreationManager.h | 24 +---
Src/DasherCore/Parameters.h | 13 +++
Src/DasherCore/StylusFilter.h | 4 +-
Src/Gtk2/DasherControl.h | 4 +
Src/MacOSX/COSXDasherControl.h | 7 +-
Src/MacOSX/COSXDasherControl.mm | 21 ++++
Src/MacOSX/Chatter.h | 40 -------
Src/MacOSX/Chatter.m | 144 --------------------------
Src/MacOSX/Dasher.xcodeproj/project.pbxproj | 16 ++--
Src/MacOSX/DasherApp.h | 8 +-
Src/MacOSX/DasherApp.mm | 40 +++++++-
Src/MacOSX/DasherEdit.h | 2 +
Src/MacOSX/DasherEdit.mm | 10 ++-
Src/MacOSX/DasherViewAqua.mm | 1 -
Src/MacOSX/DasherViewOpenGL.mm | 1 -
Src/MacOSX/Queue.h | 21 ++++
Src/MacOSX/Queue.m | 57 ++++++++++
Src/Win32/ActionSpeech.cpp | 58 ----------
Src/Win32/ActionSpeech.h | 27 -----
Src/Win32/Dasher.cpp | 67 +++++++++++--
Src/Win32/Dasher.h | 17 +++-
Src/Win32/DasherWindow.cpp | 22 ----
Src/Win32/Widgets/AdvancedPage.cpp | 7 +-
Src/Win32/Widgets/Edit.cpp | 97 +-----------------
Src/Win32/Widgets/Edit.h | 11 --
Src/iPhone/Classes/CDasherInterfaceBridge.h | 3 +
Src/iPhone/Classes/CDasherInterfaceBridge.mm | 52 +++++++++-
Src/iPhone/Classes/DasherAppDelegate.h | 15 +++-
Src/iPhone/Classes/DasherAppDelegate.mm | 90 ++++++++++++++--
Src/iPhone/Classes/InputMethodSelector.mm | 1 +
Src/iPhone/Classes/MiscSettings.mm | 33 ++++++
40 files changed, 750 insertions(+), 537 deletions(-)
---
diff --git a/Src/DasherCore/AlphabetManager.cpp b/Src/DasherCore/AlphabetManager.cpp
index bd5ce3e..ce97765 100644
--- a/Src/DasherCore/AlphabetManager.cpp
+++ b/Src/DasherCore/AlphabetManager.cpp
@@ -261,25 +261,25 @@ CDasherNode *CAlphabetManager::CreateSymbolNode(CAlphNode *pParent, symbol iSymb
// TODO: Need to fix fact that this is created even when control mode is switched off
if(iSymbol == m_pNCManager->GetAlphabet()->GetControlSymbol()) {
- //ACL setting offset as one more than parent for consistency with "proper" symbol nodes...
- pNewNode = m_pNCManager->GetCtrlRoot(pParent, iLbnd, iHbnd, pParent->offset()+1);
-
+ CControlManager *pMgr = m_pNCManager->GetControlManager();
+ if (pMgr) {
#ifdef _WIN32_WCE
- //no control manager - but (TODO!) we still try to create (0-size!) control node...
- DASHER_ASSERT(!pNewNode);
- // For now, just hack it so we get a normal root node here
- pNewNode = m_pNCManager->GetAlphRoot(pParent, iLbnd, iHbnd, false, pParent->m_iOffset+1);
-#else
- DASHER_ASSERT(pNewNode);
+ DASHER_ASSERT(false);
#endif
+ //ACL leave offset as is - like its groupnode parent, but unlike its alphnode siblings,
+ //the control node does not enter a symbol....
+ pNewNode = pMgr->GetRoot(pParent, iLbnd, iHbnd, pParent->offset());
+ } else {
+ //Control mode currently turned off...
+ DASHER_ASSERT(iLbnd == iHbnd); //zero size
+ pNewNode = NULL;
}
- else if(iSymbol == m_pNCManager->GetAlphabet()->GetStartConversionSymbol()) {
+ } else if(iSymbol == m_pNCManager->GetAlphabet()->GetStartConversionSymbol()) {
// else if(iSymbol == m_pNCManager->GetSpaceSymbol()) {
//ACL setting m_iOffset+1 for consistency with "proper" symbol nodes...
pNewNode = m_pNCManager->GetConvRoot(pParent, iLbnd, iHbnd, pParent->offset()+1);
- }
- else {
+ } else {
// TODO: Exceptions / error handling in general
CAlphNode *pAlphNode;
@@ -370,7 +370,7 @@ void CAlphabetManager::IterateChildGroups(CAlphNode *pParent, SGroupInfo *pParen
//3. loop round inner loop...
}
//created a new node - symbol or (group which will have >1 child).
- DASHER_ASSERT(pParent->GetChildren().back()==pNewChild);
+ DASHER_ASSERT((i-1 == m_pNCManager->GetAlphabet()->GetControlSymbol() && pNewChild==NULL) || pParent->GetChildren().back()==pNewChild);
//now adjust the node we've actually created, to take account of any elided group(s)...
// tho not if we've reused the existing node, assume that's been adjusted already
if (pNewChild && pNewChild!=buildAround) pNewChild->PrependElidedGroup(iOverrideColour, groupPrefix);
diff --git a/Src/DasherCore/ControlManager.cpp b/Src/DasherCore/ControlManager.cpp
index 84dce9c..b546f87 100644
--- a/Src/DasherCore/ControlManager.cpp
+++ b/Src/DasherCore/ControlManager.cpp
@@ -95,7 +95,7 @@ void CControlBase::CContNode::PopulateChildren() {
if( *it == NULL ) {
// Escape back to alphabet
- pNewNode = m_pMgr->m_pNCManager->GetAlphRoot(this, iLbnd, iHbnd, false, offset());
+ pNewNode = m_pMgr->m_pNCManager->GetAlphRoot(this, iLbnd, iHbnd, false, offset()+1);
}
else {
@@ -340,7 +340,7 @@ void COrigNodes::RegisterNode( int iID, std::string strLabel, int iColour ) {
}
void COrigNodes::ConnectNode(int iChild, int iParent, int iAfter) {
- //ACL duplicating old functionality here. Node idea had been to do
+ //ACL duplicating old functionality here. Idea had been to do
// something with iAfter "(eventually -1 = start, -2 = end)", but
// since this wasn't used, and this is all legacy code anyway ;-),
// I'm leaving as is...
@@ -361,7 +361,6 @@ void COrigNodes::DisconnectNode(int iChild, int iParent) {
}
}
-
void COrigNodes::XmlStartHandler(void *pUserData, const XML_Char *szName, const XML_Char **aszAttr) {
COrigNodes *pMgr(static_cast<COrigNodes *>(pUserData));
int colour=-1;
@@ -389,5 +388,105 @@ void COrigNodes::XmlEndHandler(void *pUserData, const XML_Char *szName) {
void COrigNodes::XmlCDataHandler(void *pUserData, const XML_Char *szData, int iLength){
}
-CControlManager::CControlManager(CNodeCreationManager *pNCMgr, CDasherInterfaceBase *pInterface) : COrigNodes(pNCMgr,pInterface) {
+CControlManager::CControlManager(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface)
+: CDasherComponent(pEventHandler, pSettingsStore), COrigNodes(pNCManager, pInterface), m_pSpeech(NULL), m_pCopy(NULL) {
+
+ updateActions();
+}
+
+CControlManager::~CControlManager() {
+ delete m_pSpeech;
+ delete m_pCopy;
+}
+
+class TextActionHeader : public CDasherInterfaceBase::TextAction, public CControlBase::NodeTemplate {
+public:
+ TextActionHeader(CDasherInterfaceBase *pIntf, const string &strHdr, NodeTemplate *pRoot) : TextAction(pIntf), NodeTemplate(strHdr,-1),
+ m_all(this, "All", &CDasherInterfaceBase::TextAction::executeOnAll),
+ m_new(this, "New", &CDasherInterfaceBase::TextAction::executeOnNew),
+ m_again(this, "Repeat", &CDasherInterfaceBase::TextAction::executeLast) {
+ successors.push_back(&m_all); m_all.successors.push_back(NULL); m_all.successors.push_back(pRoot);
+ successors.push_back(&m_new); m_new.successors.push_back(NULL); m_new.successors.push_back(pRoot);
+ successors.push_back(&m_again); m_again.successors.push_back(NULL); m_again.successors.push_back(pRoot);
+ }
+private:
+ CControlBase::MethodTemplate<CDasherInterfaceBase::TextAction> m_all, m_new, m_again;
+};
+
+class SpeechHeader : public TextActionHeader {
+public:
+ SpeechHeader(CDasherInterfaceBase *pIntf, NodeTemplate *pRoot) : TextActionHeader(pIntf, "Speak", pRoot) {
+ }
+ void operator()(const std::string &strText) {
+ m_pIntf->Speak(strText, true);
+ }
+};
+
+class CopyHeader : public TextActionHeader {
+public:
+ CopyHeader(CDasherInterfaceBase *pIntf, NodeTemplate *pRoot) : TextActionHeader(pIntf, "Copy", pRoot) {
+ }
+ void operator()(const std::string &strText) {
+ m_pIntf->CopyToClipboard(strText);
+ }
+};
+
+void CControlManager::HandleEvent(CEvent *pEvent) {
+ if (pEvent->m_iEventType == EV_PARAM_NOTIFY) {
+ switch (static_cast<CParameterNotificationEvent *>(pEvent)->m_iParameter) {
+ case BP_CONTROL_MODE_HAS_HALT:
+ case BP_CONTROL_MODE_HAS_EDIT:
+ case BP_CONTROL_MODE_HAS_SPEECH:
+ case BP_CONTROL_MODE_HAS_COPY:
+ case BP_COPY_ALL_ON_STOP:
+ case BP_SPEAK_ALL_ON_STOP:
+ case SP_INPUT_FILTER:
+ updateActions();
+ }
+ }
}
+
+void CControlManager::updateActions() {
+ vector<NodeTemplate *> &vRootSuccessors(GetRootTemplate()->successors);
+ vector<NodeTemplate *> vOldRootSuccessors;
+ vOldRootSuccessors.swap(vRootSuccessors);
+ vector<NodeTemplate *>::iterator it=vOldRootSuccessors.begin();
+ DASHER_ASSERT(*it == NULL); //escape back to alphabet
+ vRootSuccessors.push_back(*it++);
+
+ //stop does something, and we're told to add a node for it
+ // (either a dynamic filter where the user can't use the normal stop mechanism precisely,
+ // or a static filter but a 'stop' action is easier than using speak->all / copy->all then pause)
+ if (m_pInterface->hasStopTriggers() && m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_HALT))
+ vRootSuccessors.push_back(m_perId[CTL_STOP]);
+ if (*it == m_perId[CTL_STOP]) it++;
+
+ //filter is pauseable, and either 'stop' would do something (so pause is different),
+ // or we're told to have a stop node but it would be indistinguishable from pause (=>have pause)
+ CInputFilter *pInput(static_cast<CInputFilter *>(m_pInterface->GetModuleByName(m_pInterface->GetStringParameter(SP_INPUT_FILTER))));
+ if (pInput->supportsPause() && (m_pInterface->hasStopTriggers() || m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_HALT)))
+ vRootSuccessors.push_back(m_perId[CTL_PAUSE]);
+ if (*it == m_perId[CTL_PAUSE]) it++;
+
+ if (m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_SPEECH) && m_pInterface->SupportsSpeech()) {
+ if (!m_pSpeech) m_pSpeech = new SpeechHeader(m_pInterface, GetRootTemplate());
+ vRootSuccessors.push_back(m_pSpeech);
+ }
+ if (*it == m_pSpeech) it++;
+
+ if (m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_COPY) && m_pInterface->SupportsClipboard()) {
+ if (!m_pCopy) m_pCopy = new CopyHeader(m_pInterface, GetRootTemplate());
+ vRootSuccessors.push_back(m_pCopy);
+ }
+ if (*it == m_pCopy) it++;
+
+ if (m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_EDIT)) {
+ vRootSuccessors.push_back(m_perId[CTL_MOVE]);
+ vRootSuccessors.push_back(m_perId[CTL_DELETE]);
+ }
+ if (*it == m_perId[CTL_MOVE]) it++;
+ if (*it == m_perId[CTL_DELETE]) it++;
+
+ //copy anything else (custom) that might have been added...
+ while (it != vOldRootSuccessors.end()) vRootSuccessors.push_back(*it++);
+}
\ No newline at end of file
diff --git a/Src/DasherCore/ControlManager.h b/Src/DasherCore/ControlManager.h
index f5a2bde..4bd092b 100644
--- a/Src/DasherCore/ControlManager.h
+++ b/Src/DasherCore/ControlManager.h
@@ -42,6 +42,8 @@
using namespace std;
+class CNodeCreationManager;
+
namespace Dasher {
class CDasherModel;
@@ -112,6 +114,22 @@ namespace Dasher {
int m_iColour;
};
+ template <typename T> class MethodTemplate : public NodeTemplate {
+ public:
+ ///pointer to a function "void X()", that is a member of a T...
+ typedef void (T::*Method)();
+ MethodTemplate(T *pRecv, const std::string &strLabel, Method f) : NodeTemplate(strLabel,-1),m_pRecv(pRecv),m_f(f) {
+ std::cout << "Creating " << this << " with receiver " << pRecv << std::endl;
+ }
+ virtual void happen(CContNode *pNode) {
+ //invoke pointer-to-member-function m_f on object *m_pRecv!
+ (m_pRecv->*m_f)();
+ }
+ private:
+ T *m_pRecv;
+ Method m_f;
+ };
+
class EventBroadcast : public NodeTemplate {
public:
EventBroadcast(int iEvent, const std::string &strLabel, int iColour);
@@ -188,10 +206,24 @@ namespace Dasher {
CDasherInterfaceBase *m_pInterface;
};
- ///subclass which we actually construct - more of a marker than anything for now.
- class CControlManager : public COrigNodes {
+ ///subclass which we actually construct...
+ class CControlManager : public CDasherComponent, public COrigNodes {
public:
- CControlManager(CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface);
+ CControlManager(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface);
+ void HandleEvent(CEvent *pEvent);
+
+ ///Recomputes which of pause, stop, speak and copy the root control node should have amongst its children.
+ /// Automatically called whenever copy-on-stop/speak-on-stop or input filter changes;
+ /// subclasses of CDasherInterfaceBase should also call this if
+ /// (a) they override Stop() and hasStopTriggers() with additional actions, if these are enabled/disabled
+ /// and this causes the value returned by hasStopTriggers() to change;
+ /// (b) the values returned by SupportsSpeech() and/or SupportsClipboard() ever change.
+ void updateActions();
+ ~CControlManager();
+
+ private:
+ ///group headers, with three children each (all/new/repeat)
+ NodeTemplate *m_pSpeech, *m_pCopy;
};
/// @}
}
diff --git a/Src/DasherCore/DasherInterfaceBase.cpp b/Src/DasherCore/DasherInterfaceBase.cpp
index 2f552aa..b652d46 100644
--- a/Src/DasherCore/DasherInterfaceBase.cpp
+++ b/Src/DasherCore/DasherInterfaceBase.cpp
@@ -111,6 +111,8 @@ CDasherInterfaceBase::CDasherInterfaceBase() {
strCurrentContext = ". ";
strTrainfileBuffer = "";
+ m_strCurrentWord = "";
+
// Create an event handler.
m_pEventHandler = new CEventHandler(this);
@@ -140,7 +142,6 @@ void CDasherInterfaceBase::Realize() {
m_ColourIO = new CColourIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vColourFiles);
ChangeColours();
- ChangeAlphabet(); // This creates the NodeCreationManager, the Alphabet
// Create the user logging object if we are suppose to. We wait
// until now so we have the real value of the parameter and not
@@ -163,6 +164,9 @@ void CDasherInterfaceBase::Realize() {
CreateInput();
CreateInputFilter();
+
+ ChangeAlphabet(); // This creates the NodeCreationManager, the Alphabet
+
SetupActionButtons();
CParameterNotificationEvent oEvent(LP_NODE_BUDGET);
InterfaceEventHandler(&oEvent);
@@ -344,11 +348,20 @@ void CDasherInterfaceBase::InterfaceEventHandler(Dasher::CEvent *pEvent) {
strCurrentContext = strCurrentContext.substr( strCurrentContext.size() - 20 );
if(GetBoolParameter(BP_LM_ADAPTIVE))
strTrainfileBuffer += pEditEvent->m_sText;
+ if (GetBoolParameter(BP_SPEAK_WORDS) && SupportsSpeech()) {
+ if (pEditEvent->m_sText == m_Alphabet->GetText(m_Alphabet->GetSpaceSymbol())) {
+ Speak(m_strCurrentWord, false);
+ m_strCurrentWord="";
+ } else
+ m_strCurrentWord+=pEditEvent->m_sText;
+ }
}
else if(pEditEvent->m_iEditType == 2) {
strCurrentContext = strCurrentContext.substr( 0, strCurrentContext.size() - pEditEvent->m_sText.size());
if(GetBoolParameter(BP_LM_ADAPTIVE))
strTrainfileBuffer = strTrainfileBuffer.substr( 0, strTrainfileBuffer.size() - pEditEvent->m_sText.size());
+ if (GetBoolParameter(BP_SPEAK_WORDS))
+ m_strCurrentWord = m_strCurrentWord.substr(0, max(0ul, m_strCurrentWord.size()-pEditEvent->m_sText.size()));
}
}
}
@@ -369,11 +382,9 @@ void CDasherInterfaceBase::CreateModel(int iOffset) {
if(!m_pNCManager)
return;
- if(m_pDasherModel) {
- delete m_pDasherModel;
- m_pDasherModel = 0;
- }
-
+ delete m_pDasherModel;
+
+ // TODO: Eventually we'll not have to pass the NC manager to the model...(?)
m_pDasherModel = new CDasherModel(m_pEventHandler, m_pSettingsStore, m_pNCManager, this, m_pDasherView, iOffset);
// Notify the teacher of the new model
@@ -393,23 +404,14 @@ void CDasherInterfaceBase::CreateNCManager() {
if( lmID == -1 )
return;
- int iOffset;
-
- if(m_pDasherModel)
- iOffset = m_pDasherModel->GetOffset();
- else
- iOffset = 0; // TODO: Is this right?
+ //0 seems the right offset if we don't have a model (=at startup)...
+ int iOffset = m_pDasherModel ? m_pDasherModel->GetOffset() : 0;
// Delete the old model and create a new one
- if(m_pDasherModel) {
- delete m_pDasherModel;
- m_pDasherModel = 0;
- }
+ // (must delete model first, as its nodes refer to NodeManagers inside the NCManager)
+ delete m_pDasherModel; m_pDasherModel = NULL;
- if(m_pNCManager) {
- delete m_pNCManager;
- m_pNCManager = 0;
- }
+ delete m_pNCManager;
m_pNCManager = new CNodeCreationManager(this, m_pEventHandler, m_pSettingsStore, m_AlphIO);
@@ -419,6 +421,40 @@ void CDasherInterfaceBase::CreateNCManager() {
CreateModel(iOffset);
}
+CDasherInterfaceBase::TextAction::TextAction(CDasherInterfaceBase *pIntf) : m_pIntf(pIntf) {
+ m_iStartOffset= (pIntf->m_pDasherModel) ? pIntf->m_pDasherModel->GetOffset() : 0;
+ pIntf->m_vTextActions.insert(this);
+}
+
+CDasherInterfaceBase::TextAction::~TextAction() {
+ m_pIntf->m_vTextActions.erase(this);
+}
+
+void CDasherInterfaceBase::TextAction::executeOnAll() {
+ (*this)(strLast = m_pIntf->GetAllContext());
+ m_iStartOffset = m_pIntf->m_pDasherModel->GetOffset();
+}
+
+void CDasherInterfaceBase::TextAction::executeOnNew() {
+ int iNewOffset(m_pIntf->m_pDasherModel->GetOffset());
+ (*this)(strLast = m_pIntf->GetContext(m_iStartOffset, iNewOffset-m_iStartOffset));
+ m_iStartOffset=iNewOffset;
+}
+
+void CDasherInterfaceBase::TextAction::executeLast() {
+ (*this)(strLast);
+}
+
+void CDasherInterfaceBase::TextAction::NotifyOffset(int iOffset) {
+ m_iStartOffset = min(iOffset, m_iStartOffset);
+}
+
+
+bool CDasherInterfaceBase::hasStopTriggers() {
+ return (GetBoolParameter(BP_COPY_ALL_ON_STOP) && SupportsClipboard())
+ || (GetBoolParameter(BP_SPEAK_ALL_ON_STOP) && SupportsSpeech());
+}
+
void CDasherInterfaceBase::Stop() {
if (GetBoolParameter(BP_DASHER_PAUSED)) return; //already paused, no need to do anything.
SetBoolParameter(BP_DASHER_PAUSED, true);
@@ -430,6 +466,13 @@ void CDasherInterfaceBase::Stop() {
if (m_pUserLog != NULL)
m_pUserLog->StopWriting((float) GetNats());
#endif
+
+ if (GetBoolParameter(BP_COPY_ALL_ON_STOP) && SupportsClipboard()) {
+ CopyToClipboard(GetAllContext());
+ }
+ if (GetBoolParameter(BP_SPEAK_ALL_ON_STOP) && SupportsSpeech()) {
+ Speak(GetAllContext(), true);
+ }
}
void CDasherInterfaceBase::GameMessageIn(int message, void* messagedata) {
@@ -764,6 +807,11 @@ std::string CDasherInterfaceBase::GetContext(int iStart, int iLength) {
return m_strContext;
}
+void CDasherInterfaceBase::ClearAllContext() {
+ SetBuffer(0);
+ strCurrentContext = "";
+}
+
void CDasherInterfaceBase::SetContext(std::string strNewContext) {
m_strContext = strNewContext;
}
@@ -771,15 +819,18 @@ void CDasherInterfaceBase::SetContext(std::string strNewContext) {
// Control mode stuff
void CDasherInterfaceBase::RegisterNode( int iID, const std::string &strLabel, int iColour ) {
- m_pNCManager->RegisterNode(iID, strLabel, iColour);
+ CControlManager *pMgr(m_pNCManager->GetControlManager());
+ if (pMgr) pMgr->RegisterNode(iID, strLabel, iColour);
}
void CDasherInterfaceBase::ConnectNode(int iChild, int iParent, int iAfter) {
- m_pNCManager->ConnectNode(iChild, iParent, iAfter);
+ CControlManager *pMgr(m_pNCManager->GetControlManager());
+ pMgr->ConnectNode(iChild, iParent, iAfter);
}
void CDasherInterfaceBase::DisconnectNode(int iChild, int iParent) {
- m_pNCManager->DisconnectNode(iChild, iParent);
+ CControlManager *pMgr(m_pNCManager->GetControlManager());
+ pMgr->DisconnectNode(iChild, iParent);
}
void CDasherInterfaceBase::SetBoolParameter(int iParameter, bool bValue) {
@@ -1091,6 +1142,10 @@ void CDasherInterfaceBase::UnsetBuffer() {
void CDasherInterfaceBase::SetOffset(int iOffset) {
if(m_pDasherModel)
m_pDasherModel->SetOffset(iOffset, m_pDasherView);
+ //ACL TODO FIXME check that CTL_MOVE, etc., eventually come here?
+ for (set<TextAction *,bool(*)(TextAction *,TextAction *)>::iterator it = m_vTextActions.begin(); it!=m_vTextActions.end(); it++) {
+ (*it)->NotifyOffset(iOffset);
+ }
}
// Returns 0 on success, an error string on failure.
diff --git a/Src/DasherCore/DasherInterfaceBase.h b/Src/DasherCore/DasherInterfaceBase.h
index 3a72144..d03c274 100644
--- a/Src/DasherCore/DasherInterfaceBase.h
+++ b/Src/DasherCore/DasherInterfaceBase.h
@@ -41,13 +41,14 @@
#include "InputFilter.h"
#include "ModuleManager.h"
-#include <map>
+#include <set>
#include <algorithm>
namespace Dasher {
class CDasherScreen;
class CDasherView;
class CDasherInput;
+ class CInputFilter;
class CDasherModel;
class CEventHandler;
class CEvent;
@@ -204,12 +205,44 @@ public:
void PreSetNotify(int iParameter, const std::string &sValue);
+ ///Does this subclass support speech (i.e. the speak(string) method?)
+ /// Default is just to return false.
+ virtual bool SupportsSpeech() {return false;}
+ ///Does this subclass support clipboard copying (i.e. the copyToClipboard(string) method?)
+ /// Default is just to return false.
+ virtual bool SupportsClipboard() {return false;}
+
+ ///Subclasses supporting speech should override to speak the supplied text
+ /// (Default implementation does nothing)
+ virtual void Speak(const std::string &text, bool bInterrupt) {}
+
+ ///Subclasses supporting clipboard operations should override to copy
+ /// the specified text to the clipboard. (Default implementation does nothing).
+ virtual void CopyToClipboard(const std::string &text) {}
+
+ class TextAction {
+ public:
+ TextAction(CDasherInterfaceBase *pMgr);
+ void executeOnAll();
+ void executeOnNew();
+ void executeLast();
+ void NotifyOffset(int iOffset);
+ virtual ~TextAction();
+ protected:
+ virtual void operator()(const std::string &strText)=0;
+ CDasherInterfaceBase *m_pIntf;
+ private:
+ int m_iStartOffset;
+ std::string strLast;
+ };
+
/// @name Starting and stopping
/// Methods used to instruct dynamic motion of Dasher to start or stop
/// @{
- /// Stop Dasher - Sets BP_DASHER_PAUSED; subclasses may override to do more
+ /// Stop Dasher - Sets BP_DASHER_PAUSED and executes any on-stop actions
+ /// (speech, clipboard - subclasses may override to do more).
/// (But does nothing if BP_DASHER_PAUSED is not set)
virtual void Stop();
@@ -218,6 +251,11 @@ public:
/// \param Time Time in ms, used to keep a constant frame rate
void Unpause(unsigned long Time);
+ ///Whether any actions are currently setup to occur when Dasher 'stop's.
+ /// Default is to return TRUE iff we support speech and BP_SPEAK_ON_STOP is set,
+ /// and/or if we support clipboard and BP_COPY_ALL_ON_STOP is set; subclasses may
+ /// override if they have additional on-stop actions.
+ virtual bool hasStopTriggers();
/// @}
@@ -344,6 +382,11 @@ public:
};
std::string GetContext(int iStart, int iLength);
+
+ ///Subclasses should override to clear text edit box, etc., etc., but then
+ /// call this (superclass) implementation as well to rebuild the model...
+ virtual void ClearAllContext();
+ virtual std::string GetAllContext()=0;
/// Set a key value pair by name - designed to allow operation from
/// the command line. Returns 0 on success, an error string on failure.
@@ -351,7 +394,7 @@ public:
const char* ClSet(const std::string &strKey, const std::string &strValue);
void ImportTrainingText(const std::string &strPath);
-
+
protected:
/// @name Startup
@@ -546,6 +589,9 @@ protected:
std::string strTrainfileBuffer;
std::string strCurrentContext;
+
+ ///builds up the word currently being entered for speech.
+ std::string m_strCurrentWord;
std::string m_strContext;
@@ -560,6 +606,8 @@ protected:
/// @}
bool m_bLastChanged;
+
+ std::set<TextAction *> m_vTextActions;
};
/// @}
diff --git a/Src/DasherCore/DefaultFilter.h b/Src/DasherCore/DefaultFilter.h
index b4a8826..5373fea 100644
--- a/Src/DasherCore/DefaultFilter.h
+++ b/Src/DasherCore/DefaultFilter.h
@@ -12,6 +12,7 @@ class CDefaultFilter : public CInputFilter {
public:
CDefaultFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName);
~CDefaultFilter();
+ virtual bool supportsPause() {return true;}
virtual void HandleEvent(Dasher::CEvent * pEvent);
diff --git a/Src/DasherCore/DynamicFilter.h b/Src/DasherCore/DynamicFilter.h
index c0580bc..7a94357 100644
--- a/Src/DasherCore/DynamicFilter.h
+++ b/Src/DasherCore/DynamicFilter.h
@@ -31,6 +31,8 @@ class CDynamicFilter : public CInputFilter {
public:
CDynamicFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, int iType, const char *szName);
+ virtual bool supportsPause() {return true;}
+
///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, CExpansionPolicy **pol);
diff --git a/Src/DasherCore/InputFilter.h b/Src/DasherCore/InputFilter.h
index 25a704a..e7105d6 100644
--- a/Src/DasherCore/InputFilter.h
+++ b/Src/DasherCore/InputFilter.h
@@ -38,6 +38,8 @@ class CInputFilter : public CDasherModule {
return false;
}
+ virtual bool supportsPause() {return false;}
+
protected:
CDasherInterfaceBase *m_pInterface;
diff --git a/Src/DasherCore/NodeCreationManager.cpp b/Src/DasherCore/NodeCreationManager.cpp
index 506beda..c321056 100644
--- a/Src/DasherCore/NodeCreationManager.cpp
+++ b/Src/DasherCore/NodeCreationManager.cpp
@@ -16,7 +16,8 @@ using namespace Dasher;
CNodeCreationManager::CNodeCreationManager(Dasher::CDasherInterfaceBase *pInterface,
Dasher::CEventHandler *pEventHandler,
CSettingsStore *pSettingsStore,
- Dasher::CAlphIO *pAlphIO) : CDasherComponent(pEventHandler, pSettingsStore) {
+ Dasher::CAlphIO *pAlphIO) : CDasherComponent(pEventHandler, pSettingsStore),
+ m_pInterface(pInterface), m_pControlManager(NULL) {
const Dasher::CAlphIO::AlphInfo &oAlphInfo(pAlphIO->GetInfo(pSettingsStore->GetStringParameter(SP_ALPHABET_ID)));
m_pAlphabet = new CAlphabet(oAlphInfo);
@@ -119,11 +120,8 @@ CNodeCreationManager::CNodeCreationManager(Dasher::CDasherInterfaceBase *pInterf
delete pLM2;
}
#endif
-#ifndef _WIN32_WCE
- m_pControlManager = new CControlManager(this,pInterface);
-#else
- m_pControlManager = 0;
-#endif
+
+ HandleEvent(&CParameterNotificationEvent(BP_CONTROL_MODE));
switch(oAlphInfo.m_iConversionID) {
default:
@@ -154,32 +152,10 @@ CNodeCreationManager::~CNodeCreationManager() {
if (m_pConversionManager) m_pConversionManager->Unref();
}
-void CNodeCreationManager::RegisterNode( int iID, const std::string &strLabel, int iColour ) {
- if(m_pControlManager)
- m_pControlManager->RegisterNode(iID, strLabel, iColour);
-}
-
-void CNodeCreationManager::ConnectNode(int iChild, int iParent, int iAfter) {
- if(m_pControlManager)
- m_pControlManager->ConnectNode(iChild, iParent, iAfter);
-}
-
-void CNodeCreationManager::DisconnectNode(int iChild, int iParent) {
- if(m_pControlManager)
- m_pControlManager->DisconnectNode(iChild, iParent);
-}
-
CDasherNode *CNodeCreationManager::GetAlphRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, bool bEnteredLast, int iOffset) {
return m_pAlphabetManager->GetRoot(pParent, iLower, iUpper, bEnteredLast, iOffset);
}
-CDasherNode *CNodeCreationManager::GetCtrlRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, int iOffset) {
- if(m_pControlManager)
- return m_pControlManager->GetRoot(pParent, iLower, iUpper, iOffset);
- else
- return NULL;
-}
-
CDasherNode *CNodeCreationManager::GetConvRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, int iOffset) {
if(m_pConversionManager)
return m_pConversionManager->GetRoot(pParent, iLower, iUpper, iOffset);
@@ -242,6 +218,17 @@ void CNodeCreationManager::GetProbs(CLanguageModel::Context context, std::vector
}
+void CNodeCreationManager::HandleEvent(CEvent *pEvent) {
+ if (pEvent->m_iEventType == EV_PARAM_NOTIFY) {
+ if (static_cast<CParameterNotificationEvent *>(pEvent)->m_iParameter == BP_CONTROL_MODE) {
+ delete m_pControlManager;
+ m_pControlManager = (GetBoolParameter(BP_CONTROL_MODE))
+ ? new CControlManager(m_pEventHandler, m_pSettingsStore, this, m_pInterface)
+ : NULL;
+ }
+ }
+}
+
void
CNodeCreationManager::ImportTrainingText(const std::string &strPath) {
m_pTrainer->LoadFile(strPath);
diff --git a/Src/DasherCore/NodeCreationManager.h b/Src/DasherCore/NodeCreationManager.h
index f189feb..0f16117 100644
--- a/Src/DasherCore/NodeCreationManager.h
+++ b/Src/DasherCore/NodeCreationManager.h
@@ -5,9 +5,11 @@
#include "Alphabet/AlphIO.h"
#include "AlphabetManager.h"
#include "ConversionManager.h"
+#include "ControlManager.h"
#include "DasherComponent.h"
#include "LanguageModelling/LanguageModel.h"
#include "Trainer.h"
+#include "Event.h"
#include <string>
#include <vector>
@@ -28,32 +30,17 @@ class CNodeCreationManager : public Dasher::CDasherComponent {
Dasher::CAlphIO *pAlphIO);
~CNodeCreationManager();
+ //we watch for changes to BP_CONTROL_MODE and create the Control Manager lazily
+ void HandleEvent(Dasher::CEvent *pEvent);
///
/// Get a root node of a particular type
///
Dasher::CDasherNode *GetAlphRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, bool bEnteredLast, int iOffset);
- Dasher::CDasherNode *GetCtrlRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, int iOffset);
Dasher::CDasherNode *GetConvRoot(Dasher::CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, int iOffset);
- ///
- /// Register a control node
- ///
-
- void RegisterNode( int iID, const std::string &strLabel, int iColour );
-
- ///
- /// Connect control nodes in the tree
- ///
+ Dasher::CControlManager *GetControlManager() {return m_pControlManager;}
- void ConnectNode(int iChild, int iParent, int iAfter);
-
- ///
- /// Disconnect control nodes
- ///
-
- void DisconnectNode(int iChild, int iParent);
-
void GetProbs(Dasher::CLanguageModel::Context context, std::vector <unsigned int >&Probs, int iNorm) const;
///
@@ -71,6 +58,7 @@ class CNodeCreationManager : public Dasher::CDasherComponent {
Dasher::CAlphabet *m_pAlphabet; // pointer to the alphabet
Dasher::CTrainer *m_pTrainer;
+ Dasher::CDasherInterfaceBase *m_pInterface;
Dasher::CAlphabetManager *m_pAlphabetManager;
Dasher::CControlManager *m_pControlManager;
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 2e4b37e..61b50f7 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -42,6 +42,8 @@ enum {
BP_CIRCLE_START, BP_GLOBAL_KEYBOARD, BP_NONLINEAR_Y,
BP_SMOOTH_OFFSET, BP_CONVERSION_MODE, BP_STOP_OUTSIDE, BP_BACKOFF_BUTTON,
BP_TWOBUTTON_REVERSE, BP_2B_INVERT_DOUBLE, BP_SLOW_START,
+ BP_COPY_ALL_ON_STOP, BP_SPEAK_ALL_ON_STOP, BP_SPEAK_WORDS,
+ BP_CONTROL_MODE_HAS_HALT, BP_CONTROL_MODE_HAS_EDIT, BP_CONTROL_MODE_HAS_COPY, BP_CONTROL_MODE_HAS_SPEECH,
#ifdef TARGET_OS_IPHONE
BP_CUSTOM_TILT, BP_DOUBLE_X,
#endif
@@ -167,6 +169,17 @@ static bp_table boolparamtable[] = {
{BP_TWOBUTTON_REVERSE, "TwoButtonReverse", PERS, false, "Reverse the up/down buttons in two button mode"},
{BP_2B_INVERT_DOUBLE, "TwoButtonInvertDouble", PERS, false, "Double-press acts as opposite button in two-button mode"},
{BP_SLOW_START, "SlowStart", PERS, false, "Start at low speed and increase"},
+ {BP_COPY_ALL_ON_STOP, "CopyOnStop", PERS, false, "Copy all text to clipboard whenever we stop"},
+ {BP_SPEAK_ALL_ON_STOP, "SpeakOnStop", PERS, false, "Speak all text whenever we stop"},
+ {BP_SPEAK_WORDS, "SpeakWords", PERS, false, "Speak words as they are written"},
+ {BP_CONTROL_MODE_HAS_HALT, "ControlHasHalt", PERS, false, "Force Control Mode to provide a stop action (triggering clipboard/speech)"},
+#ifdef TARGET_OS_MAC
+ {BP_CONTROL_MODE_HAS_EDIT, "ControlHasEdit", PERS, false, "Provide editing functions in control mode (forward & backward movement & deletion)"},
+#else
+ {BP_CONTROL_MODE_HAS_EDIT, "ControlHasEdit", PERS, true, "Provide editing functions in control mode (forward & backward movement & deletion)"},
+#endif
+ {BP_CONTROL_MODE_HAS_COPY, "ControlHasCopy", PERS, true, "Provide copy-to-clipboard actions in Control Mode (if platforms supports)"},
+ {BP_CONTROL_MODE_HAS_SPEECH, "ControlHasSpeech", PERS, true, "Provide speech actions in Control Mode (if platform supports)"},
#ifdef TARGET_OS_IPHONE
{BP_CUSTOM_TILT, "CustomTilt", PERS, false, "Use custom tilt axes"},
{BP_DOUBLE_X, "DoubleXCoords", PERS, false, "Double X-coordinate of touch"},
diff --git a/Src/DasherCore/StylusFilter.h b/Src/DasherCore/StylusFilter.h
index 8b9ea79..3dd1905 100644
--- a/Src/DasherCore/StylusFilter.h
+++ b/Src/DasherCore/StylusFilter.h
@@ -9,7 +9,9 @@ namespace Dasher {
class CStylusFilter : public CDefaultFilter {
public:
CStylusFilter(Dasher::CEventHandler * pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName);
-
+ ///Override DefaultFilter (which supports pause), as we don't
+ /// - motion requires continually holding stylus against screen
+ virtual bool supportsPause() {return false;}
virtual bool Timer(int Time, CDasherView *pView, CDasherModel *pModel, Dasher::VECTOR_SYMBOL_PROB *pAdded, int *pNumDeleted, CExpansionPolicy **pol);
virtual void KeyDown(int iTime, int iId, CDasherView *pView, CDasherModel *pModel, CUserLogBase *pUserLog);
virtual void KeyUp(int iTime, int iId, CDasherView *pView, CDasherModel *pModel);
diff --git a/Src/Gtk2/DasherControl.h b/Src/Gtk2/DasherControl.h
index a3ef525..669ceae 100644
--- a/Src/Gtk2/DasherControl.h
+++ b/Src/Gtk2/DasherControl.h
@@ -130,6 +130,10 @@ public:
///Override to broadcast signal...
virtual void Stop();
+
+ virtual std::string GetAllContext() {
+ throw "Hack to make Gtk2 compile, should not be called as supportsSpeech()/supportsClipboard()==false";
+ }
private:
// virtual void CreateSettingsStore();
diff --git a/Src/MacOSX/COSXDasherControl.h b/Src/MacOSX/COSXDasherControl.h
index 312aa19..b47a845 100644
--- a/Src/MacOSX/COSXDasherControl.h
+++ b/Src/MacOSX/COSXDasherControl.h
@@ -45,6 +45,8 @@ public:
void SetParameter(NSString *aKey, id aValue);
NSDictionary *ParameterDictionary();
void goddamn(unsigned long iTime, bool bForceRedraw);
+ std::string GetAllContext();
+ void ClearAllContext();
private:
virtual void ScanAlphabetFiles(std::vector<std::string> &vFileList);
@@ -57,7 +59,10 @@ private:
virtual void WriteTrainFile(const std::string &strNewText);
virtual void StartTimer();
virtual void ShutdownTimer();
-
+ virtual bool SupportsSpeech();
+ virtual void Speak(const std::string &strText, bool bInterrupt);
+ virtual bool SupportsClipboard() {return true;}
+ virtual void CopyToClipboard(const std::string &strText);
///
/// Pass events coming from the core to the appropriate handler.
///
diff --git a/Src/MacOSX/COSXDasherControl.mm b/Src/MacOSX/COSXDasherControl.mm
index 72450da..eec13c1 100644
--- a/Src/MacOSX/COSXDasherControl.mm
+++ b/Src/MacOSX/COSXDasherControl.mm
@@ -288,4 +288,25 @@ void COSXDasherControl::SetParameter(NSString *aKey, id aValue) {
}
}
+bool COSXDasherControl::SupportsSpeech() {
+ return [dasherApp supportsSpeech];
+}
+
+void COSXDasherControl::Speak(const std::string &strText, bool bInterrupt) {
+ [dasherApp speak:NSStringFromStdString(strText) interrupt:bInterrupt];
+}
+
+void COSXDasherControl::CopyToClipboard(const std::string &strText) {
+ [dasherApp copyToClipboard:NSStringFromStdString(strText)];
+}
+
+std::string COSXDasherControl::GetAllContext() {
+ return StdStringFromNSString([dasherEdit allContext]);
+}
+
+void COSXDasherControl::ClearAllContext() {
+ [dasherEdit clearContext];
+ CDasherInterfaceBase::ClearAllContext();
+}
+
diff --git a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
index 0d89023..a86a47b 100755
--- a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
+++ b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
@@ -160,8 +160,6 @@
1974FE6F0714861B00B95DA0 /* ZippyCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946CABA0481AD440000000A /* ZippyCache.m */; };
1974FE700714861B00B95DA0 /* ZippyString.m in Sources */ = {isa = PBXBuildFile; fileRef = 1946CABC0481AD440000000A /* ZippyString.m */; };
1974FE780714861B00B95DA0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
- 19875621071AFB470034ECCB /* Chatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1987561F071AFB470034ECCB /* Chatter.h */; };
- 19875622071AFB470034ECCB /* Chatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 19875620071AFB470034ECCB /* Chatter.m */; };
1988ABBD0C9FF97000D97977 /* GameMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 1988ABB80C9FF97000D97977 /* GameMessages.h */; };
1988ABBE0C9FF97000D97977 /* GameStatistics.h in Headers */ = {isa = PBXBuildFile; fileRef = 1988ABB90C9FF97000D97977 /* GameStatistics.h */; };
1988ABBF0C9FF97000D97977 /* PinyinParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1988ABBA0C9FF97000D97977 /* PinyinParser.cpp */; };
@@ -363,6 +361,8 @@
335901B41009E5A900821255 /* ConversionHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 335901B31009E5A900821255 /* ConversionHelper.cpp */; };
335DB0FB100B332C006DB155 /* alphabet.spyDict.xml in Resources */ = {isa = PBXBuildFile; fileRef = 335DB0FA100B332C006DB155 /* alphabet.spyDict.xml */; };
335DB101100B3358006DB155 /* training_spyDict.txt in Resources */ = {isa = PBXBuildFile; fileRef = 335DB100100B3358006DB155 /* training_spyDict.txt */; };
+ 339055E81195FBD0001BE240 /* Queue.h in Headers */ = {isa = PBXBuildFile; fileRef = 339055E61195FBD0001BE240 /* Queue.h */; };
+ 339055E91195FBD0001BE240 /* Queue.m in Sources */ = {isa = PBXBuildFile; fileRef = 339055E71195FBD0001BE240 /* Queue.m */; };
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 */; };
@@ -567,8 +567,6 @@
1974FD9C07145C6500B95DA0 /* Credits.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
1974FE7A0714861B00B95DA0 /* Dasher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Dasher.app; sourceTree = BUILT_PRODUCTS_DIR; };
1974FE7C0714861B00B95DA0 /* Info-Dasher.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Dasher.plist"; sourceTree = SOURCE_ROOT; };
- 1987561F071AFB470034ECCB /* Chatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chatter.h; sourceTree = "<group>"; };
- 19875620071AFB470034ECCB /* Chatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Chatter.m; sourceTree = "<group>"; };
1988ABB70C9FF97000D97977 /* GameLevel.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = GameLevel.cpp; sourceTree = "<group>"; };
1988ABB80C9FF97000D97977 /* GameMessages.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GameMessages.h; sourceTree = "<group>"; };
1988ABB90C9FF97000D97977 /* GameStatistics.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GameStatistics.h; sourceTree = "<group>"; };
@@ -779,6 +777,8 @@
335901B31009E5A900821255 /* ConversionHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionHelper.cpp; sourceTree = "<group>"; };
335DB0FA100B332C006DB155 /* alphabet.spyDict.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = alphabet.spyDict.xml; sourceTree = "<group>"; };
335DB100100B3358006DB155 /* training_spyDict.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_spyDict.txt; sourceTree = "<group>"; };
+ 339055E61195FBD0001BE240 /* Queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Queue.h; sourceTree = "<group>"; };
+ 339055E71195FBD0001BE240 /* Queue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Queue.m; sourceTree = "<group>"; };
33ABFEC40FC379EA00EA2BA5 /* ButtonMultiPress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ButtonMultiPress.cpp; sourceTree = "<group>"; };
33ABFEC50FC379EA00EA2BA5 /* ButtonMultiPress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButtonMultiPress.h; sourceTree = "<group>"; };
33E173A70F3E0B6400D19B38 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
@@ -844,8 +844,6 @@
19E1AE4E0B0DB73300F3466C /* COSXDasherScreen.mm */,
19B57A74080D4E4900BCE3C6 /* AppWatcher.h */,
19B57A72080D4E4000BCE3C6 /* AppWatcher.m */,
- 1987561F071AFB470034ECCB /* Chatter.h */,
- 19875620071AFB470034ECCB /* Chatter.m */,
19C49619045029A40000000A /* DasherApp.h */,
19EEDB310450E75F0000000A /* DasherApp.mm */,
1904CDA5048813400000000A /* DasherEdit.h */,
@@ -879,6 +877,8 @@
19F36D8D0B18B60E002F41F1 /* ZippyStringGlyph.m */,
19F36D8A0B18B60E002F41F1 /* ZippyStringImage.h */,
19F36D8B0B18B60E002F41F1 /* ZippyStringImage.m */,
+ 339055E61195FBD0001BE240 /* Queue.h */,
+ 339055E71195FBD0001BE240 /* Queue.m */,
);
name = "Mac OS X Classes";
sourceTree = "<group>";
@@ -1397,7 +1397,6 @@
198EC7B407153D6E00474B38 /* KeyboardEvent.h in Headers */,
198EC7B707153D6E00474B38 /* LowLevelKeyboardHandling.h in Headers */,
198EC7B807153D6E00474B38 /* UnicharGenerator.h in Headers */,
- 19875621071AFB470034ECCB /* Chatter.h in Headers */,
19B57A75080D4E4900BCE3C6 /* AppWatcher.h in Headers */,
190257FD0B0C981300178CCD /* DasherEdit.h in Headers */,
190258010B0C981900178CCD /* DasherViewOpenGL.h in Headers */,
@@ -1498,6 +1497,7 @@
33135356102C6D8E00E28220 /* ButtonMode.h in Headers */,
3300115310A2EA7700D31B1D /* ExpansionPolicy.h in Headers */,
3300114910A2E9C900D31B1D /* DelayedDraw.h in Headers */,
+ 339055E81195FBD0001BE240 /* Queue.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1761,7 +1761,6 @@
198EC7B507153D6E00474B38 /* KeyboardEvent.m in Sources */,
198EC7B607153D6E00474B38 /* LowLevelKeyboardHandling.c in Sources */,
198EC7B907153D6E00474B38 /* UnicharGenerator.m in Sources */,
- 19875622071AFB470034ECCB /* Chatter.m in Sources */,
19B57A73080D4E4000BCE3C6 /* AppWatcher.m in Sources */,
190257FC0B0C980800178CCD /* DasherApp.mm in Sources */,
190257FE0B0C981400178CCD /* DasherEdit.mm in Sources */,
@@ -1846,6 +1845,7 @@
33135353102C6D8E00E28220 /* CompassMode.cpp in Sources */,
33135355102C6D8E00E28220 /* ButtonMode.cpp in Sources */,
3300115210A2EA7700D31B1D /* ExpansionPolicy.cpp in Sources */,
+ 339055E91195FBD0001BE240 /* Queue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/Src/MacOSX/DasherApp.h b/Src/MacOSX/DasherApp.h
index 5217f2f..c120c68 100644
--- a/Src/MacOSX/DasherApp.h
+++ b/Src/MacOSX/DasherApp.h
@@ -14,6 +14,7 @@
//#import "DasherAppInterface.h"
#import "COSXDasherScreen.h"
#import "DasherViewCocoa.h"
+#import "Queue.h"
@class AppWatcher;
@class DasherView;
@@ -27,7 +28,8 @@
IBOutlet AppWatcher *appWatcher;
NSTimer *_timer;
-
+ Queue *spQ;
+ NSSpeechSynthesizer *spSyn;
}
- (void)start;
@@ -56,5 +58,7 @@
- (void)setTimer:(NSTimer *)newTimer;
- (void)timerCallback:(NSTimer *)aTimer;
- (void)dealloc;
-
+- (bool)supportsSpeech;
+- (void)speak:(NSString *)sText interrupt:(bool)bInt;
+- (void)copyToClipboard:(NSString *)sText;
@end
diff --git a/Src/MacOSX/DasherApp.mm b/Src/MacOSX/DasherApp.mm
index 66a3273..2aed2cf 100644
--- a/Src/MacOSX/DasherApp.mm
+++ b/Src/MacOSX/DasherApp.mm
@@ -80,7 +80,8 @@
{
if (self = [super init])
{
- [self setAquaDasherControl:new COSXDasherControl(self)];
+ [self setAquaDasherControl:new COSXDasherControl(self)];
+ spQ = [[Queue alloc] init];
}
return self;
@@ -196,4 +197,41 @@
[super dealloc];
}
+- (bool)supportsSpeech {
+ if (!spSyn) {
+ //hmmm. don't see any way for this to (indicate) fail(ure)...???
+ spSyn = [[NSSpeechSynthesizer alloc] init];
+ [spSyn setDelegate:self];
+ }
+ return YES;
+}
+
+- (void)speak:(NSString *)sText interrupt:(bool)bInt {
+ if (bInt)
+ [spQ clear];
+ else {
+ @synchronized(spQ) {
+ if ([spSyn isSpeaking] || [spQ hasItems]) {
+ [spQ push:sText];
+ return;
+ }
+ }
+ }
+ [spSyn startSpeakingString:sText];
+}
+
+-(void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success {
+ @synchronized(spQ) {
+ if ([spQ hasItems]) {
+ [spSyn startSpeakingString:[spQ pop]];
+ }
+ }
+}
+
+-(void)copyToClipboard:(NSString *)sText {
+ NSPasteboard *pboard = [NSPasteboard generalPasteboard];
+ [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ [pboard setString:sText forType:NSStringPboardType];
+}
+
@end
diff --git a/Src/MacOSX/DasherEdit.h b/Src/MacOSX/DasherEdit.h
index a74d700..2618510 100644
--- a/Src/MacOSX/DasherEdit.h
+++ b/Src/MacOSX/DasherEdit.h
@@ -19,5 +19,7 @@
- (void)outputCallback:(NSString *)aString targetApp:(AXUIElementRef)aTargetApp;
- (void)deleteCallback:(NSString *)s targetApp:(AXUIElementRef)aTargetApp;
- (NSString *)textAtOffset:(int)iOffset Length:(int)iLength;
+- (NSString *)allContext;
+- (void)clearContext;
@end
diff --git a/Src/MacOSX/DasherEdit.mm b/Src/MacOSX/DasherEdit.mm
index 64cbc8c..1ce3343 100644
--- a/Src/MacOSX/DasherEdit.mm
+++ b/Src/MacOSX/DasherEdit.mm
@@ -9,7 +9,6 @@
#import "DasherEdit.h"
#import "PreferencesController.h"
#import "UnicharGenerator.h"
-#import "Chatter.h"
#import "../Common/Common.h"
#import <Carbon/Carbon.h>
@@ -38,7 +37,6 @@
[self sendString:aString toTargetApp:aTargetApp];
dasherIsModifyingText = NO;
[allTextEntered appendString:aString];
- [[Chatter sharedInstance] addToBufferedText:aString];
}
- (void)deleteCallback:(NSString *)s targetApp:(AXUIElementRef)aTargetApp
@@ -54,7 +52,6 @@
}
dasherIsModifyingText = NO;
[allTextEntered deleteCharactersInRange:NSMakeRange([allTextEntered length]-len, len)];
- [[Chatter sharedInstance] removeFromBufferedText:s];
}
-(NSString *)textAtOffset:(int)iOffset Length:(int)iLength {
@@ -62,5 +59,12 @@
return [allTextEntered substringWithRange:NSMakeRange(iOffset,iLength)];
}
+-(NSString *)allContext {
+ return allTextEntered;
+}
+
+-(void)clearContext {
+ [allTextEntered setString:@""];
+}
@end
diff --git a/Src/MacOSX/DasherViewAqua.mm b/Src/MacOSX/DasherViewAqua.mm
index f45bce3..6b8234d 100755
--- a/Src/MacOSX/DasherViewAqua.mm
+++ b/Src/MacOSX/DasherViewAqua.mm
@@ -17,7 +17,6 @@
#import "ZippyCache.h"
#import "ZippyString.h"
-#import "Chatter.h"
#import "DasherUtil.h"
#import "DasherApp.h"
diff --git a/Src/MacOSX/DasherViewOpenGL.mm b/Src/MacOSX/DasherViewOpenGL.mm
index 9d01ccc..abaf937 100755
--- a/Src/MacOSX/DasherViewOpenGL.mm
+++ b/Src/MacOSX/DasherViewOpenGL.mm
@@ -15,7 +15,6 @@
#import <sys/time.h>
-#import "Chatter.h"
#import "DasherUtil.h"
#import "DasherApp.h"
#import "GLUtils.h"
diff --git a/Src/MacOSX/Queue.h b/Src/MacOSX/Queue.h
new file mode 100644
index 0000000..c84733b
--- /dev/null
+++ b/Src/MacOSX/Queue.h
@@ -0,0 +1,21 @@
+//
+// Queue.h
+// Dasher
+//
+// Created by Alan Lawrence on 08/05/2010.
+// Copyright 2010 Cavendish Laboratory. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+ interface Queue : NSObject {
+ NSMutableArray *m_in, *m_out;
+}
+
+-(id)init;
+-(void)push:(id)obj;
+-(id)pop;
+-(bool)hasItems;
+-(void)clear;
+ end
diff --git a/Src/MacOSX/Queue.m b/Src/MacOSX/Queue.m
new file mode 100644
index 0000000..43d029d
--- /dev/null
+++ b/Src/MacOSX/Queue.m
@@ -0,0 +1,57 @@
+//
+// Queue.m
+// Dasher
+//
+// Created by Alan Lawrence on 08/05/2010.
+// Copyright 2010 Cavendish Laboratory. All rights reserved.
+//
+
+#import "Queue.h"
+
+
+ implementation Queue
+
+-(id)init {
+ if (self = [super init]) {
+ m_in = [NSMutableArray arrayWithCapacity:5];
+ [m_in retain];
+ m_out = [NSMutableArray arrayWithCapacity:5];
+ [m_out retain];
+ }
+ return self;
+}
+
+-(void)clear {
+ [m_in removeAllObjects];
+ [m_out removeAllObjects];
+ NSLog(@"Cleared\n");
+}
+
+-(void)push:(id)obj {
+ [m_in addObject:obj];
+ NSLog(@"Push %@, now %i items\n",obj,[m_in count]+[m_out count]);
+}
+
+-(id)pop {
+ NSLog(@"Pop - %i+%i items...",[m_in count],[m_out count]);
+ if ([m_out count]==0) {
+ [m_out addObjectsFromArray:[[m_in reverseObjectEnumerator] allObjects]];
+ [m_in removeAllObjects];
+ }
+ id ret= [m_out lastObject];
+ [m_out removeLastObject];
+ NSLog(@"now %i + %i\n",[m_in count],[m_out count]);
+ return ret;
+}
+
+-(bool)hasItems {
+ return [m_in count]>0 || [m_out count]>0;
+}
+
+-(void)dealloc {
+ [m_in release];
+ [m_out release];
+ [super dealloc];
+}
+
+ end
diff --git a/Src/Win32/Dasher.cpp b/Src/Win32/Dasher.cpp
index dcd4ce8..cd678e6 100644
--- a/Src/Win32/Dasher.cpp
+++ b/Src/Win32/Dasher.cpp
@@ -16,6 +16,9 @@
#include "Common/WinOptions.h"
+//ACL not sure what headers we need to include to get clipboard operations, but may need:
+//#include <afxpriv.h>
+
#ifndef _WIN32_WCE
#include <sys/stat.h>
#endif
@@ -59,13 +62,6 @@ void CDasher::CreateModules() {
RegisterModule(new CDasherMouseInput(m_pEventHandler, m_pSettingsStore, m_pCanvas->getwindow()));
}
-void CDasher::Stop() {
- if (!GetBoolParameter(BP_DASHER_PAUSED)) {
- CDasherInterfaceBase::Stop();
- if (m_pEdit) m_pEdit->HandleStop();
- }
-}
-
void CDasher::Main() {
if(m_pCanvas) {
m_pCanvas->DoFrame();
@@ -301,3 +297,60 @@ void CDasher::Move(int iX, int iY, int iWidth, int iHeight) {
void CDasher::TakeFocus() {
// TODO: Implement me
}
+#ifndef _WIN32_WCE
+bool CDasher::SupportsSpeech() {
+ if (!m_bAttemptedSpeech) {
+ //try to create speech synthesizer lazily, saving resources if no speech req'd.
+ HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
+
+ if(hr!=S_OK)
+ pVoice=0;
+ else if (pVoice) {
+ //ACL Do we need to check pVoice? copying old code again, previous comment said:
+ // TODO: Why is this needed?
+ pVoice->Speak(L"",SPF_ASYNC,NULL);
+ }
+ m_bAttemptedSpeech = true;
+ }
+ return pVoice;
+}
+
+void CDasher::Speak(const string &strText, bool bInterrupt) {
+ //ACL TODO - take account of bInterrupt
+ if (pVoice)
+ pVoice->Speak(strText.c_str(), SPF_ASYNC, NULL);
+}
+#endif
+bool CDasher::SupportsClipboard {
+ return true;
+}
+
+void CDasher::CopyToClipboard(const string &strText) {
+ CString cText(strText.c_str());
+ if (OpenClipboard())
+ {
+ EmptyClipboard(); //also frees memory containing any previous data
+
+ //Allocate global memory for string - enough for characters + NULL.
+ HGLOBAL hClipboardData = GlobalAlloc(GMEM_DDESHARE, strData.GetLength()+1);
+
+ //GlobalLock returns a pointer to the data associated with the handle returned from GlobalAlloc
+ char * pchData = (char*)GlobalLock(hClipboardData);
+
+ //now fill it...
+ strcpy(pchData, LPCSTR(strData));
+
+ //Unlock memory, i.e. release our access to it -
+ // but don't free it (with GlobalFree), as it will "belong"
+ // to the clipboard.
+ GlobalUnlock(hClipboardData);
+
+ //Now, point the clipboard at that global memory...
+ //ACL may have to use CF_TEXT or CF_OEMTEXT prior to WinNT/2K???
+ SetClipboardData(CF_UNICODETEXT,hClipboardData);
+
+ //Finally, unlock the clipboard (i.e. a pointer to the data on it!)
+ // so that other applications can see / modify it
+ CloseClipboard();
+ }
+}
diff --git a/Src/Win32/Dasher.h b/Src/Win32/Dasher.h
index 4d15c09..b57a5c0 100644
--- a/Src/Win32/Dasher.h
+++ b/Src/Win32/Dasher.h
@@ -4,6 +4,7 @@
#include "../DasherCore/DasherInterfaceBase.h"
#include "../DasherCore/UserLog.h"
+#include <sapi.h>
#include <string>
#include <vector>
@@ -46,11 +47,16 @@ public:
void GameMessageOut(int message, const void* messagedata);
virtual void WriteTrainFile(const std::string &strNewText);
- ///Override to implement copy/speak-on-stop
- void Stop();
void Main();
-
+#ifndef _WIN32_WCE
+ //on WinCE, do not support speech - so use defaults from CDasherInterfaceBase
+ bool SupportsSpeech();
+ void Speak(const std::string &text, bool bInterrupt);
+#endif
+ bool SupportsClipboard() {return true;};
+ void CopyToClipboard(const std::string &text);
+
private:
virtual void ScanAlphabetFiles(std::vector<std::string> &vFileList);
@@ -69,7 +75,10 @@ private:
CCanvas *m_pCanvas;
CDashEditbox *m_pEdit;
-
HWND m_hParent;
+#ifndef _WIN32_WCE
+ ISpVoice *pVoice;
+ bool attemptedSpeech;
+#endif
};
}
diff --git a/Src/Win32/DasherWindow.cpp b/Src/Win32/DasherWindow.cpp
index c70ba0d..a156742 100644
--- a/Src/Win32/DasherWindow.cpp
+++ b/Src/Win32/DasherWindow.cpp
@@ -137,28 +137,6 @@ HWND CDasherWindow::Create() {
if (!hSplitter)
return 0;
- // Add extra control nodes
-
- m_pDasher->RegisterNode( Dasher::CControlManager::CTL_USER, "Speak", -1 );
- m_pDasher->RegisterNode( Dasher::CControlManager::CTL_USER+1, "All", -1 );
- m_pDasher->RegisterNode( Dasher::CControlManager::CTL_USER+2, "New", -1 );
- m_pDasher->RegisterNode( Dasher::CControlManager::CTL_USER+3, "Repeat", -1 );
-
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_USER, Dasher::CControlManager::CTL_ROOT, -2);
-
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_USER+1, Dasher::CControlManager::CTL_USER, -2);
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_USER+2, Dasher::CControlManager::CTL_USER, -2);
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_USER+3, Dasher::CControlManager::CTL_USER, -2);
-
- m_pDasher->ConnectNode(-1, Dasher::CControlManager::CTL_USER+1, -2);
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_ROOT, Dasher::CControlManager::CTL_USER+1, -2);
-
- m_pDasher->ConnectNode(-1, Dasher::CControlManager::CTL_USER+2, -2);
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_ROOT, Dasher::CControlManager::CTL_USER+2, -2);
-
- m_pDasher->ConnectNode(-1, Dasher::CControlManager::CTL_USER+3, -2);
- m_pDasher->ConnectNode(Dasher::CControlManager::CTL_ROOT, Dasher::CControlManager::CTL_USER+3, -2);
-
m_pGameModeHelper = 0;
return hWnd;
diff --git a/Src/Win32/Widgets/AdvancedPage.cpp b/Src/Win32/Widgets/AdvancedPage.cpp
index 5ae21b6..1ce2156 100644
--- a/Src/Win32/Widgets/AdvancedPage.cpp
+++ b/Src/Win32/Widgets/AdvancedPage.cpp
@@ -42,9 +42,10 @@ static menuentry menutable[] = {
{BP_SHOW_SLIDER, IDC_CHECK2},
{APP_BP_TIME_STAMP, IDC_TIMESTAMP},
{BP_CONTROL_MODE, IDC_CONTROLMODE}, // Not global setting - specific to editbox/widget
- {APP_BP_SPEECH_WORD, IDC_CHECK4},
- {APP_BP_SPEECH_MODE, IDC_CHECK3},
- {APP_BP_COPY_ALL_ON_STOP, IDC_COPYONSTOP}
+ {BP_SPEAK_WORDS, IDC_CHECK4},
+ {BP_SPEAK_ALL_ON_STOP, IDC_CHECK3},
+ {BP_COPY_ALL_ON_STOP, IDC_COPYONSTOP}
+//ACL TODO BP_CONTROL_MODE_HAS_HALT, BP_CONTROL_MODE_HAS_SPEECH, BP_CONTROL_MODE_HAS_COPY - and perhaps automatically disable the latter two in direct mode, too?
};
std::string CAdvancedPage::GetControlText(HWND Dialog, int ControlID)
diff --git a/Src/Win32/Widgets/Edit.cpp b/Src/Win32/Widgets/Edit.cpp
index db0ca3f..2bf5823 100644
--- a/Src/Win32/Widgets/Edit.cpp
+++ b/Src/Win32/Widgets/Edit.cpp
@@ -31,9 +31,6 @@
#include "FilenameGUI.h"
#include "../resource.h"
#include "../../DasherCore/DasherInterfaceBase.h"
-#ifndef _WIN32_WCE
-#include "../ActionSpeech.h"
-#endif
#include "../Common/DasherEncodingToCP.h"
@@ -60,20 +57,6 @@ CEdit::CEdit(CAppSettings *pAppSettings) {
#ifndef _WIN32_WCE
m_Font = GetCodePageFont(CodePage, 14);
#endif
-
- // TODO: Generalise this (and don't duplicate - read directly from
- // text buffer).
- speech.resize(0);
-
-#ifndef _WIN32_WCE
- // TODO: Generalise actions, implement those present in Linux
- // version.
- m_pActionSpeech = new CActionSpeech;
- m_pActionSpeech->Activate();
-#else
- m_pActionSpeech = 0;
-#endif
-
}
HWND CEdit::Create(HWND hParent, bool bNewWithDate) {
@@ -93,11 +76,6 @@ CEdit::~CEdit() {
delete m_FilenameGUI;
if(FileHandle != INVALID_HANDLE_VALUE)
CloseHandle(FileHandle);
-
-#ifndef _WIN32_WCE
- m_pActionSpeech->Deactivate();
- delete m_pActionSpeech;
-#endif
}
void CEdit::Move(int x, int y, int Width, int Height) {
@@ -393,17 +371,6 @@ void CEdit::Copy() {
*/
}
-void CEdit::CopyAll() {
- // One might think this would lead to flickering of selecting and
- // unselecting. It doesn't seem to. Using the clipboard directly
- // is fiddly, so this cheat is useful.
- DWORD start, finish;
- SendMessage(EM_GETSEL, (LONG) & start, (LONG) & finish);
- SendMessage(EM_SETSEL, 0, -1);
- SendMessage(WM_COPY, 0, 0);
- SendMessage(EM_SETSEL, (LONG) start, (LONG) finish);
-}
-
void CEdit::Paste() {
SendMessage(WM_PASTE, 0, 0);
}
@@ -414,7 +381,6 @@ void CEdit::SelectAll() {
void CEdit::Clear() {
SendMessage(WM_SETTEXT, 0, (LPARAM) TEXT(""));
- speech.resize(0);
}
void CEdit::SetEncoding(Dasher::Opts::FileEncodingFormats Encoding) {
@@ -523,19 +489,6 @@ void CEdit::output(const std::string &sText) {
#endif
}
m_Output += sText;
-
- UTF8string_to_wstring(sText, newchar);
- speech += newchar;
-
- // Slightly hacky word by word preview
- if(newchar == L" ") {
- if(m_pAppSettings->GetBoolParameter(APP_BP_SPEECH_WORD) && m_pActionSpeech->GetActive())
- m_pActionSpeech->Preview(m_strCurrentWord);
- m_strCurrentWord = L"";
- }
- else {
- m_strCurrentWord += newchar;
- }
}
void CEdit::Move(int iDirection, int iDist) {
@@ -909,55 +862,12 @@ if(m_pAppSettings->GetLongParameter(APP_LP_STYLE) == APP_STYLE_DIRECT) {
#endif
}
- // Shorten the speech buffer (?)
- if(speech.length() >= iLength) {
- speech.resize(speech.length() - iLength);
- }
-
- // Shorten the speech buffer (?)
- if(m_strCurrentWord.length() >= iLength) {
- m_strCurrentWord.resize(m_strCurrentWord.length() - iLength);
- }
-
// And the output buffer (?)
if(m_Output.length() >= iLength) {
m_Output.resize(m_Output.length() - iLength);
}
}
-void CEdit::speak(int what) {
- if(!m_pActionSpeech->GetActive())
- return;
-
- // TODO: The remainder of this function is somewhat horrible and hacky...
-
- // TODO: Horrible hack - don't speak in direct entry mode
- if(m_pAppSettings->GetLongParameter(APP_LP_STYLE) == APP_STYLE_DIRECT)
- return;
-
- std::wstring strSpeech;
-
- if(what == 1) { // All
- int speechlength = GetWindowTextLength();
- LPTSTR allspeech = new TCHAR[speechlength + 1];
- GetWindowText(allspeech, speechlength + 1);
- strSpeech = allspeech;
- lastspeech = allspeech;
- delete allspeech;
- speech.resize(0);
- }
- else if(what == 2) { // New
- strSpeech = speech;
- lastspeech = speech;
- speech.resize(0);
- }
- else if(what == 3) {
- strSpeech = lastspeech;
- }
-
- m_pActionSpeech->Execute(strSpeech);
-}
-
void CEdit::SetNewWithDate(bool bNewWithDate) {
if(m_FilenameGUI)
m_FilenameGUI->SetNewWithDate(bNewWithDate);
@@ -1003,10 +913,5 @@ void CEdit::HandleEditEvent(Dasher::CEvent *pEvent) {
}
void CEdit::HandleStop() {
- // TODO: These should be more generally implemented as
- if(m_pAppSettings->GetBoolParameter(APP_BP_SPEECH_MODE))
- speak(2);
-
- if(m_pAppSettings->GetBoolParameter(APP_BP_COPY_ALL_ON_STOP))
- CopyAll();
+ //speech and copy-to-clipboard are now global/platform-independent...
}
diff --git a/Src/Win32/Widgets/Edit.h b/Src/Win32/Widgets/Edit.h
index 3ca9df7..c6d0773 100644
--- a/Src/Win32/Widgets/Edit.h
+++ b/Src/Win32/Widgets/Edit.h
@@ -136,9 +136,6 @@ class CEdit : public ATL::CWindowImpl<CEdit> {
// remove the previous character
void deletetext(const std::string & sText);
- // speak text
- void speak(int what);
-
void SetNewWithDate(bool bNewWithDate);
void HandleEvent(Dasher::CEvent *pEvent);
@@ -184,19 +181,11 @@ class CEdit : public ATL::CWindowImpl<CEdit> {
#ifdef _UNICODE
INPUT fakekey[2];
#endif
-
- Tstring speech;
- Tstring lastspeech;
- Tstring newchar;
void InsertText(Tstring InsertText); // add symbol to edit control
CAppSettings *m_pAppSettings;
HWND m_hWnd;
-
- CDasherAction *m_pActionSpeech;
-
- std::wstring m_strCurrentWord;
};
#endif /* #ifndef __Edit_h__ */
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.h b/Src/iPhone/Classes/CDasherInterfaceBridge.h
index 1fafcd1..4d92c56 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.h
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.h
@@ -47,6 +47,9 @@ public:
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);
+ std::string GetAllContext();
private:
virtual void ScanAlphabetFiles(std::vector<std::string> &vFileList);
virtual void ScanColourFiles(std::vector<std::string> &vFileList);
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.mm b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
index c7f8c55..1594f5b 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.mm
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
@@ -13,7 +13,7 @@
#import "DasherAppDelegate.h"
#import "Event.h"
#import "CalibrationController.h"
-
+#import "ControlManager.h"
#import "../Common/Common.h"
#import <iostream>
@@ -24,9 +24,9 @@
using namespace std;
-
CDasherInterfaceBridge::CDasherInterfaceBridge(DasherAppDelegate *aDasherApp) : dasherApp(aDasherApp) {
Realize();
+ [dasherApp setAlphabet:GetAlphabet()];
}
void CDasherInterfaceBridge::CreateModules() {
@@ -167,7 +167,9 @@ void CDasherInterfaceBridge::ExternalEventHandler(Dasher::CEvent *pEvent) {
Dasher::CParameterNotificationEvent *pEvt(static_cast<Dasher::CParameterNotificationEvent *>(pEvent));
if (pEvt->m_iParameter == LP_MAX_BITRATE || pEvt->m_iParameter == LP_BOOSTFACTOR)
[dasherApp notifySpeedChange];
- }
+ else if (pEvt->m_iParameter == SP_ALPHABET_ID)
+ [dasherApp setAlphabet:GetAlphabet()];
+ }
break;
case EV_EDIT:
{
@@ -200,7 +202,40 @@ void CDasherInterfaceBridge::ExternalEventHandler(Dasher::CEvent *pEvent) {
break;
}
case EV_CONTROL:
- NSLog(@"ExternalEventHandler, m_iEventType = EV_CONTROL");
+ switch (static_cast<CControlEvent *>(pEvent)->m_iID) {
+ case CControlManager::CTL_MOVE_FORWARD_CHAR:
+ [dasherApp move:EDIT_CHAR forwards:YES]; break;
+ case CControlManager::CTL_MOVE_FORWARD_WORD:
+ [dasherApp move:EDIT_WORD forwards:YES]; break;
+ case CControlManager::CTL_MOVE_FORWARD_LINE:
+ [dasherApp move:EDIT_LINE forwards:YES]; break;
+ case CControlManager::CTL_MOVE_FORWARD_FILE:
+ [dasherApp move:EDIT_FILE forwards:YES]; break;
+ case CControlManager::CTL_MOVE_BACKWARD_CHAR:
+ [dasherApp move:EDIT_CHAR forwards:NO]; break;
+ case CControlManager::CTL_MOVE_BACKWARD_WORD:
+ [dasherApp move:EDIT_WORD forwards:NO]; break;
+ case CControlManager::CTL_MOVE_BACKWARD_LINE:
+ [dasherApp move:EDIT_LINE forwards:NO]; break;
+ case CControlManager::CTL_MOVE_BACKWARD_FILE:
+ [dasherApp move:EDIT_FILE forwards:NO]; break;
+ case CControlManager::CTL_DELETE_FORWARD_CHAR:
+ [dasherApp del:EDIT_CHAR forwards:YES]; break;
+ case CControlManager::CTL_DELETE_FORWARD_WORD:
+ [dasherApp del:EDIT_WORD forwards:YES]; break;
+ case CControlManager::CTL_DELETE_FORWARD_LINE:
+ [dasherApp del:EDIT_LINE forwards:YES]; break;
+ case CControlManager::CTL_DELETE_FORWARD_FILE:
+ [dasherApp del:EDIT_FILE forwards:YES]; break;
+ case CControlManager::CTL_DELETE_BACKWARD_CHAR:
+ [dasherApp del:EDIT_CHAR forwards:NO]; break;
+ case CControlManager::CTL_DELETE_BACKWARD_WORD:
+ [dasherApp del:EDIT_WORD forwards:NO]; break;
+ case CControlManager::CTL_DELETE_BACKWARD_LINE:
+ [dasherApp del:EDIT_LINE forwards:NO]; break;
+ case CControlManager::CTL_DELETE_BACKWARD_FILE:
+ [dasherApp del:EDIT_FILE forwards:NO]; break;
+ }
break;
case EV_COMMAND:
NSLog(@"ExternalEventHandler, m_iEventType = EV_COMMAND");
@@ -230,6 +265,15 @@ void CDasherInterfaceBridge::ExternalEventHandler(Dasher::CEvent *pEvent) {
}
+void CDasherInterfaceBridge::CopyToClipboard(const std::string &strText) {
+ CDasherInterfaceBase::CopyToClipboard(strText);
+ [UIPasteboard generalPasteboard].string=NSStringFromStdString(strText);
+}
+
+string CDasherInterfaceBridge::GetAllContext() {
+ return StdStringFromNSString([dasherApp allText]);
+}
+
int CDasherInterfaceBridge::GetFileSize(const std::string &strFileName) {
struct stat sStatInfo;
diff --git a/Src/iPhone/Classes/DasherAppDelegate.h b/Src/iPhone/Classes/DasherAppDelegate.h
index 0035933..30f2c5e 100644
--- a/Src/iPhone/Classes/DasherAppDelegate.h
+++ b/Src/iPhone/Classes/DasherAppDelegate.h
@@ -11,6 +11,13 @@
#import "CDasherScreenBridge.h"
#import "TextView.h"
+typedef enum {
+ EDIT_CHAR,
+ EDIT_WORD,
+ EDIT_LINE,
+ EDIT_FILE
+} EEditDistance;
+
@class EAGLView;
@interface DasherAppDelegate : UIViewController <UIApplicationDelegate, UIActionSheetDelegate, UITextViewDelegate> {
@@ -27,14 +34,20 @@
BOOL m_bLandscapeSupported;
/// Should really be part of UIViewController (lockable), below...but then, how to find?
UILabel *screenLockLabel;
+
+ NSString *m_wordBoundary, *m_sentenceBoundary, *m_lineBoundary;
}
- (void)startTimer;
- (void)shutdownTimer;
+- (void)setAlphabet:(CAlphabet *)pAlph;
- (void)outputCallback:(NSString *)s;
- (void)deleteCallback:(NSString *)s;
+- (void)move:(EEditDistance)amt forwards:(BOOL)bForwards;
+- (void)del:(EEditDistance)amt forwards:(BOOL)bForwards;
+- (NSString *)allText;
- (void)notifySpeedChange;
-- (NSString *)textAtOffset:(int)offset Length:(int)length;
+- (NSString *)textAtOffset:(unsigned int)offset Length:(unsigned int)length;
- (void)setLockText:(NSString *)s;
- (void)displayMessage:(NSString *)msg ID:(int)iId Type:(int)type;
- (void)setLandscapeSupported:(BOOL)supported;
diff --git a/Src/iPhone/Classes/DasherAppDelegate.mm b/Src/iPhone/Classes/DasherAppDelegate.mm
index 1e0fe2f..a8bf7a1 100644
--- a/Src/iPhone/Classes/DasherAppDelegate.mm
+++ b/Src/iPhone/Classes/DasherAppDelegate.mm
@@ -25,6 +25,9 @@
- (void)speedSlid:(id)slider;
- (CGRect)doLayout:(UIInterfaceOrientation)orient;
@property (retain) UILabel *screenLockLabel;
+ property (nonatomic,retain) NSString *m_wordBoundary;
+ property (nonatomic,retain) NSString *m_sentenceBoundary;
+ property (nonatomic,retain) NSString *m_lineBoundary;
@end
//we can't call setHidden:BOOL with performSelector:withObject:, as passing [NSNumber numberWithBool:YES] (as one should)
@@ -36,7 +39,9 @@
@implementation DasherAppDelegate
@synthesize screenLockLabel;
-
+ synthesize m_wordBoundary;
+ synthesize m_sentenceBoundary;
+ synthesize m_lineBoundary;
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
return NO;
@@ -156,6 +161,7 @@
text.editable = NO;
text.delegate = self;
selectedText.location = selectedText.length = 0;
+ text.selectedRange=selectedText;
messageLabel.backgroundColor = [UIColor grayColor];
messageLabel.textColor = [UIColor whiteColor];
@@ -217,6 +223,9 @@
[self notifySpeedChange];
self.dasherInterface->OnUIRealised(); //that does startAnimation...
doneSetup = YES;
+ //The following will cause the text cursor to be displayed whenever
+ // any change is made to the textbox...
+ [text becomeFirstResponder];
}
- (void)displayMessage:(NSString *)msg ID:(int)iId Type:(int)type {
@@ -313,18 +322,16 @@
- (void)outputCallback:(NSString *)s {
text.text=[text.text stringByReplacingCharactersInRange:selectedText withString:s];
- selectedText.location++;
+ selectedText.location+=[s length];
selectedText.length = 0;
- text.selectedRange = selectedText; //shows keyboard, seems unavoidable :-(
+ text.selectedRange = selectedText;
//This isn't quite right, it jumps up then down again once you have >3 lines...
[text scrollRangeToVisible:selectedText];
}
- (void)deleteCallback:(NSString *)s {
if (selectedText.length == 0) selectedText.location -= (selectedText.length = 1); //select previous character
- text.text=[text.text stringByReplacingCharactersInRange:selectedText withString:@""];
- selectedText.length = 0;
- text.selectedRange = selectedText;
+ [self outputCallback:@""];
}
- (void)applicationWillResignActive:(UIApplication *)application {
@@ -389,13 +396,76 @@
[super dealloc];
}
-- (NSString *)textAtOffset:(int)offset Length:(int)length {
+- (NSString *)textAtOffset:(unsigned int)offset Length:(unsigned int)length {
NSRange range;
- range.location = offset;
- range.length = length;
+ //truncate both endpoints of desired range to lie within text.
+ // (Although requiring offset+length to be within text, has identified many bugs,
+ // the editing functions in control mode are too broken to fix right now! Hence,
+ // copying the Gtk2 behaviour...)
+ range.location = max(0u,min(offset,[text.text length]));
+ range.length = min(length,[text.text length] - range.location);
return [text.text substringWithRange:range];
}
+- (void)setAlphabet:(CAlphabet *)pAlph {
+ self.m_wordBoundary = pAlph->GetSpaceSymbol() ? NSStringFromStdString(pAlph->GetText(pAlph->GetSpaceSymbol())) : @" ";
+ self.m_lineBoundary = (pAlph->GetParagraphSymbol())
+ ? NSStringFromStdString(pAlph->GetText(pAlph->GetParagraphSymbol())) : nil;
+ self.m_sentenceBoundary = (pAlph->GetDefaultContext().length()>0)
+ ? NSStringFromStdString(pAlph->GetDefaultContext())
+ : @".";
+}
+
+- (int)find:(EEditDistance)amt forwards:(BOOL)bForwards {
+ if (amt==EDIT_FILE) return bForwards ? [text.text length] : 0;
+ int pos = selectedText.location;
+ for(;;) {
+ if (bForwards) {
+ if (++pos > [text.text length]) return pos-1;
+ } else {
+ if (--pos < 0) return 0;
+ }
+ NSString *lookFor;
+ switch (amt) {
+ case EDIT_CHAR:
+ //once only, never loop
+ return pos;
+ case EDIT_WORD:
+ lookFor = m_wordBoundary;
+ break;
+ case EDIT_LINE:
+ if (m_lineBoundary && [text.text compare:m_lineBoundary options:0 range:NSMakeRange(pos, [m_lineBoundary length])] == NSOrderedSame)
+ return pos;
+ lookFor = m_lineBoundary;
+ break;
+ }
+ if ([text.text compare:lookFor options:0 range:NSMakeRange(pos, [lookFor length])] == NSOrderedSame)
+ return pos;
+ }
+}
+
+- (void)move:(EEditDistance)amt forwards:(BOOL)bForwards {
+ selectedText.location = [self find:amt forwards:bForwards];
+ selectedText.length=0;
+ text.selectedRange = selectedText;
+ [text scrollRangeToVisible:selectedText];
+}
+
+- (void)del:(EEditDistance)amt forwards:(BOOL)bForwards {
+ int to = [self find:amt forwards:bForwards];
+ if (bForwards) {
+ selectedText.length = to-selectedText.location;
+ } else {
+ selectedText.length = selectedText.location - to;
+ selectedText.location = to;
+ }
+ [self outputCallback:@""];
+}
+
+- (NSString *)allText {
+ return text.text;
+}
+
#pragma mark TextViewDelegate methods
-(void)textViewDidChangeSelection:(UITextView *)textView {
@@ -494,4 +564,4 @@
[self setHidden:YES];
}
- end
\ No newline at end of file
+ end
diff --git a/Src/iPhone/Classes/InputMethodSelector.mm b/Src/iPhone/Classes/InputMethodSelector.mm
index 7474fc3..a3128f2 100644
--- a/Src/iPhone/Classes/InputMethodSelector.mm
+++ b/Src/iPhone/Classes/InputMethodSelector.mm
@@ -59,6 +59,7 @@ SSectionDesc allMeths[] = {
@interface InputMethodSelector ()
@property (retain) NSIndexPath *selectedPath;
+- (void)doSelect;
@end
@implementation InputMethodSelector
diff --git a/Src/iPhone/Classes/MiscSettings.mm b/Src/iPhone/Classes/MiscSettings.mm
index 43f9c01..11a27e3 100644
--- a/Src/iPhone/Classes/MiscSettings.mm
+++ b/Src/iPhone/Classes/MiscSettings.mm
@@ -14,19 +14,52 @@
static SModuleSettings _settings[] = { //note iStep and string description are ignored
{LP_NODE_BUDGET, T_LONG, 400, 10000, 1, 0, ""}, //hopefully appropriate for an iPhone 3GS?
{LP_MARGIN_WIDTH, T_LONG, 100, 900, 1, 0, ""},
+ {LP_DASHER_FONTSIZE, T_LONG, 1, 3, 1, 1, ""},
{BP_AUTO_SPEEDCONTROL, T_BOOL, -1, -1, -1, -1, ""},
{LP_NONLINEAR_X, T_LONG, 0, 10, 1, -1, ""},
{BP_DOUBLE_X, T_BOOL, -1, -1, -1, -1, ""},
};
static int _count = sizeof(_settings) / sizeof(_settings[0]);
+static SModuleSettings _controlSettings[] = {
+ {BP_CONTROL_MODE, T_BOOL, -1, -1, -1, -1, ""},
+ {BP_CONTROL_MODE_HAS_COPY, T_BOOL, -1, -1, -1, -1, ""},
+ {BP_CONTROL_MODE_HAS_HALT, T_BOOL, -1, -1, -1, -1, ""},
+ {BP_CONTROL_MODE_HAS_EDIT, T_BOOL, -1, -1, -1, -1, ""},
+ {BP_COPY_ALL_ON_STOP, T_BOOL, -1, -1, -1, -1, ""},
+};
+
+static int _controlCount = sizeof(_controlSettings) / sizeof(_controlSettings[0]);
+
@implementation MiscSettings
- (id)init {
if (self = [super initWithTitle:@"Misc" Settings:_settings Count:_count]) {
self.tabBarItem.image = [UIImage imageNamed:@"misc.png"];
+ if (![self.view isKindOfClass:[UIScrollView class]]) {
+ [super dealloc];
+ return nil;
+ }
+ UIScrollView *view = (UIScrollView *)self.view;
+ CGSize oldSize = [view contentSize];
+ [view setContentSize:CGSizeMake(oldSize.width, oldSize.height + 70)];
+ UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+ btn.frame = CGRectMake(40.0, oldSize.height+20.0, oldSize.width-80.0, 30.0);
+ [btn setTitle:@"Control Mode..." forState:UIControlStateNormal];
+ [btn addTarget:self action:@selector(control) forControlEvents:UIControlEventTouchUpInside];
+ [view addSubview:btn];
}
return self;
}
+- (void)control {
+ ParametersController *control = [[[ParametersController alloc] initWithTitle:@"Control Mode" Settings:_controlSettings Count:_controlCount] autorelease];
+ [control setTarget:self Selector:@selector(controlDone)];
+ [self.navigationController pushViewController:control animated:YES];
+}
+
+-(void)controlDone {
+ [self.navigationController popViewControllerAnimated:YES];
+}
+
@end
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]