Skip to content
Snippets Groups Projects
Forked from wine / wine
175984 commits behind the upstream repository.
  • Alexandre Julliard's avatar
    22945d5a
    Release 950202 · 22945d5a
    Alexandre Julliard authored
    Wed Feb  1 19:27:55 1995  Alexandre Julliard  (julliard@lamisun.epfl.ch)
    
    	* [windows/nonclient.c] [windows/winpos.c]
    	Implemented maximized windows.
    	Implemented icon positioning and ArrangeIconicWindows().
    	Bug fixes in SetWindowPos().
    
    	* [windows/painting.c]
    	Implemented GetControlBrush().
    	Window frame is no longer contained in the update region.
    
    	* [windows/win.c]
    	Destroy owned windows upon DestroyWindow().
    
    Sun Jan 29 16:17:22 1995  David Metcalfe <david@prism.demon.co.uk>
    
    	* [controls/edit.c]
    	Changed line terminator to \r\n to be compatible with
    	Windows.  Fixed bug in text selection.
    
    Sun Jan 29 14:10:22 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
    
           * [misc/shell.c]
           Rewrote RegCreateKey and RegOpenKey, since they were completely broken.
           Fixed a bug in RegQueryKeyValue. Implemented RegEnumKey
           These functions now work somewhat more the way Windows programs expect
           them to work.
    22945d5a
    History
    Release 950202
    Alexandre Julliard authored
    Wed Feb  1 19:27:55 1995  Alexandre Julliard  (julliard@lamisun.epfl.ch)
    
    	* [windows/nonclient.c] [windows/winpos.c]
    	Implemented maximized windows.
    	Implemented icon positioning and ArrangeIconicWindows().
    	Bug fixes in SetWindowPos().
    
    	* [windows/painting.c]
    	Implemented GetControlBrush().
    	Window frame is no longer contained in the update region.
    
    	* [windows/win.c]
    	Destroy owned windows upon DestroyWindow().
    
    Sun Jan 29 16:17:22 1995  David Metcalfe <david@prism.demon.co.uk>
    
    	* [controls/edit.c]
    	Changed line terminator to \r\n to be compatible with
    	Windows.  Fixed bug in text selection.
    
    Sun Jan 29 14:10:22 1995   Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
    
           * [misc/shell.c]
           Rewrote RegCreateKey and RegOpenKey, since they were completely broken.
           Fixed a bug in RegQueryKeyValue. Implemented RegEnumKey
           These functions now work somewhat more the way Windows programs expect
           them to work.
edit.c 78.53 KiB
/*
 * Edit control
 *
 * Copyright  David W. Metcalfe, 1994
 *
 * Release 3, July 1994

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <heap.h>
#include "win.h"
#include "class.h"
#include "user.h"
#include "stddebug.h"
/* #define DEBUG_EDIT */
/* #undef  DEBUG_EDIT */
#include "debug.h"


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

#define MAXTEXTLEN 30000   /* 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 */

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 */
    HFONT hFont;             /* handle of current font (if not default) */
    HANDLE hDeletedText;     /* handle to deleted txet buffer for undo */
    int DeletedLength;       /* length of deleted text */
    int DeletedCurrLine;     /* starting line from which text was deleted */
    int DeletedCurrCol;      /* starting col from which text was deleted */
    int NumTabStops;         /* number of tab stops in buffer hTabStops */
    HANDLE hTabStops;        /* handle of tab stops buffer */
} EDITSTATE;

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

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


LONG EditWndProc(HWND hWnd, WORD uMsg, WORD wParam, LONG lParam);
long EDIT_NCCreateMsg(HWND hwnd, 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_StrLength(HWND hwnd, unsigned char *str, int len, int pcol);
int EDIT_LineLength(HWND hwnd, int num);
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(HWND hwnd, 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(HWND hwnd, short ch, int pcol);
int EDIT_GetNextTabStop(HWND hwnd, int pcol);
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, WORD wParam, 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_WriteSel(HWND hwnd, int y, int start, int end);
void EDIT_StopMarking(HWND hwnd);
LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam);
LONG EDIT_GetSelMsg(HWND hwnd);
void EDIT_ReplaceSel(HWND hwnd, LONG lParam);
void EDIT_InsertText(HWND hwnd, char *str, int len);
LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam);
LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam);
LONG EDIT_LineLengthMsg(HWND hwnd, WORD wParam);
void EDIT_SetFont(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_SaveDeletedText(HWND hwnd, char *deltext, int len, int line, 
			  int col);
void EDIT_ClearDeletedText(HWND hwnd);
LONG EDIT_UndoMsg(HWND hwnd);
unsigned int EDIT_HeapAlloc(HWND hwnd, int bytes);
void *EDIT_HeapAddr(HWND hwnd, unsigned int handle);
unsigned int EDIT_HeapReAlloc(HWND hwnd, unsigned int handle, int bytes);
void EDIT_HeapFree(HWND hwnd, unsigned int handle);
unsigned int EDIT_HeapSize(HWND hwnd, unsigned int handle);
void EDIT_SetHandleMsg(HWND hwnd, WORD wParam);
LONG EDIT_SetTabStopsMsg(HWND hwnd, WORD wParam, LONG lParam);
void EDIT_CopyToClipboard(HWND hwnd);
void EDIT_PasteMsg(HWND hwnd);
void swap(int *a, int *b);


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

    switch (uMsg) {
    case EM_CANUNDO:
	lResult = es->hDeletedText;
	break;
	
    case EM_EMPTYUNDOBUFFER:
	EDIT_ClearDeletedText(hwnd);
	break;

    case EM_FMTLINES:
	fprintf(stdnimp,"edit: EM_FMTLINES message received\n");
	if (!wParam)
	    lResult = 1L;
	else
	    lResult = 0L;
	break;

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

    case EM_GETHANDLE:
	lResult = es->hText;
	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:
	fprintf(stdnimp,"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:
	fprintf(stdnimp,"edit: cannot process EM_GETWORDBREAKPROC message\n");
	break;

    case EM_LIMITTEXT:
	if (wParam)
	    es->MaxTextLen = wParam;
	else if (IsMultiLine())
	    es->MaxTextLen = 65535;
	else
	    es->MaxTextLen = 32767;
	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:
	lResult = EDIT_LineLengthMsg(hwnd, wParam);
	break;

    case EM_LINESCROLL:
	fprintf(stdnimp,"edit: cannot process EM_LINESCROLL message\n");
	break;

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

    case EM_SETHANDLE:
	HideCaret(hwnd);
	EDIT_SetHandleMsg(hwnd, wParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

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

    case EM_SETPASSWORDCHAR:
	fprintf(stdnimp,"edit: cannot process EM_SETPASSWORDCHAR message\n");
	break;

    case EM_SETREADONLY:
	fprintf(stdnimp,"edit: cannot process EM_SETREADONLY message\n");
	break;

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

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

    case EM_SETTABSTOPS:
	lResult = EDIT_SetTabStopsMsg(hwnd, wParam, lParam);
	break;

    case EM_SETWORDBREAKPROC:
	fprintf(stdnimp,"edit: cannot process EM_SETWORDBREAKPROC message\n");
	break;

    case EM_UNDO:
	HideCaret(hwnd);
	lResult = EDIT_UndoMsg(hwnd);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

    case WM_GETDLGCODE:
        return DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;

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

    case WM_COPY:
	EDIT_CopyToClipboard(hwnd);
	EDIT_ClearSel(hwnd);
	break;

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

    case WM_CUT:
	EDIT_CopyToClipboard(hwnd);
	EDIT_DeleteSel(hwnd);
	break;

    case WM_DESTROY:
	EDIT_HeapFree(hwnd, es->hTextPtrs);
	EDIT_HeapFree(hwnd, es->hCharWidths);
	EDIT_HeapFree(hwnd, es->hText);
	EDIT_HeapFree(hwnd, (HANDLE)(*(wndPtr->wExtra)));
	break;

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

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

    case WM_GETTEXTLENGTH:
	textPtr = EDIT_HeapAddr(hwnd, 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_NCCREATE:
	lResult = EDIT_NCCreateMsg(hwnd, lParam);
	break;
	
    case WM_PAINT:
	EDIT_PaintMsg(hwnd);
	break;

    case WM_PASTE:
	EDIT_PasteMsg(hwnd);
	break;

    case WM_SETFOCUS:
	CreateCaret(hwnd, 0, 2, es->txtht);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	NOTIFY_PARENT(hwnd, EN_SETFOCUS);
	break;

    case WM_SETFONT:
	HideCaret(hwnd);
	EDIT_SetFont(hwnd, wParam, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;
#if 0
    case WM_SETREDRAW:
	dprintf_edit(stddeb, "WM_SETREDRAW: hwnd=%d, wParam=%x\n",
		     hwnd, wParam);
	lResult = 0;
	break;
#endif
    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;
    }

    return lResult;
}


/*********************************************************************
 *  WM_NCCREATE message function
 */

long EDIT_NCCreateMsg(HWND hwnd, LONG lParam)
{
    CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es;
    unsigned int *textPtrs;
    char *text;

    /* store pointer to local or global heap in window structure so that */
    /* EDITSTATE structure itself can be stored on local heap  */
    if (HEAP_LocalFindHeap(createStruct->hInstance)!=NULL)
      (MDESC **)*(LONG *)(wndPtr->wExtra + 2) = 
	&HEAP_LocalFindHeap(createStruct->hInstance)->free_list;
    else
      {
	(MDESC **)*(LONG *)(wndPtr->wExtra + 2) =
	  GlobalLock(createStruct->hInstance);
	/* GlobalUnlock(createStruct->hInstance); */
      }
    /* allocate space for state variable structure */
    (HANDLE)(*(wndPtr->wExtra)) = EDIT_HeapAlloc(hwnd, sizeof(EDITSTATE));
    es = (EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    es->hTextPtrs = EDIT_HeapAlloc(hwnd, sizeof(int));
    textPtrs = (unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);
    es->hCharWidths = EDIT_HeapAlloc(hwnd, 256 * sizeof(short));

    /* --- text buffer */
    es->MaxTextLen = MAXTEXTLEN + 1;
    if (!(createStruct->lpszName))
    {
	es->textlen = EditBufLen(wndPtr) + 1;
	es->hText = EDIT_HeapAlloc(hwnd, EditBufLen(wndPtr) + 2);
	text = EDIT_HeapAddr(hwnd, es->hText);
	memset(text, 0, es->textlen + 2);
	EDIT_ClearTextPointers(hwnd);
    }
    else
    {
	if (strlen(createStruct->lpszName) < EditBufLen(wndPtr))
	{
	    es->textlen = EditBufLen(wndPtr) + 1;
	    es->hText = EDIT_HeapAlloc(hwnd, EditBufLen(wndPtr) + 2);
	    text = EDIT_HeapAddr(hwnd, es->hText);
	    strcpy(text, createStruct->lpszName);
	    *(text + es->textlen) = '\0';
	}
	else
	{
	    es->hText = EDIT_HeapAlloc(hwnd, 
				       strlen(createStruct->lpszName) + 2);
	    text = EDIT_HeapAddr(hwnd, es->hText);
	    strcpy(text, createStruct->lpszName);
	    es->textlen = strlen(createStruct->lpszName) + 1;
	}
	*(text + es->textlen + 1) = '\0';
	EDIT_BuildTextPointers(hwnd);
    }

    /* ES_AUTOVSCROLL and ES_AUTOHSCROLL are automatically applied if */
    /* the corresponding WS_* style is set                            */
    if (createStruct->style & WS_VSCROLL)
	wndPtr->dwStyle |= ES_AUTOVSCROLL;
    if (createStruct->style & WS_HSCROLL)
	wndPtr->dwStyle |= ES_AUTOHSCROLL;

    /* remove the WS_CAPTION style if it has been set - this is really a  */
    /* pseudo option made from a combination of WS_BORDER and WS_DLGFRAME */
    if (wndPtr->dwStyle & WS_BORDER && wndPtr->dwStyle & WS_DLGFRAME)
	wndPtr->dwStyle ^= WS_DLGFRAME;

    return 1;
}


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

long EDIT_CreateMsg(HWND hwnd, LONG lParam)
{
    HDC hdc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    CLASS *classPtr;
    short *charWidths;
    TEXTMETRIC tm;
    char *text;

    /* initialize state variable structure */
    hdc = GetDC(hwnd);

    /* --- char width array                                        */
    /*     only initialise chars <= 32 as X returns strange widths */
    /*     for other chars                                         */
    charWidths = (short *)EDIT_HeapAddr(hwnd, es->hCharWidths);
    memset(charWidths, 0, 256 * sizeof(short));
    GetCharWidth(hdc, 32, 254, &charWidths[32]);

    /* --- 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 = 0;
    es->SelEndLine = es->SelEndCol = 0;
    es->hFont = 0;
    es->hDeletedText = 0;
    es->DeletedLength = 0;
    es->NumTabStops = 0;
    es->hTabStops = EDIT_HeapAlloc(hwnd, sizeof(int));

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

    /* set up text cursor for edit class */
    CLASS_FindClassByName("EDIT", 0, &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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    
    es->hTextPtrs = EDIT_HeapReAlloc(hwnd, es->hTextPtrs, sizeof(int));
    textPtrs = (unsigned int *)EDIT_HeapAddr(hwnd, 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;
    EDITSTATE *es;
    unsigned int *textPtrs;
    short *charWidths;

    es = (EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    text = EDIT_HeapAddr(hwnd, es->hText);
    textPtrs = (unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);
    charWidths = (short *)EDIT_HeapAddr(hwnd, 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_HeapReAlloc(hwnd, es->hTextPtrs,
			      (es->wlines + INITLINES) * sizeof(int));
	    textPtrs = (unsigned int *)EDIT_HeapAddr(hwnd, 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 += EDIT_CharWidth(hwnd, (BYTE)*cp, len);
	                                     /* width of line in pixels */
	    cp++;
	}
	es->textwidth = max(es->textwidth, len);
	if (*cp)
	    cp++;                            /* skip '\n' */
    }

    off = (unsigned int)(cp - text);
    *(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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, 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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

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

    dprintf_edit(stddeb,"WM_PAINT: rc=(%d,%d), (%d,%d)\n", rc.left, rc.top, 
	   rc.right, rc.bottom);

    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;

    dprintf_edit(stddeb,"GetTextLine %d\n", selection);
    cp = cp1 = EDIT_TextLine(hwnd, selection);
    /* advance through line */
    while (*cp && *cp != '\r')
    {
	len++;
	cp++;
    }

    /* store selected line and return handle */
    hLine = EDIT_HeapAlloc(hwnd, len + 6);
    line = (char *)EDIT_HeapAddr(hwnd, 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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);

    if(sel>es->wlines)return NULL;
    return (text + *(textPtrs + sel));
}
    

/*********************************************************************
 *  EDIT_StrLength
 *
 *  Return length of string _str_ of length _len_ characters in pixels.
 *  The current column offset in pixels _pcol_ is required to calculate 
 *  the width of a tab.
 */

int EDIT_StrLength(HWND hwnd, unsigned char *str, int len, int pcol)
{
    int i, plen = 0;

    for (i = 0; i < len; i++)
	plen += EDIT_CharWidth(hwnd, (BYTE)(*(str + i)), pcol + plen);

    dprintf_edit(stddeb,"EDIT_StrLength: returning %d\n", plen);
    return plen;
}


/*********************************************************************
 *  EDIT_LineLength
 *
 *  Return length of line _num_ in characters.
 */

int EDIT_LineLength(HWND hwnd, int num)
{
    char *cp = EDIT_TextLine(hwnd, num);
    char *cp1;

    if(!cp)return 0;
    cp1 = strchr(cp, '\r');
    return cp1 ? (int)(cp1 - cp) : strlen(cp);
}


/*********************************************************************
 *  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;
    HANDLE hLine;
    unsigned char *lp;
    int lnlen, lnlen1;
    int col, off = 0;
    int sbl, sel, sbc, sec;
    RECT rc;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

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

    dprintf_edit(stddeb,"WriteTextLine %d\n", y);

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

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

    /* get the text and length of line */
    if ((hLine = EDIT_GetTextLine(hwnd, y)) == 0)
	return;
    lp = (unsigned char *)EDIT_HeapAddr(hwnd, hLine);
    lnlen = EDIT_StrLength(hwnd, lp, strlen(lp), 0);
    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_StrLength(hwnd, lp, sbc, 0);
	    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_StrLength(hwnd, lp, sec, 0);
		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_StrLength(hwnd, lp, sec, 0);
	    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_HeapFree(hwnd, 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, *cp, *cp1;
    int diff, num_spaces, tabwidth, scol;
    HRGN hrgnClip;
    COLORREF oldTextColor, oldBkgdColor;
    HFONT oldfont;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HeapAddr(hwnd, es->hCharWidths);
    char *blanks = (char *)EDIT_HeapAddr(hwnd, es->hBlankLine);

    dprintf_edit(stddeb,"EDIT_WriteText lp=%s, off=%d, len=%d, row=%d, col=%d, reverse=%d\n", lp, off, len, row, col, reverse);

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

    if (es->hFont)
	oldfont = (HFONT)SelectObject(hdc, (HANDLE)es->hFont);

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

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

    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));
	scol = EDIT_StrLength(hwnd, str, (int)(cp - str), 0);
	tabwidth = EDIT_CharWidth(hwnd, VK_TAB, scol);
	num_spaces = tabwidth / charWidths[32] + 1;
	TextOut(hdc, scol, row * es->txtht, blanks, num_spaces);
	cp++;
	scol += tabwidth;

	while ((cp1 = strchr(cp, VK_TAB)))
	{
	    TextOut(hdc, scol, row * es->txtht, cp, (int)(cp1 - cp));
	    scol += EDIT_StrLength(hwnd, cp, (int)(cp1 - cp), scol);
	    tabwidth = EDIT_CharWidth(hwnd, VK_TAB, scol);
	    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));
    }

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

    if (es->hFont)
	SelectObject(hdc, (HANDLE)oldfont);

    EDIT_HeapFree(hwnd, 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(HWND hwnd, char *lp, int off, int len, int *diff)
{
    HANDLE hStr;
    char *str;
    int ch = 0, i = 0, j, s_i=0;
    int ch1;

    dprintf_edit(stddeb,"EDIT_GetStr lp='%s'  off=%d  len=%d\n", lp, off, len);

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


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

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

    dprintf_edit(stddeb,"EDIT_CharMsg: wParam=%c\n", (char)wParam);

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

    case VK_TAB:
	if (!IsMultiLine())
	    break;
	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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
    char *currchar = CurrChar;
    RECT rc;
    BOOL FullPaint = FALSE;

    dprintf_edit(stddeb,"EDIT_KeyTyped: ch=%c\n", (char)ch);

    /* 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' && IsMultiLine())
    {
	/* insert a newline at end of text */
	*currchar = '\r';
	*(currchar + 1) = '\n';
	*(currchar + 2) = '\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_HeapReAlloc(hwnd, es->hText, es->textlen + 2);
	if (!es->hText)
	    NOTIFY_PARENT(hwnd, EN_ERRSPACE);
	text = EDIT_HeapAddr(hwnd, es->hText);
	text[es->textlen - 1] = '\0';
	currchar = CurrChar;
    }
    /* make space for new character and put char in buffer */
    if (ch == '\n')
    {
	memmove(currchar + 2, currchar, strlen(currchar) + 1);
	*currchar = '\r';
	*(currchar + 1) = '\n';
	EDIT_ModTextPointers(hwnd, es->CurrLine + 1, 2);
    }
    else
    {
	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_StrLength(hwnd, EDIT_TextLine(hwnd, es->CurrLine),
		    (int)(EDIT_TextLine(hwnd, es->CurrLine + 1) -
			  EDIT_TextLine(hwnd, es->CurrLine)), 0));
    }
    else
	es->textwidth = max(es->textwidth,
			    EDIT_StrLength(hwnd, text, strlen(text), 0));
    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(hwnd, (BYTE)ch, es->WndCol + es->wleft))
    {
	/* TODO:- Word wrap to be handled here */

/*	if (!(currchar == text + es->MaxTextLen - 2)) */
	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
    }
    es->WndCol += EDIT_CharWidth(hwnd, (BYTE)ch, es->WndCol + es->wleft);
    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.
 *  The current column offset in pixels _pcol_ is required to calculate 
 *  the width of a tab.
 */

int EDIT_CharWidth(HWND hwnd, short ch, int pcol)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HeapAddr(hwnd, es->hCharWidths);

    if (ch != VK_TAB)
	return (charWidths[ch]);
    else
	return (EDIT_GetNextTabStop(hwnd, pcol) - pcol);
}


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

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

    if (es->NumTabStops == 0)
	return ROUNDUP(pcol, 8 * baseUnitWidth);
    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;
    }
}


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

void EDIT_Forward(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

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

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

    dprintf_edit(stddeb,"EDIT_Downward: WndRow=%d, wtop=%d, wlines=%d\n", 
	     es->WndRow, es->wtop, es->wlines);

    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_HeapAddr(hwnd, (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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    if (es->CurrCol)
    {
	--es->CurrCol;
	if (*CurrChar == VK_TAB)
	    es->WndCol -= EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), 
					 EDIT_StrLength(hwnd, 
					 EDIT_TextLine(hwnd, es->CurrLine), 
					 es->CurrCol, 0));
	else
	    es->WndCol -= EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), 0);
	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);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    while (*CurrChar && *CurrChar != '\r')
    {
	es->WndCol += EDIT_CharWidth(hwnd, (BYTE)(*CurrChar), es->WndCol + es->wleft);
	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);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    int len = EDIT_LineLength(hwnd, es->CurrLine);
    char *cp = EDIT_TextLine(hwnd, es->CurrLine);
    char currpel;

    es->CurrCol = min(len, es->CurrCol);
    es->WndCol = min(EDIT_StrLength(hwnd, cp, len, 0) - es->wleft, es->WndCol);
    currpel = EDIT_StrLength(hwnd, cp, es->CurrCol, 0);

    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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    dprintf_edit(stddeb,"EDIT_KeyDownMsg: key=%x\n", wParam);

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

    dprintf_edit(stddeb,"EDIT_VScrollLine: direction=%d\n", opt);

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


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

void EDIT_VScrollPage(HWND hwnd, WORD opt)
{
    int vscrollpos;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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_HeapAddr(hwnd, (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)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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;
    int len;
    BOOL end = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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);
    len = EDIT_LineLength(hwnd, es->CurrLine);
    es->WndCol = LOWORD(lParam);
    if (es->WndCol > EDIT_StrLength(hwnd, cp, len, 0) - es->wleft || end)
	es->WndCol = EDIT_StrLength(hwnd, cp, len, 0) - 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, s_i = 0;
    char *text;

    dprintf_edit(stddeb,"EDIT_PixelToChar: row=%d, pixel=%d\n", row, *pixel);

    text = EDIT_TextLine(hwnd, row);
    while (i < *pixel)
    {
	s_i = i;
	i += EDIT_CharWidth(hwnd, (BYTE)(*(text + ch)), i);
	ch++;
    }

    /* if stepped past _pixel_, go back a character */
    if (i - *pixel)
    {
	i = s_i;
	--ch;
    }
    *pixel = i;
    return ch;
}


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

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

    if (strlen((char *)lParam) <= es->MaxTextLen)
    {
	len = ( lParam? strlen((char *)lParam) : 0 );
	EDIT_ClearText(hwnd);
	es->textlen = len;
	es->hText = EDIT_HeapReAlloc(hwnd, es->hText, len + 3);
	text = EDIT_HeapAddr(hwnd, es->hText);
	if (lParam)
	    strcpy(text, (char *)lParam);
	text[len]     = '\0';
	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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned int blen = EditBufLen(wndPtr) + 2;
    char *text;

    es->hText = EDIT_HeapReAlloc(hwnd, es->hText, blen);
    text = EDIT_HeapAddr(hwnd, 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, WORD wParam, LONG lParam)
{
    int so, eo;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    so = LOWORD(lParam);
    eo = HIWORD(lParam);

    if (so == -1)       /* if so == -1, clear selection */
    {
	EDIT_ClearSel(hwnd);
	return;
    }

    if (so == eo)       /* if so == eo, set caret only */
    {
	EDIT_GetLineCol(hwnd, so, &(es->CurrLine), &(es->CurrCol));
	es->WndRow = es->CurrLine - es->wtop;
	if (!wParam)
	{
	    if (es->WndRow < 0 || es->WndRow > ClientHeight(wndPtr, es))
	    {
		es->wtop = es->CurrLine;
		es->WndRow = 0;
	    }
	    es->WndCol = EDIT_StrLength(hwnd, 
					EDIT_TextLine(hwnd, es->CurrLine), 
					es->CurrCol, 0) - es->wleft;
	    if (es->WndCol > ClientWidth(wndPtr))
	    {
		es->wleft = es->WndCol;
		es->WndCol = 0;
	    }
	    else if (es->WndCol < 0)
	    {
		es->wleft += es->WndCol;
		es->WndCol = 0;
	    }
	}
    }
    else                /* otherwise set selection */
    {
	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 (!wParam)          /* don't suppress scrolling of text */
	{
	    if (es->WndRow < 0)
	    {
		es->wtop = es->SelEndLine;
		es->WndRow = 0;
	    }
	    else if (es->WndRow > ClientHeight(wndPtr, es))
	    {
		es->wtop += es->WndRow - ClientHeight(wndPtr, es);
		es->WndRow = ClientHeight(wndPtr, es);
	    }
	    es->WndCol = EDIT_StrLength(hwnd, 
					EDIT_TextLine(hwnd, es->SelEndLine), 
					es->SelEndCol, 0) - es->wleft;
	    if (es->WndCol > ClientWidth(wndPtr))
	    {
		es->wleft += es->WndCol - ClientWidth(wndPtr);
		es->WndCol = ClientWidth(wndPtr);
	    }
	    else if (es->WndCol < 0)
	    {
		es->wleft += es->WndCol;
		es->WndCol = 0;
	    }
	}

	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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);

    /* check for (0,0) */
    if (!off)
    {
	*line = 0;
	*col = 0;
	return;
    }

    if (off > strlen(text)) off = strlen(text);
    cp1 = 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 0
    if (*(text + *col) == '\0')
	(*col)--;
#endif
}


/*********************************************************************
 *  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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    if (SelMarked(es))
    {
	bbl = EDIT_TextLine(hwnd, es->SelBegLine) + es->SelBegCol;
	bel = EDIT_TextLine(hwnd, es->SelEndLine) + es->SelEndCol;
	len = (int)(bel - bbl);
	EDIT_SaveDeletedText(hwnd, bbl, len, es->SelBegLine, es->SelBegCol);
	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_StrLength(hwnd, bbl - es->SelBegCol, 
				     es->SelBegCol, 0) - 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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

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

    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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, 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)
{
    BOOL sel = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    if (SelMarked(es))
	sel = TRUE;
    EDIT_ClearSel(hwnd);
    es->SelBegLine = es->SelEndLine = row;
    es->SelBegCol = es->SelEndCol = col;
    if (sel)
    {
	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, bbc, bec;
    char *cp;
    int len, line;
    BOOL end = FALSE;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    dprintf_edit(stddeb,"EDIT_ExtendSel: x=%d, y=%d\n", x, y);

    bbl = es->SelEndLine;
    bbc = es->SelEndCol;
    line = es->wtop + y / es->txtht;
    if (line > es->wlines)
	line = es->wlines;
    cp = EDIT_TextLine(hwnd, line);
    len = EDIT_LineLength(hwnd, line);

    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_StrLength(hwnd, cp, len, 0) - es->wleft || end)
	es->WndCol = EDIT_StrLength(hwnd, cp, len, 0) - es->wleft;
    es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
    es->SelEndCol = es->CurrCol;

    bel = es->SelEndLine;
    bec = es->SelEndCol;

    /* return if no new characters to mark */
    if (bbl == bel && bbc == bec)
	return;

    /* put lowest marker first */
    if (bbl > bel)
    {
	swap(&bbl, &bel);
	swap(&bbc, &bec);
    }
    if (bbl == bel && bbc > bec)
	swap(&bbc, &bec);

    for (y = bbl; y <= bel; y++)
    {
	if (y == bbl && y == bel)
	    EDIT_WriteSel(hwnd, y, bbc, bec);
	else if (y == bbl)
	    EDIT_WriteSel(hwnd, y, bbc, -1);
	else if (y == bel)
	    EDIT_WriteSel(hwnd, y, 0, bec);
	else
	    EDIT_WriteSel(hwnd, y, 0, -1);
    }
}


/*********************************************************************
 *  EDIT_WriteSel
 *
 *  Display selection by reversing pixels in selected text.
 *  If end == -1, selection applies to end of line.
 */

void EDIT_WriteSel(HWND hwnd, int y, int start, int end)
{
    RECT rc;
    int scol, ecol;
    char *cp;
    HDC hdc;
    HBRUSH hbrush, holdbrush;
    int olddm;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    dprintf_edit(stddeb,"EDIT_WriteSel: y=%d start=%d end=%d\n", y, start,end);
    GetClientRect(hwnd, &rc);

    /* make sure y is within the window */
    if (y < es->wtop || y > (es->wtop + ClientHeight(wndPtr, es)))
	return;

    /* get pointer to text */
    cp = EDIT_TextLine(hwnd, y);

    /* get length of line if end == -1 */
    if (end == -1)
	end = EDIT_LineLength(hwnd, y);

    /* For some reason Rectangle, when called with R2_XORPEN filling,
     * appears to leave a 2 pixel gap between characters and between
     * lines.  I have kludged this by adding on two pixels to ecol and
     * to the line height in the call to Rectangle.
     */
    scol = EDIT_StrLength(hwnd, cp, start, 0);
    if (scol > rc.right) return;
    if (scol < rc.left) scol = rc.left;
    ecol = EDIT_StrLength(hwnd, cp, end, 0) + 2;   /* ??? */
    if (ecol < rc.left) return;
    if (ecol > rc.right) ecol = rc.right;

    hdc = GetDC(hwnd);
    hbrush = GetStockObject(BLACK_BRUSH);
    holdbrush = (HBRUSH)SelectObject(hdc, (HANDLE)hbrush);
    olddm = SetROP2(hdc, R2_XORPEN);
    Rectangle(hdc, scol, y * es->txtht, ecol, (y + 1) * es->txtht + 2);
    SetROP2(hdc, olddm);
    SelectObject(hdc, (HANDLE)holdbrush);
    ReleaseDC(hwnd, hdc);
}


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

void EDIT_StopMarking(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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;

    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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);

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


/*********************************************************************
 *  EM_REPLACESEL message function
 */

void EDIT_ReplaceSel(HWND hwnd, LONG lParam)
{
    EDIT_DeleteSel(hwnd);
    EDIT_InsertText(hwnd, (char *)lParam, strlen((char *)lParam));
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
}


/*********************************************************************
 *  EDIT_InsertText
 *
 *  Insert text at current line and column.
 */

void EDIT_InsertText(HWND hwnd, char *str, int len)
{
    int plen;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    char *text = EDIT_HeapAddr(hwnd, es->hText);
    
    plen = strlen(text) + len;
    if (plen + 1 > es->textlen)
    {
	es->hText = EDIT_HeapReAlloc(hwnd, es->hText, es->textlen + len);
	es->textlen = plen + 1;
    }
    memmove(CurrChar + len, CurrChar, strlen(CurrChar) + 1);
    memcpy(CurrChar, str, len);

    EDIT_BuildTextPointers(hwnd);
    es->PaintBkgd = TRUE;
    es->TextChanged = TRUE;

    EDIT_GetLineCol(hwnd, (int)((CurrChar + len) - text), &(es->CurrLine),
		                                    &(es->CurrCol));
    es->WndRow = es->CurrLine - es->wtop;
    es->WndCol = EDIT_StrLength(hwnd, EDIT_TextLine(hwnd, es->CurrLine),
				 es->CurrCol, 0) - es->wleft;
}


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

LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam)
{
    int row, col;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (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_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);

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

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


/*********************************************************************
 *  EM_LINELENGTH message function
 */

LONG EDIT_LineLengthMsg(HWND hwnd, WORD wParam)
{
    int row, col, len;
    int sbl, sbc, sel, sec;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    unsigned int *textPtrs = 
	(unsigned int *)EDIT_HeapAddr(hwnd, es->hTextPtrs);

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

	    if (sbl > sel)
	    {
		swap(&sbl, &sel);
		swap(&sbc, &sec);
	    }
	    if (sbl == sel && sbc > sec)
		swap(&sbc, &sec);

	    if (sbc == sel)
	    {
		len = *(textPtrs + sbl + 1) - *(textPtrs + sbl) - 1;
		return len - sec - sbc;
	    }

	    len = *(textPtrs + sel + 1) - *(textPtrs + sel) - sec - 1;
	    return len + sbc;
	}
	else    /* no selection marked */
	{
	    len = *(textPtrs + es->CurrLine + 1) - 
		  *(textPtrs + es->CurrLine) - 1;
	    return len;
	}
    }
    else    /* line number specified */
    {
	EDIT_GetLineCol(hwnd, wParam, &row, &col);
	len = *(textPtrs + row + 1) - *(textPtrs + row);
	return len;
    }
}


/*********************************************************************
 *  WM_SETFONT message function
 */

void EDIT_SetFont(HWND hwnd, WORD wParam, LONG lParam)
{
    HDC hdc;
    TEXTMETRIC tm;
    HFONT oldfont;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    short *charWidths = (short *)EDIT_HeapAddr(hwnd, es->hCharWidths);

    es->hFont = wParam;
    hdc = GetDC(hwnd);
    oldfont = (HFONT)SelectObject(hdc, (HANDLE)es->hFont);
    GetCharWidth(hdc, 0, 255, charWidths);
    GetTextMetrics(hdc, &tm);
    es->txtht = tm.tmHeight + tm.tmExternalLeading;
    SelectObject(hdc, (HANDLE)oldfont);
    ReleaseDC(hwnd, hdc);

    es->WndRow = (es->CurrLine - es->wtop) / es->txtht;
    es->WndCol = EDIT_StrLength(hwnd, EDIT_TextLine(hwnd, es->CurrLine),
				 es->CurrCol, 0) - es->wleft;

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


/*********************************************************************
 *  EDIT_SaveDeletedText
 *
 *  Save deleted text in deleted text buffer.
 */

void EDIT_SaveDeletedText(HWND hwnd, char *deltext, int len,
			                  int line, int col)
{
    char *text;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    es->hDeletedText = GlobalReAlloc(es->hDeletedText, len, GMEM_MOVEABLE);
    if (!es->hDeletedText) return;
    text = (char *)GlobalLock(es->hDeletedText);
    memcpy(text, deltext, len);
    GlobalUnlock(es->hDeletedText);
    es->DeletedLength = len;
    es->DeletedCurrLine = line;
    es->DeletedCurrCol = col;
}


/*********************************************************************
 *  EDIT_ClearDeletedText
 *
 *  Clear deleted text buffer.
 */

void EDIT_ClearDeletedText(HWND hwnd)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    GlobalFree(es->hDeletedText);
    es->hDeletedText = 0;
    es->DeletedLength = 0;
}


/*********************************************************************
 *  EM_UNDO message function
 */

LONG EDIT_UndoMsg(HWND hwnd)
{
    char *text;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));
    
    if (es->hDeletedText)
    {
	text = (char *)GlobalLock(es->hDeletedText);
	es->CurrLine = es->DeletedCurrLine;
	es->CurrCol = es->DeletedCurrCol;
	EDIT_InsertText(hwnd, text, es->DeletedLength);
	GlobalUnlock(es->hDeletedText);
	EDIT_ClearDeletedText(hwnd);

	es->SelBegLine = es->CurrLine;
	es->SelBegCol = es->CurrCol;
	EDIT_GetLineCol(hwnd, (int)((CurrChar + es->DeletedLength) - text), 
			&(es->CurrLine), &(es->CurrCol));
	es->WndRow = es->CurrLine - es->wtop;
	es->WndCol = EDIT_StrLength(hwnd, EDIT_TextLine(hwnd, es->CurrLine),
				     es->CurrCol, 0) - es->wleft;
	es->SelEndLine = es->CurrLine;
	es->SelEndCol = es->CurrCol;

	InvalidateRect(hwnd, NULL, TRUE);
	UpdateWindow(hwnd);
	return 1;
    }
    else
	return 0;
}


/*********************************************************************
 *  EDIT_HeapAlloc
 *
 *  Allocate the specified number of bytes on the specified local heap.
 */

unsigned int EDIT_HeapAlloc(HWND hwnd, int bytes)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    unsigned int ret;
    ret = ((unsigned int)HEAP_Alloc((MDESC **)
				     *(LONG *)(wndPtr->wExtra + 2), 
				     GMEM_MOVEABLE, bytes) & 0xffff);
    if (ret == 0)
      printf("EDIT_HeapAlloc: Out of heap-memory\n");
    return ret;
}

/*********************************************************************
 *  EDIT_HeapAddr
 *
 *  Return the address of the memory pointed to by the handle.
 */

void *EDIT_HeapAddr(HWND hwnd, unsigned int handle)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);

    return ((void *)((handle) ? ((handle) | ((unsigned int)
		    (*(MDESC **)*(LONG *)(wndPtr->wExtra + 2))   
		     & 0xffff0000)) : 0));
}


/*********************************************************************
 *  EDIT_HeapReAlloc
 *
 *  Reallocate the memory pointed to by the handle.
 */

unsigned int EDIT_HeapReAlloc(HWND hwnd, unsigned int handle, int bytes)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);

    return ((unsigned int)HEAP_ReAlloc((MDESC **)
				       *(LONG *)(wndPtr->wExtra + 2), 
				       EDIT_HeapAddr(hwnd, handle),
				       bytes, GMEM_MOVEABLE) & 0xffff);
}


/*********************************************************************
 *  EDIT_HeapFree
 *
 *  Frees the memory pointed to by the handle.
 */

void EDIT_HeapFree(HWND hwnd, unsigned int handle)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);

    HEAP_Free((MDESC **)*(LONG *)(wndPtr->wExtra + 2), 
	      EDIT_HeapAddr(hwnd, handle));
}


/*********************************************************************
 *  EDIT_HeapSize
 *
 *  Return the size of the given object on the local heap.
 */

unsigned int EDIT_HeapSize(HWND hwnd, unsigned int handle)
{
    WND *wndPtr = WIN_FindWndPtr(hwnd);

    return HEAP_LocalSize((MDESC **)*(LONG *)(wndPtr->wExtra + 2), handle);
}


/*********************************************************************
 *  EM_SETHANDLE message function
 */

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

    if (IsMultiLine())
    {
	es->hText = wParam;
	es->textlen = EDIT_HeapSize(hwnd, es->hText);
	es->wlines = 0;
	es->wtop = es->wleft = 0;
	es->CurrLine = es->CurrCol = 0;
	es->WndRow = es->WndCol = 0;
	es->TextChanged = FALSE;
	es->textwidth = 0;
	es->SelBegLine = es->SelBegCol = 0;
	es->SelEndLine = es->SelEndCol = 0;
	dprintf_edit(stddeb, "EDIT_SetHandleMsg: textlen=%d\n",
                                                es->textlen);

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


/*********************************************************************
 *  EM_SETTABSTOPS message function
 */

LONG EDIT_SetTabStopsMsg(HWND hwnd, WORD wParam, LONG lParam)
{
    unsigned short *tabstops;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    es->NumTabStops = wParam;
    if (wParam == 0)
	es->hTabStops = EDIT_HeapReAlloc(hwnd, es->hTabStops, 1);
    else if (wParam == 1)
    {
	es->hTabStops = EDIT_HeapReAlloc(hwnd, es->hTabStops, 1);
	tabstops = (unsigned short *)EDIT_HeapAddr(hwnd, es->hTabStops);
	*tabstops = (unsigned short)lParam;
    }
    else
    {
	es->hTabStops = EDIT_HeapReAlloc(hwnd, es->hTabStops, wParam);
	tabstops = (unsigned short *)EDIT_HeapAddr(hwnd, es->hTabStops);
	memcpy(tabstops, (unsigned short *)lParam, wParam);
    }
    return 0L;
}


/*********************************************************************
 *  EDIT_CopyToClipboard
 *
 *  Copy the specified text to the clipboard.
 */

void EDIT_CopyToClipboard(HWND hwnd)
{
    HANDLE hMem;
    char *lpMem;
    int i, len;
    char *bbl, *bel;
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es = 
	(EDITSTATE *)EDIT_HeapAddr(hwnd, (HANDLE)(*(wndPtr->wExtra)));

    bbl = EDIT_TextLine(hwnd, es->SelBegLine) + es->SelBegCol;
    bel = EDIT_TextLine(hwnd, es->SelEndLine) + es->SelEndCol;
    len = (int)(bel - bbl);

    hMem = GlobalAlloc(GHND, (DWORD)(len + 1));
    lpMem = GlobalLock(hMem);

    for (i = 0; i < len; i++)
	*lpMem++ = *bbl++;

    GlobalUnlock(hMem);
    OpenClipboard(hwnd);
    EmptyClipboard();
    SetClipboardData(CF_TEXT, hMem);
    CloseClipboard();
}


/*********************************************************************
 *  WM_PASTE message function
 */

void EDIT_PasteMsg(HWND hwnd)
{
    HANDLE hClipMem;
    char *lpClipMem;

    OpenClipboard(hwnd);
    if (!(hClipMem = GetClipboardData(CF_TEXT)))
    {
	/* no text in clipboard */
	CloseClipboard();
	return;
    }
    lpClipMem = GlobalLock(hClipMem);
    EDIT_InsertText(hwnd, lpClipMem, strlen(lpClipMem));
    GlobalUnlock(hClipMem);
    CloseClipboard();
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
}


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

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

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