I forgot to plug a region leak before I sent the patch in. I have
attached an updated patch.
/Ulrich
Ulrich Czekalla wrote:
>
> This patch modifies the edit control to keep the line format data
> and simply update it as changes occur. It also uses the line data
> to calculate a proper update region. The result is a significant
> reduction in flicker. Since this is a big change to the format
> engine of the edit control it should be tested for a while before
> committing.
>
> Changelog:
> Ulrich Czekalla <uczekalla(a)codeweavers.com>
> Update format engine to reduce flicker
>
Index: controls/edit.c
===================================================================
RCS file: /home/wine/wine/controls/edit.c,v
retrieving revision 1.75
diff -u -w -r1.75 edit.c
--- controls/edit.c 2001/03/13 23:31:08 1.75
+++ controls/edit.c 2001/03/29 17:31:36
@@ -63,6 +63,7 @@
INT net_length; /* netto length of a line in visible characters */
LINE_END ending;
INT width; /* width of the line in pixels */
+ INT index; /* line index into the buffer */
struct tagLINEDEF *next;
} LINEDEF;
@@ -172,7 +173,7 @@
/*
* Helper functions only valid for one type of control
*/
-static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
+static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es, INT iStart, INT iEnd, INT delta, HRGN hrgn);
static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es);
static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
@@ -263,6 +264,7 @@
static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es);
static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
+static void EDIT_UpdateTextRegion(WND *wnd, HRGN hrgn, BOOL bErase);
LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -1132,100 +1134,250 @@
* a soft return '\r\r\n' or a hard return '\r\n'
*
*/
-static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
+static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
{
HDC dc;
HFONT old_font = 0;
- LPWSTR start, cp;
+ LPWSTR current_position, cp;
INT fw;
- LINEDEF *current_def;
- LINEDEF **previous_next;
+ LINEDEF *current_line;
+ LINEDEF *previous_line;
+ LINEDEF *start_line;
+ INT line_index = 0, nstart_line = 0, nstart_index = 0;
+ INT line_count = es->line_count;
+ INT orig_net_length;
+ RECT rc;
- current_def = es->first_line_def;
- do {
- LINEDEF *next_def = current_def->next;
- HeapFree(GetProcessHeap(), 0, current_def);
- current_def = next_def;
- } while (current_def);
- es->line_count = 0;
- es->text_width = 0;
+ if (istart == iend && delta == 0)
+ return;
dc = GetDC(wnd->hwndSelf);
if (es->font)
old_font = SelectObject(dc, es->font);
+ previous_line = NULL;
+ current_line = es->first_line_def;
+
+ /* Find starting line. istart must lie inside an existing line or
+ * at the end of buffer */
+ do {
+ if (istart < current_line->index + current_line->length ||
+ current_line->ending == END_0)
+ break;
+
+ previous_line = current_line;
+ current_line = current_line->next;
+ line_index++;
+ } while (current_line);
+
+ if (!current_line) /* Error occurred start is not inside previous buffer */
+ {
+ FIXME(" modification occurred outside buffer\n");
+ return;
+ }
+
+ /* Remember start of modifications in order to calculate update region */
+ nstart_line = line_index;
+ nstart_index = current_line->index;
+
+ /* We must start to reformat from the previous line since the modifications
+ * may have caused the line to wrap upwards. */
+ if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
+ {
+ line_index--;
+ current_line = previous_line;
+ }
+ start_line = current_line;
+
fw = es->format_rect.right - es->format_rect.left;
- start = es->text;
- previous_next = &es->first_line_def;
+ current_position = es->text + current_line->index;
do {
- current_def = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
- current_def->next = NULL;
- cp = start;
+ if (current_line != start_line)
+ {
+ if (!current_line || current_line->index + delta > current_position - es->text)
+ {
+ /* The buffer has been expanded, create a new line and
+ insert it into the link list */
+ LINEDEF *new_line = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
+ new_line->next = previous_line->next;
+ previous_line->next = new_line;
+ current_line = new_line;
+ es->line_count++;
+ }
+ else if (current_line->index + delta < current_position - es->text)
+ {
+ /* The previous line merged with this line so we delete this extra entry */
+ previous_line->next = current_line->next;
+ HeapFree(GetProcessHeap(), 0, current_line);
+ current_line = previous_line->next;
+ es->line_count--;
+ continue;
+ }
+ else /* current_line->index + delta == current_position */
+ {
+ if (current_position - es->text > iend)
+ break; /* We reached end of line modifications */
+ /* else recalulate this line */
+ }
+ }
+
+ current_line->index = current_position - es->text;
+ orig_net_length = current_line->net_length;
+
+ /* Find end of line */
+ cp = current_position;
while (*cp) {
if ((*cp == '\r') && (*(cp + 1) == '\n'))
break;
cp++;
}
+
+ /* Mark type of line termination */
if (!(*cp)) {
- current_def->ending = END_0;
- current_def->net_length = strlenW(start);
- } else if ((cp > start) && (*(cp - 1) == '\r')) {
- current_def->ending = END_SOFT;
- current_def->net_length = cp - start - 1;
+ current_line->ending = END_0;
+ current_line->net_length = strlenW(current_position);
+ } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
+ current_line->ending = END_SOFT;
+ current_line->net_length = cp - current_position - 1;
} else {
- current_def->ending = END_HARD;
- current_def->net_length = cp - start;
+ current_line->ending = END_HARD;
+ current_line->net_length = cp - current_position;
}
- current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
- start, current_def->net_length,
+
+ /* Calculate line width */
+ current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
+ current_position, current_line->net_length,
es->tabs_count, es->tabs));
+
/* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
- if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
+ if ((!(es->style & ES_AUTOHSCROLL)) && (current_line->width > fw)) {
INT next = 0;
INT prev;
do {
prev = next;
- next = EDIT_CallWordBreakProc(es, start - es->text,
- prev + 1, current_def->net_length, WB_RIGHT);
- current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
- start, next, es->tabs_count, es->tabs));
- } while (current_def->width <= fw);
- if (!prev) {
+ next = EDIT_CallWordBreakProc(es, current_position - es->text,
+ prev + 1, current_line->net_length, WB_RIGHT);
+ current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
+ current_position, next, es->tabs_count, es->tabs));
+ } while (current_line->width <= fw);
+ if (!prev) { /* Didn't find a line break so force a break */
next = 0;
do {
prev = next;
next++;
- current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
- start, next, es->tabs_count, es->tabs));
- } while (current_def->width <= fw);
+ current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
+ current_position, next, es->tabs_count, es->tabs));
+ } while (current_line->width <= fw);
if (!prev)
prev = 1;
}
- current_def->net_length = prev;
- current_def->ending = END_WRAP;
- current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc, start,
- current_def->net_length, es->tabs_count, es->tabs));
+
+ /* If the first line we are calculating, wrapped before istart, we must
+ * adjust istart in order for this to be reflected in the update region. */
+ if (current_line->index == nstart_index && istart > current_line->index + prev)
+ istart = current_line->index + prev;
+ /* else if we are updating the previous line before the first line we
+ * are re-caulculating and it expanded */
+ else if (current_line == start_line &&
+ current_line->index != nstart_index && orig_net_length < prev)
+ {
+ /* Line expanded due to an upwards line wrap so we must partially include
+ * previous line in update region */
+ nstart_line = line_index;
+ nstart_index = current_line->index;
+ istart = current_line->index + orig_net_length;
+ }
+
+ current_line->net_length = prev;
+ current_line->ending = END_WRAP;
+ current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc, current_position,
+ current_line->net_length, es->tabs_count, es->tabs));
}
- switch (current_def->ending) {
+
+
+ /* Adjust length to include line termination */
+ switch (current_line->ending) {
case END_SOFT:
- current_def->length = current_def->net_length + 3;
+ current_line->length = current_line->net_length + 3;
break;
case END_HARD:
- current_def->length = current_def->net_length + 2;
+ current_line->length = current_line->net_length + 2;
break;
case END_WRAP:
case END_0:
- current_def->length = current_def->net_length;
+ current_line->length = current_line->net_length;
break;
}
- es->text_width = max(es->text_width, current_def->width);
- start += current_def->length;
- *previous_next = current_def;
- previous_next = ¤t_def->next;
- es->line_count++;
- } while (current_def->ending != END_0);
+ es->text_width = max(es->text_width, current_line->width);
+ current_position += current_line->length;
+ previous_line = current_line;
+ current_line = current_line->next;
+ line_index++;
+ } while (previous_line->ending != END_0);
+
+ /* Finish adjusting line index's by delta or remove hanging lines */
+ if (previous_line->ending == END_0)
+ {
+ LINEDEF *pnext = NULL;
+
+ previous_line->next = NULL;
+ while (current_line)
+ {
+ pnext = current_line->next;
+ HeapFree(GetProcessHeap(), 0, current_line);
+ current_line = pnext;
+ es->line_count--;
+ }
+ }
+ else
+ {
+ while (current_line)
+ {
+ current_line->index += delta;
+ current_line = current_line->next;
+ }
+ }
+
+ /* Calculate rest of modification rectangle */
+ if (hrgn)
+ {
+ HRGN tmphrgn;
+ /*
+ * We calculate two rectangles. One for the first line which may have
+ * an indent with respect to the format rect. The other is a format-width
+ * rectangle that spans the rest of the lines that changed or moved.
+ */
+ rc.top = es->format_rect.top + nstart_line * es->line_height -
+ (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
+ rc.bottom = rc.top + es->line_height;
+ rc.left = es->format_rect.left + (INT)LOWORD(GetTabbedTextExtentW(dc,
+ es->text + nstart_index, istart - nstart_index,
+ es->tabs_count, es->tabs)) - es->x_offset; /* Adjust for horz scroll */
+ rc.right = es->format_rect.right;
+ SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
+
+ rc.top = rc.bottom;
+ rc.left = es->format_rect.left;
+ rc.right = es->format_rect.right;
+ /*
+ * If lines were added or removed we must re-paint the remainder of the
+ * lines since the remaining lines were either shifted up or down.
+ */
+ if (line_count < es->line_count) /* We added lines */
+ rc.bottom = es->line_count * es->line_height;
+ else if (line_count > es->line_count) /* We removed lines */
+ rc.bottom = line_count * es->line_height;
+ else
+ rc.bottom = line_index * es->line_height;
+ rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
+ tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
+ CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
+ DeleteObject(tmphrgn);
+ }
+
if (es->font)
SelectObject(dc, old_font);
+
ReleaseDC(wnd->hwndSelf, dc);
}
@@ -2140,7 +2292,7 @@
es->format_rect.bottom = es->format_rect.top + es->line_height;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
}
@@ -2836,6 +2988,7 @@
UINT e;
UINT i;
LPWSTR p;
+ HRGN hrgn = 0;
TRACE("%s, can_undo %d, send_update %d\n",
debugstr_w(lpsz_replace), can_undo, send_update);
@@ -2916,9 +3069,14 @@
CharLowerBuffW(p, strl);
s += strl;
}
- /* FIXME: really inefficient */
if (es->style & ES_MULTILINE)
- EDIT_BuildLineDefs_ML(wnd, es);
+ {
+ INT s = min(es->selection_start, es->selection_end);
+
+ hrgn = CreateRectRgn(0, 0, 0, 0);
+ EDIT_BuildLineDefs_ML(wnd, es, s, s + strl,
+ strl - (es->selection_end - es->selection_start), hrgn);
+ }
else
EDIT_CalcLineWidth_SL(wnd, es);
@@ -2930,7 +3088,12 @@
/* force scroll info update */
EDIT_UpdateScrollInfo(wnd, es);
- /* FIXME: really inefficient */
+ if (hrgn)
+ {
+ EDIT_UpdateTextRegion(wnd, hrgn, TRUE);
+ DeleteObject(hrgn);
+ }
+ else
EDIT_UpdateText(wnd, NULL, TRUE);
if(es->flags & EF_UPDATE)
@@ -3134,7 +3297,7 @@
EDIT_EM_EmptyUndoBuffer(es);
es->flags &= ~EF_MODIFIED;
es->flags &= ~EF_UPDATE;
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
EDIT_UpdateText(wnd, NULL, TRUE);
EDIT_EM_ScrollCaret(wnd, es);
/* force scroll info update */
@@ -3200,7 +3363,7 @@
EDIT_EM_EmptyUndoBuffer(es);
es->flags &= ~EF_MODIFIED;
es->flags &= ~EF_UPDATE;
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
EDIT_UpdateText(wnd, NULL, TRUE);
EDIT_EM_ScrollCaret(wnd, es);
/* force scroll info update */
@@ -3403,7 +3566,7 @@
es->word_break_proc16 = NULL;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
EDIT_UpdateText(wnd, NULL, TRUE);
}
}
@@ -3422,7 +3585,7 @@
es->word_break_proc = NULL;
es->word_break_proc16 = wbp;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
EDIT_UpdateText(wnd, NULL, TRUE);
}
}
@@ -3683,6 +3846,8 @@
*/
static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
{
+ LINEDEF *pc, *pp;
+
if (es->hloc32W) {
while (LocalUnlock(es->hloc32W)) ;
LocalFree(es->hloc32W);
@@ -3695,6 +3860,15 @@
while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
LOCAL_Free(wnd->hInstance, es->hloc16);
}
+
+ pc = es->first_line_def;
+ while (pc)
+ {
+ pp = pc->next;
+ HeapFree(GetProcessHeap(), 0, pc);
+ pc = pp;
+ }
+
HeapFree(GetProcessHeap(), 0, es);
*(EDITSTATE **)wnd->wExtra = NULL;
}
@@ -4447,7 +4621,7 @@
EDIT_SetRectNP(wnd, es, &r);
if (es->style & ES_MULTILINE)
- EDIT_BuildLineDefs_ML(wnd, es);
+ EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
else
EDIT_CalcLineWidth_SL(wnd, es);
@@ -4678,6 +4852,21 @@
if (dy)
EDIT_EM_LineScroll(wnd, es, 0, dy);
return 0;
+}
+
+/*********************************************************************
+ *
+ * EDIT_UpdateText
+ *
+ */
+static void EDIT_UpdateTextRegion(WND *wnd, HRGN hrgn, BOOL bErase)
+{
+ EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
+
+ if (es->flags & EF_UPDATE)
+ EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE");
+
+ InvalidateRgn(wnd->hwndSelf, hrgn, bErase);
}