Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
SMFPeopleView.m
/* |
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. |
*/ |
/* |
SMFPeopleView.m |
SeeMyFriends |
Version: 1.1 |
Copyright (c) 2006 Apple Computer, Inc. All rights reserved. |
This is the custom HIView drawing all people data. |
*/ |
#import <Carbon/Carbon.h> |
#import "SMFPeopleView.h" |
#import "SMFWindowController.h" |
// ----------------------------------------------------------------------------- |
// constants |
// ----------------------------------------------------------------------------- |
// |
extern SMFWindowController *gMainWindowController; |
#pragma mark -- CUSTOM HIVIEW FUNCTION DECLARATION -- |
OSStatus SMFPeopleViewRegister(); |
OSStatus SMFPeopleViewHandler(EventHandlerCallRef inCallRef, EventRef inEvent,void *inUserData ); |
OSStatus SMFPeopleViewConstruct(EventRef inEvent ); |
OSStatus SMFPeopleViewDestruct(EventRef inEvent, SMFPeopleViewData *inData ); |
OSStatus SMFPeopleViewDraw(EventRef inEvent,SMFPeopleViewData *inData ); |
OSStatus SMFPeopleComputeEfficientSizeChange(EventRef inEvent, SMFPeopleViewData *inData ); |
OSStatus SMFPeopleViewGetData(EventRef inEvent, SMFPeopleViewData *inData ); |
OSStatus SMFPeopleViewSetData(EventRef inEvent, SMFPeopleViewData *inData ); |
OSStatus SMFPeopleViewSetOwningWindow(EventRef inEvent, SMFPeopleViewData *inData ); |
OSStatus SMFPeopleViewScrollableGetInfo(EventRef inEvent,SMFPeopleViewData *inData); |
OSStatus SMFPeopleViewScrollableScrollTo(EventRef inEvent,SMFPeopleViewData *inData); |
#pragma mark -- CUSTOM HIVIEW FUNCTION DEFINITIONS -- |
pascal OSStatus SMFPeopleViewHandler(EventHandlerCallRef inCallRef, EventRef inEvent,void *inUserData ) |
{ |
OSStatus err = eventNotHandledErr; |
UInt32 eventClass = GetEventClass( inEvent ); |
UInt32 eventKind = GetEventKind( inEvent ); |
SMFPeopleViewData* data = (SMFPeopleViewData*) inUserData; |
switch ( eventClass ) |
{ |
case kEventClassHIObject: |
{ |
switch ( eventKind ) |
{ |
case kEventHIObjectConstruct: |
err = SMFPeopleViewConstruct( inEvent ); |
break; |
case kEventHIObjectDestruct: |
err = SMFPeopleViewDestruct( inEvent, data ); |
break; |
} |
} |
break; |
case kEventClassControl: |
{ |
switch ( eventKind ) |
{ |
case kEventControlDraw: |
err = SMFPeopleViewDraw( inEvent, data ); |
break; |
case kEventControlGetData: |
err = SMFPeopleViewGetData( inEvent, data ); |
break; |
case kEventControlSetData: |
err = SMFPeopleViewSetData( inEvent, data ); |
break; |
case kEventControlOwningWindowChanged: |
err = SMFPeopleViewSetOwningWindow( inEvent, data ); |
break; |
case kEventControlInvalidateForSizeChange: |
err = SMFPeopleComputeEfficientSizeChange( inEvent, data ); |
break; |
} |
} |
break; |
case kEventClassScrollable: |
{ |
//We need to support these events as we need to be embeddded in a scroll view. |
switch ( eventKind ) { |
case kEventScrollableGetInfo: |
err = SMFPeopleViewScrollableGetInfo(inEvent, data); |
break; |
case kEventScrollableScrollTo: |
err = SMFPeopleViewScrollableScrollTo(inEvent, data); |
break; |
} |
} |
} |
return err; |
} |
// ----------------------------------------------------------------------------- |
// SMFPeopleViewConstruct |
// ----------------------------------------------------------------------------- |
// |
OSStatus SMFPeopleViewConstruct(EventRef inEvent ) |
{ |
OSStatus err; |
SMFPeopleViewData* data; |
SInt32 tmpInt32; |
// don't CallNextEventHandler! |
data = (SMFPeopleViewData*) malloc( sizeof( SMFPeopleViewData ) ); |
require_action( data != NULL, EXIT, err = memFullErr ); |
// Geometry |
data->_nbTotal = 0; |
data->_nbColumn = kPeopleViewNbColumnInit; //For now let's start at height |
data->_origX = 0.0; |
data->_origY = 0.0; |
data->changeDirection = 0; |
err = GetThemeMetric(kThemeMetricScrollBarWidth, &tmpInt32); |
require_noerr( err, EXIT ); |
data->_scrollBarWidth = (float) tmpInt32; |
//cache the icon |
err = GetIconRef(kOnSystemDisk, kSystemIconsCreator, kUnknownFSObjectIcon, &data->_noImageIcon); |
require_noerr( err, EXIT ); |
// Keep a copy of the created HIViewRef |
err = GetEventParameter( inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof( HIObjectRef ), NULL, (HIObjectRef*) &data->view ); |
require_noerr( err, EXIT ); |
// Set the userData that will be used with all subsequent eventHandler calls |
err = SetEventParameter( inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof( SMFPeopleViewData* ), &data ); |
EXIT: |
if ( err != noErr ) |
free( data ); |
return err; |
} |
// ----------------------------------------------------------------------------- |
// SMFPeopleViewDestruct |
// ----------------------------------------------------------------------------- |
// |
OSStatus SMFPeopleViewDestruct(EventRef inEvent, SMFPeopleViewData* inData) |
{ |
#pragma unused( inEvent ) |
// Clean up any allocated data |
ReleaseIconRef(inData->_noImageIcon); |
free( inData ); |
return noErr; |
} |
// ----------------------------------------------------------------------------- |
// SMFPeopleViewDraw |
// ----------------------------------------------------------------------------- |
// |
OSStatus SMFPeopleViewDraw(EventRef inEvent, SMFPeopleViewData* inData ) |
{ |
OSStatus err; |
HIRect bounds; |
float rowHeight; |
float colWidth; |
int cols; |
float rows = 0; |
int emptys = 0; |
HIRect drawRect, tmpRect, textRect, imageRect; |
ControlPartCode part; |
int idx, i, size; |
CGContextRef drawContext; |
CFTypeRef * values; |
// Get ready to do the CG drawing boogaloo! |
err = GetEventParameter( inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof( CGContextRef ), NULL, &drawContext ); |
require_noerr( err, EXIT ); |
cols = inData->_nbColumn; |
if(0 == inData->_nbTotal) { |
err = HIViewGetBounds( inData->view, &bounds ); |
CGContextSetRGBFillColor( drawContext, 0.95, .95, .95, 1 ); |
CGContextFillRect(drawContext, bounds); |
} else { |
HIThemeTextInfo txtInfo; |
CFDictionaryRef tmpPeople = [gMainWindowController allPeople]; |
size = CFDictionaryGetCount(tmpPeople); |
values = (CFTypeRef *) malloc( size * sizeof(CFTypeRef) ); |
CFDictionaryGetKeysAndValues(tmpPeople, NULL, (const void **) values); |
txtInfo.version = kHIThemeTextInfoVersionZero; |
txtInfo.state = kThemeStateActive; |
txtInfo.fontID = kThemeSmallSystemFont; |
txtInfo.horizontalFlushness = kHIThemeTextHorizontalFlushCenter; |
txtInfo.verticalFlushness = kHIThemeTextVerticalFlushBottom; |
txtInfo.options = 0; |
txtInfo.truncationPosition = kHIThemeTextTruncationEnd; |
txtInfo.truncationMaxLines = 1; |
emptys = inData->_nbTotal % cols; |
rows = 1 + ((inData->_nbTotal - emptys) / cols); |
drawRect.origin.x = 0; |
drawRect.origin.y = 0; |
drawRect.size.width = (float) kOnePeopleViewWidth; |
drawRect.size.height = (float) kOnePeopleViewHeigth; |
textRect = CGRectMake(drawRect.origin.x, drawRect.origin.y, drawRect.size.width, drawRect.size.height); |
textRect.origin.y -= 5.0; |
imageRect.origin.x = (kOnePeopleViewWidth - 32)/2; |
imageRect.origin.y = 5.0; |
imageRect.size.width = imageRect.size.height = 32; |
idx = i = 0; |
HIRect theRect; |
HIViewGetFrame(inData->view, &theRect); |
HIRect theVisRect = CGRectMake(-inData->_origX, -inData->_origY, theRect.size.width, theRect.size.height); |
//This is the core drawing method for one contact. No real error checkin done in this sample. |
//Basically it draws one people and then move to the next position. |
for(i = 0; i < size; i++) { |
if(CGRectIntersectsRect(theVisRect, drawRect)) { |
//Draw background and border |
if(!CFBooleanGetValue(CFDictionaryGetValue((CFDictionaryRef) values[i], CFSTR("SearchSelected")))) |
CGContextSetRGBFillColor( drawContext, 1, 1, 1, 1 ); |
else |
CGContextSetRGBFillColor( drawContext, 0.56, 0.70, 0.92, 1 ); |
CGContextFillRect(drawContext, drawRect); |
CGRectInset(drawRect, 1, 1); |
CGContextSetRGBStrokeColor(drawContext, 0.95, .95, .95, 1); |
CGContextStrokeRect(drawContext, drawRect); |
//Draw Name |
CGContextSetRGBFillColor( drawContext, 0, 0, 0, 1 ); |
CFStringRef tmpString = CFDictionaryGetValue((CFDictionaryRef) values[i], CFSTR("FullName")); |
HIThemeDrawTextBox(tmpString, &textRect, &txtInfo, drawContext, kHIThemeOrientationNormal); |
//Draw image |
if(CFDictionaryContainsKey(values[i], CFSTR("image"))) |
HIViewDrawCGImage(drawContext , &imageRect, (CGImageRef) CFDictionaryGetValue((CFDictionaryRef) values[i], CFSTR("image"))); |
else |
PlotIconRefInContext(drawContext, &imageRect, kAlignAbsoluteCenter, kTransformNone, NULL, kPlotIconRefNormalFlags, inData->_noImageIcon); |
} |
//update the position |
if(++idx == cols) { |
idx = 0; |
drawRect.origin.x = textRect.origin.x = 0.0; |
imageRect.origin.x = (kOnePeopleViewWidth - 32)/2; |
drawRect.origin.y+= kOnePeopleViewHeigth; |
textRect.origin.y+= kOnePeopleViewHeigth; |
imageRect.origin.y+= kOnePeopleViewHeigth; |
} else { |
drawRect.origin.x += kOnePeopleViewWidth; |
textRect.origin.x += kOnePeopleViewWidth; |
imageRect.origin.x += kOnePeopleViewWidth; |
} |
} |
if(0 != idx) { |
//there is some place on the right,let's colorize it as grey. |
CGRect leftOverRect = CGRectMake(drawRect.origin.x , drawRect.origin.y, (cols -idx) * kOnePeopleViewWidth , kOnePeopleViewHeigth); |
CGContextSetRGBFillColor( drawContext, 0.90, .90, .90, 1 ); |
CGContextFillRect(drawContext, leftOverRect); |
} |
err = HIViewGetBounds(inData->view, &tmpRect); |
if (drawRect.origin.y + drawRect.size.height < tmpRect.size.height) { |
//there is some place below,let's colorize it as grey. |
if(0 != idx) { |
tmpRect.origin.y = drawRect.origin.y + drawRect.size.height; |
tmpRect.size.height -= (drawRect.origin.y + drawRect.size.height); |
} else { |
tmpRect.origin.y = drawRect.origin.y + drawRect.size.height - kOnePeopleViewHeigth; |
tmpRect.size.height -= (drawRect.origin.y + drawRect.size.height); |
tmpRect.size.height += kOnePeopleViewHeigth; |
} |
CGContextSetRGBFillColor( drawContext, 0.95, .95, .95, 1 ); |
CGContextFillRect(drawContext, tmpRect); |
} |
} |
EXIT: |
return err; |
} |
OSStatus SMFPeopleComputeEfficientSizeChange(EventRef inEvent, SMFPeopleViewData *inData ) |
{ |
OSStatus err = eventNotHandledErr; |
HIViewRef tmpCtl; |
HIShapeRef previousShape, newShape, updateShape; |
HIRect previousRect, newRect; |
err = GetEventParameter( inEvent, kEventParamOriginalBounds, typeHIRect, NULL, sizeof( HIRect ), NULL, &previousRect ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof( HIRect ), NULL, &newRect ); |
require_noerr( err, EXIT ); |
previousShape = HIShapeCreateWithRect(&previousRect); |
newShape = HIShapeCreateWithRect(&newRect); |
updateShape = HIShapeCreateDifference(newShape, previousShape); |
if(!HIShapeIsEmpty(updateShape)) { |
err = HIViewSetNeedsDisplayInShape(inData->view, updateShape, true); |
} |
CFRelease(previousShape); |
CFRelease(newShape); |
CFRelease(updateShape); |
EXIT: |
return err; |
} |
// ----------------------------------------------------------------------------- |
// SMFPeopleViewGetData |
// ----------------------------------------------------------------------------- |
// |
OSStatus SMFPeopleViewGetData(EventRef inEvent,SMFPeopleViewData *inData ) |
{ |
OSStatus err; |
OSType tag; |
Ptr ptr; |
Size size; |
Size outSize; |
ControlPartCode part; |
err = GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof( ControlPartCode ), NULL, &part ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataTag, typeEnumeration, NULL, sizeof( OSType ), NULL, &tag ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataBuffer, typePtr, NULL, sizeof( Ptr ), NULL, &ptr ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataBufferSize, typeLongInteger, NULL, sizeof( Size ), NULL, &size ); |
require_noerr( err, EXIT ); |
switch ( tag ) |
{ |
case kControlSMFPeopleNumberTag: |
if ( size == sizeof( int ) ) |
*( (int*) ptr ) = inData->_nbTotal; |
else |
err = errDataSizeMismatch; |
outSize = sizeof( int ); |
break; |
case kControlSMFPeopleMaxColNumberTag: |
if ( size == sizeof( float ) ) |
*( (float*) ptr ) = inData->_nbColumn; |
else |
err = errDataSizeMismatch; |
outSize = sizeof( float ); |
break; |
default: |
err = errDataNotSupported; |
outSize = 0; |
break; |
} |
if ( err == noErr ) |
err = SetEventParameter( inEvent, kEventParamControlDataBufferSize, typeLongInteger, sizeof( Size ), &outSize ); |
EXIT: |
return err; |
} |
// ----------------------------------------------------------------------------- |
// SMFPeopleViewSetData |
// ----------------------------------------------------------------------------- |
// |
OSStatus SMFPeopleViewSetData(EventRef inEvent, SMFPeopleViewData *inData ) |
{ |
OSStatus err; |
ControlPartCode part; |
OSType tag; |
Ptr ptr; |
Size size; |
err = GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof( ControlPartCode ), NULL, &part ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataTag, typeEnumeration, NULL, sizeof( OSType ), NULL, &tag ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataBuffer, typePtr, NULL, sizeof( Ptr ), NULL, &ptr ); |
require_noerr( err, EXIT ); |
err = GetEventParameter( inEvent, kEventParamControlDataBufferSize, typeLongInteger, NULL, sizeof( Size ), NULL, &size ); |
require_noerr( err, EXIT ); |
switch ( tag ) |
{ |
case kControlSMFPeopleNumberTag: |
{ |
if ( size == sizeof( int ) ) |
inData->_nbTotal = *( (int*) ptr ); |
else |
err = errDataSizeMismatch; |
//here we need to compute the bounds again |
HIRect bounds, frames; |
err = HIViewGetFrame( inData->view, &frames ); |
int emptys = inData->_nbTotal % inData->_nbColumn; |
int rows = 1 + ((inData->_nbTotal - emptys) / inData->_nbColumn); |
frames.size.height = rows *kOnePeopleViewHeigth; |
frames.size.width = inData->_nbColumn *kOnePeopleViewWidth; |
err = HIViewSetFrame( inData->view, &frames ); |
EventRef tmpEvent; |
MacCreateEvent(kCFAllocatorDefault, kEventClassScrollable, kEventScrollableInfoChanged, 0, kEventAttributeNone, &tmpEvent); |
SendEventToControl(tmpEvent, HIViewGetSuperview(inData->view)); |
ReleaseEvent(tmpEvent); |
break; |
} |
case kControlSMFPeopleMaxColNumberTag: |
if ( size == sizeof( float ) ) { |
inData->_nbColumn = *( (float*) ptr ); |
err= SMFPeopleViewSetOwningWindow(inEvent, inData ); //we reconstrain the limits |
} else |
err = errDataSizeMismatch; |
break; |
default: |
err = errDataNotSupported; |
break; |
} |
EXIT: |
return err; |
} |
OSStatus SMFPeopleViewSetOwningWindow(EventRef inEvent, SMFPeopleViewData *inData ) |
{ |
OSStatus err; |
HISize outMinLimits,outMaxLimits; |
WindowRef owningWindow; |
//In fact we just use this event to constraint the owning window size, to the number of columns allowed. |
//The owning window never changes... really. |
owningWindow = HIViewGetWindow(inData->view); |
require_action(NULL != owningWindow, EXIT, err = errInvalidWindowRef); |
//set up size limit to adapt to the number of rows |
err = GetWindowResizeLimits(HIViewGetWindow(inData->view), &outMinLimits, &outMaxLimits); |
require_noerr( err, EXIT ); |
outMaxLimits.width = (float) (inData->_nbColumn * kOnePeopleViewWidth) + inData->_scrollBarWidth; |
outMaxLimits.height = 5000; ///just put something very big... |
err = SetWindowResizeLimits(HIViewGetWindow(inData->view), &outMinLimits, &outMaxLimits); |
EXIT: |
return err; |
} |
OSStatus SMFPeopleViewScrollableGetInfo(EventRef inEvent,SMFPeopleViewData *inData) |
{ |
OSStatus err; |
HIRect bounds; |
int emptys = inData->_nbTotal % inData->_nbColumn; |
int rows = 1 + ((inData->_nbTotal - emptys) / inData->_nbColumn); |
HISize tmpSize = {(float) inData->_nbColumn * kOnePeopleViewWidth, (float) rows*kOnePeopleViewHeigth}; |
err = SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof(tmpSize), &tmpSize); |
require_noerr( err, EXIT ); |
HISize lineSize = {20.0, 20.0}; |
err = SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(lineSize), &lineSize); |
require_noerr( err, EXIT ); |
HIViewGetBounds(HIViewGetSuperview(inData->view), &bounds); |
bounds.size.width -= inData->_scrollBarWidth; |
bounds.size.height -= inData->_scrollBarWidth; |
err = SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); |
require_noerr( err, EXIT ); |
HIPoint point = { inData->_origX, inData->_origY}; |
err = SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof(point), &point); |
EXIT: |
return err; |
} |
OSStatus SMFPeopleViewScrollableScrollTo(EventRef inEvent,SMFPeopleViewData *inData) |
{ |
OSStatus err = noErr; |
HIPoint tmpPoint; |
err = GetEventParameter( inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof( tmpPoint ), NULL, &tmpPoint); |
require_noerr( err, EXIT ); |
inData->_origX = -tmpPoint.x; |
inData->_origY = -tmpPoint.y; |
err = HIViewSetBoundsOrigin(inData->view, -inData->_origX, -inData->_origY); |
require_noerr( err, EXIT ); |
err = HIViewSetNeedsDisplay(inData->view, true); |
EXIT: |
return err; |
} |
//the registration function for our custom HIView. |
OSStatus SMFPeopleViewRegister() |
{ |
OSStatus err = noErr; |
static HIObjectClassRef sSMFPeopleViewClassRef = NULL; |
if ( sSMFPeopleViewClassRef == NULL ) |
{ |
EventTypeSpec eventList[] = { |
{ kEventClassHIObject, kEventHIObjectConstruct }, |
{ kEventClassHIObject, kEventHIObjectDestruct }, |
{ kEventClassControl, kEventControlDraw }, |
{ kEventClassControl, kEventControlInvalidateForSizeChange}, |
{ kEventClassControl, kEventControlGetData }, |
{ kEventClassScrollable, kEventScrollableGetInfo }, |
{ kEventClassScrollable, kEventScrollableScrollTo }, |
{ kEventClassControl, kEventControlOwningWindowChanged }, |
{ kEventClassControl, kEventControlSetData }}; |
err = HIObjectRegisterSubclass(kSMFPeopleViewClassID,kHIViewClassID,0, |
SMFPeopleViewHandler,GetEventTypeCount( eventList ),eventList,NULL, &sSMFPeopleViewClassRef ); |
} |
return err; |
} |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-10-16