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.
TURLTextView.cp
/* |
File: TURLTextView.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 |
*/ |
#include <Carbon/Carbon.h> |
#include "TRect.h" |
#include "TURLTextView.h" |
//----------------------------------------------------------------------------------- |
// macros |
//----------------------------------------------------------------------------------- |
// |
#define kTViewClassID CFSTR( "com.apple.sample.URLTextView" ) |
//----------------------------------------------------------------------------------- |
// constants |
//----------------------------------------------------------------------------------- |
// |
enum |
{ |
kEventParamURL = 'URL ', // typeCFURLRef |
kEventParamText = 'TEXT', // typeCFStringRef |
typeCFURLRef = 'cfur' |
}; |
//----------------------------------------------------------------------------------- |
// HIURLTextViewCreate |
//----------------------------------------------------------------------------------- |
// |
OSStatus HIURLTextViewCreate( const HIRect* inBounds, CFURLRef inURL, CFStringRef inText, HIViewRef* outURLTextView ) |
{ |
return TURLTextView::Create( outURLTextView, inBounds, inURL, inText ); |
} |
//----------------------------------------------------------------------------------- |
// HIURLTextViewSetURL |
//----------------------------------------------------------------------------------- |
// |
OSStatus HIURLTextViewSetURL( HIViewRef inURLTextView, CFURLRef inURL ) |
{ |
return SetControlData( inURLTextView, kControlEntireControl, kControlURLTag, sizeof( inURL ), &inURL ); |
} |
//----------------------------------------------------------------------------------- |
// HIURLTextViewCopyURL |
//----------------------------------------------------------------------------------- |
// |
OSStatus HIURLTextViewCopyURL( HIViewRef inURLTextView, CFURLRef* outURL ) |
{ |
return GetControlData( inURLTextView, kControlEntireControl, kControlURLTag, sizeof( *outURL ), outURL, NULL ); |
} |
//----------------------------------------------------------------------------------- |
// HIURLTextViewSetText |
//----------------------------------------------------------------------------------- |
// |
OSStatus HIURLTextViewSetText( HIViewRef inURLTextView, CFStringRef inText ) |
{ |
return SetControlData( inURLTextView, kControlEntireControl, kControlURLTextTag, sizeof( inText ), &inText ); |
} |
//----------------------------------------------------------------------------------- |
// HIURLTextViewCopyText |
//----------------------------------------------------------------------------------- |
// |
OSStatus HIURLTextViewCopyText( HIViewRef inURLTextView, CFStringRef* outText ) |
{ |
return GetControlData( inURLTextView, kControlEntireControl, kControlURLTextTag, sizeof( *outText ), outText, NULL ); |
} |
//----------------------------------------------------------------------------------- |
// TURLTextView constructor |
//----------------------------------------------------------------------------------- |
// |
TURLTextView::TURLTextView( |
HIViewRef inControl ) |
: TView( inControl ), |
fURL( NULL ), |
fText( NULL ) |
{ |
ActivateInterface( kKeyboardFocus ); |
ChangeAutoInvalidateFlags( kAutoInvalidateOnActivate | kAutoInvalidateOnHilite, 0 ); |
} |
//----------------------------------------------------------------------------------- |
// TURLTextView destructor |
//----------------------------------------------------------------------------------- |
// Clean up after yourself. |
// |
TURLTextView::~TURLTextView() |
{ |
if ( fURL != NULL ) |
CFRelease( fURL ); |
if ( fText != NULL ) |
CFRelease( fText ); |
} |
//----------------------------------------------------------------------------------- |
// CreateInitializationEvent |
//----------------------------------------------------------------------------------- |
// Creates an init event for the URLTextView HIObject. |
// |
EventRef TURLTextView::CreateInitializationEvent( CFURLRef inURL, CFStringRef inText ) |
{ |
EventRef event = TView::CreateInitializationEvent(); |
if ( event != NULL ) |
{ |
if ( inURL != NULL ) |
SetEventParameter( event, kEventParamURL, typeCFURLRef, sizeof( inURL ), &inURL ); |
if ( inText != NULL ) |
SetEventParameter( event, kEventParamText, typeCFStringRef, sizeof( inText ), &inText ); |
} |
return event; |
} |
//----------------------------------------------------------------------------------- |
// GetKind |
//----------------------------------------------------------------------------------- |
// |
ControlKind TURLTextView::GetKind() |
{ |
static const ControlKind kMyKind = { 'TurV', 'TurV' }; |
return kMyKind; |
} |
//----------------------------------------------------------------------------------- |
// Create |
//----------------------------------------------------------------------------------- |
// |
OSStatus TURLTextView::Create( |
HIViewRef* outControl, |
const HIRect* inBounds, |
CFURLRef inURL, |
CFStringRef inText ) |
{ |
OSStatus err; |
EventRef event = CreateInitializationEvent( inURL, inText ); |
RegisterClass(); |
err = HIObjectCreate( kTViewClassID, event, (HIObjectRef*) outControl ); |
ReleaseEvent( event ); |
if ( err == noErr ) |
HIViewSetFrame( *outControl, inBounds ); |
return err; |
} |
//----------------------------------------------------------------------------------- |
// RegisterClass |
//----------------------------------------------------------------------------------- |
// |
void TURLTextView::RegisterClass() |
{ |
static bool sRegistered; |
if ( !sRegistered ) |
{ |
TView::RegisterSubclass( kTViewClassID, Construct ); |
sRegistered = true; |
} |
} |
//----------------------------------------------------------------------------------- |
// Construct |
//----------------------------------------------------------------------------------- |
// |
OSStatus TURLTextView::Construct( |
ControlRef inControl, |
TView** outView ) |
{ |
*outView = new TURLTextView( inControl ); |
return noErr; |
} |
//----------------------------------------------------------------------------------- |
// Initialize |
//----------------------------------------------------------------------------------- |
// The control is set up. Do the last minute stuff that needs to be done like |
// |
OSStatus TURLTextView::Initialize( |
TCarbonEvent& inEvent ) |
{ |
OSStatus err = noErr; |
GetEventParameter( inEvent, kEventParamURL, typeCFURLRef, NULL, sizeof( fURL ), NULL, &fURL ); |
GetEventParameter( inEvent, kEventParamText, typeCFStringRef, NULL, sizeof( fText ), NULL, &fText ); |
if ( fURL != NULL ) |
CFRetain( fURL ); |
if ( fText != NULL ) |
CFRetain( fText ); |
return err; |
} |
//----------------------------------------------------------------------------------- |
// SetFocusPart |
//----------------------------------------------------------------------------------- |
// Focus ourselves. |
// |
OSStatus TURLTextView::SetFocusPart( |
ControlPartCode inDesiredFocus, |
RgnHandle inInvalidRgn, |
Boolean inFocusEverything, |
ControlPartCode* outActualFocus ) |
{ |
#pragma unused( inInvalidRgn ) |
ControlPartCode currentFocus; |
HIViewGetFocusPart( GetViewRef(), ¤tFocus ); |
// |
// We only support focusing if inFocusEverything is true. Otherwise, we are |
// using traditional focusing rules (focus only on edit fields, etc.) and we |
// do not have any traditionally focusable parts. |
// |
// We can focus ourselves if we are currently not focused. If we are focused, |
// then since we have only a single part, moving to the next or previous part |
// will remove the focus from us and move it to the next control. |
// |
switch ( inDesiredFocus ) |
{ |
case kControlFocusNextPart: |
case kControlFocusPrevPart: // toggle between focused or not |
if ( ( currentFocus == kControlNoPart ) && inFocusEverything ) |
inDesiredFocus = kControlLabelPart; |
else |
inDesiredFocus = kControlNoPart; |
break; |
default: // we don't focus anything but the label |
if( inDesiredFocus != kControlLabelPart ) |
inDesiredFocus = kControlNoPart; |
break; |
} |
// if the focus changes, make sure we redraw |
if ( currentFocus != inDesiredFocus ) |
Invalidate(); |
*outActualFocus = inDesiredFocus; |
return noErr; |
} |
//----------------------------------------------------------------------------------- |
// TextInput |
//----------------------------------------------------------------------------------- |
// Handles text input. |
// |
OSStatus TURLTextView::TextInput( |
TCarbonEvent& inEvent ) |
{ |
OSStatus result = eventNotHandledErr; |
if ( inEvent.GetKind() == kEventTextInputUnicodeForKeyEvent ) |
{ |
UniChar uch = 0; |
inEvent.GetParameter<UniChar>( kEventParamTextInputSendText, typeUnicodeText, &uch ); |
if ( uch == kSpaceCharCode ) |
{ |
ControlPartCode part; |
HIViewSimulateClick( GetViewRef(), kControlLabelPart, 0, &part ); |
result = noErr; |
} |
} |
return result; |
} |
//----------------------------------------------------------------------------------- |
// Draw |
//----------------------------------------------------------------------------------- |
// The fun part of the control |
// |
void TURLTextView::Draw( |
RgnHandle inLimitRgn, |
CGContextRef inContext ) |
{ |
#pragma unused( inLimitRgn ) |
CFStringRef text = CopyText(); |
if ( text != NULL ) |
{ |
RGBColor rgb = { 0, 0, 0xFFFF }; // default color is pure blue |
TRect bounds( Bounds() ); |
Rect qdBounds; |
SInt16 baseline; |
float yUnderline; |
Boolean showFocus = false; |
ControlPartCode focusPart; |
// Determine the color for disabled text. Since there is no SetThemeTextColor for |
// a CGContext, we get the RGB color and then manually place it into the context |
if ( !IsActive() || !IsEnabled() ) |
GetThemeTextColor( kThemeTextColorDialogInactive, 32, true, &rgb ); |
// set an appropriate color for the text |
CGContextSetRGBFillColor( inContext, |
(float) rgb.red / 0xFFFF, |
(float) rgb.green / 0xFFFF, |
(float) rgb.blue / 0xFFFF, |
1.0 ); |
// draw the text using the control's entire bounds |
qdBounds = bounds; |
DrawThemeTextBox( text, kThemeSystemFont, kThemeStateActive, false, &qdBounds, teFlushDefault, inContext ); |
// switch to the bounds of the text itself |
bounds.Set( TextBounds( &baseline ) ); |
// draw the underline |
yUnderline = bounds.MaxY() + baseline + 2.5; |
CGContextBeginPath( inContext ); |
CGContextMoveToPoint( inContext, bounds.MinX(), yUnderline ); |
CGContextAddLineToPoint( inContext, bounds.MaxX(), yUnderline ); |
CGContextClosePath( inContext ); |
CGContextSetRGBStrokeColor( inContext, |
(float) rgb.red / 0xFFFF, |
(float) rgb.green / 0xFFFF, |
(float) rgb.blue / 0xFFFF, |
1.0 ); |
CGContextStrokePath( inContext ); |
// we are showing focus if the control is hilited (during mouse tracking) or has keyboard focus |
if ( GetHilite() == kControlLabelPart ) |
showFocus = true; |
else if ( HIViewGetFocusPart( GetViewRef(), &focusPart ) == noErr && focusPart == kControlLabelPart ) |
showFocus = true; |
// draw focus (but only if we're active and enabled) |
if ( showFocus && IsActive() && IsEnabled() ) |
{ |
qdBounds = bounds; |
DrawThemeFocusRect( &qdBounds, true ); |
} |
CFRelease( text ); |
} |
} |
//----------------------------------------------------------------------------------- |
// ControlHit |
//----------------------------------------------------------------------------------- |
// In response to a click on the control, we use LaunchServices to open the URL. |
// |
OSStatus TURLTextView::ControlHit( |
ControlPartCode inPart, |
UInt32 inKeyModifiers ) |
{ |
#pragma unused( inPart, inKeyModifiers ) |
OSStatus result = eventNotHandledErr; |
// we only support one part code |
check( inPart == kControlLabelPart ); |
if ( fURL != NULL ) |
result = LSOpenCFURLRef( fURL, NULL ); |
return result; |
} |
//----------------------------------------------------------------------------------- |
// HitTest |
//----------------------------------------------------------------------------------- |
// Determine whether a click was in the control or not |
// |
ControlPartCode TURLTextView::HitTest( |
const HIPoint& inWhere ) |
{ |
ControlPartCode part = kControlNoPart; |
if ( IsVisible() |
&& IsEnabled() |
&& IsActive() |
&& CGRectContainsPoint( TextBounds( NULL ), inWhere ) ) |
{ |
part = kControlLabelPart; |
} |
return part; |
} |
//----------------------------------------------------------------------------------- |
// GetRegion |
//----------------------------------------------------------------------------------- |
// |
OSStatus TURLTextView::GetRegion( |
ControlPartCode inPart, |
RgnHandle outRgn ) |
{ |
OSStatus err = noErr; |
if ( inPart == kControlContentMetaPart |
|| inPart == kControlStructureMetaPart |
/* || inPart == kControlOpaqueRegionMetaPart */ ) |
{ |
TRect bounds( Bounds() ); |
Rect qdBounds = bounds; |
// leave room for the focus rect |
// ¥¥¥Êhow do we know how much room to leave? |
InsetRect( &qdBounds, -3, -3 ); |
RectRgn( outRgn, &qdBounds ); |
} |
return err; |
} |
//----------------------------------------------------------------------------------- |
// GetOptimalSize |
//----------------------------------------------------------------------------------- |
// Returns the optimal size of the view, based on its content. |
// |
OSStatus TURLTextView::GetOptimalSize( |
HISize* outSize, |
float* outBaseLine ) |
{ |
SInt16 baseline; |
HIRect bounds = TextBounds( &baseline ); |
if ( outSize != NULL ) |
{ |
outSize->width = bounds.size.width; |
outSize->height = bounds.size.height; |
} |
if ( outBaseLine != NULL ) |
*outBaseLine = baseline; |
return noErr; |
} |
//----------------------------------------------------------------------------------- |
// GetData |
//----------------------------------------------------------------------------------- |
// Implements GetControlData on the view. |
// |
OSStatus TURLTextView::GetData( |
OSType inTag, |
ControlPartCode inPart, |
Size inSize, |
Size* outSize, |
void* inPtr ) |
{ |
// TView handles kControlKindTag for us |
OSStatus err = TView::GetData( inTag, inPart, inSize, outSize, inPtr ); |
if ( err == noErr ) |
return noErr; |
// assume that we'll handle this request |
err = noErr; |
switch( inTag ) |
{ |
case kControlURLTag: |
if ( inPtr != NULL ) |
{ |
if ( inSize != sizeof( CFURLRef ) ) |
{ |
err = errDataSizeMismatch; |
} |
else |
{ |
*(CFURLRef*) inPtr = fURL; |
if ( fURL != NULL ) |
CFRetain( fURL ); |
} |
} |
*outSize = sizeof( CFURLRef ); |
break; |
case kControlURLTextTag: |
if ( inPtr != NULL ) |
{ |
if ( inSize != sizeof( CFStringRef ) ) |
{ |
err = errDataSizeMismatch; |
} |
else |
{ |
*(CFStringRef*) inPtr = fText; |
if ( fText != NULL ) |
CFRetain( fText ); |
} |
} |
*outSize = sizeof( CFStringRef ); |
break; |
default: |
err = eventNotHandledErr; |
break; |
} |
return err; |
} |
//----------------------------------------------------------------------------------- |
// SetData |
//----------------------------------------------------------------------------------- |
// Implements SetControlData on the view. |
// |
OSStatus TURLTextView::SetData( |
OSType inTag, |
ControlPartCode inPart, |
Size inSize, |
const void* inPtr ) |
{ |
#pragma unused( inPart ) |
// assume that we'll handle this request |
OSStatus err = noErr; |
switch( inTag ) |
{ |
case kControlURLTag: |
if ( inPtr != NULL ) |
{ |
if ( inSize != sizeof( CFURLRef ) ) |
{ |
err = errDataSizeMismatch; |
} |
else |
{ |
if ( fURL != NULL ) |
CFRelease( fURL ); |
fURL = *(CFURLRef*) inPtr; |
if ( fURL != NULL ) |
CFRetain( fURL ); |
err = Invalidate(); |
} |
} |
break; |
case kControlURLTextTag: |
if ( inPtr != NULL ) |
{ |
if ( inSize != sizeof( CFStringRef ) ) |
{ |
err = errDataSizeMismatch; |
} |
else |
{ |
if ( fText != NULL ) |
CFRelease( fText ); |
fText = *(CFStringRef*) inPtr; |
if ( fText != NULL ) |
CFRetain( fText ); |
err = Invalidate(); |
} |
} |
break; |
default: |
err = eventNotHandledErr; |
break; |
} |
return err; |
} |
//----------------------------------------------------------------------------------- |
// CopyText |
//----------------------------------------------------------------------------------- |
// Returns the text to be drawn by the view. |
// |
CFStringRef TURLTextView::CopyText() |
{ |
CFStringRef text = NULL; |
if ( fText != NULL ) |
{ |
text = (CFStringRef) CFRetain( fText ); |
} |
else if ( fURL != NULL ) |
{ |
// if LSCopyDisplayName returns an error, or returns empty text, |
// then we just use a string derived from the URL itself |
if ( LSCopyDisplayNameForURL( fURL, &text ) != noErr || CFStringGetLength( text ) == 0 ) |
{ |
if ( text != NULL ) |
{ |
CFRelease( text ); |
text = NULL; |
} |
// if we have a valid URL, try using its net location and path |
if ( CFURLCanBeDecomposed( fURL ) ) |
{ |
CFStringRef netLocation = CFURLCopyNetLocation( fURL ); |
CFStringRef path = CFURLCopyPath( fURL ); |
if ( netLocation != NULL && path != NULL ) |
{ |
text = CFStringCreateMutableCopy( NULL, 0, netLocation ); |
CFStringAppend( (CFMutableStringRef) text, path ); |
} |
if ( netLocation != NULL ) |
CFRelease( netLocation ); |
if ( path != NULL ) |
CFRelease( path ); |
} |
// if all else fails, just use the text of the URL itself |
if ( text == NULL ) |
text = (CFStringRef) CFRetain( CFURLGetString( fURL ) ); |
} |
} |
return text; |
} |
//----------------------------------------------------------------------------------- |
// TextBounds |
//----------------------------------------------------------------------------------- |
// Determines the bounds of the text displayed by the view. |
// |
HIRect TURLTextView::TextBounds( SInt16* outBaseline ) |
{ |
HIRect bounds = Bounds(); |
CFStringRef text = CopyText(); |
if ( text != NULL ) |
{ |
Point qdSize; |
SInt16 baseline; |
GetThemeTextDimensions( text, kThemeSystemFont, kThemeStateActive, false, &qdSize, &baseline ); |
bounds.size.width = qdSize.h; |
bounds.size.height = qdSize.v; |
if ( outBaseline != NULL ) |
*outBaseline = baseline; |
CFRelease( text ); |
} |
return bounds; |
} |
/* |
To do: |
x draw text in gray when inactive |
x hit-testing should not return a hit if the click isn't in the bounds of the text |
x keyboard focus |
x use LSCopyDisplayNameForURL to get a display name if no display text is specified |
x specify URL using CFURLRef instead of CFStringRef? Or perhaps allow either? |
x add cover APIs |
x GetOptimalSize |
ControlData tags for getting and setting URL/text |
URL underline presence or absence |
can URL text be selected/copied |
ControlFontStyle |
whether we should be focusable even in absence of full keyboard focusing |
accessibility |
drag support - allow dragging URL to desktop to get a .webloc file or something similar |
allow specifying LSLaunchURLSpec that's used when opening the URL |
*/ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30