/*	TRectEditView.cpp
 *
 *		Background view
 */

/*  YAAF Constructor - Rapid application development for YAAF
 *  Copyright (C) 1998 William Edward Woody
 *  
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <stdlib.h>
#include "TResDocument.h"
#include "TConstructorApp.h"
#include "LayoutViews.h"
#include <XMessages.h>
#include <XScroll.h>
#include <XDataUtil.h>
#include <XDraw.h>


/************************************************************************/
/*																		*/
/*	Internal Support Routines											*/
/*																		*/
/************************************************************************/

static ViewItemDeclaration *GViewItem;
static Rect GWhere;
static TResDocument *GDoc;

/*	GetInsideSize
 *
 *		Get the inside size of this thing. For most views, that's trivial.
 *	For windows and scroll views, it's not trivial
 */

static Rect GetInsideSize(ViewItemDeclaration *item, Rect r)
{
	ViewTypeRecord *vtype;
	
	vtype = GDoc->GetViewTypeByType(item->viewType);
	while (vtype) {
		if (vtype->viewType == 'wind') {
			r.top += 21;
			r.bottom -= 1;
			r.left += 1;
			r.right -= 1;
			return r;
		}
		
		if (vtype->viewType == 'scrv') {
			char buffer[256];
			
			::InsetRect(&r,1,1);
			item->data->GetString(13,buffer);
			if ((buffer[0] == 't') || (buffer[0] == 'T')) {
				r.right -= 15;
			}
			item->data->GetString(14,buffer);
			if ((buffer[0] == 't') || (buffer[0] == 'T')) {
				r.bottom -= 15;
			}
			return r;
		}
		
		vtype = vtype->childPtr;
	}
	
	return r;
}


/*	GetViewSize
 *
 *		Given the rectangle of the parent view and the view that
 *	goes here, this calculates where this view will show up
 */

static Rect GetViewSize(ViewItemDeclaration *item, Rect par)
{
	Rect s;
	char buffer[256];

	/*
	 *	Get view size
	 */
		
	item->data->GetString(6,buffer);
	s.left = atol(buffer);
	item->data->GetString(7,buffer);
	s.top = atol(buffer);
	item->data->GetString(8,buffer);
	s.right = atol(buffer);
	item->data->GetString(9,buffer);
	s.bottom = atol(buffer);
	
	/*
	 *	Adjust by the locks
	 */
	
	item->data->GetString(2,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		s.left += par.left;
	} else {
		s.left += par.right;
	}
	item->data->GetString(3,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		s.top += par.top;
	} else {
		s.top += par.bottom;
	}
	item->data->GetString(4,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		s.right += par.right;
	} else {
		s.right += par.left;
	}
	item->data->GetString(5,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		s.bottom += par.bottom;
	} else {
		s.bottom += par.top;
	}
	
	return s;
}

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	TRectEditView::TRectEditView
 *
 *		Build me
 */

TRectEditView::TRectEditView(XGOwner *owner, XGArgStream &stream) : XGView(owner,stream)
{
	fView = NULL;
	fSelect = NULL;
}

/*	TRectEditView::~TRectEditView
 *
 *		Delete me
 */

TRectEditView::~TRectEditView()
{
}

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	TRectEditView::DoDrawView
 *
 *		Draw me
 */

void TRectEditView::DoDrawView(Rect r)
{
	long x,y;
	XGScrollView *v;

	/*
	 *	Check this thing out
	 */
	
	XGDraw draw(this);
	draw.SetFont(XGFont::LoadFont(1));
	draw.Draw3DRect(r,KXGEBackground);
	if (fView == NULL) return;
	if (fView->view == NULL) return;
	
	/*
	 *	Now figure out and draw this thing
	 */
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);
	
	r = GetInitSize();
	::OffsetRect(&r,-x * 10, -y * 10);

	::InsetRect(&r,-2,-2);
	draw.Draw3DRect(r,KXGEInset);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.EraseRect(r);
	
	/*
	 *	Start drawing the contents
	 */
	
	DrawItem(draw,fView->view,r);
}

/*	TRectEditView::DoSizeView
 *
 *		handle resize
 */

void TRectEditView::DoSizeView()
{
	RecalcScroll();
	InvalView();
}

/*	InvertFrame
 *
 *		Invert the rectangle frame by inverting the rectangles that
 *	define the border
 */

static void InvertFrame(XGDraw &draw, Rect r)
{
	Rect s;
	
	s = r;
	s.right = s.left + 1;
	draw.InvertRect(s);
	
	s = r;
	s.left = s.right - 1;
	draw.InvertRect(s);
	
	r.left++;
	r.right--;
	
	s = r;
	s.bottom = s.top + 1;
	draw.InvertRect(s);
	
	s = r;
	s.top = s.bottom - 1;
	draw.InvertRect(s);
}

/*	TRectEditView::DoMouseDown
 *
 *		Handle mouse down event
 */

bool TRectEditView::DoMouseDown(Point where, short)
{
	Point pt;
	Rect r;
	int i;
	long x,y;
	XGScrollView *v;
	
	if (!SetFocus()) return false;
	if (fView == NULL) return false;
	if (fView->view == NULL) return false;
	
	/*
	 *	Figure out if I'm in the selected object
	 */
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	v->GetScrollValue(&x,&y);

	/*
	 *	Hit detect what object we're inside of.
	 */
	
	if (fSelect) {
		/*
		 *	Something is already selected. Do hit detection <<inside>>
		 *	of this object first.
		 */
		
		pt = where;
		pt.h += x * 10;
		pt.v += y * 10;
		fSelect = HitDetectInside(fSelect,pt);
		if (fSelect) {
			/*
			 *	Found something.
			 */
			
			fHitLoc = FindItemLocation(fSelect);
			::OffsetRect(&fHitLoc,-x * 10, -y * 10);
			fParLoc = FindParentLocation(fSelect);
			::OffsetRect(&fParLoc,-x * 10, -y * 10);
		}
	}
	
	if (!fSelect) {
		/*
		 *	If we get here, it's because we didn't click inside of
		 *	the current selection, or because there was no current
		 *	selection. Run again on the whole thing.
		 */
		
		pt = where;
		pt.h += x * 10;
		pt.v += y * 10;
		fSelect = HitDetectItem(pt);
		if (!fSelect) {
			/*
			 *	Not down in anything.
			 */
			
			DoDispatch(KSelectViewItem,0,(void *)NULL);
			InvalView();
			return false;
		} else {
			fHitLoc = FindItemLocation(fSelect);
			::OffsetRect(&fHitLoc,-x * 10, -y * 10);
			fParLoc = FindParentLocation(fSelect);
			::OffsetRect(&fParLoc,-x * 10, -y * 10);
		}
	}
		
	/*
	 *	We've selected something. Tell about it, and punt if it's
	 *	nothing I want to process
	 */
	
	DoDispatch(KSelectViewItem,0,(void *)fSelect);
	if (fSelect == fView->view) return false;
	if (fSelect == NULL) return false;
	
	/*
	 *	Find the handle
	 */
	
	for (i = 0; i < 8; i++) {
		r = CalcHilite(fHitLoc,i);
		if (PtInRect(where,&r)) break;
	}
	if (i >= 8) fHitState = -1;
	else fHitState = i;
	fHitStart = where;
	fHitCur = where;
	
	/*
	 *	And last: draw the drag rectangle
	 */
	
	XGDraw draw(this);
	draw.ClipRect(&fParLoc);
	InvertFrame(draw,fHitLoc);
	
	return true;
}

/*	TRectEditView::CalcDrag
 *
 *		Calculate the rectangle location for dragging. Note that when
 *	we are dragging an edge, we snap to a global grid; however, when
 *	we drag the object, we snap to a _delta_ grid.
 */

Rect TRectEditView::CalcDrag(Point where)
{
	Rect r;
	short tmp;
	
	r = fHitLoc;
	switch (fHitState) {
		case 0:
		case 1:
		case 2:
			r.top += where.v - fHitStart.v;
			if (where.v != fHitStart.v) r.top = SnapGrid(r.top);
			break;
		case 5:
		case 6:
		case 7:
			r.bottom += where.v - fHitStart.v;
			if (where.v != fHitStart.v) r.bottom = SnapGrid(r.bottom);
			break;
		case -1:
			tmp = SnapGrid(where.v - fHitStart.v);
			r.top += tmp;
			r.bottom += tmp;
			break;
	}
	
	switch (fHitState) {
		case 0:
		case 3:
		case 5:
			r.left += where.h - fHitStart.h;
			if (where.h != fHitStart.h) r.left = SnapGrid(r.left);
			break;
		case 2:
		case 4:
		case 7:
			r.right += where.h - fHitStart.h;
			if (where.h != fHitStart.h) r.right = SnapGrid(r.right);
			break;
		case -1:
			tmp = SnapGrid(where.h - fHitStart.h);
			r.left += tmp;
			r.right += tmp;
			break;
	}
	
	return r;
}


/*	TRectEditView::DoMouseMove
 *
 *		Handle mouse move event
 */

void TRectEditView::DoMouseMove(Point where, short flags)
{
	Rect r;
	Rect s;
	
	if (flags & KCapturedMouse) {
		if ((fHitCur.h == where.h) && (fHitCur.v == where.v)) return;
		
		r = CalcDrag(fHitCur);
		fHitCur = where;
		s = CalcDrag(where);
		
		XGDraw draw(this);
		draw.ClipRect(&fParLoc);
		
		InvertFrame(draw,r);
		InvertFrame(draw,s);
	} else {
		XGView::DoMouseMove(where,flags);
	}
}

/*	TRectEditView::DoMouseUp
 *
 *		Handle mouse up event
 */

void TRectEditView::DoMouseUp(Point where, short flags)
{
	Rect r;
	long x,y;
	XGScrollView *v;
	
	if (flags & KCapturedMouse) {
		r = CalcDrag(fHitCur);
		
		XGDraw draw(this);
		draw.ClipRect(&fParLoc);
		InvertFrame(draw,r);
		
		v = dynamic_cast<XGScrollView *>(GetParent());
		v->GetScrollValue(&x,&y);
		::OffsetRect(&r,10*x,10*y);
		SetItemLocation(fSelect,r);
	} else {
		XGView::DoMouseUp(where,flags);
	}
}

/************************************************************************/
/*																		*/
/*	Snap Support														*/
/*																		*/
/************************************************************************/

/*	TRectEditView::SnapGrid
 *
 *		Snap to grid support. This takes the point and snaps it to the
 *	resolution specified.
 */

short TRectEditView::SnapGrid(short where)
{
	short tmp;
	short s2;
	
	if (!GSnapGridFlag) return where;
	
	s2 = GSnapGridSize/2;
	tmp = (where+s2) % GSnapGridSize;
	if (tmp < 0) tmp += GSnapGridSize;
	tmp -= s2;
	where -= tmp;
	
	return where;
}

/************************************************************************/
/*																		*/
/*	Event Support														*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(TRectEditView,XGView)
	DISPATCHTABLE(KXGKeyEvent,TRectEditView::DoKey)
ENDDISPATCH

/*	TRectEditView::DoKey
 *
 *		Handle keyboard event. This handles the arrow commands to
 *	handle nudging this thing.
 */

long TRectEditView::DoKey(long arg, void *)
{
	short msg = GETHIWORD(arg);
	short key = GETLOWORD(arg);
	Rect r;
	
	if (msg & KSpecialKey) {
		switch (key) {
			case KKeyUpCursor:
			case KKeyDownCursor:
			case KKeyLeftCursor:
			case KKeyRightCursor:
				/*
				 *	Get the size of me
				 */
				
				if (fSelect == NULL) return -1;
				if (fSelect == fView->view) return -1;
				if (fView == NULL) return -1;
				if (fView->view == NULL) return -1;				
				r = FindItemLocation(fSelect);
				switch (key) {
					case KKeyUpCursor:
						r.top--;
						r.bottom--;
						break;
					case KKeyDownCursor:
						r.top++;
						r.bottom++;
						break;
					case KKeyLeftCursor:
						r.left--;
						r.right--;
						break;
					case KKeyRightCursor:
						r.left++;
						r.right++;
						break;
				}
				SetItemLocation(fSelect,r);
				
				return 0;
			default:
				break;
		}
	}
	
	return -1;
}

/************************************************************************/
/*																		*/
/*	Scroll Calculation													*/
/*																		*/
/************************************************************************/

/*	TRectEditView::RecalcScroll
 *
 *		Recalculate the scroll values
 */

void TRectEditView::RecalcScroll()
{
	Rect r;
	Rect s;
	XGScrollView *v;
	long x,y;
	
	v = dynamic_cast<XGScrollView *>(GetParent());
	r = GetInitSize();
	s = GetContentRect();
	
	v->SetPixelStep(10);
	x = (r.right - r.left) / 10;
	y = (r.bottom - r.top) / 10;
	if (x < 1) x = 1;
	if (y < 1) y = 1;
	v->SetPageSize(x,y);
	
	x = ((r.right - r.left) - (s.right - s.left - 20) + 9) / 10;
	y = ((r.bottom - r.top) - (s.bottom - s.top - 20) + 9) / 10;
	if (x < 0) x = 0;
	if (y < 0) y = 0;
	v->SetMaxValue(x,y);
}

/************************************************************************/
/*																		*/
/*	Support																*/
/*																		*/
/************************************************************************/

/*	TRectEditView::DrawItem
 *
 *		Draw the children and then this item
 */

void TRectEditView::DrawItem(XGDraw &draw, ViewItemDeclaration *item, Rect where)
{
	ViewItemDeclaration *child;
	Rect newloc;
	
	where = DrawViewItem(draw,item,where);
	for (child = item->child; child != NULL; child = child->sibling) {
		newloc = GetViewSize(child,where);
		DrawItem(draw,child,newloc);
	}
}

/*	TRectEditView::DrawViewItem
 *
 *		Draw the specified item
 */

Rect TRectEditView::DrawViewItem(XGDraw &draw, ViewItemDeclaration *item, Rect r)
{
	ViewTypeRecord *type;
	TResDocument *doc;
	ViewTypeRecord *tptr;
	char buffer[256];
	Rect s;
	long l;
	XGFont *ofont;
	long fnum;
	bool done;
	
	doc = dynamic_cast<TResDocument *>(GetDocument());
	type = doc->GetViewTypeByType(item->viewType);
	
	/*
	 *	Is this a window thingy?
	 */
	
	tptr = type;
	while (tptr->childPtr != NULL) tptr = tptr->childPtr;
	if (tptr->viewType == 'wind') {
		/*
		 *	Window type.
		 */
		
		draw.Draw3DRect(r,KXGEFrame);
		s = r;
		s.bottom = s.top + 21;
		
		draw.SetForeColor(KXGColorWhite);
		draw.SetBackColor(KXGColorBlack);
		draw.EraseRect(s);
		draw.ClipRect(&s);
		
		item->data->GetString(15,buffer);
		draw.MoveTo(s.left+14,s.top+14);
		draw.DrawString(buffer);
		
		draw.ClipRect();
		draw.SetForeColor(KXGColorBlack);
		draw.SetBackColor(KXGColorWhite);
		
		if (fSelect == item) DrawHilite(draw,r);
		
		::InsetRect(&r,1,1);
		r.top += 20;
		return r;
	}
	
	/*
	 *	Draw an outline and draw a sketch of me
	 */
	
	draw.EraseRect(r);
	draw.Draw3DRect(r,KXGEFrame);
	
	ofont = draw.GetFont();
	s = r;
	
	/*
	 *	Draw the view. This scans down the type until I find
	 *	a type I know about, or until I run out
	 */
	
	done = false;
	while (!done) {
		done = true;
		switch (type->viewType) {
			default:
				if (type->childPtr) {
					type = type->childPtr;
					done = false;
					break;
				}
				
				/* Fall through to */
			case 'view':
				/*
				 *	All views are drawn as the default font
				 */
				
				DrawView(draw,r);
				break;
			case 'scrv':
				/*
				 *	Scroll view
				 */
				
				r = DrawScrollView(draw,item,r);
				break;
			case 'scrb':
				/*
				 *	Scroll bar
				 */
				
				DrawScrollBar(draw,r);
				break;
			case 'pbtn':
				/*
				 *	Buttons
				 */
				
				item->data->GetString(13,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(15,buffer);
				DrawButton(draw,buffer,r);
				break;
			case 'radb':
				/*
				 *	Radio Buttons
				 */
				
				item->data->GetString(13,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(15,buffer);
				DrawControlButton(draw,false,buffer,r);
				break;
			case 'chkb':
				/*
				 *	Check Buttons
				 */
				
				item->data->GetString(13,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(15,buffer);
				DrawControlButton(draw,true,buffer,r);
				break;
			case 'text':
				/*
				 *	Text
				 */
				
				item->data->GetString(14,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(13,buffer);
				DrawText(draw,buffer,r);
				break;
			case 'edit':
				/*
				 *	Edit
				 */
				
				item->data->GetString(14,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(13,buffer);
				DrawEdit(draw,buffer,r);
				break;
			case 'grou':
				/*
				 *	Group
				 */
				
				item->data->GetString(14,buffer);
				fnum = atol(buffer);
				if (fnum == 0) draw.SetFont(XGFont::LoadFont(0));
				else draw.SetFont(XGFont::LoadFont(1));
				
				item->data->GetString(13,buffer);
				DrawGroup(draw,buffer,r);
				break;
		}
	}

	draw.SetFont(ofont);
	
	/*
	 *	Draw the view ID if it's non-zero. Draws it as a black rectangle
	 *	in the upper left of this thing
	 */
	
	item->data->GetString(0,buffer);
	l = atol(buffer);
	sprintf(buffer,"%ld",l);
	if (l != 0) {
		Rect s;
		
		s = r;
		s.bottom = s.top + draw.GetFontHeight();
		s.left = s.right - draw.StringWidth(buffer) - 4;
		
		draw.SetForeColor(KXGColorWhite);
		draw.SetBackColor(KXGColorBlack);
		
		draw.EraseRect(s);
		draw.MoveTo(s.left+2,s.top+draw.GetFontAscent());
		draw.DrawString(buffer);
		
		draw.SetForeColor(KXGColorBlack);
		draw.SetBackColor(KXGColorWhite);
	}
	
	/*
	 *	Now draw the hilite rectangles
	 */
	
	if (fSelect == item) DrawHilite(draw,s);
	
	return r;
}

/*	TRectEditView::GetInitSize
 *
 *		Get the initial size of this thing. This is determined by what
 *	type of view this thing is based on.
 */

Rect TRectEditView::GetInitSize(void)
{
	char buffer[256];
	Point min,max;
	Rect r;
	short tmp;
	TResDocument *doc;
	ViewTypeRecord *type;
	
	r = GetContentRect();
	::InsetRect(&r,10,10);
	if (fView == NULL) return r;
	if (fView->view == NULL) return r;
	
	/*
	 *	Is this based on a window?
	 */
	
	doc = dynamic_cast<TResDocument *>(GetDocument());
	type = doc->GetViewTypeByType(fView->view->viewType);
	while (type->childPtr != NULL) type = type->childPtr;
	if (type->viewType != 'wind') return r;
	
	/*
	 *	This is based on a window. Get the min, max sizes, and limit
	 *	the rectangle's growth
	 */
	
	fView->view->data->GetString(7,buffer);
	min.h = atol(buffer);
	fView->view->data->GetString(8,buffer);
	min.v = atol(buffer);
	fView->view->data->GetString(9,buffer);
	max.h = atol(buffer);
	fView->view->data->GetString(10,buffer);
	max.v = atol(buffer);

	r.bottom -= 22;
	r.right -= 2;
	
	tmp = r.right - r.left;
	if (min.h > 0) if (tmp < min.h) tmp = min.h;
	if (max.h > 0) if (tmp > max.h) tmp = max.h;
	r.right = r.left + tmp;
	
	tmp = r.bottom - r.top;
	if (min.v > 0) if (tmp < min.v) tmp = min.v;
	if (max.v > 0) if (tmp > max.v) tmp = max.v;
	r.bottom = r.top + tmp;
	
	r.bottom += 22;
	r.right += 2;
	
	return r;
}

/************************************************************************/
/*																		*/
/*	Hilites																*/
/*																		*/
/************************************************************************/

/*	TRectEditView::DrawHilite
 *
 *		Draw the 8 hilite rectangles
 */

void TRectEditView::DrawHilite(XGDraw &draw, Rect r)
{
	Rect s;
	int i;
	
	draw.SetForeColor(KXGColorHilite);
	for (i = 0; i < 8; i++) {
		s = CalcHilite(r,i);
		draw.PaintRect(s);
	}
	draw.SetForeColor(KXGColorBlack);
}

/*	TRectEditView::CalcHilite
 *
 *		Calculate the location of the 8 hilite rectangles
 */

Rect TRectEditView::CalcHilite(Rect r, short i)
{
	switch (i) {
		case 0:
		case 1:
		case 2:
			r.bottom = r.top + 5;
			break;
		case 3:
		case 4:
			r.top = (r.top + r.bottom - 2) / 2;
			r.bottom = r.top + 5;
			break;
		case 5:
		case 6:
		case 7:
			r.top = r.bottom - 5;
			break;
	}
	
	switch (i) {
		case 0:
		case 3:
		case 5:
			r.right = r.left + 5;
			break;
		case 1:
		case 6:
			r.left = (r.left + r.right - 2) / 2;
			r.right = r.left + 5;
			break;
		case 2:
		case 4:
		case 7:
			r.left = r.right - 5;
			break;
	}
	
	return r;
}

/************************************************************************/
/*																		*/
/*	View Drawing														*/
/*																		*/
/************************************************************************/

/*	TRectEditView::DrawView
 *
 *		Draw the view
 */

void TRectEditView::DrawView(XGDraw &draw, Rect r)
{
	draw.Draw3DRect(r,KXGEOutset);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
}

/*	TRectEditView::DrawScrollBar
 *
 *		Draw the scrollbar
 */

void TRectEditView::DrawScrollBar(XGDraw &draw, Rect r)
{
	short x,y;
	Rect s;
	
	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
	
	x = r.right-r.left;
	y = r.bottom-r.top;
	if (x > y) {
		s = r;
		s.right = s.left + y;
		draw.Draw3DRect(s,KXGEOutset);
		s = r;
		s.left = s.right - y;
		draw.Draw3DRect(s,KXGEOutset);
		s = r;
		s.left += y;
		s.right -= y;
		if (s.left < s.right) {
			draw.Draw3DRect(s,KXGEInset);
		}
	} else {
		s = r;
		s.bottom = s.top + x;
		draw.Draw3DRect(s,KXGEOutset);
		s = r;
		s.top = s.bottom - x;
		draw.Draw3DRect(s,KXGEOutset);
		s = r;
		s.top += x;
		s.bottom -= x;
		if (s.top < s.bottom) {
			draw.Draw3DRect(s,KXGEInset);
		}
	}
}

/*	TRectEditView::DrawScrollView
 *
 *		Draw the scrollbar view
 */

Rect TRectEditView::DrawScrollView(XGDraw &draw, ViewItemDeclaration *view, Rect r)
{
	char buffer[256];
	bool h,v,c;
	Rect s;
	
	/*
	 *	Bounds
	 */
	
	view->data->GetString(13,buffer);
	h = ((buffer[0] == 't') || (buffer[0] == 'T'));
	view->data->GetString(14,buffer);
	v = ((buffer[0] == 't') || (buffer[0] == 'T'));
	view->data->GetString(15,buffer);
	c = ((buffer[0] == 't') || (buffer[0] == 'T'));
	if (h && v) c = true;
	
	/*
	 *	Draw the boundary
	 */
	
	draw.Draw3DRect(r,KXGEFrame);
	if (c) {
		s = r;
		s.left = s.right - 16;
		s.top = s.bottom - 16;
		draw.Draw3DRect(s,KXGEBackground);
	}
	if (v) {
		s = r;
		s.left = s.right - 16;
		if (c) s.bottom -= 15;
		DrawScrollBar(draw,s);
	}
	if (h) {
		s = r;
		s.top = s.bottom - 16;
		if (c) s.right -= 15;
		DrawScrollBar(draw,s);
	}
	
	s = r;
	::InsetRect(&s,1,1);
	if (h) s.bottom -= 15;
	if (v) s.right -= 15;
	return s;
}

/*	TRectEditView::DrawButton
 *
 *		Draw a button
 */

void TRectEditView::DrawButton(XGDraw &draw, char *str, Rect r)
{
	short x,y;
	
	x = (r.right + r.left - draw.StringWidthA(str)) / 2;
	y = (r.bottom + r.top - draw.GetFontHeight()) / 2 + draw.GetFontAscent();

	::InsetRect(&r,4,4);
	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEOutset);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
	
	draw.ClipRect(&r);
	draw.MoveTo(x,y);
	draw.DrawStringA(str);
	draw.ClipRect();
}

/*	TRectEditView::DrawControlButton
 *
 *		Draw check or radio button
 */

void TRectEditView::DrawControlButton(XGDraw &draw, bool check, char *str, Rect r)
{
	short x,y;
	Rect s;
	
	s.top = (r.top + r.bottom - 12) / 2;
	s.bottom = s.top + 12;
	s.left = r.left + 2;
	s.right = s.left + 12;
	
	x = r.left + 14;
	y = (r.bottom + r.top - draw.GetFontHeight()) / 2 + draw.GetFontAscent();

	draw.Draw3DRect(r,KXGEBackground);
	draw.ClipRect(&r);
	draw.MoveTo(x,y);
	draw.DrawStringA(str);
	draw.ClipRect();
	
	if (check) {
		draw.Draw3DRect(s,KXGEFrame);
	} else {
		draw.FrameOval(s);
	}
}

/*	TRectEditView::DrawText
 *
 *		Draw text
 */

void TRectEditView::DrawText(XGDraw &draw, char *text, Rect r)
{
	short x,y;
	
	x = r.left + 2;
	y = (r.bottom + r.top - draw.GetFontHeight()) / 2 + draw.GetFontAscent();

	draw.ClipRect(&r);
	draw.MoveTo(x,y);
	draw.DrawStringA(text);
	draw.ClipRect();
}

/*	TRectEditView::DrawEdit
 *
 *		Draw editor
 */

void TRectEditView::DrawEdit(XGDraw &draw, char *text, Rect r)
{
	short x,y;
	
	x = r.left + 2;
	y = (r.bottom + r.top - draw.GetFontHeight()) / 2 + draw.GetFontAscent();

	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEInset);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
	draw.ClipRect(&r);
	draw.MoveTo(x,y);
	draw.DrawStringA(text);
	draw.ClipRect();
}

/*	TRectEditView::DrawGroup
 *
 *		Draw the group box
 */

void TRectEditView::DrawGroup(XGDraw &draw, char *text, Rect r)
{
	short x,y;
	
	x = r.left + 5;
	y = r.top + draw.GetFontAscent();
	
	draw.ClipRect(&r);
	draw.MoveTo(x,y);
	draw.DrawStringA(text);
	draw.MoveTo(x,y);
	draw.LineTo(r.left,y);
	draw.LineTo(r.left,r.bottom-1);
	draw.LineTo(r.right-1,r.bottom-1);
	draw.LineTo(r.right-1,y);
	draw.LineTo(x+draw.StringWidthA(text),y);
	draw.ClipRect();
}

/************************************************************************/
/*																		*/
/*	Hit Detection Support												*/
/*																		*/
/************************************************************************/

/*	HitDetectCore
 *
 *		Hit detect a single item
 */

static ViewItemDeclaration *HitDetectCore(Rect r, 
										  ViewItemDeclaration *item, 
										  Point where)
{
	ViewItemDeclaration *i;
	Rect s;
	
	if (!PtInRect(where,&r)) return NULL;
	r = GetInsideSize(item,r);
	for (i = item->child; i != NULL; i = i->sibling) {
		register ViewItemDeclaration *t;
		
		s = GetViewSize(i,r);
		t = HitDetectCore(s,i,where);
		if (t) return t;
	}
	return item;
}

/*	TRectEditView::HitDetectInside
 *
 *		Handle hit detection inside an object
 */

ViewItemDeclaration *TRectEditView::HitDetectInside(ViewItemDeclaration *i, Point where)
{
	Rect r;
	
	if (i == NULL) return NULL;
	GDoc = dynamic_cast<TResDocument *>(GetDocument());
	r = FindItemLocation(i);
	return HitDetectCore(r,i,where);
}

/*	TRectEditView::HitDetectItem
 *
 *		Handle hit detection
 */

ViewItemDeclaration *TRectEditView::HitDetectItem(Point where)
{
	if (fView == NULL) return NULL;
	return HitDetectInside(fView->view,where);
}

/*	FindLocation
 *
 *		Find the location of this thing
 */

static bool FindLocation(Rect r, ViewItemDeclaration *item)
{
	Rect s;
	
	if (item == GViewItem) {
		GWhere = r;
		return true;
	}
	
	r = GetInsideSize(item,r);
	for (item = item->child; item != NULL; item = item->sibling) {
		s = GetViewSize(item,r);
		if (FindLocation(s,item)) return true;
	}
	return false;
}

/*	TRectEditView::FindItemLocation
 *
 *		Find the location of this item
 */

Rect TRectEditView::FindItemLocation(ViewItemDeclaration *item)
{
	GWhere.left = 0, GWhere.top = 0, GWhere.right = 0, GWhere.bottom = 0;
	if (fView == NULL) return GWhere;
	if (fView->view == NULL) return GWhere;

	GDoc = dynamic_cast<TResDocument *>(GetDocument());
	GViewItem = item;
	FindLocation(GetInitSize(),fView->view);
	return GWhere;
}

/*	FindParLocation
 *
 *		Find the location of the parent of the specified item.
 */

static bool FindParLocation(Rect r, ViewItemDeclaration *item)
{
	ViewItemDeclaration *i;
	Rect s;
		
	/*
	 *	First, see if I'm the parent
	 */
	
	r = GetInsideSize(item,r);
	for (i = item->child; i != NULL; i = i->sibling) {
		if (i == GViewItem) {
			GWhere = r;
			return true;
		}
	}
	
	/*
	 *	Not there? Scan children
	 */
	
	for (i = item->child; i != NULL; i = i->sibling) {
		s = GetViewSize(i,r);
		if (FindParLocation(s,i)) return true;
	}
	return false;
}

/*	TRectEditView::FindParentLocation
 *
 *		Find the parent location of this item
 */

Rect TRectEditView::FindParentLocation(ViewItemDeclaration *item)
{
	GWhere.left = 0, GWhere.top = 0, GWhere.right = 0, GWhere.bottom = 0;
	if (fView == NULL) return GWhere;
	if (fView->view == NULL) return GWhere;
	if (fView->view == item) return GetContentRect();

	GDoc = dynamic_cast<TResDocument *>(GetDocument());
	GViewItem = item;
	FindParLocation(GetInitSize(),fView->view);
	return GWhere;
}

/*	TRectEditView::SetItemLocation
 *
 *		Set the item's location
 */

void TRectEditView::SetItemLocation(ViewItemDeclaration *item, Rect r)
{
	Rect s;
	char buffer[256];
	TResDocument *doc;
	
	s = FindParentLocation(item);
	
	item->data->GetString(2,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		r.left = r.left - s.left;
	} else {
		r.left = r.left - s.right;
	}
	item->data->GetString(3,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		r.top = r.top - s.top;
	} else {
		r.top = r.top - s.bottom;
	}
	item->data->GetString(4,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		r.right = r.right - s.right;
	} else {
		r.right = r.right - s.left;
	}
	item->data->GetString(5,buffer);
	if ((buffer[0] == 't') || (buffer[0] == 'T')) {
		r.bottom = r.bottom - s.bottom;
	} else {
		r.bottom = r.bottom - s.top;
	}
	
	sprintf(buffer,"%d",r.left);
	item->data->SetString(6,buffer);
	sprintf(buffer,"%d",r.top);
	item->data->SetString(7,buffer);
	sprintf(buffer,"%d",r.right);
	item->data->SetString(8,buffer);
	sprintf(buffer,"%d",r.bottom);
	item->data->SetString(9,buffer);
	
	doc = dynamic_cast<TResDocument *>(GetDocument());
	doc->MarkDirty();
	DoDispatch(KUpdateViewItem,0,NULL);
	RecalcScroll();
	InvalView();
}
