#include "windows.h"
#include <windowsx.h>
#include "fontedit.h"
#include "fcntl.h"
#include <stdio.h>

/****************************************************************************/
/*              Shared Variables                                            */
/****************************************************************************/

POINT SnapPointToGrid(POINT Pt);
LONG  APIENTRY FontEditWndProc(HWND, WORD, WPARAM, LONG);
BOOL  APIENTRY AboutDlg(
	HWND   hDlg,
	WORD   message,
	WPARAM wParam,
	LPARAM lParam
	);
BOOL   NewFile;                         /* flag indicating that NEW menu
                                           item was selected */
extern DLGPROC lpHeaderProc;            /* Pointer to Dialog Box Procedure */
extern DLGPROC lpReSizeProc;            /* Pointer to Dialog Box Procedure */
extern DLGPROC lpWidthProc;             /* Pointer to Dialog Box Procedure */

extern FontHeaderType font;             /* Structure of Font File Header */
extern CHAR matBox [wBoxLim] [kBoxLim]; /* array to hold Box */
extern HCURSOR hCross;                  /* handle to "+" shaped cursor(displayed
                                           when in ROW or COLUMN menus */
extern BOOL fReadOnly;
extern HANDLE hInst;                    /* Module Handle */
extern HBRUSH hbrBackGround;
extern HWND hFont;
extern HWND hBox;
extern RECT rectWin;                    /* Client Rectangle */
extern BOOL fLoaded;                    /* Set if a file loaded */
extern BOOL fChanged;                   /* Anything has changed */
extern BOOL fEdited;                    /* This character changed */
extern DWORD kBox;                       /* height of character */
extern DWORD wBox;                       /* Width of character */
extern DWORD kStuff;                     /* Width of Show Header */
extern INT  swH;                        /* Position in Show Window 0-100 */
extern BYTE iChar;                      /* Character being edited */
extern BYTE jChar;                      /* Last Char. of edit block */
extern CHAR szNewFile[];                /* Name of New File */
extern CHAR szFontFile[];               /* Name of Font File */
extern CHAR szFontFileFull[];               /* Name of Font File */
extern CHAR szFileNameTemp[];
extern CHAR *szFileNameSave;
extern INT cSysHeight;

extern CHAR *vrgsz[];                    /* total number of strings */
extern OFSTRUCT ofstrFile;
extern CHAR szExt[];                    /* default extension */
extern CHAR szAppName[];
extern CHAR szSCC[];
extern HCURSOR hOldCursor;              /* handle to old arrow shaped cursor */
/****************************************************************************/
/*                      Local Variables                                     */
/****************************************************************************/

HBRUSH hbrWhite;
HBRUSH hbrBlack;
HBRUSH hbrGray;
HBRUSH hbrDkGray;
HBRUSH hNullBrush;
HPEN hWhitePen;
DWORD colors[3] = {WHITENESS, BLACKNESS, PATCOPY};


CHAR matBackup [wBoxLim] [kBoxLim];     /* Backup for UNDO */
DWORD wBoxBackup;

LONG scale = 7;			/* height/width of squares in box */
WORD cursor = 0;		/* Add/Del cursor */

BOOL fAll = TRUE;		/* Redraw all if TRUE */
POINT ptBox = {10, 5};		/* where edit box is */

RECT	rectRubber;		/* Rubber banding rectangle */
HDC	hDst;			/* Rubber banding dc */

POINT ptA;			/* Start of draw/rectangle */
POINT ptB;			/* End of rectangle */
POINT ptC;			/* Current square */
CHAR colorA;			/* Color at/under point A */
DWORD newWidth;			/* Width set in WIDER option */
BOOL FillingRect = FALSE;
BOOL fRubberBanding = FALSE;	/* flag indicating if rubberbanding in
                                   progress for row/column add/delete */
BOOL fStartRubberBand = FALSE;  /* flag indicating that rubberbanding
                                   can start */
BOOL fCaptured = FALSE;		/* set if mouse is caputred */
BOOL fJustZapped = FALSE;	/* Set on row/col add/delete */
RECT FontRect;			/* rectangle bounding font pattern */

/****************************************************************************/
/*                      Local Functions                                     */
/****************************************************************************/

VOID ClearFill(DWORD col, DWORD row, WORD mode);
VOID FontEditCommand(HWND hBox, WORD id);
VOID BoxRestore(VOID);
VOID CharRectDimensions(LPRECT Rect);
VOID BoxPaint(VOID);
VOID DrawBox(HDC, DWORD, DWORD, DWORD, DWORD, INT, DWORD);
VOID PASCAL DrawRubberBand(HDC hDst, LPRECT lpRect, DWORD rop);
VOID FontEditPaint(HWND hBox, HDC hDC);
BOOL CheckSave(VOID);
VOID DupCol(DWORD col, DWORD row);
VOID DupRow(DWORD col, DWORD row);
VOID ZapCol(DWORD col, DWORD row);
VOID ZapRow(DWORD col, DWORD row);
VOID AddDel(DWORD col, DWORD row, WORD mode);
VOID MouseInBox(HWND hBox, WORD message, POINT ptMouse);
VOID BoxBackup(VOID);
VOID ReadRect(VOID);

/*****************************************************************************/
VOID
BoxPaint(
	VOID
	)      /* Our call to FontEditPaint */
{
	HDC hDC;

	hDC = GetDC(hBox);
	FontEditPaint(hBox,  hDC);
	ReleaseDC(hBox, hDC);
	if (fRubberBanding)
		DrawRubberBand(hDst, &rectRubber, R2_XORPEN);
}


/******************************************************************************
 * FontEditPaint(hBox, hDC)
 *
 * purpose: calculates coordinates for text in main window and repaints edit
 *          box,small character boxes and text
 *
 * params:  HWND hBox : handle to main window
 *          HDC hDC   I handle to display context
 * returns: none
 *
 * side effects:  alters matBox (global 2-d array with ready pixel info. on
 *                currently displayed box)
 *****************************************************************************/
VOID
FontEditPaint(
	HWND hBox,
	HDC hDC
	)
{
	CHAR szTemp[20];
	DWORD len, yText, xText;

	if (!fLoaded)               /* Must load font first */
		return;

	/* Here the application paints its window. */
	if (fAll) {               /* Draw box setting */
		GetClientRect(hBox, (LPRECT)&rectWin);
		scale = (rectWin.bottom-rectWin.top-kStuff-20) / (kBox+1);
		scale = min(scale, (min(320, rectWin.right - rectWin.left) - 
				90) / ((int)wBox + 1));
		scale = max(scale, 4);
		xText = ptBox.x + scale * wBox + 16;

		SelectObject(hDC, hbrDkGray);
		Rectangle(hDC,
		    ptBox.x - 2,
		    ptBox.y - 2,
		    ptBox.x + 3 + wBox * scale,
		    ptBox.y + 5 + kBox * scale);
		SelectObject(hDC, hbrGray);

		Rectangle(hDC,          /* Surround for font displays */
		    xText,
		    ptBox.y - 2,
		    xText + wBox + 8,
		    ptBox.y + 3 + kBox * 2 + font.ExtLeading);

		/* Now put up the text */
		yText = 14 + 2 * kBox + font.ExtLeading;
		len = (DWORD) sprintf(szTemp, vszCHAR, iChar);
		TextOut(hDC, xText, yText, (LPSTR)szTemp, len);
		len = (DWORD) sprintf(szTemp, vszWIDTH, wBox);
		TextOut(hDC, xText, yText + cSysHeight, (LPSTR)szTemp, len);
		len = (DWORD) sprintf(szTemp, vszHEIGHT, kBox);
		TextOut(hDC, xText, yText + cSysHeight + cSysHeight, 
			(LPSTR)szTemp, len);
	}

	/* Draw Character Box */
	DrawBox(hDC, ptBox.x, ptBox.y, wBox, kBox, scale, 1);

	/* Draw small character */
	xText = ptBox.x + scale * wBox + 16;
	DrawBox(hDC,
	    xText + 4,
	    ptBox.y,
	    wBox,
	    kBox,
	    1, 0);

	/* Draw another small character to show leading */
	DrawBox(hDC,
	    xText + 4,
	    ptBox.y + kBox + font.ExtLeading,
	    wBox,
	    kBox,
	    1, 0);
	fAll = TRUE;
}


/******************************************************************************
 * DrawBox(hDC, xChar, yChar, wChar, kChar, scale, htSep)
 *
 * purpose: draws the edit box for the character being edited and colors the
 *          grid squares according to the pixels set for the character.
 *
 * params:  HDC hDC         : handle to display context
 *          DWORD xChar      : x-location of char box.
 *          DWORD yChar      : y-location of char box
 *          DWORD wChar      : width of char box
 *          DWORD kChar      : height of char
 *          INT   wScale     : Scale of the squares.
 *          DWORD htSep      : height of square separators
 *
 * returns: none
 *
 * side effects:  alters matBox (global 2-d array with ready pixel info. on
 *                currently displayed box)
 *****************************************************************************/
VOID
DrawBox(
	HDC hDC,
	DWORD xChar,                             /* x-location of char. */
	DWORD yChar,                             /* y-location of char. */
	DWORD wChar,                             /* width of char. */
	DWORD kChar,                             /* height of char */
	INT   wScale,				 /* scale of the squares. */
	DWORD htSep                              /* hgt of square separators */
	)
/* draw a character of separate squares of height 'scale' with sep. 'htSep' */
{
	DWORD i, j, sep;

	if (fAll) {               /* redraw them all */
	    for (j = 0; j < kChar; j++) {
		sep = (j >= font.Ascent) ? htSep : 0;
		for (i = 0; i < wChar; i++) {
		    if (wScale == 1)
			SetPixel(hDC, xChar + i, yChar + j,
				matBox[i][j] == TRUE ? BLACK : WHITE);
		    else
			PatBlt(hDC,
				xChar + wScale * i,
				yChar + wScale * j + sep,
				wScale - htSep,
				wScale - htSep,
				colors[matBox[i][j] == TRUE ? 1 : 0]);
		}
	    }
	}
	else {			/* redraw one just flipped */
	    if (wScale == 1)
		SetPixel(hDC,
			xChar + ptC.x,
			yChar + ptC.y,
			matBox[ptC.x][ptC.y] == TRUE ? BLACK : WHITE);
	    else {
		sep = (((DWORD) ptC.y >= font.Ascent) ? htSep : 0L);
		SelectObject(hDC, hbrGray);
		PatBlt(hDC,
			xChar + wScale * ptC.x,
			yChar + wScale * ptC.y + sep,
			wScale - htSep,
			wScale - htSep,
			colors[matBox[ptC.x][ptC.y]]);
	    }
	}

}


/******************************************************************************
 * FontEditCommand(hBox, id)
 *
 * purpose: interprets menu id and calls appropriate function to do the task
 *
 * params:  HWND hBox : handle to main window
 *          WORD id   : menu command id
 * returns: none
 *
 * side effects: plenty
 *
 *****************************************************************************/
VOID
FontEditCommand(
	HWND hBox,
	WORD id
	)
{
	CHAR * szError;                 /* String for error messages */
	LONG w; 
	DWORD y, i, j;
	BOOL fRepaint = FALSE;
	HMENU hMenu;
	DLGPROC lpprocAboutDlg;
	MSG message;

	szError = "";               /* No Errors yet */

	switch (id) {
	    case FONT_EXIT:
		if (!CheckSave())    /* See if any files need saving */
			break;
		/* Window's being destroyed. */
		if (fLoaded)         /* 4/8/87 Linsh added */
			DeleteGlobalBitmap(); /* Get rid of memory DC */
		PostQuitMessage(0);  /* Cause application to be terminated */
		break;

	    case FONT_ABOUT:
		lpprocAboutDlg = (DLGPROC)AboutDlg;
		DialogBox (hInst, vszABOUT, hBox, lpprocAboutDlg);
		FreeProcInstance (lpprocAboutDlg);
		break;

	    case FONT_LOAD:             /*  Check File Name  */
	    case FONT_NEW :
		if (!CheckSave())       /*  See if current font needs saving */
			return;
	/* to prevent scrambling of Show window chars, Bring back Show
        ** window to parent window's client area before invoking the dialog */

		if (CommDlgOpen(hBox,&ofstrFile,szNewFile,szExt,szFontFile,id)
				== FALSE) {

			InvalidateRect(hFont, (LPRECT)NULL, FALSE);
			UpdateWindow(hFont);
			return;
		}
		/* else drop thru */

	    case FONT_START:	/*  Here if file name passed as argument */
		InvalidateRect(hFont, (LPRECT)NULL, FALSE);
		UpdateWindow(hFont);

		szError = FontLoad (szNewFile, &ofstrFile);

	/* Hack : needed to remove umwanted WM_MOUSEMOVE messages from the 
         * queue.
         * Apparently, Windows needs to reposition the mouse after a dialog
         * is ended with a mouse double-click (releases mouse capture?) for
         * which a couple of WM_MOUSEMOVEs may get sent to parent app.
         * These mess with the edit box below the dialog if they happen to 
         * overlap.
         */
		PeekMessage((LPMSG) &message, hBox, WM_MOUSEMOVE, WM_MOUSEMOVE,
		    PM_REMOVE);

		if (fLoaded)    /* If loaded then do a few things */ {
		    jChar = iChar = 65;                 /* Show an A */
		    if ((BYTE)iChar > (BYTE)font.LastChar)
			jChar = iChar = font.FirstChar; /* .. if we can */
		    swH = 15;                   /* Good bet to make A visible */
		    fEdited = fChanged = FALSE;
		    ResizeShow();               /* Set Box to proper size */
		    ScrollFont();               /* Set thumb */
		    CharToBox(iChar);
		}
		FontRename(szError);
		SetFocus(hBox);
		return;

	    case FONT_SAVE:
		if (!NewFile) {
		    if (fLoaded && fChanged) {
			lstrcpy((LPSTR)szNewFile, (LPSTR)szFontFileFull);
			BoxToChar(iChar);           /* Just in case */
			szError = FontSave (szNewFile, &ofstrFile);
			FontRename(szError);        /* Rename or Print Error */
			return;
		    }
		    else
			return;
		}
	/* else file has been opened by selecting NEW... on menu.
         * Fall thro' and bring up SaveAs dialog minus default
         * filename in edit window */

	    case FONT_SAVEAS:
		BoxToChar(iChar);               /* Just in case */

		if (CommDlgSaveAs (hInst, hBox, &ofstrFile, szNewFile, szExt,
				szFontFile) == TRUE) {

		    szError = FontSave (szNewFile, &ofstrFile);
		    FontRename (szError);          /* Rename or Print Error */
		}

		/* to prevent scrambling of Show window chars,
           repaint show window after dialog is brought down */
		InvalidateRect (hFont, (LPRECT)NULL, TRUE);
		UpdateWindow (hFont);
		return;

	    case FONT_HEADER:
		/* to prevent scrambling of Show window chars,
		 * repaint show window after dialog is invoked */
		DialogBox(hInst, (LPSTR)vszDHeader, hBox, lpHeaderProc);
		InvalidateRect(hFont, (LPRECT)NULL, TRUE);
		UpdateWindow(hFont);
		return;

	    case FONT_RESIZE:
		/* to prevent scrambling of Show window chars,
		   repaint show window after dialog is brought down */
		if (DialogBox(hInst, (LPSTR)vszDResize, hBox, lpReSizeProc)) {
		    /* BoxToChar(iChar);*/ /* save current before resizing */
		    ResizeShow();       /* New Font Display Size */
		    CharToBox(iChar);               /* New Box display */
		}
		InvalidateRect(hFont, (LPRECT)NULL, TRUE);
		UpdateWindow(hFont);
		return;

	    case FONT_COPY:                     /* Copy to Clipboard */
		BoxToChar(iChar);               /* Just in case */
		ToClipboard(iChar, wBox, kBox);
		break;

	    case FONT_PASTE:            /* Paste in Character form Clipboard */
		BoxBackup();            /* In case we change our minds */
		ptA.x = ptA.y = 0;
		wBox = ClipboardToBox(ptA, wBox, kBox, TRUE);
		fRepaint = TRUE;
		break;

	    case WIDER_LEFT:
	    case WIDER_RIGHT:
	    case WIDER_BOTH:
	    case NARROWER_LEFT:
	    case NARROWER_RIGHT:
	    case NARROWER_BOTH:
	    case WIDTH:
		w = newWidth = wBox;
		if (font.Family & 1)            /* Variable width or else */ {
		    switch (id) {
			case WIDER_BOTH:
			    w++;
			case WIDER_LEFT:
			case WIDER_RIGHT:
			    w++;
			    break;
			case NARROWER_BOTH:
			    w--;
			case NARROWER_LEFT:
			case NARROWER_RIGHT:
			    w--;
			    break;
			case WIDTH:
			    if (DialogBox(hInst,
					(LPSTR)vszDWidth, hBox, lpWidthProc))
				w = newWidth;
			    break;
		    }

		    if (w < 0 || w >= wBoxLim) {
			MessageBox(hBox,
				(LPSTR)vszEdLimits0To64,
				(LPSTR)szAppName,
				MB_OK | MB_ICONASTERISK);
			break;                  /* Out of range! quit */
		    }
		    if (w > (LONG) font.MaxWidth) {
			if (IDOK == MessageBox(hBox,
					    (LPSTR)vszMaxWidthIncrease,
					    (LPSTR)szAppName,
					    MB_OKCANCEL | MB_ICONQUESTION))
			    font.MaxWidth = (WORD)w;
			else
			    break;
		    }
		    BoxBackup();                /* In case we change our minds */
		    wBox = (WORD)w;             /* Reset width */
		    fRepaint = TRUE;            /* Signal redraw */
		    switch (id) {
		    case WIDER_LEFT:
		    case WIDER_BOTH:            /* Shift character one right */
			DupCol(0, kBoxLim - 1);
			for (y = 0; y < kBoxLim; y++)
			    matBox[wBox -1][y] = FALSE; /* Clear right column */
			for (y = 0; y < kBoxLim; y++)
			    matBox[0][y] = FALSE;       /* Clear left column */
			break;
		    case NARROWER_LEFT:
		    case NARROWER_BOTH:	/* Shift character one left */
			if (wBox) {	/* .. unless width is already 0 */
			    for (j = 0; j <= kBox - 1; j++)
				for (i = 0; i <= wBox - 1; i++)
				    matBox[i][j] = matBox[i + 1][j];
			    break;
			}
		    }
		}
		else {
		    MessageBox(hBox,
			    (LPSTR)vszCannotChangeWidth,
			    (LPSTR)szAppName,
			    MB_OK | MB_ICONASTERISK);
		}
		break;

	    case ROW_ADD:
	    case ROW_DEL:
	    case COL_ADD:
	    case COL_DEL:
		/* set cursor to "+" shaped cursor */
		SetCapture (hBox); /* so that cursor doesn't get restored
				      before we are done */
		hOldCursor = SetCursor (LoadCursor (NULL, IDC_CROSS));
		fCaptured = TRUE;
		cursor = id;
		break;

	    case BOX_CLEAR:
	    case BOX_FILL:
	    case BOX_INV:
	    case BOX_HATCH:
	    case BOX_LEFTRIGHT:
	    case BOX_TOPBOTTOM:
	    case BOX_COPY:
	    case BOX_PASTE:
		/* Get one o' da funky cursors */
		SetCapture(hBox);
		hOldCursor = SetCursor(LoadCursor(hInst, MAKEINTRESOURCE(id)));
		fStartRubberBand = TRUE;
		CharRectDimensions((LPRECT)&FontRect);
		cursor = id;
		break;

	    case BOX_REFRESH:           /* Go get old version of character */
		BoxBackup();            /* In case we change our minds */
		CharToBox(iChar);
		hMenu = GetMenu(hBox);
		EnableMenuItem(hMenu, BOX_UNDO, MF_ENABLED);    /* Can Unrefresh! */
		break;
	    case BOX_UNDO:
		BoxRestore();
		hMenu = GetMenu(hBox);
		EnableMenuItem(hMenu, BOX_REFRESH, MF_ENABLED);
		fRepaint = TRUE;
		break;
	}
	if (fRepaint) {
		fEdited = fChanged = TRUE;
		InvalidateRect(hBox, (LPRECT)NULL, TRUE);
	}
}


VOID
CharRectDimensions(
	LPRECT Rect
	)
/* returns the dimensions of the edit box */
{
	Rect->top    =  ptBox.y;
	Rect->bottom =  ptBox.y + (kBox) * (scale);
	Rect->left   =  ptBox.x;
	Rect->right  =  ptBox.x + (wBox) * (scale);
}


/******************************************************************************
 * BOOL  APIENTRY WidthProc(hDial, message, wParam, lParam)
 *
 * purpose: dialog function for Width menu function
 *
 * params:  same as for all dialog fns.
 *
 * side effects: changes Box width variable
 *
 *****************************************************************************/
BOOL  APIENTRY 
WidthProc(
	HWND   hDial,
	WORD   message,
	WPARAM wParam,
	LPARAM lParam
	)
{
	INT i;
	BOOL fOk;

	UNREFERENCED_PARAMETER(lParam);

	switch (message) {
	default:
		return FALSE;
	case WM_INITDIALOG:
		SetDlgItemInt(hDial, BOX_WIDTH, newWidth, FALSE);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			fChanged = TRUE;
			i = GetDlgItemInt(hDial, BOX_WIDTH, (LPBOOL)&fOk, FALSE);
			if (fOk && i < wBoxLim)
				newWidth = i;
			else
				ErrorBox(hDial, vszWidthOutOfBounds);

		case IDCANCEL:
			EndDialog(hDial, LOWORD(wParam) != IDCANCEL);
			break;

		default:
			break;
		}
	}
	return TRUE;
}


/******************************************************************************
 * BOOL CheckSave()
 *
 * purpose: checks if font is dirty and prompts user to save font. If yes,
 *          edit box is saved and file save function is called.
 *
 * params:  none
 *
 * returns: TRUE : font has been changed
 *          FALSE: font untouched
 *
 * side effects: file dirty flag is reset.
 *
 *****************************************************************************/
BOOL 
CheckSave(
	VOID
	)
{
	CHAR * szError;                 /* String for error messages */
	CHAR szMessage[MAX_STR_LEN+MAX_FNAME_LEN];

	/* Check if anything changed */
	if (fLoaded && fChanged && (!fReadOnly)) {
		lstrcpy((LPSTR)szNewFile, (LPSTR)szFontFileFull);
		DlgMergeStrings(szSCC, szNewFile, szMessage);
		switch (MessageBox(hFont,
		    (LPSTR)szMessage,
		    (LPSTR)szAppName,
		    MB_YESNOCANCEL | MB_ICONQUESTION)) {
		case IDYES:
			BoxToChar(iChar);            /* Just in case */
			szError = FontSave (szNewFile, &ofstrFile);
			FontRename(szError);            /* Rename or Print Error */
		case IDNO:
			return TRUE;
		case IDCANCEL:
			return FALSE;
		}
	}
	return TRUE;
}


/******************************************************************************
 * MouseInBox(hBox, message, ptMouse)
 *
 * purpose: do edit operation depending on currently active menu command
 *
 * params: HWND hBox       : handle to main window
 *         WORD message    : Message retrieved by main window's window fn.
 *         POINT ptMouse   : current mouse coordinates
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box). Also assigns values to ptA and ptB
 *****************************************************************************/
VOID
MouseInBox(
	HWND hBox,
	WORD message,
	POINT ptMouse
	)
{
	POINT pt;
	BOOL fRepaint = FALSE;

	pt = SnapPointToGrid(ptMouse);

	if (pt.x >= 0L && pt.y >= 0L &&
	    ((DWORD)pt.x) < wBox && ((DWORD)pt.y) < kBox) {
		fEdited = fChanged = TRUE;
		ptC.x = pt.x;
		ptC.y = pt.y;			/* Current square */
		if (message == WM_LBUTTONDOWN)
			BoxBackup();		/* Set up for UNDO */
		switch (cursor) {
		case BOX_COPY:
		case BOX_PASTE:
		case BOX_CLEAR:
		case BOX_FILL:
		case BOX_INV:
		case BOX_HATCH:
		case BOX_LEFTRIGHT:
		case BOX_TOPBOTTOM:
			ptA.x = pt.x;
			ptA.y = pt.y;		/* save anchor point */
						/* save color under marker */
			colorA = matBox[pt.x][pt.y];
			fAll = FALSE;

			fRepaint = TRUE;
			break;
		default:
			AddDel(pt.x, pt.y, cursor);
			fRepaint = TRUE;
			cursor = FALSE;
			break;
		case FALSE:
			switch (message) {
			case WM_LBUTTONDOWN:	/*invert */
				colorA = (matBox[pt.x][pt.y] ^= TRUE);
				break;

			case WM_LBUTTONUP:
				break;

			case WM_MOUSEMOVE:
				matBox[pt.x][pt.y] = colorA;    /* paint */
				break;
			}
			fRepaint = TRUE;
			fAll = FALSE;               /* Limited redraw */
			break;
		}
		if (fRepaint) {
			BoxPaint();
			return;
		}
	}
	cursor = FALSE;
}


/******************************************************************************
 * ReadRect(ptMouse)
 *
 * purpose:      defines the rectangular region in edit box to be filled by
 *               fill menu command  by fixing top left (ptA) and bottom right
 *               (ptB) coordinates of rect.
 *
 * params:
 *
 * assumes:	that rectRubber is normalized (eg, left < right, botton > top)
 *
 * returns:    none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box). Also assigns values to ptA and ptB
 *****************************************************************************/
VOID
ReadRect(
	)
{

	ptA.x = (rectRubber.left-(ptBox.x+1))   / scale;
	ptA.y = (rectRubber.top-(ptBox.y+1))    / scale;
	ptB.x = (rectRubber.right-(ptBox.x-2))  / scale - 1;
	ptB.y = (rectRubber.bottom-(ptBox.y-2)) / scale - 1;

	if (((DWORD)ptB.x) > wBox - 1)
	    ptB.x = wBox - 1;
	if (((DWORD)ptB.y) > kBox - 1)
	    ptB.y = kBox - 1;

	if (ptB.x >= 0 && ptB.y >= 0) {
	    ClearFill((DWORD)ptB.x, (DWORD)ptB.y, cursor);        
	    BoxPaint();
	}
	cursor = FALSE;
}


/******************************************************************************
 * ClearFill(col, row, mode)
 *
 * purpose:  fill the specified rectangular region in edit box with fill type
 *           indicated by mode.Top left corner of rect is global(ptA)
 *
 * params:   DWORD row : row (of bottom right corner of rect(ptB.x))
 *           DWORD col : column (of bottom right corner of rect(ptB.y))
 *           WORD mode: action to be performed
 *
 * returns:  none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
ClearFill(
	DWORD col, 
	DWORD row, 
	WORD mode
	)
{
	DWORD i, x, y;
	CHAR z;

	if (col < (DWORD)ptA.x) /* if points are reversed */ {
		i = col;
		col = ptA.x;
		ptA.x = i;
	}          /* flip them */
	if (row < (DWORD) ptA.y) {
		i = row;
		row = ptA.y;
		ptA.y = i;
	}          /* flip them */

	if (mode == BOX_LEFTRIGHT) {
		for (x = ptA.x; x <= (DWORD)((ptA.x + col) / 2); x++)
			for (y = ptA.y; y <= row; y++) {
				z = matBox[x][y];
				matBox[x][y] = matBox[ptA.x + col - x][y];
				matBox[ptA.x + col - x][y] = z;
			}
		return;
	}

	if (mode == BOX_TOPBOTTOM) {
		for (y = ptA.y; y <= ((DWORD)(ptA.y + row) / 2); y++)
			for (x = ptA.x; x <= col; x++) {
				z = matBox[x][y];
				matBox[x][y] = matBox[x][ptA.y + row - y];
				matBox[x][ptA.y + row - y] = z;
			}
		return;
	}

	if (mode == BOX_COPY)
		BoxToClipboard(ptA, col - ptA.x + 1, row - ptA.y + 1);

	if (mode == BOX_PASTE)
		ClipboardToBox(ptA, col - ptA.x + 1, row - ptA.y + 1, FALSE);

	for (x = ptA.x; x <= col; x++)
		for (y = ptA.y; y <= row; y++) {
			switch (mode) {
			case BOX_CLEAR:
			case BOX_FILL:
				matBox[x][y] = (CHAR)(mode == BOX_FILL);
				break;
			case BOX_INV:
				matBox[x][y] ^= (CHAR)TRUE;
				break;
			case BOX_HATCH:
				matBox[x][y] = (CHAR)((x+y)%2 ? TRUE : FALSE);
				break;
			}
		}
}


/******************************************************************************
 * AddDel(col, row, mode)
 *
 * purpose:  Add/Delete row/col as per mode
 *
 * params:   DWORD row : row
 *           DWORD col : column
 *           WORD mode: action to be performed
 *
 * returns:  none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
AddDel(
	DWORD col, 
	DWORD row, 
	WORD mode
	)
{
	switch (mode) {
	case ROW_ADD:
		DupRow(wBox, row);
		break;

	case ROW_DEL:
		ZapRow(wBox, row);
		break;

	case COL_ADD:
		DupCol(col, kBox);
		break;

	case COL_DEL:
		ZapCol(col, kBox);
		break;
	}
	/* restore arrow cursor */
	SetCursor (hOldCursor);
	ReleaseCapture();
	fCaptured = FALSE;
	fJustZapped = TRUE;
}


/******************************************************************************
 * ZapCol(col, row)
 *
 * purpose: delete given column in edit box. Shift cols to right given col
 *          right. Rightmost column gets duplicated.
 *
 * params : DWORD col : column
 *          DWORD row : row
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
ZapCol(
	DWORD col, 
	DWORD row
	)
{
	DWORD x, y;
	for (y = 0; y <= row; y++)
		for (x = col; x < wBox - 1; x++)
			matBox[x][y] = matBox[x + 1][y];
	for (y = 0; y <= row; y++)
		matBox[x][y] = matBox[x - 1][y];
}


/******************************************************************************
 * ZapRow(col, row)
 *
 * purpose: delete given row in edit box. Shift rows below given row up. Lowest
 *          row gets duplicated
 *
 * params:  DWORD col : column
 *          DWORD row : row
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
ZapRow(
	DWORD col, 
	DWORD row
	)
{
	DWORD x, y;
	for (x = 0; x <= col; x++)
		for (y = row; y < kBox - 1; y++)
			matBox[x][y] = matBox[x][y + 1];
	for (x = 0; x <= col; x++)
		matBox[x][y] = matBox[x][y - 1];
}


/******************************************************************************
 * DupCol(col, row)
 *
 * purpose: duplicate given column in edit box. Shift cols to right of given
 *          col right
 *
 * params:  DWORD col : column
 *          DWORD row : row
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
DupCol(
	DWORD col, 
	DWORD row
	)
{
	DWORD x, y;
	for (x = wBox - 1; x > col; x--)
		for (y = 0; y <= row; y++)
			matBox[x][y] = matBox[x - 1][y];
}


/******************************************************************************
 * DupRow(col, row)
 *
 * purpose: duplicate given row in edit box. Shift rows below given row down.
 *
 * params:  DWORD col : column
 *          DWORD row : row
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *****************************************************************************/
VOID
DupRow(
	DWORD col, 
	DWORD row
	)
{
	DWORD x, y;
	for (x = 0; x <= col; x++)
		for (y = kBox - 1; y > row; y--)
			matBox[x][y] = matBox[x][y - 1];
}


/******************************************************************************
 * ClearBox(col, row, bb)
 *
 * purpose: reset all pixels in edit box (make box white)
 *
 * params : none
 *
 * returns: none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box)
 *
 *****************************************************************************/
VOID
ClearBox(
	VOID
	)                  /* Clear edit box */
{
	DWORD x, y;
	for (x = 0; x < wBoxLim; x++)
		for (y = 0; y < kBoxLim; y++)
			matBox[x][y] = FALSE;
}


/******************************************************************************
 * BoxBackup()
 *
 * purpose:   makes a backup of pix. info. of currently displayed edit box
 *
 * params:    none
 *
 * returns:   none
 *
 * side effects: alters matBackup (local 2-d array with backup pixel info.
 *               on edit box )
 *****************************************************************************/
VOID
BoxBackup(
	VOID
	)
{
	DWORD x, y;
	HMENU hMenu;

	hMenu = GetMenu(hBox);
	EnableMenuItem(hMenu, BOX_UNDO, MF_ENABLED);
	EnableMenuItem(hMenu, BOX_REFRESH, MF_ENABLED);
	for (x = 0; x < wBoxLim; x++)
		for (y = 0; y < kBoxLim; y++)
			matBackup[x][y] = matBox[x][y];
	wBoxBackup = wBox;
}


/******************************************************************************
 * BoxRestore()
 *
 * purpose : Current edit box and backup box exchange places
 *
 * params  : none
 *
 * returns : none
 *
 * side effects: alters matBox (global 2-d array with ready pixel info. on
 *               currently displayed box
 *
 *****************************************************************************/
VOID
BoxRestore(
	VOID
	)            /* Box and Backup exchange places */
{
	DWORD x, y, temp;
	CHAR z;

	for (x = 0; x < wBoxLim; x++)
		for (y = 0; y < kBoxLim; y++) {
			z = matBackup[x][y];
			matBackup[x][y] = matBox[x][y];
			matBox[x][y] = z;
		}
	temp = wBox;
	wBox = wBoxBackup;
	wBoxBackup = temp;
}


/******************************************************************************
 * POINT SnapPointToGrid (Pt)
 *
 * purpose : Intended only for the Fill menu command where rubberbanding rect.
 *           needs to be aligned on the grid lines. Snap the current mouse
 *           coordinates to nearest grid intersection.
 *
 * params  : POINT Pt : current point mouse is over
 *
 * returns : POINT    : number of nearest square
 *
 * side effects:      : current mouse coordinate(global variable) altered to
 *                      return value
 *
 *****************************************************************************/
POINT 
SnapPointToGrid(
	POINT	Pt
	)
{

	Pt.x = (Pt.x - ptBox.x) / scale;
	if (Pt.y > (scale * (font.Ascent - 1)))
		Pt.y = Pt.y - 2;	/* Allow for break in box */
	Pt.y = (Pt.y - ptBox.y) / scale;
	return (Pt);
}


/******************************************************************************
 * VOID PASCAL EndRubberBandingRect()
 *
 * purpose: Stops rubberbanding rect for Fill menu command and cleans up
 *
 * params : HANDLE hDst : handle to dest. DC
 *
 * side effects: none
 *
 *****************************************************************************/
VOID PASCAL 
EndRubberBandingRect(
	HDC hDst  /* handle to dest. DC */
	)
{
	fRubberBanding = FALSE;      /* reset "in-progress" flag */

	ReleaseDC(hBox, hDst);
	ReleaseCapture();
	SetCursor(hOldCursor);
}


/******************************************************************************
 * HDC PASCAL InitialiseRubberBandingRect(hBox)
 *
 * purpose: Sets up rubberbanding rect for Fill menu command.
 *
 * params : HANDLE hDst : handle to box DC
 *
 * returns :handle to destination display context
 *
 * side effects: alters few global flags for rubberbanding
 *
 *****************************************************************************/
HDC PASCAL 
InitialiseRubberBandingRect(
	HDC hBox  /* handle to  DC  of box */
	)
{
	HDC hDst;

	fRubberBanding = TRUE;       /* set "in-progress" flag */
	fStartRubberBand = FALSE;    /* reset "start-proceedings" flag */
	SetCapture(hBox);            /* send all msgs to current window */

	hDst = GetDC (hBox);

	/* select pen and fill mode for rectangle*/
	hWhitePen  = SelectObject(hDst, GetStockObject (WHITE_PEN));
	hNullBrush = SelectObject(hDst, GetStockObject (NULL_BRUSH));
	SetROP2(hDst, R2_XORPEN);

	return(hDst);
}


/******************************************************************************
 * VOID PASCAL DrawRubberBand()
 *
 * purpose: Draw rubberbanding rect for Fill menu command.
 *
 * params : HANDLE hDst : handle to dest. DC
 *
 * side effects: alters few global flags for rubberbanding
 *
 *****************************************************************************/
VOID PASCAL 
DrawRubberBand(
	HDC   	hDst,                        /*  handle to dest. DC */
	LPRECT	lpRect,
	DWORD	rop
	)
{
#ifdef DBG
	char	buf[256];
#endif

	SetROP2(hDst, rop);
	Rectangle(hDst, lpRect->left,  lpRect->top,
			lpRect->right, lpRect->bottom);

#ifdef DBG
	sprintf(buf, "left=%d, top=%d, right=%d, bottom=%d", 
		lpRect->left,  lpRect->top, lpRect->right, lpRect->bottom);
	
	TextOut(hDst, ptBox.x+scale*wBox+16,
		      14+2*kBox+font.ExtLeading+3*cSysHeight,
		      buf, strlen(buf));
#endif
}


/******************************************************************************
 * long  APIENTRY FontEditWndProc(hBox, message, wParam, lParam)
 *
 * purpose: Master controller for Fontedit's all-encompassing main window
 *
 * params : same as for all window functions
 *
 * side effects: countless
 *
 *****************************************************************************/
LONG  APIENTRY 
FontEditWndProc(
	HWND   hBox,
	WORD   message,
	WPARAM wParam,
	LPARAM lParam
	)
{
	PAINTSTRUCT	ps;
	HMENU	hMenu;
	WORD	mf;
	POINT	pt;
	RECT	BoxRect;

	switch (message) {
	case WM_CLOSE:
		if (!CheckSave())    /* See if any files need saving */
			break;
		/* Window's being destroyed. */
		if (fLoaded)         /* 4/8/87 Linsh added */
			DeleteGlobalBitmap(); /* Get rid of memory DC */
		DestroyWindow(hFont);
		DestroyWindow(hBox);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);  /* Cause application to be terminated */
		break;

	case WM_QUERYENDSESSION:
		if (CheckSave())             /* See if any files need saving */
			return TRUE;
		break;

	case WM_ENDSESSION:
		if (fLoaded)
			DeleteGlobalBitmap();      /* Get rid of memory DC */
		break;

	case WM_SIZE:
		/* Window's size is changing.  lParam contains the width
        ** and height, in the low and high words, respectively.
        ** wParam contains SIZENORMAL for "normal" size changes,
        ** SIZEICONIC when the window is being made iconic, and
        ** SIZEFULLSCREEN when the window is being made full screen. */
		switch (wParam) {
		case SIZEFULLSCREEN:
		case SIZENORMAL:
			ResizeShow();
			if (kStuff != GetkStuff())	/* Did it change ? */
				ResizeShow();           /* Yes resize again */
			break;
		}
		break;

	case WM_MOVE: /* Tell popup to move with us. */
		if (!IsIconic(hBox))
			ResizeShow();
		break;

	case WM_PAINT:
		/* Time for the window to draw itself. */
		BeginPaint(hBox, (LPPAINTSTRUCT)&ps);
		FontEditPaint(hBox,  ps.hdc);
		EndPaint(hBox, (LPPAINTSTRUCT)&ps);
		break;


	case WM_COMMAND:
		/* A menu item has been selected, or a control is notifying
		 * its parent.  wParam is the menu item value (for menus),
		 * or control ID (for controls).  For controls, the low word
		 * of lParam has the window handle of the control, and the hi
		 * word has the notification code.  For menus, lParam contains
		 * 0L. */
		FontEditCommand(hBox, LOWORD(wParam));
		break;

		/* Data interchange request. */
	case WM_CUT:
	case WM_COPY:
	case WM_PASTE:
	case WM_CLEAR:
	case WM_UNDO:
	case WM_RENDERFORMAT:
	case WM_RENDERALLFORMATS:
	case WM_DESTROYCLIPBOARD:
	case WM_DRAWCLIPBOARD:
		break;
	case WM_INITMENU:
		hMenu = GetMenu(hBox);  /* Gray menu if no clipboard bitmap */
		mf = (WORD)(IsClipboardFormatAvailable(CF_BITMAP) ? MF_ENABLED : 
				MF_GRAYED);
		EnableMenuItem(hMenu, BOX_PASTE, mf);
		EnableMenuItem(hMenu, FONT_PASTE, mf);
		break;

	/* For each of following mouse window messages, wParam contains
	** bits indicating whether or not various virtual keys are down,
	** and lParam is a POINT containing the mouse coordinates.   The
	** keydown bits of wParam are:  MK_LBUTTON (set if Left Button is
	** down); MK_RBUTTON (set if Right Button is down); MK_SHIFT (set
	** if Shift Key is down); MK_ALTERNATE (set if Alt Key is down);
	** and MK_CONTROL (set if Control Key is down). */

	case WM_LBUTTONDOWN:
		((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);

		if (fStartRubberBand) {
			/* a green signal to rubberband a rectangle for the
			 * Fill menu command rectangle now has null dimensions.
			 * Snap the current mouse point to nearest grid
			 * intersection thus defining upper left corner of
			 * rectangle */

			if (PtInRect((LPRECT)&FontRect, pt)) {
				pt = SnapPointToGrid(pt);
				rectRubber.top    =  pt.y   *scale+ptBox.y+1;
				rectRubber.bottom = (pt.y+1)*scale+ptBox.y-2;
				rectRubber.left   =  pt.x   *scale+ptBox.x+1;
				rectRubber.right  = (pt.x+1)*scale+ptBox.x-2;

				hDst = InitialiseRubberBandingRect(hBox);
				DrawRubberBand(hDst, &rectRubber, R2_XORPEN);
			}
			else {
				fStartRubberBand = fRubberBanding = FALSE;
				ReleaseCapture();
			}
		}
		/* do operation depending upon current active command,
		 * but not if we just added/deleted a row/column. */
		if (!fJustZapped) {
			if (fStartRubberBand) {
				pt.x *= scale;
				pt.y *= scale;
				MouseInBox(hBox, message, pt);
			}
			else {
				((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
				MouseInBox(hBox, message, pt);
			}
		}

		break;

	case WM_LBUTTONUP:               /* Get other corner of rectangle */
		fJustZapped = FALSE;
		if (fRubberBanding) {
			/* if rubberbanding for the Fill menu command,
			 * terminate proceedings and clean up */
			DrawRubberBand(hDst, &rectRubber, R2_NOT);
			EndRubberBandingRect(hDst);
			if (cursor) {
				ReadRect();
			}
		}
		if (fCaptured ) {
			/* if cursor is + shaped, restore it to default */
			ReleaseCapture();
			SetCursor (hOldCursor);
		}
		break;

	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
		break;

	case WM_MOUSEMOVE:                      /* If mouse is down */

		((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);

		if ((fRubberBanding) && (wParam & MK_LBUTTON)) {
			/* if any of Fill menu commands is active
			** (AND the mouse key depressed) draw a rubberband
			** a rectangle with the mouse movements */

			/* get current square number */
			pt = SnapPointToGrid(pt);

			/* calculate grid for new square */
			BoxRect.top    =  pt.y   *scale+ptBox.y+1;
			BoxRect.bottom = (pt.y+1)*scale+ptBox.y-2;
			BoxRect.left   =  pt.x   *scale+ptBox.x+1;
			BoxRect.right  = (pt.x+1)*scale+ptBox.x-2;

			/* erase old mark */
			DrawRubberBand(hDst, &rectRubber, R2_NOT);

			/* limit rubber band to box */
			if (BoxRect.right > (LONG) (scale * wBox + ptBox.x))
				BoxRect.right = scale * wBox + ptBox.x;
			if (BoxRect.bottom > (LONG) (scale * kBox + ptBox.y))
				BoxRect.bottom = scale * kBox + ptBox.y;
			if (BoxRect.top < 0)
				BoxRect.top = 1;
			if (BoxRect.left < 0)
				BoxRect.left = 1;

			if (ptA.x == pt.x) {
				rectRubber.right  = BoxRect.right;
				rectRubber.left   = BoxRect.left;
			}
			if (ptA.y == pt.y) {
				rectRubber.bottom = BoxRect.bottom;
				rectRubber.top    = BoxRect.top;
			}

			/* almost an IntersectRect */
			if (ptA.x >= pt.x)
				rectRubber.left   = BoxRect.left;
			else
				rectRubber.right  = BoxRect.right;

			if (ptA.y >= pt.y)
				rectRubber.top    = BoxRect.top;
			else
				rectRubber.bottom = BoxRect.bottom;

			/* Draw new mark */
			DrawRubberBand(hDst, &rectRubber, R2_XORPEN);
		}
		else {
			/* if not "Fill"ing(AND mouse key depressed,
			 * paint with the mouse movements */
			if ((wParam & MK_LBUTTON) && cursor == FALSE && 
			    fJustZapped == FALSE)
				MouseInBox(hBox, message, pt);
		}
		break;

	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
		break;

	default:

		/* Everything else comes here.  This call MUST exist
        ** in your window proc.  */

		return(DefWindowProc(hBox, message, wParam, lParam));
		break;
	}

	/* A window proc should always return something */
	return(0L);
}


/***************************** Public  Function ****************************\
*
* BOOL  APIENTRY AboutDlg(hDlg, message, wParam, lParam)
* HWND     hDlg;
* WORD message;
* WPARAM wParam;
* LPARAM lParam;
*
*
* Effects: none.
*
\***************************************************************************/
BOOL  APIENTRY 
AboutDlg(
	HWND   hDlg,
	WORD   message,
	WPARAM wParam,
	LPARAM lParam
	)
{
	UNREFERENCED_PARAMETER(lParam);

	switch (message) {
	case WM_INITDIALOG:
		break;

	case WM_COMMAND:
		EndDialog(hDlg, LOWORD(wParam));
		/* idok or idcancel */
		break;

	default:
		return FALSE;
		break;
	}
	return(TRUE);
}
