//	CDocument.c

#ifndef __GNUC__
	#include <Controls.h>
#endif

#include "CDialog.h"
#include "FSUtils.h"
#include "Utils.h"
#include "MemUtils.h"
#include "IC_Errors.h"
#include "CDocument.h"

static	ControlActionUPP	ScrollActionUPP = NULL;
static	ControlActionUPP	ThumbActionUPP = NULL;

Boolean		CDocument::IDocument(
	ADFS_WindowType windowType, 
	short			resourceID
) {
	Boolean		success;
	
	i_macPort.window = GetNewCWindow(resourceID, NULL, (WindowPtr)-1);
	
	success = i_macPort.window != NULL;

	SetUntitled();
	
	i_prepared		= FALSE;
	i_scrollingB	= FALSE;
		
	if (success) {
		success = _inherited::IWindow(windowType);
	}
	
	if (success) {
		i_vScrollbar = GetNewControl(kVScrollID, i_macPort.window);
		if (i_vScrollbar) {
			SetControlReference(i_vScrollbar, (long)this);
		} else {
			ReportError(IC_Err_OUT_OF_MEMORY);
			success = FALSE;
		}
	}
	
	if (success) {
		i_hScrollbar = GetNewControl(kHScrollID, i_macPort.window);
		if (i_hScrollbar) {
			SetControlReference(i_hScrollbar, (long)this);
		} else {
			ReportError(IC_Err_OUT_OF_MEMORY);
			success = FALSE;
		}
	}
		
	if (success && ScrollActionUPP == NULL) {
		ScrollActionUPP = NewControlActionUPP(ScrollActionCB);
		success = ScrollActionUPP != NULL;

		if (success) {
			ThumbActionUPP = NewControlActionUPP(ThumbActionCB);
			success = ThumbActionUPP != NULL;
		}
	}
							
	if (success) {
		AdjustScrollbars();
	}
	
	return success;
}

void	CDocument::Dispose(void)
{
	_inherited::Dispose();
}

void		CDocument::SetPTitle(unsigned char *nameZu)
{
	_inherited::SetPTitle(nameZu);
	CopyString(nameZu, i_fileSpec.name);
}

Boolean		CDocument::IsUntitled(void)
{
	return i_fileSpec.parID == 0 && i_fileSpec.vRefNum == 0;
}

void		CDocument::SetUntitled(void)
{
	structclr(i_fileSpec);
}

Boolean		CDocument::GetScrollbarHit(
	Point			thePoint, 
	WindowRectType	*pane, 
	short			*subPane, 
	Rect			*hitRect
) {
	Boolean		hit = FALSE;
	
	for (
		*pane = WindowRect_H_SCROLL; 
		*pane <= WindowRect_V_SCROLL; 
		*pane = (WindowRectType)((*pane) + 1)
	) {
		*hitRect = GetWindowRect(*pane);
		
		if (PtInRect(thePoint, hitRect)) {
			hit = TRUE;
			break;
		}
	}
	
	if (!hit) {
		*pane = WindowRect_NONE;
	} else switch (*pane) {
	
		case WindowRect_H_SCROLL:
		case WindowRect_V_SCROLL: {
			ControlRef		theControl;
			
			*subPane = FindControl(thePoint, GetWindowRef(), &theControl);
			if (*subPane == 0) {
				*pane = WindowRect_NONE;
			}
			break;
		}
	}
	
	return *pane != WindowRect_NONE;
}

void		CDocument::GetLineAndPageScrolls(
	WindowRectType	scrollbar, 
	short			*line, 
	short			*page
) {
	//	override me!
	*line = 1;
	*page = 10;
}

Rect		CDocument::GetScrollExtents(void)
{
	return GetWindowRect(WindowRect_INTERIOR);
}

void		CDocument::GetScrollMaxs(
	short	*hMin, 
	short	*vMin
) {
	Rect	extentsR	= GetScrollExtents();
	Rect	frameR		= GetWindowRect(WindowRect_INTERIOR);
	
	*hMin = (extentsR.right - extentsR.left) - (frameR.right - frameR.left);
	*vMin = (extentsR.bottom - extentsR.top) - (frameR.bottom - frameR.top);

	if (*vMin < 0) *vMin = 0;
	if (*hMin < 0) *hMin = 0;
}

void	CDocument::AdjustScrollbars(void)
{
	Rect		theRect;
	short		hMax = 0, vMax = 0;
	
	GetScrollMaxs(&hMax, &vMax);
	
	GetClip(i_clipRgn);
	ClipRect(&gEmptyRect);
	
	theRect = GetWindowRect(WindowRect_H_SCROLL);
	SetControlRect(i_hScrollbar, &theRect);
	
	theRect = GetWindowRect(WindowRect_V_SCROLL);
	SetControlRect(i_vScrollbar, &theRect);

	SetControlMaximum(i_hScrollbar, hMax);
	SetControlMaximum(i_vScrollbar, vMax);

	#ifndef OLD68K
	theRect = GetWindowRect(WindowRect_INTERIOR);
	SetControlViewSize(i_hScrollbar, theRect.right - theRect.left);
	SetControlViewSize(i_vScrollbar, theRect.bottom - theRect.top);
	#endif

	SetClip(i_clipRgn);

/*	
	InvalWindow(WindowRect_INTERIOR);
	InvalWindow(WindowRect_H_SCROLL);
	InvalWindow(WindowRect_V_SCROLL);
*/
}

Boolean		CDocument::WillScrollOffset(
	ControlRef	theControl, 
	short		offset)
{
	short		oldValue, newValue, max = GetControlMaximum(theControl);
		
	oldValue = GetControlValue(theControl);
	newValue = oldValue + offset;

	if (newValue < 0) {
		newValue = 0;
	} else if (newValue > max) {
		newValue = max;
	}
	
	return newValue != oldValue;
}

void		CDocument::DeSelectAll(void)
{
}

Rect		CDocument::MarqueeSelect(Point	hitPoint, ulong modifiers)
{
	OSErr		err = noErr;
	Point		origPoint = hitPoint;
	Rect		theRect, interiorR;
	short		lineS, pageS;
	
	GetLineAndPageScrolls(WindowRect_V_SCROLL, &lineS, &pageS);
	
	interiorR = GetWindowRect(WindowRect_INTERIOR);
	
	if ((modifiers & (shiftKey | cmdKey)) == 0) {
		DeSelectAll();
	}
	
	if (!err) {
		Rect		oldRect;
		short		tempS, offsetS;
		Pattern		thePattern;
		Boolean		movedB = FALSE;
		Boolean		doneB = FALSE;

		ClipRect(&interiorR);
		PenPat(GetQDGlobalsGray(&thePattern));
		PenMode(srcXor);
		PenSize(2, 2);

		theRect.left	= theRect.right 	= hitPoint.h;
		theRect.top		= theRect.bottom	= hitPoint.v;
		oldRect = theRect;
		FrameRect(&oldRect);

		do {
			GetMouse(&hitPoint);
			theRect.left	= origPoint.h;
			theRect.top		= origPoint.v;
			theRect.right	= hitPoint.h;
			theRect.bottom	= hitPoint.v;
			
			if (theRect.right < theRect.left) {
				tempS			= theRect.right;
				theRect.right	= theRect.left;
				theRect.left	= tempS;
			}
			
			if (theRect.bottom < theRect.top) {
				tempS			= theRect.bottom;
				theRect.bottom	= theRect.top;
				theRect.top		= tempS;
			}
			
			if (!EqualRect(&oldRect, &theRect)) {
				FrameRect(&theRect);
				FrameRect(&oldRect);
				oldRect = theRect;
			}
			
			offsetS = 0;
			if (hitPoint.v < interiorR.top) {
				offsetS = hitPoint.v - interiorR.top;
			} else if (hitPoint.v > interiorR.bottom) {
				offsetS = hitPoint.v - interiorR.bottom;
			}

			movedB = FALSE;
			
			if (offsetS && WillScrollOffset(i_vScrollbar, offsetS)) {
				PenState	savePen;
				
				FrameRect(&theRect);
				GetPenState(&savePen);
				PenNormal();
				UnPrepare();
				SetScrollOffset(i_vScrollbar, offsetS);
				Prepare();
				SetPenState(&savePen);
				FrameRect(&theRect);
				interiorR = GetWindowRect(WindowRect_INTERIOR);
				ClipRect(&interiorR);
				movedB = TRUE;
			}

			offsetS = 0;
			if (hitPoint.h < interiorR.left) {
				offsetS = hitPoint.h - interiorR.left;
			} else if (hitPoint.h > interiorR.right) {
				offsetS = hitPoint.h - interiorR.right;
			}
			
			if (offsetS && WillScrollOffset(i_hScrollbar, offsetS)) {
				PenState	savePen;
				
				FrameRect(&theRect);
				GetPenState(&savePen);
				PenNormal();
				UnPrepare();
				SetScrollOffset(i_hScrollbar, offsetS);
				Prepare();
				SetPenState(&savePen);
				FrameRect(&theRect);
				interiorR = GetWindowRect(WindowRect_INTERIOR);
				ClipRect(&interiorR);
				movedB = TRUE;
			}
			
			if (movedB) {
				doneB = !StillDown_Loop();	//	StillDown(); does not work for right click
			} else {
				doneB = !StillDown_Loop();
			}
			
		} while (!doneB);

		FrameRect(&oldRect);
		PenNormal();
		OpenClipRect();
	}

	return theRect;
}

void	CDocument::SetScrollOffset(
	ControlRef	theControl, 
	short		offset)
{
	short		oldValue, newValue, max = GetControlMaximum(theControl);
		
	oldValue = GetControlValue(theControl);
	newValue = oldValue + offset;

	if (newValue < 0) {
		newValue = 0;
	} else if (newValue > max) {
		newValue = max;
	}
	
	if (newValue != oldValue) {
		SetControlValue(theControl, newValue);
		InvalWindow(WindowRect_ALL);
		i_scrollingB = TRUE;
		Update();
	}
}

void	CDocument::ScrollAction(
	ControlRef	theControl, 
	short		controlPart
) {
	short			line, page, offsetS;
	WindowRectType	scrollBar = theControl == i_vScrollbar ? WindowRect_V_SCROLL : WindowRect_H_SCROLL;
		
	GetLineAndPageScrolls(scrollBar, &line, &page);
	
	switch (controlPart) {
	
		case kControlUpButtonPart: {
			offsetS = -line;
			break;
		}
		
		case kControlDownButtonPart: {
			offsetS = line;
			break;
		}
		
		case kControlPageUpPart: {
			offsetS = -page;
			break;
		}
		
		case kControlPageDownPart: {
			offsetS = page;
			break;
		}
	}
	
	SetScrollOffset(theControl, offsetS);
}

//	static	
//	ScrollActionUPP points here
pascal	void	CDocument::ScrollActionCB(
	ControlRef	theControl, 
	short		controlPart)
{
	if (controlPart) {
		CDocument	*thiz;
		
		thiz = (CDocument *)GetControlReference(theControl);
		thiz->ScrollAction(theControl, controlPart);
	}
}

enum {
	kScrollBarWidth = 16,							// fixed
	kScrollArrowWidth = kScrollBarWidth,			//   "
	kScrollBarWidthAdjust = kScrollBarWidth - 1,	//   "
	
	lastoneS
};

short gScrollVThumbSize = 0;
short gScrollHThumbSize = 0;
short gTotalVSizeAdjust = 0;
short gTotalHSizeAdjust = 0;

RgnHandle gSaveClip = NULL;					// clipping save for control drawing exclusions

SInt32 gStartValue;							// scroll start value
SInt32 gSaveValue;
SInt32 gValueSlop;							// slop for mouse live scroll calcs

static	SInt32 CalcValueFromPoint (ControlHandle hControl, Point thePoint)	// figure where we are in scroll bar terms
{
	SInt32 theValue = 0, theRange, theDistance, thePin;
	Rect			rectControl;
		
	GetControlBounds(hControl, &rectControl);
	
	theRange = GetControlMaximum(hControl) - GetControlMinimum(hControl);

	if ((rectControl.right - rectControl.left) > (rectControl.bottom - rectControl.top)) {
		// Scroll distance adjusted for scroll arrows and the thumb
		theDistance = rectControl.right - rectControl.left - gTotalHSizeAdjust;
		// Pin thePoint to the middle of the thumb
		thePin = rectControl.left + (gScrollHThumbSize / 2);
		theValue = ((thePoint.h - thePin) * theRange) / theDistance;
	} else {
		// Scroll distance adjusted for scroll arrows and the thumb
		theDistance = rectControl.bottom - rectControl.top - gTotalVSizeAdjust;
		// Pin thePoint to the middle of the thumb
		thePin = rectControl.top + (gScrollVThumbSize / 2);
		theValue = ((thePoint.v - thePin) * theRange) / theDistance;
	}
	theValue += gValueSlop;
	return theValue;
}

static void SetScrollParams(WindowPtr pWindow, ControlRef hControl, Rect *extentRP)	// set up scroll bars statics
{
	Rect	portR;	GetWindowPortBounds(pWindow, &portR);
	short	windowHeight = portR.bottom - portR.top;
	short	windowWidth = portR.right - portR.left;
	
	// thumb size based on relative size of window to offscreen
	gScrollVThumbSize = windowHeight 
		* (windowHeight - kScrollBarWidthAdjust - kScrollArrowWidth * 2 - 6) 
		/ (extentRP->bottom - extentRP->top); 

	gScrollHThumbSize = windowWidth 
		* (windowWidth - kScrollBarWidthAdjust - kScrollArrowWidth * 2 - 6) 
		/ (extentRP->right - extentRP->left); ;

	gTotalVSizeAdjust = ((kScrollArrowWidth * 2) + gScrollVThumbSize);	// amount that scroll bar is less than full height of window
	gTotalHSizeAdjust = ((kScrollArrowWidth * 2) + gScrollHThumbSize);	// amount that scroll bar is less than full height of window

	Rect	rectNull = {0, 0, 0, 0};
//	OSErr	theErr = noErr;
	Point	thePoint;
	
	gStartValue = GetControlValue(hControl);
	
	gValueSlop = 0;
	GetMouse (&thePoint);
	gValueSlop = GetControlValue (hControl) - CalcValueFromPoint (hControl, thePoint);	// delta from center
	
	if (!gSaveClip) {
		gSaveClip = NewRgn ();
	}

	GetClip (gSaveClip);
	ClipRect (&rectNull);
}

#define		kThumbTrackSlop		100
void	CDocument::ThumbAction(ControlRef theControl, short origValueS)
{
	Rect			rectNull = {0, 0, 0, 0};
	SInt32			theValue;
    Point			thePoint;
    Rect			controlR;
	WindowRectType	scrollBarID = theControl == i_vScrollbar ? WindowRect_V_SCROLL : WindowRect_H_SCROLL;
        
	controlR = GetWindowRect(scrollBarID);
 	InsetRect(&controlR, -kThumbTrackSlop, -kThumbTrackSlop);	// expand control rect by tracking slop
    GetMouse(&thePoint);
    
    if (PtInRect(thePoint, &controlR)) {
		theValue = CalcValueFromPoint(theControl, thePoint);	// track new point point
	} else {
		theValue = origValueS;
	}
	
	if (theValue != GetControlValue(theControl)) {
		SetClip(gSaveClip);

		SetScrollOffset(theControl, theValue - GetControlValue(theControl));

		gSaveValue = GetControlValue(theControl);

		GetClip(gSaveClip);
		ClipRect(&rectNull);
	}
}

CDocument	*s_thiz		= NULL;
ControlRef	s_controlP	= NULL;

//	static	
//	ScrollActionUPP points here
pascal	void	CDocument::ThumbActionCB(
	ControlRef	theControl, 
	short		controlPart)
{
	s_thiz->ThumbAction(s_controlP, gStartValue);
}

void	CDocument::DoKeyDown(EventRecord *event)
{
	if (i_renameTE) {
		_inherited::DoKeyDown(event);
	} else {
		//	handle page up/down, home/end
		//	or other of my stuff here
		_inherited::DoKeyDown(event);
	}
}

void	CDocument::DoClick(EventRecord *event)
{
	Point			hitPoint = event->where;
	WindowRectType	pane;
	short			subPane;
	Rect			hitRect;
	
	_inherited::DoClick(event);

	Prepare();
	UnPrepare();
	GlobalToLocal(&hitPoint);
	
	if (GetScrollbarHit(hitPoint, &pane, &subPane, &hitRect)) {
	
		switch (pane) {
		
			case WindowRect_H_SCROLL:
			case WindowRect_V_SCROLL: {
				ControlRef		theControl = pane == WindowRect_H_SCROLL
					? i_hScrollbar
					: i_vScrollbar;
			
				switch (subPane) {

					case kControlUpButtonPart:
					case kControlDownButtonPart:
					case kControlPageUpPart:
					case kControlPageDownPart: {
						TrackControl(theControl, hitPoint, ScrollActionUPP);
						break;
					}
					
					case kControlIndicatorPart: {
						gStartValue = GetControlValue(theControl);
						s_thiz		= this;
						s_controlP	= theControl;
						
						hitRect = GetScrollExtents();
						SetScrollParams(GetWindowRef(), theControl, &hitRect);

						TrackControl(theControl, hitPoint, ThumbActionUPP);
						
						SetControlValue(theControl, gSaveValue);
						InvalWindow(WindowRect_ALL);

						s_thiz		= NULL;
						s_controlP	= NULL;
						break;
					}
				}
				break;
			}
		}
	}
}

void	CDocument::ReActivate(Boolean active)
{
	_inherited::ReActivate(active);
	
	if (active) {
		Rect	theRect;
		
		theRect = GetWindowRect(WindowRect_H_SCROLL);
		SetControlRect(i_hScrollbar, &theRect);
		
		theRect = GetWindowRect(WindowRect_V_SCROLL);
		SetControlRect(i_vScrollbar, &theRect);
	} else {
		Rect	theRect;
	
		theRect	= GetWindowRect(WindowRect_H_SCROLL);
		InsetRect(&theRect, 1, 1);
		RectRgn(i_clipRgn, &theRect);
		SetClip(i_clipRgn);
		HideControl(i_hScrollbar);

		theRect	= GetWindowRect(WindowRect_V_SCROLL);
		InsetRect(&theRect, 1, 1);
		RectRgn(i_clipRgn, &theRect);
		SetClip(i_clipRgn);
		HideControl(i_vScrollbar);

		OpenClipRect();
	}
	
	DrawOnlyGrowIcon(GetWindowRef());
}

void	CDocument::LiveResize(void)
{
	WindowRef	windowRef	= GetWindowRef();
	RgnHandle	visRgn		= NewRgn();

	Prepare();
	UnPrepare();
	AdjustScrollbars();

	i_scrollingB = TRUE;

	Draw();
	
	if (visRgn) {
		GetPortVisibleRegion(GetGrafPtr(), visRgn);
		UpdateControls(windowRef, visRgn);
		DisposeRgn(visRgn);
	} else {
		DrawControls(windowRef);
	}

	DrawOnlyGrowIcon(windowRef);
}

void	CDocument::Draw(void)
{
	_inherited::Draw();
	
	if (!i_active) {
		Rect	theRect;

		theRect	= GetWindowRect(WindowRect_H_SCROLL);
		FrameRect(&theRect);
		
		theRect	= GetWindowRect(WindowRect_V_SCROLL);
		FrameRect(&theRect);
	}
}

void	CDocument::Size(EventRecord *event)
{
	Rect	newRect, oldRect = GetWindowRect(WindowRect_ALL);
	
	_inherited::Size(event);
	
	newRect = GetWindowRect(WindowRect_ALL);
	
	if (!EqualRect(&oldRect, &newRect)) {
		AdjustScrollbars();
		InvalWindow(WindowRect_GROW_BOX);
	}
}

void	CDocument::Zoom(EventRecord *event, short direction)
{
	Rect	newRect, oldRect = GetWindowRect(WindowRect_ALL);
	
	_inherited::Zoom(event, direction);
	
	newRect = GetWindowRect(WindowRect_ALL);
	
	if (!EqualRect(&oldRect, &newRect)) {
		AdjustScrollbars();
		InvalWindow(WindowRect_GROW_BOX);
	}
}

//	always returns coordinates relative to topLeft { 0, 0 }
//	beeps if you call it when the offscreen is active
Rect	CDocument::GetWindowRect(WindowRectType wrType)
{
	Rect		theRect = _inherited::GetWindowRect(WindowRect_ALL);
		
	switch (wrType) {
		
		default: {
			theRect = _inherited::GetWindowRect(wrType);
			break;
		}

		case WindowRect_INTERIOR: {
			short		headerS = GetWindowRect(WindowRect_HEADER).bottom;
			 
			if (theRect.top < headerS) {
				headerS = headerS;
			}

			theRect.right	-= kScrollbarWidth - 1;
			theRect.bottom	-= kScrollbarWidth - 1;
			break;
		}

		case WindowRect_H_SCROLL: {
			theRect.left--;
			theRect.right -= kScrollbarWidth - 2;
			theRect.top = theRect.bottom - kScrollbarWidth + 1;
			theRect.bottom++;
			break;
		}

		case WindowRect_V_SCROLL: {
			theRect.left	= theRect.right - kScrollbarWidth + 1;
			theRect.top		= GetWindowRect(WindowRect_HEADER).bottom - 1;
			theRect.bottom	-= kScrollbarWidth - 2;
			theRect.right++;
			break;
		}
		
		case WindowRect_GROW_BOX: {
			theRect.left = theRect.right - kScrollbarWidth;
			theRect.top = theRect.bottom - kScrollbarWidth;
			break;
		}
	}
	
	return theRect;
}

void	CDocument::Update(void)
{
	_inherited::Update();
	i_scrollingB = FALSE;
}
