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.
main.c
/* |
File: main.c |
Abstract: A skeleton of modern nib-based and Carbon Events-based Carbon application. |
SetCustomIcon demonstrates how to programmatically set a custom icon to either |
a file or folder. A typical example may be to create an icon representation of |
a photo. This example uses QuickTime's graphics importers to draw the selected |
image onscreen, and also in the process of converting the image to a PicHandle and |
eventually to a IconFamilyHandle. The utility routine SaveCustomIcon() is |
responsible for saving the IconFamilyHandle data as a resource in the appropriate |
format. |
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 © 2004 Apple Computer, Inc., All Rights Reserved |
*/ |
#include <Carbon/Carbon.h> |
#include <QuickTime/QuickTime.h> |
#include <sys/param.h> |
struct GlobalAppInfo // Application globals |
{ |
IBNibRef mainNibRef; |
}; |
typedef struct GlobalAppInfo GlobalAppInfo; |
struct CustomIconWindowInfo // Additional information to be stored with each window |
{ |
WindowRef window; |
GraphicsImportComponent graphicsImportComponent; // QuickTime Graphics importer to draw image |
}; |
typedef struct CustomIconWindowInfo CustomIconWindowInfo; |
int main( int argc, char *argv[] ); // Added call to EnterMovies() |
static void InstallAppleEventHandlers( void ); |
static OSStatus DoNewWindow( WindowRef *window ); |
static void DoPreferences( void ); |
static OSStatus NavOpenDocument( OSType command ); |
static pascal Boolean NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode ); |
OSStatus GetControlBySigAndID( WindowRef window, OSType signature, SInt32 id, ControlRef *control ); |
static OSStatus OpenDocument( AEDescList docList, OSType command ); |
static pascal OSStatus CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ); // Change kHICommandNew to behave same as kHICommandOpen |
static pascal OSStatus CreateCustomIconWindowEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ); |
static void SendCommandProcessEvent( UInt32 commandID ); |
static OSStatus DisplayImageInWindow( WindowRef window, FSRef *fsRef ); |
void ScaleInRect( Rect *boundsRect, Rect *origRect, Rect *destRect ); |
void SetControlCString( WindowRef window, OSType signature, SInt32 id, char *cString ); |
static OSStatus DisplayCustomIconDestinationPath( WindowRef window, FSRef *fsRef ); |
char *GetControlCString( WindowRef window, OSType signature, SInt32 id, char *cString ); |
OSErr CreateCustomIconFromGraphicsComponent( GraphicsImportComponent graphicsImportComponent, FSSpec *targetSpec ); |
OSStatus SetCustomIcon( CustomIconWindowInfo *w ); |
PicHandle CreatePictHandleFromGraphicsComponent( GraphicsImportComponent graphicsImportComponent ); |
OSErr SaveCustomIcon( FSSpec *targetSpec, IconFamilyHandle icnsH ); |
static pascal OSStatus UserPaneControlHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ); |
OSErr SendFinderAppleEvent( AliasHandle aliasH, AEEventID appleEventID ); |
GlobalAppInfo g; |
#pragma mark - |
#pragma mark ¥ Main ¥ |
int main( int argc, char *argv[] ) |
{ |
OSStatus status; |
const EventTypeSpec commandProcessEvents[] = { { kEventClassCommand, kEventCommandProcess } }; |
BlockZero( &g, sizeof(g) ); // Initialize our globals |
status = EnterMovies(); // Initialize QuickTime |
require_noerr( status, CantInitializeQuickTime ); |
// Create a Nib reference passing the name of the nib file (without the .nib extension) CreateNibReference only searches into the application bundle. |
status = CreateNibReference( CFSTR("main"), &g.mainNibRef ); |
require_noerr( status, CantGetNibRef ); |
// Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar object. This name is set in InterfaceBuilder when the nib is created. |
status = SetMenuBarFromNib( g.mainNibRef, CFSTR("MenuBar") ); |
require_noerr( status, CantSetMenuBar ); |
EnableMenuCommand( NULL, kHICommandPreferences ); // Enable Preferences menu item |
InstallAppleEventHandlers(); |
InstallApplicationEventHandler( NewEventHandlerUPP(CommandProcessEventHandler), GetEventTypeCount(commandProcessEvents), commandProcessEvents, NULL, NULL ); |
RunApplicationEventLoop(); // Call the event loop |
CantSetMenuBar: |
CantGetNibRef: |
CantInitializeQuickTime: |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ AppleEvent Handlers ¥ |
static pascal OSErr HandleAppleEventOapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
SendCommandProcessEvent( kHICommandNew ); // Instantiate an empty window at the beginning so the User sees something |
return( noErr ); |
} |
static pascal OSErr HandleAppleEventRapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
return( noErr ); |
} |
static pascal OSErr HandleAppleEventOdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
AEDescList docList; |
OSErr err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList ); |
require_noerr( err, CantGetDocList ); |
err = OpenDocument( docList, 'GImg' ); |
AEDisposeDesc( &docList ); |
CantGetDocList: |
return( err ); |
} |
static pascal OSErr HandleAppleEventPdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
return( errAEEventNotHandled ); |
} |
static void InstallAppleEventHandlers( void ) |
{ |
OSErr status; |
status = AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP(HandleAppleEventOapp), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEReopenApplication, NewAEEventHandlerUPP(HandleAppleEventRapp), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(HandleAppleEventOdoc), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerUPP(HandleAppleEventPdoc), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
// Note: Since RunApplicationEventLoop installs a Quit AE Handler, there is no need to do it here. |
CantInstallAppleEventHandler: |
return; |
} |
#pragma mark - |
#pragma mark ¥ CarbonEvent Handlers ¥ |
static pascal OSStatus CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ) |
{ |
HICommand command; |
OSStatus status = eventNotHandledErr; |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
switch ( command.commandID ) |
{ |
case kHICommandNew: |
status = DoNewWindow( NULL ); |
break; |
case kHICommandOpen: |
status = NavOpenDocument( 'GImg' ); // File->Open assumes the user wants to select a source image |
break; |
case kHICommandPreferences: |
DoPreferences(); |
break; |
case kHICommandRevert: // Place holders, not handled yet |
case kHICommandPageSetup: |
case kHICommandPrint: |
break; |
} |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ Windows ¥ |
static void DoPreferences( void ) |
{ |
DialogRef alertDialog; |
CreateStandardAlert( kAlertStopAlert, CFSTR("No Preferences yet!"), NULL, NULL, &alertDialog ); |
RunStandardAlert( alertDialog, NULL, NULL ); |
} |
// Create a new window, install Event Handlers, initialize window storage |
static OSStatus DoNewWindow( WindowRef *outWindow ) |
{ |
OSStatus status; |
CustomIconWindowInfo *w; |
Rect r; |
static EventHandlerUPP createCustomIconWindowEventHandlerUPP; |
HISize minWindowSize; // width, height |
const EventTypeSpec windowEvents[] = { { kEventClassCommand, kEventCommandProcess }, { kEventClassWindow, kEventWindowClose } }; |
w = (CustomIconWindowInfo*) NewPtrClear( sizeof(CustomIconWindowInfo) ); |
status = CreateWindowFromNib( g.mainNibRef, CFSTR("MainWindow"), &w->window ); // Create a window. "MainWindow" is the name of the window object. This name is set in InterfaceBuilder when the nib is created. |
require_noerr( status, CantCreateWindow ); |
SetWRefCon( w->window, (long) w ); |
if ( createCustomIconWindowEventHandlerUPP == NULL ) createCustomIconWindowEventHandlerUPP = NewEventHandlerUPP( CreateCustomIconWindowEventHandler ); |
(void) InstallWindowEventHandler( w->window, createCustomIconWindowEventHandlerUPP, GetEventTypeCount(windowEvents), windowEvents, w, NULL ); |
GetWindowBounds( w->window, kWindowContentRgn, &r ); |
minWindowSize.width = r.right - r.left; |
minWindowSize.height = r.bottom - r.top; |
(void) SetWindowResizeLimits( w->window, &minWindowSize, NULL ); // Sets the minimum window size to the initial window size. This prevents controls from overlapping |
if ( outWindow == NULL ) ShowWindow( w->window ); // The window was created hidden so show it if the window parameter is NULL, if it's not, it will be the responsibility of the caller to show it. |
CantCreateWindow: |
if ( outWindow != NULL ) *outWindow = w->window; |
return( status ); |
} |
// Main window event handler. |
static pascal OSStatus CreateCustomIconWindowEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ) |
{ |
#pragma unused ( inCallRef ) |
HICommand command; |
ControlRef control; |
char path[MAXPATHLEN]; |
OSStatus err = eventNotHandledErr; |
UInt32 eventKind = GetEventKind( inEvent ); |
UInt32 eventClass = GetEventClass( inEvent ); |
CustomIconWindowInfo *w = (CustomIconWindowInfo *) inUserData; |
switch ( eventClass ) |
{ |
case kEventClassWindow: |
if ( eventKind == kEventWindowClose ) // Free up extra window storage |
{ |
if ( w->graphicsImportComponent != NULL ) (void) CloseComponent( w->graphicsImportComponent ); // Close the Graphics Importer |
DisposePtr( (Ptr) w ); // Dispose window storage |
} |
break; |
case kEventClassCommand: |
if ( eventKind == kEventCommandProcess ) |
{ |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
if ( (command.commandID == 'GImg') || (command.commandID == 'Targ') ) |
{ |
err = NavOpenDocument( command.commandID ); // User to select source image or target path |
GetControlCString( w->window, 'STxt', 0, path ); |
if ( (w->graphicsImportComponent != NULL) && (strlen(path) > 0) ) // If source image and target path have both been set |
{ |
GetControlBySigAndID( w->window, 'Butn', 0, &control ); |
(void) EnableControl( control ); // Enable the "Set Custom Icon" button |
} |
} |
else if ( command.commandID == kHICommandOK ) |
{ |
SetCustomIcon( w ); // Set the custom icon |
} |
} |
break; |
} |
return( err ); |
} |
#pragma mark - |
#pragma mark ¥ Save/Open Document ¥ |
static OSStatus NavOpenDocument( OSType command ) |
{ |
OSStatus status; |
NavDialogCreationOptions options; |
NavReplyRecord navReply; |
static NavObjectFilterUPP navFilterUPP; |
NavDialogRef navDialog = NULL; |
status = NavGetDefaultDialogCreationOptions( &options ); |
require_noerr( status, CantGetDefaultOptions ); |
if ( navFilterUPP == NULL ) navFilterUPP = NewNavObjectFilterUPP( NavOpenFilterProc ); // Filter only documents we can open |
if ( command == 'Targ' ) status = NavCreateChooseObjectDialog( &options, NULL, NULL, NULL, NULL, &navDialog ); // Select file, folder, anything as target |
else status = NavCreateChooseFileDialog( &options, NULL, NULL, NULL, navFilterUPP, NULL, &navDialog ); // Select source image |
require_noerr( status, CantCreateDialog ); |
status = NavDialogRun( navDialog ); |
require_noerr( status, CantRunDialog ); |
status = NavDialogGetReply( navDialog, &navReply ); |
require( (status == noErr) || (status == userCanceledErr), CantGetReply ); |
if ( navReply.validRecord ) status = OpenDocument( navReply.selection, command ); // Handle the image/target path by displaying it in the window |
else status = userCanceledErr; |
NavDisposeReply( &navReply ); |
CantGetReply: |
CantRunDialog: |
if ( navDialog != NULL ) NavDialogDispose( navDialog ); |
CantCreateDialog: |
CantGetDefaultOptions: |
return( status ); |
} |
// We use CanQuickTimeOpenFile to determine which files are displayed in ther NavServices open file dialog. |
// This filter is used to select source images which Graphics Importers can draw. |
static pascal Boolean NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode ) |
{ |
FSRef fsRef; |
OSStatus status; |
FSSpec fsSpec; |
FSCatalogInfo fsCatalogInfo; |
Boolean canOpen; |
Boolean canViewItem = false; |
if ( theItem->descriptorType == typeFSRef ) |
{ |
if ( ((NavFileOrFolderInfo*)info)->isFolder == true ) return( true ); // Allow user to recurse into all directories |
status = AEGetDescData( theItem, &fsRef, sizeof(fsRef) ); // Get the FSRef |
require_noerr( status, CantGetFSRef ); |
status = FSGetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &fsCatalogInfo, NULL, &fsSpec, NULL ); // Get the FSSpec from FSRef |
require_noerr( status, CantGetFSRef ); |
status = CanQuickTimeOpenFile( &fsSpec, ((FileInfo *)&fsCatalogInfo.finderInfo)->fileType, 0, NULL, &canOpen, NULL, kQTDontLookForMovieImporterIfGraphicsImporterFound ); |
if ( (status == noErr) && (canOpen==true) ) canViewItem = true; |
} |
CantGetFSRef: |
return( canViewItem ); |
} |
// Bottleneck routine responsible for opening files. "command" specified weather the specified file should be opened |
// as a source image or as a target to receive the custom icon. |
static OSStatus OpenDocument( AEDescList docList, OSType command ) |
{ |
FSRef fsRef; |
WindowRef window; |
OSStatus status; |
if ( (status = AEGetNthPtr( &docList, 1, typeFSRef, NULL, NULL, &fsRef, sizeof(FSRef), NULL) ) == noErr ) |
{ |
window = GetFrontWindowOfClass( kDocumentWindowClass, true ); // If no window is open yet |
if ( window == NULL ) |
{ |
status = DoNewWindow( &window ); // Create a new (Hidden) window |
require_noerr( status, CantCreateWindow ); |
} |
if ( command == 'GImg' ) status = DisplayImageInWindow( window, &fsRef ); // Display the selected source image in the window |
else status = DisplayCustomIconDestinationPath( window, &fsRef ); // Display the target path |
ShowWindow( window ); // The window was created hidden so show it |
} |
CantCreateWindow: |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ Utilities ¥ |
OSStatus GetControlBySigAndID( WindowRef window, OSType signature, SInt32 id, ControlRef *control ) |
{ |
ControlID controlID; |
controlID.id = id; |
controlID.signature = signature; |
return( GetControlByID( window, &controlID, control ) ); |
} |
void SetControlCString( WindowRef window, OSType signature, SInt32 id, char *cString ) |
{ |
ControlRef control; |
GetControlBySigAndID( window, signature, id, &control ); |
if ( control == NULL ) goto Bail; |
(void) SetControlData( control, 0, kControlStaticTextTextTag, strlen(cString), cString ); |
Bail: |
return; |
} |
char *GetControlCString( WindowRef window, OSType signature, SInt32 id, char *cString ) |
{ |
ControlRef control; |
SInt32 dataSize; |
cString[0] = 0; |
GetControlBySigAndID( window, signature, id, &control ); |
if ( control == NULL ) goto Bail; |
(void)GetControlData( control, 0, kControlStaticTextTextTag, 4096, cString, &dataSize ); |
cString[dataSize] = 0; |
Bail: |
return( cString ); |
} |
// Utility routine to synchronously send a basic "kEventClassCommand / kEventCommandProcess" CarbonEvent to the application target |
static void SendCommandProcessEvent( UInt32 commandID ) |
{ |
HICommand command; |
EventRef event; |
BlockZero( &command, sizeof(command) ); |
command.commandID = commandID; |
(void) CreateEvent( NULL, kEventClassCommand, kEventCommandProcess, GetCurrentEventTime(), kEventAttributeUserEvent, &event ); |
(void) SetEventParameter( event, kEventParamDirectObject, typeHICommand, sizeof(command), &command ); |
(void) SendEventToApplication( event ); |
(void) ReleaseEvent( event ); |
} |
OSErr FSpGetCatInfo( FSSpec *spec, CInfoPBRec *cpb ) |
{ |
cpb->hFileInfo.ioFDirIndex = 0; |
cpb->hFileInfo.ioNamePtr = spec->name; |
cpb->hFileInfo.ioVRefNum = spec->vRefNum; |
cpb->hFileInfo.ioDirID = spec->parID; |
return( PBGetCatInfoSync( cpb ) ); |
} |
// Given two rectangles, boundsRect and origRect, ScaleInRect will create the largest possible rectangle to fit in |
// boundsRect, while maintaining the aspect ratio specified by origRect. The new rectangle is centered and returned |
// in destRect |
void ScaleInRect( Rect *boundsRect, Rect *origRect, Rect *destRect ) |
{ |
SInt16 width; |
SInt16 height; |
SInt16 hSlop; |
SInt16 vSlop; |
float oRatio = ((float)(origRect->right - origRect->left)) / ((float)(origRect->bottom - origRect->top)); |
if ( ((boundsRect->bottom - boundsRect->top) * oRatio) > (boundsRect->right - boundsRect->left) ) |
{ |
width = boundsRect->right - boundsRect->left; |
height = width * (1.0/oRatio); |
} |
else |
{ |
height = boundsRect->bottom - boundsRect->top; |
width = height * oRatio; |
} |
hSlop = (boundsRect->right - boundsRect->left - width) / 2; |
vSlop = (boundsRect->bottom - boundsRect->top - height) / 2; |
SetRect( destRect, boundsRect->left + hSlop, boundsRect->top + vSlop, boundsRect->left + width + hSlop, boundsRect->top + height + vSlop ); |
} |
// Utility routine to send the Finder an event within its kAEFinderSuite. We use it to send a kAESync AppleEvent which forces the Finder |
// to immediately display the new icon. |
OSErr SendFinderAppleEvent( AliasHandle aliasH, AEEventID appleEventID ) |
{ |
ProcessInfoRec processInfo; |
OSErr err; |
AppleEvent appleEvent = { typeNull, NULL }; |
AEDesc aeDesc = { typeNull, NULL }; |
ProcessSerialNumber psn = { kNoProcess, kNoProcess }; |
AppleEvent aeReply = { typeNull, NULL }; |
BlockZero( (Ptr)&processInfo, sizeof(processInfo) ); |
processInfo.processInfoLength = sizeof( processInfo ); |
for ( err = GetNextProcess( &psn ) ; err == noErr ; err = GetNextProcess( &psn ) ) |
{ |
err = GetProcessInformation( &psn, &processInfo ); |
if ( (processInfo.processSignature == 'MACS') && (processInfo.processType == 'FNDR') ) // Find the Finders PSN by searching all running processes |
break; |
} |
require_noerr( err, Bail ); |
err = AECreateDesc( typeProcessSerialNumber, &psn, sizeof( psn ), &aeDesc ); |
require_noerr( err, Bail ); |
err = AECreateAppleEvent( kAEFinderSuite, appleEventID, &aeDesc, kAutoGenerateReturnID, kAnyTransactionID, &appleEvent ); // Create AppleEvent (kAEFinderSuite, appleEventID) |
(void) AEDisposeDesc( &aeDesc ); |
require_noerr( err, Bail ); |
err = AECreateDesc( typeAlias, *aliasH, GetHandleSize( (Handle)aliasH ), &aeDesc ); |
require_noerr( err, Bail ); |
err = AEPutParamDesc( &appleEvent, keyDirectObject, &aeDesc ); |
(void) AEDisposeDesc( &aeDesc ); |
require_noerr( err, Bail ); |
err = AESend( &appleEvent, &aeReply, kAENoReply, kAENormalPriority, kNoTimeOut, NULL, NULL ); // Send the AppleEvent to the Finder |
(void) AEDisposeDesc( &aeReply ); |
require_noerr( err, Bail ); |
(void) AEDisposeDesc( &appleEvent ); |
Bail: |
return( err ); |
} |
#pragma mark - |
#pragma mark ¥ Specialized Routines ¥ |
// Displays the selected source image within our User Pane, ( 'UPan', 0 ). We use QuickTime's Graphics importers to draw |
// the image whose maximum size is the bounds of the user pane, 128 X 128, which is coincidentally the maximum size the |
// Finder will display an icon. |
static OSStatus DisplayImageInWindow( WindowRef window, FSRef *fsRef ) |
{ |
ControlRef control; |
ControlRef rootControl; |
Rect boundsRect; |
Rect r; |
CustomIconWindowInfo *w; |
FSSpec fsSpec; |
static EventHandlerUPP userPaneControlHandlerUPP; // Drawing is done within the user pane event handler draw message |
OSStatus status = -1; |
const EventTypeSpec controlEvents[] = { { kEventClassControl, kEventControlDraw } }; |
require( (fsRef != NULL) || (window != NULL), Bail ); |
w = (CustomIconWindowInfo *) GetWRefCon( window ); // Get our window storage |
SetPortWindowPort( window ); |
GetControlBySigAndID( window, 'UPan', 0, &control ); // Get the user pane control |
if ( userPaneControlHandlerUPP == NULL ) userPaneControlHandlerUPP = NewEventHandlerUPP( UserPaneControlHandler ); |
(void) InstallControlEventHandler( control, userPaneControlHandlerUPP, GetEventTypeCount(controlEvents), controlEvents, w, NULL ); |
GetControlBounds( control, &boundsRect ); // Get the bounds of the User Pane within the window. Because it is embedded, we offset it by its parents bounds. |
GetRootControl( w->window, &rootControl ); |
for ( GetSuperControl( control, &control ) ; control != rootControl ; GetSuperControl( control, &control ) ) |
{ |
GetControlBounds( control, &r ); |
OffsetRect( &boundsRect, r.left, r.top ); |
} |
FSGetCatalogInfo( fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL ); |
if ( w->graphicsImportComponent != NULL ) (void) CloseComponent( w->graphicsImportComponent ); |
status = GetGraphicsImporterForFile( &fsSpec, &w->graphicsImportComponent ); // Get the appropriate Graphics Importer for this file |
status = GraphicsImportGetNaturalBounds( w->graphicsImportComponent, &r ); |
ScaleInRect( &boundsRect, &r, &boundsRect ); |
status = GraphicsImportSetBoundsRect( w->graphicsImportComponent, &boundsRect ); // Set the rectangle to draw to |
Bail: |
return( status ); |
} |
// Event Handler simply draws the image whenever the kEventControlDraw is received for our User pane control. |
static pascal OSStatus UserPaneControlHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ) |
{ |
#pragma unused ( inCallRef ) |
CustomIconWindowInfo *w = (CustomIconWindowInfo*) inUserData; |
UInt32 eventClass = GetEventClass( inEvent ); |
UInt32 eventKind = GetEventKind( inEvent ); |
OSStatus err = eventNotHandledErr; |
switch ( eventClass ) |
{ |
case kEventClassControl: |
if ( eventKind == kEventControlDraw ) |
err = GraphicsImportDraw( w->graphicsImportComponent ); // Graphics Importer draws the image |
break; |
} |
return( err ); |
} |
// Display the destination path selected by the user as a full path. |
static OSStatus DisplayCustomIconDestinationPath( WindowRef window, FSRef *fsRef ) |
{ |
char path[MAXPATHLEN]; |
OSStatus status = -1; |
require( (fsRef != NULL) || (window != NULL), Bail ); |
status = FSRefMakePath( fsRef, (UInt8*)path, MAXPATHLEN ); // Create a full path |
SetControlCString( window, 'STxt', 0, path ); // Display the path within the window |
Bail: |
return( status ); |
} |
OSStatus SetCustomIcon( CustomIconWindowInfo *w ) |
{ |
OSStatus status; |
char path[MAXPATHLEN]; |
Boolean isDirectory; |
FSRef fsRef; |
FSSpec fsSpec; |
GetControlCString( w->window, 'STxt', 0, path ); |
status = FSPathMakeRef( (UInt8*)path, &fsRef, &isDirectory ); // Create an FSRef from the destination path |
require_noerr( status, Bail ); |
status = FSGetCatalogInfo( &fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL ); // Create an FSSpec from the FSRef |
require_noerr( status, Bail ); |
status = CreateCustomIconFromGraphicsComponent( w->graphicsImportComponent, &fsSpec ); // Set the custom icon of fsSpec! |
require_noerr( status, Bail ); |
Bail: |
return( status ); |
} |
// This utility routine will set the custom icon in the file/folder targetSpec with an image described by the Graphics Importer. |
// It does this by creating a PicHandle, then creating an icon from the Picture, and finally saving the custom icon to disk. |
OSErr CreateCustomIconFromGraphicsComponent( GraphicsImportComponent graphicsImportComponent, FSSpec *targetSpec ) |
{ |
PicHandle pictH; |
IconFamilyHandle icnsH = NULL; |
OSErr err = -1; |
icnsH = (IconFamilyHandle) NewHandle( 8 ); // IconFamilyResource.resourceType + IconFamilyResource.resourceSize |
if ( icnsH == NULL ) return( memFullErr ); |
(**icnsH).resourceType = kIconFamilyType; |
(**icnsH).resourceSize = 8; |
pictH = CreatePictHandleFromGraphicsComponent( graphicsImportComponent ); // Create the PicHandle |
if ( pictH == NULL ) goto Bail; |
err = SetIconFamilyData( icnsH, 'PICT', (Handle)pictH ); // Create the icon from the Picture |
require_noerr( err, Bail ); |
SaveCustomIcon( targetSpec, icnsH ); // Save the custom icon to disk |
Bail: |
if ( icnsH != NULL ) DisposeHandle( (Handle) icnsH ); |
if ( pictH != NULL ) KillPicture( pictH ); |
return( err ); |
} |
// Utility routine to create a PicHandle from a Graphics Importer. |
PicHandle CreatePictHandleFromGraphicsComponent( GraphicsImportComponent graphicsImportComponent ) |
{ |
Rect imageRect; |
OSStatus status; |
PicHandle pictH = NULL; |
require( graphicsImportComponent != NULL , Bail ); |
status = GraphicsImportGetBoundsRect( graphicsImportComponent, &imageRect ); // Get the rectangle of the picture |
require_noerr( status, Bail ); |
if ( (imageRect.bottom > imageRect.top) && (imageRect.right > imageRect.left) ) // Sanity check to make sure the picture is not corrupt |
{ |
ClipRect( &imageRect ); |
pictH = OpenPicture( &imageRect ); // Create the PicHandle OpenPicture -> Draw -> ClosePicture |
status = GraphicsImportDraw( graphicsImportComponent ); |
ClosePicture(); |
} |
Bail: |
return( pictH ); |
} |
// Saves the custom icon to disk |
// A file with a custom icon must contain a resource ( 'icns', -16455 ) and have its kHasCustomIcon bit set. |
// A folder with a custom icon must contain a file named "\pIcon\r" within it with a resource ( 'icns', -16455 ) and have the folders kHasCustomIcon bit set. |
OSErr SaveCustomIcon( FSSpec *targetSpec, IconFamilyHandle icnsH ) |
{ |
short refNum; |
OSErr err; |
Handle h; |
CInfoPBRec cpb; |
AliasHandle aliasH; |
err = FSpGetCatInfo( targetSpec, &cpb ); |
require_noerr( err, Bail ); |
if ( cpb.hFileInfo.ioFlAttrib & kioFlAttribDirMask ) // If the target is a directory |
{ |
CInfoPBRec iconCPB; |
FSSpec fsSpec; |
err = FSMakeFSSpec( cpb.dirInfo.ioVRefNum, cpb.dirInfo.ioDrDirID, "\pIcon\r", &fsSpec );// Create an FSSpec to the "\pIcon\r" file |
HCreateResFile( fsSpec.vRefNum, fsSpec.parID, "\pIcon\r" ); // Create the resource forked file "\pIcon\r" |
refNum = FSpOpenResFile( &fsSpec, fsRdWrPerm ); // Open it with write permissions |
(void) FSpGetCatInfo( &fsSpec, &iconCPB ); |
iconCPB.hFileInfo.ioFlFndrInfo.fdFlags |= kIsInvisible; // Set the kIsInvisible flag of the "Icon\r" file |
iconCPB.hFileInfo.ioDirID = iconCPB.dirInfo.ioDrParID; |
(void) PBSetCatInfoSync( &iconCPB ); |
} |
else |
{ |
HCreateResFile( targetSpec->vRefNum, targetSpec->parID, targetSpec->name ); // Create the resource forked file "\pIcon\r" |
refNum = FSpOpenResFile( targetSpec, fsCurPerm ); // Open it with write permissions |
} |
if ( refNum == -1 ) goto Bail; |
UseResFile( refNum ); |
h = Get1Resource( 'icns', kCustomIconResource ); |
if ( h != NULL ) // If it already has a custom icon |
{ |
RemoveResource( h ); // Remove the existing custom icon |
DisposeHandle( h ); |
} |
AddResource( (Handle) icnsH, 'icns', kCustomIconResource, nil ); // Create a new resource ( 'icns', -16455 ) |
err = ResError(); if ( err != noErr ) DebugStr("\p AddResource Failed" ); |
WriteResource( (Handle) icnsH ); // Save the 'icns' resource |
err = ResError(); if ( err != noErr ) DebugStr("\p WriteResource Failed" ); |
DetachResource( (Handle) icnsH ); |
err = ResError(); if ( err != noErr ) DebugStr("\p DetachResource Failed" ); |
CloseResFile( refNum ); // Close the resource fork |
if ( targetSpec->parID == fsRtParID ) // As of Mac OS X 10.4, the disk icon requires it be stored in ".VolumeIcon.icns" |
{ |
FSSpec volumeIconSpec; |
long ioCount; |
refNum = 0; |
err = FSMakeFSSpec( targetSpec->vRefNum, fsRtDirID, "\p.VolumeIcon.icns", &volumeIconSpec ); // "." files are always invisible |
if ( err == noErr ) (void) FSpDelete( &volumeIconSpec ); |
err = FSpCreate( &volumeIconSpec, 0, 0, smSystemScript ); if ( err != noErr ) goto BailOnTenFour; |
err = FSpOpenDF( &volumeIconSpec, fsWrPerm, &refNum ); if ( err != noErr ) goto BailOnTenFour; |
ioCount = GetHandleSize( (Handle) icnsH ); |
err = FSWrite( refNum, &ioCount, *icnsH ); if ( err != noErr ) goto BailOnTenFour; // Write the icns file |
BailOnTenFour: |
if ( refNum != 0 ) FSClose( refNum ); |
} |
cpb.hFileInfo.ioFlFndrInfo.fdFlags |= kHasCustomIcon; // Set the kHasCustomIcon flag |
cpb.hFileInfo.ioDirID = cpb.dirInfo.ioDrParID; |
err = PBSetCatInfoSync( &cpb ); // Set the Custom Icon bit on disk |
err = NewAliasMinimal( targetSpec, &aliasH ); // Create an alias to our target |
require_noerr( err, Bail ); |
err = SendFinderAppleEvent( aliasH, kAESync ); // Send the Finder a kAESync AppleEvent to force it to update the icon immediately |
DisposeHandle( (Handle) aliasH ); |
Bail: |
return( err ); |
} |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-08-01