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

#include <z_win.hpp>
#include <z_string.hpp>
#include <z_ctype.hpp>
#include <z_stdlib.hpp>
#if defined(ZAF_MSWINDOWS)
#include <w_app.hpp>
#endif

// ----- ZafWindowObject ----------------------------------------------------

ZafWindowObject::ZafWindowObject(int left, int top, int width, int height) :
	parent(0), screenID(0),
	// user information
	userObject(ZAF_NULLP(void)), userFlags(0), userStatus(0), userText(ZAF_NULLP(ZafIChar)),
	userPaletteData(ZAF_NULLP(ZafPaletteData)),
	userFunction(ZAF_NULLF(ZafUserFunction)),
	// attributes
	acceptDrop(false), copyDraggable(false), linkDraggable(false),
	moveDraggable(false), coordinateType(ZAF_CELL),
	regionType(ZAF_INSIDE_REGION), changed(false), editMode(false),
	focus(false), noncurrent(false), disabled(false), osDraw(true),
	parentDrawBorder(false), parentDrawFocus(false), parentPalette(false),
	selected(false), supportObject(false), systemObject(true), visible(true),
	automaticUpdate(true), bordered(false), error(ZAF_ERROR_NONE),
	helpContext(ZAF_NULLP(ZafIChar)), quickTip(ZAF_NULLP(ZafIChar)),
	helpObjectTip(ZAF_NULLP(ZafIChar))
{
#if defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	modifyMode = 0;
	overlapped = false;
#endif
	// Initialize the help information.

	// Initialize the default object information.
	zafRegion.coordinateType = coordinateType;
	zafRegion.left = left;
	zafRegion.top = top;
	zafRegion.right = left + width - 1;
	zafRegion.bottom = top + height - 1;
#if defined(ZAF_TEXT_MODE)
	oldRegion.Assign(-1, -1, 0, 0, ZAF_CELL);
#else
	oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
#endif

	// Object identifiers.
	memberUserFunction = (MemberUserFunction)&ZafWindowObject::DefaultUserFunction;
}

ZafWindowObject::ZafWindowObject(const ZafWindowObject &copy) :
	ZafElement(copy), parent(0), screenID(0), zafRegion(copy.zafRegion),
	// user information
	userObject(copy.userObject), userFlags(copy.userFlags),
	userStatus(copy.userStatus),
	userPaletteData(ZAF_NULLP(ZafPaletteData)),
	userFunction(copy.userFunction), memberUserFunction(copy.memberUserFunction),
	// attributes
	acceptDrop(copy.acceptDrop), copyDraggable(copy.copyDraggable),
	linkDraggable(copy.linkDraggable), moveDraggable(copy.moveDraggable),
	coordinateType(copy.coordinateType), regionType(copy.regionType),
	changed(copy.changed), editMode(copy.editMode), focus(false),
	noncurrent(copy.noncurrent), disabled(copy.disabled), osDraw(copy.osDraw),
	parentDrawBorder(copy.parentDrawBorder), parentDrawFocus(copy.parentDrawFocus),
	parentPalette(copy.parentPalette), selected(copy.selected),
	supportObject(copy.supportObject), systemObject(copy.systemObject),
	visible(copy.visible), automaticUpdate(true), bordered(copy.bordered),
	error(copy.error)
{
#if defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	modifyMode = 0;
#endif

#if defined(ZAF_TEXT_MODE)
	oldRegion.Assign(-1, -1, 0, 0, ZAF_CELL);
#else
	oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
#endif

	// Copy the user palette.
	if (copy.userPaletteData && copy.userPaletteData->Destroyable())
		SetUserPaletteData(new ZafPaletteData(*copy.userPaletteData));
	else
		SetUserPaletteData(copy.userPaletteData);

	userText = copy.userText ? strdup(copy.userText) : ZAF_NULLP(ZafIChar);

	// Initialize the help information.
	helpContext = copy.helpContext ? strdup(copy.helpContext) : ZAF_NULLP(ZafIChar);
	quickTip = copy.quickTip ? strdup(copy.quickTip) : ZAF_NULLP(ZafIChar);
	helpObjectTip = copy.helpObjectTip ? strdup(copy.helpObjectTip) : ZAF_NULLP(ZafIChar);
}

ZafWindowObject::~ZafWindowObject(void)
{
	if (helpContext)
		delete []helpContext;
	if (quickTip)
		delete []quickTip;
	if (helpObjectTip)
		delete []helpObjectTip;
	if (userText)
		delete []userText;

	if (userPaletteData)
	{
		userPaletteData->SubtractNotification(this, (ZafUpdateFunction)UpdatePalettes);
		if (userPaletteData->Destroyable() && userPaletteData->NotifyCount() == 0)
			delete userPaletteData;
	}
}

ZafLogicalColor ZafWindowObject::BackgroundColor(ZafLogicalColor *color, ZafLogicalColor *mono)
{
	const ZafPaletteStruct *palette = ZAF_NULLP(ZafPaletteStruct);
	if (userPaletteData)
	{
		palette = MapPalette(*userPaletteData, ZAF_PM_BACKGROUND, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);
		if (palette && palette->colorBackground == ZAF_CLR_NULL)
			palette = ZAF_NULLP(ZafPaletteStruct);
	}

	if (!palette)
		palette = MapClassPalette(ZAF_PM_BACKGROUND, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);

	if (color)
		*color = palette->colorBackground;
	if (mono)
		*mono = palette->monoBackground;
	return (palette->colorBackground);
}

bool ZafWindowObject::Changed(void) const 
{ 
	return (changed); 
}

void ZafWindowObject::ConvertCoordinates(ZafCoordinateType newType)
{
	ConvertRegion(zafRegion, newType);
}

void ZafWindowObject::ConvertRegion(ZafRegionStruct &region, ZafCoordinateType newType)
{
	if (newType != region.coordinateType)
	{
		bool absolute = (parent && RegionType() != ZAF_AVAILABLE_REGION) ? false : true;

		// Adjust for preSpace and postSpace.
		if (newType == ZAF_CELL && !absolute)
		{
			int preSpace = (int)display->ConvertYValue(display->preSpace, ZAF_PIXEL, region.coordinateType);
			int postSpace = (int)display->ConvertYValue(display->postSpace, ZAF_PIXEL, region.coordinateType);
			region.top -= preSpace;
			region.bottom += postSpace;
		}

		region.ConvertCoordinates(newType);

		// Adjust for preSpace and postSpace.
		if (coordinateType == ZAF_CELL && !absolute)
		{
			int preSpace = (int)display->ConvertYValue(display->preSpace, ZAF_PIXEL, newType);
			int postSpace = (int)display->ConvertYValue(display->postSpace, ZAF_PIXEL, newType);
			region.top += preSpace;
			region.bottom -= postSpace;
		}
	}
}

ZafRegionStruct ZafWindowObject::ConvertToOSRegion(const ZafWindowObject *object,
	const ZafRegionStruct *region) const
{
	// Determine the default region.
	ZafRegionStruct tZafRegion, tOSRegion;
	tOSRegion.coordinateType = ZAF_PIXEL;
	if (region)
		tZafRegion = *(ZafRegionStruct *)region;
	else
		tZafRegion = object->zafRegion;
//??? Convert coordinate type here?

	// Convert the left/top coordinate.
	ZafPositionStruct position;
	position.column = tZafRegion.left;
	position.line = tZafRegion.top;
	position.coordinateType = tZafRegion.coordinateType;
	position = ConvertToOSPosition(object, &position);
	tOSRegion.left = position.column;
	tOSRegion.top = position.line;

	// Convert the bottom/right coordinate.
	position.column = tZafRegion.right;
	position.line = tZafRegion.bottom;
	position = ConvertToOSPosition(object, &position);
	tOSRegion.right = position.column;
	tOSRegion.bottom = position.line;

	// Return the os region.
	return (tOSRegion);
}

ZafRegionStruct ZafWindowObject::ConvertToZafRegion(const ZafWindowObject *object,
	const ZafRegionStruct *region) const
{
	// Determine the default region.
	ZafRegionStruct tOSRegion, tZafRegion;
	tOSRegion.coordinateType = ZAF_PIXEL;
	tZafRegion.coordinateType = ZAF_PIXEL;
	if (region)
		tOSRegion = *(ZafRegionStruct *)region;
	else
		tOSRegion = object->zafRegion;

	// Convert the left/top coordinate.
	ZafPositionStruct position;
	position.column = tOSRegion.left;
	position.line = tOSRegion.top;
	position.coordinateType = ZAF_PIXEL;
	position = ConvertToZafPosition(object, &position);
	tZafRegion.left = position.column;
	tZafRegion.top = position.line;

	// Convert the bottom/right coordinate.
	position.column = tOSRegion.right;
	position.line = tOSRegion.bottom;
	position = ConvertToZafPosition(object, &position);
	tZafRegion.right = position.column;
	tZafRegion.bottom = position.line;

	// Return the os region.
	return (tZafRegion);
}

bool ZafWindowObject::CopyDraggable(void) const 
{ 
	return (copyDraggable); 
}

ZafEventType ZafWindowObject::DefaultUserFunction(const ZafEventStruct &event, ZafEventType ccode)
{
	// Check for a user supplied function.
	if (ZafWindowObject::userFunction)
	{
		ZafEventStruct uEvent = event;
		return ((*userFunction)(this, uEvent, ccode));
	}
	return (ZAF_ERROR_NONE);
}

bool ZafWindowObject::Draggable(void) const 
{ 
	return (copyDraggable || linkDraggable || moveDraggable); 
}

ZafWindowObject *ZafWindowObject::Duplicate(void)
{
	return (new ZafWindowObject(*this));
}

bool ZafWindowObject::EditMode(void) const 
{ 
	return (editMode); 
}

ZafWindowObject *ZafWindowObject::FocusObject(void) const
{
	// Return a pointer to self.
	return (focus ? (ZafWindowObject *)this : ZAF_NULLP(ZafWindowObject));
}

ZafLogicalFont ZafWindowObject::Font(void)
{
	const ZafPaletteStruct *palette = ZAF_NULLP(ZafPaletteStruct);
	if (userPaletteData)
	{
		palette = MapPalette(*userPaletteData, ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);
		if (palette && palette->font == ZAF_FNT_NULL)
			palette = ZAF_NULLP(ZafPaletteStruct);
	}

	if (!palette)
		palette = MapClassPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);

	return (palette->font);
}

ZafWindowObject *ZafWindowObject::GetObject(ZafNumberID matchID)
{
	// Check the match against numberID.
	return ((numberID == matchID) ? this : ZAF_NULLP(ZafWindowObject));
}

ZafWindowObject *ZafWindowObject::GetObject(const ZafIChar *matchID)
{
	// Check for a root directory separator.
	if (*matchID == ZAF_DIRECTORY_SEPARATOR)
		return (windowManager->GetObject(matchID));

	// Check the match against stringID.
	return (!streq(matchID, stringID) ? this : ZAF_NULLP(ZafWindowObject));
}

#if defined(ZAF_UNICODE)
ZafWindowObject *ZafWindowObject::GetObject(const char *matchID)
{
	ZafStringData stringData(matchID);
	return (GetObject(stringData.Text()));
}
#endif

#if defined(USE_ZAF_PERSISTENCE)
ZafDataName ZafWindowObject::GetUserCallbackName(ZafUserFunction function, ZafObjectPersistence &persist)
{
	return (persist.GetUserCallbackName(function));
}

ZafDataName ZafWindowObject::GetUserObjectName(void *tUserObject, ZafObjectPersistence &persist)
{
	return (persist.GetUserObjectName(tUserObject));
}
#endif

bool ZafWindowObject::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafElement::IsA(compareID));
}

bool ZafWindowObject::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafElement::IsA(compareName));
}

bool ZafWindowObject::LinkDraggable(void) const 
{ 
	return (linkDraggable); 
}

ZafLogicalEvent ZafWindowObject::LogicalEvent(const ZafEventStruct &event)
{
	// Map the event using the default window object event map.
	ZafLogicalEvent ccode = MapEvent(defaultEventMap, event);
	return ((ccode == L_NONE) ? event.type : ccode);
}

ZafPaletteStruct ZafWindowObject::LogicalPalette(ZafPaletteType type, ZafPaletteState state)
{
	// Start with an empty palette.
	ZafPaletteStruct palette;
	palette.lineStyle = ZAF_LINE_NULL;
	palette.fillPattern = ZAF_PTN_NULL;
	palette.colorForeground = palette.colorBackground = ZAF_CLR_NULL;
	palette.monoForeground = palette.monoBackground = ZAF_MONO_NULL;
	palette.font = ZAF_FNT_NULL;
#	if defined(ZAF_MSWINDOWS)
	palette.osPalette.colorForeground = palette.osPalette.colorBackground = 0xFFFFFFFFL;
#	elif defined(ZAF_MOTIF)
	palette.osPalette = 0;
#	elif defined(ZAF_MACINTOSH)
	palette.osPalette = 0;
#	elif defined(ZAF_INDEPENDENT)
	palette.osPalette = 0;
#	endif

	// Try to find matching palette entries.
	const ZafPaletteStruct *matchPalette;
	ZafWindowObject *object = this;
	while (object)
	{
		// First, try the object's user palette.
		if (object->userPaletteData)
			matchPalette = object->MapPalette(*object->userPaletteData, type, state);
		else
			matchPalette = ZAF_NULLP(ZafPaletteStruct);

		// Fill in the palette gaps.
		if (matchPalette)
		{
			if (palette.lineStyle == ZAF_LINE_NULL)
				palette.lineStyle = matchPalette->lineStyle;
			if (palette.fillPattern == ZAF_PTN_NULL)
				palette.fillPattern = matchPalette->fillPattern;
			if (palette.colorForeground == ZAF_CLR_NULL)
				palette.colorForeground = matchPalette->colorForeground;
			if (palette.colorBackground == ZAF_CLR_NULL)
				palette.colorBackground = matchPalette->colorBackground;
			if (palette.monoForeground == ZAF_MONO_NULL)
				palette.monoForeground = matchPalette->monoForeground;
			if (palette.monoBackground == ZAF_MONO_NULL)
				palette.monoBackground = matchPalette->monoBackground;
			if (palette.font == ZAF_FNT_NULL)
				palette.font = matchPalette->font;
		}

		// Next, try the object's class palette.
		if (!object->ParentPalette() &&
			(palette.lineStyle == ZAF_LINE_NULL ||
			palette.fillPattern == ZAF_PTN_NULL ||
			palette.colorForeground == ZAF_CLR_NULL ||
			palette.colorBackground == ZAF_CLR_NULL ||
			palette.monoForeground == ZAF_MONO_NULL ||
			palette.monoBackground == ZAF_MONO_NULL ||
			palette.font == ZAF_FNT_NULL))
			matchPalette = object->MapClassPalette(type, state);
		else
			matchPalette = ZAF_NULLP(ZafPaletteStruct);

		// Fill in the palette gaps.
		if (matchPalette)
		{
			if (palette.lineStyle == ZAF_LINE_NULL)
				palette.lineStyle = matchPalette->lineStyle;
			if (palette.fillPattern == ZAF_PTN_NULL)
				palette.fillPattern = matchPalette->fillPattern;
			if (palette.colorForeground == ZAF_CLR_NULL)
				palette.colorForeground = matchPalette->colorForeground;
			if (palette.colorBackground == ZAF_CLR_NULL)
				palette.colorBackground = matchPalette->colorBackground;
			if (palette.monoForeground == ZAF_MONO_NULL)
				palette.monoForeground = matchPalette->monoForeground;
			if (palette.monoBackground == ZAF_MONO_NULL)
				palette.monoBackground = matchPalette->monoBackground;
			if (palette.font == ZAF_FNT_NULL)
				palette.font = matchPalette->font;
		}

		// Get palette info from the OS.
#		if !defined(ZAF_MOTIF)
		if (screenID &&
			(palette.lineStyle == ZAF_LINE_DEFAULT ||
			palette.fillPattern == ZAF_PTN_DEFAULT ||
			palette.colorForeground == ZAF_CLR_DEFAULT ||
			palette.colorBackground == ZAF_CLR_DEFAULT ||
			palette.monoForeground == ZAF_MONO_DEFAULT ||
			palette.monoBackground == ZAF_MONO_DEFAULT ||
			palette.font == ZAF_FNT_DEFAULT))
			object->OSMapPalette(palette, type, state);
#		endif

		// Clean up unresolved entries.
		if (palette.lineStyle == ZAF_LINE_PARENT)
			palette.lineStyle = ZAF_LINE_NULL;
		if (palette.fillPattern == ZAF_PTN_PARENT)
			palette.fillPattern = ZAF_PTN_NULL;
		if (palette.colorForeground == ZAF_CLR_PARENT)
			palette.colorForeground = ZAF_CLR_NULL;
		if (palette.colorBackground == ZAF_CLR_PARENT)
			palette.colorBackground = ZAF_CLR_NULL;
		if (palette.monoForeground == ZAF_MONO_PARENT)
			palette.monoForeground = ZAF_MONO_NULL;
		if (palette.monoBackground == ZAF_MONO_PARENT)
			palette.monoBackground = ZAF_MONO_NULL;
		if (palette.font == ZAF_FNT_PARENT)
			palette.font = ZAF_FNT_NULL;

		// Check the palette to see if we are done.
		if (palette.lineStyle == ZAF_LINE_NULL ||
			palette.fillPattern == ZAF_PTN_NULL ||
			palette.colorForeground == ZAF_CLR_NULL ||
			palette.colorBackground == ZAF_CLR_NULL ||
			palette.monoForeground == ZAF_MONO_NULL ||
			palette.monoBackground == ZAF_MONO_NULL ||
			palette.font == ZAF_FNT_NULL)
		{
			// Walk the parent path, ending at the window manager.
			if (object->parent)
				object = object->parent;
			else if (object != windowManager)
				object = windowManager;
			else
				break;
		}
		else
			break;
	}

	// Return the final palette.
#	if defined(ZAF_MOTIF)
	if (screenID)
		OSMapPalette(palette, type, state);
#	endif
	return (palette);
}

const ZafPaletteStruct *ZafWindowObject::MapClassPalette(ZafPaletteType type, ZafPaletteState state)
{
	// Map the palette using the default window object palette map.
	return (MapPalette(defaultPaletteMap, type, state));
}

ZafLogicalEvent ZafWindowObject::MapEvent(ZafEventMap *mapTable, const ZafEventStruct &event)
{
	// Typecast because of const event.
	if (event.converted != this)
		ConvertToZafEvent((ZafEventStruct &)event);

	// See if the type is already mapped.
	if (event.type < E_DEVICE_FIRST || event.type > E_DEVICE_LAST)
		return (event.type);
	ZafEventType logicalValue = L_NONE;
	ZafRawCode eventRawCode = event.rawCode;
	ZafEventType eventType = event.InputType();

	// Try to find a matching event.
	for (ZafEventMap *map = mapTable; map->logicalValue != L_NONE; map++)
		if (map->rawCode == eventRawCode && map->eventType == eventType &&
			map->modifiers == event.modifiers)
		{
			logicalValue = map->logicalValue;
			break;
		}

	// Return the logical match value.
	return (logicalValue);
}

const ZafPaletteStruct *ZafWindowObject::MapPalette(const ZafPaletteMap *mapTable, ZafPaletteType type, ZafPaletteState state)
{
	// Try to find a matching entry.
	for (const ZafPaletteMap *map = mapTable; map->type != ZAF_PM_NONE; map++)
		if ((map->type & type) && (map->state & state) == map->state)
			return (&map->palette);
	return (ZAF_NULLP(ZafPaletteStruct));
}

bool ZafWindowObject::MoveDraggable(void) const 
{ 
	return (moveDraggable); 
}

ZafWindowObject *ZafWindowObject::NotifyFocus(ZafWindowObject *focusObject, bool setFocus)
{
#if defined(ZAF_MOTIF)
	if (screenID && focusObject && zafWindowManager->modalWindow && zafWindowManager->modalWindow != focusObject->RootObject())
	{
		if ((!zafWindowManager->modalWindow->Focus() || setFocus) && zafWindowManager->modalWindow->OSScreenID(ZAF_SHELLID))
		{
			Widget modalWin = zafWindowManager->modalWindow->OSScreenID(ZAF_SHELLID);
			XRaiseWindow(XtDisplay(modalWin), XtWindow(modalWin));
			XmProcessTraversal(modalWin, XmTRAVERSE_CURRENT);
		}
		zafWindowManager->modalWindow->SetFocus(true);
		setFocus = false;
		return (focusObject);
	}	
#endif	
	// Check for algorithm direction.
	ZafWindowObject *invalidObject = ZAF_NULLP(ZafWindowObject);
	if (!setFocus) // down
	{
		if (screenID || (parent && parent->IsA(ID_ZAF_VT_LIST)))
		{
			// Validate the object before losing focus.
			ZafEventStruct focusEvent;
			focusEvent.type = N_NON_CURRENT;
			focusEvent.windowObject = windowManager->focusObject;
			ZafEventType ccode = Event(focusEvent);
			if (ccode == ZAF_ERROR_NONE || ccode == ZAF_ERROR_LEAVE_INVALID)
			{
				focusEvent.type = S_NON_CURRENT;
				Event(focusEvent);
			}
			else
			{
				// Restore focus (validation failed).
				SetFocus(true);
				windowManager->focusObject = invalidObject = this;
			}
		}
	}
	else if (setFocus) // up
	{
		if (focusObject == this)
		{
			windowManager->oldFocusObject = windowManager->focusObject;
			windowManager->focusObject = this;
		}

		// Recurse the entire focus path.
		if (parent)
			invalidObject = parent->NotifyFocus(this, true);
		else if (screenID)
			invalidObject = windowManager->NotifyFocus(this, true);

		// Set focus if validation succeeded.
		if (!invalidObject && (screenID || parent && parent->IsA(ID_ZAF_VT_LIST)))
		{
			ZafEventStruct focusEvent;
			focusEvent.type = S_CURRENT;
			focusEvent.windowObject = windowManager->oldFocusObject;
			Event(focusEvent);
			windowManager->focusObject = this;
			focusEvent.type = N_CURRENT;
			Event(focusEvent);
		}
	}

	// Return the object focus.
	return (invalidObject);
}

ZafWindowObject *ZafWindowObject::NotifySelection(ZafWindowObject *, bool setSelected)
{
	// Work up the tree.
	return (parent ? parent->NotifySelection(this, setSelected) : 0);
}

ZafPaletteState ZafWindowObject::PaletteState(void)
{
	// Check for the matching palette state.
	ZafPaletteState state = 0;
	if (RootObject()->Focus())
		state |= ZAF_PM_ACTIVE;
	else
		state |= ZAF_PM_INACTIVE;
	if (Focus())
		state |= ZAF_PM_CURRENT;
	else
		state |= ZAF_PM_NOT_CURRENT;
	if (Selected())
		state |= ZAF_PM_SELECTED;
	else
		state |= ZAF_PM_NOT_SELECTED;
	if (Disabled())
		state |= ZAF_PM_DISABLED;
	else
		state |= ZAF_PM_ENABLED;
	return (state);
}

ZafRegionStruct ZafWindowObject::Region(void) const
{
	return (zafRegion);
}

ZafWindowObject *ZafWindowObject::RootObject(void) const
{
	// Find the root.
	return (parent ? parent->RootObject() : (ZafWindowObject *)this);
}

ZafLogicalColor ZafWindowObject::SetBackgroundColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entry.
	ZafPaletteStruct backgroundPalette = userPaletteData->GetPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE);
	backgroundPalette.colorBackground = color;
	backgroundPalette.monoBackground = mono;
	userPaletteData->AddPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE, backgroundPalette);

	// Return the current color.
	return (color);
}

bool ZafWindowObject::SetBordered(bool setBordered)
{
	// Make sure the request is valid.
	if (!screenID && bordered != setBordered)
		bordered = setBordered;

	// Return the current attribute.
	return (bordered);
}

ZafLogicalFont ZafWindowObject::SetFont(ZafLogicalFont font)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entries.
	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED, textPalette);
	textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE, textPalette);

	// Return the current font.
	return (font);
}

bool ZafWindowObject::SetOSDraw(bool setOSDraw)
{
	// Make sure the attribute has changed.
	if (osDraw != setOSDraw && !screenID)
		osDraw = setOSDraw;

	// Return the current attribute.
	return (osDraw);
}

bool ZafWindowObject::SetParentDrawBorder(bool setParentDrawBorder)
{
	// Change the attribute.
	if (parentDrawBorder != setParentDrawBorder && !screenID)
		parentDrawBorder = setParentDrawBorder;

	// Return the current attribute.
	return (parentDrawBorder);
}

bool ZafWindowObject::SetParentDrawFocus(bool setParentDrawFocus)
{
	// Change the attribute.
	if (parentDrawFocus != setParentDrawFocus && !screenID)
		parentDrawFocus = setParentDrawFocus;

	// Return the current attribute.
	return (parentDrawFocus);
}

bool ZafWindowObject::SetParentPalette(bool setParentPalette)
{
	// Change the attribute.
	if (parentPalette != setParentPalette && !screenID)
		parentPalette = setParentPalette;

	// Return the current attribute.
	return (parentPalette);
}

void ZafWindowObject::SetRegion(const ZafRegionStruct &setRegion)
{
	zafRegion = setRegion;
}

ZafError ZafWindowObject::SetStringData(ZafStringData *)
{
	// Setting string data at window object level is invalid.
	return (ZAF_ERROR_INVALID);
}

ZafError ZafWindowObject::SetText(const ZafIChar *)
{
	// Setting text at window object level is invalid.
	return (ZAF_ERROR_INVALID);
}

#if defined(ZAF_UNICODE)
ZafError ZafWindowObject::SetText(const char *text)
{
	// Call SetText() with a ZafIChar string.
	ZafStringData stringData(text);
	return (SetText(stringData.Text()));
}
#endif

ZafLogicalColor ZafWindowObject::SetTextColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entry.
	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED);
	textPalette.colorForeground = color;
	textPalette.monoForeground = mono;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ENABLED, textPalette);

	// Return the current color.
	return (color);
}

bool ZafWindowObject::ToggleSelected(void) 
{ 
	return (SetSelected(!selected)); 
}

void *ZafWindowObject::UserInformation(ZafInfoRequest , void *, ZafClassID )
{
	// This function is a stub.
	return (ZAF_NULLP(void));
}

const ZafIChar *ZafWindowObject::Text(void)
{
	return (ZAF_NULLP(ZafIChar));
}

ZafLogicalColor ZafWindowObject::TextColor(ZafLogicalColor *color, ZafLogicalColor *mono)
{
	const ZafPaletteStruct *palette = ZAF_NULLP(ZafPaletteStruct);
	if (userPaletteData)
	{
		palette = MapPalette(*userPaletteData, ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);
		if (palette && palette->colorForeground == ZAF_CLR_NULL)
			palette = ZAF_NULLP(ZafPaletteStruct);
	}

	if (!palette)
		palette = MapClassPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED | ZAF_PM_NOT_SELECTED);

	if (color)
		*color = palette->colorForeground;
	if (mono)
		*mono = palette->monoForeground;
	return (palette->colorForeground);
}

ZafError ZafWindowObject::SetUserPaletteData(ZafPaletteData *setPaletteData)
{
	// Remove the data notification.
	if (userPaletteData)
		userPaletteData->SubtractNotification(this, (ZafUpdateFunction)UpdatePalettes);

	// Reset the palette data.
	if (userPaletteData && userPaletteData != setPaletteData &&
		userPaletteData->Destroyable() && userPaletteData->NotifyCount() == 0)
		delete userPaletteData;
	userPaletteData = setPaletteData;

	if (userPaletteData)
		userPaletteData->AddNotification(this, (ZafUpdateFunction)UpdatePalettes);

	return (OSUpdatePalettes(ZAF_PM_ANY_TYPE, ZAF_PM_ANY_TYPE));
}

// ----- Attributes ---------------------------------------------------------

bool ZafWindowObject::AutomaticUpdate(bool traverse) const
{
	// Determine the current attribute, defering to parent when appropriate.
	return ((automaticUpdate && parent && traverse) ? parent->AutomaticUpdate() : automaticUpdate);
}

bool ZafWindowObject::Disabled(bool traverse) const
{
	// Determine the current attribute, defering to parent when appropriate.
	return ((!disabled && parent && traverse) ? parent->Disabled() : disabled);
}

bool ZafWindowObject::Noncurrent(void) const
{
	// Determine the current attribute, defering to parent when appropriate.
	return ((!noncurrent && parent) ? parent->Noncurrent() : noncurrent);
}

bool ZafWindowObject::Visible(bool traverse) const
{
	// Determine the current attribute, defering to parent when appropriate.
	return ((visible && parent && traverse) ? parent->Visible() : visible);
}

bool ZafWindowObject::SetChanged(bool setChanged)
{
	// Change the attribute.
	changed = setChanged;

	// Return the current attribute.
	return (changed);
}

ZafCoordinateType ZafWindowObject::SetCoordinateType(ZafCoordinateType newCoordinateType)
{
	// Make sure the attribute has changed.
	if (coordinateType != newCoordinateType && !screenID)
		coordinateType = zafRegion.coordinateType = newCoordinateType;

	// Return the current attribute.
	return (coordinateType);
}

bool ZafWindowObject::SetCopyDraggable(bool setCopyDraggable)
{
	// Make sure the attribute has changed.
	if (copyDraggable != setCopyDraggable)
		copyDraggable = setCopyDraggable;

	// Return the current attribute.
	return (copyDraggable);
}

ZafError ZafWindowObject::SetError(ZafError setError)
{
	// Change the attribute.
	error = setError;

	// Return the current attribute.
	return (error);
}

const ZafIChar *ZafWindowObject::SetHelpObjectTip(const ZafIChar *newHelpObjectTip)
{
	// Delete old help object tip.
	if (helpObjectTip)
		delete []helpObjectTip;

	// Set new help object tip.
	helpObjectTip = newHelpObjectTip ? strdup(newHelpObjectTip) : ZAF_NULLP(ZafIChar);

	// Return the current help object tip.
	return (helpObjectTip);
}

const ZafIChar *ZafWindowObject::SetHelpContext(const ZafIChar *newHelpContext)
{
	// Delete old help context.
	if (helpContext)
		delete []helpContext;

	// Set the new help context.
	helpContext = newHelpContext ? strdup(newHelpContext) : ZAF_NULLP(ZafIChar);

	// Return the current attribute.
	return (helpContext);
}

int ZafWindowObject::SetInitialDelay(int newInitialDelay)
{
	// Make sure the attribute has changed.
	if (initialDelay != newInitialDelay)
		initialDelay = newInitialDelay;

	// Return the current attribute.
	return (initialDelay);
}

bool ZafWindowObject::SetLinkDraggable(bool setLinkDraggable)
{
	// Change the attribute.
	if (linkDraggable != setLinkDraggable)
		linkDraggable = setLinkDraggable;

	// Return the current attribute.
	return (linkDraggable);
}

bool ZafWindowObject::SetMoveDraggable(bool setMoveDraggable)
{
	// Make sure the attribute has changed.
	if (moveDraggable != setMoveDraggable)
		moveDraggable = setMoveDraggable;

	// Return the current attribute.
	return (moveDraggable);
}

const ZafIChar *ZafWindowObject::SetQuickTip(const ZafIChar *newQuickTip)
{
	// Delete old quick tip.
	if (quickTip)
		delete []quickTip;

	// Set new quick tip.
	quickTip = newQuickTip ? strdup(newQuickTip) : ZAF_NULLP(ZafIChar);

	// Return the current quick tip.
	return (quickTip);
}

ZafRegionType ZafWindowObject::SetRegionType(ZafRegionType setRegionType)
{
	// Make sure the attribute has changed.
	if (setRegionType != regionType && !screenID)
		regionType = setRegionType;

	// Return the current attribute.
	return (regionType);
}

int ZafWindowObject::SetRepeatDelay(int newRepeatDelay)
{
	// Make sure the attribute has changed.
	if (repeatDelay != newRepeatDelay)
		repeatDelay = newRepeatDelay;

	// Return the current attribute.
	return (repeatDelay);
}

bool ZafWindowObject::SetSelected(bool setSelected)
{
	if (selected == setSelected)
		return (selected);

	// Change the selection.
	selected = setSelected;

	// Check for a field change.
	if (screenID)
		SetChanged(true);

	// Check the object selection with its parent.
	ZafWindow *window = DynamicPtrCast(parent, ZafWindow);
	if (window)
		window->NotifySelection(this, setSelected);

	// Return the current attribute.
	return (selected);
}

bool ZafWindowObject::SetSupportObject(bool )
{
	// supportObject is false for this class.
	supportObject = false;
	return (supportObject);
}

bool ZafWindowObject::SetSystemObject(bool setSystemObject)
{
	// Make sure the attribute has changed.
	if (systemObject == setSystemObject)
		return (systemObject);
	if (screenID)
	{
#if defined(ZAF_MSWINDOWS)
		// Finish pending updates, then disable updating while
		// the object is destroyed.
		UpdateWindow(RootObject()->screenID);
	  	SendMessage(parent->screenID, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM)0);

		// Destroy the object.
		Event(S_DESTROY);

		// Set the system object attribute.
		systemObject = setSystemObject;

		// Reinstantiate the object.
		Event(S_REGISTER_OBJECT);
		if (systemObject)
		{
			oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
			OSSize();
		}

		// Reenable updating.
		if (parent->AutomaticUpdate(false))
		  	SendMessage(parent->screenID, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM)0);

		// Redraw the object.
		ZafEventStruct redisplayEvent(S_REDISPLAY_REGION);
		redisplayEvent.region = zafRegion;
		parent->Event(redisplayEvent);
#else
		// Destroy the object.
		Event(S_DESTROY);

		// Set the system object attribute.
		systemObject = setSystemObject;

		// Reinstantiate the object.
		Event(S_REGISTER_OBJECT);

		// Redraw the object.
		if (systemObject)
		{
#	if defined(ZAF_TEXT_MODE)
			oldRegion.Assign(-1, -1, 0, 0, ZAF_CELL);
#	else
			oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
#	endif
			OSSize();
		}
		else
			Redisplay();
#endif
	}
	else
		systemObject = setSystemObject;

	// Return the current attribute.
	return (systemObject);
}

ZafUserFunction ZafWindowObject::SetUserFunction(ZafUserFunction tUserCallback)
{
	userFunction = tUserCallback;
	return (userFunction);
}

ZafStringData *ZafWindowObject::StringData(void) const
{
	return (ZAF_NULLP(ZafStringData));
}

ZafError ZafWindowObject::UpdatePalettes(ZafWindowObject *object, ZafUpdateType type)
{
	ZafError retVal = ZAF_ERROR_INVALID;
	if (type & ZAF_UPDATE_OBJECT)
		retVal = (object->OSUpdatePalettes(ZAF_PM_ANY_TYPE, ZAF_PM_ANY_TYPE));
	if (type & ZAF_UPDATE_DATA)
		retVal = (ZAF_ERROR_NONE);
	return (retVal);
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafWindowObject::ZafWindowObject(const ZafIChar *name, ZafObjectPersistence &persist) :
	parent(0), screenID(0),
	// user information
	userObject(ZAF_NULLP(void)), userFlags(0), userStatus(0), userText(ZAF_NULLP(ZafIChar)),
	userPaletteData(ZAF_NULLP(ZafPaletteData)),
	userFunction(ZAF_NULLF(ZafUserFunction)),
	// attributes
	changed(false), editMode(false), focus(false), systemObject(true),
	automaticUpdate(true), error(ZAF_ERROR_NONE),
	// Help information
	helpContext(ZAF_NULLP(ZafIChar)), quickTip(ZAF_NULLP(ZafIChar)),
	helpObjectTip(ZAF_NULLP(ZafIChar))

{
#if defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	modifyMode = 0;
#endif

	// Initialize the window object information.
	memberUserFunction = (MemberUserFunction)&ZafWindowObject::DefaultUserFunction;
#if defined(ZAF_TEXT_MODE)
	oldRegion.Assign(-1, -1, 0, 0, ZAF_CELL);
#else
	oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
#endif

	// Set the string identification.
	SetStringID(name);

	// Prepare the file.
	persist.AllocateFile(stringID, ZAF_FILE_READ);

	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafWindowObject::ReadData(persist);
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

ZafElement *ZafWindowObject::Read(const ZafIChar *name, ZafObjectPersistence &persist)
{
	return (new ZafWindowObject(name, persist));
}

void ZafWindowObject::ReadData(ZafObjectPersistence &persist)
{
	// Read the element information.
	ZafFile *file = persist.CurrentFile();
	*file >> numberID;

	// Read the position and size.
	*file >> zafRegion.left >> zafRegion.top >> zafRegion.right >> zafRegion.bottom;

	// Map flags to values.
	ZafUInt16 flag1, flag2;
	*file >> flag1 >> flag2;
	// flag1
	acceptDrop = (flag1 & 0x0001) ? true : false;
	bordered = (flag1 & 0x0002) ? true : false;
	copyDraggable = (flag1 & 0x0004) ? true : false;
	linkDraggable = (flag1 & 0x0008) ? true : false;
	moveDraggable = (flag1 & 0x0010) ? true : false;
	noncurrent = (flag1 & 0x0020) ? true : false;
	disabled = (flag1 & 0x0040) ? true : false;
	osDraw = (flag1 & 0x0080) ? true : false;
	selected = (flag1 & 0x0100) ? true : false;
	supportObject = (flag1 & 0x0200) ? true : false;
	visible = (flag1 & 0x0400) ? true : false;
	parentDrawBorder = (flag1 & 0x0800) ? true : false;
	parentDrawFocus = (flag1 & 0x1000) ? true : false;
	parentPalette = (flag1 & 0x2000) ? true : false;
	// flag2
	zafRegion.coordinateType = coordinateType = (ZafCoordinateType)((int)flag2 & 0x000F);
	regionType = (ZafRegionType)(((int)flag2 & 0x00F0) >> 4);

	// Read help information.
	*file >> &helpContext >> &quickTip >> &helpObjectTip;

	// Read the user data.
	ZafIChar callbackName[ZAF_MAXNAMELEN], objectName[ZAF_MAXNAMELEN], userPaletteName[ZAF_MAXNAMELEN];

/* START BLOCK COMMENT
**		// Future use.
**		ZafFileSystem *fileSystem = persist.CurrentFileSystem();
**		ZafFile *file = fileSystem->Open(ZAF_ITEXT("userObject"), ZAF_FILE_READ);
**		if (file)
**		{
**			*file >> &objectName;
**			fileSystem->Close(file);
**		}
**	
**		file = fileSystem->Open(ZAF_ITEXT("userFunction"), ZAF_FILE_READ);
**		if (file)
**		{
**			*file >> &callbackName;
**			fileSystem->Close(file);
**		}
**	
**		file = fileSystem->Open(ZAF_ITEXT("userPalette"), ZAF_FILE_READ);
**		if (file)
**		{
**			*file >> &userPaletteName;
**			fileSystem->Close(file);
**		}
END BLOCK COMMENT */

	*file >> userFlags >> userStatus >> &userText >> callbackName >> objectName >> userPaletteName;
	if (*callbackName)
		userFunction = persist.GetUserCallback(callbackName);
	if (*objectName)
		userObject = persist.GetUserObject(objectName);
	if (*userPaletteName)
		SetUserPaletteData(new ZafPaletteData(userPaletteName, persist));
}

void ZafWindowObject::Write(ZafObjectPersistence &persist)
{
	// Register the read procedure.
	persist.AddObjectConstructor(ClassName(), ClassID(), ReadFunction());

	if (!stringID || !stringID[0])
	{
		ZafIChar newStringID[ZAF_MAXNAMELEN];
		strcpy(newStringID, genericFieldID);
		itoa(numberID, &newStringID[strlen(newStringID)], 10, 0);
		SetStringID(newStringID);
	}

	// Prepare the file.
	persist.AllocateFile(stringID, ZAF_FILE_CREATE | ZAF_FILE_READWRITE);

	// Write the object.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafWindowObject::WriteData(persist);
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafWindowObject::WriteData(ZafObjectPersistence &persist)
{
	// Don't write: screenID, parent, focus, systemObject,
	// automaticUpdate, error, editMode, changed.

	// Write the element information.
	ZafFile *file = persist.CurrentFile();
	*file << numberID;

	// Write the position and size.
	ZafRegionStruct region = zafRegion;
	ConvertRegion(region, coordinateType);
	*file << region.left << region.top << region.right << region.bottom;

	// Map values to flags.
	ZafUInt16 flag1 = 0, flag2 = 0;
	// flag1
	flag1 |= (ZafUInt16)(acceptDrop ? 0x0001 : 0);
	flag1 |= (ZafUInt16)(bordered ? 0x0002 : 0);
	flag1 |= (ZafUInt16)(copyDraggable ? 0x0004 : 0);
	flag1 |= (ZafUInt16)(linkDraggable ? 0x0008 : 0);
	flag1 |= (ZafUInt16)(moveDraggable ? 0x0010 : 0);
	flag1 |= (ZafUInt16)(noncurrent ? 0x0020 : 0);
	flag1 |= (ZafUInt16)(disabled ? 0x0040 : 0);
	flag1 |= (ZafUInt16)(osDraw ? 0x0080 : 0);
	flag1 |= (ZafUInt16)(selected ? 0x0100 : 0);
	flag1 |= (ZafUInt16)(supportObject ? 0x0200 : 0);
	flag1 |= (ZafUInt16)(visible ? 0x0400 : 0);
	flag1 |= (ZafUInt16)(parentDrawBorder ? 0x0800 : 0);
	flag1 |= (ZafUInt16)(parentDrawFocus ? 0x1000 : 0);
	flag1 |= (ZafUInt16)(parentPalette ? 0x2000 : 0);
	// flag2
	flag2 |= (ZafUInt16)(coordinateType & 0x000F);
	flag2 |= (ZafUInt16)((regionType & 0x000F) << 4);
	// write the flags
	*file << flag1 << flag2;

	// Write help information.
	*file << helpContext << quickTip << helpObjectTip;

	// Write the user data.
	const ZafIChar *userPaletteName = userPaletteData ? userPaletteData->StringID() : ZAF_NULLP(ZafIChar);
	ZafDataName callbackName = GetUserCallbackName(userFunction, persist);
	ZafDataName objectName = GetUserObjectName(userObject, persist);
	*file << userFlags << userStatus << userText << callbackName << objectName << userPaletteName;

	if (callbackName)
	{
		ZafFileSystem *fileSystem = persist.CurrentFileSystem();
		ZafFile *file = fileSystem->Open(ZAF_ITEXT("userFunction"),
			ZAF_FILE_CREATE | ZAF_FILE_READWRITE,
			callbackName, ID_ZAF_USER_FUNCTION);
		*file << callbackName;
		fileSystem->Close(file);
	}

	if (objectName)
	{
		ZafFileSystem *fileSystem = persist.CurrentFileSystem();
		ZafFile *file = fileSystem->Open(ZAF_ITEXT("userObject"),
			ZAF_FILE_CREATE | ZAF_FILE_READWRITE,
			objectName, ID_ZAF_USER_OBJECT);
		*file << objectName;
		fileSystem->Close(file);
	}

/* START BLOCK COMMENT
**		// Future use.
**		if (userPaletteName)
**		{
**			ZafFileSystem *fileSystem = persist.CurrentFileSystem();
**			ZafFile *file = fileSystem->Open(ZAF_ITEXT("userPalette"),
**				ZAF_FILE_CREATE | ZAF_FILE_READWRITE,
**				userPaletteName, ID_ZAF_USER_PALETTE);
**			*file << userPaletteName;
**			fileSystem->Close(file);
**		}
END BLOCK COMMENT */

	if (userPaletteData)
		userPaletteData->Write(persist);
}
#endif
