Code tidying Interface FSM
[dasher.git] / Src / DasherCore / DasherInterfaceBase.cpp
blobf92f064c80cc32668fa2d919f9bc6026c7798d9e
1 // DasherInterfaceBase.cpp
2 //
3 // Copyright (c) 2002 Iain Murray
5 #include "../Common/Common.h"
7 #include "DasherInterfaceBase.h"
9 //#include "ActionButton.h"
10 #include "AlphabetManagerFactory.h"
11 #include "DasherViewSquare.h"
12 #include "ControlManager.h"
13 #include "DasherScreen.h"
14 #include "DasherView.h"
15 #include "DasherInput.h"
16 #include "DasherModel.h"
17 #include "EventHandler.h"
18 #include "Event.h"
19 #include "NodeCreationManager.h"
20 #include "UserLog.h"
21 #include "BasicLog.h"
22 #include "WrapperFactory.h"
24 // Input filters
25 #include "ClickFilter.h"
26 #include "DefaultFilter.h"
27 #include "DasherButtons.h"
28 #include "DynamicFilter.h"
29 #include "EyetrackerFilter.h"
30 #include "OneDimensionalFilter.h"
31 #include "StylusFilter.h"
32 #include "TwoButtonDynamicFilter.h"
34 // STL headers
35 #include <iostream>
36 #include <memory>
38 // Legacy C library headers
39 namespace {
40 #include "stdio.h"
43 // Declare our global file logging object
44 #include "../DasherCore/FileLogger.h"
45 #ifdef _DEBUG
46 const eLogLevel g_iLogLevel = logDEBUG;
47 const int g_iLogOptions = logTimeStamp | logDateStamp | logDeleteOldFile;
48 #else
49 const eLogLevel g_iLogLevel = logNORMAL;
50 const int g_iLogOptions = logTimeStamp | logDateStamp;
51 #endif
52 CFileLogger* g_pLogger = NULL;
54 using namespace Dasher;
55 using namespace std;
57 // Track memory leaks on Windows to the line that new'd the memory
58 #ifdef _WIN32
59 #ifdef _DEBUG
60 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
61 #define new DEBUG_NEW
62 #undef THIS_FILE
63 static char THIS_FILE[] = __FILE__;
64 #endif
65 #endif
67 CDasherInterfaceBase::CDasherInterfaceBase()
68 :m_Alphabet(0), m_pDasherModel(0), m_DasherScreen(0),
69 m_pDasherView(0), m_pInput(0), m_AlphIO(0), m_ColourIO(0), m_pUserLog(NULL),
70 m_pInputFilter(NULL) {
72 m_iCurrentState = ST_START;
73 m_iLockCount = 0;
75 m_pNCManager = 0;
77 m_bGlobalLock = false;
79 m_bRedrawScheduled = false;
81 m_pEventHandler = new CEventHandler(this);
83 strCurrentContext = ". ";
85 strTrainfileBuffer = "";
87 // Global logging object we can use from anywhere
88 g_pLogger = new CFileLogger("dasher.log",
89 g_iLogLevel,
90 g_iLogOptions);
94 void CDasherInterfaceBase::Realize() {
96 // TODO: What exactly needs to have happened by the time we call Realize()?
97 CreateSettingsStore();
98 SetupUI();
99 SetupPaths();
101 std::vector<std::string> vAlphabetFiles;
102 ScanAlphabetFiles(vAlphabetFiles);
103 m_AlphIO = new CAlphIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vAlphabetFiles);
105 std::vector<std::string> vColourFiles;
106 ScanColourFiles(vColourFiles);
107 m_ColourIO = new CColourIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vColourFiles);
109 ChangeColours();
110 ChangeAlphabet();
112 // Create the user logging object if we are suppose to. We wait
113 // until now so we have the real value of the parameter and not
114 // just the default.
116 // TODO: Sort out log type selection
118 int iUserLogLevel = GetLongParameter(LP_USER_LOG_LEVEL_MASK);
120 if(iUserLogLevel == 10)
121 m_pUserLog = new CBasicLog(m_pEventHandler, m_pSettingsStore);
122 else if (iUserLogLevel > 0)
123 m_pUserLog = new CUserLog(m_pEventHandler, m_pSettingsStore, iUserLogLevel, m_Alphabet);
125 CreateFactories();
126 CreateLocalFactories();
128 CreateInput();
129 CreateInputFilter();
130 SetupActionButtons();
132 // FIXME - need to rationalise this sort of thing.
133 InvalidateContext(true);
134 ScheduleRedraw();
136 // All the setup is done by now, so let the user log object know
137 // that future parameter changes should be logged.
138 if (m_pUserLog != NULL)
139 m_pUserLog->InitIsDone();
141 ChangeState(TR_MODEL_INIT);
144 CDasherInterfaceBase::~CDasherInterfaceBase() {
146 DASHER_ASSERT(m_iCurrentState == ST_SHUTDOWN);
148 delete m_pDasherModel; // The order of some of these deletions matters
149 delete m_Alphabet;
150 delete m_pDasherView;
151 delete m_ColourIO;
152 delete m_AlphIO;
153 delete m_pInputFilter;
154 delete m_pNCManager;
155 // Do NOT delete Edit box or Screen. This class did not create them.
157 // When we destruct on shutdown, we'll output any detailed log file
158 if (m_pUserLog != NULL)
160 m_pUserLog->OutputFile();
161 delete m_pUserLog;
162 m_pUserLog = NULL;
165 if (g_pLogger != NULL) {
166 delete g_pLogger;
167 g_pLogger = NULL;
170 // Must delete event handler after all CDasherComponent derived classes
172 delete m_pEventHandler;
175 void CDasherInterfaceBase::PreSetNotify(int iParameter, const std::string &sNewValue) {
177 // FIXME - make this a more general 'pre-set' event in the message
178 // infrastructure
180 switch(iParameter) {
181 case SP_ALPHABET_ID:
182 // Cycle the alphabet history
183 if(GetStringParameter(SP_ALPHABET_ID) != sNewValue) {
184 if(GetStringParameter(SP_ALPHABET_1) != sNewValue) {
185 if(GetStringParameter(SP_ALPHABET_2) != sNewValue) {
186 if(GetStringParameter(SP_ALPHABET_3) != sNewValue)
187 SetStringParameter(SP_ALPHABET_4, GetStringParameter(SP_ALPHABET_3));
189 SetStringParameter(SP_ALPHABET_3, GetStringParameter(SP_ALPHABET_2));
192 SetStringParameter(SP_ALPHABET_2, GetStringParameter(SP_ALPHABET_1));
195 SetStringParameter(SP_ALPHABET_1, GetStringParameter(SP_ALPHABET_ID));
198 break;
202 void CDasherInterfaceBase::InterfaceEventHandler(Dasher::CEvent *pEvent) {
204 if(pEvent->m_iEventType == 1) {
205 Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
207 switch (pEvt->m_iParameter) {
209 case BP_OUTLINE_MODE:
210 ScheduleRedraw();
211 break;
212 case BP_DRAW_MOUSE:
213 ScheduleRedraw();
214 break;
215 case BP_CONTROL_MODE:
216 ScheduleRedraw();
217 break;
218 case BP_DRAW_MOUSE_LINE:
219 ScheduleRedraw();
220 break;
221 case LP_ORIENTATION:
222 if(GetLongParameter(LP_ORIENTATION) == Dasher::Opts::AlphabetDefault)
223 // TODO: See comment in DasherModel.cpp about prefered values
224 SetLongParameter(LP_REAL_ORIENTATION, m_Alphabet->GetOrientation());
225 else
226 SetLongParameter(LP_REAL_ORIENTATION, GetLongParameter(LP_ORIENTATION));
227 ScheduleRedraw();
228 break;
229 case SP_ALPHABET_ID:
230 ChangeAlphabet();
231 ScheduleRedraw();
232 break;
233 case SP_COLOUR_ID:
234 ChangeColours();
235 ScheduleRedraw();
236 break;
237 case SP_DEFAULT_COLOUR_ID: // Delibarate fallthrough
238 case BP_PALETTE_CHANGE:
239 if(GetBoolParameter(BP_PALETTE_CHANGE))
240 SetStringParameter(SP_COLOUR_ID, GetStringParameter(SP_DEFAULT_COLOUR_ID));
241 break;
242 case LP_LANGUAGE_MODEL_ID:
243 CreateDasherModel();
244 break;
245 case LP_LINE_WIDTH:
246 ScheduleRedraw();
247 break;
248 case LP_DASHER_FONTSIZE:
249 ScheduleRedraw();
250 break;
251 case SP_INPUT_DEVICE:
252 CreateInput();
253 break;
254 case SP_INPUT_FILTER:
255 CreateInputFilter();
256 ScheduleRedraw();
257 break;
258 default:
259 break;
262 else if(pEvent->m_iEventType == 2) {
263 CEditEvent *pEditEvent(static_cast < CEditEvent * >(pEvent));
265 if(pEditEvent->m_iEditType == 1) {
266 strCurrentContext += pEditEvent->m_sText;
267 if( strCurrentContext.size() > 20 )
268 strCurrentContext = strCurrentContext.substr( strCurrentContext.size() - 20 );
270 strTrainfileBuffer += pEditEvent->m_sText;
272 else if(pEditEvent->m_iEditType == 2) {
273 strCurrentContext = strCurrentContext.substr( 0, strCurrentContext.size() - pEditEvent->m_sText.size());
275 strTrainfileBuffer = strTrainfileBuffer.substr( 0, strTrainfileBuffer.size() - pEditEvent->m_sText.size());
278 else if(pEvent->m_iEventType == EV_CONTROL) {
279 CControlEvent *pControlEvent(static_cast <CControlEvent*>(pEvent));
281 switch(pControlEvent->m_iID) {
282 case CControlManager::CTL_STOP:
283 PauseAt(0,0);
284 break;
285 case CControlManager::CTL_PAUSE:
286 Halt();
287 break;
290 else if(pEvent->m_iEventType == EV_LOCK) {
291 CLockEvent *pLockEvent(static_cast<CLockEvent *>(pEvent));
293 // TODO: Sort this out - at the moment these don't occur in pairs, so the old boolean variable is still needed
294 if(pLockEvent->m_bLock) {
295 if(m_bGlobalLock)
296 AddLock(0);
298 else {
299 if(!m_bGlobalLock)
300 ReleaseLock(0);
303 m_bGlobalLock = pLockEvent->m_bLock;
307 void CDasherInterfaceBase::WriteTrainFileFull() {
308 WriteTrainFile(strTrainfileBuffer);
309 strTrainfileBuffer = "";
312 void CDasherInterfaceBase::WriteTrainFilePartial() {
313 // TODO: what if we're midway through a unicode character?
314 WriteTrainFile(strTrainfileBuffer.substr(0,100));
315 strTrainfileBuffer = strTrainfileBuffer.substr(100);
318 void CDasherInterfaceBase::CreateDasherModel()
320 if(!m_AlphIO)
321 return;
323 // TODO: Move training into model?
324 // TODO: Do we really need to check for a valid language model?
325 int lmID = GetLongParameter(LP_LANGUAGE_MODEL_ID);
326 if( lmID != -1 ) {
328 // Train the new language model
329 CLockEvent *pEvent;
331 pEvent = new CLockEvent("Training Dasher", true, 0);
332 m_pEventHandler->InsertEvent(pEvent);
333 delete pEvent;
335 AddLock(0);
337 // Delete the old model and create a new one
338 if(m_pDasherModel) {
339 delete m_pDasherModel;
340 m_pDasherModel = 0;
343 if(m_pNCManager) {
344 delete m_pNCManager;
345 m_pNCManager = 0;
348 if(m_deGameModeStrings.size() == 0) {
349 m_pNCManager = new CNodeCreationManager(m_pEventHandler, m_pSettingsStore, false, "", m_AlphIO);
350 m_pDasherModel = new CDasherModel(m_pEventHandler, m_pSettingsStore, m_pNCManager, this);
352 else {
353 m_pNCManager = new CNodeCreationManager(m_pEventHandler, m_pSettingsStore, true, m_deGameModeStrings[0], m_AlphIO);
354 m_pDasherModel = new CDasherModel(m_pEventHandler, m_pSettingsStore, m_pNCManager, this, true, m_deGameModeStrings[0]);
355 // m_deGameModeStrings.pop_front();
358 m_Alphabet = m_pNCManager->GetAlphabet();
360 string T = m_Alphabet->GetTrainingFile();
362 int iTotalBytes = 0;
363 iTotalBytes += GetFileSize(GetStringParameter(SP_SYSTEM_LOC) + T);
364 iTotalBytes += GetFileSize(GetStringParameter(SP_USER_LOC) + T);
366 if(iTotalBytes > 0) {
367 int iOffset;
368 iOffset = TrainFile(GetStringParameter(SP_SYSTEM_LOC) + T, iTotalBytes, 0);
369 TrainFile(GetStringParameter(SP_USER_LOC) + T, iTotalBytes, iOffset);
371 else {
372 CMessageEvent oEvent("No training text is avilable for the selected alphabet. Dasher will function, but it may be difficult to enter text.\nPlease see http://www.dasher.org.uk/alphabets/ for more information.", 0, 0);
373 m_pEventHandler->InsertEvent(&oEvent);
376 pEvent = new CLockEvent("Training Dasher", false, 0);
377 m_pEventHandler->InsertEvent(pEvent);
378 delete pEvent;
381 Start();
382 Redraw(true);
384 ReleaseLock(0);
387 void CDasherInterfaceBase::Start() {
388 // TODO: Clarify the relationship between Start() and
389 // InvalidateContext() - I believe that they essentially do the same
390 // thing
391 PauseAt(0, 0);
392 if(m_pDasherModel != 0) {
393 m_pDasherModel->Start();
395 if(m_pDasherView != 0) {
396 // m_pDasherView->ResetSum();
397 // m_pDasherView->ResetSumCounter();
398 // m_pDasherView->ResetYAutoOffset();
401 int iMinWidth;
403 if(m_pInputFilter && m_pInputFilter->GetMinWidth(iMinWidth)) {
404 m_pDasherModel->LimitRoot(iMinWidth);
408 void CDasherInterfaceBase::PauseAt(int MouseX, int MouseY) {
409 SetBoolParameter(BP_DASHER_PAUSED, true);
411 // Request a full redraw at the next time step.
412 SetBoolParameter(BP_REDRAW, true);
414 Dasher::CStopEvent oEvent;
415 m_pEventHandler->InsertEvent(&oEvent);
417 if (m_pUserLog != NULL)
418 m_pUserLog->StopWriting((float) GetNats());
421 void CDasherInterfaceBase::Halt() {
422 SetBoolParameter(BP_DASHER_PAUSED, true);
424 // Obsolete?
425 if(GetBoolParameter(BP_MOUSEPOS_MODE)) {
426 SetLongParameter(LP_MOUSE_POS_BOX, 1);
430 // This will cause us to reinitialise the frame rate counter - ie we start off slowly
431 if(m_pDasherModel != 0)
432 m_pDasherModel->Halt();
435 void CDasherInterfaceBase::Unpause(unsigned long Time) {
436 SetBoolParameter(BP_DASHER_PAUSED, false);
438 if(m_pDasherModel != 0) {
439 m_pDasherModel->Reset_framerate(Time);
440 //m_pDasherModel->Set_paused(m_Paused);
442 if(m_pDasherView != 0) {
443 // m_pDasherView->ResetSum();
444 // m_pDasherView->ResetSumCounter();
447 Dasher::CStartEvent oEvent;
448 m_pEventHandler->InsertEvent(&oEvent);
450 ResetNats();
451 if (m_pUserLog != NULL)
452 m_pUserLog->StartWriting();
455 void CDasherInterfaceBase::CreateInput() {
456 if(m_pInput) {
457 m_pInput->Deactivate();
458 m_pInput->Unref();
461 m_pInput = (CDasherInput *)GetModuleByName(GetStringParameter(SP_INPUT_DEVICE));
463 if(m_pInput) {
464 m_pInput->Ref();
465 m_pInput->Activate();
468 if(m_pDasherView != 0)
469 m_pDasherView->SetInput(m_pInput);
472 void CDasherInterfaceBase::NewFrame(unsigned long iTime, bool bForceRedraw) {
473 // Fail if Dasher is locked
474 if(m_iCurrentState != ST_NORMAL)
475 return;
477 bool bChanged(false);
479 if(m_pDasherView != 0) {
480 if(!GetBoolParameter(BP_TRAINING)) {
481 if (m_pUserLog != NULL) {
483 Dasher::VECTOR_SYMBOL_PROB vAdded;
484 int iNumDeleted = 0;
486 if(m_pInputFilter) {
487 bChanged = m_pInputFilter->Timer(iTime, m_pDasherView, m_pDasherModel, &vAdded, &iNumDeleted);
490 if (iNumDeleted > 0)
491 m_pUserLog->DeleteSymbols(iNumDeleted);
492 if (vAdded.size() > 0)
493 m_pUserLog->AddSymbols(&vAdded);
496 else {
497 if(m_pInputFilter) {
498 bChanged = m_pInputFilter->Timer(iTime, m_pDasherView, m_pDasherModel, 0, 0);
502 m_pDasherModel->CheckForNewRoot(m_pDasherView);
506 Redraw(bChanged || m_bRedrawScheduled || bForceRedraw);
507 m_bRedrawScheduled = false;
509 // This just passes the time through to the framerate tracker, so we
510 // know how often new frames are being drawn.
511 if(m_pDasherModel != 0)
512 m_pDasherModel->NewFrame(iTime);
515 void CDasherInterfaceBase::CheckRedraw() {
516 if(m_bRedrawScheduled)
517 Redraw(true);
519 m_bRedrawScheduled = false;
522 /// Full redraw - renders model, decorations and blits onto display
523 /// buffer. Not recommended if nothing has changed with the model,
524 /// otherwise we're wasting effort.
526 void CDasherInterfaceBase::Redraw(bool bRedrawNodes) {
527 if(!m_pDasherView || !m_pDasherModel)
528 return;
530 // TODO: More generic notion of 'layers'?
532 if(bRedrawNodes) {
533 m_pDasherView->Screen()->SendMarker(0);
534 m_pDasherModel->RenderToView(m_pDasherView, true);
537 m_pDasherView->Screen()->SendMarker(1);
539 bool bDecorationsChanged(false);
541 if(m_pInputFilter) {
542 bDecorationsChanged = m_pInputFilter->DecorateView(m_pDasherView);
545 bool bActionButtonsChanged(false);
546 #ifdef EXPERIMENTAL_FEATURES
547 bActionButtonsChanged = DrawActionButtons();
548 #endif
550 if(bRedrawNodes || bDecorationsChanged || bActionButtonsChanged)
551 m_pDasherView->Display();
554 void CDasherInterfaceBase::ChangeAlphabet() {
556 if(GetStringParameter(SP_ALPHABET_ID) == "") {
557 SetStringParameter(SP_ALPHABET_ID, m_AlphIO->GetDefault());
558 // This will result in ChangeAlphabet() being called again, so
559 // exit from the first recursion
560 return;
563 // Send a lock event
565 WriteTrainFileFull();
567 // Lock Dasher to prevent changes from happening while we're training.
569 SetBoolParameter( BP_TRAINING, true );
571 // m_AlphInfo = m_AlphIO->GetInfo(NewAlphabetID);
573 // //AlphabetID = m_AlphInfo.AlphID.c_str();
575 // // std::auto_ptr < CAlphabet > ptrOld(m_Alphabet); // So we can delete the old alphabet later
577 // m_Alphabet = new CAlphabet(m_AlphInfo);
579 delete m_pDasherModel;
580 m_pDasherModel = 0;
581 CreateDasherModel();
583 // Let our user log object know about the new alphabet since
584 // it needs to convert symbols into text for the log file.
585 if (m_pUserLog != NULL)
586 m_pUserLog->SetAlphabetPtr(m_Alphabet);
588 // Apply options from alphabet
590 SetBoolParameter( BP_TRAINING, false );
595 void CDasherInterfaceBase::ChangeColours() {
596 if(!m_ColourIO || !m_DasherScreen)
597 return;
599 // TODO: Make fuction return a pointer directly
600 m_DasherScreen->SetColourScheme(&(m_ColourIO->GetInfo(GetStringParameter(SP_COLOUR_ID))));
603 void CDasherInterfaceBase::ChangeScreen(CDasherScreen *NewScreen) {
604 m_DasherScreen = NewScreen;
605 ChangeColours();
607 if(m_pDasherView != 0) {
608 m_pDasherView->ChangeScreen(m_DasherScreen);
609 } else {
610 if(GetLongParameter(LP_VIEW_ID) != -1)
611 ChangeView();
614 PositionActionButtons();
615 ScheduleRedraw();
618 void CDasherInterfaceBase::ChangeView() {
619 // TODO: Actually respond to LP_VIEW_ID parameter (although there is only one view at the moment)
621 if(m_DasherScreen != 0 && m_pDasherModel != 0) {
622 delete m_pDasherView;
624 m_pDasherView = new CDasherViewSquare(m_pEventHandler, m_pSettingsStore, m_DasherScreen);
626 if (m_pInput)
627 m_pDasherView->SetInput(m_pInput);
631 void CDasherInterfaceBase::GetAlphabets(std::vector <std::string >*AlphabetList) {
632 m_AlphIO->GetAlphabets(AlphabetList);
635 const CAlphIO::AlphInfo & CDasherInterfaceBase::GetInfo(const std::string &AlphID) {
636 return m_AlphIO->GetInfo(AlphID);
639 void CDasherInterfaceBase::SetInfo(const CAlphIO::AlphInfo &NewInfo) {
640 m_AlphIO->SetInfo(NewInfo);
643 void CDasherInterfaceBase::DeleteAlphabet(const std::string &AlphID) {
644 m_AlphIO->Delete(AlphID);
647 void CDasherInterfaceBase::GetColours(std::vector <std::string >*ColourList) {
648 m_ColourIO->GetColours(ColourList);
652 I've used C style I/O because I found that C++ style I/O bloated
653 the Win32 code enormously. The overhead of loading the buffer into
654 the string instead of reading straight into a string seems to be
655 negligible compared to huge requirements elsewhere.
657 int CDasherInterfaceBase::TrainFile(string Filename, int iTotalBytes, int iOffset) {
658 if(Filename == "")
659 return 0;
661 FILE *InputFile;
662 if((InputFile = fopen(Filename.c_str(), "r")) == (FILE *) 0)
663 return 0;
665 const int BufferSize = 1024;
666 char InputBuffer[BufferSize];
667 string StringBuffer;
668 int NumberRead;
669 int iTotalRead(0);
671 vector < symbol > Symbols;
673 CAlphabetManagerFactory::CTrainer * pTrainer = m_pDasherModel->GetTrainer();
674 do {
675 NumberRead = fread(InputBuffer, 1, BufferSize - 1, InputFile);
676 InputBuffer[NumberRead] = '\0';
677 StringBuffer += InputBuffer;
678 bool bIsMore = false;
679 if(NumberRead == (BufferSize - 1))
680 bIsMore = true;
682 Symbols.clear();
683 m_Alphabet->GetSymbols(&Symbols, &StringBuffer, bIsMore);
685 pTrainer->Train(Symbols);
686 iTotalRead += NumberRead;
688 // TODO: No reason for this to be a pointer (other than cut/paste laziness!)
689 CLockEvent *pEvent;
690 pEvent = new CLockEvent("Training Dasher", true, static_cast<int>((100.0 * (iTotalRead + iOffset))/iTotalBytes));
691 m_pEventHandler->InsertEvent(pEvent);
692 delete pEvent;
694 } while(NumberRead == BufferSize - 1);
696 delete pTrainer;
698 fclose(InputFile);
700 return iTotalRead;
703 void CDasherInterfaceBase::GetFontSizes(std::vector <int >*FontSizes) const {
704 FontSizes->push_back(20);
705 FontSizes->push_back(14);
706 FontSizes->push_back(11);
707 FontSizes->push_back(40);
708 FontSizes->push_back(28);
709 FontSizes->push_back(22);
710 FontSizes->push_back(80);
711 FontSizes->push_back(56);
712 FontSizes->push_back(44);
715 double CDasherInterfaceBase::GetCurCPM() {
717 return 0;
720 double CDasherInterfaceBase::GetCurFPS() {
722 return 0;
725 // int CDasherInterfaceBase::GetAutoOffset() {
726 // if(m_pDasherView != 0) {
727 // return m_pDasherView->GetAutoOffset();
728 // }
729 // return -1;
730 // }
732 double CDasherInterfaceBase::GetNats() const {
733 if(m_pDasherModel)
734 return m_pDasherModel->GetNats();
735 else
736 return 0.0;
739 void CDasherInterfaceBase::ResetNats() {
740 if(m_pDasherModel)
741 m_pDasherModel->ResetNats();
745 void CDasherInterfaceBase::InvalidateContext(bool bForceStart) {
746 m_pDasherModel->m_strContextBuffer = "";
748 Dasher::CEditContextEvent oEvent(10);
749 m_pEventHandler->InsertEvent(&oEvent);
751 std::string strNewContext(m_pDasherModel->m_strContextBuffer);
753 // We keep track of an internal context and compare that to what
754 // we are given - don't restart Dasher if nothing has changed.
755 // This should really be integrated with DasherModel, which
756 // probably will be the case when we start to deal with being able
757 // to back off indefinitely. For now though we'll keep it in a
758 // separate string.
760 int iContextLength( 6 ); // The 'important' context length - should really get from language model
762 // FIXME - use unicode lengths
764 if(bForceStart || (strNewContext.substr( std::max(static_cast<int>(strNewContext.size()) - iContextLength, 0)) != strCurrentContext.substr( std::max(static_cast<int>(strCurrentContext.size()) - iContextLength, 0)))) {
766 if(m_pDasherModel != NULL) {
767 // TODO: Reimplement this
768 // if(m_pDasherModel->m_bContextSensitive || bForceStart) {
770 m_pDasherModel->SetContext(strNewContext);
771 PauseAt(0,0);
775 strCurrentContext = strNewContext;
776 WriteTrainFileFull();
779 if(bForceStart) {
780 int iMinWidth;
782 if(m_pInputFilter && m_pInputFilter->GetMinWidth(iMinWidth)) {
783 m_pDasherModel->LimitRoot(iMinWidth);
787 if(m_pDasherView)
788 while( m_pDasherModel->CheckForNewRoot(m_pDasherView) ) {
789 // Do nothing
792 ScheduleRedraw();
795 void CDasherInterfaceBase::SetContext(std::string strNewContext) {
796 m_pDasherModel->m_strContextBuffer = strNewContext;
799 // Control mode stuff
801 void CDasherInterfaceBase::RegisterNode( int iID, const std::string &strLabel, int iColour ) {
802 m_pNCManager->RegisterNode(iID, strLabel, iColour);
805 void CDasherInterfaceBase::ConnectNode(int iChild, int iParent, int iAfter) {
806 m_pNCManager->ConnectNode(iChild, iParent, iAfter);
809 void CDasherInterfaceBase::DisconnectNode(int iChild, int iParent) {
810 m_pNCManager->DisconnectNode(iChild, iParent);
813 void CDasherInterfaceBase::SetBoolParameter(int iParameter, bool bValue) {
814 m_pSettingsStore->SetBoolParameter(iParameter, bValue);
817 void CDasherInterfaceBase::SetLongParameter(int iParameter, long lValue) {
818 m_pSettingsStore->SetLongParameter(iParameter, lValue);
821 void CDasherInterfaceBase::SetStringParameter(int iParameter, const std::string & sValue) {
822 PreSetNotify(iParameter, sValue);
823 m_pSettingsStore->SetStringParameter(iParameter, sValue);
826 bool CDasherInterfaceBase::GetBoolParameter(int iParameter) {
827 return m_pSettingsStore->GetBoolParameter(iParameter);
830 long CDasherInterfaceBase::GetLongParameter(int iParameter) {
831 return m_pSettingsStore->GetLongParameter(iParameter);
834 std::string CDasherInterfaceBase::GetStringParameter(int iParameter) {
835 return m_pSettingsStore->GetStringParameter(iParameter);
838 void CDasherInterfaceBase::ResetParameter(int iParameter) {
839 m_pSettingsStore->ResetParameter(iParameter);
842 // We need to be able to get at the UserLog object from outside the interface
843 CUserLogBase* CDasherInterfaceBase::GetUserLogPtr() {
844 return m_pUserLog;
847 void CDasherInterfaceBase::KeyDown(int iTime, int iId) {
848 if(m_iCurrentState != ST_NORMAL)
849 return;
851 if(m_pInputFilter && !GetBoolParameter(BP_TRAINING)) {
852 m_pInputFilter->KeyDown(iTime, iId, m_pDasherModel, m_pUserLog);
855 if(m_pInput && !GetBoolParameter(BP_TRAINING)) {
856 m_pInput->KeyDown(iTime, iId);
860 void CDasherInterfaceBase::KeyUp(int iTime, int iId) {
861 if(m_iCurrentState != ST_NORMAL)
862 return;
864 if(m_pInputFilter && !GetBoolParameter(BP_TRAINING)) {
865 m_pInputFilter->KeyUp(iTime, iId, m_pDasherModel);
868 if(m_pInput && !GetBoolParameter(BP_TRAINING)) {
869 m_pInput->KeyUp(iTime, iId);
873 void CDasherInterfaceBase::CreateInputFilter()
875 if(m_pInputFilter) {
876 m_pInputFilter->Deactivate();
877 m_pInputFilter->Unref();
878 m_pInputFilter = NULL;
881 m_pInputFilter = (CInputFilter *)GetModuleByName(GetStringParameter(SP_INPUT_FILTER));
883 if(m_pInputFilter) {
884 m_pInputFilter->Ref();
885 m_pInputFilter->Activate();
887 else {
888 // Fall back to a sensible alternative if for some reason the
889 // current choice isn't valid.
890 if(GetStringParameter(SP_INPUT_FILTER) != "Normal Control")
891 SetStringParameter(SP_INPUT_FILTER, "Normal Control");
895 void CDasherInterfaceBase::RegisterFactory(CModuleFactory *pFactory) {
896 m_oModuleManager.RegisterFactory(pFactory);
899 CDasherModule *CDasherInterfaceBase::GetModule(long long int iID) {
900 return m_oModuleManager.GetModule(iID);
903 CDasherModule *CDasherInterfaceBase::GetModuleByName(const std::string &strName) {
904 return m_oModuleManager.GetModuleByName(strName);
907 void CDasherInterfaceBase::CreateFactories() {
908 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDefaultFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel,3, "Normal Control")));
909 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new COneDimensionalFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel)));
910 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CEyetrackerFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel)));
911 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CClickFilter(m_pEventHandler, m_pSettingsStore, this)));
912 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDynamicFilter(m_pEventHandler, m_pSettingsStore, this)));
913 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CTwoButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this, 14, 1, "Two Button Dynamic Mode")));
914 // TODO: specialist factory for button mode
915 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 5, 1, true,8, "Menu Mode")));
916 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 0, false,10, "Direct Mode")));
917 // RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 0, false,11, "Buttons 3")));
918 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 3, false,12, "Alternating Direct Mode")));
919 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 2, false,13, "Compass Mode")));
920 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CStylusFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel,15, "Stylus Control")));
924 void CDasherInterfaceBase::GetPermittedValues(int iParameter, std::vector<std::string> &vList) {
925 // TODO: Deprecate direct calls to these functions
926 switch (iParameter) {
927 case SP_ALPHABET_ID:
928 GetAlphabets(&vList);
929 break;
930 case SP_COLOUR_ID:
931 GetColours(&vList);
932 break;
933 case SP_INPUT_FILTER:
934 m_oModuleManager.ListModules(1, vList);
935 break;
936 case SP_INPUT_DEVICE:
937 m_oModuleManager.ListModules(0, vList);
938 break;
942 void CDasherInterfaceBase::StartShutdown() {
943 ChangeState(TR_SHUTDOWN);
946 bool CDasherInterfaceBase::GetModuleSettings(const std::string &strName, SModuleSettings **pSettings, int *iCount) {
947 return GetModuleByName(strName)->GetSettings(pSettings, iCount);
950 void CDasherInterfaceBase::SetupActionButtons() {
951 m_vLeftButtons.push_back(new CActionButton(this, "Exit", true));
952 m_vLeftButtons.push_back(new CActionButton(this, "Preferences", false));
953 m_vLeftButtons.push_back(new CActionButton(this, "Help", false));
954 m_vLeftButtons.push_back(new CActionButton(this, "About", false));
957 void CDasherInterfaceBase::DestroyActionButtons() {
958 // TODO: implement and call this
961 void CDasherInterfaceBase::PositionActionButtons() {
962 if(!m_DasherScreen)
963 return;
965 int iCurrentOffset(16);
967 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
968 (*it)->SetPosition(16, iCurrentOffset, 32, 32);
969 iCurrentOffset += 48;
972 iCurrentOffset = 16;
974 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
975 (*it)->SetPosition(m_DasherScreen->GetWidth() - 144, iCurrentOffset, 128, 32);
976 iCurrentOffset += 48;
980 bool CDasherInterfaceBase::DrawActionButtons() {
981 if(!m_DasherScreen)
982 return false;
984 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
986 bool bRV(bVisible != m_bOldVisible);
987 m_bOldVisible = bVisible;
989 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it)
990 (*it)->Draw(m_DasherScreen, bVisible);
992 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it)
993 (*it)->Draw(m_DasherScreen, bVisible);
995 return bRV;
999 void CDasherInterfaceBase::HandleClickUp(int iTime, int iX, int iY) {
1000 #ifdef EXPERIMENTAL_FEATURES
1001 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
1003 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
1004 if((*it)->HandleClickUp(iTime, iX, iY, bVisible))
1005 return;
1008 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
1009 if((*it)->HandleClickUp(iTime, iX, iY, bVisible))
1010 return;
1012 #endif
1014 KeyUp(iTime, 100);
1017 void CDasherInterfaceBase::HandleClickDown(int iTime, int iX, int iY) {
1018 #ifdef EXPERIMENTAL_FEATURES
1019 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
1021 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
1022 if((*it)->HandleClickDown(iTime, iX, iY, bVisible))
1023 return;
1026 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
1027 if((*it)->HandleClickDown(iTime, iX, iY, bVisible))
1028 return;
1030 #endif
1032 KeyDown(iTime, 100);
1036 void CDasherInterfaceBase::ExecuteCommand(const std::string &strName) {
1037 // TODO: Pointless - just insert event directly
1039 CCommandEvent *pEvent = new CCommandEvent(strName);
1040 m_pEventHandler->InsertEvent(pEvent);
1041 delete pEvent;
1044 double CDasherInterfaceBase::GetFramerate() {
1045 if(m_pDasherModel)
1046 return(m_pDasherModel->Framerate());
1047 else
1048 return 0.0;
1051 int CDasherInterfaceBase::GetRenderCount() {
1052 if(m_pDasherView)
1053 return(m_pDasherView->GetRenderCount());
1054 else
1055 return 0;
1058 void CDasherInterfaceBase::AddActionButton(const std::string &strName) {
1059 m_vRightButtons.push_back(new CActionButton(this, strName, false));
1063 void CDasherInterfaceBase::OnUIRealised() {
1064 StartTimer();
1065 ChangeState(TR_UI_INIT);
1069 void CDasherInterfaceBase::ChangeState(ETransition iTransition) {
1070 static EState iTransitionTable[ST_NUM][TR_NUM] = {
1071 {ST_MODEL, ST_UI, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1072 {ST_FORBIDDEN, ST_NORMAL, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1073 {ST_NORMAL, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1074 {ST_FORBIDDEN, ST_FORBIDDEN, ST_LOCKED, ST_FORBIDDEN, ST_SHUTDOWN},
1075 {ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_NORMAL, ST_FORBIDDEN},
1076 {ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN}
1079 EState iNewState(iTransitionTable[m_iCurrentState][iTransition]);
1081 if(iNewState != ST_FORBIDDEN) {
1082 LeaveState(m_iCurrentState);
1083 EnterState(iNewState);
1085 m_iCurrentState = iNewState;
1089 void CDasherInterfaceBase::LeaveState(EState iState) {
1093 void CDasherInterfaceBase::EnterState(EState iState) {
1094 switch(iState) {
1095 case ST_SHUTDOWN:
1096 ShutdownTimer();
1097 break;
1098 default:
1099 // Not handled
1100 break;
1104 void CDasherInterfaceBase::AddLock(int iLockFlags) {
1105 if(m_iLockCount == 0)
1106 ChangeState(TR_LOCK);
1108 ++m_iLockCount;
1111 void CDasherInterfaceBase::ReleaseLock(int iLockFlags) {
1112 if(m_iLockCount > 0)
1113 --m_iLockCount;
1115 if(m_iLockCount == 0)
1116 ChangeState(TR_UNLOCK);