[dasher: 53/217] Added edit distance for sentence and paragraph.



commit afb084f26cda379ee61b8cda125278a7b06b55c5
Author: ipomoena <amajorek google com>
Date:   Tue Oct 6 17:49:11 2015 -0700

    Added edit distance for sentence and paragraph.
    
    For now implemented only in Win32 and with hardcoded separators.
    After adding speak sentence and speak paragraph ctrl commands reading
    prepared text will be possible

 Src/DasherCore/ControlManager.cpp |   10 +-
 Src/DasherCore/ControlManager.h   |    2 +-
 Src/Win32/Widgets/Edit.cpp        |  332 ++++++++++++-------------------------
 Src/Win32/Widgets/Edit.h          |    1 +
 4 files changed, 114 insertions(+), 231 deletions(-)
---
diff --git a/Src/DasherCore/ControlManager.cpp b/Src/DasherCore/ControlManager.cpp
index 3cc0bc1..8ba9252 100644
--- a/Src/DasherCore/ControlManager.cpp
+++ b/Src/DasherCore/ControlManager.cpp
@@ -279,9 +279,13 @@ CControlBase::Action *CControlManager::parseAction(const XML_Char *name, const X
         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;
+          dist = EDIT_LINE;
+        else if (strcmp(*(atts + 1), "sentence") == 0)
+          dist = EDIT_SENTENCE;
+        else if (strcmp(*(atts + 1), "paragraph") == 0)
+          dist = EDIT_PARAGRAPH;
+        else if (strcmp(*(atts + 1), "file") == 0)
+          dist = EDIT_FILE;
       }
       atts+=2;
     }
diff --git a/Src/DasherCore/ControlManager.h b/Src/DasherCore/ControlManager.h
index 3b866aa..d83e54d 100644
--- a/Src/DasherCore/ControlManager.h
+++ b/Src/DasherCore/ControlManager.h
@@ -201,7 +201,7 @@ namespace Dasher {
     void HandleEvent(int iParameter);
 
     typedef enum {
-      EDIT_CHAR, EDIT_WORD, EDIT_LINE, EDIT_FILE
+      EDIT_CHAR, EDIT_WORD, EDIT_SENTENCE, EDIT_PARAGRAPH, EDIT_FILE, EDIT_LINE, EDIT_SELECTION,
     } EditDistance;
 
     ///Recomputes which of pause, stop, speak and copy the root control node should have amongst its 
children.
diff --git a/Src/Win32/Widgets/Edit.cpp b/Src/Win32/Widgets/Edit.cpp
index df36669..2382cd0 100644
--- a/Src/Win32/Widgets/Edit.cpp
+++ b/Src/Win32/Widgets/Edit.cpp
@@ -337,248 +337,126 @@ void CEdit::output(const std::string &sText) {
   m_Output += sText;
 }
 
-int CEdit::Move(bool bForwards, CControlManager::EditDistance iDist) {
+int _findAfterChars(const wchar_t* str, const wchar_t* chrs, int startPos) {
+  const wchar_t* ptr = str + startPos;
+  ptr += wcscspn(ptr, chrs);
+  ptr += wcsspn(ptr, chrs);
+  return ptr - str;
+}
+
+int _findBeforeChars(const wchar_t* str, const wchar_t* chrs, int startPos) {
+  const wchar_t* ptr = str + startPos;
+  // over non separators
+  while (ptr > str && !wcschr(chrs, *ptr)) {
+    --ptr;
+  }
+  // over separators
+  while (ptr > str && wcschr(chrs, *ptr)) {
+    --ptr;
+  }
+  // over non separators
+  while (ptr > str && !wcschr(chrs, *ptr)) {
+    --ptr;
+  }
+  if (wcschr(chrs, *ptr))
+    ++ptr;
 
-  // 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
-  // to have to assume that the caret is at the end...
+  return max(0, ptr - str);
+}
 
-  int iStart;
-  int iEnd;
-  SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
+// amajorek: Add list of word and sentence separators to alphabet definitions. 
+// And use that list instead of hardcoded. 
+// Fix exery place where boundaries are needed.
+const wchar_t* _wordSeparators = L" \t\v\f\r\n";
+const wchar_t* _sentenceSeparators = L".?!\r\n";
+const wchar_t* _paragraphSeparators = L"\r\n";
 
-  HLOCAL hMemHandle;
-  std::wstring strBufferText;
+void CEdit::GetRange(bool bForwards, CControlManager::EditDistance iDist, int* pStart, int* pEnd) {
+  int& iStart = *pStart;
+  int& iEnd = *pEnd;
 
-  if(bForwards) {
-    switch(iDist) {
-    case CControlManager::EDIT_CHAR:
-      //if(iStart != iEnd)
-        ++iEnd;
-      break;
-    case CControlManager::EDIT_WORD: {
-      // Hmm... words are hard - this is a rough and ready approximation:
+  switch (iDist) {
+  case CControlManager::EDIT_CHAR:
+    if (bForwards)
+      iEnd = min(iEnd + 1, (int)SendMessage(WM_GETTEXTLENGTH, 0, 0));
+    else
+      iStart = max(iStart - 1, 0);
+    break;
 
-#ifndef _WIN32_WCE
-      // TODO: Fix this on Windows CE
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      hMemHandle = (HLOCAL)SendMessage( EM_GETHANDLE, 0, 0);
-      strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
-      LocalUnlock(hMemHandle);
-
-      iEnd = strBufferText.find(' ', iEnd+1);
-      if(iEnd == -1)
-        iEnd = iNumChars + 1;
-      else
-        iEnd = iEnd + 1;
-#endif
-    }
-      break;
-    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);
-      if( iEndLine < iNumLines - 1) {
-        ++iEndLine;
-        iLineStart = SendMessage( EM_LINEINDEX, (WPARAM)iEndLine, 0);
-        iLineLength = SendMessage( EM_LINELENGTH, (WPARAM)iEndLine, 0);
-        if( iLineOffset < iLineLength )
-          iEnd = iLineStart+iLineOffset;
-        else
-          iEnd = iLineStart+iLineLength;
-      }
-         else if(iEndLine == iNumLines - 1) {
-               // we're on the last line so go to end of file
-               iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-        iEnd = iNumChars + 1;
-      }
-*/    
+  case CControlManager::EDIT_LINE: {
+    if (bForwards) {
       // Make it behave like the 'End' key, unless we're at the end of the current line.
-         // Then go down a line.
-         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;
-    }
-      break;
-    case CControlManager::EDIT_FILE: {
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      iEnd = iNumChars + 1;
+      // Then go down a line.
+      int iEndLine = SendMessage(EM_LINEFROMCHAR, iEnd, 0);
+      int iNewEnd = SendMessage(EM_LINEINDEX, iEndLine + 1, 0) - 1; // end of this line
+      // if we were already at the end so go down a line
+      if (iNewEnd <= iEnd)
+        iNewEnd = SendMessage(EM_LINEINDEX, iEndLine + 2, 0) - 1;
+      // on last line go to the end of text 
+      if (iNewEnd <= iEnd)
+        iNewEnd = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+      iEnd = max(0, iNewEnd);
     }
-      break;
-    }
-  }
-  else {
-    switch(iDist) {
-    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 )
-        --iEnd;
-      break;
-    case CControlManager::EDIT_WORD: {
-#ifndef _WIN32_WCE
-      // TODO: Fix this on Windows CE
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
-      strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
-      LocalUnlock(hMemHandle);
-
-      if(iEnd > 0) {
-        iEnd = strBufferText.rfind(' ', iEnd-2);
-        if(iEnd == -1)
-          iEnd = 0;
-        else
-          iEnd = iEnd + 1;
-      }
-#endif
-    }
-      break;
-    case CControlManager::EDIT_LINE: {
-/*
-      iStartLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iStart, 0);
-      iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
-      iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
-      if( iStartLine > 0)
-        --iStartLine;
-         else if( iStartLine == 0)
-         {
-           // we're on the first line so go to start of file...
-           iStart = iEnd = 0;
-               break;
-         }
-      iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iStartLine, 0);
-      iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iStartLine, 0);
-      if( iLineOffset < iLineLength )
-        iStart = iLineStart+iLineOffset;
-      else
-        iStart = iLineStart+iLineLength;
-*/
-         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 CControlManager::EDIT_FILE:
-      iEnd = 0;
-      break;
+    else {
+      int iStartLine = SendMessage(EM_LINEFROMCHAR, iStart, 0);
+      int iNewStart = SendMessage(EM_LINEINDEX, iStartLine, 0); // start of this line
+      // if we were already at the start so go up a line
+      iStart = (iNewStart == iStart && iStartLine>0) ? SendMessage(EM_LINEINDEX, iStartLine - 1, 0) : 
iNewStart;
     }
+    break;
   }
-  iStart = iEnd;
-  SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
-  SendMessage(EM_SCROLLCARET, 0, 0); //scroll the caret into view!
-  return iStart;
-}
-
-int CEdit::Delete(bool bForwards, CControlManager::EditDistance iDist) {
-  int iStart;
-  int iEnd;
-
-  HLOCAL hMemHandle;
-  std::wstring strBufferText;
 
-  SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
+  case CControlManager::EDIT_FILE:
+    if (bForwards)
+      iEnd = SendMessage(WM_GETTEXTLENGTH, 0, 0);
+    else
+      iStart = 0;
+    break;
 
-  if(bForwards) {
-    switch(iDist) {
-    case CControlManager::EDIT_CHAR:
-      ++iEnd;
-      break;
-    case CControlManager::EDIT_WORD: {
-#ifndef _WIN32_WCE
-      // TODO: Fix in Windows CE
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
-      strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
-      LocalUnlock(hMemHandle);
-
-      iEnd = strBufferText.find(' ', iEnd+1);
-      if(iEnd == -1)
-        iEnd = iNumChars + 1;
-#endif
-    }  
-      break;
-    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);
-      if( iEndLine < iNumLines - 1) {
-        ++iEndLine;
-        iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
-        iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iEndLine, 0);
-        if( iLineOffset < iLineLength )
-          iEnd = iLineStart+iLineOffset;
-        else
-          iEnd = iLineStart+iLineLength;
-      }
-  */
-         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 CControlManager::EDIT_FILE: {
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      iEnd = iNumChars + 1;
-    }
-      break;
-    }
+  case CControlManager::EDIT_WORD:
+  case CControlManager::EDIT_SENTENCE:
+  case CControlManager::EDIT_PARAGRAPH:
+  {
+    const wchar_t* separators = L"";
+    if (iDist == CControlManager::EDIT_WORD)
+      separators = _wordSeparators;
+    else if (iDist == CControlManager::EDIT_SENTENCE)
+      separators = _sentenceSeparators;
+    else if (iDist == CControlManager::EDIT_PARAGRAPH)
+      separators = _paragraphSeparators;
+
+    CString wideText;
+    GetWindowText(wideText);
+    if (bForwards)
+      iEnd = _findAfterChars(wideText, separators, iEnd);
+    else
+      iStart = _findBeforeChars(wideText, separators, max(0, iStart-1));
+    break;
   }
-  else {
-    switch(iDist) {
-    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 CControlManager::EDIT_WORD: {
-#ifndef _WIN32_WCE
-      int iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
-      hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
-      strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
-      LocalUnlock(hMemHandle);
-
-      if(iEnd > 0) {
-        iEnd = strBufferText.rfind(' ', iEnd-2);
-        if(iEnd == -1)
-          iEnd = 0;
-        else
-          iEnd = iEnd + 1;
-      }
-#endif
-    }
-      break;
-    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);
-      if(iEndLine > 0) {
-        --iEndLine;
-        iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
-        iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iEndLine, 0);
-        if( iLineOffset < iLineLength )
-          iEnd = iLineStart+iLineOffset;
-        else
-          iEnd = iLineStart+iLineLength;
-      }
-         */
-         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 CControlManager::EDIT_FILE:
-      iEnd = 0;
-      break;
-    }
   }
+}
 
+int CEdit::Move(bool bForwards, CControlManager::EditDistance iDist) {
+  int iStart = 0;
+  int iEnd = 0;
+  SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
+  if (iStart == iEnd) // Ignore distance if text is selected. 
+    GetRange(bForwards, iDist, &iStart, &iEnd);
+  int pos = bForwards ? iEnd : iStart;
+  SendMessage(EM_SETSEL, (WPARAM)pos, (LPARAM)pos);
+  SendMessage(EM_SCROLLCARET, 0, 0); //scroll the caret into view!
+  return pos;
+}
+
+int CEdit::Delete(bool bForwards, CControlManager::EditDistance iDist) {
+  int iStart = 0;
+  int iEnd = 0;
+  SendMessage(EM_GETSEL, (WPARAM)iStart, (LPARAM)iEnd);
+  if (iStart == iEnd) // Ignore distance if text is selected. 
+    GetRange(bForwards, iDist, &iStart, &iEnd);
   SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
   SendMessage(EM_REPLACESEL, (WPARAM)true, (LPARAM)TEXT(""));
+  SendMessage(EM_SCROLLCARET, 0, 0); //scroll the caret into view!
   return min(iStart, iEnd);
 }
 
diff --git a/Src/Win32/Widgets/Edit.h b/Src/Win32/Widgets/Edit.h
index 68d31ce..2c712ab 100644
--- a/Src/Win32/Widgets/Edit.h
+++ b/Src/Win32/Widgets/Edit.h
@@ -138,6 +138,7 @@ class CEdit : public ATL::CWindowImpl<CEdit> {
   void TNew(const Tstring & filename);
   bool TOpen(const Tstring & filename);
   bool TSaveAs(const Tstring & filename);
+  void GetRange(bool bForwards, Dasher::CControlManager::EditDistance iDist, int* iStart, int* iEnd);
   
   HFONT m_Font;
   std::string m_FontName;


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