//	Zinc Application Framework - I_METDSP.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_stdio.hpp>
#if defined(__BCPLUSPLUS__) || defined(__TCPLUSPLUS__)
#	include <mem.h>
#endif
#include <z_stdlib.hpp>
#include <z_string.hpp>
#include <z_scrdsp.hpp>
#include <z_win.hpp>
#include <z_utils.hpp>

#define ZAF_SCREEN_DISPLAY_INFO
#include "gbl_def.cpp"

bool _devicePainting = false;

#if defined(ZAF_GRAPHICS_MODE)

// Include graphics defines.
#include <metawndo.h>

Byte shortDashes[] = {1, 1};
dashRcd newDash;
grafPort *screen;

#if defined(ZAF_UNICODE)
//#	include "u_metdsp.cpp"
#endif

extern "C"
{
	#include "meta/metfind.c"
}

static bool colorUsedTable[ZAF_MAXCOLORS];

static OSFillPattern ZAF_FARDATA _fillPatternData[] =
{
	{ 8, 8, 0, 0, 1, 1, 1, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },	// EMPTY_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// SOLID_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// ZAF_LINE_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 } },	// INTERLEAVE_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// LTSLASH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// SLASH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// BKSLASH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// LTBKSLASH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// HATCH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// XHATCH_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// WIDE_DOT_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },	// CLOSE_DOT_FILL
	{ 8, 8, 0, 0, 1, 1, 1, { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 } },	
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },
	{ 8, 8, 0, 0, 1, 1, 1, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }
};

static OSFont ZAF_FARDATA _fontData[] =
{
#if defined(ZAF_UNICODE)
	{ ZAF_NULLP(char), "UNICODE.FNT", 4136, 8, 13 },	// ZAF_FNT_SMALL
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 }
#else
	{ ZAF_NULLP(char), "ISO_SMAL.FNT", 3668, 8, 12 },	// ZAF_FNT_SMALL
	{ ZAF_NULLP(char), "ISO_SMAL.FNT", 3668, 8, 12 },	// ZAF_FNT_DIALOG
//	{ ZAF_NULLP(char), "ISO_DIAL.FNT", 4136, 8, 13 },	// ZAF_FNT_DIALOG
	{ ZAF_NULLP(char), "ISO_DIAL.FNT", 4136, 8, 13 },	// ZAF_FNT_APPLICATION
	{ ZAF_NULLP(char), "ISO_SYST.FNT", 4496, 9, 15 },	// ZAF_FNT_SYSTEM
	{ ZAF_NULLP(char), "EXTSYS16.FNT", 4806, 8, 14 },	// ZAF_FNT_FIXED
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 },
	{ ZAF_NULLP(char), "", 0, 0, 0 }
#endif
};

static OSLineStyle ZAF_FARDATA _lineStyleData[] = 
{
	0, // ZAF_LINE_SOLID	
	4  // ZAF_LINE_DOTTED
};

static OSMode ZAF_FARDATA _modeData[] =
{
	zREPz, // ZAF_MODE_COPY
	zXORz  // ZAF_MODE_XOR
};

static OSMono ZAF_FARDATA _monoData[] =
{
	ZAF_MONO_BLACK,
	ZAF_MONO_DIM,
	ZAF_MONO_NORMAL,
	ZAF_MONO_HIGH
};

static ZafPaletteStruct ZAF_FARDATA __xorPalette =
{
	ZAF_LINE_SOLID, ZAF_PTN_SOLID_FILL, ZAF_CLR_WHITE, ZAF_CLR_BLACK, ZAF_MONO_NULL, ZAF_MONO_NORMAL, ZAF_FNT_DIALOG
};

static ZafPaletteStruct ZAF_FARDATA __markedTextPalette =
{
	ZAF_LINE_SOLID, ZAF_PTN_SOLID_FILL, ZAF_CLR_WHITE, ZAF_CLR_BLUE, ZAF_MONO_NULL, ZAF_MONO_NORMAL, ZAF_FNT_DIALOG
};

ZafPaletteStruct *ZafScreenDisplay::markPalette = &__markedTextPalette;
ZafPaletteStruct *ZafScreenDisplay::xorPalette = &__xorPalette;

// ----- Translation Tables -------------------------------------------------

// These tables translate the Zinc-defined constants to the appropriate value 
// in MetaWINDOW.  

static int lineTranslationTable[ZAF_MAXLINES] =
{
	0, // ZAF_LINE_SOLID	
	1  // ZAF_LINE_DOTTED
};

static int modeTranslationTable[ZAF_MAXMODES] =
{
	0, // ZAF_MODE_COPY
	1  // ZAF_MODE_XOR	
};

// Zinc's defined patterns are MetaWINDOW pattern entries 8 and greater.
static int patternTranslationTable[ZAF_MAXPATTERNS] =
{
	1,	// SOLID_FILL--Use MetaWINDOW predefined solid fill pattern.
	11,	// INTERLEAVE_FILL
	0,	// SYSTEM_COLOR
	0,	// RGB_COLOR
	10,	// ZAF_LINE_FILL
	12,	// LTSLASH_FILL
	13,	// SLASH_FILL
	14,	// BKSLASH_FILL
	15,	// LTBKSLASH_FILL
	16,	// HATCH_FILL
	17,	// XHATCH_FILL
	18,	// WIDE_DOT_FILL
	19,	// CLOSE_DOT_FILL
	20,
	21
};

void SetPrimitiveClipRegion(const ZafRegionStruct &region);

// ----- ZafScreenDisplay ---------------------------------------------------

ZafScreenDisplay::ZafScreenDisplay(int &argc, char **argv) :
	ZafDisplay(), initialized(false)
{
	// If the user passed in a MetaWINDOW argument, use it to initialize the
	// graphics display. See README.DOS for sample command line arguments.
	int mode = FindUserDisplay(argc, argv);

	// If the user didn't pass in a valid mode argument, determine the best
	// mode and initialize the graphics display accordingly. These are not
	// the same arguments as may be passed in via the command line.
	if (!mode)
		mode = FindBestDisplay(1024, 768, 256);
	if (InitGraphics(mode) != 0)
		return;
	SetDisplay(GrafPg0);

	// Initialize tables.
	int i;
	for (i = 0; i < int(sizeof(_fontData) / sizeof(OSFont)); i++)
		fontTable[i] = _fontData[i];
	for (i = 0; i < int(sizeof(_lineStyleData) / sizeof(OSLineStyle)); i++)
		lineTable[i] = _lineStyleData[i];
	for (i = 0; i < int(sizeof(_modeData) / sizeof(OSMode)); i++)
		modeTable[i] = _modeData[i];
	for (i = 0; i < int(sizeof(_monoData) / sizeof(OSMono)); i++)
		monoTable[i] = _monoData[i];
	for (i = 0; i < int(sizeof(_fillPatternData) / sizeof(OSFillPattern)); i++)
		patternTable[i] = _fillPatternData[i];

	int loadedFont = 0;
	for (int logicalFont = 0; logicalFont < ZAF_MAXFONTS; logicalFont++)
	{
		// Use dialog font ptr for application font.
		if (logicalFont == ZAF_FNT_APPLICATION)
			fontTable[logicalFont].fontPtr = fontTable[ZAF_FNT_DIALOG].fontPtr;
		else
		{
			OSFont *currentFont = &fontTable[logicalFont];
			if (currentFont->fontName && *currentFont->fontName)
			{
#if defined(ZAF_UNICODE)
//???				loadedFont += WFontLoad(currentFont->fontName);
#else
				currentFont->fontPtr = new char[currentFont->size];
				if (ResrcLoad(currentFont->fontName, currentFont->fontPtr, currentFont->size) >= 0)
					loadedFont++;
				else
				{
					delete []currentFont->fontPtr;
					currentFont->fontPtr = ZAF_NULLP(char);
				}
#endif
			}
		}
	}
	if (loadedFont == 0)
	{
		SetDisplay(TextPg0);
		return;
	}

	// Initialize the device image array.
	for (i = 0; i < ZAF_MAXIMAGES; i++)
	{
		displayImage[i].image = ZAF_NULLP(ZafLogicalColor);
		displayImage[i].screen = displayImage[i].backup = ZAF_NULLP(grafPort);
		displayImage[i].region.left = displayImage[i].region.right = -1;
		displayImage[i].region.top = displayImage[i].region.bottom = -1;
		displayImage[i].region.coordinateType = ZAF_PIXEL;
	}

	// Initialize the MetaWINDOW display variables.
	virtualCount = 0;
	stopDevice = false;

	palette.lineStyle = ZAF_LINE_DEFAULT;
	palette.fillPattern = ZAF_PTN_DEFAULT;
	palette.colorForeground = ZAF_CLR_DEFAULT;
	palette.colorBackground = ZAF_CLR_DEFAULT;
	palette.monoForeground = ZAF_MONO_DEFAULT;
	palette.monoBackground = ZAF_MONO_DEFAULT;
	palette.font = ZAF_FNT_DEFAULT;

	rect screenSize;
	ScreenRect(&screenSize);
	lines = screenSize.Ymax;
	columns = screenSize.Xmax;
	masterClip.left = masterClip.top = 0;
	masterClip.right = columns - 1;
	masterClip.bottom = lines - 1;
	masterClip.coordinateType =
		clipRegion.coordinateType = ZAF_PIXEL;
	SetClipRegion(masterClip);
//	screenID = ID_ZAF_DIRECT;
	GetPort(&screen);

	// Initialize the ZafCoordinateStruct statics.
	preSpace = 2;
	postSpace = 2;
	miniNumeratorX = miniNumeratorY = 1;
	miniDenominatorX = miniDenominatorY = 10;
	pixelsPerInchX = screen->portMap->pixResX;
	pixelsPerInchY = screen->portMap->pixResY;

	// Reset some palette colors if in >= VGA mode.
	if (QueryColors() >= 14 && columns >= 639 && lines >= 479)
	{
		palData RGBData1[1] = { {52685U, 52685U, 52685U} };
		WritePalette(0, ZAF_CLR_LIGHTGRAY, ZAF_CLR_LIGHTGRAY, RGBData1);
		palData RGBData2[1] = { {37500U, 37500U, 37500U} };
		WritePalette(0, ZAF_CLR_DARKGRAY, ZAF_CLR_DARKGRAY, RGBData2);
/* START BLOCK COMMENT
**			palData RGBData3[1]= { {65535U, 65535U, 56504U} };
**			WritePalette(0, ZAF_CLR_YELLOW, ZAF_CLR_YELLOW, RGBData3);
END BLOCK COMMENT */
			palData RGBData4[1]= { {32000U, 32000U, 0U} };
			WritePalette(0, ZAF_CLR_BROWN, ZAF_CLR_BROWN, RGBData4);
			palData RGBData5[1]= { {0U, 32768U, 32768U} };
			WritePalette(0, ZAF_CLR_CYAN, ZAF_CLR_CYAN, RGBData5);
	}

	// Ensure that ZAF_CLR_WHITE entry is white.  MetaWINDOW normally makes
	// the last palette entry white.  We want a specific entry to always be white.
	palData whiteData[1]= { {65535U, 65535U, 65535U} };
	WritePalette(0, ZAF_CLR_WHITE, ZAF_CLR_WHITE, whiteData);

	// Initialize colorUsedTable.  1st 32 colors are "in use" by default.
	for (i = 0; i < ZAF_MAXCOLORS; ++i)
		colorUsedTable[i] = i < ZAF_CLR_USER_FIRST ? true : false;

	// Initialize colorTable.  It's a direct mapping.
	for (i = 0; i < ZAF_MAXCOLORS; ++i)
		colorTable[i] = i;

	// Set up fill patterns.  MetaWINDOW fill patterns 0-7 are reserved.
	// Use pattern entries 8 and greater.
	for (int j = 0; j < (32 - 8) && j < ZAF_MAXPATTERNS; j++)
		DefinePattern(j + 8, (patRcd *)&patternTable[j]);

	// Redefine dash style used for focus rect.
	newDash.dashSize = 2;
	newDash.dashList = shortDashes;
	DefineDash(4, &newDash);

	// Reset the font information.
	PenNormal();
	TextAlign(alignLeft, alignTop);
	TextFace(cProportional + cNormal);

	// Set initial display information.
	SetForeground(ZAF_CLR_WHITE);
	SetBackground(ZAF_CLR_BLACK);
	SetMonoForeground(ZAF_MONO_NORMAL);
	SetMonoBackground(ZAF_MONO_BLACK);
	SetFillPattern(ZAF_PTN_SOLID_FILL);
	SetFont(ZAF_FNT_DIALOG);
	SetMode(ZAF_MODE_COPY);
	SetLineStyle(ZAF_LINE_SOLID);

	ZafRegionStruct sizeRegion = TextSize(ZAF_ITEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
	cellWidth = (sizeRegion.Width() + 19) / 39;
	cellHeight = (sizeRegion.Height() * 15 + 6) / 13 + 2 * ZAF_TEXT_MARGIN_SIZE +
		2 * ZAF_BORDER_SIZE + preSpace + postSpace;

	miniNumeratorX = 1;
	miniDenominatorX = 10;
	miniNumeratorY = 1;
	miniDenominatorY = 10;
	pixelsPerInchX = 72;
	pixelsPerInchY = 72;

	initialized = true;
}

ZafScreenDisplay::~ZafScreenDisplay(void)
{
	// Restore the display if it was initialized correctly.
	if (initialized)
	{
		SetDisplay(TextPg0);

		for (int logicalFont = 0; logicalFont < ZAF_MAXFONTS; logicalFont++)
			DestroyFont(logicalFont);
		for (int i = 0; i < ZAF_MAXIMAGES; i++)
		{
			if (displayImage[i].image)
				delete []displayImage[i].image;
			if (displayImage[i].screen)
				DestroyBitmap(displayImage[i].screen);
			if (displayImage[i].backup)
				DestroyBitmap(displayImage[i].backup);
		}

		StopGraphics();
	}
}

// --- Bitmap ---------------------------------------------------------------

ZafError ZafScreenDisplay::Bitmap(ZafCoordinate column, ZafCoordinate line, ZafBitmapStruct &bitmap)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Make sure there is a valid image
	grafPort *colorBitmap = 0, *monoBitmap = 0;
	if (!bitmap.handle || !bitmap.mask)
		ConvertToOSBitmap(bitmap);
	if (bitmap.handle)
		colorBitmap = bitmap.handle;
	if (bitmap.mask)
		monoBitmap = bitmap.mask;
	if (!colorBitmap)
		return (ZAF_ERROR_VALUE_MISSING);
	
	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, pixelColumn - 1, pixelLine - 1);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	// Save the user's settings so can restore them when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();
	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();

	// Draw the bitmap on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		// See the algorithm description in ConvertToOSBitmap(), below.
		if (monoBitmap)
		{
			grafPort *screenPort, *offscrPort;
			GetPort(&screenPort);
			rect scrRect, bmpRect;
			SetRect(&scrRect, pixelColumn, pixelLine, pixelColumn + bitmap.width, pixelLine + bitmap.height);
			SetRect(&bmpRect, 0, 0, bitmap.width, bitmap.height);

			// Create the offscreen port.  This also sets that port as 
			// the current port.
			offscrPort = CreateBitmap( cMEMORY, bitmap.width, bitmap.height );

			// Copy screen contents.
			CopyBlit(screenPort, offscrPort, &scrRect, &bmpRect);

			// Draw the bitmap to the offscreen port.
			RasterOp(zORz);
			PenColor(White);
			BackColor(Black);
			CopyBlit((grafPort *)monoBitmap, offscrPort, &bmpRect, &bmpRect);
			RasterOp(zXORz);
			CopyBlit((grafPort *)colorBitmap, offscrPort, &bmpRect, &bmpRect);

			SetPort(screenPort);
			CopyBlit(offscrPort, screenPort, &bmpRect, &scrRect);
			DestroyBitmap(offscrPort);
		}
		else if (colorBitmap)
		{
			rect dstRect, srcRect;
			SetRect(&dstRect, pixelColumn, pixelLine, pixelColumn + bitmap.width, pixelLine + bitmap.height);
			SetRect(&srcRect, 0, 0, bitmap.width, bitmap.height);
			grafPort *screenPort;
			GetPort(&screenPort);
			PenColor(White);
			BackColor(Black);
			RasterOp(zXORz);
			CopyBlit((grafPort *)colorBitmap, screenPort, &srcRect, &dstRect);
		}
	}

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);
	SetPrimitiveClipRegion(saveClip);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToOSBitmap(ZafBitmapStruct &bitmap)
{
	// Algorithm:  Create a mono bitmap.  Pixels that are to be transparent
	// are represented by a black pixel.  Pixels that are to be drawn are
	// represented by a white pixel.  Then create a color bitmap.  Pixels that
	// are to be transparent are represented by a black pixel.  Pixels that
	// are to be drawn are inverted (XOR'ed with WHITE).  When drawing the
	// image, first the mono bitmap is OR'ed onto the screen.  Since anything
	// OR'ed with black is the original anything and anything OR'ed with white
	// is white, we draw white pixels only where the image is actually supposed
	// to draw.  Then we XOR the color bitmap onto the mono bitmap.  Since those
	// pixels that weren't supposed to draw are black, and anything XOR'ed with 
	// black is the original anything, those parts aren't affected.  The pixels
	// that are supposed to draw have been inverted in the color bitmap, so when 
	// they are XOR'ed with the white pixels drawn by the mono bitmap, they are
	// inverted again, back to their original color.

#	if !defined(DOS16PP)

	if (!bitmap.array)
		return (ZAF_ERROR_INVALID_SOURCE);

	if (bitmap.StaticHandle() && bitmap.handle)
		return (ZAF_ERROR_INVALID_TARGET);

	if (bitmap.handle || bitmap.mask)
		DestroyOSBitmap(bitmap);

	grafPort *screenPort;
	GetPort(&screenPort);

	// Create the bitmap handles.
	bitmap.handle = CreateBitmap(cMEMORY, bitmap.width, bitmap.height);
	if (!bitmap.handle)
		return (ZAF_ERROR_INVALID);
	bitmap.mask = CreateBitmap(cMEMORY, bitmap.width, bitmap.height);
	if (!bitmap.mask)
	{
		DestroyBitmap(bitmap.handle);
		bitmap.handle = ZAF_NULLP(grafPort);
		return (ZAF_ERROR_INVALID);
	}

	// Create local variables for convenience.
	grafPort *colorPort = bitmap.handle;
	grafPort *monoPort = bitmap.mask;
	int bitmapWidth = bitmap.width;
	int bitmapHeight = bitmap.height;

	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();

	rect fillRect;
	SetRect(&fillRect, 0, 0, bitmapWidth, bitmapHeight);
	SetPort(monoPort);
	PenColor(White);
	FillRect(&fillRect, 1);
	SetPort(colorPort);
	PenColor(White);
	FillRect(&fillRect, 1);

	const ZafLogicalColor *bitmapPtr = bitmap.array;

	int x, y;
	RasterOp(zXORz);
	int XORRasterOp = true;
	for (y = 0; y < bitmapHeight; ++y)
		for (x = 0; x < bitmapWidth; ++x)
		{
			if (*bitmapPtr == ZAF_CLR_BACKGROUND)
			{
				if (XORRasterOp)
				{
					RasterOp(zREPz);
					XORRasterOp = false;
				}
				PenColor(Black);
				SetPixel(x, y);
			}
			else
			{
				// Invert colors so can OR onto white monoBitmap when drawing.
				if (!XORRasterOp)
				{
					RasterOp(zXORz);
					XORRasterOp = true;
				}
				PenColor(*bitmapPtr);
				SetPixel(x, y);
			}
			++bitmapPtr;
		}

	bitmapPtr = bitmap.array;
	SetPort(monoPort);
	RasterOp(zREPz);
	PenColor(Black);
	for (y = 0; y < bitmapHeight; ++y)
		for (x = 0; x < bitmapWidth; ++x)
		{
			if (*bitmapPtr == ZAF_CLR_BACKGROUND)
				SetPixel(x, y);
			++bitmapPtr;
		}
	SetPort(screenPort);

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);

	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

ZafError ZafScreenDisplay::ConvertToZafBitmap(ZafBitmapStruct &bitmap)
{
#	if !defined(DOS16PP)
	// Make sure there is a valid Handle.
	if (bitmap.StaticArray())
		return (ZAF_ERROR_INVALID_TARGET);

	if (!bitmap.handle)
		return (ZAF_ERROR_INVALID_SOURCE);

	// For convenience use local variables.
	grafPort *colorPort = bitmap.handle;
	grafPort *monoPort = bitmap.mask;
	grafPort *screenPort;
	GetPort(&screenPort);

	// Get the width and height.
	bitmap.width = colorPort->portMap->pixWidth;
	bitmap.height = colorPort->portMap->pixHeight;

	int bitmapWidth = bitmap.width;
	int bitmapHeight = bitmap.height;

	// Create temporary bitmap to assemble the actual image.
	grafPort *tempPort = CreateBitmap(cMEMORY, bitmapWidth, bitmapHeight);
	rect copyRect;
	SetRect(&copyRect, 0, 0, bitmapWidth, bitmapHeight);

	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();
 
	// Assemble the actual image from the mono and color bitmaps.
	// See the algorithm description in ConvertToOSBitmap() above.
	if (bitmap.mask)
	{
		RasterOp(zREPz);
		PenColor(White);
		BackColor(Black);
		CopyBlit(monoPort, tempPort, &copyRect, &copyRect);
		RasterOp(zXORz);
		CopyBlit(colorPort, tempPort, &copyRect, &copyRect);
	}
	else if (bitmap.handle)
	{
		PenColor(White);
		BackColor(Black);
		RasterOp(zXORz);
		CopyBlit(colorPort, tempPort, &copyRect, &copyRect);
	}
	
	// Allocate buffer if necessary and then clear it.
	int imageSize = bitmapWidth * bitmapHeight;
	if (!bitmap.array)
		bitmap.array = new ZafLogicalColor[imageSize];
	memset(bitmap.array, ZAF_CLR_BACKGROUND, imageSize);

	// Now convert the handle.  If a pixel in the mono bitmap is white, then
	// that pixel was to be drawn, so get it from the actual image in tempPort.
	SetPort(monoPort);
	for (int y = 0; y < bitmapHeight; y++)
		for (int x = 0; x < bitmapWidth; x++)
		{
			// Do as much calculation here as possible
			int offset = y * bitmapWidth + x;
			if (GetPixel(x, y) == White) // WHITE means the pixel was drawn.
			{
				SetPort(tempPort);
				(bitmap.array)[offset] = GetPixel(x, y);
				SetPort(monoPort);
			}
		}

	if (tempPort)
		DestroyBitmap(tempPort);

	SetPort(screenPort);

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);

	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

ZafError ZafScreenDisplay::DestroyOSBitmap(ZafBitmapStruct &bitmap)
{
#	if !defined(DOS16PP)
	if (bitmap.handle)
	{
		DestroyBitmap(bitmap.handle);
		bitmap.handle = ZAF_NULLP(grafPort);
	}
	if (bitmap.mask)
	{
		DestroyBitmap(bitmap.mask);
		bitmap.mask = ZAF_NULLP(grafPort);
	}
	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

// --- Icon -----------------------------------------------------------------

ZafError ZafScreenDisplay::ConvertToOSIcon(ZafIconStruct &icon)
{
	// Algorithm:  Create a mono icon.  Pixels that are to be transparent
	// are represented by a black pixel.  Pixels that are to be drawn are
	// represented by a white pixel.  Then create a color icon.  Pixels that
	// are to be transparent are represented by a black pixel.  Pixels that
	// are to be drawn are inverted (XOR'ed with WHITE).  When drawing the
	// image, first the mono icon is OR'ed onto the screen.  Since anything
	// OR'ed with black is the original anything and anything OR'ed with white
	// is white, we draw white pixels only where the image is actually supposed
	// to draw.  Then we XOR the color icon onto the mono icon.  Since those
	// pixels that weren't supposed to draw are black, and anything XOR'ed with 
	// black is the original anything, those parts aren't affected.  The pixels
	// that are supposed to draw have been inverted in the color icon, so when 
	// they are XOR'ed with the white pixels drawn by the mono icon, they are
	// inverted again, back to their original color.

#	if !defined(DOS16PP)
	if (!icon.array)
		return (ZAF_ERROR_INVALID_SOURCE);

	if (icon.StaticHandle() && icon.handle)
		return (ZAF_ERROR_INVALID_TARGET);

	if (icon.handle || icon.mask)
		DestroyOSIcon(icon);

	grafPort *screenPort;
	GetPort(&screenPort);

	// Create the icon handles.
	if (!icon.handle)
		icon.handle = CreateBitmap(cMEMORY, icon.width, icon.height);
	if (!icon.handle)
		return (ZAF_ERROR_INVALID);
	if (!icon.mask)
		icon.mask = CreateBitmap(cMEMORY, icon.width, icon.height);
	if (!icon.mask)
	{
		DestroyBitmap(icon.handle);
		icon.handle = ZAF_NULLP(grafPort);
		return (ZAF_ERROR_INVALID);
	}

	// Assign local variables for convenience.
	grafPort *colorPort = icon.handle;
	grafPort *monoPort = icon.mask;
	int iconWidth = icon.width;
	int iconHeight = icon.height;

	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();

	rect fillRect;
	SetRect(&fillRect, 0, 0, iconWidth, iconHeight);
	SetPort(monoPort);
	PenColor(White);
	FillRect(&fillRect, 1);
	SetPort(colorPort);
	PenColor(White);
	FillRect(&fillRect, 1);

	const ZafLogicalColor *iconPtr = icon.array;

	int x, y;
	RasterOp(zXORz);
	int XORRasterOp = true;
	for (y = 0; y < iconHeight; ++y)
		for (x = 0; x < iconWidth; ++x)
		{
			if (*iconPtr == ZAF_CLR_BACKGROUND)
			{
				if (XORRasterOp)
				{
					RasterOp(zREPz);
					XORRasterOp = false;
				}
				PenColor(Black);
				SetPixel(x, y);
			}
			else
			{
				// Invert colors so can OR onto white monoBitmap when drawing.
				if (!XORRasterOp)
				{
					RasterOp(zXORz);
					XORRasterOp = true;
				}
				PenColor(*iconPtr);
				SetPixel(x, y);
			}
			++iconPtr;
		}

	iconPtr = icon.array;
	SetPort(monoPort);
	RasterOp(zREPz);
	PenColor(Black);
	for (y = 0; y < iconHeight; ++y)
		for (x = 0; x < iconWidth; ++x)
		{
			if (*iconPtr == ZAF_CLR_BACKGROUND)
				SetPixel(x, y);
			++iconPtr;
		}

	SetPort(screenPort);

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);

	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

ZafError ZafScreenDisplay::ConvertToZafIcon(ZafIconStruct &icon)
{
#	if !defined(DOS16PP)
	// Make sure there is a valid Handle.
	if (!icon.handle)
		return (ZAF_ERROR_INVALID_SOURCE);

	if (icon.StaticArray())
		return (ZAF_ERROR_INVALID_TARGET);

	// For convenience use local pointers.
	grafPort *colorPort = icon.handle;
	grafPort *monoPort = icon.mask;
	grafPort *screenPort;
	GetPort(&screenPort);

	// Get the width and height.
	icon.width = colorPort->portMap->pixWidth;
	icon.height = colorPort->portMap->pixHeight;

	int iconWidth = icon.width;
	int iconHeight = icon.height;

	// Create temporary icon to assemble the actual image.
	grafPort *tempPort = CreateBitmap(cMEMORY, iconWidth, iconHeight);
	rect copyRect;
	SetRect(&copyRect, 0, 0, iconWidth, iconHeight);

	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();
 
	// Assemble the actual image from the mono and color icons.
	// See the algorithm description in ConvertToOSBitmap() above.
	if (icon.mask)
	{
		RasterOp(zREPz);
		PenColor(White);
		BackColor(Black);
		CopyBlit(monoPort, tempPort, &copyRect, &copyRect);
		RasterOp(zXORz);
		CopyBlit(colorPort, tempPort, &copyRect, &copyRect);
	}
	else if (icon.handle)
	{
		PenColor(White);
		BackColor(Black);
		RasterOp(zXORz);
		CopyBlit(colorPort, tempPort, &copyRect, &copyRect);
	}
	
	// Allocate buffer if necessary and then clear it.
	int imageSize = iconWidth * iconHeight;
	if (!icon.array)
		icon.array = new ZafLogicalColor[imageSize];
	memset(icon.array, ZAF_CLR_BACKGROUND, imageSize);

	// Now convert the handle.  If a pixel in the mono icon is white, then
	// that pixel was to be drawn, so get it from the actual image in tempPort.
	SetPort(monoPort);
	for (int y = 0; y < iconHeight; y++)
		for (int x = 0; x < iconWidth; x++)
		{
			// Do as much calculation here as possible
			int offset = y * iconWidth + x;
			if (GetPixel(x, y) == White) // WHITE means the pixel was drawn.
			{
				SetPort(tempPort);
				(icon.array)[offset] = GetPixel(x, y);
				SetPort(monoPort);
			}
		}

	if (tempPort)
		DestroyBitmap(tempPort);

	SetPort(screenPort);

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);

	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

ZafError ZafScreenDisplay::DestroyOSIcon(ZafIconStruct &icon)
{
#	if !defined(DOS16PP)
	if (icon.handle)
	{
		DestroyBitmap(icon.handle);
		icon.handle = ZAF_NULLP(grafPort);
	}
	if (icon.mask)
	{
		DestroyBitmap(icon.mask);
		icon.mask = ZAF_NULLP(grafPort);
	}
	return (ZAF_ERROR_NONE);
#	else
	return (ZAF_ERROR_INVALID);
#	endif
}

ZafError ZafScreenDisplay::Icon(ZafCoordinate column, ZafCoordinate line, ZafIconStruct &icon)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Make sure there is a valid image
	grafPort *colorBitmap = 0, *monoBitmap = 0;
	if (!icon.handle || !icon.mask)
		ConvertToOSIcon(icon);
	if (icon.handle)
		colorBitmap = icon.handle;
	if (icon.mask)
		monoBitmap = icon.mask;
	if (!colorBitmap)
		return (ZAF_ERROR_VALUE_MISSING);

	// Assign the icon to the region structure.
	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, pixelColumn - 1, pixelLine - 1);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	// Save the user's settings so can restore them when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();
	ZafLogicalColor saveColor = Foreground();
	ZafLogicalMode saveMode = Mode();

	// Draw the icon on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		// See the algorithm description in ConvertToOSBitmap(), below.
		if (monoBitmap)
		{
			grafPort *screenPort, *offscrPort;
			GetPort(&screenPort);
			rect scrRect, bmpRect;
			SetRect(&scrRect, pixelColumn, pixelLine, pixelColumn + icon.width, pixelLine + icon.height);
			SetRect(&bmpRect, 0, 0, icon.width, icon.height);

			// Create the offscreen port.  This also sets that port as 
			// the current port.
			offscrPort = CreateBitmap( cMEMORY, icon.width, icon.height );

			// Copy screen contents.
			CopyBlit(screenPort, offscrPort, &scrRect, &bmpRect);

			// Draw the icon to the offscreen port.
			RasterOp(zORz);
			PenColor(White);
			BackColor(Black);
			CopyBlit((grafPort *)monoBitmap, offscrPort, &bmpRect, &bmpRect);
			RasterOp(zXORz);
			CopyBlit((grafPort *)colorBitmap, offscrPort, &bmpRect, &bmpRect);

			SetPort(screenPort);
			CopyBlit(offscrPort, screenPort, &bmpRect, &scrRect);
			DestroyBitmap(offscrPort);
		}
		else if (colorBitmap)
		{
			rect dstRect, srcRect;
			SetRect(&dstRect, pixelColumn, pixelLine, pixelColumn + icon.width, pixelLine + icon.height);
			SetRect(&srcRect, 0, 0, icon.width, icon.height);
			grafPort *screenPort;
			GetPort(&screenPort);
			PenColor(White);
			BackColor(Black);
			RasterOp(zXORz);
			CopyBlit((grafPort *)colorBitmap, screenPort, &srcRect, &dstRect);
		}
	}

	// Restore user's settings.
	SetForeground(saveColor);
	SetMode(saveMode);
	SetPrimitiveClipRegion(saveClip);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

// --- Mouse -----------------------------------------------------------------

ZafError ZafScreenDisplay::Mouse(ZafCoordinate, ZafCoordinate, ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToOSMouse(ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToZafMouse(ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyOSMouse(ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

// --- Text -----------------------------------------------------------------

ZafError ZafScreenDisplay::Text(ZafCoordinate left, ZafCoordinate top,
	const ZafIChar *text, int length, int hotKeyIndex, bool fill)
{
	// Get the text size.
	ZafRegionStruct textSize = TextSize(text, length);

	// Draw the text (textSize.right and textSize.bottom are width & height).
	return (Text(left, top,
		left + textSize.right - 1, top + textSize.bottom - 1, text, length,
		ZAF_HZ_LEFT, ZAF_VT_TOP, hotKeyIndex, fill));
}

ZafError ZafScreenDisplay::Text(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	const ZafIChar *text, int length, ZafHzJustify hzJustify,
	ZafVtJustify vtJustify, int hotKeyIndex, bool fill)
{
	// Make sure we have a valid string.
	if (!text || !text[0])
		return (ZAF_ERROR_INVALID);

	int pixelLeft = (int)(ConvertXValue(left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Set up the fill line.
	static ZafIChar ZAF_FARDATA fillLine[256];
	if (length < 0)
		length = strlen(text);
	if (length > 255)
		length = 255;

	strncpy(fillLine, text, length);
	fillLine[length] = '\0';

	// Determine the hot-key values and shrink the string to remove ZAF_HOTMARK.
/* START BLOCK COMMENT
**		ZafIChar *hotKey = ZAF_NULLP(ZafIChar);
**		int hotKeyLeft, hotKeyRight;
**		if (!ignoreUnderscore)
**		{
**			ZafIChar hotKeyBuf[2];
**			ZafIChar *hotChar = fillLine;
**			for (int i=0; i < length; i++, hotChar++)
**			{
**				if ((*hotChar == ZAF_HOTMARK && hotChar[1] == ZAF_HOTMARK) ||
**					(*hotChar == '~' && hotChar[1] == '~'))
**					memmove(hotChar, hotChar + 1, strlen(hotChar) * sizeof(ZafIChar));
**				else if (*hotChar == ZAF_HOTMARK || *hotChar == '~')
**				{
**					hotKeyBuf[0] = hotChar[1];
**					hotKeyBuf[1] =	*hotChar = '\0';
**					hotKeyLeft = TextSize(fillLine).Width();
**					memmove(hotChar, hotChar + 1, (strlen(hotChar + 1) + 1) * sizeof(ZafIChar));
**					hotKey = hotChar;
**					int markLen = TextSize(hotKeyBuf).Width();
**					hotKeyRight = hotKeyLeft + markLen - 1;
**				}
**			}
**		}
END BLOCK COMMENT */
	int hotKeyLeft = 0, hotKeyRight = 0;
	if (hotKeyIndex >= 0)
	{
		ZafIChar hotKeyBuf[2];
		ZafIChar *hotChar = fillLine;
		hotKeyBuf[0] = hotChar[hotKeyIndex];
		hotKeyBuf[1] =	hotChar[hotKeyIndex] = '\0';
		hotKeyLeft = hotKeyIndex == 0 ? 0 : TextSize(fillLine).Width();
		hotChar[hotKeyIndex] = hotKeyBuf[0];
		hotKeyRight = hotKeyLeft + TextSize(hotKeyBuf).Width() - 1;
	}
	// Check for special characters.
	strrepc(fillLine, '\t', ' ');
	strrepc(fillLine, '\r', ' ');
	strrepc(fillLine, '\n', ' ');

	// Assign the rectangle to the region structure.
	ZafRegionStruct textSize = TextSize(text), cRegion = ClipRegion();
	int pixelWidth = (int)(ConvertXValue(textSize.Width(), coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelHeight = (int)(ConvertYValue(textSize.Height(), coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, columns, lines);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	int leftText = pixelLeft;
	if (hzJustify == ZAF_HZ_RIGHT)
		leftText = pixelRight - pixelWidth;
	else if (hzJustify == ZAF_HZ_CENTER)
	{
		leftText = ((pixelRight + pixelLeft) / 2) - (pixelWidth / 2);
		if (pixelWidth <= (pixelRight - pixelLeft + 1) && leftText < pixelLeft)
			leftText = pixelLeft;
	}

	// Adjust hotKey offsets.
	hotKeyLeft += leftText;
	hotKeyRight += leftText;

	int topText = pixelTop;
	if (vtJustify == ZAF_VT_BOTTOM)
		topText = pixelBottom - pixelHeight;
	else if (vtJustify == ZAF_VT_CENTER)
	{
		topText = ((pixelBottom + pixelTop) / 2) - (pixelHeight / 2);
		if (pixelHeight <= (pixelBottom - pixelTop + 1) && topText < pixelTop)
			topText = pixelTop;
	}

	// Save the user's clip region so can restore it when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();
	ZafLogicalMode saveMode = Mode();
	// Draw the text.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		rect rectangle;
		if (fill)
		{
			// Fill with background color.
			PenColor(palette.colorBackground);
			SetRect(&rectangle, leftText, topText, leftText + pixelWidth, topText + pixelHeight);
			PaintRect(&rectangle);

			// Restore foreground color.
			PenColor(palette.colorForeground);
		}

		MoveTo(leftText, topText);

		// Cause only the text, not the background to draw.
		if (saveMode == ZAF_MODE_XOR)
			RasterOp(xXORx);
		else if (saveMode == ZAF_MODE_COPY)
			RasterOp(xREPx);
#if defined(ZAF_UNICODE)
//???			WDrawString(fillLine);
#else
//		DrawString(fillLine);
#define MetaMaxStringWidth 127
		int val1;
		int val2 = strlen(fillLine);
		char saveChar;
		for (val1 = 0; val1 < val2; val1 += MetaMaxStringWidth)
		{
			if (val1 + MetaMaxStringWidth < val2)
			{
				saveChar = fillLine[val1 + MetaMaxStringWidth];
				fillLine[val1 + MetaMaxStringWidth] = 0;
			}
			DrawString(&fillLine[val1]);
			if (val1 + MetaMaxStringWidth < val2)
				fillLine[val1 + MetaMaxStringWidth] = saveChar;
		}
#endif
		SetMode(saveMode);
		if (hotKeyIndex >= 0)
		{
			MoveTo(hotKeyLeft, topText + pixelHeight - 1);
			LineTo(hotKeyRight, topText + pixelHeight - 1);
		}
	}

	// Restore the user's settings.
	SetPrimitiveClipRegion(saveClip);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafRegionStruct ZafScreenDisplay::TextSize(const ZafIChar *string, int length)
{
	ZafRegionStruct size;
	size.Assign(1, 1, 0, 0, ZAF_PIXEL);

	if (!string || !string[0])
		return (size);

	size.bottom = fontTable[palette.font].maxHeight;

	// StringWidth apparently works for up to 127 characters only.
	int bufferLength = 127;
	int textWidth = 0;
	int stringLength = length < 0 ? strlen(string) : length;
	if (stringLength > bufferLength || length > 0)
	{
		ZafIChar *stringPtr = new ZafIChar[stringLength + 1];
		ZafIChar *origPtr = stringPtr;
		stringPtr[stringLength] = '\0';
		strncpy(stringPtr, string, stringLength);
		ZafIChar saveChar;
		int tempLength = ZafMin(strlen(stringPtr), bufferLength);
		do
		{
			saveChar = stringPtr[tempLength];
			stringPtr[tempLength] = 0;
#if defined(ZAF_UNICODE)
//???			textWidth += WStringWidth(stringPtr);
#else
			textWidth += StringWidth((char *)stringPtr);
#endif
			stringPtr[tempLength] = saveChar;
			stringPtr += tempLength;
			tempLength = ZafMin(strlen(stringPtr), bufferLength);
		} while (*stringPtr);
		delete [] origPtr;
	}
	else
#if defined(ZAF_UNICODE)
//???		textWidth = WStringWidth(string);
#else
		textWidth = StringWidth((char *)string);
#endif

	for (int i=0; i < stringLength; i++)
	{
/* START BLOCK COMMENT
**			if (!ignoreUnderscore && (string[i] == ZAF_HOTMARK || string[i] == '~'))
**			{
**				ZafIChar hotKeyBuf[2];
**				hotKeyBuf[0] = string[i];
**				hotKeyBuf[1] = '\0';
**	#if defined(ZAF_UNICODE)
**				int hotMarkLen = WStringWidth(hotKeyBuf);
**	#else
**				int hotMarkLen = StringWidth(hotKeyBuf);
**	#endif
**				textWidth -= hotMarkLen;
**				if ((string[i] == ZAF_HOTMARK && string[i+1] == ZAF_HOTMARK) ||
**					(string[i] == '~' && string[i+1] == '~'))
**					i++;
**			}
END BLOCK COMMENT */
		if (string[i] == '\t')
		{
			int spaceLen = StringWidth(" ");
			int tabLen = StringWidth("\t");
			textWidth += spaceLen - tabLen;
		}
	}

	size.right = textWidth;

	// left and top stay 1 so that right and bottom contain the width & height.
	size.right = (int)(ConvertXValue(size.right, ZAF_PIXEL, coordinateType));
	size.bottom = (int)(ConvertYValue(size.bottom, ZAF_PIXEL, coordinateType));
	size.coordinateType = coordinateType;

	return (size);
}

// --- Primitives -----------------------------------------------------------

ZafError ZafScreenDisplay::Ellipse(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	float startAngle, float endAngle, int width, bool fill)
{
	int pixelLeft = (int)(ConvertXValue(left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(width == ZAF_HAIR_LINE ? 1 : 0);

	if (pixelRight - pixelLeft <= 0 || pixelBottom - pixelTop <= 0)
		return (ZAF_ERROR_VALUE_MISSING);

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, columns, lines);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	float angleWidth = endAngle - startAngle;
	ZafRegionStruct clip, saveClip = ClipRegion();

	if (pixelWidth > 0)
		PenSize(pixelWidth, pixelWidth);

	// Draw the ellipse on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		rect rectangle;

		if (angleWidth == 0.0 || angleWidth == 360.0)
		{
			// Draw an ellipse.
			if (fill)
			{
				// Fill with background color.
				PenColor(palette.colorBackground);
				SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
				PaintOval(&rectangle);
				if (mode == ZAF_MODE_COPY && pixelWidth == 0)
					FrameOval(&rectangle);

				// Restore foreground color.
				PenColor(palette.colorForeground);
			}
			if ((!fill || palette.colorBackground != palette.colorForeground) && pixelWidth)
			{
				SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
				FrameOval(&rectangle);
			}
		}
		else
		{
			// Draw an arc or sector.
			if (fill)
			{
				// Fill with background color.
				PenColor(palette.colorBackground);
				SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
				PaintArc(&rectangle, (int)(startAngle * 10), (int)(angleWidth * 10));
				if (mode == ZAF_MODE_COPY && pixelWidth == 0)
					FrameArc(&rectangle, (int)(startAngle * 10), (int)(angleWidth * 10));

				// Restore foreground color.
				PenColor(palette.colorForeground);
			}
			if ((!fill || palette.colorBackground != palette.colorForeground) && pixelWidth)
			{
				SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
				FrameArc(&rectangle, (int)(startAngle * 10), (int)(angleWidth * 10));
			}
		}
	}

	// Restore user's settings.
	SetPrimitiveClipRegion(saveClip);

	if (endDraw)
		EndDraw();

	PenSize(1, 1);

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Line(ZafCoordinate x1, ZafCoordinate y1, ZafCoordinate x2, ZafCoordinate y2, int width)
{
	int pixelX1 = (int)(ConvertXValue(x1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelY1 = (int)(ConvertYValue(y1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelX2 = (int)(ConvertXValue(x2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelY2 = (int)(ConvertYValue(y2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(width == ZAF_HAIR_LINE ? 1 : 0);

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, columns, lines);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	// Save the user's clip region so can restore it when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();

	if (pixelWidth > 0)
		PenSize(pixelWidth, pixelWidth);

	// Draw the line on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		MoveTo(pixelX1, pixelY1);
		LineTo(pixelX2, pixelY2);
	}

	// Restore user's settings.
	SetPrimitiveClipRegion(saveClip);
	PenSize(1, 1);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Pixel(ZafCoordinate column, ZafCoordinate line, ZafLogicalColor color,
	ZafLogicalColor mono)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, pixelColumn, pixelLine);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	// Save the user's clip region so can restore it when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();

	SetForeground(color);
	SetMonoForeground(mono);

	// Draw the pixel on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		SetPixel(pixelColumn, pixelLine);
	}

	// Restore the user's settings.
	SetPrimitiveClipRegion(saveClip);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Polygon(int numPoints, const ZafCoordinate *polygonPoints,
	int width, bool fill, bool close)
{
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(width == ZAF_HAIR_LINE ? 1 : 0);

	if (fill && !close && (polygonPoints[2 * numPoints - 2] != polygonPoints[0] ||
		polygonPoints[2 * numPoints - 1] != polygonPoints[1]))
			close = true;

	int realNumPoints = numPoints;
	if (close)
		++realNumPoints;

	// Assign the polygon to the region structure.
	int left = 0x0FFF, top = 0x0FFF, right = 0, bottom = 0;
	for (int i = 0; i < numPoints * 2; i += 2)
	{
		left = ZafMin(left, (int)(ConvertXValue(polygonPoints[i], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX));
		top = ZafMin(top, (int)(ConvertYValue(polygonPoints[i+1], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY));
		right = ZafMax(right, (int)(ConvertXValue(polygonPoints[i], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX));
		bottom = ZafMax(bottom, (int)(ConvertYValue(polygonPoints[i+1], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY));
	}

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, columns, lines);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	if (pixelWidth > 0)
		PenSize(pixelWidth, pixelWidth);

	// Save the user's clip region so can restore it when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();

	// Create a list of MetaWINDOW points.
	point *realPoints = new point[realNumPoints];
	for (int p = 0; p < numPoints; ++p)
	{
		realPoints[p].X = (short)(ConvertXValue(polygonPoints[2 * p], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
		realPoints[p].Y = (short)(ConvertYValue(polygonPoints[2 * p + 1], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	}

	if (close)
	{
		realPoints[numPoints].X = realPoints[0].X;
		realPoints[numPoints].Y = realPoints[0].Y;
	}

	// Draw the polygon on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if(regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		if (fill)
		{
			// Fill with background color.
			PenColor(palette.colorBackground);
			
			rect rectangle;
			SetRect(&rectangle, clip.left, clip.top, clip.right, clip.bottom);
			polyHead polyHDR;
			polyHDR.polyBgn = 0;
			polyHDR.polyEnd = (Word)(realNumPoints - 1);
			polyHDR.polyRect = rectangle;
			PaintPoly(1, &polyHDR, realPoints);
			FramePoly(1, &polyHDR, realPoints);

			// Restore foreground color.
			PenColor(palette.colorForeground);
		}

		if (!fill || palette.colorBackground != palette.colorForeground)
			PolyLine(realNumPoints, realPoints);
	}

	delete []realPoints;

	// Restore user's settings.
	SetPrimitiveClipRegion(saveClip);
	PenSize(1, 1);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Rectangle(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	int width, bool fill)
{
	int pixelLeft = (int)(ConvertXValue(left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(width == ZAF_HAIR_LINE ? 1 : 0);

	bool endDraw = false;
	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.Assign(0, 0, columns, lines);
		drawRegion.coordinateType = ZAF_PIXEL;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	rect rectangle;

	// Save the user's clip region so can restore it when done drawing.
	ZafRegionStruct clip, saveClip = ClipRegion();

	if (pixelWidth > 0)
		PenSize(pixelWidth, pixelWidth);

	// Draw the rectangle on the display.
	for (ZafRegionElement *regionElement = drawRegionList.First(); regionElement; regionElement = regionElement->Next())
		if (regionElement->region.Overlap(saveClip, clip))
	{
//		regionElement->region.Overlap(saveClip, clip);
		SetPrimitiveClipRegion(clip);

		if (fill)
		{
			// Swap foreground and background colors for fill, including 
			// patterns.
			PenColor(palette.colorBackground);
			BackColor(palette.colorForeground);
			SetRect(&rectangle, pixelLeft, pixelTop, pixelRight + 1, pixelBottom + 1);
			PaintRect(&rectangle);

			// Restore colors.
			PenColor(palette.colorForeground);
			BackColor(palette.colorBackground);
		}
		else if (pixelWidth < 0)
		{
			// Draws a dashed rectangle to indicate focus
			SetLineStyle(ZAF_LINE_DOTTED);
			SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
			FrameRect(&rectangle);
			SetLineStyle(ZAF_LINE_SOLID);
		}

		if ((!fill || palette.colorBackground != palette.colorForeground) && pixelWidth > 0)
		{
			SetRect(&rectangle, pixelLeft, pixelTop, pixelRight, pixelBottom);
			FrameRect(&rectangle);
		}
	}

	// Restore user's settings.
	SetPrimitiveClipRegion(saveClip);
	PenSize(1, 1);

	if (endDraw)
		EndDraw();

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::RectangleXORDiff(const ZafRegionStruct *oldRegion,
	const ZafRegionStruct *newRegion)
{
	// See if the regions match.
	if (oldRegion && newRegion &&
		oldRegion->left == newRegion->left && oldRegion->top == newRegion->top &&
		oldRegion->right == newRegion->right && oldRegion->bottom == newRegion->bottom)
		return (ZAF_ERROR_INVALID);

	ZafRegionStruct clip = ClipRegion();
	ZafPaletteStruct savePalette = Palette();

	// Hide the screen devices.
	ZafRegionStruct region;
/* START BLOCK COMMENT
**		if (!RegionInitialize(region, &clip,
**			ZafMin(oldRegion ? oldRegion->left : clip.left, newRegion ? newRegion->left : clip.left),
**			ZafMin(oldRegion ? oldRegion->top : clip.top, newRegion ? newRegion->top : clip.top),
**			ZafMax(oldRegion ? oldRegion->right : clip.right, newRegion ? newRegion->right : clip.right),
**			ZafMax(oldRegion ? oldRegion->bottom : clip.bottom, newRegion ? newRegion->bottom : clip.bottom)))
**			return (ZAF_ERROR_INVALID);
END BLOCK COMMENT */

//	UnvirtualizeRegion(region);

	BeginDraw(0, ID_ZAF_DIRECT, region, region);

	// Draw the XOR rectangles on the display.
	SetPalette(*xorPalette);
	SetMode(ZAF_MODE_XOR);
	rect rectangle;

	if (oldRegion)
	{
		SetRect(&rectangle, oldRegion->left, oldRegion->top, oldRegion->right, oldRegion->bottom);
		FrameRect(&rectangle);
	}
	if (newRegion)
	{
		SetRect(&rectangle, newRegion->left, newRegion->top, newRegion->right, newRegion->bottom);
		FrameRect(&rectangle);
	}

	// Update the screen.
	EndDraw();

	// Restore user's settings.
	SetPalette(savePalette);

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::RegionCopy(const ZafRegionStruct &oldRegion,
	int newColumn, int newLine)
{
	static char inCopy = 0;

	if (inCopy)
		return (ZAF_ERROR_NONE);

	inCopy++;

	bool endDraw = false;

	if (virtualCount >= 0)
	{
		endDraw = true;
		ZafRegionStruct drawRegion;
		drawRegion.left = ZafMin(oldRegion.left, newColumn);
		drawRegion.top = ZafMin(oldRegion.top, newLine);
		drawRegion.right = ZafMax(oldRegion.right, newColumn + (oldRegion.right - oldRegion.left));
		drawRegion.bottom = ZafMax(oldRegion.bottom, newLine + (oldRegion.bottom - oldRegion.top));
		drawRegion.coordinateType = oldRegion.coordinateType;
		BeginDraw(0, ID_ZAF_DIRECT, drawRegion, drawRegion);
	}

	rect oldRectangle;
	rect newRectangle;
	SetRect(&oldRectangle, oldRegion.left, oldRegion.top, oldRegion.right + 1,
		oldRegion.bottom + 1);
	SetRect(&newRectangle, newColumn, newLine,
		newColumn + (oldRegion.right - oldRegion.left) + 1,
		newLine + (oldRegion.bottom - oldRegion.top) + 1);
	grafPort *screenPort;
	GetPort(&screenPort);
	CopyBlit(screenPort, screenPort, &oldRectangle, &newRectangle);

	if (endDraw == true)
		EndDraw();

	inCopy--;

	return (ZAF_ERROR_NONE);
}

// --- Tables ---------------------------------------------------------------

ZafLogicalColor ZafScreenDisplay::AddColor(ZafLogicalColor index,
	ZafUInt8 red, ZafUInt8 green, ZafUInt8 blue)
{
	if (index != ZAF_CLR_NULL &&
		(index < 0 || index >= ZAF_MAXCOLORS))
		return (ZAF_CLR_NULL);

	int realIndex = index;
	if (realIndex == ZAF_CLR_NULL)
	{
		// Find next available color.
		int i = 0;
		while (colorUsedTable[i] && i < ZAF_MAXCOLORS)
			++i;
		if (i < ZAF_MAXCOLORS)
			realIndex = i;
		else
			return (ZAF_CLR_NULL);
	}

	// If that color is already in use, or the system doesn't support a color 
	// of that index, do nothing.
	if (colorUsedTable[realIndex] || realIndex > QueryColors())
		return (ZAF_CLR_NULL);

	palData RGBColor;
	RGBColor.palRed = red << 8;
	RGBColor.palGreen = green << 8;
	RGBColor.palBlue = blue << 8;

	WritePalette(0, realIndex, realIndex, &RGBColor);
	colorUsedTable[realIndex] = true;

	return (realIndex);
}

ZafLogicalFont ZafScreenDisplay::AddFont(ZafLogicalFont index, char *fontFamily,
	int, ZafFontWeight, ZafFontSlant, ZafFontPitch)
{
	if (index != ZAF_FNT_NULL &&
		(index < 0 || index >= ZAF_MAXFONTS))
		return (ZAF_FNT_NULL);

	int realIndex = index;
	if (realIndex == ZAF_FNT_NULL)
	{
		// Find next available font.
		int i = 0;
		while (fontTable[i].fontPtr && i < ZAF_MAXFONTS)
			++i;
		if (i < ZAF_MAXFONTS)
			realIndex = i;
		else
			return (ZAF_FNT_NULL);
	}

	// Get info for fontFamily (really a file name).
	dirRec fileInfo;
	int fileFound = ResrcQuery(fontFamily, &fileInfo, 1);

	if (!fileFound)
		return (ZAF_FNT_NULL);

	fontTable[realIndex].fontPtr = new char[(int)fileInfo.fileSize];
	if (ResrcLoad(fontFamily, fontTable[realIndex].fontPtr, (int)fileInfo.fileSize) < 0)
	{
		delete []fontTable[realIndex].fontPtr;
		fontTable[realIndex].fontPtr = ZAF_NULLP(char);
	}
	else
	{
		fontTable[realIndex].size = (int)fileInfo.fileSize;
		strcpy(fontTable[realIndex].fontName, fontFamily);
		fontRcd *fontHeader = (fontRcd *)fontTable[realIndex].fontPtr;
		fontTable[realIndex].maxWidth = fontHeader->chWidth;
		fontTable[realIndex].maxHeight = fontHeader->chHeight;
	}

	return (realIndex);
}

ZafError ZafScreenDisplay::DestroyColor(ZafLogicalColor index)
{
	if (index < 0 || index >= ZAF_MAXCOLORS)
		return (ZAF_ERROR_INVALID_INDEX);

	palData RGBColor;
	RGBColor.palRed = 0U;
	RGBColor.palGreen = 0U;
	RGBColor.palBlue = 0U;

	WritePalette(0, index, index, &RGBColor);
	colorUsedTable[index] = false;
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyFont(ZafLogicalFont index)
{
	if (index < 0 || index >= ZAF_MAXFONTS)
		return (ZAF_ERROR_INVALID_INDEX);

	OSFont *deleteFont = &fontTable[index];

	if (!deleteFont->fontPtr)
		return (ZAF_ERROR_INVALID);

	// Reset internal font so we don't hose MetaWINDOW.
	if (index == palette.font)
		SetFont(ZAF_FNT_NULL);

	bool needDelete = true;

	for (int i = 0; i < ZAF_MAXFONTS; ++i)
	{
		if (i != index && fontTable[i].fontPtr == deleteFont->fontPtr)
		{
			needDelete = false;
			break;
		}
	}

	if (needDelete)
		delete []deleteFont->fontPtr;

	deleteFont->fontPtr = ZAF_NULLP(char);
	deleteFont->fontName[0] = '\0';
	deleteFont->size = 0;
	deleteFont->maxWidth = deleteFont->maxHeight = 0;

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ColorInfo(ZafLogicalColor index, ZafUInt8 &red, ZafUInt8 &green, ZafUInt8 &blue)
{
	if (index < 0 || index >= ZAF_MAXCOLORS || !colorUsedTable[index])
		return (ZAF_ERROR_INVALID_INDEX);

	// Return the color information.
	palData RGBColor;
	ReadPalette(0, index, index, &RGBColor);

	red = RGBColor.palRed >> 8;
	green = RGBColor.palGreen >> 8;
	blue = RGBColor.palBlue >> 8;

	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::FontInfo(ZafLogicalFont index, char *fontFamily, int bufferSize,
	int *pointSize, ZafFontWeight *weight, ZafFontSlant *slant,
	ZafFontPitch *pitch, ZafCoordinate *averageCharWidth,
	ZafCoordinate *height, ZafCoordinate *leading)
{
	if (index < 0 || index >= ZAF_MAXFONTS || !fontTable[index].fontPtr)
		return (ZAF_ERROR_INVALID_INDEX);

	// Return the font information.
	if (fontFamily && fontTable[index].fontName)
		strncpy(fontFamily, fontTable[index].fontName, bufferSize);

	if (pointSize)
		*pointSize = fontTable[index].size;

	if (weight)
		*weight = ZAF_FNT_WEIGHT_NORMAL;

	if (slant)
		*slant = ZAF_FNT_SLANT_NORMAL;

	if (pitch)
		*pitch = ZAF_FNT_PITCH_DEFAULT;

	if (averageCharWidth)
		*averageCharWidth = 0; // Not yet implemented.

	if (height)
		*height = fontTable[index].maxHeight;

	if (leading)
		*leading = 1; // Not yet implemented.

	return (ZAF_ERROR_NONE);
}

// --- Get ------------------------------------------------------------------

ZafLogicalColor ZafScreenDisplay::Background(void) const
{
	return (palette.colorBackground);
}

ZafLogicalColor ZafScreenDisplay::MonoBackground(void) const
{
	return (palette.monoBackground);
}

ZafRegionStruct ZafScreenDisplay::ClipRegion(void) const
{
	return (clipRegion);
}

ZafLogicalFont ZafScreenDisplay::Font(void) const
{
	return (palette.font);
}

ZafLogicalColor ZafScreenDisplay::Foreground(void) const
{
	return (palette.colorForeground);
}

ZafLogicalColor ZafScreenDisplay::MonoForeground(void) const
{
	return (palette.monoForeground);
}

ZafLogicalLineStyle ZafScreenDisplay::LineStyle(void) const
{
	return (palette.lineStyle);
}

ZafLogicalMode ZafScreenDisplay::Mode(void) const
{
	return (mode);
}

ZafPaletteStruct ZafScreenDisplay::Palette(void) const
{
	return (palette);
}

ZafLogicalFillPattern ZafScreenDisplay::FillPattern(void) const
{
	return (palette.fillPattern);
}

// --- Set ------------------------------------------------------------------

ZafLogicalColor ZafScreenDisplay::SetBackground(ZafLogicalColor color)
{
	ZafLogicalColor oldBackground = palette.colorBackground;

	if (color >= 0 && color < ZAF_MAXCOLORS)
	{
		palette.colorBackground = color;
		BackColor(color);
	}
	else
		return (ZAF_CLR_NULL);

	return(oldBackground);
}

ZafRegionStruct ZafScreenDisplay::SetClipRegion(const ZafRegionStruct &region)
{
	// Check region against master clip region. Clipping must be within
	// the master clip region.
	ZafRegionStruct convertRegion = region.Region(ZAF_PIXEL);
	convertRegion.left = (int)((long)convertRegion.left * scaleNumerator / scaleDenominator + originX);
	convertRegion.top = (int)((long)convertRegion.top * scaleNumerator / scaleDenominator + originY);
	convertRegion.right = (int)((long)convertRegion.right * scaleNumerator / scaleDenominator + originX);
	convertRegion.bottom = (int)((long)convertRegion.bottom * scaleNumerator / scaleDenominator + originY);
	if (!convertRegion.Overlap(masterClip, clipRegion))
		clipRegion.left =
			clipRegion.top =
			clipRegion.right=
			clipRegion.bottom = -1;

	rect clipRect;
	SetRect(&clipRect, clipRegion.left, clipRegion.top,
		clipRegion.right + 1, clipRegion.bottom + 1);

	ClipRect(&clipRect);

	return (clipRegion);
}

ZafLogicalFillPattern ZafScreenDisplay::SetFillPattern(ZafLogicalFillPattern pattern)
{
	if (pattern >= 0 && pattern < ZAF_MAXPATTERNS)
	{
		palette.fillPattern = pattern;
		PenPattern(patternTranslationTable[pattern]);
	}

	return(palette.fillPattern);
}

ZafLogicalFont ZafScreenDisplay::SetFont(ZafLogicalFont font)
{
	if (font == ZAF_FNT_NULL)
	{
		::SetFont(NULL);
		palette.font = font;
	}
	else if (font != palette.font && font >= 0 && font < ZAF_MAXFONTS)
	{
		// Update the font information.
		OSFont fontInfo;

		fontInfo = fontTable[font];
		if (fontInfo.fontPtr)
			::SetFont((fontRcd *)fontInfo.fontPtr);
		palette.font = font;
	}

	return(palette.font);
}

ZafLogicalColor ZafScreenDisplay::SetForeground(ZafLogicalColor color)
{
	ZafLogicalColor oldForeground = palette.colorForeground;
	if (color >= 0 && color < ZAF_MAXCOLORS)
	{
		palette.colorForeground = color;
		PenColor(color);
	}
	else
		return (ZAF_CLR_NULL);
		
	return(oldForeground);
}

ZafLogicalLineStyle ZafScreenDisplay::SetLineStyle(ZafLogicalLineStyle line)
{
	if (line >= 0 && line < ZAF_MAXLINES)
	{
		PenDash(lineTable[lineTranslationTable[line]]);
		palette.lineStyle = line;
	}

	return (palette.lineStyle);
}

ZafLogicalMode ZafScreenDisplay::SetMode(ZafLogicalMode tMode)
{
	if (tMode >= 0 && tMode < ZAF_MAXMODES)
	{
		RasterOp(modeTable[modeTranslationTable[tMode]]);
		mode = tMode;
	}
	return (mode);
}

ZafLogicalColor ZafScreenDisplay::SetMonoBackground(ZafLogicalColor color)
{
	if (color != ZAF_MONO_NULL && color >= 0 && color < ZAF_MAXCOLORS)
	{
		palette.monoBackground = color;
	}

	return (palette.monoBackground);
}

ZafLogicalColor ZafScreenDisplay::SetMonoForeground(ZafLogicalColor color)
{
	if (color != ZAF_MONO_NULL && color >= 0 && color < ZAF_MAXCOLORS)
	{
		palette.monoBackground = color;
	}

	return (palette.monoBackground);
}

ZafPaletteStruct ZafScreenDisplay::SetPalette(ZafPaletteStruct palette)
{
	SetLineStyle(palette.lineStyle);
	SetFillPattern(palette.fillPattern);
	SetForeground(palette.colorForeground);
	SetBackground(palette.colorBackground);
	SetMonoForeground(palette.monoForeground);
	SetMonoBackground(palette.monoBackground);
	SetFont(palette.font);

	return (palette);
}

void SetPrimitiveClipRegion(const ZafRegionStruct &clipRegion)
{
	rect clipRect;
	SetRect(&clipRect, clipRegion.left, clipRegion.top,
		clipRegion.right + 1, clipRegion.bottom + 1);

	ClipRect(&clipRect);
}

// --- Draw Setup -----------------------------------------------------------

ZafError ZafScreenDisplay::BeginDraw(OSDisplayContext, OSDrawContext dC,
	const ZafRegionStruct &draw, const ZafRegionStruct &clip)
{
	static int i;
	static ZafDisplayImage *view;
	static grafPort *screen;
	grafPort *screenPort;
	ZafError retVal = ZAF_ERROR_NONE;

	if (--virtualCount == -1)
	{
		GetPort(&screenPort);

//		screenID = dC;
 		stopDevice = true;
//		destroyDrawList = false;

		rect screenSize;
		ScreenRect(&screenSize);
		masterClip.left = screenSize.Xmin;
		masterClip.top = screenSize.Ymin;
		masterClip.right = screenSize.Xmax;
		masterClip.bottom = screenSize.Ymax;
		masterClip.coordinateType = ZAF_PIXEL;
		SetClipRegion(masterClip);

		ZafLogicalMode saveMode = Mode();

		// Set _virtualRegion for most efficient overlap checking.
		viewPort.left = ZafMax(0, draw.left);
		viewPort.top = ZafMax(0, draw.top);
		viewPort.right = ZafMin(columns - 1, draw.right);
		viewPort.bottom = ZafMin(lines - 1, draw.bottom);
		viewPort.coordinateType = draw.coordinateType;
		for (i = ZAF_MAXIMAGES - 1; i >= 0; i--)
			if (viewPort.Overlap(displayImage[i].region) &&
				displayImage[i].image && displayImage[i].screen)
			{
				view = &displayImage[i];
				screen = view->screen;
				RasterOp(zREPz);
				ShowPen();
 				rect dstRect, srcRect;
				SetRect(&dstRect, view->region.left, view->region.top,
					view->region.right + 1, view->region.bottom + 1);
				SetRect(&srcRect, 0, 0, view->region.Width(), view->region.Height());
				CopyBlit(view->screen, screenPort, &srcRect, &dstRect);
				HidePen();
			}

//		if (dC != ID_ZAF_DIRECT && dC != ID_ZAF_SCREEN)
		{
			// Set the _viewPort so that Unvirtualize() and Virtualize()
			// work right.
			viewPort.left = draw.left;
			viewPort.top = draw.top;
			viewPort.right = draw.right;
			viewPort.bottom = draw.bottom;

			// Object is drawn with (0, 0) as the object's upper-left corner.
			// If the region is off the left or top of the screen, set the
			// port so it is non-negative but adjust the port's origin so it
			// behaves like the port went negative.
			int xOrigin = 0, yOrigin = 0,
				portLeft = viewPort.left, portTop = viewPort.top;
			if (draw.left < 0)
			{
				portLeft = 0;
				xOrigin = -draw.left;
			}
			if (draw.top < 0)
			{
				portTop = 0;
				yOrigin = -draw.top;
			}

			// If the drawing region is negative, set to an unusable point,
			// since MetWINDOW will otherwise invert the rectangle to be 
			// usable.
			if (viewPort.right < viewPort.left ||
				viewPort.bottom < viewPort.top)
				viewPort.left
					= viewPort.top
					= viewPort.right
					= viewPort.bottom = 10000;

			if (dC != ID_ZAF_DIRECT && dC != ID_ZAF_SCREEN)
			{
				MovePortTo(portLeft, portTop);
				PortSize(viewPort.right - portLeft + 1, viewPort.bottom - portTop + 1);
				::SetOrigin(xOrigin, yOrigin);

				// Set the master clip region.  This is the region that drawing
				// must clip to, so subsequent calls to SetClipRegion() will
				// be checked against this region.
				masterClip.left = clip.left - draw.left;
				masterClip.top = clip.top - draw.top;
				masterClip.right = clip.right - draw.left;
				masterClip.bottom = clip.bottom - draw.top;
				masterClip.coordinateType = draw.coordinateType;
				SetClipRegion(masterClip);

				// Set the draw regions.
				for (ZafRegionElement *element = drawRegionList.First(); element;
					element = element->Next())
				{
					element->region.VtShift(-viewPort.top);
					element->region.HzShift(-viewPort.left);
				}
			}

			if (!drawRegionList.First())
			{
				drawRegionList.Allocate(0, masterClip);
				destroyDrawList = true;
				retVal = ZAF_ERROR_VALUE_MISSING;
//				screenID = ID_ZAF_DIRECT;
			}
		}

		// Restore user's setting.
		SetMode(saveMode);
	}
	return (retVal);
}

ZafError ZafScreenDisplay::EndDraw(void)
{
	static int i, x, y;
	static ZafDisplayImage *view;
	static ZafLogicalColor *image;
	static grafPort *screen;
	grafPort *screenPort;

	if (++virtualCount == 0)
	{
		GetPort(&screenPort);

		// Restore port.
		MovePortTo(0, 0);
		PortSize(columns, lines);

		// Object is drawn with (0, 0) as the object's upper-left corner.
		::SetOrigin(0, 0);
		masterClip.left = masterClip.top = 0;
		masterClip.right = columns - 1;
		masterClip.bottom = lines - 1;
		masterClip.coordinateType = ZAF_PIXEL;

		for (i = 0; i < ZAF_MAXIMAGES; i++)
			if (viewPort.Overlap(displayImage[i].region) &&
				displayImage[i].image && displayImage[i].screen)
			{
				view = &displayImage[i];
				screen = view->screen;
				image = view->image;
				RasterOp(zREPz);

				SetClipRegion(displayImage[i].clip);

				// Ensure that pen is on.  Pen could be off if an interrupt
				// occurred while getting a string width or something.
				ShowPen();
 				rect dstRect, srcRect;
				SetRect(&dstRect, 0, 0, view->region.Width(), view->region.Height());
				SetRect(&srcRect, view->region.left, view->region.top,
					view->region.right + 1, view->region.bottom + 1);
				CopyBlit(screenPort, view->screen, &srcRect, &dstRect);
				short savePattern = screenPort->pnPat;
				PenPattern(patternTranslationTable[ZAF_PTN_SOLID_FILL]);
				for (y = view->region.top; y <= view->region.bottom; y++)
					for (x = view->region.left; x <= view->region.right; x++, screen++, image++)
						if (*image != ZAF_CLR_BACKGROUND)
						{
							PenColor(*image);
							SetPixel(x, y);
						}
				PenPattern(savePattern);

				// Restore pen to previous state.
				HidePen();
			}

		if (destroyDrawList)
			drawRegionList.Destroy();
		else
		{
			for (ZafRegionElement *element = drawRegionList.First(); element;
				element = element->Next())
			{
				element->region.VtShift(viewPort.top);
				element->region.HzShift(viewPort.left);
			}
		}

		stopDevice = false;

		// Restore display.
		coordinateType = ZAF_PIXEL;
		mode = ZAF_MODE_COPY;
		originX = originY = 0;
		scaleNumerator = scaleDenominator = 1;

/* START BLOCK COMMENT
**			if (screenID == ID_ZAF_DIRECT || screenID == ID_ZAF_SCREEN)
**				drawRegionList.Destroy();
END BLOCK COMMENT */

//		screenID = ID_ZAF_DIRECT;
	}
	else if (virtualCount > 0)
		virtualCount = 0;
	return (ZAF_ERROR_NONE);
}


OSDisplayContext ZafScreenDisplay::DisplayContext(void) const
{
	return (ZAF_NULLH(OSDisplayContext));
}

OSDisplayContext ZafScreenDisplay::RestoreDisplayContext(void)
{
	return (ZAF_NULLH(OSDisplayContext));
}

OSDrawContext ZafScreenDisplay::DrawContext(void) const
{
	return (ZAF_NULLH(OSDrawContext));
}

OSDrawContext ZafScreenDisplay::RestoreDrawContext(void)
{
	return (ZAF_NULLH(OSDrawContext));
}

OSDisplayContext ZafScreenDisplay::SetDisplayContext(OSDisplayContext)
{
  	// Meaningless on MSDOS.
	return (0);
}

OSDrawContext ZafScreenDisplay::SetDrawContext(OSDrawContext)
{
	// Meaningless on MSDOS.
	return (0);
}

// --- Devices --------------------------------------------------------------

int ZafScreenDisplay::DeviceMove(ZafDeviceImageType imageType, int newColumn,
	int newLine, ZafRegionStruct &clip)
{
	// Make sure the image type is valid.
	static int i, x, y, oldX, newX, oldY, newY;
	static int width, height, overlap, deltaX, deltaY;
	static ZafDisplayImage *view;
	static ZafRegionStruct newRegion, viewPort;
	static ZafLogicalColor *image;
	static grafPort *screen, *backup;
	static rect screenSize;

	grafPort *screenPort;
 	GetPort(&screenPort);

	view = &displayImage[imageType];
	if (!view->image || stopDevice || virtualCount != 0 ||
		(view->region.left == newColumn && view->region.top == newLine))
		return (false);
	stopDevice = true;

	displayImage[imageType].clip = clip;

	width = view->region.right - view->region.left + 1;
	height = view->region.bottom - view->region.top + 1;
	deltaX = newColumn - view->region.left;
	deltaY = newLine - view->region.top;

	newRegion.left = view->region.left + deltaX;
	newRegion.right = view->region.right + deltaX;
	newRegion.top = view->region.top + deltaY;
	newRegion.bottom = view->region.bottom + deltaY;
	newRegion.coordinateType = viewPort.coordinateType = view->region.coordinateType;

	// Overlap indicates if multiple device images occupy the same screen region.
	overlap = false;
	for (i = 0; i < ZAF_MAXIMAGES; i++)
		if (i != imageType && (view->region.Overlap(displayImage[i].region) ||
			newRegion.Overlap(displayImage[i].region)))
		{
			overlap = true;
			break;
		}

	if (overlap)
	{
		viewPort.left = ZafMin(view->region.left, newRegion.left);
		viewPort.top = ZafMin(view->region.top, newRegion.top);
		viewPort.right = ZafMax(view->region.right, newRegion.right);
		viewPort.bottom = ZafMax(view->region.bottom, newRegion.bottom);
		viewPort.coordinateType = view->region.coordinateType;

		BeginDraw(0, 0, viewPort, viewPort);

		view->region.left += deltaX;
		view->region.right += deltaX;
		view->region.top += deltaY;
		view->region.bottom += deltaY;

		EndDraw();
	}
	else
	{
		// Save user's settings so can restore them when done.
		ZafLogicalColor saveColor = Foreground();
		ZafLogicalMode saveMode = Mode();
		ZafRegionStruct saveClip = ClipRegion();

		// Compute the update coordinates.
		RasterOp(zREPz);
		ScreenRect(&screenSize);
		SetClipRegion(view->clip);
		image = view->image;
		screen = view->screen;
		backup = view->backup;

		oldY = view->region.top;
		newY = newLine;
		ShowPen();
		rect dstRect, srcRect;

		// Restore screen (hide device).
		SetRect(&dstRect, view->region.left, view->region.top,
			view->region.right + 1, view->region.bottom + 1);
		SetRect(&srcRect, 0, 0, view->region.Width(), view->region.Height());
		CopyBlit(view->screen, screenPort, &srcRect, &dstRect);

		// Get screen.
		SetRect(&dstRect, 0, 0, newRegion.Width(), newRegion.Height());
		SetRect(&srcRect, newRegion.left, newRegion.top,
			newRegion.right + 1, newRegion.bottom + 1);
		CopyBlit(screenPort, view->backup, &srcRect, &dstRect);

		// Save user's pattern.
		short savePattern = screenPort->pnPat;
		PenPattern(patternTranslationTable[ZAF_PTN_SOLID_FILL]);

		for (y = 0; y < height; y++, oldY++, newY++)
		{
			oldX = view->region.left;
			newX = newColumn;
			for (x = 0; x < width; x++, oldX++, newX++, image++, screen++, backup++)
			{
				if (*image != ZAF_CLR_BACKGROUND)
				{
					PenColor(*image);
					SetPixel(newX, newY);
				}
			}
		}

		// Restore user's pattern.
		PenPattern(savePattern);
		screen = view->screen;

		SetRect(&dstRect, 0, 0, width + 1, height + 1);
		SetRect(&srcRect, 0, 0, width + 1, height + 1);
		CopyBlit(view->backup, view->screen, &srcRect, &dstRect);

		HidePen();

		view->region.left = newColumn;
		view->region.top = newLine;
		view->region.right = newColumn + width - 1;
		view->region.bottom = newLine + height - 1;
		view->region.coordinateType = ZAF_PIXEL;

		// Restore user's settings.
		SetForeground(saveColor);
		SetMode(saveMode);
		SetClipRegion(saveClip);
	}

	stopDevice = false;
	return (true);
}

int ZafScreenDisplay::DeviceSet(ZafDeviceImageType imageType, int column, int line,
	int width, int height, ZafLogicalColor *image, ZafRegionStruct &clip)
{
	grafPort *screenPort;
 	GetPort(&screenPort);
	if (screenPort != screenPort)
		return (false);
	if (stopDevice)
		return (false);
	stopDevice = true;

	displayImage[imageType].clip = clip;

	ZafDisplayImage *view = &displayImage[imageType];
	ZafRegionStruct viewPort;
	if (view->image && view->region.left < columns && view->region.top < lines)
	{
		viewPort.left = ZafMin(view->region.left, column);
		viewPort.top = ZafMin(view->region.top, line);
		viewPort.right = ZafMax(view->region.right, column + width - 1);
		viewPort.bottom = ZafMax(view->region.bottom, line + height - 1);
		viewPort.coordinateType = view->region.coordinateType;
	}
	else
	{
		viewPort.left = column;
		viewPort.top = line;
		viewPort.right = column + width - 1;
		viewPort.bottom = line + height - 1;
		viewPort.coordinateType = ZAF_PIXEL;
	}

	BeginDraw(0, 0, viewPort, viewPort);
//	BeginDraw(0, 0, viewPort, clip);

	if (view->image)
		delete []view->image;
	view->image = new ZafLogicalColor[width * height];
	memcpy(view->image, image, width * height);
	view->region.left = column;
	view->region.top = line;
	view->region.right = column + width - 1;
	view->region.bottom = line + height - 1;
	view->region.coordinateType = ZAF_PIXEL;
	if (view->image)
	{
		if (view->screen)
		{
			DestroyBitmap(view->screen);
			DestroyBitmap(view->backup);
		}
		view->screen = CreateBitmap(cMEMORY, width + 1, height + 1);
		view->backup = CreateBitmap(cMEMORY, width + 1, height + 1);
	}

	SetPort(screenPort);
	EndDraw();
	stopDevice = false;
	return (true);
}

// --- Regions --------------------------------------------------------------

/* START BLOCK COMMENT
**	void ZafScreenDisplay::RegionDefine(OSWindowID screenID, int left, int top,
**		int right, int bottom)
**	{
**		// See if it is a full screen definition.
**		ZafRegionStruct region;
**		region.left = left;
**		region.top = top;
**		region.right = right;
**		region.bottom = bottom;
**		region.coordinateType = ZAF_PIXEL;
**		if (region.left <= 0 && region.top <= 0 && region.right >= columns - 1 && region.bottom >= lines - 1)
**		{
**			screenList->Destroy();
**			screenList->Add(new ZafRegionElement(screenID, 0, 0, columns - 1, lines - 1, ZAF_PIXEL));
**			return;
**		}
**	
**		// Throw away regions with a negative width or height.
**		if (region.right < region.left || region.bottom < region.top)
**			return;
**		
**		// Throw away regions that are completely off the screen.
**		if (region.left >= columns || region.right < 0 ||
**			region.top >= lines || region.bottom < 0)
**			return;
**	
**		// Clip regions partially off the screen to fit on the screen.
**		if (region.left < 0)
**			region.left = 0;
**		if (region.right >= columns)
**			region.right = columns - 1;
**		if (region.top < 0)
**			region.top = 0;
**		if (region.bottom >= lines)
**			region.bottom = lines - 1;
**	
**		// Split any overlapping regions.
**		screenList->Split(screenID, region, false);
**	
**		// Define the new display region.
**		screenList->Add(new ZafRegionElement(screenID, region));
**	}
END BLOCK COMMENT */

/* START BLOCK COMMENT
**	int ZafScreenDisplay::RegionInitialize(ZafRegionStruct &region,
**		const ZafRegionStruct *clipRegion, int left, int top, int right, int bottom)
**	{
**		region.left = left;
**		region.top = top;
**		region.right = right;
**		region.bottom = bottom;
**		region.coordinateType = ZAF_PIXEL;
**	
**		if (clipRegion)
**		{
**			ZafRegionStruct convertRegion = clipRegion->Region(ZAF_PIXEL);
**			region.left = ZafMax(region.left, convertRegion.left);
**			region.top = ZafMax(region.top, convertRegion.top);
**			region.right = ZafMin(region.right, convertRegion.right);
**			region.bottom = ZafMin(region.bottom, convertRegion.bottom);
**		}
**		return (region.left <= region.right && region.top <= region.bottom);
**	}
END BLOCK COMMENT */

/* START BLOCK COMMENT
**	ZafError ZafScreenDisplay::UnvirtualizeRegion(ZafRegionStruct &region)
**	{
**		region.left += viewPort.left;
**		region.top += viewPort.top;
**		region.right += viewPort.left;
**		region.bottom += viewPort.top;
**		return (ZAF_ERROR_NONE);
**	}
END BLOCK COMMENT */

/* START BLOCK COMMENT
**	ZafError ZafScreenDisplay::VirtualizeRegion(ZafRegionStruct &region)
**	{
**		region.left -= viewPort.left;
**		region.top -= viewPort.top;
**		region.right -= viewPort.left;
**		region.bottom -= viewPort.top;
**		return (ZAF_ERROR_NONE);
**	}
END BLOCK COMMENT */

#endif // ZAF_GRAPHICS_MODE
