Skip to content
Snippets Groups Projects
edit.c 68.7 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
/*
 * Edit control
 *
 * Copyright  David W. Metcalfe, 1994
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Release 2, June 1994
Alexandre Julliard's avatar
Alexandre Julliard committed
 */

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

#include <stdlib.h>
#include <string.h>
#include <windows.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <heap.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "win.h"
#include "class.h"
#include "user.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "scroll.h"
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
/* #define DEBUG_EDIT /* */
Alexandre Julliard's avatar
Alexandre Julliard committed

#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 */
Alexandre Julliard's avatar
Alexandre Julliard committed
    MDESC **localheap;       /* pointer to application's local heap */
Alexandre Julliard's avatar
Alexandre Julliard committed
    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 */
Alexandre Julliard's avatar
Alexandre Julliard committed
    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 */
Alexandre Julliard's avatar
Alexandre Julliard committed
} 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)
Alexandre Julliard's avatar
Alexandre Julliard committed
#define SelMarked(es) (es->SelBegLine != 0 || es->SelBegCol != 0 || \
		       es->SelEndLine != 0 || es->SelEndCol != 0)
Alexandre Julliard's avatar
Alexandre Julliard committed

/* 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);
Alexandre Julliard's avatar
Alexandre Julliard committed
long EDIT_NCCreateMsg(HWND hwnd, LONG lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
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);
Alexandre Julliard's avatar
Alexandre Julliard committed
void EDIT_WriteSel(HWND hwnd, int y, int start, int end);
Alexandre Julliard's avatar
Alexandre Julliard committed
void EDIT_StopMarking(HWND hwnd);
LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam);
LONG EDIT_GetSelMsg(HWND hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
void EDIT_ReplaceSel(HWND hwnd, LONG lParam);
void EDIT_InsertText(HWND hwnd, char *str, int len);
Alexandre Julliard's avatar
Alexandre Julliard committed
LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam);
LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
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_TextAlloc(EDITSTATE *es, int bytes);
void *EDIT_TextAddr(EDITSTATE *es, unsigned int handle);
unsigned int EDIT_TextReAlloc(EDITSTATE *es, unsigned int handle, int bytes);
void EDIT_SetHandleMsg(HWND hwnd, WORD wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
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:
Alexandre Julliard's avatar
Alexandre Julliard committed
	lResult = es->hDeletedText;
Alexandre Julliard's avatar
Alexandre Julliard committed
	break;
	
Alexandre Julliard's avatar
Alexandre Julliard committed
    case EM_EMPTYUNDOBUFFER:
	EDIT_ClearDeletedText(hwnd);
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
    case EM_FMTLINES:
Alexandre Julliard's avatar
Alexandre Julliard committed
	printf("edit: EM_FMTLINES message received\n");
	if (!wParam)
	    lResult = 1L;
	else
	    lResult = 0L;
Alexandre Julliard's avatar
Alexandre Julliard committed
	break;

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

    case EM_GETHANDLE:
Alexandre Julliard's avatar
Alexandre Julliard committed
	lResult = es->hText;
Alexandre Julliard's avatar
Alexandre Julliard committed
	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:
Alexandre Julliard's avatar
Alexandre Julliard committed
	if (wParam)
	    es->MaxTextLen = wParam;
	else
	    es->MaxTextLen = 65000;
Alexandre Julliard's avatar
Alexandre Julliard committed
	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:
Alexandre Julliard's avatar
Alexandre Julliard committed
	lResult = EDIT_LineLengthMsg(hwnd, wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
	break;

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

    case EM_REPLACESEL:
Alexandre Julliard's avatar
Alexandre Julliard committed
	HideCaret(hwnd);
	EDIT_ReplaceSel(hwnd, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
	break;

    case EM_SETHANDLE:
Alexandre Julliard's avatar
Alexandre Julliard committed
	EDIT_SetHandleMsg(hwnd, wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
	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;

Alexandre Julliard's avatar
Alexandre Julliard committed
    case EM_UNDO:
	HideCaret(hwnd);
	lResult = EDIT_UndoMsg(hwnd);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
    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:
Alexandre Julliard's avatar
Alexandre Julliard committed
	textPtr = EDIT_TextAddr(es, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
	if ((int)wParam > (len = strlen(textPtr)))
	{
	    strcpy((char *)lParam, textPtr);
	    lResult = (DWORD)len;
	}
	else
	    lResult = 0L;
	break;

    case WM_GETTEXTLENGTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
	textPtr = EDIT_TextAddr(es, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
	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);
Alexandre Julliard's avatar
Alexandre Julliard committed
	SetFocus(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
	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;

Alexandre Julliard's avatar
Alexandre Julliard committed
    case WM_NCCREATE:
	lResult = EDIT_NCCreateMsg(hwnd, lParam);
	break;
	
Alexandre Julliard's avatar
Alexandre Julliard committed
    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:
Alexandre Julliard's avatar
Alexandre Julliard committed
	HideCaret(hwnd);
	EDIT_SetFont(hwnd, wParam, lParam);
	SetCaretPos(es->WndCol, es->WndRow * es->txtht);
	ShowCaret(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
	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;
}


/*********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *  WM_NCCREATE message function
Alexandre Julliard's avatar
Alexandre Julliard committed
long EDIT_NCCreateMsg(HWND hwnd, LONG lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
    WND *wndPtr = WIN_FindWndPtr(hwnd);
    EDITSTATE *es;
    unsigned int *textPtrs;
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *text;
    int len;
Alexandre Julliard's avatar
Alexandre Julliard committed

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

    /* --- text buffer */
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->localheap = &HEAP_LocalFindHeap(createStruct->hInstance)->free_list;
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->MaxTextLen = MAXTEXTLEN + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(es->hText))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	es->textlen = EditBufLen(wndPtr) + 1;
	es->hText = EDIT_TextAlloc(es, EditBufLen(wndPtr) + 2);
	text = EDIT_TextAddr(es, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
	memset(text, 0, es->textlen + 2);
	EDIT_ClearTextPointers(hwnd);
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
	if (strlen(createStruct->lpszName) < EditBufLen(wndPtr))
	{
	    es->hText = EDIT_TextAlloc(es, EditBufLen(wndPtr) + 2);
	    es->textlen = EditBufLen(wndPtr) + 1;
	    *(text + es->textlen) = '\0';
	}
	else
	{
	    es->hText = EDIT_TextAlloc(es, strlen(createStruct->lpszName) + 2);
	    es->textlen = strlen(createStruct->lpszName) + 1;
	}
	text = EDIT_TextAddr(es, es->hText);
	*(text + es->textlen + 1) = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
	EDIT_BuildTextPointers(hwnd);
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((createStruct->style & WS_VSCROLL) || 
	(createStruct->style & WS_HSCROLL)) NC_CreateScrollBars(hwnd);

    /* 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_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
    CLASS *classPtr;
    short *charWidths;
    TEXTMETRIC tm;
    char *text;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* --- 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;
Alexandre Julliard's avatar
Alexandre Julliard committed
    es->SelBegLine = es->SelBegCol = 0;
    es->SelEndLine = es->SelEndCol = 0;
    es->hFont = 0;
    es->hDeletedText = 0;
    es->DeletedLength = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* 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)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    text = EDIT_TextAddr(es, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
    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)));
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *text = EDIT_TextAddr(es, es->hText);
Alexandre Julliard's avatar
Alexandre Julliard committed
    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;
Alexandre Julliard's avatar
Alexandre Julliard committed

Alexandre Julliard's avatar
Alexandre Julliard committed
    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;
Alexandre Julliard's avatar
Alexandre Julliard committed
    HFONT oldfont;
Alexandre Julliard's avatar
Alexandre Julliard committed
    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);

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

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
    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.
 */