riched20: new selection invalidation logic

Krzysztof Foltman wdev at foltman.com
Mon Aug 7 10:27:16 CDT 2006


ChangeLog:
  * New, clean, simple selection repaint logic - should fix all 
outstanding refresh issues (and bug#5882)

Krzysztof
-------------- next part --------------
diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c
index 5124822..5096bf8 100644
--- a/dlls/riched20/caret.c
+++ b/dlls/riched20/caret.c
@@ -24,6 +24,7 @@ #include "editor.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
 
+#define static
 
 static BOOL
 ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs);
@@ -87,7 +88,7 @@ void ME_SetSelection(ME_TextEditor *edit
     editor->pCursors[1].nOffset = 0; 
     editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun); 
     editor->pCursors[0].nOffset = 0; 
-    ME_Repaint(editor);
+    ME_InvalidateSelection(editor);
     ME_ClearTempStyle(editor);
     return;
   }
@@ -119,6 +120,8 @@ ME_GetCursorCoordinates(ME_TextEditor *e
   assert(!pCursor->nOffset || !editor->bCaretAtEnd);
   assert(height && x && y);
   assert(!(ME_GetParagraph(pCursorRun)->member.para.nFlags & MEPF_REWRAP));
+  assert(pCursor->pRun);
+  assert(pCursor->pRun->type == diRun);
   
   if (pCursorRun->type == diRun) {
     ME_DisplayItem *row = ME_FindItemBack(pCursorRun, diStartRowOrParagraph);
@@ -127,7 +130,7 @@ ME_GetCursorCoordinates(ME_TextEditor *e
       HDC hDC = GetDC(editor->hWnd);
       ME_Context c;
       ME_DisplayItem *run = pCursorRun;
-      ME_DisplayItem *para;
+      ME_DisplayItem *para = NULL;
       SIZE sz = {0, 0};
     
       ME_InitContext(&c, editor, hDC);
@@ -135,19 +138,25 @@ ME_GetCursorCoordinates(ME_TextEditor *e
       if (!pCursor->nOffset && !editor->bCaretAtEnd)
       {
         ME_DisplayItem *prev = ME_FindItemBack(pCursorRun, diRunOrStartRow);
+        assert(prev);
         if (prev->type == diRun)
           pSizeRun = prev;
       }
       assert(row->type == diStartRow); /* paragraph -> run without start row ?*/
       para = ME_FindItemBack(row, diParagraph);
+      assert(para);
+      assert(para->type == diParagraph);
       if (editor->bCaretAtEnd && !pCursor->nOffset && 
           run == ME_FindItemFwd(row, diRun))
       {
         ME_DisplayItem *tmp = ME_FindItemBack(row, diRunOrParagraph);
+        assert(tmp);
         if (tmp->type == diRun)
         {
           row = ME_FindItemBack(tmp, diStartRow);
           pSizeRun = run = tmp;
+          assert(run);
+          assert(run->type == diRun);
           sz = ME_GetRunSize(&c, &para->member.para, &run->member.run, ME_StrLen(run->member.run.strText));
         }
       }
@@ -175,6 +184,7 @@ ME_MoveCaret(ME_TextEditor *editor)
 {
   int x, y, height;
 
+  ME_WrapMarkedParagraphs(editor);
   ME_GetCursorCoordinates(editor, &editor->pCursors[0], &x, &y, &height);
   CreateCaret(editor->hWnd, NULL, 0, height);
   SetCaretPos(x, y);
@@ -992,6 +1002,10 @@ static void ME_ArrowPageDown(ME_TextEdit
 static void ME_ArrowHome(ME_TextEditor *editor, ME_Cursor *pCursor)
 {
   ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
+  /* bCaretAtEnd doesn't make sense if the cursor isn't set at the
+  first character of the next row */
+  assert(!editor->bCaretAtEnd || !pCursor->nOffset);
+  ME_WrapMarkedParagraphs(editor);
   if (pRow) {
     ME_DisplayItem *pRun;
     if (editor->bCaretAtEnd && !pCursor->nOffset) {
@@ -1150,10 +1164,8 @@ ME_ArrowKey(ME_TextEditor *editor, int n
   ME_Cursor *p = &editor->pCursors[nCursor];
   ME_Cursor tmp_curs = *p;
   BOOL success = FALSE;
-
-  if (ME_IsSelection(editor) && !extend)
-    ME_InvalidateSelection(editor);
   
+  ME_CheckCharOffsets(editor);
   editor->nUDArrowX = -1;
   switch(nVKey) {
     case VK_LEFT:
@@ -1202,8 +1214,8 @@ ME_ArrowKey(ME_TextEditor *editor, int n
     editor->pCursors[1] = tmp_curs;
   *p = tmp_curs;
   
-  if (ME_IsSelection(editor))
-    ME_InvalidateSelection(editor);
+  ME_InvalidateSelection(editor);
+  ME_Repaint(editor);
   HideCaret(editor->hWnd);
   ME_EnsureVisible(editor, tmp_curs.pRun); 
   ME_ShowCaret(editor);
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 1268782..7c45edb 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -1048,7 +1048,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD n
         ME_DeleteTextAtCursor(editor, 1, 1);
       else
         return TRUE;
-      ME_QueueInvalidateFromCursor(editor, 1);
+      ME_CommitUndo(editor);
       ME_UpdateRepaint(editor);
       ME_SendRequestResize(editor, FALSE);
       return TRUE;
@@ -1130,6 +1130,7 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) 
   ed->nUndoMode = umAddToUndo;
   ed->nParagraphs = 1;
   ed->nLastSelStart = ed->nLastSelEnd = 0;
+  ed->pLastSelStartPara = ed->pLastSelEndPara = ME_FindItemFwd(ed->pBuffer->pFirst, diParagraph);
   ed->nScrollPosY = 0;
   ed->nZoomNumerator = ed->nZoomDenominator = 0;
   ed->bRedraw = TRUE;
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index 93952fd..4409069 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -202,10 +202,12 @@ ME_DisplayItem *ME_MakeRow(int height, i
 void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd);
 void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp);
 BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor);
+void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor);
 void ME_SendRequestResize(ME_TextEditor *editor, BOOL force);
 
 /* para.c */
 ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run); 
+void ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayItem **para_end);
 void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *editor);
 ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style);
 ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp);
@@ -217,6 +219,7 @@ void ME_GetParaFormat(ME_TextEditor *edi
 void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
 /* marks from first up to (but not including) last */
 void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last);
+void ME_MarkForPainting(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last);
 void ME_MarkAllForWrapping(ME_TextEditor *editor);
 
 /* paint.c */
@@ -230,7 +233,6 @@ int ME_GetYScrollPos(ME_TextEditor *edit
 void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun);
 COLORREF ME_GetBackColor(ME_TextEditor *editor);
 void ME_Scroll(ME_TextEditor *editor, int cx, int cy);
-void ME_InvalidateFromOfs(ME_TextEditor *editor, int nCharOfs);
 void ME_InvalidateSelection(ME_TextEditor *editor);
 void ME_QueueInvalidateFromCursor(ME_TextEditor *editor, int nCursor);
 BOOL ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator);
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index 7d096e8..1109175 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -64,6 +64,7 @@ typedef struct tagME_Style
 } ME_Style;
 
 typedef enum {
+  diInvalid,
   diTextStart, /* start of the text buffer */
   diParagraph, /* paragraph start */
   diRun, /* run (sequence of chars with the same character format) */
@@ -292,7 +293,6 @@ typedef struct tagME_TextEditor
   int nTotalLength, nLastTotalLength;
   int nUDArrowX;
   int nSequence;
-  int nOldSelFrom, nOldSelTo;
   COLORREF rgbBackColor;
   HBRUSH hbrBackground;
   BOOL bCaretAtEnd;
@@ -304,6 +304,7 @@ typedef struct tagME_TextEditor
   ME_UndoMode nUndoMode;
   int nParagraphs;
   int nLastSelStart, nLastSelEnd;
+  ME_DisplayItem *pLastSelStartPara, *pLastSelEndPara;
   ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE];
   BOOL bScrollX, bScrollY;
   int nScrollPosY;
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index 899f2d0..41f2652 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -505,36 +505,50 @@ ME_InvalidateFromOfs(ME_TextEditor *edit
 void
 ME_InvalidateSelection(ME_TextEditor *editor)
 {
+  ME_DisplayItem *para1, *para2;
+  int nStart, nEnd;
+  int len = ME_GetTextLength(editor);
+
+  ME_GetSelection(editor, &nStart, &nEnd);
+  /* if both old and new selection are 0-char (= caret only), then
+  there's no (inverted) area to be repainted, neither old nor new */
+  if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd)
+    return;
   ME_WrapMarkedParagraphs(editor);
-  if (ME_IsSelection(editor) || editor->nLastSelStart != editor->nLastSelEnd)
-  {
-    int x, y, height;
-    int x2, y2, height2;
-    int last_x, last_y, last_height;
-    int last_x2, last_y2, last_height2;
-    RECT rc;
-    ME_Cursor tmp;
-  
-    ME_GetCursorCoordinates(editor, &editor->pCursors[1], &x, &y, &height);
-    ME_GetCursorCoordinates(editor, &editor->pCursors[0], &x2, &y2, &height2);
-    ME_RunOfsFromCharOfs(editor, editor->nLastSelStart, &tmp.pRun, &tmp.nOffset);
-    ME_GetCursorCoordinates(editor, &tmp, &last_x, &last_y, &last_height);
-    ME_RunOfsFromCharOfs(editor, editor->nLastSelEnd, &tmp.pRun, &tmp.nOffset);
-    ME_GetCursorCoordinates(editor, &tmp, &last_x2, &last_y2, &last_height2);
-    {
-      rc.left = 0;
+  ME_GetSelectionParas(editor, &para1, &para2);
+  assert(para1->type == diParagraph);
+  assert(para2->type == diParagraph);
+  /* last selection markers aren't always updated, which means
+  they can point past the end of the document */ 
+  if (editor->nLastSelStart > len)
+    editor->nLastSelEnd = len; 
+  if (editor->nLastSelEnd > len)
+    editor->nLastSelEnd = len; 
+    
+  /* if the start part of selection is being expanded or contracted... */
+  if (nStart < editor->nLastSelStart) {
+    ME_MarkForPainting(editor, para1, ME_FindItemFwd(editor->pLastSelStartPara, diParagraphOrEnd));
+  } else 
+  if (nStart > editor->nLastSelStart) {
+    ME_MarkForPainting(editor, editor->pLastSelStartPara, ME_FindItemFwd(para1, diParagraphOrEnd));
+  }
 
-      rc.top = min(min(y, last_y), min(y2, last_y2));
-      rc.right = editor->rcFormat.right;
-      rc.bottom = max(max(y + height, last_y + last_height),
-                      max(y2 + height2, last_y2 + last_height2));
-      InvalidateRect(editor->hWnd, &rc, FALSE);
-    }
+  /* if the end part of selection is being contracted or expanded... */
+  if (nEnd < editor->nLastSelEnd) {
+    ME_MarkForPainting(editor, para2, ME_FindItemFwd(editor->pLastSelEndPara, diParagraphOrEnd));
+  } else 
+  if (nEnd > editor->nLastSelEnd) {
+    ME_MarkForPainting(editor, editor->pLastSelEndPara, ME_FindItemFwd(para2, diParagraphOrEnd));
   }
+
+  ME_InvalidateMarkedParagraphs(editor);
+  /* remember the last invalidated position */
   ME_GetSelection(editor, &editor->nLastSelStart, &editor->nLastSelEnd);
+  ME_GetSelectionParas(editor, &editor->pLastSelStartPara, &editor->pLastSelEndPara);
+  assert(editor->pLastSelStartPara->type == diParagraph);
+  assert(editor->pLastSelEndPara->type == diParagraph);
 }
 
-
 void
 ME_QueueInvalidateFromCursor(ME_TextEditor *editor, int nCursor)
 {
diff --git a/dlls/riched20/para.c b/dlls/riched20/para.c
index def9012..1774957 100644
--- a/dlls/riched20/para.c
+++ b/dlls/riched20/para.c
@@ -92,6 +92,15 @@ void ME_MarkForWrapping(ME_TextEditor *e
   }
 }
 
+void ME_MarkForPainting(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last)
+{
+  while(first != last)
+  {
+    first->member.para.nFlags |= MEPF_REPAINT;
+    first = first->member.para.next_para;
+  }
+}
+
 /* split paragraph at the beginning of the run */
 ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style)
 {
@@ -245,6 +254,11 @@ ME_DisplayItem *ME_JoinParagraphs(ME_Tex
   ME_Remove(pRun);
   ME_DestroyDisplayItem(pRun);
 
+  if (editor->pLastSelStartPara == pNext)
+    editor->pLastSelStartPara = tp;
+  if (editor->pLastSelEndPara == pNext)
+    editor->pLastSelEndPara = tp;
+    
   tp->member.para.next_para = pNext->member.para.next_para;
   pNext->member.para.next_para->member.para.prev_para = tp;
   ME_Remove(pNext);
diff --git a/dlls/riched20/undo.c b/dlls/riched20/undo.c
index 63c9910..181121a 100644
--- a/dlls/riched20/undo.c
+++ b/dlls/riched20/undo.c
@@ -148,7 +148,6 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEdito
 }
 
 void ME_CommitUndo(ME_TextEditor *editor) {
-  
   if (editor->nUndoMode == umIgnore)
     return;
   
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c
index 6206c0f..af6e9a9 100644
--- a/dlls/riched20/wrap.c
+++ b/dlls/riched20/wrap.c
@@ -462,33 +462,40 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEdit
 
   ME_DestroyContext(&c);
   ReleaseDC(hWnd, hDC);
+  
+  if (bModified || editor->nTotalLength < editor->nLastTotalLength)
+    ME_InvalidateMarkedParagraphs(editor);
+  return bModified;
+}
 
+void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor) {
+  ME_Context c;
+  HDC hDC = GetDC(editor->hWnd);
+
+  ME_InitContext(&c, editor, hDC);
   if (editor->bRedraw)
   {
-     RECT rc = c.rcView;
+    RECT rc = c.rcView;
+    int ofs = ME_GetYScrollPos(editor); 
      
-     /* Invalidate rewrapped rows */
-     if (yStart != -1)
-     {
-       yStart -= ME_GetYScrollPos(editor);
-       yEnd -= ME_GetYScrollPos(editor);
-       if ((yStart >= 0 && yStart < c.rcView.bottom - c.rcView.top)
-           || (yEnd >= 0 && yEnd < c.rcView.bottom - c.rcView.top))
-       {
-         rc.top = yStart;
-         rc.bottom = yEnd;
-         InvalidateRect(editor->hWnd, &rc, TRUE);
-       }
-     }
-
-     /* Invalidate cursor row */
-     if (editor->nInvalidOfs != -1)
-     {
-       ME_InvalidateFromOfs(editor, editor->nInvalidOfs);
-       editor->nInvalidOfs = -1;
-     }
+    ME_DisplayItem *item = editor->pBuffer->pFirst;
+    while(item != editor->pBuffer->pLast) {
+      if (item->member.para.nFlags & MEPF_REPAINT) { 
+        rc.top = item->member.para.nYPos - ofs;
+        rc.bottom = item->member.para.nYPos + item->member.para.nHeight - ofs;
+        InvalidateRect(editor->hWnd, &rc, TRUE);
+      }
+      item = item->member.para.next_para;
+    }
+    if (editor->nTotalLength < editor->nLastTotalLength)
+    {
+      rc.top = editor->nTotalLength - ofs;
+      rc.bottom = editor->nLastTotalLength - ofs;
+      InvalidateRect(editor->hWnd, &rc, TRUE);
+    }
   }
-  return bModified;
+  ME_DestroyContext(&c);
+  ReleaseDC(editor->hWnd, hDC);
 }
 
 


More information about the wine-patches mailing list