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

#include <z_win.hpp>
#include <z_mouse2.hpp>
#include <z_string.hpp>
#include <z_timer.hpp>

#if defined(ZAF_LOCAL_FILE_STATICS)
#define ZAF_WINDOW_INFO
#include "gbl_def.cpp"
#else
#	if defined(ZAF_PERSIST_ALL)
#include <zaf.hpp>
#	endif
#include <z_winall.hpp>
#define ZAF_LOCAL_FILE_STATICS
#define ZAF_OBJECT_PERSISTENCE_INFO
#define ZAF_APPLICATION_INFO
#define ZAF_BIGNUM_INFO
#define ZAF_BORDER_INFO
#define ZAF_BUTTON_INFO
#define ZAF_COMBO_BOX_INFO
#define ZAF_DATE_INFO
#define ZAF_FORMATTED_STRING_INFO
#define ZAF_GROUP_INFO
#define ZAF_HZ_LIST_INFO
#define ZAF_ICON_INFO
#define ZAF_IMAGE_INFO
#define ZAF_INTEGER_INFO
#define ZAF_MAXIMIZE_BUTTON_INFO
#define ZAF_MINIMIZE_BUTTON_INFO
#define ZAF_NOTEBOOK_INFO
#define ZAF_POPUP_INFO
#define ZAF_PROMPT_INFO
#define ZAF_PROGRESS_BAR_INFO
#define ZAF_PULLDOWN_INFO
#define ZAF_REAL_INFO
#define ZAF_SCROLL_BAR_INFO
#define ZAF_SPIN_CONTROL_INFO
#define ZAF_STATUS_BAR_INFO
#define ZAF_STRING_INFO
#define ZAF_SYSTEM_BUTTON_INFO
#define ZAF_TABLE_INFO
#define ZAF_TEXT_INFO
#define ZAF_TIME_INFO
#define ZAF_TITLE_INFO
#define ZAF_TOOL_BAR_INFO
#define ZAF_TREE_INFO
#define ZAF_UTIME_INFO
#define ZAF_VT_LIST_INFO
#define ZAF_WINDOW_INFO
#define ZAF_GEOMETRY_INFO
#include "gbl_def.cpp"
#endif

#if defined(ZAF_INDEPENDENT)
#include "i_winmgr.hpp"
#endif

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

ZafWindowManager::ZafWindowManager(ZafExitFunction zExitFunction) :
	ZafWindow(0, 0, 0, 0),
	exitFunction(zExitFunction),
	defaultEventRoute(ZAF_NULLP(ZafWindowObject)),
	dragObject(ZAF_NULLP(ZafWindowObject)),
	focusObject(ZAF_NULLP(ZafWindowObject)),
	helpObject(ZAF_NULLP(ZafWindowObject)),
	mouseEventRoute(ZAF_NULLP(ZafWindowObject)),
	mouseObject(ZAF_NULLP(ZafWindowObject)),
	oldFocusObject(ZAF_NULLP(ZafWindowObject))
{
	// Set the static variables.
	if (!ZafWindowObject::display)
		ZafWindowObject::display = zafDisplay;
	if (!ZafWindowObject::eventManager)
		ZafWindowObject::eventManager = zafEventManager;
	if (!ZafWindowObject::windowManager)
		ZafWindowObject::windowManager = this;

	// Initialize the window manager.
#if defined(ZAF_TEXT_MODE)
	zafRegion.coordinateType = ZAF_CELL;
#else
	zafRegion.coordinateType = ZAF_PIXEL;
#endif
	zafRegion.right = Display()->columns - 1;
	zafRegion.bottom = Display()->lines - 1;
	clientRegion = zafRegion;
	SetRegionType(ZAF_AVAILABLE_REGION);

	// Initialize the mouse cursor.
	eventManager->SetDeviceState(E_MOUSE, DM_VIEW);

#if defined(ZAF_MOTIF)
	// This is used in the modal window list in Motif.
	modalWindow = ZAF_NULLP(ZafWindow);
#elif defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	// Initialize the display background.
	ZafRegionStruct drawRegion;
#	if defined(ZAF_TEXT_MODE)
	drawRegion.Assign(0, 0, Display()->columns, Display()->lines, ZAF_CELL);
#	else
	drawRegion.Assign(0, 0, Display()->columns, Display()->lines, ZAF_PIXEL);
#	endif

	OSInvalidateRegion(&drawRegion, true);

	// Add a timer to insure that delayed damage repair happens within a
	// reasonable time.
	ZafTimer *timer = new ZafTimer(D_OFF, 1500);
	timer->SetStringID(ZAF_ITEXT("RepairDamageTimer"));
	timer->Add(this, S_REPAIR_DAMAGE);
//	timer->SetNotifyMessage(S_REPAIR_DAMAGE);
	eventManager->Add(timer);
	cursorObject = ZAF_NULLP(ZafWindowObject);
	windowFocusObject = ZAF_NULLP(ZafWindowObject);
#endif
}

void ZafWindowManager::Center(ZafWindowObject *object)
{
	// Change coordinates so we can work with the window.
#if defined(ZAF_TEXT_MODE)
	object->ConvertCoordinates(ZAF_CELL);
#else
	object->ConvertCoordinates(ZAF_PIXEL);
#endif
	// Check the window size.
	int columns = Display()->columns;
	int lines = Display()->lines;
	int width = object->zafRegion.Width();
	if (width > columns)
		width = (int)columns;
	int height = object->zafRegion.Height();
	if (height > lines)
		height = (int)lines;

	// Update the window according to the center position.
	object->zafRegion.left = (columns - width) / 2;
	object->zafRegion.top = (lines - height) / 2;
	object->zafRegion.right = object->zafRegion.left + width - 1;
	object->zafRegion.bottom = object->zafRegion.top + height - 1;

	// Re-display the window if it already is present on the screen.
	if (object->screenID)
		object->OSSize();
}

ZafWindowObject *ZafWindowManager::Duplicate(void)
{
	return (ZAF_NULLP(ZafWindowObject));
}

ZafWindowObject *ZafWindowManager::FocusObject(void) const
{
	// Return a pointer to self.
	if (first && First()->focus)
		return (First()->FocusObject());
	return (ZAF_NULLP(ZafWindowObject));
}

ZafWindowObject *ZafWindowManager::GetObject(ZafNumberID matchID)
{
	// Search the list of children.
	ZafWindowObject *match = ZAF_NULLP(ZafWindowObject);
	for (ZafWindowObject *object = First(); object && !match; object = object->Next())
		match = object->GetObject(matchID);
	return (match);
}

ZafWindowObject *ZafWindowManager::GetObject(const ZafIChar *matchID)
{
	// Check for a root directory separator.
	bool directory = false;
	if (*matchID == ZAF_DIRECTORY_SEPARATOR)
	{
		directory = true;
		matchID++;
	}

	// Re-point the arguments to do partial matching.
	ZafIChar parseID[ZAF_MAXNAMELEN];
	ZafIChar *tParseID = parseID;
	for ( ; *matchID && *matchID != ZAF_DIRECTORY_SEPARATOR; matchID++, tParseID++)
		*tParseID = *matchID;
	*tParseID = '\0';
	if (*matchID == ZAF_DIRECTORY_SEPARATOR)
	{
		directory = true;
		matchID++;
	}

	// Try to match with one of the children.
	ZafWindowObject *match = ZAF_NULLP(ZafWindowObject);
	for (ZafWindowObject *object = First(); object && !match; object = object->Next())
		if (!directory)
			match = object->GetObject(parseID);
		else if (!streq(parseID, object->StringID()))
			match = *matchID ? object->GetObject(matchID) : object;
	return (match);
}

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

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

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

ZafLogicalEvent ZafWindowManager::LogicalEvent(const ZafEventStruct &event)
{
	ZafLogicalEvent ccode = MapEvent(defaultEventMap, event);
	return ((ccode == L_NONE) ? ZafWindow::LogicalEvent(event) : ccode);
}

const ZafPaletteStruct *ZafWindowManager::MapClassPalette(ZafPaletteType type, ZafPaletteState state)
{
	return (MapPalette(defaultPaletteMap, type, state));
}

ZafWindowObject *ZafWindowManager::NotifyFocus(ZafWindowObject *tFocusObject, bool setFocus)
{
	// Remove focus from the current window without validation.
	ZafWindowObject *invalidObject = ZAF_NULLP(ZafWindowObject);
	if (Current() && Current() != tFocusObject && setFocus)
		invalidObject = Current()->NotifyFocus(ZAF_NULLP(ZafWindowObject), false);

	// Reset current object.
	if (!invalidObject)
	{
		if (setFocus && (Index(tFocusObject) != -1))
		{
			// Move the object to the front of the list.
			if (tFocusObject != First())
			{
#if defined(ZAF_INDEPENDENT)
				// Owned windows.
				// Do nothing if it already is the focus object.
				if (tFocusObject == current)
					return (invalidObject);

				ZafRegionList regionList;
				ZafRegionStruct updateRegion;

				ZafWindow *focusWin = DynamicPtrCast(tFocusObject, ZafWindow);
				ZafWindowLink *link = 0;
				if (ownerWindowList && ownerWindowList->First())
				{
					if (focusWin->Owner())
						for (link = (ZafWindowLink *)ownerWindowList->First();
							link; link = link->Next())
						{
							if (link->Object() == focusWin->Owner())
							{
								// Move it to the front of the list.
								for (ZafWindowLink *tLink = link->FirstChild();
									tLink && tLink->Object() != focusWin;
									tLink = tLink->Next())
								{
									if (tLink->Object()->zafRegion.Overlap(focusWin->zafRegion, updateRegion))
										regionList.Allocate(0, updateRegion);
								}
								link->RemoveChild(focusWin);
								link->AddChild(focusWin);
								break;
							}
						}
					else
						for (link = (ZafWindowLink *)ownerWindowList->First();
							link; link = link->Next())
						{
							if (link->Object() == focusWin)
								break;
						}
				}

				ZafWindowObject *end = (link  && link->FirstChild()) ?
					link->FirstChild()->Object() : tFocusObject;
				for (ZafWindowObject *tWindow = First();
					tWindow && tWindow != end; tWindow = tWindow->Next())
				{
					if (tFocusObject->zafRegion.Overlap(tWindow->zafRegion, updateRegion))
						regionList.Allocate(0, updateRegion);
					// Bring all of the owned windows and the owner to the front.
					if (link)
					{
						if (link->Object()->zafRegion.Overlap(tWindow->zafRegion, updateRegion))
						{
							regionList.Allocate(0, updateRegion);
						}
						for (ZafWindowLink *tLink = link->FirstChild();
							tLink; tLink = tLink->Next())
						{
							if (tLink->Object()->zafRegion.Overlap(tWindow->zafRegion,
								updateRegion))
							{
								regionList.Allocate(0, updateRegion);
							}
						}
					}
				}
#endif
				ZafList::Subtract(tFocusObject);

				// If the window gaining focus is not temporary, remove all
				// temporary windows.
				if (!DynamicPtrCast(tFocusObject, ZafWindow)->Temporary())
				{
					ZafWindow *firstWindow = DynamicPtrCast(First(), ZafWindow);

					while (firstWindow && firstWindow->Temporary())
					{
						Subtract(firstWindow);
						if (firstWindow->Destroyable())
							delete firstWindow;
						firstWindow = DynamicPtrCast(First(), ZafWindow);
					}
				}

				ZafList::Add(tFocusObject, First());
#if defined(ZAF_INDEPENDENT)
				// Bring all of the owned windows and the owner to the front.
				if (link)
				{
					ZafList::Subtract(link->Object());
					ZafList::Add(link->Object(), first);
					for (ZafWindowLink *tLink = link->FirstChild();
						tLink; tLink = tLink->Next())
					{
						ZafList::Subtract(tLink->Object());
						ZafList::Add(tLink->Object(), link->Object());
					}
				}

				for (ZafRegionElement *element = regionList.First(); element;
					element = element->Next())
				{
					OSInvalidateRegion(&element->region);
//					tFocusObject->OSInvalidateRegion(&element->region);
				}
#endif
			}

			// Set the current pointer.
			ZafList::SetCurrent(tFocusObject);
			focus = true;
		}
		else
		{
			windowManager->focusObject = ZAF_NULLP(ZafWindowObject);
			ZafWindow::NotifyFocus(tFocusObject, setFocus);
		}
	}

	// Validation is not performed at the window manager level.
	return (invalidObject);
}

bool ZafWindowManager::SetAcceptDrop(bool )
{
	// acceptDrop is false for this class.
	acceptDrop = false;
	return (acceptDrop);
}

bool ZafWindowManager::SetAutomaticUpdate(bool )
{
	// automaticUpdate is true for this class.
	automaticUpdate = true;
	return (automaticUpdate);
}

bool ZafWindowManager::SetBordered(bool )
{
	// bordered is false for this class.
	bordered = false;
	return (bordered);
}

ZafExitFunction ZafWindowManager::SetExitFunction(ZafExitFunction tExitFunction)
{
	exitFunction = tExitFunction;
	return (exitFunction);
}

bool ZafWindowManager::SetNormalHotKeys(bool )
{
	// normalHotKeys is false for this class.
	normalHotKeys = false;
	return (normalHotKeys);
}

bool ZafWindowManager::SetOSDraw(bool )
{
	// osDraw is true for this class.
	osDraw = true;
	return (osDraw);
}

bool ZafWindowManager::SetParentDrawBorder(bool )
{
	// parentDrawBorder is false for this class.
	parentDrawBorder = false;
	return (parentDrawBorder);
}

bool ZafWindowManager::SetParentDrawFocus(bool )
{
	// parentDrawFocus is false for this class.
	parentDrawFocus = false;
	return (parentDrawFocus);
}

bool ZafWindowManager::SetParentPalette(bool )
{
	// parentPalette is false for this class.
	parentPalette = false;
	return (parentPalette);
}

ZafRegionType ZafWindowManager::SetRegionType(ZafRegionType )
{
	// regionType is ZAF_AVAILABLE_REGION for this class.
	regionType = ZAF_AVAILABLE_REGION;
	return (regionType);
}

ZafSelectionType ZafWindowManager::SetSelectionType(ZafSelectionType )
{
	// selectionType is ZAF_SINGLE_SELECTION for this class.
	selectionType = ZAF_SINGLE_SELECTION;
	return (selectionType);
}

bool ZafWindowManager::SetTemporary(bool )
{
	// temporary is false for this class.
	temporary = false;
	return (temporary);
}

