/*	XGScrollBar.cpp
 *
 *		This is the scrollbar
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; 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 <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XMessages.h>
#include <XDraw.h>
#include <XScroll.h>
#include <XWindow.h>
#include <XCursorUtil.h>

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

/*	@F"XGScrollBar::XGScrollBar" @I
 *
 *		Construct my scrollbar
 */

XGScrollBar::XGScrollBar(XGOwner *p, XGSScrollInitRecord &init) : XGSlider(p,init.view)
{
#if OPT_MACOS || OPT_WINOS
	fControl = NULL;
#endif

	fPage = 1;
	fStep = 1;
	fValue = 0;
	fMinValue = 0;
	fMaxValue = 0;

	InitScrollBar(init);
}

XGScrollBar::XGScrollBar(XGOwner *p, XGArgStream &s) : XGSlider(p,s)
{
	XGSScrollInitRecord init;
	
	fPage = 1;
	fStep = 1;
	fValue = 0;
	fMinValue = 0;
	fMaxValue = 0;

	init.fValue = s.ReadInteger();
	init.fMinValue = s.ReadInteger();
	init.fMaxValue = s.ReadInteger();
	init.fStepValue = s.ReadInteger();
	init.fPageValue = s.ReadInteger();
	InitScrollBar(init);
}

/*	@F"XGScrollBar::~XGScrollBar" @I
 *
 *		Delete this
 */

XGScrollBar::~XGScrollBar()
{
#if OPT_MACOS
	if (fControl) ::DisposeControl(fControl);
#endif

#if OPT_WINOS
	if (fControl) ::DestroyWindow(fControl);
#endif
}

/************************************************************************/
/*																		*/
/*	Scrollbar Tracking													*/
/*																		*/
/************************************************************************/

#if OPT_MACOS

static XGScrollBar		*GBar;

/*	TrackScrollbar
 *
 *		Track the scrollbar's values
 */

pascal void XGScrollBar::Track(ControlHandle, short part)
{
	long value;
	
	value = GBar->GetValue();
	switch (part) {
		case kControlUpButtonPart:
			value -= GBar->GetStepSize();
			break;
		case kControlDownButtonPart:
			value += GBar->GetStepSize();
			break;
		case kControlPageUpPart:
			value -= GBar->GetPageSize();
			break;
		case kControlPageDownPart:
			value += GBar->GetPageSize();
			break;
		default:
			return;
	}
	
	if (value < GBar->fMinValue) value = GBar->fMinValue;
	if (value > GBar->fMaxValue) value = GBar->fMaxValue;
	if (value != GBar->fValue) {
		XGSValue v;
		
		v.slider = GBar;
		v.oldVal = GBar->fValue;
		v.newVal = value;
		GBar->fValue = value;
		{
			XGDraw draw(GBar,false);
			::SetControlValue(GBar->fControl,value/(GBar->fScale));
		}
		GBar->DoDispatch(KXGSliderValue,GBar->GetViewID(),(void *)&v);
	}
}

#endif

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

/*	@F"XGScrollBar::DoDrawView" @I
 *
 *		Handle the drawing
 */

void XGScrollBar::DoDrawView(Rect)
{
#if OPT_MACOS
	if (fControl) {
		XGDraw draw(this,false);
		
		if (IsEnabled() && IsActive()) {
			/*
			 *	Enabled
			 */
			
			::HiliteControl(fControl,0);
		} else {
			::HiliteControl(fControl,255);
		}
		::ShowControl(fControl);
		::Draw1Control(fControl);
	}
#endif

#if OPT_WINOS
	if (fControl && IsVisible()) {
		::ShowWindow(fControl,SW_SHOW);
	}
#endif

#if OPT_XWIN
	DrawScrollBar(-1);
#endif
}

/*	@F"XGScrollBar::DoActivate" @I
 *
 *		Activate/deactivate.
 */

void XGScrollBar::DoActivate(bool)
{
#if OPT_MACOS || OPT_XWIN
	if (IsEnabled()) {
		InvalView();
	}
#endif

#if OPT_WINOS
	/* Does nothing; activation/deactivation handled automatically */
#endif
}

/*	@F"XGScrollBar::DoMouseDown" @I
 *
 *		Handle the mouse down event. Only used for the Macintosh
 */

bool XGScrollBar::DoMouseDown(Point pt, short a)
{
#if OPT_MACOS
	short i;
	
	if (fControl) {
		ControlHandle c;
		XGDraw draw(this,false);
		ViewToGlobal(&pt);
		
		i = ::FindControl(pt,GetRootWindow(),&c);
		if (c != fControl) return false;			//???
		
		/*
		 *	Now track this
		 */
		
		GBar = this;								// update at old
		if (i == kControlIndicatorPart) {
			/*
			 *	In the thumb? Track the thumb, and when it's dropped
			 *	to a new location, this updates the scrollbar's
			 *	stored value.
			 */
			
			if (::TrackControl(fControl,pt,NULL)) {
				long value;
				
				value = ::GetControlValue(fControl);
				if (value == ::GetControlMinimum(fControl)) {
					value = fMinValue;
				} else if (value == ::GetControlMaximum(fControl)) {
					value = fMaxValue;
				} else {
					value *= fScale;
					if (value < fMinValue) value = fMinValue;
					if (value > fMaxValue) value = fMaxValue;
				}
				
				SetValue(value);
			}
		} else {
			ControlActionUPP proc;
			
			DoDispatch(KXGSliderStart,GetViewID(),NULL);
			proc = NewControlActionProc(Track);
			::TrackControl(fControl,pt,proc);
			DisposeRoutineDescriptor(proc);
		}
	} else {
		XGControl::DoMouseDown(pt,a);
	}
	return false;
#endif

#if OPT_WINOS
	return XGControl::DoMouseDown(pt,a);
#endif

#if OPT_XWIN
	int i;
	Rect r;
	
	for (i = 0; i < 5; i++) {
		r = CalcScrollRect(i);
		if (::PtInRect(pt,&r)) break;
	}
	if (i >= 5) return false;
	
	fDown = true;
	fTrack = i;
	
	if (i == 2) {
		UpdateTrackValue(pt);
	} else {
		DrawScrollBar(fTrack);
	}
	return true;
#endif
}

/*	@F"XGScrollBar::DoMouseMove" @I
 *
 *		handle mouse move events
 */

void XGScrollBar::DoMouseMove(Point pt, short a)
{
#if OPT_XWIN
	Rect r;
	
	if (a & KCapturedMouse) {
		if (fTrack == 2) {
			UpdateTrackValue(pt);
		} else {
			r = CalcScrollRect(fTrack);
			if (::PtInRect(pt,&r)) {
				if (!fDown) {
					fDown = true;
					DrawScrollBar(fTrack);
				}
			} else {
				if (fDown) {
					fDown = false;
					DrawScrollBar(-1);
				}
			}
		}
	} else {
		::SetCursorID(KNormalCursor);
	}
#else
	XGControl::DoMouseMove(pt,a);
#endif
}

/*	@F"XGScrollBar::DoMouseUp" @I
 *
 *		handle mouse up
 */

void XGScrollBar::DoMouseUp(Point pt, short a)
{
#if OPT_XWIN
	if (fDown) {
		switch (fTrack) {
			case 0:
				SetValue(GetValue() - GetStepSize());
				break;
			case 1:
				SetValue(GetValue() - GetPageSize());
				break;
			case 2:
				break;
			case 3:
				SetValue(GetValue() + GetPageSize());
				break;
			case 4:
				SetValue(GetValue() + GetStepSize());
				break;
		}
		DrawScrollBar();
	}
#else
	XGControl::DoMouseUp(pt,a);
#endif
}

/*	@F"XGScrollBar::DoSizeView" @I
 *
 *		Handle resize event
 */

void XGScrollBar::DoSizeView()
{
#if OPT_MACOS || OPT_WINOS
	DoMoveView();
#endif

	/*
	 *	Pass upwards
	 */
	
	XGControl::DoSizeView();
}

/*	@F"XGScrollBar::DoMoveView" @I
 *
 *		Move the view.
 */

void XGScrollBar::DoMoveView()
{
#if OPT_MACOS || OPT_WINOS
	Rect r;
	
	if (!fControl) return;
	r = GetContentRect();
	ViewToGlobal(&r);
	
#if OPT_MACOS
	XGDraw draw(this,false);
	::HideControl(fControl);
	::MoveControl(fControl,r.left,r.top);
	::SizeControl(fControl,r.right-r.left,r.bottom-r.top);
#endif

#if OPT_WINOS
	::SetWindowPos(fControl,
				   NULL,
				   r.left,
				   r.top,
				   r.right-r.left,
				   r.bottom-r.top,
				   SWP_NOZORDER | SWP_HIDEWINDOW);
#endif
#endif
}

/*	@F"XGScrollBar::DoShowHide" @I
 *
 *		This is called when the control is to be hidden or
 *	shown. This only needs to hide the control; the call to
 *	DoDrawView() will show the control if needed.
 */

void XGScrollBar::DoShowHide()
{
#if OPT_MACOS
	if (!IsVisible() && fControl) {
		XGDraw draw(this,false);
		::HideControl(fControl);
		::MoveControl(fControl,-20,-20);
		::SizeControl(fControl,10,10);
	} else {
		if (fControl) {
			Rect r;
			
			r = GetContentRect();
			ViewToGlobal(&r);
			::MoveControl(fControl,r.left,r.top);
			::SizeControl(fControl,r.right-r.left,r.bottom-r.top);
		}
	}
#endif

#if OPT_WINOS
	if (!IsVisible() && fControl) {
		::ShowWindow(fControl,SW_HIDE);
	}
#endif

	XGView::DoShowHide();
}

/*	@F"XGScrollBar::DoEnable" @I
 *
 *		Handle enable/disable events. Passes the enable/disable
 *	event to the control
 */

void XGScrollBar::DoEnable()
{
#if OPT_WINOS
	if (fControl) ::EnableWindow(fControl,IsEnabled());
#endif
}

#if OPT_WINOS

/*	@F"XGScrollBar::SetFocus" @I
 *
 *		Set the focus. This overrides stuff on Windows by setting the
 *	MS Window focus as well.
 */

bool XGScrollBar::SetFocus(void)
{
	if (XGDispatch::SetFocus()) {
		::SetFocus(fControl);
		return true;
	} else return false;
}

#endif

/************************************************************************/
/*																		*/
/*	Value Management													*/
/*																		*/
/************************************************************************/

/*	@F"XGScrollBar::GetValue" @I
 *
 *		Get the current value
 */

long XGScrollBar::GetValue()
{
	return fValue;
}

/*	@F"XGScrollBar::GetMinValue" @I
 *
 *		Get the current value
 */

long XGScrollBar::GetMinValue()
{
	return fMinValue;
}

/*	@F"XGScrollBar::GetMaxValue" @I
 *
 *		Get the current value
 */

long XGScrollBar::GetMaxValue()
{
	return fMaxValue;
}

/*	@F"XGScrollBar::GetStepSize" @I
 *
 *		Get the step size
 */

long XGScrollBar::GetStepSize()
{
	return fStep;
}

/*	@F"XGScrollBar::GetPageSize" @I
 *
 *		Get the page size
 */

long XGScrollBar::GetPageSize()
{
	return fPage;
}


/*	@F"XGScrollBar::SetStepSize" @I
 *
 *		Set the step size; the amount things step
 */

void XGScrollBar::SetStepSize(long size)
{
	fStep = size;
}

/*	@F"XGScrollBar::SetPageSize" @I
 *
 *		Set the page size; the amount this thing steps
 *	by when paged up or down
 */

void XGScrollBar::SetPageSize(long size)
{
	fPage = size;

#if OPT_XWIN
	DrawScrollBar();
#endif

#if OPT_WINOS
	LoadControl();
#endif
}

/*	@F"XGScrollBar::SetMinValue" @I
 *
 *		Set the minimum value
 */

void XGScrollBar::SetMinValue(long min)
{
	long value;
	XGSValue v;

	if (min > fMaxValue) min = fMaxValue;		// don't do this...
	if (min == fMinValue) return;
	fMinValue = min;
	
	/*
	 *	Make sure the value is set correctly
	 */
		
	value = fValue;
	if (value < fMinValue) value = fMinValue;
	if (value > fMaxValue) value = fMaxValue;

	/*
	 *	And send the messages indicating the value changed
	 */
	
	if (value != fValue) {
		DoDispatch(KXGSliderStart,GetViewID(),NULL);
		v.slider = this;
		v.oldVal = fValue;
		v.newVal = value;
		fValue = value;
		DoDispatch(KXGSliderValue,GetViewID(),(void *)&v);
	}

	/*
	 *	Now update the scrollbar
	 */
	
#if OPT_MACOS || OPT_WINOS
	AdjustScale();
	LoadControl();							// causes control redraw
#endif

#if OPT_XWIN
	DrawScrollBar();						// redraw the bar
#endif
}

/*	@F"XGScrollBar::SetMaxValue" @I
 *
 *		Set the maximum value
 */

void XGScrollBar::SetMaxValue(long max)
{
	long value;
	XGSValue v;

	if (max < fMinValue) max = fMinValue;		// don't do this...
	if (max == fMaxValue) return;
	fMaxValue = max;
	
	/*
	 *	Make sure the value is set correctly
	 */
		
	value = fValue;
	if (value < fMinValue) value = fMinValue;
	if (value > fMaxValue) value = fMaxValue;

	/*
	 *	And send the messages indicating the value changed
	 */
	
	if (value != fValue) {
		DoDispatch(KXGSliderStart,GetViewID(),NULL);
		v.slider = this;
		v.oldVal = fValue;
		v.newVal = value;
		fValue = value;
		DoDispatch(KXGSliderValue,GetViewID(),(void *)&v);
	}

	/*
	 *	Now update the scrollbar
	 */
	
#if OPT_MACOS || OPT_WINOS
	AdjustScale();
	LoadControl();							// causes control redraw
#endif

#if OPT_XWIN
	DrawScrollBar();						// redraw the bar
#endif
}

/*	@F"XGScrollBar::SetValue" @I
 *
 *		Set the value
 */

void XGScrollBar::SetValue(long value)
{
	XGSValue v;
	
	/*
	 *	Make sure the value is set correctly
	 */
		
	if (value < fMinValue) value = fMinValue;
	if (value > fMaxValue) value = fMaxValue;

	/*
	 *	And send the messages indicating the value changed
	 */
	
	if (value != fValue) {
		DoDispatch(KXGSliderStart,GetViewID(),NULL);
		v.slider = this;
		v.oldVal = fValue;
		v.newVal = value;
		fValue = value;
		DoDispatch(KXGSliderValue,GetViewID(),(void *)&v);
	}

	/*
	 *	Now update the scrollbar
	 */
	
#if OPT_MACOS || OPT_WINOS
	AdjustScale();
	LoadControl();							// causes control redraw
#endif

#if OPT_XWIN
	DrawScrollBar();						// redraw the bar
#endif
}


/************************************************************************/
/*																		*/
/*	Construction Support												*/
/*																		*/
/************************************************************************/

/*	@F"XGScrollBar::InitScrollBar" @I
 *
 *		Initialize the scrollbar
 */

void XGScrollBar::InitScrollBar(XGSScrollInitRecord &init)
{
#if OPT_MACOS
	Rect r;
	
	if (IsVisible()) {
		r = GetContentRect();
		ViewToGlobal(&r);
	} else {
		r.left = -20,
		r.top = -20,
		r.right = -10,
		r.bottom = -10;
	}
	fControl = ::NewControl(GetRootWindow(),
							&r,
							"\p",
							false,
							0,
							0,
							0,
							16,
							0L);
#endif

#if OPT_WINOS
	Rect r;
	long attrib;
	
	r = GetContentRect();
	ViewToGlobal(&r);
	
	attrib = WS_CHILD;
	if (r.right - r.left > r.bottom - r.top) attrib |= SBS_HORZ;
	else attrib |= SBS_VERT;
	
	fControl = ::CreateWindow("SCROLLBAR",
							  "",
							  attrib,
							  r.left,
							  r.top,
							  r.right-r.left,
							  r.bottom-r.top,
							  GetRootWindow(),
							  NULL,
							  _GInstance,
							  NULL);
	
	::SetProp(fControl,_GControlClass,(HANDLE)this);
#endif

	SetMinValue(init.fMinValue);
	SetMaxValue(init.fMaxValue);
	SetValue(init.fValue);
	SetPageSize(init.fPageValue);
	SetStepSize(init.fStepValue);
}

#if OPT_MACOS || OPT_WINOS

/*	@F"XGScrollBar::AdjustScale" @I
 *
 *		Adjust the scale parameter. This converts the min/max
 *	values so that they fit from 0 to 16384.
 */

void XGScrollBar::AdjustScale(void)
{
	long width;
	fScale = 1;
	
	width = fMaxValue - fMinValue;
	fScale = (width + 16383) / 16384;
	if (fScale < 1) fScale = 1;
}

/*	@F"XGScrollBar::LoadControl" @I
 *
 *		Load the control's values. Make sure the control is only
 *	updated once
 */

void XGScrollBar::LoadControl(void)
{
#if OPT_MACOS
	XGDraw draw(this,false);

	::SetControlMinimum(fControl,(short)(fMinValue/fScale));
	::SetControlMaximum(fControl,(short)(fMaxValue/fScale));
	::SetControlValue(fControl,(short)(fValue/fScale));
#endif

#if OPT_WINOS
	SCROLLINFO info;
	int mpage;
	
	info.cbSize = sizeof(info);
	info.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
	info.nMin = fMinValue / fScale;
	info.nMax = fMaxValue / fScale;
	info.nPage = fPage / fScale;
	info.nPos = fValue / fScale;
	
	mpage = info.nPage - 1;
	if (mpage < 0) mpage = 0;
	info.nMax += mpage;				// force max to *mean* max value

	if (fMaxValue <= fMinValue) {
		::EnableWindow(fControl,false);
	} else {
		::SetScrollInfo(fControl,SB_CTL,&info,true);
		::EnableWindow(fControl,true);
	}
#endif
}

#endif

/************************************************************************/
/*																		*/
/*	Default Sizes														*/
/*																		*/
/************************************************************************/

/*	@F"XGScrollBar::GetDefaultSize" @I
 *
 *		Get the scrollbar's default size. Used for placement by other
 *	routines
 */

Point XGScrollBar::GetDefaultSize()
{
	Point pt;
	
#if OPT_MACOS || OPT_XWIN
	pt.h = 16;
	pt.v = 16;
#endif

#if OPT_WINOS
	pt.h = ::GetSystemMetrics(SM_CXVSCROLL);
	pt.v = ::GetSystemMetrics(SM_CYHSCROLL);
#endif

	return pt;
}

/************************************************************************/
/*																		*/
/*	Windows Event Implementation										*/
/*																		*/
/************************************************************************/

#if OPT_WINOS

BEGINDISPATCH(XGScrollBar,XGView)
	DISPATCHTABLE(KXGWInternal,XGScrollBar::DoWInternal)
ENDDISPATCH

/*	XGScrollBar::DoWInternal
 *
 *		This is where messages that are received from my
 *	control is sent
 */

long XGScrollBar::DoWInternal(long,void *ptr)
{
	XGSWInternalRecord *r;
	
	r = (XGSWInternalRecord *)ptr;
	
	/*
	 *	How to handle this
	 */
	
	if ((r->msg == WM_HSCROLL) || (r->msg == WM_VSCROLL)) {
		/*
		 *	I've been clicked on. Scroll. This does this cheap.
		 */
		
		switch (LOWORD(r->wp)) {
			case SB_LEFT:
				SetValue(fMinValue);
				break;
			case SB_RIGHT:
				SetValue(fMaxValue);
				break;
			case SB_LINELEFT:
				SetValue(GetValue() - GetStepSize());
				break;
			case SB_LINERIGHT:
				SetValue(GetValue() + GetStepSize());
				break;
			case SB_PAGELEFT:
				SetValue(GetValue() - GetPageSize());
				break;
			case SB_PAGERIGHT:
				SetValue(GetValue() + GetPageSize());
				break;
			case SB_THUMBPOSITION:
			case SB_THUMBTRACK:
				SetValue(HIWORD(r->wp) * fScale);
				break;
		}
		return 0;
	}
	return -1;
}

#endif

/************************************************************************/
/*																		*/
/*	X scollbar support													*/
/*																		*/
/************************************************************************/

#if OPT_XWIN

/*	ScaleToPixel
 *
 *		Given a minimum and maximum pixel value and a long integer
 *	value and maximum, this returns the pixel value that represents
 *	the specified position along the scale
 */

static short ScaleToPixel(short minpix, short maxpix, long val, long max)
{
	long scale;
	
	/*
	 *	Scale down to guarentee the (maxpix - minpix)*val
	 *	operation doesn't overflow. The disadvantage is that
	 *	for scrollbars that are 10,000 pixels tall, this can
	 *	be less than smooth. But then, a 10,000 pixel screen
	 *	at 100 pixels/inch would be 8 feet call--and who is
	 *	going to have an 8 foot monitor?
	 */
	
	scale = (16383 + max) / 16384;
	if (scale <= 0) return minpix;			// ???
	val /= scale;
	max /= scale;
	return (short)(minpix +
				   (((maxpix - minpix) * val) / max));
}

/*	XGScrollBar::ThumbWidth
 *
 *		Get the width of the thumb
 */

short XGScrollBar::ThumbWidth()
{
	Rect r;
	short widthx,widthy;
	short width;
	long value;
	Rect thumb;
	
	r = GetContentRect();
	widthx = r.right - r.left;
	widthy = r.bottom - r.top;

	if (widthx > widthy) {
		widthx -= widthy*2;
		width = ScaleToPixel(0,widthx,fPage,fPage + fMaxValue - fMinValue);
	} else {
		widthy -= widthx*2;
		width = ScaleToPixel(0,widthy,fPage,fPage + fMaxValue - fMinValue);
	}
	
	if (width < 8) width = 8;
	return width;
}

/*	XGScrollBar::CalcScrollRect
 *
 *		Calculate the scrollbar rectangle borders
 */

Rect XGScrollBar::CalcScrollRect(short index)
{
	Rect r = GetContentRect();
	short width,tmp;
	short ttop,tbottom;
	short thumb = ThumbWidth();
	
	width = r.right - r.left;
	tmp = r.bottom - r.top;
	if (tmp < width) {
		width = tmp;
		
		/*
		 *	Bar is on it's side
		 */
	
		switch (index) {
			case 0:						// left arrow
				r.right = r.left + width;
				break;
				
			case 1:						// Left margin
				r.right = ScaleToPixel(r.left + width,
									   r.right - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.left += width;
				break;
								
			case 2:						// thumb
				tmp = r.left;
				r.left = ScaleToPixel(r.left + width,
									   r.right - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.right = r.left + thumb;
				break;
				
			case 3:						// right margin
				r.left = thumb +
						 ScaleToPixel(r.left + width,
									   r.right - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.right -= width;
				break;
				
			case 4:						// right arrow
				r.left = r.right - width;
				break;
		}	 
	} else {
		/*
		 *	Bar is upright
		 */
		
		switch (index) {
			case 0:						// top arrow
				r.bottom = r.top + width;
				break;
				
			case 1:						// Left margin
				r.bottom = ScaleToPixel(r.top + width,
									   r.bottom - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.top += width;
				break;
								
			case 2:						// thumb
				tmp = r.top;
				r.top = ScaleToPixel(r.top + width,
									   r.bottom - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.bottom = r.top + thumb;
				break;
				
			case 3:						// bottom margin
				r.top = thumb +
						 ScaleToPixel(r.top + width,
									   r.bottom - width - thumb,
									   fValue - fMinValue,
									   fMaxValue - fMinValue);
				r.bottom -= width;
				break;
				
			case 4:						// bottom arrow
				r.top = r.bottom - width;
				break;
		}	 
	}
	
	return r;
}

/*	XGScrollBar::DrawScrollBar
 *
 *		Draw the scrollbar itself. This also hilites the specified
 *	part.
 */

void XGScrollBar::DrawScrollBar(short part)
{
	Rect r;
	XGDraw draw(this);
	int x,y;
	bool width;
	
	r = GetContentRect();
	width = (r.right - r.left > r.bottom - r.top);
	
	/*
	 *	If this thing is empty, this draws an empty rectangle.
	 */
	
	if (fMaxValue <= fMinValue) {
		draw.Draw3DRect(r,KXGEFrame);
		::InsetRect(&r,1,1);
		draw.Draw3DRect(r,KXGEBackground);
		return;
	}
	
	/*
	 *	Draw the top arrow
	 */
	
	r = CalcScrollRect(0);
	draw.Draw3DRect(r,KXGEBackground);
	if (part == 0) {
		draw.Draw3DRect(r,KXGEInButton);
		::OffsetRect(&r,1,1);
	} else {
		draw.Draw3DRect(r,KXGEButton);
	}
	
	if (width) {
		draw.MoveTo((r.left*2+r.right)/3,(r.top+r.bottom)/2);
		draw.LineTo((r.left+2*r.right)/3,(r.top*2+r.bottom)/3);
		draw.LineTo((r.left+2*r.right)/3,(r.top+2*r.bottom)/3);
		draw.LineTo((r.left*2+r.right)/3,(r.top+r.bottom)/2);
	} else {
		draw.MoveTo((r.left+r.right)/2,(r.top*2+r.bottom)/3);
		draw.LineTo((r.left*2+r.right)/3,(r.top+2*r.bottom)/3);
		draw.LineTo((r.left+2*r.right)/3,(r.top+2*r.bottom)/3);
		draw.LineTo((r.left+r.right)/2,(r.top*2+r.bottom)/3);
	}
	
	/*
	 *	Draw the bottom arrow
	 */
	
	r = CalcScrollRect(4);
	draw.Draw3DRect(r,KXGEBackground);
	if (part == 4) {
		draw.Draw3DRect(r,KXGEInButton);
		::OffsetRect(&r,1,1);
	} else {
		draw.Draw3DRect(r,KXGEButton);
	}
	
	if (width) {
		draw.MoveTo((r.left+2*r.right)/3,(r.top+r.bottom)/2);
		draw.LineTo((r.left*2+r.right)/3,(r.top*2+r.bottom)/3);
		draw.LineTo((r.left*2+r.right)/3,(r.top+2*r.bottom)/3);
		draw.LineTo((r.left+2*r.right)/3,(r.top+r.bottom)/2);
	} else {
		draw.MoveTo((r.left+r.right)/2,(r.top+2*r.bottom)/3);
		draw.LineTo((r.left*2+r.right)/3,(r.top*2+r.bottom)/3);
		draw.LineTo((r.left+2*r.right)/3,(r.top*2+r.bottom)/3);
		draw.LineTo((r.left+r.right)/2,(r.top+2*r.bottom)/3);
	}
	
	/*
	 *	Draw the top border
	 */
	
	r = CalcScrollRect(1);
	if (width) ::InsetRect(&r,-1,0);
	else ::InsetRect(&r,0,-1);
	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
	if (part == 1) {
		draw.Draw3DRect(r,KXGEInset);
	}
	
	/*
	 *	Draw the bottom border
	 */
	
	r = CalcScrollRect(3);
	if (width) ::InsetRect(&r,-1,0);
	else ::InsetRect(&r,0,-1);
	draw.Draw3DRect(r,KXGEFrame);
	::InsetRect(&r,1,1);
	draw.Draw3DRect(r,KXGEBackground);
	if (part == 3) {
		draw.Draw3DRect(r,KXGEInset);
	}
	
	/*
	 *	Draw the thumb
	 */
	
	r = CalcScrollRect(2);
	draw.Draw3DRect(r,KXGEBackground);
	if (part == 2) {
		draw.Draw3DRect(r,KXGEInButton);
	} else {
		draw.Draw3DRect(r,KXGEButton);
	}
}

/*	XGScrollBar::UpdateTrackValue
 *
 *		Update the trackbar's value based on the position of the mouse.
 *
 *		NOTE: I gotta figure out how to deal with very large values.
 *	Right now, (fMaxValue - fMinValue) * pixel size cannot exceed
 *	the largest signed long integer value.
 */

void XGScrollBar::UpdateTrackValue(Point where)
{
	XGSValue v;
	Rect r;
	short widthx,widthy;
	long value;
	short thumb;
	
	r = GetContentRect();
	widthx = r.right - r.left;
	widthy = r.bottom - r.top;
	thumb = ThumbWidth();
	
	/*
	 *	Calculate value if thumb was in middle
	 */
	
	if (widthx > widthy) {
		::InsetRect(&r,widthy,0);
		::InsetRect(&r,thumb/2,0);
		if (r.right <= r.left) value = fMinValue;
		else {
			value = fMinValue + 
					(((fMaxValue - fMinValue) * (where.h - r.left))/(r.right-r.left));
		}
	} else {
		::InsetRect(&r,0,widthx);
		::InsetRect(&r,0,thumb/2);
		if (r.bottom <= r.top) value = fMinValue;
		else {
			value = fMinValue + 
					(((fMaxValue - fMinValue) * (where.v - r.top))/(r.bottom-r.top));
		}
	}
	
	/*
	 *	Peg
	 */
	
	if (value > fMaxValue) value = fMaxValue;
	if (value < fMinValue) value = fMinValue;
	
	/*
	 *	Make sure the value is set correctly
	 */
		
	if (value < fMinValue) value = fMinValue;
	if (value > fMaxValue) value = fMaxValue;

	/*
	 *	And send the messages indicating the value changed
	 */
	
	if (value != fValue) {
		DoDispatch(KXGSliderStart,GetViewID(),NULL);
		v.slider = this;
		v.oldVal = fValue;
		v.newVal = value;
		fValue = value;
		DrawScrollBar(2);				// draw the scroll bar in new pos
		DoDispatch(KXGSliderValue,GetViewID(),(void *)&v);
	}
}

#endif

