From bad844d0a90c6947f2365a1d1b259be56ebf5ca5 Mon Sep 17 00:00:00 2001 From: Krzysztof Foltman Date: Mon, 7 Aug 2006 17:27:16 +0200 Subject: [PATCH] riched20: New, clean, simple selection repaint logic - should fix all outstanding refresh issues. --- dlls/riched20/caret.c | 26 +++++++++++----- dlls/riched20/editor.c | 3 +- dlls/riched20/editor.h | 4 ++- dlls/riched20/editstr.h | 3 +- dlls/riched20/paint.c | 66 +++++++++++++++++++++++++---------------- dlls/riched20/para.c | 14 +++++++++ dlls/riched20/undo.c | 1 - dlls/riched20/wrap.c | 51 +++++++++++++++++-------------- 8 files changed, 108 insertions(+), 60 deletions(-) diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c index 51248220f15..ff6ff008c8a 100644 --- a/dlls/riched20/caret.c +++ b/dlls/riched20/caret.c @@ -24,7 +24,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(richedit); - static BOOL ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs); @@ -87,7 +86,7 @@ void ME_SetSelection(ME_TextEditor *editor, int from, int to) 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 +118,8 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, 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 +128,7 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, 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 +136,25 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, 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, ¶->member.para, &run->member.run, ME_StrLen(run->member.run.strText)); } } @@ -175,6 +182,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 +1000,10 @@ static void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor) 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 +1162,8 @@ ME_ArrowKey(ME_TextEditor *editor, int nVKey, BOOL extend, BOOL ctrl) 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 +1212,8 @@ ME_ArrowKey(ME_TextEditor *editor, int nVKey, BOOL extend, BOOL ctrl) 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 12687829750..7c45edb958f 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -1048,7 +1048,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) 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 93952fd0da9..4409069626f 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -202,10 +202,12 @@ ME_DisplayItem *ME_MakeRow(int height, int baseline, int width); 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 *editor, ME_DisplayItem *para, PARAFORMAT2 * 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 *editor); 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 7d096e870f1..1109175832f 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 899f2d00f56..41f26521ad0 100644 --- a/dlls/riched20/paint.c +++ b/dlls/riched20/paint.c @@ -505,35 +505,49 @@ ME_InvalidateFromOfs(ME_TextEditor *editor, int nCharOfs) 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; - - 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); - } + ME_GetSelectionParas(editor, ¶1, ¶2); + 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)); } - ME_GetSelection(editor, &editor->nLastSelStart, &editor->nLastSelEnd); -} + /* 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 def9012741c..1774957bc37 100644 --- a/dlls/riched20/para.c +++ b/dlls/riched20/para.c @@ -92,6 +92,15 @@ void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_Display } } +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_TextEditor *editor, ME_DisplayItem *tp) 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 63c99106fd0..181121a3777 100644 --- a/dlls/riched20/undo.c +++ b/dlls/riched20/undo.c @@ -148,7 +148,6 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayIte } void ME_CommitUndo(ME_TextEditor *editor) { - if (editor->nUndoMode == umIgnore) return; diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 6206c0f4e60..af6e9a9152e 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -462,33 +462,40 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { 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); }