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

#include <w_app.hpp>
#include <z_ctype.hpp>
#include <z_help.hpp>
#include <z_htips.hpp>
#include <z_key.hpp>
#include <z_keymap.hpp>
#include <z_mouse2.hpp>
#include <z_string.hpp>
#include <z_stdlib.hpp>
#include <z_win.hpp>
#include <z_utils.hpp>

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

ZafRegionStruct ZafWindowObject::BeginDraw(void)
{
	if (editMode && parent)
	{
		HRGN hRegion = CreateRectRgn(0, 0, 10000, 10000);
		HDC hDC = GetDCEx(OSScreenID(ZAF_FRAMEID), hRegion, DCX_PARENTCLIP);
		DeleteObject(hRegion);

		display->BeginDraw(hDC, 0, zafRegion, zafRegion);
	}
	else
		// Prepare the display for drawing.
		display->BeginDraw(0, screenID, zafRegion, zafRegion);

	return (ConvertToDrawRegion(this));
}

ZafRegionStruct ZafWindowObject::ConvertToDrawRegion(const ZafWindowObject *object, const ZafRegionStruct *region) const
{
	ZafRegionStruct drawRegion;
	if (systemObject)
	{
		if (region)
			drawRegion = *region;
		else if (object && object != this)
			drawRegion = object->zafRegion;
		else
		{
			drawRegion = zafRegion;
			drawRegion.right -= drawRegion.left;
			drawRegion.bottom -= drawRegion.top;
			drawRegion.left = drawRegion.top = 0;
		}
	}
	else if (region)
	{
		drawRegion = *region;
		drawRegion.left += zafRegion.left;
		drawRegion.right += zafRegion.left;
		drawRegion.top += zafRegion.top;
		drawRegion.bottom += zafRegion.top;
		drawRegion = parent->ConvertToOSRegion(this, &drawRegion);
	}
	else
		drawRegion = parent->ConvertToOSRegion(this);

	return (drawRegion);
}

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

	// Adjust the position for non-system parent objects.
	for (const ZafWindowObject *root = this; 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
{
	ZafPositionStruct osPosition;
	if (parent)
		osPosition = parent->ConvertToOSPosition(this, &objectPosition);
	else
		osPosition = windowManager->ConvertToOSPosition(this, &objectPosition);

	POINT point;
	osPosition.Export(point);

	MapWindowPoints(screenID, HWND_DESKTOP, &point, 1);
	osPosition.Import(point);

	return (osPosition);
}

ZafPositionStruct ZafWindowObject::ConvertToObjectPosition(const ZafPositionStruct screenPosition) const
{
	POINT point;
	screenPosition.Export(point);

	MapWindowPoints(HWND_DESKTOP, screenID, &point, 1);

	ZafPositionStruct zafPosition;
	zafPosition.Import(point);
	if (parent)
		zafPosition = parent->ConvertToZafPosition(this, &zafPosition);
	else
		zafPosition = windowManager->ConvertToZafPosition(this, &zafPosition);

	return (zafPosition);
}

ZafPositionStruct ZafWindowObject::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
//??? Convert coordinate type?
	// Compute the position.
	ZafPositionStruct zafPosition;
	if (position)
		zafPosition = *position;
	else
	{
		zafPosition.coordinateType = object->zafRegion.coordinateType;
		zafPosition.column = object->zafRegion.left;
		zafPosition.line = object->zafRegion.top;
	}

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

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

bool ZafWindowObject::ConvertToZafEvent(ZafEventStruct &event)
{
	// See if event has already been converted.
	if (event.InputType() == E_KEY && !event.converted)
	{
		// Convert keyboard messages.
		ZafKeyStruct key;
		ZafRawCode rawCode;
		ZafRawCode modifiers;

		BYTE keyboardState[256];
		GetKeyboardState(keyboardState);

		rawCode = event.osEvent.wParam;

		char keyBuffer[4];
		UINT scanCode = (UINT)((event.osEvent.lParam & 0x00FF0000L) >> 16);
#if defined(ZAF_WIN32)
		int result = ToAscii(event.osEvent.wParam, scanCode,
			keyboardState, (WORD *)keyBuffer, 0);
#else
		int result = ToAscii(event.osEvent.wParam, scanCode,
			keyboardState, (DWORD *)keyBuffer, 0);
#endif
		if (keyboardState[VK_CONTROL] & 0x80)
		{
			key.value = MapVirtualKey(event.osEvent.wParam, 2);
			if (keyboardState[VK_SHIFT] & 0x80)
				key.value = ToUpper(key.value);
			else
				key.value = ToLower(key.value);
		}
		else if (result < 0)
		{
			// The character is a dead key.
			key.value = (ZafIChar)keyBuffer[0];

			// Reset the Windows internal dead key flag so that
			// TranslateMessage() will work properly.
#if defined(ZAF_WIN32)
			ToAscii(event.osEvent.wParam, scanCode,
				keyboardState, (WORD *)keyBuffer, 0);
#else
			ToAscii(event.osEvent.wParam, scanCode,
				keyboardState, (DWORD *)keyBuffer, 0);
#endif
		}
		else if (result == 0)
			// Not a translatable key.
			key.value = 0;
		else if (result == 1)
			// Normal translation.
			key.value = (ZafIChar)keyBuffer[0];
		else if (result == 2)
			// keyBuffer[0] is the dead key.
			key.value = (ZafIChar)keyBuffer[1];

		if (ZafMSWindowsApp::convertText)
			zafCodeSet->ConvertToZafChar((char *)&key.value, key.value);

		// Set the shift state.
		key.shiftState = 0;
		if (keyboardState[VK_SHIFT] & 0x80)
			key.shiftState |= S_SHIFT;
		if (keyboardState[VK_CONTROL] & 0x80)
			key.shiftState |= S_CTRL;
		if (keyboardState[VK_MENU] & 0x80)
			key.shiftState |= S_ALT;

		// Set the modifiers.
		modifiers = key.shiftState;

		if (keyboardState[VK_NUMLOCK] & 0x01)
			key.shiftState |= S_NUM_LOCK;
		if (keyboardState[VK_CAPITAL] & 0x01)
			key.shiftState |= S_CAPS_LOCK;
		if (keyboardState[VK_INSERT] & 0x80)
			key.shiftState |= S_INSERT;

		if (event.osEvent.message == WM_KEYDOWN || event.osEvent.message == WM_SYSKEYDOWN)
		{
			modifiers |= S_KEYDOWN;
//??? Repeat detections?
//???		if (event.osEvent.lParam & 0x40000000L)
//???			modifiers |= S_KEY_REPEAT;
		}
		else
			modifiers |= S_KEYUP;

		// Set the event values.
		event.rawCode = rawCode;
		event.modifiers = modifiers;
		event.key = key;
		event.converted = this;

		return (true);
	}
	else if (event.InputType() == E_MOUSE && event.converted != this)
	{
		// Convert mouse message.
		ZafRawCode rawCode = 0;
		ZafRawCode modifiers = 0;
		ZafPositionStruct position;
		position.coordinateType = ZAF_PIXEL;

		// Set the rawCode.
		UINT message = event.osEvent.message;
		if (message == WM_MOUSEMOVE || message == WM_NCMOUSEMOVE)
		{
		 	if (message == WM_MOUSEMOVE)
			{
				WPARAM wParam = event.osEvent.wParam;
				if (wParam & MK_LBUTTON)
					rawCode |= M_LEFT;
				if (wParam & MK_RBUTTON)
					rawCode |= M_RIGHT;
				if (wParam & MK_MBUTTON)
					rawCode |= M_MIDDLE;
			}
			else
			{
				if (GetAsyncKeyState(MK_LBUTTON))
				{
					if (GetSystemMetrics(SM_SWAPBUTTON))
						rawCode |= M_RIGHT;
					else
						rawCode |= M_LEFT;
				}
				if (GetAsyncKeyState(MK_RBUTTON))
				{
					if (GetSystemMetrics(SM_SWAPBUTTON))
						rawCode |= M_LEFT;
					else
						rawCode |= M_RIGHT;
				}
				if (GetAsyncKeyState(MK_MBUTTON))
					rawCode |= M_MIDDLE;
			}
		}
		else
		{
			if (message == WM_LBUTTONDOWN || message == WM_NCLBUTTONDOWN)
				rawCode |= M_LEFT | M_LEFT_CHANGE;
			else if (message == WM_LBUTTONUP || message == WM_NCLBUTTONUP)
				rawCode |= M_LEFT_CHANGE;
			else if (message == WM_RBUTTONDOWN || message == WM_NCRBUTTONDOWN)
				rawCode |= M_RIGHT | M_RIGHT_CHANGE;
			else if (message == WM_RBUTTONUP || message == WM_NCRBUTTONUP)
				rawCode |= M_RIGHT_CHANGE;
			else if (message == WM_MBUTTONDOWN || message == WM_NCMBUTTONDOWN)
				rawCode |= M_MIDDLE | M_MIDDLE_CHANGE;
			else if (message == WM_MBUTTONUP || message == WM_NCMBUTTONUP)
				rawCode |= M_MIDDLE_CHANGE;
			else if (message == WM_LBUTTONDBLCLK || message == WM_NCLBUTTONDBLCLK)
			{
				rawCode |= M_LEFT | M_LEFT_CHANGE;
				modifiers |= S_DOUBLE_CLICK;
			}
			else if (message == WM_RBUTTONDBLCLK || message == WM_NCRBUTTONDBLCLK)
			{
				rawCode |= M_RIGHT | M_RIGHT_CHANGE;
				modifiers |= S_DOUBLE_CLICK;
			}
			else if (message == WM_MBUTTONDBLCLK || message == WM_NCMBUTTONDBLCLK)
			{
				rawCode |= M_MIDDLE | M_MIDDLE_CHANGE;
				modifiers |= S_DOUBLE_CLICK;
			}
		}

		// Set the keyboard modifiers.
 		if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
			modifiers |= S_SHIFT;
		if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
			modifiers |= S_CTRL;
		if (GetAsyncKeyState(VK_MENU) & 0x8000)
			modifiers |= S_ALT;

		// Convert the position.
		LPARAM lParam = event.osEvent.lParam;
#if defined(ZAF_WIN32)
		POINTS points = MAKEPOINTS(lParam);
		POINT point;
		point.x = points.x;
		point.y = points.y;
#else
		POINT point = MAKEPOINT(lParam);
#endif
		// Convert client mouse events to screen coordinates.
	 	if (event.osEvent.message >= WM_MOUSEFIRST && event.osEvent.message <= WM_MOUSELAST)
			ClientToScreen(event.osEvent.hwnd, &point);

		// Set the event coordinates in "window" coordintes.
		// ("Window" coordinates are used because ZafWindowObject knows
		//  nothing of "client" coordinates.)
		if (screenID)
		{
			RECT rect;
			GetWindowRect(OSScreenID(ZAF_FRAMEID), &rect);
			position.column = point.x - rect.left;
			position.line = point.y - rect.top;
			event.position = ConvertToZafPosition(this, &position);
		}
		else
		{
			position.Import(point);
			event.position = position;
		}


		// Set the event values.
		event.rawCode = rawCode;
		event.modifiers = modifiers;

		event.converted = this;

		return (true);
	}
	
	return (false);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafWindowObject::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw a border if specified.
	if (Bordered())
		DrawBorder(drawRegion, ccode);

	// Fill the background.
	DrawBackground(drawRegion, ccode);

	// Draw the text.
	const ZafIChar *text = Text();
	if (text && (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION || ccode == S_REDISPLAY_DATA))
	{
		// Set the text palette.
		display->SetPalette(LogicalPalette(ZAF_PM_TEXT, PaletteState()));

		// Draw the text.
		//??? This function should clip text?
		display->Text(drawRegion, text, -1, ZAF_HZ_LEFT, ZAF_VT_TOP);
	}

	// Draw the focus indicator.
	DrawFocus(drawRegion, ccode);

	// End the drawing operation.
	EndDraw();

	return (ccode);
}

ZafEventType ZafWindowObject::DrawBackground(ZafRegionStruct &region, ZafEventType ccode)
{
	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION ||
		ccode == S_REDISPLAY_DATA || ccode == L_SELECT)
	{
		// Fill the background.
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, PaletteState()));
		display->Rectangle(region, 0, true);
	}

	return (ccode);
}

ZafEventType ZafWindowObject::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	if (ParentDrawBorder())
//		ccode = parent->DrawBorder(ConvertToDrawRegion(this), ccode);
		ccode = parent->DrawBorder(region, ccode);
	else
	{
#if defined(ZAF_MSWINDOWS_3D)
		// Draw a 3D style border.
		DrawShadow(region, -2, ccode);
#else
		if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION)
		{
			// Draw a single pixel border.
			display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, PaletteState()));
			display->Rectangle(region, 1, false);
		}

		region--;
#endif
	}

	return (ccode);
}

ZafEventType ZafWindowObject::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	if (ParentDrawFocus())
	{
		if (!Focus() && (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION))
			ccode = 0;
		ZafRegionStruct focusRegion = ConvertToDrawRegion(this);
		return parent->DrawFocus(focusRegion, ccode);
	}
	else
	{
		if (ccode == S_CURRENT || ccode == S_NON_CURRENT ||
			((ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION) && Focus()))
		{
			// Convert rectangle.
			RECT rect;
			region.ExportPoint(rect);

  			// Set colors into the device context.
			display->SetPalette(LogicalPalette(ZAF_PM_FOCUS, PaletteState()));

			// Draw the focus rect.
			DrawFocusRect(display->DisplayContext(), &rect);
		}

		// Update the draw region.
		region--;

	  	return (ccode);
	}
}

ZafEventType ZafWindowObject::DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode)
{
	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION)
	{
		// Get the state of the object.
		ZafPaletteState state = PaletteState();

		// Generate palettes for each of the shadow colors.
		ZafPaletteStruct palette3DLight;
		ZafPaletteStruct palette3DShadow;
		ZafPaletteStruct palette3DHiLight;
		ZafPaletteStruct palette3DDarkShadow;
		palette3DLight = palette3DHiLight = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
		palette3DShadow = palette3DDarkShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
		palette3DLight.colorForeground = palette3DHiLight.colorBackground;
		palette3DLight.osPalette.colorForeground = palette3DHiLight.osPalette.colorBackground;
		palette3DDarkShadow.colorForeground = palette3DDarkShadow.colorBackground;
		palette3DDarkShadow.osPalette.colorForeground = palette3DDarkShadow.osPalette.colorBackground;

		// Draw the shadow.
		int width = depth ? ZafAbs(depth) : 2;
		for (int i = 0; i < width; i++)
		{
			// Assign the top-left and bottom-right shadow colors for the
			// current level.
			ZafPaletteStruct *paletteTopLeft;
			ZafPaletteStruct *paletteBottomRight;
			if (depth < 0 && i == 0)
			{
				// 1st level colors for depth < 0.
				paletteTopLeft = &palette3DShadow;
				paletteBottomRight = &palette3DHiLight;
			}
			else if (depth < 0)
			{
				// 2nd level colors for depth < 0.
				paletteTopLeft = &palette3DDarkShadow;
				paletteBottomRight = &palette3DLight;
			}
			else if (depth > 1 && i == 0)
			{
				// 1st level colors for depth > 1.
				paletteTopLeft = &palette3DLight;
				paletteBottomRight = &palette3DDarkShadow;
			}
			else if (depth > 0)
			{
				// Colors for depth == 1.
				paletteTopLeft = &palette3DHiLight;
				paletteBottomRight = &palette3DShadow;
			}
			else if (i == 0)
			{
				// 1st level colors for depth == 0.
				paletteTopLeft = &palette3DShadow;
				paletteBottomRight = &palette3DHiLight;
			}
			else
			{
				// 2nd level colors for depth == 0.
				paletteTopLeft = &palette3DHiLight;
				paletteBottomRight = &palette3DShadow;
			}

			// Draw one level of shadowing.
			display->SetPalette(*paletteBottomRight);
			display->Line(region.left, region.bottom, region.right, region.bottom);
			region.bottom--;
			display->Line(region.right, region.top, region.right, region.bottom);
			region.right--;
			display->SetPalette(*paletteTopLeft);
 			display->Line(region.left, region.top, region.right, region.top);
			region.top++;
			display->Line(region.left, region.top, region.left, region.bottom);
			region.left++;
		}
	}
	else
		region -= ZafAbs(depth);

	return (ccode);
}
#endif

void ZafWindowObject::EndDraw(void)
{
	if (editMode && parent)
	{
		HDC hDC = display->DisplayContext();
		display->EndDraw();
		ReleaseDC(OSScreenID(ZAF_FRAMEID), hDC);
	}
	else
		display->EndDraw();
}

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

	// This is a special case for the Zinc design tool.
	// Keyboard and mouse events are intercepted here to allow editing.
	if (editMode &&	(event.InputType() == E_MOUSE || event.InputType() == E_KEY))
	{
		ccode = LogicalEvent(event);

		ZafEventStruct editEvent = event;
		switch (ccode)
		{
		case L_BEGIN_SELECT:
			SetFocus(true);
			// Continue.
		case L_VIEW:
			{
			// Determine the type of modify operation.
			ZafPositionStruct mousePosition = ConvertToOSPosition(this, &event.position);
			ZafRawCode sizeOperation = 0;
			if (mousePosition.column < ZAF_EDIT_MARGIN_SIZE)
				sizeOperation |= ZAF_SIZE_LEFT;
			else if (mousePosition.column >= zafRegion.Width() - ZAF_EDIT_MARGIN_SIZE)
				sizeOperation |= ZAF_SIZE_RIGHT;
			if (mousePosition.line < ZAF_EDIT_MARGIN_SIZE)
				sizeOperation |= ZAF_SIZE_TOP;
			else if (mousePosition.line >= zafRegion.Height() - ZAF_EDIT_MARGIN_SIZE)
				sizeOperation |= ZAF_SIZE_BOTTOM;

			// Set the mouse cursor image.
			if (sizeOperation & ZAF_SIZE_LEFT && sizeOperation & ZAF_SIZE_TOP)
				eventManager->SetDeviceImage(E_MOUSE, DM_TOP_LEFT_CORNER);
			else if (sizeOperation & ZAF_SIZE_RIGHT && sizeOperation & ZAF_SIZE_BOTTOM)
				eventManager->SetDeviceImage(E_MOUSE, DM_BOTTOM_RIGHT_CORNER);
			else if (sizeOperation & ZAF_SIZE_RIGHT && sizeOperation & ZAF_SIZE_TOP)
				eventManager->SetDeviceImage(E_MOUSE, DM_TOP_RIGHT_CORNER);
			else if (sizeOperation & ZAF_SIZE_LEFT && sizeOperation & ZAF_SIZE_BOTTOM)
				eventManager->SetDeviceImage(E_MOUSE, DM_BOTTOM_LEFT_CORNER);
			else if (sizeOperation & ZAF_SIZE_LEFT)
				eventManager->SetDeviceImage(E_MOUSE, DM_LEFT_SIDE);
			else if (sizeOperation & ZAF_SIZE_RIGHT)
				eventManager->SetDeviceImage(E_MOUSE, DM_RIGHT_SIDE);
			else if (sizeOperation & ZAF_SIZE_TOP)
				eventManager->SetDeviceImage(E_MOUSE, DM_TOP_SIDE);
			else if (sizeOperation & ZAF_SIZE_BOTTOM)
				eventManager->SetDeviceImage(E_MOUSE, DM_BOTTOM_SIDE);
			else
				eventManager->SetDeviceImage(E_MOUSE, DM_MOVE);

			if (ccode == L_BEGIN_SELECT)
			{
				// Modify (move / size) the object.
			 	SetCapture(event.osEvent.hwnd);
			 	Modify(event);
			 	ReleaseCapture();

				// 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 = event.rawCode;
				editEvent.position = event.position;
				Event(editEvent);
			}
			}
			break;

		case L_BEGIN_ESCAPE:
			// Suppress so the OS doesn't process it and put up a context 
			// pop-up menu.
			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;

		default:
			ccode = DefaultCallback(event);
			break;
		}

		return (ccode);
	}

	if (ccode == E_OSEVENT)
		return ZafWindowObject::OSEvent(event);

	switch (ccode)
	{

	// ----- System Events -----

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

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

		// Initialize member variables.
		Event(S_INITIALIZE);

		// Create the object with the OS.
		Event(S_REGISTER_OBJECT);

		// Allow the object to update its size/position (zafRegion).
		Event(S_COMPUTE_SIZE);

		// Size the object with the OS.
		OSSize();

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

	case S_CURRENT:
		focus = true;
		break;

 	case S_NON_CURRENT:
		focus = false;
		break;

	case S_DEINITIALIZE:
		screenID = 0;
		focus = false;

		if (windowManager->mouseObject == this)
		    windowManager->mouseObject = ZAF_NULLP(ZafWindowObject);
		break;

	case S_DESTROY:
		if (screenID)
		{
			// Remove focus before destruction to allow data to be saved.
			if (focus)
			{
				windowManager->focusObject = ZAF_NULLP(ZafWindowObject);
				NotifyFocus(ZAF_NULLP(ZafWindowObject), false);
			}

			// Destroy the object's OS counterpart. (Destroys entire tree.)
			if (SystemObject())
				DestroyWindow(OSScreenID(ZAF_FRAMEID));
			else
				ZafWindowObject::Event(S_REDISPLAY);

			// Deinitialize the object.
			Event(ZafEventStruct(S_DEINITIALIZE));
		}
		break;

	case S_INITIALIZE:
		{
//??? Moved from RegisterObject for portability.
		// Convert "zafRegion" to pixel coordinates.
		ConvertCoordinates(ZAF_PIXEL);

//??? Moved from RegisterObject for portability.
//		// Initialize oldRegion so that OSSize() will work properly.
		oldRegion.coordinateType = ZAF_PIXEL;
		oldRegion.Assign(-1, -1, 0, 0);

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

		// Initialize numberID and screenID.
		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_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 S_REDISPLAY:
		if (screenID && AutomaticUpdate())
		{
			if (SystemObject())
				// Invalidate the entire object.
				RedrawWindow(screenID, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
			else
			{
				// Get the objects region in OS coordiantes.
				ZafRegionStruct region = ConvertToDrawRegion(this);
				RECT rect;
				region.ExportPoint(rect);

				// Invalidate the object region.
				RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
			}
		}
		break;

	case S_REDISPLAY_DATA:
		if (screenID && AutomaticUpdate())
		{
			if (!OSDraw() || !SystemObject())
			{
				ZafEventStruct editEvent = event;
				editEvent.region = zafRegion;
				editEvent.region.HzShift(-zafRegion.left);
				editEvent.region.VtShift(-zafRegion.top);
				Draw(editEvent, ccode);
			}
			else
			{
				Redisplay();
				UpdateWindow(screenID);
			}
		}
		break;

	case S_REDISPLAY_REGION:
		if (screenID && AutomaticUpdate())
		{
			// Get the redisplay region in OS coordinates.
			ZafRegionStruct region = ConvertToDrawRegion(this, &event.region);
			RECT rect;
			region.ExportPoint(rect);

			// Invalidate the redisplay region.
			RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
		}
		break;

	case S_REGISTER_OBJECT:
		RegisterObject();
		break;

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

	case S_SIZE:
		// Set the new region for the object.
		SetRegion(event.region);

		if (screenID)
		{
			ConvertCoordinates(ZAF_PIXEL);

			// Let the object adjust its size.
			Event(S_COMPUTE_SIZE);

			// Reflect the new size with the OS.
			OSSize();
		}
		break;

	// ----- Notification Events -----

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

	case N_MOUSE_ENTER:
		// If help tip, update the help tips device.
		if ((QuickTip() || HelpObjectTip()) && windowManager->Focus())
			eventManager->Event(event, E_HELPTIPS);
		break;

	case N_MOUSE_LEAVE:
		// If help tip, update the help tips device.
		if ((QuickTip() || HelpObjectTip()) && windowManager->Focus())
			eventManager->Event(event, E_HELPTIPS);
		break;

//??? E_KEY definition?
/* START BLOCK COMMENT
**		case E_KEY:
**	#if defined(ZAF_UNICODE)
**	#	if defined(ZAF_WIN32)
**			switch (ZAF_WINDOWS::versionInfo.dwPlatformID)
**			{
**			case VER_PLATFORM_WIN32s:
**				{
**				int i = WideCharToMultiByte(CP_????, WC_COMPOSITECHECK|WC_DEFAULTCHAR, text, -1, NULL, 0, NULL, NULL);
**				OSAPIChar *tmp = new OSAPIChar[i+1];
**				WideCharToMultiByte(CP_????, WC_COMPOSITECHECK|WC_DEFAULTCHAR, text, -1, tmp, i+1, NULL, NULL);
**				for (int j=1; tmp[j]; j++)
**					ccode = PostMessage(screenID, WM_CHAR, tmp[j], 0);
**				event.key.value = tmp[0];
**				event.osEvent.message = WM_CHAR;
**				ccode = DefaultCallback(event);
**				delete []tmp;
**				}
**				break;
**			case VER_PLATFORM_WIN32_WINDOWS:
**			case VER_PLATFORM_WIN32_NT:
**				event.osEvent.message = WM_IME_CHAR;
**				ccode = DefaultCallback(event);
**				break;
**			}
**	#	else
**			OSAPIChar *tmp = zafCodeSet->ConvertToOSChar(event.key.value);
**			for (int j=1; tmp[j]; j++)
**				ccode = PostMessage(screenID, WM_CHAR, tmp[j], 0);
**			event.key.value = tmp[0];
**			event.osEvent.message = WM_CHAR;
**			ccode = DefaultCallback(event);
**			delete []tmp;
**	#	endif
**	#else
**			ccode = DefaultCallback(event);
**	#endif
**			break;
END BLOCK COMMENT */

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

	default:
		// Unhandled events.
		ccode = S_UNKNOWN;
		break;
	}

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

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

//??? Temporary cludge.
	if (ccode != S_BEGIN_DRAG && !AcceptDrop())
	{
		ZafWindowObject *dragObject = windowManager->dragObject;
		while (dragObject && dragObject != this)
			dragObject = dragObject->parent;
		if (dragObject != this)
			return S_ERROR;
	}

	switch (ccode)
	{
	case S_DRAG_DEFAULT:
		if (windowManager->dragObject->MoveDraggable())
		{
			ccode = S_DRAG_MOVE;
			eventManager->SetDeviceImage(E_MOUSE, DM_DRAG_MOVE);
		}
		else if (windowManager->dragObject->CopyDraggable())
		{
			ccode = S_DRAG_COPY;
			eventManager->SetDeviceImage(E_MOUSE, DM_DRAG_COPY);
		}
		else
			ccode = S_ERROR;
		break;

	case S_DRAG_MOVE:
		eventManager->SetDeviceImage(E_MOUSE, DM_DRAG_MOVE);
		break;

	case S_DRAG_COPY:
		eventManager->SetDeviceImage(E_MOUSE, DM_DRAG_COPY);
		break;

	case S_DROP_DEFAULT:
		if (windowManager->dragObject->MoveDraggable())
			ccode = S_DROP_MOVE;
		else if (windowManager->dragObject->CopyDraggable())
			ccode = S_DROP_COPY;
		else
		{
			ccode = S_ERROR;
			break;
		}
		//Continue.
	case S_DROP_COPY:
	case S_DROP_MOVE:
		if (Disabled() || windowManager->dragObject == this)
			ccode = S_ERROR;
		else
		{
			const ZafIChar *text = windowManager->dragObject->Text();
			if (text && *text)
			{
				SetText(text);
				if (ccode == S_DROP_MOVE)
					windowManager->dragObject->SetText(ZAF_ITEXT(""));
			}
		}
		break;

	case S_BEGIN_DRAG:
		windowManager->dragObject = this;
		break;

	case S_END_DRAG:
		windowManager->dragObject = ZAF_NULLP(ZafWindowObject);
		break;

	default:
		ccode = S_ERROR;
	}

	return (ccode);
}

ZafRegionStruct ZafWindowObject::MaxRegion(ZafWindowObject *, ZafVtJustify, ZafHzJustify)
{
//???Regions?
//	ZafRegionStruct maxRegion = { 0, 0, zafRegion.right - zafRegion.left, zafRegion.bottom - zafRegion.top };
	ZafRegionStruct maxRegion;
	maxRegion.coordinateType = ZAF_PIXEL;
	maxRegion.left = maxRegion.top = 0;
	maxRegion.right = zafRegion.right - zafRegion.left;
	maxRegion.bottom = zafRegion.bottom - zafRegion.top;
	return (maxRegion);
}

bool ZafWindowObject::RegisterMouse(bool, bool, bool)
{
	return(true);
}

void ZafWindowObject::RegisterObject(void)
{
	if (!screenID)
	{
//??? Moved to S_INITIALIZE for portability.
//???		// Convert "zafRegion" to pixel coordinates.
//???		ConvertCoordinates(ZAF_PIXEL);

//??? Moved to S_INITIALIZE for portability.
//???	// Initialize oldRegion so that OSSize() will work properly.
//???	oldRegion.left = oldRegion.top = -1;
//???	oldRegion.right = oldRegion.bottom = -2;

		if (SystemObject())
			OSRegisterObject();
		else if (parent)
			screenID = parent->screenID;
	}
}

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

bool ZafWindowObject::SetAcceptDrop(bool setAcceptDrop)
{
	acceptDrop = setAcceptDrop;
	return acceptDrop;
}

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

		if (screenID && visible)
		{
			if (SystemObject())
				// Turn redraw on or off.
				SendMessage(screenID, WM_SETREDRAW, (WPARAM)(automaticUpdate ? TRUE : FALSE), 0);

			if (automaticUpdate)
				Redisplay();
		}
	}

	return automaticUpdate;
}

bool ZafWindowObject::SetEditMode(bool setEditMode)
{
	// Make sure the attribute has changed.
	if (editMode != setEditMode && !screenID)
		editMode = setEditMode;

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

bool ZafWindowObject::SetFocus(bool setFocus)
{
	// Make sure the focus operation is allowed.
	if (Disabled() || Noncurrent())
		setFocus = false;

	// If no screenID just make it the current object.
	if (!screenID && setFocus)
		NotifyFocus(this, true);

	if (setFocus != focus)
	{
		if (screenID)
		{
			if (SystemObject())
			{
				// Set the focus with the OS.
				if (setFocus)
					::SetFocus(screenID);
				else if (parent && !NotifyFocus(parent, false))
					::SetFocus(parent->screenID);
			}
			else
			{
				if (setFocus && GetFocus() != screenID)
					::SetFocus(screenID);
				NotifyFocus(setFocus ? this : parent, setFocus);
			}
		}
	}
	// During validation, SetFocus() can be used to restore focus
	// with the OS even though focus was never lost with ZAF.
	else if (focus && screenID)
		::SetFocus(screenID);

	return (focus);
}

bool ZafWindowObject::SetNoncurrent(bool setNoncurrent)
{
	// Set the attribute.
	noncurrent = setNoncurrent;

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

bool ZafWindowObject::SetDisabled(bool setDisabled)
{
	if (setDisabled != disabled)
	{
		// Set the attribute.
		disabled = setDisabled;

		// Set the selectabilty with the OS.
		if (screenID)
		{
			if (SystemObject() && !editMode)
				EnableWindow(OSScreenID(ZAF_FRAMEID), (BOOL)!disabled);
			else
				Redisplay();
		}
	}

	return disabled;
}

/* START BLOCK COMMENT
**	bool ZafWindowObject::SetSupportObject(bool setSupportObject)
**	{
**		// The support object attribute can't be changed while the object
**		// is attached to a window.
**		if (!parent)
**			supportObject = setSupportObject;
**	
**		return supportObject;
**	}
END BLOCK COMMENT */

bool ZafWindowObject::SetVisible(bool setVisible)
{
	// Make sure attribute has changed.
	if (setVisible != visible)
	{
		visible = setVisible;

		// Show or hide the object.
		if (screenID)
		{
			if (SystemObject())
				ShowWindow(OSScreenID(ZAF_FRAMEID), visible ? SW_SHOW : SW_HIDE);
			else
				Redisplay();
		}
	}

	return visible;
}

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

ZafEventType ZafWindowObject::OSEvent(const ZafEventStruct &event)
{
	ZafEventType ccode = event.osEvent.message;

	// Process the event.
	switch (ccode)
	{
#if defined(ZAF_WIN32)
	case WM_INPUTLANGCHANGEREQUEST:
		DefWindowProc(event.osEvent.hwnd, event.osEvent.message,
			event.osEvent.wParam, event.osEvent.lParam);
		break;
#endif

#if defined(ZAF_MSWINDOWS_3D)
#	if defined(ZAF_WIN32)
	case WM_CTLCOLORBTN:
	case WM_CTLCOLORDLG:
	case WM_CTLCOLOREDIT:
	case WM_CTLCOLORLISTBOX:
	case WM_CTLCOLORMSGBOX:
	case WM_CTLCOLORSCROLLBAR:
	case WM_CTLCOLORSTATIC:
#	else
	case WM_CTLCOLOR:
#	endif
		{
		// Get the palettes.
		ZafPaletteState state = PaletteState();
		ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, state);
		ZafPaletteStruct backgroundPalette = LogicalPalette(ZAF_PM_BACKGROUND, state);

		// Get the OS colors from the text palette.
		COLORREF colorForeground = (textPalette.colorForeground == ZAF_CLR_DEFAULT) ?
			textPalette.osPalette.colorForeground :
			((ZafScreenDisplay *)display)->colorTable[textPalette.colorForeground];
		COLORREF colorBackground = (backgroundPalette.colorBackground == ZAF_CLR_DEFAULT) ?
			backgroundPalette.osPalette.colorBackground :
			((ZafScreenDisplay *)display)->colorTable[backgroundPalette.colorBackground];

		// Set the OS colors.
		SelectPalette((HDC)event.osEvent.wParam, ((ZafScreenDisplay *)display)->logicalPalette, FALSE);
		::SetTextColor((HDC)event.osEvent.wParam, colorForeground);
		SetBkColor((HDC)event.osEvent.wParam, colorBackground);

		// Get a background brush.
		HBRUSH backgroundBrush = ((ZafScreenDisplay *)display)->GetGdiCacheBrush(backgroundPalette);
		((ZafScreenDisplay *)display)->ReleaseGdiCacheObject(backgroundBrush);
		ccode = (ZafEventType)backgroundBrush;
		}
		break;
#endif

	case WM_COMMAND:
		{
#if defined(ZAF_WIN32)
			HWND hwndCtrl = (HWND)event.osEvent.lParam;
#else
			HWND hwndCtrl = (HWND)LOWORD(event.osEvent.lParam);
#endif
			if (hwndCtrl)
				ccode = DefaultCallback(event);
		}
		break;

	case WM_DRAWITEM:
		{
		DRAWITEMSTRUCT *drawItemStruct = (DRAWITEMSTRUCT *)event.osEvent.lParam;

		// Prepare the display for drawing.
		display->BeginDraw(drawItemStruct->hDC, 0, zafRegion, zafRegion);

		// Let the drawItem message determine the state of the object.
		bool saveFocus = focus;
		bool saveSelected = selected;
		focus = (drawItemStruct->itemState & ODS_FOCUS) ? true : false;
		if (!Disabled() && drawItemStruct->CtlType != ODT_BUTTON)
			selected = (drawItemStruct->itemState & ODS_SELECTED) ? true : false;

		// Let the object perform the drawing operation.
		if (drawItemStruct->itemAction & ODA_DRAWENTIRE)
			Draw(event, S_REDISPLAY);
		else
		{
			if (drawItemStruct->itemAction & ODA_FOCUS)
				Draw(event, (drawItemStruct->itemState & ODS_FOCUS) ? S_CURRENT : S_NON_CURRENT);
			if (drawItemStruct->itemAction & ODA_SELECT)
				Draw(event, L_SELECT);
		}

		// Restore the object state.
		focus = saveFocus;
		selected = saveSelected;

		// End drawing operation.
		display->EndDraw();
		}
		break;

	case WM_ERASEBKGND:
		// Draw() is responsible for erasing the background.
		ccode = 1;
		break;

	case WM_KEYDOWN:
		if (event.osEvent.wParam == VK_F1 && zafHelpSystem)
		{
			if (parent && !helpContext)
				ccode = S_UNKNOWN;
			else
			 	zafHelpSystem->DisplayHelp(helpContext);
			break;
		}
		//Continue.
	case WM_KEYUP:
		TranslateMessage(&event.osEvent);
		DefaultCallback(event);
		ccode = S_UNKNOWN;
		break;

	case WM_SYSKEYDOWN:
	case WM_SYSKEYUP:
		ccode = S_UNKNOWN;  // Needed to process hot keys at the window.
		break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		if (!Noncurrent())
			ccode = DefaultCallback(event);

		// Initialize drag/drop detection.
		if (Draggable() && !Noncurrent())
		{
			ZafMSWindowsApp::dragTest = true;
#if defined(ZAF_WIN32)
			ZafMSWindowsApp::dragStartPosition = MAKEPOINTS(event.osEvent.lParam);
#else
			ZafMSWindowsApp::dragStartPosition = MAKEPOINT(event.osEvent.lParam);
#endif
			if (GetCapture() != event.osEvent.hwnd)
				SetCapture(event.osEvent.hwnd);
		}
		break;

	case WM_MOUSEMOVE:
		// Generate N_MOUSE_ENTER and N_MOUSE_LEAVE messages.
		if (windowManager->mouseObject != this)
		{
			ZafEventStruct notifyEvent = event;

			ZafWindowObject *oldMouseObject = windowManager->mouseObject;
			windowManager->mouseObject = this;
			if (oldMouseObject)
			{
				notifyEvent.type = N_MOUSE_LEAVE;
				oldMouseObject->Event(notifyEvent);
			}

			// Send an N_MOUSE_ENTER event.
			notifyEvent.type = N_MOUSE_ENTER;
			Event(notifyEvent);

			// Set the mouse timer to make sure N_MOUSE_LEAVE is sent
			// even if the mouse is moved onto a different application.
			if (!ZafMSWindowsApp::mouseTimerID)
				ZafMSWindowsApp::mouseTimerID = SetTimer(NULL, ZAF_MOUSE_TIMER_ID, ZAF_MOUSE_TEST_INTERVAL, NULL);
		}

		// Test for a begin drag operation
		if (Draggable() && ZafMSWindowsApp::dragTest && !Noncurrent())
		{
#if defined(ZAF_WIN32)
			POINTS mousePos = MAKEPOINTS(event.osEvent.lParam);
#else
			POINT mousePos = MAKEPOINT(event.osEvent.lParam);
#endif
			if (ZafAbs(mousePos.x - ZafMSWindowsApp::dragStartPosition.x) >= 4 ||
				ZafAbs(mousePos.y - ZafMSWindowsApp::dragStartPosition.y) >= 4)
			{
				// Initiate the drag-drop operation.
				ZafMSWindowsApp::dragTest = false;
				ZafEventStruct dragEvent = event;
				dragEvent.type = S_BEGIN_DRAG;
				dragEvent.position.coordinateType = ZAF_PIXEL;
				dragEvent.position.column = mousePos.x;
				dragEvent.position.line = mousePos.y;
				Event(dragEvent);
			}
		}

		if (!windowManager->dragObject)
		{
			ccode = DefaultCallback(event);
			break;
		}
		// else continue.
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
		// Perform default operation.
		if (!Noncurrent() && (ccode == WM_LBUTTONUP || !windowManager->dragObject))
			ccode = DefaultCallback(event);

		if (Draggable() && windowManager->dragObject)
		{
			// Get the drop object (object underneath the mouse).
			ZafWindowObject *dragObject = windowManager->dragObject;
			ZafWindowObject *dropObject = ZAF_NULLP(ZafWindowObject);
#if defined(ZAF_WIN32)
			POINTS points = MAKEPOINTS(event.osEvent.lParam);
			POINT point;
			point.x = points.x;
			point.y = points.y;
#else
			POINT point = MAKEPOINT(event.osEvent.lParam);
#endif
			ClientToScreen(event.osEvent.hwnd, &point);
			HWND hDropObject = WindowFromPoint(point);
			if (GetWindowTask(hDropObject) == GetWindowTask(screenID))
			{
				dropObject = ZafMSWindowsApp::ObjectFromHandle(hDropObject);
				if (!dropObject)
				{
					// Sometimes ZAF objects (like combo boxes) have non-ZAF
					// childrend (like the edit field in a combo box).
					HWND hParent = GetParent(hDropObject);
					if (hParent)
						dropObject = ZafMSWindowsApp::ObjectFromHandle(hParent);
				}
			}

			if (dropObject && (dropObject->AcceptDrop() || dropObject == this))
			{
				// Determine the requested operation.
				bool copy = false, link = false, move = false;
				if (event.osEvent.wParam & MK_SHIFT && event.osEvent.wParam & MK_CONTROL)
					link = true;
				else if (event.osEvent.wParam & MK_CONTROL)
					copy = true;
				else if (event.osEvent.wParam & MK_SHIFT)
					move = true;

				// Send an S_DRAG or S_DROP event.
				ZafEventStruct dragEvent;
				if (link && dragObject->LinkDraggable())
					dragEvent.type = (event.osEvent.message == WM_MOUSEMOVE) ? S_DRAG_LINK : S_DROP_LINK;
				else if (move && dragObject->MoveDraggable())
					dragEvent.type = (event.osEvent.message == WM_MOUSEMOVE) ? S_DRAG_MOVE : S_DROP_MOVE;
				else if (copy && dragObject->CopyDraggable())
					dragEvent.type = (event.osEvent.message == WM_MOUSEMOVE) ? S_DRAG_COPY : S_DROP_COPY;
				else
					dragEvent.type = (event.osEvent.message == WM_MOUSEMOVE) ? S_DRAG_DEFAULT : S_DROP_DEFAULT;

				ScreenToClient(dropObject->screenID, &point);
				dragEvent.position.coordinateType = ZAF_PIXEL;
				dragEvent.position.column = point.x;
				dragEvent.position.line = point.y;
				if (dropObject->Event(dragEvent) == S_ERROR)
					eventManager->SetDeviceImage(E_MOUSE, DM_CANCEL);
//??? Better drag/drop image control could probably be implemented here.
			}
			else if (event.osEvent.message == WM_MOUSEMOVE)
				eventManager->SetDeviceImage(E_MOUSE, DM_CANCEL);
		}

		if (Draggable() && (event.osEvent.message == WM_LBUTTONUP ||
			event.osEvent.message == WM_RBUTTONUP))
		{
			// End the drag/drop operation.
			if (GetCapture() == event.osEvent.hwnd)
				ReleaseCapture();
			ZafMSWindowsApp::dragTest = false;
			windowManager->dragObject = ZAF_NULLP(ZafWindowObject);
		}
		break;

	case WM_LBUTTONDBLCLK:
		if (!Noncurrent())
			ccode = DefaultCallback(event);

		// Call the object's callback on a double click.
		(this->*memberUserFunction)(event, L_DOUBLE_CLICK);
		break;

	case WM_MBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
		if (!Noncurrent())
			ccode = DefaultCallback(event);
		break;

	case WM_MOUSEACTIVATE:
		if (!RootObject()->Focus())
		{
			if (!Disabled() && !Noncurrent())
				Event(S_SET_FOCUS);
			else
				RootObject()->Event(S_SET_FOCUS);
			return (MA_ACTIVATE);
		}
		return (DefaultCallback(event));

	case WM_NCDESTROY:
		// Restore the original window procedure.
		SetWindowLong(screenID, GWL_WNDPROC, (LONG)GetClassLong(screenID, GCL_WNDPROC));

#if !defined(ZAF_WIN32)
		// Remove the window properties.
		RemoveProp(screenID, (LPCSTR)ZafMSWindowsApp::objectLowWord);
		RemoveProp(screenID, (LPCSTR)ZafMSWindowsApp::objectHighWord);
#endif

		// Perform the default operation.
		ccode = DefaultCallback(event);
		break;

//??? Is this effect still supported. (Disabled black)
/* START BLOCK COMMENT
**		case WM_NCHITTEST:
**			// Don't allow objects that are view-only and non-current 
**			// to get mouse messages.
**			if (ViewOnly() && Noncurrent())
**				return (HTTRANSPARENT);
**			else
**				ccode = DefaultCallback(event);
**			break;
END BLOCK COMMENT */

	case WM_NCMOUSEMOVE:
		if (windowManager->mouseObject)
		{
			// Generate N_MOUSE_LEAVE messages when entering non-client regions.
			ZafEventStruct notifyEvent = event;
			notifyEvent.type = N_MOUSE_LEAVE;
			windowManager->mouseObject->Event(notifyEvent);
			windowManager->mouseObject = ZAF_NULLP(ZafWindowObject);
		}
		ccode = DefaultCallback(event);
		break;

	case WM_PAINT:
		{
		// Begin the draw operation.
		PAINTSTRUCT paintStruct;
		display->BeginDraw(BeginPaint(screenID, &paintStruct), 0, zafRegion, zafRegion);

		// Generate the draw event.
		ZafEventStruct drawEvent = event;
		drawEvent.region.ImportPoint(paintStruct.rcPaint);

		// Draw the object.
		Draw(drawEvent, S_REDISPLAY);

		// End the draw operation.
		display->EndDraw();
		EndPaint(screenID, &paintStruct);

		ccode = 0;
		}
		break;

	case WM_PALETTECHANGED:
	case WM_QUERYNEWPALETTE:
		// No logical palette needs to be realized.
		ccode = FALSE;
		break;

	case WM_SETCURSOR:
		if (editMode && (HWND)event.osEvent.wParam == screenID)
			ccode = TRUE;
		else if (Noncurrent() && parent)
		{
			eventManager->SetDeviceImage(E_MOUSE, DM_VIEW);
			ccode = TRUE;
		}
		else
			ccode = DefaultCallback(event);
		break;

	case WM_SETFOCUS:
//??? Moved for table problem.
		ccode = DefaultCallback(event);

		if (!Focus())
		{
			// Perform validation and notification.
			if (NotifyFocus(this, true))
			{
				// Validation failed.
				ccode = 0;
				break;
			}

			// See if object lost focus during validation.
			HWND hwndFocus = GetFocus();
			if (hwndFocus != screenID && !IsChild(screenID, hwndFocus))
			{
				// Restore focus.
				::SetFocus(event.osEvent.hwnd);

				// Release mouse capture.
				// (If focus was lost during validation, the object
				// may not have received a button-up message even
				// thought the mouse button was released.)
				PostMessage(screenID, WM_LBUTTONUP, 0, 0);

				// Focus change was completed with SetFocus() call above.
				ccode = 0;
				break;
			}
		}

//??? See above.
//		// Focus change was successfull.
//		ccode = DefaultCallback(event);
		break;

	case WM_WINDOWPOSCHANGED:
		{
		ccode = DefaultCallback(event);

		WINDOWPOS *windowPos = (WINDOWPOS *)event.osEvent.lParam;

		// See if the object has been moved or sized.
		if ((windowPos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
		{
			// Get the OS region.
			ZafRegionStruct osRegion;
			osRegion.coordinateType = ZAF_PIXEL;
			osRegion.left = windowPos->x;
			osRegion.right = osRegion.left + windowPos->cx - 1;
			osRegion.top = windowPos->y;
			osRegion.bottom = osRegion.top + windowPos->cy - 1;

			// Update zafRegion by converting the OS region.
			zafRegion = parent ? parent->ConvertToZafRegion(this, &osRegion) :
				windowManager->ConvertToZafRegion(this, &osRegion);

			ZafEventStruct notifyEvent;
			notifyEvent.region = oldRegion;

			// Notify the object that it has been moved.
			if (!(windowPos->flags & SWP_NOMOVE))
			{
				notifyEvent.type = N_MOVE;
				Event(notifyEvent);
			}

			// Notify the object that it has been sized.
			if (!(windowPos->flags & SWP_NOSIZE))
			{
				notifyEvent.type = N_SIZE;
				Event(notifyEvent);
			}
			oldRegion = zafRegion;
		}
		}
		break;

	default:
		ccode = DefaultCallback(event);
		break;
	}

	return (ccode);
}

void ZafWindowObject::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Get default colors from the operating system.
	if (type == ZAF_PM_TEXT)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
		{
			if (state & ZAF_PM_DISABLED)
				palette.osPalette.colorForeground = GetSysColor(COLOR_3DSHADOW);
			else
				palette.osPalette.colorForeground = GetSysColor(COLOR_WINDOWTEXT);
		}
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
		{
			if (state & ZAF_PM_DISABLED)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DHILIGHT);
			else
#if defined(ZAF_MSWINDOWS_3D)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
#else
				palette.osPalette.colorBackground = GetSysColor(COLOR_WINDOW);
#endif
		}
	}
	else if (type == ZAF_PM_FOREGROUND)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_WINDOWTEXT);
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
#if defined(ZAF_MSWINDOWS_3D)
			palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
#else
			palette.osPalette.colorBackground = GetSysColor(COLOR_WINDOW);
#endif
	}
	else if (type == ZAF_PM_BACKGROUND || type == ZAF_PM_FOCUS)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
#if defined(ZAF_MSWINDOWS_3D)
			palette.osPalette.colorForeground = GetSysColor(COLOR_3DFACE);
#else
			palette.osPalette.colorForeground = GetSysColor(COLOR_WINDOW);
#endif
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
#if defined(ZAF_MSWINDOWS_3D)
			palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
#else
			palette.osPalette.colorBackground = GetSysColor(COLOR_WINDOW);
#endif
	}
	else if (type == ZAF_PM_LIGHT_SHADOW)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_3DHILIGHT);
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
		{
			if ((ZafMSWindowsApp::windowsPlatform == ZAF_WIN32_WINDOWS) ||
				(ZafMSWindowsApp::windowsPlatform == ZAF_WIN32_NT && ZafMSWindowsApp::windowsVersion >= 400))
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DLIGHT);
			else
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
		}
	}
	else if (type == ZAF_PM_DARK_SHADOW)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_3DSHADOW);
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
		{
			if ((ZafMSWindowsApp::windowsPlatform == ZAF_WIN32_WINDOWS) ||
				(ZafMSWindowsApp::windowsPlatform == ZAF_WIN32_NT && ZafMSWindowsApp::windowsVersion >= 400))
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DDKSHADOW);
			else
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DSHADOW);
		}
	}
}

void ZafWindowObject::OSRegisterObject(void)
{
	// Set the Windows styles.
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
	DWORD dwExStyle = 0;
	if (visible)
		dwStyle |= WS_VISIBLE;
	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;

	if (Bordered() && OSDraw())
	{
#if defined(ZAF_MSWINDOWS_3D)
		if (ZafMSWindowsApp::native3D)
			dwExStyle |= WS_EX_CLIENTEDGE;
		else
			dwStyle |= WS_BORDER;
#else
		dwStyle |= WS_BORDER;
#endif
	}

	// Create the window.
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle,
		ZAF_NULLP(ZafIChar), 0, dwExStyle);
}

OSWindowID ZafWindowObject::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

ZafError ZafWindowObject::OSSetText(void)
{
	if (screenID && parent->IsA(ID_ZAF_LIST) && parent->OSDraw())
	{
		int index = ListIndex();
		if (parent->parent && parent->parent->IsA(ID_ZAF_COMBO_BOX))
		{
			SendMessage(screenID, CB_DELETESTRING, index, 0);
			if (ZafMSWindowsApp::convertText)
			{
				char *osText = zafCodeSet->ConvertToOSString(Text(), ZAF_NULLP(char), FALSE);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
				SendMessageA(screenID, CB_INSERTSTRING, (WPARAM)index, (LPARAM)osText);
#else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)index, (LPARAM)osText);
#endif
			}
			else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)index, (LPARAM)Text());
		}
		else
		{
			SendMessage(screenID, LB_DELETESTRING, index, 0);
			if (ZafMSWindowsApp::convertText)
			{
				char *osText = zafCodeSet->ConvertToOSString(Text());
				SendMessage(screenID, LB_INSERTSTRING, (WPARAM)index, (LPARAM)osText);
				delete []osText;
			}
			else
				SendMessage(screenID, LB_INSERTSTRING, (WPARAM)index, (LPARAM)Text());
			if (selected)
			{
				if (DynamicPtrCast(parent, ZafWindow)->SelectionType() == ZAF_SINGLE_SELECTION)
					SendMessage(screenID, LB_SETCURSEL, (WPARAM)index, (LPARAM)0);
				else
					SendMessage(screenID, LB_SETSEL, (WPARAM)TRUE, (LPARAM)index);
			}
		}

		return (ZAF_ERROR_NONE);
	}

	return (ZAF_ERROR_INVALID);
}

void ZafWindowObject::OSSize(void)
{
	if (zafRegion != oldRegion)
	{
		// Convert zafRegion to OS coordinates.
		ZafRegionStruct osRegion = parent ? parent->ConvertToOSRegion(this) :
			windowManager->ConvertToOSRegion(this);

		if (SystemObject())
		{
			// Size/move the object with the OS.
			MoveWindow(OSScreenID(ZAF_FRAMEID), osRegion.left, osRegion.top, osRegion.Width(),
				osRegion.Height(), TRUE);
			UpdateWindow(OSScreenID(ZAF_FRAMEID));
		}
		else
		{
			osRegion = ConvertToDrawRegion(this);

			// Convert oldRegion to OS coordinates.
			ZafRegionStruct oldOsRegion = parent->ConvertToOSRegion(this, &oldRegion);

/* START BLOCK COMMENT
**				// Get the update region.
**				RECT updateRect;
**				ZafRegionStruct updateRegion;
**				GetUpdateRect(screenID, &updateRect, FALSE);
**				updateRegion.ImportPoint(updateRect);
END BLOCK COMMENT */

			// See if the object has been moved.
			if (osRegion.left != oldOsRegion.left || osRegion.top != oldOsRegion.top)
			{
				if (Visible() && AutomaticUpdate())
				{
					// Invalidate the old object region.
					RECT rect;
					oldOsRegion.ExportPoint(rect);
					RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE);

//??? This bit blit optimization is good, but it unfortunately can have bad
//??? side effects if the source region has been obscured.
//???					if (!updateRegion.Overlap(oldOsRegion))
//???					{
//???						// BitBlt copy the object.
//???						HDC hDC = GetDC(screenID);
//???						BitBlt(hDC, osRegion.left, osRegion.top, oldOsRegion.Width(), oldOsRegion.Height(),
//???							hDC, oldOsRegion.left, oldOsRegion.top, SRCCOPY);
//???						ReleaseDC(screenID, hDC);
//???
//???						// Validate the new object region.
//???						rect.left = osRegion.left;
//???						rect.top = osRegion.top;
//???						rect.right = rect.left + oldOsRegion.Width() - 1;
//???						rect.bottom = rect.top + oldOsRegion.Height() - 1;
//???						RedrawWindow(screenID, &rect, 0, RDW_VALIDATE);
//???					}
//???					else
//???					{
					// Invalidate the new object region.
					osRegion.ExportPoint(rect);
					RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE);
//???					}
				}

				Event(ZafEventStruct(N_MOVE, 0, oldRegion));
			}

			// See if the object has been sized.
			if (osRegion.Width() != oldOsRegion.Width() ||
				osRegion.Height() != oldOsRegion.Height())
			{
				if (Visible() && AutomaticUpdate())
				{
					int maxHeight =	ZafMax(osRegion.Height(), oldOsRegion.Height());
					int minHeight = ZafMin(osRegion.Height(), oldOsRegion.Height());
					int maxWidth =	ZafMax(osRegion.Width(), oldOsRegion.Width());
					int minWidth = ZafMin(osRegion.Width(), oldOsRegion.Width());

					// Invalidate the bottom region.
					RECT rect;
					rect.left = osRegion.left;
					rect.top = osRegion.top + minHeight;
					rect.right = osRegion.left + minWidth;
					rect.bottom = osRegion.top + maxHeight;
					RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE);

					// Invalidate the right region.
					rect.left = osRegion.left + minWidth;
					rect.top = osRegion.top;
					rect.right = osRegion.left + maxWidth;
					rect.bottom = osRegion.top + maxHeight;
					RedrawWindow(screenID, &rect, 0, RDW_INVALIDATE | RDW_ERASE);
				}
				Event(ZafEventStruct(N_SIZE, 0, oldRegion));
			}

			oldRegion = zafRegion;
		}
	}
}

ZafError ZafWindowObject::OSUpdatePalettes(ZafPaletteType , ZafPaletteType )
{
	if (screenID)
	{
		if (SystemObject() && OSDraw())
		{
			ZafPaletteStruct fontPalette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED);
			SendMessage(screenID, WM_SETREDRAW, (WPARAM)FALSE, 0);
			SendMessage(screenID, WM_SETFONT, (WPARAM)((ZafScreenDisplay *)display)->fontTable[fontPalette.font], (LPARAM)FALSE);
			if (automaticUpdate && visible)
				SendMessage(screenID, WM_SETREDRAW, (WPARAM)TRUE, 0);
		}
		Redisplay();
	}

	return (ZAF_ERROR_NONE);
}

// ----- Stubs --------------------------------------------------------------

void ZafWindowObject::GetClip(ZafWindowObject *, ZafRegionStruct &, ZafRegionStruct &)
{
	// Handled automatically by Windows.
}

ZafEventType ZafWindowObject::Modify(const ZafEventStruct &modifyEvent)
{
	// Save the original mouse coordinates.
	ZafPositionStruct originalPosition = ConvertToOSPosition(this, &modifyEvent.position);

	// Determine the type of modify operation.
	ZafRawCode sizeOperation = 0;
	if (originalPosition.column <= ZAF_EDIT_MARGIN_SIZE)
		sizeOperation |= ZAF_SIZE_LEFT;
	else if (originalPosition.column >= zafRegion.Width() - ZAF_EDIT_MARGIN_SIZE)
		sizeOperation |= ZAF_SIZE_RIGHT;
	if (originalPosition.line <= ZAF_EDIT_MARGIN_SIZE)
		sizeOperation |= ZAF_SIZE_TOP;
	else if (originalPosition.line >= zafRegion.Height() - ZAF_EDIT_MARGIN_SIZE)
		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;

	// Initialize the display for drawing operations.
	RECT rect;
	GetWindowRect(OSScreenID(ZAF_FRAMEID), &rect);
	MapWindowPoints(HWND_DESKTOP, OSScreenID(ZAF_FRAMEID), (POINT *)&rect, 2);
	ZafRegionStruct drawRegion;
	drawRegion.ImportPoint(rect);
	ZafRegionStruct lastRegion = drawRegion;
	ZafRegionStruct newRegion = lastRegion;

	// Draw the initial xor rectangle.
	BeginDraw();
	display->RectangleXORDiff(ZAF_NULLP(ZafRegionStruct), &newRegion);
	EndDraw();

	// Perform the "rubber-banding" loop.
	ZafEventType ccode;
	do
	{
		// Get the next event.
		ZafEventStruct event;
		eventManager->Get(event, Q_NORMAL);
		ccode = LogicalEvent(event);

		// Process the event.
		if (ccode == L_CONTINUE_SELECT || ccode == L_END_SELECT)
		{
			// Get the delta position.
			ZafPositionStruct deltaPosition = ConvertToOSPosition(this, &event.position);
			deltaPosition.column -= originalPosition.column;
			deltaPosition.line -= originalPosition.line;

			// Snap the position to the proper coordinates.
			deltaPosition.ConvertCoordinates(coordinateType);
			deltaPosition.ConvertCoordinates(zafRegion.coordinateType);

			// Compute the new moving/sizeing region.
			newRegion = drawRegion;
			if (sizeOperation & ZAF_SIZE_LEFT)
				newRegion.left += deltaPosition.column;
			if (sizeOperation & ZAF_SIZE_TOP)
				newRegion.top += deltaPosition.line;
			if (sizeOperation & ZAF_SIZE_RIGHT)
				newRegion.right += deltaPosition.column;
			if (sizeOperation & ZAF_SIZE_BOTTOM)
				newRegion.bottom += deltaPosition.line;

			if (newRegion.Width() <= 0)
			{
				newRegion.left = lastRegion.left;
				newRegion.right = lastRegion.right;
			}

			if (newRegion.Height() <= 0)
			{
				newRegion.top = lastRegion.top;
				newRegion.bottom = lastRegion.bottom;
			}

			// Move the xor rectangle.
			if (newRegion != lastRegion)
			{
				BeginDraw();
				display->RectangleXORDiff(&lastRegion, &newRegion);
				EndDraw();
				lastRegion = newRegion;
			}
		}
		else if (event.type == E_OSEVENT && (event.osEvent.message == WM_PAINT ||
			event.osEvent.message == WM_DRAWITEM))
		{
			BeginDraw();
			display->RectangleXORDiff(&lastRegion, ZAF_NULLP(ZafRegionStruct));
			EndDraw();
			windowManager->Event(event);
			BeginDraw();
			display->RectangleXORDiff(ZAF_NULLP(ZafRegionStruct), &lastRegion);
			EndDraw();
		}
		else
			windowManager->Event(event);

	} while (ccode != L_END_SELECT);

	// Erase the xor rectangle.
	BeginDraw();
	display->RectangleXORDiff(&lastRegion, ZAF_NULLP(ZafRegionStruct));
	EndDraw();

	// Perform the move/size operation.
	ZafEventStruct sizeEvent(S_SIZE);
	sizeEvent.region = Region();
	sizeEvent.region.left += newRegion.left - drawRegion.left;
	sizeEvent.region.top += newRegion.top - drawRegion.top;
	sizeEvent.region.right += newRegion.right - drawRegion.right;
	sizeEvent.region.bottom += newRegion.bottom - drawRegion.bottom;
	if (sizeEvent.region != zafRegion)
	{
		Event(sizeEvent);
		if (parent)
			parent->Event(ZafEventStruct(N_SIZE));
	}

	// Return success.
	return (ccode);
}

ZafEventType ZafWindowObject::MoveEvent(const ZafEventStruct &event)
{
	return (event.type);
}

ZafEventType ZafWindowObject::ScrollEvent(const ZafEventStruct &event)
{
	return (event.type);
}

