[dasher: 26/43] CMessageDisplay: add bool param 4 non-/modal; use instead of many cout/cerr's



commit af8fa9f41433fe21e7f6247727662941c6c67e1b
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Mon Jun 13 18:28:41 2011 +0100

    CMessageDisplay: add bool param 4 non-/modal; use instead of many cout/cerr's
    
    Message(string,bool): true = modal/interrupt user, false = non-modal/don't
    
    CDashIntfScreenMsgs renders modal messages on canvas too, but in yellow;
     and keeps around until user restarts Dasher (by calling Unpause(time)).

 Src/DasherCore/AbstractXMLParser.cpp         |   28 ++++++---
 Src/DasherCore/AbstractXMLParser.h           |    8 ++-
 Src/DasherCore/Alphabet/AlphIO.cpp           |   10 ++--
 Src/DasherCore/Alphabet/AlphIO.h             |    2 +-
 Src/DasherCore/Alphabet/AlphInfo.h           |    1 +
 Src/DasherCore/Alphabet/AlphabetMap.cpp      |   27 +++++----
 Src/DasherCore/Alphabet/AlphabetMap.h        |    6 ++-
 Src/DasherCore/AlphabetManager.cpp           |    2 +-
 Src/DasherCore/AlphabetManager.h             |    4 +-
 Src/DasherCore/AutoSpeedControl.cpp          |   15 +++--
 Src/DasherCore/ColourIO.cpp                  |   10 ++--
 Src/DasherCore/ColourIO.h                    |    2 +-
 Src/DasherCore/ControlManager.cpp            |    8 +-
 Src/DasherCore/ControlManager.h              |    3 +-
 Src/DasherCore/ConversionManager.cpp         |    8 +-
 Src/DasherCore/ConversionManager.h           |    4 +-
 Src/DasherCore/DashIntfScreenMsgs.cpp        |   81 ++++++++++++++++++++------
 Src/DasherCore/DashIntfScreenMsgs.h          |   28 +++++++--
 Src/DasherCore/DasherInterfaceBase.cpp       |    6 +-
 Src/DasherCore/DasherInterfaceBase.h         |    2 +-
 Src/DasherCore/MandarinAlphMgr.cpp           |    4 +-
 Src/DasherCore/Messages.h                    |   15 ++++-
 Src/DasherCore/NodeCreationManager.cpp       |   27 +++++++--
 Src/DasherCore/SocketInput.cpp               |    4 +-
 Src/DasherCore/SocketInput.h                 |    2 +-
 Src/DasherCore/SocketInputBase.cpp           |   39 +++++++------
 Src/DasherCore/SocketInputBase.h             |    9 ++-
 Src/DasherCore/Trainer.cpp                   |   59 ++++++++++++-------
 Src/DasherCore/Trainer.h                     |   15 ++---
 Src/Gtk2/DasherControl.cpp                   |    2 +-
 Src/Win32/Dasher.cpp                         |    2 +-
 Src/Win32/Sockets/SocketInput.cpp            |    4 +-
 Src/Win32/Sockets/SocketInput.h              |    2 +-
 Src/iPhone/Classes/CDasherInterfaceBridge.h  |   13 +++-
 Src/iPhone/Classes/CDasherInterfaceBridge.mm |    7 ++-
 35 files changed, 298 insertions(+), 161 deletions(-)
---
diff --git a/Src/DasherCore/AbstractXMLParser.cpp b/Src/DasherCore/AbstractXMLParser.cpp
index bf85e0b..14a567d 100644
--- a/Src/DasherCore/AbstractXMLParser.cpp
+++ b/Src/DasherCore/AbstractXMLParser.cpp
@@ -8,10 +8,12 @@
  */
 
 #include "AbstractXMLParser.h"
-#ifdef DEBUG
-#include <iostream>
-#endif
-bool AbstractXMLParser::ParseFile(const std::string &strFilename) {
+
+#include <string.h>
+
+using std::string;
+
+bool AbstractXMLParser::ParseFile(CMessageDisplay *pMsgs, const std::string &strFilename) {
   FILE *Input;
   if((Input = fopen(strFilename.c_str(), "r")) == (FILE *) 0) {
     // could not open file
@@ -26,24 +28,30 @@ bool AbstractXMLParser::ParseFile(const std::string &strFilename) {
 
   XML_SetElementHandler(Parser, XML_StartElement, XML_EndElement);
   XML_SetCharacterDataHandler(Parser, XML_CharacterData);
-
+  bool bRes(true);
   char Buffer[1024];
   int Done;
   do {
     size_t len = fread(Buffer, 1, sizeof(Buffer), Input);
     Done = len < sizeof(Buffer);
     if(XML_Parse(Parser, Buffer, len, Done) == XML_STATUS_ERROR) {
-      //TODO, should we make sure we return false, if this happens?
-#ifdef DEBUG
-      std::cout << "Parse error! on " << std::string(Buffer,len) << std::endl;
-#endif
+      bRes=false;
+      if (pMsgs) {
+        const char *msg=_("XML Error %s in file %s somewhere in block: %s");
+        const XML_LChar *xmle=XML_ErrorString(XML_GetErrorCode(Parser)); //think XML_LChar==char, depends on preprocessor variables...
+        char *buf(new char[strlen(msg)+ strlen(xmle) + strFilename.length() + len + 3]);
+        //constructing a string here to get a null terminator. Can we put a null into Buffer instead?
+        sprintf(buf, msg, xmle, strFilename.c_str(), string(Buffer,len).c_str());
+        pMsgs->Message(buf,true);
+        delete buf;
+      }
       break;
     }
   } while (!Done);
 
   XML_ParserFree(Parser);
   fclose(Input);
-  return true;
+  return bRes;
 }
 
 void AbstractXMLParser::XmlCData(const XML_Char *str, int len) {
diff --git a/Src/DasherCore/AbstractXMLParser.h b/Src/DasherCore/AbstractXMLParser.h
index 0a4a335..6f64fa1 100644
--- a/Src/DasherCore/AbstractXMLParser.h
+++ b/Src/DasherCore/AbstractXMLParser.h
@@ -15,6 +15,8 @@
 #include <config.h>
 #endif
 
+#include "Messages.h"
+
 #include <string>
 #include <expat.h>
 
@@ -24,8 +26,10 @@
 class AbstractXMLParser {
 public:
   ///Parse (the whole) file - done in chunks to avoid loading the whole thing into memory.
-  /// \return true if the file was opened+parsed; false if not (i.e. filename did not exist)
-  bool ParseFile(const std::string &strFilename);
+  /// \param pInterface if non-null, any errors _besides_ file-not-found, will be passed
+  /// to the Message(,true) method to report to the user.
+  /// \return true if the file was opened+parsed OK; false if there was an error (e.g. FNF)
+  bool ParseFile(CMessageDisplay *pMsgs, const std::string &strFilename);
 protected:
   ///Subclass should override to handle a start tag
   virtual void XmlStartHandler(const XML_Char *name, const XML_Char **atts)=0;
diff --git a/Src/DasherCore/Alphabet/AlphIO.cpp b/Src/DasherCore/Alphabet/AlphIO.cpp
index d854552..f26d321 100644
--- a/Src/DasherCore/Alphabet/AlphIO.cpp
+++ b/Src/DasherCore/Alphabet/AlphIO.cpp
@@ -36,7 +36,7 @@ static char THIS_FILE[] = __FILE__;
 #endif
 #endif
 
-CAlphIO::CAlphIO(const std::string &SystemLocation, const std::string &UserLocation, const std::vector<std::string> &Filenames)
+CAlphIO::CAlphIO(CMessageDisplay *pMsgs, const std::string &SystemLocation, const std::string &UserLocation, const std::vector<std::string> &Filenames)
 : LoadMutable(false), CData("") {
   Alphabets["Default"]=CreateDefault();
 
@@ -63,17 +63,17 @@ CAlphIO::CAlphIO(const std::string &SystemLocation, const std::string &UserLocat
   }
 
   LoadMutable = false;
-  ParseFile(SystemLocation + "alphabet.xml");
+  ParseFile(pMsgs, SystemLocation + "alphabet.xml");
   if(Filenames.size() > 0) {
     for(unsigned int i = 0; i < Filenames.size(); i++) {
-      ParseFile(SystemLocation + Filenames[i]);
+      ParseFile(pMsgs, SystemLocation + Filenames[i]);
     }
   }
   LoadMutable = true;
-  ParseFile(UserLocation + "alphabet.xml");
+  ParseFile(pMsgs, UserLocation + "alphabet.xml");
   if(Filenames.size() > 0) {
     for(unsigned int i = 0; i < Filenames.size(); i++) {
-      ParseFile(UserLocation + Filenames[i]);
+      ParseFile(pMsgs, UserLocation + Filenames[i]);
     }
   }
 }
diff --git a/Src/DasherCore/Alphabet/AlphIO.h b/Src/DasherCore/Alphabet/AlphIO.h
index 7d969fc..908ec0e 100644
--- a/Src/DasherCore/Alphabet/AlphIO.h
+++ b/Src/DasherCore/Alphabet/AlphIO.h
@@ -50,7 +50,7 @@ namespace Dasher {
 class Dasher::CAlphIO : private AbstractXMLParser {
 public:
 
-  CAlphIO(const std::string &SystemLocation, const std::string &UserLocation, const std::vector < std::string > &Filenames);
+  CAlphIO(CMessageDisplay *pMsgs, const std::string &SystemLocation, const std::string &UserLocation, const std::vector < std::string > &Filenames);
   ~CAlphIO();
   void GetAlphabets(std::vector < std::string > *AlphabetList) const;
   std::string GetDefault();
diff --git a/Src/DasherCore/Alphabet/AlphInfo.h b/Src/DasherCore/Alphabet/AlphInfo.h
index 7e137af..7cbbc1a 100644
--- a/Src/DasherCore/Alphabet/AlphInfo.h
+++ b/Src/DasherCore/Alphabet/AlphInfo.h
@@ -66,6 +66,7 @@ private:
     std::string Foreground;
   };
 public:
+  const std::string &GetID() const {return AlphID;}
   /// Return number of text symbols - inc space and para, but no control/conversion start/end
   /// Note symbol numbers are 1-indexed; 0 is reserved to indicate an "unknown symbol" (-1 = End-Of-Stream),
   /// and for element 0 of the probability array to contain a 0.
diff --git a/Src/DasherCore/Alphabet/AlphabetMap.cpp b/Src/DasherCore/Alphabet/AlphabetMap.cpp
index 8adf97b..a873bd9 100644
--- a/Src/DasherCore/Alphabet/AlphabetMap.cpp
+++ b/Src/DasherCore/Alphabet/AlphabetMap.cpp
@@ -75,8 +75,8 @@ int utf8_length::operator[](const unsigned char i) const
 
 ////////////////////////////////////////////////////////////////////////////
 
-CAlphabetMap::SymbolStream::SymbolStream(std::istream &_in)
-: pos(0), len(0), in(_in) {
+CAlphabetMap::SymbolStream::SymbolStream(std::istream &_in, CMessageDisplay *pMsgs)
+: pos(0), len(0), in(_in), m_pMsgs(pMsgs) {
   readMore();
 }
 
@@ -112,19 +112,24 @@ inline int CAlphabetMap::SymbolStream::findNext() {
     if (int numChars = m_utf8_count_array[buf[pos]]) {
       if (pos+numChars > len) {
         //no more bytes in file (would have tried to read earlier), but not enough for char
-#ifdef DEBUG
-        std::cerr << "Incomplete UTF-8 character beginning 0x" << hex << buf[pos] << dec;
-        std::cerr << "(expecting " << numChars << " bytes but only " << (len-pos) << ")" << std::endl;
-#endif
-        pos=len;
+        if (m_pMsgs) {
+          const char *msg(_("File ends with incomplete UTF-8 character beginning 0x%x (expecting %i bytes but only %i)"));
+          char *mbuf(new char[strlen(msg) + 4]);
+          sprintf(mbuf, msg, static_cast<unsigned int>(buf[pos] & 0xff), numChars, len-pos);
+          m_pMsgs->Message(mbuf,true);
+          delete mbuf;
+        }
         return 0;
       }
       return numChars;
     }
-#ifdef DEBUG
-    std::cerr << "Read invalid UTF-8 character 0x" << hex << buf[pos]
-    << dec << std::endl;
-#endif
+    if (m_pMsgs) {
+      const char *msg(_("Read invalid UTF-8 character 0x%x"));
+      char *mbuf(new char[strlen(msg) + 2]);
+      sprintf(mbuf, msg, static_cast<unsigned int>(buf[pos] & 0xff));
+      m_pMsgs->Message(mbuf,true);
+      delete mbuf;
+    }
     ++pos;    
   }
 }
diff --git a/Src/DasherCore/Alphabet/AlphabetMap.h b/Src/DasherCore/Alphabet/AlphabetMap.h
index 4ba04bc..181786c 100644
--- a/Src/DasherCore/Alphabet/AlphabetMap.h
+++ b/Src/DasherCore/Alphabet/AlphabetMap.h
@@ -72,6 +72,8 @@ namespace Dasher {
 /// 
 /// IAM 08/2002
 
+#include "../Messages.h"
+
 class Dasher::CAlphabetMap {
 
 public:
@@ -83,7 +85,8 @@ public:
 
   class SymbolStream {
   public:
-    SymbolStream(std::istream &_in);
+    ///pMsgs used for reporting errors in utf8 encoding
+    SymbolStream(std::istream &_in, CMessageDisplay *pMsgs=NULL);
     ///Gets the next symbol in the stream, using the specified AlphabetMap
     /// to convert unicode characters to symbols.
     /// \return 0 for unknown symbol (not in map); -1 for EOF; else symbol#.
@@ -116,6 +119,7 @@ public:
     char buf[1024];
     off_t pos, len;
     std::istream &in;
+    CMessageDisplay * const m_pMsgs;
   };
   
   // Fills Symbols with the symbols corresponding to Input. {{{ Note that this
diff --git a/Src/DasherCore/AlphabetManager.cpp b/Src/DasherCore/AlphabetManager.cpp
index 05c73da..9bc7f32 100644
--- a/Src/DasherCore/AlphabetManager.cpp
+++ b/Src/DasherCore/AlphabetManager.cpp
@@ -88,7 +88,7 @@ void CAlphabetManager::CreateLanguageModel(CEventHandler *pEventHandler, CSettin
 }
 
 CTrainer *CAlphabetManager::GetTrainer() {
-  return new CTrainer(m_pLanguageModel, m_pAlphabet, m_pAlphabetMap);
+  return new CTrainer(m_pInterface, m_pLanguageModel, m_pAlphabet, m_pAlphabetMap);
 }
 
 void CAlphabetManager::MakeLabels(CDasherScreen *pScreen) {
diff --git a/Src/DasherCore/AlphabetManager.h b/Src/DasherCore/AlphabetManager.h
index 01fbf12..be98812 100644
--- a/Src/DasherCore/AlphabetManager.h
+++ b/Src/DasherCore/AlphabetManager.h
@@ -232,6 +232,8 @@ namespace Dasher {
     ///  scheme for symbols not specifying a colour, and (b) implements
     /// colour-cycling by phase (two cycles, using the LSBit of offset)
     virtual int GetColour(symbol sym, int iOffset) const;
+    
+    CDasherInterfaceBase * const m_pInterface;
 
     CLanguageModel *m_pLanguageModel;
 
@@ -255,8 +257,6 @@ namespace Dasher {
     /// and graft itself in in place of a new node, when appropriate.
     void IterateChildGroups(CAlphNode *pParent, const SGroupInfo *pParentGroup, CAlphBase *buildAround);
 
-    CDasherInterfaceBase *m_pInterface;
-
     ///Last node (owned by this manager) that was output; if a node
     /// is Undo()ne, this is set to its parent. This is used to detect
     /// context switches.
diff --git a/Src/DasherCore/AutoSpeedControl.cpp b/Src/DasherCore/AutoSpeedControl.cpp
index a8dfe8a..10a8be3 100644
--- a/Src/DasherCore/AutoSpeedControl.cpp
+++ b/Src/DasherCore/AutoSpeedControl.cpp
@@ -8,8 +8,8 @@
 
 #include <cmath>
 #include <cfloat>
-#include <iostream>
-#include <sstream>
+
+#include <string.h>
 
 #ifndef WITH_DARWIN
 double round(double dVal) {
@@ -24,7 +24,6 @@ double round(double dVal) {
 #endif
 
 using namespace Dasher;
-using std::ostringstream;
 
 CAutoSpeedControl::CAutoSpeedControl(CMessageDisplay *pMsgs, Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore)
   : CDasherComponent(pEventHandler, pSettingsStore), m_pMsgs(pMsgs) {
@@ -210,10 +209,12 @@ void CAutoSpeedControl::SpeedControl(myint iDasherX, myint iDasherY, CDasherView
       UpdateBitrate();
       long lBitrateTimes100 =  long(round(m_dBitrate * 100)); //Dasher settings want long numerical parameters
       if (lBitrateTimes100 != GetLongParameter(LP_MAX_BITRATE)) {
-        ostringstream os;
-        os << "Auto-" << ((lBitrateTimes100 > GetLongParameter(LP_MAX_BITRATE)) ? "increasing" : "decreasing");
-        os << " speed to " << (lBitrateTimes100/100.0);
-        m_pMsgs->Message(os.str());
+        const char *msg((lBitrateTimes100 > GetLongParameter(LP_MAX_BITRATE)) ? 
+                   _("Auto-increasing speed to %0.2f") : _("Auto-decreasing speed to %0.2f"));
+        char *buf(new char[strlen(msg) + 5]);
+        sprintf(buf, msg, (lBitrateTimes100/100.0));
+        m_pMsgs->Message(buf,false);
+        delete buf;
         SetLongParameter(LP_MAX_BITRATE, lBitrateTimes100);
       }
       m_nSpeedCounter = 0;
diff --git a/Src/DasherCore/ColourIO.cpp b/Src/DasherCore/ColourIO.cpp
index 1b68df1..5df2232 100644
--- a/Src/DasherCore/ColourIO.cpp
+++ b/Src/DasherCore/ColourIO.cpp
@@ -23,22 +23,22 @@ static char THIS_FILE[] = __FILE__;
 
 // TODO: Share information with AlphIO class?
 
-CColourIO::CColourIO(const std::string &SystemLocation, const std::string &UserLocation, std::vector<std::string> &Filenames)
+CColourIO::CColourIO(CMessageDisplay *pMsgs, const string &SystemLocation, const string &UserLocation, const vector<string> &Filenames)
 :BlankInfo(), LoadMutable(false), CData("") {
   CreateDefault();
 
   LoadMutable = false;
-  ParseFile(SystemLocation + "colour.xml");
+  ParseFile(pMsgs, SystemLocation + "colour.xml");
   if(Filenames.size() > 0) {
     for(unsigned int i = 0; i < Filenames.size(); i++) {
-      ParseFile(SystemLocation + Filenames[i]);
+      ParseFile(pMsgs, SystemLocation + Filenames[i]);
     }
   }
   LoadMutable = true;
-  ParseFile(UserLocation + "colour.xml");
+  ParseFile(pMsgs, UserLocation + "colour.xml");
   if(Filenames.size() > 0) {
     for(unsigned int i = 0; i < Filenames.size(); i++) {
-      ParseFile(UserLocation + Filenames[i]);
+      ParseFile(pMsgs, UserLocation + Filenames[i]);
     }
   }
 }
diff --git a/Src/DasherCore/ColourIO.h b/Src/DasherCore/ColourIO.h
index 1d03e58..e707a4d 100644
--- a/Src/DasherCore/ColourIO.h
+++ b/Src/DasherCore/ColourIO.h
@@ -39,7 +39,7 @@ public:
     std::vector < int >Blues;
   };
 
-  CColourIO(const std::string &SystemLocation, const std::string &UserLocation, std::vector < std::string > &Filenames);
+  CColourIO(CMessageDisplay *pMsgs, const std::string &SystemLocation, const std::string &UserLocation, const std::vector < std::string > &Filenames);
   void GetColours(std::vector < std::string > *ColourList) const;
   const ColourInfo & GetInfo(const std::string & ColourID);
 private:
diff --git a/Src/DasherCore/ControlManager.cpp b/Src/DasherCore/ControlManager.cpp
index 7fd77b1..3ac66f4 100644
--- a/Src/DasherCore/ControlManager.cpp
+++ b/Src/DasherCore/ControlManager.cpp
@@ -158,7 +158,7 @@ public:
   vector<CControlBase::Action*> actions;
 };
 
-bool CControlParser::LoadFile(const string &strFileName) {
+bool CControlParser::LoadFile(CMessageDisplay *pMsgs, const string &strFileName) {
   ///Template used for all node defns read in from XML - just
   /// execute a list of Actions.
 
@@ -237,7 +237,7 @@ bool CControlParser::LoadFile(const string &strFileName) {
   };
 
   ParseHandler p(this);
-  if (!p.ParseFile(strFileName)) return false;
+  if (!p.ParseFile(pMsgs, strFileName)) return false;
   p.resolveRefs();
   return true;
 }
@@ -257,8 +257,8 @@ CControlManager::CControlManager(CEventHandler *pEventHandler, CSettingsStore *p
   m_pStop->successors.push_back(GetRootTemplate());
 
   //TODO, have a parameter to try first, and if that fails:
-  if(!LoadFile(m_pNCManager->GetStringParameter(SP_USER_LOC) + "control.xml")) {
-    LoadFile(m_pNCManager->GetStringParameter(SP_SYSTEM_LOC)+"control.xml");
+  if(!LoadFile(m_pInterface, m_pNCManager->GetStringParameter(SP_USER_LOC) + "control.xml")) {
+    LoadFile(m_pInterface, m_pNCManager->GetStringParameter(SP_SYSTEM_LOC)+"control.xml");
     //if that fails, we'll have no editing functions. Fine -
     // doesn't seem vital enough to hardcode a fallback as well!
   }
diff --git a/Src/DasherCore/ControlManager.h b/Src/DasherCore/ControlManager.h
index 3cf0676..793784e 100644
--- a/Src/DasherCore/ControlManager.h
+++ b/Src/DasherCore/ControlManager.h
@@ -146,9 +146,10 @@ namespace Dasher {
     ///Loads all node definitions from the specified filename, adding them to
     /// any loaded from previous calls. (However, files processed independently:
     /// e.g. names defined in one file will not be seen from another)
+    /// \param pMsgs Used to report errors via Message(,true) (i.e. modal)
     /// \param strFilename name+full-path of xml file to load
     /// \return true if the file was opened successfully; false if not.
-    bool LoadFile(const std::string &strFilename);
+    bool LoadFile(CMessageDisplay *pMsgs, const std::string &strFilename);
     /// \return all node definitions that have been loaded by this CControlParser.
     const vector<CControlBase::NodeTemplate*> &parsedNodes();
     ///Subclasses may override to parse other nodes (besides "node", "ref" and "alph").
diff --git a/Src/DasherCore/ConversionManager.cpp b/Src/DasherCore/ConversionManager.cpp
index b3b7f37..4e66155 100644
--- a/Src/DasherCore/ConversionManager.cpp
+++ b/Src/DasherCore/ConversionManager.cpp
@@ -126,14 +126,14 @@ CConversionManager::CConvNode::~CConvNode() {
   m_pMgr->Unref();
 }
 
-void CConversionManager::RecursiveDumpTree(SCENode *pCurrent, unsigned int iDepth) {
+void CConversionManager::RecursiveDumpTree(ostream &out, SCENode *pCurrent, unsigned int iDepth) {
   const std::vector<SCENode *> &children = pCurrent->GetChildren();
   for (std::vector<SCENode *>::const_iterator it = children.begin(); it!=children.end(); it++) {
     SCENode *pCurrent(*it);
     for(unsigned int i(0); i < iDepth; ++i)
-      std::cout << "-";
-    std::cout << " " << pCurrent->pszConversion << std::endl;//" " << pCurrent->IsHeadAndCandNum << " " << pCurrent->CandIndex << " " << pCurrent->IsComplete << " " << pCurrent->AcCharCount << std::endl;
-    RecursiveDumpTree(pCurrent, iDepth + 1);
+      out << "-";
+    out << " " << pCurrent->pszConversion << std::endl;//" " << pCurrent->IsHeadAndCandNum << " " << pCurrent->CandIndex << " " << pCurrent->IsComplete << " " << pCurrent->AcCharCount << std::endl;
+    RecursiveDumpTree(out, pCurrent, iDepth + 1);
   }
 }
 
diff --git a/Src/DasherCore/ConversionManager.h b/Src/DasherCore/ConversionManager.h
index 7964765..0d910ec 100644
--- a/Src/DasherCore/ConversionManager.h
+++ b/Src/DasherCore/ConversionManager.h
@@ -152,10 +152,10 @@ namespace Dasher {
     CDasherScreen *m_pScreen;
 
     ///
-    /// Dump tree to stdout (debug)
+    /// Dump tree to provided stream (debug)
     ///
 
-    void RecursiveDumpTree(SCENode *pCurrent, unsigned int iDepth);
+    void RecursiveDumpTree(std::ostream &out, SCENode *pCurrent, unsigned int iDepth);
 
 	///
 	/// Reference count
diff --git a/Src/DasherCore/DashIntfScreenMsgs.cpp b/Src/DasherCore/DashIntfScreenMsgs.cpp
index 1f05f43..ae7fa84 100644
--- a/Src/DasherCore/DashIntfScreenMsgs.cpp
+++ b/Src/DasherCore/DashIntfScreenMsgs.cpp
@@ -2,34 +2,63 @@
 
 using namespace Dasher;
 
-void CDashIntfScreenMsgs::Message(const string &strText) {
-  m_dqMessages.push_back(pair<CDasherScreen::Label*,unsigned long>(m_DasherScreen->MakeLabel(strText, GetLongParameter(LP_MESSAGE_FONTSIZE)), 0));
+void CDashIntfScreenMsgs::Message(const string &strText, bool bInterrupt) {
+  //Just store the messages for Redraw...
+  CDasherScreen::Label *lab = m_DasherScreen->MakeLabel(strText,GetLongParameter(LP_MESSAGE_FONTSIZE));
+  if (bInterrupt) {
+    m_dqModalMessages.push_back(pair<CDasherScreen::Label*,bool>(lab,false));
+    SetBoolParameter(BP_DASHER_PAUSED, true);
+  }
+  else
+    m_dqAsyncMessages.push_back(pair<CDasherScreen::Label*,unsigned long>(lab, 0));
 }
 
 bool CDashIntfScreenMsgs::FinishRender(unsigned long ulTime) {
   bool bMsgsChanged=false;
-  //Finally any messages. Newest that will fit at bottom, proceeding upwards
-  while (!m_dqMessages.empty() && m_dqMessages.front().second && ulTime-m_dqMessages.front().second>GetLongParameter(LP_MESSAGE_TIME)) {
-    //message has been displayed for long enough.
-    m_dqMessages.pop_front(); // => stop displaying it
+  //Finally any messages - newest that will fit at bottom, proceeding upwards.
+  // Firstly clear any non-modal messages that have been onscreen for long enough
+  while (!m_dqAsyncMessages.empty() && m_dqAsyncMessages.front().second && ulTime-m_dqAsyncMessages.front().second>GetLongParameter(LP_MESSAGE_TIME)) {
+    delete m_dqAsyncMessages.front().first; //the Label
+    m_dqAsyncMessages.pop_front(); // => stop displaying it
     bMsgsChanged=true;
   }
-  if (!m_dqMessages.empty()) {
-    //still messages to display...first find out longest-ago N that will fit
-    const unsigned int iFontSize(GetLongParameter(LP_MESSAGE_FONTSIZE));
+  if (!m_dqAsyncMessages.empty() || !m_dqModalMessages.empty()) {
     screenint iY = m_DasherScreen->GetHeight();
     const screenint iMinY((iY*3)/4), iSW(m_DasherScreen->GetWidth());
-    for (deque<pair<CDasherScreen::Label*, unsigned long> >::iterator it = m_dqMessages.begin(); it!=m_dqMessages.end() && iY>iMinY; it++) {
-      if (it->second==0) {it->second = ulTime; bMsgsChanged=true;} //display message for first time
-      iY-=m_DasherScreen->TextSize(it->first, iFontSize).second;
+    //still messages to display...first find out longest-ago N that will fit
+    for (deque<pair<CDasherScreen::Label*, unsigned long> >::iterator it = m_dqAsyncMessages.begin(); it!=m_dqAsyncMessages.end() && iY>iMinY; it++) {
+      if (it->second==0) {
+        //reached a not-yet-displayed asynchronous message
+        if (!m_dqModalMessages.empty()) break; //don't start displaying anything while there are modal msgs
+        it->second = ulTime; //display message for first time
+        bMsgsChanged=true;
+      } 
+      iY-=m_DasherScreen->TextSize(it->first, GetLongParameter(LP_MESSAGE_FONTSIZE)).second;
+    }
+    if (!m_dqModalMessages.empty()) {
+      bool bDisp(m_dqModalMessages.front().second != 0); //displaying anything atm?
+      for (deque<pair<CDasherScreen::Label*,unsigned long> >::iterator it=m_dqModalMessages.begin(); it!=m_dqModalMessages.end() && iY>iMinY; it++) {
+        if (bDisp) {
+          if (it->second==0) break; //don't start displaying more until previous dismissed
+        } else {
+          DASHER_ASSERT(it->second==0);
+          it->second = ulTime;
+          bMsgsChanged = true;
+        }
+        iY-=m_DasherScreen->TextSize(it->first, GetLongParameter(LP_MESSAGE_FONTSIZE)).second;
+      }
     }
-    //then render oldest first proceeding downwards
-    for (deque<pair<CDasherScreen::Label*, unsigned long> >::iterator it = m_dqMessages.begin(); it!=m_dqMessages.end() && it->second!=0; it++) {
-      pair<screenint,screenint> textDims = m_DasherScreen->TextSize(it->first, iFontSize);
+    //Now render messages proceeding downwards - non-modal first, then oldest first
+    bool bModal(false);
+    for (deque<pair<CDasherScreen::Label*, unsigned long> >::iterator it = m_dqAsyncMessages.begin(); ; it++) {
+      if (it==m_dqAsyncMessages.end()) {it=m_dqModalMessages.begin(); bModal=true;}
+      if (it==m_dqModalMessages.end()) break;
+      if (it->second==0) continue;
+      pair<screenint,screenint> textDims = m_DasherScreen->TextSize(it->first, GetLongParameter(LP_MESSAGE_FONTSIZE));
       //black (5) rectangle:
       m_DasherScreen->DrawRectangle((iSW - textDims.first)/2, iY, (iSW+textDims.first)/2, iY+textDims.second, 5, -1, -1);
-      //white (0) text:
-      m_DasherScreen->DrawString(it->first, (iSW-textDims.first)/2, iY, iFontSize, 0);
+      //white (0) text for non-modal, yellow (111) for modal
+      m_DasherScreen->DrawString(it->first, (iSW-textDims.first)/2, iY, GetLongParameter(LP_MESSAGE_FONTSIZE), bModal ? 111 : 0);
       iY+=textDims.second;
     }
   }
@@ -38,9 +67,25 @@ bool CDashIntfScreenMsgs::FinishRender(unsigned long ulTime) {
 
 void CDashIntfScreenMsgs::ChangeScreen(CDasherScreen *pNewScreen) {
   CDasherInterfaceBase::ChangeScreen(pNewScreen);
-  for (deque<pair<CDasherScreen::Label*,unsigned long> >::iterator it=m_dqMessages.begin(); it!=m_dqMessages.end(); it++) {
+  for (deque<pair<CDasherScreen::Label*,unsigned long> >::iterator it=m_dqAsyncMessages.begin(); ; it++) {
+    if (it==m_dqAsyncMessages.end()) it = m_dqModalMessages.begin();
+    if (it==m_dqModalMessages.end()) break;
     const CDasherScreen::Label *pOldLabel(it->first);
     it->first = pNewScreen->MakeLabel(pOldLabel->m_strText, pOldLabel->m_iWrapSize);
     delete pOldLabel;
   }
 }
+
+void CDashIntfScreenMsgs::Unpause(unsigned long lTime) {
+  if (!GetBoolParameter(BP_DASHER_PAUSED)) return;
+  while (!m_dqModalMessages.empty()) {
+    if (m_dqModalMessages.front().second) {
+      //Message has been displayed; delete it
+      delete m_dqModalMessages.front().first; //the label
+      m_dqModalMessages.pop_front();
+    } else return; //there are more, not-yet displayed, modal messages!
+      //These should be after any that were displayed (which have now been erased), so:
+      // do not unpause; next frame will render more messages instead.
+  }
+  CDasherInterfaceBase::Unpause(lTime);
+}
diff --git a/Src/DasherCore/DashIntfScreenMsgs.h b/Src/DasherCore/DashIntfScreenMsgs.h
index 48bde43..6881a39 100644
--- a/Src/DasherCore/DashIntfScreenMsgs.h
+++ b/Src/DasherCore/DashIntfScreenMsgs.h
@@ -27,8 +27,16 @@
 namespace Dasher {
 class CDashIntfScreenMsgs : public CDasherInterfaceBase {
 public:
-  /// Stores the message for Redraw to render onto the Screen on top of the view
-  virtual void Message(const std::string &strText);
+  /// Stores messages for Redraw to render onto the Screen on top of the view.
+  /// For modal messages (bInterrupt=true), pauses Dasher, and keeps the message
+  /// onscreen until the user starts moving again (via normal mechanisms);
+  /// For non-modal or asynchronous messages (bInterrupt=false), we render
+  /// the message over the canvas for LP_MESSAGE_TIME milliseconds without pausing.
+  /// (This method merely stores the messages into m_dqAsyncMessages or m_dqModalMessages
+  /// as appropriate; display, timeout, etc. is handled in Redraw.)
+  /// \param strText text of message to display.
+  /// \param bInterrupt whether to interrupt any text entry in progress.
+  virtual void Message(const std::string &strText, bool bInterrupt);
   
   /// Override to render (on top of nodes+decorations) any messages, for
   /// LP_MESSAGE_TIME ms, before removing from queue.
@@ -36,13 +44,19 @@ public:
 
   ///Override to re-MakeLabel any messages.
   void ChangeScreen(CDasherScreen *pNewScreen);
-
+  
+  ///Override to clear any modal messages currently being displayed before resuming.
+  void Unpause(unsigned long lTime);
 private:
-  ///Messages to be displayed to the user, longest-ago at the front,
-  /// along with the timestamp of the frame at which each was first displayed
-  /// to the user, or 0 if not yet displayed.
-  std::deque<pair<CDasherScreen::Label*, unsigned long> > m_dqMessages;
+  /// Asynchronous (non-modal) messages to be displayed to the user, longest-ago
+  /// at the front, along with the timestamp of the frame at which each was first
+  /// displayed to the user - 0 if not yet displayed.
+  std::deque<pair<CDasherScreen::Label*, unsigned long> > m_dqAsyncMessages;
   
+  /// Modal messages being or waiting to be displayed to the user, longest-ago
+  /// at the front, along with the timestamp when each was first displayed to the
+  /// user (0 if not yet displayed).
+  std::deque<pair<CDasherScreen::Label*, unsigned long> > m_dqModalMessages;  
 };
 
 }
diff --git a/Src/DasherCore/DasherInterfaceBase.cpp b/Src/DasherCore/DasherInterfaceBase.cpp
index 557abc5..f8de7ca 100644
--- a/Src/DasherCore/DasherInterfaceBase.cpp
+++ b/Src/DasherCore/DasherInterfaceBase.cpp
@@ -137,11 +137,11 @@ void CDasherInterfaceBase::Realize() {
 
   std::vector<std::string> vAlphabetFiles;
   ScanAlphabetFiles(vAlphabetFiles);
-  m_AlphIO = new CAlphIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vAlphabetFiles);
+  m_AlphIO = new CAlphIO(this, GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vAlphabetFiles);
 
   std::vector<std::string> vColourFiles;
   ScanColourFiles(vColourFiles);
-  m_ColourIO = new CColourIO(GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vColourFiles);
+  m_ColourIO = new CColourIO(this, GetStringParameter(SP_SYSTEM_LOC), GetStringParameter(SP_USER_LOC), vColourFiles);
 
   ChangeColours();
 
@@ -483,6 +483,7 @@ void CDasherInterfaceBase::GameMessageIn(int message, void* messagedata) {
 
 void CDasherInterfaceBase::Unpause(unsigned long Time) {
   if (!GetBoolParameter(BP_DASHER_PAUSED)) return; //already running, no need to do anything
+  
   SetBoolParameter(BP_DASHER_PAUSED, false);
 
   if(m_pDasherModel != 0)
@@ -618,6 +619,7 @@ bool CDasherInterfaceBase::Redraw(unsigned long ulTime, bool bRedrawNodes, CExpa
 #endif
   
   return bRedrawNodes;
+
 }
 
 void CDasherInterfaceBase::ChangeAlphabet() {
diff --git a/Src/DasherCore/DasherInterfaceBase.h b/Src/DasherCore/DasherInterfaceBase.h
index e9d7f03..9fb3df1 100644
--- a/Src/DasherCore/DasherInterfaceBase.h
+++ b/Src/DasherCore/DasherInterfaceBase.h
@@ -270,7 +270,7 @@ public:
   /// Unpause Dasher. Clears BP_DASHER_PAUSED.
   /// (But does nothing if BP_DASHER_PAUSED is currently set).
   /// \param Time Time in ms, used to keep a constant frame rate
-  void Unpause(unsigned long Time);
+  virtual 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,
diff --git a/Src/DasherCore/MandarinAlphMgr.cpp b/Src/DasherCore/MandarinAlphMgr.cpp
index e053bbf..ade9e6a 100644
--- a/Src/DasherCore/MandarinAlphMgr.cpp
+++ b/Src/DasherCore/MandarinAlphMgr.cpp
@@ -128,7 +128,7 @@ CMandarinAlphMgr::~CMandarinAlphMgr() {
 
 void CMandarinAlphMgr::CreateLanguageModel(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore) {
   //std::cout<<"CHALphabet size "<< pCHAlphabet->GetNumberTextSymbols(); [7603]
-  std::cout<<"Setting PPMPY model"<<std::endl;
+  //std::cout<<"Setting PPMPY model"<<std::endl;
   m_pLanguageModel = new CPPMPYLanguageModel(pEventHandler, pSettingsStore, m_CHtext.size()-1, m_pAlphabet->GetNumberTextSymbols());
 }
 
@@ -136,7 +136,7 @@ CTrainer *CMandarinAlphMgr::GetTrainer() {
   //We pass in the pinyin alphabet to define the context-switch escape character, and the default context.
   // Although the default context will be symbolified via the _chinese_ alphabet, this seems reasonable
   // as it is the Pinyin alphabet which defines the conversion mapping (i.e. m_strConversionTarget!)
-  return new CMandarinTrainer(static_cast<CPPMPYLanguageModel*>(m_pLanguageModel), m_pAlphabet, m_pAlphabetMap, &m_CHAlphabetMap, m_pAlphabet->m_strConversionTrainingDelimiter);
+  return new CMandarinTrainer(m_pInterface, static_cast<CPPMPYLanguageModel*>(m_pLanguageModel), m_pAlphabet, m_pAlphabetMap, &m_CHAlphabetMap, m_pAlphabet->m_strConversionTrainingDelimiter);
 }
 
 CAlphabetManager::CAlphNode *CMandarinAlphMgr::GetRoot(CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, bool bEnteredLast, int iOffset) {
diff --git a/Src/DasherCore/Messages.h b/Src/DasherCore/Messages.h
index bef3b69..035fd9a 100644
--- a/Src/DasherCore/Messages.h
+++ b/Src/DasherCore/Messages.h
@@ -32,10 +32,17 @@
 
 class CMessageDisplay {
 public:
-  ///Displays a message to the user - somehow. This should not interrupt the user:
-  /// if Dasher is in motion, it should continue just as if no message were displayed.
-  /// \param strText text of message to display. TODO, do we want an enum for seriousness?
-  virtual void Message(const std::string &strText)=0;
+  ///Displays a message to the user - somehow. Two styles
+  /// of message are supported: (1) modal messages, i.e. which interrupt text entry;
+  /// these should be explicitly dismissed (somehow) before text entry resumes; and
+  /// (2) non-modal or asynchronous messages, which should be displayed in the background
+  /// but allow the user to continue text entry as normal.
+  /// NOTE for subclasses: it is best not to popup any modal window here but rather to
+  /// store all messages until the next frame is rendered and then combine them into one.
+  /// \param strText text of message to display.
+  /// \param bInterrupt if true, text entry should be interrupted; if false, user should
+  /// be able to continue writing uninterrupted.
+  virtual void Message(const std::string &strText, bool bInterrupt)=0;
 };
 
 /// @}
diff --git a/Src/DasherCore/NodeCreationManager.cpp b/Src/DasherCore/NodeCreationManager.cpp
index d0b50f5..adf227e 100644
--- a/Src/DasherCore/NodeCreationManager.cpp
+++ b/Src/DasherCore/NodeCreationManager.cpp
@@ -6,6 +6,8 @@
 #include "ControlManager.h"
 #include "EventHandler.h"
 
+#include <string.h>
+
 using namespace Dasher;
 
 class ProgressNotifier : public CTrainer::ProgressIndicator {
@@ -18,11 +20,13 @@ public:
       m_pInterface->SetLockStatus(m_strDisplay, m_iPercent = iNewPercent);
     }
   }
-  void run(const string &strDisplay, string strFile) {
+  bool run(const string &strDisplay, string strFile) {
     m_pInterface->SetLockStatus(m_strDisplay=strDisplay, m_iPercent=0);
     m_iStart = 0;
     m_iStop = m_pInterface->GetFileSize(strFile);
-    m_pTrainer->LoadFile(strFile,this);
+    if (m_iStop==0) return false;
+    m_pTrainer->LoadFile(strFile,this); //Hmmm. Error-reporting is only via Message()...?
+    return true;
   }
 private:
   CDasherInterfaceBase *m_pInterface;
@@ -80,17 +84,28 @@ CNodeCreationManager::CNodeCreationManager(Dasher::CDasherInterfaceBase *pInterf
     
   if (!pAlphInfo->GetTrainingFile().empty()) {
     ProgressNotifier pn(pInterface, m_pTrainer);
-
     //1. Look for system training text...
-    pn.run("Training on System Text", GetStringParameter(SP_SYSTEM_LOC) + pAlphInfo->GetTrainingFile());
+    bool bFound=pn.run(_("Training on System Text"), GetStringParameter(SP_SYSTEM_LOC) + pAlphInfo->GetTrainingFile());
     //2. Now add in any user-provided individual training text...
-    pn.run("Training on User Text", GetStringParameter(SP_USER_LOC) + pAlphInfo->GetTrainingFile());
+    if (!pn.run(_("Training on User Text"), GetStringParameter(SP_USER_LOC) + pAlphInfo->GetTrainingFile())) {
+      ///TRANSLATORS: These 3 messages will be displayed when the user has just chosen a new alphabet. The %s parameter will be the name of the alphabet.
+      const char *msg = bFound ? _("No user training text found - if you have written in \"%s\" before, this means Dasher may not be learning from previous sessions")
+      : _("No training text (user or system) found for \"%s\". Dasher will still work but entry will be slower. We suggest downloading a training text file from the Dasher website, or constructing your own.");
+      char *buf(new char[strlen(msg)+pAlphInfo->GetID().length()]);
+      sprintf(buf,msg,pAlphInfo->GetID().c_str());
+      pInterface->Message(buf, true);
+      delete buf;
+    }
     //3. Finished, so unlock.
     m_pInterface->SetLockStatus("", -1);
   }
 #ifdef DEBUG
   else {
-    std::cout << "Alphabet does not specify training file" << std::endl;
+    const char *msg = _("\"%s\" does not specify training file. Dasher will work but entry will be slower. Check you have the latest version of the alphabet definition.");
+    char *buf(new char[strlen(msg) + pAlphInfo->GetID().length()]);
+    sprintf(buf, msg, pAlphInfo->GetID().c_str());
+    pInterface->Message(buf, true);
+    delete buf;
   }
 #endif
 #ifdef DEBUG_LM_READWRITE
diff --git a/Src/DasherCore/SocketInput.cpp b/Src/DasherCore/SocketInput.cpp
index db20dde..3ecee0a 100644
--- a/Src/DasherCore/SocketInput.cpp
+++ b/Src/DasherCore/SocketInput.cpp
@@ -16,8 +16,8 @@
 
 using namespace Dasher;
 
-CSocketInput::CSocketInput(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore)
-:CSocketInputBase(pEventHandler, pSettingsStore) {
+CSocketInput::CSocketInput(CMessageDisplay *pMsgs, CEventHandler *pEventHandler, CSettingsStore *pSettingsStore)
+:CSocketInputBase(pMsgs, pEventHandler, pSettingsStore) {
 }
 
 CSocketInput::~CSocketInput() {
diff --git a/Src/DasherCore/SocketInput.h b/Src/DasherCore/SocketInput.h
index f6c2a7a..9adf5b0 100644
--- a/Src/DasherCore/SocketInput.h
+++ b/Src/DasherCore/SocketInput.h
@@ -45,7 +45,7 @@ class CSocketInput:public CSocketInputBase {
 
 public:
 
-  CSocketInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
+  CSocketInput(CMessageDisplay *pMsgs, CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
   ~CSocketInput();
 
 private:
diff --git a/Src/DasherCore/SocketInputBase.cpp b/Src/DasherCore/SocketInputBase.cpp
index e54f619..d4ff09c 100644
--- a/Src/DasherCore/SocketInputBase.cpp
+++ b/Src/DasherCore/SocketInputBase.cpp
@@ -6,7 +6,7 @@
 
 #include "SocketInputBase.h"
 
-#include "../DasherCore/Event.h"
+#include "DasherInterfaceBase.h"
 
 #include <string.h>
 #include <errno.h>
@@ -34,8 +34,8 @@ static SModuleSettings sSettings[] = {
   {BP_SOCKET_DEBUG, T_BOOL, -1, -1, -1, -1, _("Print socket-related debugging information to console:")}
 };
 
-Dasher::CSocketInputBase::CSocketInputBase(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore) 
-  : CScreenCoordInput(pEventHandler, pSettingsStore, 1, _("Socket Input")) {
+Dasher::CSocketInputBase::CSocketInputBase(CMessageDisplay *pMsgs, CEventHandler * pEventHandler, CSettingsStore * pSettingsStore) 
+  : CScreenCoordInput(pEventHandler, pSettingsStore, 1, _("Socket Input")), m_pMsgs(pMsgs) {
   port = -1;
   debug_socket_input = false;
   readerRunning = false;
@@ -107,8 +107,8 @@ bool Dasher::CSocketInputBase::StartListening() {
   SocketDebugMsg("Socket input: binding to socket and starting to listen.");
 
   if((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
-    ReportErrnoError("Error creating socket"); //FIXME should use _( (gettext), but need something cross-platform.
-    // Could have a member functino, overridden in the platform-specific subclasses, which returns the localised text
+    //TODO This is not a very good error message even in English...???
+    m_pMsgs->Message(_("Error creating socket"),true);
     return false;
   }
 
@@ -116,7 +116,7 @@ bool Dasher::CSocketInputBase::StartListening() {
   name.sin_port = htons(port);
   name.sin_addr.s_addr = htonl(INADDR_ANY);
   if(bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) {
-    ReportErrnoError("Error binding to socket - already in use?");
+    ReportErrnoError(_("Error binding to socket - already in use?"));
     DASHER_SOCKET_CLOSE_FUNCTION(sock);
     sock = -1;
     return false;
@@ -169,7 +169,11 @@ void CSocketInputBase::SetReaderPort(int _port) {
 void CSocketInputBase::SetCoordinateLabel( int iWhichCoordinate, const char *Label) {
   DASHER_ASSERT(iWhichCoordinate < DASHER_SOCKET_INPUT_MAX_COORDINATE_COUNT);
   if(strlen(Label) > DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH) {
-    cerr << "Warning truncating socket input label '" << Label << "' to " << DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH << " characters." << endl;
+    const char *msg=_("Warning truncating socket input label '%s' to %i characters.");
+    char *buf(new char[strlen(msg)+strlen(Label)+DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH]);
+    sprintf(buf,msg,Label,DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH);
+    m_pMsgs->Message(buf, true);
+    delete buf;
   }
   strncpy(coordinateNames[iWhichCoordinate], Label, DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH);
   SocketDebugMsg("Socket input: set coordinate %d label to '%s'.", iWhichCoordinate,  coordinateNames[iWhichCoordinate]);
@@ -191,7 +195,7 @@ void CSocketInputBase::ReadForever() {
   while(sock >= 0) {
     SocketDebugMsg("Reading from socket...");
     if((numbytes = recv(sock, buffer, sizeof(buffer) - 1, 0)) == -1) {
-      ReportError("Socket input: Error reading from socket");
+      m_pMsgs->Message(_("Socket input: Error reading from socket"),false);
       continue;
     }
     buffer[numbytes] = '\0';
@@ -239,10 +243,12 @@ void CSocketInputBase::ParseMessage(char *message) {
           double actualMax = (rawMaxValues[i] > rawMinValues[i]) ? rawMaxValues[i] : rawMinValues[i];
           double actualMin = (rawMaxValues[i] > rawMinValues[i]) ? rawMinValues[i] : rawMaxValues[i];
           if(rawdouble < actualMin) {
+            //TODO: Should these be converted to calls to Message() ? On first occurrence only???
             cerr << "Socket input: clipped " << coordinateNames[i] << " value of " << rawdouble << "to configured minimum of " << actualMin << endl;
             rawdouble = actualMin;
           }
           if(rawdouble > actualMax) {
+            //TODO: Should these be converted to calls to Message() ? On first occurrence only???
             cerr << "Socket input: clipped " << message << " value of " << rawdouble << "to configured maximum of " << actualMax << endl;
             rawdouble = actualMax;
           }
@@ -289,15 +295,14 @@ void CSocketInputBase::SetDebug(bool _debug) {
   }
 }
 
-void CSocketInputBase::ReportError(std::string s) {
-  // override this to pop up a message box etc.
-  cerr << s << endl;
-}
-
-void CSocketInputBase::ReportErrnoError(std::string prefix) {
-  // override this to pop up a message box
-  cerr << "Dasher Socket Input error: ";
-  perror(prefix.c_str());
+void CSocketInputBase::ReportErrnoError(const std::string &prefix) {
+  int err = errno; errno=0;
+  const char *msg = _("Dasher Socket Input error: %s: %s");
+  char *e = strerror(err);
+  char *buf(new char[strlen(msg) + prefix.length() + strlen(e)]);
+  sprintf(buf,msg,prefix.c_str(),e);
+  m_pMsgs->Message(buf,true);
+  delete buf;
 }
 
 void CSocketInputBase::SocketDebugMsg(const char *pszFormat, ...) {
diff --git a/Src/DasherCore/SocketInputBase.h b/Src/DasherCore/SocketInputBase.h
index 80a023a..2da884f 100644
--- a/Src/DasherCore/SocketInputBase.h
+++ b/Src/DasherCore/SocketInputBase.h
@@ -10,6 +10,7 @@
 #include "../DasherCore/DasherInput.h"
 #include "../DasherCore/DasherComponent.h"
 #include "../DasherCore/EventHandler.h"
+#include "Messages.h"
 
 #include <iostream>
 
@@ -26,7 +27,7 @@ class CSocketInputBase : public CScreenCoordInput {
 
 public:
 
-  CSocketInputBase(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
+  CSocketInputBase(CMessageDisplay *pMsgs, CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
 
   virtual ~CSocketInputBase();
 
@@ -120,12 +121,12 @@ protected:
 
   virtual void ParseMessage(char *message);
 
-  virtual void ReportErrnoError(std::string prefix); // override as appropriate for each platform
-
-  virtual void ReportError(std::string s); // override as appropriate for each platform
+  //Reports an error by appending an error message obtained from strerror(errno) onto the provided prefix
+  void ReportErrnoError(const std::string &prefix);
     
   virtual void SocketDebugMsg(const char *pszFormat, ...);
   
+  CMessageDisplay *const m_pMsgs;
 };
 }
 /// \}
diff --git a/Src/DasherCore/Trainer.cpp b/Src/DasherCore/Trainer.cpp
index c8c267d..1815c21 100644
--- a/Src/DasherCore/Trainer.cpp
+++ b/Src/DasherCore/Trainer.cpp
@@ -2,12 +2,14 @@
 #include "../Common/Common.h"
 
 #include "Trainer.h"
-#include "DasherInterfaceBase.h"
 #include "LanguageModelling/PPMPYLanguageModel.h"
+#include <vector>
 #include <cstring>
 #include <sstream>
+#include <string>
 
 using namespace Dasher;
+using namespace std;
 
 // Track memory leaks on Windows to the line that new'd the memory
 #ifdef _WIN32
@@ -19,16 +21,19 @@ static char THIS_FILE[] = __FILE__;
 #endif
 #endif
 
-CTrainer::CTrainer(CLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pAlphabet)
-  : m_pAlphabet(pAlphabet), m_pLanguageModel(pLanguageModel), m_pInfo(pInfo) {
+CTrainer::CTrainer(CMessageDisplay *pMsgs, CLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pAlphabet)
+  : m_pMsgs(pMsgs), m_pAlphabet(pAlphabet), m_pLanguageModel(pLanguageModel), m_pInfo(pInfo) {
     vector<symbol> syms;
     pAlphabet->GetSymbols(syms,pInfo->GetContextEscapeChar());
     if (syms.size()==1)
       m_iCtxEsc = syms[0];
     else {      
-#ifdef DEBUG
-      std::cout << "Warning: escape sequence " << pInfo->GetContextEscapeChar() << " must be a single unicode character; no context-switch commands will be executed." << std::endl;
-#endif
+      //no context switch commands will be executed!
+      const char *msg(_("Warning: faulty alphabet definition, escape sequence %s must be a single unicode character. This may worsen Dasher's text prediction."));
+      char *buf(new char[strlen(msg) + pInfo->GetContextEscapeChar().length() +1]);
+      sprintf(buf,msg,pInfo->GetContextEscapeChar().c_str());
+      pMsgs->Message(string(buf),true);
+      delete buf;
       m_iCtxEsc = -1;
     }
 }
@@ -80,7 +85,7 @@ bool CTrainer::readEscape(CLanguageModel::Context &sContext, CAlphabetMap::Symbo
 
 class ProgressStream : public CAlphabetMap::SymbolStream {
 public:
-  ProgressStream(std::istream &_in, CTrainer::ProgressIndicator *pProg, off_t iStart=0) : SymbolStream(_in), m_iLastPos(iStart), m_pProg(pProg) {
+  ProgressStream(std::istream &_in, CTrainer::ProgressIndicator *pProg, CMessageDisplay *pMsgs, off_t iStart=0) : SymbolStream(_in,pMsgs), m_iLastPos(iStart), m_pProg(pProg) {
   }
   void bytesRead(off_t num) {
     if (m_pProg) m_pProg->bytesRead(m_iLastPos += num);
@@ -110,14 +115,18 @@ Dasher::CTrainer::LoadFile(const std::string &strFileName, ProgressIndicator *pP
     //Invoke AbstractXMLParser method
     m_bInSegment = false;
     m_iLastBytes=0;
-    ParseFile(strFileName);
+    ParseFile(m_pMsgs, strFileName);
   } else {
     std::ifstream in(strFileName.c_str(), std::ios::binary);
     if (in.fail()) {
-      std::cerr << "Unable to open file \"" << strFileName << "\" for reading" << std::endl;
+      const char *msg=_("Unable to open file \"%f\" for reading");
+      char *buf(new char[strlen(msg) + strFileName.length()+1]);
+      sprintf(buf, msg, strFileName.c_str());
+      m_pMsgs->Message(buf, true);
+      delete buf;
       return;
     }
-    ProgressStream syms(in,pProg);
+    ProgressStream syms(in,pProg,m_pMsgs);
     Train(syms);
   
     in.close();
@@ -134,7 +143,7 @@ void CTrainer::XmlStartHandler(const XML_Char *szName, const XML_Char **pAtts) {
 void CTrainer::XmlEndHandler(const XML_Char *szName) {
   if(!strcmp(szName, "segment")) {
     std::istringstream in(m_strCurrentText);
-    ProgressStream syms(in, m_pProg, m_iLastBytes);
+    ProgressStream syms(in, m_pProg, m_pMsgs, m_iLastBytes);
     Train(syms);
     m_iLastBytes = syms.m_iLastPos; //count that segment, ready for next
     m_bInSegment = false;
@@ -146,8 +155,8 @@ void CTrainer::XmlCData(const XML_Char *szS, int iLen) {
     m_strCurrentText += std::string(szS, iLen);
 }
 
-CMandarinTrainer::CMandarinTrainer(CPPMPYLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pPYAlphabet, const CAlphabetMap *pCHAlphabet, const std::string &strDelim)
-: CTrainer(pLanguageModel, pInfo, pCHAlphabet), m_pPYAlphabet(pPYAlphabet), m_strDelim(strDelim) {
+CMandarinTrainer::CMandarinTrainer(CMessageDisplay *pMsgs, CPPMPYLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pPYAlphabet, const CAlphabetMap *pCHAlphabet, const std::string &strDelim)
+: CTrainer(pMsgs, pLanguageModel, pInfo, pCHAlphabet), m_pPYAlphabet(pPYAlphabet), m_strDelim(strDelim) {
 }
 
 void CMandarinTrainer::Train(CAlphabetMap::SymbolStream &syms) {
@@ -160,16 +169,24 @@ void CMandarinTrainer::Train(CAlphabetMap::SymbolStream &syms) {
     if (s == m_strDelim) { //found delimiter, so process next two characters
       symbol Sympy = syms.next(m_pPYAlphabet);
       if (Sympy==-1) break; //EOF
-#ifdef DEBUG
-      if (Sympy==0)
-        std::cout << "Unknown pinyin character " << syms.peekBack() << std::endl;
-#endif
+      if (Sympy==0) {
+        const char *msg(_("Training file contains unknown source alphabet character %s"));
+        string prev = syms.peekBack();
+        char *buf(new char[strlen(msg) + prev.length()+1]);
+        sprintf(buf, msg, prev.c_str());
+        m_pMsgs->Message(buf,true);
+        delete buf;
+      }
       symbol Symchar = syms.next(m_pAlphabet);
       if (Symchar==-1) break; //EOF...ignore final Pinyin?
-#ifdef DEBUG
-      if (Symchar==0)
-        std::cout << "Unknown chinese character " << syms.peekBack() << std::endl;
-#endif
+      if (Symchar==0) {
+        const char *msg=_("Training file contains unknown target alphabet character %s");
+        string prev = syms.peekBack();
+        char *buf(new char[strlen(msg) + prev.length()+1]);
+        sprintf(buf,msg,prev.c_str());
+        m_pMsgs->Message(buf,true);
+        delete buf;
+      }
       static_cast<CPPMPYLanguageModel *>(m_pLanguageModel)->LearnPYSymbol(trainContext, Sympy);
       m_pLanguageModel->LearnSymbol(trainContext, Symchar);
       numberofchar++;    
diff --git a/Src/DasherCore/Trainer.h b/Src/DasherCore/Trainer.h
index ca201e7..340a207 100644
--- a/Src/DasherCore/Trainer.h
+++ b/Src/DasherCore/Trainer.h
@@ -6,12 +6,10 @@
 #include "AbstractXMLParser.h"
 
 namespace Dasher {
-  class CDasherInterfaceBase;
-	
   class CTrainer : private AbstractXMLParser {
             
   public:
-    CTrainer(CLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pAlphabet);
+    CTrainer(CMessageDisplay *pMsgs, CLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pAlphabet);
 
     class ProgressIndicator {
     public:
@@ -34,10 +32,11 @@ namespace Dasher {
     /// \return true if a context-switch command was found (=> sContext reinitialized);
     ///  false, if instead a double-escape-character (=encoding of that actual symbol) was read
     bool readEscape(CLanguageModel::Context &sContext, CAlphabetMap::SymbolStream &syms);
-    
-    const CAlphabetMap *m_pAlphabet;
-    CLanguageModel *m_pLanguageModel;
-    const CAlphInfo *m_pInfo;
+
+    CMessageDisplay * const m_pMsgs;
+    const CAlphabetMap * const m_pAlphabet;
+    CLanguageModel * const m_pLanguageModel;
+    const CAlphInfo * const m_pInfo;
     // symbol number in alphabet of the context-switch character (maybe 0 if not in alphabet!)
     int m_iCtxEsc;
   private:
@@ -63,7 +62,7 @@ namespace Dasher {
     /// \param pPYAlphabet mapping from text to symbol# in PY alphabet
     /// \param pCHAlphabet mapping from text to symbol# (rehashed by MandarinAlphMgr) in CHAlphabet
     /// \param strDelim delimiter character (1 unicode, maybe >1 octet; if not, will never be matched)
-    CMandarinTrainer(CPPMPYLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pPYAlphabet, const CAlphabetMap *pCHAlphabet, const std::string &strDelim);
+    CMandarinTrainer(CMessageDisplay *pMsgs, CPPMPYLanguageModel *pLanguageModel, const CAlphInfo *pInfo, const CAlphabetMap *pPYAlphabet, const CAlphabetMap *pCHAlphabet, const std::string &strDelim);
 
   protected:
     //override...
diff --git a/Src/Gtk2/DasherControl.cpp b/Src/Gtk2/DasherControl.cpp
index a226681..f58eb6d 100644
--- a/Src/Gtk2/DasherControl.cpp
+++ b/Src/Gtk2/DasherControl.cpp
@@ -95,7 +95,7 @@ void CDasherControl::CreateModules() {
   SetDefaultInputDevice(m_pMouseInput);
   m_p1DMouseInput =
     (CDasher1DMouseInput *)RegisterModule(new CDasher1DMouseInput(m_pEventHandler, m_pSettingsStore));
-  RegisterModule(new CSocketInput(m_pEventHandler, m_pSettingsStore));
+  RegisterModule(new CSocketInput(this, m_pEventHandler, m_pSettingsStore));
 
 #ifdef JOYSTICK
   RegisterModule(new CDasherJoystickInput(m_pEventHandler, m_pSettingsStore, this));
diff --git a/Src/Win32/Dasher.cpp b/Src/Win32/Dasher.cpp
index 2f8c72b..180b18a 100644
--- a/Src/Win32/Dasher.cpp
+++ b/Src/Win32/Dasher.cpp
@@ -57,7 +57,7 @@ void CDasher::CreateModules() {
   //create default set first.
   CDasherInterfaceBase::CreateModules();
 #ifndef _WIN32_WCE
-  RegisterModule(new CSocketInput(m_pEventHandler, m_pSettingsStore));
+  RegisterModule(new CSocketInput(this, m_pEventHandler, m_pSettingsStore));
   RegisterModule(new CBTSocketInput(m_pEventHandler, m_pSettingsStore));
 #endif
   RegisterModule(new CDasherMouseInput(m_pEventHandler, m_pSettingsStore, m_pCanvas->getwindow()));
diff --git a/Src/Win32/Sockets/SocketInput.cpp b/Src/Win32/Sockets/SocketInput.cpp
index 40a7f86..7ef733f 100644
--- a/Src/Win32/Sockets/SocketInput.cpp
+++ b/Src/Win32/Sockets/SocketInput.cpp
@@ -52,9 +52,9 @@ static char THIS_FILE[] = __FILE__;
 
 
 
-CSocketInput::CSocketInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore)
+CSocketInput::CSocketInput(CMessageDisplay *pMsgs, CEventHandler * pEventHandler, CSettingsStore * pSettingsStore)
 
-:CSocketInputBase(pEventHandler, pSettingsStore) {
+:CSocketInputBase(pMsgs, pEventHandler, pSettingsStore) {
 
 
 
diff --git a/Src/Win32/Sockets/SocketInput.h b/Src/Win32/Sockets/SocketInput.h
index 628028d..923bb6b 100644
--- a/Src/Win32/Sockets/SocketInput.h
+++ b/Src/Win32/Sockets/SocketInput.h
@@ -15,7 +15,7 @@ class Dasher::CSocketInput:public CSocketInputBase {
 
 public:
 
-  CSocketInput(CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
+  CSocketInput(CMessageDisplay *pMsgs, CEventHandler * pEventHandler, CSettingsStore * pSettingsStore);
   ~CSocketInput();
 
   void SetDebug(bool _debug);
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.h b/Src/iPhone/Classes/CDasherInterfaceBridge.h
index f16e4e4..ec04329 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.h
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.h
@@ -6,7 +6,7 @@
  *
  */
 
-#import "../DasherCore/DasherInterfaceBase.h"
+#import "../DasherCore/DashIntfScreenMsgs.h"
 #import "IPhoneInputs.h"
 #import "IPhoneFilters.h"
 #import "DefaultFilter.h"
@@ -21,8 +21,12 @@
 /// Class representing the Dasher UI component (ie the canvas and speed slider).
 /// Implements the necessary abstract methods by bridging them into Objective C
 /// and sending messages onto the DasherAppDelegate.
-
-class CDasherInterfaceBridge : public Dasher::CDasherInterfaceBase {
+///
+/// We inherit from CDashIntfScreenMsgs but use it for displaying modal messages
+/// only (over the canvas). TODO: we could possibly do better by displaying a modal
+/// View, however we would need some way for users not capable of touching the screen
+/// (who might be using Dasher via tilt control & start-on-circle) to clear the display!
+class CDasherInterfaceBridge : public Dasher::CDashIntfScreenMsgs {
   
 public:
   
@@ -59,7 +63,8 @@ public:
   void editDelete(const string &strText, CDasherNode *pNode);
   void editConvert(CDasherNode *pNode);
   void editProtect(CDasherNode *pNode);
-  void Message(const string &strText);
+  ///Override for asynchronous messages only...TODO?
+  void Message(const string &strText, bool bInterrupt);
 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 c927a05..b9af70a 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.mm
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
@@ -189,8 +189,11 @@ void CDasherInterfaceBridge::editProtect(CDasherNode *pSource) {
   CDasherInterfaceBase::editProtect(pSource);
 }
 
-void CDasherInterfaceBridge::Message(const string &strMessage) {
-  [dasherApp displayMessage:NSStringFromStdString(strMessage)];
+void CDasherInterfaceBridge::Message(const string &strMessage, bool bInterrupt) {
+  if (bInterrupt)
+    CDashIntfScreenMsgs::Message(strMessage,true);
+  else
+    [dasherApp displayMessage:NSStringFromStdString(strMessage)];
 }
 
 void CDasherInterfaceBridge::SetLockStatus(const string &strText, int iPercent) {



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