//	Zinc Interface Library - D_MSCDSP.CPP
//	COPYRIGHT (C) 1990-1995.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA
/* This file is part of OpenZinc

OpenZinc is free software: You can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the license or
 (at your option) any later version.

OpenZinc is distributed in the hope that it will be useful,
but without ANY WARRANTY; without even the implied warranty of
MARCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lessor Public license for more details

You should have received a copy of the GNU Lessor Public License
along with OpenZinc. If not, see <http://www.gnu.org/licenses/>.
*/

#	if __BORLANDC__ >= 0X0500
#		undef true
#		define true OSRegion
#	endif

#include <malloc.h>
#include <stdio.h>
#include <dos.h>
#if defined (DOS386)
#	include <graph32.h>
#else
#	include <graph.h>
#endif
#include "ui_dsp.hpp"

#define RGB(r,g,b) (((long) ((b) << 8 | (g)) << 8) | (r))

// ----- Static member variables --------------------------------------------

UI_PATH *UI_MSC_DISPLAY::searchPath = ZIL_NULLP(UI_PATH);
UI_MSC_DISPLAY::MSCFONT UI_MSC_DISPLAY::fontTable[ZIL_MAXFONTS] =
{
	{ "Helv", "bh8" },	// FNT_SMALL_FONT
	{ "Helv", "bh10" },	// FNT_DIALOG_FONT
	{ "Helv", "bh12" },	// FNT_SYSTEM_FONT
	{ "Helv", "bh10" },
	{ "Helv", "bh10" },
	{ "Helv", "bh10" },
	{ "Helv", "bh10" },
	{ "Helv", "bh10" },
	{ "Helv", "bh10" },
	{ "Helv", "bh10" }
};
UI_MSC_DISPLAY::MSCPATTERN UI_MSC_DISPLAY::patternTable[ZIL_MAXPATTERNS] =
{
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },	// EMPTY_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// SOLID_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// LINE_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// LTSLASH_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// SLASH_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// BKSLASH_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// LTBKSLASH_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// HATCH_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// XHATCH_FILL
	{ 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 },	// INTERLEAVE_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// WIDE_DOT_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },	// CLOSE_DOT_FILL

	{ 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 },	// PTN_BACKGROUND_FILL
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
};

// ----- UI_MSC_DISPLAY -----------------------------------------------------

static ZIL_ICHAR fontName[] = { 'H', 'E', 'L', 'V', 'B', '.', 'F', 'O', 'N', 0 };
static ZIL_ICHAR _operatingSystem[] = { 'D','O','S',0 };
static ZIL_ICHAR _windowingSystem[] = { 'M','S','C',0 };
static ZIL_ICHAR _Mstr[] = { 'M', 0 };
static ZIL_ICHAR _codeSet[] = { 'I', 'B', 'M', '_', '8', '5', '0', 0 };

#pragma check_stack (off)
UI_MSC_DISPLAY::UI_MSC_DISPLAY(int mode):
	UI_DISPLAY(FALSE, _operatingSystem, _windowingSystem)
{
	strcpy(codeSet, _codeSet);

	// Initialize the basic member variables.
	_virtualCount = 0;
	_stopDevice = FALSE;
	_fillPattern = -1;
	_backgroundColor = -1;
	_foregroundColor = -1;

	// Register and select a font.
	struct _fontinfo fontInfo;

	// Use temporary path if not installed in main().
	int pathInstalled = searchPath ? TRUE : FALSE;
	if (!pathInstalled)
		searchPath = new UI_PATH;
	const ZIL_ICHAR *pathName = searchPath->FirstPathName();
	int registered = 0;
	ZIL_ICHAR path[128];
	while (pathName && registered <= 0)
	{
		strcpy(path, pathName);
		if (path[strlen(path) - 1] != defaultCharMap->dirSepStr[0])
			strcat(path, defaultCharMap->dirSepStr);
		strcat(path, fontName);
		char *TEXT_ = MapText(path, ZIL_NULLP(char), FALSE);
		registered = _registerfonts(TEXT_);
		if (registered > 0)
			break;
		pathName = searchPath->NextPathName();
	}

	if (registered <= 0)
		return;

	// Delete path if it was installed temporarily.
	if (!pathInstalled)
	{
		delete searchPath;
		searchPath = ZIL_NULLP(UI_PATH);
	}

	// Set mode to maximum resolution if mode is not specified
	struct videoconfig config;

	if (!mode)
		mode = _MAXRESMODE;
	if (!_setvideomode(mode))
		return;

	// Get specifics about the video setup.
	_getvideoconfig(&config);
	lines = config.numypixels;
	columns = config.numxpixels;
	maxColors = config.numcolors;
	isMono = (maxColors == 2);

	// Reset some palette colors if in >= VGA mode.
	if (maxColors >= 15 && columns >= 639 && lines >= 479)
	{
		_remappalette(7, RGB(50, 50, 50));		// A lighter lightgray.
		_remappalette(8, RGB(35, 35, 35));		// A lighter darkgray.
		_remappalette(14, RGB(63, 63, 50));		// A lighter yellow.
	}

	// Reset the font information.
	SetFont(-1);

	// Fill the screen according to the specified palette.
	cellWidth = TextWidth(_Mstr, ID_SCREEN, FNT_DIALOG_FONT) - 2;
	cellHeight = TextHeight(ZIL_NULLP(ZIL_ICHAR), ID_SCREEN, FNT_DIALOG_FONT) +
		preSpace + postSpace + 2 + 3;  // 2 above the text, 2 below the text.

	// Define the screen display region.
	Add(0, new UI_REGION_ELEMENT(ID_SCREEN, 0, 0, columns - 1, lines - 1));
	Rectangle(ID_DIRECT, 0, 0, columns - 1, lines - 1, backgroundPalette, 0, TRUE);
	installed = TRUE;
}

UI_MSC_DISPLAY::~UI_MSC_DISPLAY(void)
{
	// Restore the display.
	if (installed)
		_setvideomode(_DEFAULTMODE);
}

void UI_MSC_DISPLAY::Bitmap(ZIL_SCREENID screenID, int column, int line,
	int bitmapWidth, int bitmapHeight, const ZIL_UINT8 *bitmapArray,
	const UI_PALETTE *palette, const UI_REGION *clipRegion, ZIL_BITMAP_HANDLE *,
	ZIL_BITMAP_HANDLE *)
{
	// Assign the bitmap to the region structure.
	UI_REGION region, tRegion;
	if (!RegionInitialize(region, clipRegion, column, line,
		column + bitmapWidth - 1, line + bitmapHeight - 1))
		return;
	if (!palette)
		palette = colorMap;

	// Draw the rectangle on the display.
	int changedScreen = FALSE;
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
			_setviewport(0, 0, columns - 1, lines - 1);
			for (int y = tRegion.top; y <= tRegion.bottom; y++)
			{
				const ZIL_UINT8 *pixel = bitmapArray + (y - line) * bitmapWidth + tRegion.left - column;
				for (int x = tRegion.left; x <= tRegion.right; x++, pixel++)
					if (*pixel != BACKGROUND)
					{
						_setcolor(MapColor(palette+*pixel, TRUE));
						_setpixel(x, y);
					}
			}
			if (screenID == ID_DIRECT)
				break;
		}

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

void UI_MSC_DISPLAY::Ellipse(ZIL_SCREENID screenID, int x, int y,
	int startAngle, int endAngle, int xRadius, int yRadius,
	const UI_PALETTE *palette, int fill, int xor, const UI_REGION *clipRegion)
{
	const int pSin[] = { 0, 871, 1736, 2588, 3420, 4226, 5000, 5736, 6428,
		7071, 7660, 8191, 8660, 9063, 9397, 9659, 9848, 9962, 10000 };

	// Assign the ellipse to the region structure.
	UI_REGION region, tRegion;
	if (!RegionInitialize(region, clipRegion, x - xRadius, y - yRadius,
		x + xRadius, y + yRadius))
		return;
	while (startAngle < 0)
		startAngle += 360, endAngle += 360;
	if (startAngle > 360)
		startAngle %= 360;
	while (endAngle < 0)
		endAngle += 360;
	if (endAngle > 360)
		endAngle %= 360;

	// Draw the ellipse on the display.
	int startX = 0;
	int startY = 0;
	int endX = 0;
	int endY = 0;
	int changedScreen = FALSE;
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
			{
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
				SetPattern(palette, xor);

				// Compute points to start and stop.
				if (startAngle <= 90)
				{
					startX = x + (int)(1L * pSin[(90 - startAngle) / 5] * xRadius / 10000L);
					startY = y - (int)(1L * pSin[startAngle / 5] * yRadius / 10000L);
				}
				else if (startAngle <= 180)
				{
					startX = x - (int)(1L * pSin[(startAngle - 90) / 5] * xRadius / 10000L);
					startY = y - (int)(1L * pSin[(180 - startAngle) / 5] * yRadius / 10000L);
				}
				else if (startAngle <= 270)
				{
					startX = x - (int)(1L * pSin[(270 - startAngle) / 5] * xRadius / 10000L);
					startY = y + (int)(1L * pSin[(startAngle - 180) / 5] * yRadius / 10000L);
				}
				else if (startAngle <= 360)
				{
					startX = x + (int)(1L * pSin[(startAngle - 270) / 5] * xRadius / 10000L);
					startY = y + (int)(1L * pSin[(360 - startAngle) / 5] * yRadius / 10000L);
				}

				if (endAngle <= 90)
				{
					endX = x + (int)(1L * pSin[(90 - endAngle) / 5] * xRadius / 10000L);
					endY = y - (int)(1L * pSin[endAngle / 5] * yRadius / 10000L);
				}
				else if (endAngle <= 180)
				{
					endX = x - (int)(1L * pSin[(endAngle - 90) / 5] * xRadius / 10000L);
					endY = y - (int)(1L * pSin[(180 - endAngle) / 5] * yRadius / 10000L);
				}
				else if (endAngle <= 270)
				{
					endX = x - (int)(1L * pSin[(270 - endAngle) / 5] * xRadius / 10000L);
					endY = y + (int)(1L * pSin[(endAngle - 180) / 5] * yRadius / 10000L);
				}
				else if (endAngle <= 360)
				{
					endX = x + (int)(1L * pSin[(endAngle - 270) / 5] * xRadius / 10000L);
					endY = y + (int)(1L * pSin[(360 - endAngle) / 5] * yRadius / 10000L);
				}
			}
			_setviewport(tRegion.left, tRegion.top, tRegion.right, tRegion.bottom);

			if (fill && _fillPattern != PTN_SOLID_FILL)
			{
				_setcolor(_foregroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				if (startAngle == 0 && endAngle == 360)
					_ellipse(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top);
				else
					_pie(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top, startX - tRegion.left, startY - tRegion.top, endX - tRegion.left, endY - tRegion.top);

				_setcolor(_backgroundColor);
				_setfillmask(patternTable[_fillPattern]);
				if (startAngle == 0 && endAngle == 360)
					_ellipse(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top);
				else
					_pie(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top, startX - tRegion.left, startY - tRegion.top, endX - tRegion.left, endY - tRegion.top);
			}
			else if (fill)
			{
				_setcolor(_backgroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				if (startAngle == 0 && endAngle == 360)
					_ellipse(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top);
				else
					_pie(_GFILLINTERIOR, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top, startX - tRegion.left, startY - tRegion.top, endX - tRegion.left, endY - tRegion.top);
			}
			if (!fill || _backgroundColor != _foregroundColor)
			{
				_setcolor(_foregroundColor);
				if (startAngle == 0 && endAngle == 360)
					_ellipse(_GBORDER, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top);
				else
					_pie(_GBORDER, x - xRadius - tRegion.left, y - yRadius - tRegion.top, x + xRadius - tRegion.left, y + yRadius - tRegion.top, startX - tRegion.left, startY - tRegion.top, endX - tRegion.left, endY - tRegion.top);
			}
			if (screenID == ID_DIRECT)
				break;
		}

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

int UI_MSC_DISPLAY::DeviceMove(ZIL_IMAGE_TYPE imageType, int newColumn, int newLine)
{
	static int i, x, y, oldX, newX, oldY, newY;
	static int width, height, overlap, deltaX, deltaY;
	static UI_DISPLAY_IMAGE *view;
	static UI_REGION newRegion, virtualRegion;
	static ZIL_UINT8 *image, *screen, *backup, *tScreen;

	// Make sure the image type is valid.
	view = &displayImage[imageType];
	if (!view->image || _stopDevice || _virtualCount != 0 ||
		(view->region.left == newColumn && view->region.top == newLine))
		return (FALSE);
	_stopDevice = TRUE;

	width = view->region.right - view->region.left + 1;
	height = view->region.bottom - view->region.top + 1;

	overlap = FALSE;
	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;

	for (i = 0; i < ZIL_MAXIMAGES && !overlap; i++)
		overlap = (i != imageType &&
			   (view->region.Overlap(displayImage[i].region) ||
			    newRegion.Overlap(displayImage[i].region)));
	if (overlap)
	{
		virtualRegion.left = MinValue(view->region.left, newRegion.left);
		virtualRegion.top = MinValue(view->region.top, newRegion.top);
		virtualRegion.right = MaxValue(view->region.right, newRegion.right);
		virtualRegion.bottom = MaxValue(view->region.bottom, newRegion.bottom);

		VirtualGet(0, virtualRegion.left, virtualRegion.top,
			   virtualRegion.right, virtualRegion.bottom);

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

		VirtualPut(0);
	}
	else
	{
		// Compute the update coordinates.
		_setviewport(0, 0, columns - 1, lines - 1);
		image = view->image;
		screen = view->screen;
		backup = view->backup;

		oldY = view->region.top;
		newY = newLine;
		for (y = 0; y < height; y++, oldY++, newY++)
		{
			tScreen = &screen[deltaY * width + deltaX];
			oldX = view->region.left;
			newX = newColumn;
			for (x = 0; x < width; x++, oldX++, newX++, image++, screen++, backup++, tScreen++)
			{
				if (*image == BACKGROUND)
					*backup = BACKGROUND;
				else if (*image != BACKGROUND &&
					 x + deltaX >= 0 && x + deltaX < width &&
					 y + deltaY >= 0 && y + deltaY < height &&
					 *tScreen != BACKGROUND)
				{
					*backup = *tScreen;
					*tScreen = BACKGROUND;
				}
				else
					*backup = _getpixel(newX, newY);
				if (*image != BACKGROUND)
				{
					_setcolor(*image);
					_setpixel(newX, newY);
				}
			}
		}
		screen = view->screen;
		for (y = view->region.top; y <= view->region.bottom; y++)
			for (x = view->region.left; x <= view->region.right; x++, screen++)
				if (*screen != BACKGROUND)
				{
					_setcolor(*screen);
					_setpixel(x, y);
				}
		memcpy(view->screen, view->backup, width * height);
		view->region.left = newColumn;
		view->region.top = newLine;
		view->region.right = newColumn + width - 1;
		view->region.bottom = newLine + height - 1;
	}

	_stopDevice = FALSE;
	return (TRUE);
}

int UI_MSC_DISPLAY::DeviceSet(ZIL_IMAGE_TYPE imageType, int column, int line,
	int width, int height, ZIL_UINT8 *image)
{
	if (_stopDevice)
		return (FALSE);
	_stopDevice = TRUE;

	UI_DISPLAY_IMAGE *view = &displayImage[imageType];
	UI_REGION virtualRegion;
	if (view->image)
	{
		virtualRegion.left = MinValue(view->region.left, column);
		virtualRegion.top = MinValue(view->region.top, line);
		virtualRegion.right = MaxValue(view->region.right, column + width - 1);
		virtualRegion.bottom = MaxValue(view->region.bottom, line + height - 1);
	}
	else
	{
		virtualRegion.left = column;
		virtualRegion.top = line;
		virtualRegion.right = column + width - 1;
		virtualRegion.bottom = line + height - 1;
	}

	VirtualGet(0, virtualRegion.left, virtualRegion.top, virtualRegion.right,
		virtualRegion.bottom);

	view->image = image;
	view->region.left = column;
	view->region.top = line;
	view->region.right = column + width - 1;
	view->region.bottom = line + height - 1;
	if (view->image)
	{
		if (view->screen)
		{
			delete view->screen;
			delete view->backup;
		}
		view->screen = new ZIL_UINT8[width * height];
		view->backup = new ZIL_UINT8[width * height];
		memset(view->screen, BACKGROUND, width * height);
	}

	VirtualPut(0);
	_stopDevice = FALSE;
	return (TRUE);
}

void UI_MSC_DISPLAY::Line(ZIL_SCREENID screenID, int x1, int y1,
	int x2, int y2, const UI_PALETTE *palette, int width, int xor,
	const UI_REGION *clipRegion)
{
	// Assign the line to a region structure.
	UI_REGION region, tRegion;
	if (!RegionInitialize(region, clipRegion, MinValue(x1, x2), MinValue(y1, y2),
		MaxValue(x1, x2) + width, MaxValue(y1, y2) + width))
		return;

	// Draw the line on the display.
	int changedScreen = FALSE;
	int slope;
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
			{
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
				SetPattern(palette, xor);
				_setcolor(_foregroundColor);
				slope = x1 == x2 ? 1 : (y1 - y2) / (x1 - x2);
			}
			_setviewport(tRegion.left, tRegion.top, tRegion.right, tRegion.bottom);
			for (int i = 0; i < width; i++)
				if (slope)
				{
					_moveto(x1 - tRegion.left + i, y1 - tRegion.top);
					_lineto(x2 - tRegion.left + i, y2 - tRegion.top);
				}
				else
				{
					_moveto(x1 - tRegion.left, y1 - tRegion.top + i);
					_lineto(x2 - tRegion.left, y2 - tRegion.top + i);
				}
			if (screenID == ID_DIRECT)
				break;
		}

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

ZIL_COLOR UI_MSC_DISPLAY::MapColor(const UI_PALETTE *palette, int foreground)
{
	// Match the color request based on the type of display.
	if (maxColors == 2)
		return (foreground ? palette->bwForeground : palette->bwBackground);
	else if (maxColors < 16)
		return (foreground ? palette->grayScaleForeground : palette->grayScaleBackground);
	return (foreground ? palette->colorForeground : palette->colorBackground);
}

void UI_MSC_DISPLAY::Polygon(ZIL_SCREENID screenID, int numPoints,
	const int *polygonPoints, const UI_PALETTE *palette, int fill, int xor,
	const UI_REGION *clipRegion)
{
	// 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 = MinValue(left, polygonPoints[i]);
		top = MinValue(top, polygonPoints[i+1]);
		right = MaxValue(right, polygonPoints[i]);
		bottom = MaxValue(bottom, polygonPoints[i+1]);
	}
	UI_REGION region, tRegion;
	if (!RegionInitialize(region, clipRegion, left, top, right, bottom))
		return;

	// Draw the polygon on the display.
	int changedScreen = FALSE;
	int *tPolygon = new int[numPoints * 2];
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
			{
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
				SetPattern(palette, xor);
			}
			_setviewport(tRegion.left, tRegion.top, tRegion.right, tRegion.bottom);
			for (i = 0; i < numPoints * 2; i += 2)
			{
				tPolygon[i] = polygonPoints[i] - tRegion.left;
				tPolygon[i+1] = polygonPoints[i+1] - tRegion.top;
			}
			if (fill && _fillPattern != PTN_SOLID_FILL)
			{
				_setcolor(_foregroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_polygon(_GFILLINTERIOR, (struct _xycoord *)tPolygon, numPoints);

				_setcolor(_backgroundColor);
				_setfillmask(patternTable[_fillPattern]);
				_polygon(_GFILLINTERIOR, (struct _xycoord *)tPolygon, numPoints);
			}
			else if (fill)
			{
				_setcolor(_backgroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_polygon(_GFILLINTERIOR, (struct _xycoord *)tPolygon, numPoints);
			}
			if (!fill || _backgroundColor != _foregroundColor)
			{
				_setcolor(_foregroundColor);
				for (i = 2; i < numPoints * 2; i += 2)
				{
					_moveto(tPolygon[i-2], tPolygon[i-1]);
					_lineto(tPolygon[i], tPolygon[i+1]);
				}
			}
			if (screenID == ID_DIRECT)
				break;
		}
	delete tPolygon;

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

void UI_MSC_DISPLAY::Rectangle(ZIL_SCREENID screenID, int left, int top,
	int right, int bottom, const UI_PALETTE *palette, int width, int fill,
	int xor, const UI_REGION *clipRegion)
{
	// Assign the rectangle to the region structure.
	UI_REGION region, tRegion;
	if (!RegionInitialize(region, clipRegion, left, top, right, bottom))
		return;

	// Draw the rectangle on the display.
	int changedScreen = FALSE;
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
			{
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
				SetPattern(palette, xor);
			}
			_setviewport(tRegion.left, tRegion.top, tRegion.right, tRegion.bottom);
			if (fill && _fillPattern != PTN_SOLID_FILL)
			{
				_setcolor(_foregroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_rectangle(_GFILLINTERIOR, left - tRegion.left, top - tRegion.top, right - tRegion.left, bottom - tRegion.top);

				_setcolor(_backgroundColor);
				_setfillmask(patternTable[_fillPattern]);
				_rectangle(_GFILLINTERIOR, left - tRegion.left, top - tRegion.top, right - tRegion.left, bottom - tRegion.top);
			}
			else if (fill)
			{
				_setcolor(_backgroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_rectangle(_GFILLINTERIOR, left - tRegion.left, top - tRegion.top, right - tRegion.left, bottom - tRegion.top);
			}
			else if (width < 0)
			{
				// Draws a dashed rounded rectangle to indicate focus
				_setcolor(palette->colorForeground);
				_setlinestyle(0x5555);
				_setviewport(0, 0, columns - 1, lines - 1);
			  	_moveto(left + 1, top);
			  	_lineto(right - 1, top);
			  	_moveto(right, top + 1);
			  	_lineto(right, bottom - 1);
			  	_moveto(left + 1, bottom);
			  	_lineto(right - 1, bottom);
			  	_moveto(left, top + 1);
			  	_lineto(left, bottom - 1);
				_setlinestyle(0xFFFF);
			}
			if (!fill || _backgroundColor != _foregroundColor)
			{
				_setcolor(_foregroundColor);
				for (int i = 0; i < width; i++)
				{
					_rectangle(_GBORDER, left - (tRegion.left - i), top - (tRegion.top - i),
						right - (tRegion.left + i), bottom - (tRegion.top + i));
				}
			}
			if (screenID == ID_DIRECT)
				break;
		}

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

void UI_MSC_DISPLAY::RectangleXORDiff(const UI_REGION &oldRegion,
	const UI_REGION &newRegion, ZIL_SCREENID, const UI_REGION *clipRegion)
{
	// See if the regions match.
	if (oldRegion.left == newRegion.left && oldRegion.top == newRegion.top &&
		oldRegion.right == newRegion.right && oldRegion.bottom == newRegion.bottom)
		return;

	// Hide the screen devices.
	UI_REGION region;
	if (!RegionInitialize(region, ZIL_NULLP(UI_REGION),
		MinValue(oldRegion.left, newRegion.left), MinValue(oldRegion.top, newRegion.top),
		MaxValue(oldRegion.right, newRegion.right), MaxValue(oldRegion.bottom, newRegion.bottom)))
		return;
	VirtualGet(ID_DIRECT, region.left, region.top, region.right, region.bottom);

	// Draw the XOR rectangles on the display.
	SetPattern(xorPalette, TRUE);
	_setcolor(_foregroundColor);

	if (clipRegion)
	{
		_setviewport(clipRegion->left, clipRegion->top, clipRegion->right, clipRegion->bottom);
		_rectangle(_GBORDER, oldRegion.left - clipRegion->left, oldRegion.top - clipRegion->top,
			oldRegion.right - clipRegion->left, oldRegion.bottom - clipRegion->top);
		_rectangle(_GBORDER, newRegion.left - clipRegion->left, newRegion.top - clipRegion->top,
			newRegion.right - clipRegion->left, newRegion.bottom - clipRegion->top);
	}
	else
	{
		_setviewport(0, 0, columns - 1, lines - 1);
		_rectangle(_GBORDER, oldRegion.left, oldRegion.top, oldRegion.right, oldRegion.bottom);
		_rectangle(_GBORDER, newRegion.left, newRegion.top, newRegion.right, newRegion.bottom);
	}

	// Update the screen.
	VirtualPut(ID_DIRECT);
}

void UI_MSC_DISPLAY::RegionDefine(ZIL_SCREENID screenID, int left, int top,
	int right, int bottom)
{
	// See if it is a full screen definition.
	UI_REGION region = { left, top, right, bottom };
	if (region.left <= 0 && region.top <= 0 && region.right >= columns - 1 && region.bottom >= lines - 1)
	{
		UI_REGION_LIST::Destroy();
		Add(0, new UI_REGION_ELEMENT(screenID, 0, 0, columns - 1, lines - 1));
		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.
	Split(screenID, region, FALSE);

	// Define the new display region.
	UI_REGION_LIST::Add(0, new UI_REGION_ELEMENT(screenID, region));
}

void UI_MSC_DISPLAY::RegionMove(const UI_REGION &oldRegion,
	int newColumn, int newLine, ZIL_SCREENID oldScreenID, ZIL_SCREENID)
{
	VirtualGet(oldScreenID, MinValue(oldRegion.left, newColumn), MinValue(oldRegion.top, newLine),
		MaxValue(oldRegion.right, newColumn + (oldRegion.right - oldRegion.left)),
		MaxValue(oldRegion.bottom, newLine + (oldRegion.bottom - oldRegion.top)));

	void __huge *buffer = ZIL_NULLP(void);
	_setviewport(0, 0, columns - 1, lines - 1);
	long size = _imagesize(oldRegion.left, oldRegion.top, oldRegion.right, oldRegion.bottom);
#if defined (DOS386)
	buffer = malloc(size);
#elif defined (DOSX286)
	buffer = calloc(size, sizeof(char));
#else
	buffer = _halloc(size, sizeof(char));
#endif
	if (buffer)
	{
		_getimage(oldRegion.left, oldRegion.top, oldRegion.right, oldRegion.bottom, (char __huge *)buffer);
		_putimage(newColumn, newLine, (char __huge *)buffer, _GPSET);
	}
	else if (oldRegion.top < newLine)
	{
		size = _imagesize(oldRegion.left, oldRegion.top, oldRegion.right, oldRegion.top);
#if defined (DOS386)
		buffer = malloc(size);
#elif defined (DOSX286)
		buffer = calloc(size, sizeof(char));
#else
		buffer = _halloc(size, sizeof(char));
#endif
		if (!buffer)
		{
			printf("_halloc");
			return;
		}
		int height = oldRegion.bottom - oldRegion.top;
		for (int i = height; i >= 0; i--)
			if (oldRegion.top + i > 0 && oldRegion.top + i < lines)
			{
				_getimage(oldRegion.left, oldRegion.top + i, oldRegion.right, oldRegion.top + i, (char __huge *)buffer);
				_putimage(newColumn, newLine + i, (char __huge *)buffer, _GPSET);
			}
	}
	else
	{
		size = _imagesize(oldRegion.left, oldRegion.top, oldRegion.right, oldRegion.top);
#if defined (DOS386)
		buffer = malloc(size);
#elif defined (DOSX286)
		buffer = calloc(size, sizeof(char));
#else
		buffer = _halloc(size, sizeof(char));
#endif
		if (!buffer)
		{
			printf("_halloc");
			return;
		}
		int height = oldRegion.bottom - oldRegion.top;
		for (int i = 0; i <= height; i++)
			if (oldRegion.top + i > 0 && oldRegion.top + i < lines)
			{
				_getimage(oldRegion.left, oldRegion.top + i, oldRegion.right, oldRegion.top + i, (char __huge *)buffer);
				_putimage(newColumn, newLine + i, (char __huge *)buffer, _GPSET);
			}
	}
#if defined (DOS386) || defined (DOSX286)
	free(buffer);
#else
	_hfree(buffer);
#endif

	VirtualPut(oldScreenID);
}

void UI_MSC_DISPLAY::Text(ZIL_SCREENID screenID, int left, int top,
	const ZIL_ICHAR *text, const UI_PALETTE *palette, int length, int fill,
	int xor, const UI_REGION *clipRegion, ZIL_LOGICAL_FONT logicalFont)
{
	// Make sure we have a valid string.
	if (!text || !text[0] || !palette)
		return;

	// Set up the fill line.
	ZIL_ICHAR fillLine[256];
	if (length < 0)
		length = strlen(text);
	else if (length > 255)
		length = 255;
	strncpy(fillLine, text, length);
	fillLine[length] = 0;

	// Determine the hot-key values and shrink the string to remove '&'.
	ZIL_ICHAR *hotKey = ZIL_NULLP(ZIL_ICHAR);
	int hotKeyLeft, hotKeyRight;
	if (!FlagSet(logicalFont, FNT_IGNORE_UNDERSCORE))
	{
		ZIL_ICHAR hotKeyBuf[2];
		ZIL_ICHAR *hotChar = fillLine;
		for (int i=0; i < length; i++, hotChar++)
		{
			if ((*hotChar == ZIL_HOTMARK && hotChar[1] == ZIL_HOTMARK) ||
				(*hotChar == '~' && hotChar[1] == '~'))
				memmove(hotChar, hotChar + 1, strlen(hotChar) * sizeof(ZIL_ICHAR));
			else if (*hotChar == ZIL_HOTMARK || *hotChar == '~')
			{
				hotKeyBuf[0] = hotChar[1];
				hotKeyBuf[1] =	*hotChar = '\0';
				hotKeyLeft = left + TextWidth(fillLine, screenID, logicalFont | FNT_IGNORE_UNDERSCORE);
				memmove(hotChar, hotChar + 1, (strlen(hotChar + 1) + 1) * sizeof(ZIL_ICHAR));
				hotKey = hotChar;
				int markLen = TextWidth(hotKeyBuf, screenID, logicalFont | FNT_IGNORE_UNDERSCORE);
				hotKeyRight = hotKeyLeft + markLen - 1;
			}
		}
	}

	// Check for special characters.
	strrepc(fillLine, '\t', ' ');
	strrepc(fillLine, '\r', ' ');
	strrepc(fillLine, '\n', ' ');

	// Assign the rectangle to the region structure.
	SetFont(logicalFont);
	UI_REGION region, tRegion;
	int width = TextWidth(fillLine, screenID, logicalFont | FNT_IGNORE_UNDERSCORE);
	int height = TextHeight(fillLine, screenID, logicalFont);
	int right = left + width;
	int bottom = top + height;
	if (!RegionInitialize(region, clipRegion, left, top, right, bottom))
		return;

	// Draw the rectangle on the display.
	int changedScreen = FALSE;
	for (UI_REGION_ELEMENT *dRegion = First(); dRegion; dRegion = dRegion->Next())
		if (screenID == ID_DIRECT ||
			(screenID == dRegion->screenID && dRegion->region.Overlap(region, tRegion)))
		{
			if (screenID == ID_DIRECT)
				tRegion = region;
			if (!changedScreen)
			{
				changedScreen = VirtualGet(screenID, region.left, region.top, region.right, region.bottom);
				SetPattern(palette, xor);
			}
			_setviewport(tRegion.left, tRegion.top, tRegion.right, tRegion.bottom);
			if (fill && _fillPattern != PTN_SOLID_FILL)
			{
				_setcolor(_foregroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_rectangle(_GFILLINTERIOR, region.left - tRegion.left, region.top - tRegion.top,
					region.right - tRegion.left, region.bottom - tRegion.top);

				_setcolor(_backgroundColor);
				_setfillmask(patternTable[_fillPattern]);
				_rectangle(_GFILLINTERIOR, region.left - tRegion.left, region.top - tRegion.top,
					region.right - tRegion.left, region.bottom - tRegion.top);
			}
			else if (fill)
			{
				_setcolor(_backgroundColor);
				_setfillmask(ZIL_NULLP(unsigned char));
				_rectangle(_GFILLINTERIOR, region.left - tRegion.left, region.top - tRegion.top,
					region.right - tRegion.left, region.bottom - tRegion.top);
			}
			if (!fill || _backgroundColor != _foregroundColor)
			{
				_setcolor(_foregroundColor);
				_moveto(left - tRegion.left, top - tRegion.top);
				char *TEXT_ = MapText(fillLine, ZIL_NULLP(char), FALSE);
				_outgtext(TEXT_);
			}
			if (hotKey)
			{
				_moveto(hotKeyLeft - tRegion.left, bottom - tRegion.top);
				_lineto(hotKeyRight - tRegion.left, bottom - tRegion.top);
			}
			if (screenID == ID_DIRECT)
				break;
		}

	// Update the screen.
	if (changedScreen)
		VirtualPut(screenID);
}

void UI_MSC_DISPLAY::SetFont(ZIL_LOGICAL_FONT logicalFont)
{
	static int _logicalFont = -1;
	static MSCFONT _fontInfo;
	MSCFONT fontInfo;
	char fontString[32];

	// Check for invalid or new font set.
	if (_logicalFont == -1 || logicalFont == -1)
	{
		logicalFont = FNT_DIALOG_FONT;
		fontInfo = fontTable[FNT_DIALOG_FONT];
		::sprintf(fontString, "t'%s'%s", fontInfo.typeFace, fontInfo.options);
		_setfont(fontString);
		_logicalFont = logicalFont;
		_fontInfo = fontInfo;
	}
	else
	{
		logicalFont &= 0x0FFF;
		if (logicalFont == _logicalFont)
			return;
	}

	// Update the font information.
	fontInfo = fontTable[logicalFont];
	if (fontInfo.typeFace != _fontInfo.typeFace ||
		fontInfo.options != _fontInfo.options)
	{
		::sprintf(fontString, "t'%s'%s", fontInfo.typeFace, fontInfo.options);
		_setfont(fontString);
	}
	_fontInfo = fontInfo;
	_logicalFont = logicalFont;
}

void UI_MSC_DISPLAY::SetPattern(const UI_PALETTE *palette, int xor)
{
	static int _xor = -1;

	if (palette)
	{
		_fillPattern = palette->fillPattern;
		_backgroundColor = MapColor(palette, FALSE);
		_foregroundColor = MapColor(palette, TRUE);
	}
	if (xor != _xor)
	{
		_setwritemode(xor ? _GXOR : _GPSET);
		_xor = xor;
	}
}

int UI_MSC_DISPLAY::TextHeight(const ZIL_ICHAR *, ZIL_SCREENID,
	ZIL_LOGICAL_FONT logicalFont)
{
	logicalFont &= 0x0FFF;
	SetFont(logicalFont);
	struct _fontinfo fontInfo;
	_getfontinfo(&fontInfo);
	return (fontInfo.pixheight);
}

int UI_MSC_DISPLAY::TextWidth(const ZIL_ICHAR *string, ZIL_SCREENID,
	ZIL_LOGICAL_FONT logicalFont)
{
	if (!string || !(*string))
		return (0);
	SetFont(logicalFont & 0x0FFF);
	char *TEXT_ = MapText(string, ZIL_NULLP(char), FALSE);
	int length = _getgtextextent(TEXT_);

	int lineLen = strlen(string);
	for (int j=0; j < lineLen; j++)
		if (!FlagSet(logicalFont, FNT_IGNORE_UNDERSCORE) &&	(string[j] == ZIL_HOTMARK || string[j] == '~'))
		{
			char hotKeyBuf[2];
			hotKeyBuf[0] = string[j];
			hotKeyBuf[1] = '\0';
			length -= _getgtextextent(hotKeyBuf);
			if ((string[j] == ZIL_HOTMARK && string[j+1] == ZIL_HOTMARK) ||
				(string[j] == '~' && string[j+1] == '~'))
				j++;
		}
	return (length);
}

int UI_MSC_DISPLAY::VirtualGet(ZIL_SCREENID, int left, int top,
	int right, int bottom)
{
	static int i, x, y;
	static UI_DISPLAY_IMAGE *view;
	static ZIL_UINT8 *screen;

	if (--_virtualCount == -1)
	{
 		_stopDevice = TRUE;
		_virtualRegion.left = MaxValue(0, left);
		_virtualRegion.top = MaxValue(0, top);
		_virtualRegion.right = MinValue(columns - 1, right);
		_virtualRegion.bottom = MinValue(lines - 1, bottom);
		for (i = ZIL_MAXIMAGES - 1; i >= 0; i--)
			if (_virtualRegion.Overlap(displayImage[i].region) &&
				displayImage[i].image && displayImage[i].screen)
			{
				view = &displayImage[i];
				screen = view->screen;
				_setviewport(0, 0, columns - 1, lines - 1);
				for (y = view->region.top; y <= view->region.bottom; y++)
					for (x = view->region.left; x <= view->region.right; x++, screen++)
						if (*screen != BACKGROUND)
						{
							_setcolor(*screen);
							_setpixel(x, y);
						}
			}
	}
	return (TRUE);
}

int UI_MSC_DISPLAY::VirtualPut(ZIL_SCREENID)
{
	static int i, x, y;
	static UI_DISPLAY_IMAGE *view;
	static ZIL_UINT8 *screen, *image;

	if (++_virtualCount == 0)
	{
		for (i = 0; i < ZIL_MAXIMAGES; i++)
			if (_virtualRegion.Overlap(displayImage[i].region) &&
				displayImage[i].image && displayImage[i].screen)
			{
				view = &displayImage[i];
				screen = view->screen;
				image = view->image;
				_setviewport(0, 0, columns - 1, lines - 1);
				for (y = view->region.top; y <= view->region.bottom; y++)
					for (x = view->region.left; x <= view->region.right; x++, screen++, image++)
						if (*image != BACKGROUND)
						{
							*screen = _getpixel(x, y);
							_setcolor(*image);
							_setpixel(x, y);
						}
			}
		_stopDevice = FALSE;
	}
	else if (_virtualCount > 0)
		_virtualCount = 0;
	return (TRUE);
}

void UI_MSC_DISPLAY::BitmapArrayToHandle(ZIL_SCREENID, int,
	int, const ZIL_UINT8 *, const UI_PALETTE *,
	ZIL_BITMAP_HANDLE *, ZIL_BITMAP_HANDLE *)
{
}

void UI_MSC_DISPLAY::BitmapHandleToArray(ZIL_SCREENID, ZIL_BITMAP_HANDLE,
		ZIL_BITMAP_HANDLE, int *, int *,
		ZIL_UINT8 **)
{
}

void UI_MSC_DISPLAY::DestroyBitmapHandle(ZIL_SCREENID, ZIL_BITMAP_HANDLE *colorBitmap, ZIL_BITMAP_HANDLE *monoBitmap)
{
	if (colorBitmap && *colorBitmap)
	{
		delete *colorBitmap;
		*colorBitmap = ZIL_NULLP(void);
	}
	if (monoBitmap && *monoBitmap)
	{
		delete *monoBitmap;
		*monoBitmap = ZIL_NULLP(void);
	}
}

void UI_MSC_DISPLAY::IconArrayToHandle(ZIL_SCREENID , int ,
	int , const ZIL_UINT8 *, const UI_PALETTE *,
	ZIL_ICON_HANDLE *)
{
}

void UI_MSC_DISPLAY::IconHandleToArray(ZIL_SCREENID , ZIL_ICON_HANDLE ,
	int *, int *, ZIL_UINT8 **)
{
}

void UI_MSC_DISPLAY::DestroyIconHandle(ZIL_SCREENID, ZIL_ICON_HANDLE *icon)
{
	if (icon && icon->colorBitmap)
	{
		delete icon->colorBitmap;
		icon->colorBitmap = ZIL_NULLP(void);
	}
	if (icon && icon->monoBitmap)
	{
		delete icon->monoBitmap;
		icon->monoBitmap = ZIL_NULLP(void);
	}
}


#	if __BORLANDC__ >= 0X0500
#		undef true
#	endif

