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

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

  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 <XApplication.h>
#include <XMessages.h>
#include <XDraw.h>
#include <XScroll.h>
#include <XWindow.h>
#include <XCursorUtil.h>

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

/*$	XGScrollBar::XGScrollBar
 *
 *		Construct my scrollbar
 */

XGScrollBar::XGScrollBar(XGView *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(XGView *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);
}

/*$	XGScrollBar::~XGScrollBar
 *
 *		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 control, short part)
{
	XGScrollBar *bar;
	long value;

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

#endif

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

/*$	XGScrollBar::DoDrawView
 *
 *		Handle the drawing
 */

void XGScrollBar::DoDrawView(XRect)
{
#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);
		::InvalidateRect(fControl,NULL,false);
	}
#endif

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

/*$	XGScrollBar::DoActivate
 *
 *		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
}

/*$	XGScrollBar::DoMouseDown
 *
 *		Handle the mouse down event. Only used for the Macintosh
 */

bool XGScrollBar::DoMouseDown(XPoint pt, short a)
{
#if OPT_MACOS
	short i;
	
	if (fControl) {
		ControlHandle c;
		XGDraw draw(this,false);
		ViewToGlobal(&pt);
		
		i = ::FindControl(pt.MacPoint(),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.MacPoint(),(ControlActionUPP)-1)) {
				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 = NewControlActionUPP(Track);
			::TrackControl(fControl,pt.MacPoint(),proc);
			DisposeControlActionUPP(proc);
		}
	} else {
		XGControl::DoMouseDown(pt,a);
	}
	return false;
#endif

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

#if OPT_XWIN
	int i;
	XRect 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
}

/*$	XGScrollBar::DoMouseMove
 *
 *		handle mouse move events
 */

void XGScrollBar::DoMouseMove(XPoint pt, short a)
{
#if OPT_XWIN
	XRect 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
}

/*$	XGScrollBar::DoMouseUp
 *
 *		handle mouse up
 */

void XGScrollBar::DoMouseUp(XPoint 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
}

/*$	XGScrollBar::DoSizeView
 *
 *		Handle resize event
 */

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

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

/*$	XGScrollBar::DoMoveView
 *
 *		Move the view.
 */

void XGScrollBar::DoMoveView()
{
#if OPT_MACOS || OPT_WINOS
	XRect 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
}

/*$	XGScrollBar::DoShowHide
 *
 *		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) {
			XRect 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();
}

/*$	XGScrollBar::DoEnable
 *
 *		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

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

void XGScrollBar::SetFocus(void)
{
	XGControl::SetControlFocus(fControl);
}

#endif

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

/*$	XGScrollBar::GetValue
 *
 *		Get the current value
 */

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

/*$	XGScrollBar::GetMinValue
 *
 *		Get the current value
 */

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

/*$	XGScrollBar::GetMaxValue
 *
 *		Get the current value
 */

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

/*$	XGScrollBar::GetStepSize
 *
 *		Get the step size
 */

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

/*$	XGScrollBar::GetPageSize
 *
 *		Get the page size
 */

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


/*$	XGScrollBar::SetStepSize
 *
 *		Set the step size; the amount things step
 */

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

/*$	XGScrollBar::SetPageSize
 *
 *		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
}

/*$	XGScrollBar::SetMinValue
 *
 *		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
}

/*$	XGScrollBar::SetMaxValue
 *
 *		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
}

/*$	XGScrollBar::SetValue
 *
 *		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												*/
/*																		*/
/************************************************************************/

/*$	XGScrollBar::InitScrollBar
 *
 *		Initialize the scrollbar
 */

void XGScrollBar::InitScrollBar(XGSScrollInitRecord &init)
{
#if OPT_MACOS
	static ControlActionUPP fLiveProc = NULL;
	XRect r;
	int procid;
	
	if (IsVisible()) {
		r = GetContentRect();
		ViewToGlobal(&r);
	} else {
		r.left = -20,
		r.top = -20,
		r.right = -10,
		r.bottom = -10;
	}
	procid = scrollBarProc;
	if (_GAppearance)
		procid = kControlScrollBarLiveProc;
	Rect mr = r.MacRect();
	fControl = ::NewControl(GetRootWindow(),
							&mr,
							"\p",
							false,
							0,
							0,
							0,
							procid,
							(long)this);
	if (_GAppearance) {
		if (!fLiveProc)
			fLiveProc = NewControlActionProc(XGScrollBar::Track);
		SetControlAction(fControl, fLiveProc);
	}
#endif

#if OPT_WINOS
	XRect 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)(XGView *)this);
#endif

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

#if OPT_MACOS || OPT_WINOS

/*$	XGScrollBar::AdjustScale
 *
 *		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;
}

/*$	XGScrollBar::LoadControl
 *
 *		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));
	
#if powerc
	if (_GAppearance >= 0x0110) {
		/* this call requires Appearance Manager 1.1 */
		::SetControlViewSize(fControl, (short)(fPage/fScale));
	}
#endif
#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														*/
/*																		*/
/************************************************************************/

/*$	XGScrollBar::GetDefaultSize
 *
 *		Get the scrollbar's default size. Used for placement by other
 *	routines
 */

XPoint XGScrollBar::GetDefaultSize()
{
	XPoint 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()
{
	XRect r;
	short widthx,widthy;
	short width;
	long value;
	XRect 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
 */

XRect XGScrollBar::CalcScrollRect(short index)
{
	XRect 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)
{
	XRect 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(XPoint where)
{
	XGSValue v;
	XRect 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

