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

#include <z_sclwin.hpp>
#include <z_utils.hpp>
#include "m_win.hpp"

// ----- ZafScrolledWindow --------------------------------------------------

ZafPositionStruct ZafScrolledWindow::ConvertToOSPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	// Defer to the base class.
	return (ZafWindow::ConvertToOSPosition(object, position));
}

ZafPositionStruct ZafScrolledWindow::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	// Defer to the base class.
	return (ZafWindow::ConvertToZafPosition(object, position));
}

ZafEventType ZafScrolledWindow::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	// ----- Position & Size messages -----
	case S_INITIALIZE:
		ccode = ZafWindow::Event(event);
		ConvertRegion(scrollRegion, zafRegion.coordinateType);
		break;

	case N_SIZE:
		// Defer to the base class.
		ccode = ZafWindow::Event(event);

		// Adjust the scroll information.
		ScrollEvent(S_VSCROLL_COMPUTE);
		ScrollEvent(S_HSCROLL_COMPUTE);

		// Adjust the scroll region.
		if (scrollRegion.left < 0 && scrollRegion.right < clientRegion.Width() - 1)
		{
			ZafEventStruct scrollEvent(S_HSCROLL);
			scrollEvent.scroll.delta = -ZafMin(-scrollRegion.left, clientRegion.Width() - scrollRegion.right - 1);
			Event(scrollEvent);
		}

		if (scrollRegion.top < 0 && scrollRegion.bottom < clientRegion.Height() - 1)
		{
			ZafEventStruct scrollEvent(S_VSCROLL);
			scrollEvent.scroll.delta = -ZafMin(-scrollRegion.top, clientRegion.Height() - scrollRegion.bottom - 1);
			Event(scrollEvent);
		}
		break;

	default:
		// Defer to the base class.
		ccode = ZafWindow::Event(event);
		break;
	}

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

void ZafScrolledWindow::GetClip(ZafWindowObject *object, ZafRegionStruct &clip, ZafRegionStruct &virtualRegion)
{
	// Defer to the base class.
	ZafWindowObject::GetClip(object, clip, virtualRegion);
}

ZafEventType ZafScrolledWindow::ScrollEvent(const ZafEventStruct &event)
{
	// Process the scrolling request.  Scrolling at the window level is 
	// based on pixel boundaries.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case S_VSCROLL:
	case S_HSCROLL:
		{
		// Scroll the window contents <delta> pixels.
		ZafScrollBar *vtScrollBar = VerticalScrollBar();
		ZafScrollBar *hzScrollBar = HorizontalScrollBar();
		if (ccode == S_VSCROLL && vtScrollBar)
		{
			ZafScrollStruct vScroll = *vtScrollBar->ScrollData();
			vScroll.current += event.scroll.delta;
			vtScrollBar->ScrollData()->SetScroll(vScroll);
		}
		else if (ccode == S_HSCROLL && hzScrollBar)
		{
			ZafScrollStruct hScroll = *hzScrollBar->ScrollData();
			hScroll.current += event.scroll.delta;
			hzScrollBar->ScrollData()->SetScroll(hScroll);
		}
		}
		// Continue to N_VSCROLL and N_HSCROLL.

	case N_VSCROLL:
	case N_HSCROLL:
		if (screenID)
		{
			// Scroll the window contents <delta> cells in the direction 
			// indicated by the type of scrolling operation.
			ZafScrollBar *vtScrollBar = VerticalScrollBar();
			ZafScrollBar *hzScrollBar = HorizontalScrollBar();
			XtMoveWidget(screenID,
				hzScrollBar ? (int)-hzScrollBar->ScrollData()->Current() : 0,
				vtScrollBar ? (int)-vtScrollBar->ScrollData()->Current() : 0);
		
			if (vtScrollBar)
			{
				scrollRegion.VtShift((int)(-scrollRegion.top -
				 vtScrollBar->ScrollData()->Current()));
			}
			if (hzScrollBar)
				scrollRegion.HzShift((int)(-scrollRegion.left -
 				 hzScrollBar->ScrollData()->Current()));
		}
		break;

	case S_VSCROLL_CHECK:
	case S_HSCROLL_CHECK:
		// Check the current object to ensure it is currently visible within
		// the window.  If not, scroll the window so the object appears on 
		// the screen.
		break;

	case S_VSCROLL_COMPUTE:
		{
		// Re-compute the scrolling values for the window.
		ZafScrollBar *vtScrollBar = VerticalScrollBar();
		if (vtScrollBar)
		{
			ZafScrollStruct vScroll = *vtScrollBar->ScrollData();
			vScroll.minimum = 0;
			vScroll.maximum = scrollRegion.Height() - clientRegion.Height();
			vScroll.delta = display->cellHeight;
			vScroll.current = -scrollRegion.top;
			vScroll.showing = clientRegion.Height();
			vtScrollBar->ScrollData()->SetScroll(vScroll);
		}

		// Resize the sub-window.
		if (screenID && XtIsRealized(screenID))
			XtConfigureWidget(screenID, scrollRegion.left, scrollRegion.top,
				(scrollRegion.Width() > clientRegion.Width()) ? scrollRegion.Width() : clientRegion.Width(),
				(scrollRegion.Height() > clientRegion.Height()) ? scrollRegion.Height() : clientRegion.Height(),
				0);
		}
		break;

	case S_HSCROLL_COMPUTE:
		{
		// Re-compute the scrolling values for the window.
		ZafScrollBar *hzScrollBar = HorizontalScrollBar();
		if (hzScrollBar)
		{
			ZafScrollStruct hScroll = *hzScrollBar->ScrollData();
			hScroll.minimum = 0;
			hScroll.maximum = scrollRegion.Width() - clientRegion.Width();
			hScroll.delta = display->cellWidth;
			hScroll.current = -scrollRegion.left;
			hScroll.showing = clientRegion.Width();
			hzScrollBar->ScrollData()->SetScroll(hScroll);
		}

		// Resize the sub-window.
		if (screenID && XtIsRealized(screenID))
			XtConfigureWidget(screenID, scrollRegion.left, scrollRegion.top,
				(scrollRegion.Width() > clientRegion.Width()) ? scrollRegion.Width() : clientRegion.Width(),
				(scrollRegion.Height() > clientRegion.Height()) ? scrollRegion.Height() : clientRegion.Height(),
				0);
		}
		break;

	default:
		// No other messages apply to the scrolling window.
		return (S_ERROR);
	}

	// Return the control code.
	return (event.type);
}

ZafWindowObject *ZafScrolledWindow::NotifyFocus(ZafWindowObject *object, bool setFocus)
{
	// Defer to the base class.
	return (ZafWindow::NotifyFocus(object, setFocus));
}

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

ZafEventType ZafScrolledWindow::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

void ZafScrolledWindow::OSRegisterObject(void)
{
	// Levels 1 & 2, defer to the base class.
	ZafWindow::OSRegisterObject();
	// Level 3 - Attach window scrolling.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = XtVaCreateManagedWidget(name,
		(WidgetClass)&MtfWindowClient::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNresizePolicy, XmRESIZE_NONE,
		XmNtraversalOn, Noncurrent() ? false : true,
		XmNx, 0,
		XmNy, 0,
		XmNmarginHeight, 0,
		XmNmarginWidth, 0,
		XmNwidth, (scrollRegion.Width() > zafRegion.Width()) ? scrollRegion.Width() : zafRegion.Width(),
		XmNheight, (scrollRegion.Height() > zafRegion.Height()) ? scrollRegion.Height() : zafRegion.Height(),
		0);

	// Set uninitialized variables.
	MtfWindowClient *mtfObject = MtfWindowClient::GetMotifWidget(screenID);
	mtfObject->manager.has_focus = false;
}

OSWindowID ZafScrolledWindow::OSScreenID(ZafScreenIDType type) const
{
	// Return the frame widget.
	if (!screenID)
		return (screenID);
	else if (type == ZAF_FRAMEID)
		return (XtParent(XtParent(screenID)));
	else if (type == ZAF_SHELLID)
	{
		if (parent)
			return (parent->OSScreenID(type));
		return (XtParent(XtParent(XtParent(screenID))));
	}
	return (screenID);
}

void ZafScrolledWindow::OSSize(void)
{
	// Defer to the base class.
	ZafWindow::OSSize();
}

