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

// The next two lines must be in this order to compile on SunOS 4.1.x
#include <z_stdlib.hpp>
#include <z_stdio.hpp>
#include <z_ctype.hpp>
#include <z_string.hpp>
#include <z_scrdsp.hpp>
#include <z_cset.hpp>
#include <z_pal.hpp>
#include <z_utils.hpp>
#include <math.h>

#define ZAF_SCREEN_DISPLAY_INFO											 
#include "gbl_def.cpp"

extern "C"
{
#include _DragDrop_
#include _PrimitiveP_
}

static int _virtualCount = 0;
static Display *_xDisplay = 0;
static GC _xGC = 0;
static GC _xorGC = 0;
static GC _blitGC = 0;
static Drawable _xDrawable = 0;
static XGCValues _gcValues;
static unsigned long _gcMask = GCFunction | GCFont | GCFillStyle |
	GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle | GCForeground |
	GCBackground | GCFillStyle | GCTile;
static ZafRegionStruct virtualRegion;

static struct _fontResource
{
	char *name;
	char *tag;
	XmFontType type;
	XmFontList fontList;
} fontResource[ZAF_MAXFONTS] =
{
#	if defined(ZAF_UNICODE)
	{ ZAF_NULLP(char), "ZafFntSmall", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFntDialog", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFntApplication", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFntSystem", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFntFixed", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFnt5", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFnt6", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFnt7", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFnt8", XmFONT_IS_FONTSET, 0 },
	{ ZAF_NULLP(char), "ZafFnt9", XmFONT_IS_FONTSET, 0 }
#	else
	{ ZAF_NULLP(char), "ZafFntSmall", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFntDialog", XmFONT_IS_FONT, 0 },
	{ "-*-helvetica-bold-r-*--12-*-iso8859-1", "ZafFntApplication", XmFONT_IS_FONT, 0 },
	{ "-*-helvetica-medium-r-*--14-*-iso8859-1", "ZafFntSystem", XmFONT_IS_FONT, 0 },
	{ "-*-fixed-medium-r-*--13-*-iso8859-1", "ZafFntFixed", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFnt5", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFnt6", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFnt7", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFnt8", XmFONT_IS_FONT, 0 },
	{ ZAF_NULLP(char), "ZafFnt9", XmFONT_IS_FONT, 0 }
#	endif
};

#if defined(ZAF_UNICODE)
// Variation of the example from Programmers Supplement for Release 5.
static XIMStyle ChooseBetterStyle(XIMStyle style1, XIMStyle style2)
{
	// Set to what are the allowed preedit and status flags
	XIMStyle preedit = XIMPreeditNothing | XIMPreeditNone;
	XIMStyle status =  XIMStatusNothing | XIMStatusNone;

	if (style1 == 0)
		return style2;
	if (style2 == 0)
		return style1;
	if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
		return style1;
    
	XIMStyle s1 = style1 & preedit;
	XIMStyle s2 = style2 & preedit;
	if (s1 != s2)
	{
		if ((s1 | s2) & XIMPreeditCallbacks)
			return (s1 & XIMPreeditCallbacks)?style1:style2;
		if ((s1 | s2) & XIMPreeditPosition)
			return (s1 & XIMPreeditPosition)?style1:style2;
		if ((s1 | s2) & XIMPreeditArea)
			return (s1 & XIMPreeditArea)?style1:style2;
		if ((s1 | s2) & XIMPreeditNothing)
			return (s1 & XIMPreeditNothing)?style1:style2;
	}
	else
	{
		s2 = style1 & status;
		s2 = style2 & status;
		if ((s1 | s2) & XIMStatusCallbacks)
			return (s1 & XIMStatusCallbacks)?style1:style2;
		if ((s1 | s2) & XIMStatusArea)
			return (s1 & XIMStatusArea)?style1:style2;
		if ((s1 | s2) & XIMStatusNothing)
			return (s1 & XIMStatusNothing)?style1:style2;
	}
	return 0;
}
#else
static char *fallback_resources[] =
{
	"?*buttonFontList: -*-helvetica-medium-r-*--12-*-iso8859-1",
	"?*textFontList: -*-helvetica-medium-r-*--12-*-iso8859-1",
	"?*labelFontList: -*-helvetica-bold-r-*--12-*-iso8859-1",
	"?*fontList: -*-helvetica-medium-r-*--12-*-iso8859-1",
	"?*ZafFntApplication: -*-helvetica-bold-r-*--12-*-iso8859-1",
#	if defined(_IBM_RS6000)
	"?*ZafFntSystem: -*-helvetica-medium-r-*--12-*-iso8859-1",
#	else
	"?*ZafFntSystem: -*-helvetica-medium-r-*--14-*-iso8859-1",
#	endif
	"?*ZafFntFixed: -*-fixed-medium-r-*--13-*-iso8859-1",
	ZAF_NULLP(char)
};
#endif

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

ZafScreenDisplay::ZafScreenDisplay(int &argc, char **argv) :
	ZafDisplay()
{
	int i;

	// Determine the application name.
	char appName[256];
	strcpy(appName, "ZincApp");
	if (argc > 0)
	{
		// Application names may be in several forms.  For example, the
		// following examples are valid: "hello", "xhello".  By convention,
		// this name should have the first letter capitalized, or if the
		// name begins with an 'x' then the first and second letters should
		// be capitalized. Pathnames may have either a / or \ directory 
		// separator.  In addition, the .exe extension must be eliminated.
		// For more information on resource files see:
		// 		Vol IV, Chapter 2.3.3-The App-defaults File
		// 		Vol IV, Chapter 10-Resource Management
		//		HP --- search directory, /usr/lib/X11/app-defaults
		//		DesqviewX --- \dvx\app-defa\<progname>.res
		int offset;
		char *arg = argv[0];
		for (offset = strlen(arg); offset > 0; offset--)
			if (arg[offset-1] == '\\' || arg[offset-1] == '/')
				break;
		strcpy(appName, &arg[offset]);
		// Strip the .exe extension.
		arg = strstr(appName, ".EXE");
		if (arg)
			*arg = '\0';
		// Check for a proper app-default naming.
		if (islower(appName[0]))
			appName[0] = toupper(appName[0]);
		if (appName[0] == 'X' && islower(appName[1]))
			appName[1] = toupper(appName[1]);
	}

	// Initialize the application.
#	if defined(ZAF_UNICODE)
	XtSetLanguageProc(0, 0, 0);
#	endif
	topLevelShell = XtAppInitialize(&xAppContext, appName,
		0, 0,
		&argc, argv,
#		if !defined(ZAF_UNICODE)
		fallback_resources,
#		else
		0,
#		endif
		0, 0);

	_xDisplay = xDisplay = XtDisplay(topLevelShell);
	_xDrawable = xDrawable = XtWindow(topLevelShell);
	xScreenNumber = DefaultScreen(xDisplay);

	// Set Drag and Drop to be dynamic protacol instead of preregister protacal.
	int nargs = 0;
	Arg args[10];
	Widget appDisplay = XmGetXmDisplay(xDisplay);
	XtSetArg(args[nargs], XmNdragInitiatorProtocolStyle, XmDRAG_PREFER_DYNAMIC); nargs++;
	XtSetArg(args[nargs], XmNdragReceiverProtocolStyle, XmDRAG_PREFER_DYNAMIC); nargs++;
	XtSetValues(appDisplay, args, nargs);

	// Determine the display size.
	columns = DisplayWidth(xDisplay, xScreenNumber);
	lines = DisplayHeight(xDisplay, xScreenNumber);

	// Set display default values (mini-cells, twips, and pixels-per-inch).
	miniNumeratorX = 1;
	miniDenominatorX = 10;
	miniNumeratorY = 1;
	miniDenominatorY = 10;

	// Determine the cell sizes (using the Label widget font)
	preSpace = 2;
	postSpace = 2;
	cellWidth = 0;
	cellHeight = 0;
	pixelsPerInchX = XmConvertUnits(topLevelShell, XmHORIZONTAL, Xm1000TH_INCHES, 1000, XmPIXELS);
	pixelsPerInchY = XmConvertUnits(topLevelShell, XmVERTICAL, Xm1000TH_INCHES, 1000, XmPIXELS);

	// Zinc only supports 16 colors in its portable bitmap array structure.
	// The logical and rgb colors defined below represent these colors. The 
	// definitions are taken from the system color database. See "Volume 1,
	// Appendix D - The Server-side Color Database" for more information.
	// Algorithm for gray scale is based on the NTSC standard (from the 
	// _x11rgb table below):
	//		intensity = ((.30 * red) + (.59 * green) + (.11 * blue)) / 255 * 100;
	// File location of color definitions:
	//		HP --- /usr/lib/X11/rgb.txt
	//		DesqviewX --- \dvx\server\rgb.txt
	//
	static struct
	{
		char *gray;		// gray scale (0..100)
		char *risc;		// risc based colors
	} colorName[] =
	{
		{ "gray0",		"black" },		// black	- MONO_BLACK
		{ "gray7",		"blue4" },		// blue		- ZAF_MONO_DIM
		{ "gray40",		"green4" },		// green	- ZAF_MONO_NORMAL
		{ "gray47",		"cyan4" },		// cyan		- ZAF_MONO_HIGH
		{ "gray20",		"red4" },		// red
		{ "gray27",		"magenta4" },	// magenta
		{ "gray40",		"brown4" },		// brown
		{ "gray67",		"light gray" },	// light gray
		{ "gray33",		"dim gray" },	// dark gray
		{ "gray41",		"blue1" },		// light blue
		{ "gray73",		"green1" },		// light green
		{ "gray80",		"cyan1" },		// light cyan
		{ "gray53",		"red1" },		// light red
		{ "gray61",		"magenta1" },	// light magenta
		{ "gray93",		"yellow1" },	// yellow
		{ "gray100",	"white" },		// white
		{ 0,			0 }				// end-of-table
	};
	// monoTable
	memset(monoTable, 0, sizeof(monoTable));
	for (i = ZAF_MONO_BLACK; i <= ZAF_MONO_DIM; i++)
	{
		monoTable[i].pixel = colorTable[i].pixel = XBlackPixel(xDisplay, xScreenNumber);
		monoTable[i].flags = colorTable[i].flags = DoRed | DoGreen | DoBlue;
	}
	for (i = ZAF_MONO_NORMAL; i <= ZAF_MONO_HIGH; i++)
	{
		monoTable[i].pixel = colorTable[i].pixel = XWhitePixel(xDisplay, xScreenNumber);
		monoTable[i].flags = colorTable[i].flags = DoRed | DoGreen | DoBlue;
	}
	// colorTable
	XVisualInfo visualInfo;
	unsigned int depth = XDefaultDepth(xDisplay, xScreenNumber);
	Colormap colorMap = XDefaultColormapOfScreen(XtScreen(topLevelShell));
	memset(colorTable, 0, sizeof(colorTable));
	if (depth == 1)
	{
		// System is in monochrome mode.
		displayMode = ZAF_DISPLAY_MONO;
	}
	else if (XMatchVisualInfo(xDisplay, xScreenNumber, depth, TrueColor, &visualInfo) || 
		XMatchVisualInfo(xDisplay, xScreenNumber, depth, DirectColor, &visualInfo) ||
		XMatchVisualInfo(xDisplay, xScreenNumber, depth, PseudoColor, &visualInfo) ||
		XMatchVisualInfo(xDisplay, xScreenNumber, depth, StaticColor, &visualInfo))
	{
		// System is in color mode.
		displayMode = ZAF_DISPLAY_COLOR;
		XColor screenColor, exactColor;
		for (i = 0; colorName[i].risc; i++)
			if (XAllocNamedColor(xDisplay, colorMap, colorName[i].risc, &screenColor, &exactColor))
			{
				colorTable[i] = screenColor;
				colorTable[i].flags = DoRed | DoGreen | DoBlue;
			}
	}
	else if (XMatchVisualInfo(xDisplay, xScreenNumber, depth, GrayScale, &visualInfo) ||
		XMatchVisualInfo(xDisplay, xScreenNumber, depth, StaticGray, &visualInfo))
	{
		// System is gray-scale.
		displayMode = ZAF_DISPLAY_COLOR;
		XColor screenColor, exactColor;
		for (i = 0; colorName[i].gray; i++)
			if (XAllocNamedColor(xDisplay, colorMap, colorName[i].gray, &screenColor, &exactColor))
			{
				colorTable[i] = screenColor;
				colorTable[i].flags = DoRed | DoGreen | DoBlue;
			}
	}

	// Set the MDI colors.
	XColor *activeMDI = &colorTable[ZAF_CLR_MDIACTIVE];
	*activeMDI = colorTable[ZAF_CLR_LIGHTGRAY];

	XColor *inactiveMDI = &colorTable[ZAF_CLR_MDIINACTIVE];
	*inactiveMDI = colorTable[ZAF_CLR_DARKGRAY];
	inactiveMDI->red += (activeMDI->red - inactiveMDI->red) / 2;
	inactiveMDI->green += (activeMDI->green - inactiveMDI->green) / 2;
	inactiveMDI->blue += (activeMDI->blue - inactiveMDI->blue) / 2;
	XAllocColor(xDisplay, colorMap, inactiveMDI);

	// Initialize the patternTable.
	memset(patternTable, 0, sizeof(patternTable));
	patternTable[ZAF_PTN_SOLID_FILL] = FillSolid;
	patternTable[ZAF_PTN_INTERLEAVE_FILL] = FillStippled;

	// Initialize the penTable.
	//
	memset(lineTable, 0, sizeof(lineTable));
	lineTable[ZAF_LINE_SOLID] = LineSolid;
	lineTable[ZAF_LINE_DOTTED] = LineOnOffDash;

	// Initialize the modeTable.
	//
	memset(modeTable, 0, sizeof(modeTable));
	modeTable[ZAF_MODE_COPY] = GXcopy;
	modeTable[ZAF_MODE_XOR] = GXxor;

	// Set up the xGC and xorGC.
	XGCValues gcValues;
	_xGC = xGC = XCreateGC(xDisplay, RootWindow(xDisplay, xScreenNumber), 0, &gcValues);
	_blitGC = XCreateGC(xDisplay, RootWindow(xDisplay, xScreenNumber), 0, &gcValues);
	XGetGCValues(xDisplay, xGC, _gcMask, &_gcValues);

	gcValues.function = GXxor;
	gcValues.line_width = 2;
	gcValues.line_style = LineSolid;
	gcValues.subwindow_mode = IncludeInferiors;
	gcValues.background = gcValues.foreground = (unsigned long)~0;
	_xorGC = XCreateGC(xDisplay, RootWindow(xDisplay, xScreenNumber),
		GCFunction | GCLineWidth | GCLineStyle | GCSubwindowMode | 
		GCForeground | GCBackground, &gcValues);

	// Initialize the fontTable.
	//
	// File location of default font definitions:
	//		HP --- search directory, /usr/lib/X11/app-defaults
	//		DesqviewX --- \dvx\app-defa\<progname>.res
	// Font files are located in various directories:
	//		HP --- /usr/lib/X11/fonts, /usr/lib/X11/nls
	//		DesqviewX --- \dvx\server\fonts, \dvx\lib\x11\nls
	// Valid font requests for VendorShell are:
	// 		XmNbuttonFontList, XmNlabelFontList, and XmNtextFontList.
	//		See Volume Six A, Motif Programming Manual, Chapter 19.2.2 
	//		Font List Tags for more information about these fonts. In
	//		addition, you can run the application xlsfonts for information
	//		about fonts available on your system.
	//
	// Actual font manipulation is done at two levels: Xm and X. The
	// following structures are defined:
	// 		XmFontList, XmFontListEntry, XmFontContext
	// 		XFontSet, Font, XFontSetExtents, XFontStruct
	//
	// Possible values for initial search are retreived from VendorShell:
	// XmNbuttonFontList, XmNlabelFontList, and XmNtextFontList.
	memset(fontTable, 0, sizeof(fontTable));
	XtVaGetValues(topLevelShell,
		XmNtextFontList, &fontTable[ZAF_FNT_SMALL],
		XmNtextFontList, &fontTable[ZAF_FNT_DIALOG],
		XmNbuttonFontList, &fontTable[ZAF_FNT_APPLICATION],
		XmNbuttonFontList, &fontTable[ZAF_FNT_SYSTEM],
		0);
	// For window size to be correct ZafFntDialog must match textFontList.
	// Check for an override of the defaults.
	for (i = 0; i < ZAF_MAXFONTS; i++)
	{
		if (i <= ZAF_FNT_FIXED || (i > ZAF_FNT_FIXED && fontResource[i].name))
		{
			XtResource resource;
			resource.resource_name = fontResource[i].tag;
			resource.resource_class = XmCFontList;
			resource.resource_type = XmRFontList;
			resource.resource_size = sizeof(XmFontList);
			resource.resource_offset = XtOffsetOf(_fontResource, fontList);
			resource.default_type = XmRString;
			// Don't create a default.
			resource.default_addr = NULL;

			XtGetApplicationResources(topLevelShell, &fontResource[i], &resource,
			 1, NULL, 0);
		}
	}

	// Override default font values.
	for (i = 0; i < ZAF_MAXFONTS; i++)
		// Check for the default font.
		if (fontResource[i].fontList)
			fontTable[i] = fontResource[i].fontList;
		else if (!fontTable[i] && i < ZAF_FNT_FIXED)
		{
			// If a font is not created create a very basic font.  Should never happen.
			XmFontListEntry entry = XmFontListEntryLoad(xDisplay,	"-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
			 fontResource[i].type, fontResource[i].tag);
			if (entry)
			{
				fontResource[i].fontList = XmFontListAppendEntry(0, entry);
				XmFontListEntryFree(&entry);
				if (fontResource[i].fontList)
					fontTable[i] = fontResource[i].fontList;
			}
			if (!fontTable[i])
				//Big problem! No font created we need to exit.
				exit(1);
		}

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

	// Initialize the palette structure.
	palette.fillPattern = ZAF_PTN_DEFAULT;
	palette.colorForeground = palette.colorBackground = ZAF_CLR_DEFAULT;
	palette.monoForeground = palette.monoBackground = ZAF_MONO_DEFAULT;
	palette.lineStyle = ZAF_LINE_DEFAULT;
	palette.font = ZAF_FNT_DIALOG;
	palette.osPalette = 0;

	clipRegion.Assign(0, 0, columns, lines, ZAF_PIXEL);

	virtualRegion.Assign(0, 0, columns, lines, ZAF_PIXEL);

	mode = ZAF_MODE_COPY;

	// Initialize the input method.
	ic = 0;
#	if defined(ZAF_UNICODE)
	XFontSet fontSet = MtfGetFontSet(fontTable[palette.font]);
	XIM inputMethod = XOpenIM(xDisplay, NULL, NULL, NULL);
	if (inputMethod)
	{
		XIMStyles *styles;
		XGetIMValues(inputMethod, XNQueryInputStyle, &styles, NULL);
		XIMStyle best = 0;
		for (i = 0; i < styles->count_styles; i++)
			best = ChooseBetterStyle(styles->supported_styles[i], best);
		if (best == 0)
		{
			fprintf(stderr, "Can't support requested interaction style.\n");
			exit(1);
		}
		XVaNestedList list = XVaCreateNestedList(0, XNFontSet, fontSet, NULL);
		ic = XCreateIC(inputMethod, XNInputStyle, best, XNPreeditAttributes,
			list, XNStatusAttributes, list, NULL);
		XFree(list);
		if (ic)
		{
			// XGetICValues(ic, XNFilterEvents, &im_event_mask, NULL);
			// XSelectInput(xDisplay, win, ExposureMask | KeyPressMask
			// 	     | StructureNotifyMask | im_event_mask);
			XSetICFocus(ic);
		}
	}
#	endif
}

ZafScreenDisplay::~ZafScreenDisplay(void)
{
	// Free the fonts.
	for (int i = 0; i < ZAF_MAXFONTS; i++)
	{
		// Check for any fontLists that may be used more than once.
		for (int j = i + 1; j < ZAF_MAXFONTS; j++)
			if (fontResource[j].fontList && 
			 fontResource[i].fontList == fontResource[j].fontList)
				fontResource[j].fontList = 0;
		if (fontResource[i].fontList)
		{
			XmFontListFree(fontResource[i].fontList);
			fontResource[i].fontList = 0;
		}
	}

	// Free the GCs
	XFreeGC(xDisplay, _xGC);
	XFreeGC(xDisplay, _xorGC);
	XFreeGC(xDisplay, _blitGC);

	// Free the top-level shell.
	if (XtIsRealized(topLevelShell))
		XtDestroyWidget(topLevelShell);

	// Close the input method.
#if defined(ZAF_UNICODE)
	if (ic)
		XCloseIM(XIMOfIC(ic));
#endif
}

ZafError ZafScreenDisplay::BeginDraw(OSDisplayContext displayContext,
	OSDrawContext drawContext, const ZafRegionStruct &draw,
	const ZafRegionStruct &clip)
{
	// Clear the display context.
	if (_virtualCount++ == 0)
	{
		// Reset the display context values.
		xDisplay = displayContext;
		xDrawable = drawContext;
		xGC = _xGC;
		XGetGCValues(xDisplay, xGC, _gcMask, &_gcValues);

		// Set the draw region.
		virtualRegion = draw;
	}

	// Set the clip region.
	SetClipRegion(clip);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::EndDraw(void)
{
	// Set the display context.
	if (--_virtualCount == 0)
	{
		// Reset the motif information.
		xDisplay = _xDisplay;
		xDrawable = _xDrawable;
		xGC = _xGC;

		// Reset the palette information.
		palette.fillPattern = ZAF_PTN_DEFAULT;
		palette.colorForeground = palette.colorBackground = ZAF_CLR_DEFAULT;
		palette.monoForeground = palette.monoBackground = ZAF_MONO_DEFAULT;
		palette.font = ZAF_FNT_DIALOG;
		palette.osPalette = 0;

		clipRegion.Assign(0, 0, columns, lines, ZAF_PIXEL);

		virtualRegion.Assign(0, 0, columns, lines, ZAF_PIXEL);

		mode = ZAF_MODE_COPY;
		coordinateType = ZAF_PIXEL;
		originX = originY = 0;
		scaleNumerator = scaleDenominator = 1;
	}

	// Restore the original clip region.
	SetClipRegion(virtualRegion);

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

ZafError ZafScreenDisplay::Bitmap(ZafCoordinate column, ZafCoordinate line, ZafBitmapStruct &bitmap)
{
	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Check for a valid bitmap handle.
	if (!bitmap.handle && bitmap.array)
		ZafArrayToOSPixmap(bitmap.width, bitmap.height, bitmap.array,
			&bitmap.handle, &bitmap.mask);
	if (!bitmap.handle)
		return (ZAF_ERROR_INVALID);

	// Draw the bitmap.
	if (bitmap.mask)
	{
		// Create a temporary pixmap.
		Drawable drawable = RootWindowOfScreen(XtScreen(topLevelShell));
		unsigned int depth = XDefaultDepth(xDisplay, xScreenNumber);
		Pixmap pixmap = XCreatePixmap(xDisplay, drawable, bitmap.width,
			bitmap.height, depth);

		// Copy the bitmap image.
		XSetClipMask(xDisplay, _blitGC, None);
		XSetFunction(xDisplay, _blitGC, GXcopy);
		XCopyArea(xDisplay, xDrawable, pixmap, _blitGC, pixelColumn, pixelLine,
			bitmap.width, bitmap.height, 0, 0);
		XSetFunction(xDisplay, _blitGC, GXandInverted);
		XCopyArea(xDisplay, bitmap.mask, pixmap, _blitGC, 0, 0,
			bitmap.width, bitmap.height, 0, 0);
		XSetFunction(xDisplay, _blitGC, GXor);
		XCopyArea(xDisplay, bitmap.handle, pixmap, _blitGC, 0, 0,
			bitmap.width, bitmap.height, 0, 0);

		// Blit the pixmap.
		XCopyArea(xDisplay, pixmap, xDrawable, xGC, 0, 0,
			bitmap.width, bitmap.height, pixelColumn, pixelLine);

		// Free the pixmap.
		XFreePixmap(xDisplay, pixmap);
	}
	else
		XCopyArea(xDisplay, bitmap.handle, xDrawable, xGC, 0, 0,
			bitmap.width, bitmap.height, pixelColumn, pixelLine);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;
	
	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToOSBitmap(ZafBitmapStruct &bitmap)
{
	// Call the bitmap array-to-handle function.
	ZafArrayToOSPixmap(bitmap.width, bitmap.height, bitmap.array,
		&bitmap.handle, &bitmap.mask);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToZafBitmap(ZafBitmapStruct &bitmap)
{
	// Call the bitmap handle-to-array function.
	OSPixmapToZafArray(&bitmap.width, &bitmap.height, &bitmap.array,
		bitmap.handle, bitmap.mask);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyOSBitmap(ZafBitmapStruct &bitmap)
{
	// Destroy the color handle and mask.
	if (!bitmap.StaticHandle())
	{
		if (bitmap.handle)
			XFreePixmap(xDisplay, bitmap.handle);
		if (bitmap.mask)
			XFreePixmap(xDisplay, bitmap.mask);
	}
	bitmap.handle = bitmap.mask = 0;

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

ZafError ZafScreenDisplay::Icon(ZafCoordinate column, ZafCoordinate line, ZafIconStruct &icon)
{
	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Check for a valid icon handle.
	if (!icon.handle && icon.array)
		ZafArrayToOSPixmap(icon.width, icon.height, icon.array, &icon.handle,
			&icon.mask);
	if (!icon.handle)
		return (ZAF_ERROR_INVALID);

	// Draw the icon.
	if (icon.mask)
	{
		// Create a temporary pixmap.
		Drawable drawable = RootWindowOfScreen(XtScreen(topLevelShell));
		unsigned int depth = XDefaultDepth(xDisplay, xScreenNumber);
		Pixmap pixmap = XCreatePixmap(xDisplay, drawable, icon.width,
			icon.height, depth);

		// Copy the icon image.
		XSetFunction(xDisplay, _blitGC, GXcopy);
		XCopyArea(xDisplay, xDrawable, pixmap, _blitGC, pixelColumn, pixelLine,
			icon.width, icon.height, 0, 0);
		XSetFunction(xDisplay, _blitGC, GXandInverted);
		XCopyArea(xDisplay, icon.mask, pixmap, _blitGC, 0, 0,
			icon.width, icon.height, 0, 0);
		XSetFunction(xDisplay, _blitGC, GXor);
		XCopyArea(xDisplay, icon.handle, pixmap, _blitGC, 0, 0,
			icon.width, icon.height, 0, 0);

		// Blit the pixmap.
		XCopyArea(xDisplay, pixmap, xDrawable, xGC, 0, 0,
			icon.width, icon.height, pixelColumn, pixelLine);

		// Free the pixmap.
		XFreePixmap(xDisplay, pixmap);
	}
	else
		XCopyArea(xDisplay, icon.handle, xDrawable, xGC, 0, 0,
			icon.width, icon.height, pixelColumn, pixelLine);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToOSIcon(ZafIconStruct &icon)
{
	// Call the bitmap array-to-handle function.
	ZafArrayToOSPixmap(icon.width, icon.height, icon.array, &icon.handle,
		&icon.mask);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToZafIcon(ZafIconStruct &icon)
{
	// Call the bitmap handle-to-array function.
	OSPixmapToZafArray(&icon.width, &icon.height, &icon.array, icon.handle, icon.mask);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyOSIcon(ZafIconStruct &icon)
{
	// Destroy the handle.
	if (!icon.StaticHandle())
	{
		if (icon.handle)
			XFreePixmap(xDisplay, icon.handle);
		if (icon.mask)
			XFreePixmap(xDisplay, icon.mask);
	}
	icon.handle = icon.mask = 0;

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

ZafError ZafScreenDisplay::Mouse(ZafCoordinate column, ZafCoordinate line, ZafMouseStruct &mouse)
{
	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Check for a valid mouse handle.
	if (!mouse.handle)
		ZafArrayToOSPixmap(mouse.width, mouse.height, mouse.array,
			&mouse.handle, 0);
	if (!mouse.handle)
		return (ZAF_ERROR_INVALID);

	// Draw the mouse.
	XCopyArea(xDisplay, mouse.handle, xDrawable, xGC, 0, 0,
		mouse.width, mouse.height, pixelColumn, pixelLine);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;
	
	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToOSMouse(ZafMouseStruct &mouse)
{
	// Call the bitmap array-to-handle function.
	ZafArrayToOSPixmap(mouse.width, mouse.height, mouse.array, &mouse.handle, 0);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::ConvertToZafMouse(ZafMouseStruct &mouse)
{
	// Call the bitmap handle-to-array function.
	OSPixmapToZafArray(&mouse.width, &mouse.height, &mouse.array, mouse.handle, 0);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyOSMouse(ZafMouseStruct &mouse)
{
	// Destroy the handle.
	if (mouse.handle && !mouse.StaticHandle())
		XFreeCursor(xDisplay, mouse.handle);
	mouse.handle = 0;

	// Return success.
	return (ZAF_ERROR_NONE);
}

// --- Pixmap ---------------------------------------------------------------

bool ZafScreenDisplay::ZafArrayToOSPixmap(int width, int height,
	ZafLogicalColor *array, Pixmap *handle, Pixmap *mask)
{
	// Check for valid parameters.
	if (!array || !width || !height)
		return (ZAF_ERROR_NONE);

	// We need a whole copy of the image, not a clipped version.
	ZafRegionStruct saveClip = SetClipRegion(virtualRegion);

	// Initialize some needed values.
	Drawable drawable = RootWindowOfScreen(XtScreen(topLevelShell));
	unsigned int depth = XDefaultDepth(xDisplay, xScreenNumber);
	Visual *visual = XDefaultVisual(xDisplay, xScreenNumber);

	// Create the image (client side).
	int pad = (depth > 16) ? 32 : (depth > 8) ? 16 : 8;
	XImage *image = XCreateImage(xDisplay, visual, depth, ZPixmap, 0,
		ZAF_NULLP(char), width, height, pad, 0);
	image->data = (char *)malloc(image->bytes_per_line * height);

	// Convert the zinc values to x11 colors, based on the type of X visual.
	unsigned long black = 0;  
	unsigned long white = ~0;
	int row, column;
	ZafLogicalColor *temp;
	for (row = 0, temp = array; row < height; row++)
		for (column = 0; column < width; column++, temp++)
			if (*temp == ZAF_CLR_BACKGROUND)
				XPutPixel(image, column, row, black);
			else
				XPutPixel(image, column, row, white);
	*mask = XCreatePixmap(xDisplay, drawable, width, height, depth);
	XPutImage(xDisplay, *mask, xGC, image, 0, 0, 0, 0, width, height);

	// Convert to a pixmap (server side).
	for (row = 0, temp = array; row < height; row++)
		for (column = 0; column < width; column++, temp++)
			if (*temp == ZAF_CLR_BACKGROUND)
				XPutPixel(image, column, row, black);
			else if (displayMode == ZAF_DISPLAY_MONO)
				XPutPixel(image, column, row, monoTable[*temp].pixel);
			else
				XPutPixel(image, column, row, colorTable[*temp].pixel);
	*handle = XCreatePixmap(xDisplay, drawable, width, height, depth);
	XPutImage(xDisplay, *handle, xGC, image, 0, 0, 0, 0, width, height);

	// Restore the clip area and return success.
	XDestroyImage(image);
	SetClipRegion(saveClip);

	// Return success.
	return (ZAF_ERROR_NONE);
}

bool ZafScreenDisplay::OSPixmapToZafArray(int *widthPtr, int *heightPtr,
	ZafLogicalColor **arrayPtr, Pixmap handle, Pixmap)
{
	int left, top;
	unsigned int width, height, border;
	Window rootWindow;
	unsigned int depth = XDefaultDepth(xDisplay, xScreenNumber);
	XGetGeometry(xDisplay, handle, &rootWindow, &left, &top, &width,
		&height, &border, &depth);
	ZafLogicalColor *array = new ZafLogicalColor[width * height];

	// Set the arguments.
	if (arrayPtr)
		*arrayPtr = array;
	if (widthPtr)
		*widthPtr = width;
	if (heightPtr)
		*heightPtr = height;

	// Get the image (server side).
	int minusOne = -1;
	XImage *image = XGetImage(xDisplay, handle, 0, 0, width, height,
		minusOne, ZPixmap);
	Colormap colorMap = XDefaultColormapOfScreen(XtScreen(topLevelShell));
	for (int y = 0; y < (int)height; y++)
		for (int x = 0; x < (int)width; x++)
		{
			*array = ZAF_CLR_BACKGROUND;
			XColor color;
			color.pixel = XGetPixel(image, x, y);
 			XQueryColor(xDisplay, colorMap, &color);

 			color.red = ((unsigned short)color.red >> 8);
			color.green = ((unsigned short)color.green >> 8);
			color.blue = ((unsigned short)color.blue >> 8);
 
			ZafUInt32 minErrorValue = 0xffffffff;
			
			for (int z = 0; z < ZAF_MAXCOLORS; z++)
				if ((displayMode == ZAF_DISPLAY_MONO) && (monoTable[z].pixel == color.pixel))
				{
					*array = (ZafLogicalColor)z;
					break;
				}
				else 
				{
					if (colorTable[z].pixel == color.pixel)
					{
						*array = (ZafLogicalColor)z;
						break;
					}

					ZafUInt8 redDif = colorTable[z].red - color.red;
					ZafUInt8 greenDif = colorTable[z].green - color.green;
					ZafUInt8 blueDif = colorTable[z].blue - color.blue;
					ZafUInt32 errorValue = (redDif * redDif) + (greenDif * greenDif) 
					 + (blueDif * blueDif);
					if (errorValue == 0)
					{
						*array = (ZafLogicalColor)z;
						break;
					}
					else if (errorValue < minErrorValue)
					{
						minErrorValue = errorValue;
						*array = (ZafLogicalColor)z;
					}
				}
			array++;
		}

	// Return success.
	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 there is a valid string.
	int i, j;
	if (!text || text[0] == '\0')
		return (ZAF_ERROR_NONE);
	if (length < 0)
		length = strlen(text);

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

	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	// Fill the region.
	if (fill)
		Rectangle(pixelLeft, pixelTop, pixelRight, pixelBottom, 0, fill);

	// Unicode handling of text.
#if defined(ZAF_UNICODE)
	XFontSet fontSet = MtfGetFontSet(fontTable[palette.font]);
	if (fontSet)
	{
		// Compute the text region.
		XRectangle ink, logical;
		char *temp = new char[length*sizeof(ZafIChar)+1];
		for (i = 0, j = 0; i < length; i++)
			j += zafCodeSet->ConvertToOSChar(text[i], &temp[j]);
		temp[j] = '\0';
		wchar_t *osText = new wchar_t[length + 1];
		::mbstowcs(osText, temp, length + 1);
		delete []temp;

		XwcTextExtents(fontSet, osText, length, &ink, &logical);
		// pixelLeft
		if (hzJustify == ZAF_HZ_CENTER)
			pixelLeft += (pixelRight - pixelLeft - ink.width) / 2;
		else if (hzJustify == ZAF_HZ_RIGHT)
			pixelLeft = pixelRight - ink.width;
		// pixelTop
		if (vtJustify == ZAF_VT_TOP)
			pixelBottom = pixelTop + ink.height;
		else if (vtJustify == ZAF_VT_BOTTOM)
			pixelTop = pixelBottom - ink.height;
		else
			pixelTop += (ink.height - ink.x) + (pixelBottom - pixelTop - ink.height) / 2;

		// Draw the text.
		XwcDrawString(xDisplay, xDrawable, fontSet, xGC, pixelLeft, pixelTop, osText, length);

		// Draw the hotkey.
		if (hotKeyIndex >= 0 && hotKeyIndex < length)
		{
			// Save needed values.
			pixelTop += ink.x;
			int width = ink.width / length;

			// Re-compute the text for line spacing.
			length = 0;
			for (i = 0; i < hotKeyIndex; i++)
				length += zafCodeSet->ConvertToOSWChar(text[i], &osText[length]);
			osText[length] = '\0';

			// Draw the underline.
			XwcTextExtents(fontSet, osText, length, &ink, &logical);
			pixelLeft += ink.width;
			pixelRight = pixelLeft + width - 1;
			XDrawLine(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, pixelRight, pixelTop);
		}

		// Reset the xGC if in Xor mode.
		if (mode == ZAF_MODE_XOR)
			xGC = tempGC;

		// Return the status.
		delete []osText;
		return (ZAF_ERROR_NONE);
	}
#endif

	// Check for a valid font.
	XGCValues osFont;
	XGetGCValues(xDisplay, xGC, GCFont, &osFont);
	if ((int)osFont.font == -1)
	{
		XFontStruct *font = MtfGetFont(fontTable[palette.font > 0 ? palette.font : ZAF_FNT_DIALOG]);
		XSetFont(xDisplay, xGC, font->fid);
		osFont.font = font->fid;
	}

	// Initialize positioning variables.
	XCharStruct overall;
	int direction, ascent, descent, column = pixelLeft, line = pixelTop;
	char *osText = new char[length*sizeof(ZafIChar)+1];
	for (i = 0, j = 0; i < length; i++)
		j += zafCodeSet->ConvertToOSChar(text[i], &osText[j]);
	osText[j] = '\0';

	// Look for tab and newline segments.
	char *beginText = osText, *endText = osText;
	for ( ; *endText && length > 0; endText++, length--)
		if (*endText == '\t')
		{
			// Draw up to the tab character.
			int size = (int)(endText - beginText);
			if (size)
			{
				XQueryTextExtents(xDisplay, osFont.font, beginText, size, 
					&direction, &ascent, &descent, &overall);
				if (line == pixelTop)
				{
					// line has not been converted.
					if (vtJustify == ZAF_VT_TOP)
						line = pixelTop + ascent;
					else if (vtJustify == ZAF_VT_BOTTOM)
						line = pixelBottom - descent;
					else
						line = pixelTop + ascent + (pixelBottom - pixelTop - ascent - descent) / 2;
				}
				XDrawString(xDisplay, xDrawable, xGC, column, line, beginText, size);
			}
			else
				overall.width = 0;
			beginText = &endText[1];

			// Reset the tab on a half-inch boundary.
			int tabWidth = (int)pixelsPerInchX / 2;
			column += overall.width;
			column = (column - pixelLeft) / tabWidth + 1;
			column = pixelLeft + column * tabWidth;
		}
		else if (*endText == '\n' || *endText == '\r')
		{
			// Draw up to the newline character.
			int size = (int)(endText - beginText);
			XQueryTextExtents(xDisplay, osFont.font, beginText, size ? size : length,
				&direction, &ascent, &descent, &overall);
			if (line == pixelTop)
			{
				line = pixelTop + ascent;
				vtJustify = ZAF_VT_TOP;
			}
			if (size)
				XDrawString(xDisplay, xDrawable, xGC, column, line, beginText, size);
			if (*endText == '\r' && endText[1] == '\n')
				endText++, length--;
			beginText = &endText[1];

			// Reset the line and column values.
			line += ascent + descent + 1;
			column = pixelLeft;
		}

	// Compute, then draw, the final text segment.
	length = (int)(endText - beginText);
	XQueryTextExtents(xDisplay, osFont.font, beginText, length, &direction,
		&ascent, &descent, &overall);
	// column
	if (column == pixelLeft)
	{
		// column has not been converted.
		if (hzJustify == ZAF_HZ_CENTER)
			column = pixelLeft + (pixelRight - pixelLeft - overall.width) / 2;
		else if (hzJustify == ZAF_HZ_RIGHT)
			column = pixelRight - overall.width + 1;
	}
	// line
	if (line == pixelTop)
	{
		// line has already been converted.
		if (vtJustify == ZAF_VT_TOP)
			line = pixelTop + ascent;
		else if (vtJustify == ZAF_VT_BOTTOM)
			line = pixelBottom - descent;
		else
			line = pixelTop + ascent + (pixelBottom - pixelTop - ascent - descent) / 2;
	}
	// text
	XDrawString(xDisplay, xDrawable, xGC, column, line, beginText, length);

	// Draw the hotkey.
	if (hotKeyIndex >= 0 && hotKeyIndex < length)
	{
		// Save needed values.
		line += descent - 1;
		int width = overall.width / length;

		// Re-compute the text for line spacing.
		if (hotKeyIndex)
		{
			length = 0;
			for (i = 0; i < hotKeyIndex; i++)
				length += zafCodeSet->ConvertToOSChar(text[i], &osText[length]);
			XQueryTextExtents(xDisplay, osFont.font, osText, length,
				&direction, &ascent, &descent, &overall);
			column += overall.width;
		}

		// Draw the underline.
		XDrawLine(xDisplay, xDrawable, xGC, column, line, column + width - 1, line);
	}

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	delete []osText;
	return (ZAF_ERROR_NONE);
}

ZafRegionStruct ZafScreenDisplay::TextSize(const ZafIChar *text, int length)
{
	// Initialize the return region.
	int i, j;
	ZafRegionStruct region;
	// Following line is needed to remove a warning on broken compilers.
	region.top = 0;
	region.Assign(1, 1, 0, 0, ZAF_PIXEL);

	// Check for a valid string.
	if (!text || !text[0])
		return (region);
	if (length < 0)
		length = strlen(text);

#if defined(ZAF_UNICODE)
	// Height is based on the font, not the actual text.
	XFontSet fontSet = MtfGetFontSet(fontTable[palette.font]);
	if (fontSet)
	{
		// Convert the text.
		char *temp = new char[length*sizeof(ZafIChar)+1];
		for (i = 0, j = 0; i < length; i++)
			j += zafCodeSet->ConvertToOSChar(text[i], &temp[j]);
		temp[j] = '\0';
		wchar_t *osText = new wchar_t[length + 1];
		::mbstowcs(osText, temp, length + 1);
		delete []temp;

		// Determine the text size.
		XRectangle ink, logical;
		XwcTextExtents(fontSet, osText, strlen(osText), &ink, &logical);
		region.bottom = ink.height - 1;
		region.right = ink.width - 1;

		// Return the region.
		delete []osText;
		return (region);
	}
#endif

	// Check for a valid font.
	XGCValues osFont;
	XGetGCValues(xDisplay, xGC, GCFont, &osFont);
	if ((int)osFont.font == -1)
	{
		XFontStruct *font = MtfGetFont(fontTable[palette.font > 0 ? palette.font : ZAF_FNT_DIALOG]);
		XSetFont(xDisplay, xGC, font->fid);
		osFont.font = font->fid;
	}

	// Compute the overall size of the text.
	XCharStruct overall;
	int direction, ascent = 0, descent = 0, column = 0;
	char *osText = new char[length*sizeof(ZafIChar)+1];
	for (i = 0, j = 0; i < length; i++)
		j += zafCodeSet->ConvertToOSChar(text[i], &osText[j]);
	osText[j] = '\0';

 	int longestLine = 0;
	// Check for multi-line calculations.
	char *beginText = osText, *endText = osText;
	for ( ; *endText && length > 0; endText++, length--)
		if (*endText == '\t')
		{
			// Compute up to the tab character.
			int size = (int)(endText - beginText);
			if (size)
				XQueryTextExtents(xDisplay, osFont.font, beginText, size, 
					&direction, &ascent, &descent, &overall);
			else
				overall.width = 0;
			beginText = &endText[1];

			// Reset the tab on a half-inch boundary.
			int tabWidth = (int)pixelsPerInchX / 2;
			column += overall.width;
			column = column / tabWidth + 1;
			column = column * tabWidth;
		}
		else if (*endText == '\n' || *endText == '\r')
		{
			// Compute up to the newline character.
			int size = (int)(endText - beginText);
			if (size > longestLine)
				longestLine = size;
			XQueryTextExtents(xDisplay, osFont.font, beginText, size ? size : longestLine,
				&direction, &ascent, &descent, &overall);
			if (*endText == '\r' && endText[1] == '\n')
				endText++;
			beginText = &endText[1];

			// Reset the line and column values.
			if (region.right < column + overall.width)
				region.right = column + overall.width - 1;
			region.bottom += ascent + descent + 1;
			column = 0;
		}

	// Finish calculations for the last line.
	length = (int)(endText - beginText);
	XQueryTextExtents(xDisplay, osFont.font, beginText, length,
		&direction, &ascent, &descent, &overall);
	if (region.right < column + overall.width)
		region.right = column + overall.width + 1;
	region.bottom += ascent + descent + 1;

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

	// Delete the text and return the region.
	delete []osText;
	return (region);
}

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

ZafError ZafScreenDisplay::Ellipse(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	float startAngle, float endAngle, int lineWidth, 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 pixelLineWidth = (lineWidth > 0) ? ZafMax((int)(ConvertXValue(lineWidth, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(lineWidth == ZAF_HAIR_LINE ? 1 : 0);

	// Normalize the start and end angles of the ellipse.
	while (startAngle > 360.0)
		startAngle -= 360.0;
	while (startAngle < 0.0)
		startAngle += 360.0;
	while (endAngle > 360.0)
		endAngle -= 360.0;
	while (endAngle < 0.0)
		endAngle += 360.0;									

	// Calculate width and height of bounding rectangle.
	int width = pixelRight - pixelLeft + 1;
	int height = pixelBottom - pixelTop + 1;

	// Motif does not take into acount the aspect ration of an ellipse, they
	// assume the bounding rectangle is a 1 high by 1 wide, because of this 
	// the angle drawn does not match the angle passed in.
	// To ensure that the angle passed in is what is drawn we must caclulate
	// the angle drawn in a 1 by 1 square that will give the correct angle
	// when the asspect ration of the bounding rectangle is applied and pass 
	// this angle to Motif.

	// Calculate angle Motif needs to draw arc with correct aspect.
	int startQuad = 0;
	int endQuad = 0;
	// Normalize angles to first quadrant.
	while (startAngle > 90.0)
	{
		startQuad++;
		startAngle -= 90;
	}
	while (endAngle > 90.0)
	{
		endQuad++;
		endAngle -= 90;
	}

	// Convert angles to radians and find new angle with aspect ratio.
	const double pieOver180 = 0.0174529278;
	// Use aspect for angles in quadrent 1 and 3, inverseAspect for 2 and 4.
	double aspect = (double)width / (double)height;
	double inverseAspect = (double)height / (double)width;

	// Angles of 0 and 90 will be correct so they can be ignored.
	if (startAngle != 0 && startAngle != 90.0)
	{
		double startRad = startAngle * pieOver180;
		// Calculate new angles in radians.										
		if (startQuad == 0 || startQuad == 2)
			startRad = atan(aspect * tan(startRad));
		else
			startRad = atan(inverseAspect * tan(startRad));
		// Convert radians back to angles.
		startAngle = startRad / pieOver180; 
	}
	if (endAngle != 0 && endAngle != 90.0)
	{
		double endRad = endAngle * pieOver180;
		if (endQuad == 0 || endQuad == 2)
			endRad = atan(aspect * tan(endRad));
		else
			endRad = atan(inverseAspect * tan(endRad));
		endAngle = endRad / pieOver180;
	}
	// Return angle to original quadrant.
	startAngle = startAngle + startQuad * 90; 
	endAngle = endAngle + endQuad * 90; 

	// Nomralize end angle. Motif referances the end angle from the start angle.
	// Zaf has starting angle and end angle both referanced from 0.
	if (endAngle > startAngle)
		endAngle -= startAngle;
	else
		// Accound for starAngle being less than 360 and endAngle being more.
		endAngle = 360 - startAngle + endAngle;

	// Motif uses 64th of a degree values.
	startAngle *= 64.0;
	endAngle *= 64.0;

	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	// Check the line width.
	XGCValues osLine;
	XGetGCValues(xDisplay, xGC, GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle, &osLine);
	if (pixelLineWidth == 1)
		pixelLineWidth = 0; // Drawing optimization (See XSetLineAttribute()).
	if (pixelLineWidth != osLine.line_width)
		XSetLineAttributes(xDisplay, xGC, pixelLineWidth, osLine.line_style,
			osLine.cap_style, osLine.join_style);

	// Fill the ellipse.
	if (fill && _gcValues.fill_style == FillSolid && palette.colorBackground < ZAF_MAXCOLORS)
	{
		// Motif uses the foreground color to fill solid polygons.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoBackground].pixel);
		else
			XSetForeground(xDisplay, xGC, colorTable[palette.colorBackground].pixel);
		XFillArc(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width, height, (int)startAngle, (int)endAngle);
		// Reset the foreground color on the GC.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoForeground].pixel);
		else
			XSetForeground(xDisplay, xGC, colorTable[palette.colorForeground].pixel);	
	}
	else if (fill)
		XFillArc(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width + 1, height + 1, (int)startAngle, (int)endAngle);

	// Draw the ellipse.
	XDrawArc(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width, height, (int)startAngle, (int)endAngle);

	// Restore the line width.
	if (pixelLineWidth != osLine.line_width)
		XSetLineAttributes(xDisplay, xGC, osLine.line_width,
			osLine.line_style, osLine.cap_style, osLine.join_style);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Line(ZafCoordinate column1, ZafCoordinate line1, ZafCoordinate column2, ZafCoordinate line2,
	int lineWidth)
{
	
	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}
	
	int pixelColumn1 = (int)(ConvertXValue(column1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine1 = (int)(ConvertYValue(line1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelColumn2 = (int)(ConvertXValue(column2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine2 = (int)(ConvertYValue(line2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelLineWidth = (lineWidth > 0) ? ZafMax((int)(ConvertXValue(lineWidth, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(lineWidth == ZAF_HAIR_LINE ? 1 : 0);

	int endPointX = pixelColumn2, endPointY = pixelLine2;

	// Calculate the slope of the line.  (Divide by zero errors are
	// avoided by the next line of code.)
	if (pixelColumn1 != pixelColumn2)	
	{
		long slope = (pixelLine2 - pixelLine1) * 1000L / (pixelColumn2 - pixelColumn1);
		if (slope < 1000 && slope > -1000)
		{
			// This specified line is less than 45 degrees from horizontal.
			// The "x" coordinate of the enpoint can be extended, and the
			// "y" coordinate can be calculated using the slope-intercept
			// formula.
			int deltaX = pixelColumn2 - pixelColumn1;
               		endPointX = pixelColumn1 + deltaX;
			endPointY = (int)(pixelLine1 + deltaX * slope / 1000L);
		}
		else
		{
			// This specified line is more than 45 degrees from horizontal.
			// The "y" coordinate of the enpoint can be extended, and the
			// "x" coordinate can be calculated using the slope-intercept
			// formula.
			int deltaY = pixelLine2 - pixelLine1;
			endPointX = (int)(pixelColumn1 + deltaY * 1000L / slope);
			endPointY = pixelLine1 + deltaY;
		}
	}
        // Check the line width.
	XGCValues osLine;

	XGetGCValues(xDisplay, xGC, GCFunction | GCForeground | GCBackground | GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle, &osLine);

	if (pixelLineWidth == 1)
		pixelLineWidth = 0; // Drawing optimization (See XSetLineAttribute()).
	if (pixelLineWidth != osLine.line_width)
 		XSetLineAttributes(xDisplay, xGC, pixelLineWidth, osLine.line_style,
			osLine.cap_style, osLine.join_style);

	// Draw the line.
		XDrawLine(xDisplay, xDrawable, xGC, pixelColumn1, pixelLine1, endPointX, endPointY);

	// Restore the line width.
	if (pixelLineWidth != osLine.line_width)
		XSetLineAttributes(xDisplay, xGC, osLine.line_width,
			osLine.line_style, osLine.cap_style, osLine.join_style);

	// Reset the xGC if in Xor mode.
 	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	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);

	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	// Draw the point.
	if (displayMode == ZAF_DISPLAY_MONO && color < ZAF_MAXCOLORS)
		XSetForeground(xDisplay, xGC, monoTable[mono].pixel);
	else if (color < ZAF_MAXCOLORS)
		XSetForeground(xDisplay, xGC, colorTable[color].pixel);
	XDrawPoint(xDisplay, xDrawable, xGC, pixelColumn, pixelLine);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

	// Set up the Motif polygon points.
	int i, j;
	XPoint *xPolygon = new XPoint[numPoints+1];
	for (i = 0, j = 0; i < numPoints; i++)
	{
		xPolygon[i].x = (short)(ConvertXValue(polygonPoints[j++], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
		xPolygon[i].y = (short)(ConvertYValue(polygonPoints[j++], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	}
	if (close)
	{
		xPolygon[numPoints] = xPolygon[0];
		numPoints++;
	}

	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	// Fill the polygon.
	if (fill && _gcValues.fill_style == FillSolid && palette.colorBackground < ZAF_MAXCOLORS)
	{
		// Motif uses the foreground color to fill solid polygons.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoBackground].pixel);
		else
			XSetForeground(xDisplay, xGC, colorTable[palette.colorBackground].pixel);
		XFillPolygon(xDisplay, xDrawable, xGC, xPolygon, numPoints, Complex, CoordModeOrigin);
		// Reset the foreground color on the GC.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoForeground].pixel);
		else
			XSetForeground(xDisplay, xGC, colorTable[palette.colorForeground].pixel);	
	}
	else if (fill)
		XFillPolygon(xDisplay, xDrawable, xGC, xPolygon, numPoints, Complex, CoordModeOrigin);

	// Check the line width.
	XGCValues osLine;
	XGetGCValues(xDisplay, xGC, GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle, &osLine);
	if (pixelLineWidth == 1)
		pixelLineWidth = 0; // Drawing optimization (See XSetLineAttribute()).
	if (pixelLineWidth != osLine.line_width)
		XSetLineAttributes(xDisplay, xGC, pixelLineWidth, osLine.line_style,
			osLine.cap_style, osLine.join_style);

	// Draw the polygon.
	XDrawLines(xDisplay, xDrawable, xGC, xPolygon, numPoints, CoordModeOrigin);
	delete []xPolygon;

	// Restore the line width.
	if (pixelLineWidth != osLine.line_width)
		XSetLineAttributes(xDisplay, xGC, osLine.line_width,
			osLine.line_style, osLine.cap_style, osLine.join_style);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::Rectangle(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	int lineWidth, 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 pixelLineWidth = (lineWidth > 0) ? ZafMax((int)(ConvertXValue(lineWidth, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) :
		(lineWidth == ZAF_HAIR_LINE ? 1 : 0);

	GC tempGC = 0;
	// If in Xor mode set correct colors in _xorGC and set xGC to be _xorGC
	if (mode == ZAF_MODE_XOR)
	{
		SetBackground(palette.colorBackground);
		SetForeground(palette.colorForeground);
		tempGC = xGC;
		xGC = _xorGC;
	}

	// Fill the rectangle.
	int width = pixelRight - pixelLeft + 1;
	int height = pixelBottom - pixelTop + 1;
	if (fill && _gcValues.fill_style == FillSolid && palette.colorBackground < ZAF_MAXCOLORS)
	{
		// Motif uses the foreground color to fill solid rectangles.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoBackground].pixel);
		else
			XSetForeground(xDisplay, xGC, colorTable[palette.colorBackground].pixel);
		XFillRectangle(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width, height);
		// Reset the foreground color on the GC.
		if (displayMode == ZAF_DISPLAY_MONO)
			XSetForeground(xDisplay, xGC, monoTable[palette.monoForeground].pixel);	
		else	
			XSetForeground(xDisplay, xGC, colorTable[palette.colorForeground].pixel);
	}
	else if (fill)
		XFillRectangle(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width, height);

	// Draw the rectangle.
	for (int i = 0; i < pixelLineWidth; i++, pixelLeft++, pixelTop++, width -= 2, height -= 2)
		XDrawRectangle(xDisplay, xDrawable, xGC, pixelLeft, pixelTop, width - 1, height - 1);

	// Reset the xGC if in Xor mode.
	if (mode == ZAF_MODE_XOR)
		xGC = tempGC;

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::RectangleXORDiff(const ZafRegionStruct *oldRegion,
	const ZafRegionStruct *newRegion)
{
	// Set the xorGC to the correct colors.
	
	ZafLogicalMode oldMode = mode;
	SetMode(ZAF_MODE_XOR);
	SetBackground(palette.colorBackground);
	SetForeground(palette.colorForeground);
	SetMode(oldMode);

	// Draw the xor rectangles.
	if (oldRegion)
		XDrawRectangle(xDisplay, xDrawable, _xorGC, oldRegion->left,
			oldRegion->top, oldRegion->right - oldRegion->left + 1,
			oldRegion->bottom - oldRegion->top + 1);
	if (newRegion)
		XDrawRectangle(xDisplay, xDrawable, _xorGC, newRegion->left,
			newRegion->top, newRegion->right - newRegion->left + 1,
			newRegion->bottom - newRegion->top + 1);

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::RegionCopy(const ZafRegionStruct &oldRegion,
	int newColumn, int newLine)
{
	// Move the region.
	XCopyArea(xDisplay, xDrawable, xDrawable, xGC, oldRegion.left,
		oldRegion.top, oldRegion.Width(), oldRegion.Height(), newColumn,
		newLine);

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

ZafLogicalColor ZafScreenDisplay::AddColor(ZafLogicalColor index, ZafUInt8 red, ZafUInt8 green, ZafUInt8 blue)
{
	// Check for a valid entry.
	if (index >= ZAF_MAXCOLORS)
	{
		// Find the first null entry.
		for (index = ZAF_CLR_USER_FIRST; index < ZAF_MAXCOLORS; index++)
			if (!colorTable[index].flags)
				break;
	}
	if (index >= ZAF_MAXCOLORS)
		return (ZAF_CLR_NULL);

	// Allocate the color entry.
	// These calculations are the same as (65535L * color / 255L).
	colorTable[index].red = (unsigned short)red + ((unsigned short)red << 8);
	colorTable[index].green = (unsigned short)green + ((unsigned short)green << 8);
	colorTable[index].blue = (unsigned short)blue + ((unsigned short)blue << 8);
	colorTable[index].flags = DoRed | DoGreen | DoBlue;
	Colormap colorMap = XDefaultColormapOfScreen(XtScreen(topLevelShell));
	if (XAllocColor(xDisplay, colorMap, &colorTable[index]))
		return (index);

	// Clean up.
	colorTable[index].pixel = 0;
	colorTable[index].red = colorTable[index].green = colorTable[index].blue = 0;
	colorTable[index].flags = 0;

	// Return failure.
	return (ZAF_CLR_NULL);
}

ZafLogicalFont ZafScreenDisplay::AddFont(ZafLogicalFont index, char *fontFamily,
	int pointSize, ZafFontWeight weight, ZafFontSlant slant, ZafFontPitch)
{
	// Check for a valid entry.
	if (index < 0)
	{
		// Find the first null entry.
		for (index = 0; index < ZAF_MAXFONTS; index++)
			if (!fontTable[index] && !fontResource[index].fontList)
				break;
	}
	if (index >= ZAF_MAXFONTS)
		return (ZAF_FNT_NULL);

	// Determine the weight.
	char *sWeight = (weight == ZAF_FNT_WEIGHT_BOLD) ? "bold" : "Medium";
	char *sSlant = (slant == ZAF_FNT_SLANT_ITALIC) ? "i" : "r";

	// Motif expects point size to be specified in 10ths of a point.
	int mtfPointSize = pointSize * 10;

	// General format is in Appendix A: Specifying Fonts - A.1.1 Font Naming 
	// Conventions of XlibProgramming Manual Vol. 1.
	// -foundry-fontfamily-weight-slant-setwidth--pixels-points-resolution-dpiresolution-spacing-avewidth-charset
	static char format[] = "-*-%s-%s-%s-*--*-%d-*-*-*-*-iso8859-1";
	char fontName[256];
	sprintf(fontName, format, fontFamily, sWeight, sSlant, mtfPointSize);
	XmFontListEntry newFontEntry = XmFontListEntryLoad(xDisplay,fontName, 
		fontResource[index].type, fontResource[index].tag);
	if (newFontEntry)	
	{	
		XmFontList newFont = XmFontListAppendEntry(0, newFontEntry);
		fontTable[index] = fontResource[index].fontList = newFont;
		XmFontListEntryFree(&newFontEntry);	
		return (index);
	}

	// Return failure.
	return (ZAF_FNT_NULL);
}

ZafError ZafScreenDisplay::DestroyColor(ZafLogicalColor color)
{
	// Make sure the entry is valid.
	int index = (int)color;
	if (index < 0 || index >= ZAF_MAXCOLORS)
		return (ZAF_ERROR_INVALID_INDEX);

	// Remove the color entry.
	colorTable[color].pixel = 0;
	colorTable[color].red = 0;
	colorTable[color].green = 0;
	colorTable[color].blue = 0;
	colorTable[color].flags = 0;

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafScreenDisplay::DestroyFont(ZafLogicalFont font)
{
	// Make sure the entry is valid.
	int index = (int)font;
	if (index < 0 || index >= ZAF_MAXFONTS)
		return (ZAF_ERROR_INVALID_INDEX);

	// Check for existing entries and make sure the font is not
	// used in multiple entries. If it is just set fontList to NULL;
	for (int i = 0; i < ZAF_MAXFONTS && fontResource[index].fontList; i++)
		if (i != index && fontResource[index].fontList == fontResource[i].fontList)
		{
			fontResource[index].fontList = 0;
			break;
		}

	// Remove the font entry.
	if (fontResource[index].fontList)
		XmFontListFree(fontResource[index].fontList);
	fontResource[index].fontList = 0;
	fontTable[index] = 0;

	// Return success.
	return (ZAF_ERROR_NONE);
}

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

	// Return the color information.
	int redTemp = colorTable[index].red;
	int greenTemp = colorTable[index].green;
	int blueTemp = colorTable[index].blue;
	red = (redTemp >> 8);
	green = (greenTemp >> 8);
	blue = (blueTemp >> 8);

	return (ZAF_ERROR_NONE);
}

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

	*fontFamily = 0;
	*pointSize = 0;
	*weight = ZAF_FNT_WEIGHT_NORMAL;
	*slant = ZAF_FNT_SLANT_NORMAL;

	// Get the font.
	XFontStruct *fontStruct = MtfGetFont(fontTable[index]);
	if (!fontStruct)
		return (ZAF_ERROR_INVALID);

	// Get the font information and return it.
	unsigned long fontValue = 0;	
	if (XGetFontProperty(fontStruct, XA_FAMILY_NAME, &fontValue))
	{
		char *nameString;
		if ((nameString = XGetAtomName(xDisplay, fontValue)) != NULL)
		{
			strncpy(fontFamily, nameString, bufferSize - 1);
			XFree(nameString);
		}
		fontFamily[bufferSize] = 0;	
	}
	if (XGetFontProperty(fontStruct, XA_POINT_SIZE, &fontValue))
		*pointSize = (int)fontValue / 10;
	if (XGetFontProperty(fontStruct, XA_WEIGHT, &fontValue))
		*weight = ZAF_FNT_WEIGHT_BOLD;
	if (XGetFontProperty(fontStruct, XA_ITALIC_ANGLE, &fontValue))
		*slant = ZAF_FNT_SLANT_ITALIC;

	return (ZAF_ERROR_NONE);
}

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

ZafLogicalColor ZafScreenDisplay::Background(void) const
{
	// Try to find a matching color.
	if (palette.colorBackground == ZAF_CLR_DEFAULT)
	{
		XGCValues gcValues;
		XGetGCValues(xDisplay, xGC, GCBackground, &gcValues);
		for (int i = 0; i < ZAF_MAXCOLORS; i++)
			if ((displayMode == ZAF_DISPLAY_MONO && monoTable[i].pixel == gcValues.background) ||
				(displayMode != ZAF_DISPLAY_MONO && colorTable[i].pixel == gcValues.background))
				return ((ZafLogicalColor)i);
	}
	return (palette.colorBackground);
}

ZafRegionStruct ZafScreenDisplay::ClipRegion(void) const
{
	// Return the current clip region.
	return (clipRegion);
}

ZafLogicalFont ZafScreenDisplay::Font(void) const
{
	// Try to find a matching font entry.
	if (palette.font == ZAF_FNT_DEFAULT)
	{
		XGCValues gcValues;
		XGetGCValues(xDisplay, xGC, GCFont, &gcValues);
		for (int i = 0; i < ZAF_MAXFONTS; i++)
		{
			XFontStruct *font = MtfGetFont(fontTable[i]);
			if (font && font->fid == gcValues.font)
				return ((ZafLogicalFont)i);
		}
	}
	return (palette.font);
}

ZafLogicalColor ZafScreenDisplay::Foreground(void) const
{
	// Try to find a matching color.
	if (palette.colorForeground == ZAF_CLR_DEFAULT)
	{
		XGCValues gcValues;
		XGetGCValues(xDisplay, xGC, GCForeground, &gcValues);
		for (int i = 0; i < ZAF_MAXCOLORS; i++)
			if ((displayMode == ZAF_DISPLAY_MONO && monoTable[i].pixel == gcValues.foreground) ||
				(displayMode != ZAF_DISPLAY_MONO && colorTable[i].pixel == gcValues.foreground))
				return ((ZafLogicalColor)i);
	}
	return (palette.colorForeground);
}

ZafLogicalLineStyle ZafScreenDisplay::LineStyle(void) const
{
	// Try to find a matching color.
	if (palette.lineStyle == ZAF_LINE_DEFAULT)
	{
		XGCValues gcValues;
		XGetGCValues(xDisplay, xGC, GCForeground, &gcValues);
		for (int i = 0; i < ZAF_MAXCOLORS; i++)
			if (lineTable[i] == gcValues.line_style)
				return ((ZafLogicalLineStyle)i);
	}
	return (palette.lineStyle);
}

ZafLogicalColor ZafScreenDisplay::MonoBackground(void) const
{
	// There are no mono colors.
	return (palette.monoBackground);
}

ZafLogicalColor ZafScreenDisplay::MonoForeground(void) const
{
	// There are no mono colors.
	return (palette.monoForeground);
}

ZafPaletteStruct ZafScreenDisplay::Palette(void) const
{
	// Determine the palette entries.
	return (palette);
}

ZafLogicalFillPattern ZafScreenDisplay::FillPattern(void) const
{
	// Try to find a matching pattern.
	if (palette.fillPattern == ZAF_PTN_DEFAULT)
	{
		XGCValues gcValues;
		XGetGCValues(xDisplay, xGC, GCFillStyle, &gcValues);
		for (int i = 0; i < ZAF_MAXCOLORS; i++)
			if (patternTable[i] == gcValues.fill_style)
				return ((ZafLogicalFillPattern)i);
	}
	return (palette.fillPattern);
}

ZafLogicalColor ZafScreenDisplay::MtfGetColor(char *colorName)
{
	XColor screenColor, exactColor;
	Colormap colorMap = XDefaultColormapOfScreen(XtScreen(topLevelShell));
	if (XAllocNamedColor(xDisplay, colorMap, colorName, &screenColor, &exactColor))
	{
		for (ZafLogicalColor index = 0; index < ZAF_MAXCOLORS; index++)
			if (!colorTable[index].flags)
			{
				colorTable[index] = screenColor;
				colorTable[index].flags = DoRed | DoGreen | DoBlue;
				return (index);
			}
	}
	return (ZAF_CLR_NULL);
}

XFontSet ZafScreenDisplay::MtfGetFontSet(XmFontList fontList) const
{
	XFontSet fontSet = 0;

	// Walk the font list for individual font entries.
	XmFontContext context = 0;
	bool fontContext = XmFontListInitFontContext(&context, fontList);
	while (fontContext && !fontSet)
	{
		// Get the next logical entry.
		XmFontType type;
		XmFontListEntry entry = XmFontListNextEntry(context);
		if (!entry)
			break;

		// Sift out the true structure.
		XtPointer pointer = XmFontListEntryGetFont(entry, &type);
		if (type == XmFONT_IS_FONTSET)
			fontSet = (XFontSet)pointer;
	}
	if (context)
		XmFontListFreeFontContext(context);

	// Return the matching font set.
	return (fontSet);
}

XFontStruct *ZafScreenDisplay::MtfGetFont(XmFontList fontList) const
{
	XFontStruct *font = 0;

	// Walk the font list for individual font entries.
	XmFontContext context = 0;
	bool fontContext = XmFontListInitFontContext(&context, fontList);
	while (fontContext && !font)
	{
		// Get the next logical entry.
		XmFontType type;
		XmFontListEntry entry = XmFontListNextEntry(context);
		if (!entry)
			break;

		// Sift out the true structure.
		XtPointer pointer = XmFontListEntryGetFont(entry, &type);
		if (type == XmFONT_IS_FONTSET)
		{
			XFontStruct **table;
			char **name;
			XFontsOfFontSet((XFontSet)pointer, &table, &name);
			if (table)
				font = table[0];
		}
		else
			font = (XFontStruct *)pointer;
	}
	if (context)
		XmFontListFreeFontContext(context);

	// Return the matching font struct.
	return (font);
}

ZafLogicalMode ZafScreenDisplay::Mode(void) const
{
	// Determine the current mode.
	return (mode);
}

// --- Restore --------------------------------------------------------------

OSDisplayContext ZafScreenDisplay::RestoreDisplayContext(void)
{
	// Restore the global display context.
	xDisplay = _xDisplay;
	return (_xDisplay);
}

OSDrawContext ZafScreenDisplay::RestoreDrawContext(void)
{
	// Restore the global draw context.
	xDrawable = _xDrawable;
	return (_xDrawable);
}

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

ZafLogicalColor ZafScreenDisplay::SetBackground(ZafLogicalColor newColor)
{
	// Set the background color.
	ZafLogicalColor oldBackground = palette.colorBackground;
	if (newColor < ZAF_MAXCOLORS)
	{
		if (displayMode == ZAF_DISPLAY_MONO)
			if (mode == ZAF_MODE_COPY)
				XSetBackground(xDisplay, xGC, monoTable[newColor].pixel);
			else 
				XSetBackground(xDisplay, _xorGC, monoTable[newColor].pixel);
		else
			if (mode == ZAF_MODE_COPY)
				XSetBackground(xDisplay, xGC, colorTable[newColor].pixel);
			else
			{
				// On some pseudocolor visuals index white as 0 and black as 1.
				// If this happens XORing with white gives you the same color.
				// If white has an index of 0 change background color to black (1).
				if (newColor == ZAF_CLR_WHITE && colorTable[ZAF_CLR_WHITE].pixel == 0)
					newColor = ZAF_CLR_BLACK;
				XSetBackground(xDisplay, _xorGC, colorTable[newColor].pixel); 
			}
	}
	else if (newColor == ZAF_CLR_DEFAULT)
		if (mode == ZAF_MODE_COPY)
			XSetBackground(xDisplay, xGC, _gcValues.background);
		else
			XSetBackground(xDisplay, _xorGC, _gcValues.background);
  	else
		return (ZAF_CLR_NULL);

	// Modify the palette color.
	if (displayMode == ZAF_DISPLAY_MONO)
		palette.monoBackground = newColor;
	else
		palette.colorBackground = newColor;

	// Return the old color.
	return (oldBackground);
}

ZafRegionStruct ZafScreenDisplay::SetClipRegion(const ZafRegionStruct &newRegion)
{
	// Set the clip region.
	ZafRegionStruct oldRegion = clipRegion;
	clipRegion = newRegion;
	if (clipRegion != virtualRegion)
	{
		XRectangle xRegion;
		xRegion.x = (short)newRegion.left;
		xRegion.y = (short)newRegion.top;
		xRegion.width = (short)newRegion.Width();
		xRegion.height = (short)newRegion.Height();
		XSetClipRectangles(xDisplay, xGC, 0, 0, &xRegion, 1, YXBanded);
	}
	else
		XSetClipMask(xDisplay, xGC, None);

	// Return the old region.
	return (oldRegion);
}

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

OSDisplayContext ZafScreenDisplay::SetDisplayContext(OSDisplayContext newContext)
{
	// Set the display context.
	OSDisplayContext oldContext = xDisplay;
	xDisplay = newContext;
	return (oldContext);
}

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

OSDrawContext ZafScreenDisplay::SetDrawContext(OSDrawContext newContext)
{
	// Set the draw context.
	OSDrawContext oldContext = xDrawable;
	xDrawable = newContext;
	return (oldContext);
}

ZafLogicalFont ZafScreenDisplay::SetFont(ZafLogicalFont newFont)
{
	// Set the font.
	ZafLogicalFont oldFont = newFont;
	if (newFont >= 0 && newFont < ZAF_MAXFONTS && fontTable[newFont])
	{
		XFontStruct *font = MtfGetFont(fontTable[newFont]);
		if (font)
		{
			XSetFont(xDisplay, xGC, font->fid);
			palette.font = newFont;
		}
	}
	else if (newFont == ZAF_FNT_DEFAULT)
	{
		if ((int)_gcValues.font == -1)
		{
			XFontStruct *font = MtfGetFont(fontTable[ZAF_FNT_DIALOG]);
			XSetFont(xDisplay, xGC, font->fid);
			palette.font = newFont;
		}
		else
		{
			XSetFont(xDisplay, xGC, _gcValues.font);
			palette.font = newFont;
		}
	}
	else
		return (ZAF_FNT_NULL);
	return (oldFont);
}

ZafLogicalColor ZafScreenDisplay::SetForeground(ZafLogicalColor newColor)
{
	// Set the foreground color.
	ZafLogicalColor oldForeground = palette.colorForeground;
	if (newColor < ZAF_MAXCOLORS)
	{
		if (displayMode == ZAF_DISPLAY_MONO)
			if (mode == ZAF_MODE_COPY)
				XSetForeground(xDisplay, xGC, monoTable[newColor].pixel);
			else
				XSetForeground(xDisplay, _xorGC, monoTable[newColor].pixel);
		else
			if (mode == ZAF_MODE_COPY)
				XSetForeground(xDisplay, xGC, colorTable[newColor].pixel);
			else
			{
				// On some pseudocolor visuals index white as 0 and black as 1.
				// If this happens XORing with white gives you the same color.
				// If white has an index of 0 change foreground color to black (1).
				if (newColor == ZAF_CLR_WHITE && colorTable[ZAF_CLR_WHITE].pixel == 0)
					newColor = ZAF_CLR_BLACK;
				XSetForeground(xDisplay, _xorGC, colorTable[newColor].pixel);
			}
	}
	else if (newColor == ZAF_CLR_DEFAULT)
		if (mode == ZAF_MODE_COPY)
			XSetForeground(xDisplay, xGC, _gcValues.foreground);
		else
			XSetForeground(xDisplay, _xorGC, _gcValues.foreground);
	else
		return (ZAF_CLR_NULL);

	// Modify the palette color.
	if (displayMode == ZAF_DISPLAY_MONO)
		palette.monoForeground = newColor;
	else
		palette.colorForeground = newColor;

	// Return the old color.
	return (oldForeground);
}

ZafLogicalLineStyle ZafScreenDisplay::SetLineStyle(ZafLogicalLineStyle newLine)
{
	// Set the line style.
	ZafLogicalLineStyle oldLine = palette.lineStyle;
	if (newLine >= 0 && newLine < ZAF_MAXLINES)
	{
		XSetLineAttributes(xDisplay, xGC, _gcValues.line_width,
			lineTable[newLine], _gcValues.cap_style, _gcValues.join_style);
		palette.lineStyle = newLine;
	}
	else
		return (ZAF_LINE_NULL);
	return (oldLine);
}

ZafLogicalMode ZafScreenDisplay::SetMode(ZafLogicalMode newMode)
{
	// Set the mode.
	ZafLogicalMode oldMode = newMode;
	if (newMode >= 0 && newMode < ZAF_MAXMODES)
		mode = newMode;
	else
		return (ZAF_MODE_NULL);
	return (oldMode);
}

ZafLogicalColor ZafScreenDisplay::SetMonoBackground(ZafLogicalColor newColor)
{
	// There are no mono colors.
	ZafLogicalColor oldBackground = palette.monoBackground;
	palette.monoBackground = newColor;
	return (oldBackground);
}

ZafLogicalColor ZafScreenDisplay::SetMonoForeground(ZafLogicalColor newColor)
{
	// There are no mono colors.
	ZafLogicalColor oldForeground = palette.monoForeground;
	palette.monoForeground = newColor;
	return (oldForeground);
}

ZafPaletteStruct ZafScreenDisplay::SetPalette(ZafPaletteStruct newPalette)
{
	// Set the os palette.
	ZafPaletteStruct oldPalette = palette;
	if (newPalette.osPalette)
	{
		// Copy the GC and preserve the defaults.
		XCopyGC(xDisplay, newPalette.osPalette, _gcMask, xGC);
		XGetGCValues(xDisplay, xGC, _gcMask, &_gcValues);
	}

	// Override the default palette values.
	if (newPalette.lineStyle >= 0)
		SetLineStyle(newPalette.lineStyle);
	else
		palette.lineStyle = ZAF_LINE_DEFAULT;
	if (newPalette.fillPattern >= 0)
		SetFillPattern(newPalette.fillPattern);
	else
		palette.fillPattern = ZAF_PTN_DEFAULT;
	if (newPalette.colorForeground < ZAF_MAXCOLORS)
		SetForeground(newPalette.colorForeground);
	else
		palette.colorForeground = ZAF_CLR_DEFAULT;
	if (newPalette.colorBackground < ZAF_MAXCOLORS)
		SetBackground(newPalette.colorBackground);
	else
		palette.colorBackground = ZAF_CLR_DEFAULT;
	if (newPalette.monoForeground < ZAF_MAXCOLORS)
		SetMonoForeground(newPalette.monoForeground);
	else
		palette.monoForeground = ZAF_MONO_DEFAULT;
	if (newPalette.monoBackground < ZAF_MAXCOLORS)
		SetMonoBackground(newPalette.monoBackground);
	else
		palette.monoBackground = ZAF_MONO_DEFAULT;

	if (newPalette.font >= 0)
		SetFont(newPalette.font);
	else
		palette.font = ZAF_FNT_DIALOG;
	return (oldPalette);
}

ZafLogicalFillPattern ZafScreenDisplay::SetFillPattern(ZafLogicalFillPattern newPattern)
{
	// Set the pattern.
	ZafLogicalFillPattern oldPattern = newPattern;
	if (newPattern >= 0 && newPattern < ZAF_MAXPATTERNS)
	{
		XSetFillStyle(xDisplay, xGC, patternTable[newPattern]);
		palette.fillPattern = newPattern;
	}
	else if (newPattern == ZAF_PTN_DEFAULT)
	{
		XSetFillStyle(xDisplay, xGC, _gcValues.fill_style);
		palette.fillPattern = newPattern;
	}
	else
		return (ZAF_PTN_NULL);
	return (oldPattern);
}

