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

#include <z_string.hpp>
#include <z_htips.hpp>
#include "m_win.hpp"

// ----- MtfWindowShell widget definition -----------------------------------

XmOffsetPtr MtfWindowShell::offsets;

MtfWindowShellClassRec MtfWindowShell::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&topLevelShellClassRec,	// superclass
		"ZafWindowShell",						// class_name
		sizeof(MtfWindowShellPart),					// widget_size (data part)
		MtfWindowShell::classInitialize,		// class_initialize
		0,										// class_part_initialize
		false,									// class inited
		0,										// initialize
		0,										// initialize_hook
		XtInheritRealize,						// realize
		0,										// actions
		0,										// num_actions
		0, 										// resources
		0, 										// num_resources
		NULLQUARK,								// xrm
		false,									// compress_motion
		true,									// compress_exposure
		false,									// compress_enterleave
		false,									// visible_interest
		0, 										// destroy
		XtInheritResize,						// resize
		XtInheritExpose,						// expose
		0,										// set_values
		0, 										// set_values_hook
		XtInheritSetValuesAlmost,				// set_values_almost
		0, 										// get_values_hook
		XtInheritAcceptFocus,					// accept_focus
		XtVersionDontCheck,					// version
		0, 										// callback_private
		XtInheritTranslations,					// tm_table
		XtInheritQueryGeometry, 				// query_geometry
		XtInheritDisplayAccelerator,			// display_accelerator
		0 										// extension
	},
	// --- CompositeClassPart ---
	{
		XtInheritGeometryManager,				// geometry_manager
		XtInheritChangeManaged,					// change_managed
		XtInheritInsertChild,					// insert_child
		XtInheritDeleteChild,					// delete_child
		0										// extension
	},
	// --- ShellClassPart ---
	{
		0										// extension
	},
	// --- WMShellClassPart ---
	{
		0										// extension
	},
	// --- VendorShellClassPart ---
	{
		0										// extension
	},
	// --- TopLevelShellClassPart ---
	{
		0										// extension
	},
	// --- MtfWindowShellClassPart ---
	{
		0									// extension
	}
};

void MtfWindowShell::callback(MtfWindowShell *shell, ZafWindow *window, XEvent *xEvent, Boolean *)
{
	// Check the new state of the window. The Unmap notification is only
	// sent when the window is minimized.
	if (xEvent->type == ConfigureNotify)
	{
		// Get the border and title size.
		if (!BorderSizeInitialized((Widget)shell) 
			|| !TitleSizeInitialized((Widget)shell))
		{
			if (!window->Border())
			{
				BorderSizeInitialized((Widget)shell) = true;
				BorderSize((Widget)shell) = 0;
			}
			if (!window->Title())
			{
				TitleSizeInitialized((Widget)shell) = true;
				TitleSize((Widget)shell) = 0;
			}
			int osLeft = window->zafRegion.left;
			int osTop = window->zafRegion.top;

			if (!osLeft)
				osLeft = 1;
			if (!osTop)
				osTop = 1;

			Window rootWin, thisWin, clientWin; 
			int left, top, transLeft, transTop; 
			int clientLeft, clientTop, clientTransLeft, clientTransTop;
			unsigned int width, height, borderWidth, depth; 
			unsigned int clientWidth, clientHeight;

			thisWin = XtWindow(window->OSScreenID());
			OSDisplayContext displayContext = ((ZafScreenDisplay *)window->Display())->xDisplay;
			XGetGeometry(displayContext, thisWin, &rootWin, &left, &top, 
				&width, &height, &borderWidth, &depth);
			XTranslateCoordinates(displayContext, thisWin, rootWin, 0, 0, &transLeft,
				&transTop, &clientWin);
			
			if (clientWin)
			{
				XGetGeometry(displayContext, clientWin, &rootWin, &clientLeft, &clientTop,
				 &clientWidth, &clientHeight, &borderWidth, &depth);
				XTranslateCoordinates(displayContext, clientWin, rootWin, 0, 0,
				 &clientTransLeft, &clientTransTop, &thisWin);
			}
			else
			{
				BorderSize((Widget)shell) = 0;
				BorderSizeInitialized((Widget)shell) = true;
				TitleSize((Widget)shell) = 0;
				TitleSizeInitialized((Widget)shell) = true;
			}

			// Compensate for nonfield support objects.
			ZafRegionStruct cRegion = window->ClientRegion();
			transTop -= cRegion.top;
			transLeft -= cRegion.left;

			if (!BorderSizeInitialized((Widget)shell))
			{
				BorderSize((Widget)shell) = transLeft - clientTransLeft;
				if (transLeft <= osLeft)
					BorderSize((Widget)shell) = 0;
				BorderSizeInitialized((Widget)shell) = true;

				if (BorderSize((Widget)shell) > osLeft)
					BorderSizeInitialized((Widget)shell) = false;
			}

			if (!TitleSizeInitialized((Widget)shell))
			{
				TitleSize((Widget)shell) = transTop - clientTransTop;

				if (transTop <= osTop)
					TitleSize((Widget)shell) = 0;

				TitleSizeInitialized((Widget)shell) = true;
				if (TitleSize((Widget)shell) > osTop)
					TitleSizeInitialized((Widget)shell) = false;
			}
		}		

		// Adjust the window region.
		int oldLeft = window->zafRegion.left;
		int oldTop = window->zafRegion.top;
		if (!oldLeft)
			oldLeft = 1;
		if (!oldTop)
			oldTop = 1;

		window->zafRegion.left = shell->core.x - BorderSize((Widget)shell);
		window->zafRegion.right = shell->core.x + shell->core.width -
			BorderSize((Widget)shell) - 1;
		window->zafRegion.bottom = shell->core.y + shell->core.height - 1;
		if (window->Title())
		{
			window->zafRegion.top = shell->core.y - TitleSize((Widget)shell);
			window->zafRegion.bottom -= TitleSize((Widget)shell);
		}
		else
			window->zafRegion.top = shell->core.y;

		if (!TitleSizeInitialized((Widget)shell) && TitleSize((Widget)shell))
		{
			if (window->zafRegion.top < oldTop)
			{
				window->zafRegion.VtShift(TitleSize((Widget)shell));
				TitleSize((Widget)shell) = 0;
			}
			TitleSizeInitialized((Widget)shell) = true;
		}
		if (!BorderSizeInitialized((Widget)shell) && BorderSize((Widget)shell))
		{
			if (window->zafRegion.left < oldLeft)
			{
				window->zafRegion.HzShift(BorderSize((Widget)shell));
				BorderSize((Widget)shell) = 0;
			}
			BorderSizeInitialized((Widget)shell) = true;
		}

		window->Event(N_SIZE);

		// Try and determine if Window Was maximized or restored from Max state.
	 	if (2 * shell->core.x + shell->core.width == window->display->columns &&
	 	 shell->core.x + shell->core.y + shell->core.height == window->display->lines &&
		 !window->Maximized()) 
	 	{
	 		window->SetMaximized(true);
	 		window->Event(N_MAXIMIZE);
	 	}
	 	else if (window->Maximized() && 
	   (2 * shell->core.x + shell->core.width != window->display->columns ||
	 	 shell->core.x + shell->core.y + shell->core.height != window->display->lines))
		{
			window->SetMaximized(false);
			window->Event(N_RESTORE);
		}
	}
	else if (xEvent->type == UnmapNotify)
	{
		window->SetMinimized(true);
		window->Event(N_MINIMIZE);
	}
	else if (xEvent->type == MapNotify || xEvent->type == VisibilityNotify)
	{
		window->SetMinimized(false);
		window->SetMaximized(false);
		window->Event(N_RESTORE);
	}
	else if (xEvent->type == FocusOut && xEvent->xfocus.detail == NotifyNonlinear)
	{
		window->windowManager->focusObject = ZAF_NULLP(ZafWindowObject);
		window->NotifyFocus(ZAF_NULLP(ZafWindowObject), false);
	}
	else if (xEvent->type == FocusIn)
		window->NotifyFocus(window, true);
}

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

void MtfWindowShell::delete_window(MtfWindowShell *, ZafWindow *window, XtPointer)
{
	ZafEventManager *eventManager = window->eventManager;
	ZafWindowManager *windowManager = window->windowManager;

	// Check for a deleted help object.
	if (windowManager->helpObject && windowManager->helpObject->RootObject() == window)
	{
		windowManager->helpObject = ZAF_NULLP(ZafWindowManager);
		windowManager->mouseObject = ZAF_NULLP(ZafWindowManager);
		ZafEventStruct tEvent;
		tEvent.type = DH_SET_HELP_OBJECT;
		tEvent.windowObject = ZAF_NULLP(ZafWindowObject);
		eventManager->Event(tEvent, E_HELPTIPS);
	}

	// Make sure the object is still in the window manager.  This function
	// can be called if the delete originated from either the Zinc or Motif
	// side.
	if (windowManager->Index(window) >= 0)
	{
		// Make sure the window has the focus.
		if (windowManager->First() != window)
			windowManager->NotifyFocus(window, true);

		// Close the window.
		if (window->Temporary())
			eventManager->Put(S_CLOSE_TEMPORARY, Q_BEGIN);
		else
			eventManager->Put(S_CLOSE, Q_BEGIN);
	}
}

