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 of CoreTextTest |
* |
* Contains: A small Carbon application showing how to use the new CoreText APIs. |
* |
* Note: The project is set up so that the DEBUG macro is set to one when the "Development" |
* build style is chosen and not at all when the "Deployment" build style is chosen. |
* Thus, all the require asserts "fire" only in "Development". |
* |
* Version: 1.0 |
* |
* Created: 5/8/06 |
* |
* 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: Copyright © 2006 Apple Computer, Inc, All Rights Reserved |
*/ |
//**************************************************** |
#pragma mark * compilation directives * |
//**************************************************** |
#pragma mark - |
#pragma mark * includes & imports * |
#include <Carbon/Carbon.h> |
//**************************************************** |
#pragma mark - |
#pragma mark * typedef's, struct's, enums, defines, etc. * |
//**************************************************** |
#pragma mark - |
#pragma mark * local (static) function prototypes * |
// Functions found in almost all Sample Codes: |
static pascal OSErr Handle_OpenApplication(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon); |
static pascal OSErr Handle_ReopenApplication(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon); |
static pascal OSErr Handle_OpenDocuments(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon); |
static pascal OSErr Handle_PrintDocuments(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon); |
static void Install_AppleEventHandlers(void); |
static pascal OSStatus Handle_CommandUpdateStatus(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); |
static pascal OSStatus Handle_CommandProcess(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); |
static void Do_Preferences(void); |
static OSStatus Do_CleanUp(void); |
// Functions specific to this Sample Code: |
static OSStatus InitializeSampleCodeGlobals(void); |
static OSStatus Do_NewWindow(void); |
static OSStatus HIViewCoreTextDraw( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ); |
static OSStatus AddingStyleAttributes( CFAttributedStringRef inAttrString, CFMutableAttributedStringRef * outMutAttrString ); |
static OSStatus AddingOtherStyleAttributes( CFAttributedStringRef inAttrString, CFMutableAttributedStringRef * outMutAttrString ); |
//**************************************************** |
#pragma mark - |
#pragma mark * exported globals * |
//**************************************************** |
#pragma mark - |
#pragma mark * local (static) globals * |
static IBNibRef gIBNibRef; |
static UInt32 gWindowCount = 0; |
static UniChar * gUnicodeText1 = NULL; |
static SInt32 gUnicodeTextLength1 = 0; |
static UniChar * gUnicodeText2 = NULL; |
static SInt32 gUnicodeTextLength2 = 0; |
//**************************************************** |
#pragma mark - |
#pragma mark * exported function implementations * |
/***************************************************** |
* |
* main (argc, argv) |
* |
* Purpose: main program entry point |
* |
* Inputs: argc - the number of elements in the argv array |
* argv - an array of pointers to the parameters to this application |
* |
* Returns: int - error code (0 == no error) |
*/ |
int main(int argc, char* argv[]) |
{ |
OSStatus status; |
// Can we run this particular demo application? |
long response; |
status = Gestalt(gestaltSystemVersion, &response); |
Boolean ok = ((status == noErr) && (response >= 0x00001050)); |
if (!ok) |
{ |
DialogRef theAlert; |
CreateStandardAlert(kAlertStopAlert, CFSTR("Mac OS X 10.5 (minimum) is required for this application"), NULL, NULL, &theAlert); |
RunStandardAlert(theAlert, NULL, NULL); |
ExitToShell(); |
} |
// 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"), &gIBNibRef); |
require_noerr(status, CreateNibReference); |
// 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(gIBNibRef, CFSTR("MenuBar")); |
require_noerr(status, SetMenuBarFromNib); |
// Enabling Preferences menu item |
EnableMenuCommand(NULL, kHICommandPreferences); |
// Let's react to User's commands. |
Install_AppleEventHandlers(); |
EventTypeSpec eventTypeCP = {kEventClassCommand, kEventCommandProcess}; |
status = InstallEventHandler(GetApplicationEventTarget(), Handle_CommandProcess, 1, &eventTypeCP, NULL, NULL); |
require_noerr(status, InstallEventHandler); |
EventTypeSpec eventTypeCUS = {kEventClassCommand, kEventCommandUpdateStatus}; |
status = InstallEventHandler(GetApplicationEventTarget(), Handle_CommandUpdateStatus, 1, &eventTypeCUS, NULL, NULL); |
require_noerr(status, InstallEventHandler); |
status = InitializeSampleCodeGlobals(); |
require_noerr(status, InitializeSampleCodeGlobals); |
// Call the event loop |
RunApplicationEventLoop(); |
InitializeSampleCodeGlobals: |
InstallEventHandler: |
SetMenuBarFromNib: |
CreateNibReference: |
return status; |
} // main |
/*****************************************************/ |
#pragma mark - |
#pragma mark * local (static) function implementations * |
#pragma mark * AppleEvent Handlers * |
/***************************************************** |
* |
* Handle_OpenApplication(inAppleEvent, reply, inHandlerRefcon) |
* |
* Purpose: AppleEvent handler for the kAEOpenApplication event |
* |
* Inputs: inAppleEvent - the Apple event |
* reply - our reply to the Apple event |
* inHandlerRefcon - refcon passed to AEInstallEventHandler when this hander was installed |
* |
* Returns: OSErr - error code (0 == no error) |
*/ |
static pascal OSErr Handle_OpenApplication(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon) |
{ |
// we create 2 cascading new windows with different content |
Do_NewWindow(); |
Do_NewWindow(); |
return noErr; |
} // Handle_OpenApplication |
/***************************************************** |
* |
* Handle_ReopenApplication(inAppleEvent, reply, inHandlerRefcon) |
* |
* Purpose: AppleEvent handler for the kAEReopenApplication event |
* |
* Inputs: inAppleEvent - the Apple event |
* reply - our reply to the Apple event |
* inHandlerRefcon - refcon passed to AEInstallEventHandler when this hander was installed |
* |
* Returns: OSErr - error code (0 == no error) |
*/ |
static pascal OSErr Handle_ReopenApplication(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon) |
{ |
// We were already running but with no windows so we create an empty one. |
WindowRef theWindow = GetFrontWindowOfClass(kDocumentWindowClass, true); |
if (theWindow == NULL) |
return Do_NewWindow(); |
else |
return noErr; |
} // Handle_ReopenApplication |
/***************************************************** |
* |
* Handle_OpenDocuments(inAppleEvent, reply, inHandlerRefcon) |
* |
* Purpose: AppleEvent handler for the kAEOpenDocuments event |
* |
* Inputs: inAppleEvent - the Apple event |
* reply - our reply to the Apple event |
* inHandlerRefcon - refcon passed to AEInstallEventHandler when this hander was installed |
* |
* Returns: OSErr - error code (0 == no error) |
*/ |
static pascal OSErr Handle_OpenDocuments(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon) |
{ |
return errAEEventNotHandled; |
} // Handle_OpenDocuments |
/***************************************************** |
* |
* Handle_PrintDocuments(inAppleEvent, reply, inHandlerRefcon) |
* |
* Purpose: AppleEvent handler for the kAEPrintDocuments event |
* |
* Inputs: inAppleEvent - the Apple event |
* reply - our reply to the Apple event |
* inHandlerRefcon - refcon passed to AEInstallEventHandler when this hander was installed |
* |
* Returns: OSErr - error code (0 == no error) |
*/ |
static pascal OSErr Handle_PrintDocuments(const AppleEvent *inAppleEvent, AppleEvent *outAppleEvent, long inHandlerRefcon) |
{ |
return errAEEventNotHandled; |
} // Handle_PrintDocuments |
/***************************************************** |
* |
* Install_AppleEventHandlers(void) |
* |
* Purpose: installs the AppleEvent handlers |
* |
* Inputs: none |
* |
* Returns: none |
*/ |
static void Install_AppleEventHandlers(void) |
{ |
OSErr status; |
status = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, Handle_OpenApplication, 0, false); |
require_noerr(status, CantInstallAppleEventHandlerOpenAppl); |
status = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, Handle_ReopenApplication, 0, false); |
require_noerr(status, CantInstallAppleEventHandlerReOpenAppl); |
status = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, Handle_OpenDocuments, 0, false); |
require_noerr(status, CantInstallAppleEventHandlerOpenDocs); |
status = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, Handle_PrintDocuments, 0, false); |
require_noerr(status, CantInstallAppleEventHandlerPrintDocs); |
// Note: Since RunApplicationEventLoop installs a Quit AE Handler, there is no need to do it here. |
CantInstallAppleEventHandlerOpenAppl: |
CantInstallAppleEventHandlerReOpenAppl: |
CantInstallAppleEventHandlerOpenDocs: |
CantInstallAppleEventHandlerPrintDocs: |
return; |
} // Install_AppleEventHandlers |
#pragma mark - |
#pragma mark * CarbonEvent Handlers * |
/***************************************************** |
* |
* Handle_CommandUpdateStatus(inHandlerCallRef, inEvent, inUserData) |
* |
* Purpose: called to update status of the commands, enabling or disabling the menu items |
* |
* Inputs: inHandlerCallRef - reference to the current handler call chain |
* inEvent - the event |
* inUserData - app-specified data you passed in the call to InstallEventHandler |
* |
* Returns: OSStatus - noErr indicates the event was handled |
* eventNotHandledErr indicates the event was not handled and the Toolbox should take over |
*/ |
static pascal OSStatus Handle_CommandUpdateStatus(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) |
{ |
OSStatus status = eventNotHandledErr; |
HICommand aCommand; |
GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &aCommand); |
WindowRef aWindowRef = GetFrontWindowOfClass(kDocumentWindowClass, true); |
if (aWindowRef == NULL) |
{ |
switch (aCommand.commandID) |
{ |
case 'SngS': |
case 'DblS': |
case kHICommandClose: |
DisableMenuItem(aCommand.menu.menuRef, aCommand.menu.menuItemIndex); |
break; |
} |
} |
else |
{ |
switch (aCommand.commandID) |
{ |
case 'SngS': |
case 'DblS': |
{ |
Boolean singleSpace = true; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'SBSP', sizeof(singleSpace), NULL, &singleSpace); |
if (status != noErr) |
DisableMenuItem(aCommand.menu.menuRef, aCommand.menu.menuItemIndex); |
else |
{ |
EnableMenuItem(aCommand.menu.menuRef, aCommand.menu.menuItemIndex); |
CheckMenuItem( |
aCommand.menu.menuRef, aCommand.menu.menuItemIndex, |
(singleSpace && (aCommand.commandID == 'SngS')) || ((!singleSpace) && (aCommand.commandID == 'DblS')) |
); |
} |
break; |
} |
case kHICommandClose: |
EnableMenuItem(aCommand.menu.menuRef, aCommand.menu.menuItemIndex); |
break; |
} |
} |
return status; |
} // Handle_CommandUpdateStatus |
/***************************************************** |
* |
* Handle_CommandProcess(inHandlerCallRef, inEvent, inUserData) |
* |
* Purpose: called to process commands from Carbon events |
* |
* Inputs: inHandlerCallRef - reference to the current handler call chain |
* inEvent - the event |
* inUserData - app-specified data you passed in the call to InstallEventHandler |
* |
* Returns: OSStatus - noErr indicates the event was handled |
* eventNotHandledErr indicates the event was not handled and the Toolbox should take over |
*/ |
static pascal OSStatus Handle_CommandProcess(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) |
{ |
OSStatus status = eventNotHandledErr; |
HICommand aCommand; |
GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &aCommand); |
switch (aCommand.commandID) |
{ |
case kHICommandPreferences: |
Do_Preferences(); |
break; |
case kHICommandNew: |
status = Do_NewWindow(); |
break; |
case kHICommandQuit: |
status = Do_CleanUp(); |
break; |
case 'SngS': |
case 'DblS': |
{ |
WindowRef aWindowRef = GetFrontWindowOfClass(kDocumentWindowClass, true); |
require(aWindowRef != NULL, GetFrontWindowOfClass); |
Boolean singleSpace; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'SBSP', sizeof(singleSpace), NULL, &singleSpace); |
// we change from single-space to double-space (or vice versa) for the blue gray text |
// we just change the window property and tell the HIView to update its contents. |
if (status == noErr) |
{ |
singleSpace = !singleSpace; |
status = SetWindowProperty(aWindowRef, 'CTTT', 'SBSP', sizeof(singleSpace), &singleSpace); |
require_noerr(status, SetWindowProperty); |
HIViewID baseHIViewID = { 'BLNK', 100 }; |
HIViewRef baseView; |
HIViewFindByID(HIViewGetRoot(aWindowRef), baseHIViewID, &baseView); |
require(baseView != NULL, HIViewFindByID); |
HIViewSetNeedsDisplay(baseView, true); |
} |
break; |
} |
} |
HIViewFindByID: |
SetWindowProperty: |
GetFrontWindowOfClass: |
return status; |
} // Handle_CommandProcess |
/***************************************************** |
* |
* Handle_WindowClosing(inHandlerCallRef, inEvent, inUserData) |
* |
* Purpose: called when the window is closing, time to dispose of the properties |
* |
* Inputs: inHandlerCallRef - reference to the current handler call chain |
* inEvent - the event |
* inUserData - app-specified data you passed in the call to InstallEventHandler |
* |
* Returns: OSStatus - noErr indicates the event was handled |
* eventNotHandledErr indicates the event was not handled and the Toolbox should take over |
*/ |
static pascal OSStatus Handle_WindowClosing(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) |
{ |
OSStatus status = eventNotHandledErr; |
WindowRef aWindowRef = (WindowRef)inUserData; |
// cleaning up the storage associated with the window |
CFAttributedStringRef attrString; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CFA1', sizeof(attrString), NULL, &attrString); |
if ((status == noErr) && (attrString != NULL)) CFRelease(attrString); |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CFA2', sizeof(attrString), NULL, &attrString); |
if ((status == noErr) && (attrString != NULL)) CFRelease(attrString); |
CTFramesetterRef setter; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CTF1', sizeof(setter), NULL, &setter); |
if ((status == noErr) && (setter != NULL)) CFRelease(setter); |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CTF2', sizeof(setter), NULL, &setter); |
if ((status == noErr) && (setter != NULL)) CFRelease(setter); |
return status; |
} // Handle_WindowClosing |
#pragma mark - |
#pragma mark * Windows * |
/***************************************************** |
* |
* Do_Preferences(void) |
* |
* Purpose: routine to display dialog to set our applications preferences |
* |
* Inputs: none |
* |
* Returns: none |
*/ |
static void Do_Preferences(void) |
{ |
DialogRef theAlert; |
CreateStandardAlert(kAlertStopAlert, CFSTR("No Preferences yet!"), NULL, NULL, &theAlert); |
RunStandardAlert(theAlert, NULL, NULL); |
} // Do_Preferences |
/***************************************************** |
* |
* Do_CleanUp(void) |
* |
* Purpose: called when we get the quit event, closes all the windows. |
* |
* Inputs: none |
* |
* Returns: OSStatus - eventNotHandledErr indicates that the quit process can continue |
*/ |
static OSStatus Do_CleanUp(void) |
{ |
WindowRef windowToDispose, aWindowRef = GetFrontWindowOfClass(kDocumentWindowClass, true); |
for ( ; aWindowRef != NULL; ) |
{ |
windowToDispose = aWindowRef; |
aWindowRef = GetNextWindowOfClass(aWindowRef, kDocumentWindowClass, true); |
DisposeWindow(windowToDispose); |
} |
return eventNotHandledErr; |
} // Do_CleanUp |
#pragma mark - |
#pragma mark * Functions specific to this Sample Code * |
//-------------------------------------------------------------------------------------------- |
static OSStatus InitializeSampleCodeGlobals(void) |
{ |
OSStatus status; |
Boolean ok; |
// we will add the following block of text to the text files we read to explain to the use what's going on |
CFStringRef theHeader = CFSTR("The text in black is rendered using CTFrameDraw, the text in blue gray is rendered using CTLineDraw. The latter can also be rendered single-spaced or double-spaced using the Options menu.\r\r"); |
CFIndex theLength = CFStringGetLength(theHeader); |
UniChar * headerText = malloc(2 * theLength); |
CFStringGetCharacters(theHeader, CFRangeMake(0, theLength), headerText); |
// Get the Unicode Text from our resource file |
CFBundleRef mainBundle = CFBundleGetMainBundle(); |
require_action( mainBundle != NULL, CFBundleGetMainBundle, status = coreFoundationUnknownErr ); |
// first text file |
CFURLRef cfurl = CFBundleCopyResourceURL(mainBundle, CFSTR("WorldText Sample File"), CFSTR("utxt"), NULL); |
require_action( cfurl != NULL, CFBundleCopyResourceURL, status = coreFoundationUnknownErr ); |
FSRef fsRef; |
ok = CFURLGetFSRef(cfurl, &fsRef); |
CFRelease(cfurl); |
require_action( ok, CFURLGetFSRef, status = coreFoundationUnknownErr ); |
SInt16 refNum; |
status = FSOpenFork(&fsRef, 0, NULL, fsRdPerm, &refNum); |
require_noerr( status, FSOpenFork ); |
SInt64 forkSize; |
status = FSGetForkSize(refNum, &forkSize); |
require_noerr( status, FSGetForkSize ); |
gUnicodeTextLength1 = forkSize; // we'll never be that big... |
gUnicodeText1 = malloc(gUnicodeTextLength1 + 2 * theLength); |
require_action( gUnicodeText1 != NULL, malloc, status = memFullErr ); |
memcpy(gUnicodeText1, headerText, 2 * theLength); |
status = FSReadFork(refNum, fsFromStart, 0, gUnicodeTextLength1, &gUnicodeText1[theLength], NULL); |
require_noerr( status, FSReadFork ); |
FSCloseFork(refNum); |
// second test file, bis repetita |
cfurl = CFBundleCopyResourceURL(mainBundle, CFSTR("US Text Sample File"), CFSTR("utxt"), NULL); |
require_action( cfurl != NULL, CFBundleCopyResourceURL, status = coreFoundationUnknownErr ); |
ok = CFURLGetFSRef(cfurl, &fsRef); |
CFRelease(cfurl); |
require_action( ok, CFURLGetFSRef, status = coreFoundationUnknownErr ); |
status = FSOpenFork(&fsRef, 0, NULL, fsRdPerm, &refNum); |
require_noerr( status, FSOpenFork ); |
status = FSGetForkSize(refNum, &forkSize); |
require_noerr( status, FSGetForkSize ); |
gUnicodeTextLength2 = forkSize; // we'll never be that big... |
gUnicodeText2 = malloc(gUnicodeTextLength2 + 2 * theLength); |
require_action( gUnicodeText2 != NULL, malloc, status = memFullErr ); |
memcpy(gUnicodeText2, headerText, 2 * theLength); |
status = FSReadFork(refNum, fsFromStart, 0, gUnicodeTextLength2, &gUnicodeText2[theLength], NULL); |
require_noerr( status, FSReadFork ); |
FSCloseFork(refNum); |
FSReadFork: |
malloc: |
FSGetForkSize: |
FSOpenFork: |
CFURLGetFSRef: |
CFBundleCopyResourceURL: |
CFBundleGetMainBundle: |
return status; |
} // InitializeSampleCodeGlobals |
/***************************************************** |
* |
* Do_NewWindow() |
* |
* Purpose: called to create a new window, each other window will be created from APIs and the other one from Interface Builder |
* |
* Notes: called by Handle_CommandProcess() ("File/New" menu item), Handle_OpenApplication(). Handle_ReopenApplication() |
* |
* Inputs: none |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
static OSStatus Do_NewWindow(void) |
{ |
OSStatus status; |
WindowRef aWindowRef = NULL; |
CFStringRef theTitle = NULL; |
CFMutableStringRef theNewTitle = 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(gIBNibRef, CFSTR("MainWindow"), &aWindowRef); |
require_noerr(status, CreateWindowFromNib); |
require(aWindowRef != NULL, CreateWindowFromNib); |
// handling the window title |
status = CopyWindowTitleAsCFString(aWindowRef, &theTitle); |
require_noerr(status, CopyWindowTitleAsCFString); |
theNewTitle = CFStringCreateMutableCopy(NULL, 0, theTitle); |
require(theNewTitle != NULL, CFStringCreateMutableCopy); |
CFStringAppendFormat(theNewTitle, NULL, CFSTR(" %ld"), ++gWindowCount); |
status = SetWindowTitleWithCFString(aWindowRef, theNewTitle); |
require_noerr(status, SetWindowTitleWithCFString); |
// making sure we will clean up after ourselves |
EventTypeSpec eventTypeWC = {kEventClassWindow, kEventWindowClosed}; |
status = InstallWindowEventHandler(aWindowRef, Handle_WindowClosing, 1, &eventTypeWC, (void *)aWindowRef, NULL); |
require_noerr(status, CantInstallEventHandler); |
// installing our custom drawing function to the HIView |
HIViewID baseHIViewID = { 'BLNK', 100 }; |
HIViewRef baseView; |
HIViewFindByID(HIViewGetRoot(aWindowRef), baseHIViewID, &baseView); |
require(baseView != NULL, HIViewFindByID); |
EventTypeSpec eventTypeCD = {kEventClassControl, kEventControlDraw}; |
HIViewInstallEventHandler(baseView, HIViewCoreTextDraw, 1, &eventTypeCD, aWindowRef, NULL); |
// for performance reasons, let's associate the strings and frame setters to the window |
// there are 2 strings and 2 setters, 1 for each flow of text. 1 will be rendered with CTFrameDraw |
// and the other one with CTLineDraw. |
UniChar * unicodeText = (gWindowCount % 2 == 0) ? gUnicodeText1 : gUnicodeText2 ; |
SInt32 unicodeTextLength = (gWindowCount % 2 == 0) ? gUnicodeTextLength1 : gUnicodeTextLength2 ; |
CFIndex unicodeCharLength = unicodeTextLength / sizeof(UniChar); |
CFStringRef uniCFString = CFStringCreateWithCharacters(NULL, unicodeText, unicodeCharLength); |
require(uniCFString != NULL, CFStringCreateWithCharacters); |
CFAttributedStringRef attrString; |
attrString = CFAttributedStringCreate(NULL, uniCFString, NULL); |
CFRelease(uniCFString); |
require(attrString != NULL, CFAttributedStringCreate); |
CFRetain(attrString); |
CFMutableAttributedStringRef mutAttrString1; |
status = AddingStyleAttributes(attrString, &mutAttrString1); |
require_noerr(status, AddingStyleAttributes); |
CFMutableAttributedStringRef mutAttrString2; |
status = AddingOtherStyleAttributes(attrString, &mutAttrString2); |
require_noerr(status, AddingOtherStyleAttributes); |
CTFramesetterRef setter1 = CTFramesetterCreateWithAttributedString(mutAttrString1); |
require(setter1 != NULL, CTFramesetterCreateWithAttributedString); |
CTFramesetterRef setter2 = CTFramesetterCreateWithAttributedString(mutAttrString2); |
require(setter2 != NULL, CTFramesetterCreateWithAttributedString); |
status = SetWindowProperty(aWindowRef, 'CTTT', 'CFA1', sizeof(mutAttrString1), &mutAttrString1); |
require_noerr(status, SetWindowProperty); |
status = SetWindowProperty(aWindowRef, 'CTTT', 'CTF1', sizeof(setter1), &setter1); |
require_noerr(status, SetWindowProperty); |
status = SetWindowProperty(aWindowRef, 'CTTT', 'CFA2', sizeof(mutAttrString2), &mutAttrString2); |
require_noerr(status, SetWindowProperty); |
status = SetWindowProperty(aWindowRef, 'CTTT', 'CTF2', sizeof(setter2), &setter2); |
require_noerr(status, SetWindowProperty); |
Boolean singleSpace = true; |
status = SetWindowProperty(aWindowRef, 'CTTT', 'SBSP', sizeof(singleSpace), &singleSpace); |
require_noerr(status, SetWindowProperty); |
// The window was created hidden, so show it |
ShowWindow(aWindowRef); |
SetWindowModified(aWindowRef, false); |
AddingOtherStyleAttributes: |
AddingStyleAttributes: |
SetWindowProperty: |
CTFramesetterCreateWithAttributedString: |
CFAttributedStringCreate: |
CFStringCreateWithCharacters: |
CantInstallEventHandler: |
SetWindowTitleWithCFString: |
CFStringCreateMutableCopy: |
CopyWindowTitleAsCFString: |
if (theTitle != NULL) |
CFRelease(theTitle); |
if (theNewTitle != NULL) |
CFRelease(theNewTitle); |
HIViewFindByID: |
CantAllocateWindowData: |
CreateWindowFromNib: |
return status; |
} // Do_NewWindow |
/***************************************************** |
* |
* HIViewCoreTextDraw(inCaller, inEvent, inRefcon) |
* |
* Purpose: custom drawing using the CoreText APIs |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
static OSStatus HIViewCoreTextDraw( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ) |
{ |
OSStatus status; |
// retrieving important items through the event parameters |
HIViewRef view; |
status = GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(view), NULL, &view); |
require(view != NULL, GetEventParameter); |
CGContextRef context; |
status = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(context), NULL, &context); |
require(context != NULL, GetEventParameter); |
HIRect bounds; |
HIViewGetBounds(view, &bounds); |
CGContextScaleCTM(context, 1, -1); |
WindowRef aWindowRef = GetControlOwner(view); |
require(aWindowRef != NULL, GetControlOwner); |
// retrieving important items through the window properties |
CFAttributedStringRef attrString1; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CFA1', sizeof(attrString1), NULL, &attrString1); |
require_noerr(status, GetWindowProperty); |
require(attrString1 != NULL, GetWindowProperty); |
CTFramesetterRef setter1; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CTF1', sizeof(setter1), NULL, &setter1); |
require_noerr(status, GetWindowProperty); |
require(setter1 != NULL, GetWindowProperty); |
CFAttributedStringRef attrString2; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CFA2', sizeof(attrString2), NULL, &attrString2); |
require_noerr(status, GetWindowProperty); |
require(attrString2 != NULL, GetWindowProperty); |
CTFramesetterRef setter2; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'CTF2', sizeof(setter2), NULL, &setter2); |
require_noerr(status, GetWindowProperty); |
require(setter2 != NULL, GetWindowProperty); |
Boolean singleSpace = true; |
status = GetWindowProperty(aWindowRef, 'CTTT', 'SBSP', sizeof(singleSpace), NULL, &singleSpace); |
require_noerr(status, GetWindowProperty); |
// setting boundaries |
CFIndex unicodeCharLength1 = CFAttributedStringGetLength(attrString1); |
CFIndex unicodeCharLength2 = CFAttributedStringGetLength(attrString2); |
HIRect frameBounds[4] = |
{ |
{ { 10, 10 }, { (bounds.size.width - 30) / 2, (bounds.size.height - 30) / 2 + 50 } }, |
{ { (bounds.size.width - 30) / 2 + 20, (bounds.size.height - 30) / 2 + 70 }, { (bounds.size.width - 30) / 2, (bounds.size.height - 30) / 2 - 50 } }, |
{ { (bounds.size.width - 30) / 2 + 20, 10 }, { (bounds.size.width - 30) / 2, (bounds.size.height - 30) / 2 + 50 } }, |
{ { 10, (bounds.size.height - 30) / 2 + 70 }, { (bounds.size.width - 30) / 2, (bounds.size.height - 30) / 2 - 50 } } |
}; |
int i; |
CGMutablePathRef path = NULL; |
CFRange currentRange; |
CGContextSaveGState(context); |
// first render using CTFrameDraw, that's the easiest way. |
currentRange = CFRangeMake(0, 0); |
for (i = 0; i < 2; i++) |
{ |
path = CGPathCreateMutable(); |
CGPathAddRect(path, NULL, frameBounds[i]); |
CTFrameRef frameRef = CTFramesetterCreateFrame(setter1, currentRange, path, NULL); |
require(frameRef != NULL, CTFramesetterCreateFrame); |
CFRelease(path); path = NULL; |
// CoreText is drawing using the Quartz coordinate system (positive y up) |
// but the HIView architecture has conveniently modified the context matrix so |
// that we live in a HIToolbox coordinate system (positive y down) |
// we thus need to invert once again the context matrix so that CoreText is happy. |
CGContextTranslateCTM(context, 0, -frameBounds[i].origin.y-frameBounds[i].size.height); |
CTFrameDraw(frameRef, context); |
// grab the range that covered the string and create the range |
currentRange = CTFrameGetVisibleStringRange(frameRef); |
currentRange.location += currentRange.length; |
currentRange.length = 0; |
CFRelease(frameRef); |
// if we've hit the end of the string, break out early |
if (currentRange.location == unicodeCharLength1) break; |
} |
CGContextRestoreGState(context); |
// second render using CTLineDraw, that's the most flexible way. |
// code below is for handling the single space / double space in blue gray color |
currentRange = CFRangeMake(0, 0); |
for (i = 2; i < 4; i++) |
{ |
HIRect pathFrame = frameBounds[i]; |
if (!singleSpace) |
pathFrame.size.height /= 2; |
path = CGPathCreateMutable(); |
CGPathAddRect(path, NULL, pathFrame); |
CTFrameRef frameRef = CTFramesetterCreateFrame(setter2, currentRange, path, NULL); |
require(frameRef != NULL, CTFramesetterCreateFrame); |
CFRelease(path); path = NULL; |
// CoreText is drawing using the Quartz coordinate system (positive y up) |
// but the HIView architecture has conveniently modified the context matrix so |
// that we live in a HIToolbox coordinate system (positive y down) |
// we thus need to invert once again the context matrix so that CoreText is happy. |
CGContextTranslateCTM(context, 0, -frameBounds[i].origin.y-frameBounds[i].size.height); |
// instead of just calling CTFrameDraw, we simply have to iterate over the lines |
// and draw each one. That's not too complicate. |
CGPoint penPosition; |
penPosition.y = frameBounds[i].origin.y + frameBounds[i].size.height; |
// grab the lines |
CFArrayRef lineArray = CTFrameGetLines(frameRef); |
require(lineArray != NULL, CTFrameGetLines); |
CFIndex j = 0, lineCount = CFArrayGetCount(lineArray); |
for ( ; j < lineCount; j++ ) |
{ |
CTLineRef currentLine = (CTLineRef)CFArrayGetValueAtIndex(lineArray, j); |
CGFloat ascent, descent, leading; |
CTLineGetTypographicBounds(currentLine, &ascent, &descent, &leading); |
double penOffset = CTLineGetPenOffsetForFlush(currentLine, 0, frameBounds[i].size.width); |
penPosition.x = frameBounds[i].origin.x + penOffset; |
if (singleSpace) |
penPosition.y -= ascent; |
else |
penPosition.y -= 2 * ascent; |
CGContextSetTextPosition(context, penPosition.x, penPosition.y); |
CTLineDraw(currentLine, context); |
if (singleSpace) |
penPosition.y -= ( descent + leading ); |
else |
penPosition.y -= 2 * ( descent + leading ); |
} |
// grab the range that covered the string and create the range |
currentRange = CTFrameGetVisibleStringRange(frameRef); |
currentRange.location += currentRange.length; |
currentRange.length = 0; |
CFRelease(frameRef); |
// if we've hit the end of the string, break out early |
if (currentRange.location == unicodeCharLength2) break; |
} |
GetWindowProperty: |
GetControlOwner: |
CTFrameGetLines: |
CTFramesetterCreateFrame: |
CTFramesetterCreateWithAttributedString: |
if (path != NULL) CFRelease(path); |
GetEventParameter: |
return status; |
} |
/***************************************************** |
* |
* AddingStyleAttributes(inAttrString, outMutAttrString) |
* |
* Purpose: convenience function to add some styles to an attributed string |
* |
* Note: the attributed string inAttrString is released by this function. Retain it if you want to keep it around. |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
static OSStatus AddingStyleAttributes( CFAttributedStringRef inAttrString, CFMutableAttributedStringRef * outMutAttrString ) |
{ |
OSStatus status = coreFoundationUnknownErr; |
CTFontRef times24Font = CTFontCreateWithName(CFSTR("Times"), 24.0, NULL); |
require(times24Font != NULL, CTFontCreateWithName); |
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); |
CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 }; |
CGColorRef red = CGColorCreate(rgbColorSpace, components); |
CGColorSpaceRelease(rgbColorSpace); |
SInt32 one = 1; |
CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &one); |
float half = 0.5; |
CFNumberRef weight = CFNumberCreate(NULL, kCFNumberFloatType, &half); |
CFMutableAttributedStringRef mutAttrString = CFAttributedStringCreateMutableCopy(NULL, 0, inAttrString); |
CFRelease(inAttrString); |
require(mutAttrString != NULL, CFAttributedStringCreateMutableCopy); |
#if 0 |
// |
// First method: adding the styles in a dictionary, applying the dictionary to a range of text |
// |
CFMutableDictionaryRef styleDict = CFDictionaryCreateMutable(NULL, 1, NULL, NULL); |
require(styleDict != NULL, CFDictionaryCreateMutable); |
CFDictionaryAddValue(styleDict, kCTFontAttributeName, times24Font); |
CFAttributedStringSetAttributes(mutAttrString, CFRangeMake(189+6, 21), styleDict, false); |
CFRelease(styleDict); |
#else |
// |
// Second method: adding each style to a range of text |
// |
CFAttributedStringSetAttribute(mutAttrString, CFRangeMake(189+6, 21), kCTFontAttributeName, times24Font); |
CFAttributedStringSetAttribute(mutAttrString, CFRangeMake(189+21, 21), kCTForegroundColorAttributeName, red); |
CFAttributedStringSetAttribute(mutAttrString, CFRangeMake(189+39, 21), kCTUnderlineStyleAttributeName, underline); |
CFAttributedStringSetAttribute(mutAttrString, CFRangeMake(189+39, 21), kCTFontWeightTrait, weight); |
#endif |
CFRelease(times24Font); |
CFRelease(red); |
CFRelease(underline); |
CFRelease(weight); |
status = noErr; |
CFAttributedStringCreateMutableCopy: |
CFDictionaryCreateMutable: |
CTFontCreateWithName: |
if (outMutAttrString != NULL) *outMutAttrString = mutAttrString; |
return status; |
} |
/***************************************************** |
* |
* AddingOtherStyleAttributes(inAttrString, outMutAttrString) |
* |
* Purpose: convenience function to add some styles (blue gray color) to an attributed string |
* |
* Note: the attributed string inAttrString is released by this function. Retain it if you want to keep it around. |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
static OSStatus AddingOtherStyleAttributes( CFAttributedStringRef inAttrString, CFMutableAttributedStringRef * outMutAttrString ) |
{ |
OSStatus status = coreFoundationUnknownErr; |
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); |
CGFloat components[] = { 0.3, 0.3, 1.0, 1.0 }; |
CGColorRef blueGray = CGColorCreate(rgbColorSpace, components); |
CGColorSpaceRelease(rgbColorSpace); |
CFMutableAttributedStringRef mutAttrString = CFAttributedStringCreateMutableCopy(NULL, 0, inAttrString); |
CFRelease(inAttrString); |
require(mutAttrString != NULL, CFAttributedStringCreateMutableCopy); |
CFAttributedStringSetAttribute(mutAttrString, CFRangeMake(0, CFAttributedStringGetLength(mutAttrString)), kCTForegroundColorAttributeName, blueGray); |
CFRelease(blueGray); |
status = noErr; |
CFAttributedStringCreateMutableCopy: |
if (outMutAttrString != NULL) *outMutAttrString = mutAttrString; |
return status; |
} |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-12-19