tagging release
[dasher.git] / Src / DasherCore / DasherInterfaceBase.cpp
bloba449172298ab28abc57021312fa61acfb3ae1826
1 // DasherInterfaceBase.cpp
2 //
3 // Copyright (c) 2007 The Dasher Team
4 //
5 // This file is part of Dasher.
6 //
7 // Dasher is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
12 // Dasher is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with Dasher; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "../Common/Common.h"
23 #include "DasherInterfaceBase.h"
25 //#include "ActionButton.h"
26 #include "AlphabetManagerFactory.h"
27 #include "DasherViewSquare.h"
28 #include "ControlManager.h"
29 #include "DasherScreen.h"
30 #include "DasherView.h"
31 #include "DasherInput.h"
32 #include "DasherModel.h"
33 #include "EventHandler.h"
34 #include "Event.h"
35 #include "NodeCreationManager.h"
36 #include "UserLog.h"
37 #include "BasicLog.h"
38 #include "WrapperFactory.h"
40 // Input filters
41 #include "ClickFilter.h"
42 #include "DefaultFilter.h"
43 #include "DasherButtons.h"
44 #include "DynamicFilter.h"
45 #include "EyetrackerFilter.h"
46 #include "OneDimensionalFilter.h"
47 #include "StylusFilter.h"
48 #include "TwoButtonDynamicFilter.h"
50 // STL headers
51 #include <iostream>
52 #include <memory>
54 // Legacy C library headers
55 namespace {
56 #include "stdio.h"
59 // Declare our global file logging object
60 #include "../DasherCore/FileLogger.h"
61 #ifdef _DEBUG
62 const eLogLevel g_iLogLevel = logDEBUG;
63 const int g_iLogOptions = logTimeStamp | logDateStamp | logDeleteOldFile;
64 #else
65 const eLogLevel g_iLogLevel = logNORMAL;
66 const int g_iLogOptions = logTimeStamp | logDateStamp;
67 #endif
68 CFileLogger* g_pLogger = NULL;
70 using namespace Dasher;
71 using namespace std;
73 // Track memory leaks on Windows to the line that new'd the memory
74 #ifdef _WIN32
75 #ifdef _DEBUG
76 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
77 #define new DEBUG_NEW
78 #undef THIS_FILE
79 static char THIS_FILE[] = __FILE__;
80 #endif
81 #endif
83 CDasherInterfaceBase::CDasherInterfaceBase() {
85 // Ensure that pointers to 'owned' objects are set to NULL.
86 m_Alphabet = NULL;
87 m_pDasherModel = NULL;
88 m_DasherScreen = NULL;
89 m_pDasherView = NULL;
90 m_pInput = NULL;
91 m_AlphIO = NULL;
92 m_ColourIO = NULL;
93 m_pUserLog = NULL;
94 m_pInputFilter = NULL;
95 m_pNCManager = NULL;
97 // Various state variables
98 m_bRedrawScheduled = false;
100 m_iCurrentState = ST_START;
102 m_iLockCount = 0;
103 m_bGlobalLock = false;
105 // TODO: Are these actually needed?
106 strCurrentContext = ". ";
107 strTrainfileBuffer = "";
109 // Create an event handler.
110 m_pEventHandler = new CEventHandler(this);
112 m_bLastChanged = true;
114 // Global logging object we can use from anywhere
115 g_pLogger = new CFileLogger("dasher.log",
116 g_iLogLevel,
117 g_iLogOptions);
121 void CDasherInterfaceBase::Realize() {
123 // TODO: What exactly needs to have happened by the time we call Realize()?
124 CreateSettingsStore();
125 SetupUI();
126 SetupPaths();
128 std::vector<std::string> vAlphabetFiles;
129 ScanAlphabetFiles(vAlphabetFiles);
130 m_AlphIO = new CAlphIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vAlphabetFiles);
132 std::vector<std::string> vColourFiles;
133 ScanColourFiles(vColourFiles);
134 m_ColourIO = new CColourIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vColourFiles);
136 ChangeColours();
137 ChangeAlphabet();
139 // Create the user logging object if we are suppose to. We wait
140 // until now so we have the real value of the parameter and not
141 // just the default.
143 // TODO: Sort out log type selection
145 int iUserLogLevel = GetLongParameter(LP_USER_LOG_LEVEL_MASK);
147 if(iUserLogLevel == 10)
148 m_pUserLog = new CBasicLog(m_pEventHandler, m_pSettingsStore);
149 else if (iUserLogLevel > 0)
150 m_pUserLog = new CUserLog(m_pEventHandler, m_pSettingsStore, iUserLogLevel, m_Alphabet);
152 CreateFactories();
153 CreateLocalFactories();
155 CreateInput();
156 CreateInputFilter();
157 SetupActionButtons();
159 // FIXME - need to rationalise this sort of thing.
160 // InvalidateContext(true);
161 ScheduleRedraw();
163 // All the setup is done by now, so let the user log object know
164 // that future parameter changes should be logged.
165 if (m_pUserLog != NULL)
166 m_pUserLog->InitIsDone();
168 // TODO: Make things work when model is created latet
169 ChangeState(TR_MODEL_INIT);
172 CDasherInterfaceBase::~CDasherInterfaceBase() {
173 DASHER_ASSERT(m_iCurrentState == ST_SHUTDOWN);
175 delete m_pDasherModel; // The order of some of these deletions matters
176 delete m_Alphabet;
177 delete m_pDasherView;
178 delete m_ColourIO;
179 delete m_AlphIO;
180 delete m_pInputFilter;
181 delete m_pNCManager;
182 // Do NOT delete Edit box or Screen. This class did not create them.
184 // When we destruct on shutdown, we'll output any detailed log file
185 if (m_pUserLog != NULL)
187 m_pUserLog->OutputFile();
188 delete m_pUserLog;
189 m_pUserLog = NULL;
192 if (g_pLogger != NULL) {
193 delete g_pLogger;
194 g_pLogger = NULL;
197 // Must delete event handler after all CDasherComponent derived classes
199 delete m_pEventHandler;
202 void CDasherInterfaceBase::PreSetNotify(int iParameter, const std::string &sNewValue) {
204 // FIXME - make this a more general 'pre-set' event in the message
205 // infrastructure
207 switch(iParameter) {
208 case SP_ALPHABET_ID:
209 // Cycle the alphabet history
210 if(GetStringParameter(SP_ALPHABET_ID) != sNewValue) {
211 if(GetStringParameter(SP_ALPHABET_1) != sNewValue) {
212 if(GetStringParameter(SP_ALPHABET_2) != sNewValue) {
213 if(GetStringParameter(SP_ALPHABET_3) != sNewValue)
214 SetStringParameter(SP_ALPHABET_4, GetStringParameter(SP_ALPHABET_3));
216 SetStringParameter(SP_ALPHABET_3, GetStringParameter(SP_ALPHABET_2));
219 SetStringParameter(SP_ALPHABET_2, GetStringParameter(SP_ALPHABET_1));
222 SetStringParameter(SP_ALPHABET_1, GetStringParameter(SP_ALPHABET_ID));
225 break;
229 void CDasherInterfaceBase::InterfaceEventHandler(Dasher::CEvent *pEvent) {
231 if(pEvent->m_iEventType == 1) {
232 Dasher::CParameterNotificationEvent * pEvt(static_cast < Dasher::CParameterNotificationEvent * >(pEvent));
234 switch (pEvt->m_iParameter) {
236 case BP_OUTLINE_MODE:
237 ScheduleRedraw();
238 break;
239 case BP_DRAW_MOUSE:
240 ScheduleRedraw();
241 break;
242 case BP_CONTROL_MODE:
243 ScheduleRedraw();
244 break;
245 case BP_DRAW_MOUSE_LINE:
246 ScheduleRedraw();
247 break;
248 case LP_ORIENTATION:
249 if(GetLongParameter(LP_ORIENTATION) == Dasher::Opts::AlphabetDefault)
250 // TODO: See comment in DasherModel.cpp about prefered values
251 SetLongParameter(LP_REAL_ORIENTATION, m_Alphabet->GetOrientation());
252 else
253 SetLongParameter(LP_REAL_ORIENTATION, GetLongParameter(LP_ORIENTATION));
254 ScheduleRedraw();
255 break;
256 case SP_ALPHABET_ID:
257 ChangeAlphabet();
258 ScheduleRedraw();
259 break;
260 case SP_COLOUR_ID:
261 ChangeColours();
262 ScheduleRedraw();
263 break;
264 case SP_DEFAULT_COLOUR_ID: // Delibarate fallthrough
265 case BP_PALETTE_CHANGE:
266 if(GetBoolParameter(BP_PALETTE_CHANGE))
267 SetStringParameter(SP_COLOUR_ID, GetStringParameter(SP_DEFAULT_COLOUR_ID));
268 break;
269 case LP_LANGUAGE_MODEL_ID:
270 CreateNCManager();
271 break;
272 case LP_LINE_WIDTH:
273 ScheduleRedraw();
274 break;
275 case LP_DASHER_FONTSIZE:
276 ScheduleRedraw();
277 break;
278 case SP_INPUT_DEVICE:
279 CreateInput();
280 break;
281 case SP_INPUT_FILTER:
282 CreateInputFilter();
283 ScheduleRedraw();
284 break;
285 case BP_DASHER_PAUSED:
286 ScheduleRedraw();
287 break;
288 default:
289 break;
292 else if(pEvent->m_iEventType == 2) {
293 CEditEvent *pEditEvent(static_cast < CEditEvent * >(pEvent));
295 if(pEditEvent->m_iEditType == 1) {
296 strCurrentContext += pEditEvent->m_sText;
297 if( strCurrentContext.size() > 20 )
298 strCurrentContext = strCurrentContext.substr( strCurrentContext.size() - 20 );
300 strTrainfileBuffer += pEditEvent->m_sText;
302 else if(pEditEvent->m_iEditType == 2) {
303 strCurrentContext = strCurrentContext.substr( 0, strCurrentContext.size() - pEditEvent->m_sText.size());
305 strTrainfileBuffer = strTrainfileBuffer.substr( 0, strTrainfileBuffer.size() - pEditEvent->m_sText.size());
308 else if(pEvent->m_iEventType == EV_CONTROL) {
309 CControlEvent *pControlEvent(static_cast <CControlEvent*>(pEvent));
311 switch(pControlEvent->m_iID) {
312 case CControlManager::CTL_STOP:
313 PauseAt(0,0);
314 break;
315 case CControlManager::CTL_PAUSE:
316 Halt();
317 break;
320 else if(pEvent->m_iEventType == EV_LOCK) {
321 CLockEvent *pLockEvent(static_cast<CLockEvent *>(pEvent));
323 // // TODO: Sort this out - at the moment these don't occur in pairs, so the old boolean variable is still needed
324 // if(pLockEvent->m_bLock) {
325 // if(m_bGlobalLock)
326 // AddLock(0);
327 // }
328 // else {
329 // if(!m_bGlobalLock)
330 // ReleaseLock(0);
331 // }
333 m_bGlobalLock = pLockEvent->m_bLock;
337 void CDasherInterfaceBase::WriteTrainFileFull() {
338 WriteTrainFile(strTrainfileBuffer);
339 strTrainfileBuffer = "";
342 void CDasherInterfaceBase::WriteTrainFilePartial() {
343 // TODO: what if we're midway through a unicode character?
344 WriteTrainFile(strTrainfileBuffer.substr(0,100));
345 strTrainfileBuffer = strTrainfileBuffer.substr(100);
348 void CDasherInterfaceBase::CreateModel(int iOffset) {
349 // Creating a model without a node creation manager is a bad plan
350 if(!m_pNCManager)
351 return;
353 if(m_pDasherModel) {
354 delete m_pDasherModel;
355 m_pDasherModel = 0;
358 if(m_deGameModeStrings.size() == 0) {
359 m_pDasherModel = new CDasherModel(m_pEventHandler, m_pSettingsStore, m_pNCManager, this, m_pDasherView, iOffset);
361 else {
362 m_pDasherModel = new CDasherModel(m_pEventHandler, m_pSettingsStore, m_pNCManager, this, m_pDasherView, iOffset, true, m_deGameModeStrings[0]);
366 void CDasherInterfaceBase::CreateNCManager() {
367 // TODO: Try and make this work without necessarilty rebuilding the model
369 if(!m_AlphIO)
370 return;
372 int lmID = GetLongParameter(LP_LANGUAGE_MODEL_ID);
374 if( lmID == -1 )
375 return;
377 // Train the new language model
378 CLockEvent *pEvent;
380 pEvent = new CLockEvent("Training Dasher", true, 0);
381 m_pEventHandler->InsertEvent(pEvent);
382 delete pEvent;
384 AddLock(0);
386 int iOffset;
388 if(m_pDasherModel)
389 iOffset = m_pDasherModel->GetOffset();
390 else
391 iOffset = 0; // TODO: Is this right?
393 // Delete the old model and create a new one
394 if(m_pDasherModel) {
395 delete m_pDasherModel;
396 m_pDasherModel = 0;
399 if(m_pNCManager) {
400 delete m_pNCManager;
401 m_pNCManager = 0;
404 if(m_deGameModeStrings.size() == 0) {
405 m_pNCManager = new CNodeCreationManager(this, m_pEventHandler, m_pSettingsStore, false, "", m_AlphIO);
407 else {
408 m_pNCManager = new CNodeCreationManager(this, m_pEventHandler, m_pSettingsStore, true, m_deGameModeStrings[0], m_AlphIO);
411 m_Alphabet = m_pNCManager->GetAlphabet();
413 string T = m_Alphabet->GetTrainingFile();
415 int iTotalBytes = 0;
416 iTotalBytes += GetFileSize(GetStringParameter(SP_SYSTEM_LOC) + T);
417 iTotalBytes += GetFileSize(GetStringParameter(SP_USER_LOC) + T);
419 if(iTotalBytes > 0) {
420 int iOffset;
421 iOffset = TrainFile(GetStringParameter(SP_SYSTEM_LOC) + T, iTotalBytes, 0);
422 TrainFile(GetStringParameter(SP_USER_LOC) + T, iTotalBytes, iOffset);
424 else {
425 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);
426 m_pEventHandler->InsertEvent(&oEvent);
429 pEvent = new CLockEvent("Training Dasher", false, 0);
430 m_pEventHandler->InsertEvent(pEvent);
431 delete pEvent;
433 ReleaseLock(0);
435 // TODO: Eventually we'll not have to pass the NC manager to the model...
436 CreateModel(iOffset);
439 // void CDasherInterfaceBase::Start() {
440 // // TODO: Clarify the relationship between Start() and
441 // // InvalidateContext() - I believe that they essentially do the same
442 // // thing
443 // PauseAt(0, 0);
444 // if(m_pDasherModel != 0) {
445 // m_pDasherModel->Start();
446 // }
447 // if(m_pDasherView != 0) {
448 // // m_pDasherView->ResetSum();
449 // // m_pDasherView->ResetSumCounter();
450 // // m_pDasherView->ResetYAutoOffset();
451 // }
453 // TODO: Reimplement this sometime
455 // int iMinWidth;
457 // if(m_pInputFilter && m_pInputFilter->GetMinWidth(iMinWidth)) {
458 // m_pDasherModel->LimitRoot(iMinWidth);
459 // }
460 // }
462 void CDasherInterfaceBase::PauseAt(int MouseX, int MouseY) {
463 SetBoolParameter(BP_DASHER_PAUSED, true);
465 // Request a full redraw at the next time step.
466 SetBoolParameter(BP_REDRAW, true);
468 Dasher::CStopEvent oEvent;
469 m_pEventHandler->InsertEvent(&oEvent);
471 if (m_pUserLog != NULL)
472 m_pUserLog->StopWriting((float) GetNats());
475 void CDasherInterfaceBase::Halt() {
476 SetBoolParameter(BP_DASHER_PAUSED, true);
478 // This will cause us to reinitialise the frame rate counter - ie we start off slowly
479 if(m_pDasherModel != 0)
480 m_pDasherModel->Halt();
483 void CDasherInterfaceBase::Unpause(unsigned long Time) {
484 SetBoolParameter(BP_DASHER_PAUSED, false);
486 if(m_pDasherModel != 0)
487 m_pDasherModel->Reset_framerate(Time);
489 Dasher::CStartEvent oEvent;
490 m_pEventHandler->InsertEvent(&oEvent);
492 ResetNats();
493 if (m_pUserLog != NULL)
494 m_pUserLog->StartWriting();
497 void CDasherInterfaceBase::CreateInput() {
498 if(m_pInput) {
499 m_pInput->Deactivate();
500 m_pInput->Unref();
503 m_pInput = (CDasherInput *)GetModuleByName(GetStringParameter(SP_INPUT_DEVICE));
505 if(m_pInput) {
506 m_pInput->Ref();
507 m_pInput->Activate();
510 if(m_pDasherView != 0)
511 m_pDasherView->SetInput(m_pInput);
514 void CDasherInterfaceBase::NewFrame(unsigned long iTime, bool bForceRedraw) {
515 // Fail if Dasher is locked
516 if(m_iCurrentState != ST_NORMAL)
517 return;
519 bool bChanged(false);
521 if(m_pDasherView != 0) {
522 if(!GetBoolParameter(BP_TRAINING)) {
523 if (m_pUserLog != NULL) {
525 Dasher::VECTOR_SYMBOL_PROB vAdded;
526 int iNumDeleted = 0;
528 if(m_pInputFilter) {
529 bChanged = m_pInputFilter->Timer(iTime, m_pDasherView, m_pDasherModel, &vAdded, &iNumDeleted);
532 if (iNumDeleted > 0)
533 m_pUserLog->DeleteSymbols(iNumDeleted);
534 if (vAdded.size() > 0)
535 m_pUserLog->AddSymbols(&vAdded);
538 else {
539 if(m_pInputFilter) {
540 bChanged = m_pInputFilter->Timer(iTime, m_pDasherView, m_pDasherModel, 0, 0);
544 m_pDasherModel->CheckForNewRoot(m_pDasherView);
548 // TODO: This is a bit hacky - we really need to sort out the redraw logic
549 if((!bChanged && m_bLastChanged) || m_bRedrawScheduled || bForceRedraw) {
550 m_pDasherView->Screen()->SetCaptureBackground(true);
551 m_pDasherView->Screen()->SetLoadBackground(true);
554 Redraw((bChanged || m_bLastChanged) || m_bRedrawScheduled || bForceRedraw);
556 m_bLastChanged = bChanged;
557 m_bRedrawScheduled = false;
559 // This just passes the time through to the framerate tracker, so we
560 // know how often new frames are being drawn.
561 if(m_pDasherModel != 0)
562 m_pDasherModel->NewFrame(iTime);
565 void CDasherInterfaceBase::CheckRedraw() {
566 if(m_bRedrawScheduled)
567 Redraw(true);
569 m_bRedrawScheduled = false;
572 /// Full redraw - renders model, decorations and blits onto display
573 /// buffer. Not recommended if nothing has changed with the model,
574 /// otherwise we're wasting effort.
576 void CDasherInterfaceBase::Redraw(bool bRedrawNodes) {
578 if(!m_pDasherView || !m_pDasherModel)
579 return;
581 // TODO: More generic notion of 'layers'?
583 if(bRedrawNodes) {
584 m_pDasherView->Screen()->SendMarker(0);
585 m_pDasherModel->RenderToView(m_pDasherView, true);
588 m_pDasherView->Screen()->SendMarker(1);
590 bool bDecorationsChanged(false);
592 if(m_pInputFilter) {
593 bDecorationsChanged = m_pInputFilter->DecorateView(m_pDasherView);
596 bool bActionButtonsChanged(false);
597 #ifdef EXPERIMENTAL_FEATURES
598 bActionButtonsChanged = DrawActionButtons();
599 #endif
601 if(bRedrawNodes || bDecorationsChanged || bActionButtonsChanged)
602 m_pDasherView->Display();
605 void CDasherInterfaceBase::ChangeAlphabet() {
607 if(GetStringParameter(SP_ALPHABET_ID) == "") {
608 SetStringParameter(SP_ALPHABET_ID, m_AlphIO->GetDefault());
609 // This will result in ChangeAlphabet() being called again, so
610 // exit from the first recursion
611 return;
614 // Send a lock event
616 WriteTrainFileFull();
618 // Lock Dasher to prevent changes from happening while we're training.
620 SetBoolParameter( BP_TRAINING, true );
622 CreateNCManager();
624 // Let our user log object know about the new alphabet since
625 // it needs to convert symbols into text for the log file.
626 if (m_pUserLog != NULL)
627 m_pUserLog->SetAlphabetPtr(m_Alphabet);
629 // Apply options from alphabet
631 SetBoolParameter( BP_TRAINING, false );
636 void CDasherInterfaceBase::ChangeColours() {
637 if(!m_ColourIO || !m_DasherScreen)
638 return;
640 // TODO: Make fuction return a pointer directly
641 m_DasherScreen->SetColourScheme(&(m_ColourIO->GetInfo(GetStringParameter(SP_COLOUR_ID))));
644 void CDasherInterfaceBase::ChangeScreen(CDasherScreen *NewScreen) {
645 m_DasherScreen = NewScreen;
646 ChangeColours();
648 if(m_pDasherView != 0) {
649 m_pDasherView->ChangeScreen(m_DasherScreen);
650 } else {
651 if(GetLongParameter(LP_VIEW_ID) != -1)
652 ChangeView();
655 PositionActionButtons();
656 ScheduleRedraw();
659 void CDasherInterfaceBase::ChangeView() {
660 // TODO: Actually respond to LP_VIEW_ID parameter (although there is only one view at the moment)
662 if(m_DasherScreen != 0 && m_pDasherModel != 0) {
663 delete m_pDasherView;
665 m_pDasherView = new CDasherViewSquare(m_pEventHandler, m_pSettingsStore, m_DasherScreen);
667 if (m_pInput)
668 m_pDasherView->SetInput(m_pInput);
672 const CAlphIO::AlphInfo & CDasherInterfaceBase::GetInfo(const std::string &AlphID) {
673 return m_AlphIO->GetInfo(AlphID);
676 void CDasherInterfaceBase::SetInfo(const CAlphIO::AlphInfo &NewInfo) {
677 m_AlphIO->SetInfo(NewInfo);
680 void CDasherInterfaceBase::DeleteAlphabet(const std::string &AlphID) {
681 m_AlphIO->Delete(AlphID);
685 I've used C style I/O because I found that C++ style I/O bloated
686 the Win32 code enormously. The overhead of loading the buffer into
687 the string instead of reading straight into a string seems to be
688 negligible compared to huge requirements elsewhere.
690 int CDasherInterfaceBase::TrainFile(std::string Filename, int iTotalBytes, int iOffset) {
691 if(Filename == "")
692 return 0;
694 FILE *InputFile;
695 if((InputFile = fopen(Filename.c_str(), "r")) == (FILE *) 0)
696 return 0;
698 AddLock(0);
700 const int BufferSize = 1024;
701 char InputBuffer[BufferSize];
702 string StringBuffer;
703 int NumberRead;
704 int iTotalRead(0);
706 vector < symbol > Symbols;
708 CAlphabetManagerFactory::CTrainer * pTrainer = m_pNCManager->GetTrainer();
709 do {
710 NumberRead = fread(InputBuffer, 1, BufferSize - 1, InputFile);
711 InputBuffer[NumberRead] = '\0';
712 StringBuffer += InputBuffer;
713 bool bIsMore = false;
714 if(NumberRead == (BufferSize - 1))
715 bIsMore = true;
717 Symbols.clear();
718 m_Alphabet->GetSymbols(&Symbols, &StringBuffer, bIsMore);
720 pTrainer->Train(Symbols);
721 iTotalRead += NumberRead;
723 // TODO: No reason for this to be a pointer (other than cut/paste laziness!)
724 CLockEvent *pEvent;
725 pEvent = new CLockEvent("Training Dasher", true, static_cast<int>((100.0 * (iTotalRead + iOffset))/iTotalBytes));
726 m_pEventHandler->InsertEvent(pEvent);
727 delete pEvent;
729 } while(NumberRead == BufferSize - 1);
731 delete pTrainer;
733 fclose(InputFile);
735 ReleaseLock(0);
737 return iTotalRead;
740 void CDasherInterfaceBase::GetFontSizes(std::vector <int >*FontSizes) const {
741 FontSizes->push_back(20);
742 FontSizes->push_back(14);
743 FontSizes->push_back(11);
744 FontSizes->push_back(40);
745 FontSizes->push_back(28);
746 FontSizes->push_back(22);
747 FontSizes->push_back(80);
748 FontSizes->push_back(56);
749 FontSizes->push_back(44);
752 double CDasherInterfaceBase::GetCurCPM() {
754 return 0;
757 double CDasherInterfaceBase::GetCurFPS() {
759 return 0;
762 // int CDasherInterfaceBase::GetAutoOffset() {
763 // if(m_pDasherView != 0) {
764 // return m_pDasherView->GetAutoOffset();
765 // }
766 // return -1;
767 // }
769 double CDasherInterfaceBase::GetNats() const {
770 if(m_pDasherModel)
771 return m_pDasherModel->GetNats();
772 else
773 return 0.0;
776 void CDasherInterfaceBase::ResetNats() {
777 if(m_pDasherModel)
778 m_pDasherModel->ResetNats();
782 // TODO: Check that none of this needs to be reimplemented
784 // void CDasherInterfaceBase::InvalidateContext(bool bForceStart) {
785 // m_pDasherModel->m_strContextBuffer = "";
787 // Dasher::CEditContextEvent oEvent(10);
788 // m_pEventHandler->InsertEvent(&oEvent);
790 // std::string strNewContext(m_pDasherModel->m_strContextBuffer);
792 // // We keep track of an internal context and compare that to what
793 // // we are given - don't restart Dasher if nothing has changed.
794 // // This should really be integrated with DasherModel, which
795 // // probably will be the case when we start to deal with being able
796 // // to back off indefinitely. For now though we'll keep it in a
797 // // separate string.
799 // int iContextLength( 6 ); // The 'important' context length - should really get from language model
801 // // FIXME - use unicode lengths
803 // if(bForceStart || (strNewContext.substr( std::max(static_cast<int>(strNewContext.size()) - iContextLength, 0)) != strCurrentContext.substr( std::max(static_cast<int>(strCurrentContext.size()) - iContextLength, 0)))) {
805 // if(m_pDasherModel != NULL) {
806 // // TODO: Reimplement this
807 // // if(m_pDasherModel->m_bContextSensitive || bForceStart) {
808 // {
809 // m_pDasherModel->SetContext(strNewContext);
810 // PauseAt(0,0);
811 // }
812 // }
814 // strCurrentContext = strNewContext;
815 // WriteTrainFileFull();
816 // }
818 // if(bForceStart) {
819 // int iMinWidth;
821 // if(m_pInputFilter && m_pInputFilter->GetMinWidth(iMinWidth)) {
822 // m_pDasherModel->LimitRoot(iMinWidth);
823 // }
824 // }
826 // if(m_pDasherView)
827 // while( m_pDasherModel->CheckForNewRoot(m_pDasherView) ) {
828 // // Do nothing
829 // }
831 // ScheduleRedraw();
832 // }
834 // TODO: Fix this
836 std::string CDasherInterfaceBase::GetContext(int iStart, int iLength) {
837 m_strContext = "";
839 CEditContextEvent oEvent(iStart, iLength);
840 m_pEventHandler->InsertEvent(&oEvent);
842 return m_strContext;
845 void CDasherInterfaceBase::SetContext(std::string strNewContext) {
846 m_strContext = strNewContext;
849 // Control mode stuff
851 void CDasherInterfaceBase::RegisterNode( int iID, const std::string &strLabel, int iColour ) {
852 m_pNCManager->RegisterNode(iID, strLabel, iColour);
855 void CDasherInterfaceBase::ConnectNode(int iChild, int iParent, int iAfter) {
856 m_pNCManager->ConnectNode(iChild, iParent, iAfter);
859 void CDasherInterfaceBase::DisconnectNode(int iChild, int iParent) {
860 m_pNCManager->DisconnectNode(iChild, iParent);
863 void CDasherInterfaceBase::SetBoolParameter(int iParameter, bool bValue) {
864 m_pSettingsStore->SetBoolParameter(iParameter, bValue);
867 void CDasherInterfaceBase::SetLongParameter(int iParameter, long lValue) {
868 m_pSettingsStore->SetLongParameter(iParameter, lValue);
871 void CDasherInterfaceBase::SetStringParameter(int iParameter, const std::string & sValue) {
872 PreSetNotify(iParameter, sValue);
873 m_pSettingsStore->SetStringParameter(iParameter, sValue);
876 bool CDasherInterfaceBase::GetBoolParameter(int iParameter) {
877 return m_pSettingsStore->GetBoolParameter(iParameter);
880 long CDasherInterfaceBase::GetLongParameter(int iParameter) {
881 return m_pSettingsStore->GetLongParameter(iParameter);
884 std::string CDasherInterfaceBase::GetStringParameter(int iParameter) {
885 return m_pSettingsStore->GetStringParameter(iParameter);
888 void CDasherInterfaceBase::ResetParameter(int iParameter) {
889 m_pSettingsStore->ResetParameter(iParameter);
892 // We need to be able to get at the UserLog object from outside the interface
893 CUserLogBase* CDasherInterfaceBase::GetUserLogPtr() {
894 return m_pUserLog;
897 void CDasherInterfaceBase::KeyDown(int iTime, int iId) {
898 if(m_iCurrentState != ST_NORMAL)
899 return;
901 if(m_pInputFilter && !GetBoolParameter(BP_TRAINING)) {
902 m_pInputFilter->KeyDown(iTime, iId, m_pDasherModel, m_pUserLog);
905 if(m_pInput && !GetBoolParameter(BP_TRAINING)) {
906 m_pInput->KeyDown(iTime, iId);
910 void CDasherInterfaceBase::KeyUp(int iTime, int iId) {
911 if(m_iCurrentState != ST_NORMAL)
912 return;
914 if(m_pInputFilter && !GetBoolParameter(BP_TRAINING)) {
915 m_pInputFilter->KeyUp(iTime, iId, m_pDasherModel);
918 if(m_pInput && !GetBoolParameter(BP_TRAINING)) {
919 m_pInput->KeyUp(iTime, iId);
923 void CDasherInterfaceBase::CreateInputFilter()
925 if(m_pInputFilter) {
926 m_pInputFilter->Deactivate();
927 m_pInputFilter->Unref();
928 m_pInputFilter = NULL;
931 m_pInputFilter = (CInputFilter *)GetModuleByName(GetStringParameter(SP_INPUT_FILTER));
933 if(m_pInputFilter) {
934 m_pInputFilter->Ref();
935 m_pInputFilter->Activate();
937 else {
938 // Fall back to a sensible alternative if for some reason the
939 // current choice isn't valid.
940 if(GetStringParameter(SP_INPUT_FILTER) != "Normal Control")
941 SetStringParameter(SP_INPUT_FILTER, "Normal Control");
945 void CDasherInterfaceBase::RegisterFactory(CModuleFactory *pFactory) {
946 m_oModuleManager.RegisterFactory(pFactory);
949 CDasherModule *CDasherInterfaceBase::GetModule(ModuleID_t iID) {
950 return m_oModuleManager.GetModule(iID);
953 CDasherModule *CDasherInterfaceBase::GetModuleByName(const std::string &strName) {
954 return m_oModuleManager.GetModuleByName(strName);
957 void CDasherInterfaceBase::CreateFactories() {
958 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDefaultFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel,3, _("Normal Control"))));
959 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new COneDimensionalFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel)));
960 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CEyetrackerFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel)));
961 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CClickFilter(m_pEventHandler, m_pSettingsStore, this)));
962 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDynamicFilter(m_pEventHandler, m_pSettingsStore, this)));
963 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CTwoButtonDynamicFilter(m_pEventHandler, m_pSettingsStore, this, 14, 1, _("Two Button Dynamic Mode"))));
964 // TODO: specialist factory for button mode
965 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 5, 1, true,8, _("Menu Mode"))));
966 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 0, false,10, _("Direct Mode"))));
967 // RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 0, false,11, "Buttons 3")));
968 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 3, 3, false,12, _("Alternating Direct Mode"))));
969 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CDasherButtons(m_pEventHandler, m_pSettingsStore, this, 4, 2, false,13, _("Compass Mode"))));
970 RegisterFactory(new CWrapperFactory(m_pEventHandler, m_pSettingsStore, new CStylusFilter(m_pEventHandler, m_pSettingsStore, this, m_pDasherModel,15, _("Stylus Control"))));
974 void CDasherInterfaceBase::GetPermittedValues(int iParameter, std::vector<std::string> &vList) {
975 // TODO: Deprecate direct calls to these functions
976 switch (iParameter) {
977 case SP_ALPHABET_ID:
978 if(m_AlphIO)
979 m_AlphIO->GetAlphabets(&vList);
980 break;
981 case SP_COLOUR_ID:
982 if(m_ColourIO)
983 m_ColourIO->GetColours(&vList);
984 break;
985 case SP_INPUT_FILTER:
986 m_oModuleManager.ListModules(1, vList);
987 break;
988 case SP_INPUT_DEVICE:
989 m_oModuleManager.ListModules(0, vList);
990 break;
994 void CDasherInterfaceBase::StartShutdown() {
995 ChangeState(TR_SHUTDOWN);
998 bool CDasherInterfaceBase::GetModuleSettings(const std::string &strName, SModuleSettings **pSettings, int *iCount) {
999 return GetModuleByName(strName)->GetSettings(pSettings, iCount);
1002 void CDasherInterfaceBase::SetupActionButtons() {
1003 m_vLeftButtons.push_back(new CActionButton(this, "Exit", true));
1004 m_vLeftButtons.push_back(new CActionButton(this, "Preferences", false));
1005 m_vLeftButtons.push_back(new CActionButton(this, "Help", false));
1006 m_vLeftButtons.push_back(new CActionButton(this, "About", false));
1009 void CDasherInterfaceBase::DestroyActionButtons() {
1010 // TODO: implement and call this
1013 void CDasherInterfaceBase::PositionActionButtons() {
1014 if(!m_DasherScreen)
1015 return;
1017 int iCurrentOffset(16);
1019 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
1020 (*it)->SetPosition(16, iCurrentOffset, 32, 32);
1021 iCurrentOffset += 48;
1024 iCurrentOffset = 16;
1026 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
1027 (*it)->SetPosition(m_DasherScreen->GetWidth() - 144, iCurrentOffset, 128, 32);
1028 iCurrentOffset += 48;
1032 bool CDasherInterfaceBase::DrawActionButtons() {
1033 if(!m_DasherScreen)
1034 return false;
1036 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
1038 bool bRV(bVisible != m_bOldVisible);
1039 m_bOldVisible = bVisible;
1041 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it)
1042 (*it)->Draw(m_DasherScreen, bVisible);
1044 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it)
1045 (*it)->Draw(m_DasherScreen, bVisible);
1047 return bRV;
1051 void CDasherInterfaceBase::HandleClickUp(int iTime, int iX, int iY) {
1052 #ifdef EXPERIMENTAL_FEATURES
1053 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
1055 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
1056 if((*it)->HandleClickUp(iTime, iX, iY, bVisible))
1057 return;
1060 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
1061 if((*it)->HandleClickUp(iTime, iX, iY, bVisible))
1062 return;
1064 #endif
1066 KeyUp(iTime, 100);
1069 void CDasherInterfaceBase::HandleClickDown(int iTime, int iX, int iY) {
1070 #ifdef EXPERIMENTAL_FEATURES
1071 bool bVisible(GetBoolParameter(BP_DASHER_PAUSED));
1073 for(std::vector<CActionButton *>::iterator it(m_vLeftButtons.begin()); it != m_vLeftButtons.end(); ++it) {
1074 if((*it)->HandleClickDown(iTime, iX, iY, bVisible))
1075 return;
1078 for(std::vector<CActionButton *>::iterator it(m_vRightButtons.begin()); it != m_vRightButtons.end(); ++it) {
1079 if((*it)->HandleClickDown(iTime, iX, iY, bVisible))
1080 return;
1082 #endif
1084 KeyDown(iTime, 100);
1088 void CDasherInterfaceBase::ExecuteCommand(const std::string &strName) {
1089 // TODO: Pointless - just insert event directly
1091 CCommandEvent *pEvent = new CCommandEvent(strName);
1092 m_pEventHandler->InsertEvent(pEvent);
1093 delete pEvent;
1096 double CDasherInterfaceBase::GetFramerate() {
1097 if(m_pDasherModel)
1098 return(m_pDasherModel->Framerate());
1099 else
1100 return 0.0;
1103 int CDasherInterfaceBase::GetRenderCount() {
1104 if(m_pDasherView)
1105 return(m_pDasherView->GetRenderCount());
1106 else
1107 return 0;
1110 void CDasherInterfaceBase::AddActionButton(const std::string &strName) {
1111 m_vRightButtons.push_back(new CActionButton(this, strName, false));
1115 void CDasherInterfaceBase::OnUIRealised() {
1116 StartTimer();
1117 ChangeState(TR_UI_INIT);
1121 void CDasherInterfaceBase::ChangeState(ETransition iTransition) {
1122 static EState iTransitionTable[ST_NUM][TR_NUM] = {
1123 {ST_MODEL, ST_UI, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1124 {ST_FORBIDDEN, ST_NORMAL, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1125 {ST_NORMAL, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN},
1126 {ST_FORBIDDEN, ST_FORBIDDEN, ST_LOCKED, ST_FORBIDDEN, ST_SHUTDOWN},
1127 {ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_NORMAL, ST_FORBIDDEN},
1128 {ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN, ST_FORBIDDEN}
1131 EState iNewState(iTransitionTable[m_iCurrentState][iTransition]);
1133 if(iNewState != ST_FORBIDDEN) {
1134 LeaveState(m_iCurrentState);
1135 EnterState(iNewState);
1137 m_iCurrentState = iNewState;
1141 void CDasherInterfaceBase::LeaveState(EState iState) {
1145 void CDasherInterfaceBase::EnterState(EState iState) {
1146 switch(iState) {
1147 case ST_SHUTDOWN:
1148 ShutdownTimer();
1149 WriteTrainFileFull();
1150 break;
1151 default:
1152 // Not handled
1153 break;
1157 void CDasherInterfaceBase::AddLock(int iLockFlags) {
1158 if(m_iLockCount == 0)
1159 ChangeState(TR_LOCK);
1161 ++m_iLockCount;
1164 void CDasherInterfaceBase::ReleaseLock(int iLockFlags) {
1165 if(m_iLockCount > 0)
1166 --m_iLockCount;
1168 if(m_iLockCount == 0)
1169 ChangeState(TR_UNLOCK);
1172 void CDasherInterfaceBase::SetBuffer(int iOffset) {
1173 CreateModel(iOffset);
1176 void CDasherInterfaceBase::UnsetBuffer() {
1177 // TODO: Write training file?
1178 if(m_pDasherModel)
1179 delete m_pDasherModel;
1181 m_pDasherModel = 0;
1184 void CDasherInterfaceBase::SetOffset(int iOffset) {
1185 if(m_pDasherModel)
1186 m_pDasherModel->SetOffset(iOffset, m_pDasherView);
1189 void CDasherInterfaceBase::SetControlOffset(int iOffset) {
1190 if(m_pDasherModel)
1191 m_pDasherModel->SetControlOffset(iOffset);