[dasher: 42/43] Merge in game mode branch



commit 1508c2dc21182aa808077f833ffad53cbae0ed62
Merge: 6384ff8 cd50a3a
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Jun 22 19:13:18 2011 +0100

    Merge in game mode branch
    
    Game mode (6384ff8951c25a765f2370f04dd7b3e557943313) inc. CDasherScreen::Label,
      replacing CLockEvent, CMessageEvent and CControlEvent (read from control.xml),
      rewriting events+settings, game mode inc. Gtk2+iPhone+Mac multicolor textboxes
    
    Main branch inc. chinese/groups and many Gtk+alphabet fixes (not conflicting)

 Data/GUI/Makefile.am                        |    1 -
 Data/alphabets/Makefile.am                  |    3 +
 Data/alphabets/alphabet.bopoDict.xml        | 4945 ++++++++++++++++++++++++++
 Data/alphabets/alphabet.chineseRuby.xml     |    8 +-
 Data/alphabets/alphabet.hiragana.xml        |    8 +-
 Data/alphabets/alphabet.hiragana2.xml       |    7 +-
 Data/alphabets/alphabet.korean.xml          |   44 +-
 Data/alphabets/alphabet.spyDict.xml         | 2586 +++++++-------
 Data/alphabets/alphabet.spyToneMarks.xml    | 4586 +++++++++++++++++++++++++
 Data/alphabets/alphabet.spyTones2.xml       | 4959 +++++++++++++++++++++++++++
 Data/alphabets/bopo.py                      |  108 +
 Data/alphabets/bpmf-pinyin.txt              |  418 +++
 Doc/user/Makefile.am                        |    2 +-
 Src/DasherCore/Alphabet/AlphIO.cpp          |   61 +-
 Src/DasherCore/Alphabet/AlphIO.h            |    1 -
 Src/DasherCore/Alphabet/AlphInfo.cpp        |    2 +-
 Src/DasherCore/Alphabet/AlphabetMap.cpp     |    4 +-
 Src/DasherCore/AlphabetManager.cpp          |    4 +-
 Src/DasherCore/DasherNode.cpp               |    8 +-
 Src/DasherCore/DasherNode.h                 |   50 +-
 Src/DasherCore/DasherView.cpp               |    6 +-
 Src/DasherCore/DasherViewSquare.cpp         |   39 +-
 Src/DasherCore/DasherViewSquare.h           |    4 +-
 Src/DasherCore/MandarinAlphMgr.cpp          |  138 +-
 Src/DasherCore/MandarinAlphMgr.h            |    5 +-
 Src/DasherCore/Parameters.cpp               |    1 +
 Src/DasherCore/Parameters.h                 |    2 +-
 Src/DasherCore/SocketInputBase.h            |   11 +-
 Src/DasherCore/TimeSpan.h                   |    2 +-
 Src/DasherCore/UserButton.h                 |    2 +-
 Src/MacOSX/Dasher.xcodeproj/project.pbxproj |   12 +
 Src/MacOSX/PreferencesController.mm         |   41 +-
 Src/Makefile.am                             |    2 +
 Src/Tools/ParseChinese/ParseChinese.cpp     |  313 --
 configure.ac                                |   46 +-
 po/POTFILES.in                              |    6 +-
 po/ru.po                                    |  291 +-
 po/sl.po                                    |  225 +-
 38 files changed, 16881 insertions(+), 2070 deletions(-)
---
diff --cc Src/DasherCore/Alphabet/AlphIO.cpp
index f26d321,4b0cdde..e92abf0
--- a/Src/DasherCore/Alphabet/AlphIO.cpp
+++ b/Src/DasherCore/Alphabet/AlphIO.cpp
@@@ -176,24 -385,61 +176,23 @@@ CAlphInfo *CAlphIO::CreateDefault() 
  // Below here handlers for the Expat XML input library
  ////////////////////////////////////////////////////////////////////////////////////
  
 -void CAlphIO::XML_StartElement(void *userData, const XML_Char *name, const XML_Char **atts) {
 -  CAlphIO *Me = (CAlphIO *) userData;
 +void CAlphIO::XmlStartHandler(const XML_Char *name, const XML_Char **atts) {
  
 -  Me->CData = "";
 +  CData = "";
  
    if(strcmp(name, "alphabet") == 0) {
 -    Me->InputInfo = new CAlphInfo();
 -    Me->InputInfo->Mutable = Me->LoadMutable;
 -    Me->ParagraphCharacter = NULL;
 -    Me->SpaceCharacter = NULL;
 -    Me->iGroupIdx = 0;
 +    InputInfo = new CAlphInfo();
 +    InputInfo->Mutable = LoadMutable;
 +    ParagraphCharacter = NULL;
 +    SpaceCharacter = NULL;
-     bFirstGroup = true;
 +    iGroupIdx = 0;
      while(*atts != 0) {
        if(strcmp(*atts, "name") == 0) {
 -        atts++;
 -        Me->InputInfo->AlphID = *atts;
 -        atts--;
 +        InputInfo->AlphID = *(atts+1);
        } else if (strcmp(*atts, "hidden") == 0) {
 -        Me->InputInfo->m_bHidden = (strcmp(*(atts+1), "yes")==0);
 +        InputInfo->m_bHidden = (strcmp(*(atts+1), "yes")==0);
        } else if (strcmp(*atts, "escape") == 0) {
 -        Me->InputInfo->m_strCtxChar = *(atts+1);
 +        InputInfo->m_strCtxChar = *(atts+1);
        }
        atts += 2;
      }
@@@ -255,17 -505,11 +254,11 @@@
    if(strcmp(name, "group") == 0) {
      SGroupInfo *pNewGroup(new SGroupInfo);
      pNewGroup->iNumChildNodes=0;
-     pNewGroup->iColour = (iGroupIdx % 3) + 110;
-     ++iGroupIdx;
+     pNewGroup->iColour = -1; //marker for "none specified"; if so, will compute later
 -    if (Me->m_vGroups.empty()) Me->InputInfo->iNumChildNodes++; else Me->m_vGroups.back()->iNumChildNodes++;
 +    if (m_vGroups.empty()) InputInfo->iNumChildNodes++; else m_vGroups.back()->iNumChildNodes++;
  
-     if(bFirstGroup) {
-       pNewGroup->bVisible = false;
-       bFirstGroup = false;
-     }
-     else {
-       pNewGroup->bVisible = true;
-     }
+     //by default, the first group in the alphabet is invisible
 -    pNewGroup->bVisible = (Me->InputInfo->m_pBaseGroup!=NULL);
++    pNewGroup->bVisible = (InputInfo->m_pBaseGroup!=NULL);
  
      while(*atts != 0) {
        if(strcmp(*atts, "name") == 0) {
@@@ -296,21 -534,29 +283,29 @@@
        atts += 2;
      }
  
 -    SGroupInfo *&prevSibling(Me->m_vGroups.empty() ? Me->InputInfo->m_pBaseGroup : Me->m_vGroups.back()->pChild);
++    SGroupInfo *&prevSibling(m_vGroups.empty() ? InputInfo->m_pBaseGroup : m_vGroups.back()->pChild);
+ 
+     if (pNewGroup->iColour==-1 && pNewGroup->bVisible) {
+       //no colour specified. Try to colour cycle, but make sure we choose
+       // a different colour from both its parent and any previous sibling
+       for (;;) {
 -        pNewGroup->iColour=(Me->iGroupIdx++ % 3) + 110;
 -        if (!Me->m_vGroups.empty() && Me->m_vGroups.back()->iColour == pNewGroup->iColour)
++        pNewGroup->iColour=(iGroupIdx++ % 3) + 110;
++        if (!m_vGroups.empty() && m_vGroups.back()->iColour == pNewGroup->iColour)
+           continue; //same colour as parent -> try again
+         if (prevSibling && prevSibling->iColour == pNewGroup->iColour)
+           continue; //same colour as previous sibling -> try again
+         break; //different from parent and previous sibling (if any!), so ok
+       }
+     }
+ 
 -    pNewGroup->iStart = Me->InputInfo->m_vCharacters.size()+1;
 +    pNewGroup->iStart = InputInfo->m_vCharacters.size()+1;
  
      pNewGroup->pChild = NULL;
  
-     if(m_vGroups.size() > 0) {
-       pNewGroup->pNext = m_vGroups.back()->pChild;
-       m_vGroups.back()->pChild = pNewGroup;
-     }
-     else {
-       pNewGroup->pNext = InputInfo->m_pBaseGroup;
-       InputInfo->m_pBaseGroup = pNewGroup;
-     }
- 
+     pNewGroup->pNext = prevSibling;
+     prevSibling = pNewGroup;
  
 -    Me->m_vGroups.push_back(pNewGroup);
 +    m_vGroups.push_back(pNewGroup);
  
      return;
    }
diff --cc Src/DasherCore/Alphabet/AlphIO.h
index 908ec0e,d501ffa..ce9f32d
--- a/Src/DasherCore/Alphabet/AlphIO.h
+++ b/Src/DasherCore/Alphabet/AlphIO.h
@@@ -76,12 -88,16 +76,11 @@@ private
    // Data gathered
    std::string CData;            // Text gathered from when an elemnt starts to when it ends
    CAlphInfo *InputInfo;
-   bool bFirstGroup;
    int iGroupIdx;
  
 -  // Callback functions. These involve the normal dodgy casting to a pointer
 -  // to an instance to get a C++ class to work with a plain C library.
 -  static void XML_StartElement(void *userData, const XML_Char * name, const XML_Char ** atts);
 -  static void XML_EndElement(void *userData, const XML_Char * name);
 -  static void XML_CharacterData(void *userData, const XML_Char * s, int len);
 +  void XmlStartHandler(const XML_Char * name, const XML_Char ** atts);
 +  void XmlEndHandler(const XML_Char * name);
 +  void XmlCData(const XML_Char * s, int len);
  };
  /// @}
  
diff --cc Src/DasherCore/Alphabet/AlphabetMap.cpp
index a873bd9,e3944ce..9c3e82c
--- a/Src/DasherCore/Alphabet/AlphabetMap.cpp
+++ b/Src/DasherCore/Alphabet/AlphabetMap.cpp
@@@ -123,14 -121,11 +123,14 @@@ inline int CAlphabetMap::SymbolStream::
        }
        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;    
+     ++pos;
    }
  }
  
diff --cc Src/DasherCore/AlphabetManager.cpp
index 71699d4,8422c23..9dc76d6
--- a/Src/DasherCore/AlphabetManager.cpp
+++ b/Src/DasherCore/AlphabetManager.cpp
@@@ -260,7 -175,8 +260,8 @@@ CAlphabetManager::CGroupNode::CGroupNod
  : CAlphNode(pParent, iOffset, iLbnd, iHbnd,
              pGroup ? (pGroup->bVisible ? pGroup->iColour : iBkgCol)
              : (iOffset&1) ? 7 : 137, //special case for root nodes
 -            pGroup ? strEnc+pGroup->strLabel : strEnc, pMgr), m_pGroup(pGroup) {
 +            pLabel, pMgr), m_pGroup(pGroup) {
+   if (m_pGroup && !m_pGroup->bVisible) SetFlag(NF_VISIBLE, false);
  }
  
  CAlphabetManager::CAlphNode *CAlphabetManager::GetRoot(CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, bool bEnteredLast, int iOffset) {
diff --cc Src/DasherCore/DasherNode.cpp
index 38abc19,c36a68c..b0bd452
--- a/Src/DasherCore/DasherNode.cpp
+++ b/Src/DasherCore/DasherNode.cpp
@@@ -42,8 -42,8 +42,8 @@@ static int iNumNodes = 0
  int Dasher::currentNumNodeObjects() {return iNumNodes;}
  
  //TODO this used to be inline - should we make it so again?
 -CDasherNode::CDasherNode(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, int iColour, const string &strDisplayText)
 -: m_pParent(pParent), m_iFlags(DEFAULT_FLAGS), onlyChildRendered(NULL), m_iLbnd(iLbnd), m_iHbnd(iHbnd), m_iOffset(iOffset), m_iColour(iColour), m_strDisplayText(strDisplayText) {
 +CDasherNode::CDasherNode(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, int iColour, CDasherScreen::Label *pLabel)
- : m_pParent(pParent), m_iLbnd(iLbnd), m_iHbnd(iHbnd), m_iOffset(iOffset), m_iColour(iColour), m_pLabel(pLabel) {
++: m_pParent(pParent), m_iFlags(DEFAULT_FLAGS), onlyChildRendered(NULL), m_iLbnd(iLbnd), m_iHbnd(iHbnd), m_iOffset(iOffset), m_iColour(iColour), m_pLabel(pLabel) {
    DASHER_ASSERT(iHbnd >= iLbnd);
  
    if (pParent) {
diff --cc Src/DasherCore/DasherNode.h
index d056f1d,3c2767c..e069606
--- a/Src/DasherCore/DasherNode.h
+++ b/Src/DasherCore/DasherNode.h
@@@ -38,13 -37,40 +38,37 @@@ namespace Dasher 
  #include <vector>
  
  // Node flag constants
+ /// NF_COMMITTED: The node is 'above' the root, i.e. all onscreen parts of it
+ /// are covered by the root; this is when we train the language model etc
  #define NF_COMMITTED 1
+ 
+ /// NF_SEEN - Node is under the crosshair and has (already) been output
  #define NF_SEEN 2
+ 
+ /// NF_CONVERTED - Node has been converted (eg Japanese mode)
  #define NF_CONVERTED 4
+ 
+ /// NF_GAME - Node is on the path in game mode
  #define NF_GAME 8
+ 
+ /// NF_ALLCHILDREN - Node has all children. TODO Since nodes only
+ /// ever have all their children, or none of them, can we not
+ /// just check it has children, and get rid of this flag?
  #define NF_ALLCHILDREN 16
+ 
+ /// NF_SUPER - Node covers entire visible area (and so is eligible
+ /// to be made the new root)
  #define NF_SUPER 32
  
 -/// NF_END_GAME - Node is the last one of the phrase in game mode
 -#define NF_END_GAME 64
 -
+ /// NF_VISIBLE - an invisible node is one which lets its parent's
+ /// colour show through, and has no outline drawn round it (it may
+ /// still have a label). Note that this flag is set (i.e. the node
+ /// is drawn and outlined) by default in the constructor.
 -#define NF_VISIBLE 128
++#define NF_VISIBLE 64
+ 
+ ///Flags to assign to a newly created node:
+ #define DEFAULT_FLAGS NF_VISIBLE
+ 
  /// \ingroup Model
  /// @{
  
@@@ -80,13 -106,15 +104,13 @@@ class Dasher::CDasherNode:private NoClo
  
    /// @brief Constructor
    ///
+   /// Note the flags of the new node are initialized to DEFAULT_FLAGS.
+   ///
 -  /// @param pParent Parent of the new node; automatically adds self to children
 -  /// @param iOffset index into text box of character being/last entered
 -  /// @param iLbnd Lower bound of node within parent
 -  /// @param iHbnd Upper bound of node within parent
 -  /// @param iColour colour to render node; for invisible nodes, should be same as parent.
 -  /// @param strDisplayText label to render upon node
 -  CDasherNode(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, int iColour, const std::string &strDisplayText);
 +  /// \param pParent Parent of the new node; automatically adds to end of parent's child list
 +  /// \param iOffset Index into text buffer of character to LHS of cursor _after_ this node is Output().
-   /// \param iLbnd Lower bound of node within parent
-   /// \param iHbnd Upper bound of node within parent
 +  /// \param iColour background colour of node (for transparent nodes, same colour as parent)
 +  /// \param pLabel label to render onto node, NULL if no label required.
 +  CDasherNode(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, int iColour, CDasherScreen::Label *pLabel);
  
    /// @brief Destructor
    ///
diff --cc Src/DasherCore/DasherView.cpp
index dd3daf4,e1eaa95..9f767aa
--- a/Src/DasherCore/DasherView.cpp
+++ b/Src/DasherCore/DasherView.cpp
@@@ -168,4 -174,22 +168,4 @@@ void CDasherView::DasherDrawCentredRect
    Dasher2Screen(iDasherX, iDasherY, iScreenX, iScreenY);
  
    Screen()->DrawRectangle(iScreenX - iSize, iScreenY - iSize, iScreenX + iSize, iScreenY + iSize, Color, -1, bDrawOutline ? 1 : 0);
- }
+ }
 -
 -void CDasherView::DrawText(const std::string & str, myint x, myint y, int Size, int iColor) {
 -  screenint X, Y;
 -  Dasher2Screen(x, y, X, Y);
 -
 -  Screen()->DrawString(str, X, Y, Size, iColor);
 -}
 -
 -
 -void CDasherView::SetDemoMode(bool bDemoMode)
 -{
 -  m_bDemoMode = bDemoMode;
 -}
 -
 -void CDasherView::SetGameMode(bool bGameMode)
 -{
 -  m_bGameMode = bGameMode;
 -}
diff --cc Src/DasherCore/DasherViewSquare.cpp
index 4902e6e,71f62c7..d407fac
--- a/Src/DasherCore/DasherViewSquare.cpp
+++ b/Src/DasherCore/DasherViewSquare.cpp
@@@ -756,13 -752,17 +755,13 @@@ beginning
      if (newy1<=iDasherMaxY && newy2 >= iDasherMinY) { //onscreen
        if (newy2-newy1 > GetLongParameter(LP_MIN_NODE_SIZE)) {
          //definitely big enough to render.
-         NewRender(pChild, newy1, newy2, pPrevText, policy, dMaxCost, myColor, pOutput);
+         NewRender(pChild, newy1, newy2, pPrevText, policy, dMaxCost, pOutput);
 -        if (newy2 > iDasherMaxY) {
 -          //remaining children offscreen.
 -          if (newy1 < iDasherMinY) pRender->onlyChildRendered = pChild; //...and previous were too!
 -          break; //skip remaining children...
 -        }
 -      } else {
 -        //did not recurse, or store
 -        if (!pChild->GetFlag(NF_GAME | NF_SEEN)) pChild->Delete_children();
 +      } else if (!pChild->GetFlag(NF_SEEN)) pChild->Delete_children();
 +      if (newy2>iDasherMaxY && !bExpectGameNode) {
 +        //remaining children offscreen and no game-mode child among them
 +        if (newy1 < iDasherMinY) pRender->onlyChildRendered = pChild; //previous children also offscreen!
 +        break; //skip remaining children
        }
 -      if (newy2>iDasherMaxY) break; //remaining children offscreen
      }
      I++;
      newy1=newy2;
diff --cc Src/DasherCore/MandarinAlphMgr.cpp
index a4c13af,d0e7e3d..7d987c7
--- a/Src/DasherCore/MandarinAlphMgr.cpp
+++ b/Src/DasherCore/MandarinAlphMgr.cpp
@@@ -46,9 -46,9 +46,9 @@@ static char THIS_FILE[] = __FILE__
  #endif
  #endif
  
 -CMandarinAlphMgr::CMandarinAlphMgr(CDasherInterfaceBase *pInterface, CNodeCreationManager *pNCManager, const CAlphInfo *pAlphabet, const CAlphabetMap *pAlphMap)
 -  : CAlphabetManager(pInterface, pNCManager, pAlphabet, pAlphMap),
 +CMandarinAlphMgr::CMandarinAlphMgr(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, CNodeCreationManager *pNCManager, const CAlphInfo *pAlphabet, const CAlphIO *pAlphIO)
 +  : CAlphabetManager(pCreator, pInterface, pNCManager, pAlphabet),
-     m_pConversionsBySymbol(new set<symbol>[GetAlphabet()->GetNumberTextSymbols()+1]) {
+     m_pConversionsBySymbol(new vector<symbol>[GetAlphabet()->GetNumberTextSymbols()+1]) {
    DASHER_ASSERT(pAlphabet->m_iConversionID==2);
        
    //the CHAlphabet contains a group for each SPY syllable+tone, with symbols being chinese characters.      
@@@ -269,99 -259,91 +269,101 @@@ void CMandarinAlphMgr::CConvRoot::SetFl
    CDasherNode::SetFlag(iFlag,bValue);
  }
  
- void CMandarinAlphMgr::GetConversions(std::vector<pair<symbol,unsigned int> > &vChildren, symbol pySym, Dasher::CLanguageModel::Context context) {
+ // For sorting pair<symbol, probability>s into descending order
+ // (most probable first, hence sense of >)
+ bool CompSecond(const pair<symbol,unsigned int> &a, const pair<symbol,unsigned int> &b) {
+   return a.second>b.second;
+ }
  
-   const set<symbol> &convs(m_pConversionsBySymbol[pySym]);
-   for(set<symbol>::const_iterator it = convs.begin(); it != convs.end(); ++it) {
-     vChildren.push_back(std::pair<symbol, unsigned int>(*it,0));
-   }
-   //ACL I think it's a good idea to keep those in a consistent order - symbol order will do nicely
-   sort(vChildren.begin(),vChildren.end());
+ void CMandarinAlphMgr::GetConversions(std::vector<pair<symbol,unsigned int> > &vChildren, symbol pySym, Dasher::CLanguageModel::Context context) {
  
-   const uint64 iNorm(GetLongParameter(LP_NORMALIZATION));
-   const unsigned int uniform((GetLongParameter(LP_UNIFORM)*iNorm)/1000);
-     
-   //ACL pass in iNorm and uniform directly - GetPartProbs distributes the last param between
-   // however elements there are in vChildren...
-   static_cast<CPPMPYLanguageModel *>(m_pLanguageModel)->GetPartProbs(context, vChildren, iNorm, uniform);
-   
-   //std::cout<<"after get probs "<<std::endl;
+   const vector<symbol> &convs(m_pConversionsBySymbol[pySym]);
+ 
+   //Symbols which we are including with the probabilities predicted by the LM.
+   // We do this for the most probable symbols first, up to at least BY_PY_PROB_SORT_THRES
+   // (a percentage); after this, the remaining probability mass is distributed
+   // uniformly between the remaining symbols, which are kept in alphabet order (after the
+   // more probable ones).
+   //Two degenerate cases: PROB_SORT_THRES=0 => all (legal) ch symbols predicted uniformly
+   // PROB_SORT_THRES=100 => all symbols put into probability order
+   set<symbol> haveProbs;
 -  uint64 iRemaining(m_pNCManager->GetLongParameter(LP_NORMALIZATION));
++  uint64 iRemaining(GetLongParameter(LP_NORMALIZATION));
    
-   uint64 sumProb=0;  
-   for (std::vector<pair<symbol,unsigned int> >::const_iterator it = vChildren.begin(); it!=vChildren.end(); it++) {
-     sumProb += it->second;
-   }
-   DASHER_ASSERT(sumProb==iNorm);
-   //  std::cout<<"Sum Prob "<<sumProb<<std::endl;
-   //  std::cout<<"norm "<<nonuniform_norm<<std::endl;
-   
-   //Match, sumProbs = nonuniform_norm  
-   //but fix one element 'Da4'
-   // Finally, iterate through the nodes and actually assign the sizes.
-   
-  // std::cout<<"sumProb "<<sumProb<<std::endl;
-   
-   int iRemaining(iNorm);
-   for (std::vector<pair<symbol,unsigned int> >::iterator it = vChildren.begin(); it!=vChildren.end(); it++) {
-     DASHER_ASSERT(it->first>-1); //ACL Will's code tested for both these conditions explicitly, and if so 
-     DASHER_ASSERT(sumProb>0);   //then used a probability of 0. I don't think either
-                                 //should ever happen if the alphabet files are right (there'd have to
-                                 //be either no conversions of the syllable+tone, or else the LM'd have
-                                 //to assign zero probability to each), so I'm removing these tests for now...
-     iRemaining -= it->second = (it->second*iNorm)/sumProb;
-     if (it->second==0) {
- #ifdef DEBUG
-       std::cout << "WARNING: Erasing zero-probability conversion with symbol " << it->first << std::endl;
- #endif
-       vChildren.erase(it--);
 -  if (long percent=m_pNCManager->GetLongParameter(LP_PY_PROB_SORT_THRES)) {
++  if (long percent=GetLongParameter(LP_PY_PROB_SORT_THRES)) {
+     const uint64 iNorm(iRemaining);
 -    const unsigned int uniform((m_pNCManager->GetLongParameter(LP_UNIFORM)*iNorm)/1000);
++    const unsigned int uniform((GetLongParameter(LP_UNIFORM)*iNorm)/1000);
+     
+     //Set up list of symbols with blank probability entries...
+     for(vector<symbol>::const_iterator it = convs.begin(); it != convs.end(); ++it) {
+       vChildren.push_back(std::pair<symbol, unsigned int>(*it,0));
      }
-     //  std::cout<<pNode->pszConversion<<std::endl;
-     // std::cout<<pNode->Symbol<<std::endl;
-     //    std::cout<<"Probs i "<<pNode<<std::endl;
-     // std::cout<<"Symbol i"<<SymbolStore[iIdx]<<std::endl;
-     // std::cout<<"symbols size "<<SymbolStore.size()<<std::endl;
-     // std::cout<<"Symbols address "<<&SymbolStore<<std::endl;
-   }
-   DASHER_ASSERT(iRemaining==0);
-   
-   //std::cout<<"iRemaining "<<iRemaining<<std::endl;
-   
-   
-   // Last of all, allocate anything left over due to rounding error
+     
+     //Then call LM to fill in the probs, passing iNorm and uniform directly -
+     // GetPartProbs distributes the last param between however elements there are in vChildren...
+     static_cast<CPPMPYLanguageModel *>(m_pLanguageModel)->GetPartProbs(context, vChildren, iNorm, uniform);
    
-   int iLeft(vChildren.size());
+     //std::cout<<"after get probs "<<std::endl;
    
-   for (std::vector<pair<symbol,unsigned int> >::iterator it = vChildren.begin(); it!=vChildren.end(); it++) {
-     int iDiff(iRemaining / iLeft);
+     uint64 sumProb=0;  
+     for (std::vector<pair<symbol,unsigned int> >::const_iterator it = vChildren.begin(); it!=vChildren.end(); it++) {
+       sumProb += it->second;
+     }
+     DASHER_ASSERT(sumProb==iNorm);
      
-     it->second += iDiff;
+     //Sort all symbols into probability order (highest first)
+     stable_sort(vChildren.begin(), vChildren.end(), CompSecond);
+     if (percent>=100) return; //and that's what's required.
      
-     iRemaining -= iDiff;
-     --iLeft;
+     //ok, some symbols as predicted by LM, others not...
+     vector<pair<symbol,unsigned int> > probOrder;
+     swap(vChildren, probOrder);
+     const unsigned int stop(iNorm - (iNorm*percent)/100);//intermediate values are uint64
+     for (vector<pair<symbol, unsigned int> >::iterator it=probOrder.begin(); iRemaining>stop; it++) {
+       //assert: the remaining probability mass, divided by the remaining symbols,
+       // (i.e. the probability mass each symbol would receive if we uniformed the rest)
+       // must be less than the probability predicted by the LM (as we're processing
+       // symbols in decreasing order)
+       DASHER_ASSERT(iRemaining <= it->second*(convs.size()-vChildren.size()));
+       vChildren.push_back(*it);
+       haveProbs.insert(it->first);
+       iRemaining-=it->second;
+     }
+   }
+   //Now distribute iRemaining uniformly between all remaining symbols,
+   // keeping them in alphabet order
+   if (iRemaining) {
+     unsigned int iEach(iRemaining / (convs.size() - haveProbs.size()));
+     for (vector<symbol>::const_iterator it=convs.begin(); it!=convs.end(); it++) {
+       if (haveProbs.count(*it)==0)
+         vChildren.push_back(pair<symbol,unsigned int>(*it,iEach));
+     }
      
-     //    std::cout<<"Node size for "<<pNode->pszConversion<<std::endl;
-     //std::cout<<"is "<<pNode->NodeSize<<std::endl;
+     //account for rounding error by topping up
+     DASHER_ASSERT(vChildren.size() == convs.size());
+     iRemaining -= iEach * (convs.size() - haveProbs.size());
+     unsigned int iLeft = vChildren.size();
+     for (vector<pair<symbol, unsigned int> >::iterator it=vChildren.end(); iRemaining && it-- != vChildren.begin();) {
+       const unsigned int p(iRemaining / iLeft);
+       it->second+=p; iRemaining-=p; iLeft--;
+     }
    }
-   
-   DASHER_ASSERT(iRemaining == 0);
-   
  }
  
 -CMandarinAlphMgr::CMandSym::CMandSym(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, const std::string &strGroup, CMandarinAlphMgr *pMgr, symbol iSymbol, symbol pyParent)
 -: CSymbolNode(pParent, iOffset, iLbnd, iHbnd, pMgr->GetCHColour(iSymbol,iOffset), strGroup+pMgr->m_CHdisplayText[iSymbol], pMgr, iSymbol), m_pyParent(pyParent) {
 +CDasherScreen::Label *CMandarinAlphMgr::GetLabel(int iCHsym) {
 +  //TODO: LRU cache, keep down to some sensible #labels allocated?
 +  if (iCHsym>=m_vLabels.size()) {
 +    m_vLabels.resize(iCHsym+1);
 +  } else if (m_vLabels[iCHsym]) {
 +    return m_vLabels[iCHsym];
 +  }
 +  return m_vLabels[iCHsym] = m_pScreen->MakeLabel(m_CHdisplayText[iCHsym]);
 +}
 +
 +CMandarinAlphMgr::CMandSym::CMandSym(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, CMandarinAlphMgr *pMgr, symbol iSymbol, symbol pyParent)
 +: CSymbolNode(pParent, iOffset, iLbnd, iHbnd, pMgr->GetCHColour(iSymbol,iOffset), pMgr->GetLabel(iSymbol), pMgr, iSymbol), m_pyParent(pyParent) {
  }
  
 -CDasherNode *CMandarinAlphMgr::CMandSym::RebuildSymbol(CAlphNode *pParent, unsigned int iLbnd, unsigned int iHbnd, const std::string &strGroup, int iBkgCol, symbol iSymbol) {
 +CDasherNode *CMandarinAlphMgr::CMandSym::RebuildSymbol(CAlphNode *pParent, unsigned int iLbnd, unsigned int iHbnd, symbol iSymbol) {
    DASHER_ASSERT(m_pyParent!=0); //should have been computed in RebuildForwardsFromAncestor()
    if (iSymbol==m_pyParent) {
      //create the PY node that lead to this chinese
diff --cc Src/DasherCore/Parameters.cpp
index cb0f55d,0000000..a461cea
mode 100644,000000..100644
--- a/Src/DasherCore/Parameters.cpp
+++ b/Src/DasherCore/Parameters.cpp
@@@ -1,221 -1,0 +1,222 @@@
 +#include "Parameters.h"
 +
 +namespace Dasher{
 +  namespace Settings {
 +    
 +// The important thing here is that these are in the same order
 +// as the enum declarations (could add check in class that enforces this instead)
 +const bp_table boolparamtable[] = {
 +  {BP_DRAW_MOUSE_LINE, "DrawMouseLine", PERS, true, "Draw Mouse Line"},
 +  {BP_DRAW_MOUSE, "DrawMouse", PERS, false, "Draw Mouse Position"},
 +  {BP_CURVE_MOUSE_LINE, "CurveMouseLine", PERS, false, "Curve mouse line according to screen nonlinearity"},
 +#ifdef WITH_MAEMO
 +  {BP_SHOW_SLIDER, "ShowSpeedSlider", PERS, false, "ShowSpeedSlider"},
 +#else
 +  {BP_SHOW_SLIDER, "ShowSpeedSlider", PERS, true, "ShowSpeedSlider"},
 +#endif
 +  {BP_START_MOUSE, "StartOnLeft", PERS, true, "StartOnLeft"},
 +  {BP_START_SPACE, "StartOnSpace", PERS, false, "StartOnSpace"},
 +  {BP_STOP_IDLE, "StopOnIdle", PERS, false, "StopOnIdle"},
 +  {BP_CONTROL_MODE, "ControlMode", PERS, false, "ControlMode"},
 +  {BP_COLOUR_MODE, "ColourMode", PERS, true, "ColourMode"},
 +  {BP_MOUSEPOS_MODE, "StartOnMousePosition", PERS, false, "StartOnMousePosition"},
 +  {BP_PALETTE_CHANGE, "PaletteChange", PERS, true, "PaletteChange"},
 +  {BP_AUTOCALIBRATE, "Autocalibrate", PERS, true, "Automatically learn TargetOffset e.g. gazetracking"},
 +  {BP_REMAP_XTREME, "RemapXtreme", PERS, false, "Pointer at extreme Y translates more and zooms less"},
 +  {BP_DASHER_PAUSED, "DasherPaused", !PERS, true, "Dasher Paused"},
 +  {BP_GAME_MODE, "GameMode", !PERS, false, "Dasher Game Mode"},
 +  {BP_LM_DICTIONARY, "Dictionary", PERS, true, "Whether the word-based language model uses a dictionary"},
 +  {BP_LM_LETTER_EXCLUSION, "LetterExclusion", PERS, true, "Whether to do letter exclusion in the word-based model"},
 +  {BP_AUTO_SPEEDCONTROL, "AutoSpeedControl", PERS, true, "AutoSpeedControl"},
 +  {BP_LM_ADAPTIVE, "LMAdaptive", PERS, true, "Whether language model should learn as you enter text"},
 +  {BP_SOCKET_INPUT_ENABLE, "SocketInputEnable", PERS, false, "Read pointer coordinates from network socket instead of mouse"},
 +  {BP_SOCKET_DEBUG, "SocketInputDebug", PERS, false, "Print information about socket input processing to console"},
 +  {BP_CIRCLE_START, "CircleStart", PERS, false, "Start on circle mode"},
 +  {BP_GLOBAL_KEYBOARD, "GlobalKeyboard", PERS, false, "Whether to assume global control of the keyboard"},
 +#ifdef WITH_MAEMO
 +  {BP_NONLINEAR_Y, "NonlinearY", PERS, false, "Apply nonlinearities to Y axis (i.e. compress top &amp; bottom)"},
 +#else
 +  {BP_NONLINEAR_Y, "NonlinearY", PERS, true, "Apply nonlinearities to Y axis (i.e. compress top &amp; bottom)"},
 +#endif
 +  {BP_SMOOTH_OFFSET, "DelayView", !PERS, false, "Smooth dynamic-button-mode jumps over several frames"},
 +  {BP_STOP_OUTSIDE, "PauseOutside", PERS, false, "Whether to stop when pointer leaves canvas area"},
 +#ifdef TARGET_OS_IPHONE
 +  {BP_BACKOFF_BUTTON, "BackoffButton", PERS, false, "Whether to enable the extra backoff button in dynamic mode"},
 +#else
 +  {BP_BACKOFF_BUTTON, "BackoffButton", PERS, true, "Whether to enable the extra backoff button in dynamic mode"},
 +#endif
 +  {BP_TWOBUTTON_REVERSE, "TwoButtonReverse", PERS, false, "Reverse the up/down buttons in two button mode"},
 +  {BP_2B_INVERT_DOUBLE, "TwoButtonInvertDouble", PERS, false, "Double-press acts as opposite button in two-button mode"},
 +  {BP_SLOW_START, "SlowStart", PERS, false, "Start at low speed and increase"},
 +  {BP_COPY_ALL_ON_STOP, "CopyOnStop", PERS, false, "Copy all text to clipboard whenever we stop"},
 +  {BP_SPEAK_ALL_ON_STOP, "SpeakOnStop", PERS, false, "Speak all text whenever we stop"},
 +  {BP_SPEAK_WORDS, "SpeakWords", PERS, false, "Speak words as they are written"},
 +  {BP_CONTROL_MODE_HAS_HALT, "ControlHasHalt", PERS, false, "Force Control Mode to provide a stop action (triggering clipboard/speech)"},
 +#ifdef TARGET_OS_MAC
 +  {BP_CONTROL_MODE_HAS_EDIT, "ControlHasEdit", PERS, false, "Provide editing functions in control mode (forward &amp; backward movement &amp; deletion)"},
 +#else
 +  {BP_CONTROL_MODE_HAS_EDIT, "ControlHasEdit", PERS, true, "Provide editing functions in control mode (forward &amp; backward movement &amp; deletion)"},
 +#endif
 +  {BP_CONTROL_MODE_HAS_COPY, "ControlHasCopy", PERS, true, "Provide copy-to-clipboard actions in Control Mode (if platforms supports)"},
 +  {BP_CONTROL_MODE_HAS_SPEECH, "ControlHasSpeech", PERS, true, "Provide speech actions in Control Mode (if platform supports)"},
 +#ifdef TARGET_OS_IPHONE
 +  {BP_CUSTOM_TILT, "CustomTilt", PERS, false, "Use custom tilt axes"},
 +  {BP_DOUBLE_X, "DoubleXCoords", PERS, false, "Double X-coordinate of touch"},
 +#endif
 +};
 +
 +const lp_table longparamtable[] = {
 +  {LP_ORIENTATION, "ScreenOrientation", PERS, -2, "Screen Orientation"},
 +  {LP_REAL_ORIENTATION, "RealOrientation", !PERS, 0, "Actual screen orientation (allowing for alphabet default)"},
 +  {LP_MAX_BITRATE, "MaxBitRateTimes100", PERS, 80, "Max Bit Rate Times 100"},
 +  {LP_FRAMERATE, "FrameRate", PERS, 3200, "Decaying average of last known frame rates, *100"},
 +  {LP_VIEW_ID, "ViewID", PERS, 1, "ViewID"},
 +  {LP_LANGUAGE_MODEL_ID, "LanguageModelID", PERS, 0, "LanguageModelID"},
 +  {LP_DASHER_FONTSIZE, "DasherFontSize", PERS, 2, "DasherFontSize"},
 +  {LP_MESSAGE_FONTSIZE, "MessageFontSize", PERS, 14, "Size of font for messages (in points)"},
 +  {LP_SHAPE_TYPE, "RenderStyle", PERS, 1, "Shapes to render in (0/1=disjoint/overlapping rects, 2/3=triangles/truncated, 4=quadrics, 5=circles)"},
 +  {LP_UNIFORM, "UniformTimes1000", PERS, 50, "UniformTimes1000"},
 +  {LP_YSCALE, "YScaling", PERS, 0, "YScaling"},
 +  {LP_MOUSEPOSDIST, "MousePositionBoxDistance", PERS, 50, "MousePositionBoxDistance"},
 +  {LP_STOP_IDLETIME, "StopIdleTime", PERS, 1000, "StopIdleTime" },
++  {LP_PY_PROB_SORT_THRES, "PYProbabilitySortThreshold", PERS, 85, "Sort converted syms in descending probability order up to this %age"},
 +  {LP_MESSAGE_TIME, "MessageTime", PERS, 2500, "Time for which non-modal messages are displayed, in ms"},
 +  {LP_LM_MAX_ORDER, "LMMaxOrder", PERS, 5, "LMMaxOrder"},
 +  {LP_LM_EXCLUSION, "LMExclusion", PERS, 0, "LMExclusion"},
 +  {LP_LM_UPDATE_EXCLUSION, "LMUpdateExclusion", PERS, 1, "LMUpdateExclusion"},
 +  {LP_LM_ALPHA, "LMAlpha", PERS, 49, "LMAlpha"},
 +  {LP_LM_BETA, "LMBeta", PERS, 77, "LMBeta"},
 +  {LP_LM_MIXTURE, "LMMixture", PERS, 50, "LMMixture"},
 +  {LP_NORMALIZATION, "Normalization", !PERS, 1 << 16, "Interval for child nodes"},
 +  {LP_LINE_WIDTH, "LineWidth", PERS, 1, "Width to draw crosshair and mouse line"},
 +  {LP_GEOMETRY, "Geometry", PERS, 0, "Screen geometry (mostly for tall thin screens) - 0=old-style, 1=square no-xhair, 2=squish, 3=squish+log"},
 +  {LP_LM_WORD_ALPHA, "WordAlpha", PERS, 50, "Alpha value for word-based model"},
 +  {LP_USER_LOG_LEVEL_MASK, "UserLogLevelMask", PERS, 0, "Controls level of user logging, 0 = none, 1 = short, 2 = detailed, 3 = both"},
 +  {LP_ZOOMSTEPS, "Zoomsteps", PERS, 32, "Integerised ratio of zoom size for click/button mode, denom 64."},
 +  {LP_B, "ButtonMenuBoxes", PERS, 4, "Number of boxes for button menu mode"},
 +  {LP_S, "ButtonMenuSafety", PERS, 25, "Safety parameter for button mode, in percent."},
 +#ifdef TARGET_OS_IPHONE
 +  {LP_BUTTON_SCAN_TIME, "ButtonMenuScanTime", PERS, 600, "Scanning time in menu mode (0 = don't scan), in ms"},
 +#else
 +  {LP_BUTTON_SCAN_TIME, "ButtonMenuScanTime", PERS, 0, "Scanning time in menu mode (0 = don't scan), in ms"},
 +#endif
 +  {LP_R, "ButtonModeNonuniformity", PERS, 0, "Button mode box non-uniformity"},
 +  {LP_RIGHTZOOM, "ButtonCompassModeRightZoom", PERS, 5120, "Zoomfactor (*1024) for compass mode"},
 +#if defined(WITH_MAEMO) || defined (TARGET_OS_IPHONE)
 +  {LP_NODE_BUDGET, "NodeBudget", PERS, 1000, "Target (min) number of node objects to maintain"},
 +#else
 +  {LP_NODE_BUDGET, "NodeBudget", PERS, 3000, "Target (min) number of node objects to maintain"},
 +#endif
 +  {LP_OUTLINE_WIDTH, "OutlineWidth", PERS, 0, "Absolute value is line width to draw boxes (fill iff >=0)" },
 +  {LP_MIN_NODE_SIZE, "MinNodeSize", PERS, 50, "Minimum size of node (in dasher coords) to draw" }, 
 +#ifdef WITH_MAEMO
 +  {LP_NONLINEAR_X, "NonLinearX", PERS, 0, "Nonlinear compression of X-axis (0 = none, higher = more extreme)"},
 +#else
 +  {LP_NONLINEAR_X, "NonLinearX", PERS, 5, "Nonlinear compression of X-axis (0 = none, higher = more extreme)"},
 +#endif
 +  {LP_BOOSTFACTOR, "BoostFactor", !PERS, 100, "Boost/brake factor (multiplied by 100)"},
 +  {LP_AUTOSPEED_SENSITIVITY, "AutospeedSensitivity", PERS, 100, "Sensitivity of automatic speed control (percent)"},
 +  {LP_SOCKET_PORT, "SocketPort", PERS, 20320, "UDP/TCP socket to use for network socket input"},
 +  {LP_SOCKET_INPUT_X_MIN, "SocketInputXMinTimes1000", PERS, 0, "Bottom of range of X values expected from network input"},
 +  {LP_SOCKET_INPUT_X_MAX, "SocketInputXMaxTimes1000", PERS, 1000, "Top of range of X values expected from network input"},
 +  {LP_SOCKET_INPUT_Y_MIN, "SocketInputYMinTimes1000", PERS, 0, "Bottom of range of Y values expected from network input"},
 +  {LP_SOCKET_INPUT_Y_MAX, "SocketInputYMaxTimes1000", PERS, 1000, "Top of range of Y values expected from network input"},
 +  {LP_OX, "OX", PERS, 2048, "X coordinate of crosshair"},
 +  {LP_OY, "OY", PERS, 2048, "Y coordinate of crosshair"},
 +  {LP_MAX_Y, "MaxY", PERS, 4096, "Maximum Y coordinate"},
 +  {LP_INPUT_FILTER, "InputFilterID", PERS, 3, "Module ID of input filter"},
 +  {LP_CIRCLE_PERCENT, "CirclePercent", PERS, 10, "Percentage of nominal vertical range to use for radius of start circle"},
 +  {LP_TWO_BUTTON_OFFSET, "TwoButtonOffset", PERS, 1638, "Offset for two button dynamic mode"},
 +  {LP_HOLD_TIME, "HoldTime", PERS, 1000, "Time for which buttons must be held to count as long presses, in ms"},
 +  {LP_MULTIPRESS_TIME, "MultipressTime", PERS, 1000, "Time in which multiple presses must occur, in ms"},
 +  {LP_SLOW_START_TIME, "SlowStartTime", PERS, 1000, "Time over which slow start occurs"},
 +  {LP_CONVERSION_ORDER, "ConversionOrder", PERS, 0, "Conversion ordering"},
 +  {LP_CONVERSION_TYPE, "ConversionType", PERS, 0, "Conversion type"},
 +  {LP_TWO_PUSH_OUTER, "TwoPushOuter", PERS, 1792, "Offset for one button dynamic mode outer marker"},
 +  {LP_TWO_PUSH_UP, "TwoPushUp", PERS, 1536, "Offset to up marker in one button dynamic"},
 +  {LP_TWO_PUSH_DOWN, "TwoPushDown", PERS, 1280, "Offset to down marker in one button dynamic"},
 +  {LP_TWO_PUSH_TOLERANCE, "TwoPushTolerance", PERS, 100, "Tolerance of two-push-mode pushes, in ms"},
 +  {LP_DYNAMIC_BUTTON_LAG, "DynamicButtonLag", PERS, 50, "Lag of pushes in dynamic button mode (ms)"},
 +  {LP_STATIC1B_TIME, "Static1BTime", PERS, 2000, "Time for static-1B mode to scan from top to bottom (ms)"},
 +  {LP_STATIC1B_ZOOM, "Static1BZoom", PERS, 8, "Zoom factor for static-1B mode"},
 +  {LP_DEMO_SPRING, "DemoSpring", PERS, 100, "Springyness in Demo-mode"},
 +  {LP_DEMO_NOISE_MEM, "DemoNoiseMem", PERS, 100, "Memory parameter for noise in Demo-mode"},
 +  {LP_DEMO_NOISE_MAG, "DemoNoiseMag", PERS, 325, "Magnitude of noise in Demo-mode"},
 +  {LP_MAXZOOM, "ClickMaxZoom", PERS, 200, "Maximum zoom possible in click mode (times 10)"},
 +  {LP_DYNAMIC_SPEED_INC, "DynamicSpeedInc", PERS, 3, "%age by which dynamic mode auto speed control increases speed"},
 +  {LP_DYNAMIC_SPEED_FREQ, "DynamicSpeedFreq", PERS, 10, "Seconds after which dynamic mode auto speed control increases speed"},
 +  {LP_DYNAMIC_SPEED_DEC, "DynamicSpeedDec", PERS, 8, "%age by which dynamic mode auto speed control decreases speed on reverse"},
 +  {LP_TAP_TIME, "TapTime", PERS, 200, "Max length of a stylus 'tap' rather than hold (ms)"},
 +#ifdef TARGET_OS_IPHONE
 +  {LP_MARGIN_WIDTH, "MarginWidth", PERS, 500, "Width of RHS margin (in Dasher co-ords)"},
 +#else
 +  {LP_MARGIN_WIDTH, "MarginWidth", PERS, 300, "Width of RHS margin (in Dasher co-ords)"},
 +#endif
 +  {LP_TARGET_OFFSET, "TargetOffset", PERS, 0, "Vertical distance between mouse pointer and target (400=screen height)"},
 +};
 +
 +const sp_table stringparamtable[] = {
 +  {SP_ALPHABET_ID, "AlphabetID", PERS, "", "AlphabetID"},
 +  {SP_ALPHABET_1, "Alphabet1", PERS, "", "Alphabet History 1"},
 +  {SP_ALPHABET_2, "Alphabet2", PERS, "", "Alphabet History 2"},
 +  {SP_ALPHABET_3, "Alphabet3", PERS, "", "Alphabet History 3"},
 +  {SP_ALPHABET_4, "Alphabet4", PERS, "", "Alphabet History 4"},
 +  {SP_COLOUR_ID, "ColourID", PERS, "", "ColourID"}, 
 +  {SP_DEFAULT_COLOUR_ID, "DefaultColourID", !PERS, "", "Default Colour ID (Used for auto-colour mode)"},
 +  {SP_DASHER_FONT, "DasherFont", PERS, "", "DasherFont"},
 +  {SP_SYSTEM_LOC, "SystemLocation", !PERS, "sys_", "System Directory"},
 +  {SP_USER_LOC, "UserLocation", !PERS, "usr_", "User Directory"},
 +  {SP_GAME_TEXT_FILE, "GameTextFile", PERS, "", "User-specified file with strings to practice writing"},
 +  {SP_SOCKET_INPUT_X_LABEL, "SocketInputXLabel", PERS, "x", "Label preceding X values for network input"},
 +  {SP_SOCKET_INPUT_Y_LABEL, "SocketInputYLabel", PERS, "y", "Label preceding Y values for network input"},
 +#if defined(WITH_MAEMO) || defined(TARGET_OS_IPHONE)
 +  {SP_INPUT_FILTER, "InputFilter", PERS, "Stylus Control", "Input filter used to provide the current control mode"},
 +#else
 +  {SP_INPUT_FILTER, "InputFilter", PERS, "Normal Control", "Input filter used to provide the current control mode"},
 +#endif
 +  {SP_INPUT_DEVICE, "InputDevice", PERS, "Mouse Input", "Driver for the input device"},
 +  {SP_BUTTON_0, "Button0", PERS, "", "Assignment to button 0"},
 +  {SP_BUTTON_1, "Button1", PERS, "", "Assignment to button 1"},
 +  {SP_BUTTON_2, "Button2", PERS, "", "Assignment to button 2"},
 +  {SP_BUTTON_3, "Button3", PERS, "", "Assignment to button 3"},
 +  {SP_BUTTON_4, "Button4", PERS, "", "Assignment to button 4"},
 +  {SP_BUTTON_10, "Button10", PERS, "", "Assignment to button 10"},
 +  {SP_JOYSTICK_DEVICE, "JoystickDevice", PERS, "/dev/input/js0", "Joystick device"},
 +#ifdef TARGET_OS_IPHONE
 +  {SP_CUSTOM_TILT, "CustomTiltParams", PERS, "(0.0,0.0,0.0) - (0.0,-1.0,0.0) / (-1.0,0.0,0.0)", "Custom tilt axes"},
 +  {SP_VERTICAL_TILT, "VerticalTiltParams", PERS, "-0.1 - -0.9 / -0.4 - 0.4", "Limits of vertical tilting"}, 
 +#endif
 +};
 +
 +ParameterType GetParameterType(int iParameter) {
 +  if ((iParameter >= FIRST_BP) && (iParameter < FIRST_LP))
 +    return ParamBool;
 +  if ((iParameter >= FIRST_LP) && (iParameter < FIRST_SP))
 +    return ParamLong;
 +  if ((iParameter >= FIRST_SP) && (iParameter < END_OF_SPS))
 +    return ParamString;
 +  
 +  return ParamInvalid;
 +}
 +
 +std::string GetParameterName(int iParameter) {
 +  // Pull the registry name out of the correct table depending on the parameter type
 +  switch (GetParameterType(iParameter)) {
 +    case (ParamBool):
 +      DASHER_ASSERT(iParameter == boolparamtable[iParameter - FIRST_BP].key);
 +      return boolparamtable[iParameter - FIRST_BP].regName;
 +    case (ParamLong):
 +      DASHER_ASSERT(iParameter == longparamtable[iParameter - FIRST_LP].key);
 +      return longparamtable[iParameter - FIRST_LP].regName;
 +    case (ParamString):
 +      DASHER_ASSERT(iParameter == stringparamtable[iParameter - FIRST_SP].key);
 +      return stringparamtable[iParameter - FIRST_SP].regName;
 +    case ParamInvalid:
 +      DASHER_ASSERT(false);
 +      break;
 +  };
 +  return "";
 +}
 +
 +} //end namespace Settings
 +} //end namespace Dasher
diff --cc Src/DasherCore/Parameters.h
index ffbac04,39511a7..f98d867
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@@ -50,8 -52,8 +50,8 @@@ enum 
  
  enum { 
    LP_ORIENTATION = END_OF_BPS, LP_REAL_ORIENTATION, LP_MAX_BITRATE, LP_FRAMERATE,
 -  LP_VIEW_ID, LP_LANGUAGE_MODEL_ID, LP_DASHER_FONTSIZE, LP_SHAPE_TYPE,
 -  LP_UNIFORM, LP_YSCALE, LP_MOUSEPOSDIST, LP_STOP_IDLETIME, LP_PY_PROB_SORT_THRES,
 +  LP_VIEW_ID, LP_LANGUAGE_MODEL_ID, LP_DASHER_FONTSIZE, LP_MESSAGE_FONTSIZE, LP_SHAPE_TYPE,
-   LP_UNIFORM, LP_YSCALE, LP_MOUSEPOSDIST, LP_STOP_IDLETIME, LP_MESSAGE_TIME,
++  LP_UNIFORM, LP_YSCALE, LP_MOUSEPOSDIST, LP_STOP_IDLETIME, LP_PY_PROB_SORT_THRES, LP_MESSAGE_TIME,
    LP_LM_MAX_ORDER, LP_LM_EXCLUSION,
    LP_LM_UPDATE_EXCLUSION, LP_LM_ALPHA, LP_LM_BETA,
    LP_LM_MIXTURE, LP_NORMALIZATION, LP_LINE_WIDTH, LP_GEOMETRY,
diff --cc Src/DasherCore/SocketInputBase.h
index 3c6d97b,a923551..c6dfb6d
--- a/Src/DasherCore/SocketInputBase.h
+++ b/Src/DasherCore/SocketInputBase.h
@@@ -20,9 -20,9 +20,9 @@@ namespace Dasher 
    class CSocketInputBase;
  
  using namespace std;
- /// \ingroup Input 
+ /// \ingroup Input
  /// \{
 -class CSocketInputBase : public CScreenCoordInput {
 +class CSocketInputBase : public CScreenCoordInput, public CSettingsUserObserver {
  
  public:
  
@@@ -120,12 -120,12 +120,13 @@@ 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 --cc Src/MacOSX/Dasher.xcodeproj/project.pbxproj
index cffbba7,c27910f..71d39c9
--- a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
+++ b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
@@@ -378,14 -388,11 +378,17 @@@
  		33E173E40F3E0B6400D19B38 /* training_swedish_SE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 33E173C40F3E0B6400D19B38 /* training_swedish_SE.txt */; };
  		33E173E50F3E0B6400D19B38 /* training_turkish_TR.txt in Resources */ = {isa = PBXBuildFile; fileRef = 33E173C50F3E0B6400D19B38 /* training_turkish_TR.txt */; };
  		33E173E60F3E0B6400D19B38 /* training_welsh_GB.txt in Resources */ = {isa = PBXBuildFile; fileRef = 33E173C60F3E0B6400D19B38 /* training_welsh_GB.txt */; };
 +		33E756A41202D6180012A0E9 /* WordGeneratorBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 33E756A31202D6180012A0E9 /* WordGeneratorBase.h */; };
  		33E91A770F55E60B00B5F513 /* KeyboardHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33E91A750F55E60B00B5F513 /* KeyboardHelper.cpp */; };
  		33E91A780F55E60B00B5F513 /* KeyboardHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 33E91A760F55E60B00B5F513 /* KeyboardHelper.h */; };
+ 		33F3E72E1354D65A008AE1F2 /* alphabet.spyToneMarks.xml in Resources */ = {isa = PBXBuildFile; fileRef = 33F3E72D1354D65A008AE1F2 /* alphabet.spyToneMarks.xml */; };
 +		33F6C9EA133241A000745B06 /* AbstractXMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 33F6C9E8133241A000745B06 /* AbstractXMLParser.h */; };
 +		33F6C9EB133241A000745B06 /* AbstractXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33F6C9E9133241A000745B06 /* AbstractXMLParser.cpp */; };
+ 		33F8F5CC1357704D009F4E79 /* alphabet.spyTones2.xml in Resources */ = {isa = PBXBuildFile; fileRef = 33F8F5CB1357704D009F4E79 /* alphabet.spyTones2.xml */; };
+ 		33F8F67C135780B3009F4E79 /* alphabet.bopoDict.xml in Resources */ = {isa = PBXBuildFile; fileRef = 33F8F67B135780B3009F4E79 /* alphabet.bopoDict.xml */; };
 +		33FC1D2B13ACE7E7007642CD /* ScreenGameModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33FC1D2913ACE7E7007642CD /* ScreenGameModule.cpp */; };
 +		33FC1D2C13ACE7E7007642CD /* ScreenGameModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 33FC1D2A13ACE7E7007642CD /* ScreenGameModule.h */; };
 +		33FC1D3113ACE88F007642CD /* Parameters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33FC1D3013ACE88F007642CD /* Parameters.cpp */; };
  		33FC93390FEFA2C900A9F08D /* TwoPushDynamicFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33FC93370FEFA2C900A9F08D /* TwoPushDynamicFilter.cpp */; };
  		33FC933A0FEFA2C900A9F08D /* TwoPushDynamicFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 33FC93380FEFA2C900A9F08D /* TwoPushDynamicFilter.h */; };
  		33FC93430FEFA2FB00A9F08D /* FrameRate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33FC93420FEFA2FB00A9F08D /* FrameRate.cpp */; };
@@@ -781,14 -800,11 +784,17 @@@
  		33E173C40F3E0B6400D19B38 /* training_swedish_SE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_swedish_SE.txt; sourceTree = "<group>"; };
  		33E173C50F3E0B6400D19B38 /* training_turkish_TR.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_turkish_TR.txt; sourceTree = "<group>"; };
  		33E173C60F3E0B6400D19B38 /* training_welsh_GB.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_welsh_GB.txt; sourceTree = "<group>"; };
 +		33E756A31202D6180012A0E9 /* WordGeneratorBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WordGeneratorBase.h; sourceTree = "<group>"; };
  		33E91A750F55E60B00B5F513 /* KeyboardHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardHelper.cpp; sourceTree = "<group>"; };
  		33E91A760F55E60B00B5F513 /* KeyboardHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyboardHelper.h; sourceTree = "<group>"; };
+ 		33F3E72D1354D65A008AE1F2 /* alphabet.spyToneMarks.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = alphabet.spyToneMarks.xml; sourceTree = "<group>"; };
 +		33F6C9E8133241A000745B06 /* AbstractXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractXMLParser.h; sourceTree = "<group>"; };
 +		33F6C9E9133241A000745B06 /* AbstractXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractXMLParser.cpp; sourceTree = "<group>"; };
+ 		33F8F5CB1357704D009F4E79 /* alphabet.spyTones2.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = alphabet.spyTones2.xml; sourceTree = "<group>"; };
+ 		33F8F67B135780B3009F4E79 /* alphabet.bopoDict.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = alphabet.bopoDict.xml; sourceTree = "<group>"; };
 +		33FC1D2913ACE7E7007642CD /* ScreenGameModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScreenGameModule.cpp; sourceTree = "<group>"; };
 +		33FC1D2A13ACE7E7007642CD /* ScreenGameModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScreenGameModule.h; sourceTree = "<group>"; };
 +		33FC1D3013ACE88F007642CD /* Parameters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Parameters.cpp; sourceTree = "<group>"; };
  		33FC93370FEFA2C900A9F08D /* TwoPushDynamicFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TwoPushDynamicFilter.cpp; sourceTree = "<group>"; };
  		33FC93380FEFA2C900A9F08D /* TwoPushDynamicFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwoPushDynamicFilter.h; sourceTree = "<group>"; };
  		33FC93420FEFA2FB00A9F08D /* FrameRate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FrameRate.cpp; sourceTree = "<group>"; };
@@@ -1699,9 -1722,9 +1708,12 @@@
  				33E173E60F3E0B6400D19B38 /* training_welsh_GB.txt in Resources */,
  				335DB0FB100B332C006DB155 /* alphabet.spyDict.xml in Resources */,
  				335DB101100B3358006DB155 /* training_spyDict.txt in Resources */,
 +				334DE238135E3E68007C8D6D /* control.textlabels.xml in Resources */,
 +				334DE239135E3E68007C8D6D /* control.xml in Resources */,
 +				334DE23B135E3F4B007C8D6D /* control.dtd in Resources */,
+ 				33F3E72E1354D65A008AE1F2 /* alphabet.spyToneMarks.xml in Resources */,
+ 				33F8F5CC1357704D009F4E79 /* alphabet.spyTones2.xml in Resources */,
+ 				33F8F67C135780B3009F4E79 /* alphabet.bopoDict.xml in Resources */,
  			);
  			runOnlyForDeploymentPostprocessing = 0;
  		};



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