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

#define OSWindow ZafMSWindowsApp
#include <w_app.hpp>
#include <z_mdiwin.hpp>

LRESULT CallDefFrameProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	// Get a pointer to the Zinc window.
	ZafWindow *window = (ZafWindow *)ZafMSWindowsApp::ObjectFromHandle(hwnd);

	HWND clientHandle = window ? window->screenID : 0;
	return DefFrameProc(hwnd, clientHandle, wMsg, wParam, lParam);
//	return DefFrameProc(hwnd, window ? window->screenID : 0, wMsg, wParam, lParam);
}

LRESULT CALLBACK ZafMSWindowsApp::MDIFrameJumpProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	switch (wMsg)
	{
	case WM_COMMAND:
		{
#if defined(ZAF_WIN32)
		WORD wID = LOWORD(wParam);
#else
		WORD wID = wParam;
#endif
		if (wID >= 0xF000)
			return (CallDefFrameProc(hwnd, wMsg, wParam, lParam));
		else
			return FrameProc(CallDefFrameProc, hwnd, wMsg, wParam, lParam);
		}

	case WM_SIZE:
		return (0);

	default:
		return FrameProc(CallDefFrameProc, hwnd, wMsg, wParam, lParam);
	}
}

LRESULT CallDefMDIChildProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	return DefMDIChildProc(hwnd, wMsg, wParam, lParam);
}

LRESULT CALLBACK ZafMSWindowsApp::MDIChildJumpProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	switch (wMsg)
	{
	case WM_SYSCOMMAND:
		{
		if ((WORD)(wParam & 0xFFF0) == SC_RESTORE)
		{
			// Get a pointer to the Zinc window.
			ZafWindow *window = DynamicPtrCast(ObjectFromHandle(hwnd), ZafWindow);

			// If minimized the operation can be handled normally.
			if (window->Minimized())
				break;

			// On restore, reset the maximize Zinc attribute for all maximized
			// MDI child windows, otherwise the OS may try to maximize the
			// child on its next update.
			ZafWindowObject *nObj = window->Next();
			while (nObj)
			{
				if (DynamicPtrCast(nObj, ZafWindow)->Maximized())
					nObj->Event(S_MDI_RESTORE);
				nObj = nObj->Next();
			}
		}
		}
		break;

	case WM_CLOSE:
		{
		ZafWindow *window = DynamicPtrCast(ObjectFromHandle(hwnd), ZafWindow);

		// Prevent the close operation and post an S_MDI_CLOSE event.
		ZafEventStruct closeEvent(S_MDI_CLOSE);
		closeEvent.windowObject = window;
		window->eventManager->Put(closeEvent);
		}
		return (0);

	case WM_MDIACTIVATE:
		FrameProc(CallDefMDIChildProc, hwnd, wMsg, wParam, lParam);
#if defined(ZAF_WIN32)
		if ((HWND)lParam == hwnd)
#else
		if ((HWND)LOWORD(lParam) == hwnd)
#endif
		{
			ZafWindow *window = DynamicPtrCast(ObjectFromHandle(hwnd), ZafWindow);
			if (window)
				window->NotifyFocus(window, true);
		}
		return 0;
	}

	return FrameProc(CallDefMDIChildProc, hwnd, wMsg, wParam, lParam);
}

// ----- ZafMDIWindow -----------------------------------------------------

ZafEventMap ZAF_FARDATA ZafMDIWindow::defaultEventMap[] =
{
	{ L_NONE, 0, 0, 0 }
};

ZafWindowObject *ZafMDIWindow::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	if (mdiType == ZAF_MDI_PARENT)
	{
		ZafWindowObject *positionObject = position;
		if (!position && !object->SupportObject())
			positionObject = First();
		ZafWindowObject *retObj = ZafWindow::Add(object, positionObject);
		return (retObj);
	}
	else
		return ZafWindow::Add(object, position);
}

ZafPositionStruct ZafMDIWindow::ConvertToOSPosition(const ZafWindowObject *object,
	const ZafPositionStruct *zafPosition) const
{
	return (ZafWindow::ConvertToOSPosition(object, zafPosition));
}

ZafPositionStruct ZafMDIWindow::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *osPosition) const
{
	return (ZafWindow::ConvertToZafPosition(object, osPosition));
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafMDIWindow::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	return ZafWindow::Draw(event, ccode);
}
#endif

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

	// Process Windows Messages.
	if (ccode == E_OSEVENT)
		return ZafMDIWindow::OSEvent(event);

	switch (ccode)
	{
	case S_CREATE:
		ccode = ZafWindow::Event(event);
		Event(N_SIZE);
		break;

	case S_INITIALIZE:
		// Force the correct mdiType (for persistence and programmer mistakes).
		if (parent && mdiType == ZAF_MDI_PARENT)
			mdiType = ZAF_MDI_CHILD;
		else if (!parent && mdiType == ZAF_MDI_CHILD)
			mdiType = ZAF_MDI_PARENT;

		if (mdiType == ZAF_MDI_PARENT)
			ccode = ZafWindow::Event(event);
		else
		{
			// Default to the base class.  Bypass ZafWindow since it will 
			// disable root windows if this window is modal.
			ccode = ZafWindowObject::Event(event);

			//??? This should be handled in ConvertCoordinates.
			ConvertRegion(clientRegion, ZAF_PIXEL);

			// Broadcast to all of the children.
			BroadcastEvent(event);
		}
		break;

	case S_COMPUTE_SIZE:
		ccode = ZafWindow::Event(event);

		// This is not really the place to do this, but have to disable other
		// child windows after all children have been registered.
		if (mdiType == ZAF_MDI_CHILD && modal)
		{
			// Disable other windows if modal.
			for (ZafWindowObject *object = Next(); object; object = object->Next())
					EnableWindow(object->OSScreenID(ZAF_FRAMEID), (BOOL)false);
		}
		break;

	case S_MDI_MAXIMIZE:
		if (MDIType() == ZAF_MDI_CHILD)
			SetMaximized(true);
		else if (Current())
			Current()->Event(event);
		break;

	case S_MDI_MINIMIZE:
		if (MDIType() == ZAF_MDI_CHILD)
			SetMinimized(true);
		else if (Current())
			Current()->Event(event);
		break;

	case S_MDI_RESTORE:
		if (MDIType() == ZAF_MDI_CHILD)
		{
			maximized = minimized = false;
			SendMessage(parent->screenID, WM_MDIRESTORE, (WPARAM)OSScreenID(ZAF_FRAMEID), (LPARAM)0);
		}
		else if (Current())
			Current()->Event(event);
		break;

	case S_MDI_NEXT_WINDOW:
		if (MDIType() == ZAF_MDI_PARENT && Current())
			SendMessage(screenID, WM_MDINEXT, (WPARAM)Current()->OSScreenID(ZAF_FRAMEID), (LPARAM)0);
		break;

	case S_MDI_CASCADE_WINDOWS:
		if (MDIType() == ZAF_MDI_PARENT)
		{
			ZafWindow *child = DynamicPtrCast(Current(), ZafWindow);
			if (child->Maximized())
				child->SetMaximized(false);

			// Restore any minimized children
			for (child = DynamicPtrCast(First(), ZafWindow);
			 child; child = DynamicPtrCast(child->Next(), ZafWindow))
				child->SetMinimized(false);
			SendMessage(screenID, WM_MDICASCADE, (WPARAM)0, (LPARAM)0);
		}
		break;

	case S_MDI_TILE_WINDOWS:
		if (MDIType() == ZAF_MDI_PARENT)
		{
			ZafWindow *child = DynamicPtrCast(Current(), ZafWindow);
			if (child->Maximized())
				child->SetMaximized(false);

			// Restore any minimized children
			for (child = DynamicPtrCast(First(), ZafWindow);
			 child; child = DynamicPtrCast(child->Next(), ZafWindow))
				child->SetMinimized(false);
			SendMessage(screenID, WM_MDITILE, (WPARAM)MDITILE_HORIZONTAL, (LPARAM)0);
		}
		break;

	case S_DESTROY:
		if (mdiType == ZAF_MDI_PARENT)
			ccode = ZafWindow::Event(event);
		else if (screenID)
		{
			// Re-enable other windows if modal.
			if (Modal())
			{
				HWND hWnd = GetWindow(OSScreenID(ZAF_FRAMEID), GW_HWNDNEXT);
				ZafWindow *window = hWnd ? DynamicPtrCast(ZafMSWindowsApp::ObjectFromHandle(hWnd), ZafWindow) : ZAF_NULLP(ZafWindow);
				while (window)
				{
					EnableWindow(hWnd, (BOOL)!window->Disabled());
					if (window->Modal())
						break;
					hWnd = GetWindow(hWnd, GW_HWNDNEXT);
					window = hWnd ? DynamicPtrCast(ZafMSWindowsApp::ObjectFromHandle(hWnd), ZafWindow) : ZAF_NULLP(ZafWindow);
				}
			}

			// 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.)
			SendMessage(parent->screenID, WM_MDIDESTROY, (WPARAM)OSScreenID(ZAF_FRAMEID), (LPARAM)0);

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

	case S_REGISTER_OBJECT:
		// Register the object.
		ccode = ZafWindowObject::Event(event);

		// Register all of the children.
		if (mdiType == ZAF_MDI_PARENT)
		{
			// Broadcast the event to all children.
			ZafWindowObject *object;
			for (object = SupportFirst(); object; object = object->Next())
				object->Event(event);
			for (object = Last(); object; object = object->Previous())
				object->Event(event);
		}
		else
			BroadcastEvent(event);
		break;

	case S_MDI_CLOSE:
		// Get a pointer to the window to be closed.
		{
		ZafWindow *closeWindow;
		ZafWindowObject *object = event.windowObject;
		if (object && object->parent == this)
			// The window to be closed was provided with the event.
			closeWindow = DynamicPtrCast(object, ZafWindow);
		else
			// Get the first window.
			closeWindow = DynamicPtrCast(Current(), ZafWindow);

		// Make sure it is ok to close the window.
		if (closeWindow && !closeWindow->Locked() &&
			(closeWindow->Event(ZafEventStruct(N_CLOSE)) == 0))
		{
			Subtract(closeWindow);
			if (closeWindow->Destroyable())
				delete closeWindow;
		}
		}
		break;

	default:
		ccode = ZafWindow::Event(event);
	}

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

void ZafMDIWindow::GetClip(ZafWindowObject *object, ZafRegionStruct &clip,
	ZafRegionStruct &virtualRegion)
{
	ZafWindow::GetClip(object, clip, virtualRegion);
}

ZafEventType ZafMDIWindow::MoveEvent(const ZafEventStruct &)
{
	return S_UNKNOWN;
}

ZafWindowObject *ZafMDIWindow::NotifyFocus(ZafWindowObject *object, bool setFocus)
{
	if (object != this && MDIType() == ZAF_MDI_PARENT && setFocus)
	{
		SendMessage(screenID, WM_MDIACTIVATE, (WPARAM)object->OSScreenID(ZAF_FRAMEID), (LPARAM)0);
		ZafWindowObject *invalidObject = ZafWindow::NotifyFocus(object, setFocus);
		if (!invalidObject && Index(object) != -1)
		{
			ZafList::Subtract(object);
			ZafList::Add(object, First());
			ZafList::SetCurrent(object);
		}

		return (invalidObject);
	}

	return (ZafWindow::NotifyFocus(object, setFocus));
}

ZafEventType ZafMDIWindow::ScrollEvent(const ZafEventStruct &event)
{
	return (ZafWindow::ScrollEvent(event));
}

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

bool ZafMDIWindow::SetMaximized(bool setMaximized)
{
	if (maximized != setMaximized)
	{
		if (MDIType() == ZAF_MDI_PARENT)
			return (ZafWindow::SetMaximized(setMaximized));
		else
		{
			maximized = setMaximized;

			if (screenID)
				SendMessage(parent->screenID, maximized ? WM_MDIMAXIMIZE : WM_MDIRESTORE,
					(WPARAM)OSScreenID(ZAF_FRAMEID), (LPARAM)0);
		}
	}

	return (maximized);
}

bool ZafMDIWindow::SetMinimized(bool setMinimized)
{
	// Defer to the base class.
	return (ZafWindow::SetMinimized(setMinimized));
}

bool ZafMDIWindow::SetVisible(bool visible)
{
	return (ZafWindow::SetVisible(visible));
}

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

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

	switch (ccode)
	{
	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_HSCROLL:
	case WM_VSCROLL:
		if (parent)
			ccode = ZafWindow::OSEvent(event);
		else if ( event.osEvent.wParam != SB_ENDSCROLL )
			ccode = DefaultCallback(event);
		break;

	default:
		ccode = ZafWindow::OSEvent(event);
	}

	return ccode;
}

void ZafMDIWindow::OSRegisterObject(void)
{
	DWORD dwFrameStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	DWORD dwFrameExStyle = 0;
	DWORD dwClientStyle = WS_VISIBLE | WS_CHILD;
	if (!parent)
		dwClientStyle |= MDIS_ALLCHILDSTYLES | WS_CLIPCHILDREN;
	if (visible)
		dwFrameStyle |= WS_VISIBLE;
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
	{
		if (object->NumberID() == ZAF_NUMID_BORDER)
		{
			if (Sizeable())
				dwFrameStyle |= WS_SIZEBOX;
			else
				dwFrameStyle |= WS_BORDER;
		}
		else if (object->NumberID() == ZAF_NUMID_MAXIMIZE)
			dwFrameStyle |= WS_MAXIMIZEBOX;
		else if (object->NumberID() == ZAF_NUMID_MINIMIZE)
			dwFrameStyle |= WS_MINIMIZEBOX;
		else if (object->NumberID() == ZAF_NUMID_TITLE)
			dwFrameStyle |= WS_CAPTION;
		else if (object->NumberID() == ZAF_NUMID_HZ_SCROLL)
			dwClientStyle |= WS_HSCROLL;
		else if (object->NumberID() == ZAF_NUMID_VT_SCROLL)
			dwClientStyle |= WS_VSCROLL;
		else if (object->NumberID() == ZAF_NUMID_SYSTEM)
			dwFrameStyle |= WS_SYSMENU;
	}

	if (Bordered() && !(dwFrameStyle & (WS_SIZEBOX | WS_BORDER)))
	{
#if defined(ZAF_MSWINDOWS_3D)
		if (parent)
			dwFrameExStyle |= WS_EX_CLIENTEDGE;
		else
			dwFrameStyle |= WS_BORDER;
#else
		dwFrameStyle |= WS_BORDER;
#endif
	}

	if (!parent && !(dwFrameStyle & WS_CAPTION))
		dwFrameStyle |= WS_POPUP;

	if (!parent)
	{
		// Create the frame window.
		HWND hwndOwner = ZAF_NULLH(HWND);
		if (Owner() && Owner()->screenID)
			hwndOwner = Owner()->OSScreenID(ZAF_FRAMEID);
		frameID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_MDI_FRAME_CLASS, dwFrameStyle, NULL, hwndOwner, dwFrameExStyle);

		// Re-direct the frame window's "window procedure."
		SetWindowLong(frameID, GWL_WNDPROC, (LONG)ZafMSWindowsApp::MDIFrameJumpProc);

		// Create the client window.
		CLIENTCREATESTRUCT clientCreateStruct;
		clientCreateStruct.hWindowMenu = 0;
		clientCreateStruct.idFirstChild = 0x1000;
		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this,
			ZAF_ITEXT("MDICLIENT"), dwClientStyle,
			ZAF_NULLP(ZafIChar), frameID,
			WS_EX_NOPARENTNOTIFY, 0, &clientCreateStruct);
	}
	else
	{
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
		MDICREATESTRUCTA mdiCreateStruct;
#else
		MDICREATESTRUCT mdiCreateStruct;
#endif
		mdiCreateStruct.szClass = ZAF_MDI_CHILD_CLASSA;
		mdiCreateStruct.szTitle = 0;
		mdiCreateStruct.hOwner = ZafMSWindowsApp::hInstance;
		mdiCreateStruct.x = 0;
		mdiCreateStruct.y = 0;
		mdiCreateStruct.cx = 0;
		mdiCreateStruct.cy = 0;
		mdiCreateStruct.style = dwFrameStyle;
		mdiCreateStruct.lParam = 0;

#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
		frameID = (HWND)SendMessageA(parent->screenID, WM_MDICREATE, (WPARAM)0, (LPARAM)&mdiCreateStruct);
#else
		frameID = (HWND)SendMessage(parent->screenID, WM_MDICREATE, (WPARAM)0, (LPARAM)&mdiCreateStruct);
#endif

#if defined(ZAF_WIN32)
		SetWindowLong(frameID, GWL_USERDATA, (LONG)this);
#else
		SetProp(frameID, (LPCSTR)ZafMSWindowsApp::objectLowWord, (HANDLE)LOWORD(this));
		SetProp(frameID, (LPCSTR)ZafMSWindowsApp::objectHighWord, (HANDLE)HIWORD(this));
#endif

		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwClientStyle, ZAF_NULLP(ZafIChar), frameID);

		// Set the "z-order."
		if (next && Next()->screenID)
		{
			HWND hwndPrevious = (previous ? Previous()->OSScreenID(ZAF_FRAMEID) : HWND_TOP);
			SetWindowPos(frameID, hwndPrevious, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);
		}

		// Update Active child.
		HWND hwndFirst = GetWindow(parent->screenID, GW_CHILD);
#if defined(ZAF_WIN32)
		HWND hwndActive = (HWND)SendMessage(parent->screenID, WM_MDIGETACTIVE, (WPARAM)0, (LPARAM)0);
#else
		HWND hwndActive = (HWND)LOWORD(SendMessage(parent->screenID, WM_MDIGETACTIVE, (WPARAM)0, (LPARAM)0));
#endif
		if (hwndActive != hwndFirst)
			SendMessage(parent->screenID, WM_MDIACTIVATE, (WPARAM)hwndFirst, (LPARAM)0);
	}
}

void ZafMDIWindow::OSSize(void)
{
	ZafWindow::OSSize();

	if (maximized && this == DynamicPtrCast(parent, ZafWindow)->First())
		SendMessage(parent->screenID, WM_MDIMAXIMIZE, (WPARAM)(HWND)frameID, (LPARAM)0);
	if (minimized)
		ShowWindow(frameID, SW_SHOWMINIMIZED);
}

void ZafMDIWindow::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Get default colors from the operating system.
	if (type == ZAF_PM_BACKGROUND)
	{
		if (mdiType == ZAF_MDI_PARENT)
		{
			if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
				palette.osPalette.colorForeground = GetSysColor(COLOR_APPWORKSPACE);
			if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
				palette.osPalette.colorBackground = GetSysColor(COLOR_APPWORKSPACE);
		}
		else
		{
			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
		ZafWindow::OSMapPalette(palette, type, state);
}
