/*	XGView.cpp
 *
 *		This is the implementation of the view workhorse class. This is
 *	a glorified hierarchy of rectangles, which are used to clip against
 *	when drawing is requested.
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XCursorUtil.h>
#include <XDataUtil.h>
#include <XError.h>
#include <XDraw.h>
#include <XView.h>
#include <ctype.h>
#include <XToolTip.h>
#include <XApplication.h>
#include <XWindow.h>
#include <XInline.h>
#include <XScroll.h>


#if OPT_WINOS
extern "C" {
	extern HWND _GMDIWindow;
	extern char _GWindowClass[];
}
#endif

/************************************************************************/
/*																		*/
/*	Internal Support													*/
/*																		*/
/*		TODO: Rewrite in terms of the normal rectangle routines.		*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark Internal Support Routines
#endif

/*	IsEmptyRect
 *
 *		Is this an empty rectangle?
 */

static bool IsEmptyRect( const XRect &r)
{
	return ((r.right <= r.left) || (r.bottom <= r.top));
}

/*	IsPtInRect
 *
 *		Is the point in the rectangle? The portable version
 */

static bool IsPtInRect(XPoint pt, const XRect &r)
{
	if (IsEmptyRect(r)) return false;
	if ((pt.h < r.left) || (pt.h > r.right)) return false;
	if ((pt.v < r.top) || (pt.v > r.bottom)) return false;
	return true;
}

/*	DoIntersect
 *
 *		This calculates the intersection of two rectangles
 */

static bool DoIntersect(XRect &a, XRect &b, XRect *out)
{
	if (a.top < b.top) out->top = b.top;
	else out->top = a.top;
	
	if (a.bottom < b.bottom) out->bottom = a.bottom;
	else out->bottom = b.bottom;
	
	if (a.left < b.left) out->left = b.left;
	else out->left = a.left;
	
	if (a.right < b.right) out->right = a.right;
	else out->right = b.right;
	
	if (IsEmptyRect(*out)) {
		out->left = 0;
		out->top = 0;
		out->right = 0;
		out->bottom = 0;
		return false;
	} else return true;
}

/*	LAbs
 *
 *		Absolute value
 */

inline long LAbs(long x)
{
	return (x > 0) ? x : -x;
}

/************************************************************************/
/*																		*/
/*	View Definition														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Construction/Destruction
#endif

/*$	XGView::XGView
 *
 *		Construct the XGView object that is also the window. This version
 *	of the constructor should not be called except while building the
 *	XGWindow object
 */

XGView::XGView(XGWindow *pThis)
{
	XGSViewInitRecord i;

	i.fViewType = 0;
	
	i.fViewID = 0;
	i.fRefNum = 0;
	
	i.fLockLeft = 0;	
	i.fLockTop = 0;
	i.fLockRight = 0;	
	i.fLockBottom = 0;
	
	i.fAutoLeft = true;
	i.fAutoTop = true;
	i.fAutoRight = true;
	i.fAutoBottom = true;

	i.fVisible = true;
	i.fEnabled = true;
	i.fTabStop = false;
	
	IInitView(NULL,i);

	/*
	 *  Now note that I am my own owner. This is the prelude to
	 *  window initialization.
	 */
	 
	fWindow = pThis;
	fRootWindow = NULL;
}

/*$	XGView::XGView
 *
 *		Construct the XGView object. Normally this version of the
 *	constructor is called internally by YAAF.
 */

XGView::XGView(XGView *parent, XGArgStream &init)
{
	XGSViewInitRecord i;
	
	i.fViewType = init.ReadInteger();
	
	i.fViewID = init.ReadInteger();
	i.fRefNum = init.ReadInteger();
	
	i.fLockLeft = init.ReadBoolean();	
	i.fLockTop = init.ReadBoolean();
	i.fLockRight = init.ReadBoolean();	
	i.fLockBottom = init.ReadBoolean();
	
	i.fAutoLeft = init.ReadInteger();
	i.fAutoTop = init.ReadInteger();
	i.fAutoRight = init.ReadInteger();
	i.fAutoBottom = init.ReadInteger();

	i.fVisible = init.ReadBoolean();
	i.fEnabled = init.ReadBoolean();
	i.fTabStop = init.ReadBoolean();
	
	IInitView(parent,i);
}

/*$	XGView::XGView
 *
 *		Construct the XGView object from the XGSViewInitRecord.
 */

XGView::XGView(XGView *parent, XGSViewInitRecord &i)
{
	IInitView(parent,i);
}

/*$	XGView::~XGView
 *
 *		Delete me. Deletes my children as well.
 */

XGView::~XGView()
{
	XGView *child;
	XGWindow *w;
	
	/*
	 *	Does my window think I have the focus? Update
	 */
	
	w = GetWindow();
	if (w) {
		if (w->fFocus == this) {
			w->fFocus = GetParent();
		}
	}
	
	/*
	 *	I don't want the leave command... (ugly hack)
	 */
	
	if (XGWindow::gLastView == this) {
#if OPT_WINOS
		XGWindow::gLastHWND = 0;
#endif
		XGWindow::gLastView = NULL;
	}
	
	/*
	 *	Invalidate the space where I'm being deleted from
	 */
	
	if (fRootWindow != 0) InvalView();

	/* 
	 *	Delete my children 
	 */

	while (NULL != (child = GetChild())) delete child;
	
	/* 
	 *	Now delete me from my parent 
	 */
	 
	if (fParent) {
		if (fParent->fChild == this) {
			fParent->fChild = fSibling;
		} else {
			for (child = fParent->fChild; child != NULL; child = child->fSibling) {
				if (child->fSibling == this) {
					child->fSibling = fSibling;
					break;
				}
			}
		}
	}
}

/************************************************************************/
/*																		*/
/*	Events																*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Event Stubs
#endif

/*$	XGView::DoDrawView
 *
 *		This method is called by YAAF when this view needs to be redawn.
 *	By default this draws a debugging rectangle.
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoDrawView(XRect)
{
	XRect r;
	char buffer[256];
	
	if (GetChild()) return;						// don't draw if have children
	
	XGDraw draw(this);
	r = GetContentRect();
	draw.Draw3DRect(r,KXGEBackground);			// draw as grey background
	yformat(buffer,"'#I'#L",fViewType,(long)fViewID);
	draw.MoveTo(5,15);
	draw.DrawString(buffer);					// draw the identifier
	draw.Draw3DRect(r,KXGEOutset);				// Frame the border
}

/*$	XGView::DoActivate
 *
 *		Called by YAAF when this view needs to be activated or deactivated.
 *	By default does nothing.
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoActivate(bool)
{
}

/*$	XGView::DoMouseDown
 *
 *		Called by YAAF when the mouse is clicked in this view. By default
 *	this returns 'false'. Return 'true' if your view needs to capture the
 *	mouse for a click/drag cycle.
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

bool XGView::DoMouseDown(XPoint,short)
{
	XGInlineEdit::Close();		// close any open inlines if clicking here.
	return false;
}

/*$	XGView::DoMouseMove
 *
 *		Process mouse move. Sets the cursor to the arrow cursor
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoMouseMove(XPoint,short)
{
	SetCursorID(KNormalCursor);
}

/*$	XGView::DoMouseUp
 *
 *		Process mouse up. Does nothing
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoMouseUp(XPoint,short)
{
}

/*$	XGView::DoMouseEnter
 *
 *		Dispatched when the mouse first enters this view
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoMouseEnter(XPoint,short)
{
}

/*$	XGView::DoMouseLeave
 *
 *		Dispatched when the mouse leaves this view. By default this
 *	closes the tool tip if one has been opened
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoMouseLeave()
{
	XGToolTip::CloseTip();
}

/*$	XGView::DoSizeView
 *
 *		Event sent when the view is resized. Does nothing
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoSizeView()
{
	XGView *foo;
	
	for (foo = fChild; foo != NULL; foo = foo->fSibling) {
		foo->SetLocation(CalcChildPos(foo));
	}
}

/*$	XGView::DoMoveView
 *
 *		Event sent when the view is moved. Does nothing
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoMoveView()
{
}

/*$	XGView::DoShowHide
 *
 *		When the view is either shown or hidden, this is sent
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoShowHide()
{
	XGView *view;
	
	if (!IsVisible() && HasFocus()) {
		fWindow->SetFocus();		// invisible windows don't have focus
	}
	
	for (view = fChild; view != NULL; view = view->fSibling) {
		view->DoShowHide();
	}
}

/*$	XGView::DoEnable
 *
 *		Sent when the view is enabled or disabled
 *
 *		When creating your own view, you would normally override this
 *	method.
 */

void XGView::DoEnable()
{
}

/*$	XGView::HoldCapture
 *
 *		Called by the DoMouseUp() method to indicate the mouse should
 *	remain captured through the next mouse down/move/up cycle.
 *	(If this is the case, the next mouse down event will not be
 *	received--it will act as if the user never released the mouse
 *	button.)
 */

void XGView::HoldCapture()
{
	XGWindow::HoldCapture();
}

/*$	XGView::SetCapture
 *
 *		Called by the DoMouseDown() method to indicate the mouse should
 *	be captured by this view instead of another view.
 */

void XGView::SetCapture(XGView *v)
{
	XGWindow::SetCapture(v);
}

/************************************************************************/
/*																		*/
/*	Focus Processing													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Focus
#endif

/*$	XGView::SetFocus
 *
 *		Set the focus. This overrides stuff on Windows by setting the
 *	MS Window focus as well.
 */

void XGView::SetFocus(void)
{
	XGWindow *w;
	
	XGDispatch::SetFocus();
	
	w = GetWindow();
	if (w) w->fFocus = this;

#if OPT_WINOS
	if (XGWindow::GetFrontWindow() == fWindow) {
		::SetFocus(fRootWindow);
	}
#endif
}

/************************************************************************/
/*																		*/
/*	View display processing												*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark View attributes
#endif

/*$	XGView::IsVisible
 *
 *		Is this view visible? Only if the parent is also visible
 */
 
bool XGView::IsVisible() const
{
	if (fParent) {
		if (!(fParent->IsVisible())) return false;
	}
	return fVisible;
}

/*$	XGView::ShowView
 *
 *		This shows the view. If the view is hidden, this sends the
 *	'showhide' message and then shows the view
 */

void XGView::ShowView()
{
	if (fVisible) return;
	
	fVisible = true;
	if (IsVisible()) {
		DoShowHide();
		InvalView();
	}
}

/*$ XGView::HideView
 *
 *		This hides the view. If the view is shown, this sends the
 *	'showide' message and hides the view
 */

void XGView::HideView()
{
	if (!fVisible) return;

	bool v = IsVisible();
	fVisible = false;
	if (v) {
		DoShowHide();
		InvalView();
	}
}

/*$ XGView::IsEnabled
 *
 *		Is this view enabled? Only if the parent is also enabled
 */
 
bool XGView::IsEnabled() const
{
	if (fParent) {
		if (!(fParent->IsEnabled())) return false;
	}
	return fEnabled;
}


bool XGView::IsEnabled( short id ) const
{
	const XGView *view = FindViewByID( id );
	if (!view) return false;
	return view->IsEnabled();
}


/*$ XGView::EnableView
 *
 *		Enable this view. If the view is not enabled, this will enable
 *	the view
 */

void XGView::EnableView()
{
	if (fEnabled) return;
	
	fEnabled = true;
	if (IsEnabled()) {
		DoEnable();
		InvalView();
	}
}

/*$ XGView::DisableView
 *
 *		Disable this view.
 */

void XGView::DisableView()
{
	if (!fEnabled) return;

	bool e = IsEnabled();
	fEnabled = false;
	
	XGWindow *w = GetWindow();
	if (w && w->fFocus == this) {
		w->NextTabStop();
	}
	
	if (e) {
		DoEnable();
		InvalView();
	}
}


void XGView::SetEnabled( bool enabled )
{
	if (enabled) {
		EnableView();
	} else {
		DisableView();
	}
}


void XGView::SetEnabled( short id, bool enabled )
{
	XGView *view = FindViewByID( id );
	if (view) view->SetEnabled( enabled );
}

/*$ XGView::IsActive
 *
 *		Am I an active view?
 */

bool XGView::IsActive() const
{
#if OPT_MACOS
	if (fRootWindow == NULL) return false;
	return (((WindowPeek)fRootWindow)->hilited) ? true : false;
#endif

#if OPT_WINOS
	if (fRootWindow == NULL) return false;
	if (fRootWindow == ::GetActiveWindow()) return true;
	
	if (_GMDIWindow) {
		HWND w;
		
		/*
		 *	MDI application. Determine active frontmost
		 */
		
		w = ::GetWindow(_GMDIWindow,GW_CHILD);
		while (w != NULL) {
			if (::GetProp(w,_GWindowClass)) {
				if (::IsWindowVisible(w)) break;
			}
			w = ::GetWindow(w,GW_HWNDNEXT);
		}
		if (w == fRootWindow) return true;
	}
	return false;
#endif

#if OPT_XWIN
	if (fWindow) return fWindow->fActive;
	else return false;
#endif
}

/*$ XGView::GetTitle
 *
 *		Get the title of this view. By default returns an empty string.
 *	This is necessary for '&' processing for dialogs
 */

void XGView::GetTitle(char *c) const
{
	*c = 0;
}

/*$ XGView::SetTitle
 *
 *		Set the title. This should never be called
 */

void XGView::SetTitle(const char *)
{
	Assert(0);
}

/************************************************************************/
/*																		*/
/*	View Hierarchy search support										*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark View Hierarchy
#endif

/*$	XGView::GetWindow
 *
 *		Get the XGWindow this view is contained in
 */

XGWindow *XGView::GetWindow() const
{
	return fWindow;
}

/*$	XGView::GetForm
 *
 *		Get the XGWindow this view is contained in
 */

XGForm *XGView::GetForm() const
{
	XGWindow *w = GetWindow();
	if (w) return w->GetForm();
	else return NULL;
}

/*$ XGView::FindViewByID
 *
 *		Find the specified view by it's identifier
 */
 
XGView *XGView::FindViewByID( short index )
{
	return const_cast<XGView *>(static_cast<const XGView *>(this)->FindViewByID( index ));
}

const XGView *XGView::FindViewByID(short index) const
{
	XGView *ptr;
	XGView *ret;
	
	/*
	 *	Is it me?
	 */
	
	if (GetViewID() == index) return this;
	
	/*
	 *	Scan
	 */
	
	for (ptr = GetChild(); ptr != NULL; ptr = ptr->GetSibling()) {
		ret = ptr->FindViewByID(index);
		if (ret) return ret;
	}
	
	/*
	 *	None
	 */
	
	return NULL;
}

/*$ XGView::FindViewByPoint
 *
 *		Find the specified view by it's location. Find the view which
 *	contains this rectangle. Note: This stops at invisible views, but
 *	searches enabled views. Further, to support "pop-up" views (that
 *	is, views which are inserted temporarly over other views), this
 *	returns the *last* view which intersects the 'where' point.
 */

XGView *XGView::FindViewByPoint(XPoint where, bool recurse)
{
	return const_cast<XGView *>(static_cast<const XGView *>(this)->FindViewByPoint( where, recurse ));
}

const XGView *XGView::FindViewByPoint(XPoint where, bool recurse) const
{
	XGView *ptr;
	const XGView *ret;
	XGView *tmp;
	
	if (!fVisible) return NULL;
	if (!IsPtInRect(where,fGlobalRect)) return NULL;
	ret = this;
	if (recurse) {
		for (ptr = GetChild(); ptr != NULL; ptr = ptr->GetSibling()) {
			tmp = ptr->FindViewByPoint(where,true);
			if (tmp) ret = tmp;
		}
	} else {
		for (ptr = GetChild(); ptr != NULL; ptr = ptr->GetSibling()) {
			if (IsPtInRect(where,ptr->fGlobalRect)) ret = ptr;
		}
	}
	return ret;
}

/*$ XGView::FindFirstView
 *
 *		Find the first view in the list of views
 */

XGView *XGView::FindFirstView()
{
	XGView *vc;
	short min;
	XGView *c;
	XGView *m;
	
	/*
	 *	See if I can add me to the list
	 */
	
	vc = NULL;
	min = GetViewID();
	if ((0 != min) && IsTabStop() && IsEnabled() && IsVisible()) {
		vc = this;
	}
	
	/*
	 *	Scan my children
	 */
	
	for (c = GetChild(); c != NULL; c = c->GetSibling()) {
		m = c->FindFirstView();
		if (m) {
			if (!vc) {
				min = m->GetViewID();
				vc = m;
			} else if (m->GetViewID() < min) {
				min = m->GetViewID();
				vc = m;
			}
		}
	}
	
	/*
	 *	Done. Return my results
	 */
	 
	return vc;
}

/*$ XGView::FindLastView
 *
 *		Find the first view in the list of views
 */

XGView *XGView::FindLastView()
{
	XGView *vc;
	short min;
	XGView *c;
	XGView *m;
	
	/*
	 *	See if I can add me to the list
	 */
	
	vc = NULL;
	min = GetViewID();
	if ((0 != min) && IsTabStop() && IsEnabled() && IsVisible()) {
		vc = this;
	}
	
	/*
	 *	Scan my children
	 */
	
	for (c = GetChild(); c != NULL; c = c->GetSibling()) {
		m = c->FindLastView();
		if (m) {
			if (!vc) {
				min = m->GetViewID();
				vc = m;
			} else if (m->GetViewID() > min) {
				min = m->GetViewID();
				vc = m;
			}
		}
	}
	
	/*
	 *	Done. Return my results
	 */
	 
	return vc;
}

/*$ XGView::FindBeforeView
 *
 *		Find the first view in the list of views
 */

XGView *XGView::FindBeforeView(short vid)
{
	XGView *vc;
	short min;
	XGView *c;
	XGView *m;
	short n;
	
	/*
	 *	See if I can add me to the list
	 */
	
	vc = NULL;
	min = GetViewID();
	if ((0 != min) && IsTabStop() && IsEnabled() && IsVisible()) {
		if (min < vid) vc = this;
	}
	
	/*
	 *	Scan my children
	 */
	
	for (c = GetChild(); c != NULL; c = c->GetSibling()) {
		m = c->FindBeforeView(vid);
		if (m) {
			n = m->GetViewID();
			if (!vc && (n < vid)) {
				min = m->GetViewID();
				vc = m;
			} else if ((n > min) && (n < vid)) {
				min = n;
				vc = m;
			}
		}
	}
	
	/*
	 *	Done. Return my results
	 */
	 
	return vc;
}

/*$ XGView::FindAfterView
 *
 *		Find the first view in the list of views after this ID
 */

XGView *XGView::FindAfterView(short vid)
{
	XGView *vc;
	short min;
	XGView *c;
	XGView *m;
	short n;
	
	/*
	 *	See if I can add me to the list
	 */
	
	vc = NULL;
	min = GetViewID();
	if ((0 != min) && IsTabStop() && IsEnabled() && IsVisible()) {
		if (min > vid) vc = this;
	}
	
	/*
	 *	Scan my children
	 */
	
	for (c = GetChild(); c != NULL; c = c->GetSibling()) {
		m = c->FindAfterView(vid);
		if (m) {
			n = m->GetViewID();
			if (!vc && (n > vid)) {
				min = m->GetViewID();
				vc = m;
			} else if ((n < min) && (n > vid)) {
				min = n;
				vc = m;
			}
		}
	}
	
	/*
	 *	Done. Return my results
	 */
	 
	return vc;
}

/*$ XGView::FindViewByAccel
 *
 *		Find the view with the specified accelerator
 */

XGView *XGView::FindViewByAccel(char accel)
{
	XGView *ptr;
	XGView *ret;
	char buffer[256];
	char *x;
	
	/*
	 *	Is it me?
	 */
	
	GetTitle(buffer);
	for (x = buffer; *x != 0; x++) {
		if (*x == '&') {
			if (toupper(x[1]) == toupper(accel)) return this;
		}
	}
	
	/*
	 *	Scan
	 */
	
	for (ptr = GetChild(); ptr != NULL; ptr = ptr->GetSibling()) {
		ret = ptr->FindViewByAccel(accel);
		if (ret) return ret;
	}
	
	/*
	 *	None
	 */
	
	return NULL;
}

/*$ XGView::FindNextView
 *
 *		Find the next view; this finds the view after this one in
 *	the overall hierarchy
 */

XGView *XGView::FindNextView()
{
	XGView *tmp;
	XGView *ptr;
	
	/*
	 *	Do I have children? If so, move down the tree
	 */
	
	if (NULL != (tmp = GetChild())) return tmp;
	
	/*
	 *	Move up the tree until I find a sibling of me, my parent,
	 *	or one above my parent...
	 */
	
	ptr = this;
	for (;;) {
		if (NULL != (tmp = ptr->GetSibling())) return tmp;
		ptr = ptr->GetParent();
		if (ptr == NULL) return NULL;
	}
}


/************************************************************************/
/*																		*/
/*	View Location and View Manipulation									*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark View Location and View Manipulation
#endif

/*$ XGView::GetContentRect
 *
 *		Get the content rectangle for this view
 */

XRect XGView::GetContentRect() const
{
	XRect r;
	
	r.left = 0;
	r.top = 0;
	r.right = fSize.h;
	r.bottom = fSize.v;
	
	return r;
}

/*$ XGView::RecalcAutoLocation
 *
 *		Recalculate the auto-position values for this view given the
 *	new position settings in r.  If another view is supplied, then
 *	this will ensure that there is no overlap by moving this view
 *	down or to the right (based on the fLock* flags).
 */

void XGView::RecalcAutoLocation (XGView *below, XGView *beside, XRect *r)
{
	if (r) {
		if ((short)(r->left & 0xFFFF)   != (short)0x8000) fAutoLeft = r->left;
		if ((short)(r->right & 0xFFFF)  != (short)0x8000) fAutoRight = r->right;
		if ((short)(r->top & 0xFFFF)    != (short)0x8000) fAutoTop = r->top;
		if ((short)(r->bottom & 0xFFFF) != (short)0x8000) fAutoBottom = r->bottom;
	}

	if (below && below->fParent) {
		XRect vr = below->CalcAutoLocate(below->fParent->GetContentRect());;
		int delta = vr.bottom - fAutoTop;
		fAutoTop += delta;
		if (!fLockBottom)
			fAutoBottom += delta;
	}

	if (beside && beside->fParent) {
		XRect vr = beside->CalcAutoLocate(beside->fParent->GetContentRect());;
		int delta = vr.right - fAutoLeft;
		fAutoLeft += delta;
		if (!fLockRight)
			fAutoRight += delta;
	}

	if (fParent)
		SetLocation(CalcAutoLocate(fParent->GetContentRect()));
}


void XGView::RePosition( int xpos, int ypos )
{
	int w = fAutoRight - fAutoLeft;
	int h = fAutoBottom - fAutoTop;
	fAutoLeft = xpos;
	fAutoTop = ypos;
	fAutoRight = xpos + w;
	fAutoBottom = ypos + h;
	
	if (fParent) {
		this->SetLocation( fParent->CalcChildPos(this) );
	}
}


/*$ XGView::SetLocation
 *
 *		Set the location of this thing within my parent's content
 *	area. This will invalidate the rectangle areas where the location
 *	was.
 */

void XGView::SetLocation(XRect r)
{
	XPoint newSize;
	XPoint newPos;
	XPoint oldSize;
	XPoint oldPos;
	XRect oldGlobalRect;
	bool needNewGlobal;
	
	/*
	 *	Kludge for XGInlineEdit--if there is one in me, delete it.
	 *	This is done because anything that calls this routine is
	 *	attempting to resize me. And if I get resized, I should
	 *	blow away the inline editor.
	 */

	XGInline::CloseInline(this);

	/*
	 *	Calculate the new position and size
	 */
	
	newPos.h  = r.left;
	newPos.v  = r.top;
	newSize.h = r.right - r.left;
	newSize.v = r.bottom - r.top;
	
	/*
	 *	Determine if this view even moves; if it doesn't, this
	 *	aborts this operation.
	 */
	
//	if ((newPos.h == fPos.h) && (newPos.v == fPos.v) &&
//			(newSize.h == fSize.h) && (newSize.v == fSize.v)) return;
			
	/*
	 *	We're about to move this thing. First, invalidate the old
	 *	area where this thing used to be.
	 */
	
	InvalView();
	
	/*
	 *	Now, calculate this view's positioning information
	 */
	
	oldGlobalRect = fGlobalRect;
	fGlobalRect = r;
	if (fParent) {
		fParent->ViewToGlobal(&fGlobalRect);
		OffsetRect( &fGlobalRect, fParent->fChildOffset.h, fParent->fChildOffset.v );
		::DoIntersect(fGlobalRect,fParent->fGlobalRect,&fGlobalRect);
	}
	needNewGlobal = !::EqualRect(&oldGlobalRect,&fGlobalRect);
	
	oldSize = fSize;
	oldPos = fPos;
	
	fPos = newPos;
	fSize = newSize;
	fGlobalPos = newPos;
	if (fParent) {
		fParent->ViewToGlobal(&fGlobalPos);
		fGlobalPos.h += fParent->fChildOffset.h;
		fGlobalPos.v += fParent->fChildOffset.v;
	}

	/*
	 *	Invalidate the new location
	 */
	
	InvalView();

	/*
	 *	And send the callbacks
	 */
	
	if ((oldPos.h != fPos.h) || (oldPos.v != fPos.v)) {
		CalcPos();
		needNewGlobal = false;
	}
	
	if ((oldSize.h != fSize.h) || (oldSize.v != fSize.v)) {
		DoSizeView();
		needNewGlobal = false;
	}
	
	if (needNewGlobal) {
		CalcGlobalRect();
	}
}

/*$ XGView::CalcChildPos
 *
 *		This automatically calculates the position of this particular
 *	child inside of it's parent.
 */

XRect XGView::CalcChildPos(XGView *child)
{
	XRect r = child->CalcAutoLocate(GetContentRect());
	//OffsetRect( &r, fChildOffset.h, fChildOffset.v );
	return r;
}

/*$ XGView::CalcAutoLocate
 *
 *		Given the parameters for how this view should be locked to my parent's
 *	view, this calculates the various location parameters
 */

XRect XGView::CalcAutoLocate(XRect r)
{
	XRect s;
	
	if (fLockLeft) s.left = r.left + fAutoLeft;
	else s.left = r.right + fAutoLeft;
	if (!fLockRight) s.right = r.left + fAutoRight;
	else s.right = r.right + fAutoRight;
	
	if (fLockTop) s.top = r.top + fAutoTop;
	else s.top = r.bottom + fAutoTop;
	if (!fLockBottom) s.bottom = r.top + fAutoBottom;
	else s.bottom = r.bottom + fAutoBottom;

	return s;
}


void XGView::GetAutoLocate( XRect *r,
								bool *lockleft, bool *locktop,
								bool *lockright, bool *lockbottom )
{
	if (r) {
		r->left = fAutoLeft;
		r->top = fAutoTop;
		r->right = fAutoRight;
		r->bottom = fAutoBottom;
	}
	if (lockleft) *lockleft = fLockLeft;
	if (locktop) *locktop = fLockTop;
	if (lockright) *lockright = fLockRight;
	if (lockbottom) *lockbottom = fLockBottom;
}


void XGView::SetAutoLocate( const XRect &r,
								bool lockleft, bool locktop,
								bool lockright, bool lockbottom )
{
	fAutoLeft = r.left;
	fAutoTop = r.top;
	fAutoRight = r.right;
	fAutoBottom = r.bottom;
	
	fLockLeft = lockleft;
	fLockTop = locktop;
	fLockRight = lockright;
	fLockBottom = lockbottom;
	
	if (fParent)
		SetLocation(CalcAutoLocate(fParent->GetContentRect()));
}


/*$ XGView::ViewToScreen
 *
 *		Converts the point from view to global screen coordinates
 */
 
void XGView::ViewToScreen (XPoint *p)
{
#if OPT_MACOS
	PixMapHandle pmap = ((CGrafPtr)fRootWindow)->portPixMap;
	long dx = pmap[0]->bounds.left - fGlobalPos.h - fRootWindow->portRect.left;
	long dy = pmap[0]->bounds.top - fGlobalPos.v - fRootWindow->portRect.top;
	
	p->h -= dx;
	p->v -= dy;
#endif

#if OPT_WINOS
	POINT pt;

	pt.x = 0;
	pt.y = 0;
	ViewToGlobal(p);
	::ClientToScreen(GetRootWindow(), &pt);
	p->h += pt.x;
	p->v += pt.y;
#endif

#if OPT_XWIN
	int newx,newy;
	Window tmpw;

	ViewToGlobal(p);
	::XTranslateCoordinates(_GDisplay,
							GetRootWindow(),
							DefaultRootWindow(_GDisplay),
							0,0,
							&newx,&newy,
							&tmpw);
	p->h += newx;
	p->v += newy;
#endif
}

/*$ XGView::ViewToScreen
 *
 *		Converts rectangle from view to global screen coordinates
 */
 
void XGView::ViewToScreen (XRect *r)
{
#if OPT_MACOS
	PixMapHandle pmap = ((CGrafPtr)fRootWindow)->portPixMap;
	long dx = pmap[0]->bounds.left - fGlobalPos.h - fRootWindow->portRect.left;
	long dy = pmap[0]->bounds.top - fGlobalPos.v - fRootWindow->portRect.top;
	
	OffsetRect( r, -dx, -dy );
#endif

#if OPT_WINOS
	POINT pt;

	pt.x = 0;
	pt.y = 0;
	ViewToGlobal(r);
	::ClientToScreen(GetRootWindow(), &pt);
	::OffsetRect(r, pt.x, pt.y);
#endif

#if OPT_XWIN
	int newx,newy;
	Window tmpw;

	ViewToGlobal(r);
	::XTranslateCoordinates(_GDisplay,
							GetRootWindow(),
							DefaultRootWindow(_GDisplay),
							0,0,
							&newx,&newy,
							&tmpw);
	::OffsetRect(r, newx, newy);
#endif
}

/*$ XGView::ScreenToView
 *
 *		Converts the point from global screen to view coordinates
 */

void XGView::ScreenToView (XPoint *p)
{
#if OPT_MACOS
	PixMapHandle pmap = ((CGrafPtr)fRootWindow)->portPixMap;
	long dx = pmap[0]->bounds.left - fGlobalPos.h - fRootWindow->portRect.left;
	long dy = pmap[0]->bounds.top - fGlobalPos.v - fRootWindow->portRect.top;
	
	p->h += dx;
	p->v += dy;
	
//	p->h = p->h + fRootWindow->portRect.left - fGlobalPos.h;
//	p->v = p->v + fRootWindow->portRect.top - fGlobalPos.v;
#endif

#if OPT_WINOS
	POINT pt;

	pt.x = 0;
	pt.y = 0;
	GlobalToView(p);
	::ClientToScreen(GetRootWindow(), &pt);
	p->h -= pt.x;
	p->v -= pt.y;
#endif

#if OPT_XWIN
	int newx,newy;
	Window tmpw;

	GlobalToView(p);
	::XTranslateCoordinates(_GDisplay,
							GetRootWindow(),
							DefaultRootWindow(_GDisplay),
							0,0,
							&newx,&newy,
							&tmpw);
	p->h -= newx;
	p->v -= newy;
#endif
}

/*$ XGView::ScreenToView
 *
 *		Converts rectangle from global screen to view coordinates
 */
 
void XGView::ScreenToView (XRect *r)
{
#if OPT_MACOS
/*	XGDraw draw(this);
	Point pt;

	pt.h = 0;
	pt.v = 0;
	::LocalToGlobal(&pt); */
	
	PixMapHandle pmap = ((CGrafPtr)fRootWindow)->portPixMap;
	long dx = pmap[0]->bounds.left - fGlobalPos.h - fRootWindow->portRect.left;
	long dy = pmap[0]->bounds.top - fGlobalPos.v - fRootWindow->portRect.top;
	
	OffsetRect( r, dx, dy );
#endif

#if OPT_WINOS
	POINT pt;

	pt.x = 0;
	pt.y = 0;
	GlobalToView(r);
	::ClientToScreen(GetRootWindow(), &pt);
	::OffsetRect(r, -pt.x, -pt.y);
#endif

#if OPT_XWIN
	int newx,newy;
	Window tmpw;

	GlobalToView(r);
	::XTranslateCoordinates(_GDisplay,
							GetRootWindow(),
							DefaultRootWindow(_GDisplay),
							0,0,
							&newx,&newy,
							&tmpw);
	::OffsetRect(r, -newx, -newy);
#endif
}



/************************************************************************/
/*																		*/
/*	Drawing Support Routines											*/
/*																		*/
/*		The following five routines are highly OS platform dependant.	*/
/*	The idea is that all of the drawing routines that are OS dependant	*/
/*	live in XGDraw. However, it is convenient to be able to scroll a	*/
/*	view or invalidate it's contents without having to set up a drawing	*/
/*	context. Thus, these routines live here.							*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Drawing Support
#endif

/*$ XGView::InvalView
 *
 *		This invalidates the drawing area where this view appears.
 */

void XGView::InvalView(XRect *r)
{
	XRect s;
	
	if (fRootWindow == 0) return;				// can't do this...
	XGDraw draw(this);

	/*
	 *	Find the area to invalidate
	 */
		
	s = GetContentRect();
	if (r) if (!::DoIntersect(*r,s,&s)) return;

	/*
	 *	Invalidate the region within me.
	 */
	
#if OPT_MACOS == 1
	XPoint off;

	off.h = fRootWindow->portRect.left;			/* Calculate the area to */
	off.v = fRootWindow->portRect.top;			/* Invalidate based on the */
	ViewToGlobal(&s);							/* Current origin */
	
	s.left += off.h;
	s.right += off.h;
	s.top += off.v;
	s.bottom += off.v;
	Rect mr = s.MacRect();
	::InvalRect(&mr);
#endif

#if OPT_WINOS == 1
	ViewToGlobal(&s);
	::InvalidateRect(fRootWindow,&s,false);
#endif

#if OPT_XWIN == 1
	ViewToGlobal(&s);
	
	if (fWindow) {
		if (fWindow->fUpdateRgn == 0) {
			fWindow->fUpdateRgn = XCreateRegion();
		}
		XRectangle ss;
		
		ss.x = s.left;
		ss.y = s.top;
		ss.width = s.right - s.left;
		ss.height = s.bottom - s.top;
		::XUnionRectWithRegion(&ss,fWindow->fUpdateRgn,fWindow->fUpdateRgn);
	} else {
		/*
		 *	We're not a view in a traditional XGWindow object. Do
		 *	something else.
		 */
		
		::XClearArea(_GDisplay,
					 fRootWindow,
					 s.left,
					 s.top,
					 s.right-s.left,
					 s.bottom-s.top,
					 1);
	}
#endif
}
/*$ XGView::ValidView
 *
 *		This validates the drawing area.
 */

void XGView::ValidView(XRect *r)
{
	XRect s;

	if (fRootWindow == 0) return;				// can't do this...
	XGDraw draw(this);

	/*
	 *	Find the area to invalidate
	 */
		
	s = GetContentRect();
	if (r) if (!::DoIntersect(*r,s,&s)) return;

	/*
	 *	Invalidate the region within me.
	 */
	
#if OPT_MACOS == 1
	XPoint off;

	off.h = fRootWindow->portRect.left;			/* Calculate the area to */
	off.v = fRootWindow->portRect.top;			/* Invalidate based on the */
	ViewToGlobal(&s);							/* Current origin */
	
	s.left += off.h;
	s.right += off.h;
	s.top += off.v;
	s.bottom += off.v;
	Rect mr = s.MacRect();
	::ValidRect(&mr);
#endif

#if OPT_WINOS == 1
	ViewToGlobal(&s);
	::ValidateRect(fRootWindow,&s);
#endif

#if OPT_XWIN == 1
	/*
	 *	There is no way to validate a portion of a window in X, as
	 *	far as I'm aware. Thus, I simply skip this step, and we can
	 *	survive with the occassional extra unnecessary redraw.
	 *
	 *	It has been my experience that while ValidView() is an
	 *	important routine, it is in practice seldom used. And in
	 *	the few cases where it is, the extra update later won't hurt.
	 */
	 
#endif
}

/*$ XGView::UpdateView
 *
 *		This forces this view to be updated _right_ _now_
 */

void XGView::UpdateView()
{
	if (fRootWindow == 0) return;		// can't do this...

#if OPT_XWIN == 1
	XRect r;
	
	/*
	 *	Right now, this is a kludge.
	 *
	 *	X simply redraws this view regardless of if it needs
	 *	redrawing, without mucking with the event queue. Later,
	 *	this should check the update status <<in my window's 
	 *	update region>>, but this is later.
	 */
	
	r = GetContentRect();
	ViewToGlobal(&r);
	ProcessDraw(r);
#endif

#if OPT_WINOS == 1
	UpdateWindow(fRootWindow);			// overkill??? ###
#endif

#if OPT_MACOS == 1
	RgnHandle rgn;
	Point pt;
	RgnHandle tmp;
	XRect r;
	GrafPtr port;
	
	if (!fVisible) return;
	
	/*
	 *	Initialize stuff
	 */
	
	XGDraw draw(this,false);			// Set this as current port
	::GetPort(&port);
	pt.h = 0, pt.v = 0;
	::GlobalToLocal(&pt);				// Shift to window coordinates
	
	/*
	 *	Calculate the intersection of the update region with the
	 *	view rectangle
	 */
	
	rgn = ::NewRgn();					// Get the update region
	::CopyRgn(((WindowPeek)port)->updateRgn,rgn);
	::OffsetRgn(rgn,pt.h,pt.v);			// Offset the region to window coords
	tmp = ::NewRgn();
	r = GetContentRect();
	::OffsetRect(&r,fGlobalPos.h,fGlobalPos.v);
	Rect mr = r.MacRect();
	::RectRgn(tmp,&mr);
	::SectRgn(rgn,tmp,rgn);				// Intersect with view rectangle
	::DisposeRgn(tmp);
	
	if (!EmptyRgn(rgn)) {
		/*
		 *	This is a bit wierd, but what I do is to set the invalid
		 *	region to the region that I calculated, force a normal
		 *	validation (BeginUpdate/EndUpdate) cycle, and then
		 *	invalidate the area not in the region I calculated...
		 */
		
		tmp = ::NewRgn();
		::CopyRgn(((WindowPeek)port)->updateRgn,tmp);
		::OffsetRgn(tmp,pt.h,pt.v);		// Offset to window coordinates
		::DiffRgn(tmp,rgn,tmp);			// Area remaining (window coords)
		
		r = (**rgn).rgnBBox;			// Get bounding rectangle
		::OffsetRgn(rgn,-pt.h,-pt.v);	// Move my invalid area to global cds
		::CopyRgn(rgn,((WindowPeek)port)->updateRgn);	// Replace area
		
		::BeginUpdate(port);			// Update this window
		ProcessDraw(r);
		::EndUpdate(port);
		
		::SetPort(port);
		::SetOrigin(0,0);				// Move to window coordinates
		::InvalRgn(tmp);				// Invalidate the remaining area
		::DisposeRgn(tmp);
	}
		
	::DisposeRgn(rgn);					// Clean up and exit
#endif
}

/************************************************************************/
/*																		*/
/*	Scrolling Support													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Scroll Support
#endif

/*$ XGView::Scroll
 *
 *		This is the method which is called when it's time to scroll this
 *	thing, such as processing scrollbar messages. By default, this scrolls
 *	the entire thing.
 */

void XGView::Scroll(long x, long y)
{
	ScrollView(x,y);
	
	long xval, yval, step;
	if (GetScrollParams( &xval, &yval, &step )) {
		SetChildOffset( -xval * step, -yval * step );
	}
	
}

/*$ XGView::ScrollView
 *
 *		This handles the scrolling view itself. This is a bit tricky, given
 *	the various operating systems this thing can be hosted on. Basically,
 *	I can scroll the bits in this view without having a drawing context
 *	open to this view. This is accomplished with various black magic
 *	incantations.
 */

void XGView::ScrollView(long x, long y, XRect *r, Color c)
{
	XRect s;
	
	/*
	 *	Blow away any inline editor that was associated with me.
	 *	This is done at this point because inline editor objects
	 *	cannot be scrolled.
	 */
	
	XGInline::CloseInline(this);
	
	/*
	 *	First, in an OS independant way, find the area that actually
	 *	gets scrolled
	 */
	
	if (r == NULL) s = GetContentRect();
	else s = *r;
	ViewToGlobal(&s);
	if (!::DoIntersect(fGlobalRect,s,&s)) return;	/* Not visible */
	
	/*
	 *	Determine if the amount we get scrolled is too wide or tall.
	 *	If it is, simply invalidate the entire view
	 */
	
	if (((s.right - s.left) <= LAbs(x)) || ((s.bottom - s.top) <= LAbs(y))) {
		InvalView();
		return;
	}
	
	/*
	 *	We have to scroll. Oh, well. At least do it with a little class...
	 */
	
#if OPT_MACOS == 1
	/*
	 *	Macintosh version: Calculate the location inside the current
	 *	drawing port where I should start scrolling bits. This requires
	 *	finding the current coordinate system the window is in, finding
	 *	the bits where my view is, and modifying
	 */
	
	XGDraw draw(this,false);
	RgnHandle rgn;
	XPoint off;

	if (c)
		draw.SetBackColor(c);

	off.h = fRootWindow->portRect.left;		/* Calculate the area to */
	off.v = fRootWindow->portRect.top;		/* scroll based on the */
	
	s.left += off.h;
	s.right += off.h;
	s.top += off.v;
	s.bottom += off.v;
	
	rgn = ::NewRgn();
	Rect mr = s.MacRect();
	::ScrollRect(&mr,(short)x,(short)y,rgn);
	::InvalRgn(rgn);
	::DisposeRgn(rgn);
#endif

#if OPT_WINOS == 1
	#pragma unused(c)
	::ScrollWindowEx(fRootWindow,(int)x,(int)y,&s,&s,NULL,NULL,SW_ERASE | SW_INVALIDATE);
#endif

#if OPT_XWIN == 1
	#pragma unused(c)
	short xsrc,ysrc;
	short xdst,ydst;
	short xwid,ywid;
	GC gc;
	
	/*
	 *	X Windows doesn't support the scroll stuff like the Macintosh
	 *	or like Windows. I gotta do this the long way
	 *
	 *	First, find the bounding rectangle that is getting moved
	 */
	
	xwid = s.right - s.left;
	ywid = s.bottom - s.top;
	if (x < 0) {
		xsrc = s.left - x;
		xdst = s.left;
		xwid += x;
	} else {
		xsrc = s.left;
		xdst = s.left + x;
		xwid -= x;
	}
	if (y < 0) {
		ysrc = s.top - y;
		ydst = s.top;
		ywid += y;
	} else {
		ysrc = s.top;
		ydst = s.top + y;
		ywid -= y;
	}
	
	gc = ::XCreateGC(_GDisplay,fRootWindow,0,NULL);
	::XCopyArea(_GDisplay,fRootWindow,fRootWindow,gc,xsrc,ysrc,
						xwid,ywid,xdst,ydst);
	::XFreeGC(_GDisplay,gc);
	
	/*
	 *	Second, find the (up to) two rectangles which need to be
	 *	invalidated
	 */
	
	if (x < 0) {
		xsrc = s.right + x;
		xwid = -x;
		::XClearArea(_GDisplay,
					 fRootWindow,
					 xsrc,
					 s.top,
					 xwid,
					 s.bottom-s.top,
					 1);
	} else if (x > 0) {
		xsrc = s.left;
		xwid = x;
		::XClearArea(_GDisplay,
					 fRootWindow,
					 xsrc,
					 s.top,
					 xwid,
					 s.bottom-s.top,
					 1);
	}
	if (y < 0) {
		ysrc = s.bottom + y;
		ywid = -y;
		::XClearArea(_GDisplay,
					 fRootWindow,
					 s.left,
					 ysrc,
					 s.right-s.left,
					 ywid,
					 1);
	} else {
		ysrc = s.top;
		ywid = y;
		::XClearArea(_GDisplay,
					 fRootWindow,
					 s.left,
					 ysrc,
					 s.right-s.left,
					 ywid,
					 1);
	}
#endif
}

/*$	XGView::GetScrollParams
 *
 *		This routine provides assistance if this view is embedded
 *	in an XGScrollView. This gets the scroll parameters for this
 *	if this is in a scroll view.
 */

bool XGView::GetScrollParams(long *x, long *y, long *step)
{
	XGScrollView *sv;
	
	try {
		sv = dynamic_cast<XGScrollView *>(GetParent());
	}
	catch (...) {
		sv = NULL;
	}
	if (sv == NULL) {
		*x = 0;
		*y = 0;
		*step = 12;	/* Random value */
		return false;
	} else {
		sv->GetScrollValue(x,y);
		*step = sv->GetPixelStep();
		return true;
	}
}

/*$	XGView::SetScrollLimits
 *
 *		This routine uses the current view size as well as the max
 *	size (in pixels) and the step size to set theparameters for
 *	the XGScrollView I'm embedded in. If there is none, this
 *	punts
 */

void XGView::SetScrollLimits(long x, long y, long step)
{
	XGScrollView *sv;
	XRect r;
	long xval,yval;
	
	try {
		sv = dynamic_cast<XGScrollView *>(GetParent());
	}
	catch (...) {
		sv = NULL;
	}
	if (sv == NULL) return;
	
	/*
	 *	Set the step size
	 */
	
	sv->SetPixelStep(step);
	
	/*
	 *	Set the page size
	 */
	
	r = GetContentRect();
	xval = (r.right - r.left - (step >> 1)) / step;
	if (xval < 1) xval = 1;
	yval = (r.bottom - r.top - (step >> 1)) / step;
	if (yval < 1) yval = 1;
	sv->SetPageSize(xval,yval);
	
	/*
	 *	Set the max scroll limits
	 */
	
	x -= r.right - r.left;
	y -= r.bottom - r.top;
	xval = (x + step - 1) / step;
	yval = (y + step - 1) / step;
	if (xval < 0) xval = 0;
	if (yval < 0) yval = 0;
	sv->SetMinValue(0,0);
	sv->SetMaxValue(xval,yval);
	
	sv->GetScrollValue( &xval, &yval );
	SetChildOffset( -xval * step, -yval * step );
}

/*$	XGView::ScrollInRange
 *
 *		Assuming we have a rectangular region inside of this thing
 *	which represents the selection range, this scrolls that selection
 *	range into view. These values should be given in global
 *	coordinates.
 */

void XGView::ScrollInRange(long x, long y, long w, long h)
{
	XGScrollView *sv;
	XRect r;
	long xval,yval;
	long st;
	
	try {
		sv = dynamic_cast<XGScrollView *>(GetParent());
	}
	catch (...) {
		sv = NULL;
	}
	if (sv == NULL) return;
	
	/*
	 *	Scroll this into range
	 */
	
	sv->GetScrollValue(&xval,&yval);
	st = sv->GetPixelStep();
	
	if (w == -1) w = 2;
	if (h == -1) h += st;
	w += x;
	h += y;
	
	r = GetContentRect();
	if (w > r.right + xval * st) {
		xval = (w - r.right + st - 1) / st;
	}
	if (w < r.left + xval * st) {
		xval = (w - r.left + st - 1) / st;
	}
	
	if (h > r.bottom + yval * st) {
		yval = (h - r.bottom + st - 1) / st;
	}
	if (h < r.top + yval * st) {
		yval = (h - r.top + st - 1) / st;
	}

	sv->SetScrollValue(xval,yval);
}


void XGView::SetChildOffset( long x, long y )
{
	if (x == fChildOffset.h && y == fChildOffset.v) return;
	fChildOffset.h = x;
	fChildOffset.v = y;
	CalcPos();
}


XPoint XGView::GetChildOffset()
{
	return fChildOffset;
}


/************************************************************************/
/*																		*/
/*	View Management														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark View Management
#endif

/*$ XGView::ProcessDraw
 *
 *		Process drawing command. This runs through all of the views
 *	that are inside of me and passes the appropriate command
 */

void XGView::ProcessDraw(XRect ginval)
{	XRect r;
	XRect s;
	XGView *v;
	
	/*
	 *	Am I visible?
	 */
	
	if (!fVisible) return;				// Nope.
	
	/*
	 *	Am I even in the invalid area?
	 */
	
	if (::DoIntersect(ginval,fGlobalRect,&r)) {
		/*
		 *	Do me first
		 */
		
		s = r;
		GlobalToView(&s);
		DoDrawView(s);
		
		/*
		 *	Now let my children draw inside of me
		 */
		
		for (v = fChild; v != NULL; v = v->fSibling) {
			v->ProcessDraw(r);
		}
	}
}

/*$ XGView::ProcessActivate
 *
 *		Run through and activate or deactivate all the views
 */

void XGView::ProcessActivate(bool active)
{
	XGView *v;
	XGView *ptr;
	
	ptr = fChild;
	while (ptr != NULL) {
		v = ptr;
		ptr = ptr->fSibling;
		v->ProcessActivate(active);
	}
	DoActivate(active);
}

/*$ XGView::FindClick
 *
 *		Find the view to send this click event. This skips disabled
 *	and invisible views
 */

XGView *XGView::FindClick(XPoint where)
{
	XGView *ptr;
	XGView *ret;
	XGView *tmp;
	
	if (!fVisible) return NULL;				// view not visible
	if (!fEnabled) return NULL;				// view not enabled
	if (!IsPtInRect(where,fGlobalRect)) return NULL;
	
	ret = this;
	for (ptr = GetChild(); ptr != NULL; ptr = ptr->GetSibling()) {
		tmp = ptr->FindClick(where);
		if (tmp) ret = tmp;
	}
	return ret;
}

/*$	XGView::GetDispatchParent
 *
 *		Who gets my messages if I don't? This overrides the XGDispatch
 *	method to do the right thing.
 */

XGDispatch *XGView::GetParentDispatch()
{
	if (fParent) return fParent;
	if (fWindow) return fWindow;
	return XGAppCore::GetApplication();			// ???
}

/************************************************************************/
/*																		*/
/*	OS Specific Support													*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark OS Specific Support
#endif

/*$ XGView::Is72DPI
 *
 *		Return true if this is 72 DPI, and false if this is higher.
 *
 *	###FIX ME
 */

bool XGView::Is72DPI()
{
#if OPT_MACOS == 1
	return true;
#endif

#if OPT_WINOS == 1
	return false;
#endif

#if OPT_XWIN == 1
	return false;
#endif
}

/************************************************************************/
/*																		*/
/*	Resize Support														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Resize Support
#endif

/* XGView::CalcPos
 *
 *		Calculate the position of the children views. This recurses down,
 *	moving the children around as well.
 */

void XGView::CalcPos(void)
{
	XPoint newPos;
	XGView *v;
	
	/*
	 *	Run through the children, and move them. If they do move, then
	 *	we move them, and recurse
	 */
	
	for (v = fChild; v != NULL; v = v->fSibling) {
		newPos.h = fGlobalPos.h + fChildOffset.h + v->fPos.h;
		newPos.v = fGlobalPos.v + fChildOffset.v + v->fPos.v;
		if ((newPos.h != v->fGlobalPos.h) || (newPos.v != v->fGlobalPos.v)) {
			/*
			 *	Update global pos
			 */
			
			v->fGlobalPos = newPos;
			
			/*
			 *	Update global rectangle
			 */
			
			v->fGlobalRect.left = v->fGlobalPos.h;
			v->fGlobalRect.top  = v->fGlobalPos.v;
			v->fGlobalRect.right = v->fGlobalRect.left + v->fSize.h;
			v->fGlobalRect.bottom = v->fGlobalRect.top + v->fSize.v;
			::DoIntersect(v->fGlobalRect,fGlobalRect,&(v->fGlobalRect));

			/*
			 *	And move the children
			 */
			
			v->CalcPos();
		} else {
			XRect test;
			
			test.left = v->fGlobalPos.h;
			test.top  = v->fGlobalPos.v;
			test.right = test.left + v->fSize.h;
			test.bottom = test.top + v->fSize.v;
			::DoIntersect(test,fGlobalRect,&test);
			if (!::EqualRect( &test, &v->fGlobalRect )) {
				v->fGlobalRect = test;
				v->CalcGlobalRect();
			}
		}
	}
	
	/*
	 *	Let me know we moved
	 */
	
	DoMoveView();
}


void XGView::CalcGlobalRect(void)
{
	XRect test;
	XGView *v;
	
	for (v = fChild; v != NULL; v = v->fSibling) {
		test.left = v->fGlobalPos.h;
		test.top  = v->fGlobalPos.v;
		test.right = test.left + v->fSize.h;
		test.bottom = test.top + v->fSize.v;
		::DoIntersect(test,fGlobalRect,&test);
		if (!::EqualRect( &test, &v->fGlobalRect )) {
			v->fGlobalRect = test;
			v->CalcGlobalRect();
		}
	}
}


/************************************************************************/
/*																		*/
/*	Resize Support														*/
/*																		*/
/************************************************************************/

#if 0
#pragma mark -
#pragma mark Initialization Support
#endif

/*	XGView::IInitView
 *
 *		Initialize the view. This contains the hard stuff
 */

void XGView::IInitView(XGView *parent, XGSViewInitRecord &init)
{
	XRect s;
	
	/*
	 *	Initialize basic hierarchy
	 */
		
	fChild = NULL;
	if (parent) {
		XGView *end;
		
		// Hook me into the parent at the end of the linked list.
		// order preservation is important.
		fParent = parent;
		fSibling = NULL;
		fWindow = parent->fWindow;
		fRootWindow = parent->fRootWindow;	
		
		if (parent->fChild == NULL) {
			parent->fChild = this;
		} else {
			for (end = parent->fChild; end->fSibling != NULL; end = end->fSibling) ;
			end->fSibling = this;
		}
	} else {
		// Don't hook me into the parent--I *am* the root
		fParent = NULL;
		fSibling = NULL;
		fWindow = NULL;
		fRootWindow = NULL;
	}
	
	/*
	 *	Get the view specification parameters
	 */
	
	fViewType   = init.fViewType;
	fViewID     = init.fViewID;
	fRefNum     = init.fRefNum;
	
	/*
	 *	Get the size of this thing
	 */
	
	fLockLeft   = init.fLockLeft;
	fLockTop    = init.fLockTop;
	fLockRight  = init.fLockRight;
	fLockBottom = init.fLockBottom;
	
	fAutoLeft   = init.fAutoLeft;
	fAutoTop    = init.fAutoTop;
	fAutoRight  = init.fAutoRight;
	fAutoBottom = init.fAutoBottom;
	
	/*
	 *	Calculate the size of this thing based on the auto parameters
	 */
	
	if (fParent) {
		s = fParent->CalcChildPos(this);
	} else {
		/*
		 *	For initialization purposes only. This will be refined
		 *	later. I set this to an arbitrarly large value so that
		 *	my autolocate calculations work during initialization.
		 */

		s.left = 0;
		s.top = 0;
		s.right = 1000;
		s.bottom = 1000;
	}
	
	/*
	 *	Initialize the view positioning information
	 */
	
	fSize.h = s.right - s.left;
	fSize.v = s.bottom - s.top;
	fPos.h = s.left;
	fPos.v = s.top;
	fGlobalPos = fPos;
	fGlobalRect = s;
	if (fParent) {
		fParent->ViewToGlobal(&fGlobalPos);
		fParent->ViewToGlobal(&fGlobalRect);
		
		::DoIntersect(fParent->fGlobalRect,fGlobalRect,&fGlobalRect);
	}
	
	fChildOffset.h = 0;
	fChildOffset.v = 0;
	
	/*
	 *	Initialization
	 */
	
	fVisible    = init.fVisible;
	fEnabled    = init.fEnabled;
	fTabStop    = init.fTabStop;
	
	/*
	 *	Now invalidate the area where this was created.
	 */
	
	InvalView();
}
