Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
LabelItemView.cp
/* |
File: LabelItemView.cp |
Version: Mac OS X |
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 © 2003 Apple Computer, Inc., All Rights Reserved |
*/ |
#include "LabelItemView.h" |
#include "TView.h" |
//====================================================================================== |
// ¥ Constants |
//====================================================================================== |
#define kTViewHILabelViewClassID CFSTR( "com.apple.sample.HILabelView" ) // HILabelView class ID |
#define kRolloverTimerDelay 0.1 // delay before the rollover timer fires |
enum |
{ |
kLabelNameHeight = 15, // height allowed for the label name |
kLabelPartWidth = 17, // width of the entire label part |
kLabelPartHeight = 16, // height of the entire label part |
kFirstLabelSpace = 5 // extra space between the "none" part and the first label part |
}; |
// menu item property constants |
enum |
{ |
kMenuPropertyLabelViewCreator = 'Labl', // used for all LabelView properties |
kMenuPropertyLabelViewTag = 'LbVw' // an HILabelView* |
}; |
//====================================================================================== |
// ¥ Types |
//====================================================================================== |
class HILabelView : public TView |
{ |
public: |
static OSStatus Create( MenuRef inMenu, MenuItemIndex inMenuItem, HIViewRef inMenuContentView, HIViewRef* outView ); |
protected: |
HILabelView( HIViewRef inControl ); |
~HILabelView(); |
ControlKind GetKind(); |
OSStatus Initialize( TCarbonEvent& inEvent ); |
OSStatus ControlHit( HIViewPartCode inPart, UInt32 inModifiers ); |
void Draw( RgnHandle inLimitRgn, CGContextRef inContext ); |
OSStatus GetRegion( ControlPartCode inPart, RgnHandle outRgn ); |
HIViewPartCode HitTest( const HIPoint& inWhere ); |
OSStatus Track( TCarbonEvent& inEvent, ControlPartCode* outPartHit ); |
OSStatus GetSizeConstraints( HISize* outMin, HISize* outMax ); |
OSStatus GetOptimalSizeSelf( HISize* outSize, float* outBaseLine ); |
void VisibilityChanged(); |
private: |
enum { kNumParts = 8 }; // seven label buttons, plus the "none" button |
static const EventTypeSpec kControlEvents[]; |
static const EventTypeSpec kWindowEvents[]; |
static const EventTypeSpec kMenuEvents[]; |
static const EventTypeSpec kMenuContentEvents[]; |
static CGImageRef sImages[ kNumParts ]; |
static CGImageRef sSelectedBackgroundImage; |
static CGImageRef sPressedBackgroundImage; |
static CFStringRef sTitleString; |
static CFStringRef sLabelStrings[ 7 ]; |
MenuRef fMenu; // menu that owns this label item view |
MenuItemIndex fMenuItem; // menu item that is being used to draw this label item view |
HIViewRef fMenuContentView; // menu content view in which we are embedded |
EventHandlerRef fMenuHandler; // event handler installed on the menu |
EventHandlerRef fMenuContentHandler; // event handler installed on the menu content view |
EventHandlerRef fWindowHandler; // event handler installed on our owning window |
EventLoopTimerRef fRolloverTimer; // event loop timer used to avoid flicker |
HIViewPartCode fRollover; // the item part that is currently showing rollover effects |
HIViewPartCode fTextRollover; // during SimulateHit, the item part that shows rollover for its text |
HIViewPartCode fPartRollover; // during SimulateHit, the item part that shows rollover for its part |
HIViewPartCode fNewRollover; // the item part that the mouse has just moved over |
OptionBits fSelected; // which labels are selected |
int fLeftEdge; // x offset from left side of view to left edge of text and label parts |
float fPartYOffset; // y offset from top of label view to top of label parts |
static OSStatus Construct( HIObjectRef inObjectRef, TObject** outObject ); |
static pascal OSStatus EventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ); |
static pascal void RolloverChangedTimer( EventLoopTimerRef inTimer, void* inRefcon ); |
static CFStringRef GetTitleString(); |
static CFStringRef GetLabelString( int inPart ); |
static OSStatus LoadPartImages(); |
static int LabelIndexFromPart( int inPart ); |
static CGImageRef GetPartImage( int inPart ); |
static CGImageRef GetSelectedBackgroundImage(); |
static CGImageRef GetPressedBackgroundImage(); |
static CGImageRef GetBackgroundImage( CFStringRef inName, CGImageRef* ioImage ); |
MenuItemIndex FindItem(); |
HILabelView* GetMenuItemViewProperty( MenuItemIndex inItem ); |
void RedrawWithDelay( int inDelayTicks ); |
void DrawCenteredImage( CGContextRef inContext, CGPoint inCenter, CGImageRef inImage ); |
void GetTitleBounds( HIRect* outBounds ); |
void GetLabelBounds( HIRect* outBounds ); |
void GetPartBounds( int inPart, HIRect* outBounds ); |
int GetLeftEdge(); |
void ClearRollover() |
{ |
fRollover = fTextRollover = fPartRollover = fNewRollover = 0; |
} |
}; |
static void _HIViewConvertFromGlobalPoint( HIPoint* ioPoint, HIViewRef inDestView ); |
//====================================================================================== |
// ¥ Globals |
//====================================================================================== |
const EventTypeSpec HILabelView::kControlEvents[] = |
{ |
{ kEventClassControl, kEventControlVisibilityChanged } |
}; |
const EventTypeSpec HILabelView::kWindowEvents[] = |
{ |
{ kEventClassMouse, kEventMouseMoved }, |
{ kEventClassMouse, kEventMouseDragged } |
}; |
const EventTypeSpec HILabelView::kMenuEvents[] = |
{ |
{ kEventClassMenu, kEventMenuMeasureItemWidth }, |
{ kEventClassMenu, kEventMenuMeasureItemHeight }, |
{ kEventClassMenu, kEventMenuDrawItem }, |
{ kEventClassMenu, kEventMenuCalculateSize }, |
{ kEventClassCommand, kEventCommandProcess }, |
/* |
These are currently required to support rollover effects in contextual menus. |
For pull-down menus, we get these events by registering on the menu window; |
however, the WWDC build of HIToolbox sends these events to the clicked window |
rather than to the menu window for contextual menus, so we need to register |
for them on the menu as well. This problem is fixed in HIToolbox for the post- |
WWDC builds, and once those builds are available, these handlers can be removed. |
*/ |
{ kEventClassMouse, kEventMouseMoved }, |
{ kEventClassMouse, kEventMouseDragged } |
}; |
const EventTypeSpec HILabelView::kMenuContentEvents[] = |
{ |
{ kEventClassControl, kEventControlSimulateHit } |
}; |
CGImageRef HILabelView::sImages[]; |
CGImageRef HILabelView::sSelectedBackgroundImage; |
CGImageRef HILabelView::sPressedBackgroundImage; |
CFStringRef HILabelView::sTitleString; |
CFStringRef HILabelView::sLabelStrings[]; |
//====================================================================================== |
// ¥ Functions and methods |
//====================================================================================== |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ HILabelViewCreate |
// Creates a new instance of the label item view. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelViewCreate( MenuRef inMenu, MenuItemIndex inMenuItem, HIViewRef inMenuContentView, HIViewRef* outView ) |
{ |
return HILabelView::Create( inMenu, inMenuItem, inMenuContentView, outView ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ HILabelView constructor |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
HILabelView::HILabelView( |
HIViewRef inControl ) |
: TView( inControl ), |
fMenu( NULL ), |
fMenuItem( 0 ), |
fMenuContentView( NULL ), |
fMenuHandler( NULL ), |
fMenuContentHandler( NULL ), |
fWindowHandler( NULL ), |
fRolloverTimer( NULL ), |
fRollover( 0 ), |
fTextRollover( 0 ), |
fPartRollover( 0 ), |
fNewRollover( 0 ), |
fSelected( 0 ), |
fLeftEdge( 0 ), |
fPartYOffset( 0 ) |
{ |
// add autoinvalidation |
ChangeAutoInvalidateFlags( kAutoInvalidateOnHilite | |
kAutoInvalidateOnActivate | |
kAutoInvalidateOnEnable, |
0 ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ HILabelView destructor |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// |
HILabelView::~HILabelView() |
{ |
if ( fMenuHandler != NULL ) |
RemoveEventHandler( fMenuHandler ); |
if ( fMenuContentHandler != NULL ) |
RemoveEventHandler( fMenuContentHandler ); |
if ( fWindowHandler != NULL ) |
RemoveEventHandler( fWindowHandler ); |
if ( fRolloverTimer != NULL ) |
RemoveEventLoopTimer( fRolloverTimer ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ Create |
// Public class method for creating a HILabelView. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::Create( |
MenuRef inMenu, |
MenuItemIndex inMenuItem, |
HIViewRef inMenuContentView, |
HIViewRef* outView ) |
{ |
OSStatus err = noErr; |
EventRef event = CreateInitializationEvent(); // create initialization event |
require_action( event != NULL, CantCreateEvent, err = eventInternalErr ); |
// add extra parameters |
if ( inMenu != NULL ) |
SetEventParameter( event, kEventParamMenuRef, typeMenuRef, sizeof( inMenu ), &inMenu ); |
if ( inMenuItem != 0 ) |
SetEventParameter( event, kEventParamMenuItemIndex, typeMenuItemIndex, sizeof( inMenuItem ), &inMenuItem ); |
if ( inMenuContentView != 0 ) |
SetEventParameter( event, kEventParamControlRef, typeControlRef, sizeof( inMenuContentView ), &inMenuContentView ); |
// register the subclass |
static bool sRegistered = false; |
if( !sRegistered ) |
{ |
RegisterSubclass( kTViewHILabelViewClassID, Construct ); |
sRegistered = true; |
} |
// instantiate the object |
err = HIObjectCreate( kTViewHILabelViewClassID, event, (HIObjectRef*) outView ); |
ReleaseEvent( event ); |
CantCreateEvent: |
return err; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetKind |
// Returns this view's ControlKind. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// |
ControlKind |
HILabelView::GetKind() |
{ |
static const ControlKind kHILabelViewKind = { 'LblI', 'LblI' }; |
return kHILabelViewKind; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ Construct |
// Allocates a new HILabelView object. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::Construct( |
HIObjectRef inObjectRef, |
TObject** outObject ) |
{ |
*outObject = new HILabelView( (HIViewRef) inObjectRef ); |
return noErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ Initialize |
// Initializes a new HILabelView object. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::Initialize( |
TCarbonEvent& inEvent ) |
{ |
OSStatus err; |
MenuItemIndex item; |
err = TView::Initialize( inEvent ); |
require_noerr( err, CantInitBaseClass ); |
inEvent.GetParameter( kEventParamMenuRef, &fMenu ); |
item = inEvent.GetParameter<MenuItemIndex>( kEventParamMenuItemIndex, typeMenuItemIndex ); |
// install a handler on ourselves for a few events that are not currently wrapped by TView |
InstallControlEventHandler( GetViewRef(), EventHandler, GetEventTypeCount( kControlEvents ), |
kControlEvents, this, NULL ); |
if ( fMenu != NULL ) |
{ |
// |
// We can't allow this menu to be cached, because the appearance of the label menu item |
// changes dynamically. If the menu image is cached, then the menu will always initially |
// display the label item as it was before the user started interacting with it (selecting/ |
// unselecting label colors), and any changes to the label item won't be initially visible. |
// |
ChangeMenuAttributes( fMenu, kMenuAttrDoNotCacheImage, 0 ); |
// |
// Insert a new item that will have the specified item index. Use CustomDraw |
// for this item so we can override MeasureItemWidth/Height to give it the |
// right size for our view. |
// |
InsertMenuItemTextWithCFString( fMenu, NULL, item - 1, kMenuItemAttrCustomDraw, 0 ); |
// |
// Set a menu property on the item that we've inserted so that we can identify this item |
// later, even if other items are inserted or removed before it (which will change its index). |
// |
HILabelView* view = this; |
SetMenuItemProperty( fMenu, item, kMenuPropertyLabelViewCreator, kMenuPropertyLabelViewTag, sizeof( this ), &view ); |
// |
// Install our handlers to implement custom measurement for this item. |
// The view itself will provide the custom drawing. |
// |
InstallMenuEventHandler( fMenu, EventHandler, GetEventTypeCount( kMenuEvents ), kMenuEvents, |
this, &fMenuHandler ); |
// |
// If we are given a menu content view, attach ourselves to it. |
// |
inEvent.GetParameter( kEventParamControlRef, &fMenuContentView ); |
if ( fMenuContentView != NULL ) |
{ |
HIViewAddSubview( fMenuContentView, GetViewRef() ); |
InstallEventHandler( GetControlEventTarget( fMenuContentView ), EventHandler, |
GetEventTypeCount( kMenuContentEvents ), kMenuContentEvents, |
this, &fMenuContentHandler ); |
} |
} |
CantInitBaseClass: |
return err; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ ControlHit |
// Handles clicks on the parts of a label item. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::ControlHit( HIViewPartCode inPart, UInt32 inModifiers ) |
{ |
UInt32 oldSelected; |
if ( inPart == 1 ) |
fSelected = 0; |
else |
fSelected ^= 1 << ( inPart - 1 ); |
if ( oldSelected != fSelected ) |
HIViewSetNeedsDisplay( GetViewRef(), true ); |
return noErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ Draw |
// Draws the label item. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::Draw( |
RgnHandle inLimitRgn, |
CGContextRef inContext ) |
{ |
HIRect bounds; |
GetTitleBounds( &bounds ); |
HIThemeTextInfo textInfo = { kHIThemeTextInfoVersionZero, kThemeStateActive, kThemeMenuItemFont, |
kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, 0, |
kHIThemeTextTruncationNone, 1, 0 }; |
HIThemeDrawTextBox( GetTitleString(), &bounds, &textInfo, inContext, kHIThemeOrientationNormal ); |
HIViewPartCode rollPart = fTextRollover; |
if ( rollPart == 0 ) |
rollPart = fRollover; |
if ( rollPart > 1 ) |
{ |
CFStringRef str = GetLabelString( rollPart ); |
if ( str != NULL ) |
{ |
GetLabelBounds( &bounds ); |
textInfo.fontID = kThemeSmallEmphasizedSystemFont; |
textInfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter; |
textInfo.truncationPosition = kHIThemeTextTruncationEnd; |
// draw this text in gray |
CGContextSetRGBFillColor( inContext, 0.5, 0.5, 0.5, 1.0 ); |
HIThemeDrawTextBox( str, &bounds, &textInfo, inContext, kHIThemeOrientationNormal ); |
} |
} |
rollPart = fPartRollover; |
if ( rollPart == 0 /* && GetCurrentEventButtonState() != 0 */ ) |
rollPart = fRollover; |
for ( int i = 1; i <= kNumParts; i++ ) |
{ |
Boolean rollover = i == rollPart; |
Boolean pressed = i == GetControlHilite( GetViewRef() ); |
Boolean selected = ( fSelected & ( 1 << ( i - 1 ) ) ) != 0; |
GetPartBounds( i, &bounds ); |
CGPoint center = { bounds.origin.x + ( bounds.size.width / 2 ), |
bounds.origin.y + ( bounds.size.height / 2 ) }; |
if ( rollover ) |
// ideally we'd have a custom image here for the rollover effect, rather than re-using the pressed image |
DrawCenteredImage( inContext, center, GetPressedBackgroundImage() ); |
else |
if ( pressed ) |
DrawCenteredImage( inContext, center, GetPressedBackgroundImage() ); |
else if ( selected ) |
DrawCenteredImage( inContext, center, GetSelectedBackgroundImage() ); |
DrawCenteredImage( inContext, center, GetPartImage( i ) ); |
} |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetTitleString |
// Returns the string that is drawn above the label buttons ("Color Label:"). |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CFStringRef |
HILabelView::GetTitleString() |
{ |
if ( sTitleString == NULL ) |
sTitleString = CFBundleCopyLocalizedString( CFBundleGetMainBundle(), CFSTR("HILabelTitle"), NULL, NULL ); |
return sTitleString; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetLabelString |
// Returns a string containing the name of a label part; |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CFStringRef |
HILabelView::GetLabelString( int inPart ) |
{ |
check( inPart >= 2 ); |
check( inPart <= 8 ); |
// convert from 1-based label part index to zero-based label array index |
int index = inPart - 2; |
// ¥¥¥ we need to invalidate this cache when the label changes |
if ( sLabelStrings[ index ] == NULL ) |
{ |
RGBColor rgb; |
Str255 st; |
GetLabel( LabelIndexFromPart( inPart ), &rgb, st ); |
CFStringRef labelStr = CFStringCreateWithPascalString( NULL, st, GetApplicationTextEncoding() ); |
if ( labelStr != NULL ) |
{ |
CFStringRef formatStr = CFBundleCopyLocalizedString( CFBundleGetMainBundle(), |
CFSTR("HILabelFormat"), |
NULL, NULL ); |
if ( formatStr != NULL ) |
{ |
sLabelStrings[ index ] = CFStringCreateWithFormat( NULL, NULL, formatStr, labelStr ); |
CFRelease( formatStr ); |
} |
CFRelease( labelStr ); |
} |
} |
return sLabelStrings[ index ]; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ DrawCenteredImage |
// Draws a CGImageRef centered around a point. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::DrawCenteredImage( CGContextRef inContext, CGPoint inCenter, CGImageRef inImage ) |
{ |
float imageWidth = CGImageGetWidth( inImage ); |
float imageHeight = CGImageGetHeight( inImage ); |
HIRect bounds; |
bounds.size.width = imageWidth; |
bounds.size.height = imageHeight; |
bounds.origin.x = inCenter.x - ( imageWidth / 2 ); |
bounds.origin.y = inCenter.y - ( imageHeight / 2 ); |
HIViewDrawCGImage( inContext, &bounds, inImage ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊGetRegion |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::GetRegion( ControlPartCode inPart, RgnHandle outRgn ) |
{ |
// |
// We provide the clickable meta-region so that if the user clicks in portions of the view |
// that are not trackable, the click can pass through the control and initiate async dragging. |
// |
if ( inPart == kControlClickableMetaPart ) |
{ |
RgnHandle tempRgn = NewRgn(); |
if ( tempRgn == NULL ) |
return eventNotHandledErr; |
for ( int i = 1; i <= kNumParts; i++ ) |
{ |
HIRect bounds; |
GetPartBounds( i, &bounds ); |
SetRectRgn( tempRgn, |
(short) bounds.origin.x, (short) bounds.origin.y, |
(short) CGRectGetMaxX( bounds ), (short) CGRectGetMaxY( bounds ) ); |
UnionRgn( tempRgn, outRgn, outRgn ); |
} |
DisposeRgn( tempRgn ); |
return noErr; |
} |
else |
{ |
return eventNotHandledErr; |
} |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ HitTest |
// Hit-tests a point against the label item view. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
HIViewPartCode |
HILabelView::HitTest( |
const HIPoint& inWhere ) |
{ |
HIViewPartCode part = kControlNoPart; |
// is the mouse on the item? |
if( CGRectContainsPoint( Bounds(), inWhere ) ) |
{ |
for ( int i = 1; i <= kNumParts; i++ ) |
{ |
HIRect bounds; |
GetPartBounds( i, &bounds ); |
if ( CGRectContainsPoint( bounds, inWhere ) ) |
{ |
part = i; |
break; |
} |
} |
} |
return part; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊTrack |
// Handles control tracking. We only use it to clear our rollover state. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::Track( TCarbonEvent& inEvent, ControlPartCode* outPartHit ) |
{ |
// |
// Clear rollover state so that it doesn't interfere with proper part drawing during tracking. |
// Otherwise, when you move the mouse out of the part that you clicked on, the part remains |
// drawn in its rollover state because fRollover still contains that part. |
// |
ClearRollover(); |
// let the Control Manager's standard control tracking occur |
return eventNotHandledErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊGetSizeConstraints |
// Returns our minimum and maximum sizes. Required for views that are used as custom |
// toolbar item views. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::GetSizeConstraints( HISize* outMin, HISize* outMax ) |
{ |
GetOptimalSize( outMin, NULL ); |
*outMax = *outMin; |
return noErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊGetOptimalSizeSelf |
// Returns our optimal size. Required for views that are used as custom toolbar item |
// views. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::GetOptimalSizeSelf( HISize* outSize, float* outBaseLine ) |
{ |
// get the right edge of the last part |
HIRect partBounds; |
GetPartBounds( kNumParts, &partBounds ); |
outSize->width = CGRectGetMaxX( partBounds ); |
// add the origin of the first part, so that the same space is on the right side of the last part |
GetPartBounds( 1, &partBounds ); |
outSize->width += partBounds.origin.x; |
HIThemeTextInfo textInfo = { kHIThemeTextInfoVersionZero, kThemeStateActive, kThemeMenuItemFont, |
kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, |
0, kHIThemeTextTruncationNone, 0 }; |
HIThemeGetTextDimensions( CFSTR("Sample"), 0, &textInfo, NULL, &fPartYOffset, outBaseLine ); |
// |
// Calculate the bounds of the first part. This will take the new value of fPartYOffset |
// into account. The bottom of the part bounds is our view height. |
// |
GetPartBounds( 1, &partBounds ); |
outSize->height = CGRectGetMaxY( partBounds ) + kLabelNameHeight; |
return noErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊVisibilityChanged |
// Our visibility has changed. Effectively, this is called when the menu or toolbar |
// containing us has changed visibility. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::VisibilityChanged() |
{ |
// |
// Whenever the label view becomes visible, we install a handler on our owning window |
// to track mouse-moved and mouse-dragged events; this lets us show rollover feedback as |
// the mouse passes over the label parts. |
// |
if ( IsVisible() ) |
{ |
if ( fWindowHandler == NULL ) |
{ |
InstallEventHandler( GetWindowEventTarget( GetControlOwner( GetViewRef() ) ), EventHandler, |
GetEventTypeCount( kWindowEvents ), |
kWindowEvents, this, &fWindowHandler ); |
} |
// start out with no rollover highlighting |
ClearRollover(); |
} |
else |
{ |
if ( fWindowHandler != NULL ) |
{ |
RemoveEventHandler( fWindowHandler ); |
fWindowHandler = NULL; |
} |
} |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊEventHandler |
// Handles various events for the view. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
pascal OSStatus |
HILabelView::EventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ) |
{ |
TCarbonEvent event( inEvent ); |
OSStatus result = eventNotHandledErr; |
HILabelView* pView = (HILabelView*) inRefcon; |
if ( event.GetClass() == kEventClassMouse ) |
{ |
check( event.GetKind() == kEventMouseMoved || event.GetKind() == kEventMouseDragged ); |
if ( pView->GetOwner() == NULL || !IsWindowVisible( pView->GetOwner() ) ) |
return eventNotHandledErr; |
HIPoint pt; |
event.GetParameter<HIPoint>( kEventParamMouseLocation, typeHIPoint, &pt ); |
_HIViewConvertFromGlobalPoint( &pt, pView->GetViewRef() ); |
pView->fNewRollover = pView->HitTest( pt ); |
if ( pView->fRollover != pView->fNewRollover ) |
{ |
HIRect firstLabelBounds; |
HIRect lastLabelBounds; |
pView->GetPartBounds( 2, &firstLabelBounds ); |
pView->GetPartBounds( kNumParts, &lastLabelBounds ); |
// |
// If we are now over a label part, immediately invalidate the label name for redraw. |
// Also do this if the mouse passed outside of the area used by the label parts, so |
// that we immediately erase the name in this case. By passing fRolloverTimer (which |
// may NULL or may be a valid timer) to RolloverChangedTimer, we re-use its behavior |
// to remove any installed timer. |
// |
// If we are not over a label part, delay the invalidation just a little to see if the |
// user moves the mouse over another label part. If so, then we'll get back here again |
// and we'll invalidate the old and new parts, but never completely erase the label name; |
// this avoids flicker. If the user doesn't quickly move the mouse over another label, |
// then the timer will fire and we'll invalidate and erase the label name. |
// |
if ( pView->fNewRollover != 0 || pt.x < firstLabelBounds.origin.x || pt.x > CGRectGetMaxX( lastLabelBounds ) ) |
RolloverChangedTimer( pView->fRolloverTimer, pView ); |
else if ( pView->fRolloverTimer == NULL ) |
InstallEventLoopTimer( GetMainEventLoop(), kRolloverTimerDelay, 0, RolloverChangedTimer, pView, &pView->fRolloverTimer ); |
else |
SetEventLoopTimerNextFireTime( pView->fRolloverTimer, kRolloverTimerDelay ); |
} |
check( result == eventNotHandledErr ); |
} |
else if ( event.GetClass() == kEventClassMenu ) |
{ |
switch ( event.GetKind() ) |
{ |
case kEventMenuMeasureItemWidth: |
{ |
MenuItemIndex item = event.GetParameter<MenuItemIndex>( kEventParamMenuItemIndex, typeMenuItemIndex ); |
if ( pView->GetMenuItemViewProperty( item ) == pView ) |
{ |
HISize size; |
pView->GetOptimalSize( &size, NULL ); |
SInt16 width = (SInt16) size.width; |
event.SetParameter<SInt16>( kEventParamMenuItemWidth, typeShortInteger, width ); |
result = noErr; |
} |
break; |
} |
case kEventMenuMeasureItemHeight: |
{ |
MenuItemIndex item = event.GetParameter<MenuItemIndex>( kEventParamMenuItemIndex, typeMenuItemIndex ); |
if ( pView->GetMenuItemViewProperty( item ) == pView ) |
{ |
HISize size; |
pView->GetOptimalSize( &size, NULL ); |
SInt16 height = (SInt16) size.height; |
event.SetParameter<SInt16>( kEventParamMenuItemHeight, typeShortInteger, height ); |
result = noErr; |
} |
break; |
} |
case kEventMenuDrawItem: |
// draw nothing |
result = noErr; |
break; |
// |
// When the menu is asked to calculate its size, let it do so, and then reposition |
// the label view to match the bounds of the menu item that we're implementing. |
// |
case kEventMenuCalculateSize: |
result = CallNextEventHandler( inCaller, inEvent ); |
if ( result == noErr ) |
{ |
MenuItemIndex item = pView->FindItem(); |
if ( item != 0 ) |
{ |
RgnHandle itemRgn = NewRgn(); |
if ( itemRgn != NULL ) |
{ |
GetControlRegion( pView->fMenuContentView, item, itemRgn ); |
Rect bounds; |
GetRegionBounds( itemRgn, &bounds ); |
DisposeRgn( itemRgn ); |
HIRect frame = { { bounds.left, bounds.top }, |
{ bounds.right - bounds.left, bounds.bottom - bounds.top } }; |
HIViewSetFrame( pView->GetViewRef(), &frame ); |
} |
} |
} |
break; |
} |
} |
else if ( event.GetClass() == kEventClassControl ) |
{ |
switch ( event.GetKind() ) |
{ |
// a future version of TView will provide this handler automatically |
case kEventControlVisibilityChanged: |
pView->VisibilityChanged(); |
break; |
case kEventControlSimulateHit: |
{ |
ControlPartCode part = event.GetParameter<ControlPartCode>( kEventParamControlPart, typeControlPartCode ); |
if ( pView->GetMenuItemViewProperty( part ) == pView ) |
{ |
// remember which item was under the mouse |
HIViewPartCode selectedPart = pView->fRollover; |
// detect the selected item even if the user releases the mouse before RolloverChangedTimer fires |
if ( selectedPart != 0 ) |
selectedPart = pView->fNewRollover; |
// if we are over an item, flash it to show feedback, and invert its state |
if ( selectedPart != 0 ) |
{ |
enum { kFlashTicks = 5 }; |
// turn off normal rollover effects |
pView->ClearRollover(); |
// make sure that rollover text continues to be displayed (don't blink it on and off) |
pView->fTextRollover = selectedPart; |
// turn off part rollover |
pView->fPartRollover = 0; |
pView->RedrawWithDelay( kFlashTicks ); |
// turn on part rollover |
pView->fPartRollover = selectedPart; |
pView->RedrawWithDelay( kFlashTicks ); |
// turn off part rollover |
pView->fPartRollover = 0; |
pView->RedrawWithDelay( kFlashTicks ); |
// invert selected state for this part |
pView->ControlHit( selectedPart, 0 ); |
pView->RedrawWithDelay( kFlashTicks ); |
} |
// we've handled this SimulateHit event; the standard menu view does not need to be called |
result = noErr; |
} |
break; |
} |
default: |
break; |
} |
} |
else if ( event.GetClass() == kEventClassCommand ) |
{ |
HICommand cmd; |
event.GetParameter( kEventParamDirectObject, &cmd ); |
// |
// If the user is using non-sticky menu tracking and releases the mouse over our view, the Menu Manager |
// will send a kEventCommandProcess event for our menu item. We don't want this event to be propagated, |
// since it is meaningless for our view, so we just catch it here and return noErr to stop it. |
// |
if ( ( cmd.attributes & kHICommandFromMenu ) != 0 |
&& cmd.menu.menuRef == pView->fMenu |
&& pView->GetMenuItemViewProperty( cmd.menu.menuItemIndex ) == pView ) |
{ |
result = noErr; |
} |
} |
return result; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊFindItem |
// Locates the menu item in the menu that we're currently using to display the view. |
// May return 0 if no item could be found. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
MenuItemIndex |
HILabelView::FindItem() |
{ |
// |
// We cache the menu item that we find, and reverify it each time that we need it. |
// If the menu item index has changed, then our property won't be there, and we |
// invalidate the cache. |
// |
if ( fMenuItem != 0 && GetMenuItemViewProperty( fMenuItem ) != this ) |
fMenuItem = 0; |
// if we don't have a cached menu item, search the menu for an item with our property |
if ( fMenuItem == 0 ) |
{ |
for ( int i = 1, cItems = CountMenuItems( fMenu ); i <= cItems; i++ ) |
{ |
if ( GetMenuItemViewProperty( i ) == this ) |
{ |
fMenuItem = i; |
break; |
} |
} |
} |
return fMenuItem; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊGetMenuItemViewProperty |
// Returns the LabelView property attached to a menu item, if any. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
HILabelView* |
HILabelView::GetMenuItemViewProperty( MenuItemIndex inItem ) |
{ |
HILabelView* itemView = NULL; |
GetMenuItemProperty( fMenu, inItem, kMenuPropertyLabelViewCreator, kMenuPropertyLabelViewTag, |
sizeof( itemView ), NULL, &itemView ); |
return itemView; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊRedrawWithDelay |
// Invalidates and redraws the view, and then delays a specified time. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::RedrawWithDelay( int inDelayTicks ) |
{ |
HIViewRef view = GetViewRef(); |
HIWindowRef window = GetControlOwner( view ); |
UInt32 endTicks; |
HIViewSetNeedsDisplay( view, true ); |
HIViewRender( view ); |
HIWindowFlush( window ); |
Delay( inDelayTicks, &endTicks ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ _HIViewConvertFromGlobalPoint |
// Converts a point from global coordinates to view coordinates. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
static void |
_HIViewConvertFromGlobalPoint( HIPoint* ioPoint, HIViewRef inDestView ) |
{ |
Point pt = { (short) ioPoint->y, (short) ioPoint->x }; |
WindowRef window = GetControlOwner( inDestView ); |
GrafPtr port = GetWindowPort( window ); |
PixMapHandle pixmap = GetPortPixMap( port ); |
// convert to local coordinates |
QDGlobalToLocalPoint( port, &pt ); |
// convert to window coordinates |
pt.h -= (*pixmap)->bounds.left; |
pt.v -= (*pixmap)->bounds.top; |
// convert to view coordinates |
ioPoint->x = pt.h; |
ioPoint->y = pt.v; |
HIViewConvertPoint( ioPoint, HIViewGetRoot( window ), inDestView ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ RolloverChangedTimer |
// A timer that's used to invalidate rolled-over parts after a short delay. We use a |
// timer to delay the invalidate to reduce flicker; if we invalidate immediately, what |
// tends to happen as you move horizontally is that the mouse passes from part A, to |
// the space between parts, to part B, and when the mouse is in the space between parts, |
// we erase the label name entirely, only to redraw it shortly when the mouse passes over |
// part B. This flickers. By delaying the invalidation slightly, we give the user time |
// to move into another part, and avoid erasing the label name at all. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::RolloverChangedTimer( EventLoopTimerRef inTimer, void* inRefcon ) |
{ |
HILabelView* pView = (HILabelView*) inRefcon; |
HIRect bounds; |
Rect r; |
RgnHandle rgn = NewRgn(); |
HIViewPartCode oldRollover = pView->fRollover; |
HIViewPartCode newRollover = pView->fNewRollover; |
// if either the old or new rollover part was not the "none" part, we need to update the label |
if ( oldRollover > 1 || newRollover > 1 ) |
{ |
pView->GetLabelBounds( &bounds ); |
SetRect( &r, (short) bounds.origin.x, (short) bounds.origin.y, |
(short) CGRectGetMaxX( bounds ), (short) CGRectGetMaxY( bounds ) ); |
RectRgn( rgn, &r ); |
HIViewSetNeedsDisplayInRegion( pView->GetViewRef(), rgn, true ); |
} |
if ( oldRollover != 0 ) |
{ |
pView->GetPartBounds( oldRollover, &bounds ); |
SetRect( &r, (short) bounds.origin.x, (short) bounds.origin.y, |
(short) CGRectGetMaxX( bounds ), (short) CGRectGetMaxY( bounds ) ); |
RectRgn( rgn, &r ); |
HIViewSetNeedsDisplayInRegion( pView->GetViewRef(), rgn, true ); |
} |
if ( newRollover != 0 ) |
{ |
pView->GetPartBounds( newRollover, &bounds ); |
SetRect( &r, (short) bounds.origin.x, (short) bounds.origin.y, |
(short) CGRectGetMaxX( bounds ), (short) CGRectGetMaxY( bounds ) ); |
RectRgn( rgn, &r ); |
HIViewSetNeedsDisplayInRegion( pView->GetViewRef(), rgn, true ); |
} |
pView->fRollover = newRollover; |
if ( inTimer != NULL ) |
{ |
RemoveEventLoopTimer( inTimer ); |
pView->fRolloverTimer = NULL; |
} |
DisposeRgn( rgn ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetTitleBounds |
// Returns the bounds of the title text ("Color Label:"). |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::GetTitleBounds( HIRect* outBounds ) |
{ |
int leftEdge = GetLeftEdge(); |
*outBounds = Bounds(); |
outBounds->origin.x = leftEdge; |
outBounds->size.width -= leftEdge; |
outBounds->size.height = fPartYOffset; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetLabelBounds |
// Returns the bounds of the label name text ("Project 1"). |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::GetLabelBounds( HIRect* outBounds ) |
{ |
// start with view bounds so that we center the label name text on the view horizontally |
*outBounds = Bounds(); |
// position the label name text vertically under the label buttons |
HIRect partBounds; |
GetPartBounds( 1, &partBounds ); |
outBounds->origin.y = CGRectGetMaxY( partBounds ); |
outBounds->size.height = CGRectGetMaxY( Bounds() ) - outBounds->origin.y; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetPartBounds |
// Returns the bounds of one of the label button parts. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
void |
HILabelView::GetPartBounds( int inPart, HIRect* outBounds ) |
{ |
CGImageRef image = GetPartImage( 1 ); |
float imageWidth = CGImageGetWidth( image ); |
// calculate an origin for the "none" part that aligns the left side of the image with the item text |
HIRect bounds = Bounds(); |
bounds.origin.x += GetLeftEdge(); |
bounds.origin.x -= ( kLabelPartWidth - imageWidth ) / 2; |
// position each part at an offset of kLabelPartWidth + 1 from the last part |
outBounds->origin.x = bounds.origin.x + ( kLabelPartWidth + 1 ) * ( inPart - 1 ); |
// if we're getting the bounds of a label part, add the space between the none and first label parts |
if ( inPart > 1 ) |
outBounds->origin.x += kFirstLabelSpace; |
outBounds->origin.y = bounds.origin.y + fPartYOffset; |
outBounds->size.width = kLabelPartWidth; |
outBounds->size.height = kLabelPartHeight; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ HILabelView::GetLeftEdge |
// Returns the left edge (in view coordinates) of the "Color Label:" text and the "none" part. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
int |
HILabelView::GetLeftEdge() |
{ |
if ( fLeftEdge == 0 ) |
{ |
SInt32 metric; |
MenuAttributes attr = 0; |
if ( fMenu != NULL ) |
GetMenuAttributes( fMenu, &attr ); |
if ( ( attr & kMenuAttrExcludesMarkColumn ) != 0 ) |
GetThemeMetric( kThemeMetricMenuExcludedMarkColumnWidth, &metric ); |
else |
GetThemeMetric( kThemeMetricMenuMarkColumnWidth, &metric ); |
fLeftEdge = metric; |
} |
return fLeftEdge; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetPartImage |
// Returns a CGImage for a label part. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CGImageRef |
HILabelView::GetPartImage( int inPart ) |
{ |
check( inPart > 0 ); |
check( inPart <= kNumParts ); |
// convert from 1-based part code to 0-based array index |
inPart--; |
if ( sImages[ inPart ] == NULL ) |
LoadPartImages(); |
return sImages[ inPart ]; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetSelectedBackgroundImage |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CGImageRef |
HILabelView::GetSelectedBackgroundImage() |
{ |
return GetBackgroundImage( CFSTR("item"), &sSelectedBackgroundImage ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetPressedBackgroundImage |
// Returns a CGImageRef for the background of a pressed label button. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CGImageRef |
HILabelView::GetPressedBackgroundImage() |
{ |
return GetBackgroundImage( CFSTR("itempressed"), &sPressedBackgroundImage ); |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ GetSelectedBackgroundImage |
// Returns a CGImageRef for the background of a selected label button. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
CGImageRef |
HILabelView::GetBackgroundImage( CFStringRef inName, CGImageRef* ioImage ) |
{ |
if ( *ioImage == NULL ) |
{ |
CFURLRef url = CFBundleCopyResourceURL( CFBundleGetMainBundle(), inName, CFSTR("png"), NULL ); |
CGDataProviderRef data = CGDataProviderCreateWithURL( url ); |
CFRelease( url ); |
*ioImage = CGImageCreateWithPNGDataProvider( data, NULL, true, kCGRenderingIntentDefault ); |
CFRelease( data ); |
} |
return *ioImage; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ LoadPartImages |
// Loads the CGImages for the label parts. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
OSStatus |
HILabelView::LoadPartImages() |
{ |
check( sImages[0] == NULL ); |
static const char* kImageNames[ kNumParts ] = { |
"none", |
"red", |
"orange", |
"yellow", |
"green", |
"blue", |
"grape", |
"grey" |
}; |
CFBundleRef bundle = CFBundleGetMainBundle(); |
for ( int i = 0; i < kNumParts; i++ ) |
{ |
CFStringRef str = CFStringCreateWithCString( NULL, kImageNames[ i ], kTextEncodingMacRoman ); |
CFURLRef url = CFBundleCopyResourceURL( bundle, str, CFSTR("png"), NULL ); |
CGDataProviderRef data = CGDataProviderCreateWithURL( url ); |
CFRelease( url ); |
// create our image |
sImages[ i ] = CGImageCreateWithPNGDataProvider( data, NULL, true, kCGRenderingIntentDefault ); |
CFRelease( data ); |
} |
return noErr; |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// ¥ÊLabelIndexFromPart |
// The ordering of the classic Mac OS labels is not the same as the order in which we |
// draw them in this view. The classic Mac OS label colors were (in order from 1 to 7): |
// brown, green, dark blue, light blue, pink, red, and orange. This function maps from |
// the label part code space to the label index space. |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
int |
HILabelView::LabelIndexFromPart( int inPart ) |
{ |
static const int kMap[] = { 6, 7, 5, 2, 4, 3, 1 }; |
// part code 1 is the "none" part; first label part is 2; last is 8 |
check( inPart >= 2 ); |
check( inPart <= 8 ); |
return kMap[ inPart - 2 ]; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-24