Fixed some problems with scrolling in the edit control.

This commit is contained in:
Dmitry Timoshkov 2001-01-05 03:40:35 +00:00 committed by Alexandre Julliard
parent a1d23f5da6
commit 11dbda6bf9
1 changed files with 155 additions and 60 deletions

View File

@ -90,6 +90,8 @@ typedef struct
INT left_margin; /* in pixels */ INT left_margin; /* in pixels */
INT right_margin; /* in pixels */ INT right_margin; /* in pixels */
RECT format_rect; RECT format_rect;
INT text_width; /* width of the widest line in pixels for multi line controls
and just line width for single line controls */
INT region_posx; /* Position of cursor relative to region: */ INT region_posx; /* Position of cursor relative to region: */
INT region_posy; /* -1: to left, 0: within, 1: to right */ INT region_posy; /* -1: to left, 0: within, 1: to right */
EDITWORDBREAKPROC16 word_break_proc16; EDITWORDBREAKPROC16 word_break_proc16;
@ -105,7 +107,6 @@ typedef struct
INT lock_count; /* amount of re-entries in the EditWndProc */ INT lock_count; /* amount of re-entries in the EditWndProc */
INT tabs_count; INT tabs_count;
LPINT tabs; LPINT tabs;
INT text_width; /* width of the widest line in pixels */
LINEDEF *first_line_def; /* linked list of (soft) linebreaks */ LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
HLOCAL hloc32W; /* our unicode local memory block */ HLOCAL hloc32W; /* our unicode local memory block */
HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
@ -167,6 +168,7 @@ static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
* Helper functions only valid for one type of control * 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);
static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es);
static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es); static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend); static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend); static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
@ -194,6 +196,7 @@ static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT co
static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap); static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc); static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force); static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es);
static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action); static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
/* /*
* EM_XXX message handlers * EM_XXX message handlers
@ -209,6 +212,7 @@ static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line); static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
static INT EDIT_EM_LineLength(EDITSTATE *es, INT index); static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy); static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy);
static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap); static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update); static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update);
static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action); static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
@ -1221,6 +1225,15 @@ static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
ReleaseDC(wnd->hwndSelf, dc); ReleaseDC(wnd->hwndSelf, dc);
} }
/*********************************************************************
*
* EDIT_CalcLineWidth_SL
*
*/
static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es)
{
es->text_width = SLOWORD(EDIT_EM_PosFromChar(wnd, es, strlenW(es->text), FALSE));
}
/********************************************************************* /*********************************************************************
* *
@ -1474,7 +1487,7 @@ static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
if(es->hloc32W) if(es->hloc32W)
{ {
TRACE("Locking 32-bit UNICODE buffer\n"); /*TRACE("Locking 32-bit UNICODE buffer\n");*/
es->text = LocalLock(es->hloc32W); es->text = LocalLock(es->hloc32W);
if(es->hloc32A) if(es->hloc32A)
@ -2047,19 +2060,7 @@ static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
BOOL after_wrap) BOOL after_wrap)
{ {
LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap); LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
INT x = SLOWORD(res); SetCaretPos(SLOWORD(res), SHIWORD(res));
INT y = SHIWORD(res);
if(x < es->format_rect.left)
x = es->format_rect.left;
if(x > es->format_rect.right - 2)
x = es->format_rect.right - 2;
if(y > es->format_rect.bottom)
y = es->format_rect.bottom;
if(y < es->format_rect.top)
y = es->format_rect.top;
SetCaretPos(x, y);
return;
} }
@ -2087,10 +2088,29 @@ static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
es->format_rect.right -= es->right_margin; es->format_rect.right -= es->right_margin;
es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width); es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
if (es->style & ES_MULTILINE) if (es->style & ES_MULTILINE)
es->format_rect.bottom = es->format_rect.top + {
max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height; INT fw, vlc, max_x_offset, max_y_offset;
vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
es->format_rect.bottom = es->format_rect.top + max(1, vlc) * es->line_height;
/* correct es->x_offset */
fw = es->format_rect.right - es->format_rect.left;
max_x_offset = es->text_width - fw;
if(max_x_offset < 0) max_x_offset = 0;
if(es->x_offset > max_x_offset)
es->x_offset = max_x_offset;
/* correct es->y_offset */
max_y_offset = es->line_count - vlc;
if(max_y_offset < 0) max_y_offset = 0;
if(es->y_offset > max_y_offset)
es->y_offset = max_y_offset;
}
else else
/* Windows doesn't care to fix text placement for SL controls */
es->format_rect.bottom = es->format_rect.top + es->line_height; es->format_rect.bottom = es->format_rect.top + es->line_height;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
EDIT_BuildLineDefs_ML(wnd, es); EDIT_BuildLineDefs_ML(wnd, es);
} }
@ -2157,6 +2177,42 @@ static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
} }
/*********************************************************************
*
* EDIT_UpdateScrollInfo
*
*/
static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es)
{
if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->line_count - 1;
si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
si.nPos = es->y_offset;
TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
si.nMin, si.nMax, si.nPage, si.nPos);
SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
}
if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->text_width - 1;
si.nPage = es->format_rect.right - es->format_rect.left;
si.nPos = es->x_offset;
TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
si.nMin, si.nMax, si.nPage, si.nPos);
SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
}
}
/********************************************************************* /*********************************************************************
* *
* EDIT_WordBreakProc * EDIT_WordBreakProc
@ -2275,7 +2331,7 @@ static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
* *
* Hopefully this won't fire back at us. * Hopefully this won't fire back at us.
* We always start with a fixed buffer in the local heap. * We always start with a fixed buffer in the local heap.
* Despite of the documenation says that the local heap is used * Despite of the documentation says that the local heap is used
* only if DS_LOCALEDIT flag is set, NT and 2000 always allocate * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
* buffer on the local heap. * buffer on the local heap.
* *
@ -2542,22 +2598,46 @@ static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
* *
* EM_LINESCROLL * EM_LINESCROLL
* *
* FIXME: dx is in average character widths * NOTE: dx is in average character widths, dy - in lines;
* However, we assume it is in pixels when we use this
* function internally
* *
*/ */
static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy) static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
{ {
INT nyoff;
if (!(es->style & ES_MULTILINE)) if (!(es->style & ES_MULTILINE))
return FALSE; return FALSE;
if (-dx > es->x_offset) dx *= es->char_width;
dx = -es->x_offset; return EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
if (dx > es->text_width - es->x_offset) }
dx = es->text_width - es->x_offset;
/*********************************************************************
*
* EDIT_EM_LineScroll_internal
*
* Version of EDIT_EM_LineScroll for internal use.
* It doesn't refuse if ES_MULTILINE is set and assumes that
* dx is in pixels, dy - in lines.
*
*/
static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy)
{
INT nyoff;
INT x_offset_in_pixels;
if (es->style & ES_MULTILINE)
{
x_offset_in_pixels = es->x_offset;
}
else
{
dy = 0;
x_offset_in_pixels = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->x_offset, FALSE));
}
if (-dx > x_offset_in_pixels)
dx = -x_offset_in_pixels;
if (dx > es->text_width - x_offset_in_pixels)
dx = es->text_width - x_offset_in_pixels;
nyoff = max(0, es->y_offset + dy); nyoff = max(0, es->y_offset + dy);
if (nyoff >= es->line_count) if (nyoff >= es->line_count)
nyoff = es->line_count - 1; nyoff = es->line_count - 1;
@ -2565,12 +2645,17 @@ static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
if (dx || dy) { if (dx || dy) {
RECT rc1; RECT rc1;
RECT rc; RECT rc;
es->y_offset = nyoff;
if(es->style & ES_MULTILINE)
es->x_offset += dx;
else
es->x_offset += dx / es->char_width;
GetClientRect(wnd->hwndSelf, &rc1); GetClientRect(wnd->hwndSelf, &rc1);
IntersectRect(&rc, &rc1, &es->format_rect); IntersectRect(&rc, &rc1, &es->format_rect);
ScrollWindowEx(wnd->hwndSelf, -dx, dy, ScrollWindowEx(wnd->hwndSelf, -dx, dy,
NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE); NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
es->y_offset = nyoff;
es->x_offset += dx;
} }
if (dx && !(es->flags & EF_HSCROLL_TRACK)) if (dx && !(es->flags & EF_HSCROLL_TRACK))
EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL"); EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
@ -2742,6 +2827,8 @@ static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR l
/* FIXME: really inefficient */ /* FIXME: really inefficient */
if (es->style & ES_MULTILINE) if (es->style & ES_MULTILINE)
EDIT_BuildLineDefs_ML(wnd, es); EDIT_BuildLineDefs_ML(wnd, es);
else
EDIT_CalcLineWidth_SL(wnd, es);
EDIT_EM_SetSel(wnd, es, s, s, FALSE); EDIT_EM_SetSel(wnd, es, s, s, FALSE);
es->flags |= EF_MODIFIED; es->flags |= EF_MODIFIED;
@ -2788,8 +2875,14 @@ static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
return (LRESULT)FALSE; return (LRESULT)FALSE;
} }
if (dy) { if (dy) {
INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
/* check if we are going to move too far */
if(es->y_offset + dy > es->line_count - vlc)
dy = es->line_count - vlc - es->y_offset;
/* Notification is done in EDIT_EM_LineScroll */
if(dy)
EDIT_EM_LineScroll(wnd, es, 0, dy); EDIT_EM_LineScroll(wnd, es, 0, dy);
EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
} }
return MAKELONG((INT16)dy, (BOOL16)TRUE); return MAKELONG((INT16)dy, (BOOL16)TRUE);
} }
@ -2826,7 +2919,13 @@ static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
if (x > es->format_rect.right) if (x > es->format_rect.right)
dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw; dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
if (dy || dx) if (dy || dx)
EDIT_EM_LineScroll(wnd, es, dx, dy); {
/* check if we are going to move too far */
if(es->x_offset + dx + ww > es->text_width)
dx = es->text_width - ww - es->x_offset;
if(dx || dy)
EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
}
} else { } else {
INT x; INT x;
INT goal; INT goal;
@ -2858,6 +2957,9 @@ static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
EDIT_UpdateText(wnd, NULL, TRUE); EDIT_UpdateText(wnd, NULL, TRUE);
} }
} }
if(es->flags & EF_FOCUSED)
EDIT_SetCaretPos(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
} }
@ -3107,8 +3209,6 @@ static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL a
es->flags |= EF_AFTER_WRAP; es->flags |= EF_AFTER_WRAP;
else else
es->flags &= ~EF_AFTER_WRAP; es->flags &= ~EF_AFTER_WRAP;
if (es->flags & EF_FOCUSED)
EDIT_SetCaretPos(wnd, es, end, after_wrap);
/* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */ /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
ORDER_UINT(start, end); ORDER_UINT(start, end);
ORDER_UINT(end, old_end); ORDER_UINT(end, old_end);
@ -3242,6 +3342,7 @@ static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
EDIT_EM_EmptyUndoBuffer(es); EDIT_EM_EmptyUndoBuffer(es);
EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE); EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE); EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
HeapFree(GetProcessHeap(), 0, utext); HeapFree(GetProcessHeap(), 0, utext);
TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n", TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
@ -3636,7 +3737,14 @@ static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos)
return 0; return 0;
} }
if (dx) if (dx)
EDIT_EM_LineScroll(wnd, es, dx, 0); {
INT fw = es->format_rect.right - es->format_rect.left;
/* check if we are going to move too far */
if(es->x_offset + dx + fw > es->text_width)
dx = es->text_width - fw - es->x_offset;
if(dx)
EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
}
return ret; return ret;
} }
@ -3706,7 +3814,14 @@ static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
return 0; return 0;
} }
if (dx) if (dx)
EDIT_EM_LineScroll(wnd, es, dx, 0); {
INT fw = es->format_rect.right - es->format_rect.left;
/* check if we are going to move too far */
if(es->x_offset + dx + fw > es->text_width)
dx = es->text_width - fw - es->x_offset;
if(dx)
EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
}
return 0; return 0;
} }
@ -4180,33 +4295,11 @@ static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
} }
if (es->font) if (es->font)
SelectObject(dc, old_font); SelectObject(dc, old_font);
if (es->flags & EF_FOCUSED)
EDIT_SetCaretPos(wnd, es, es->selection_end,
es->flags & EF_AFTER_WRAP);
if (!wParam) if (!wParam)
EndPaint(wnd->hwndSelf, &ps); EndPaint(wnd->hwndSelf, &ps);
if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height; EDIT_UpdateScrollInfo(wnd, es);
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->line_count + vlc - 2;
si.nPage = vlc;
si.nPos = es->y_offset;
SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
}
if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
SCROLLINFO si;
INT fw = es->format_rect.right - es->format_rect.left;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->text_width + fw - 1;
si.nPage = fw;
si.nPos = es->x_offset;
SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
}
} }
@ -4289,6 +4382,8 @@ static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
if (es->style & ES_MULTILINE) if (es->style & ES_MULTILINE)
EDIT_BuildLineDefs_ML(wnd, es); EDIT_BuildLineDefs_ML(wnd, es);
else
EDIT_CalcLineWidth_SL(wnd, es);
if (redraw) if (redraw)
EDIT_UpdateText(wnd, NULL, TRUE); EDIT_UpdateText(wnd, NULL, TRUE);