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

#include <z_stdlib.hpp>
#include <z_string.hpp>
#include <z_help.hpp>
#include <z_mouse2.hpp>
#include <z_utils.hpp>
#include "m_win.hpp"
#include "m_utils.hpp"

// ----- MtfWindowObject widget definition ----------------------------------

XmOffsetPtr MtfWindowObject::offsets;

XtActionsRec MtfWindowObject::action[] =
{
	{ "ZafMouse", 		(XtActionProc)mouse },
	{ "ZafCallDefault", (XtActionProc)call_default },
	{ "ZafDragDrop",	(XtActionProc)drag_drop },
	{ "ZafEnter",		(XtActionProc)enter },
	{ "ZafFocusIn",		(XtActionProc)focus_in },
	{ "ZafHelp", 		(XtActionProc)help },
	{ "ZafKey", 		(XtActionProc)key },
	{ "ZafLeave",		(XtActionProc)leave }
};

char MtfWindowObject::translations[] =
	"<Key>osfActivate:		ZafCallDefault()\n\
	 <Key>osfSelect:		ZafCallDefault()\n\
	 ~s ~m ~a <Key>Return:	ZafCallDefault()\n\
	 <Btn2Down>:			ZafDragDrop()\n\
	 <Enter>:				ZafEnter()\n\
	 <FocusIn>:				ZafFocusIn()\n\
	 <Key>F1:				ZafHelp()\n\
	 <Key>osfHelp:			ZafHelp()\n\
	 <Leave>:				ZafLeave()\n\
	 <Key>:					ZafKey()\n\
	 <Btn1Down>:			ZafMouse()\n\
	 <Btn1Up>:				ZafMouse()\n\
	 <Btn1Motion>:			ZafMouse()\n\
	 <Btn3Down>:			ZafMouse()\n\
	 <Btn3Up>:				ZafMouse()\n\
	 <Btn3Motion>:			ZafMouse()";

MtfWindowObjectClassRec MtfWindowObject::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&xmPrimitiveClassRec,	// superclass
		"ZafWindowObject", 					// class_name
		sizeof(MtfWindowObjectPart), 			// widget_size (data part)
		MtfWindowObject::classInitialize,		// class_initialize
		0,									// class_part_initialize
		false,								// class inited
		0,									// initialize
		0,									// initialize_hook
		XtInheritRealize,					// realize
		action,								// actions
		XtNumber(action),					// num_actions
		0, 									// resources
		0, 									// num_resources
		NULLQUARK,							// xrm
		true,								// compress_motion
		XtExposeCompressMultiple,			// compress_exposure
		true,								// compress_enterleave
		false,								// visible_interest
		0, 									// destroy
		(XtWidgetProc)resize,				// resize
		(XtExposeProc)expose,				// expose
		0,									// set_values
		0, 									// set_values_hook
		XtInheritSetValuesAlmost,			// set_values_almost
		0, 									// get_values_hook
		XtInheritAcceptFocus,				// accept_focus
		XtVersionDontCheck,					// version
		0, 									// callback_private
		XtInheritTranslations,				// tm_table
		XtInheritQueryGeometry, 			// query_geometry
		XtInheritDisplayAccelerator,		// display_accelerator
		0 									// extension
	},
	// --- XmPrimitiveClassPart ---
	{
		(XtWidgetProc)highlight,			// border_highlight
		(XtWidgetProc)unhighlight,			// border_unhighlight
		XtInheritTranslations,				// translations
		0,									// arm_and_activate
		0,									// syn_resources
		0,									// num_syn_resources
		0									// extension
	},
	// --- MtfWindowObjectClassPart ---
	{
		0									// extension
	}
};

void MtfWindowObject::classInitialize()
{
	XmResolvePartOffsets((WidgetClass) &(MtfWindowObject::classRecord),
	 &(MtfWindowObject::offsets));
}

void MtfWindowObject::drag_drop(MtfWindowObject *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// Begin the drag/drop operation.
	ZafEventStruct event(S_BEGIN_DRAG, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfWindowObject::enter(MtfWindowObject *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Chain to the base class.
	XtCallActionProc((Widget)mtfObject, "PrimitiveEnter", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_ENTER, xEvent);
	event.position.column = xEvent->xcrossing.x_root;
	event.position.line = xEvent->xcrossing.y_root;
	GetZincObject(mtfObject)->Event(event);
}

void MtfWindowObject::expose(MtfWindowObject *mtfObject, XEvent *xEvent, Region xRegion)
{
	// Make sure the object is active.
	if (!XtIsRealized((Widget)mtfObject) || !XtWindow((Widget)mtfObject))
		return;

	// Draw the object.
	ZafEventStruct event(E_MOTIF, xEvent);
	XRectangle xRect;
	XClipBox(xRegion, &xRect);
  	event.region.Assign(xRect.x, xRect.y, xRect.width, xRect.height, ZAF_PIXEL);
	GetZincObject(mtfObject)->Draw(event, E_MOTIF);
}

void MtfWindowObject::focus_in(MtfWindowObject *mtfObject, XEvent *xEvent, String *params, Cardinal *nparams)
{
	// Chain to the base class.
	XtCallActionProc((Widget)mtfObject, "PrimitiveFocusIn", xEvent, params, *nparams);
	if (!mtfObject->primitive.have_traversal)
		return; // Object is not really getting the keyboard focus.

	// Push down changes in Zinc.
	ZafWindowObject *zafObject = GetZincObject(mtfObject);
	ZafWindow *window = DynamicPtrCast(zafObject, ZafWindow);
	while (window && window->Current())
	{
		zafObject = window->Current();
		window = DynamicPtrCast(zafObject, ZafWindow);
	}
	zafObject->NotifyFocus(zafObject, true);
}

void MtfWindowObject::highlight(MtfWindowObject *mtfObject)
{
	// Draw the button highlight.
	ZafWindowObject *zafObject = GetZincObject(mtfObject);
	ZafRegionStruct drawRegion = zafObject->BeginDraw();
	zafObject->DrawFocus(drawRegion, S_CURRENT);
	zafObject->EndDraw();
}

void MtfWindowObject::key(MtfWindowObject *, XEvent *, String *, Cardinal *)
{
	// This is a no-op since the key automatically passes through the 
	// window manager.  It simply provides a stub so the Motif event manager
	// will process the key messages.
}

void MtfWindowObject::leave(MtfWindowObject *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "PrimitiveLeave", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_LEAVE, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfWindowObject::mouse(MtfWindowObject *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This event is received at the callback level.  Thus, the mouse
	// message has already passed through the window manager and must 
	// be interpreted as a logical event to prevent recursion.
	ZafWindowObject *zafObject = GetZincObject(mtfObject);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	// Choose the proper message.
	if (xEvent->type == MotionNotify)
	{
		if (xEvent->xmotion.state & Button1Mask)
			event.type = L_CONTINUE_SELECT;
		else if (xEvent->xmotion.state & Button3Mask)
			event.type = L_CONTINUE_ESCAPE;
		else
			event.type = L_VIEW;                                    
		zafObject->Event(event);
	}
	else if (xEvent->xbutton.button == Button1)
	{
		if (xEvent->xbutton.type == ButtonPress)
			event.type = L_BEGIN_SELECT;
		else if (xEvent->xbutton.type == ButtonRelease)
			event.type = L_END_SELECT;
		else
			event.type = L_CONTINUE_SELECT;
		zafObject->Event(event);
	}
	else if (xEvent->xbutton.button == Button3)
	{
		if (xEvent->xbutton.type == ButtonPress)
			event.type = L_BEGIN_ESCAPE;
		else if (xEvent->xbutton.type == ButtonRelease)
			event.type = L_END_ESCAPE;
		else
			event.type = L_CONTINUE_ESCAPE;
		zafObject->Event(event);
	}
}

void MtfWindowObject::resize(MtfWindowObject *mtfObject)
{
	GetZincObject(mtfObject)->Event(N_SIZE);
}

void MtfWindowObject::unhighlight(MtfWindowObject *mtfObject)
{
	// Draw the button highlight.
	ZafWindowObject *zafObject = GetZincObject(mtfObject);
	ZafRegionStruct drawRegion = zafObject->BeginDraw();
	zafObject->DrawFocus(drawRegion, S_NON_CURRENT);
	zafObject->EndDraw();
}

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

// Values for initial delay and repeat delay are taken from the Motif
// scroll-bar widget.
int ZafWindowObject::initialDelay = 250;
int ZafWindowObject::repeatDelay = 50;

ZafRegionStruct ZafWindowObject::BeginDraw(void)
{
	// Determine the draw area and register with the display.
	ZafRegionStruct region = parent ? parent->ConvertToDrawRegion(this, &zafRegion) : ConvertToDrawRegion(this, &zafRegion);
	if (screenID)
		display->BeginDraw(XtDisplay(screenID), XtWindow(screenID), region, region);

	// Convert the actual draw region.
	return (region);
}

ZafPositionStruct ZafWindowObject::ConvertToOSPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	// Compute the position.
	ZafPositionStruct osPosition;
	if (position)
		osPosition = *position;
	else
	{
		osPosition.column = object->zafRegion.left;
		osPosition.line = object->zafRegion.top;
	}

	// Adjust the position for non-system parent objects.
	for (ZafWindowObject *root = object->parent; root && !root->SystemObject(); root = root->parent)
	{
		osPosition.column += root->zafRegion.left;
		osPosition.line += root->zafRegion.top;
	}

	// Return the position.
	return (osPosition);
}

ZafPositionStruct ZafWindowObject::ConvertToScreenPosition(const ZafPositionStruct objectPosition) const
{
	// Get the object position.
	ZafPositionStruct osPosition;
	if (parent)
		osPosition = parent->ConvertToOSPosition(this, &objectPosition);
	else
		osPosition = windowManager->ConvertToOSPosition(this, &objectPosition);

	// Adjust to the screen coordinates.
	if (screenID && SystemObject())
	{
		for (Widget widget = screenID; widget; widget = XtParent(widget))
		{
			osPosition.column += widget->core.x;
			osPosition.line += widget->core.y;
		}
	}
	else
	{
		osPosition.column += zafRegion.left;
		osPosition.line += zafRegion.top;
		for (ZafWindowObject *object = parent; object; object = object->parent)
		{
			osPosition.column += object->zafRegion.left;
			osPosition.line += object->zafRegion.top;
		}
	}

	// Return the position.
	return (osPosition);
}

ZafPositionStruct ZafWindowObject::ConvertToObjectPosition(const ZafPositionStruct screenPosition) const
{
	ZafPositionStruct zafPosition = screenPosition;
	if (parent)
		zafPosition = parent->ConvertToZafPosition(this, &zafPosition);
	else
		zafPosition = windowManager->ConvertToZafPosition(this, &zafPosition);

	return (zafPosition);
}

bool ZafWindowObject::ConvertToZafEvent(ZafEventStruct &event)
{
	// Make sure the event is valid.
	if (event.type != E_MOTIF && event.InputType() != L_LOGICAL_EVENT)
		return (false);

	// Convert according to the motif values.
	switch (event.osEvent.type)
	{
	case KeyPress:
	case KeyRelease:
		// Check for a converted event.
		if (event.converted)
			break;

		// Determine the event modifiers and shift state.
		{
		XKeyEvent xEvent = event.osEvent.xkey;
		event.modifiers = 0;
		event.key.shiftState = 0;
		if (xEvent.state & ShiftMask)
			event.key.shiftState |= S_SHIFT;
		if (xEvent.state & ControlMask)
			event.key.shiftState |= S_CTRL;
		if (xEvent.state & Mod1Mask)
		{
			event.key.shiftState |= S_ALT;
			// Special code to handle hot-keys.
			xEvent.state &= ~(ControlMask | Mod1Mask);
		}
		if (xEvent.state & LockMask)
			event.key.shiftState |= S_CAPS_LOCK;
		event.modifiers = event.key.shiftState;
		if (xEvent.type == KeyPress)
			event.modifiers |= S_KEYDOWN;
		else
			event.modifiers |= S_KEYUP;

		// For key presses, the keysym is stored in rawCode and the ascii
		// value is stored in key.value. The keycode isn't stored, but it's
		// accesible thru XKeysymToKeycode().
		KeySym keySym;
		event.rawCode = 0;
		ZafIChar c;
		int numChars = 1;
#if defined(ZAF_UNICODE)
		Status status;
		if (!((ZafScreenDisplay *)display)->ic)
		{
			char tmp[4];
			numChars = XLookupString(&xEvent, tmp, 1, &keySym, ZAF_NULLP(XComposeStatus));
			zafCodeSet->ConvertToZafChar(tmp, c);
		}
		else if (xEvent.type == KeyPress)
		{
			wchar_t tmp[256];
			numChars = XwcLookupString(((ZafScreenDisplay *)display)->ic, &xEvent, tmp, 256, &keySym, &status);
			zafCodeSet->ConvertToZafChar(tmp, c);
		}
		else
			numChars = XwcLookupString(((ZafScreenDisplay *)display)->ic, &xEvent, &c, 1, &keySym, &status);
#else
		numChars = XLookupString(&xEvent, &c, 1, &keySym, ZAF_NULLP(XComposeStatus));
#endif
		event.rawCode = (ZafRawCode)keySym;
		if (numChars == 1)
			event.key.value = c;
		else if (numChars == 0 && xEvent.state & Mod1Mask)
		{
			// Key isn't mapped in this font.  Try with no modifiers.
			event.rawCode = (ZafRawCode)XLookupKeysym(&xEvent, 0);
			event.key.value = 0;
		}
		else
			event.key.value = 0;
		}
		break;

	case ButtonPress:
	case ButtonRelease:
		// Check for screenID or converted event.
		if (!screenID || event.converted == this)
			break;

		// Determine the event modifiers.
		{
		XButtonEvent xEvent = event.osEvent.xbutton;
		event.modifiers = xEvent.state;
		event.modifiers &= ~(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask);
		// On some OSs a ModMask is being set on the button release.  This also
		// needs to be masked off for the event to be mapped.
		event.modifiers &= ~(Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);

		// Determine the event rawCode.
		event.rawCode &= S_DOUBLE_CLICK; // preserve the double-click.
		if (xEvent.type == ButtonPress)
		{
			if (xEvent.button == Button1)
				event.rawCode = M_LEFT | M_LEFT_CHANGE;
			else if (xEvent.button == Button2)
				event.rawCode = M_MIDDLE | M_MIDDLE_CHANGE;
			else if (xEvent.button == Button3)
				event.rawCode = M_RIGHT | M_RIGHT_CHANGE;
		}
		else	// ButtonRelease
		{
			if (xEvent.button == Button1)
				event.rawCode = M_LEFT_CHANGE;
			else if (xEvent.button == Button2)
				event.rawCode = M_MIDDLE_CHANGE;
			else if (xEvent.button == Button3)
				event.rawCode = M_RIGHT_CHANGE;
		}

		// Check for double-click.
		if (!event.converted && xEvent.type == ButtonPress)
		{
			static OSWindowID lastScreenID = 0;
			static Time lastTime = 0;
			static Time doubleClickRate = 0;
			if (!doubleClickRate)
				doubleClickRate = (Time)XtGetMultiClickTime(XtDisplay(screenID));
			if (screenID == lastScreenID && xEvent.time - lastTime <= doubleClickRate)
				event.rawCode |= S_DOUBLE_CLICK;
			lastTime = xEvent.time;
			lastScreenID = screenID;
		}

		// Compute the event position.
		event.position.Assign(xEvent.x, xEvent.y, ZAF_PIXEL);
		event.position = ConvertToZafPosition(this, &event.position);

		// See if coordinates need to be pushed up.
		if (this == windowManager)
		{
			Position root_x, root_y;
			XtTranslateCoords(XtWindowToWidget(xEvent.display, xEvent.window),
				xEvent.x, xEvent.y, &root_x, &root_y);
			event.position.column = (int)root_x;
			event.position.line = (int)root_y;
		}
		else if (SystemObject() && XtWindow(screenID) != xEvent.window)
		{
			for (Widget widget = XtParent(screenID); widget && XtWindow(widget) != xEvent.window; widget = XtParent(widget))
			{
				event.position.column -= widget->core.x;
				event.position.line -= widget->core.y;
			}
		}
		}
		break;

	case MotionNotify:
		// Check for a screenID or converted event.
		if (!screenID || event.converted == this)
			break;

		// Determine the event rawCode.
		{
		XMotionEvent xEvent = event.osEvent.xmotion;
		if (xEvent.state & Button1Mask)
			event.rawCode = M_LEFT;
		else if (xEvent.state & Button2Mask)
			event.rawCode = M_MIDDLE;
		else if (xEvent.state & Button3Mask)
			event.rawCode = M_RIGHT;

		// Compute the event position.
		event.position.Assign(xEvent.x, xEvent.y, ZAF_PIXEL);
		event.position = ConvertToZafPosition(this, &event.position);

		// See if coordinates need to be pushed up.
		if (this == windowManager)
		{
			Position root_x, root_y;
			XtTranslateCoords(XtWindowToWidget(xEvent.display, xEvent.window),
				xEvent.x, xEvent.y, &root_x, &root_y);
			event.position.column = (int)root_x;
			event.position.line = (int)root_y;
		}
		else if (SystemObject() && XtWindow(screenID) != xEvent.window)
		{
			for (Widget widget = XtParent(screenID); widget && XtWindow(widget) != xEvent.window; widget = XtParent(widget))
			{
				event.position.column -= widget->core.x;
				event.position.line -= widget->core.y;
			}
		}
		}
		break;

	default:
		// Un-processed event types.
		return (false);
	}

	// Return a processed event.
	event.converted = this;
	return (true);
}

ZafPositionStruct ZafWindowObject::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	// Adjust the position for non-system parent objects.
	ZafPositionStruct zafPosition;
	if (position)
		zafPosition = *position;
	else
	{
		zafPosition.column = object->zafRegion.left;
		zafPosition.line = object->zafRegion.top;
	}

	// Adjust the position for non-system parent objects.  This must be done
	// in two pieces because object is "const."
	for (const ZafWindowObject *root = object; root && !root->SystemObject(); root = root->parent)
	{
		zafPosition.column -= root->zafRegion.left;
		zafPosition.line -= root->zafRegion.top;
	}

	// Return the position.
	return (zafPosition);
}

ZafRegionStruct ZafWindowObject::ConvertToDrawRegion(
	const ZafWindowObject *object, const ZafRegionStruct *defaultRegion) const
{
	// Start with the os region.
	ZafRegionStruct drawRegion;
	drawRegion.coordinateType = ZAF_PIXEL;
	if (object->SystemObject() && object->screenID)
	{
		drawRegion.left = 0;
		drawRegion.top = 0;
		drawRegion.right = object->screenID->core.width - 1;
		drawRegion.bottom = object->screenID->core.height - 1;
	}
	else
		drawRegion = ConvertToOSRegion(object, defaultRegion);

	// Return the result.
	return (drawRegion);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafWindowObject::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Return the control code.
	return (ccode);
}

ZafEventType ZafWindowObject::DrawBackground(ZafRegionStruct &region, ZafEventType ccode)
{
	// Draw the background.
	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, PaletteState()));
	display->Rectangle(region, 0, true);

	// Return the status.
	return (ccode);
}

ZafEventType ZafWindowObject::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	// Check for parent draw-border.
	if (ParentDrawBorder() && parent)
		return (parent->DrawBorder(region, ccode));

	// Draw the border.
	ZafPaletteState state = PaletteState();
	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	_XmDrawShadows(XtDisplay(screenID), XtWindow(screenID),
		topShadow.osPalette, bottomShadow.osPalette,
		region.left, region.top, region.Width(), region.Height(),
		ZAF_SHADOW_SIZE, XmSHADOW_IN);
	region -= ZAF_SHADOW_SIZE;

	// Return the status.
	return (ccode);
}

ZafEventType ZafWindowObject::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Check for a valid system object.
	if (!SystemObject() || (ParentDrawFocus() && parent))
		return (parent->DrawFocus(region, ccode));

	// Draw the focus rectangle.
	XmPrimitiveRec *mtfObject = (XmPrimitiveRec *)screenID;
	if (!mtfObject->primitive.highlight_thickness)
		return (ccode);
	else if (ccode == S_CURRENT)
		(*xmPrimitiveClassRec.primitive_class.border_highlight)(screenID);
	else
		(*xmPrimitiveClassRec.primitive_class.border_unhighlight)(screenID);

	// Update the draw region.
	region -= mtfObject->primitive.highlight_thickness;
	return (ccode);
}

ZafEventType ZafWindowObject::DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode)
{
	// Check for a system object.
	if (!depth)
		return (S_ERROR);
	else if (!SystemObject())
		return (parent->DrawShadow(region, depth, ccode));

	// Draw the shadow and update the region.
	ZafPaletteState state = PaletteState();
	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	_XmDrawShadows(XtDisplay(screenID), XtWindow(screenID),
		topShadow.osPalette, bottomShadow.osPalette, region.left,
		region.top, region.Width(), region.Height(), ZafAbs(depth),
		(depth < 0) ? XmSHADOW_IN : XmSHADOW_OUT);
	region -= ZafAbs(depth);

	// Return the status.
	return (ccode);
}
#endif

ZafEventType ZafWindowObject::DragDropEvent(const ZafEventStruct &event)
{
	// Determine the drag/drop operation.
	ZafEventType ccode = event.type;
	ZafWindowObject *object;
	switch (ccode)
	{
	// --- Drag operations ---
	case S_DRAG_DEFAULT:
	case S_DRAG_MOVE:
	case S_DRAG_COPY:
	case S_DRAG_LINK:
		// Motif requires no functionality for these commands.
		break;

	// --- Drop operations ---
	case S_DROP_DEFAULT:
		object = windowManager->dragObject;
		if (Disabled())
			return (S_ERROR);
		else if (AcceptDrop() && object && object != this)
		{
			// First, try copy operation.
			ZafEventStruct dropEvent;
			dropEvent.type = S_DROP_COPY;
			dropEvent.windowObject = object;
			if (object->CopyDraggable() && (ccode = DragDropEvent(dropEvent)) != S_ERROR)
				break;

			// Next, try move operation.
			dropEvent.type = S_DROP_MOVE;
			if (object->MoveDraggable() && (ccode = DragDropEvent(dropEvent)) != S_ERROR)
				break;

			// Finally, try link operation.
			dropEvent.type = S_DROP_LINK;
			if (object->LinkDraggable())
				ccode = DragDropEvent(dropEvent);
		}
		else
			ccode = S_ERROR;
		break;

	case S_DROP_COPY:
	case S_DROP_MOVE:
		object = windowManager->dragObject;
		if (Disabled())
			return (S_ERROR);
		else if (AcceptDrop() && object && object != this)
		{
			// Perform the specified drop operation.
			const ZafIChar *text = object->Text();
			if (!text || text == Text())
				ccode = S_ERROR;
			else if (ccode == S_DROP_COPY)
				SetText(text);
			else
			{
				SetText(text);
				object->SetText(ZafLanguageManager::blankString);
			}
		}
		else
			ccode = S_ERROR;
		break;

	case S_DROP_LINK:
		// Option not supported.
		ccode = S_ERROR;
		break;

	// --- Logical operations ---
	case S_BEGIN_DRAG:
		ccode = ZafDragBegin(this, event);
		break;

	case S_END_DRAG:
		ccode = ZafDragEnd(this, event);
		break;

	// --- Unknown or un-supported operations ---
	default:
		ccode = S_ERROR;
		break;
	}

	// Return the control code.
	return (ccode);
}

void ZafWindowObject::EndDraw(void)
{
	// Restore the display.
	if (screenID)
		display->EndDraw();
}

ZafEventType ZafWindowObject::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	// Check for edit mode.
	if (editMode && event.InputType() == L_LOGICAL_EVENT)
	{
		ZafEventStruct editEvent;
		switch (ccode)
		{
		case L_BEGIN_SELECT:
			// Modify the object.
			if (parent)
				Modify(event);

			// Notify of active object.
			editEvent.type = D_SET_OBJECT;
			editEvent.rawCode = ClassID();
			editEvent.windowObject = this;
			Event(editEvent);

			// Notify of current position.
			editEvent.type = D_SET_POSITION;
			editEvent.rawCode = M_LEFT | M_LEFT_CHANGE;
			editEvent.position = event.position;
			Event(editEvent);
			break;

		case L_SELECT:
		case L_DOUBLE_CLICK:
			// Notify of edit request.
			editEvent.type = D_EDIT_OBJECT;
			editEvent.rawCode = ClassID();
			editEvent.data = this;
			Event(editEvent);
			break;

		case L_VIEW:
		case L_END_SELECT:
		case L_END_ESCAPE:
			// Determine the Zaf edit level.
			if (parent)
			{
				// Define the cursor images.
				static struct
				{
					ZafDeviceImage image;
					ZafRawCode operation;
				} cursor[] =
				{
					{ DM_MOVE, ZAF_SIZE_LEFT | ZAF_SIZE_TOP | ZAF_SIZE_RIGHT | ZAF_SIZE_BOTTOM },
					{ DM_LEFT_SIDE, ZAF_SIZE_LEFT },
					{ DM_RIGHT_SIDE, ZAF_SIZE_RIGHT },
					{ DM_TOP_SIDE, ZAF_SIZE_TOP },
					{ DM_BOTTOM_SIDE, ZAF_SIZE_BOTTOM },
					{ DM_TOP_LEFT_CORNER, ZAF_SIZE_TOP | ZAF_SIZE_LEFT },
					{ DM_TOP_RIGHT_CORNER, ZAF_SIZE_TOP | ZAF_SIZE_RIGHT },
					{ DM_BOTTOM_LEFT_CORNER, ZAF_SIZE_BOTTOM | ZAF_SIZE_LEFT },
					{ DM_BOTTOM_RIGHT_CORNER, ZAF_SIZE_BOTTOM | ZAF_SIZE_RIGHT },
					{ 0, 0 }
				};

				// Get the size options.
				const int SIZE_MARGIN = ZAF_BORDER_SIZE + ZAF_SHADOW_SIZE;
				ZafRawCode sizeOperation = 0;
				if (event.position.column <= SIZE_MARGIN)
					sizeOperation |= ZAF_SIZE_LEFT;
				else if (event.position.column >= zafRegion.Width() - SIZE_MARGIN)
					sizeOperation |= ZAF_SIZE_RIGHT;
				if (event.position.line <= SIZE_MARGIN)
					sizeOperation |= ZAF_SIZE_TOP;
				else if (event.position.line >= zafRegion.Height() - SIZE_MARGIN)
					sizeOperation |= ZAF_SIZE_BOTTOM;

				// Setting all flags acts as a move operation.
				if (!sizeOperation)
					sizeOperation = ZAF_SIZE_LEFT | ZAF_SIZE_RIGHT | ZAF_SIZE_TOP | ZAF_SIZE_BOTTOM;

				// Find the matching object.
				for (int i = 0; cursor[i].image; i++)
					if (sizeOperation == cursor[i].operation)
					{
						ZafEventStruct mouseEvent(cursor[i].image);
						mouseEvent.screenID = OSScreenID(ZAF_FRAMEID);
						eventManager->Event(mouseEvent, E_MOUSE);
						break;
					}
			}
			break;

		// --- Unknown or invalid requests ---
		default:
			ccode = S_UNKNOWN;
			break;
		}

		// Return the control code.
		return (ccode);
	}

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		{
		// Convert "zafRegion" to pixel coordinates.
		ConvertCoordinates(ZAF_PIXEL);

		// Initialize oldRegion.
		oldRegion.coordinateType = ZAF_PIXEL;
		oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);

		ZafWindowObject *rootObject = this;
		while (rootObject->parent)
			rootObject = rootObject->parent;

		// Initialize numberID and screenID and reset error.
		SetError(ZAF_ERROR_NONE);
		if (!numberID)
		{
			if (parent)
			{
				numberID = rootObject->NumberID();
				rootObject->SetNumberID((ZafNumberID)(numberID + 1));
			}
			else
			{
				numberID = windowManager->NumberID();
				windowManager->SetNumberID((ZafNumberID)(numberID + 1));
			}
		}
		// Make sure the root object always has the largest numberID.	
		else if (parent && numberID >= rootObject->NumberID())
			rootObject->SetNumberID((ZafNumberID)(numberID + 1));	
		// Set the stringID.
		if (!stringID)
		{
			ZafIChar newStringID[ZAF_MAXNAMELEN];
			strcpy(newStringID, parent ? genericFieldID : ClassName());
			itoa(numberID, &newStringID[strlen(newStringID)], 10, 0);
			SetStringID(newStringID);
		}
		}
		break;

	case S_DEINITIALIZE:
		// Clean up other variables.
		oldRegion.Assign(-1, -1, 0, 0, ZAF_PIXEL);
		screenID = 0;
		
		if (windowManager->mouseObject == this)
		    windowManager->mouseObject = ZAF_NULLP(ZafWindowObject);
		break;

	case S_REGISTER_OBJECT:
		RegisterObject();
		break;

	case S_CREATE:
		{
		// Don't allow the object to be displayed until the creation
		// process is finished.
		bool saveVisible = visible;
		SetVisible(false);

		// Initialize and register the object.
		Event(S_INITIALIZE);
		Event(S_REGISTER_OBJECT);

		// Perform the size operations.
		Event(S_COMPUTE_SIZE);
		OSSize();

		// Display the object on the screen (if visible).
		SetVisible(saveVisible);
		}
		break;

	case S_DESTROY:
		{
		// Deinitialize the object (so we don't recurse on the XtDestroyWidget).
		Widget widget = OSScreenID(ZAF_FRAMEID);
		Event(S_DEINITIALIZE);

		// Destroy the object's OS counterpart (destroys entire tree).
		if (systemObject && widget)
		{
			XtDestroyWidget(widget);

			// Let the OS finish updating before we move on.
//			ZafProcessXtEvents();
		}
		}
		break;

	// ----- Activation messages -----
	case S_CURRENT:
	case S_NON_CURRENT:
		// Change the focus.
		focus = (ccode == S_CURRENT) ? true : false;
		break;

	case L_SELECT:
	case N_CURRENT:
	case N_NON_CURRENT:
		// Call the associated user function.
		ccode = (this->*memberUserFunction)(event, ccode);
		break;

	case S_REDISPLAY:
	case S_REDISPLAY_DATA:
	case S_REDISPLAY_REGION:
		// Redisplay the region by calling expose directly.
		if (screenID && Visible() && XtWindow(screenID) &&
			XtIsRealized(screenID) && XtIsManaged(screenID))
		{
			XEvent xEvent;
			xEvent.type = Expose;
			xEvent.xexpose.serial = 0;
			xEvent.xexpose.send_event = true;
			xEvent.xexpose.display = XtDisplay(screenID);
			xEvent.xexpose.window = XtWindow(screenID);
			ZafRegionStruct region = ConvertToOSRegion(this, &zafRegion);
			if (ccode == S_REDISPLAY_REGION)
				region = event.region;
			else if (SystemObject())
			{
				region.left = region.top = 0;
				region.right = screenID->core.width - 1;
				region.bottom = screenID->core.height - 1;
			}
			if (ccode == S_REDISPLAY_DATA && screenID == OSScreenID(ZAF_FRAMEID))
			{
				if (XmIsPrimitive(screenID))
				{
					region -= ((XmPrimitiveRec *)screenID)->primitive.shadow_thickness;
					region -= ((XmPrimitiveRec *)screenID)->primitive.highlight_thickness;
				}
				else if (XmIsManager(screenID))
				{
					region -= ((XmManagerRec *)screenID)->core.border_width;
					region -= ((XmManagerRec *)screenID)->manager.shadow_thickness;
				}
			}
			xEvent.xexpose.x = region.left;
			xEvent.xexpose.y = region.top;
			xEvent.xexpose.width = region.Width();
			xEvent.xexpose.height = region.Height();
			xEvent.xexpose.count = 0;
			XSendEvent(xEvent.xexpose.display, xEvent.xexpose.window, false, ExposureMask, &xEvent);
		}
		break;

	case S_SET_FOCUS:
		{
		ZafEventStruct focusEvent = event;
		focusEvent.windowObject = this;
		eventManager->Put(focusEvent);
		}
		break;

	// ----- Position & size messages -----
	case S_SIZE:
		// Set the new region.
		SetRegion(event.region);

		// Recompute the object's size.
		if (screenID)
		{
			ConvertCoordinates(ZAF_PIXEL);
			Event(S_COMPUTE_SIZE);
			OSSize();
		}
		break;

	case S_COMPUTE_SIZE:
		if (RegionType() != ZAF_INSIDE_REGION)
   			SetRegion(parent ? parent->MaxRegion(this) : windowManager->MaxRegion(this));
		break;

	// ----- Drag & Drop -----
	case S_DRAG_DEFAULT:
	case S_DRAG_MOVE:
	case S_DRAG_COPY:
	case S_DRAG_LINK:
	case S_DROP_DEFAULT:
	case S_DROP_MOVE:
	case S_DROP_COPY:
	case S_DROP_LINK:
	case S_BEGIN_DRAG:
	case S_END_DRAG:
		ccode = DragDropEvent(event);
		break;

	// ----- Scroll messages -----
	case N_VSCROLL:
	case N_HSCROLL:
	case S_VSCROLL:
	case S_HSCROLL:
	case S_VSCROLL_SET:
	case S_HSCROLL_SET:
	case S_VSCROLL_CHECK:
	case S_HSCROLL_CHECK:
	case S_VSCROLL_COMPUTE:
	case S_HSCROLL_COMPUTE:
		ccode = ScrollEvent(event);
		break;

	// ----- Miscellaneous -----
	case S_HELP:
	case L_HELP:
		// Display help to the user.
		if (zafHelpSystem)
		{
			// Try to find a specific help request.
			for (ZafWindowObject *object = this; object; object = object->Parent())
				if (object->HelpContext())
				{
					zafHelpSystem->DisplayHelp(object->HelpContext());
					return (ccode);
				}

			// Defer to default help, if no specific context exists.
			zafHelpSystem->DisplayHelp(helpContext);
		}
		break;

	case N_MOUSE_ENTER:
		{
		// Update the windowManager's mouseObject.
		ZafWindowObject *root = this;
		while (!root->QuickTip() && !root->HelpObjectTip() && root->parent)
			root = root->parent;

		// Make sure the help-tip needs to be changed.
		if (windowManager->mouseObject == root)
			break;
		
		// If windowManager->mouseObject is not NULL, it didn't get
		// an N_MOUSE_LEAVE, so send it one.
		if (windowManager->mouseObject)
		{
			ZafEventStruct tEvent = event;
			tEvent.type = N_MOUSE_LEAVE;
			windowManager->mouseObject->Event(tEvent);
		}
		        
		// If help tip, update the help tips device.
		windowManager->mouseObject = root;
		if ((root->QuickTip() || root->HelpObjectTip()) && windowManager->Focus())
			eventManager->Event(event, E_HELPTIPS);
		}
		break;

	case N_MOUSE_LEAVE:
		{
		// Update the windowManager's mouseObject.
		ZafWindowObject *root = this;
		while (!root->QuickTip() && !root->HelpObjectTip() && root->parent)
			root = root->parent;

		// Make sure the help-tip needs to be changed.
		if (windowManager->mouseObject != root)
			break;

		// If help tip, update the help tips device.
		if ((root->QuickTip() || root->HelpObjectTip()) && windowManager->Focus())
		{
			int posX = event.osEvent.xcrossing.x;
			int posY = event.osEvent.xcrossing.y;
			if (posX > 0 && posY > 0 && posX < zafRegion.Width() &&
				posY < zafRegion.Height())
				break;
			eventManager->Event(event, E_HELPTIPS);
		}

		// Update the windowManager's mouseObject.
		windowManager->mouseObject = ZAF_NULLP(ZafWindowObject);
		}
		break;

	// ----- Unknown or error messages -----
	default:
		return (S_UNKNOWN);
	}

	// Return the control code.
	return (ccode);
}

void ZafWindowObject::GetClip(ZafWindowObject *object, ZafRegionStruct &clip, ZafRegionStruct &virtualRegion)
{
	// Set the values.
	clip = virtualRegion = object->zafRegion;
}

ZafEventType ZafWindowObject::Modify(const ZafEventStruct &mEvent)
{
	ZafEventStruct event = mEvent;
	ZafRegionStruct lastRegion = zafRegion, region = zafRegion;
	ZafPositionStruct lastPosition = mEvent.position, position = mEvent.position;

	// Determine the type of modify operation.
	ZafRawCode sizeOperation = 0;
	if (NumberID() == ZAF_NUMID_MIN_ICON)
		sizeOperation = ZAF_SIZE_LEFT | ZAF_SIZE_RIGHT | ZAF_SIZE_TOP | ZAF_SIZE_BOTTOM;
	else if (mEvent.type == L_BEGIN_SELECT)
	{
		// Determine the size constraints.
		const int SIZE_MARGIN = ZAF_BORDER_SIZE + ZAF_SHADOW_SIZE;
		if (position.column <= SIZE_MARGIN)
			sizeOperation |= ZAF_SIZE_LEFT;
		else if (position.column >= zafRegion.Width() - SIZE_MARGIN)
			sizeOperation |= ZAF_SIZE_RIGHT;
		if (position.line <= SIZE_MARGIN)
			sizeOperation |= ZAF_SIZE_TOP;
		else if (position.line >= zafRegion.Height() - SIZE_MARGIN)
			sizeOperation |= ZAF_SIZE_BOTTOM;
	}
	else
		lastPosition.column = lastPosition.line = 0;

	// Setting all flags acts as a move operation.
	if (!sizeOperation)
		sizeOperation = ZAF_SIZE_LEFT | ZAF_SIZE_RIGHT | ZAF_SIZE_TOP | ZAF_SIZE_BOTTOM;

	// Go to X directly, so the ZAF display is not flooded with two updates
	// at the same time.
	Widget frame = OSScreenID(ZAF_FRAMEID);
	Widget boundary = XtParent(frame);
	OSDisplayContext xDisplay = XtDisplay(boundary);
	Drawable xDrawable = XtWindow(boundary);
	XGCValues gcValues;
	gcValues.function = GXinvert;
	gcValues.line_width = 2;
	gcValues.line_style = LineSolid;
	gcValues.subwindow_mode = IncludeInferiors;
	gcValues.background = gcValues.foreground = (unsigned long)~0;
	GC xorGC = XCreateGC(xDisplay, xDrawable, GCFunction | GCLineWidth |
		GCLineStyle | GCSubwindowMode | GCForeground | GCBackground,
		&gcValues);

	// Move the object.
	ZafEventType ccode;
	XDrawRectangle(xDisplay, xDrawable, xorGC, region.left, region.top,
		region.Width(), region.Height());
	do
	{
		// Get the next event.
		eventManager->Get(event, Q_NORMAL);

		// Check for specific mouse messages.
		if (event.type == E_MOTIF)
		{
			if (event.osEvent.type == ButtonPress)
				ccode = L_BEGIN_SELECT;
			else if (event.osEvent.type == ButtonRelease)
				ccode = L_END_SELECT;
			else if (event.osEvent.type == MotionNotify)
			{
				ZafEventStruct tEvent;
				while (eventManager->Get(tEvent, Q_NO_BLOCK | Q_NO_DESTROY) == 0 && tEvent.osEvent.type == event.osEvent.type)
					eventManager->Get(event, Q_NORMAL);
					// Sometimes the ButtonRelease is pulled of the queue. Make sure
					// we don't miss it.
					if (event.osEvent.type == ButtonRelease)
						ccode = L_END_SELECT;
					else
						ccode = L_CONTINUE_SELECT;
			}
			else if (event.osEvent.type == EnterNotify || event.osEvent.type == LeaveNotify)
				continue; // prevent redraws.

			// Normalize the coordinates.
			if (ccode != E_MOTIF)
			{
				event.position.Assign(event.osEvent.xbutton.x, event.osEvent.xbutton.y, ZAF_PIXEL);
				Widget tWidget = XtWindowToWidget(event.osEvent.xbutton.display, event.osEvent.xbutton.window);
				while (tWidget && tWidget != frame)
				{
					event.position.column += tWidget->core.x;
					event.position.line += tWidget->core.y;
					tWidget = XtParent(tWidget);
				}
			}
		}

		// Process the event.
		if (ccode == L_BEGIN_SELECT || ccode == L_CONTINUE_SELECT || ccode == L_END_SELECT)
		{
			// Check for out of bounds.
			position = event.position;
			if (position.column + frame->core.x < 0)
				position.column = -frame->core.x;
			else if (position.column + frame->core.x >= boundary->core.width)
				position.column = boundary->core.width - frame->core.x + 1;
			if (position.line + frame->core.y < 0)
				position.line = -frame->core.y;
			else if (position.line + frame->core.y >= boundary->core.height)
				position.line = boundary->core.height - frame->core.y + 1;

			// Get the new position.
			position.column -= lastPosition.column;
			position.line -= lastPosition.line;
			if (editMode)
			{
				position.ConvertCoordinates(coordinateType);
				position.ConvertCoordinates(zafRegion.coordinateType);
			}

			// Modify the object's region.
			if (sizeOperation & ZAF_SIZE_LEFT)
				region.left = zafRegion.left + position.column;
			if (sizeOperation & ZAF_SIZE_TOP)
				region.top = zafRegion.top + position.line;
			if (sizeOperation & ZAF_SIZE_RIGHT)
				region.right = zafRegion.right + position.column;
			if (sizeOperation & ZAF_SIZE_BOTTOM)
				region.bottom = zafRegion.bottom + position.line;

			// Check for region out-of-range.
			if (region.right <= region.left || region.bottom <= region.top)
				continue;

			// Modify the focus rectangle.
			if (lastRegion != region)
			{
				XDrawRectangle(xDisplay, xDrawable, xorGC, lastRegion.left,
					lastRegion.top, lastRegion.Width(), lastRegion.Height());
				XDrawRectangle(xDisplay, xDrawable, xorGC, region.left,
					region.top, region.Width(), region.Height());
				lastRegion = region;
			}
		}

		if (ccode != L_CONTINUE_SELECT)
			windowManager->Event(event);
	} while (ccode != S_EXIT && ccode != L_END_SELECT);
	XDrawRectangle(xDisplay, xDrawable, xorGC, lastRegion.left, lastRegion.top,
		lastRegion.Width(), lastRegion.Height());
	XFreeGC(xDisplay, xorGC);

	// Complete the move/size operation (lastRegion has the last valid region).
	if (lastRegion != zafRegion)
		Event(ZafEventStruct(S_SIZE, 0, lastRegion));

	// Return success.
	return (ccode);
}

ZafEventType ZafWindowObject::MoveEvent(const ZafEventStruct &)
{
	// No movement is defined at this level.
	return (S_ERROR);
}

ZafRegionStruct ZafWindowObject::MaxRegion(ZafWindowObject *, ZafVtJustify , ZafHzJustify )
{
	// Default is the object's region, adjusted for 0,0 top-left coordinates.
	ZafRegionStruct region = zafRegion;
	if (parent)
	{
		region.right = zafRegion.right - zafRegion.left;
		region.bottom = zafRegion.bottom - zafRegion.top;
	}
	else
	{
		Widget widget = OSScreenID(ZAF_FRAMEID);
		region.right = widget->core.width - 1;
		region.bottom = widget->core.height - 1;
	}
	region.left = region.top = 0;
	region.coordinateType = ZAF_PIXEL;

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

bool ZafWindowObject::RegisterMouse(bool view, bool leftMouse, bool rightMouse)
{
	// Check for a valid ZAF object.
	if (!screenID || (!view && !leftMouse && !rightMouse))
		return (false);

	// Add edit mode operations.
	static bool initialized = false;
	static XtActionsRec mouse_actions[] =
	{
		{ "ZafMouseCallback", 	ZafMouseCallback }
	};
	if (!initialized)
	{
		initialized = true;
		XtAppAddActions(((ZafScreenDisplay *)zafDisplay)->xAppContext, mouse_actions, XtNumber(mouse_actions));
	}

	// Define the object translations and event mask.
	EventMask eventMask = 0;
	char translations[512];
	translations[0] = '\0';
	if (leftMouse)
	{
		eventMask |= ButtonPress | ButtonRelease;
		strcat(translations,
			"<Btn1Down>:	ZafMouseCallback()\n\
			 <Btn1Motion>: 	ZafMouseCallback()\n\
			 <Btn1Up>: 		ZafMouseCallback()\n");
	}
	if (rightMouse)
	{
		eventMask |= ButtonPress | ButtonRelease;
		strcat(translations,
			"<Btn3Down>:	ZafMouseCallback()\n\
			 <Btn3Motion>:	ZafMouseCallback()\n\
			 <Btn3Up>:		ZafMouseCallback()\n");
	}
	if (view)
	{
		eventMask |= MotionNotify;
		strcat(translations,
			"<Motion>:		ZafMouseCallback()");
	}
	XtTranslations translationTable = XtParseTranslationTable(translations);

	// Determine the appropriate override levels.
	Widget widget = screenID;
	XtAddEventHandler(widget, eventMask, false,
		(XtEventHandler)mouse_callback, (XtPointer)this);
	XtOverrideTranslations(widget, translationTable);
	return (true);
}

void ZafWindowObject::RegisterObject(void)
{
	// Convert to the OS region.
	if (screenID)
		return;
	else if (SystemObject())
	{
		OSRegisterObject();
		OSUpdatePalettes(ZAF_PM_ANY_TYPE, ZAF_PM_LIGHT_SHADOW | ZAF_PM_DARK_SHADOW);
	}
	else if (parent)
		screenID = parent->screenID;
}

ZafEventType ZafWindowObject::ScrollEvent(const ZafEventStruct &)
{
	// No scrolling is defined at this level.
	return (S_ERROR);
}

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

bool ZafWindowObject::SetAcceptDrop(bool setAcceptDrop)
{
	// Make sure the attribute has changed.
	if (acceptDrop != setAcceptDrop)
	{
		acceptDrop = setAcceptDrop;

		// Activate/Deactivate the Motif drop capabilities.
		if (screenID)
			ZafDropSiteRegister(this);
	}

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

bool ZafWindowObject::SetAutomaticUpdate(bool setAutomaticUpdate)
{
	// Make sure the attribute has changed.
	if (setAutomaticUpdate != automaticUpdate)
	{
		automaticUpdate = setAutomaticUpdate;

		// Match up the OS.
		if (screenID && SystemObject() && AutomaticUpdate())
		{
			OSUpdatePalettes(ZAF_PM_ANY_TYPE, ZAF_PM_ANY_TYPE);
			Widget widget = OSScreenID(ZAF_FRAMEID);
			if (!XtIsManaged(widget) && visible)
				XtManageChild(widget);
		}
	}

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

bool ZafWindowObject::SetEditMode(bool setEditMode)
{
	// Make sure the attribute has changed.
	if (editMode != setEditMode && !screenID)
		editMode = setEditMode;
	else if (screenID && !setEditMode && Visible())
		XUndefineCursor(XtDisplay(screenID), XtWindow(screenID));

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

bool ZafWindowObject::SetFocus(bool setFocus)
{
	// Check for valid focus.
	ZafWindow *window = DynamicPtrCast(RootObject(), ZafWindow);
	if (Disabled() || (Noncurrent() && (!window || !window->Temporary())))
		setFocus = false;

	if (!screenID)
	{
		if (focus == setFocus)
			;
		else if (setFocus)
			NotifyFocus(this, true);
		else if (Event(N_NON_CURRENT) == 0)
			focus = false;
	}
	if (!screenID || !XtIsRealized(screenID) || !XtIsManaged(screenID))
		return (focus);

	// Update the focus with the OS.
	if (setFocus)
	{
		// If there is a modal window then the modal window should be raised
		// if it is not the top most window.
		if (zafWindowManager->modalWindow && window != zafWindowManager->modalWindow)
		{	
			setFocus = false;
			if (!zafWindowManager->modalWindow->Focus() && 
				zafWindowManager->modalWindow->OSScreenID(ZAF_SHELLID))
			{	
				// Raise the modal window.
				Widget modalWin = zafWindowManager->modalWindow->OSScreenID(ZAF_SHELLID);
				XRaiseWindow(XtDisplay(modalWin), XtWindow(modalWin));
				XmProcessTraversal(modalWin, XmTRAVERSE_CURRENT);
			}
			return (setFocus);
		}
		else
		{
			// First, make sure the shell is raised.
			Widget shellID = window->OSScreenID(ZAF_SHELLID);

			// Check to see if we are working with a child 
			// temporary. If so don't set the focus up too far.
			ZafWindow *tWindow = DynamicPtrCast(parent, ZafWindow);
			if (tWindow && tWindow->Temporary() && tWindow->OSScreenID(ZAF_SHELLID))
				shellID = tWindow->OSScreenID(ZAF_SHELLID);

			XRaiseWindow(XtDisplay(shellID), XtWindow(shellID));

			// Change the widget focus.
			for (Widget widget = screenID; widget; widget = XtParent(widget))
				if (XmProcessTraversal(widget, XmTRAVERSE_CURRENT))
					break;
		}
	}

	// Make sure the attribute has changed in Zinc.
	if (focus == setFocus)
		return (focus);
	else if (setFocus)
		NotifyFocus(this, true);
	else if (Event(N_NON_CURRENT) == 0)
		focus = false;

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

bool ZafWindowObject::SetNoncurrent(bool setNoncurrent)
{
	// Make sure the attribute has changed.
	if (noncurrent != setNoncurrent)
	{
		// Set the attribute.
		noncurrent = setNoncurrent;
		if (screenID && SystemObject())
		{
			XtVaSetValues(screenID, XmNtraversalOn, noncurrent ? false : true, 0);
			XtVaSetValues(screenID, XmNeditable, noncurrent ? false : true, 0);
		}
	
	}

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

bool ZafWindowObject::SetDisabled(bool setDisabled)
{
	// Make sure the attribute has changed.
	if (disabled != setDisabled)
	{
		disabled = setDisabled;

		// Update the display.
		if (screenID && SystemObject())
		{	
			bool tempDisabled = !Disabled();
			XtVaSetValues(screenID, XmNsensitive, tempDisabled, 0);
			XtVaSetValues(screenID, XmNeditable, tempDisabled, 0);
			XtVaSetValues(screenID, XmNcursorPositionVisible, tempDisabled, 0);
		}
		if (screenID)
			RedisplayData();
	}

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

bool ZafWindowObject::SetVisible(bool setVisible)
{
	// Check for identical values.
	if (visible == setVisible)
		return (visible);
	visible = setVisible;

	// Update the field.
	if (!screenID)
		return (visible);
	else if (SystemObject())
	{
		Widget widget = OSScreenID(ZAF_FRAMEID);
		Widget shell = OSScreenID(ZAF_SHELLID);

		// If it is a window it must be realized or unrealized.
		if (visible && shell && !XtIsRealized(shell))
		{
			XtRealizeWidget(shell);
			XRaiseWindow(XtDisplay(shell), XtWindow(shell));
		}
		else if (!visible && shell && XtIsRealized(shell) && !parent)
			XtUnrealizeWidget(shell);

		if (visible && !XtIsManaged(widget))
			XtManageChild(widget);
		else if (!visible && XtIsManaged(widget))
			XtUnmanageChild(widget);
	}
	else if (XtWindow(screenID))
	{	
		ZafRegionStruct eraseRegion = ConvertToDrawRegion(this);
		XClearArea(XtDisplay(screenID), XtWindow(screenID), eraseRegion.left,
			eraseRegion.top, eraseRegion.Width(), eraseRegion.Height(), true);
	}
	// Return the current attribute.
	return (visible);
}

// ----- OS Specific Functions ----------------------------------------------

ZafEventType ZafWindowObject::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

void ZafWindowObject::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Check for a non-system object.
	if (!screenID)
		return;
	else if (!SystemObject())
	{
		parent->OSMapPalette(palette, type, state);
		return;
	}
	else if (!XmIsPrimitive(screenID))
		return;

	// Determine the proper palette.
	XGCValues values;
	XtGCMask mask = GCForeground | GCBackground;
	XmPrimitiveRec *widget = (XmPrimitiveRec *)screenID;
	if (type == ZAF_PM_FOCUS || type == ZAF_PM_OUTLINE)
	{
		values.foreground = widget->primitive.highlight_color;
		values.background = widget->core.background_pixel;
		if (widget->primitive.highlight_pixmap != XmUNSPECIFIED_PIXMAP)
		{
   			mask |= GCFillStyle | GCTile;
   			values.fill_style = FillTiled;
   			values.tile = widget->primitive.highlight_pixmap;
		}
	}
	else if (type == ZAF_PM_LIGHT_SHADOW)
	{
		if (palette.colorForeground < ZAF_MAXCOLORS && display->DisplayMode() == ZAF_DISPLAY_COLOR)
			values.foreground = ((ZafScreenDisplay *)display)->colorTable[palette.colorForeground].pixel;
		else if (palette.monoForeground < ZAF_MAXCOLORS && display->DisplayMode() == ZAF_DISPLAY_MONO)
			values.foreground = ((ZafScreenDisplay *)display)->monoTable[palette.monoForeground].pixel;
		else
			values.foreground = widget->primitive.top_shadow_color;
		values.background = widget->primitive.foreground;
		if (widget->primitive.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
		{
  			mask |= GCFillStyle | GCTile;
  			values.fill_style = FillTiled;
  			values.tile = widget->primitive.top_shadow_pixmap;
		}
	}
	else if (type == ZAF_PM_DARK_SHADOW)
	{
		if (palette.colorForeground < ZAF_MAXCOLORS && display->DisplayMode() == ZAF_DISPLAY_COLOR)
			values.foreground = ((ZafScreenDisplay *)display)->colorTable[palette.colorForeground].pixel;
		else if (palette.monoForeground < ZAF_MAXCOLORS && display->DisplayMode() == ZAF_DISPLAY_MONO)
			values.foreground = ((ZafScreenDisplay *)display)->monoTable[palette.monoForeground].pixel;
		else
			values.foreground = widget->primitive.bottom_shadow_color;
		values.background = widget->primitive.foreground;
		if (widget->primitive.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
		{
			mask |= GCFillStyle | GCTile;
			values.fill_style = FillTiled;
			values.tile = widget->primitive.bottom_shadow_pixmap;
		}
	}
	else
	{
		parent->OSMapPalette(palette, type, state);
		return;
	}
	palette.osPalette = XtGetGC(screenID, mask, &values);
}

void ZafWindowObject::OSRegisterObject(void)
{
	// Create the motif widget.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = XtVaCreateWidget(name,
		(WidgetClass)&MtfWindowObject::classRecord,
		parent->OSScreenID(ZAF_CLIENTID),
		XmNuserData, this,
		XmNsensitive, Disabled(false) ? false : true,
		XmNtraversalOn, Noncurrent() ? false : true,
		0);

	// Override translations.
	XtTranslations translationTable = XtParseTranslationTable(MtfWindowObject::translations);
	XtOverrideTranslations(screenID, translationTable);

	// Add edit mode overrides.
	ZafEditModeRegister(this);

	// Add drag/drop capabilities.
	ZafDropSiteRegister(this);
}

OSWindowID ZafWindowObject::OSScreenID(ZafScreenIDType type) const
{
	// Return the client widget.
	if (type == ZAF_SHELLID)
		return (0);
	return (screenID);
}

ZafError ZafWindowObject::OSSetText(void)
{
	// Setting ostext at window object level is invalid.
	return (ZAF_ERROR_INVALID);
}

void ZafWindowObject::OSSize(void)
{
	// Convert to the os region.
	ZafRegionStruct newRegion;
	if (parent)
		newRegion = parent->ConvertToOSRegion(this, &zafRegion);
	else
		newRegion = windowManager->ConvertToOSRegion(this, &zafRegion);

	if (newRegion.Width() <= 0 || newRegion.Height() <=0
		&& oldRegion != newRegion)
	{
		oldRegion.Assign(0, 0, 0, 0);
		bool saveVisible = visible;
		SetVisible(false);
		visible = saveVisible;
		Event(N_SIZE);
		return;
	}
	else
		SetVisible(visible);

	// Determine whether the object needs to be sized.
	if (oldRegion == newRegion)
		return; // no action needed.
	else if (screenID && SystemObject())
	{
		// Check the widget size.
		Widget widget = OSScreenID(parent ? ZAF_FRAMEID : ZAF_SHELLID);
		if (!parent)
		  XtVaSetValues(widget,
			XmNx, newRegion.left,
			XmNy, newRegion.top,
			XmNwidth, newRegion.Width(),
			XmNheight, newRegion.Height(),
			0);
		else if (newRegion.Width() != oldRegion.Width() ||
			newRegion.Height() != oldRegion.Height())
			XtConfigureWidget(widget, newRegion.left, newRegion.top,
				newRegion.Width(), newRegion.Height(), 0);
		else
			XtMoveWidget(widget, newRegion.left, newRegion.top);

		// Check for unmanaged children.
		if (!parent)
			widget = OSScreenID(ZAF_FRAMEID);
		if (visible && !XtIsManaged(widget) && (!parent || parent->AutomaticUpdate()))
			XtManageChild(widget);

		Event(N_SIZE);
	}
	else if (!SystemObject())
	{
		// Update the region.
		Event(N_SIZE);

		// Invalidate the appropriate regions.
		if (screenID && XtIsRealized(screenID) && XtIsManaged(screenID) &&
			XtWindow(screenID) && (!parent || parent->AutomaticUpdate()))
		{
			if (oldRegion.bottom >= 0 && oldRegion.top < screenID->core.height)
				XClearArea(XtDisplay(screenID), XtWindow(screenID),
					oldRegion.left, oldRegion.top, oldRegion.Width(),
					oldRegion.Height(), true);
			if (newRegion.bottom >= 0 && newRegion.top < screenID->core.height)
				XClearArea(XtDisplay(screenID), XtWindow(screenID),
					newRegion.left, newRegion.top, newRegion.Width(),
					newRegion.Height(), true);
		}
	}

	// Update the old region.
	oldRegion = newRegion;
}

ZafError ZafWindowObject::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Make sure the widget can be updated.
	if (!screenID || !AutomaticUpdate())
		return (ZAF_ERROR_INVALID);
	else if(!SystemObject())
	{
		Redisplay();
		return (ZAF_ERROR_NONE);
	}		

	// Reconfigure the automatic update settings, where appropriate.
	Arg args[10];
	Cardinal nargs = 0;

	Pixel mtfBackground, mtfForeground, mtfHighlight;
	Pixel mtfTopShadow, mtfBottomShadow;
	XmFontList mtfFont = 0;
	XtSetArg(args[nargs], XmNbackground, &mtfBackground); nargs++;
	XtSetArg(args[nargs], XmNforeground, &mtfForeground); nargs++;
	XtSetArg(args[nargs], "fontList", &mtfFont); nargs++;
	XtSetArg(args[nargs], XmNtopShadowColor, &mtfTopShadow); nargs++;
	XtSetArg(args[nargs], XmNbottomShadowColor, &mtfBottomShadow); nargs++;
	XtSetArg(args[nargs], XmNhighlightColor, &mtfHighlight); nargs++;
	XtGetValues(screenID, args, nargs);
	nargs = 0;

	int width = screenID->core.width;
	int height = screenID->core.height;

	// Background palette.
	ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_NOT_SELECTED);
	if (zafTypes & ZAF_PM_BACKGROUND)
	{
		if (display->DisplayMode() == ZAF_DISPLAY_COLOR && background.colorBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->colorTable[background.colorBackground].pixel != mtfBackground)
		{
			XtSetArg(args[nargs], XmNbackground, ((ZafScreenDisplay *)display)->colorTable[background.colorBackground].pixel);
			nargs++;
		}
		else if (display->DisplayMode() == ZAF_DISPLAY_MONO && background.monoBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->monoTable[background.monoBackground].pixel != mtfBackground)
		{
			XtSetArg(args[nargs], XmNbackground, ((ZafScreenDisplay *)display)->monoTable[background.monoBackground].pixel);
			nargs++;
		}
	}

	// Foreground palette.
	ZafPaletteStruct foreground = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED);
	if (zafTypes & ZAF_PM_FOREGROUND)
	{
		if (display->DisplayMode() == ZAF_DISPLAY_COLOR && foreground.colorForeground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->colorTable[foreground.colorForeground].pixel != mtfForeground)
		{
			XtSetArg(args[nargs], XmNforeground, ((ZafScreenDisplay *)display)->colorTable[foreground.colorForeground].pixel);
			nargs++;
		}
		else if (display->DisplayMode() == ZAF_DISPLAY_MONO && foreground.monoForeground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->monoTable[foreground.monoForeground].pixel != mtfForeground)
		{
			XtSetArg(args[nargs], XmNforeground, ((ZafScreenDisplay *)display)->monoTable[foreground.monoForeground].pixel);
			nargs++;
		}
	}

	// Font palette.
	if (zafTypes & ZAF_PM_TEXT)
	{
		ZafPaletteStruct palette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED);
		if (palette.font >= 0 &&
			mtfFont && ((ZafScreenDisplay *)display)->fontTable[palette.font] != mtfFont)
		{
#if defined(_IBM_RS6000)
		if ((!IsA(ID_ZAF_STRING) && !IsA(ID_ZAF_TEXT)) || !XtIsRealized(screenID))	
#endif
			{
			XtSetArg(args[nargs], "fontList", ((ZafScreenDisplay *)display)->fontTable[palette.font]);
			nargs++;
			}
		}
		else if (palette.font == ZAF_FNT_DEFAULT && (osTypes & ZAF_PM_TEXT))
		{
			XmFontList font = 0;
			static XtResource resource = { "fontList", "FontList", "FontList", sizeof(XmFontList), 0, "Immediate", (XtPointer)0 };
			XtGetApplicationResources(screenID, &font, &resource, 1, 0, 0);
			if (font != mtfFont)
			{
				XtSetArg(args[nargs], "fontList", font);
				nargs++;
			}
		}
	}

	// Top-shadow palette.
	ZafPaletteStruct lightShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, ZAF_PM_ACTIVE);
	if (zafTypes & ZAF_PM_LIGHT_SHADOW)
	{
		if (display->DisplayMode() == ZAF_DISPLAY_COLOR && lightShadow.colorBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->colorTable[lightShadow.colorBackground].pixel != mtfTopShadow)
		{
			XtSetArg(args[nargs], XmNtopShadowColor, ((ZafScreenDisplay *)display)->colorTable[lightShadow.colorBackground].pixel);
			nargs++;
		}
		else if (display->DisplayMode() == ZAF_DISPLAY_MONO && lightShadow.monoBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->monoTable[lightShadow.monoBackground].pixel != mtfTopShadow)
		{
			XtSetArg(args[nargs], XmNtopShadowColor, ((ZafScreenDisplay *)display)->monoTable[lightShadow.monoBackground].pixel);
			nargs++;
		}
	}

	// Bottom-shadow palette.
	ZafPaletteStruct darkShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, ZAF_PM_ACTIVE);
	if (zafTypes & ZAF_PM_DARK_SHADOW)
	{
		if (display->DisplayMode() == ZAF_DISPLAY_COLOR && darkShadow.colorBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->colorTable[darkShadow.colorBackground].pixel != mtfBottomShadow)
		{
			XtSetArg(args[nargs], XmNbottomShadowColor, ((ZafScreenDisplay *)display)->colorTable[darkShadow.colorBackground].pixel);
			nargs++;
		}
		else if (display->DisplayMode() == ZAF_DISPLAY_MONO && darkShadow.monoBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->monoTable[darkShadow.monoBackground].pixel != mtfBottomShadow)
		{
			XtSetArg(args[nargs], XmNbottomShadowColor, ((ZafScreenDisplay *)display)->monoTable[darkShadow.monoBackground].pixel);
			nargs++;
		}
	}

	// Highlight palette.
	if (zafTypes & ZAF_PM_FOCUS)
	{
		ZafPaletteStruct palette = LogicalPalette(ZAF_PM_FOCUS, ZAF_PM_ACTIVE);
		if (display->DisplayMode() == ZAF_DISPLAY_COLOR && palette.colorBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->colorTable[palette.colorBackground].pixel != mtfHighlight)
		{
			XtSetArg(args[nargs], XmNhighlightColor, ((ZafScreenDisplay *)display)->colorTable[palette.colorBackground].pixel);
			nargs++;
		}
		else if (display->DisplayMode() == ZAF_DISPLAY_MONO && palette.monoBackground < ZAF_MAXCOLORS &&
			((ZafScreenDisplay *)display)->monoTable[palette.monoBackground].pixel != mtfHighlight)
		{
			XtSetArg(args[nargs], XmNhighlightColor, ((ZafScreenDisplay *)display)->monoTable[palette.monoBackground].pixel);
			nargs++;
		}
	}

	// Reset the default arguments.
	if (nargs)
		XtSetValues(screenID, args, nargs);

	// Background and Foreground must be matched after other requests are made.
	if ((osTypes & ZAF_PM_BACKGROUND) &&
		((display->DisplayMode() == ZAF_DISPLAY_COLOR && background.colorBackground == ZAF_CLR_DEFAULT) ||
		 (display->DisplayMode() == ZAF_DISPLAY_MONO && background.monoBackground == ZAF_CLR_DEFAULT)))
	{
		Pixel background;
		static XtResource resource = { "background", "Background", "Pixel", sizeof(Pixel), 0, "CallProc", _XmBackgroundColorDefault };
		XtGetApplicationResources(screenID, &background, &resource, 1, 0, 0);
		if (background != mtfBackground)
			XtVaSetValues(screenID, XmNbackground, background, 0);
	}
	if ((osTypes & ZAF_PM_FOREGROUND) &&
		((display->DisplayMode() == ZAF_DISPLAY_COLOR && foreground.colorForeground == ZAF_CLR_DEFAULT) ||
		 (display->DisplayMode() == ZAF_DISPLAY_MONO && foreground.monoForeground == ZAF_CLR_DEFAULT)))
	{
		Pixel foreground;
		static XtResource resource = { "foreground", "Foreground", "Pixel", sizeof(Pixel), 0, "CallProc", _XmForegroundColorDefault };
		XtGetApplicationResources(screenID, &foreground, &resource, 1, 0, 0);
		if (foreground != mtfForeground)
			XtVaSetValues(screenID, XmNforeground, foreground, 0);
	}
	if ((osTypes & ZAF_PM_LIGHT_SHADOW) &&
		((display->DisplayMode() == ZAF_DISPLAY_COLOR && lightShadow.colorBackground == ZAF_CLR_DEFAULT) ||
		 (display->DisplayMode() == ZAF_DISPLAY_MONO && lightShadow.monoBackground == ZAF_CLR_DEFAULT)))
	{
		Pixel lightShadow;
		static XtResource resource = { "topShadowColor", "TopShadowColor", "Pixel", sizeof(Pixel), 0, "CallProc", _XmTopShadowColorDefault };
		XtGetApplicationResources(screenID, &lightShadow, &resource, 1, 0, 0);
		if (lightShadow != mtfTopShadow)
			XtVaSetValues(screenID, XmNtopShadowColor, lightShadow, 0);
	}
	if ((osTypes & ZAF_PM_DARK_SHADOW) &&
		((display->DisplayMode() == ZAF_DISPLAY_COLOR && darkShadow.colorBackground == ZAF_CLR_DEFAULT) ||
		 (display->DisplayMode() == ZAF_DISPLAY_MONO && darkShadow.monoBackground == ZAF_CLR_DEFAULT)))
	{
		Pixel darkShadow;
		static XtResource resource = { "bottomShadowColor", "BottomShadowColor", "Pixel", sizeof(Pixel), 0, "CallProc", _XmBottomShadowColorDefault };
		XtGetApplicationResources(screenID, &darkShadow, &resource, 1, 0, 0);
		if (darkShadow != mtfBottomShadow)
			XtVaSetValues(screenID, XmNbottomShadowColor, darkShadow, 0);
	}

	// Resize, then redraw the object if it is a MtfWindowObject.
	XtConfigureWidget(screenID, screenID->core.x, screenID->core.y, width, height, 0);
	if (osTypes && MtfWindowObject::IsMtfWindowObject(screenID))
		Redisplay();
	return (ZAF_ERROR_NONE);
}





