/*
 * Edit control
 *
 * Copyright  David W. Metcalfe, 1994
 *
 * Release 1, April 1994
 */

static char Copyright[] = "Copyright  David W. Metcalfe, 1994";

#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "win.h"
#include "class.h"
#include "user.h"

/*
#define DEBUG_EDIT
*/

#define NOTIFY_PARENT(hWndCntrl, wNotifyCode) \
	SendMessage(GetParent(hWndCntrl), WM_COMMAND, \
		 GetDlgCtrlID(hWndCntrl), MAKELPARAM(hWndCntrl, wNotifyCode));

#define MAXTEXTLEN 32000   /* maximum text buffer length */
#define EDITLEN     1024   /* starting length for multi-line control */
#define ENTRYLEN     256   /* starting length for single line control */
#define GROWLENGTH    64   /* buffers grow by this much */

#define HSCROLLDIM (ClientWidth(wndPtr) / 3)
                           /* "line" dimension for horizontal scroll */

#define EDIT_HEAP_ALLOC(size)          USER_HEAP_ALLOC(GMEM_MOVEABLE,size)
#define EDIT_HEAP_REALLOC(handle,size) USER_HEAP_REALLOC(handle,size,\
							 GMEM_MOVEABLE)
#define EDIT_HEAP_ADDR(handle)         USER_HEAP_ADDR(handle)
#define EDIT_HEAP_FREE(handle)         USER_HEAP_FREE(handle)

typedef struct
{
    int wlines;              /* number of lines of text */
    int wtop;                /* top line that is displayed */
    int wleft;               /* left pixel that is displayed */
    unsigned int textlen;    /* text buffer length */
    int textwidth;           /* width of longest line in pixels */
    RECT fmtrc;              /* rectangle in which to format text */
    int txtht;               /* height of text line in pixels */
    HANDLE hText;            /* handle to text buffer */
    HANDLE hCharWidths;      /* widths of chars in font */
    HANDLE hTextPtrs;        /* list of line offsets */
    HANDLE hBlankLine;       /* to fill blank lines quickly */
    int CurrCol;             /* current column */
    int CurrLine;            /* current line */
    int WndCol;              /* current window column */
    int WndRow;              /* current window row */
    BOOL TextChanged;        /* TRUE if text has changed */
    BOOL PaintBkgd;          /* paint control background */
    unsigned int MaxTextLen; /* maximum text buffer length */
    int SelBegLine;          /* beginning line of selection */
    int SelBegCol;           /* beginning column of selection */
    int SelEndLine;          /* ending line of selection */
    int SelEndCol;           /* ending column of selection */
} EDITSTATE;


#define ClientWidth(wndPtr) (wndPtr->rectClient.right - \
			     wndPtr->rectClient.left)
#define ClientHeight(wndPtr, es) ((wndPtr->rectClient.bottom - \
				   wndPtr->rectClient.top) / es->txtht)
#define EditBufLen(wndPtr) (wndPtr->dwStyle & ES_MULTILINE \
			    ? EDITLEN : ENTRYLEN)
#define CurrChar (EDIT_TextLine(hwnd, es->CurrLine) + es->CurrCol)
#define SelMarked(es) (es->SelBegLine != -1 && es->SelBegCol != -1 && \
		       es->SelEndLine != -1 && es->SelEndCol != -1)

/* macros to access window styles */
#define IsAutoVScroll() (wndPtr->dwStyle & ES_AUTOVSCROLL)
#define IsAutoHScroll() (wndPtr->dwStyle & ES_AUTOHSCROLL)
#define IsMultiLine() (wndPtr->dwStyle & ES_MULTILINE)
#define IsVScrollBar() (wndPtr->dwStyle & WS_VSCROLL)
#define IsHScrollBar() (wndPtr->dwStyle & WS_HSCROLL)

/* internal variables */
static BOOL TextMarking;         /* TRUE if text marking in progress */
static BOOL ButtonDown;          /* TRUE if left mouse button down */
static int ButtonRow;              /* row in text buffer when button pressed */
static int ButtonCol;              /* col in text buffer when button pressed */
static BOOL Print = FALSE;


LONG EditWndProc(HWND hWnd, WORD uMsg, WORD wParam, LONG lParam);
long EDIT_CreateMsg(HWND hwnd, LONG lParam);
void EDIT_ClearTextPointers(HWND hwnd);
void EDIT_BuildTextPointers(HWND hwnd);
void EDIT_ModTextPointers(HWND hwnd, int lineno, int var);
void EDIT_PaintMsg(HWND hwnd);
HANDLE EDIT_GetTextLine(HWND hwnd, int selection);
char *EDIT_TextLine(HWND hwnd, int sel);
int EDIT_LineLength(EDITSTATE *es, char *str, int len);
void EDIT_WriteTextLine(HWND hwnd, RECT *rc, int y);
void EDIT_WriteText(HWND hwnd, char *lp, int off, int len, int row, 
		    int col, RECT *rc, BOOL blank, BOOL reverse);
HANDLE EDIT_GetStr(EDITSTATE *es, char *lp, int off, int len, int *diff);
void EDIT_CharMsg(HWND hwnd, WORD wParam);
void EDIT_KeyTyped(HWND hwnd, short ch);
int EDIT_CharWidth(EDITSTATE *es, short ch);
void EDIT_Forward(HWND hwnd);
void EDIT_Downward(HWND hwnd);
void EDIT_Upward(HWND hwnd);
void EDIT_Backward(HWND hwnd);
void EDIT_End(HWND hwnd);
void EDIT_Home(HWND hwnd);
void EDIT_StickEnd(HWND hwnd);
void EDIT_KeyDownMsg(HWND hwnd, WORD wParam);
void EDIT_KeyHScroll(HWND hwnd, WORD opt);
void EDIT_KeyVScrollLine(HWND hwnd, WORD opt);
void EDIT_KeyVScrollPage(HWND hwnd, WORD opt);
void EDIT_KeyVScrollDoc(HWND hwnd, WORD opt);
int EDIT_ComputeVScrollPos(HWND hwnd);
int EDIT_ComputeHScrollPos(HWND hwnd);
void EDIT_DelKey(HWND hwnd);
void EDIT_VScrollMsg(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_VScrollLine(HWND hwnd, WORD opt);
void EDIT_VScrollPage(HWND hwnd, WORD opt);
void EDIT_HScrollMsg(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_SizeMsg(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_LButtonDownMsg(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_MouseMoveMsg(HWND hwnd, WORD wParam, LONG lParam);
int EDIT_PixelToChar(HWND hwnd, int row, int *pixel);
LONG EDIT_SetTextMsg(HWND hwnd, LONG lParam);
void EDIT_ClearText(HWND hwnd);
void EDIT_SetSelMsg(HWND hwnd, LONG lParam);
void EDIT_GetLineCol(HWND hwnd, int off, int *line, int *col);
void EDIT_DeleteSel(HWND hwnd);
void EDIT_ClearSel(HWND hwnd);
int EDIT_TextLineNumber(HWND hwnd, char *lp);
void EDIT_SetAnchor(HWND hwnd, int row, int col);
void EDIT_ExtendSel(HWND hwnd, int x, int y);
void EDIT_StopMarking(HWND hwnd);
LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam);
LONG EDIT_GetSelMsg(HWND hwnd);
LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam);
LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam);
void swap(int *a, int *b);


LONG EditWndProc(HWND hwnd, WORD uMsg, WORD wParam, LONG lParam)
{
    LONG lResult = 0L;
    HDC hdc;
    char *textPtr;
    int len;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    switch (uMsg) {
    case EM_CANUNDO:
	/* cannot process undo message */
	lResult = 0L;
	break;
	
    case EM_FMTLINES:
	printf("edit: cannot process EM_FMTLINES message\n");
	lResult = 0L;
	break;

    case EM_GETFIRSTVISIBLELINE:
	lResult = es->wtop;
	break;

    case EM_GETHANDLE:
	printf("edit: cannot process EM_GETHANDLE message\n");
	break;

    case EM_GETLINE:
	if (IsMultiLine())
	    lResult = EDIT_GetLineMsg(hwnd, wParam, lParam);
	else
	    lResult = 0L;
	break;

    case EM_GETLINECOUNT:
	if (IsMultiLine())
	    lResult = es->wlines;
	else
	    lResult = 0L;
	break;

    case EM_GETMODIFY:
	lResult = es->TextChanged;
	break;

    case EM_GETPASSWORDCHAR:
	printf("edit: cannot process EM_GETPASSWORDCHAR message\n");
	break;

    case EM_GETRECT:
	GetWindowRect(hwnd, (LPRECT)lParam);
	break;

    case EM_GETSEL:
	lResult = EDIT_GetSelMsg(hwnd);
	break;

    case EM_GETWORDBREAKPROC:
	printf("edit: cannot process EM_GETWORDBREAKPROC message\n");
	break;

    case EM_LIMITTEXT:
	es->MaxTextLen = wParam;
	break;

    case EM_LINEFROMCHAR:
	lResult = EDIT_LineFromCharMsg(hwnd, wParam);
	break;

    case EM_LINEINDEX:
	if (IsMultiLine())
	    lResult = EDIT_LineIndexMsg(hwnd, wParam);
	else
	    lResult = 0L;
	break;

    case EM_LINELENGTH:
	printf("edit: cannot process EM_LINELENGTH message\n");
	break;

    case EM_LINESCROLL:
	printf("edit: cannot process EM_LINESCROLL message\n");
	break;

    case EM_REPLACESEL:
	printf("edit: cannot process EM_REPLACESEL message\n");
	break;

    case EM_SETHANDLE:
	printf("edit: cannot process EM_SETHANDLE message\n");
	break;

    case EM_SETMODIFY:
	es->TextChanged = wParam;
	break;

    case EM_SETPASSWORDCHAR:
	printf("edit: cannot process EM_SETPASSWORDCHAR message\n");
	break;

    case EM_SETREADONLY:
	printf("edit: cannot process EM_SETREADONLY message\n");
	break;

    case EM_SETRECT:
    case EM_SETRECTNP:
	printf("edit: cannot process EM_SETRECT(NP) message\n");
	break;

    case EM_SETSEL:
	HideCaret(hwnd);
	EDIT_SetSelMsg(hwnd, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

    case EM_SETTABSTOPS:
	printf("edit: cannot process EM_SETTABSTOPS message\n");
	break;

    case EM_SETWORDBREAKPROC:
	printf("edit: cannot process EM_SETWORDBREAKPROC message\n");
	break;

    case WM_CHAR:
	EDIT_CharMsg(hwnd, wParam);
	break;

    case WM_CREATE:
	lResult = EDIT_CreateMsg(hwnd, lParam);
	break;

    case WM_DESTROY:
	EDIT_HEAP_FREE(es->hTextPtrs);
	EDIT_HEAP_FREE(es->hCharWidths);
	EDIT_HEAP_FREE((HANDLE)(*(wndPtr->wExtra)));
	break;

    case WM_ENABLE:
	InvalidateRect(hwnd, NULL, FALSE);
	break;

    case WM_GETTEXT:
	textPtr = (LPSTR)EDIT_HEAP_ADDR(es->hText);
	if ((int)wParam > (len = strlen(textPtr)))
	{
	    strcpy((char *)lParam, textPtr);
	    lResult = (DWORD)len;
	}
	else
	    lResult = 0L;
	break;

    case WM_GETTEXTLENGTH:
	textPtr = (LPSTR)EDIT_HEAP_ADDR(es->hText);
	lResult = (DWORD)strlen(textPtr);
	break;

    case WM_HSCROLL:
	EDIT_HScrollMsg(hwnd, wParam, lParam);
	break;

    case WM_KEYDOWN:
	EDIT_KeyDownMsg(hwnd, wParam);
	break;

    case WM_KILLFOCUS:
	DestroyCaret();
	NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
	break;

    case WM_LBUTTONDOWN:
	HideCaret(hwnd);
	SetFocus(hwnd);
	EDIT_LButtonDownMsg(hwnd, wParam, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

    case WM_LBUTTONUP:
	ButtonDown = FALSE;
	if (TextMarking)
	    EDIT_StopMarking(hwnd);
	break;

    case WM_MOUSEMOVE:
	HideCaret(hwnd);
	EDIT_MouseMoveMsg(hwnd, wParam, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

    case WM_MOVE:
	lResult = 0;
	break;

    case WM_PAINT:
	EDIT_PaintMsg(hwnd);
	break;

    case WM_SETFOCUS:
	es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
	CreateCaret(hwnd, 0, 2, es->txtht);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	NOTIFY_PARENT(hwnd, EN_SETFOCUS);
	break;

    case WM_SETFONT:
	break;

    case WM_SETTEXT:
	EDIT_SetTextMsg(hwnd, lParam);
	break;

    case WM_SIZE:
	EDIT_SizeMsg(hwnd, wParam, lParam);
	lResult = 0;
	break;

    case WM_VSCROLL:
	EDIT_VScrollMsg(hwnd, wParam, lParam);
	break;

    default:
	lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
	break;
    }

    GlobalUnlock(hwnd);
    return lResult;
}


/*********************************************************************
 *  WM_CREATE message function
 */

long EDIT_CreateMsg(HWND hwnd, LONG lParam)
{
    HDC hdc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es;
    CLASS *classPtr;
    short *charWidths;
    TEXTMETRIC tm;
    char *text;
    unsigned int *textPtrs;

    /* allocate space for state variable structure */
    (HANDLE)(*(wndPtr->wExtra)) = 
	EDIT_HEAP_ALLOC(sizeof(EDITSTATE));
    es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    es->hTextPtrs = EDIT_HEAP_ALLOC(sizeof(int));
    textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
    es->hCharWidths = EDIT_HEAP_ALLOC(256 * sizeof(short));

    /* initialize state variable structure */
    /* --- char width array */
    hdc = GetDC(hwnd);
    charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
    memset(charWidths, 0, 256 * sizeof(short));
    GetCharWidth(hdc, 0, 255, charWidths);

    /* --- text buffer */
    es->MaxTextLen = MAXTEXTLEN + 1;
    if (!(wndPtr->hText))
    {
	es->textlen = EditBufLen(wndPtr);
	es->hText = EDIT_HEAP_ALLOC(EditBufLen(wndPtr) + 2);
	text = (LPSTR)EDIT_HEAP_ADDR(es->hText);
	memset(text, 0, es->textlen + 2);
	EDIT_ClearTextPointers(hwnd);
    }
    else
    {
	es->hText = wndPtr->hText;
	wndPtr->hText = 0;
	es->textlen = GetWindowTextLength(hwnd) + 1;
	EDIT_BuildTextPointers(hwnd);
    }

    /* --- other structure variables */
    GetTextMetrics(hdc, &tm);
    es->txtht = tm.tmHeight + tm.tmExternalLeading;
    es->wlines = 0;
    es->wtop = es->wleft = 0;
    es->CurrCol = es->CurrLine = 0;
    es->WndCol = es->WndRow = 0;
    es->TextChanged = FALSE;
    es->textwidth = 0;
    es->SelBegLine = es->SelBegCol = -1;
    es->SelEndLine = es->SelEndCol = -1;

    /* allocate space for a line full of blanks to speed up */
    /* line filling */
    es->hBlankLine = EDIT_HEAP_ALLOC((ClientWidth(wndPtr) / 
				      charWidths[32]) + 2); 
    text = EDIT_HEAP_ADDR(es->hBlankLine);
    memset(text, ' ', (ClientWidth(wndPtr) / charWidths[32]) + 2);

    /* set up text cursor for edit class */
    CLASS_FindClassByName("EDIT", &classPtr);
    classPtr->wc.hCursor = LoadCursor(0, IDC_IBEAM);

    /* paint background on first WM_PAINT */
    es->PaintBkgd = TRUE;

    ReleaseDC(hwnd, hdc);
    return 0L;
}


/*********************************************************************
 *  EDIT_ClearTextPointers
 *
 *  Clear and initialize text line pointer array.
 */

void EDIT_ClearTextPointers(HWND hwnd)
{
    unsigned int *textPtrs;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    
    es->hTextPtrs = EDIT_HEAP_REALLOC(es->hTextPtrs, sizeof(int));
    textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
    *textPtrs = 0;
}


/*********************************************************************
 *  EDIT_BuildTextPointers
 *
 *  Build array of pointers to text lines.
 */

#define INITLINES 100

void EDIT_BuildTextPointers(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    char *text, *cp;
    int incrs = INITLINES;
    unsigned int off, len, temp;
    EDITSTATE *es;
    unsigned int *textPtrs;
    short *charWidths;

    es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    text = (char *)EDIT_HEAP_ADDR(es->hText);
    textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
    charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

    es->textwidth = es->wlines = 0;
    cp = text;

    /* advance through text buffer */
    while (*cp)
    {
	/* increase size of text pointer array */ 
	if (incrs == INITLINES)
	{
	    incrs = 0;
	    es->hTextPtrs = EDIT_HEAP_REALLOC(es->hTextPtrs,
			      (es->wlines + INITLINES) * sizeof(int));
	    textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
	}
	off = (unsigned int)(cp - text);     /* offset of beginning of line */
	*(textPtrs + es->wlines) = off;
	es->wlines++;
	incrs++;
	len = 0;
	
	/* advance through current line */
	while (*cp && *cp != '\n')
	{
	    len += charWidths[*cp];          /* width of line in pixels */
	    cp++;
	}
	es->textwidth = max(es->textwidth, len);
	if (*cp)
	    cp++;                            /* skip '\n' */
    }
    off = (unsigned int)(cp - text);     /* offset of beginning of line */
    *(textPtrs + es->wlines) = off;
}


/*********************************************************************
 *  EDIT_ModTextPointers
 *
 *  Modify text pointers from a specified position.
 */

void EDIT_ModTextPointers(HWND hwnd, int lineno, int var)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    while (lineno < es->wlines)
	*(textPtrs + lineno++) += var;
}


/*********************************************************************
 *  WM_PAINT message function
 */

void EDIT_PaintMsg(HWND hwnd)
{
    PAINTSTRUCT ps;
    HDC hdc;
    int y;
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    hdc = BeginPaint(hwnd, &ps);
    rc = ps.rcPaint;

#ifdef DEBUG_EDIT
    printf("WM_PAINT: rc=(%d,%d), (%d,%d)\n", rc.left, rc.top, 
	   rc.right, rc.bottom);
#endif

    if (es->PaintBkgd)
	FillWindow(GetParent(hwnd), hwnd, hdc, CTLCOLOR_EDIT);

    for (y = (rc.top / es->txtht); y <= (rc.bottom / es->txtht); y++)
    {
	if (y < es->wlines - es->wtop)
	    EDIT_WriteTextLine(hwnd, &rc, y + es->wtop);
    }

    EndPaint(hwnd, &ps);
}


/*********************************************************************
 *  EDIT_GetTextLine
 *
 *  Get a copy of the text in the specified line.
 */

HANDLE EDIT_GetTextLine(HWND hwnd, int selection)
{
    char *line;
    HANDLE hLine;
    int len = 0;
    char *cp, *cp1;

#ifdef DEBUG_EDIT
    printf("GetTextLine %d\n", selection);
#endif
    cp = cp1 = EDIT_TextLine(hwnd, selection);
    /* advance through line */
    while (*cp && *cp != '\n')
    {
	len++;
	cp++;
    }

    /* store selected line and return handle */
    hLine = EDIT_HEAP_ALLOC(len + 6);
    line = (char *)EDIT_HEAP_ADDR(hLine);
    memmove(line, cp1, len);
    line[len] = '\0';
    return hLine;
}


/*********************************************************************
 *  EDIT_TextLine
 *
 *  Return a pointer to the text in the specified line.
 */

char *EDIT_TextLine(HWND hwnd, int sel)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    return (text + *(textPtrs + sel));
}
    

/*********************************************************************
 *  EDIT_LineLength
 *
 *  Return length of line _str_ of length _len_ characters in pixels.
 */

int EDIT_LineLength(EDITSTATE *es, char *str, int len)
{
    int i, plen = 0;
    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

    for (i = 0; i < len; i++)
	plen += charWidths[*(str + i)];

#ifdef DEBUG_EDIT
    printf("EDIT_LineLength: returning %d\n", plen);
#endif
    return plen;
}


/*********************************************************************
 *  EDIT_WriteTextLine
 *
 *  Write the line of text at offset _y_ in text buffer to a window.
 */

void EDIT_WriteTextLine(HWND hwnd, RECT *rect, int y)
{
    int len = 0;
    unsigned char line[200];
    HANDLE hLine;
    unsigned char *lp;
    int lnlen, lnlen1;
    int col, off = 0;
    int sbl, sel, sbc, sec;
    RECT rc;

    BOOL trunc = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    /* initialize rectangle if NULL, else copy */
    if (rect)
	CopyRect(&rc, rect);
    else
	GetClientRect(hwnd, &rc);

#ifdef DEBUG_EDIT
    printf("WriteTextLine %d\n", y);
#endif

    /* make sure y is inside the window */
    if (y < es->wtop || y > (es->wtop + ClientHeight(wndPtr, es)))
    {
#ifdef DEBUG_EDIT
	printf("EDIT_WriteTextLine: y (%d) is not a displayed line\n", y);
#endif
	return;
    }

    /* make sure rectangle is within window */
    if (rc.left >= ClientWidth(wndPtr) - 1)
    {
#ifdef DEBUG_EDIT
	printf("EDIT_WriteTextLine: rc.left (%d) is greater than right edge\n",
	       rc.left);
#endif
	return;
    }
    if (rc.right <= 0)
    {
#ifdef DEBUG_EDIT
	printf("EDIT_WriteTextLine: rc.right (%d) is less than left edge\n",
	       rc.right);
#endif
	return;
    }
    if (y - es->wtop < (rc.top / es->txtht) || 
	y - es->wtop > (rc.bottom / es->txtht))
    {
#ifdef DEBUG_EDIT
	printf("EDIT_WriteTextLine: y (%d) is outside window\n", y);
#endif
	return;
    }

    /* get the text and length of line */
    if ((hLine = EDIT_GetTextLine(hwnd, y)) == 0)
	return;
    lp = (unsigned char *)EDIT_HEAP_ADDR(hLine);
    lnlen = EDIT_LineLength(es, lp, strlen(lp));
    lnlen1 = lnlen;

    /* build the line to display */
    if (lnlen < es->wleft)
	lnlen = 0;
    else
	off += es->wleft;

    if (lnlen > rc.left)
    {
	off += rc.left;
	lnlen = lnlen1 - off;
	len = min(lnlen, rc.right - rc.left);
    }

    if (SelMarked(es))
    {
	sbl = es->SelBegLine;
	sel = es->SelEndLine;
	sbc = es->SelBegCol;
	sec = es->SelEndCol;

	/* put lowest marker first */
	if (sbl > sel)
	{
	    swap(&sbl, &sel);
	    swap(&sbc, &sec);
	}
	if (sbl == sel && sbc > sec)
	    swap(&sbc, &sec);

	if (y < sbl || y > sel)
	    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
			   TRUE, FALSE);
	else if (y > sbl && y < sel)
	    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
			   TRUE, TRUE);
	else if (y == sbl)
	{
	    col = EDIT_LineLength(es, lp, sbc);
	    if (col > (es->wleft + rc.left))
	    {
		len = min(col - off, rc.right - off);
		EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, 
			       rc.left, &rc, FALSE, FALSE);
		off = col;
	    }
	    if (y == sel)
	    {
		col = EDIT_LineLength(es, lp, sec);
		if (col < (es->wleft + rc.right))
		{
		    len = min(col - off, rc.right - off);
		    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
				   off - es->wleft, &rc, FALSE, TRUE);
		    off = col;
		    len = min(lnlen - off, rc.right - off);
		    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
				   off - es->wleft, &rc, TRUE, FALSE);
		}
		else
		{
		    len = min(lnlen - off, rc.right - off);
		    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
				   off - es->wleft, &rc, TRUE, TRUE);
		}
	    }
	    else
	    {
		len = min(lnlen - off, rc.right - off);
		if (col < (es->wleft + rc.right))
		    EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
				   off - es->wleft, &rc, TRUE, TRUE);
	    }
	}
	else if (y == sel)
	{
	    col = EDIT_LineLength(es, lp, sec);
	    if (col < (es->wleft + rc.right))
	    {
		len = min(col - off, rc.right - off);
		EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
			       off - es->wleft, &rc, FALSE, TRUE);
		off = col;
		len = min(lnlen - off, rc.right - off);
		EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
			       off - es->wleft, &rc, TRUE, FALSE);
	    }
	}
    }
    else
	EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
		       TRUE, FALSE);
	      
    EDIT_HEAP_FREE(hLine);
}


/*********************************************************************
 *  EDIT_WriteText
 *
 *  Write text to a window
 *     lp  - text line
 *     off - offset in text line (in pixels)
 *     len - length from off (in pixels)
 *     row - line in window
 *     col - column in window
 *     rc  - rectangle in which to display line
 *     blank - blank remainder of line?
 *     reverse - reverse color of line?
 */

void EDIT_WriteText(HWND hwnd, char *lp, int off, int len, int row, 
		    int col, RECT *rc, BOOL blank, BOOL reverse)
{
    HDC hdc;
    HANDLE hStr;
    char *str, *blanks;
    int diff, num_spaces;
    HRGN hrgnClip;
    COLORREF oldTextColor, oldBkgdColor;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

#ifdef DEBUG_EDIT
    printf("EDIT_WriteText lp=%s, off=%d, len=%d, row=%d, col=%d, reverse=%d\n", lp, off, len, row, col, reverse);
#endif

    hdc = GetDC(hwnd);
    hStr = EDIT_GetStr(es, lp, off, len, &diff);
    str = (char *)EDIT_HEAP_ADDR(hStr);
    hrgnClip = CreateRectRgnIndirect(rc);
    SelectClipRgn(hdc, hrgnClip);

    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);
    }

    TextOut(hdc, col - diff, row * es->txtht, str, strlen(str));

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

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

    EDIT_HEAP_FREE(hStr);
    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.
 */

HANDLE EDIT_GetStr(EDITSTATE *es, char *lp, int off, int len, int *diff)
{
    HANDLE hStr;
    char *str;
    int ch = 0, i = 0, j, tmp;
    int ch1;
    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

#ifdef DEBUG_EDIT
    printf("EDIT_GetStr %s %d %d\n", lp, off, len);
#endif

    while (i < off)
    {
	i += charWidths[*(lp + ch)];
	ch++;
    }

    /* if stepped past _off_, go back a character */
    if (i - off)
	i -= charWidths[*(lp + --ch)];
    *diff = off - i;
    ch1 = ch;

    while (i < len + off)
    {
	i += charWidths[*(lp + ch)];
	ch++;
    }
    
    hStr = EDIT_HEAP_ALLOC(ch - ch1 + 3);
    str = (char *)EDIT_HEAP_ADDR(hStr);
    for (i = ch1, j = 0; i < ch; i++, j++)
	str[j] = lp[i];
    str[++j] = '\0';
#ifdef DEBUG_EDIT
    printf("EDIT_GetStr: returning %s\n", str);
#endif
    return hStr;
}


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

void EDIT_CharMsg(HWND hwnd, WORD wParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

#ifdef DEBUG_EDIT
    printf("EDIT_CharMsg: wParam=%c\n", (char)wParam);
#endif

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

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);
    char *currchar = CurrChar;
    RECT rc;
    BOOL FullPaint = FALSE;

#ifdef DEBUG_EDIT
    printf("EDIT_KeyTyped: ch=%c\n", (char)ch);
#endif

    /* 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;
    }

    if (*currchar == '\0')
    {
	/* 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;
	es->hText = EDIT_HEAP_REALLOC(es->hText, es->textlen + 2);
	if (!es->hText)
	    NOTIFY_PARENT(hwnd, EN_ERRSPACE);
	text = (char *)EDIT_HEAP_ADDR(es->hText);
	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,
		    EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
		    (int)(EDIT_TextLine(hwnd, es->CurrLine + 1) -
			  EDIT_TextLine(hwnd, es->CurrLine))));
    }
    else
	es->textwidth = max(es->textwidth,
			    EDIT_LineLength(es, text, strlen(text)));
    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 */
    if (es->WndCol >= ClientWidth(wndPtr) - EDIT_CharWidth(es, ch))
    {
	/* TODO:- Word wrap to be handled here */

/*	if (!(currchar == text + es->MaxTextLen - 2)) */
	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
    }
    es->WndCol += EDIT_CharWidth(es, ch);
    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.
 */

int EDIT_CharWidth(EDITSTATE *es, short ch)
{
    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

    return (charWidths[ch]);
}


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

void EDIT_Forward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);
    char *cc = CurrChar + 1;

    if (*cc == '\0')
	return;

    if (*CurrChar == '\n')
    {
	EDIT_Home(hwnd);
	EDIT_Downward(hwnd);
    }
    else
    {
	es->WndCol += EDIT_CharWidth(es, *CurrChar);
	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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

#ifdef DEBUG_EDIT
    printf("EDIT_Downward: WndRow=%d, wtop=%d, wlines=%d\n", es->WndRow, es->wtop, es->wlines);
#endif

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);

    if (es->CurrCol)
    {
	--es->CurrCol;
	es->WndCol -= EDIT_CharWidth(es, *CurrChar);
	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)
{
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);

    while (*CurrChar && *CurrChar != '\n')
    {
	es->WndCol += EDIT_CharWidth(es, *CurrChar);
	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)
{
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char currpel;

    char *cp = EDIT_TextLine(hwnd, es->CurrLine);
    char *cp1 = strchr(cp, '\n');
    int len = cp1 ? (int)(cp1 - cp) : 0;

    es->CurrCol = min(len, es->CurrCol);
    es->WndCol = min(EDIT_LineLength(es, cp, len) - es->wleft, es->WndCol);
    currpel = EDIT_LineLength(es, cp, es->CurrCol);

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

#ifdef DEBUG_EDIT
    printf("EDIT_KeyDownMsg: key=%x\n", wParam);
#endif

    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)
{
    RECT rc;
    int hscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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)
{
    RECT rc;
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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)
{
    RECT rc;
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

#ifdef DEBUG_EDIT
    printf("EDIT_VScrollLine: direction=%d\n", opt);
#endif

    if (opt == SB_LINEDOWN)
    {
	/* move down one line */
	if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
	    return;
	es->wtop++;
	printf("Scroll line down: wtop=%d\n", es->wtop);
    }
    else
    {
	/* move up one line */
	if (es->wtop == 0)
	    return;
	--es->wtop;
	printf("Scroll line up: wtop=%d\n", 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;
	}
    }
}


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

void EDIT_VScrollPage(HWND hwnd, WORD opt)
{
    RECT rc;
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    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;
    
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);

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


/*********************************************************************
 *  WM_HSCROLL message function
 */

void EDIT_HScrollMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    switch (wParam)
    {
    case SB_LINEUP:
    case SB_LINEDOWN:
	HideCaret(hwnd);

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


/*********************************************************************
 *  WM_SIZE message function
 */

void EDIT_SizeMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    if (wParam != SIZE_MAXIMIZED && wParam != SIZE_RESTORED) return;

    InvalidateRect(hwnd, NULL, TRUE);
    es->PaintBkgd = TRUE;
    UpdateWindow(hwnd);
}


/*********************************************************************
 *  WM_LBUTTONDOWN message function
 */

void EDIT_LButtonDownMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    char *cp, *cp1;
    int len;
    BOOL end = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    if (SelMarked(es))
	EDIT_ClearSel(hwnd);

    es->WndRow = HIWORD(lParam) / es->txtht;
    if (es->WndRow > es->wlines - es->wtop - 1)
    {
	if (es->wlines)
	    es->WndRow = es->wlines - es->wtop - 1;
	else
	    es->WndRow = 0;
	end = TRUE;
    }
    es->CurrLine = es->wtop + es->WndRow;

    cp = EDIT_TextLine(hwnd, es->CurrLine);
    cp1 = strchr(cp, '\n');
    len = cp1 ? (int)(cp1 - cp) : 0;

    es->WndCol = LOWORD(lParam);
    if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
	es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
    es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));

    ButtonDown = TRUE;
    ButtonRow = es->CurrLine;
    ButtonCol = es->CurrCol;
}


/*********************************************************************
 *  WM_MOUSEMOVE message function
 */

void EDIT_MouseMoveMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    if (wParam != MK_LBUTTON)
	return;

    if (ButtonDown)
    {
	EDIT_SetAnchor(hwnd, ButtonRow, ButtonCol);
	TextMarking = TRUE;
	ButtonDown = FALSE;
    }

    if (TextMarking)
	EDIT_ExtendSel(hwnd, LOWORD(lParam), HIWORD(lParam));
}


/*********************************************************************
 *  EDIT_PixelToChar
 *
 *  Convert a pixel offset in the given row to a character offset,
 *  adjusting the pixel offset to the nearest whole character if
 *  necessary.
 */

int EDIT_PixelToChar(HWND hwnd, int row, int *pixel)
{
    int ch = 0, i = 0;
    char *text;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);

#ifdef DEBUG_EDIT
    printf("EDIT_PixelToChar: row=%d, pixel=%d\n", row, *pixel);
#endif

    text = EDIT_TextLine(hwnd, row);
    while (i < *pixel)
    {
	i += charWidths[*(text + ch)];
	ch++;
    }

    /* if stepped past _pixel_, go back a character */
    if (i - *pixel)
	i -= charWidths[*(text + ch)];
    *pixel = i;
    return ch;
}


/*********************************************************************
 *  WM_SETTEXT message function
 */

LONG EDIT_SetTextMsg(HWND hwnd, LONG lParam)
{
    int len;
    char *text;
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    if (strlen((char *)lParam) <= es->MaxTextLen)
    {
	len = strlen((char *)lParam);
	EDIT_ClearText(hwnd);
	es->textlen = len;
	es->hText = EDIT_HEAP_REALLOC(es->hText, len + 3);
	text = EDIT_HEAP_ADDR(es->hText);
	strcpy(text, (char *)lParam);
	text[len] = '\n';
	text[len + 1] = '\0';
	text[len + 2] = '\0';
	EDIT_BuildTextPointers(hwnd);
	InvalidateRect(hwnd, NULL, TRUE);
	es->PaintBkgd = TRUE;
	es->TextChanged = TRUE;
	return 0L;
    }
    else
	return EN_ERRSPACE;
}


/*********************************************************************
 *  EDIT_ClearText
 *
 *  Clear text from text buffer.
 */

void EDIT_ClearText(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    unsigned int blen = EditBufLen(wndPtr) + 2;
    char *text;

    es->hText = EDIT_HEAP_REALLOC(es->hText, blen);
    text = EDIT_HEAP_ADDR(es->hText);
    memset(text, 0, blen);
    es->textlen = 0;
    es->wlines = 0;
    es->CurrLine = es->CurrCol = 0;
    es->WndRow = es->WndCol = 0;
    es->wleft = es->wtop = 0;
    es->textwidth = 0;
    es->TextChanged = FALSE;
    EDIT_ClearTextPointers(hwnd);
}


/*********************************************************************
 *  EM_SETSEL message function
 */

void EDIT_SetSelMsg(HWND hwnd, LONG lParam)
{
    int so, eo;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    so = LOWORD(lParam);
    eo = HIWORD(lParam);
    if (so > eo)
	swap(&so, &eo);

    EDIT_GetLineCol(hwnd, so, &(es->SelBegLine), &(es->SelBegCol));
    EDIT_GetLineCol(hwnd, eo, &(es->SelEndLine), &(es->SelEndCol));

    es->CurrLine = es->SelEndLine;
    es->CurrCol = es->SelEndCol;
    es->WndRow = es->SelEndLine - es->wtop;
    if (es->WndRow < 0)
    {
	es->wtop = es->SelEndLine;
	es->WndRow = 0;
    }
    es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->SelEndLine), 
				 es->SelEndCol) - es->wleft;

    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
}


/*********************************************************************
 *  EDIT_GetLineCol
 *
 *  Return line and column in text buffer from character offset.
 */

void EDIT_GetLineCol(HWND hwnd, int off, int *line, int *col)
{
    int lineno;
    char *cp, *cp1;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    if (off > strlen(text)) off = strlen(text);
    for (lineno = 0; lineno < es->wlines; lineno++)
    {
	cp = text + *(textPtrs + lineno);
	if (off == (int)(cp - text))
	{
	    *line = lineno;
	    *col = 0;
	    return;
	}
	if (off < (int)(cp - text))
	    break;
	cp1 = cp;
    }
    *line = lineno - 1;
    *col = off - (int)(cp1 - text);
    if (*(text + *col) == '\0')
	(*col)--;
}


/*********************************************************************
 *  EDIT_DeleteSel
 *
 *  Delete the current selected text (if any)
 */

void EDIT_DeleteSel(HWND hwnd)
{
    char *bbl, *bel;
    int len;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);

    if (SelMarked(es))
    {
	bbl = EDIT_TextLine(hwnd, es->SelBegLine) + es->SelBegCol;
	bel = EDIT_TextLine(hwnd, es->SelEndLine) + es->SelEndCol;
	len = (int)(bel - bbl);
	es->TextChanged = TRUE;
	strcpy(bbl, bel);

	es->CurrLine = es->SelBegLine;
	es->CurrCol = es->SelBegCol;
	es->WndRow = es->SelBegLine - es->wtop;
	if (es->WndRow < 0)
	{
	    es->wtop = es->SelBegLine;
	    es->WndRow = 0;
	}
	es->WndCol = EDIT_LineLength(es, bbl - es->SelBegCol, 
				     es->SelBegCol) - es->wleft;

	EDIT_BuildTextPointers(hwnd);
	es->PaintBkgd = TRUE;
	EDIT_ClearSel(hwnd);
    }
}
    

/*********************************************************************
 *  EDIT_ClearSel
 *
 *  Clear the current selection.
 */

void EDIT_ClearSel(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    es->SelBegLine = es->SelBegCol = -1;
    es->SelEndLine = es->SelEndCol = -1;

    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
}


/*********************************************************************
 *  EDIT_TextLineNumber
 *
 *  Return the line number in the text buffer of the supplied
 *  character pointer.
 */

int EDIT_TextLineNumber(HWND hwnd, char *lp)
{
    int lineno;
    char *cp;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    char *text = (char *)EDIT_HEAP_ADDR(es->hText);
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    for (lineno = 0; lineno < es->wlines; lineno++)
    {
	cp = text + *(textPtrs + lineno);
	if (cp == lp)
	    return lineno;
	if (cp > lp)
	    break;
    }
    return lineno - 1;
}


/*********************************************************************
 *  EDIT_SetAnchor
 *
 *  Set down anchor for text marking.
 */

void EDIT_SetAnchor(HWND hwnd, int row, int col)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    EDIT_ClearSel(hwnd);
    es->SelBegLine = es->SelEndLine = row;
    es->SelBegCol = es->SelEndCol = col;
    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);
}


/*********************************************************************
 *  EDIT_ExtendSel
 *
 *  Extend selection to the given screen co-ordinates.
 */

void EDIT_ExtendSel(HWND hwnd, int x, int y)
{
    int bbl, bel;
    int ptop, pbot;
    char *cp, *cp1;
    int len;
    BOOL end = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

#ifdef DEBUG_EDIT
    printf("EDIT_ExtendSel: x=%d, y=%d\n", x, y);
#endif

    ptop = min(es->SelBegLine, es->SelEndLine);
    pbot = max(es->SelBegLine, es->SelEndLine);
    cp = EDIT_TextLine(hwnd, es->wtop + y / es->txtht);
    cp1 = strchr(cp, '\n');
    len = cp1 ? (int)(cp1 - cp) : 0;

    es->WndRow = y / es->txtht;
    if (es->WndRow > es->wlines - es->wtop - 1)
    {
	if (es->wlines)
	    es->WndRow = es->wlines - es->wtop - 1;
	else
	    es->WndRow = 0;
	end = TRUE;
    }
    es->CurrLine = es->wtop + es->WndRow;
    es->SelEndLine = es->CurrLine;

    es->WndCol = x;
    if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
	es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
    es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
    es->SelEndCol = es->CurrCol;

    bbl = min(es->SelBegLine, es->SelEndLine);
    bel = max(es->SelBegLine, es->SelEndLine);
    while (ptop < bbl)
    {
	EDIT_WriteTextLine(hwnd, NULL, ptop);
	ptop++;
    }
    for (y = bbl; y <= bel; y++)
	EDIT_WriteTextLine(hwnd, NULL, y);
    while (pbot > bel)
    {
	EDIT_WriteTextLine(hwnd, NULL, pbot);
	--pbot;
    }
}


/*********************************************************************
 *  EDIT_StopMarking
 *
 *  Stop text marking (selection).
 */

void EDIT_StopMarking(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    TextMarking = FALSE;
    if (es->SelBegLine > es->SelEndLine)
    {
	swap(&(es->SelBegLine), &(es->SelEndLine));
	swap(&(es->SelBegCol), &(es->SelEndCol));
    }
    if (es->SelBegLine == es->SelEndLine && es->SelBegCol > es->SelEndCol)
	swap(&(es->SelBegCol), &(es->SelEndCol));
}


/*********************************************************************
 *  EM_GETLINE message function
 */

LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    char *cp, *cp1;
    int len;
    char *buffer = (char *)lParam;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    cp = EDIT_TextLine(hwnd, wParam);
    cp1 = EDIT_TextLine(hwnd, wParam + 1);
    len = min((int)(cp1 - cp), (WORD)(*buffer));
    strncpy(buffer, cp, len);

    return (LONG)len;
}


/*********************************************************************
 *  EM_GETSEL message function
 */

LONG EDIT_GetSelMsg(HWND hwnd)
{
    int so, eo;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    so = *(textPtrs + es->SelBegLine) + es->SelBegCol;
    eo = *(textPtrs + es->SelEndLine) + es->SelEndCol;

    return MAKELONG(so, eo);
}


/*********************************************************************
 *  EM_LINEFROMCHAR message function
 */

LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam)
{
    int row, col;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));

    if (wParam == (WORD)-1)
	return (LONG)(es->SelBegLine);
    else
	EDIT_GetLineCol(hwnd, wParam, &row, &col);

    return (LONG)row;
}


/*********************************************************************
 *  EM_LINEINDEX message function
 */

LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);

    if (wParam == (WORD)-1)
	wParam = es->CurrLine;

    return (LONG)(*(textPtrs + wParam));
}


/*********************************************************************
 *  Utility functions
 */

void swap(int *a, int *b)
{
    int x;

    x = *a;
    *a = *b;
    *b = x;
}