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.
TTickerView.cp
/* |
File: TTickerView.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 © 2002 Apple Computer, Inc., All Rights Reserved |
Created by dam on Tue Jul 31 2002 |
*/ |
#include <Carbon/Carbon.h> |
#include "TTickerView.h" |
// ----------------------------------------------------------------------------- |
// prototypes |
// ----------------------------------------------------------------------------- |
// |
void HIRectToQDRect( |
const HIRect* inHIRect, |
Rect* outQDRect ); |
Boolean FindCharacterPosition( |
const char * inString, |
UInt32 inStartPosition, |
char inCharacter, |
UInt32 * outPosition ); |
// ----------------------------------------------------------------------------- |
// macros |
// ----------------------------------------------------------------------------- |
// |
#define kTTickerViewClassID CFSTR( "com.apple.sample.TTickerView" ) |
#define kTextKey CFSTR( "text" ) |
#define kWidthKey CFSTR( "width" ) |
#define kOffsetKey CFSTR( "offset" ) |
#define kGapBetweenStrings 0 |
// ----------------------------------------------------------------------------- |
// TTickerView constructor |
// ----------------------------------------------------------------------------- |
// |
TTickerView::TTickerView( |
HIViewRef inControl ) |
: TView( inControl ), |
fArray( NULL ), |
fStream( NULL ), |
fText( NULL ), |
fDownloadBuffer( NULL ), |
fAnimationTimer( NULL ), |
fDownloadTimer( NULL ), |
fDoOffset( false ) |
{ |
ChangeAutoInvalidateFlags( kAutoInvalidateOnActivate | kAutoInvalidateOnEnable, 0 ); |
} |
// ----------------------------------------------------------------------------- |
// TTickerView destructor |
// ----------------------------------------------------------------------------- |
// Clean up after yourself. |
// |
TTickerView::~TTickerView() |
{ |
if ( fDownloadTimer ) |
RemoveEventLoopTimer( fDownloadTimer ); |
if ( fAnimationTimer ) |
RemoveEventLoopTimer( fAnimationTimer ); |
if ( fStream ) |
TerminateDownloadStream(); |
PurgeDownloadBuffer(); |
if ( fText ) |
CFRelease( fText ); |
if ( fArray ) |
CFRelease( fArray ); |
} |
// ----------------------------------------------------------------------------- |
// GetKind |
// ----------------------------------------------------------------------------- |
// |
ControlKind TTickerView::GetKind() |
{ |
const ControlKind kMyKind = { 'TikV', 'TikV' }; |
return kMyKind; |
} |
//----------------------------------------------------------------------------------- |
// Create |
//----------------------------------------------------------------------------------- |
// |
OSStatus TTickerView::Create( |
HIViewRef* outControl, |
const HIRect* inBounds, |
WindowRef inWindow ) |
{ |
OSStatus err; |
EventRef event = CreateInitializationEvent(); |
ControlRef root; |
RegisterClass(); |
err = HIObjectCreate( kTTickerViewClassID, event, (HIObjectRef*) outControl ); |
ReleaseEvent( event ); |
require_noerr( err, CantCreateHIObject ); |
if ( inWindow != NULL ) |
{ |
GetRootControl( inWindow, &root ); |
HIViewAddSubview( root, *outControl ); |
} |
if ( inBounds != NULL ) |
HIViewSetFrame( *outControl, inBounds ); |
CantCreateHIObject: |
return err; |
} |
//----------------------------------------------------------------------------------- |
// RegisterClass |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::RegisterClass() |
{ |
static bool sRegistered; |
if ( !sRegistered ) |
{ |
TView::RegisterSubclass( kTTickerViewClassID, Construct ); |
sRegistered = true; |
} |
} |
//----------------------------------------------------------------------------------- |
// Construct |
//----------------------------------------------------------------------------------- |
// |
OSStatus TTickerView::Construct( |
ControlRef inControl, |
TView** outView ) |
{ |
*outView = new TTickerView( inControl ); |
return noErr; |
} |
//----------------------------------------------------------------------------------- |
// Initialize |
//----------------------------------------------------------------------------------- |
// The control is set up. Do the last minute stuff that needs to be done like |
// installing EventLoopTimers. |
// |
OSStatus TTickerView::Initialize( |
TCarbonEvent& inEvent ) |
{ |
#pragma unused( inEvent ) |
OSStatus err; |
// build our array |
fArray = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); |
require_action( fArray != NULL, CantCreateArray, err = coreFoundationUnknownErr ); |
// Install a timer that gets called once per second to update the scrolling text |
err = InstallEventLoopTimer( GetCurrentEventLoop(), 0, |
kEventDurationSecond / 60, AnimationTimer, this, &fAnimationTimer ); |
require_noerr( err, CantInstallAnimationTimer ); |
// install a timer that gets called when we need to refetch the ticker information |
err = InstallEventLoopTimer( GetCurrentEventLoop(), 0, |
kEventDurationForever, DownloadTimer, this, &fDownloadTimer ); |
require_noerr( err, CantInstallAnimationTimer ); |
CantInstallAnimationTimer: |
CantCreateArray: |
return err; |
} |
//----------------------------------------------------------------------------------- |
// Draw |
//----------------------------------------------------------------------------------- |
// The fun part of the control |
// |
void TTickerView::Draw( |
RgnHandle inLimitRgn, |
CGContextRef inContext ) |
{ |
#pragma unused( inLimitRgn ) |
Boolean active; |
Boolean enabled; |
ThemeDrawState state; |
HIRect bounds; |
SInt32 lastRightEdgeOffset; |
CFIndex count; |
CFIndex index; |
CFMutableDictionaryRef item; |
CFNumberRef cfOffset; |
CFNumberRef cfWidth; |
SInt32 offset; |
SInt32 width; |
SInt32 rightEdgeOffset; |
Point dimensions; |
SInt16 baseline; |
ThemeTextColor themeTextColor; |
RGBColor textColor; |
CFStringRef text; |
Rect qdBounds; |
active = IsActive(); |
enabled = IsEnabled(); |
if ( active && enabled ) |
state = kThemeStateActive; |
else if ( ! active && ! enabled ) |
state = kThemeStateUnavailableInactive; |
else if ( ! enabled ) |
state = kThemeStateUnavailable; |
else |
state = kThemeStateInactive; |
bounds = Bounds(); |
if ( fDoOffset ) |
{ |
// adjust each item in the array to have a new offset. prune array |
// items that are no longer visible. as we do this, determine what |
// rightmost edge offset of the last item in the array is. this |
// will help us determine when we can add a new item to the array. |
lastRightEdgeOffset = -(kGapBetweenStrings + 1); |
for ( index = 0, count = CFArrayGetCount( fArray ); index < count; ) |
{ |
item = (CFMutableDictionaryRef)CFArrayGetValueAtIndex( fArray, index ); |
check( item != NULL ); |
cfOffset = (CFNumberRef)CFDictionaryGetValue( item, kOffsetKey ); |
check( cfOffset != NULL ); |
CFNumberGetValue( cfOffset, kCFNumberSInt32Type, &offset ); |
cfWidth = (CFNumberRef)CFDictionaryGetValue( item, kWidthKey ); |
check( cfWidth != NULL ); |
CFNumberGetValue( cfWidth, kCFNumberSInt32Type, &width ); |
offset--; |
rightEdgeOffset = offset + width; |
if ( bounds.size.width + rightEdgeOffset < 0 ) |
{ |
// this item can no longer be seen. remove it from the array. |
CFArrayRemoveValueAtIndex( fArray, index ); |
// don't increment the index, since we just removed an item. |
// instead, decrement the count. |
count--; |
} |
else |
{ |
// this item can still be seen. store its new offset. |
cfOffset = CFNumberCreate( NULL, kCFNumberSInt32Type, &offset ); |
check( cfOffset != NULL ); |
CFDictionaryReplaceValue( item, kOffsetKey, cfOffset ); |
CFRelease( cfOffset ); |
// increment the index so we move on to the next item |
index++; |
lastRightEdgeOffset = rightEdgeOffset; |
} |
} |
// if there's room for a new item, add one to the array |
if ( (lastRightEdgeOffset + kGapBetweenStrings) < 0 && fText != NULL ) |
{ |
item = CFDictionaryCreateMutable( NULL, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); |
check( item != NULL ); |
CFDictionaryAddValue( item, kTextKey, fText ); |
GetThemeTextDimensions( fText, kThemeSystemFont, kThemeStateActive, false, &dimensions, &baseline ); |
width = dimensions.h; |
cfWidth = CFNumberCreate( NULL, kCFNumberSInt32Type, &width ); |
CFDictionaryAddValue( item, kWidthKey, cfWidth ); |
CFRelease( cfWidth ); |
offset = -1; |
cfOffset = CFNumberCreate( NULL, kCFNumberSInt32Type, &offset ); |
CFDictionaryAddValue( item, kOffsetKey, cfOffset ); |
CFRelease( cfOffset ); |
CFArrayAppendValue( fArray, item ); |
CFRelease( item ); |
} |
fDoOffset = false; |
} |
// Set up the color appropriately based on our theme draw state |
if ( state == kThemeStateActive ) |
themeTextColor = kThemeTextColorModelessDialogActive; |
else |
themeTextColor = kThemeTextColorModelessDialogInactive; |
GetThemeTextColor( themeTextColor, 32, true, &textColor ); |
CGContextSetRGBFillColor( inContext, (float)textColor.red / (float)65535, |
(float)textColor.green / (float)65535, (float)textColor.blue / (float)65535, 1.0 ); |
// Now draw every item in the array at the appropriate offset |
for ( index = 0, count = CFArrayGetCount( fArray ); index < count; index++ ) |
{ |
item = (CFMutableDictionaryRef)CFArrayGetValueAtIndex( fArray, index ); |
check( item != NULL ); |
text = (CFStringRef)CFDictionaryGetValue( item, kTextKey ); |
check( text != NULL ); |
cfOffset = (CFNumberRef)CFDictionaryGetValue( item, kOffsetKey ); |
check( cfOffset != NULL ); |
CFNumberGetValue( cfOffset, kCFNumberSInt32Type, &offset ); |
cfWidth = (CFNumberRef)CFDictionaryGetValue( item, kWidthKey ); |
check( cfWidth != NULL ); |
CFNumberGetValue( cfWidth, kCFNumberSInt32Type, &width ); |
HIRectToQDRect( &bounds, &qdBounds ); |
qdBounds.left = qdBounds.right; |
qdBounds.right = qdBounds.left + width; |
OffsetRect( &qdBounds, offset, 0 ); |
DrawThemeTextBox( text, kThemeSystemFont, state, true, &qdBounds, teFlushDefault, inContext ); |
} |
} |
//----------------------------------------------------------------------------------- |
// GetRegion |
//----------------------------------------------------------------------------------- |
// |
OSStatus TTickerView::GetRegion( |
ControlPartCode inPart, |
RgnHandle outRgn ) |
{ |
OSStatus err = eventNotHandledErr; |
HIRect bounds; |
Rect qdBounds; |
switch ( inPart ) |
{ |
case kControlContentMetaPart: |
case kControlStructureMetaPart: |
bounds = Bounds(); |
HIRectToQDRect( &bounds, &qdBounds ); |
RectRgn( outRgn, &qdBounds ); |
err = noErr; |
break; |
} |
return err; |
} |
//----------------------------------------------------------------------------------- |
// AnimationTimer |
//----------------------------------------------------------------------------------- |
// This timer tells the view when it needs to scroll and update itself. We set a |
// state flag which indicates that we need to scroll. Then, we request a redraw. |
// Our draw method will do the actual scroll. |
// |
pascal void TTickerView::AnimationTimer( |
EventLoopTimerRef inTimer, |
void* inUserData ) |
{ |
#pragma unused( inTimer ) |
TTickerView* view = (TTickerView*)inUserData; |
view->fDoOffset = true; |
view->Invalidate(); |
} |
//----------------------------------------------------------------------------------- |
// ResetDownloadTimer |
//----------------------------------------------------------------------------------- |
// Sets our download timer to fire thirty seconds from now. |
// |
void TTickerView::ResetDownloadTimer() |
{ |
SetEventLoopTimerNextFireTime( fDownloadTimer, kEventDurationSecond * 30 ); |
} |
//----------------------------------------------------------------------------------- |
// DownloadTimer |
//----------------------------------------------------------------------------------- |
// Starts a new fetch of the ticker information. |
// |
pascal void TTickerView::DownloadTimer( |
EventLoopTimerRef inTimer, |
void* inUserData ) |
{ |
#pragma unused( inTimer ) |
TTickerView* view = (TTickerView*)inUserData; |
view->StartDownloadStream(); |
} |
//----------------------------------------------------------------------------------- |
// StartDownloadStream |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::StartDownloadStream() |
{ |
char* url = "http://quote.yahoo.com/d?f=sl1c1p2&s=AAPL,MSFT,INTC,T,HAS,MO,FMAGX,FCNTX"; |
CFURLRef urlRef; |
CFHTTPMessageRef messageRef; |
CFStreamClientContext context = { 0, (void*)this, NULL, NULL, NULL }; |
Boolean success = false; |
// These are the network events we are interested in. |
// We set StreamCallback() as the notifier proc to handle these events. |
static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted | |
kCFStreamEventHasBytesAvailable | |
kCFStreamEventEndEncountered | |
kCFStreamEventErrorOccurred; |
urlRef = CFURLCreateWithBytes( kCFAllocatorDefault, (const UInt8*)url, strlen( url ), kCFStringEncodingMacRoman, NULL ); |
require( urlRef != NULL, CantCreateCFURL ); |
messageRef = CFHTTPMessageCreateRequest( kCFAllocatorDefault, CFSTR( "GET" ), urlRef, kCFHTTPVersion1_1 ); |
require( messageRef != NULL, CantCreateMessage ); |
fStream = CFReadStreamCreateForHTTPRequest( kCFAllocatorDefault, messageRef ); |
require( fStream != NULL, CantCreateStream ); |
success = CFReadStreamSetProperty( fStream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue ); |
require( success, CantSetAutodirect ); |
success = CFReadStreamSetClient( fStream, kNetworkEvents, TTickerView::StreamCallback, &context ); |
require( success, CantSetClient ); |
// *** looks bad to refer to the run loop here instead of the current event loop |
CFReadStreamScheduleWithRunLoop( fStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes ); |
success = CFReadStreamOpen( fStream ); |
CantSetClient: |
CantSetAutodirect: |
CantCreateStream: |
CFRelease( messageRef ); |
CantCreateMessage: |
CFRelease( urlRef ); |
CantCreateCFURL: |
if ( ! success && fStream ) |
TerminateDownloadStream(); |
} |
//----------------------------------------------------------------------------------- |
// TerminateDownloadStream |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::TerminateDownloadStream() |
{ |
// ALWAYS set the stream client (notifier) to NULL if you are releasing it |
// otherwise your notifier may be called after you released the stream, |
// leaving you with a bogus stream within your notifier. |
CFReadStreamSetClient( fStream, NULL, NULL, NULL ); |
CFRelease( fStream ); |
fStream = NULL; |
} |
//----------------------------------------------------------------------------------- |
// StreamCallback |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::StreamCallback( |
CFReadStreamRef inStream, |
CFStreamEventType inType, |
void* inRefCon ) |
{ |
TTickerView* view = (TTickerView*)inRefCon; |
UInt8 buffer[1024]; |
CFIndex bytesRead; |
Boolean terminate = false; |
switch ( inType ) |
{ |
case kCFStreamEventHasBytesAvailable: |
bytesRead = CFReadStreamRead( inStream, buffer, sizeof(buffer) ); |
if ( bytesRead > 0 ) |
view->AddToDownloadBuffer( buffer, bytesRead ); |
else |
terminate = true; |
break; |
case kCFStreamEventEndEncountered: |
view->FinalizeDownloadBuffer(); |
terminate = true; |
break; |
case kCFStreamEventErrorOccurred: |
terminate = true; |
break; |
default: |
break; |
} |
if ( terminate ) |
{ |
view->TerminateDownloadStream(); |
view->PurgeDownloadBuffer(); |
view->ResetDownloadTimer(); |
} |
} |
//----------------------------------------------------------------------------------- |
// AddToDownloadBuffer |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::AddToDownloadBuffer( |
UInt8* inData, |
CFIndex inNumberOfBytes ) |
{ |
Size previousSize; |
if ( fDownloadBuffer == NULL ) |
{ |
previousSize = 0; |
fDownloadBuffer = NewPtr( inNumberOfBytes ); |
check( fDownloadBuffer != NULL ); |
} |
else |
{ |
previousSize = GetPtrSize( fDownloadBuffer ); |
SetPtrSize( fDownloadBuffer, previousSize + inNumberOfBytes ); |
} |
BlockMoveData( inData, fDownloadBuffer + previousSize, inNumberOfBytes ); |
} |
//----------------------------------------------------------------------------------- |
// FinalizeDownloadBuffer |
//----------------------------------------------------------------------------------- |
// This is where we parse the download buffer and build a CFString from it. |
// Warning: The following logic is not particularly robust. If the data format |
// changes even slightly, this logic may break horribly. |
// |
void TTickerView::FinalizeDownloadBuffer() |
{ |
Size size; |
CFMutableStringRef newText; |
UInt32 bufferSize; |
UInt32 curPos; |
UInt32 nextPos; |
// NULL-terminate our download buffer so we can use it like a big C string |
size = GetPtrSize( fDownloadBuffer ); |
SetPtrSize( fDownloadBuffer, size + 1 ); |
fDownloadBuffer[size] = 0; |
newText = CFStringCreateMutable( NULL, 0 ); |
require_quiet( newText != NULL, CantAllocateNewText ); |
curPos = 0; |
bufferSize = GetPtrSize( fDownloadBuffer ); |
// loop until we run out of text to parse |
while ( curPos < bufferSize ) |
{ |
// |
// Name |
// |
// find the start quote on the symbol name |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, '\"', &nextPos ) ) |
break; |
curPos = nextPos + 1; // eat the start quote |
// find the end quote on the symbol name |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, '\"', &nextPos ) ) |
break; |
fDownloadBuffer[nextPos] = 0; // NULL-terminate the symbol name |
// add the symbol name to the string |
CFStringAppendCString( newText, &fDownloadBuffer[curPos], kCFStringEncodingMacRoman ); |
curPos = nextPos + 1; // eat the NULL terminator |
// |
// Price |
// |
// find the comma on the price |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, ',', &nextPos ) ) |
break; |
curPos = nextPos + 1; // eat the comma |
// find the end comma on the price |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, ',', &nextPos ) ) |
break; |
fDownloadBuffer[nextPos] = 0; // NULL-terminate the price |
// add the price to the string |
CFStringAppendCString( newText, " ", kCFStringEncodingMacRoman ); |
CFStringAppendCString( newText, &fDownloadBuffer[curPos], kCFStringEncodingMacRoman ); |
curPos = nextPos + 1; // eat the NULL terminator |
// |
// Change |
// |
// find the end comma on the price |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, ',', &nextPos ) ) |
break; |
fDownloadBuffer[nextPos] = 0; // NULL-terminate the change |
// add the change to the string |
CFStringAppendCString( newText, " (", kCFStringEncodingMacRoman ); |
CFStringAppendCString( newText, &fDownloadBuffer[curPos], kCFStringEncodingMacRoman ); |
curPos = nextPos + 1; // eat the NULL terminator |
// |
// Percentage |
// |
// find the start quote on the percentage |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, '\"', &nextPos ) ) |
break; |
curPos = nextPos + 1; // eat the start quote |
// find the end quote on the precentage |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, '\"', &nextPos ) ) |
break; |
fDownloadBuffer[nextPos] = 0; // NULL-terminate the percentage |
// add the percentage to the string |
CFStringAppendCString( newText, ", ", kCFStringEncodingMacRoman ); |
CFStringAppendCString( newText, &fDownloadBuffer[curPos], kCFStringEncodingMacRoman ); |
CFStringAppendCString( newText, ")", kCFStringEncodingMacRoman ); |
curPos = nextPos + 1; // eat the NULL terminator |
// |
// Next Symbol |
// |
// find the newline which terminates this symbol |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, 13, &nextPos ) ) |
break; |
curPos = nextPos + 1; |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, 10, &nextPos ) ) |
break; |
curPos = nextPos + 1; |
// add a separator to the string so we can start the next symbol |
CFStringAppendCString( newText, ", ", kCFStringEncodingMacRoman ); |
// find the start quote for the next symbol |
if ( ! FindCharacterPosition( fDownloadBuffer, curPos, '\"', &nextPos ) ) |
break; |
} |
// now, purge the old text and set the data member to our newly created text |
if ( fText ) |
CFRelease( fText ); |
fText = newText; |
CantAllocateNewText: |
; |
} |
//----------------------------------------------------------------------------------- |
// PurgeDownloadBuffer |
//----------------------------------------------------------------------------------- |
// |
void TTickerView::PurgeDownloadBuffer() |
{ |
if ( fDownloadBuffer != NULL ) |
DisposePtr( fDownloadBuffer ); |
fDownloadBuffer = NULL; |
} |
//----------------------------------------------------------------------------------- |
// HIRectToQDRect |
//----------------------------------------------------------------------------------- |
// Converts an HIRect to a Quickdraw rectangle, snipping off non-integral bits. |
// |
void HIRectToQDRect( |
const HIRect* inHIRect, |
Rect* outQDRect ) |
{ |
outQDRect->top = (SInt16) CGRectGetMinY( *inHIRect ); |
outQDRect->left = (SInt16) CGRectGetMinX( *inHIRect ); |
outQDRect->bottom = (SInt16) CGRectGetMaxY( *inHIRect ); |
outQDRect->right = (SInt16) CGRectGetMaxX( *inHIRect ); |
} |
//----------------------------------------------------------------------------------- |
// FindCharacterPosition |
//----------------------------------------------------------------------------------- |
// |
Boolean FindCharacterPosition( |
const char * inString, |
UInt32 inStartPosition, |
char inCharacter, |
UInt32 * outPosition ) |
{ |
UInt32 index; |
Boolean found = false; |
for ( index = inStartPosition; inString[index] != 0; index++ ) |
{ |
if ( inString[index] == inCharacter ) |
{ |
*outPosition = index; |
found = true; |
break; |
} |
} |
return found; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30