Matt Finnicum : riched20: Implement EM_SETUNDOLIMIT and its conformance tests.

Alexandre Julliard julliard at wine.codeweavers.com
Tue May 16 06:24:44 CDT 2006


Module: wine
Branch: refs/heads/master
Commit: a379ac9ef5b987d7071593daf0a63af31c89499c
URL:    http://source.winehq.org/git/?p=wine.git;a=commit;h=a379ac9ef5b987d7071593daf0a63af31c89499c

Author: Matt Finnicum <mattfinn at gmail.com>
Date:   Mon May 15 14:00:15 2006 -0400

riched20: Implement EM_SETUNDOLIMIT and its conformance tests.

---

 dlls/riched20/editor.c       |   21 ++++++++++++--
 dlls/riched20/editstr.h      |    4 ++-
 dlls/riched20/tests/editor.c |   65 ++++++++++++++++++++++++++++++++++++++++++
 dlls/riched20/undo.c         |   27 +++++++++++++++++
 4 files changed, 112 insertions(+), 5 deletions(-)

diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index d73690c..3e8c834 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -117,7 +117,7 @@
   + EM_SETTEXTEX 3.0 (unicode only, no rich text insertion handling, proper style?)
   - EM_SETTEXTMODE 2.0
   - EM_SETTYPOGRAPHYOPTIONS 3.0
-  - EM_SETUNDOLIMIT 2.0
+  + EM_SETUNDOLIMIT 2.0
   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
   - EM_SETWORDBREAKPROCEX
   - EM_SETWORDWRAPMODE 1.0asian
@@ -229,6 +229,9 @@ #include "shlwapi.h"
 #include "imm.h"
 #include "textserv.h"
 #include "rtf.h"
+
+#define STACK_SIZE_DEFAULT  100
+#define STACK_SIZE_MAX     1000
  
 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
 
@@ -1119,7 +1122,9 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) 
   ed->bCaretAtEnd = FALSE;
   ed->nEventMask = 0;
   ed->nModifyStep = 0;
-  ed->pUndoStack = ed->pRedoStack = NULL;
+  ed->pUndoStack = ed->pRedoStack = ed->pUndoStackBottom = NULL;
+  ed->nUndoStackSize = 0;
+  ed->nUndoLimit = STACK_SIZE_DEFAULT;
   ed->nUndoMode = umAddToUndo;
   ed->nParagraphs = 1;
   ed->nLastSelStart = ed->nLastSelEnd = 0;
@@ -1426,7 +1431,6 @@ LRESULT WINAPI RichEditANSIWndProc(HWND 
   UNSUPPORTED_MSG(EM_SETTABSTOPS)
   UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
-  UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
   UNSUPPORTED_MSG(EM_SHOWSCROLLBAR)
   UNSUPPORTED_MSG(WM_SETFONT)
@@ -1477,6 +1481,17 @@ LRESULT WINAPI RichEditANSIWndProc(HWND 
     TRACE("EM_EXGETSEL = (%ld,%ld)\n", pRange->cpMin, pRange->cpMax);
     return 0;
   }
+  case EM_SETUNDOLIMIT:
+  {
+    if ((int)wParam < 0)
+      editor->nUndoLimit = STACK_SIZE_DEFAULT;
+    else
+      editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
+    /* Setting a max stack size keeps wine from getting killed 
+      for hogging memory. Windows allocates all this memory at once, so
+      no program would realisticly set a value above our maxiumum. */  
+    return editor->nUndoLimit;
+  }
   case EM_CANUNDO:
     return editor->pUndoStack != NULL;
   case EM_CANREDO:
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index 0911e90..bb151fa 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -298,7 +298,9 @@ typedef struct tagME_TextEditor
   BOOL bCaretAtEnd;
   int nEventMask;
   int nModifyStep;
-  ME_DisplayItem *pUndoStack, *pRedoStack;
+  ME_DisplayItem *pUndoStack, *pRedoStack, *pUndoStackBottom;
+  int nUndoStackSize;
+  int nUndoLimit;
   ME_UndoMode nUndoMode;
   int nParagraphs;
   int nLastSelStart, nLastSelEnd;
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 6b80903..837fb15 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -747,6 +747,70 @@ static void test_EM_SCROLL()
   DestroyWindow(hwndRichEdit);
 }
 
+static void test_EM_SETUNDOLIMIT()
+{
+  /* cases we test for:
+   * default behaviour - limiting at 100 undo's 
+   * undo disabled - setting a limit of 0
+   * undo limited -  undo limit set to some to some number, like 2
+   * bad input - sending a negative number should default to 100 undo's */
+ 
+  HWND hwndRichEdit = new_richedit(NULL);
+  CHARRANGE cr;
+  int i;
+  int result;
+  
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
+  cr.cpMin = 0;
+  cr.cpMax = 1;
+  SendMessage(hwndRichEdit, WM_COPY, 0, 0);
+    /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
+      also, multiple pastes don't combine like WM_CHAR would */
+  SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
+
+  /* first case - check the default */
+  SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
+  for (i=0; i<101; i++) /* Put 101 undo's on the stack */
+    SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
+  for (i=0; i<100; i++) /* Undo 100 of them */
+    SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
+  ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
+     "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
+
+  /* second case - cannot undo */
+  SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
+  SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
+  SendMessage(hwndRichEdit,
+              WM_PASTE, 0, 0); /* Try to put something in the undo stack */
+  ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
+     "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
+
+  /* third case - set it to an arbitrary number */
+  SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
+  SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
+  SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
+  SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
+  SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
+  /* If SETUNDOLIMIT is working, there should only be two undo's after this */
+  ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
+     "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
+  SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
+  ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
+     "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
+  SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
+  ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
+     "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
+  
+  /* fourth case - setting negative numbers should default to 100 undos */
+  SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
+  result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
+  ok (result == 100, 
+      "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100",result);
+      
+  DestroyWindow(hwndRichEdit);
+}
+
+
 START_TEST( editor )
 {
   MSG msg;
@@ -764,6 +828,7 @@ START_TEST( editor )
   test_EM_SETOPTIONS();
   test_WM_GETTEXT();
   test_EM_AUTOURLDETECT();
+  test_EM_SETUNDOLIMIT();
 
   /* Set the environment variable WINETEST_RICHED20 to keep windows
    * responsive and open for 30 seconds. This is useful for debugging.
diff --git a/dlls/riched20/undo.c b/dlls/riched20/undo.c
index 916dcb3..f8895cb 100644
--- a/dlls/riched20/undo.c
+++ b/dlls/riched20/undo.c
@@ -32,7 +32,8 @@ void ME_EmptyUndoStack(ME_TextEditor *ed
   TRACE("Emptying undo stack\n");
 
   p = editor->pUndoStack;
-  editor->pUndoStack = NULL;
+  editor->pUndoStack = editor->pUndoStackBottom = NULL;
+  editor->nUndoStackSize = 0;
   while(p) {
     pNext = p->next;
     ME_DestroyDisplayItem(p);    
@@ -50,6 +51,8 @@ void ME_EmptyUndoStack(ME_TextEditor *ed
 ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) {
   if (editor->nUndoMode == umIgnore)
     return NULL;
+  else if (editor->nUndoLimit == 0)
+    return NULL;
   else
   {
     ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem);
@@ -93,10 +96,31 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEdito
         TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
       else
         TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
+
       pItem->next = editor->pUndoStack;
+      if (type == diUndoEndTransaction)
+        editor->nUndoStackSize++;
       if (editor->pUndoStack)
         editor->pUndoStack->prev = pItem;
+      else
+        editor->pUndoStackBottom = pItem;
       editor->pUndoStack = pItem;
+      
+      if (editor->nUndoStackSize > editor->nUndoLimit)
+      { /* remove oldest undo from stack */
+        ME_DisplayItem *p = editor->pUndoStackBottom;
+        while (p->type !=diUndoEndTransaction)
+          p = p->prev; /*find new stack bottom */
+        editor->pUndoStackBottom = p->prev;
+          editor->pUndoStackBottom->next = NULL;
+        do
+        {
+          ME_DisplayItem *pp = p->next;
+          ME_DestroyDisplayItem(p);
+          p = pp;
+        } while (p);
+        editor->nUndoStackSize--;
+      }
       /* any new operation (not redo) clears the redo stack */
       if (editor->nUndoMode == umAddToUndo) {
         ME_DisplayItem *p = editor->pRedoStack;
@@ -233,6 +257,7 @@ void ME_Undo(ME_TextEditor *editor) {
   } while(p && p->type != diUndoEndTransaction);
   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
   editor->pUndoStack = p;
+  editor->nUndoStackSize--;
   if (p)
     p->prev = NULL;
   editor->nUndoMode = nMode;




More information about the wine-cvs mailing list