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

#define USE_RAW_KEYS
#include <z_appevt.hpp>
#include <z_error.hpp>
#include <z_help.hpp>
#include <z_keymap.hpp>
#include <z_mouse2.hpp>	// mouse image messages
#include <z_win.hpp>
#include "i_winmgr.hpp"

#if defined(__BORLANDC__)
extern unsigned _stklen = 20480U;
#endif

ZafListBlock *ownerWindowList;

// ----- ZafWindowManager --------------------------------------------------

ZafWindowManager::~ZafWindowManager(void)
{
	ZafWindowObject *object;

	// Remove and destroy repair damage timer.
	ZafDevice *timer = eventManager->GetObject(ZAF_ITEXT("RepairDamageTimer"));
	if (timer)
	{
		eventManager->Subtract(timer);
		delete timer;
	}

	// Remove all objects from the window manager.
	for (object = First(); object; object = First())
	{
		ZafList::Subtract(object);
		object->Event(S_DEINITIALIZE);
		if (DynamicPtrCast(object, ZafWindow)->Destroyable())
			delete object;
	}

	if (pasteBuffer)
		delete []pasteBuffer;
	pasteBuffer = ZAF_NULLP(ZafIChar);
	pasteLength = 0;

	// Clear the static variables.
	ZafWindowObject::display = ZAF_NULLP(ZafDisplay);
	ZafWindowObject::eventManager = ZAF_NULLP(ZafEventManager);
	ZafWindowObject::windowManager = ZAF_NULLP(ZafWindowManager);

	delete ownerWindowList;
}

ZafWindowObject *ZafWindowManager::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Check for a null or current object.
	if (!object)
		return (ZAF_NULLP(ZafWindowObject));
	else if (object == Current())
		return (object);

	ZafWindow *firstWindow = DynamicPtrCast(first, ZafWindow),
		*newWindow = DynamicPtrCast(object, ZafWindow);

	// Remove any temporary objects if we have a non-temporary window.
	if (!newWindow->Temporary())
	{
		while (firstWindow && firstWindow->Temporary())
		{
			ZafWindowManager::Subtract(firstWindow);
			if (firstWindow->Destroyable())
				delete firstWindow;
			firstWindow = DynamicPtrCast(first, ZafWindow);
		}
	}
	// Remove any temporary objects if the window is already in the list and
	// is temporary.
	else if (Index(newWindow) != -1)
	{
		while (firstWindow && firstWindow != newWindow)
		{
			ZafWindowManager::Subtract(firstWindow);
			if (firstWindow->Destroyable())
				delete firstWindow;
			firstWindow = DynamicPtrCast(first, ZafWindow);
		}
	}

	// See if the object is already the first on the list.
	if (object == First())
		return (object);

	// Owned window
	if (newWindow->Owner())
	{
		if (!ownerWindowList)
			ownerWindowList = new ZafListBlock(0);

		ZafWindowLink *link;
		for (link = (ZafWindowLink *)ownerWindowList->First();
			link; link = link->Next())
		{
			if (link->Object() == newWindow->Owner())
				break;
		}
		if (!link)
		{
			link = (ZafWindowLink *)ownerWindowList->Add();
			if (!link)
			{
				link = new ZafWindowLink;
				ownerWindowList->ZafList::Add(link, 0);
			}
			link->SetObject(newWindow->Owner());
		}
		link->AddChild(newWindow);
	}

	// Add the object to the list.
	if (ZafList::Index(newWindow) == -1)
	{
		if (position)
			ZafList::Add(newWindow, position);
		else
			ZafList::Add(newWindow, First());
	}
	else
	{
		ZafList::Subtract(newWindow);
		ZafList::Add(newWindow, First());
	}

	// Make the object current and show the window.
	if (!newWindow->screenID)
	{
		// SetVisible(false) to avoid flash during SetFocus(true).
		bool saveVisible = newWindow->Visible(false);
		newWindow->SetVisible(false);
		newWindow->Event(S_CREATE);
		if (saveVisible)
		{
			newWindow->SetFocus(true);
			newWindow->SetVisible(true);
			RepairDamage();
		}
	}
	else
		newWindow->SetFocus(true);

	// Return a pointer to the object.
	return (newWindow);
}

ZafEventType ZafWindowManager::ApplicationEvent(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	// ----- Document requests -----
	case A_OPEN_DOCUMENT:
	case A_PRINT_DOCUMENT:
		// If these are not processed, we must delete event.text.
		ccode = First() ? First()->Event(event) : S_NO_OBJECT;
		if ((ccode == S_UNKNOWN || ccode == S_NO_OBJECT) && event.text)
			delete []event.text;
		break;

	// ----- Window requests -----
	case A_OPEN_WINDOW:
		if (event.text)
		{
			ZafIChar *windowName = strrchr(event.text, ZAF_DIRECTORY_SEPARATOR);
			if (windowName)
				windowName++;
			else
				windowName = event.text;
			ZafWindowObject *window = GetObject(windowName);
			if (window && !window->parent)
				window->SetFocus(true);
#if defined(USE_ZAF_PERSISTENCE)
			else if (zafObjectPersistence)
			{
				ZafFileSystem *fileSystem = zafObjectPersistence->CurrentFileSystem();
				ZafIChar *fileName = strrchr(event.text, ZAF_DIRECTORY_SEPARATOR);
				if (fileName)
				{
					*(fileName++) = 0;
					fileSystem->ChDir(event.text);
					ZafFileInfoStruct fileInfo;
					if (fileSystem->FindFirst(fileInfo, fileName) == 0 &&
						fileInfo.stringID)
					{
						ZafObjectConstructor constructor = zafObjectPersistence->GetObjectConstructor((ZafClassID)0, fileInfo.stringID);
						if (constructor)
						{
							zafObjectPersistence->PushLevel(fileInfo.stringID, fileInfo.numberID, ZAF_PERSIST_DIRECTORY);
							Add((ZafWindowObject *)(constructor)(fileInfo.name, *zafObjectPersistence));
							zafObjectPersistence->PopLevel();
						}
					}
				}
			}
#endif

			delete []event.text;
		}
		break;

	case A_CLOSE_WINDOW:
		if (event.text)
		{
			ZafWindowObject *object = First();
			while (object)
			{
				ZafWindowObject *nextObj = object->Next();
				if (object->StringID() && strcmp(object->StringID(), event.text) == 0)
				{
					ZafEventStruct closeEvent(S_CLOSE);
					closeEvent.windowObject = object;
					ccode = Event(closeEvent);
				}
				object = nextObj;
			}

			delete []event.text;
		}
		break;

	case A_MINIMIZE_WINDOWS:
		{
		for (ZafWindowObject *object = First(); object; object = object->Next())
		{
			ZafWindow *window = DynamicPtrCast(object, ZafWindow);
			if (window)
				window->SetMinimized(true);
		}
		}
		break;

	case A_RESTORE_WINDOWS:
		{
		for (ZafWindowObject *object = First(); object; object = object->Next())
		{
			ZafWindow *window = DynamicPtrCast(object, ZafWindow);
			if (window)
				window->SetMinimized(false);
		}
		}
		break;

	case A_HELP_CONTEXT:
		if (event.text)
		{
			if (zafHelpSystem)
				zafHelpSystem->DisplayHelp(event.text);
			delete []event.text;
		}
		break;

	case A_RESET_I18N:
		// Reset the i18n data.
		if (event.text)
		{
			ZafI18nData::ResetI18n(event.text);
			delete []event.text;
		}

		// Notify all objects of the i18n change.
		BroadcastEvent(N_RESET_I18N, -1);
		break;

	// ----- Errors -----
	default:
		ccode = S_ERROR;
	}

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

ZafPositionStruct ZafWindowManager::ConvertToOSPosition(const ZafWindowObject *, const ZafPositionStruct *zafPosition) const
{
	ZafPositionStruct osPosition;
	if (zafPosition)
	{
		osPosition.column = zafPosition->column;
		osPosition.line = zafPosition->line;
		osPosition.coordinateType = zafPosition->coordinateType;
	}
	else
	{
		osPosition.column = osPosition.line = 0;
#if defined(ZAF_GRAPHICS_MODE)
		osPosition.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
		osPosition.coordinateType = ZAF_CELL;
#endif
	}
	return (osPosition);
}

ZafRegionStruct ZafWindowManager::ConvertToOSRegion(const ZafWindowObject *object,
	const ZafRegionStruct *region) const
{
	// Determine the default region.
	ZafRegionStruct osRegion = ZafWindowObject::ConvertToOSRegion(object, region);
	// Adjust the position from multi-platform values.
	osRegion.right -= 2 * (display->cellWidth - ZAF_BORDER_SIZE);
	osRegion.bottom -= 2 * (display->cellHeight - ZAF_BORDER_SIZE);

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

ZafPositionStruct ZafWindowManager::ConvertToZafPosition(const ZafWindowObject *, const ZafPositionStruct *osPosition) const
{
	ZafPositionStruct zafPosition;
	if (osPosition)
	{
		zafPosition.column = osPosition->column;
		zafPosition.line = osPosition->line;
		zafPosition.coordinateType = osPosition->coordinateType;
	}
	else
	{
		zafPosition.column = zafPosition.line = 0;
#if defined(ZAF_GRAPHICS_MODE)
		zafPosition.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
		zafPosition.coordinateType = ZAF_CELL;
#endif
	}
	return (zafPosition);
}

ZafRegionStruct ZafWindowManager::ConvertToZafRegion(const ZafWindowObject *object,
	const ZafRegionStruct *region) const
{
	// Determine the default region.
	ZafRegionStruct zafRegion = ZafWindowObject::ConvertToZafRegion(object, region);
	// Adjust the position from multi-platform values.
	zafRegion.right += 2 * (display->cellWidth - ZAF_BORDER_SIZE);
	zafRegion.bottom += 2 * (display->cellHeight - ZAF_BORDER_SIZE);

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

ZafEventType ZafWindowManager::DispatchMouseEvent(const ZafEventStruct &event)
{
	// Dispatch all mouse events to the leaf-most object.

	// LogicalEvent() must be called first to map the logical events (L_*) and
	// to convert the event to the context of the window manager.
	ZafEventType ccode = LogicalEvent(event);
	ZafWindowObject *tClickObject = ZAF_NULLP(ZafWindowObject);

	ZafWindowObject *firstObject = First();
	ZafWindowObject *object;
	switch (ccode)
	{
	case L_DOUBLE_CLICK:
	case L_BEGIN_SELECT:
		// Optimize for common event routing over the top window.
		if (firstObject && firstObject->Visible() &&
			firstObject->zafRegion.Overlap(event.position))
		{
			tClickObject = firstObject->FindMouseObject(event);
			if (tClickObject)
				ccode = tClickObject->DispatchMouseEvent(event);
			break;
		}
		// Else continue to L_END_SELECT, etc.

	case L_CONTINUE_SELECT:
	case L_END_SELECT:
	case L_VIEW:
		{
			ZafWindow *firstWindow = DynamicPtrCast(firstObject, ZafWindow),
				*window;
			for (window = firstWindow; window;
				window = DynamicPtrCast(window->Next(), ZafWindow))
			{
				if (!window->Disabled() && window->Visible() &&
					window->zafRegion.Overlap(event.position))
				{
					// If on current window, or if event is L_VIEW, dispatch and 
					// quit.
					if (window == firstWindow || ccode == L_VIEW)
					{
						tClickObject = window->FindMouseObject(event);
						if (ccode == L_VIEW && tClickObject != mouseObject)
						{
							ZafEventStruct tEvent = event;
							tEvent.type = N_MOUSE_LEAVE;
							if (mouseObject)
								mouseObject->Event(tEvent);
							mouseObject = tClickObject;
							tEvent.type = N_MOUSE_ENTER;
							if (mouseObject)
								mouseObject->Event(tEvent);
						}
						if (tClickObject)
							return (tClickObject->DispatchMouseEvent(event));
						else
							return (ccode);
					}

					// Event occurred over a "background" window.  This could
					// be a window whose pull-down menu is active, which may
					// require special handling.  Or, a new window may need to
					// be given focus.

					if (!window->Minimized() && firstWindow->Temporary())
					{
						// Check to see if the event occurred over an ancestor 
						// of that window (indicating that that window is part 
						// of a menu structure).						
						for (object = First()->parent; object; object = object->parent)
							if (object == window)
								break;

						// Temporary object is direct child (menu) of window.
						if (object == window)
						{
							if (ccode == L_BEGIN_SELECT)
							{
								// Close all menus up to immediate child of 
								// overlapped item.
								ZafWindowObject *leaveWindow = window;
								ZafWindowObject *prevWindow = window->Previous();

								// prevWindow->parent is a pop-up item or system 
								// button on window.  If the mouse click 
								// overlapped this item shut all pop-up menus up 
								// to prevWindow.
								if (prevWindow)
								{
									ZafPositionStruct mousePosition =
										prevWindow->parent->parent->ConvertToZafPosition(prevWindow->parent, &event.position);
									if (prevWindow->parent->zafRegion.Overlap(mousePosition))
										leaveWindow = prevWindow;
								}
								while (First() && First() != leaveWindow)
									Event(S_CLOSE_TEMPORARY);
								// Remove focus from the pulldown menu.
								while (prevWindow && prevWindow->parent != leaveWindow)
									prevWindow = prevWindow->parent;
								if (prevWindow)
									prevWindow->NotifyFocus(0, false);
							}
							tClickObject = window->FindMouseObject(event);
							return (tClickObject ?
								tClickObject->DispatchMouseEvent(event) : ccode);
						}
					}

					// If still here, it means that the event occurred over a 
					// background window and either there were no temporary 
					// windows open or there were, but they weren't part of a 
					// menu structure, at least not for window.

					// Only L_BEGIN_SELECT is valid for processing at this point.
					if (ccode != L_BEGIN_SELECT)
						return (ccode);

					// Close any temporary windows, such as pop-up menus.
					while (first && first != window &&
						DynamicPtrCast(first, ZafWindow)->Temporary())
						Event(S_CLOSE_TEMPORARY);

					ZafWindowObject *tFocusObject = windowFocusObject;
					window->SetFocus(true);
					if (tFocusObject)
						tFocusObject->NotifyFocus(tFocusObject, false);
					tClickObject = window->FindMouseObject(event);
					return (tClickObject ?
						tClickObject->DispatchMouseEvent(event) : ccode);
				}
				// If not overlapping the window and the window is modal, don't 
				// go any further.
				else if (window->Modal())
				{
					if (ccode == L_BEGIN_SELECT)
						ZafErrorStub::Beep();
					break;
				}
			}

			// If we're here, the event didn't overlap any windows.
			if (ccode == L_VIEW)
			{
				if (mouseObject)
				{
					mouseObject->Event(N_MOUSE_LEAVE);
					mouseObject = ZAF_NULLP(ZafWindowObject);
				}
				eventManager->SetDeviceState(E_MOUSE, DM_VIEW);
			}
			else if (ccode == L_BEGIN_SELECT)
			{
				while (firstWindow && firstWindow->Temporary() &&
					!firstWindow->Locked())
				{
					Subtract(firstWindow);
					if (firstWindow->Destroyable())
						delete firstWindow;
					firstWindow = DynamicPtrCast(first, ZafWindow);
					if (windowFocusObject)
						windowFocusObject->SetFocus(true);
				}
			}
		}
		break;

	default:
		{
			ZafWindow *firstWindow = DynamicPtrCast(firstObject, ZafWindow),
				*window;
			for (window = firstWindow; window;
				window = DynamicPtrCast(window->Next(), ZafWindow))
			{
				if (!window->Disabled() && window->Visible() &&
					window->zafRegion.Overlap(event.position))
				{
					tClickObject = window->FindMouseObject(event);
					if (tClickObject)
						ccode = tClickObject->DispatchMouseEvent(event);
					break;
				}
			}
		}
		break;
	}

	return (ccode);
}

ZafEventType ZafWindowManager::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Compute the actual draw region.
	ZafRegionStruct drawRegion = BeginDraw();

	ZafPaletteStruct palette = LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE);
	display->SetPalette(palette);

	display->Rectangle(drawRegion, 0, true);

	EndDraw();

	return (ccode);
}

ZafEventType ZafWindowManager::Event(const ZafEventStruct &event)
{
	// Check for an empty list.
	ZafWindowObject *focusObject = Current();
	ZafEventType ccode = LogicalEvent(event);

	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_DATA)
	{
		OSInvalidateRegion(0, (ccode == S_REDISPLAY_DATA) ? true : false);
		return (ccode);
	}
	if (!focusObject && (ccode == L_EXIT || ccode == S_EXIT))
		return (S_EXIT);
	else if (!focusObject && (ccode != L_EXIT && ccode != S_EXIT && ccode != S_ADD_OBJECT))
		return (S_NO_OBJECT);
/* START BLOCK COMMENT
**		else if (event.type == E_OSEVENT)
**			return (OSEvent(event));
END BLOCK COMMENT */

//	ZafWindowObject *object;
	ZafWindow *focusWindow = DynamicPtrCast(focusObject, ZafWindow);
	ZafWindow *firstWindow = DynamicPtrCast(first, ZafWindow);

	// Check for event routing.
	if (event.route)
		return (event.route->Event(event));
	
	bool processed = false;
	ZafEventType saveType = ccode;
	if (event.InputType() == E_MOUSE && MouseEventRoute())
	{
		ccode = MouseEventRoute()->Event(event);
		processed = true;
	}
	else if (DefaultEventRoute())
	{
		ccode = DefaultEventRoute()->Event(event);
		processed = true;
	}

	if (processed && ccode != S_UNKNOWN)
		return (ccode);
	else if (processed)
		ccode = saveType;

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Position messages -----
	case S_CLOSE_TEMPORARY:
		{
//				if (focusWindow->Temporary())
			if (firstWindow->Temporary())
			{
				ZafWindowManager::Subtract(firstWindow);
				if (firstWindow->Destroyable())
					delete firstWindow;

				// If the event was generated from user input, pass the event
				// to the first object.
				if (event.type != S_CLOSE_TEMPORARY && first)
				{
					firstWindow = DynamicPtrCast(first, ZafWindow);
					if (!firstWindow->Temporary())
						firstWindow->Event(L_CANCEL);
				}
			}
			// If the event was generated from user input, pass the event
			// to the first object.
			else if (event.type != S_CLOSE_TEMPORARY)
				ccode = focusObject->Event(event);
		}
		break;

	case S_MOVE_MODE:
	case S_SIZE_MODE:
	case L_MOVE_MODE:
	case L_SIZE_MODE:
	case L_MINIMIZE:
	case L_MAXIMIZE:
	case L_RESTORE:
		{
			// Remove temporary windows.
			while (focusWindow && focusWindow->Temporary())
			{
				ZafWindowManager::Subtract(focusWindow);
				if (focusWindow->Destroyable())
					delete focusWindow;
				focusWindow = DynamicPtrCast(first, ZafWindow);
			}

			if (!focusWindow)
				ccode = S_NO_OBJECT;
			else
				ccode = focusWindow->Event(event);
		}
		break;

	case S_CLOSE:
		{
			// Get a pointer to the window that should be closed.
			ZafWindow *closeWindow;
			ZafWindowObject *object = event.windowObject;
			if (event.type == S_CLOSE && object)
				// The window to be closed was provided with the event.
//??? An S_CLOSE_WINDOW or similar event that makes use of
//??? event.windowObject is needed.
				closeWindow = DynamicPtrCast(object->RootObject(), ZafWindow);
			else
			{
				// Get the first window.
				closeWindow = DynamicPtrCast(current, ZafWindow);

				// The first non-temporary window should be closed.
				if (ccode == S_CLOSE)
					while (closeWindow && closeWindow->Temporary())
						closeWindow = DynamicPtrCast(closeWindow->Next(), ZafWindow);
			}

			// Make sure it is ok to close the window.
			if (closeWindow && !closeWindow->Locked() &&
				!(ccode == S_CLOSE_TEMPORARY && !closeWindow->Temporary()) &&
				!(screenID == closeWindow->screenID && Event(ZafEventStruct(N_EXIT)) != 0) &&
				closeWindow->Event(ZafEventStruct(N_CLOSE)) == 0)
			{
				if (closeWindow->screenID == screenID)
					ccode = Event(S_EXIT);

				// Close the window and all temporary windows in front of it.
				ZafWindow *tFirst = DynamicPtrCast(first, ZafWindow);
				while (tFirst && tFirst != closeWindow)
				{
					if (tFirst->Temporary())
					{
						Event(S_CLOSE_TEMPORARY);
						tFirst = DynamicPtrCast(first, ZafWindow);
					}
					else
						tFirst = 0;

				}

				// Close the window.
				Subtract(closeWindow);
				if (closeWindow->Destroyable())
					delete closeWindow;
			}

			// See if there are any windows left.
			if (!first)
				ccode = S_NO_OBJECT;
  		}
  		break;

	// ----- Exit messages -----
	case S_EXIT:
	case L_EXIT:
		{
			if (focusWindow->Modal())
				break;
			else if (Event(N_EXIT))
			{
				ccode = S_CONTINUE;
				break;
			}

			ccode = S_EXIT;
			for (ZafWindow *win = DynamicPtrCast(first, ZafWindow); win &&
				win->Temporary(); win = DynamicPtrCast(first, ZafWindow))
			{
				Event(S_CLOSE_TEMPORARY);
			}
		}
		break;

	case N_EXIT:
		// See if it is OK to exit.
		ccode = exitFunction ? (*exitFunction)(display, eventManager, this) : 0;

		// Clear the exit function pointer if exiting, so that the exit
		// function is not called again for the last window subtracted.
		if (ccode == 0)
			SetExitFunction(ZAF_NULLF(ZafExitFunction));
		break;

	// ----- Activation messages -----
	case S_REDISPLAY:
		{
			// Redisplay the objects on the screen by "damaging" the entire
			// screen and repairing it.
			ZafRegionStruct updateRegion;

			updateRegion.left = updateRegion.top = 0;
			updateRegion.right = display->columns - 1;
			updateRegion.bottom = display->lines - 1;
#if defined(ZAF_GRAPHICS_MODE)
			updateRegion.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
			updateRegion.coordinateType = ZAF_CELL;
#endif
//				damageList.Allocate(0, updateRegion);
			OSInvalidateObject(&updateRegion, this);

			RepairDamage();
		}
		break;

	case S_REDISPLAY_REGION:
		{
			// Redisplay the damaged part of the screen.
			ZafRegionStruct updateRegion = event.region;

//				damageList.Allocate(0, updateRegion);
			OSInvalidateObject(&updateRegion, this);

			RepairDamage();
		}
		break;

	case S_REPAIR_DAMAGE:
		RepairDamage();
		break;

	case S_SET_FOCUS:
		if (event.windowObject)
			event.windowObject->SetFocus(true);
		break;

	// ----- Miscellaneous messages -----
	case S_HELP:
	case L_HELP:
		// Pass the event to the current child for processing.
		if (focusObject && focusObject->Event(event) == S_UNKNOWN &&
			zafHelpSystem)
		{
			// The current child branch doesn't have an assigned help context,
			// so we'll display ours (or the index).
			zafHelpSystem->DisplayHelp(helpContext);
		}
		break;

	case L_ALT_KEY:
		{
			bool closedMenu = false;
			while (firstWindow && firstWindow->IsA(ID_ZAF_POP_UP_MENU) &&
				firstWindow->Temporary() && !firstWindow->Locked())
			{
				Event(S_CLOSE_TEMPORARY);
				firstWindow = DynamicPtrCast(first, ZafWindow);
				closedMenu = true;
			}

			if (firstWindow)
			{
				if (closedMenu)
					return (firstWindow->Event(L_CANCEL));
				else
					return firstWindow->Event(event);
			}
			ZafWindow::Event(event);
		}
		break;

/* START BLOCK COMMENT
**			case S_CASCADE:
**				{
**					ZafEventStruct event;
**					int column = 0, line = 0;
**					((ZafScreenDisplay *)display)->RegionDefine(ID_ZAF_SCREEN, 0, 0, display->columns, display->lines);
**	
**					// Recompute the object regions.
**					for (object = Last(); object; object = object->Previous())
**					{
**						int width = object->zafRegion.Width();
**						int height = object->zafRegion.Height();
**						int columnDelta = 3 * display->cellWidth;
**						float lineDelta = 1.5 * display->cellHeight;
**						if (object->CoordinateType() == ZAF_CELL)
**						{
**							width /= display->cellWidth;
**							height /= display->cellHeight;
**							columnDelta /= display->cellWidth;
**							lineDelta /= display->cellHeight;
**						}
**						else if (object->CoordinateType() == ZAF_MINICELL)
**						{
**							width /= display->cellWidth * display->miniNumeratorX / display->miniDenominatorX;
**							height /= display->cellHeight * display->miniNumeratorY / display->miniDenominatorY;
**							columnDelta /= display->cellWidth * display->miniNumeratorX / display->miniDenominatorX;
**							lineDelta /= display->cellHeight * display->miniNumeratorY / display->miniDenominatorY;
**						}
**						if (line + height >= display->lines)
**							line = 0;
**						event.type = S_SIZE;
**						event.region.left = column;
**						event.region.top = line;
**						event.region.right = column + width - 1;
**						event.region.bottom = line + height - 1;
**	#if defined(ZAF_GRAPHICS_MODE)
**						event.region.coordinateType = ZAF_PIXEL;
**	#elif defined(ZAF_TEXT_MODE)
**						event.region.coordinateType = ZAF_CELL;
**	#endif
**						object->Event(event);
**						column += columnDelta;
**						line += lineDelta;
**					}
**				}
**				break;
END BLOCK COMMENT */

/* START BLOCK COMMENT
**			case L_CONTINUE_MOVE_DRAG:
**			case L_CONTINUE_COPY_DRAG:
**			case L_CONTINUE_LINK_DRAG:
**			case L_END_MOVE_DRAG:
**			case L_END_COPY_DRAG:
**			case L_END_LINK_DRAG:
**				if (dragObject)
**				{
**					// Route event to the window under the mouse.  Give the window
**					// focus if appropriate.
**					for (object = First(); object; object = object->Next())
**						if (object->zafRegion.Overlap(event.position))
**						{
**							if (focusWindow->Modal() && focusWindow != object)
**								eventManager->SetDeviceImage(E_MOUSE, DM_CANCEL);
**							else
**							{
**								if (object != focusWindow &&
**									eventManager->SetDeviceState(E_MOUSE, D_STATE) != DM_CANCEL &&
**									(ccode == L_END_MOVE_DRAG || ccode == L_END_COPY_DRAG))
**									object->SetFocus(true);
**								object->Event(event);
**							}
**							break;
**						}
**	
**					// If the event was a drop event, reset dragObject and the mouse 
**					// image.
**					if (ccode == L_END_MOVE_DRAG || ccode == L_END_COPY_DRAG ||
**						ccode == L_END_LINK_DRAG)
**					{
**						dragObject = ZAF_NULLP(ZafWindowObject);
**						if (ccode == L_END_MOVE_DRAG)
**							focusWindow->Event(event);
**						Event(ZafEventStruct(L_VIEW, 0, event.position));
**					}
**					else if (!object)
**						eventManager->SetDeviceImage(E_MOUSE, DM_CANCEL);
**		  		}
**				else
**					ccode = focusWindow ? focusWindow->Event(event) : S_UNKNOWN;
**				break;
END BLOCK COMMENT */

	// ----- Base class messages -----
	case S_ADD_OBJECT:
	case S_SUBTRACT_OBJECT:
		ccode = ZafWindow::Event(event);
		break;

	// ----- Application messages -----
	case A_OPEN_DOCUMENT:
	case A_PRINT_DOCUMENT:
	case A_OPEN_WINDOW:
	case A_CLOSE_WINDOW:
	case A_RESET_I18N:
	case A_MINIMIZE_WINDOWS:
	case A_RESTORE_WINDOWS:
		ccode = ApplicationEvent(event);
		break;

	case N_RESET_I18N:
		BroadcastEvent(event, -1);
		break;

	// ----- Current object messages -----
	case S_CONTINUE:
		ccode = Current() ? Current()->Event(event) : S_NO_OBJECT;
		break;

	case L_NEXT_WINDOW:
		{
			while (focusWindow && focusWindow->Temporary())
				focusWindow = DynamicPtrCast(focusWindow->Next(), ZafWindow);
			if (focusWindow && !focusWindow->Modal() &&
				!Last()->Disabled() && focusWindow != Last())
				Last()->SetFocus(true);
		}
		break;

	// ----- Unknown messages -----
	default:
		// Send all mouse messages to the leaf-most object.
		if (event.InputType() == E_MOUSE)
			ccode = DispatchMouseEvent(event);
		else
		{
			focusObject = firstWindow;
			while (focusObject->parent)
				focusObject = focusObject->parent;
			if (focusObject == focusWindow)
				ccode = firstWindow->Event(event);
			else
				ccode = focusWindow->Event(event);
		}
		break;
	}

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

ZafRegionStruct ZafWindowManager::MaxRegion(ZafWindowObject *, ZafVtJustify, ZafHzJustify)
{
	ZafRegionStruct region;
	region.left = region.top = 0;
	region.right = display->columns - 1;
	region.bottom = display->lines - 1;
#if defined(ZAF_GRAPHICS_MODE)
	region.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
	region.coordinateType = ZAF_CELL;
#endif

	return (region);
}

void ZafWindowManager::RepairDamage(void)
{
	if (!damageList.First())
		return;

	ZafDevice *timer = eventManager->GetObject(ZAF_ITEXT("RepairDamageTimer"));
	if (timer)
		timer->Event(D_OFF);

	// Reset the device states.
	ZafEventType oldCursorState = eventManager->SetDeviceState(E_CURSOR, D_STATE);
	eventManager->SetDeviceState(E_CURSOR, D_OFF);
//	eventManager->SetDeviceState(E_MOUSE, DM_VIEW);

	ZafEventStruct event;
	event.region = damageList.First()->Object()->zafRegion;

#if defined(ZAF_SHADOW_BORDER)
	display->Rectangle(ID_ZAF_SCREEN, event.region, display->backgroundPalette, 0, TRUE);
	for (tObject = Last(); tObject; tObject = tObject->Previous())
	{
		int updateRegion = false;
		if (tObject == first)
		{
			event.type = S_CURRENT;
			tObject->Event(event);
			updateRegion = true;
			break;
		}
		else if (tObject->zafRegion.Overlap(event.region))
		{
			if (FlagSet(tObject->woAdvancedFlags, WOAF_TEMPORARY) ||
				(tObject->Previous() && FlagSet(tObject->Previous()->woAdvancedFlags, WOAF_TEMPORARY)))
				event.type = S_CURRENT;
			else
				event.type = S_DISPLAY_INACTIVE;
			tObject->Event(event);
			updateRegion = true;
		}
		if (updateRegion) // Fix for shadow border problem.
		{
			event.region.left = MinValue(event.region.left, tObject->zafRegion.left);
			event.region.top = MinValue(event.region.top, tObject->zafRegion.top);
			event.region.right = MaxValue(event.region.right, tObject->zafRegion.right);
			event.region.bottom = MaxValue(event.region.bottom, tObject->zafRegion.bottom);
			updateRegion = false;
		}
	}
#else // !ZAF_SHADOW_BORDER

	ZafUpdateRegionElement *nextElement;
	for (ZafUpdateRegionElement *element = damageList.First(); element;
		element = nextElement)
	{
		nextElement = element->Next();
		if (element->Object() != this)
		{
			damageList.Subtract(element);

			// We Must consolidate the regions for XOR calls to draw.
			ZafRegionElement *tRegion1;
			ZafRegionElement *tRegion2;
			for (tRegion1 = element->List().First(); tRegion1;
				tRegion1 = tRegion2)
			{
				tRegion2 = tRegion1->Next();
				if (tRegion2)
				{
					if (tRegion1->region.Overlap(tRegion2->region))
						tRegion2 = element->List().First();
					element->List().Subtract(tRegion1);
					element->List().Split(0, tRegion1->region);
					element->List().Add(tRegion1, element->List().First());
				}
			}

			element->Object()->OSUpdateRegions(&element->List());
			delete element;
			nextElement = damageList.First();
		}
		else
		{
			damageList.Subtract(element);
			ZafRegionList &regionList = element->List();
			ZafRegionList drawList;
			ZafRegionElement *nextDamageElement;
			ZafRegionStruct clipRegion;

			for (ZafWindowObject *window = First(); window && regionList.First();
				window = window->Next())
			{
				drawList.Destroy();
				// Quick test to see if the window might need to be repaired.
				{

					// Cycle through the damage list regions, repairing any that 
					// overlap the window.
					for (ZafRegionElement *damageElement = regionList.First(); damageElement;
						damageElement = nextDamageElement)
					{
						nextDamageElement = damageElement->Next();
						if (window->zafRegion.Overlap(damageElement->region, clipRegion) && window->Visible(false))
						{
							// Remove the region we are repairing from the damage list.
							regionList.Split(0, clipRegion, false);
							nextDamageElement = regionList.First();

							clipRegion.HzShift(-window->zafRegion.left);
							clipRegion.VtShift(-window->zafRegion.top);
							drawList.Allocate(0, clipRegion);
						}
					}
				}
				if (drawList.First())
					window->OSUpdateRegions(&drawList);
			}

			damageList.Add(element);
			nextElement = damageList.First();
			if (nextElement == element)
				nextElement = 0;
		}
	}

	if (!damageList.First())
	{
		eventManager->SetDeviceState(E_CURSOR, oldCursorState);
		return;
	}

	nextElement = damageList.First();
	damageList.Subtract(nextElement);
	ZafRegionList &regionList = nextElement->List();

	// If damageList still has regions, they need to be drawn with the
	// background.
//	if (damageList.First())
	if (regionList.First())
	{
		display->SetDrawRegions(&regionList);
		ZafEventStruct event(S_REDISPLAY_REGION);
		Draw(event, S_REDISPLAY_REGION);
	}

	delete nextElement;

#endif // !ZAF_SHADOW_BORDER

	eventManager->SetDeviceState(E_CURSOR, oldCursorState);
}

ZafWindowObject *ZafWindowManager::Subtract(ZafWindowObject *object)
{
	// Make sure there is an object to subtract.
	if (!object)
		return (ZAF_NULLP(ZafWindowObject));
	if (ZafList::Index(object) == -1)
		return (object);

	// Send the mouseObject an N_MOUSE_LEAVE if it's on the window getting subtracted.
	if (mouseObject)
	{
		// Search the object hierarchy to try to find a match.
		ZafWindowObject *parentWin = mouseObject;
		while (parentWin && parentWin != object)
			parentWin = parentWin->parent;

		// If parentWin, then the while loop stopped because a parent of 
		// mouseObject (or mouseObject itself) is the subtracted object.
		if (parentWin)
		{
			mouseObject->Event(N_MOUSE_LEAVE);
			mouseObject = ZAF_NULLP(ZafWindowObject);
		}
	}

	// Owned window
	if (ownerWindowList)
	{
		ZafEventStruct closeEvent(S_CLOSE);
		ZafWindowLink *link;
		ZafWindowObject *target = DynamicPtrCast(object, ZafWindow)->Owner() ?
			DynamicPtrCast(object, ZafWindow)->Owner() : object;
		for (link = (ZafWindowLink *)ownerWindowList->First();
			link; link = link->Next())
		{
			if (link->Object() == object)
			{
				ZafWindowLink *tempLink;
				for (ZafWindowLink *childLink = link->FirstChild(); childLink;)
				{
				 	tempLink = childLink;
					childLink = childLink->Next();
					closeEvent.windowObject = tempLink->Object();
					link->RemoveChild(closeEvent.windowObject);
					Event(closeEvent);
				}
				break;
			}
			else if (link->Object() == target)
			{
				link->RemoveChild(object);
				link = 0;
				break;
			}
		}
		if (link)
			ownerWindowList->Subtract(link);
	}

/* START BLOCK COMMENT
**		ZafWindow *tWindow = DynamicPtrCast(first, ZafWindow);
**		ZafWindow *tNextWin;
**	
**		while (tWindow)
**		{
**			tNextWin = DynamicPtrCast(tWindow->Next(), ZafWindow);
**			if (tWindow->Owner() == object)
**				Subtract(tWindow);
**	#* START BLOCK COMMENT
**	**			else if (tWindow == object)
**	**				break;
**	END BLOCK COMMENT #/
**			tWindow = tNextWin;
**		}
END BLOCK COMMENT */
	// Owned window

	// Update the windows according to their new list positions.
	ZafEventStruct event;
	ZafWindowObject *nextObject = object->Next();
	ZafWindowObject *newCurrent = First();
	if (newCurrent == object)
		newCurrent = newCurrent->Next();

	// De-activate the specified object.
	event.type = S_NON_CURRENT;
	event.region.left = event.region.top =
		event.region.right = event.region.bottom = -1;
#if defined(ZAF_GRAPHICS_MODE)
	event.region.coordinateType = ZAF_PIXEL;
#elif defined(ZAF_TEXT_MODE)
	event.region.coordinateType = ZAF_CELL;
#endif
	object->Event(event);		// Call any user-functions.

  	// Remove the window from the manager's list.
	ZafWindowObject *previous = object->Previous();
	ZafList::Subtract(object);
	event.type = S_DEINITIALIZE;
	object->Event(event);

	// Update the screen.
	ZafRegionStruct damageRegion = object->zafRegion;
//	ZafRegionList damageList;
//	damageList.Allocate(0, damageRegion);
	OSInvalidateObject(&damageRegion, this);

	// If the window was not the front window, don't need to redraw areas
	// that are covered by windows in front of it.
	ZafRegionList *updateList = 0;
	ZafUpdateRegionElement *element;
	for (element = damageList.First(); element;
		element = element->Next())
		if (element->Object() == this)
		{
			updateList = &element->List();
			break;
		}

	while (previous)
	{
		updateList->Split(0, previous->zafRegion);
		previous = previous->Previous();
	}

	if (updateList && !updateList->First())
	{
		damageList.Subtract(element);
		delete element;
	}

	// Repair the damage caused by the old window disappearing.
	RepairDamage();
	// Second call necessary because first call queues up requests for 
	// individual objects.
	RepairDamage();

	// If there is a new current window, set focus to it.  Because the window
	// is already the first window in the windowManager's list, 
	// ZAF_WIN::SetFocus() will not cause the window to redraw.  Only those
	// subobjects that need to redraw, e.g., title bar, max, min, and system
	// buttons, will redraw to show focus.
/* START BLOCK COMMENT
**		if (DynamicPtrCast(object, ZafWindow)->Temporary() && newCurrent)
**			SetCurrent(newCurrent);
**		else if (!DynamicPtrCast(object, ZafWindow)->Temporary() && newCurrent)
**			newCurrent->SetFocus(true);
END BLOCK COMMENT */
	if (newCurrent)
		newCurrent->SetFocus(true);

	// Return a pointer to the next object.
	return (nextObject);
}

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

ZafEventType ZafWindowManager::OSEvent(const ZafEventStruct &)
{
	return (S_CONTINUE);
}

void ZafWindowManager::GetDrawRegionList(ZafWindowObject *object,
	ZafRegionList &regionList)
{
	if (!object)
		return;

	ZafRegionStruct oldRegion = object->zafRegion;
	bool osRegionCalculated = false;
	ZafRegionStruct osRegion;
	ZafWindowObject *win;
	ZafWindowObject *root;
	root = object->RootObject();
	ZafRegionElement *updateElement;

	// Special handling for MDI children. Clip behind previous MDI children.
	if (root->parent)
	{
		oldRegion.VtShift(-oldRegion.top);
		oldRegion.HzShift(-oldRegion.left);
		osRegion = object->ConvertToOSRegion(object, &oldRegion);
		osRegionCalculated = true;

		// 0,0 based regions in list. Convert to screen coordinates.
		for (updateElement = regionList.First();
			updateElement; updateElement = updateElement->Next())
		{
			updateElement->region.VtShift(osRegion.top);
			updateElement->region.HzShift(osRegion.left);
		}

		ZafRegionStruct winRegion;
		for (win = root->Previous(); win && regionList.First();
			win = win->Previous())
		{
			winRegion = win->zafRegion;
			winRegion.VtShift(-winRegion.top);
			winRegion.HzShift(-winRegion.left);
			winRegion = win->ConvertToOSRegion(win, &winRegion);
			if (osRegion.Overlap(winRegion))
				regionList.Split(0, winRegion, false);
		}
	}

	for (root = object; root; root = root->parent)
		if (root == first)
			break;

	if (!root)
	{
		// Test to see if the object is on a popup menu.
		root = First();
		while (root)
		{
			if (root == object || (object->parent &&
				root == object->parent && DynamicPtrCast(root, ZafWindow) &&
				DynamicPtrCast(root, ZafWindow)->Temporary()))
			{
				if (osRegionCalculated)
				{
					for (updateElement = regionList.First();
						updateElement; updateElement = updateElement->Next())
					{
						updateElement->region.VtShift(-osRegion.top);
						updateElement->region.HzShift(-osRegion.left);
					}
				}
				return;
			}
			root = root->parent;
		}

		// Calculate visible regions.
		root = object;
		while (root->parent)
			root = root->parent;

		if (!osRegionCalculated)
		{
			oldRegion.VtShift(-oldRegion.top);
			oldRegion.HzShift(-oldRegion.left);
			osRegion = object->ConvertToOSRegion(object, &oldRegion);
			osRegionCalculated = true;
		}

		// 0,0 based regions in list. Convert to screen coordinates.
		for (updateElement = regionList.First();
			updateElement; updateElement = updateElement->Next())
		{
			updateElement->region.VtShift(osRegion.top);
			updateElement->region.HzShift(osRegion.left);
		}

		for (win = root->Previous(); win && regionList.First();
			win = win->Previous())
		{
			if (osRegion.Overlap(win->zafRegion))
				regionList.Split(0, win->zafRegion, false);
		}
	}
	if (osRegionCalculated)
	{
		for (updateElement = regionList.First();
			updateElement; updateElement = updateElement->Next())
		{
			updateElement->region.VtShift(-osRegion.top);
			updateElement->region.HzShift(-osRegion.left);
		}
	}
}

void ZafWindowManager::OSInvalidateObject(ZafRegionStruct *region, ZafWindowObject *object)
{
	if (!object)
		object = this;

	if (damageList.First() == 0)
	{
		ZafDevice *timer = eventManager->GetObject(ZAF_ITEXT("RepairDamageTimer"));
		if (timer)
			timer->Event(D_ON);
	}

	ZafUpdateRegionElement *element;

	if (object != this)
	{
		ZafRegionList regionList;
		ZafRegionElement *updateElement;
		ZafRegionStruct oldRegion = region ? *region : object->zafRegion;
		if (!region)
		{
			oldRegion.VtShift(-oldRegion.top);
			oldRegion.HzShift(-oldRegion.left);
		}
		regionList.Allocate(0, oldRegion);
		GetDrawRegionList(object, regionList);
		if (!regionList.First())
			return;

		for (element = damageList.First(); element; element = element->Next())
			if (object == element->Object())
			{
				for (updateElement = regionList.First();
					updateElement; updateElement = updateElement->Next())
				{
					element->AddRegion(updateElement->region);
				}
				return;
			}

		updateElement = regionList.First();
		element = new ZafUpdateRegionElement(object,	updateElement->region);
		damageList.Add(element);
		for (updateElement = updateElement->Next(); updateElement;
			updateElement = updateElement->Next())
		{
			element->AddRegion(updateElement->region);
		}
		return;
	}

	for (element = damageList.First(); element; element = element->Next())
		if (object == element->Object())
		{
			element->AddRegion(region ? *region : object->zafRegion);
			return;
		}
	damageList.Add(new ZafUpdateRegionElement(object, region ? *region :
		object->zafRegion));
}

void ZafWindowManager::OSInvalidateRegion(ZafRegionStruct *damagedRegion, bool immediateRedraw, bool)
{
	OSInvalidateObject(damagedRegion, this);
//	damageList.Allocate(0, damagedRegion ? *damagedRegion : clientRegion);
	if (immediateRedraw)
		RepairDamage();
}

void ZafWindowManager::OSValidateObject(ZafWindowObject *object)
{
	if (!object)
		object = this;

	for (ZafUpdateRegionElement *element = damageList.First(); element; element = element->Next())
		if (object == element->Object())
		{
			damageList.Subtract(element);
			delete element;
			return;
		}
}

void ZafWindowManager::OSMapPalette(ZafPaletteStruct &, ZafPaletteType, ZafPaletteState)
{
}

