diff options
Diffstat (limited to 'macosx/TView.cxx')
-rw-r--r-- | macosx/TView.cxx | 1462 |
1 files changed, 1462 insertions, 0 deletions
diff --git a/macosx/TView.cxx b/macosx/TView.cxx new file mode 100644 index 000000000..9ed02b110 --- /dev/null +++ b/macosx/TView.cxx @@ -0,0 +1,1462 @@ +/* + 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 ) ) + { + OSStatus err; + err = RemoveEventHandler( mouseEventHandler ); + assert( err == noErr ); + } + 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 ); + assert( result == noErr ); + } + + 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 ); + assert( result != NULL ); + } + mouseEventHandler = NULL; + + static const EventTypeSpec kWindowMouseEvents[] = + { + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + }; + + assert( mouseEventHandler == NULL ); + result = InstallEventHandler( GetWindowEventTarget( this->GetOwner() ), WindowEventHandler, + GetEventTypeCount( kWindowMouseEvents ), kWindowMouseEvents, + this, &mouseEventHandler ); + assert( result == noErr && mouseEventHandler != NULL ); + } + // 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 ); + assert( result == noErr ); + } + } + + //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(); + assert( window != NULL ); + + // 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 ); + assert( rootView != NULL ); + + // 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 ); + assert( err == noErr ); + + ptMouse.x -= bounds.left; + ptMouse.y -= bounds.top; + + event.SetParameter( kEventParamWindowMouseLocation, ptMouse ); + } + + HIViewRef targetView = NULL; + err = HIViewGetViewForMouseEvent( rootView, inEvent, &targetView ); + assert( err == noErr && targetView != NULL ); + 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, ®ion ); + 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, ®ion ); + 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: + assert( false ); + break; + } + break; + + case kEventClassTextInput: + result = TextInput( inEvent ); + break; + + case kEventClassMouse: + result = inEvent.GetParameter<HIPoint>( kEventParamWindowMouseLocation, typeHIPoint, &where ); + HIViewConvertPoint( &where, NULL, fViewRef ); + assert( result == noErr ); + + UInt32 inKeyModifiers; + result = inEvent.GetParameter( kEventParamKeyModifiers, &inKeyModifiers ); + assert( result == noErr ); + + switch ( inEvent.GetKind() ) + { + case kEventMouseWheelMoved: + { + EventMouseWheelAxis inAxis; + result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis ); + assert( noErr == result ); + + SInt32 inDelta; + result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta ); + assert( noErr == result ); + + result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers ); + } + break; + + + // some other kind of Mouse event: This is (in my opinion) an error + // TODO: There is also a MouseMoved event that we do not handle + default: + { + EventMouseButton inMouseButton; + result = inEvent.GetParameter<EventMouseButton>( kEventParamMouseButton, typeMouseButton, &inMouseButton ); + assert( result == noErr ); + + UInt32 inClickCount; + result = inEvent.GetParameter( kEventParamClickCount, &inClickCount ); + assert( result == noErr ); + + switch ( inEvent.GetKind() ) + { + 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; + } + } + break; + } + break; + + // some other event class + default: + assert( false ); + 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 ) ); +} |