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: This source demonstrates how to use NSCursor from a Carbon based application. |
We provide some wrapper functions CreateCocoaCursor, ReleaseCocoaCursor, and SetCocoaCursor |
to abstract the underlying Cocoa calls to manage the cursor. NSCursor provides support for |
large cursors, alpha, and hot spots. |
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 © 2006 Apple Computer, Inc., All Rights Reserved |
*/ |
#include <Carbon/Carbon.h> |
#include <Cocoa/Cocoa.h> |
#include "NSCursorWrappers.h" |
static OSStatus DoNewWindow( WindowRef *outWindow ); |
static OSStatus CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ); |
static OSStatus HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl ); |
static OSStatus CursorEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData ); |
static OSStatus MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData ); |
CursorRef SetCursorFromCFURL( CFURLRef urlToImage ); |
IBNibRef gMainNibRef; |
struct CustomCursorStruct { |
CursorRef *cursor; |
}; |
typedef struct CustomCursorStruct CustomCursorStruct; |
int main( int argc, char *argv[] ) |
{ |
OSStatus status; |
const EventTypeSpec commandProcessEvents[] = { { kEventClassCommand, kEventCommandProcess } }; |
status = CreateNibReference( CFSTR("main"), &gMainNibRef ); |
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( gMainNibRef, CFSTR("MenuBar") ); |
require_noerr( status, CantSetMenuBar ); |
InstallApplicationEventHandler( NewEventHandlerUPP(CommandProcessEventHandler), GetEventTypeCount(commandProcessEvents), commandProcessEvents, NULL, NULL ); |
NSApplicationLoad(); // Needed for Carbon based applications which call into Cocoa |
// As of Mac OS X 10.4 RunApplicationEventLoop handles the autorelease pool for us. But since we are invoking Obj-C Cocoa code from within |
// routines called by DoNewWindow, and we haven't yet called RAEL, we need to allocate and release the autorelease pool ourselves. |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
DoNewWindow( NULL ); |
[pool release]; |
RunApplicationEventLoop(); // Call the event loop |
CantSetMenuBar: |
CantGetNibRef: |
return( 0 ); |
} |
//------------------------------------------------------------------------------ |
// GetPasteboardFromDragEvent |
//------------------------------------------------------------------------------ |
// |
static PasteboardRef GetPasteboardFromDragEvent( |
EventRef inEvent ) |
{ |
DragRef drag; |
PasteboardRef pasteboard = NULL; |
// get the drag pasteboard, if it exists |
require_noerr( GetEventParameter( inEvent, kEventParamDragRef, typeDragRef, NULL, sizeof( DragRef ), |
NULL, &drag ), CantGetDragRefFromEvent ); |
GetDragPasteboard( drag, &pasteboard ); |
CantGetDragRefFromEvent: |
return pasteboard; |
} |
// Creates a CGImageRef from a URL. Caller is responsible for disposing of it. |
CGImageRef CGImageCreateFromURL( CFURLRef urlToImage ) |
{ |
CGImageSourceRef imageSource = CGImageSourceCreateWithURL( urlToImage, NULL ); |
CGImageRef theImage = nil; |
require( imageSource != nil, CantCreateImageSource ); |
theImage = CGImageSourceCreateImageAtIndex( imageSource, 0, NULL ); |
require( theImage != nil, CantGetImage ); |
CFRelease( imageSource ); |
CantCreateImageSource: |
CantGetImage: |
return( theImage ); |
} |
//------------------------------------------------------------------------------ |
// CreateFileURLFromPasteboard |
//------------------------------------------------------------------------------ |
// |
static CFURLRef CreateFileURLFromPasteboard( |
PasteboardRef inPasteboard, |
CFIndex inIndex ) |
{ |
PasteboardItemID item; |
CFDataRef fileURLData; |
CFURLRef fileURL = NULL; |
LSItemInfoRecord info; |
CFStringRef uti = NULL; |
bool isSupported = false; |
require_noerr( PasteboardGetItemIdentifier( inPasteboard, inIndex, &item ), CantGetPasteboardIdentifier ); |
require_noerr_quiet( PasteboardCopyItemFlavorData( inPasteboard, item, kUTTypeFileURL, &fileURLData ), |
CantCopyFileURLFromPasteboard ); |
// create the file URL with the dragged data |
fileURL = CFURLCreateWithBytes( kCFAllocatorDefault, CFDataGetBytePtr( fileURLData ), CFDataGetLength( fileURLData ), |
kCFStringEncodingMacRoman, NULL ); |
// get the UTI for the dragged file |
require_noerr( LSCopyItemInfoForURL( fileURL, kLSRequestExtension | kLSRequestTypeCreator, &info ), CantCopyItemInfo ); |
if ( info.extension != NULL ) |
{ |
uti = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, info.extension, kUTTypeData ); |
CFRelease( info.extension ); |
} |
if ( uti == NULL ) |
{ |
CFStringRef typeString = UTCreateStringForOSType( info.filetype ); |
if ( typeString != NULL ) |
{ |
uti = UTTypeCreatePreferredIdentifierForTag( kUTTagClassOSType, typeString, kUTTypeData ); |
CFRelease( typeString ); |
} |
} |
require( uti != NULL, CantCreateFileUTI ); |
// verify we're dealing with a file that ImageIO can understand |
{ |
CFArrayRef supportedTypes = CGImageSourceCopyTypeIdentifiers(); |
CFIndex i, typeCount = CFArrayGetCount( supportedTypes ); |
for( i = 0; i < typeCount; i++ ) |
{ |
if ( UTTypeConformsTo( uti, (CFStringRef)CFArrayGetValueAtIndex( supportedTypes, i ) ) ) |
{ |
isSupported = true; |
break; |
} |
} |
CFRelease( supportedTypes ); |
} |
CFRelease( uti ); |
CantCreateFileUTI: |
CantCopyItemInfo: |
if ( !isSupported ) |
{ |
CFRelease( fileURL ); |
fileURL = NULL; |
} |
CFRelease( fileURLData ); |
CantCopyFileURLFromPasteboard: |
CantGetPasteboardIdentifier: |
return fileURL; |
} |
//------------------------------------------------------------------------------ |
// CreateFirstFileURLFromDragEvent |
//------------------------------------------------------------------------------ |
// |
static CFURLRef CreateFirstFileURLFromDragEvent( |
EventRef inEvent ) |
{ |
PasteboardRef pasteboard; |
CFURLRef fileURL = NULL; |
pasteboard = GetPasteboardFromDragEvent( inEvent ); |
require( pasteboard != NULL, CantGetDragPasteboard ); |
fileURL = CreateFileURLFromPasteboard( pasteboard, 1 ); |
CantGetDragPasteboard: |
return fileURL; |
} |
//------------------------------------------------------------------------------ |
// ImageViewDragEnter |
//------------------------------------------------------------------------------ |
// |
static OSStatus ImageViewDragEnter( |
EventRef inEvent) |
{ |
CFURLRef fileURL = CreateFirstFileURLFromDragEvent( inEvent ); |
Boolean wouldAccept = (fileURL != NULL); // accept the drag if it contains an image file URL |
// let the HIView drag apparatus know that we want to continue receiving drag messages |
verify_noerr( SetEventParameter( inEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof( Boolean ), &wouldAccept ) ); |
if ( fileURL != NULL ) |
CFRelease( fileURL ); |
return noErr; |
} |
OSStatus ImageViewDragReceive( EventRef event, CustomCursorStruct *customCursor, HIViewRef imageView ) |
{ |
PasteboardRef pasteboard; |
CFURLRef fileURL = NULL; |
CursorRef newCursor; |
pasteboard = GetPasteboardFromDragEvent( event ); |
require( pasteboard != NULL, CantGetDragPasteboard ); |
fileURL = CreateFirstFileURLFromDragEvent( event ); |
if ( fileURL ) |
{ |
CGImageRef theImage; |
newCursor = SetCursorFromCFURL( fileURL ); |
if (newCursor) |
{ |
ReleaseCocoaCursor( customCursor->cursor ); |
customCursor->cursor = newCursor; |
SetCocoaCursor( newCursor ); |
theImage = CGImageCreateFromURL( fileURL ); |
HIImageViewSetImage( imageView, theImage ); |
if ( theImage ) |
CFRelease( theImage ); |
} |
CFRelease( fileURL ); |
} |
CantGetDragPasteboard: |
return noErr; |
} |
CursorRef SetCursorFromCFURL( CFURLRef urlToImage ) |
{ |
CGImageRef cgImageRef = CGImageCreateFromURL( urlToImage ); |
CursorRef newCursor = NULL; |
require( cgImageRef != NULL, CantCreateImage ); |
newCursor = CreateCocoaCursor( cgImageRef, 48, 16 ); |
if ( cgImageRef ) |
CFRelease( cgImageRef ); |
CantCreateImage: |
return( (CursorRef)newCursor ); |
} |
static 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; |
} |
return( status ); |
} |
// The tracking handler to manage the cursor as it rolls over our HIImageView. |
static OSStatus HIImageViewTrackingHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData ) |
{ |
CustomCursorStruct *customCursor; |
OSStatus status = eventNotHandledErr; |
UInt32 eventKind = GetEventKind( inEvent ); |
UInt32 eventClass = GetEventClass( inEvent ); |
status = GetControlProperty( (ControlRef) inUserData, 'Curs', 'Cstm', sizeof(CustomCursorStruct*), NULL, &customCursor ); // For convenience we store the pointer to the cursor |
require_noerr( status, Bail ); |
switch ( eventClass ) |
{ |
case kEventClassControl: |
switch ( eventKind ) |
{ |
case kEventControlTrackingAreaEntered: |
SetCocoaCursor( customCursor->cursor ); |
break; |
case kEventControlTrackingAreaExited: |
SetThemeCursor( kThemeArrowCursor ); |
break; |
case kEventControlDispose: |
ReleaseCocoaCursor( customCursor->cursor ); |
DisposePtr( (Ptr) customCursor ); |
break; |
case kEventControlDragEnter: |
status = ImageViewDragEnter( inEvent ); |
break; |
case kEventControlDragReceive: |
status = ImageViewDragReceive( inEvent, customCursor, (ControlRef) inUserData ); |
break; |
} |
break; |
} |
Bail: |
return( status ); |
} |
static OSStatus DoNewWindow( WindowRef *outWindow ) |
{ |
OSStatus status; |
CustomCursorStruct *customCursor; |
static EventHandlerUPP windowEventHandlerUPP; |
const EventTypeSpec windowEvents[] = { { kEventClassCommand, kEventCommandProcess } }; |
WindowRef window = NULL; |
// Create a window. "MainWindow" is the name of the window object. This name is set in InterfaceBuilder when the nib is created. |
status = CreateWindowFromNib( gMainNibRef, CFSTR("MainWindow"), &window ); |
require_noerr( status, CantCreateWindow ); |
if ( windowEventHandlerUPP == NULL ) windowEventHandlerUPP = NewEventHandlerUPP( MainWindowEventHandler ); |
status = InstallWindowEventHandler( window, windowEventHandlerUPP, GetEventTypeCount(windowEvents), windowEvents, window, NULL ); |
require_noerr( status, CantInstallWindowEventHandler ); |
{ |
HIViewRef hiImageView; |
HIViewTrackingAreaRef dummyTrackingArea; // The tracking area is disposed automatically with the view, so we don't need to keep it in this case |
CGImageRef cgImageRef = NULL; |
const EventTypeSpec ourEvents[] = { { kEventClassControl, kEventControlTrackingAreaEntered }, |
{ kEventClassControl, kEventControlTrackingAreaExited }, |
{ kEventClassControl, kEventControlDispose }, |
{ kEventClassControl, kEventControlDragReceive }, |
{ kEventClassControl, kEventControlDragEnter }, |
}; |
CFURLRef cfURL = CFBundleCopyResourcesDirectoryURL( CFBundleGetMainBundle() ); |
CFURLRef fullUrlRef = CFURLCreateCopyAppendingPathComponent( NULL, cfURL, CFSTR("BlueArrow.png"), false ); |
CFRelease( cfURL ); |
cgImageRef = CGImageCreateFromURL( fullUrlRef ); |
status = HIViewFindBySigAndID( HIViewGetRoot(window), 'HIiv', 0, &hiImageView ); require_noerr( status, Bail ); // Get the HIImageView |
status = HIImageViewSetImage( hiImageView, cgImageRef ); require_noerr( status, Bail ); // Set the image of the HIImageView |
status = HIImageViewSetOpaque( hiImageView, false ); require_noerr( status, Bail ); // Image can have transparency |
// status = HIImageViewSetScaleToFit( hiImageView, true ); require_noerr( status, Bail ); // Don't scale the image, it's embeded in an HIScrollView so scaling doesn't make much sense |
status = HIViewSetVisible( hiImageView, true ); require_noerr( status, Bail ); // Make it visible |
customCursor = (CustomCursorStruct*) NewPtrClear( sizeof(CustomCursorStruct) ); |
SetControlProperty( hiImageView,'Curs','Cstm', sizeof(CustomCursorStruct*), &customCursor ); // For convenience we store the pointer to the cursor in the window's RefCon |
status = HIViewNewTrackingArea( hiImageView, NULL, 0, &dummyTrackingArea); |
customCursor->cursor = SetCursorFromCFURL( fullUrlRef ); |
CFRelease( fullUrlRef ); |
status = InstallControlEventHandler( hiImageView, HIImageViewTrackingHandler, GetEventTypeCount(ourEvents), ourEvents, hiImageView, NULL ); |
status = SetAutomaticControlDragTrackingEnabledForWindow( window, true ); |
status = SetControlDragTrackingEnabled( hiImageView, true ); |
Bail: |
if ( cgImageRef != NULL ) CFRelease( cgImageRef ); else status = -1; |
} |
ShowWindow( window ); |
CantInstallWindowEventHandler: |
CantCreateWindow: |
if ( outWindow != NULL ) *outWindow = window; |
return( status ); |
} |
static OSStatus MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData ) |
{ |
#pragma unused ( inCallRef ) |
HICommand command; |
HIViewRef myButton; |
Boolean isButtonDefault; |
WindowRef window = (WindowRef) inUserData; |
OSStatus status = eventNotHandledErr; |
UInt32 eventKind = GetEventKind( inEvent ); |
UInt32 eventClass = GetEventClass( inEvent ); |
switch ( eventClass ) |
{ |
case kEventClassCommand: |
if ( eventKind == kEventCommandProcess ) |
{ |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
if ( command.commandID == 'Curs' ) // Our button was clicked |
{ |
status = HIViewFindBySigAndID( HIViewGetRoot(window), 'Curs', 0, &myButton ); require_noerr( status, Bail ); |
status = GetControlData( myButton, kControlEntireControl, kControlPushButtonDefaultTag, sizeof(Boolean), &isButtonDefault, NULL ); require_noerr( status, Bail ); |
if ( isButtonDefault ) |
{ |
// If the button was default, show the cursor and make the button non-default |
ShowCocoaCursor(); |
SetControlTitleWithCFString( myButton, CFSTR( "Hide Cursor" )); |
SetWindowDefaultButton( window, NULL ); |
} |
else |
{ |
// We hit the hide cursor button. Hide the cursor and make the button default so that the user can hit return |
// to show the cursor again. This is only useful as sample code, it's not good UI to hide the cursor generally. |
HideCocoaCursor(); |
SetControlTitleWithCFString( myButton, CFSTR( "Show Cursor" )); |
SetWindowDefaultButton( window, myButton ); |
} |
} |
} |
break; |
} |
Bail: |
return( status ); |
} |
static OSStatus HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl ) |
{ |
HIViewID hiViewID = { signature, id }; |
return( HIViewFindByID( inStartView, hiViewID, outControl ) ); |
} |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-01-12