/*
    File:		TView.cp
    
    Version:	1.0

	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
				("Apple") in consideration of your agreement to the following terms, and your
				use, installation, modification or redistribution of this Apple software
				constitutes acceptance of these terms.  If you do not agree with these terms,
				please do not use, install, modify or redistribute this Apple software.

				In consideration of your agreement to abide by the following terms, and subject
				to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
				copyrights in this original Apple software (the "Apple Software"), to use,
				reproduce, modify and redistribute the Apple Software, with or without
				modifications, in source and/or binary forms; provided that if you redistribute
				the Apple Software in its entirety and without modifications, you must retain
				this notice and the following text and disclaimers in all such redistributions of
				the Apple Software.  Neither the name, trademarks, service marks or logos of
				Apple Computer, Inc. may be used to endorse or promote products derived from the
				Apple Software without specific prior written permission from Apple.  Except as
				expressly stated in this notice, no other rights or licenses, express or implied,
				are granted by Apple herein, including but not limited to any patent rights that
				may be infringed by your derivative works or by other works in which the Apple
				Software may be incorporated.

				The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
				WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
				WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
				PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
				COMBINATION WITH YOUR PRODUCTS.

				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
				CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
				GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
				OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
				(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
				ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	Copyright © 2002 Apple Computer, Inc., All Rights Reserved
*/

/*
 NOTE: This is NOWHERE near a completely exhaustive implementation of a view. There are
       many more carbon events one could intercept and hook into this.
*/

#include "TView.h"

//-----------------------------------------------------------------------------------
//	constants
//-----------------------------------------------------------------------------------
//
const EventTypeSpec kHIObjectEvents[] =
{	{ kEventClassHIObject, kEventHIObjectConstruct },
	{ kEventClassHIObject, kEventHIObjectInitialize },
	{ kEventClassHIObject, kEventHIObjectDestruct }
};
	
const EventTypeSpec kHIViewEvents[] =
{	{ kEventClassCommand, kEventCommandProcess },
	{ kEventClassCommand, kEventCommandUpdateStatus },
	
	{ kEventClassControl, kEventControlInitialize },
	{ kEventClassControl, kEventControlDraw },
	{ kEventClassControl, kEventControlHitTest },
	{ kEventClassControl, kEventControlGetPartRegion },
	{ kEventClassControl, kEventControlGetData },
	{ kEventClassControl, kEventControlSetData },
	{ kEventClassControl, kEventControlGetOptimalBounds },
	{ kEventClassControl, kEventControlBoundsChanged },
	{ kEventClassControl, kEventControlTrack },
	{ kEventClassControl, kEventControlGetSizeConstraints },
	{ kEventClassControl, kEventControlHit },
	
	{ kEventClassControl, kEventControlHiliteChanged },
	{ kEventClassControl, kEventControlActivate },
	{ kEventClassControl, kEventControlDeactivate },
	{ kEventClassControl, kEventControlValueFieldChanged },
	{ kEventClassControl, kEventControlTitleChanged },
	{ kEventClassControl, kEventControlEnabledStateChanged },
};

// This param name was accidentally left unexported for
// the release of Jaguar.
const EventParamName kEventParamControlLikesDrag = 'cldg';

//-----------------------------------------------------------------------------------
//	TView constructor
//-----------------------------------------------------------------------------------
//
TView::TView(
	HIViewRef			inControl )
	:	fViewRef( inControl )
{
	verify_noerr( InstallEventHandler( GetControlEventTarget( fViewRef ), ViewEventHandler,
			GetEventTypeCount( kHIViewEvents ), kHIViewEvents, this, &fHandler ) );

	mouseEventHandler = NULL;
	fAutoInvalidateFlags = 0;
	debugPrint = false;
}

//-----------------------------------------------------------------------------------
//	TView destructor
//-----------------------------------------------------------------------------------
//
TView::~TView()
{
	// If we have installed our custom mouse events handler on the window,
	// go forth and remove it. Note: -1 is used to indicate that no handler has
	// been installed yet, but we want to once we get a window.
	if ( mouseEventHandler != NULL && mouseEventHandler != reinterpret_cast<void*>( -1 ) ) 
		RemoveEventHandler( mouseEventHandler );
	mouseEventHandler = NULL;
}

//-----------------------------------------------------------------------------------
//	Initialize
//-----------------------------------------------------------------------------------
//	Called during HIObject construction, this is your subclasses' chance to extract
//	any parameters it might have added to the initialization event passed into the
//	HIObjectCreate call.
//
OSStatus TView::Initialize( TCarbonEvent& /*inEvent*/ )
{
	return noErr;
}

//-----------------------------------------------------------------------------------
//	GetBehaviors
//-----------------------------------------------------------------------------------
//	Returns our behaviors. Any subclass that overrides this should OR in its behaviors
//	into the inherited behaviors.
//
UInt32 TView::GetBehaviors()
{
	return kControlSupportsDataAccess | kControlSupportsGetRegion;
}

//-----------------------------------------------------------------------------------
//	Draw
//-----------------------------------------------------------------------------------
//	Draw your view. You should draw based on VIEW coordinates, not frame coordinates.
//
void TView::Draw(
	RgnHandle			/*inLimitRgn*/,
	CGContextRef		/*inContext*/ )
{
}

//-----------------------------------------------------------------------------------
//	HitTest
//-----------------------------------------------------------------------------------
//	Asks your view to return what part of itself (if any) is hit by the point given
//	to it. The point is in VIEW coordinates, so you should get the view rect to do
//	bounds checking.
//
ControlPartCode TView::HitTest(
	const HIPoint&		/*inWhere*/ )
{
	return kControlNoPart;
}

//-----------------------------------------------------------------------------------
//	GetRegion
//-----------------------------------------------------------------------------------
//	This is called when someone wants to know certain metrics regarding this view.
//	The base class does nothing. Subclasses should handle their own parts, such as
//	the content region by overriding this method. The structure region is, by default,
//	the view's bounds. If a subclass does not have a region for a given part, it 
//	should always call the inherited method.
//
OSStatus TView::GetRegion(
	ControlPartCode		inPart,
	RgnHandle			outRgn )
{
#pragma unused( inPart, outRgn )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	PrintDebugInfo
//-----------------------------------------------------------------------------------
//	This is called when asked to print debugging information.
//
void TView::PrintDebugInfo()
{
}

//-----------------------------------------------------------------------------------
//	GetData
//-----------------------------------------------------------------------------------
//	Gets some data from our view. Subclasses should override to handle their own
//	defined data tags. If a tag is not understood by the subclass, it should call the
//	inherited method. As a convienience, we map the request for ControlKind into our
//	GetKind method.
//
OSStatus TView::GetData(
	OSType				inTag,
	ControlPartCode		inPart,
	Size				inSize,
	Size*				outSize,
	void*				inPtr )
{
#pragma unused( inPart )

	OSStatus			err = noErr;
	
	switch( inTag )
	{
		case kControlKindTag:
			if ( inPtr )
			{
				if ( inSize != sizeof( ControlKind ) )
					err = errDataSizeMismatch;
				else
					( *(ControlKind *) inPtr ) = GetKind();
			}
			*outSize = sizeof( ControlKind );
			break;
		
		default:
			err = eventNotHandledErr;
			break;
	}
	
	return err;
}

//-----------------------------------------------------------------------------------
//	SetData
//-----------------------------------------------------------------------------------
//	Sets some data on our control. Subclasses should override to handle their own
//	defined data tags. If a tag is not understood by the subclass, it should call the
//	inherited method.
//
OSStatus TView::SetData(
	OSType				inTag,
	ControlPartCode		inPart,
	Size				inSize,
	const void*			inPtr )
{
#pragma unused( inTag, inPart, inSize, inPtr )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	GetOptimalSize
//-----------------------------------------------------------------------------------
//	Someone wants to know this view's optimal size and text baseline, probably to help
//	do some type of layout. The base class does nothing, but subclasses should
//	override and do something meaningful here.
//
OSStatus TView::GetOptimalSize(
	HISize*				outSize,
	float*				outBaseLine )
{
#pragma unused( outSize, outBaseLine )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	GetSizeConstraints
//-----------------------------------------------------------------------------------
//	Someone wants to know this view's minimum and maximum sizes, probably to help
//	do some type of layout. The base class does nothing, but subclasses should
//	override and do something meaningful here.
//
OSStatus TView::GetSizeConstraints(
	HISize*				outMin,
	HISize*				outMax )
{
#pragma unused( outMin, outMax )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	BoundsChanged
//-----------------------------------------------------------------------------------
//	The bounds of our view have changed. Subclasses can override here to make note
//	of it and flush caches, etc. The base class does nothing.
//
OSStatus TView::BoundsChanged(
	UInt32 				inOptions,
	const HIRect& 		inOriginalBounds,
	const HIRect& 		inCurrentBounds,
	RgnHandle 			inInvalRgn )
{
#pragma unused( inOptions, inOriginalBounds, inCurrentBounds, inInvalRgn )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	ControlHit
//-----------------------------------------------------------------------------------
//	The was hit.  Subclasses can overide to care about what part was hit.
//
OSStatus TView::ControlHit(
	ControlPartCode		inPart,
	UInt32				inModifiers )
{
#pragma unused( inPart, inModifiers )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	HiliteChanged
//-----------------------------------------------------------------------------------
//	The hilite of our view has changed. Subclasses can override here to make note
//	of it and flush caches, etc. The base class does nothing.
//
OSStatus TView::HiliteChanged(
	ControlPartCode		inOriginalPart,
	ControlPartCode		inCurrentPart,
	RgnHandle 			inInvalRgn )
{
#pragma unused( inOriginalPart, inCurrentPart, inInvalRgn )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	DragEnter
//-----------------------------------------------------------------------------------
//	A drag has entered our bounds. The Drag and Drop interface also should have been
//	activated or else this method will NOT be called. If true is returned, this view
//	likes the drag and will receive drag within/leave/receive messages as appropriate.
//	If false is returned, it is assumed the drag is not valid for this view, and no
//	further drag activity will flow into this view unless the drag leaves and is
//	re-entered.
//
bool TView::DragEnter(
	DragRef				inDrag )
{
#pragma unused( inDrag )

	return false;
}

//-----------------------------------------------------------------------------------
//	DragWithin
//-----------------------------------------------------------------------------------
//	A drag has moved within our bounds. In order for this method to be called, the
//	view must have signaled the drag as being desirable in the DragEnter method. The
//	Drag and Drop interface also should have been activated.
//
bool TView::DragWithin(
	DragRef				inDrag )
{
#pragma unused( inDrag )

	return false;
}

//-----------------------------------------------------------------------------------
//	DragLeave
//-----------------------------------------------------------------------------------
//	A drag has left. Deal with it. Subclasses should override as necessary. The
//	Drag and Drop interface should be activated in order for this method to be valid.
//	The drag must have also been accepted in the DragEnter method, else this method
//	will NOT be called.
//
bool TView::DragLeave(
	DragRef				inDrag )
{
#pragma unused( inDrag )

	return false;
}

//-----------------------------------------------------------------------------------
//	DragReceive
//-----------------------------------------------------------------------------------
//	Deal with receiving a drag. By default we return dragNotAcceptedErr. I'm not sure
//	if this is correct, or eventNotHandledErr. Time will tell...
//
OSStatus TView::DragReceive(
	DragRef				inDrag )
{
#pragma unused( inDrag )

	return dragNotAcceptedErr;
}

//-----------------------------------------------------------------------------------
//	Track
//-----------------------------------------------------------------------------------
//	Default tracking method. Subclasses should override as necessary. We do nothing
//	here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::Track(
	TCarbonEvent&		inEvent,
	ControlPartCode*	outPart )
{
#pragma unused( inEvent, outPart )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	SetFocusPart
//-----------------------------------------------------------------------------------
//	Handle focusing. Our base behavior is to punt.
//
OSStatus TView::SetFocusPart(
	ControlPartCode		inDesiredFocus,
	RgnHandle			inInvalidRgn,
	Boolean				inFocusEverything,
	ControlPartCode*	outActualFocus )
{
#pragma unused( inDesiredFocus, inInvalidRgn, inFocusEverything, outActualFocus )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	ProcessCommand
//-----------------------------------------------------------------------------------
//	Process a command. Subclasses should override as necessary.
//
OSStatus TView::ProcessCommand(
	const HICommand&	inCommand )
{
#pragma unused( inCommand )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	UpdateCommandStatus
//-----------------------------------------------------------------------------------
//	Update the status for a command. Subclasses should override as necessary.
//
OSStatus
TView::UpdateCommandStatus(
	const HICommand&	inCommand )
{
#pragma unused( inCommand )

	return eventNotHandledErr;
}

OSStatus TView::MouseDown(HIPoint& /*inMouseLocation*/, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ ,
			  TCarbonEvent& /*inEvent*/)
{
	return eventNotHandledErr;
}

OSStatus TView::MouseUp(HIPoint& /*inMouseLocation*/, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ )
{
	return eventNotHandledErr;
}

OSStatus TView::MouseDragged(HIPoint& /*inMouseLocation*/, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ )
{
	return eventNotHandledErr;
}

OSStatus TView::MouseEntered(HIPoint& /*inMouseLocation*/, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ )
{
	return eventNotHandledErr;
}

OSStatus TView::MouseExited(HIPoint& /*inMouseLocation*/, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ )
{
	return eventNotHandledErr;
}

OSStatus TView::MouseWheelMoved( EventMouseWheelAxis /*inAxis*/, SInt32 /*inDelta*/, UInt32 /*inKeyModifiers*/ )
{
	return eventNotHandledErr;
}

OSStatus TView::ContextualMenuClick( HIPoint& /*inMouseLocation*/ )
{
	return eventNotHandledErr;
}


//-----------------------------------------------------------------------------------
//	ActivateInterface
//-----------------------------------------------------------------------------------
//	This routine is used to allow a subclass to turn on a specific event or suite of
//	events, like Drag and Drop. This allows us to keep event traffic down if we are
//	not interested, but register for the events if we are.
//
OSStatus TView::ActivateInterface(
	TView::Interface	inInterface )
{
	OSStatus		result = noErr;
	
	switch( inInterface )
	{
		case kDragAndDrop:
			{
				static const EventTypeSpec kDragEvents[] =
				{
					{ kEventClassControl, kEventControlDragEnter },			
					{ kEventClassControl, kEventControlDragLeave },			
					{ kEventClassControl, kEventControlDragWithin },			
					{ kEventClassControl, kEventControlDragReceive }
				};
				
				result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kDragEvents ),
						kDragEvents );

				SetControlDragTrackingEnabled( GetViewRef(), true);
			}
			break;

		case kKeyboardFocus:
			{
				static const EventTypeSpec kKeyboardFocusEvents[] =
				{
					{ kEventClassControl, kEventControlSetFocusPart },
					{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
				};
				
				result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kKeyboardFocusEvents ),
						kKeyboardFocusEvents );
			}
			break;

		case kMouse:
			{
				if ( mouseEventHandler == NULL )
					{
					// This case is quite different: Mouse events are delivered to the window, not the controls.
					// mouse wheel events are the exception: They ARE delivered to the control
					static const EventTypeSpec kControlMouseEvents[] =
					{
						{ kEventClassMouse, kEventMouseWheelMoved },
						{ kEventClassControl, kEventControlContextualMenuClick },
						// So we can reinstall our events when the window changes
						{ kEventClassControl, kEventControlOwningWindowChanged }, 
					};

					result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kControlMouseEvents ),
									  kControlMouseEvents );
					}

				if ( this->GetOwner() != NULL )
					{
					// We use "-1" to indicate that we want to install an event handler once we get a window
					if ( mouseEventHandler != NULL &&  mouseEventHandler != reinterpret_cast<void*>( -1 ) )
						{
						result = RemoveEventHandler( mouseEventHandler );
						}
					mouseEventHandler = NULL;
					
					static const EventTypeSpec kWindowMouseEvents[] =
					{
						{ kEventClassMouse, kEventMouseDown },
						{ kEventClassMouse, kEventMouseUp },
						{ kEventClassMouse, kEventMouseMoved },
						{ kEventClassMouse, kEventMouseDragged },
					};

					result = InstallEventHandler( GetWindowEventTarget( this->GetOwner() ), WindowEventHandler,
								      GetEventTypeCount( kWindowMouseEvents ), kWindowMouseEvents, 
								      this, &mouseEventHandler );
					}
				// If we have no window yet. Set the mouseEventHandler to -1 so when we get one we
				// will install the event handler
				else
					{
					mouseEventHandler = reinterpret_cast<EventHandlerRef>( -1 );
					}
			}
			break;
		case kMouseTracking:
		  {
		    {
		      static const EventTypeSpec kControlMouseEvents[] =
			{
			  { kEventClassMouse, kEventMouseEntered }, // only works if mousetracking is started
			  { kEventClassMouse, kEventMouseExited }, // only works if mousetracking is started
			};
		      
		      result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kControlMouseEvents ),
						       kControlMouseEvents );
		    }
		  }

		//default:
		//	assert( false );
	}
	
	return result;
}

OSStatus TView::InstallTimer( EventTimerInterval inFireDelay, EventLoopTimerRef* outTimer )
{
	return InstallEventLoopTimer( GetCurrentEventLoop(), inFireDelay, inFireDelay, TimerEventHandler, this, outTimer );
}

//-----------------------------------------------------------------------------------
//	RegisterSubclass
//-----------------------------------------------------------------------------------
//	This routine should be called by subclasses so they can be created as HIObjects.
//
OSStatus TView::RegisterSubclass(
	CFStringRef			inID,
	ConstructProc		inProc )
{
	return HIObjectRegisterSubclass( inID, kHIViewClassID, 0, ObjectEventHandler,
			GetEventTypeCount( kHIObjectEvents ), kHIObjectEvents, (void*) inProc, NULL );
}

//-----------------------------------------------------------------------------------
//	ObjectEventHandler
//-----------------------------------------------------------------------------------
//	Our static event handler proc. We handle any HIObject based events directly in
// 	here at present.
//
pascal OSStatus TView::ObjectEventHandler(
	EventHandlerCallRef	inCallRef,
	EventRef			inEvent,
	void*				inUserData )
{
	OSStatus			result = eventNotHandledErr;
	TView*				view = (TView*) inUserData;
	TCarbonEvent		event( inEvent );
	
	switch ( event.GetClass() )
	{
		case kEventClassHIObject:
			switch ( event.GetKind() )
			{
				case kEventHIObjectConstruct:
					{
						ControlRef		control; // ControlRefs are HIObjectRefs
						TView*			view;

						result = event.GetParameter<HIObjectRef>( kEventParamHIObjectInstance,
								typeHIObjectRef, (HIObjectRef*)&control );
						require_noerr( result, ParameterMissing );
						
						// on entry for our construct event, we're passed the
						// creation proc we registered with for this class.
						// we use it now to create the instance, and then we
						// replace the instance parameter data with said instance
						// as type void.

						result = (*(ConstructProc)inUserData)( control, &view );
						if ( result == noErr )
							event.SetParameter<TViewPtr>( kEventParamHIObjectInstance,
									typeVoidPtr, view ); 
					}
					break;
				
				case kEventHIObjectInitialize:
					result = CallNextEventHandler( inCallRef, inEvent );
					if ( result == noErr )
						result = view->Initialize( event );
					break;
				
				case kEventHIObjectDestruct:
					delete view;
					break;
			}
			break;
	}

ParameterMissing:

	return result;
}

//-----------------------------------------------------------------------------------
//	ViewEventHandler
//-----------------------------------------------------------------------------------
//	Our static event handler proc. We handle all non-HIObject events here.
//
pascal OSStatus TView::ViewEventHandler(
	EventHandlerCallRef	inCallRef,
	EventRef			inEvent,
	void*				inUserData )
{
	OSStatus			result;
	TView*				view = (TView*) inUserData;
	TCarbonEvent		event( inEvent );
	//if (view->debugPrint)
	//  fprintf(stderr,"TView::ViewEventHandler\n");
	result = view->HandleEvent( inCallRef, event );

	return result;
}


pascal OSStatus TView::WindowEventHandler(
	EventHandlerCallRef	inCallRef,
	EventRef			inEvent,
	void*				inUserData )
{
	TView* view = reinterpret_cast<TView*>( inUserData );
	TCarbonEvent event( inEvent );

	const WindowRef window = view->GetOwner();
	if (NULL == window)
		return eventNotHandledErr;

	// If the window is not active, let the standard window handler execute.
	if ( ! IsWindowActive( window ) ) return eventNotHandledErr;
	if (view->debugPrint)
	  fprintf(stderr,"TView::WindowEventHandler\n");
	
	const HIViewRef rootView = HIViewGetRoot( window );
	if (NULL == rootView)
		return eventNotHandledErr;

	// TODO: On OS X 10.3, test if this bug still exists
	// This is a hack to work around a bug in the OS. See:
	// http://lists.apple.com/archives/carbon-development/2002/Sep/29/keventmousemovedeventsno.004.txt
	OSStatus err;
	if ( event.GetKind() == kEventMouseMoved )
	{
		// We need to set some parameters correctly
		event.SetParameter( kEventParamWindowRef, window );

		HIPoint ptMouse;
		event.GetParameter( kEventParamMouseLocation, &ptMouse );

		// convert screen coords to window relative
		Rect bounds;
		err = GetWindowBounds( window, kWindowStructureRgn, &bounds );
		if( err == noErr )
		{
			ptMouse.x -= bounds.left;
			ptMouse.y -= bounds.top;
			event.SetParameter( kEventParamWindowMouseLocation, ptMouse );
		}
	}
	
	HIViewRef targetView = NULL;
	err = HIViewGetViewForMouseEvent( rootView, inEvent, &targetView );
	//if (view->debugPrint)
	//  fprintf(stderr,"TView::WindowEventHandler root[%08X] viewRef[%08X] targetView[%08X]\n", rootView, view->GetViewRef(), targetView);
	if ( targetView == view->GetViewRef() || event.GetKind() == kEventMouseDragged )
		{
		return view->HandleEvent( inCallRef, event );
		}

	return eventNotHandledErr;
}

pascal void TView::TimerEventHandler( EventLoopTimerRef inTimer, void* view )
{
	reinterpret_cast<TView*>( view )->TimerFired( inTimer );
}

//-----------------------------------------------------------------------------------
//	HandleEvent
//-----------------------------------------------------------------------------------
//	Our object's virtual event handler method. I'm not sure if we need this these days.
//	We used to do various things with it, but those days are long gone...
//
OSStatus TView::HandleEvent(
	EventHandlerCallRef	inCallRef,
	TCarbonEvent&		inEvent )
{
#pragma unused( inCallRef )

	OSStatus			result = eventNotHandledErr;
	HIPoint				where;
	OSType				tag;
	void *				ptr;
	Size				size, outSize;
	UInt32				features;
	RgnHandle			region = NULL;
	ControlPartCode		part;
	RgnHandle			invalRgn;
	
	switch ( inEvent.GetClass() )
	{
		case kEventClassCommand:
			{
				HICommand		command;
				
				result = inEvent.GetParameter( kEventParamDirectObject, &command );
				require_noerr( result, MissingParameter );
				
				switch ( inEvent.GetKind() )
				{
					case kEventCommandProcess:
						result = ProcessCommand( command );
						break;
					
					case kEventCommandUpdateStatus:
						result = UpdateCommandStatus( command );
						break;
				}
			}
			break;

		case kEventClassControl:
			switch ( inEvent.GetKind() )
			{
				case kEventControlInitialize:
					features = GetBehaviors();
					inEvent.SetParameter( kEventParamControlFeatures, features );
					result = noErr;
					break;
					
				case kEventControlDraw:
					{
						CGContextRef		context = NULL;
						
						inEvent.GetParameter( kEventParamRgnHandle, &region );
						inEvent.GetParameter<CGContextRef>( kEventParamCGContextRef, typeCGContextRef, &context );

						Draw( region, context );
						result = noErr;
					}
					break;

				case kEventControlHitTest:
					inEvent.GetParameter<HIPoint>( kEventParamMouseLocation, typeHIPoint, &where );
					part = HitTest( where );
					inEvent.SetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, part );
					result = noErr;
					break;
					
				case kEventControlGetPartRegion:
					inEvent.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, &part );
					inEvent.GetParameter( kEventParamControlRegion, &region );
					result = GetRegion( part, region );
					break;
				
				case kEventControlGetData:
					inEvent.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, &part );
					inEvent.GetParameter<OSType>( kEventParamControlDataTag, typeEnumeration, &tag );
					inEvent.GetParameter<Ptr>( kEventParamControlDataBuffer, typePtr, (Ptr*)&ptr );
					inEvent.GetParameter<Size>( kEventParamControlDataBufferSize, typeLongInteger, &size );

					result = GetData( tag, part, size, &outSize, ptr );

					if ( result == noErr )
						verify_noerr( inEvent.SetParameter<Size>( kEventParamControlDataBufferSize, typeLongInteger, outSize ) );
					break;
				
				case kEventControlSetData:
					inEvent.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, &part );
					inEvent.GetParameter<OSType>( kEventParamControlDataTag, typeEnumeration, &tag );
					inEvent.GetParameter<Ptr>( kEventParamControlDataBuffer, typePtr, (Ptr*)&ptr );
					inEvent.GetParameter<Size>( kEventParamControlDataBufferSize, typeLongInteger, &size );

					result = SetData( tag, part, size, ptr );
					break;
				
				case kEventControlGetOptimalBounds:
					{
						HISize		size;
						float		floatBaseLine;
						
						result = GetOptimalSize( &size, &floatBaseLine );
						if ( result == noErr )
						{
							Rect		bounds;
							SInt16		baseLine;

							GetControlBounds( GetViewRef(), &bounds );

							bounds.bottom = bounds.top + (SInt16)size.height;
							bounds.right = bounds.left + (SInt16)size.width;
							baseLine = (SInt16)floatBaseLine;
							
							inEvent.SetParameter( kEventParamControlOptimalBounds, bounds );
							inEvent.SetParameter<SInt16>( kEventParamControlOptimalBaselineOffset, typeShortInteger, baseLine );
						}
					}
					break;
				
				case kEventControlBoundsChanged:
					{
						HIRect		prevRect, currRect;
						UInt32		attrs;
						
						inEvent.GetParameter( kEventParamAttributes, &attrs );
						inEvent.GetParameter( kEventParamOriginalBounds, &prevRect );
						inEvent.GetParameter( kEventParamCurrentBounds, &currRect );
						inEvent.GetParameter( kEventParamControlInvalRgn, &invalRgn );

						result = BoundsChanged( attrs, prevRect, currRect, invalRgn );

						if ( mouseEventHandler != NULL )
							{
							ActivateInterface( kMouse );
							}

					}
					break;

				case kEventControlHit:
					{
						UInt32		modifiers;
						
						inEvent.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, &part );
						inEvent.GetParameter( kEventParamKeyModifiers, &modifiers );
	
						result = ControlHit( part, modifiers );
					}
					break;
				
				case kEventControlHiliteChanged:
					{
						ControlPartCode	prevPart, currPart;
						
						inEvent.GetParameter<ControlPartCode>( kEventParamControlPreviousPart, typeControlPartCode, &prevPart );
						inEvent.GetParameter<ControlPartCode>( kEventParamControlCurrentPart, typeControlPartCode, &currPart );
						inEvent.GetParameter( kEventParamControlInvalRgn, &invalRgn );

						result = HiliteChanged( prevPart, currPart, invalRgn );
						
						if ( GetAutoInvalidateFlags() & kAutoInvalidateOnHilite )
							Invalidate();
					}
					break;
					
				case kEventControlActivate:
					result = ActiveStateChanged();

					if ( GetAutoInvalidateFlags() & kAutoInvalidateOnActivate )
						Invalidate();
					break;
					
				case kEventControlDeactivate:
					result = ActiveStateChanged();

					if ( GetAutoInvalidateFlags() & kAutoInvalidateOnActivate )
						Invalidate();
					break;
					
				case kEventControlValueFieldChanged:
					result = ValueChanged();

					if ( GetAutoInvalidateFlags() & kAutoInvalidateOnValueChange )
						Invalidate();
					break;
					
				case kEventControlTitleChanged:
					result = TitleChanged();

					if ( GetAutoInvalidateFlags() & kAutoInvalidateOnTitleChange )
						Invalidate();
					break;
					
				case kEventControlEnabledStateChanged:
					result = EnabledStateChanged();

					if ( GetAutoInvalidateFlags() & kAutoInvalidateOnEnable )
						Invalidate();
					break;
					
				case kEventControlDragEnter:
				case kEventControlDragLeave:
				case kEventControlDragWithin:
					{
						DragRef		drag;
						bool		likesDrag;
						
						inEvent.GetParameter( kEventParamDragRef, &drag );

						switch ( inEvent.GetKind() )
						{
							case kEventControlDragEnter:
								likesDrag = DragEnter( drag );
								// Why only if likesDrag?  What if it doesn't?  No parameter?
								if ( likesDrag )
									result = inEvent.SetParameter( kEventParamControlLikesDrag, likesDrag );
								break;
							
							case kEventControlDragLeave:
								DragLeave( drag );
								result = noErr;
								break;
							
							case kEventControlDragWithin:
								DragWithin( drag );
								result = noErr;
								break;
						}
					}
					break;
				
				case kEventControlDragReceive:
					{
						DragRef		drag;
						
						inEvent.GetParameter( kEventParamDragRef, &drag );

						result = DragReceive( drag );
					}
					break;
				
				case kEventControlTrack:
					{
						ControlPartCode		part;
						
						result = Track( inEvent, &part );
						if ( result == noErr )
							verify_noerr( inEvent.SetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, part ) );
					}
					break;

				case kEventControlGetSizeConstraints:
					{
						HISize		minSize, maxSize;
						
						result = GetSizeConstraints( &minSize, &maxSize );

						if ( result == noErr )
						{
							verify_noerr( inEvent.SetParameter( kEventParamMinimumSize, minSize ) );
							verify_noerr( inEvent.SetParameter( kEventParamMaximumSize, maxSize ) );
						}
					}
					break;

				case kEventControlSetFocusPart:
					{
						ControlPartCode		desiredFocus;
						RgnHandle			invalidRgn;
						Boolean				focusEverything;
						ControlPartCode		actualFocus;
						
						result = inEvent.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, &desiredFocus ); 
						require_noerr( result, MissingParameter );
						
						inEvent.GetParameter( kEventParamControlInvalRgn, &invalidRgn );

						focusEverything = false; // a good default in case the parameter doesn't exist

						inEvent.GetParameter( kEventParamControlFocusEverything, &focusEverything );

						result = SetFocusPart( desiredFocus, invalidRgn, focusEverything, &actualFocus );
						
						if ( result == noErr )
							verify_noerr( inEvent.SetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode, actualFocus ) );
					}
					break;

				case kEventControlOwningWindowChanged:
					{
						// If our owning window has changed, reactivate the mouse interface
						if ( mouseEventHandler != NULL )
							{
							ActivateInterface( kMouse );
							}
					}
					break;

				case kEventControlContextualMenuClick:
					{
						HIPoint ptMouse;
						inEvent.GetParameter( kEventParamMouseLocation, &ptMouse );
						result = ContextualMenuClick( ptMouse );
					}
					break;
					
				// some other kind of Control event
				default:
					break;
			}
			break;
			
		case kEventClassTextInput:
			result = TextInput( inEvent );
			break;

		case kEventClassMouse:
		{
			result = inEvent.GetParameter<HIPoint>( kEventParamWindowMouseLocation, typeHIPoint, &where );
			HIViewConvertPoint( &where, NULL, fViewRef );
			
			UInt32 inKeyModifiers;
			result = inEvent.GetParameter( kEventParamKeyModifiers, &inKeyModifiers );
			if( result != noErr )
				inKeyModifiers = 0;
			EventMouseButton inMouseButton = 0;
			result = inEvent.GetParameter<EventMouseButton>( kEventParamMouseButton, typeMouseButton, &inMouseButton );
			if (noErr != result)
				// assume no button is pressed if there is no button info
				inMouseButton = 0;
			UInt32 inClickCount;
			result = inEvent.GetParameter( kEventParamClickCount, &inClickCount );
			if( result != noErr )
				inClickCount = 0;
				
			switch ( inEvent.GetKind() )
			{
				case kEventMouseWheelMoved:
				{
					EventMouseWheelAxis inAxis;
					result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis );

					SInt32 inDelta;
					result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta );

					result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers );
					break;				
				}
				case kEventMouseDown:
					result = MouseDown( where, inKeyModifiers, inMouseButton, inClickCount, inEvent );
					break;
				case kEventMouseUp:
					result = MouseUp( where, inKeyModifiers, inMouseButton, inClickCount );
					break;
				case kEventMouseExited:
					result = MouseExited( where, inKeyModifiers, inMouseButton, inClickCount );
					break;
				case kEventMouseEntered:
					result = MouseEntered( where, inKeyModifiers, inMouseButton, inClickCount );
					break;
				case kEventMouseMoved:
				case kEventMouseDragged:
					result = MouseDragged( where, inKeyModifiers, inMouseButton, inClickCount );
					break;
				default:;
			}
			break;
                }
		// some other event class
		default:
			break;
	}

MissingParameter:
	return result;
}

//-----------------------------------------------------------------------------------
//	CreateInitializationEvent
//-----------------------------------------------------------------------------------
// 	Create a basic intialization event containing the parent control and bounds. At
//	present we set the bounds to empty and the parent to NULL. In theory, after creating
//	this event, any subclass could add its own parameter to receive in its
//	Initialize method.
//
EventRef TView::CreateInitializationEvent()
{
	OSStatus		result = noErr;
	EventRef		event;

	result = CreateEvent( NULL, kEventClassHIObject, kEventHIObjectInitialize,
					GetCurrentEventTime(), 0, &event );
	require_noerr_action( result, CantCreateEvent, event = NULL );
		
CantCreateEvent:
	return event;
}

//-----------------------------------------------------------------------------------
//	Frame
//-----------------------------------------------------------------------------------
//
HIRect TView::Frame()
{
	HIRect		frame;

	HIViewGetFrame( GetViewRef(), &frame );
	
	return frame;
}

//-----------------------------------------------------------------------------------
//	SetFrame
//-----------------------------------------------------------------------------------
//
OSStatus TView::SetFrame(
	const HIRect&			inFrame )
{
	OSStatus				err;
	
	err = HIViewSetFrame( GetViewRef(), &inFrame );
	
	return err;
}

//-----------------------------------------------------------------------------------
//	Bounds
//-----------------------------------------------------------------------------------
//
HIRect TView::Bounds()
{
	HIRect		bounds;
	
	HIViewGetBounds( GetViewRef(), &bounds );
	
	return bounds;
}

//-----------------------------------------------------------------------------------
//	Show
//-----------------------------------------------------------------------------------
//
OSStatus TView::Show()
{
	return HIViewSetVisible( GetViewRef(), true );
}

//-----------------------------------------------------------------------------------
//	Hide
//-----------------------------------------------------------------------------------
//
OSStatus TView::Hide()
{
	return HIViewSetVisible( GetViewRef(), false );
}

//-----------------------------------------------------------------------------------
//	GetEventTarget
//-----------------------------------------------------------------------------------
//
EventTargetRef TView::GetEventTarget()
{
	return HIObjectGetEventTarget( (HIObjectRef) GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	AddSubView
//-----------------------------------------------------------------------------------
//
OSStatus TView::AddSubView(
	TView*				inSubView )
{
	return HIViewAddSubview( GetViewRef(), inSubView->GetViewRef() );;
}

//-----------------------------------------------------------------------------------
//	RemoveFromSuperView
//-----------------------------------------------------------------------------------
//
OSStatus TView::RemoveFromSuperView()
{
	return HIViewRemoveFromSuperview( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	GetHilite
//-----------------------------------------------------------------------------------
//
ControlPartCode TView::GetHilite()
{
	return GetControlHilite( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	GetValue
//-----------------------------------------------------------------------------------
//
SInt32 TView::GetValue()
{
	return GetControl32BitValue( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	SetValue
//-----------------------------------------------------------------------------------
//
void TView::SetValue(
	SInt32					inValue )
{
	SetControl32BitValue( GetViewRef(), inValue );
}

//-----------------------------------------------------------------------------------
//	GetMinimum
//-----------------------------------------------------------------------------------
//
SInt32 TView::GetMinimum()
{
	return GetControlMinimum( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	SetMinimum
//-----------------------------------------------------------------------------------
//
void TView::SetMinimum(
	SInt32					inMinimum )
{
	SetControlMinimum( GetViewRef(), inMinimum );
}

//-----------------------------------------------------------------------------------
//	GetMaximum
//-----------------------------------------------------------------------------------
//
SInt32 TView::GetMaximum()
{
	return GetControlMaximum( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	SetMaximum
//-----------------------------------------------------------------------------------
//
void TView::SetMaximum(
	SInt32					inMaximum )
{
	SetControlMaximum( GetViewRef(), inMaximum );
}

//-----------------------------------------------------------------------------------
//	GetOwner
//-----------------------------------------------------------------------------------
//
WindowRef TView::GetOwner()
{
	return GetControlOwner( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	Hilite
//-----------------------------------------------------------------------------------
//
void TView::Hilite(
	ControlPartCode			inPart)
{
	return HiliteControl( GetViewRef(), inPart );
}

//-----------------------------------------------------------------------------------
//	Invalidate
//-----------------------------------------------------------------------------------
//
OSStatus TView::Invalidate()
{
	return HIViewSetNeedsDisplay( GetViewRef(), true );
}

void TView::TimerFired( EventLoopTimerRef inTimer )
{
#pragma unused( inTimer )
}


//-----------------------------------------------------------------------------------
//	IsVisible
//-----------------------------------------------------------------------------------
//
Boolean TView::IsVisible()
{
	return IsControlVisible( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	IsEnabled
//-----------------------------------------------------------------------------------
//
Boolean TView::IsEnabled()
{
	return IsControlEnabled( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	IsActive
//-----------------------------------------------------------------------------------
//
Boolean TView::IsActive()
{
	return IsControlActive( GetViewRef() );
}

//-----------------------------------------------------------------------------------
//	ActiveStateChanged
//-----------------------------------------------------------------------------------
//	Default activation method. Subclasses should override as necessary. We do nothing
//	here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::ActiveStateChanged()
{
	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	ValueChanged
//-----------------------------------------------------------------------------------
//	Default value changed method. Subclasses should override as necessary. We do
//	nothing here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::ValueChanged()
{
	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	TitleChanged
//-----------------------------------------------------------------------------------
//	Default title changed method. Subclasses should override as necessary. We
//	do nothing here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::TitleChanged()
{
	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	EnabledStateChanged
//-----------------------------------------------------------------------------------
//	Default enable method. Subclasses should override as necessary. We
//	do nothing here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::EnabledStateChanged()
{
	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	TextInput
//-----------------------------------------------------------------------------------
//	Default text (Unicode) input method. Subclasses should override as necessary. We
//	do nothing here in the base class, so we return eventNotHandledErr.
//
OSStatus TView::TextInput(
	TCarbonEvent&		inEvent )
{
#pragma unused( inEvent )

	return eventNotHandledErr;
}

//-----------------------------------------------------------------------------------
//	ChangeAutoInvalidateFlags
//-----------------------------------------------------------------------------------
//	Change behavior for auto-invalidating views on certain actions.
//
void TView::ChangeAutoInvalidateFlags(
	OptionBits			inSetThese,
	OptionBits			inClearThese )
{
    fAutoInvalidateFlags = ( ( fAutoInvalidateFlags | inSetThese ) & ( ~inClearThese ) );
}
