Skip to content
Snippets Groups Projects
edit.c 77.5 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_WriteText lp=%s, off=%d, len=%d, row=%d, col=%d, reverse=%d\n", lp, off, len, row, col, reverse);
Alexandre Julliard's avatar
Alexandre Julliard committed

    hdc = GetDC(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    hStr = EDIT_GetStr(hwnd, lp, off, len, &diff);
    str = (char *)EDIT_HeapAddr(hwnd, hStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
    hrgnClip = CreateRectRgnIndirect(rc);
    SelectClipRgn(hdc, hrgnClip);

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (es->hFont)
	oldfont = (HFONT)SelectObject(hdc, (HANDLE)es->hFont);

Alexandre Julliard's avatar
Alexandre Julliard committed
    SendMessage(GetParent(hwnd), WM_CTLCOLOR, (WORD)hdc,
		MAKELPARAM(hwnd, CTLCOLOR_EDIT));

    if (reverse)
    {
	oldBkgdColor = GetBkColor(hdc);
	oldTextColor = GetTextColor(hdc);
	SetBkColor(hdc, oldTextColor);
	SetTextColor(hdc, oldBkgdColor);
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (strlen(blanks) < (ClientWidth(wndPtr) / charWidths[32]) + 2)
    {
        es->hBlankLine = EDIT_HeapReAlloc(hwnd, es->hBlankLine,
             (ClientWidth(wndPtr) / charWidths[32]) + 2);
        blanks = EDIT_HeapAddr(hwnd, es->hBlankLine);
        memset(blanks, ' ', (ClientWidth(wndPtr) / charWidths[32]) + 2);
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(cp = strchr(str, VK_TAB)))
	TextOut(hdc, col - diff, row * es->txtht, str, strlen(str));
    else
    {
	TextOut(hdc, col - diff, row * es->txtht, str, (int)(cp - str));
Alexandre Julliard's avatar
Alexandre Julliard committed
	scol = EDIT_StrLength(hwnd, str, (int)(cp - str), 0);
	tabwidth = EDIT_CharWidth(hwnd, VK_TAB, scol);
Alexandre Julliard's avatar
Alexandre Julliard committed
	num_spaces = tabwidth / charWidths[32] + 1;
	TextOut(hdc, scol, row * es->txtht, blanks, num_spaces);
	cp++;
	scol += tabwidth;

Alexandre Julliard's avatar
Alexandre Julliard committed
	while ((cp1 = strchr(cp, VK_TAB)))
Alexandre Julliard's avatar
Alexandre Julliard committed
	{
	    TextOut(hdc, scol, row * es->txtht, cp, (int)(cp1 - cp));
Alexandre Julliard's avatar
Alexandre Julliard committed
	    scol += EDIT_StrLength(hwnd, cp, (int)(cp1 - cp), scol);
	    tabwidth = EDIT_CharWidth(hwnd, VK_TAB, scol);
Alexandre Julliard's avatar
Alexandre Julliard committed
	    num_spaces = tabwidth / charWidths[32] + 1;
	    TextOut(hdc, scol, row * es->txtht, blanks, num_spaces);
	    cp = ++cp1;
	    scol += tabwidth;
	}

	TextOut(hdc, scol, row * es->txtht, cp, strlen(cp));
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (reverse)
    {
	SetBkColor(hdc, oldBkgdColor);
	SetTextColor(hdc, oldTextColor);
    }

    /* blank out remainder of line if appropriate */
    if (blank)
    {
	if ((rc->right - col) > len)
	{
	    num_spaces = (rc->right - col - len) / charWidths[32];
	    TextOut(hdc, col + len, row * es->txtht, blanks, num_spaces);
	}
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (es->hFont)
	SelectObject(hdc, (HANDLE)oldfont);

Alexandre Julliard's avatar
Alexandre Julliard committed
    EDIT_HeapFree(hwnd, hStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
    ReleaseDC(hwnd, hdc);
}


/*********************************************************************
 *  EDIT_GetStr
 *
 *  Return sub-string starting at pixel _off_ of length _len_ pixels.
 *  If _off_ is part way through a character, the negative offset of
 *  the beginning of the character is returned in _diff_, else _diff_ 
 *  will be zero.
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
HANDLE EDIT_GetStr(HWND hwnd, char *lp, int off, int len, int *diff)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    HANDLE hStr;
    char *str;
Alexandre Julliard's avatar
Alexandre Julliard committed
    int ch = 0, i = 0, j, s_i=0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    int ch1;

Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_GetStr lp='%s'  off=%d  len=%d\n", lp, off, len);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (off<0) off=0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    while (i < off)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	s_i = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
	i += EDIT_CharWidth(hwnd, (BYTE)(*(lp + ch)), i);
Alexandre Julliard's avatar
Alexandre Julliard committed
	ch++;
    }
    /* if stepped past _off_, go back a character */
    if (i - off)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
	i = s_i;
	ch--;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    *diff = off - i;
    ch1 = ch;
    while (i < len + off)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	i += EDIT_CharWidth(hwnd, (BYTE)(*(lp + ch)), i);
Alexandre Julliard's avatar
Alexandre Julliard committed
    hStr = EDIT_HeapAlloc(hwnd, ch - ch1 + 3);
    str = (char *)EDIT_HeapAddr(hwnd, hStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
    for (i = ch1, j = 0; i < ch; i++, j++)
	str[j] = lp[i];
    str[++j] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_GetStr: returning %s\n", str);
Alexandre Julliard's avatar
Alexandre Julliard committed
    return hStr;
}


/*********************************************************************
 *  WM_CHAR message function
 */

void EDIT_CharMsg(HWND hwnd, WORD wParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);

Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_CharMsg: wParam=%c\n", (char)wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed

    switch (wParam)
    {
    case '\r':
    case '\n':
	if (!IsMultiLine())
	    break;
	wParam = '\n';
	EDIT_KeyTyped(hwnd, wParam);
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
    case VK_TAB:
	if (!IsMultiLine())
	    break;
	EDIT_KeyTyped(hwnd, wParam);
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
    default:
	if (wParam >= 20 && wParam <= 126)
	    EDIT_KeyTyped(hwnd, wParam);
	break;
    }
}


/*********************************************************************
 *  EDIT_KeyTyped
 *
 *  Process keystrokes that produce displayable characters.
 */

void EDIT_KeyTyped(HWND hwnd, short ch)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *currchar = CurrChar;
    RECT rc;
    BOOL FullPaint = FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_KeyTyped: ch=%c\n", (char)ch);
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* delete selected text (if any) */
    if (SelMarked(es))
	EDIT_DeleteSel(hwnd);

    /* test for typing at end of maximum buffer size */
    if (currchar == text + es->MaxTextLen)
    {
	NOTIFY_PARENT(hwnd, EN_ERRSPACE);
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (*currchar == '\0' && IsMultiLine())
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
	/* insert a newline at end of text */
	*currchar = '\n';
	*(currchar + 1) = '\0';
	EDIT_BuildTextPointers(hwnd);
    }

    /* insert the typed character */
    if (text[es->textlen - 1] != '\0')
    {
	/* current text buffer is full */
	if (es->textlen == es->MaxTextLen)
	{
	    /* text buffer is at maximum size */
	    NOTIFY_PARENT(hwnd, EN_ERRSPACE);
	    return;
	}

	/* increase the text buffer size */
	es->textlen += GROWLENGTH;
	/* but not above maximum size */
	if (es->textlen > es->MaxTextLen)
	    es->textlen = es->MaxTextLen;
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->hText = EDIT_HeapReAlloc(hwnd, es->hText, es->textlen + 2);
Alexandre Julliard's avatar
Alexandre Julliard committed
	if (!es->hText)
	    NOTIFY_PARENT(hwnd, EN_ERRSPACE);
Alexandre Julliard's avatar
Alexandre Julliard committed
	text = EDIT_HeapAddr(hwnd, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
	text[es->textlen - 1] = '\0';
	currchar = CurrChar;
    }
    /* make space for new character and put char in buffer */
    memmove(currchar + 1, currchar, strlen(currchar) + 1);
    *currchar = ch;
    EDIT_ModTextPointers(hwnd, es->CurrLine + 1, 1);
    es->TextChanged = TRUE;
    NOTIFY_PARENT(hwnd, EN_UPDATE);

    /* re-adjust textwidth, if necessary, and redraw line */
    HideCaret(hwnd);
    if (IsMultiLine() && es->wlines > 1)
    {
	es->textwidth = max(es->textwidth,
Alexandre Julliard's avatar
Alexandre Julliard committed
		    EDIT_StrLength(hwnd, EDIT_TextLine(hwnd, es->CurrLine),
Alexandre Julliard's avatar
Alexandre Julliard committed
		    (int)(EDIT_TextLine(hwnd, es->CurrLine + 1) -
Alexandre Julliard's avatar
Alexandre Julliard committed
			  EDIT_TextLine(hwnd, es->CurrLine)), 0));
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    else
	es->textwidth = max(es->textwidth,
Alexandre Julliard's avatar
Alexandre Julliard committed
			    EDIT_StrLength(hwnd, text, strlen(text), 0));
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDIT_WriteTextLine(hwnd, NULL, es->wtop + es->WndRow);

    if (ch == '\n')
    {
	if (es->wleft > 0)
	    FullPaint = TRUE;
	es->wleft = 0;
	EDIT_BuildTextPointers(hwnd);
	EDIT_End(hwnd);
	EDIT_Forward(hwnd);

	/* invalidate rest of window */
	GetClientRect(hwnd, &rc);
	if (!FullPaint)
	    rc.top = es->WndRow * es->txtht;
	InvalidateRect(hwnd, &rc, FALSE);

	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	UpdateWindow(hwnd);
	NOTIFY_PARENT(hwnd, EN_CHANGE);
	return;
    }

    /* test end of window */
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (es->WndCol >= ClientWidth(wndPtr) - 
Alexandre Julliard's avatar
Alexandre Julliard committed
	                    EDIT_CharWidth(hwnd, (BYTE)ch, es->WndCol + es->wleft))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
	/* TODO:- Word wrap to be handled here */

/*	if (!(currchar == text + es->MaxTextLen - 2)) */
	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->WndCol += EDIT_CharWidth(hwnd, (BYTE)ch, es->WndCol + es->wleft);
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->CurrCol++;
    SetCaretPos(es->WndCol, es->WndRow * es->txtht);
    ShowCaret(hwnd);
    NOTIFY_PARENT(hwnd, EN_CHANGE);
}


/*********************************************************************
 *  EDIT_CharWidth
 *
 *  Return the width of the given character in pixels.
Alexandre Julliard's avatar
Alexandre Julliard committed
 *  The current column offset in pixels _pcol_ is required to calculate 
 *  the width of a tab.
Alexandre Julliard's avatar
Alexandre Julliard committed
int EDIT_CharWidth(HWND hwnd, short ch, int pcol)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HeapAddr(hwnd, es->hCharWidths);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (ch != VK_TAB)
	return (charWidths[ch]);
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
	return (EDIT_GetNextTabStop(hwnd, pcol) - pcol);
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/*********************************************************************
 *  EDIT_GetNextTabStop
 *
 *  Return the next tab stop beyond _pcol_.
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
int EDIT_GetNextTabStop(HWND hwnd, int pcol)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    int i;
Alexandre Julliard's avatar
Alexandre Julliard committed
    int baseUnitWidth = LOWORD(GetDialogBaseUnits());
Alexandre Julliard's avatar
Alexandre Julliard committed
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned short *tabstops = EDIT_HeapAddr(hwnd, es->hTabStops);
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (es->NumTabStops == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
	return ROUNDUP(pcol, 8 * baseUnitWidth);
Alexandre Julliard's avatar
Alexandre Julliard committed
    else if (es->NumTabStops == 1)
	return ROUNDUP(pcol, *tabstops * baseUnitWidth / 4);
    else
    {
	for (i = 0; i < es->NumTabStops; i++)
	{
	    if (*(tabstops + i) * baseUnitWidth / 4 >= pcol)
		return (*(tabstops + i) * baseUnitWidth / 4);
	}
	return pcol;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/*********************************************************************
 *  EDIT_Forward
 *
 *  Cursor right key: move right one character position.
 */

void EDIT_Forward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (*CurrChar == '\0')
Alexandre Julliard's avatar
Alexandre Julliard committed
	return;

    if (*CurrChar == '\n')
    {
	EDIT_Home(hwnd);
	EDIT_Downward(hwnd);
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->WndCol += EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), es->WndCol + es->wleft);
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->CurrCol++;
	if (es->WndCol >= ClientWidth(wndPtr))
	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
    }
}



/*********************************************************************
 *  EDIT_Downward
 *
 *  Cursor down key: move down one line.
 */

void EDIT_Downward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_Downward: WndRow=%d, wtop=%d, wlines=%d\n", 
	     es->WndRow, es->wtop, es->wlines);
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (IsMultiLine() && (es->WndRow + es->wtop + 1 < es->wlines))
    {
	es->CurrLine++;
	if (es->WndRow == ClientHeight(wndPtr, es) - 1)
	{
	    es->WndRow++;
	    EDIT_KeyVScrollLine(hwnd, SB_LINEDOWN);
	}
	else
	    es->WndRow++;
	EDIT_StickEnd(hwnd);
    }
}


/*********************************************************************
 *  EDIT_Upward
 *
 *  Cursor up key: move up one line.
 */

void EDIT_Upward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (IsMultiLine() && es->CurrLine != 0)
    {
	--es->CurrLine;
	if (es->WndRow == 0)
	{
	    --es->WndRow;
	    EDIT_KeyVScrollLine(hwnd, SB_LINEUP);
	}
	else
	    --es->WndRow;
	EDIT_StickEnd(hwnd);
    }
}


/*********************************************************************
 *  EDIT_Backward
 *
 *  Cursor left key: move left one character position.
 */

void EDIT_Backward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (es->CurrCol)
    {
	--es->CurrCol;
Alexandre Julliard's avatar
Alexandre Julliard committed
	if (*CurrChar == VK_TAB)
Alexandre Julliard's avatar
Alexandre Julliard committed
	    es->WndCol -= EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), 
Alexandre Julliard's avatar
Alexandre Julliard committed
					 EDIT_StrLength(hwnd, 
					 EDIT_TextLine(hwnd, es->CurrLine), 
					 es->CurrCol, 0));
	else
Alexandre Julliard's avatar
Alexandre Julliard committed
	    es->WndCol -= EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
	if (es->WndCol < 0)
	    EDIT_KeyHScroll(hwnd, SB_LINEUP);
    }
    else if (IsMultiLine() && es->CurrLine != 0)
    {
	EDIT_Upward(hwnd);
	EDIT_End(hwnd);
    }
}


/*********************************************************************
 *  EDIT_End
 *
 *  End key: move to end of line.
 */

void EDIT_End(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    while (*CurrChar && *CurrChar != '\n')
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->WndCol += EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), es->WndCol + es->wleft);
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->CurrCol++;
    }

    if (es->WndCol >= ClientWidth(wndPtr))
    {
	es->wleft = es->WndCol - ClientWidth(wndPtr) + HSCROLLDIM;
	es->WndCol -= es->wleft;
	InvalidateRect(hwnd, NULL, FALSE);
	UpdateWindow(hwnd);
    }
}


/*********************************************************************
 *  EDIT_Home
 *
 *  Home key: move to beginning of line.
 */

void EDIT_Home(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    es->CurrCol = es->WndCol = 0;
    if (es->wleft != 0)
    {
	es->wleft = 0;
	InvalidateRect(hwnd, NULL, FALSE);
	UpdateWindow(hwnd);
    }
}


/*********************************************************************
 *  EDIT_StickEnd
 *
 *  Stick the cursor to the end of the line.
 */

void EDIT_StickEnd(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    int len = EDIT_LineLength(hwnd, es->CurrLine);
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *cp = EDIT_TextLine(hwnd, es->CurrLine);
Alexandre Julliard's avatar
Alexandre Julliard committed
    char currpel;
Alexandre Julliard's avatar
Alexandre Julliard committed

    es->CurrCol = min(len, es->CurrCol);
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->WndCol = min(EDIT_StrLength(hwnd, cp, len, 0) - es->wleft, es->WndCol);
    currpel = EDIT_StrLength(hwnd, cp, es->CurrCol, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (es->wleft > currpel)
    {
	es->wleft = max(0, currpel - 20);
	es->WndCol = currpel - es->wleft;
	UpdateWindow(hwnd);
    }
    else if (currpel - es->wleft >= ClientWidth(wndPtr))
    {
	es->wleft = currpel - (ClientWidth(wndPtr) - 5);
	es->WndCol = currpel - es->wleft;
	UpdateWindow(hwnd);
    }
}


/*********************************************************************
 *  WM_KEYDOWN message function
 */

void EDIT_KeyDownMsg(HWND hwnd, WORD wParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_KeyDownMsg: key=%x\n", wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed

    HideCaret(hwnd);
    switch (wParam)
    {
    case VK_UP:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	if (IsMultiLine())
	    EDIT_Upward(hwnd);
	else
	    EDIT_Backward(hwnd);
	break;

    case VK_DOWN:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	if (IsMultiLine())
	    EDIT_Downward(hwnd);
	else
	    EDIT_Forward(hwnd);
	break;

    case VK_RIGHT:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	EDIT_Forward(hwnd);
	break;

    case VK_LEFT:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	EDIT_Backward(hwnd);
	break;

    case VK_HOME:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	EDIT_Home(hwnd);
	break;

    case VK_END:
	if (SelMarked(es))
	    EDIT_ClearSel(hwnd);
	EDIT_End(hwnd);
	break;

    case VK_PRIOR:
	if (IsMultiLine())
	{
	    if (SelMarked(es))
		EDIT_ClearSel(hwnd);
	    EDIT_KeyVScrollPage(hwnd, SB_PAGEUP);
	}
	break;

    case VK_NEXT:
	if (IsMultiLine())
	{
	    if (SelMarked(es))
		EDIT_ClearSel(hwnd);
	    EDIT_KeyVScrollPage(hwnd, SB_PAGEDOWN);
	}
	break;

    case VK_BACK:
	if (SelMarked(es))
	    EDIT_DeleteSel(hwnd);
	else
	{
	    if (es->CurrCol == 0 && es->CurrLine == 0)
		break;
	    EDIT_Backward(hwnd);
	    EDIT_DelKey(hwnd);
	}
	break;

    case VK_DELETE:
	if (SelMarked(es))
	    EDIT_DeleteSel(hwnd);
	else
	    EDIT_DelKey(hwnd);
	break;
    }

    SetCaretPos(es->WndCol, es->WndRow * es->txtht);
    ShowCaret(hwnd);
}


/*********************************************************************
 *  EDIT_KeyHScroll
 *
 *  Scroll text horizontally using cursor keys.
 */

void EDIT_KeyHScroll(HWND hwnd, WORD opt)
{
    int hscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (opt == SB_LINEDOWN)
    {
	es->wleft += HSCROLLDIM;
	es->WndCol -= HSCROLLDIM;
    }
    else
    {
	if (es->wleft == 0)
	    return;
	if (es->wleft - HSCROLLDIM < 0)
	{
	    es->WndCol += es->wleft;
	    es->wleft = 0;
	}	    
	else
	{
	    es->wleft -= HSCROLLDIM;
	    es->WndCol += HSCROLLDIM;
	}
    }

    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);

    if (IsHScrollBar())
    {
	hscrollpos = EDIT_ComputeHScrollPos(hwnd);
	SetScrollPos(hwnd, SB_HORZ, hscrollpos, TRUE);
    }
}


/*********************************************************************
 *  EDIT_KeyVScrollLine
 *
 *  Scroll text vertically by one line using keyboard.
 */

void EDIT_KeyVScrollLine(HWND hwnd, WORD opt)
{
    RECT rc;
    int y, vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (!IsMultiLine())
	return;

    if (opt == SB_LINEDOWN)
    {
	/* move down one line */
	if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
	    return;
	es->wtop++;
    }
    else
    {
	/* move up one line */
	if (es->wtop == 0)
	    return;
	--es->wtop;
    }

    if (IsWindowVisible(hwnd))
    {
	/* adjust client bottom to nearest whole line */
	GetClientRect(hwnd, &rc);
	rc.bottom = (rc.bottom / es->txtht) * es->txtht;

	if (opt == SB_LINEUP)
	{
	    /* move up one line (scroll window down) */
	    ScrollWindow(hwnd, 0, es->txtht, &rc, &rc);
	    /* write top line */
	    EDIT_WriteTextLine(hwnd, NULL, es->wtop);
	    es->WndRow++;
	}
	else
	{
	    /* move down one line (scroll window up) */
	    ScrollWindow(hwnd, 0, -(es->txtht), &rc, &rc);
	    /* write bottom line */
	    y = (((rc.bottom - rc.top) / es->txtht) - 1);
	    EDIT_WriteTextLine(hwnd, NULL, es->wtop + y);
	    --es->WndRow;
	}
    }

    /* reset the vertical scroll bar */
    if (IsVScrollBar())
    {
	vscrollpos = EDIT_ComputeVScrollPos(hwnd);
	SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
    }
}


/*********************************************************************
 *  EDIT_KeyVScrollPage
 *
 *  Scroll text vertically by one page using keyboard.
 */

void EDIT_KeyVScrollPage(HWND hwnd, WORD opt)
{
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (IsMultiLine())
    {
	if (opt == SB_PAGEUP)
	{
	    if (es->wtop)
		es->wtop -= ClientHeight(wndPtr, es);
	}
	else
	{
	    if (es->wtop + ClientHeight(wndPtr, es) < es->wlines)
	    {
		es->wtop += ClientHeight(wndPtr, es);
		if (es->wtop > es->wlines - ClientHeight(wndPtr, es))
		    es->wtop = es->wlines - ClientHeight(wndPtr, es);
	    }
	}
	if (es->wtop < 0)
	    es->wtop = 0;

	es->CurrLine = es->wtop + es->WndRow;
	EDIT_StickEnd(hwnd);
	InvalidateRect(hwnd, NULL, TRUE);
	UpdateWindow(hwnd);

	/* reset the vertical scroll bar */
	if (IsVScrollBar())
	{
	    vscrollpos = EDIT_ComputeVScrollPos(hwnd);
	    SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
	}
    }
}


/*********************************************************************
 *  EDIT_KeyVScrollDoc
 *
 *  Scroll text to top and bottom of document using keyboard.
 */

void EDIT_KeyVScrollDoc(HWND hwnd, WORD opt)
{
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (!IsMultiLine())
	return;

    if (opt == SB_TOP)
	es->wtop = es->wleft = 0;
    else if (es->wtop + ClientHeight(wndPtr, es) < es->wlines)
    {
	es->wtop = es->wlines - ClientHeight(wndPtr, es);
	es->wleft = 0;
    }

    es->CurrLine = es->wlines;
    es->WndRow = es->wlines - es->wtop;
    EDIT_End(hwnd);
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);

    /* reset the vertical scroll bar */
    if (IsVScrollBar())
    {
	vscrollpos = EDIT_ComputeVScrollPos(hwnd);
	SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
    }
}


/*********************************************************************
 *  EDIT_ComputeVScrollPos
 *
 *  Compute the vertical scroll bar position from the window
 *  position and text width.
 */

int EDIT_ComputeVScrollPos(HWND hwnd)
{
    int vscrollpos;
    short minpos, maxpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    GetScrollRange(hwnd, SB_VERT, &minpos, &maxpos);

    if (es->wlines > ClientHeight(wndPtr, es))
	vscrollpos = (double)(es->wtop) / (double)(es->wlines - 
		     ClientHeight(wndPtr, es)) * (maxpos - minpos);
    else
	vscrollpos = minpos;

    return vscrollpos;
}
    

/*********************************************************************
 *  EDIT_ComputeHScrollPos
 *
 *  Compute the horizontal scroll bar position from the window
 *  position and text width.
 */

int EDIT_ComputeHScrollPos(HWND hwnd)
{
    int hscrollpos;
    short minpos, maxpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    GetScrollRange(hwnd, SB_HORZ, &minpos, &maxpos);

    if (es->textwidth > ClientWidth(wndPtr))
	hscrollpos = (double)(es->wleft) / (double)(es->textwidth - 
		     ClientWidth(wndPtr)) * (maxpos - minpos);
    else
	hscrollpos = minpos;

    return hscrollpos;
}


/*********************************************************************
 *  EDIT_DelKey
 *
 *  Delete character to right of cursor.
 */

void EDIT_DelKey(HWND hwnd)
{
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *currchar = CurrChar;
    BOOL repaint = *currchar == '\n';

    if (IsMultiLine() && *currchar == '\n' && *(currchar + 1) == '\0')
	return;
    strcpy(currchar, currchar + 1);
    NOTIFY_PARENT(hwnd, EN_UPDATE);
    
    if (repaint)
    {
	EDIT_BuildTextPointers(hwnd);
	GetClientRect(hwnd, &rc);
	rc.top = es->WndRow * es->txtht;
	InvalidateRect(hwnd, &rc, FALSE);
	UpdateWindow(hwnd);
    }
    else
    {
	EDIT_ModTextPointers(hwnd, es->CurrLine + 1, -1);
	EDIT_WriteTextLine(hwnd, NULL, es->WndRow + es->wtop);
    }

    es->TextChanged = TRUE;
    NOTIFY_PARENT(hwnd, EN_CHANGE);
}

/*********************************************************************
 *  WM_VSCROLL message function
 */

void EDIT_VScrollMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (IsMultiLine())
    {
	HideCaret(hwnd);

	switch (wParam)
	{
	case SB_LINEUP:
	case SB_LINEDOWN:
	    EDIT_VScrollLine(hwnd, wParam);
	    break;

	case SB_PAGEUP:
	case SB_PAGEDOWN:
	    EDIT_VScrollPage(hwnd, wParam);
	    break;
	}
    }
    
    SetCaretPos(es->WndCol, es->WndRow);
    ShowCaret(hwnd);
}


/*********************************************************************
 *  EDIT_VScrollLine
 *
 *  Scroll text vertically by one line using scrollbars.
 */

void EDIT_VScrollLine(HWND hwnd, WORD opt)
{
    RECT rc;
    int y;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    dprintf_edit(stddeb,"EDIT_VScrollLine: direction=%d\n", opt);
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (opt == SB_LINEDOWN)
    {
	/* move down one line */
	if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
	    return;
	es->wtop++;
    }
    else
    {
	/* move up one line */
	if (es->wtop == 0)
	    return;
	--es->wtop;
    }

    if (IsWindowVisible(hwnd))
    {
	/* adjust client bottom to nearest whole line */
	GetClientRect(hwnd, &rc);
	rc.bottom = (rc.bottom / es->txtht) * es->txtht;