[dasher: 4/43] Rm CControlEvent, add virtual CDashIntf::ctrl{Move, Delete}, cmds read from xml



commit 605f56aa94064aaf4daa43b5c534038a1c1b78ee
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Mar 16 22:58:40 2011 +0000

    Rm CControlEvent, add virtual CDashIntf::ctrl{Move,Delete}, cmds read from xml
    
    reads control.xml from user or system locations
    inc control.xml (w/ -> arrows) and controlarrows.xml (unicode arrows, + "New")
    
    MacOSX: backwards deletion only, manually searches context => many delete-chars
    iPhone: keep previous ctx code.
    Gtk2: remove control signal etc, call through to editor edit_move/delete
    Win32: Dasher::ctrlMove/Delete calls straight through to m_pEdit. Also tidy
      CEdit::Move/Delete slightly. TODO: these are v similar, but not quite
      the same - is this right, and/or can the common code be combined?
    
    (Thus, removed 3 copies of conversion from ControlID!)
    
    Linux: Makefile+configure.ac copies control.xml into 'share'; also OSX, iPhone
    
    control.dtd defines structure, validator checks ids unique + refs exist!

 Data/GUI/dasher.compose.ui                   |    1 -
 Data/GUI/dasher.direct.ui                    |    1 -
 Data/GUI/dasher.fullscreen.ui                |    1 -
 Data/GUI/dasher.gameWIP.ui                   |    1 -
 Data/GUI/dasher.traditional.ui               |    1 -
 Data/GUI/dashermaemo.ui                      |    1 -
 Data/GUI/dashermaemofullscreen.ui            |    1 -
 Data/Makefile.am                             |    2 +-
 Data/control/Makefile.am                     |    3 +
 Data/control/control.dtd                     |   26 ++
 Data/control/control.textlabels.xml          |  112 +++++++
 Data/control/control.xml                     |  116 +++++++
 Data/controllabels/Makefile.am               |    3 -
 Data/controllabels/controllabels.dtd         |    7 -
 Data/controllabels/controllabels.xml         |   32 --
 Src/DasherCore/AbstractXMLParser.cpp         |    9 +-
 Src/DasherCore/AbstractXMLParser.h           |    3 +-
 Src/DasherCore/ControlManager.cpp            |  436 ++++++++++--------------
 Src/DasherCore/ControlManager.h              |  145 ++++-----
 Src/DasherCore/DasherInterfaceBase.h         |   17 +-
 Src/DasherCore/Event.h                       |   11 +-
 Src/Gtk2/DasherControl.cpp                   |   12 +-
 Src/Gtk2/DasherControl.h                     |   18 +-
 Src/Gtk2/GtkDasherControl.cpp                |   10 +
 Src/Gtk2/GtkDasherControl.h                  |    5 +-
 Src/Gtk2/dasher_editor.cpp                   |  106 +++---
 Src/Gtk2/dasher_editor.h                     |   32 +--
 Src/Gtk2/dasher_editor_external.cpp          |   75 +----
 Src/Gtk2/dasher_editor_internal.cpp          |  461 +++++++++++---------------
 Src/Gtk2/dasher_editor_internal.h            |    1 -
 Src/Gtk2/dasher_main.cpp                     |   26 +--
 Src/MacOSX/COSXDasherControl.h               |   18 +
 Src/MacOSX/COSXDasherControl.mm              |   33 ++-
 Src/MacOSX/Dasher.xcodeproj/project.pbxproj  |   38 ++-
 Src/MacOSX/DasherEdit.mm                     |    8 +-
 Src/Win32/Dasher.cpp                         |   12 +-
 Src/Win32/Dasher.h                           |    3 +
 Src/Win32/DasherWindow.cpp                   |   71 ----
 Src/Win32/DasherWindow.h                     |    1 -
 Src/Win32/Widgets/Edit.cpp                   |  103 +++---
 Src/Win32/Widgets/Edit.h                     |   18 +-
 Src/iPhone/Classes/CDasherInterfaceBridge.h  |    2 +
 Src/iPhone/Classes/CDasherInterfaceBridge.mm |   44 +--
 Src/iPhone/Classes/DasherAppDelegate.h       |   11 +-
 Src/iPhone/Classes/DasherAppDelegate.mm      |   16 +-
 Src/iPhone/Dasher.xcodeproj/project.pbxproj  |    4 +
 configure.ac                                 |    2 +-
 47 files changed, 975 insertions(+), 1084 deletions(-)
---
diff --git a/Data/GUI/dasher.compose.ui b/Data/GUI/dasher.compose.ui
index 13563ec..9d96f30 100644
--- a/Data/GUI/dasher.compose.ui
+++ b/Data/GUI/dasher.compose.ui
@@ -459,7 +459,6 @@
                             <property name="int2">0</property>
                             <signal handler="parameter_notification" name="dasher_changed"/>
                             <signal handler="handle_stop_event" name="dasher_stop"/>
-                            <signal handler="handle_control_event" name="dasher_control"/>
                             <signal handler="gtk2_edit_output_callback" name="dasher_edit_insert"/>
                             <signal handler="gtk2_edit_delete_callback" name="dasher_edit_delete"/>
                             <signal handler="handle_request_settings" name="dasher_request_settings"/>
diff --git a/Data/GUI/dasher.direct.ui b/Data/GUI/dasher.direct.ui
index c18e7eb..5ea8898 100644
--- a/Data/GUI/dasher.direct.ui
+++ b/Data/GUI/dasher.direct.ui
@@ -56,7 +56,6 @@
                 <property name="has_focus">True</property>
                 <signal handler="parameter_notification" name="dasher_changed"/>
                 <signal handler="handle_stop_event" name="dasher_stop"/>
-                <signal handler="handle_control_event" name="dasher_control"/>
                 <signal handler="gtk2_edit_output_callback" name="dasher_edit_insert"/>
                 <signal handler="gtk2_edit_delete_callback" name="dasher_edit_delete"/>
                 <signal handler="handle_request_settings" name="dasher_request_settings"/>
diff --git a/Data/GUI/dasher.fullscreen.ui b/Data/GUI/dasher.fullscreen.ui
index 82e55ab..bd7f228 100644
--- a/Data/GUI/dasher.fullscreen.ui
+++ b/Data/GUI/dasher.fullscreen.ui
@@ -172,7 +172,6 @@
                     <property name="last_modification_time">Sun, 05 Mar 2006 19:33:03 GMT</property>
                     <signal handler="parameter_notification" last_modification_time="Sun, 05 Mar 2006 19:45:23 GMT" name="dasher_changed"/>
                     <signal handler="handle_stop_event" last_modification_time="Sun, 05 Mar 2006 19:45:43 GMT" name="dasher_stop"/>
-                    <signal handler="handle_control_event" last_modification_time="Sun, 05 Mar 2006 19:45:53 GMT" name="dasher_control"/>
                     <signal handler="gtk2_edit_output_callback" last_modification_time="Sun, 05 Mar 2006 19:46:13 GMT" name="dasher_edit_insert"/>
                     <signal handler="gtk2_edit_delete_callback" last_modification_time="Sun, 05 Mar 2006 19:46:32 GMT" name="dasher_edit_delete"/>
                     <signal handler="handle_request_settings" last_modification_time="Sun, 05 Mar 2006 19:47:12 GMT" name="dasher_request_settings"/>
diff --git a/Data/GUI/dasher.gameWIP.ui b/Data/GUI/dasher.gameWIP.ui
index cd2a245..2c52f0c 100644
--- a/Data/GUI/dasher.gameWIP.ui
+++ b/Data/GUI/dasher.gameWIP.ui
@@ -647,7 +647,6 @@
                 <property name="last_modification_time">Sun, 05 Mar 2006 19:33:03 GMT</property>
                 <signal handler="parameter_notification" last_modification_time="Sun, 05 Mar 2006 19:45:23 GMT" name="dasher_changed"/>
                 <signal handler="handle_stop_event" last_modification_time="Sun, 05 Mar 2006 19:45:43 GMT" name="dasher_stop"/>
-                <signal handler="handle_control_event" last_modification_time="Sun, 05 Mar 2006 19:45:53 GMT" name="dasher_control"/>
                 <signal handler="gtk2_edit_output_callback" last_modification_time="Sun, 05 Mar 2006 19:46:13 GMT" name="dasher_edit_insert"/>
                 <signal handler="gtk2_edit_delete_callback" last_modification_time="Sun, 05 Mar 2006 19:46:32 GMT" name="dasher_edit_delete"/>
                 <signal handler="handle_request_settings" last_modification_time="Sun, 05 Mar 2006 19:47:12 GMT" name="dasher_request_settings"/>
diff --git a/Data/GUI/dasher.traditional.ui b/Data/GUI/dasher.traditional.ui
index fab4371..f0bfb27 100644
--- a/Data/GUI/dasher.traditional.ui
+++ b/Data/GUI/dasher.traditional.ui
@@ -243,7 +243,6 @@
                 <property name="visible">True</property>
                 <signal handler="parameter_notification" name="dasher_changed"/>
                 <signal handler="handle_stop_event" name="dasher_stop"/>
-                <signal handler="handle_control_event" name="dasher_control"/>
                 <signal handler="gtk2_edit_output_callback" name="dasher_edit_insert"/>
                 <signal handler="gtk2_edit_delete_callback" name="dasher_edit_delete"/>
                 <signal handler="handle_request_settings" name="dasher_request_settings"/>
diff --git a/Data/GUI/dashermaemo.ui b/Data/GUI/dashermaemo.ui
index c0742c9..412c33c 100644
--- a/Data/GUI/dashermaemo.ui
+++ b/Data/GUI/dashermaemo.ui
@@ -90,7 +90,6 @@
             <property name="int1">0</property>
             <property name="int2">0</property>
             <signal handler="handle_stop_event" name="dasher_stop"/>
-            <signal handler="handle_control_event" name="dasher_control"/>
             <signal handler="gtk2_edit_output_callback" name="dasher_edit_insert"/>
             <signal handler="gtk2_edit_delete_callback" name="dasher_edit_delete"/>
             <signal handler="handle_request_settings" name="dasher_request_settings"/>
diff --git a/Data/GUI/dashermaemofullscreen.ui b/Data/GUI/dashermaemofullscreen.ui
index c16dd17..fce3c41 100644
--- a/Data/GUI/dashermaemofullscreen.ui
+++ b/Data/GUI/dashermaemofullscreen.ui
@@ -217,7 +217,6 @@
                 <property name="last_modification_time">Wed, 29 Mar 2006 17:31:58 GMT</property>
                 <signal handler="parameter_notification" last_modification_time="Sun, 05 Mar 2006 19:45:23 GMT" name="dasher_changed"/>
                 <signal handler="handle_stop_event" last_modification_time="Sun, 05 Mar 2006 19:45:43 GMT" name="dasher_stop"/>
-                <signal handler="handle_control_event" last_modification_time="Sun, 05 Mar 2006 19:45:53 GMT" name="dasher_control"/>
                 <signal handler="gtk2_edit_output_callback" last_modification_time="Sun, 05 Mar 2006 19:46:13 GMT" name="dasher_edit_insert"/>
                 <signal handler="gtk2_edit_delete_callback" last_modification_time="Sun, 05 Mar 2006 19:46:32 GMT" name="dasher_edit_delete"/>
                 <signal handler="handle_request_settings" last_modification_time="Sun, 05 Mar 2006 19:47:12 GMT" name="dasher_request_settings"/>
diff --git a/Data/Makefile.am b/Data/Makefile.am
index 49e735a..e988ace 100644
--- a/Data/Makefile.am
+++ b/Data/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS =  training alphabets colours controllabels GUI Help
+SUBDIRS =  training alphabets colours control GUI Help
 
 desktopdir = $(datadir)/applications
 desktop_in_files = dasher.desktop.in
diff --git a/Data/control/Makefile.am b/Data/control/Makefile.am
new file mode 100644
index 0000000..3e88bb9
--- /dev/null
+++ b/Data/control/Makefile.am
@@ -0,0 +1,3 @@
+pkgdata_DATA = control.xml control.textlabels.xml control.dtd
+
+EXTRA_DIST = control.xml control.textlabels.xml control.dtd
diff --git a/Data/control/control.dtd b/Data/control/control.dtd
new file mode 100644
index 0000000..28a5b79
--- /dev/null
+++ b/Data/control/control.dtd
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!ELEMENT nodes (node*)>
+
+<!ELEMENT node ((node|move|delete|ref|root|alph)*)>
+<!--by specifying the name attribute as type ID rather than CDATA, the XML
+    validator checks that all IDs are distinct:-->
+<!ATTLIST node name ID #IMPLIED>
+<!ATTLIST node label CDATA #REQUIRED>
+<!ATTLIST node color CDATA #IMPLIED>
+
+<!ELEMENT move EMPTY>
+<!ATTLIST move forward (yes|no) #REQUIRED>
+<!ATTLIST move dist (char|word|line|file) #REQUIRED>
+
+<!ELEMENT delete EMPTY>
+<!ATTLIST delete forward (yes|no) #REQUIRED>
+<!ATTLIST delete dist (char|word|line|file) #REQUIRED>
+
+<!ELEMENT ref EMPTY>
+<!--by specifying the name attribute as type IDREF rather than CDATA, the XML
+    validator checks that a node with that name (as an ID) exists-->
+<!ATTLIST ref name IDREF #REQUIRED>
+
+<!ELEMENT root EMPTY>
+
+<!ELEMENT alph EMPTY>
\ No newline at end of file
diff --git a/Data/control/control.textlabels.xml b/Data/control/control.textlabels.xml
new file mode 100644
index 0000000..35eae6d
--- /dev/null
+++ b/Data/control/control.textlabels.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Standard control editing nodes but with labels using only hyphen and left/right angle brackets - in case the unicode arrow/triangle characters are missing from anyone's font. Copy to your user directory and rename as control.xml to replace the standard definition with this one.-->
+<nodes>
+<node name="CTL_MOVE" label="Move" color="-1">
+  <node name="CTL_MOVE_FORWARD" label="-&gt;" color="-1">
+    <node label="&gt;" color="-1">
+      <move forward="yes" dist="char"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;" color="-1">
+      <move forward="yes" dist="word"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;&gt;" color="-1">
+      <move forward="yes" dist="line"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;&gt;&gt;" color="-1">
+      <move forward="yes" dist="file"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+  </node>
+  <node name="CTL_MOVE_BACKWARD" label="&lt;-" color="-1">
+    <node label="&lt;" color="-1">
+      <move forward="no" dist="char"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;" color="-1">
+      <move forward="no" dist="word"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;&lt;" color="-1">
+      <move forward="no" dist="line"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;&lt;&lt;" color="-1">
+      <move forward="no" dist="file"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+  </node>
+</node>
+<node name="CTL_DELETE" label="Delete" color="-1">
+  <node name="CTL_DELETE_FORWARD" label="-&gt;" color="-1">
+    <node label="&gt;" color="-1">
+      <delete forward="yes" dist="char"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;" color="-1">
+      <delete forward="yes" dist="word"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;&gt;" color="-1">
+      <delete forward="yes" dist="line"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&gt;&gt;&gt;&gt;" color="-1">
+      <delete forward="yes" dist="file"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+  </node>
+  <node name="CTL_DELETE_BACKWARD" label="&lt;-" color="-1">
+    <node label="&lt;" color="-1">
+      <delete forward="no" dist="char"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;" color="-1">
+      <delete forward="no" dist="word"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;&lt;" color="-1">
+      <delete forward="no" dist="line"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&lt;&lt;&lt;&lt;" color="-1">
+      <delete forward="no" dist="file"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+  </node>
+</node>
+</nodes>
diff --git a/Data/control/control.xml b/Data/control/control.xml
new file mode 100644
index 0000000..5c88aff
--- /dev/null
+++ b/Data/control/control.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes>
+<node name="CTL_MOVE" label="Move" color="-1">
+  <node name="CTL_MOVE_FORWARD" label="&#x2192;" color="-1">
+    <node label="&#x25B8;" color="-1">
+      <move forward="yes" dist="char"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;" color="-1">
+      <move forward="yes" dist="word"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;&#x25B8;" color="-1">
+      <move forward="yes" dist="line"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;&#x25B8;&#x25B8;" color="-1">
+      <move forward="yes" dist="file"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+  </node>
+  <node name="CTL_MOVE_BACKWARD" label="&#x2190;" color="-1">
+    <node label="&#x25C2;" color="-1">
+      <move forward="no" dist="char"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;" color="-1">
+      <move forward="no" dist="word"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;&#x25C2;" color="-1">
+      <move forward="no" dist="line"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;&#x25C2;&#x25C2;" color="-1">
+      <move forward="no" dist="file"/>
+      <root/>
+      <ref name="CTL_MOVE_FORWARD"/>
+      <ref name="CTL_MOVE_BACKWARD"/>
+    </node>
+  </node>
+</node>
+<node name="CTL_DELETE" label="Delete" color="-1">
+  <node name="CTL_DELETE_FORWARD" label="&#x2192;" color="-1">
+    <node label="&#x25B8;" color="-1">
+      <delete forward="yes" dist="char"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;" color="-1">
+      <delete forward="yes" dist="word"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;&#x25B8;" color="-1">
+      <delete forward="yes" dist="line"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25B8;&#x25B8;&#x25B8;&#x25B8;" color="-1">
+      <delete forward="yes" dist="file"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+  </node>
+  <node name="CTL_DELETE_BACKWARD" label="&#x2190;" color="-1">
+    <node label="&#x25C2;" color="-1">
+      <delete forward="no" dist="char"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;" color="-1">
+      <delete forward="no" dist="word"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;&#x25C2;" color="-1">
+      <delete forward="no" dist="line"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+    <node label="&#x25C2;&#x25C2;&#x25C2;&#x25C2;" color="-1">
+      <delete forward="no" dist="file"/>
+      <root/>
+      <ref name="CTL_DELETE_FORWARD"/>
+      <ref name="CTL_DELETE_BACKWARD"/>
+    </node>
+  </node>
+</node>
+<node label="New">
+  <delete forward="yes" dist="file"/>
+  <delete forward="no" dist="file"/>
+  <alph/>
+</node>
+</nodes>
diff --git a/Src/DasherCore/AbstractXMLParser.cpp b/Src/DasherCore/AbstractXMLParser.cpp
index b9acdd5..bf85e0b 100644
--- a/Src/DasherCore/AbstractXMLParser.cpp
+++ b/Src/DasherCore/AbstractXMLParser.cpp
@@ -8,7 +8,9 @@
  */
 
 #include "AbstractXMLParser.h"
-
+#ifdef DEBUG
+#include <iostream>
+#endif
 bool AbstractXMLParser::ParseFile(const std::string &strFilename) {
   FILE *Input;
   if((Input = fopen(strFilename.c_str(), "r")) == (FILE *) 0) {
@@ -32,6 +34,9 @@ bool AbstractXMLParser::ParseFile(const std::string &strFilename) {
     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
       break;
     }
   } while (!Done);
@@ -42,7 +47,7 @@ bool AbstractXMLParser::ParseFile(const std::string &strFilename) {
 }
 
 void AbstractXMLParser::XmlCData(const XML_Char *str, int len) {
-  DASHER_ASSERT(false);
+
 }
 
 void AbstractXMLParser::XML_Escape(std::string &Input, bool Attribute) {
diff --git a/Src/DasherCore/AbstractXMLParser.h b/Src/DasherCore/AbstractXMLParser.h
index 0a80c3a..0a4a335 100644
--- a/Src/DasherCore/AbstractXMLParser.h
+++ b/Src/DasherCore/AbstractXMLParser.h
@@ -31,8 +31,7 @@ protected:
   virtual void XmlStartHandler(const XML_Char *name, const XML_Char **atts)=0;
   ///Subclass should override to handle an end tag
   virtual void XmlEndHandler(const XML_Char *name)=0;
-  ///Subclass may override to handle character data;
-  /// Default implementation asserts false, and ignores.
+  ///Subclass may override to handle character data; the default implementation does nothing.
   ///\param str pointer to string data, note is NOT null-terminated
   ///\param len number of bytes to read from pointer
   virtual void XmlCData(const XML_Char *str, int len);
diff --git a/Src/DasherCore/ControlManager.cpp b/Src/DasherCore/ControlManager.cpp
index 62752ec..0df295e 100644
--- a/Src/DasherCore/ControlManager.cpp
+++ b/Src/DasherCore/ControlManager.cpp
@@ -21,6 +21,7 @@
 #include "../Common/Common.h"
 
 #include "ControlManager.h"
+#include "DasherInterfaceBase.h"
 #include <cstring>
 
 using namespace Dasher;
@@ -64,18 +65,6 @@ CControlBase::NodeTemplate::NodeTemplate(const string &strLabel,int iColour)
 : m_strLabel(strLabel), m_iColour(iColour) {
 }
 
-CControlBase::EventBroadcast::EventBroadcast(int iEvent, const string &strLabel, int iColour)
-: NodeTemplate(strLabel, iColour), m_iEvent(iEvent) {
-
-}
-
-void CControlBase::EventBroadcast::happen(CContNode *pNode) {
-  CControlEvent oEvent(m_iEvent);
-  // TODO: Need to reimplement this
-  //  m_pNCManager->m_bContextSensitive=false;
-  pNode->mgr()->m_pNCManager->InsertEvent(&oEvent);
-}
-
 CControlBase::CContNode::CContNode(CDasherNode *pParent, int iOffset, unsigned int iLbnd, unsigned int iHbnd, NodeTemplate *pTemplate, CControlBase *pMgr)
 : CDasherNode(pParent, iOffset, iLbnd, iHbnd, (pTemplate->colour() != -1) ? pTemplate->colour() : (pParent->ChildCount()%99)+11, pTemplate->label()), m_pTemplate(pTemplate), m_pMgr(pMgr) {
 }
@@ -134,264 +123,197 @@ void CControlBase::CContNode::Leave() {
   }
 }
 
-COrigNodes::COrigNodes(CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface) : CControlBase(pNCManager), m_pInterface(pInterface) {
-  m_iNextID = 0;
-
-  // TODO: Need to fix this on WinCE build
-#ifndef _WIN32_WCE
-  if(!LoadLabelsFromFile(m_pNCManager->GetStringParameter(SP_USER_LOC) + "controllabels.xml")) {
-    //  something went wrong
-    if (!LoadLabelsFromFile(m_pNCManager->GetStringParameter(SP_SYSTEM_LOC)+"controllabels.xml")) {
-      // all else fails do something default
-      LoadDefaultLabels();
-    }
-  }
-  ConnectNodes();
-  SetRootTemplate(m_perId[CTL_ROOT]);
-#endif
+const vector<CControlBase::NodeTemplate *> &CControlParser::parsedNodes() {
+  return m_vParsed;
 }
 
-bool COrigNodes::LoadLabelsFromFile(string strFileName) {
-  int iFileSize;
-  {
-    struct stat sFileInfo;
-    if (stat(strFileName.c_str(), &sFileInfo)==-1) return false; //fail
-    iFileSize = sFileInfo.st_size;
+class XMLNodeTemplate : public CControlBase::NodeTemplate {
+public:
+  XMLNodeTemplate(const string &label, int color) : NodeTemplate(label, color) {
   }
-  // Implement Unicode names via xml from file:
-  char* szFileBuffer = new char[iFileSize];
-  ifstream oFile(strFileName.c_str());
-  oFile.read(szFileBuffer, iFileSize);
-  XML_Parser Parser = XML_ParserCreate(NULL);
-
-  // Members passed as callbacks must be static, so don't have a "this" pointer.
-  // We give them one through horrible casting so they can effect changes.
-  XML_SetUserData(Parser, this);
-
-  XML_SetElementHandler(Parser, XmlStartHandler, XmlEndHandler);
-  XML_SetCharacterDataHandler(Parser, XmlCDataHandler);
-  XML_Parse(Parser, szFileBuffer, iFileSize, false);
-  //  deallocate resources
-  XML_ParserFree(Parser);
-  oFile.close();
-  delete [] szFileBuffer;
-  return 0;
-}
-
-COrigNodes::Pause::Pause(const string &strLabel, int iColour) : NodeTemplate(strLabel,iColour) {
-}
-void COrigNodes::Pause::happen(CContNode *pNode) {
-  static_cast<COrigNodes *>(pNode->mgr())->m_pNCManager->SetBoolParameter(BP_DASHER_PAUSED,true);
-}
-
-COrigNodes::Stop::Stop(const string &strLabel, int iColour) : NodeTemplate(strLabel, iColour) {
-}
-void COrigNodes::Stop::happen(CContNode *pNode) {
-  static_cast<COrigNodes *>(pNode->mgr())->m_pInterface->Stop();
-}
-
-bool COrigNodes::LoadDefaultLabels() {
-  //hmmm. This is probably not the most flexible policy...
-  if (!m_perId.empty()) return false;
-
-  // TODO: Need to figure out how to handle offset changes here
-  RegisterNode(CTL_ROOT, "Control", 8);
-  RegisterNode(CTL_STOP, "Stop", 242);
-  RegisterNode(CTL_PAUSE, "Pause", 241);
-  RegisterNode(CTL_MOVE, "Move", -1);
-  RegisterNode(CTL_MOVE_FORWARD, "->", -1);
-  RegisterNode(CTL_MOVE_FORWARD_CHAR, ">", -1);
-  RegisterNode(CTL_MOVE_FORWARD_WORD, ">>", -1);
-  RegisterNode(CTL_MOVE_FORWARD_LINE, ">>>", -1);
-  RegisterNode(CTL_MOVE_FORWARD_FILE, ">>>>", -1);
-  RegisterNode(CTL_MOVE_BACKWARD, "<-", -1);
-  RegisterNode(CTL_MOVE_BACKWARD_CHAR, "<", -1);
-  RegisterNode(CTL_MOVE_BACKWARD_WORD, "<<", -1);
-  RegisterNode(CTL_MOVE_BACKWARD_LINE, "<<<", -1);
-  RegisterNode(CTL_MOVE_BACKWARD_FILE, "<<<<", -1);
-  RegisterNode(CTL_DELETE, "Delete", -1);
-  RegisterNode(CTL_DELETE_FORWARD, "->", -1);
-  RegisterNode(CTL_DELETE_FORWARD_CHAR, ">", -1);
-  RegisterNode(CTL_DELETE_FORWARD_WORD, ">>", -1);
-  RegisterNode(CTL_DELETE_FORWARD_LINE, ">>>", -1);
-  RegisterNode(CTL_DELETE_FORWARD_FILE, ">>>>", -1);
-  RegisterNode(CTL_DELETE_BACKWARD, "<-", -1);
-  RegisterNode(CTL_DELETE_BACKWARD_CHAR, "<", -1);
-  RegisterNode(CTL_DELETE_BACKWARD_WORD, "<<", -1);
-  RegisterNode(CTL_DELETE_BACKWARD_LINE, "<<<", -1);
-  RegisterNode(CTL_DELETE_BACKWARD_FILE, "<<<<", -1);
-  return true;
-}
-
-void COrigNodes::ConnectNodes() {
-  ConnectNode(-1, CTL_ROOT, -2);
-  ConnectNode(CTL_STOP, CTL_ROOT, -2);
-  ConnectNode(CTL_PAUSE, CTL_ROOT, -2);
-  ConnectNode(CTL_MOVE, CTL_ROOT, -2);
-  ConnectNode(CTL_DELETE, CTL_ROOT, -2);
-
-  ConnectNode(-1, CTL_STOP, -2);
-  ConnectNode(CTL_ROOT, CTL_STOP, -2);
-
-  ConnectNode(-1, CTL_PAUSE, -2);
-  ConnectNode(CTL_ROOT, CTL_PAUSE, -2);
-
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE, -2);
-
-  ConnectNode(CTL_MOVE_FORWARD_CHAR, CTL_MOVE_FORWARD, -2);
-  ConnectNode(CTL_MOVE_FORWARD_WORD, CTL_MOVE_FORWARD, -2);
-  ConnectNode(CTL_MOVE_FORWARD_LINE, CTL_MOVE_FORWARD, -2);
-  ConnectNode(CTL_MOVE_FORWARD_FILE, CTL_MOVE_FORWARD, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_FORWARD_CHAR, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_FORWARD_CHAR, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_FORWARD_CHAR, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_FORWARD_WORD, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_FORWARD_WORD, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_FORWARD_WORD, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_FORWARD_LINE, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_FORWARD_LINE, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_FORWARD_LINE, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_FORWARD_FILE, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_FORWARD_FILE, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_FORWARD_FILE, -2);
-
-  ConnectNode(CTL_MOVE_BACKWARD_CHAR, CTL_MOVE_BACKWARD, -2);
-  ConnectNode(CTL_MOVE_BACKWARD_WORD, CTL_MOVE_BACKWARD, -2);
-  ConnectNode(CTL_MOVE_BACKWARD_LINE, CTL_MOVE_BACKWARD, -2);
-  ConnectNode(CTL_MOVE_BACKWARD_FILE, CTL_MOVE_BACKWARD, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_BACKWARD_CHAR, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_BACKWARD_CHAR, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_BACKWARD_CHAR, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_BACKWARD_WORD, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_BACKWARD_WORD, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_BACKWARD_WORD, -2);
-
-  ConnectNode(CTL_ROOT, CTL_MOVE_BACKWARD_LINE, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_BACKWARD_LINE, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_BACKWARD_LINE, -2);
 
-  ConnectNode(CTL_ROOT, CTL_MOVE_BACKWARD_FILE, -2);
-  ConnectNode(CTL_MOVE_FORWARD, CTL_MOVE_BACKWARD_FILE, -2);
-  ConnectNode(CTL_MOVE_BACKWARD, CTL_MOVE_BACKWARD_FILE, -2);
-
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE, -2);
-
-  ConnectNode(CTL_DELETE_FORWARD_CHAR, CTL_DELETE_FORWARD, -2);
-  ConnectNode(CTL_DELETE_FORWARD_WORD, CTL_DELETE_FORWARD, -2);
-  ConnectNode(CTL_DELETE_FORWARD_LINE, CTL_DELETE_FORWARD, -2);
-  ConnectNode(CTL_DELETE_FORWARD_FILE, CTL_DELETE_FORWARD, -2);
-
-  ConnectNode(CTL_ROOT, CTL_DELETE_FORWARD_CHAR, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_FORWARD_CHAR, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_FORWARD_CHAR, -2);
+  void happen(CControlBase::CContNode *pNode) {
+    for (vector<Action*>::iterator it=actions.begin(); it!=actions.end(); it++)
+      (*it)->happen(pNode);
+  }
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_FORWARD_WORD, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_FORWARD_WORD, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_FORWARD_WORD, -2);
+  ~XMLNodeTemplate() {
+    for (vector<Action*>::iterator it=actions.begin(); it!=actions.end(); it++)
+      delete *it;
+  }
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_FORWARD_LINE, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_FORWARD_LINE, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_FORWARD_LINE, -2);
+  vector<CControlBase::Action*> actions;
+};
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_FORWARD_FILE, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_FORWARD_FILE, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_FORWARD_FILE, -2);
+bool CControlParser::LoadFile(const string &strFileName) {
+  ///Template used for all node defns read in from XML - just
+  /// execute a list of Actions.
+
+  class ParseHandler : public AbstractXMLParser {
+    typedef CControlBase::NodeTemplate NodeTemplate;
+  protected:
+    void XmlStartHandler(const XML_Char *name, const XML_Char **atts) {
+      vector<NodeTemplate *> &parent(nodeStack.empty() ? m_pMgr->m_vParsed : nodeStack.back()->successors);
+      if (strcmp(name,"node")==0) {
+        string label,nodeName; int color=-1;
+        while (*atts) {
+          if (strcmp(*atts,"name")==0) {
+            nodeName=*(atts+1);
+            DASHER_ASSERT(namedNodes.find(nodeName)==namedNodes.end());
+          } else if (strcmp(*atts,"label")==0) {
+            label = *(atts+1);
+          } else if (strcmp(*atts,"color")==0) {
+            color = atoi(*(atts+1));
+          }
+          atts+=2;
+        }
+        XMLNodeTemplate *n = new XMLNodeTemplate(label,color);
+        parent.push_back(n);
+        nodeStack.push_back(n);
+        if (nodeName!="")
+          namedNodes[nodeName]=n; //all refs resolved at end.
+      } else if (strcmp(name,"ref")==0) {
+        string target;
+        while (*atts) {
+          if (strcmp(*atts,"name")==0)
+            target=*(atts+1);
+          atts+=2;
+        }
+        map<string,NodeTemplate*>::iterator it=namedNodes.find(target);
+        if (it!=namedNodes.end())
+          parent.push_back(it->second);
+        else {
+          parent.push_back(NULL);
+          unresolvedRefs.push_back(pair<NodeTemplate**,string>(&(parent.back()),target));
+        }
+      } else if (strcmp(name,"alph")==0) {
+        parent.push_back(NULL);
+      } else if (NodeTemplate *n = m_pMgr->parseOther(name, atts)) {
+        parent.push_back(n);
+      } else if (CControlBase::Action *a=m_pMgr->parseAction(name, atts)) {
+        DASHER_ASSERT(!nodeStack.empty());
+        nodeStack.back()->actions.push_back(a);
+      }
+    }
 
-  ConnectNode(CTL_DELETE_BACKWARD_CHAR, CTL_DELETE_BACKWARD, -2);
-  ConnectNode(CTL_DELETE_BACKWARD_WORD, CTL_DELETE_BACKWARD, -2);
-  ConnectNode(CTL_DELETE_BACKWARD_LINE, CTL_DELETE_BACKWARD, -2);
-  ConnectNode(CTL_DELETE_BACKWARD_FILE, CTL_DELETE_BACKWARD, -2);
+    void XmlEndHandler(const XML_Char *szName) {
+      if (strcmp(szName,"node")==0) {
+        DASHER_ASSERT(!nodeStack.empty());
+        nodeStack.pop_back();
+      }
+    }
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_BACKWARD_CHAR, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_BACKWARD_CHAR, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_BACKWARD_CHAR, -2);
+  private:
+    ///Following only used in parsing...
+    map<string,NodeTemplate*> namedNodes;
+    vector<pair<NodeTemplate**,string> > unresolvedRefs;
+    vector<XMLNodeTemplate*> nodeStack;
+    CControlParser *m_pMgr;
+  public:
+    ParseHandler(CControlParser *pMgr) : m_pMgr(pMgr) {
+    }
+    void resolveRefs() {
+      //resolve any forward references to nodes declared later
+      for (vector<pair<NodeTemplate**,string> >::iterator it=unresolvedRefs.begin(); it!=unresolvedRefs.end(); it++) {
+        map<string,NodeTemplate*>::iterator target = namedNodes.find(it->second);
+        if (target != namedNodes.end())
+          *(it->first) = target->second;
+      }
+      //somehow, need to clear out any refs that weren't resolved...???
+    }
+  };
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_BACKWARD_WORD, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_BACKWARD_WORD, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_BACKWARD_WORD, -2);
+  ParseHandler p(this);
+  if (!p.ParseFile(strFileName)) return false;
+  p.resolveRefs();
+  return true;
+}
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_BACKWARD_LINE, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_BACKWARD_LINE, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_BACKWARD_LINE, -2);
+CControlManager::CControlManager(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface)
+: CDasherComponent(pEventHandler, pSettingsStore), CControlBase(pNCManager), m_pInterface(pInterface), m_pSpeech(NULL), m_pCopy(NULL) {
+  //TODO, used to be able to change label+colour of root/pause/stop from controllabels.xml
+  // (or, get the root node title "control" from the alphabet!)
+  SetRootTemplate(new NodeTemplate("Control",8)); //default NodeTemplate does nothing
+  GetRootTemplate()->successors.push_back(NULL);
+
+  m_pPause = new Pause("Pause",241);
+  m_pPause->successors.push_back(NULL);
+  m_pPause->successors.push_back(GetRootTemplate());
+  m_pStop = new MethodTemplate<CDasherInterfaceBase>("Stop", 242, pInterface, &CDasherInterfaceBase::Stop);
+  m_pStop->successors.push_back(NULL);
+  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 that fails, we'll have no editing functions. Fine -
+    // doesn't seem vital enough to hardcode a fallback as well!
+  }
 
-  ConnectNode(CTL_ROOT, CTL_DELETE_BACKWARD_FILE, -2);
-  ConnectNode(CTL_DELETE_FORWARD, CTL_DELETE_BACKWARD_FILE, -2);
-  ConnectNode(CTL_DELETE_BACKWARD, CTL_DELETE_BACKWARD_FILE, -2);
+  updateActions();
 }
 
-COrigNodes::~COrigNodes() {
-  for (std::map<int,NodeTemplate *>::iterator it = m_perId.begin(); it!=m_perId.end(); it++)
-    delete it->second;
+CControlManager::Pause::Pause(const string &strLabel, int iColour) : NodeTemplate(strLabel,iColour) {
 }
-
-void COrigNodes::RegisterNode( int iID, std::string strLabel, int iColour ) {
-  DASHER_ASSERT(m_perId.count(iID)==0);
-  if (iID == CTL_STOP) m_perId[iID] = new Stop(strLabel,iColour);
-  else if (iID == CTL_PAUSE) m_perId[iID] = new Pause(strLabel, iColour);
-  else m_perId[iID] = new EventBroadcast(iID,strLabel,iColour);
+void CControlManager::Pause::happen(CContNode *pNode) {
+  static_cast<CControlManager *>(pNode->mgr())->m_pNCManager->SetBoolParameter(BP_DASHER_PAUSED,true);
 }
 
-void COrigNodes::ConnectNode(int iChild, int iParent, int iAfter) {
-  //ACL duplicating old functionality here. Idea had been to do
-  // something with iAfter "(eventually -1 = start, -2 = end)", but
-  // since this wasn't used, and this is all legacy code anyway ;-),
-  // I'm leaving as is...
-
-  NodeTemplate *pParent(m_perId[iParent]);
-  if (pParent) //Note - old code only checked this if iChild==-1...?!
-    pParent->successors.push_back(iChild==-1 ? NULL : m_perId[iChild]);
+CControlBase::NodeTemplate *CControlManager::parseOther(const XML_Char *name, const XML_Char **atts) {
+  if (strcmp(name,"root")==0) return GetRootTemplate();
+  return CControlParser::parseOther(name, atts);
 }
 
-void COrigNodes::DisconnectNode(int iChild, int iParent) {
-  NodeTemplate *pChild(m_perId[iChild]), *pParent(m_perId[iParent]);
-  if (pParent && (pChild || iChild == -1)) {
-    for (vector<NodeTemplate *>::iterator it = pParent->successors.begin(); it!=pParent->successors.end(); it++) {
-      if (*it == pChild) {
-        pParent->successors.erase(it);
+CControlBase::Action *CControlManager::parseAction(const XML_Char *name, const XML_Char **atts) {
+  if (strcmp(name,"delete")==0 || strcmp(name,"move")==0) {
+    bool bForwards=true; //pick some defaults...
+    EditDistance dist=EDIT_WORD; // (?!)
+    while (*atts) {
+      if (strcmp(*atts,"forward")==0)
+        bForwards=(strcmp(*(atts+1),"yes")==0 || strcmp(*(atts+1),"true")==0 || strcmp(*(atts+1),"on")==0);
+      else if (strcmp(*atts,"dist")==0) {
+        if (strcmp(*(atts+1),"char")==0)
+          dist=EDIT_CHAR;
+        else if (strcmp(*(atts+1),"word")==0)
+          dist=EDIT_WORD;
+        else if (strcmp(*(atts+1),"line")==0)
+          dist=EDIT_LINE;
+        else if (strcmp(*(atts+1),"file")==0)
+          dist=EDIT_FILE;
       }
+      atts+=2;
     }
-  }
-}
-
-void COrigNodes::XmlStartHandler(void *pUserData, const XML_Char *szName, const XML_Char **aszAttr) {
-  COrigNodes *pMgr(static_cast<COrigNodes *>(pUserData));
-  int colour=-1;
-  string str;
-  if(0==strcmp(szName, "label"))
-  {
-    for(int i = 0; aszAttr[i]; i += 2)
-    {
-      if(0==strcmp(aszAttr[i],"value"))
-      {
-        str = string(aszAttr[i+1]);
-      }
-      if(0==strcmp(aszAttr[i],"color"))
-      {
-        colour = atoi(aszAttr[i+1]);
+    class DirDist {
+    protected:
+      const bool m_bForwards;
+      const EditDistance m_dist;
+    public:
+      DirDist(bool bForwards,EditDistance dist) : m_bForwards(bForwards), m_dist(dist) {
       }
+    };
+
+    if (name[0]=='d') {
+      class Delete : private DirDist, public Action {
+      public:
+        Delete(bool bForwards,EditDistance dist) : DirDist(bForwards,dist) {
+        }
+        void happen(CContNode *pNode) {
+          static_cast<CControlManager*>(pNode->mgr())->m_pInterface->ctrlDelete(m_bForwards,m_dist);
+        }
+      };
+      return new Delete(bForwards,dist);
+    } else {
+      class Move : private DirDist, public Action {
+      public:
+        Move(bool bForwards,EditDistance dist) : DirDist(bForwards, dist) {}
+        void happen(CContNode *pNode) {
+          static_cast<CControlManager*>(pNode->mgr())->m_pInterface->ctrlMove(m_bForwards,m_dist);
+        };
+      };
+      return new Move(bForwards, dist);
     }
-    pMgr->RegisterNode(pMgr->m_iNextID++, str, colour);
   }
-}
-
-void COrigNodes::XmlEndHandler(void *pUserData, const XML_Char *szName) {
-}
-
-void COrigNodes::XmlCDataHandler(void *pUserData, const XML_Char *szData, int iLength){
-}
-
-CControlManager::CControlManager(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface)
-: CDasherComponent(pEventHandler, pSettingsStore), COrigNodes(pNCManager, pInterface), m_pSpeech(NULL), m_pCopy(NULL) {
-
-  updateActions();
+  return CControlParser::parseAction(name, atts);
 }
 
 CControlManager::~CControlManager() {
@@ -402,9 +324,9 @@ CControlManager::~CControlManager() {
 class TextActionHeader : public CDasherInterfaceBase::TextAction, public CControlBase::NodeTemplate {
 public:
   TextActionHeader(CDasherInterfaceBase *pIntf, const string &strHdr, NodeTemplate *pRoot) : TextAction(pIntf), NodeTemplate(strHdr,-1),
-  m_all(this, "All", &CDasherInterfaceBase::TextAction::executeOnAll),
-  m_new(this, "New", &CDasherInterfaceBase::TextAction::executeOnNew),
-  m_again(this, "Repeat", &CDasherInterfaceBase::TextAction::executeLast) {
+  m_all("All", -1, this, &CDasherInterfaceBase::TextAction::executeOnAll),
+  m_new("New", -1, this, &CDasherInterfaceBase::TextAction::executeOnNew),
+  m_again("Repeat", -1, this, &CDasherInterfaceBase::TextAction::executeLast) {
     successors.push_back(&m_all); m_all.successors.push_back(NULL); m_all.successors.push_back(pRoot);
     successors.push_back(&m_new); m_new.successors.push_back(NULL); m_new.successors.push_back(pRoot);
     successors.push_back(&m_again); m_again.successors.push_back(NULL); m_again.successors.push_back(pRoot);
@@ -458,15 +380,15 @@ void CControlManager::updateActions() {
   // (either a dynamic filter where the user can't use the normal stop mechanism precisely,
   //  or a static filter but a 'stop' action is easier than using speak->all / copy->all then pause)
   if (m_pInterface->hasStopTriggers() && m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_HALT))
-    vRootSuccessors.push_back(m_perId[CTL_STOP]);
-  if (it!=vOldRootSuccessors.end() && *it == m_perId[CTL_STOP]) it++;
+    vRootSuccessors.push_back(m_pStop);
+  if (it!=vOldRootSuccessors.end() && *it == m_pStop) it++;
 
   //filter is pauseable, and either 'stop' would do something (so pause is different),
   // or we're told to have a stop node but it would be indistinguishable from pause (=>have pause)
   CInputFilter *pInput(static_cast<CInputFilter *>(m_pInterface->GetModuleByName(m_pInterface->GetStringParameter(SP_INPUT_FILTER))));
   if (pInput->supportsPause() && (m_pInterface->hasStopTriggers() || m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_HALT)))
-    vRootSuccessors.push_back(m_perId[CTL_PAUSE]);
-  if (it!=vOldRootSuccessors.end() && *it == m_perId[CTL_PAUSE]) it++;
+    vRootSuccessors.push_back(m_pPause);
+  if (it!=vOldRootSuccessors.end() && *it == m_pPause) it++;
 
   if (m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_SPEECH) && m_pInterface->SupportsSpeech()) {
     if (!m_pSpeech) m_pSpeech = new SpeechHeader(m_pInterface, GetRootTemplate());
@@ -481,11 +403,11 @@ void CControlManager::updateActions() {
   if (it!=vOldRootSuccessors.end() && *it == m_pCopy) it++;
 
   if (m_pInterface->GetBoolParameter(BP_CONTROL_MODE_HAS_EDIT)) {
-    vRootSuccessors.push_back(m_perId[CTL_MOVE]);
-    vRootSuccessors.push_back(m_perId[CTL_DELETE]);
+    for (vector<NodeTemplate *>::const_iterator it2=parsedNodes().begin(); it2!=parsedNodes().end(); it2++)
+      vRootSuccessors.push_back(*it2);
   }
-  if (it!=vOldRootSuccessors.end() && *it == m_perId[CTL_MOVE]) it++;
-  if (it!=vOldRootSuccessors.end() && *it == m_perId[CTL_DELETE]) it++;
+  for (vector<NodeTemplate *>::const_iterator it2=parsedNodes().begin(); it2!=parsedNodes().end(); it2++)
+    if (it!=vOldRootSuccessors.end() && *it == *it2) it++;
 
   //copy anything else (custom) that might have been added...
   while (it != vOldRootSuccessors.end()) vRootSuccessors.push_back(*it++);
diff --git a/Src/DasherCore/ControlManager.h b/Src/DasherCore/ControlManager.h
index 70db150..828f279 100644
--- a/Src/DasherCore/ControlManager.h
+++ b/Src/DasherCore/ControlManager.h
@@ -25,7 +25,6 @@
 #include "Event.h"
 #include "NodeManager.h"
 #include "NodeCreationManager.h"
-#include "DasherInterfaceBase.h"
 
 #include <vector>
 #include <map>
@@ -46,7 +45,7 @@ class CNodeCreationManager;
 
 namespace Dasher {
 
-  class CDasherModel;
+  class CDasherInterfaceBase;
 
   /// \ingroup Model
   /// @{
@@ -56,27 +55,8 @@ namespace Dasher {
   class CControlBase : public CNodeManager {
   public:
 
-    enum { CTL_ROOT, CTL_STOP, CTL_PAUSE, CTL_MOVE, CTL_MOVE_FORWARD,
-	   CTL_MOVE_FORWARD_CHAR, CTL_MOVE_FORWARD_WORD, CTL_MOVE_FORWARD_LINE,
-	   CTL_MOVE_FORWARD_FILE, CTL_MOVE_BACKWARD, CTL_MOVE_BACKWARD_CHAR,
-	   CTL_MOVE_BACKWARD_WORD, CTL_MOVE_BACKWARD_LINE, CTL_MOVE_BACKWARD_FILE,
-	   CTL_DELETE, CTL_DELETE_FORWARD,
-	   CTL_DELETE_FORWARD_CHAR, CTL_DELETE_FORWARD_WORD, CTL_DELETE_FORWARD_LINE,
-	   CTL_DELETE_FORWARD_FILE, CTL_DELETE_BACKWARD, CTL_DELETE_BACKWARD_CHAR,
-	   CTL_DELETE_BACKWARD_WORD, CTL_DELETE_BACKWARD_LINE, CTL_DELETE_BACKWARD_FILE,
-	   CTL_USER
-    };
-
     class NodeTemplate;
 
-  protected:
-    ///Sets the root - should be called by subclass constructor to make
-    /// superclass ready for use.
-    ///Note, may only be called once, and with a non-null pRoot, or will throw an error message.
-    void SetRootTemplate(NodeTemplate *pRoot);
-
-    CNodeCreationManager *m_pNCManager;
-
     class CContNode : public CDasherNode {
     public:
       CControlBase *mgr() {return m_pMgr;}
@@ -100,15 +80,17 @@ namespace Dasher {
       CControlBase *m_pMgr;
     };
 
-  public:
-    class NodeTemplate {
+    class Action {
+    public:
+      virtual void happen(CContNode *pNode) {}
+    };
+    class NodeTemplate : public Action {
     public:
       NodeTemplate(const std::string &strLabel, int iColour);
       virtual ~NodeTemplate() {}
       int colour() {return m_iColour;};
       const std::string &label() {return m_strLabel;};
       std::vector<NodeTemplate *> successors;
-      virtual void happen(CContNode *pNode) {}
     private:
       std::string m_strLabel;
       int m_iColour;
@@ -116,9 +98,9 @@ namespace Dasher {
 
     template <typename T> class MethodTemplate : public NodeTemplate {
     public:
-      ///pointer to a function "void X()", that is a member of a T...
+      ///A "Method" is pointer to a function "void X()", that is a member of a T...
       typedef void (T::*Method)();
-      MethodTemplate(T *pRecv, const std::string &strLabel, Method f) : NodeTemplate(strLabel,-1),m_pRecv(pRecv),m_f(f) {
+      MethodTemplate(const std::string &strLabel, int color, T *pRecv, Method f) : NodeTemplate(strLabel,color),m_pRecv(pRecv),m_f(f) {
       }
       virtual void happen(CContNode *pNode) {
         //invoke pointer-to-member-function m_f on object *m_pRecv!
@@ -129,14 +111,6 @@ namespace Dasher {
       Method m_f;
     };
 
-    class EventBroadcast : public NodeTemplate {
-    public:
-      EventBroadcast(int iEvent, const std::string &strLabel, int iColour);
-      virtual void happen(CContNode *pNode);
-    private:
-      const int m_iEvent;
-    };
-
     NodeTemplate *GetRootTemplate();
 
     CControlBase(CNodeCreationManager *pNCManager);
@@ -147,6 +121,14 @@ namespace Dasher {
 
     virtual CDasherNode *GetRoot(CDasherNode *pParent, unsigned int iLower, unsigned int iUpper, int iOffset);
 
+  protected:
+    ///Sets the root - should be called by subclass constructor to make
+    /// superclass ready for use.
+    ///Note, may only be called once, and with a non-null pRoot, or will throw an error message.
+    void SetRootTemplate(NodeTemplate *pRoot);
+
+    CNodeCreationManager *m_pNCManager;
+
   private:
     NodeTemplate *m_pRoot;
 
@@ -156,61 +138,56 @@ namespace Dasher {
 
   };
 
-  ///subclass attempts to recreate interface of previous control manager...
-  class COrigNodes : public CControlBase {
-  public:
-    COrigNodes(CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface);
-    ~COrigNodes();
-
-    //keep these around for now, as this might let Win32/Gtk2 work?
-    void RegisterNode( int iID, std::string strLabel, int iColour );
-    void ConnectNode(int iChild, int iParent, int iAfter);
-    void DisconnectNode(int iChild, int iParent);
+  ///Class reads node tree definitions from an XML file, linking together the NodeTemplates
+  /// according to defined names, nesting of <node/>s, and  <ref/>s. Also handles the
+  /// <alph/> tag, meaning one child of the node is to escape back to the alphabet. Subclasses
+  /// may override parseAction to provide actions for the nodes to perform, also parseOther
+  /// to link with NodeTemplates from other sources.
+  class CControlParser {
+  protected:
+    ///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 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);
+    /// \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").
+    ///The default implementation always returns NULL.
+    /// \return A node template, if the name was recognised; NULL if not recognised.
+    virtual CControlBase::NodeTemplate *parseOther(const XML_Char *name, const XML_Char **atts) {
+      return NULL;
+    }
+    ///Subclasses may override to parse actions within nodes.
+    ///The default implementation always returns NULL.
+    /// \return A (new) action pointer, if the name+attributes were successfully parsed; NULL if not recognised.
+    virtual CControlBase::Action *parseAction(const XML_Char *name, const XML_Char **atts) {
+      return NULL;
+    };
+    //TODO cleanup/deletion
+  private:
+    ///all top-level parsed nodes
+    vector<CControlBase::NodeTemplate *> m_vParsed;
+  };
 
+  ///subclass which we actually construct! Parses editing node definitions from a file,
+  /// then adds Pause and/or Stop, Speak, and Copy (to clipboard), all as children
+  /// of the "root" control node.
+  class CControlManager : public CDasherComponent, public CControlBase, public CControlParser {
+  public:
     class Pause : public NodeTemplate {
     public:
       Pause(const std::string &strLabel, int iColour);
       void happen(CContNode *pNode);
     };
-    class Stop : public NodeTemplate {
-    public:
-      Stop(const std::string &strLabel, int iColour);
-      void happen(CContNode *pNode);
-    };
-
-  private:
-    //For now, make all the loading routines private:
-    // they are called from the constructor in the same fashion as in old ControlManager.
-
-    // The possibility of loading labels/layouts from different files/formats
-    // remains, but is left to alternative subclasses of ControlBase.
-
-    ///Attempts to load control labels from specified file.
-    /// Returns true for success, false for failure (e.g. no such file!)
-    bool LoadLabelsFromFile(string strFileName);
-
-    ///Load a default set of labels. Return true for success, or false
-    /// if labels already loaded
-    bool LoadDefaultLabels();
-
-    void ConnectNodes();
-
-    static void XmlStartHandler(void *pUserData, const XML_Char *szName, const XML_Char **aszAttr);
-    static void XmlEndHandler(void *pUserData, const XML_Char *szName);
-    static void XmlCDataHandler(void *pUserData, const XML_Char *szData, int iLength);
-
-    int m_iNextID;
-  protected:
-    std::map<int,NodeTemplate *> m_perId;
-    CDasherInterfaceBase *m_pInterface;
-  };
-
-  ///subclass which we actually construct...
-  class CControlManager : public CDasherComponent, public COrigNodes {
-  public:
     CControlManager(CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CNodeCreationManager *pNCManager, CDasherInterfaceBase *pInterface);
     void HandleEvent(CEvent *pEvent);
 
+    typedef enum {
+      EDIT_CHAR, EDIT_WORD, EDIT_LINE, EDIT_FILE
+    } EditDistance;
+
     ///Recomputes which of pause, stop, speak and copy the root control node should have amongst its children.
     /// Automatically called whenever copy-on-stop/speak-on-stop or input filter changes;
     /// subclasses of CDasherInterfaceBase should also call this if
@@ -220,7 +197,15 @@ namespace Dasher {
     void updateActions();
     ~CControlManager();
 
+  protected:
+    ///Override to allow a <root/> tag to include a fresh control root
+    NodeTemplate *parseOther(const XML_Char *name, const XML_Char **atts);
+    ///Override to recognise <move/> and <delete/> tags as actions.
+    Action *parseAction(const XML_Char *name, const XML_Char **atts);
+
   private:
+    NodeTemplate *m_pPause, *m_pStop;
+    CDasherInterfaceBase *m_pInterface;
     ///group headers, with three children each (all/new/repeat)
     NodeTemplate *m_pSpeech, *m_pCopy;
   };
diff --git a/Src/DasherCore/DasherInterfaceBase.h b/Src/DasherCore/DasherInterfaceBase.h
index 1898726..029bab6 100644
--- a/Src/DasherCore/DasherInterfaceBase.h
+++ b/Src/DasherCore/DasherInterfaceBase.h
@@ -39,7 +39,7 @@
 #include "ColourIO.h"
 #include "InputFilter.h"
 #include "ModuleManager.h"
-
+#include "ControlManager.h"
 #include <set>
 #include <algorithm>
 
@@ -217,6 +217,21 @@ public:
   /// the specified text to the clipboard. (Default implementation does nothing).
   virtual void CopyToClipboard(const std::string &text) {}
 
+  ///Called to execute a control-mode "move" command.
+  ///\param bForwards true to move forwards (right), false for backwards
+  ///\param dist how far to move: character, word, line, file. (Usually defined
+  /// by OS, e.g. for non-european languages)
+  ///\return the offset, into the edit buffer of the cursor *after* the move.
+  virtual unsigned int ctrlMove(bool bForwards, CControlManager::EditDistance dist)=0;
+ 
+  ///Called to execute a control-mode "delete" command.
+  ///\param bForwards true to delete forwards (right), false for backwards
+  ///\param dist how much to delete: character, word, line, file. (Usually defined
+  /// by OS, e.g. for non-european languages)
+  ///\return the offset, into the edit buffer, of the cursor *after* the delete
+  /// (for forwards deletion, this will be the same as the offset *before*)
+  virtual unsigned int ctrlDelete(bool bForwards, CControlManager::EditDistance dist)=0;
+ 
   class TextAction {
   public:
     TextAction(CDasherInterfaceBase *pMgr);
diff --git a/Src/DasherCore/Event.h b/Src/DasherCore/Event.h
index a65fa5c..d15758e 100644
--- a/Src/DasherCore/Event.h
+++ b/Src/DasherCore/Event.h
@@ -10,14 +10,13 @@ namespace Dasher {
   class CEvent;
   class CParameterNotificationEvent;
   class CEditEvent;
-  class CControlEvent;
   class CLockEvent;
   class CMessageEvent;
   class CScreenGeomEvent;
 }
 
 enum {
-  EV_PARAM_NOTIFY = 1, EV_EDIT, EV_CONTROL, EV_LOCK, EV_MESSAGE, EV_SCREEN_GEOM
+  EV_PARAM_NOTIFY = 1, EV_EDIT, EV_LOCK, EV_MESSAGE, EV_SCREEN_GEOM
 };
 
 /// \ingroup Core
@@ -51,14 +50,6 @@ public:
   const int m_iOffset;
 };
 
-class Dasher::CControlEvent:public Dasher::CEvent {
-public:
-  CControlEvent(int iID) : CEvent(EV_CONTROL), m_iID(iID) {
-  };
-  
-  const int m_iID;
-};
-
 class Dasher::CLockEvent : public Dasher::CEvent {
 public:
   CLockEvent(const std::string &strMessage, bool bLock, int iPercent) : CEvent(EV_LOCK) {
diff --git a/Src/Gtk2/DasherControl.cpp b/Src/Gtk2/DasherControl.cpp
index 6fe31da..59bad50 100644
--- a/Src/Gtk2/DasherControl.cpp
+++ b/Src/Gtk2/DasherControl.cpp
@@ -362,10 +362,6 @@ void CDasherControl::ExternalEventHandler(Dasher::CEvent *pEvent) {
       g_signal_emit_by_name(GTK_WIDGET(m_pDasherControl), "dasher_edit_protect");
     }
   }
-  else if(pEvent->m_iEventType == EV_CONTROL) {
-    CControlEvent *pControlEvent(static_cast < CControlEvent * >(pEvent));
-    g_signal_emit_by_name(GTK_WIDGET(m_pDasherControl), "dasher_control", pControlEvent->m_iID);
-  }
   else if(pEvent->m_iEventType == EV_LOCK) {
     CLockEvent *pLockEvent(static_cast<CLockEvent *>(pEvent));
     DasherLockInfo sInfo;
@@ -386,6 +382,14 @@ void CDasherControl::ExternalEventHandler(Dasher::CEvent *pEvent) {
   }
 };
 
+unsigned int CDasherControl::ctrlMove(bool bForwards, CControlManager::EditDistance dist) {
+  return gtk_dasher_control_ctrl_move(m_pDasherControl,bForwards,dist);
+}
+
+unsigned int CDasherControl::ctrlDelete(bool bForwards, CControlManager::EditDistance dist) {
+  return gtk_dasher_control_ctrl_delete(m_pDasherControl,bForwards,dist);
+}
+
 void CDasherControl::ExecuteCommand(const std::string &strCommand) {
     g_signal_emit_by_name(GTK_WIDGET(m_pDasherControl), "dasher_command", strCommand.c_str());
 }
diff --git a/Src/Gtk2/DasherControl.h b/Src/Gtk2/DasherControl.h
index 98573dc..07fcb88 100644
--- a/Src/Gtk2/DasherControl.h
+++ b/Src/Gtk2/DasherControl.h
@@ -11,7 +11,7 @@
 
 #ifdef JOYSTICK
 #include "joystick_input.h"
-#endif 
+#endif
 
 #ifdef TILT
 #include "tilt_input.h"
@@ -46,7 +46,7 @@ public:
   /// \param pVBox GTK VBox to populate with the DasherControl
   /// component widgets. This needs to be created externally by the
   /// GObject wrapper and passed to the C++ class rather than being
-  /// created internally.  
+  /// created internally.
   /// \param pDasherControl Pointer to the GObject wrapper. This is
   /// needed so that we can emit signals from the GObject.
   ///
@@ -106,11 +106,11 @@ public:
   ///
 
   void CanvasDestroyEvent();
-  
+
   ///
   /// Key press event on the canvas
   ///
-  
+
   gint KeyReleaseEvent(GdkEventKey * event);
   gint KeyPressEvent(GdkEventKey * event);
 
@@ -129,20 +129,22 @@ public:
 
   void ExternalKeyDown(int iKeyVal);
   void ExternalKeyUp(int iKeyVal);
-  
+
   gboolean ExposeEvent();
 
   ///Override to broadcast signal...
   virtual void Stop();
- 
+
   virtual void WriteTrainFile(const std::string &filename, const std::string &strNewText);
- 
+
   virtual std::string GetAllContext();
   std::string GetContext(unsigned int iStart, unsigned int iLength);
 
   virtual bool SupportsClipboard();
   virtual void CopyToClipboard(const std::string &strText);
 
+  unsigned int ctrlMove(bool bForwards, CControlManager::EditDistance dist);
+  unsigned int ctrlDelete(bool bForwards, CControlManager::EditDistance dist);
 #ifdef WITH_SPEECH
   ///override default non-implementation if compiling with speech...
   virtual bool SupportsSpeech();
@@ -166,7 +168,7 @@ private:
   virtual void StartTimer();
   virtual void ShutdownTimer();
 
-  
+
   /// Override to emit Gtk2 signals (previously in response to CCommandEvent)
   void ExecuteCommand(const std::string &strName);
 
diff --git a/Src/Gtk2/GtkDasherControl.cpp b/Src/Gtk2/GtkDasherControl.cpp
index 9e440f7..f338bd6 100644
--- a/Src/Gtk2/GtkDasherControl.cpp
+++ b/Src/Gtk2/GtkDasherControl.cpp
@@ -225,6 +225,16 @@ gtk_dasher_control_set_offset(GtkDasherControl *pControl, int iOffset) {
   pPrivate->pControl->SetOffset(iOffset);
 }
 
+gint gtk_dasher_control_ctrl_move(GtkDasherControl *pControl, bool bForwards, CControlManager::EditDistance dist) {
+  GtkDasherControlPrivate *pPrivate = GTK_DASHER_CONTROL_GET_PRIVATE(pControl);
+  return dasher_editor_ctrl_move(pPrivate->pEditor, bForwards, dist);
+}
+
+gint gtk_dasher_control_ctrl_delete(GtkDasherControl *pControl, bool bForwards, CControlManager::EditDistance dist) {
+  GtkDasherControlPrivate *pPrivate = GTK_DASHER_CONTROL_GET_PRIVATE(pControl);
+  return dasher_editor_ctrl_delete(pPrivate->pEditor, bForwards, dist);
+}
+
 void 
 gtk_dasher_control_external_key_down(GtkDasherControl *pControl, int iKeyVal) {
   GtkDasherControlPrivate *pPrivate = GTK_DASHER_CONTROL_GET_PRIVATE(pControl);
diff --git a/Src/Gtk2/GtkDasherControl.h b/Src/Gtk2/GtkDasherControl.h
index 06541f5..5968a20 100644
--- a/Src/Gtk2/GtkDasherControl.h
+++ b/Src/Gtk2/GtkDasherControl.h
@@ -5,7 +5,7 @@
 
 #include "../Common/ModuleSettings.h"
 #include "Parameters.h"
-#include "ControlManager.h"
+#include "../DasherCore/ControlManager.h"
 
 /*Forward declaration*/
 typedef struct _DasherEditor DasherEditor;
@@ -49,6 +49,7 @@ struct _GtkDasherControlClass {
 
   void (*dasher_changed) (GtkDasherControl * pDasherControl, gint iParameter, gpointer data);
   void (*dasher_stop) (GtkDasherControl * pDasherControl, gpointer data);
+  //These are for ordinary node entry & deletion upon exit
   void (*dasher_edit_insert) (GtkDasherControl * pDasherControl, const gchar * szText, int iOffset, gpointer data);
   void (*dasher_edit_delete) (GtkDasherControl * pDasherControl, const gchar * szText, int iOffset, gpointer data);
   void (*dasher_edit_convert) (GtkDasherControl * pDasherControl, gpointer data);
@@ -83,6 +84,8 @@ const gchar* gtk_dasher_control_get_context(GtkDasherControl *pControl, unsigned
 //void gtk_dasher_control_invalidate_context(GtkDasherControl *pControl, bool bForceStart);
 void gtk_dasher_control_set_buffer(GtkDasherControl *pControl, int iOffset);
 void gtk_dasher_control_set_offset(GtkDasherControl *pControl, int iOffset);
+gint gtk_dasher_control_ctrl_move(GtkDasherControl *pControl, bool bForwards, Dasher::CControlManager::EditDistance dist);
+gint gtk_dasher_control_ctrl_delete(GtkDasherControl *pControl, bool bForwards, Dasher::CControlManager::EditDistance dist);
 void gtk_dasher_control_external_key_down(GtkDasherControl *pControl, int iKeyVal);
 void gtk_dasher_control_external_key_up(GtkDasherControl *pControl, int iKeyVal);
 gboolean gtk_dasher_control_get_module_settings(GtkDasherControl * pControl, const gchar *szModule, SModuleSettings **pSettings, gint *iCount);
diff --git a/Src/Gtk2/dasher_editor.cpp b/Src/Gtk2/dasher_editor.cpp
index fbdf20a..53d8db3 100644
--- a/Src/Gtk2/dasher_editor.cpp
+++ b/Src/Gtk2/dasher_editor.cpp
@@ -15,7 +15,7 @@
 // GNU General Public License for more details.
 //
 // You should have received a copy of the GNU General Public License
-// along with Dasher; if not, write to the Free Software 
+// along with Dasher; if not, write to the Free Software
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 #ifdef HAVE_CONFIG_H
@@ -104,7 +104,7 @@ G_DEFINE_TYPE(DasherEditor, dasher_editor, GTK_TYPE_VBOX);
 
 static void dasher_editor_finalize(GObject *pObject);
 
-static void 
+static void
 dasher_editor_class_init(DasherEditorClass *pClass) {
   g_type_class_add_private(pClass, sizeof(DasherEditorPrivate));
 
@@ -112,22 +112,22 @@ dasher_editor_class_init(DasherEditorClass *pClass) {
   pObjectClass->finalize = dasher_editor_finalize;
 
   /* Setup signals */
-  dasher_editor_signals[FILENAME_CHANGED] = g_signal_new("filename-changed", G_TYPE_FROM_CLASS(pClass), 
-							 static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), 
-							 G_STRUCT_OFFSET(DasherEditorClass, filename_changed), 
-							 NULL, NULL, g_cclosure_marshal_VOID__VOID, 
+  dasher_editor_signals[FILENAME_CHANGED] = g_signal_new("filename-changed", G_TYPE_FROM_CLASS(pClass),
+							 static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+							 G_STRUCT_OFFSET(DasherEditorClass, filename_changed),
+							 NULL, NULL, g_cclosure_marshal_VOID__VOID,
 							 G_TYPE_NONE, 0);
 
-  dasher_editor_signals[BUFFER_CHANGED] = g_signal_new("buffer-changed", G_TYPE_FROM_CLASS(pClass), 
-						       static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), 
-						       G_STRUCT_OFFSET(DasherEditorClass, buffer_changed), 
-						       NULL, NULL, g_cclosure_marshal_VOID__VOID, 
+  dasher_editor_signals[BUFFER_CHANGED] = g_signal_new("buffer-changed", G_TYPE_FROM_CLASS(pClass),
+						       static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+						       G_STRUCT_OFFSET(DasherEditorClass, buffer_changed),
+						       NULL, NULL, g_cclosure_marshal_VOID__VOID,
 						       G_TYPE_NONE, 0);
 
-  dasher_editor_signals[CONTEXT_CHANGED] = g_signal_new("context-changed", G_TYPE_FROM_CLASS(pClass), 
-							static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), 
-							G_STRUCT_OFFSET(DasherEditorClass, context_changed), 
-							NULL, NULL, g_cclosure_marshal_VOID__VOID, 
+  dasher_editor_signals[CONTEXT_CHANGED] = g_signal_new("context-changed", G_TYPE_FROM_CLASS(pClass),
+							static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+							G_STRUCT_OFFSET(DasherEditorClass, context_changed),
+							NULL, NULL, g_cclosure_marshal_VOID__VOID,
 							G_TYPE_NONE, 0);
 
   pClass->initialise = NULL;
@@ -149,14 +149,13 @@ dasher_editor_class_init(DasherEditorClass *pClass) {
   pClass->end_compose = NULL;
   pClass->get_context = NULL;
   pClass->get_offset = NULL;
-  pClass->edit_move = NULL;
-  pClass->edit_delete = NULL;
+  pClass->ctrl_move = NULL;
+  pClass->ctrl_delete = NULL;
   pClass->edit_convert = NULL;
   pClass->edit_protect = NULL;
   pClass->handle_parameter_change = NULL;
   pClass->handle_stop = NULL;
   pClass->handle_start = NULL;
-  pClass->handle_control = NULL;
   pClass->grab_focus = NULL;
   pClass->file_changed = NULL;
   pClass->get_filename = NULL;
@@ -166,7 +165,7 @@ dasher_editor_class_init(DasherEditorClass *pClass) {
   pClass->context_changed = NULL;
 }
 
-static void 
+static void
 dasher_editor_init(DasherEditor *pDasherControl) {
   DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pDasherControl);
 
@@ -181,7 +180,7 @@ dasher_editor_init(DasherEditor *pDasherControl) {
   pPrivate->bFileModified = FALSE;
 }
 
-static void 
+static void
 dasher_editor_finalize(GObject *pObject) {
   g_debug("Finalising DasherEditor");
 
@@ -192,7 +191,7 @@ dasher_editor_finalize(GObject *pObject) {
 
   if(pCurrentAction) {
     bool bStarted = false;
-    
+   
     while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
       bStarted = true;
       dasher_action_deactivate(pCurrentAction->pAction);
@@ -212,39 +211,32 @@ dasher_editor_initialise(DasherEditor *pSelf, DasherAppSettings *pAppSettings, D
     DASHER_EDITOR_GET_CLASS(pSelf)->initialise(pSelf, pAppSettings, pDasherMain, pXML, szFullPath);
 }
 
-void 
+void
 dasher_editor_handle_stop(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->handle_stop)
     DASHER_EDITOR_GET_CLASS(pSelf)->handle_stop(pSelf);
 }
 
-void 
+void
 dasher_editor_handle_start(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->handle_start)
     DASHER_EDITOR_GET_CLASS(pSelf)->handle_start(pSelf);
 }
 
-/* TODO: This is obsolete - sort this out when commands are reconsidered */
-void 
-dasher_editor_handle_control(DasherEditor *pSelf, int iNodeID) {
-  if(DASHER_EDITOR_GET_CLASS(pSelf)->handle_control)
-    DASHER_EDITOR_GET_CLASS(pSelf)->handle_control(pSelf, iNodeID);
-}
-
 #ifdef XXXPRLWACTIONSAREFIXED
-void 
+void
 dasher_editor_action_button(DasherEditor *pSelf, DasherAction *pAction) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->action_button)
     DASHER_EDITOR_GET_CLASS(pSelf)->action_button(pSelf, pAction);
 }
 
-void 
+void
 dasher_editor_actions_start(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->actions_start)
     DASHER_EDITOR_GET_CLASS(pSelf)->actions_start(pSelf);
 }
 
-bool 
+bool
 dasher_editor_actions_more(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->actions_more)
     return DASHER_EDITOR_GET_CLASS(pSelf)->actions_more(pSelf);
@@ -252,56 +244,56 @@ dasher_editor_actions_more(DasherEditor *pSelf) {
     return false;
 }
 
-void 
+void
 dasher_editor_actions_get_next(DasherEditor *pSelf, const gchar **szName, gint *iID, gboolean *bShow, gboolean *bControl, gboolean *bAuto) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->actions_get_next)
     DASHER_EDITOR_GET_CLASS(pSelf)->actions_get_next(pSelf, szName, iID, bShow, bControl, bAuto);
 }
 
-void 
+void
 dasher_editor_action_set_show(DasherEditor *pSelf, int iActionID, bool bValue) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->action_set_show)
     DASHER_EDITOR_GET_CLASS(pSelf)->action_set_show(pSelf, iActionID, bValue);
 }
 
-void 
+void
 dasher_editor_action_set_control(DasherEditor *pSelf, int iActionID, bool bValue) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->action_set_control)
     DASHER_EDITOR_GET_CLASS(pSelf)->action_set_control(pSelf, iActionID, bValue);
 }
 
-void 
-dasher_editor_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) { 
+void
+dasher_editor_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->action_set_auto)
     DASHER_EDITOR_GET_CLASS(pSelf)->action_set_auto(pSelf, iActionID, bValue);
 }
 #endif
 
-void 
+void
 dasher_editor_grab_focus(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->grab_focus)
     DASHER_EDITOR_GET_CLASS(pSelf)->grab_focus(pSelf);
 }
 
-void 
+void
 dasher_editor_output(DasherEditor *pSelf, const gchar *szText, int iOffset) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->output)
     DASHER_EDITOR_GET_CLASS(pSelf)->output(pSelf, szText, iOffset);
 }
 
-void 
+void
 dasher_editor_delete(DasherEditor *pSelf, int iLength, int iOffset) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->delete_text)
     DASHER_EDITOR_GET_CLASS(pSelf)->delete_text(pSelf, iLength, iOffset);
 }
 
-void 
+void
 dasher_editor_start_compose(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->start_compose)
     DASHER_EDITOR_GET_CLASS(pSelf)->start_compose(pSelf);
 }
 
-void 
+void
 dasher_editor_end_compose(DasherEditor *pSelf, bool bKeep) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->end_compose)
     DASHER_EDITOR_GET_CLASS(pSelf)->end_compose(pSelf, bKeep);
@@ -316,22 +308,26 @@ dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength) {
     return NULL;
 }
 
-gint 
+gint
 dasher_editor_get_offset(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->get_offset)
     return DASHER_EDITOR_GET_CLASS(pSelf)->get_offset(pSelf);
   else
     return 0;
 }
-
-void dasher_editor_edit_move(DasherEditor *pSelf, gint iDirection, gint iDist) {
- if(DASHER_EDITOR_GET_CLASS(pSelf)->edit_move)
-    DASHER_EDITOR_GET_CLASS(pSelf)->edit_move(pSelf,iDirection,iDist);
+using Dasher::CControlManager;
+gint dasher_editor_ctrl_move(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist) {
+  if(DASHER_EDITOR_GET_CLASS(pSelf)->ctrl_move)
+    return DASHER_EDITOR_GET_CLASS(pSelf)->ctrl_move(pSelf,bForwards,iDist);
+  else
+    return dasher_editor_get_offset(pSelf);
 }
 
-void dasher_editor_edit_delete(DasherEditor *pSelf, gint iDirection, gint iDist) {
- if(DASHER_EDITOR_GET_CLASS(pSelf)->edit_delete)
-    DASHER_EDITOR_GET_CLASS(pSelf)->edit_delete(pSelf, iDirection, iDist);
+gint dasher_editor_ctrl_delete(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist) {
+  if(DASHER_EDITOR_GET_CLASS(pSelf)->ctrl_delete)
+    return DASHER_EDITOR_GET_CLASS(pSelf)->ctrl_delete(pSelf, bForwards, iDist);
+  else
+    return dasher_editor_get_offset(pSelf);
 }
 
 void dasher_editor_edit_convert(DasherEditor *pSelf) {
@@ -344,7 +340,7 @@ void dasher_editor_edit_protect(DasherEditor *pSelf) {
     DASHER_EDITOR_GET_CLASS(pSelf)->edit_protect(pSelf);
 }
 
-gboolean 
+gboolean
 dasher_editor_command(DasherEditor *pSelf, const gchar *szCommand) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->command)
     return DASHER_EDITOR_GET_CLASS(pSelf)->command(pSelf, szCommand);
@@ -352,7 +348,7 @@ dasher_editor_command(DasherEditor *pSelf, const gchar *szCommand) {
     return false;
 }
 
-gboolean 
+gboolean
 dasher_editor_file_changed(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->file_changed)
     return DASHER_EDITOR_GET_CLASS(pSelf)->file_changed(pSelf);
@@ -369,7 +365,7 @@ dasher_editor_get_filename(DasherEditor *pSelf) {
 }
 
 const gchar *
-dasher_editor_get_all_text(DasherEditor *pSelf) { 
+dasher_editor_get_all_text(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->get_all_text)
     return DASHER_EDITOR_GET_CLASS(pSelf)->get_all_text(pSelf);
   else
@@ -377,14 +373,14 @@ dasher_editor_get_all_text(DasherEditor *pSelf) {
 }
 
 const gchar *
-dasher_editor_get_new_text(DasherEditor *pSelf) { 
+dasher_editor_get_new_text(DasherEditor *pSelf) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->get_new_text)
     return DASHER_EDITOR_GET_CLASS(pSelf)->get_new_text(pSelf);
   else
     return NULL;
 }
 
-void 
+void
 dasher_editor_handle_parameter_change(DasherEditor *pSelf, gint iParameter) {
   if(DASHER_EDITOR_GET_CLASS(pSelf)->handle_parameter_change)
     DASHER_EDITOR_GET_CLASS(pSelf)->handle_parameter_change(pSelf, iParameter);
diff --git a/Src/Gtk2/dasher_editor.h b/Src/Gtk2/dasher_editor.h
index 6d8fdca..373628a 100644
--- a/Src/Gtk2/dasher_editor.h
+++ b/Src/Gtk2/dasher_editor.h
@@ -8,6 +8,7 @@
 #ifdef XXXPRLWACTIONSAREFIXED
 #include "dasher_action.h"
 #endif
+#include "../DasherCore/ControlManager.h"
 
 /* Forward declaration */
 typedef struct _DasherMain DasherMain;
@@ -15,19 +16,6 @@ struct _DasherMain;
 typedef struct _DasherAppSettings DasherAppSettings;
 struct _DasherAppSettings;
 
-// Basic cursor movement commands
-enum {
-  EDIT_FORWARDS, 
-  EDIT_BACKWARDS
-};
-  
-enum {
-  EDIT_CHAR,
-  EDIT_WORD,
-  EDIT_LINE,
-  EDIT_FILE
-};
-
 G_BEGIN_DECLS
 #define DASHER_TYPE_EDITOR            (dasher_editor_get_type())
 #define DASHER_EDITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), DASHER_TYPE_EDITOR, DasherEditor ))
@@ -69,7 +57,6 @@ struct _DasherEditorClass {
   void (*handle_parameter_change)(DasherEditor *, gint);
   void (*handle_start)(DasherEditor *);
   void (*handle_stop)(DasherEditor *);
-  void (*handle_control)(DasherEditor *, int);
   void (*grab_focus)(DasherEditor *);
   gboolean (*file_changed)(DasherEditor *);
   const gchar *(*get_filename)(DasherEditor *);
@@ -83,8 +70,8 @@ struct _DasherEditorClass {
   //void (*insert_text)(DasherEditor *pSelf, const gchar *szText, int iOffset);// = output
   //void (*delete_text)(DasherEditor *pSelf, gint iLength, int iOffset);//duplicate
   //gchar *(*get_context)(DasherEditor *pSelf, gint iOffset, gint iLength);//duplicate
-  void (*edit_move)(DasherEditor *pSelf, gint iDirection, gint iDist);
-  void (*edit_delete)(DasherEditor *pSelf, gint iDirection, gint iDist);
+  gint (*ctrl_move)(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance iDist);
+  gint (*ctrl_delete)(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance iDist);
   void (*edit_convert)(DasherEditor *pSelf);
   void (*edit_protect)(DasherEditor *pSelf);
   //gint (*get_offset)(DasherEditor *pSelf);//duplicate
@@ -95,8 +82,8 @@ GType dasher_editor_get_type();
 
 /* Functions for initialisation and takedown */
 void dasher_editor_initialise(DasherEditor *pSelf,
-                              DasherAppSettings *pAppSettings, 
-                              DasherMain *pDasherMain, GtkBuilder *pXML, 
+                              DasherAppSettings *pAppSettings,
+                              DasherMain *pDasherMain, GtkBuilder *pXML,
                               const gchar *szFullPath);
 
 /* Abstract command handler */
@@ -107,8 +94,8 @@ gboolean dasher_editor_command(DasherEditor *pSelf, const gchar *szCommand);
 void dasher_editor_action_button(DasherEditor *pSelf, DasherAction *pAction);
 void dasher_editor_actions_start(DasherEditor *pSelf);
 bool dasher_editor_actions_more(DasherEditor *pSelf);
-void dasher_editor_actions_get_next(DasherEditor *pSelf, const gchar **szName, 
-				    gint *iID, gboolean *bShow, gboolean *bControl, 
+void dasher_editor_actions_get_next(DasherEditor *pSelf, const gchar **szName,
+				    gint *iID, gboolean *bShow, gboolean *bControl,
 				    gboolean *bAuto);
 void dasher_editor_action_set_show(DasherEditor *pSelf, int iActionID, bool bValue);
 void dasher_editor_action_set_control(DasherEditor *pSelf, int iActionID, bool bValue);
@@ -128,8 +115,8 @@ void dasher_editor_end_compose(DasherEditor *pSelf, bool bKeep);
 const gchar *dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength);
 gint dasher_editor_get_offset(DasherEditor *pSelf);
 
-void dasher_editor_edit_move(DasherEditor *pSelf, gint iDirection, gint iDist);
-void dasher_editor_edit_delete(DasherEditor *pSelf, gint iDirection, gint iDist);
+gint dasher_editor_ctrl_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance dist);
+gint dasher_editor_ctrl_delete(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance dist);
 void dasher_editor_edit_convert(DasherEditor *pSelf);
 void dasher_editor_edit_protect(DasherEditor *pSelf);
 
@@ -137,7 +124,6 @@ void dasher_editor_edit_protect(DasherEditor *pSelf);
 void dasher_editor_handle_parameter_change(DasherEditor *pSelf, gint iParameter);
 void dasher_editor_handle_stop(DasherEditor *pSelf);
 void dasher_editor_handle_start(DasherEditor *pSelf);
-void dasher_editor_handle_control(DasherEditor *pSelf, int iNodeID);
 
 /* Functions needed to maintain application UI */
 void dasher_editor_grab_focus(DasherEditor *pSelf);
diff --git a/Src/Gtk2/dasher_editor_external.cpp b/Src/Gtk2/dasher_editor_external.cpp
index d41be96..8d1a103 100644
--- a/Src/Gtk2/dasher_editor_external.cpp
+++ b/Src/Gtk2/dasher_editor_external.cpp
@@ -83,16 +83,14 @@ static void dasher_editor_external_output(DasherEditor *pSelf, const gchar *szTe
 static void dasher_editor_external_delete(DasherEditor *pSelf, int iLength, int iOffset);
 static const gchar *dasher_editor_external_get_context(DasherEditor *pSelf, int iOffset, int iLength);
 static gint dasher_editor_external_get_offset(DasherEditor *pSelf);
-static void dasher_editor_external_handle_control(DasherEditor *pSelf, int iNodeID);
 static void dasher_editor_external_grab_focus(DasherEditor *pSelf);
 static const gchar *dasher_editor_external_get_all_text(DasherEditor *pSelf);
 static const gchar *dasher_editor_external_get_new_text(DasherEditor *pSelf);
 //from dasher_external_buffer:
-static void dasher_editor_external_edit_convert(DasherEditor *pSelf);
-static void dasher_editor_external_edit_protect(DasherEditor *pSelf);
-static void dasher_editor_external_edit_move(DasherEditor *pSelf, int iDirection, int iDist);
-static void dasher_editor_external_edit_delete(DasherEditor *pSelf, int iDirection, int iDist);
-
+//These were no-ops, so removing.
+//static void dasher_editor_external_edit_convert(DasherEditor *pSelf);
+//static void dasher_editor_external_edit_protect(DasherEditor *pSelf);
+//TODO: Implement ctrl_move/ctrl_delete (ATM uses dasher_editor default i.e. returns get_offset)
 #ifdef GNOME_A11Y
 void dasher_editor_external_handle_focus(DasherEditorExternal *pSelf, const AccessibleEvent *pEvent);
 void dasher_editor_external_handle_caret(DasherEditorExternal *pSelf, const AccessibleEvent *pEvent);
@@ -123,14 +121,9 @@ dasher_editor_external_class_init(DasherEditorExternalClass *pClass) {
   pParentClass->delete_text = dasher_editor_external_delete;
   pParentClass->get_context = dasher_editor_external_get_context;
   pParentClass->get_offset = dasher_editor_external_get_offset;
-  pParentClass->handle_control = dasher_editor_external_handle_control;
   pParentClass->grab_focus = dasher_editor_external_grab_focus;
   pParentClass->get_all_text = dasher_editor_external_get_all_text;
   pParentClass->get_new_text = dasher_editor_external_get_new_text;
-  pParentClass->edit_move = dasher_editor_external_edit_move;
-  pParentClass->edit_delete = dasher_editor_external_edit_delete;
-  pParentClass->edit_convert = dasher_editor_external_edit_convert;
-  pParentClass->edit_protect = dasher_editor_external_edit_protect;
 }
 
 static void 
@@ -179,50 +172,6 @@ dasher_editor_external_initialise(DasherEditor *pSelf, DasherAppSettings *pAppSe
   dasher_editor_external_create_buffer(pSelf);
 }
 
-/* TODO: This is obsolete - sort this out when commands are reconsidered */
-static void 
-dasher_editor_external_handle_control(DasherEditor *pSelf, int iNodeID) {
-  DasherEditorExternalPrivate *pPrivate = DASHER_EDITOR_EXTERNAL_GET_PRIVATE(pSelf);
-
-  // TODO: Think about changing signals so we don't need to do this translation
-
-  struct SControlMap {
-    int iEvent;
-    int iDir;
-    int iDist;
-    bool bDelete;
-  };
-
-  static struct SControlMap sMap[] = {
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, false},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, true}
-  };    
-
-  for(unsigned int i(0); i < sizeof(sMap)/sizeof(struct SControlMap); ++i) {
-    if(sMap[i].iEvent == iNodeID) {
-      if(sMap[i].bDelete) 
-        dasher_editor_external_edit_delete(pSelf, sMap[i].iDir, sMap[i].iDist);
-      else
-        dasher_editor_external_edit_move(pSelf, sMap[i].iDir, sMap[i].iDist);	
-    }
-  }
-}
-
-
 static void 
 dasher_editor_external_grab_focus(DasherEditor *pSelf) {
 }
@@ -322,22 +271,6 @@ dasher_editor_external_get_new_text(DasherEditor *pSelf) {
   return NULL;
 }
 
-static void 
-dasher_editor_external_edit_convert(DasherEditor *pSelf) {
-}
-
-static void 
-dasher_editor_external_edit_protect(DasherEditor *pSelf) {
-}
-
-void dasher_editor_external_edit_move(DasherEditor *pSelf, int iDirection, int iDist) {
-  // TODO: Implement
-}
-
-void dasher_editor_external_edit_delete(DasherEditor *pSelf, int iDirection, int iDist) {
-  // TODO: Implement
-}
-
 /* Callback Functions */
 
 #ifdef GNOME_A11Y
diff --git a/Src/Gtk2/dasher_editor_internal.cpp b/Src/Gtk2/dasher_editor_internal.cpp
index d436696..008191b 100644
--- a/Src/Gtk2/dasher_editor_internal.cpp
+++ b/Src/Gtk2/dasher_editor_internal.cpp
@@ -46,7 +46,7 @@
 //   gtk_text_buffer_get_iter_at_mark( the_text_buffer, &oNewStart, gtk_text_buffer_get_mark(the_text_buffer, "new_start"));
 
 //   return gtk_text_buffer_get_text( the_text_buffer, &oNewStart, &oNewEnd, false );
-  
+
 // }
 
 // ---
@@ -104,7 +104,10 @@ struct _DasherEditorInternalPrivate {
   gboolean bConversionMode;
   gint iLastOffset;
   gint iCurrentState; // 0 = unconverted, 1 = converted
-
+  //Paralleling the previous approach in dasher_main, we _don't_ send context_changed
+  // events if we're in the middle of executing a control action (as this would rebuild
+  // the canvas)
+  gboolean bInControlAction;
 };
 
 #define DASHER_EDITOR_INTERNAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), DASHER_TYPE_EDITOR_INTERNAL, DasherEditorInternalPrivate))
@@ -149,8 +152,9 @@ static void dasher_editor_internal_set_filename(DasherEditor *pSelf, const gchar
 
 static void dasher_editor_internal_edit_convert(DasherEditor *pSelf);
 static void dasher_editor_internal_edit_protect(DasherEditor *pSelf);
-static void dasher_editor_internal_edit_delete(DasherEditor *pSelf, int iDirection, int iDist);
-static void dasher_editor_internal_edit_move(DasherEditor *pSelf, int iDirection, int iDist);
+using Dasher::CControlManager;
+static gint dasher_editor_internal_ctrl_delete(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist);
+static gint dasher_editor_internal_ctrl_move(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist);
 
 static void dasher_editor_internal_new_buffer(DasherEditor *pSelf, const gchar *szFilename);
 
@@ -171,7 +175,6 @@ void dasher_editor_internal_mark_changed(DasherEditorInternal *pSelf, GtkTextIte
 /* Events proagated from main */
 void dasher_editor_internal_handle_stop(DasherEditor *pSelf);
 void dasher_editor_internal_handle_start(DasherEditor *pSelf);
-void dasher_editor_internal_handle_control(DasherEditor *pSelf, int iNodeID);
 
 /* Action related methods - TODO: a lot of this should be moved to dasher_main (eg action on stop etc) - that way we get a better level of abstraction, and can incorporate commands from other modules too. Actions should only be externally visible as a list of string commands*/
 #ifdef XXXPRLWACTIONSAREFIXED
@@ -211,7 +214,7 @@ extern "C" void handle_request_settings(GtkDasherControl * pDasherControl, gpoin
 extern "C" void gtk2_edit_delete_callback(GtkDasherControl *pDasherControl, const gchar *szText, int iOffset, gpointer user_data);
 extern "C" void gtk2_edit_output_callback(GtkDasherControl *pDasherControl, const gchar *szText, int iOffset, gpointer user_data);
 
-static void 
+static void
 dasher_editor_internal_class_init(DasherEditorInternalClass *pClass) {
   g_type_class_add_private(pClass, sizeof(DasherEditorInternalPrivate));
 
@@ -225,14 +228,13 @@ dasher_editor_internal_class_init(DasherEditorInternalClass *pClass) {
   pParentClass->delete_text = dasher_editor_internal_delete;
   pParentClass->get_context = dasher_editor_internal_get_context;
   pParentClass->get_offset = dasher_editor_internal_get_offset;
-  pParentClass->edit_move = dasher_editor_internal_edit_move;
-  pParentClass->edit_delete = dasher_editor_internal_edit_delete;
+  pParentClass->ctrl_move = dasher_editor_internal_ctrl_move;
+  pParentClass->ctrl_delete = dasher_editor_internal_ctrl_delete;
   pParentClass->edit_convert = dasher_editor_internal_edit_convert;
   pParentClass->edit_protect = dasher_editor_internal_edit_protect;
 
   pParentClass->handle_stop = dasher_editor_internal_handle_stop;
   pParentClass->handle_start = dasher_editor_internal_handle_start;
-  pParentClass->handle_control = dasher_editor_internal_handle_control;
 #ifdef XXXPRLWACTIONSAREFIXED
   pParentClass->action_button = dasher_editor_internal_action_button;
   pParentClass->actions_start = dasher_editor_internal_actions_start;
@@ -250,7 +252,7 @@ dasher_editor_internal_class_init(DasherEditorInternalClass *pClass) {
   pParentClass->handle_parameter_change = dasher_editor_internal_handle_parameter_change;
 }
 
-static void 
+static void
 dasher_editor_internal_init(DasherEditorInternal *pSelf) {
   DasherEditorInternalPrivate *pPrivate =
                                    DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
@@ -271,6 +273,7 @@ dasher_editor_internal_init(DasherEditorInternal *pSelf) {
   pPrivate->pNewMark =
        gtk_text_buffer_create_mark(pPrivate->pBuffer, NULL, &oStartIter, TRUE);
   pPrivate->bFileModified = FALSE;
+  pPrivate->bInControlAction = FALSE;
 
   GtkWidget *pScrolledWindow = gtk_scrolled_window_new(NULL, NULL);
 
@@ -290,7 +293,7 @@ dasher_editor_internal_init(DasherEditorInternal *pSelf) {
   gtk_widget_show_all(GTK_WIDGET(&(pSelf->parent.box)));
 }
 
-static void 
+static void
 dasher_editor_internal_finalize(GObject *pObject) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pObject);
 
@@ -299,7 +302,7 @@ dasher_editor_internal_finalize(GObject *pObject) {
 
   if(pCurrentAction) {
     bool bStarted = false;
-    
+  
     while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
       bStarted = true;
       dasher_action_deactivate(pCurrentAction->pAction);
@@ -329,9 +332,9 @@ dasher_editor_internal_initialise(DasherEditor *pSelf, DasherAppSettings *pAppSe
   pPrivate->pDasherMain = pDasherMain;
 
   dasher_editor_internal_handle_font(pSelf,
-				     dasher_app_settings_get_string(pPrivate->pAppSettings, 
+				     dasher_app_settings_get_string(pPrivate->pAppSettings,
 								    APP_SP_EDIT_FONT));
-  
+
 #ifdef XXXPRLWACTIONSAREFIXED
   GtkVBox *pActionPane = GTK_VBOX(gtk_builder_get_object(pXML, "vbox39"));
 
@@ -353,11 +356,11 @@ dasher_editor_internal_initialise(DasherEditor *pSelf, DasherAppSettings *pAppSe
     dasher_editor_internal_generate_filename(pSelf);
     dasher_editor_internal_clear(pSelf, false);
   }
-  
+
 //  pPrivate->pGameModeHelper = GAME_MODE_HELPER(game_mode_helper_new(pXML, (void*)pSelf));
 }
 
-static void 
+static void
 dasher_editor_internal_clipboard(DasherEditor *pSelf, clipboard_action act) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -397,106 +400,40 @@ dasher_editor_internal_clipboard(DasherEditor *pSelf, clipboard_action act) {
   delete end;
 }
 
-void 
+void
 dasher_editor_internal_handle_stop(DasherEditor *pSelf) {
 #ifdef XXXPRLWACTIONSAREFIXED
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   // See if anything is set to auto:
   EditorAction *pCurrentAction = pPrivate->pActionRing;
 
   if(pCurrentAction) {
     bool bStarted = false;
-    
+  
     while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
       bStarted = true;
       if(pCurrentAction->bAuto)
-	dasher_action_execute(pCurrentAction->pAction, DASHER_EDITOR(pSelf), -1); 
+	dasher_action_execute(pCurrentAction->pAction, DASHER_EDITOR(pSelf), -1);
       pCurrentAction = pCurrentAction->pNext;
     }
   }
 #endif
 }
 
-void 
+void
 dasher_editor_internal_handle_start(DasherEditor *pSelf) {
-  // The edit box keeps track of where we started 
+  // The edit box keeps track of where we started
 
   // TODO: This should be filtered through the buffer, rather than directly to the edit box
   //  set_mark();
 }
 
-/* TODO: This is obsolete - sort this out when commands are reconsidered */
-void 
-dasher_editor_internal_handle_control(DasherEditor *pSelf, int iNodeID) {
-  DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-
-  if(iNodeID == Dasher::CControlManager::CTL_USER + 1)
-    dasher_editor_internal_clear(pSelf, false); // Clear node is a special case (it shouldn't be)
-#ifdef XXXPRLWACTIONSAREFIXED
-  else if (pPrivate->pActionRing) {
-    EditorAction *pCurrentAction = pPrivate->pActionRing;
-    bool bStarted = false;
-    
-    while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
-      bStarted = true;
-      //ACL 14/6/10 Adding test that iControlID is a sensible value -
-      // it never is, as it's never set, but it prevents the old code from
-      // being erroneously activated when e.g. the root control node is entered!
-      if(pCurrentAction->iControlID >= Dasher::CControlManager::CTL_USER && (iNodeID >= pCurrentAction->iControlID) && (iNodeID <= pCurrentAction->iControlID + pCurrentAction->iNSub)) {
-	dasher_action_execute(pCurrentAction->pAction, DASHER_EDITOR(pSelf), iNodeID - pCurrentAction->iControlID - 1); 
-	//	dasher_editor_internal_clear(pSelf, true); 
-      }
-      pCurrentAction = pCurrentAction->pNext;
-    }
-  }
-#endif
-
-
-  // TODO: Think about changing signals so we don't need to do this translation
-
-  struct SControlMap {
-    int iEvent;
-    int iDir;
-    int iDist;
-    bool bDelete;
-  };
-
-  static struct SControlMap sMap[] = {
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, false},
-    {Dasher::CControlManager::CTL_MOVE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, false},
-    {Dasher::CControlManager::CTL_MOVE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, false},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, true},
-    {Dasher::CControlManager::CTL_DELETE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, true},
-    {Dasher::CControlManager::CTL_DELETE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, true}
-  };    
-
-  for(unsigned int i(0); i < sizeof(sMap)/sizeof(struct SControlMap); ++i) {
-    if(sMap[i].iEvent == iNodeID) {
-      if(sMap[i].bDelete) 
-        dasher_editor_internal_edit_delete(pSelf, sMap[i].iDir, sMap[i].iDist);
-      else
-        dasher_editor_internal_edit_move(pSelf, sMap[i].iDir, sMap[i].iDist);	
-    }
-  }
-}
-
-
 #ifdef XXXPRLWACTIONSAREFIXED
-void 
+void
 dasher_editor_internal_action_button(DasherEditor *pSelf, DasherAction *pAction) {
   if(pAction) {
-    dasher_action_execute(pAction, DASHER_EDITOR(pSelf), -1); 
+    dasher_action_execute(pAction, DASHER_EDITOR(pSelf), -1);
     dasher_editor_internal_clear(pSelf, true);
   }
   else { // Clear button
@@ -505,7 +442,7 @@ dasher_editor_internal_action_button(DasherEditor *pSelf, DasherAction *pAction)
 }
 #endif
 
-static void 
+static void
 dasher_editor_internal_clear(DasherEditor *pSelf, gboolean bStore) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -529,7 +466,7 @@ dasher_editor_internal_clear(DasherEditor *pSelf, gboolean bStore) {
 
 
 #ifdef XXXPRLWACTIONSAREFIXED
-void 
+void
 dasher_editor_internal_actions_start(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -537,28 +474,28 @@ dasher_editor_internal_actions_start(DasherEditor *pSelf) {
   pPrivate->pActionIter = pPrivate->pActionRing;
 }
 
-bool 
+bool
 dasher_editor_internal_actions_more(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
  return(!pPrivate->bActionIterStarted || (pPrivate->pActionIter != pPrivate->pActionRing));
 }
 
-void 
+void
 dasher_editor_internal_actions_get_next(DasherEditor *pSelf, const gchar **szName, gint *iID, gboolean *bShow, gboolean *bControl, gboolean *bAuto) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   *szName = dasher_action_get_name(pPrivate->pActionIter->pAction);
   *iID = pPrivate->pActionIter->iID;
-  *bShow = pPrivate->pActionIter->bShow; 
+  *bShow = pPrivate->pActionIter->bShow;
   *bControl = pPrivate->pActionIter->bControl;
   *bAuto = pPrivate->pActionIter->bAuto;
-  
+
   pPrivate->pActionIter = pPrivate->pActionIter->pNext;
   pPrivate->bActionIterStarted = true;
 }
 
-void 
+void
 dasher_editor_internal_action_set_show(DasherEditor *pSelf, int iActionID, bool bValue) {
   EditorAction *pAction;
   pAction = dasher_editor_internal_get_action_by_id(pSelf, iActionID);
@@ -572,7 +509,7 @@ dasher_editor_internal_action_set_show(DasherEditor *pSelf, int iActionID, bool
   }
 }
 
-void 
+void
 dasher_editor_internal_action_set_control(DasherEditor *pSelf, int iActionID, bool bValue) {
   // TODO: Need to actually change behaviour in resonse to these calls
 
@@ -580,7 +517,7 @@ dasher_editor_internal_action_set_control(DasherEditor *pSelf, int iActionID, bo
 
 //   EditorAction *pAction;
 //   pAction = dasher_editor_internal_get_action_by_id(pSelf, iActionID);
-  
+
 //   if(pAction) {
 //     pAction->bControl = bValue;
 //     dasher_editor_internal_check_activity(pSelf, pAction);
@@ -588,26 +525,26 @@ dasher_editor_internal_action_set_control(DasherEditor *pSelf, int iActionID, bo
 //       gtk_dasher_control_connect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER, -2);
 //     else
 //       gtk_dasher_control_disconnect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER);
-    
+  
 //     dasher_editor_internal_action_save_state(pSelf, pAction);
 //   }
 }
 
-void 
-dasher_editor_internal_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) { 
+void
+dasher_editor_internal_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) {
 EditorAction *pAction;
   pAction = dasher_editor_internal_get_action_by_id(pSelf, iActionID);
 
   if(pAction) {
     pAction->bAuto = bValue;
     dasher_editor_internal_check_activity(pSelf, pAction);
-    
+  
     dasher_editor_internal_action_save_state(pSelf, pAction);
   }
 }
 #endif
 
-void 
+void
 dasher_editor_internal_grab_focus(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -616,11 +553,11 @@ dasher_editor_internal_grab_focus(DasherEditor *pSelf) {
 }
 
 
-static void 
+static void
 dasher_editor_internal_create_buffer(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-    
-    
+  
+  
   pPrivate->pOutputTag = gtk_text_buffer_create_tag(pPrivate->pBuffer, NULL, NULL);
 
 #if GTK_CHECK_VERSION(2,8,0)
@@ -639,7 +576,7 @@ dasher_editor_internal_create_buffer(DasherEditor *pSelf) {
   g_signal_connect(G_OBJECT(pPrivate->pBuffer), "mark-set", G_CALLBACK(mark_set_handler), pSelf);
 }
 
-void 
+void
 dasher_editor_internal_output(DasherEditor *pSelf, const gchar *szText, int iOffset) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -650,14 +587,14 @@ dasher_editor_internal_output(DasherEditor *pSelf, const gchar *szText, int iOff
   if(!strcmp(szText, " ")) {
     gboolean bActionIterStarted = false;
     EditorAction *pActionIter = pPrivate->pActionRing;
-    
+  
     while((pActionIter != pPrivate->pActionRing) || !bActionIterStarted) {
       bActionIterStarted = true;
-      
+    
       if(!strcmp(dasher_action_get_name(pActionIter->pAction), "Speak")) {
         dasher_action_preview(pActionIter->pAction, DASHER_EDITOR(pSelf));
       }
-      
+    
       pActionIter = pActionIter->pNext;
     }
   }
@@ -703,10 +640,10 @@ dasher_editor_internal_output(DasherEditor *pSelf, const gchar *szText, int iOff
   pPrivate->bFileModified = TRUE;
 }
 
-void 
+void
 dasher_editor_internal_delete(DasherEditor *pSelf, int iLength, int iOffset) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   GtkTextIter end;
 
   gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &end, gtk_text_buffer_get_insert(pPrivate->pBuffer));
@@ -740,7 +677,7 @@ dasher_editor_internal_get_context(DasherEditor *pSelf, int iOffset, int iLength
   return gtk_text_buffer_get_text( pPrivate->pBuffer, &start, &end, false );
 }
 
-gint 
+gint
 dasher_editor_internal_get_offset(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
   GtkTextIter iter1,iter2;
@@ -755,15 +692,16 @@ void dasher_editor_internal_mark_changed(DasherEditorInternal *pSelf, GtkTextIte
     //ACL: used to emit "offset_changed" signal from buffer, which was picked up
     // by a callback registered by editor_internal, which then emitted a context_changed
     // signal from the editor_internal. So just emit the context_changed directly...
-    g_signal_emit_by_name(G_OBJECT(pSelf), "context_changed", G_OBJECT(pSelf), NULL, NULL);
+    if (!DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf)->bInControlAction) //tho not if it's the result of a control-mode edit/delete
+      g_signal_emit_by_name(G_OBJECT(pSelf), "context_changed", G_OBJECT(pSelf), NULL, NULL);
   }
 }
 
 
-static void 
+static void
 dasher_editor_internal_generate_filename(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   gchar *szNewFilename = NULL;
 
   if( dasher_app_settings_get_bool(pPrivate->pAppSettings,  APP_BP_TIME_STAMP )) {
@@ -788,7 +726,7 @@ dasher_editor_internal_generate_filename(DasherEditor *pSelf) {
   g_free(szNewFilename);
 }
 
-static void 
+static void
 dasher_editor_internal_open(DasherEditor *pSelf, const gchar *szFilename) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -818,17 +756,17 @@ dasher_editor_internal_open(DasherEditor *pSelf, const gchar *szFilename) {
       // for now, just assert that we can't load these files.
       for(gsize i = 0; i < size; ++i)
 	if(buffer[i] == 0) {
-// 	  GtkWidget *pErrorBox = gtk_message_dialog_new(GTK_WINDOW(window), 
-// 							GTK_DIALOG_MODAL, 
+// 	  GtkWidget *pErrorBox = gtk_message_dialog_new(GTK_WINDOW(window),
+// 							GTK_DIALOG_MODAL,
 // 							GTK_MESSAGE_ERROR,
-// 							GTK_BUTTONS_OK, 
-// 							"Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n", 
+// 							GTK_BUTTONS_OK,
+// 							"Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n",
 // 							myfilename);
-	  GtkWidget *pErrorBox = gtk_message_dialog_new(NULL, 
-							GTK_DIALOG_MODAL, 
+	  GtkWidget *pErrorBox = gtk_message_dialog_new(NULL,
+							GTK_DIALOG_MODAL,
 							GTK_MESSAGE_ERROR,
-							GTK_BUTTONS_OK, 
-							"Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n", 
+							GTK_BUTTONS_OK,
+							"Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n",
 							szFilename);
 	  gtk_dialog_run(GTK_DIALOG(pErrorBox));
 	  gtk_widget_destroy(pErrorBox);
@@ -856,8 +794,8 @@ dasher_editor_internal_open(DasherEditor *pSelf, const gchar *szFilename) {
   dasher_editor_internal_set_filename(pSelf, szFilename);
 }
 
-static bool 
-dasher_editor_internal_save_as(DasherEditor *pSelf, const gchar *szFilename, bool bAppend) { 
+static bool
+dasher_editor_internal_save_as(DasherEditor *pSelf, const gchar *szFilename, bool bAppend) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
   unsigned long long length;
@@ -896,10 +834,10 @@ dasher_editor_internal_save_as(DasherEditor *pSelf, const gchar *szFilename, boo
 //   switch (fileencoding) {
 //   case Dasher::Opts::UserDefault:
 //   case Dasher::Opts::AlphabetDefault:
-//     //FIXME - need to call GetAlphabetType and do appropriate stuff regarding 
+//     //FIXME - need to call GetAlphabetType and do appropriate stuff regarding
 //     //the character set. Arguably we should always be saving in either UTF-8 or
-//     //the user's locale (which may, of course, be UTF-8) because otherwise 
-//     //we're going to read in rubbish, and we shouldn't be encouraging weird 
+//     //the user's locale (which may, of course, be UTF-8) because otherwise
+//     //we're going to read in rubbish, and we shouldn't be encouraging weird
 //     //codepage madness any further
 
 //     //FIXME - error handling
@@ -948,7 +886,7 @@ dasher_editor_internal_save_as(DasherEditor *pSelf, const gchar *szFilename, boo
   return true;
 }
 
-// void 
+// void
 // dasher_editor_internal_start_tutorial(DasherEditor *pSelf) {
 //    DasherEditorInternalPrivate *pPrivate = (DasherEditorInternalPrivate *)(pSelf->private_data);
 
@@ -956,7 +894,7 @@ dasher_editor_internal_save_as(DasherEditor *pSelf, const gchar *szFilename, boo
 //   //  pPrivate->pGameModeHelper = GAME_MODE_HELPER(game_mode_helper_new(GTK_DASHER_CONTROL(pDasherWidget)));
 // }
 
-gboolean 
+gboolean
 dasher_editor_internal_command(DasherEditor *pSelf, const gchar *szCommand) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -964,12 +902,12 @@ dasher_editor_internal_command(DasherEditor *pSelf, const gchar *szCommand) {
     dasher_editor_internal_command_new(pSelf);
     return TRUE;
   }
-  
+
   if(!strcmp(szCommand, "action_open")) { //select open file
     dasher_editor_internal_command_open(pSelf);
     return TRUE;
   }
-  
+
   if(!strcmp(szCommand, "action_save")) { //save_file
     dasher_editor_internal_command_save(pSelf, FALSE, FALSE);
     return TRUE;
@@ -995,7 +933,7 @@ dasher_editor_internal_command(DasherEditor *pSelf, const gchar *szCommand) {
     return TRUE;
   }
 
-  if(!strcmp(szCommand, "action_copyall")) { // clipboard_copyall 
+  if(!strcmp(szCommand, "action_copyall")) { // clipboard_copyall
     dasher_editor_internal_clipboard(pSelf, CLIPBOARD_COPYALL);
     return TRUE;
   }
@@ -1004,29 +942,29 @@ dasher_editor_internal_command(DasherEditor *pSelf, const gchar *szCommand) {
     dasher_editor_internal_clipboard(pSelf, CLIPBOARD_PASTE);
     return TRUE;
   }
- 
+
   // TODO: This isn't actually accessible from anywhere
   if(!strcmp(szCommand, "action_selectall")) { // clipboard_paste
     dasher_editor_internal_clipboard(pSelf, CLIPBOARD_SELECTALL);
     return TRUE;
   }
- 
+
   return FALSE;
 }
 
 
-void 
+void
 dasher_editor_internal_handle_font(DasherEditor *pSelf, const gchar *szFont) {
   if(strcmp(szFont, "")) {
     DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-    
+  
     PangoFontDescription *pFD = pango_font_description_from_string(szFont);
     gtk_widget_modify_font(GTK_WIDGET(pPrivate->pTextView), pFD);
   }
 }
 
 
-gboolean 
+gboolean
 dasher_editor_internal_file_changed(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
   return pPrivate->bFileModified;
@@ -1041,7 +979,7 @@ dasher_editor_internal_get_filename(DasherEditor *pSelf) {
 
 // TODO: We shouldn't need to know about the buffer here - make this a method of the buffer set
 const gchar *
-dasher_editor_internal_get_all_text(DasherEditor *pSelf) { 
+dasher_editor_internal_get_all_text(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
   GtkTextIter oStart;
@@ -1056,7 +994,7 @@ dasher_editor_internal_get_all_text(DasherEditor *pSelf) {
 }
 
 const gchar *
-dasher_editor_internal_get_new_text(DasherEditor *pSelf) { 
+dasher_editor_internal_get_new_text(DasherEditor *pSelf) {
   // TODO: Implement this properly
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -1075,7 +1013,7 @@ dasher_editor_internal_get_new_text(DasherEditor *pSelf) {
 
 
 /* Private methods */
-static void 
+static void
 dasher_editor_internal_select_all(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
   GtkTextIter *start, *end;
@@ -1097,10 +1035,10 @@ dasher_editor_internal_select_all(DasherEditor *pSelf) {
 }
 
 #ifdef XXXPRLWACTIONSAREFIXED
-static void 
+static void
 dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   // TODO: Activate and deactivate methods for actions
   // TODO: Clear shouldn't be a special case (include support for false in clear method)
 
@@ -1132,10 +1070,10 @@ dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
     while((szFilename = g_dir_read_name(pDirectory))) {
       dasher_editor_internal_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szUserScriptDir, szFilename)));
     }
-    
+  
     g_dir_close(pDirectory);
   }
-  
+
   delete[] szUserScriptDir;
 
   gchar *szSystemScriptDir = new gchar[strlen(dasher_app_settings_get_string(pPrivate->pAppSettings, SP_SYSTEM_LOC))+9];
@@ -1148,7 +1086,7 @@ dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
     while((szFilename = g_dir_read_name(pDirectory))) {
       dasher_editor_internal_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szSystemScriptDir, szFilename)));
     }
-    
+  
     g_dir_close(pDirectory);
   }
 
@@ -1177,9 +1115,9 @@ dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
 //     if(pCurrentAction->bControl) {
 //       gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, dasher_action_get_name(pCurrentAction->pAction), -1 );
 //       gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, Dasher::CControlManager::CTL_USER, -2);
-      
+    
 //       int iNSub(dasher_action_get_sub_count(pCurrentAction->pAction));
-      
+    
 //       if(iNSub == 0) {
 // 	gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
 //       }
@@ -1190,12 +1128,12 @@ dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
 // 	  gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset + i + 1, -2);
 // 	}
 //       }
-      
+    
 //       pCurrentAction->iControlID = Dasher::CControlManager::CTL_USER + iControlOffset;
 //       pCurrentAction->iNSub = iNSub;
 //       iControlOffset += iNSub + 1;
 //     }
-      
+    
 //     pCurrentAction = pCurrentAction->pNext;
 //   }
 
@@ -1206,7 +1144,7 @@ dasher_editor_internal_setup_actions(DasherEditor *pSelf) {
 #endif /* XXXPRLWACTIONSAREFIXED */
 
 #ifdef XXXPRLWACTIONSAREFIXED
-static void 
+static void
 dasher_editor_internal_add_action(DasherEditor *pSelf, DasherAction *pNewAction) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -1252,7 +1190,7 @@ dasher_editor_internal_add_action(DasherEditor *pSelf, DasherAction *pNewAction)
   }
 
   pPrivate->pActionRing = pNewEditorAction;
-  
+
   // TODO: Reimplement
 //   if(iState & ACTION_STATE_SHOW)
 //     gtk_dasher_control_add_action_button(GTK_DASHER_CONTROL(pDasherWidget), dasher_action_get_name(pNewEditorAction->pAction));
@@ -1266,23 +1204,23 @@ dasher_editor_internal_get_action_by_id(DasherEditor *pSelf, int iID){
 
   EditorAction *pCurrentAction = pPrivate->pActionRing;
   bool bStarted = false;
-  
+
   while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
     bStarted = true;
     if(pCurrentAction->iID == iID)
       return pCurrentAction;
     pCurrentAction = pCurrentAction->pNext;
   }
-  
+
   return 0;
 }
 #endif /* XXXPRLWACTIONSAREFIXED */
 
 #ifdef XXXPRLWACTIONSAREFIXED
-static void 
+static void
 dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   // Delete any existing widgets
   gtk_container_foreach(GTK_CONTAINER(pPrivate->pActionPane), delete_children_callback, 0);
 
@@ -1293,7 +1231,7 @@ dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
   void **pUserData = new void *[2];
   pUserData[0] = (void *)pSelf;
   pUserData[1] = 0;
-  
+
   g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
 #ifdef WITH_MAEMO
   // For Maemo we want the packing to expand
@@ -1301,7 +1239,7 @@ dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
 #else
   gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), false, false, 0);
 #endif
- 
+
 
   EditorAction *pCurrentAction = pPrivate->pActionRing;
   bool bStarted = false;
@@ -1311,11 +1249,11 @@ dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
     if(pCurrentAction->bShow) {
       GtkButton *pNewButton = GTK_BUTTON(gtk_button_new_with_label(dasher_action_get_name(pCurrentAction->pAction)));
       gtk_widget_show(GTK_WIDGET(pNewButton));
-      
+    
       pUserData = new void *[2];
       pUserData[0] = (void *)pSelf;
       pUserData[1] = (void *)(pCurrentAction->pAction);
-      
+    
       g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
 #ifdef WITH_MAEMO
       // For Maemo we want the packing to expand
@@ -1330,7 +1268,7 @@ dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
 #endif /* XXXPRLWACTIONSAREFIXED */
 
 // TODO: This shouldn't be a part of the editor
-//static void 
+//static void
 //dasher_editor_internal_display_message(DasherEditor *pSelf, DasherMessageInfo *pMessageInfo) {
 //  GtkMessageDialog *pDialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(0, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, pMessageInfo->szMessage));
 //  gtk_dialog_run(GTK_DIALOG(pDialog));
@@ -1338,7 +1276,7 @@ dasher_editor_internal_rebuild_action_pane(DasherEditor *pSelf) {
 //}
 
 #ifdef XXXPRLWACTIONSAREFIXED
-static void 
+static void
 dasher_editor_internal_check_activity(DasherEditor *pSelf, EditorAction *pAction) {
   gboolean bNeedActive(pAction->bShow || pAction->bControl || pAction->bAuto);
   gboolean bActive(dasher_action_get_active(pAction->pAction));
@@ -1351,7 +1289,7 @@ dasher_editor_internal_check_activity(DasherEditor *pSelf, EditorAction *pAction
 #endif
 
 #ifdef XXXPRLWACTIONSAREFIXED
-static void 
+static void
 dasher_editor_internal_action_save_state(DasherEditor *pSelf, EditorAction *pAction) {
   gchar szRegistryName[256];
   strncpy(szRegistryName, "Action_", 256);
@@ -1365,7 +1303,7 @@ dasher_editor_internal_action_save_state(DasherEditor *pSelf, EditorAction *pAct
 
   if(pAction->bShow)
     iState += ACTION_STATE_SHOW;
-  
+
   if(pAction->bControl)
     iState += ACTION_STATE_CONTROL;
 
@@ -1377,22 +1315,22 @@ dasher_editor_internal_action_save_state(DasherEditor *pSelf, EditorAction *pAct
 }
 #endif
 
-static void 
+static void
 dasher_editor_internal_command_new(DasherEditor *pSelf) {
   dasher_editor_internal_new_buffer(pSelf, NULL);
 }
 
-static void 
-dasher_editor_internal_command_open(DasherEditor *pSelf) { 
+static void
+dasher_editor_internal_command_open(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
   GtkWidget *pTopLevel = gtk_widget_get_toplevel(GTK_WIDGET(pPrivate->pTextView));
-  GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), 
+  GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"),
 						   GTK_WINDOW(pTopLevel),
 						   GTK_FILE_CHOOSER_ACTION_OPEN,
-						   GTK_STOCK_OPEN, 
-						   GTK_RESPONSE_ACCEPT, 
-						   GTK_STOCK_CANCEL, 
+						   GTK_STOCK_OPEN,
+						   GTK_RESPONSE_ACCEPT,
+						   GTK_STOCK_CANCEL,
 						   GTK_RESPONSE_CANCEL, NULL);
 
 #ifdef HAVE_GIO
@@ -1412,8 +1350,8 @@ dasher_editor_internal_command_open(DasherEditor *pSelf) {
   gtk_widget_destroy(filesel);
 }
 
-static void 
-dasher_editor_internal_command_save(DasherEditor *pSelf, gboolean bPrompt, gboolean bAppend) { 
+static void
+dasher_editor_internal_command_save(DasherEditor *pSelf, gboolean bPrompt, gboolean bAppend) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
   gchar *szFilename = NULL;
@@ -1421,18 +1359,18 @@ dasher_editor_internal_command_save(DasherEditor *pSelf, gboolean bPrompt, gbool
   // Hmm... this makes no sense - surely this always evaluates to true?
   if(bPrompt || !szFilename) {
     GtkWidget *pTopLevel = gtk_widget_get_toplevel(GTK_WIDGET(pPrivate->pTextView));
-    GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), 
-						     GTK_WINDOW(pTopLevel), 
-						     GTK_FILE_CHOOSER_ACTION_SAVE, 
-						     GTK_STOCK_SAVE, 
-						     GTK_RESPONSE_ACCEPT, 
-						     GTK_STOCK_CANCEL, 
+    GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"),
+						     GTK_WINDOW(pTopLevel),
+						     GTK_FILE_CHOOSER_ACTION_SAVE,
+						     GTK_STOCK_SAVE,
+						     GTK_RESPONSE_ACCEPT,
+						     GTK_STOCK_CANCEL,
 						     GTK_RESPONSE_CANCEL, NULL);
-    
+  
 #ifdef HAVE_GIO
     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filesel), FALSE);
 #endif
-    
+  
     if(gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
 #ifdef HAVE_GIO
       szFilename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filesel));
@@ -1444,7 +1382,7 @@ dasher_editor_internal_command_save(DasherEditor *pSelf, gboolean bPrompt, gbool
       gtk_widget_destroy(filesel);
       return;
     }
-    
+  
     gtk_widget_destroy(filesel);
   }
   else {
@@ -1456,7 +1394,7 @@ dasher_editor_internal_command_save(DasherEditor *pSelf, gboolean bPrompt, gbool
 }
 
 #ifdef HAVE_GIO
-static void 
+static void
 dasher_editor_internal_gvfs_print_error(DasherEditor *pSelf, GError *error, const char *myfilename) {
   // Turns a GVFS error into English
   GtkWidget *error_dialog;
@@ -1468,7 +1406,7 @@ dasher_editor_internal_gvfs_print_error(DasherEditor *pSelf, GError *error, cons
   return;
 }
 
-static gboolean 
+static gboolean
 dasher_editor_internal_gvfs_open_file(DasherEditor *pSelf, const char *uri, gchar **buffer, gsize *size)
 {
   GFile *file;
@@ -1544,7 +1482,7 @@ static GFileOutputStream *append_or_replace_file(GFile *file, bool append, GErro
   return write_handle;
 }
 
-static gboolean 
+static gboolean
 dasher_editor_internal_gvfs_save_file(DasherEditor *pSelf, const char *uri, gchar *buffer, gsize length, bool append)
 {
   GFile *file;
@@ -1602,7 +1540,7 @@ dasher_editor_internal_gvfs_save_file(DasherEditor *pSelf, const char *uri, gcha
 
 #else /* not HAVE_GIO */
 
-static gboolean 
+static gboolean
 dasher_editor_internal_unix_vfs_open_file(DasherEditor *pSelf, const char *myfilename, gchar **buffer, gsize *size) {
   GtkWidget *error_dialog;
 
@@ -1629,7 +1567,7 @@ dasher_editor_internal_unix_vfs_open_file(DasherEditor *pSelf, const char *myfil
   return TRUE;
 }
 
-static gboolean 
+static gboolean
 dasher_editor_internal_unix_vfs_save_file(DasherEditor *pSelf, const char *myfilename, gchar *buffer, gsize length, bool append) {
   int opened = 1;
   GtkWidget *error_dialog;
@@ -1666,10 +1604,10 @@ dasher_editor_internal_unix_vfs_save_file(DasherEditor *pSelf, const char *myfil
 }
 #endif
 
-static void 
+static void
 dasher_editor_internal_set_filename(DasherEditor *pSelf, const gchar *szFilename) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
+
   if(pPrivate->szFilename)
     g_free((void *)pPrivate->szFilename);
 
@@ -1681,7 +1619,7 @@ dasher_editor_internal_set_filename(DasherEditor *pSelf, const gchar *szFilename
   g_signal_emit_by_name(G_OBJECT(pSelf), "filename_changed", G_OBJECT(pSelf), NULL, NULL);
 }
 
-static void 
+static void
 dasher_editor_internal_edit_convert(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -1698,7 +1636,7 @@ dasher_editor_internal_edit_convert(DasherEditor *pSelf) {
   pPrivate->iLastOffset = gtk_text_buffer_get_char_count(pPrivate->pBuffer);
 }
 
-static void 
+static void
 dasher_editor_internal_edit_protect(DasherEditor *pSelf) {
   DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
@@ -1709,114 +1647,87 @@ dasher_editor_internal_edit_protect(DasherEditor *pSelf) {
   pPrivate->iLastOffset = gtk_text_buffer_get_char_count(pPrivate->pBuffer);
 }
 
-static void
-dasher_editor_internal_edit_delete(DasherEditor *pSelf, int iDirection, int iDist) { 
-  DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
-  
-  GtkTextIter sPosStart;
-  GtkTextIter sPosEnd;
-
-  gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &sPosStart, gtk_text_buffer_get_insert(pPrivate->pBuffer));
-
-  sPosEnd = sPosStart;
-  
-  if(iDirection == EDIT_FORWARDS) {
+static void edit_find(bool bForwards, CControlManager::EditDistance iDist, DasherEditorInternalPrivate *pPrivate, GtkTextIter *pPos) {
+  if(bForwards) {
     switch(iDist) {
-    case EDIT_CHAR:
-      gtk_text_iter_forward_char(&sPosStart);
+    case CControlManager::EDIT_CHAR:
+      gtk_text_iter_forward_char(pPos);
       break;
-    case EDIT_WORD:
-      gtk_text_iter_forward_word_end(&sPosStart);
+    case CControlManager::EDIT_WORD:
+      gtk_text_iter_forward_word_end(pPos);
       break;
-    case EDIT_LINE:
-      if(!gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), &sPosStart))
+    case CControlManager::EDIT_LINE:
+      if(!gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), pPos))
       {
-        gtk_text_view_forward_display_line (GTK_TEXT_VIEW(pPrivate->pTextView), &sPosStart);
-        gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), &sPosStart);
+        gtk_text_view_forward_display_line (GTK_TEXT_VIEW(pPrivate->pTextView), pPos);
+        gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), pPos);
       }
       break;
-    case EDIT_FILE:
-      gtk_text_iter_forward_to_end(&sPosStart);
+    case CControlManager::EDIT_FILE:
+      gtk_text_iter_forward_to_end(pPos);
       break;
     }
   }
-  else { 
+  else {
     switch(iDist) {
-    case EDIT_CHAR:
-      gtk_text_iter_backward_char(&sPosStart);
+    case CControlManager::EDIT_CHAR:
+      gtk_text_iter_backward_char(pPos);
       break;
-    case EDIT_WORD:
-      gtk_text_iter_backward_word_start(&sPosStart);
+    case CControlManager::EDIT_WORD:
+      gtk_text_iter_backward_word_start(pPos);
       break;
-    case EDIT_LINE:
-      if(!gtk_text_view_backward_display_line_start(GTK_TEXT_VIEW(pPrivate->pTextView), &sPosStart))
-        gtk_text_view_backward_display_line(GTK_TEXT_VIEW(pPrivate->pTextView), &sPosStart);
+    case CControlManager::EDIT_LINE:
+  
+      if(!gtk_text_view_backward_display_line_start(GTK_TEXT_VIEW(pPrivate->pTextView), pPos))
+        gtk_text_view_backward_display_line(GTK_TEXT_VIEW(pPrivate->pTextView), pPos);
       break;
-    case EDIT_FILE:
-      gtk_text_buffer_get_start_iter(pPrivate->pBuffer, &sPosStart);
+    case CControlManager::EDIT_FILE:
+      gtk_text_buffer_get_start_iter(pPrivate->pBuffer, pPos);
       break;
     }
   }
+}
+
+static gint
+dasher_editor_internal_ctrl_delete(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist) {
+  DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);
 
+  GtkTextIter sPosStart;
+  GtkTextIter sPosEnd;
+
+  gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &sPosStart, gtk_text_buffer_get_insert(pPrivate->pBuffer));
+
+  sPosEnd = sPosStart;
+  edit_find(bForwards, iDist, pPrivate, &sPosStart);
+  gint iRet = gtk_text_iter_get_offset(&sPosStart);
+  pPrivate->bInControlAction = true;
   gtk_text_buffer_delete(pPrivate->pBuffer, &sPosStart, &sPosEnd);
+  pPrivate->bInControlAction = false;
   gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate->pTextView), gtk_text_buffer_get_insert(pPrivate->pBuffer));
+  return iRet;
 }
 
-static void
-dasher_editor_internal_edit_move(DasherEditor *pSelf, int iDirection, int iDist) {
-  DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf);  
+static gint
+dasher_editor_internal_ctrl_move(DasherEditor *pSelf, bool bForwards, CControlManager::EditDistance iDist) {
+  DasherEditorInternalPrivate *pPrivate = DASHER_EDITOR_INTERNAL_GET_PRIVATE(pSelf); 
   GtkTextIter sPos;
 
   gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &sPos, gtk_text_buffer_get_insert(pPrivate->pBuffer));
-  
-  if(iDirection == EDIT_FORWARDS) {
-    switch(iDist) {
-    case EDIT_CHAR:
-      gtk_text_iter_forward_char(&sPos);
-      break;
-    case EDIT_WORD:
-      gtk_text_iter_forward_word_end(&sPos);
-      break;
-    case EDIT_LINE:
-      if(!gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), &sPos))
-      {
-        gtk_text_view_forward_display_line (GTK_TEXT_VIEW(pPrivate->pTextView), &sPos);
-        gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate->pTextView), &sPos);
-      }
-      break;
-    case EDIT_FILE:
-      gtk_text_iter_forward_to_end(&sPos);
-      break;
-    }
-  }
-  else { 
-    switch(iDist) {
-    case EDIT_CHAR:
-      gtk_text_iter_backward_char(&sPos);
-      break;
-    case EDIT_WORD:
-      gtk_text_iter_backward_word_start(&sPos);
-      break;
-    case EDIT_LINE:
-    
-      if(!gtk_text_view_backward_display_line_start(GTK_TEXT_VIEW(pPrivate->pTextView), &sPos))
-        gtk_text_view_backward_display_line(GTK_TEXT_VIEW(pPrivate->pTextView), &sPos);
-      break;
-    case EDIT_FILE:
-      gtk_text_buffer_get_start_iter(pPrivate->pBuffer, &sPos);
-      break;
-    }
-  }
 
+  edit_find(bForwards, iDist, pPrivate, &sPos);
+  gint iRet = gtk_text_iter_get_offset(&sPos);
+  pPrivate->bInControlAction = true;
   gtk_text_buffer_place_cursor(pPrivate->pBuffer, &sPos);
+  pPrivate->bInControlAction = false;
   gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate->pTextView), gtk_text_buffer_get_insert(pPrivate->pBuffer));
+  return iRet;
 }
 
 
-static void 
+static void
 dasher_editor_internal_new_buffer(DasherEditor *pSelf, const gchar *szFilename) {
   /* TODO: eventually rewrite this without references to external functions */
-  
+
   if(szFilename) {
     dasher_editor_internal_open(pSelf, szFilename);
   }
@@ -1841,18 +1752,18 @@ dasher_editor_internal_handle_parameter_change(DasherEditor *pSelf, gint iParame
 
 /* Callback Functions */
 
-extern "C" void 
+extern "C" void
 delete_children_callback(GtkWidget *pWidget, gpointer pUserData) {
   gtk_widget_destroy(pWidget);
 }
 
-extern "C" void 
+extern "C" void
 main_window_realized(DasherMain *pMain, gpointer pUserData) {
 }
 
 #ifdef XXXPRLWACTIONSAREFIXED
-extern "C" void 
-action_button_callback(GtkWidget *pWidget, gpointer pUserData) { 
+extern "C" void
+action_button_callback(GtkWidget *pWidget, gpointer pUserData) {
   void **pPointers((void **)pUserData);
   dasher_editor_internal_action_button((DasherEditor *)pPointers[0], (DasherAction *)pPointers[1]);
 }
diff --git a/Src/Gtk2/dasher_editor_internal.h b/Src/Gtk2/dasher_editor_internal.h
index 61c4a33..55e1a49 100644
--- a/Src/Gtk2/dasher_editor_internal.h
+++ b/Src/Gtk2/dasher_editor_internal.h
@@ -66,7 +66,6 @@ GType dasher_editor_internal_get_type();
 /* /\* Events proagated from main *\/ */
 /* void dasher_editor_internal_handle_stop(DasherEditorInternal *pSelf); */
 /* void dasher_editor_internal_handle_start(DasherEditorInternal *pSelf); */
-/* void dasher_editor_internal_handle_control(DasherEditorInternal *pSelf, int iNodeID); */
 
 /* /\* Action related methods - TODO: a lot of this should be moved to dasher_main (eg action on stop etc) - that way we get a better level of abstraction, and can incorporate commands from other modules too. Actions should only be externally visible as a list of string commands*\/ */
 /* void dasher_editor_internal_action_button(DasherEditorInternal *pSelf, DasherAction *pAction); */
diff --git a/Src/Gtk2/dasher_main.cpp b/Src/Gtk2/dasher_main.cpp
index 0f020fc..419ee83 100644
--- a/Src/Gtk2/dasher_main.cpp
+++ b/Src/Gtk2/dasher_main.cpp
@@ -25,14 +25,10 @@
 #include "DasherAppSettings.h"
 #include "dasher_editor_internal.h"
 #include "dasher_editor_external.h"
-
+#include "math.h"
 /* Static instance of singleton, USE SPARINGLY */
 static DasherMain *g_pDasherMain = NULL; 
 
-// TODO: The following global variable makes control mode editing work
-// - this needs to be sorted out properly.
-static gboolean g_bSend = true;
-
 struct _DasherMainPrivate {
   GtkBuilder *pXML;
   GtkBuilder *pPrefXML;
@@ -152,7 +148,6 @@ extern "C" gboolean edit_key_release(GtkWidget *widget, GdkEventKey *event, gpoi
 /* ... Temporary test/debug functions */
 extern "C" gboolean test_focus_handler(GtkWidget *pWidget, GtkDirectionType iDirection, gpointer *pUserData);
 
-extern "C" void handle_control_event(GtkDasherControl *pDasherControl, gint iEvent, gpointer data);
 extern "C" void handle_start_event(GtkDasherControl *pDasherControl, gpointer data);
 extern "C" gint dasher_main_key_snooper(GtkWidget *pWidget, GdkEventKey *pEvent, gpointer pUserData);
 
@@ -1226,9 +1221,6 @@ dasher_main_cb_context_changed(DasherEditor *pEditor, gpointer pUserData) {
   if(!g_pDasherMain)
     return;
 
-  if(!g_bSend)
-    return;
-
   DasherMain *pDasherMain = DASHER_MAIN(g_pDasherMain);
   DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pDasherMain);
 
@@ -1306,22 +1298,6 @@ test_focus_handler(GtkWidget *pWidget, GtkDirectionType iDirection, gpointer *pU
 }
 
 extern "C" void 
-handle_control_event(GtkDasherControl *pDasherControl, gint iEvent, gpointer data) { 
-  if(!g_pDasherMain)
-    return;
-  
-  // TODO: This is a horrible hack here to make the release work!  
-
-  g_bSend = false;
-
-  DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(g_pDasherMain);
-  dasher_editor_handle_control(pPrivate->pEditor, iEvent);
-  g_bSend = true;
-  // ---
-}
-
-
-extern "C" void 
 handle_start_event(GtkDasherControl *pDasherControl, gpointer data) { 
   if(!g_pDasherMain)
     return;
diff --git a/Src/MacOSX/COSXDasherControl.h b/Src/MacOSX/COSXDasherControl.h
index 76101bd..5d39e21 100644
--- a/Src/MacOSX/COSXDasherControl.h
+++ b/Src/MacOSX/COSXDasherControl.h
@@ -63,6 +63,24 @@ private:
   virtual void Speak(const std::string &strText, bool bInterrupt);
   virtual bool SupportsClipboard() {return true;}
   virtual void CopyToClipboard(const std::string &strText);
+  ///Control-mode editing commands not currently supported on MacOSX,
+  /// so just returns the current offset unchanged.
+  ///Could try to send arrow keys via LowLevelKeyboardHandling,
+  /// but at best we'd lose our context.
+  ///Really this will have to wait for either (a)reinstating an in-Dasher
+  /// text edit box, or (b) implementing the MacOSX input method API.
+  unsigned int ctrlMove(bool bForwards, CControlManager::EditDistance dist);
+
+  ///Control-mode editing commands not currently supported on MacOSX;
+  /// forward deletion returns current offset and does nothing,
+  /// backwards deletion we look for space/paragraph characters
+  /// and send corresponding # of (char-)deletes.
+  ///Could try to send complex keypresses (option-delete=word, line/file...
+  /// = select then delete?) via LowLevelKeyboardHandling, but at best we'd
+  /// lose our context.
+  ///Really this will have to wait for either (a)reinstating an in-Dasher
+  /// text edit box, or (b) implementing the MacOSX input method API.
+  unsigned int ctrlDelete(bool bForwards, CControlManager::EditDistance dist);
   ///
   /// Pass events coming from the core to the appropriate handler.
   ///
diff --git a/Src/MacOSX/COSXDasherControl.mm b/Src/MacOSX/COSXDasherControl.mm
index cb51ccd..2600f21 100644
--- a/Src/MacOSX/COSXDasherControl.mm
+++ b/Src/MacOSX/COSXDasherControl.mm
@@ -182,9 +182,6 @@ void COSXDasherControl::ExternalEventHandler(Dasher::CEvent *pEvent) {
       }
       break;
     }
-    case EV_CONTROL:
-      NSLog(@"ExternalEventHandler, m_iEventType = EV_CONTROL");
-      break;
     case EV_LOCK:
 //      CLockEvent *lockEvent(static_cast < CLockEvent * >(pEvent));
 //      NSLog(@"ExternalEventHandler, m_iEventType = EV_LOCK, mess: %@, bLock = %d, pct = %d", NSStringFromStdString(lockEvent->m_strMessage), lockEvent->m_bLock, lockEvent->m_iPercent);
@@ -194,6 +191,9 @@ void COSXDasherControl::ExternalEventHandler(Dasher::CEvent *pEvent) {
       NSLog(@"ExternalEventHandler, m_iEventType = EV_MESSAGE, mess: %@, id = %d, type = %d", NSStringFromStdString(messageEvent->m_strMessage), messageEvent->m_iID, messageEvent->m_iType);
       break;
     }
+    case EV_SCREEN_GEOM:
+      //no need to do anything, so avoid log message
+      break;
     default:
       NSLog(@"ExternalEventHandler, UNKNOWN m_iEventType = %d", pEvent->m_iEventType);
       break;
@@ -201,6 +201,33 @@ void COSXDasherControl::ExternalEventHandler(Dasher::CEvent *pEvent) {
   
 }
 
+unsigned int COSXDasherControl::ctrlMove(bool bForwards, CControlManager::EditDistance dist) {
+#ifdef DEBUG
+  std::cout << "Call to edit move, doing nothing" << std::endl;
+#endif
+  return [[dasherEdit allContext] length];
+}
+
+unsigned int COSXDasherControl::ctrlDelete(bool bForwards, CControlManager::EditDistance dist) {
+  if (!bForwards) {
+    const unsigned int iCurrentOffset([[dasherEdit allContext] length]);
+    int iStartOffset;
+    if (dist==CControlManager::EDIT_CHAR)
+      iStartOffset=(iCurrentOffset==0) ? 0 : iCurrentOffset-1;
+    else if (dist==CControlManager::EDIT_FILE)
+      iStartOffset=0;
+    else {
+      const CAlphInfo *pAlph(GetInfo(GetStringParameter(SP_ALPHABET_ID)));
+      const string &target(pAlph->GetText(dist==CControlManager::EDIT_WORD ? pAlph->GetSpaceSymbol() : pAlph->GetParagraphSymbol()));
+      NSRange range = [[dasherEdit allContext] rangeOfString:NSStringFromStdString(target) options:NSBackwardsSearch];
+      iStartOffset = (range.length==0) ? 0 : range.location; //0=> not found, so go to beginning
+    }
+    DASHER_ASSERT(iStartOffset<=[[dasherEdit allContext] length]);
+    [dasherEdit deleteCallback:[[dasherEdit allContext] substringFromIndex:iStartOffset] targetApp:[dasherApp targetAppUIElementRef]];
+  }
+  return [[dasherEdit allContext] length];
+}
+
 int COSXDasherControl::GetFileSize(const std::string &strFileName) {
   struct stat sStatInfo;
   
diff --git a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
index 38dbcf8..d6efe89 100755
--- a/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
+++ b/Src/MacOSX/Dasher.xcodeproj/project.pbxproj
@@ -317,8 +317,6 @@
 		19BEF4130C2291CE00275D06 /* colour.vowels2.xml in Resources */ = {isa = PBXBuildFile; fileRef = 19BEF4060C2291CE00275D06 /* colour.vowels2.xml */; };
 		19BEF4140C2291CE00275D06 /* colour.xml in Resources */ = {isa = PBXBuildFile; fileRef = 19BEF4070C2291CE00275D06 /* colour.xml */; };
 		19BEF4150C2291CE00275D06 /* colour.xsl in Resources */ = {isa = PBXBuildFile; fileRef = 19BEF4080C2291CE00275D06 /* colour.xsl */; };
-		19BEF41C0C22922400275D06 /* controllabels.dtd in Resources */ = {isa = PBXBuildFile; fileRef = 19BEF4190C22922400275D06 /* controllabels.dtd */; };
-		19BEF41D0C22922400275D06 /* controllabels.xml in Resources */ = {isa = PBXBuildFile; fileRef = 19BEF41A0C22922400275D06 /* controllabels.xml */; };
 		19C190AB0C3267D700979F34 /* DasherViewAqua.h in Headers */ = {isa = PBXBuildFile; fileRef = 19C190A90C3267D700979F34 /* DasherViewAqua.h */; };
 		19C190AC0C3267D700979F34 /* DasherViewAqua.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19C190AA0C3267D700979F34 /* DasherViewAqua.mm */; };
 		19C1AE820B130F18005C68D3 /* COSXMouseInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 19C1AE810B130F18005C68D3 /* COSXMouseInput.h */; };
@@ -349,6 +347,9 @@
 		33135354102C6D8E00E28220 /* CompassMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3313534C102C6D8E00E28220 /* CompassMode.h */; };
 		33135355102C6D8E00E28220 /* ButtonMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3313534D102C6D8E00E28220 /* ButtonMode.cpp */; };
 		33135356102C6D8E00E28220 /* ButtonMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3313534E102C6D8E00E28220 /* ButtonMode.h */; };
+		334DE238135E3E68007C8D6D /* control.textlabels.xml in Resources */ = {isa = PBXBuildFile; fileRef = 334DE236135E3E68007C8D6D /* control.textlabels.xml */; };
+		334DE239135E3E68007C8D6D /* control.xml in Resources */ = {isa = PBXBuildFile; fileRef = 334DE237135E3E68007C8D6D /* control.xml */; };
+		334DE23B135E3F4B007C8D6D /* control.dtd in Resources */ = {isa = PBXBuildFile; fileRef = 334DE23A135E3F4B007C8D6D /* control.dtd */; };
 		335901B41009E5A900821255 /* ConversionHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 335901B31009E5A900821255 /* ConversionHelper.cpp */; };
 		335DB0FB100B332C006DB155 /* alphabet.spyDict.xml in Resources */ = {isa = PBXBuildFile; fileRef = 335DB0FA100B332C006DB155 /* alphabet.spyDict.xml */; };
 		335DB101100B3358006DB155 /* training_spyDict.txt in Resources */ = {isa = PBXBuildFile; fileRef = 335DB100100B3358006DB155 /* training_spyDict.txt */; };
@@ -718,8 +719,6 @@
 		19BEF4060C2291CE00275D06 /* colour.vowels2.xml */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = colour.vowels2.xml; sourceTree = "<group>"; };
 		19BEF4070C2291CE00275D06 /* colour.xml */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = colour.xml; sourceTree = "<group>"; };
 		19BEF4080C2291CE00275D06 /* colour.xsl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = colour.xsl; sourceTree = "<group>"; };
-		19BEF4190C22922400275D06 /* controllabels.dtd */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = controllabels.dtd; sourceTree = "<group>"; };
-		19BEF41A0C22922400275D06 /* controllabels.xml */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = controllabels.xml; sourceTree = "<group>"; };
 		19C190A90C3267D700979F34 /* DasherViewAqua.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DasherViewAqua.h; sourceTree = "<group>"; };
 		19C190AA0C3267D700979F34 /* DasherViewAqua.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = DasherViewAqua.mm; sourceTree = "<group>"; };
 		19C1AE810B130F18005C68D3 /* COSXMouseInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = COSXMouseInput.h; sourceTree = "<group>"; };
@@ -758,6 +757,9 @@
 		3313534C102C6D8E00E28220 /* CompassMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompassMode.h; sourceTree = "<group>"; };
 		3313534D102C6D8E00E28220 /* ButtonMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ButtonMode.cpp; sourceTree = "<group>"; };
 		3313534E102C6D8E00E28220 /* ButtonMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButtonMode.h; sourceTree = "<group>"; };
+		334DE236135E3E68007C8D6D /* control.textlabels.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = control.textlabels.xml; sourceTree = "<group>"; };
+		334DE237135E3E68007C8D6D /* control.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = control.xml; sourceTree = "<group>"; };
+		334DE23A135E3F4B007C8D6D /* control.dtd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = control.dtd; sourceTree = "<group>"; };
 		335901B31009E5A900821255 /* ConversionHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionHelper.cpp; sourceTree = "<group>"; };
 		335DB0FA100B332C006DB155 /* alphabet.spyDict.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = alphabet.spyDict.xml; sourceTree = "<group>"; };
 		335DB100100B3358006DB155 /* training_spyDict.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = training_spyDict.txt; sourceTree = "<group>"; };
@@ -1252,16 +1254,6 @@
 			path = ../../Data/colours;
 			sourceTree = SOURCE_ROOT;
 		};
-		19BEF4180C22922400275D06 /* controllabels */ = {
-			isa = PBXGroup;
-			children = (
-				19BEF4190C22922400275D06 /* controllabels.dtd */,
-				19BEF41A0C22922400275D06 /* controllabels.xml */,
-			);
-			name = controllabels;
-			path = ../../Data/controllabels;
-			sourceTree = SOURCE_ROOT;
-		};
 		19C28FACFE9D520D11CA2CBB /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -1285,7 +1277,7 @@
 				19351BF304575C6C0000000A /* mktar.sh */,
 				190697550492B65B0000000A /* ReadmeDeveloper.txt */,
 				190697530492AACE0000000A /* Readme.html */,
-				19BEF4180C22922400275D06 /* controllabels */,
+				334DE235135E3E68007C8D6D /* control */,
 				19BEF3FC0C2291CE00275D06 /* colours */,
 				19BEF2CD0C228F7300275D06 /* alphabets */,
 				33E173A60F3E0B6400D19B38 /* training */,
@@ -1328,6 +1320,17 @@
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
+		334DE235135E3E68007C8D6D /* control */ = {
+			isa = PBXGroup;
+			children = (
+				334DE23A135E3F4B007C8D6D /* control.dtd */,
+				334DE236135E3E68007C8D6D /* control.textlabels.xml */,
+				334DE237135E3E68007C8D6D /* control.xml */,
+			);
+			name = control;
+			path = ../../Data/control;
+			sourceTree = SOURCE_ROOT;
+		};
 		33E173A60F3E0B6400D19B38 /* training */ = {
 			isa = PBXGroup;
 			children = (
@@ -1681,8 +1684,6 @@
 				19BEF4130C2291CE00275D06 /* colour.vowels2.xml in Resources */,
 				19BEF4140C2291CE00275D06 /* colour.xml in Resources */,
 				19BEF4150C2291CE00275D06 /* colour.xsl in Resources */,
-				19BEF41C0C22922400275D06 /* controllabels.dtd in Resources */,
-				19BEF41D0C22922400275D06 /* controllabels.xml in Resources */,
 				33E173C70F3E0B6400D19B38 /* Makefile.am in Resources */,
 				33E173C80F3E0B6400D19B38 /* training_albanian_SQ.txt in Resources */,
 				33E173CB0F3E0B6400D19B38 /* training_bengali_BD.txt in Resources */,
@@ -1713,6 +1714,9 @@
 				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 */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Src/MacOSX/DasherEdit.mm b/Src/MacOSX/DasherEdit.mm
index 1d54244..c49948f 100644
--- a/Src/MacOSX/DasherEdit.mm
+++ b/Src/MacOSX/DasherEdit.mm
@@ -54,7 +54,13 @@
 }
 
 -(NSString *)textAtOffset:(unsigned int)iOffset Length:(unsigned int)iLength {
-  DASHER_ASSERT(iOffset+iLength <= [allTextEntered length]);
+  //This does not hold if using control mode since it gets the offsets wrong...
+  //DASHER_ASSERT(iOffset+iLength <= [allTextEntered length]);
+  //Instead we shall handle all the out-of-bounds cases ourselves
+  // (substringWithRange is not at all forgiving!)
+  if (iOffset >= [allTextEntered length]) return @"";
+  if (iOffset+iLength > [allTextEntered length])
+    iLength = [allTextEntered length]-iOffset;
   return [allTextEntered substringWithRange:NSMakeRange(iOffset,iLength)];
 }
 
diff --git a/Src/Win32/Dasher.cpp b/Src/Win32/Dasher.cpp
index f850219..c45742f 100644
--- a/Src/Win32/Dasher.cpp
+++ b/Src/Win32/Dasher.cpp
@@ -115,9 +115,6 @@ void Dasher::CDasher::ExternalEventHandler(CEvent* pEvent) {
       m_pWindow->HandleParameterChange(iParam);
       m_pEdit->HandleParameterChange(iParam);
       break;
-    case EV_CONTROL:
-      m_pWindow->HandleControlEvent(((CControlEvent *)pEvent)->m_iID);
-      break;
     }
     case EV_EDIT:  {
       CEditEvent *pEvt(static_cast<CEditEvent *> (pEvent));
@@ -139,6 +136,15 @@ void Dasher::CDasher::ExternalEventHandler(CEvent* pEvent) {
   }
 }
 
+unsigned int Dasher::CDasher::ctrlMove(bool bForwards, CControlManager::EditDistance iDist) {
+  return m_pEdit->Move(bForwards, iDist);
+}
+
+unsigned int Dasher::CDasher::ctrlDelete(bool bForwards, CControlManager::EditDistance iDist) {
+  return m_pEdit->Delete(bForwards, iDist);
+}
+
+
 void Dasher::CDasher::GameMessageOut(int message, const void *messagedata)
 {
   SendMessage(m_hParent, WM_DASHER_GAME_MESSAGE, (WPARAM)message, (LPARAM)messagedata);
diff --git a/Src/Win32/Dasher.h b/Src/Win32/Dasher.h
index ffc4a08..3cc0a3a 100644
--- a/Src/Win32/Dasher.h
+++ b/Src/Win32/Dasher.h
@@ -40,6 +40,9 @@ public:
   void TakeFocus();
 
   void ExternalEventHandler(Dasher::CEvent *pEvent);
+  unsigned int ctrlMove(bool bForwards, CControlManager::EditDistance iDist);
+  unsigned int ctrlDelete(bool bForwards, CControlManager::EditDistance iDist);
+  
   void GameMessageOut(int message, const void* messagedata);
   
   virtual void WriteTrainFile(const std::string &filename, const std::string &strNewText);
diff --git a/Src/Win32/DasherWindow.cpp b/Src/Win32/DasherWindow.cpp
index 8660e99..5a2b84a 100644
--- a/Src/Win32/DasherWindow.cpp
+++ b/Src/Win32/DasherWindow.cpp
@@ -235,77 +235,6 @@ void CDasherWindow::HandleParameterChange(int iParameter) {
   }
 }
 
-void CDasherWindow::HandleControlEvent(int iID) {
-  // TODO: Some kind of automatic translation here? Or at least a table
-
-  switch(iID) {
-  case Dasher::CControlManager::CTL_MOVE_FORWARD_CHAR:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_FORWARDS, EDIT_CHAR);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_FORWARD_WORD:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_FORWARDS, EDIT_WORD);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_FORWARD_LINE:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_FORWARDS, EDIT_LINE);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_FORWARD_FILE: 
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_FORWARDS, EDIT_FILE);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_BACKWARD_CHAR:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_BACKWARDS, EDIT_CHAR);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_BACKWARD_WORD:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_BACKWARDS, EDIT_WORD);
-    break;
-  case Dasher::CControlManager::CTL_MOVE_BACKWARD_LINE:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_BACKWARDS, EDIT_LINE);    
-    break;
-  case Dasher::CControlManager::CTL_MOVE_BACKWARD_FILE:
-    if(m_pEdit)
-      m_pEdit->Move(EDIT_BACKWARDS, EDIT_FILE);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_FORWARD_CHAR:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_FORWARDS, EDIT_CHAR);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_FORWARD_WORD:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_FORWARDS, EDIT_WORD);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_FORWARD_LINE:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_FORWARDS, EDIT_LINE);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_FORWARD_FILE:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_FORWARDS, EDIT_FILE);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_BACKWARD_CHAR:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_BACKWARDS, EDIT_CHAR);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_BACKWARD_WORD:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_BACKWARDS, EDIT_WORD);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_BACKWARD_LINE:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_BACKWARDS, EDIT_LINE);
-    break;
-  case Dasher::CControlManager::CTL_DELETE_BACKWARD_FILE:
-    if(m_pEdit)
-      m_pEdit->Delete(EDIT_BACKWARDS, EDIT_FILE);
-    break;
-  }
-}
-
 LRESULT CDasherWindow::OnCommand(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
   const int wmId = LOWORD(wParam);
   const int wmEvent = HIWORD(wParam);
diff --git a/Src/Win32/DasherWindow.h b/Src/Win32/DasherWindow.h
index 8f99204..0f6e687 100644
--- a/Src/Win32/DasherWindow.h
+++ b/Src/Win32/DasherWindow.h
@@ -92,7 +92,6 @@ public:
 
   ///ACL making these public so can be called directly from CDasher,
   /// rather than sending a windows message.
-  void HandleControlEvent(int iID);
   void HandleParameterChange(int iParameter);
   
 private:
diff --git a/Src/Win32/Widgets/Edit.cpp b/Src/Win32/Widgets/Edit.cpp
index 853a864..09c7ebc 100644
--- a/Src/Win32/Widgets/Edit.cpp
+++ b/Src/Win32/Widgets/Edit.cpp
@@ -491,7 +491,7 @@ void CEdit::output(const std::string &sText) {
   m_Output += sText;
 }
 
-void CEdit::Move(int iDirection, int iDist) {
+int CEdit::Move(bool bForwards, CControlManager::EditDistance iDist) {
 
   // Unfortunately there doesn't seem to be a sane way of obtaining the caret
   // position (as opposed to the bounds of the selection), so we're just going
@@ -501,30 +501,21 @@ void CEdit::Move(int iDirection, int iDist) {
   int iEnd;
   SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
 
-//  int iStartLine;
-  int iEndLine;
-//  int iLineOffset;
-//  int iLineLength;
-//  int iLineStart;
-//  int iNumLines;
-  int iNumChars;
-
   HLOCAL hMemHandle;
   std::wstring strBufferText;
 
-  if(iDirection == EDIT_FORWARDS) {
+  if(bForwards) {
     switch(iDist) {
-    case EDIT_CHAR:
+    case CControlManager::EDIT_CHAR:
       //if(iStart != iEnd)
         ++iEnd;
-      iStart = iEnd;
       break;
-    case EDIT_WORD:
+    case CControlManager::EDIT_WORD: {
       // Hmm... words are hard - this is a rough and ready approximation:
 
 #ifndef _WIN32_WCE
       // TODO: Fix this on Windows CE
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       hMemHandle = (HLOCAL)SendMessage( EM_GETHANDLE, 0, 0);
       strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
       LocalUnlock(hMemHandle);
@@ -534,10 +525,10 @@ void CEdit::Move(int iDirection, int iDist) {
         iEnd = iNumChars + 1;
       else
         iEnd = iEnd + 1;
-      iStart = iEnd;
 #endif
+    }
       break;
-    case EDIT_LINE:
+    case CControlManager::EDIT_LINE: {
 /*      iEndLine = SendMessage( EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
       iLineOffset = iEnd - SendMessage( EM_LINEINDEX, (WPARAM)iEndLine, 0);
       iNumLines = SendMessage( EM_GETLINECOUNT, 0, 0);
@@ -558,30 +549,31 @@ void CEdit::Move(int iDirection, int iDist) {
 */    
       // Make it behave like the 'End' key, unless we're at the end of the current line.
 	  // Then go down a line.
-	  iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
+	  int iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
 	  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 1), 0) - 1; // end of this line
 	  if(iStart==iEnd)  // we were already at the end so go down a line
 		  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 2), 0) - 1;
-	  iStart = iEnd;
+    }
       break;
-    case EDIT_FILE: 
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+    case CControlManager::EDIT_FILE: {
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       iEnd = iNumChars + 1;
-      iStart = iEnd;
+    }
       break;
     }
   }
   else {
     switch(iDist) {
-    case EDIT_CHAR:
+    case CControlManager::EDIT_CHAR:
+        //ACL this case at least differs from Delete(bool,EditDistance):
+        // there we decrement iEnd whether or not iStart==iEnd.
       if( iStart == iEnd )
-        --iStart;
-      iEnd = iStart;
+        --iEnd;
       break;
-    case EDIT_WORD:
+    case CControlManager::EDIT_WORD: {
 #ifndef _WIN32_WCE
       // TODO: Fix this on Windows CE
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
       strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
       LocalUnlock(hMemHandle);
@@ -593,10 +585,10 @@ void CEdit::Move(int iDirection, int iDist) {
         else
           iEnd = iEnd + 1;
       }
-      iStart = iEnd;
 #endif
+    }
       break;
-    case EDIT_LINE:
+    case CControlManager::EDIT_LINE: {
 /*
       iStartLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iStart, 0);
       iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
@@ -616,47 +608,41 @@ void CEdit::Move(int iDirection, int iDist) {
       else
         iStart = iLineStart+iLineLength;
 */
-	  iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
+	  int iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
 	  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine), 0); // start of this line
 	  if(iStart==iEnd)  // we were already at the start so go up a line
 		  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine - 1), 0);
-	  iStart = iEnd;
+    }
       break;
-    case EDIT_FILE:
-      iStart = 0;
+    case CControlManager::EDIT_FILE:
       iEnd = 0;
       break;
     }
   }
-
+  iStart = iEnd;
   SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
   SendMessage(EM_SCROLLCARET, 0, 0); //scroll the caret into view!
+  return iStart;
 }
 
-void CEdit::Delete(int iDirection, int iDist) {
+int CEdit::Delete(bool bForwards, CControlManager::EditDistance iDist) {
   int iStart;
   int iEnd;
-  int iEndLine;
-//  int iLineOffset;
-//  int iLineLength;
-//  int iLineStart;
-//  int iNumLines;
-  int iNumChars;
 
   HLOCAL hMemHandle;
   std::wstring strBufferText;
 
   SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
 
-  if(iDirection == EDIT_FORWARDS) {
+  if(bForwards) {
     switch(iDist) {
-    case EDIT_CHAR:
+    case CControlManager::EDIT_CHAR:
       ++iEnd;
       break;
-    case EDIT_WORD:
+    case CControlManager::EDIT_WORD: {
 #ifndef _WIN32_WCE
       // TODO: Fix in Windows CE
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
       strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
       LocalUnlock(hMemHandle);
@@ -665,8 +651,9 @@ void CEdit::Delete(int iDirection, int iDist) {
       if(iEnd == -1)
         iEnd = iNumChars + 1;
 #endif
+    }  
       break;
-    case EDIT_LINE:
+    case CControlManager::EDIT_LINE: {
 /*
       iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
       iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
@@ -681,25 +668,29 @@ void CEdit::Delete(int iDirection, int iDist) {
           iEnd = iLineStart+iLineLength;
       }
   */
-	  iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
+	  int iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
 	  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 1), 0); // end of this line
 	  if(iStart==iEnd)  // we were already at the end so go down a line
 		  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 2), 0);
+    }
       break;
-    case EDIT_FILE: 
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+    case CControlManager::EDIT_FILE: {
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       iEnd = iNumChars + 1;
+    }
       break;
     }
   }
   else {
     switch(iDist) {
-    case EDIT_CHAR:
+    case CControlManager::EDIT_CHAR:
+        //ACL this case at least differs from that for Move(bool, EditDistance):
+        // there we only decrement if iStart==iEnd.
       --iEnd;
       break;
-    case EDIT_WORD:
+    case CControlManager::EDIT_WORD: {
 #ifndef _WIN32_WCE
-      iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
       hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
       strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
       LocalUnlock(hMemHandle);
@@ -712,8 +703,9 @@ void CEdit::Delete(int iDirection, int iDist) {
           iEnd = iEnd + 1;
       }
 #endif
+    }
       break;
-    case EDIT_LINE:
+    case CControlManager::EDIT_LINE: {
 /*       iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
       iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
       iNumLines = SendMessage(EM_GETLINECOUNT, 0, 0);
@@ -727,13 +719,13 @@ void CEdit::Delete(int iDirection, int iDist) {
           iEnd = iLineStart+iLineLength;
       }
 	  */
-	  iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
+	  int iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
 	  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine), 0); // start of this line
 	  if(iStart==iEnd)  // we were already at the start so go up a line
 		  iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine - 1), 0);
-
+    }
       break;
-    case EDIT_FILE:
+    case CControlManager::EDIT_FILE:
       iEnd = 0;
       break;
     }
@@ -741,6 +733,7 @@ void CEdit::Delete(int iDirection, int iDist) {
 
   SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
   SendMessage(EM_REPLACESEL, (WPARAM)true, (LPARAM)"");
+  return min(iStart, iEnd);
 }
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/Src/Win32/Widgets/Edit.h b/Src/Win32/Widgets/Edit.h
index 3f39545..c780e79 100644
--- a/Src/Win32/Widgets/Edit.h
+++ b/Src/Win32/Widgets/Edit.h
@@ -35,7 +35,7 @@ extern CComModule _Module;
 #include "../AppSettings.h"
 #include "../DasherAction.h"
 #include "../../DasherCore/DasherTypes.h"
-
+#include "../../DasherCore/ControlManager.h"
 #include <Oleacc.h>
 
 class CCanvas;
@@ -46,18 +46,6 @@ namespace Dasher {
   class CEvent;
 };
 
-enum {
-  EDIT_FORWARDS,
-  EDIT_BACKWARDS
-};
-
-enum {
-  EDIT_CHAR,
-  EDIT_WORD,
-  EDIT_LINE,
-  EDIT_FILE
-};
-
 class CEdit : public ATL::CWindowImpl<CEdit> {
  public:
   
@@ -96,8 +84,8 @@ class CEdit : public ATL::CWindowImpl<CEdit> {
   // As EN_UPDATE message go to parent, need this. void UserSave(HANDLE FileHandle);
   void UserOpen(HANDLE FileHandle);
   
-  void Move(int iDirection, int iDist);
-  void Delete(int iDirection, int iDist);
+  int Move(bool bForwards, Dasher::CControlManager::EditDistance iDist);
+  int Delete(bool bForwards, Dasher::CControlManager::EditDistance iDist);
   void SetKeyboardTarget(HWND hwnd);
   
   // Overriding file virtual functions
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.h b/Src/iPhone/Classes/CDasherInterfaceBridge.h
index 39d568a..f3cef4d 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.h
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.h
@@ -50,6 +50,8 @@ public:
   void Speak(const std::string &strText, bool bInterrupt);
   std::string GetAllContext();
   std::string GetContext(unsigned int iStart, unsigned int iLength);
+  unsigned int ctrlMove(bool bForwards, CControlManager::EditDistance dist);
+  unsigned int ctrlDelete(bool bForwards, CControlManager::EditDistance dist);
 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 779633e..0eaabfe 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.mm
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
@@ -180,42 +180,6 @@ void CDasherInterfaceBridge::ExternalEventHandler(Dasher::CEvent *pEvent) {
       }
 	  }
         break;
-    case EV_CONTROL:
-      switch (static_cast<CControlEvent *>(pEvent)->m_iID) {
-        case CControlManager::CTL_MOVE_FORWARD_CHAR:
-          [dasherApp move:EDIT_CHAR forwards:YES]; break;
-        case CControlManager::CTL_MOVE_FORWARD_WORD:
-          [dasherApp move:EDIT_WORD forwards:YES]; break;
-        case CControlManager::CTL_MOVE_FORWARD_LINE:
-          [dasherApp move:EDIT_LINE forwards:YES]; break;
-        case CControlManager::CTL_MOVE_FORWARD_FILE:
-          [dasherApp move:EDIT_FILE forwards:YES]; break;
-        case CControlManager::CTL_MOVE_BACKWARD_CHAR:
-          [dasherApp move:EDIT_CHAR forwards:NO]; break;
-        case CControlManager::CTL_MOVE_BACKWARD_WORD:
-          [dasherApp move:EDIT_WORD forwards:NO]; break;
-        case CControlManager::CTL_MOVE_BACKWARD_LINE:
-          [dasherApp move:EDIT_LINE forwards:NO]; break;
-        case CControlManager::CTL_MOVE_BACKWARD_FILE:
-          [dasherApp move:EDIT_FILE forwards:NO]; break;
-        case CControlManager::CTL_DELETE_FORWARD_CHAR:
-          [dasherApp del:EDIT_CHAR forwards:YES]; break;
-        case CControlManager::CTL_DELETE_FORWARD_WORD:
-          [dasherApp del:EDIT_WORD forwards:YES]; break;
-        case CControlManager::CTL_DELETE_FORWARD_LINE:
-          [dasherApp del:EDIT_LINE forwards:YES]; break;
-        case CControlManager::CTL_DELETE_FORWARD_FILE:
-          [dasherApp del:EDIT_FILE forwards:YES]; break;
-        case CControlManager::CTL_DELETE_BACKWARD_CHAR:
-          [dasherApp del:EDIT_CHAR forwards:NO]; break;
-        case CControlManager::CTL_DELETE_BACKWARD_WORD:
-          [dasherApp del:EDIT_WORD forwards:NO]; break;
-        case CControlManager::CTL_DELETE_BACKWARD_LINE:
-          [dasherApp del:EDIT_LINE forwards:NO]; break;
-        case CControlManager::CTL_DELETE_BACKWARD_FILE:
-        [dasherApp del:EDIT_FILE forwards:NO]; break;
-      }
-      break;
     case EV_LOCK:
     {
       CLockEvent *evt(static_cast<CLockEvent *>(pEvent));
@@ -261,6 +225,14 @@ string CDasherInterfaceBridge::GetContext(unsigned int iOffset, unsigned int iLe
   return StdStringFromNSString([dasherApp textAtOffset:iOffset Length:iLength]);
 }
 
+unsigned int CDasherInterfaceBridge::ctrlMove(bool bForwards, CControlManager::EditDistance dist) {
+  return [dasherApp move:dist forwards:bForwards];
+}
+
+unsigned int CDasherInterfaceBridge::ctrlDelete(bool bForwards, CControlManager::EditDistance dist) {
+  return [dasherApp del:dist forwards:bForwards];
+}
+
 int CDasherInterfaceBridge::GetFileSize(const std::string &strFileName) {
   struct stat sStatInfo;
   
diff --git a/Src/iPhone/Classes/DasherAppDelegate.h b/Src/iPhone/Classes/DasherAppDelegate.h
index 9880ee8..c40f898 100644
--- a/Src/iPhone/Classes/DasherAppDelegate.h
+++ b/Src/iPhone/Classes/DasherAppDelegate.h
@@ -12,13 +12,6 @@
 #import "TextView.h"
 #import "Actions.h"
 
-typedef enum {
-  EDIT_CHAR,
-  EDIT_WORD,
-  EDIT_LINE,
-  EDIT_FILE
-} EEditDistance;
-
 @class EAGLView;
 @class FliteTTS;
 
@@ -46,8 +39,8 @@ typedef enum {
 - (void)setAlphabet:(const CAlphInfo *)pAlph;
 - (void)outputCallback:(NSString *)s;
 - (void)deleteCallback:(NSString *)s;
-- (void)move:(EEditDistance)amt forwards:(BOOL)bForwards;
-- (void)del:(EEditDistance)amt forwards:(BOOL)bForwards;
+- (unsigned int)move:(CControlManager::EditDistance)amt forwards:(BOOL)bForwards;
+- (unsigned int)del:(CControlManager::EditDistance)amt forwards:(BOOL)bForwards;
 - (BOOL)supportsSpeech;
 - (void)speak:(NSString *)text interrupt:(BOOL)bInt;
 - (void)copy:(NSString *)text;
diff --git a/Src/iPhone/Classes/DasherAppDelegate.mm b/Src/iPhone/Classes/DasherAppDelegate.mm
index d8bdf9c..c4686d1 100644
--- a/Src/iPhone/Classes/DasherAppDelegate.mm
+++ b/Src/iPhone/Classes/DasherAppDelegate.mm
@@ -469,8 +469,8 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
       : @".";
 }
 
-- (int)find:(EEditDistance)amt forwards:(BOOL)bForwards {
-  if (amt==EDIT_FILE) return bForwards ? [text.text length] : 0;
+- (int)find:(CControlManager::EditDistance)amt forwards:(BOOL)bForwards {
+  if (amt==CControlManager::EDIT_FILE) return bForwards ? [text.text length] : 0;
   int pos = selectedText.location;
   for(;;) {
     if (bForwards) {
@@ -480,13 +480,13 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
     }
     NSString *lookFor;
     switch (amt) {
-      case EDIT_CHAR:
+      case CControlManager::EDIT_CHAR:
         //once only, never loop
         return pos;
-      case EDIT_WORD:
+      case CControlManager::EDIT_WORD:
         lookFor = m_wordBoundary;
         break;
-      case EDIT_LINE:
+      case CControlManager::EDIT_LINE:
         if (m_lineBoundary && [text.text compare:m_lineBoundary options:0 range:NSMakeRange(pos, [m_lineBoundary length])] == NSOrderedSame)
           return pos;
         lookFor = m_lineBoundary;
@@ -497,14 +497,15 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
   }
 }
 
-- (void)move:(EEditDistance)amt forwards:(BOOL)bForwards {
+- (unsigned int)move:(CControlManager::EditDistance)amt forwards:(BOOL)bForwards {
   selectedText.location = [self find:amt forwards:bForwards];
   selectedText.length=0;
   text.selectedRange = selectedText;
   [text scrollRangeToVisible:selectedText];
+  return selectedText.location;
 }
 
-- (void)del:(EEditDistance)amt forwards:(BOOL)bForwards {
+- (unsigned int)del:(CControlManager::EditDistance)amt forwards:(BOOL)bForwards {
   int to = [self find:amt forwards:bForwards];
   if (bForwards) {
     selectedText.length = to-selectedText.location;
@@ -513,6 +514,7 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
     selectedText.location = to;
   }
   [self outputCallback:@""];
+  return selectedText.location;
 }
 
 - (NSString *)allText {
diff --git a/Src/iPhone/Dasher.xcodeproj/project.pbxproj b/Src/iPhone/Dasher.xcodeproj/project.pbxproj
index 6d0d624..23cb35e 100755
--- a/Src/iPhone/Dasher.xcodeproj/project.pbxproj
+++ b/Src/iPhone/Dasher.xcodeproj/project.pbxproj
@@ -251,6 +251,7 @@
 		337691710F9CE8630083FEB2 /* StringParamController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 337691700F9CE8630083FEB2 /* StringParamController.mm */; };
 		337691860F9CEFC70083FEB2 /* InputMethodSelector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 337691850F9CEFC70083FEB2 /* InputMethodSelector.mm */; };
 		3378A23F1335425300A96C5D /* AbstractXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3378A23D1335425200A96C5D /* AbstractXMLParser.cpp */; };
+		3378A267133543B800A96C5D /* control.xml in Resources */ = {isa = PBXBuildFile; fileRef = 3378A266133543B800A96C5D /* control.xml */; };
 		337ECC1B10DD5E0700D0C6A5 /* ExpansionPolicy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 337ECC1910DD5E0700D0C6A5 /* ExpansionPolicy.cpp */; };
 		339F8A330FF5088000282847 /* CalibrationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 339F8A320FF5088000282847 /* CalibrationController.mm */; };
 		33C71AF20FF7B51700A20992 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 33C71AF10FF7B51700A20992 /* Default.png */; };
@@ -697,6 +698,7 @@
 		337691850F9CEFC70083FEB2 /* InputMethodSelector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMethodSelector.mm; sourceTree = "<group>"; };
 		3378A23D1335425200A96C5D /* AbstractXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractXMLParser.cpp; sourceTree = "<group>"; };
 		3378A23E1335425200A96C5D /* AbstractXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractXMLParser.h; sourceTree = "<group>"; };
+		3378A266133543B800A96C5D /* control.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = control.xml; path = ../../Data/control/control.xml; sourceTree = SOURCE_ROOT; };
 		337ECC1910DD5E0700D0C6A5 /* ExpansionPolicy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExpansionPolicy.cpp; sourceTree = "<group>"; };
 		337ECC1A10DD5E0700D0C6A5 /* ExpansionPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExpansionPolicy.h; sourceTree = "<group>"; };
 		339F8A310FF5088000282847 /* CalibrationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CalibrationController.h; sourceTree = "<group>"; };
@@ -814,6 +816,7 @@
 		29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
 			isa = PBXGroup;
 			children = (
+				3378A266133543B800A96C5D /* control.xml */,
 				332F3495103D8F54008448D7 /* colours */,
 				331F28B60F7A9C270044EB9C /* alphabets */,
 				33627F980F7A82CE000C8818 /* training */,
@@ -1537,6 +1540,7 @@
 				3302678111B8026900C07880 /* scissors.png in Resources */,
 				3302678C11B8040D00C07880 /* spanner_lg.png in Resources */,
 				3302678D11B8040D00C07880 /* spanner.png in Resources */,
+				3378A267133543B800A96C5D /* control.xml in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/configure.ac b/configure.ac
index 64be616..c5902fd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -441,7 +441,7 @@ AC_CONFIG_FILES([Data/dasher.desktop.in
 		 Data/training/Makefile
 		 Data/alphabets/Makefile
 		 Data/colours/Makefile
-		 Data/controllabels/Makefile
+		 Data/control/Makefile
 		 Data/GUI/Makefile
 		 Data/Help/Makefile
 		 Data/Help/Gnome/Makefile



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