<<

Integrating Carbon and Cocoa in Your Application

Preliminary January 1, 2003 Simultaneously published in the United Apple Computer, Inc. States and Canada. ' 2003, 2004 Apple Computer, Inc. Even though Apple has reviewed this manual, All rights reserved. APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS MANUAL, No part of this publication may be ITS QUALITY, ACCURACY, MERCHANTABILITY, OR FOR A reproduced, stored in a retrieval system, or PARTICULAR PURPOSE. AS A RESULT, THIS transmitted, in any form or by any means, MANUAL IS SOLD “AS IS,” AND YOU, THE mechanical, electronic, photocopying, PURCHASER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. recording, or otherwise, without prior IN NO EVENT WILL APPLE BE LIABLE FOR written permission of Apple Computer, Inc., DIRECT, INDIRECT, SPECIAL, INCIDENTAL, with the following exceptions: Any person OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OR is hereby authorized to store documentation INACCURACY IN THIS MANUAL, even if on a single computer for personal use only advised of the possibility of such damages. and to print copies of documentation for THE WARRANTY AND REMEDIES personal use provided that the FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL OR WRITTEN, documentation contains Apple’s copyright EXPRESS OR IMPLIED. No Apple dealer, agent, notice. or employee is authorized to any modification, extension, or addition to this The Apple logo is a trademark of Apple warranty. Computer, Inc. Some states do not allow the exclusion or limitation of implied warranties or liability for Use of the “keyboard” Apple logo incidental or consequential damages, so the (Option-Shift-K) for commercial purposes above limitation or exclusion may not apply to you. This warranty gives you specific legal without the prior written consent of Apple rights, and you may also have other rights which may constitute trademark infringement and vary from state to state. unfair competition in violation of federal and state laws. No licenses, express or implied, are granted with respect to any of the technology described in this document. Apple retains all intellectual property rights associated with the technology described in this document. This document is intended to assist application developers to develop applications only for Apple-labeled or Apple-licensed computers. Every effort has been made to ensure that the information in this document is accurate. Apple is not responsible for typographical errors. Apple Computer, Inc. 1 Infinite Loop Cupertino, CA 95014 408-996-1010

Apple, the Apple logo, , Carbon, Cocoa, Mac, Mac OS, and QuickTime are trademarks of Apple Computer, Inc., registered in the United States and other countries. Objective- is a trademark of NeXT Software, Inc. Java and all Java-based trademarks are trademarks or registered trademarks of , Inc. in the U.S. and other countries. Contents

Chapter 1 Introduction to Carbon and Cocoa Integration 7

Chapter 2 Carbon and Cocoa Integration Concepts 9

Carbon and Cocoa Communication 9 C-Callable Wrapper Functions 12 Compiler Preprocessors 13 Interchangeable Data Types 13

Chapter 3 Carbon and Cocoa Integration Tasks 15

Using Cocoa Functionality in a Carbon Application 15 About the Spelling Checker Application 16 Writing a Cocoa Bundle 16 Making Your Carbon Code Access the Cocoa Bundle 20 Summary 24 Using Carbon Functionality in a Cocoa Application 24 Working With QuickTime Movies 25 Accessing a From Cocoa 26 Obtaining an FSSpec Structure 26 Managing Objects in Cocoa 27 Summary 28 Using a Cocoa User Interface in a Carbon Application 28 About the CocoaInCarbon Application 29 Writing the Cocoa Bundle 31 Setting Up the Carbon Application to Use the Cocoa Interface 36 Summary 39 Using a Carbon User Interface in a Cocoa Application 39 About the CarbonInCocoa Application 40 Creating the Carbon User Interface 41 Setting Up the Cocoa Application to Use the Carbon User Interface 41 Summary 44

Document Revision History 45

3 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CONTENTS

4 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. Tables, Figures, and Listings

Chapter 2 Carbon and Cocoa Integration Concepts 9

Figure 2-1 The path of user events in Mac OS X 10 Figure 2-2 Events communicated between Carbon and Cocoa 11 Table 2-1 extensions and compilation 13 Table 2-2 Data types that can be used interchangeably between Carbon and Cocoa 14

Chapter 3 Carbon and Cocoa Integration Tasks 15

Figure 3-1 The user interface for the Spelling Checker application 16 Figure 3-2 The Carbon user interface for the CocoaInCarbon application 30 Figure 3-3 The Cocoa window for the CocoaInCarbon application 30 Figure 3-4 Connecting a button to a button-pressed action 36 Figure 3-5 The Cocoa user interface for the CarbonInCocoa application 40 Figure 3-6 The Carbon user interface for the CarbonInCocoa application 40 Listing 3-1 A C-callable wrapper function for the uniqueSpellDocumentTag: method 19 Listing 3-2 A C-callable wrapper function for the checkSpellingOfString:startingAt: method 19 Listing 3-3 A C-callable wrapper function for the sharedApplication method 20 Listing 3-4 A utility function to load a from the application’s Frameworks folder 21 Listing 3-5 Defining data types for function pointers 22 Listing 3-6 Declaring function pointers 22 Listing 3-7 Calling a C-wrapper function from your Carbon application 23 Listing 3-8 Building an array of track media types for a QuickTime movie 25 Listing 3-9 Calling Resource Manager functions from a Cocoa application 26 Listing 3-10 A Cocoa method to obtain an FSSpec structure 27 Listing 3-11 The contents of the common header for the Cocoa bundle and the Carbon application 32 Listing 3-12 Implementing the controller in the Cocoa bundle 32 Listing 3-13 Declaring the interface for the controller 33 Listing 3-14 C-callable wrapper functions for the Cocoa bundle 34 Listing 3-15 Calling the initializeBundle C-wrapper function using a function pointer 37 Listing 3-16 Calling the C-wrapper function to open the Cocoa window 38 Listing 3-17 A Carbon function that handles a Cocoa button press 38 Listing 3-18 The declaration for the controller 42 Listing 3-19 Loading a nib file for a Carbon window 42

5 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. TABLES, FIGURES, AND LISTINGS

Table 3-1 C-callable wrapper functions for Cocoa methods 18

6 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 1

Introduction to Carbon and Cocoa Integration

No matter which development environment you choose to develop applications—Cocoa or Carbon—you may find the other development environment offers functionality you’ like to use in your application. Choosing the Cocoa or Carbon development environment to create new applications doesn’t restrict you to using the API defined for that environment. You can use the Carbon API from a Cocoa application or the Cocoa API from a Carbon application. This document shows you how.

There are a number of reasons you might want to integrate Cocoa and Carbon in an application, including the following:

■ You want to leverage existing code while getting the benefits of technologies offered by another framework. ■ You are developing a common service that you want to make available to both Carbon and Cocoa. ■ It’s easier for you to do some tasks in Cocoa than in Carbon, or vice versa. ■ You already created a terrific user interface in one environment and want to access it from the other environment. ■ Your programming team consists of engineers with different skill sets—Cocoa and Carbon. You’ll find, by reading the following chapters, that intermixing Carbon and Cocoa in an application is a relatively straightforward process.

■ Chapter 2, “Carbon and Cocoa Integration Concepts”, discusses how Carbon and Cocoa communicate between each other and provides information on C-callable wrapper functions, compiler preprocessors, and interchangeable data types. ■ Chapter 3, “Carbon and Cocoa Integration Tasks”, provides information on how you can call Cocoa functions from a Carbon application and Carbon functions from a Cocoa application, including how to share user interface elements between the two environments. This document assumes you are programming for Cocoa in Objective-C and does not discuss Java integration issues. To get the full benefit of this document, you should have experience programming in either the Cocoa or Carbon environment. In addition, it is desirable that you have some basic knowledge of the other environment. See the Mac OS X Programming Getting Started website for tutorials, examples, and a reading list of essential documents: http://developer.apple.com/macosx/gettingstarted/

7 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 1 Introduction to Carbon and Cocoa Integration

Important: This is a preliminary document. Although it has been reviewed for technical accuracy, it is not final. Apple Computer, Inc. is supplying this information to help you plan for the adoption of the technology described herein. This information is subject to change, and software implemented according to this document should be tested with final operating-system software and final documentation. You can check http://developer.apple.com/documentation/ for information about updates to this and other developer documents. To receive notification of documentation updates, you can sign up for ADC's free Online Program and receive their weekly Connection News email newsletter. (See http://developer.apple.com/membership/ for more details about the Online Program.)

8 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2

Carbon and Cocoa Integration Concepts

It has always been possible to integrate Cocoa and Carbon functionality in the same application, at least when it comes to functionality that doesn’t handle user interface elements. For a Cocoa application to call Carbon functions, the only requirements are that the compiler has access to the appropriate header files and the application is linked against the appropriate frameworks. Because Cocoa already links against the Application Services framework, all you need to do to access the low-level Carbon functionality exposed in that framework is to include the ApplicationServices.h header file in your application. To access high-level Carbon functionality, you can simply import Carbon.h and link against the Carbon framework. A Carbon application, by taking a few extra steps, can use many Cocoa technologies. When it comes to functions that handle the user interface, it has not been possible until the Jaguar release of Mac OS X, for developers to use Cocoa and Carbon in the same application.

There are a few fundamental concepts that you should be familiar with before you begin to integrate Cocoa and Carbon in the same application. These concepts are covered in this chapter, in the following sections:

■ “Carbon and Cocoa Communication” (page 9) discusses how Mac OS X communicates user events between Carbon and Cocoa application environments. ■ “C-Callable Wrapper Functions” (page 12) defines wrapper functions and provides an overview of how a Carbon application uses them to access Cocoa functionality. ■ “Compiler Preprocessors” (page 13) lists filename extensions you can use when you mix programming languages in a project. ■ “Interchangeable Data Types” (page 13) provides information on Foundation (Cocoa) and Core Foundation (Carbon) data types you can use interchangeably. The concepts discussed in this chapter are put into practice in the sample code provided in Chapter 3, “Carbon and Cocoa Integration Tasks” (page 15).

Carbon and Cocoa Communication

In versions of Mac OS X earlier than Jaguar, using Carbon windows in a Cocoa application or using Cocoa windows in a Carbon application posed a challenge because of the way user events are generated and forwarded to applications. This section provides an overview on event handling and outlines the recent changes that allow Carbon and Cocoa to communicate user events to each other.

Carbon and Cocoa Communication 9 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2 Carbon and Cocoa Integration Concepts

Figure 2-1 shows the path of a user event (such as a mouse click or a keypress) through the system to the Carbon and Cocoa application environments. The event originates when the that controls an input device such as a mouse detects a user action and passes it to the window server.

Figure 2-1 The path of user events in Mac OS X

Events Kernel Window server

Event queue Place event on queue

Pull event Pull event and dispatch and dispatch

Carbon Cocoa

eventHandler NSResponder

When the window server receives the event, it consults a database of currently open windows. The window server then sends the event to the event port of the run loop belonging to the process that owns the window in which the event occurred. The event gets the event from the run-loop port, packages the event in an appropriate form, and passes it to the event-handling mechanism specific to the application environment of the process. This mechanism ensures that the event is handled by the function or method associated with the control that is clicked (or key that is pressed).

In versions of Mac OS X earlier than Jaguar, events are passed to the application environment of the process. So when a Carbon application tries to use a Cocoa window, events for the Cocoa window are passed to the Carbon application. But because the Carbon application does not have a handler for events in the Cocoa window and no way to communicate the event to the Cocoa bundle that creates the window, the event is dropped. The reverse is true for a Carbon window in a Cocoa application. Events in the Carbon window are passed to the Cocoa application, but the Cocoa application does not have a handler for the Carbon window and no way to communicate the event to the Carbon bundle.

In the Jaguar release of Mac OS X, the system automatically installs the appropriate handlers to allow Cocoa and Carbon to communicate events between the two environments. Figure 2-2 shows the communication path between Carbon and Cocoa environments for a Carbon application that uses a Cocoa bundle (on the left-side of the figure) and for a Cocoa application that uses a Carbon bundle (on the right-side of the figure).

Let’s first look at how communicating user events works for a Cocoa window used in a Carbon application. The system automatically installs Carbon event handlers for the Cocoa window using a WindowRef created for that purpose. When a user event (for example, a button click) occurs in the Cocoa window, the button-click event is passed to the Carbon application. The

10 Carbon and Cocoa Communication Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2 Carbon and Cocoa Integration Concepts

Carbon application dispatches the button-click event to the event handler for the WindowRef for the Cocoa window. The system-supplied event handler knows how to the event as a Cocoa NSEvent object. It then passes the NSEvent to the window for processing using the normal Cocoa event-processing mechanisms, including the responder chain for events not targeted at a specific control. In the case of a mouse click on a button, the button receives the button-click event and handles it using the normal Cocoa target/action mechanism.

Figure 2-2 Events communicated between Carbon and Cocoa

Event queue Event queue

Pull event Pull event and dispatch and dispatch

Carbon Cocoa Cocoa Carbon application bundle application bundle

eventHandler NSResponder NSResponder eventHandler

Conversely, for a Carbon window used in a Cocoa application, the system automatically creates a Cocoa NSWindow object to represent the Carbon window. When a user clicks a button in the Carbon window, an NSEvent for the button-click is passed to the Cocoa application. Cocoa’s normal event-handling mechanisms pass the event to the system-supplied NSWindow object that corresponds to the Carbon window. The NSWindow knows how to create a Carbon event and pass it to the event handler of the Carbon window. From there, the event is processed through the Carbon event target containment hierarchy.

In summary, Carbon and Cocoa can share the same window because the Jaguar version of Mac OS X has mechanisms for automatically translating between Cocoa and Carbon events at the user-interface element level, rather than just at the application level.

For more information on application environments, the system architecture, and how events are tracked by the system, see Inside Mac OS X: System Overview: http://developer.apple.com/documentation/MacOSX/Conceptual/SystemOverview/index.

For more information on Carbon events, see Inside Mac OS X: Handling Carbon Events: http://developer.apple.com/documentation/Carbon/Conceptual/CarbonEventManager/carboneventmanager.html

For more information on Cocoa events, see the Cocoa Programming Topics “Overview of Events” and “Basic Event Handling”.

Carbon and Cocoa Communication 11 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2 Carbon and Cocoa Integration Concepts

C-Callable Wrapper Functions

If you want to call Cocoa methods from a Carbon application, you need to write C-callable wrapper functions (or simply C-wrapper functions) for the Cocoa portion of the code. In the context of Carbon and Cocoa integration, a C-callable wrapper function is a C function whose body contains Objective-C code that allows data to be passed to or obtained from Cocoa.

Let’s look at a typical scenario for a Carbon application that accesses functionality provided in a Cocoa bundle. The Cocoa bundle must contain all the necessary Objective-C code for its classes and methods. It must also contain a C-callable wrapper function for each method whose functionality is needed by the Carbon application. For example, for a changeText:(NSString *)message method that takes a string and manipulates it in some way, the C-callable wrapper function would look similar to the following:

OSStatus changeText (CFStringRef message) { NSAutoreleasePool *localPool;

localPool = [[NSAutoreleasePool alloc] init]; [[Controller sharedController] changeText:(NSString *)message]; [localPool release]; return noErr; }

Note: The C-wrapper function must allocate and initialize an NSAutoreleasePool and then release it when it is no longer needed, as shown in the changeText function. This is a requirement for a Cocoa bundle that’s used by a Carbon application.

It’s not enough to provide C-callable wrapper functions. To actually use C-wrapper functions in a Carbon application, the Carbon application must load the Cocoa bundle and obtain a function pointer for each C-wrapper function the Carbon application plans to use.

Core Foundation Bundle Services provides functions that can load a bundle, obtain its bundle reference, and obtain function pointers from the bundle. You’ll see how to load the bundle and obtain its reference in Chapter 3, “Carbon and Cocoa Integration Tasks” (page 15). Once you have a bundle reference, you can obtain a function pointer to any C-callable function using the function CFBundleGetFunctionPointerForName. This function takes two parameters, a bundle reference and a Core Foundation string that specifies the name of the C-callable function. For example, you’d get a function pointer for the changeText C-wrapper function using this code:

OSStatus (*funcPtr)(CFStringRef message); funcPtr = CFBundleGetFunctionPointerForName (bundleRef, CFSTR ("changeText"));

In summary, here’s how C-callable wrapper functions are used to allow Carbon applications to access Cocoa functionality:

1. Access to the Cocoa functionality is provided in a Cocoa bundle. The bundle contains C-callable wrapper functions for any Cocoa method that’s needed by the Carbon application. 2. The Carbon application loads the Cocoa bundle at runtime. 3. The Carbon application obtains function pointers from the Cocoa bundle for each C-callable wrapper function the Carbon application needs.

12 C-Callable Wrapper Functions Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2 Carbon and Cocoa Integration Concepts

4. The Carbon application invokes the C-wrapper functions as needed.

Compiler Preprocessors

Carbon and Cocoa use different languages for their . Carbon uses C while Cocoa uses Objective-C. In addition, Carbon may prefer to use C++. When you build an application that uses C, Objective-C, Objective-C++, and C++ files, you must make sure the compiler performs the appropriate preprocessing. The of the file you are compiling determines the kind of compilation that is done, as shown in Table 2-1.

Table 2-1 Filename extensions and compilation

Filename extension Indicates to the compiler

.c C source code that must be preprocessed

.i C source code that should not be preprocessed

.ii C++ source code that should not be preprocessed

.m Objective-C source code

.mm Objective-C++ source code

.mi Objective-C source code that should not be preprocessed

.h C header file (not to be compiled or linked)

.cc .cp .ccx .cpp C++ source code that must be preprocessed .c++ .C

You can specify a compiler to be used to compile all source files, regardless of the filename extension. For example, to compile all files that have a .cpp extension with the Objective-C++ compiler, you would add the -ObjC++ flag to the compiler flags (OTHER_CFLAGS). You can specify that the Objective-C compiler should be used for all compilation by setting the -ObjC flag. These and other flags are set in the Build Settings pane in . See Project Builder Help for instructions on how to set compiler flags. Project Builder Help is available from the Help menu in Project Builder or from the developer tools online documentation:

http://developer.apple.com/documentation/DeveloperTools/Tools.html

Interchangeable Data Types

There are a number of data types in Cocoa and Carbon that can be used interchangeably in all functions or methods that use either type. Table 2-2 is a partial listing of the data types that are interchangeable between Foundation (Cocoa) and Core Foundation (Carbon). Data types that can be used interchangeably are also referred to as toll-free bridged data types.

Compiler Preprocessors 13 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 2 Carbon and Cocoa Integration Concepts

Table 2-2 Data types that can be used interchangeably between Carbon and Cocoa

Core Foundation data type Cocoa Foundation class

CFArray NSArray, NSMutableArray

CFCharacterSet NSCharacterSet, NSMutableCharacterSet

CFData NSData, NSMutableData

CFDate NSDate

CFDictionary NSDictionary, NSMutableDictionary

CFRunLoopTimer NSTimer

CFSet NSSet, NSMutableSet

CFString NSString, NSMutableString

CFURL NSURL

CFTimeZone NSTimeZone

To use the data types in Table 2-2 interchangeably you must cast one type to the other. For example:

(CFStringRef) myNSString (NSString *) myCFString

You can find more information about using toll-free bridged data types in “Managing Core Foundation Objects in Cocoa” (page 27).

Note: Not all Foundation classes have an interchangeable data type in Core Foundation, even though they may appear to have one. For example, the data type CFRunLoop is not toll-free bridged to the NSRunLoop class and neither is the CFBundle data type to the NSBundle class.

Toll-free bridged data types are more likely to be used in Cocoa applications than in Carbon applications. That’s because interchanging data types requires that Core Foundation and Foundation are loaded at the same time. For Cocoa applications, this happens automatically. For Carbon applications, Foundation must be included specifically. If Foundation is already loaded, a Carbon application would most likely use a Cocoa class with the Cocoa method rather than using a Core Foundation data type with a Cocoa method.

14 Interchangeable Data Types Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3

Carbon and Cocoa Integration Tasks

This chapter shows you how to integrate Carbon and Cocoa in an application. The tasks you must perform to integrate Carbon and Cocoa depend on whether you need to access Cocoa functionality in a Carbon application or Carbon functionality in a Cocoa application and whether or not you use an interface created in one development environment in the other development environment.

Specifically, in this chapter you’ll find information on the following:

■ “Using Cocoa Functionality in a Carbon Application” (page 15) ■ “Using Carbon Functionality in a Cocoa Application” (page 24) ■ “Using a Cocoa User Interface in a Carbon Application” (page 28) ■ “Using a Carbon User Interface in a Cocoa Application” (page 39)

Using Cocoa Functionality in a Carbon Application

This section describes how to use Cocoa functionality in a Carbon application—Cocoa functionality that is unrelated to the user interface. You can access Cocoa functionality in a Carbon application in Mac OS X version 10.1 and later. There are two major tasks you need to perform to use Cocoa functionality in a Carbon application:

■ Write a Cocoa bundle that contains C-callable wrapper functions for any Cocoa method whose functionality you want to access from your Carbon application. The bundle must also contain a function to initialize Cocoa. See “Writing a Cocoa Bundle” (page 16) for details. ■ Write Carbon code that loads the Cocoa bundle, obtains function pointers to the C-callable wrapper functions in the Cocoa bundle, and calls the C-wrapper function that initializes Cocoa. See “Making Your Carbon Code Access the Cocoa Bundle” (page 20) for details. The tasks described in the following sections are illustrated using sample code taken from a working application—Spelling Checker. The sample application uses Cocoa spelling checking services. See “About the Spelling Checker Application” (page 16) for a description of the application. You can download the code for this application from the developer sample code website:

http://developer.apple.com/samplecode/index.html

Using Cocoa Functionality in a Carbon Application 15 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

About the Spelling Checker Application

The sample Carbon application—Spelling Checker—provides spelling checking services for text typed into a window. The user interface is shown in Figure 3-1. The Spelling Checker window is a Carbon window, created with . The user can type text into the large text box on the left side of the window.

To check spelling, the user clicks the Check Spelling button. The first misspelled word is displayed under the button, as shown in Figure 3-1 (“clal”). Suggestions for a replacement word are shown in the Guesses list. The user can choose to

■ ignore the word by clicking the Ignore Word button ■ replace the misspelled word by selecting a word from the list of guesses and clicking the Use This Word button

■ specify another word to use by typing a word and clicking the Use This Word button

Figure 3-1 The user interface for the Spelling Checker application

The spelling checking services are provided by the Cocoa frameworks and accessed through C-callable wrapper functions in a Cocoa bundle, but called from the Carbon application.

Note: The text box in the sample application is a TextEdit control. For a more complex application, it is desirable to use the text editing capabilities provided by the Multilingual Text Engine (MLTE) API.

Writing a Cocoa Bundle

Writing the Cocoa bundle requires performing the tasks described in the following sections:

16 Using Cocoa Functionality in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

■ “Creating a New Cocoa Bundle Using Project Builder” (page 17) ■ “Identifying Cocoa Methods” (page 17) ■ “Writing C-Callable Wrapper Functions” (page 18) ■ “Writing an Initialization Function” (page 20)

Creating a New Cocoa Bundle Using Project Builder

To make a Cocoa bundle using Project Builder, do the following:

1. Open Project Builder. 2. Select New Project from the File Menu. 3. Select Cocoa Bundle from the Bundle list in the New Project window and click the Next button. 4. Type a project name and specify a location (optional), then click the Finish button.

5. Rename the default file so it has the appropriate extension, such as the .m extension.

To rename the main.c file, click the filename, choose Rename from the Project menu, then type the new name. The sample code main filename is SpellCheck.m.

Recall from “Compiler Preprocessors” (page 13) that the .m extension indicates to the compiler that the code is Objective-C.

6. Add the following statements to your renamed main file: #include #include

As long as you create your Cocoa bundle using Project Builder, you should not need to modify build settings and values. When you write your Carbon application, you need to specify the name of your Cocoa bundle. This name, listed in the bundle’s property list, should be the same name you specify as the project name.

Identifying Cocoa Methods

You need to identify the Cocoa methods that provide the functionality your Carbon application needs. The methods you identify are the ones for which you’ll need to write a C-callable wrapper function.

The Spelling Checker application requires the functionality provided by the following methods:

■ uniqueSpellDocumentTag returns a tag for a document. This tag is guaranteed to be unique. Using a tag with each document ensures spelling checking services are unique for a document.

■ closeSpellDocumentWithTag: called when a document closes to make sure the ignored-word list associated with the document is cleaned up.

■ checkSpellingOfString:startingAt: starts the searching for a misspelled word in a string, starting at the specified location. This method returns the range of the first misspelled word.

Using Cocoa Functionality in a Carbon Application 17 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

■ checkSpellingOfString:startingAt:language:wrap:inSpellDocumentWithTag:wordCount: starts the searching for a misspelled word in a string, starting at the specified location and using a number of other options. This method returns the range of the first misspelled word.

■ ignoreWord:inSpellDocumentWithTag: adds a word to the list of words to be ignored when checking a document’s spelling.

■ setIgnoredWords:inSpellDocumentWithTag: initializes the list of ignored words for a document to an array of words to ignore.

■ ignoredWordsInSpellDocumentWithTag: returns the array of ignored words for a document.

■ guessesForWord: returns an array of suggested spellings for a misspelled word. For additional information see the documentation for NSSpellChecker and its methods.

You also need to identify any other methods needed to implement the Cocoa functionality. For example, the class method sharedSpellChecker returns an instance of NSSpellChecker.

Writing C-Callable Wrapper Functions

After you have identified the Cocoa methods that provide the functionality you want to use, you need to write C-callable wrapper functions for those methods.

For the Spelling Checker application, there are eight Cocoa methods (see “Identifying Cocoa Methods” (page 17)) that provide services to manage and check spelling in a document. For the Carbon portion of the application to access the Cocoa methods, you need to write C-callable wrapper functions and put them in the Cocoa bundle. Table 3-1 lists the names of the C-callable wrapper functions in the Spelling Checker application.

Table 3-1 C-callable wrapper functions for Cocoa methods

C-Callable wrapper function Cocoa method

UniqueSpellDocumentTag uniqueSpellDocumentTag

CloseSpellDocumentWithTag closeSpellDocumentWithTag:

CheckSpellingOfString checkSpellingOfString:startingAt:

CheckSpellingOfStringWithOptions checkSpellingOfString:startingAt: language:wrap:inSpellDocumentWithTag: wordCount:

IgnoreWord ignoreWord:inSpellDocumentWithTag:

SetIgnoredWords setIgnoredWords:inSpellDocumentWithTag:

CopyIgnoredWordsInSpellDocumentWithTag ignoredWordsInSpellDocumentWithTag:

GuessesForWords guessesForWord:

Listing 3-1 shows a C-callable wrapper function UniqueSpellDocumentTag. Note the code for the autorelease pool. For a Cocoa bundle loaded into a Carbon application, you must set up an autorelease pool each time it’s needed.

18 Using Cocoa Functionality in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Listing 3-1 A C-callable wrapper function for the uniqueSpellDocumentTag: method int UniqueSpellDocumentTag () { int tag;

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; tag = [NSSpellChecker uniqueSpellDocumentTag]; [pool release];

return (tag); }

All the other C-callable wrapper functions for the Spelling Checker application are written in the same manner as shown in Listing 3-1 using these guidelines:

■ The C-wrapper function must have parameters that match what’s needed by the Cocoa method. For examples, see Listing 3-2. The C-wrapper function parameters stringToCheck and startingOffset match the two parameters required by the checkSpellingOfString:startingAt: method. ■ The C-wrapper function must allocate and initialize an NSAutoreleasePool and then release it when it is no longer needed. This is a requirement for a Cocoa bundle that’s used by a Carbon application. You can see examples of this in Listing 3-1 and Listing 3-2. ■ The C-wrapper function must return the data returned by the Cocoa method it wraps. For example, the UniqueSpellDocumentTag function in Listing 3-1 returns the tag value obtained from the uniqueSpellDocumentTag method; the CheckSpellingOfString function in Listing 3-2 returns the range obtained from the checkSpellingOfString:startingAt: method. ■ Where appropriate, the C-wrapper function can use toll-free bridged (interchangeable) data types. For example, the C-wrapper function in Listing 3-2 takes a CFStringRef as a parameter, but casts this to NSString* when the function passes the string to the Cocoa method.

Listing 3-2 A C-callable wrapper function for the checkSpellingOfString:startingAt: method

CFRange CheckSpellingOfString (CFStringRef stringToCheck, int startingOffset) { NSRange range = {0,0};

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; range =[[NSSpellChecker sharedSpellChecker] checkSpellingOfString:(NSString *) stringToCheck

startingAt:startingOffset];

[pool release]; return ( *(CFRange*)&range); }

The code for the rest of the C-callable wrapper functions needed for the Spelling Checker application are in the SpellCheck.m file of the Cocoa bundle. You can download the code from the developer sample code website:

Using Cocoa Functionality in a Carbon Application 19 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

http://developer.apple.com/samplecode/index.html

Writing an Initialization Function

In Mac OS X version 10.1, a Carbon application can’t use the functionality provided by a Cocoa bundle unless Cocoa is initialized by calling the sharedApplication method. You need to write a C-callable wrapper function for the sharedApplication method, as shown in Listing 3-3. This initialization function must be in your Cocoa bundle, although you call the C-wrapper function from your Carbon application.

Listing 3-3 A C-callable wrapper function for the sharedApplication method

void InitializeCocoa () { NSApplication *NSApp=[NSApplication sharedApplication]; }

You call the InitializeCocoa function from your Carbon application only once. If you invoke the sharedApplication method more than once, it simply returns the NSApplication object it created previously.

For more information see the NSApplication class.

Making Your Carbon Code Access the Cocoa Bundle

Your Carbon application must perform a number a tasks to access the functionality provided by a Cocoa bundle. These tasks are described in the following sections:

■ “Loading the Cocoa Bundle” (page 20) ■ “Obtaining Function Pointers and Initializing Cocoa” (page 22) ■ “Calling C-Wrapper Functions From Your Carbon Application” (page 23)

Loading the Cocoa Bundle

Your Carbon application must load the Cocoa bundle that contains the functionality you want to access. Listing 3-4 shows a function that calls a series of Core Foundation functions to load a bundle from an application’s Frameworks folder. For example, for the Spelling Checker application, the MyLoadPrivateFrameworkBundle function shown in the listing loads the bundle located in

SpellingChecker.app/Contents/Frameworks/

and whose name is specified by the framework parameter. In this case, the name is SpellCheck.bundle.

A detailed explanation for each numbered line of code follows Listing 3-4. Note that error checking is used after each call to a Core Foundation function, and that if an error is found, the function cleans up and terminates gracefully.

20 Using Cocoa Functionality in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Listing 3-4 A utility function to load a bundle from the application's Frameworks folder void MyLoadPrivateFrameworkBundle (CFStringRef framework, CFBundleRef *bundlePtr ) // 1 { CFURLRef baseURL = NULL; CFURLRef bundleURL = NULL; CFBundleRef myAppsBundle= NULL;

if (bundlePtr == NULL) goto Bail; *bundlePtr = NULL;

myAppsBundle= CFBundleGetMainBundle(); // 2 if ( myAppsBundle == NULL ) goto Bail;

baseURL = CFBundleCopyPrivateFrameworksURL (myAppsBundle); // 3 if (baseURL == NULL) goto Bail;

bundleURL = CFURLCreateCopyAppendingPathComponent ( kCFAllocatorSystemDefault, baseURL, framework, false ); // 4 if ( bundleURL == NULL ) goto Bail;

*bundlePtr = CFBundleCreate (kCFAllocatorSystemDefault, bundleURL ); // 5 if ( *bundlePtr == NULL ) goto Bail;

if ( ! CFBundleLoadExecutable (*bundlePtr) ) // 6 { CFRelease (*bundlePtr); *bundlePtr = NULL; } Bail: if (bundleURL != NULL) CFRelease (bundleURL); // 7 if (baseURL != NULL) CFRelease (baseURL); // 8 }

Here’s what the code does:

1. Takes a Core Foundation string reference that specifies the bundle name and a Core Foundation bundle reference. After the function has executed, the bundle reference refers to the bundle specified by the string reference.

2. Calls the function CFBundleGetMainBundle to obtain an instance of your Carbon application’s main bundle.

3. Calls the function CFBundleCopyPrivateFrameworksURL to get the URL of the Frameworks folder within the application bundle.

4. Calls the function CFURLCreateCopyAppendingPathComponent to create a copy of the URL obtained in the previous step and append the Core Foundation string reference that specifies the bundle name. In this case, the string reference specifies the Cocoa bundle name.

Using Cocoa Functionality in a Carbon Application 21 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

5. Calls the function CFBundleCreate to obtain the bundle reference for the Cocoa bundle specified by the URL. You use this reference when you obtain function pointers to the functions in the Cocoa bundle.

6. Calls the function CFBundleLoadExecutable to load the bundle’s main executable code into memory and dynamically link it to the Carbon application.

7. Releases the bundle URL reference (CFURLRef).

8. Releases the base URL reference (CFURLRef). You can find more information on the Core Foundation Bundle Services and URL Services functions used in Listing 3-4 from the Carbon Developer Documentation website:

http://developer.apple.com/documentation/Carbon/Carbon.html

Obtaining Function Pointers and Initializing Cocoa

After you have loaded the Cocoa bundle in your Carbon application, you can obtain function pointers to the C-callable wrapper functions that are in the Cocoa bundle. You need the Core Foundation bundle reference (CFBundleRef) to the Cocoa bundle to obtain these function pointers. The bundleRef parameter to the function MyLoadPrivateFrameworkBundle shown in Listing 3-4 (page 21) is, on return, a reference to the bundle.

Before you can obtain pointers to the C-callable wrapper functions in your Cocoa bundle, you need to define types for the function pointers you want to obtain, as shown in Listing 3-5.

Listing 3-5 Defining data types for function pointers

typedef CFRange (*CheckSpellingOfStringProc)(CFStringRef, int); typedef void (*IgnoreWordProc)(CFStringRef, int); typedef CFArrayRef (*GuessesForWordProc)(CFStringRef); typedef void (*InitializeCocoa)();

Then you can declare function pointers for each data type, as shown in Listing 3-6. Declaring function pointers as shown can make the code more readable.

Listing 3-6 Declaring function pointers

CheckSpellingOfStringProc MyCocoaCheckSpellingOfString; IgnoreWordProc MyCocoaIgnoreWord; GuessesForWordProc MyCocoaGuessesForWord; InitializeCocoa MyCocoaInitializeCocoa;

When you call your function to load the bundle, you obtain a bundle reference. For example, if you declare

CFBundleRef gMyCocoaBundle;

then when you call your function to load the bundle (as shown in the following line of code) gMyCocoaBundle is a reference to your Cocoa bundle.

22 Using Cocoa Functionality in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

MyLoadPrivateFrameworkBundle (CFSTR("MyCocoa.bundle"), &gMyCocoaBundle );

To obtain function pointers to the C-callable wrapper functions in the Cocoa bundle, you pass the bundle reference to the Core Foundation Bundle Services functions CFBundleGetFunctionPointerForName or CFBundleGetFunctionPointersForNames. The first function obtains a pointer for one function, while the other obtains a table of function pointers.

The function CFBundleGetFunctionPointerForName takes two parameters: a bundle reference to the bundle you want to search and a Core Foundation string reference that specifies the name of the function you want to locate.

To obtain a function pointer to the initialization function (shown in Listing 3-3 (page 20)) you pass the bundle reference you obtained from the MyLoadPrivateFrameworkBundle function (shown in Listing 3-4 (page 21)) along with the string reference, as shown here:

MyCocoaInitializeCocoa = (InitializeCocoa) CFBundleGetFunctionPointerForName ( gSpellingBundle, CFSTR ("InitializeCocoa") );

Before you obtain function pointers to the other C-callable wrapper functions, you must make sure the function pointer to the initialization function isn’t NULL. If it is not NULL, call the initialization function and make sure that the function executes successfully before you continue.

Calling C-Wrapper Functions From Your Carbon Application

You can use the C-callable wrapper functions for which you’ve obtained function pointers as needed in your Carbon application. Listing 3-7 shows how to call a C-callable wrapper function (CheckSpellingOfString) from your Carbon application’s event handler. (You can see this code in context by downloading the Spelling Checker application from the developer sample code website.) A detailed explanation for each numbered line of code appears following the listing.

Listing 3-7 Calling a C-wrapper function from your Carbon application if (command.commandID == 'Spel') // 1 { GetControlByID (window, &controlID, &control); // 2 err = GetControlData (control, 0, kControlStaticTextCFStringTag, sizeof(CFStringRef), &stringToSpellCheck, &count); // 3 if (err == noErr) { windowInfo->range = MyCocoaCheckSpellingOfString ( stringToSpellCheck, 0); // 4 if ( windowInfo->range.length > 0 ) // 5 SetMisspelledWord (window, stringToSpellCheck, &windowInfo->range); else windowInfo->range.location = 0; }

Using Cocoa Functionality in a Carbon Application 23 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

}

Here’s what the code does:

1. Checks to see if the command ID is the one that’s issued when the user clicks the Check Spelling button.

2. Calls the Control Manager function GetControlByID to obtain the ControlRef of the Unicode TextEdit control. This is the text box that contains text typed by the user. See Figure 3-1 (page 16).

3. Calls the Control Manager function GetControlData to obtain the string typed by the user that needs to have its spelling checked.

4. Calls the C-wrapper function CheckSpellingOfString by using the function pointer (MyCocoaCheckSpellingOfString) obtained previously. Recall that the C-wrapper function CheckSpellingOfString wraps the Cocoa method checkSpellingOfString:startingAt:.

5. Uses the location information returned from the C-wrapper function to set the location of the misspelled word if one is found.

Summary

This section demonstrated how you can write a Carbon application that uses Cocoa functionality. The Cocoa functionality needed by the Carbon application is contained in a Cocoa bundle. The bundle has C-callable wrapper functions for the Cocoa methods that provide the needed functionality. The Cocoa bundle is loaded by the Carbon application at runtime. Once the bundle is loaded, the Carbon application obtains function pointers for each of the C-wrapper functions the Carbon application needs.

The code in this section is taken from the sample application, Spelling Checker, which uses Cocoa spelling checking services in a Carbon application. Although a lot of the code from the Spelling Checker application is shown in the listings in this section, not all of the code is included or explained. For example, none of the code that handles the Carbon window has been included. To see exactly how the Carbon and Cocoa pieces fit together you should download the Spelling Checker Project Builder project from the developer sample code website:

http://developer.apple.com/samplecode/index.html

Using Carbon Functionality in a Cocoa Application

Objective-C is a super set of ANSI-C, so calling Carbon functions from a Cocoa application is easy to do as long as they are not user-interface functions. A Cocoa application can always call low-level Carbon functions because Cocoa already links against the Application Services framework. To use high-level Carbon functions, a Cocoa application must import Carbon.h and link against the Carbon framework.

The following sections describe some of the situations in which you can use Carbon functions (other than user-interface ones) in a Cocoa application:

■ “Working With QuickTime Movies” (page 25)

24 Using Carbon Functionality in a Cocoa Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

■ “Accessing a Resource Fork From Cocoa” (page 26) ■ “Obtaining an FSSpec Structure” (page 26) ■ “Managing Core Foundation Objects in Cocoa” (page 27)

Working With QuickTime Movies

The Cocoa NSMovieView class displays an NSMovie (a wrapper for a QuickTime movie) in a frame and provides methods to play and edit the movie. Although there are methods for editing, sizing, setting the controller, setting play modes, and handling sound, the methods available for the NSMovieView class do not access all the functionality available for working with QuickTime movies. For example, there are no methods to set the language of a QuickTime movie if alternate language tracks are available. However, you can call any QuickTime function from your Carbon application, such as the function SetMovieLanguage. All you need to do is to initialize the Movie Toolbox by calling the QuickTime function EnterMovies.

See the QuickTime Developer Documentation website for information on the functions you can call from your application: http://developer.apple.com/documentation/quicktime/

Listing 3-8 shows one example of calling QuickTime functions from a Cocoa method. The code in the listing builds an array of track media types for a QuickTime movie. The track media types are displayed in the NSTableView control in the movies properties window. An explanation for each numbered line of code appears following the listing.

Listing 3-8 Building an array of track media types for a QuickTime movie

// Before you call QuickTime functions you must initialize the // Movie Toolbox by calling the function EnterMovies();

- (void) myBuildTrackMediaTypesArray:(Movie) qtmovie { short i;

for (i = 0; i < GetMovieTrackCount (qtmovie); ++i) // 1 { Str255 mediaName; OSErr myErr; Track movieTrack = GetMovieIndTrack (qtmovie, i+1); // 2 Media trackMedia = GetTrackMedia (movieTrack); // 3 MediaHandler trackMediaHandler = GetMediaHandler(trackMedia);

myErr = MediaGetName (trackMediaHandler, mediaName, 0, NULL); // 4 [myMovieTrackMediaTypesArray insertObject:[NSString stringWithCString:&mediaName[1] length:mediaName[0]] atIndex:i]; // 5 } }

Here’s what the code does:

Using Carbon Functionality in a Cocoa Application 25 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

1. Calls the QuickTime () function GetMovieTrackCount to obtain the number of tracks in the movie.

2. Calls the QT function GetMovieIndTrack to determine the track identifier for a track. Note that tracks start at an index value of 1.

3. Calls the QT function GetTrackMedia to obtain the media structure that contains sample data for the track.

4. Calls the QT function MediaGetName to obtain the name (Str255) of the media type. 5. Adds the media name to the NSArray as an NSString. For complete documentation see the NSMovieView class.

Accessing a Resource Fork From Cocoa

A Cocoa application that works with legacy files may need to read the resource of a Mac OS 9 file and then parse the resource data. The Resource Manager is a Carbon API so you can call the appropriate functions from within your Cocoa code, as shown in Listing 3-9. Once you read the data you can call the Cocoa method stringWithCString:length: to obtain an NSString that you can then parse.

Listing 3-9 Calling Resource Manager functions from a Cocoa application

FSRef ref; NSString* theFilePath; // the full path of the resources file if (FSPathMakeRef ([theFilePath fileSystemRepresentation,&ref,NULL]) == noErr) { short res = FSOpenResFile (&ref,fsRdPerm); if (ResError() == oErr) { // Call Resource Manager functions to read resources. CloseResFile(res); } }

Obtaining an FSSpec Structure

An FSSpec structure is used by the File Manager in Carbon to specify the name and location of a file or directory. When you call Carbon functions from a Cocoa application, you may need to pass an FSSpec structure as a parameter to a Carbon function. For example, if you call the QuickTime function FlattenMovieData, you must supply an FSSpec structure.

You can use the code in Listing 3-10 to obtain an FSSpec structure for a file that already exists. An explanation for each numbered line of code appears following the listing.

26 Using Carbon Functionality in a Cocoa Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Listing 3-10 A Cocoa method to obtain an FSSpec structure

- (BOOL) myMakeFSSpec:(FSSpec *) myFSSpecPtr fromPath:(NSString *)inPath { FSRef myFSRef; OSStatus status = FSPathMakeRef ([inPath fileSystemRepresentation], &myFSRef, NULL); // 1 if (status == noErr) status = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, myFSSpecPtr, NULL); // 2 return status == noErr; }

Here’s what the code does:

1. Calls the File Manager function FSPathMakeRef to convert a path into an FSRef structure (myFSRef).

2. Calls the File Manager function FSGetCatalogInfo to map the FSRef (myFSRef) to an FSSpec structure. You must pass the FSRef obtained in the previous call. The constant kFSCatInfoNone specifies that you don’t want any catalog information. On return, myFSSpecPtr is a pointer to the FSSpec structure for the file specified by the inPath parameter passed to the myMakeFSSpec:fromPath: method.

For complete documentation of the File Manger, see Inside Mac OS X: File Manager Reference: http://developer.apple.com/documentation/Carbon/Reference/File_Manager/index.html

Managing Core Foundation Objects in Cocoa

Cocoa and Core Foundation (Carbon) use similar memory allocation conventions to allocate, retain, and release objects. Core Foundation functions that have Copy or Create in their name return values the caller must release. All other functions return values the caller should not release. Cocoa objects created with alloc, copy, or new methods must be released by the caller, while all other return values should not be released by the caller. In addition, there is a set of data types that can be used interchangeably; these are referred to as toll-free bridged data types. (See “Interchangeable Data Types” (page 13) for a list.) As a result, you can use functions and methods from each environment in the same application. You do not need to include Carbon when you use toll-free bridged data types in a Cocoa application.

The following code calls the Cocoa method initWithCharacters to initialize a newly allocated NSString. After code that uses the string is executed, you need to release the string.

NSString *str = [[NSString alloc] initWithCharacters: ...]; // Your code that uses the string goes here. [str release];

You can achieve the same result by calling the following Carbon code. This code uses the Core Foundation function CFStringCreateWithCharacters.

Using Carbon Functionality in a Cocoa Application 27 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

CFStringRef str = CFStringCreateWithCharacters(...); // Your code that uses the string goes here. CFRelease (str);

The following code calls the Core Foundation function CFStringCreateWithCharacters, type casts the returned string as a Cocoa class NSString, and releases the string using the Cocoa method release.

NSString *str = (NSString *) CFStringCreateWithCharacters(...); // Your code that uses the string goes here. [str release];

Similarly, the following code intermixes Carbon and Cocoa but calls the Cocoa method autorelease to dispose of the string.

NSString *str = (NSString *) CFStringCreateWithCharacters(...); // Your code that uses the string goes here. [str autorelease];

Note: Because Core Foundation has no concept of autorelease, a larger percentage of Core Foundation functions are Create or Copy functions as compared to the Cocoa methods that use alloc, copy , or new. You must make sure your code releases Core Foundation objects, when appropriate, using either the release or autorelease methods.

For a complete discussion of memory management in Cocoa, see the Cocoa Programming Topic “Memory Management”.

For a complete discussion of memory management in Core Foundation, see the “Overview of Core Foundation”:

http://developer.apple.com/documentation/CoreFoundation/CoreFoundation.html

Summary

This section demonstrated how you can use Carbon functionality in a Cocoa application. Low-level Carbon functions can always be called by a Cocoa application because Cocoa links against Application Services. High-level Carbon functions can be available to a Cocoa application by importing Carbon.h and linking against the Carbon framework.

The situations in which you can call Carbon functions in a Cocoa application are not limited to the four examples in this section. The examples are to illustrate the variety of ways you can use non-UI Carbon functions in a Cocoa application. If you want to use a Carbon interface in a Cocoa application, read “Using a Carbon User Interface in a Cocoa Application” (page 39).

Using a Cocoa User Interface in a Carbon Application

You can use a Cocoa user interface in a Carbon application in the Jaguar version of Mac OS X. The system automatically provides code that allows Cocoa and Carbon to communicate user events to each other, so there are only a few tasks you must perform to enable the Cocoa user interface to work properly in Carbon. Most tasks are similar to what you would do to use non-UI

28 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Cocoa functionality in a Carbon application. That is, writing C-callable wrapper functions and obtaining function pointers to them. (See “Using Cocoa Functionality in a Carbon Application” (page 15).)

There are two major tasks you need to perform to use a Cocoa user interface in a Carbon application:

■ Write a Cocoa bundle that contains the interface you want to use, the Cocoa methods that support the interface, and C-callable wrapper functions that allow access to the Cocoa functionality needed by the Carbon application. These tasks are described in detail in “Writing the Cocoa Bundle” (page 31). ■ Write Carbon code that loads the Cocoa bundle, obtains function pointers to the C-callable wrapper functions, initializes Cocoa, and provides handlers, as appropriate, for the interface. See “Setting Up the Carbon Application to Use the Cocoa Interface” (page 36) for details on how to implement these tasks.

The tasks described in the following sections are illustrated using sample code taken from a working application—CocoaInCarbon. See “About the CocoaInCarbon Application” (page 29) for a description of the application. You can download the code for this application from the ADC Member Site Download Software area: http://connect.apple.com

Keep in mind that many parts of the CocoaInCarbon application are specific to the sample application; you need to customize the code for your own purposes.

About the CocoaInCarbon Application

When you look at the code in the subsequent sections, it may be helpful to have an idea of how the CocoaInCarbon application behaves and what the user interface looks like. When the Carbon application is launched, an empty window appears. This window, shown in Figure 3-2, is a Carbon window created with Interface Builder. The application provides a Test menu, also shown in the figure, that contains one command—Open Cocoa Window.

Using a Cocoa User Interface in a Carbon Application 29 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Figure 3-2 The Carbon user interface for the CocoaInCarbon application

When the user chooses Open Cocoa Window from the Test menu, the Carbon application loads the Cocoa bundle, gets function pointers to C-callable wrapper functions, calls the C-wrapper function that initializes Cocoa, then calls the C-wrapper function that opens the Cocoa window (shown in Figure 3-3) and makes the window active.

Figure 3-3 The Cocoa window for the CocoaInCarbon application

When the user clicks the button shown in Figure 3-3, the mouse event is received by the Carbon application. The Carbon application automatically dispatches the event to Cocoa. The button receives the mouse event and sends its action method. The action method triggers a button command handler provided by the Carbon application. The button command handler calls a C-wrapper function that sends the text “Button pressed!” to the text field below the button, as shown in Figure 3-3.

Although the sample application is obviously a contrived example, it illustrates the extent to which a Carbon application and a Cocoa bundle can communicate between each other.

30 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Writing the Cocoa Bundle

Writing the Cocoa bundle requires performing the tasks described in the following sections:

■ “Creating a New Cocoa Bundle Using Project Builder” (page 31) ■ “Writing a Common Header File” (page 31) ■ “Implementing the Controller” (page 32) ■ “Declaring the Controller Interface” (page 33) ■ “Writing C-Callable Wrapper Functions” (page 34) ■ “Creating the Cocoa Window in Interface Builder” (page 35)

Creating a New Cocoa Bundle Using Project Builder

You need to make a Cocoa bundle that contains all the Cocoa functionality needed by your Carbon application. Follow these steps to create a Cocoa bundle:

1. Open Project Builder. 2. Select New Project from the File Menu. 3. Select Cocoa Bundle from the Bundle list in the New Project window and click the Next button. 4. Type a project name and specify a location (optional), then click the Finish button.

5. Rename the default file so it has the appropriate extension, such as the .m extension.

To rename the main.c file, click the filename, choose Rename from the Project menu, then type the new name. The sample code main filename is controller.m.

Recall from “Compiler Preprocessors” (page 13) that the .m extension indicates to the compiler that the code is Objective-C.

6. Create any other files you need for the bundle. For example, the Cocoa bundle created for the CocoaInCarbon application has an interface file, controller.h. This file must be created and must be imported to the controller.m file by adding an import statement to the controller.m file. As long as you create your Cocoa bundle using Project Builder, you should not need to modify build settings and property list values. When you write your Carbon application, you need to specify the name of your bundle. This name, listed in the bundle’s property list, should be the same name you specify as the project name.

Writing a Common Header File

The Cocoa bundle and the Carbon application that loads the bundle each need to have a copy of the header file that declares any constants used to communicate events with each other. The button-press event in the CocoaInCarbon application is one such constant. Listing 3-11 shows the contents of the header file (CocoaBundle.h) that must be included in both the Cocoa bundle and the Carbon application for the CocoaInCarbon project.

Using a Cocoa User Interface in a Carbon Application 31 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Listing 3-11 The contents of the common header for the Cocoa bundle and the Carbon application

enum { kEventButtonPressed = 1 };

Implementing the Controller

The code to implement the controller for the Cocoa bundle in the CocoaInCarbon application is shown in Listing 3-12. The critical item in this code is the use of the function NSApplicationLoad. A Carbon application cannot access the Cocoa interface unless the Cocoa bundle provides a call to NSApplicationLoad. This method is not needed for Cocoa applications that load a Cocoa bundle, but it is mandatory for Carbon applications that load a Cocoa bundle. The NSApplicationLoad method is available in the Jaguar version of Mac OS X.

You can find a detailed explanation for each numbered line of code in Listing 3-12 following the listing.

Listing 3-12 Implementing the controller in the Cocoa bundle

#import "Controller.h"

static Controller *sharedController;

@implementation Controller

+ (Controller *) sharedController { return sharedController; }

- (id) init // 1 { self = [super init]; NSApplicationLoad(); // 2 if (![NSBundle loadNibNamed:@"MyWindow" owner:self]) { NSLog (@"failed to load MyWindow nib"); } sharedController = self; return self; }

- (void) setCallBack:(CallBackType) callBack // 3 { _callBack = callBack; }

- (void) showWindow // 4 { [window makeKeyAndOrderFront:nil]; }

32 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

- (void) changeText:(NSString *)text // 5 { [textField setStringValue:text]; }

- (IBAction) buttonPressed:(id)sender // 6 { (*_callBack) (kEventButtonPressed); }

@end

Here what the code does:

1. Defines the method that initializes Cocoa. You’ll write a C-callable wrapper function for this method later. See “Writing C-Callable Wrapper Functions” (page 34).

2. Calls NSApplicationLoad as required. This entry is needed for Cocoa bundles loaded by Carbon applications, but is a no-op for Cocoa applications loading this bundle. 3. Sets a callback for the controller. The callback (as you’ll see later) is provided by the Carbon application to handle the button-press event. 4. Shows the Cocoa window and makes it active and in front. You’ll write a C-callable wrapper function for this method later. See “Writing C-Callable Wrapper Functions” (page 34). 5. Displays a string in the text field of the Cocoa window. You’ll write a C-callable wrapper function for this method later. See “Writing C-Callable Wrapper Functions” (page 34). 6. Defines the Interface Builder action associated with the button in the Cocoa window. It invokes the callback (provided by Carbon) that handles the button-press event. You’ll connect the action method to its button target later. See “Creating the Cocoa Window in Interface Builder” (page 35).

Note: The Interface Builder action is an example of how an action method in Cocoa could control something in a Carbon application. The example shows that it is possible, but doesn't perform a task that requires Carbon. That is, the Cocoa bundle could update the text field itself.

Declaring the Controller Interface

The code to declare the interface for the controller is shown in Listing 3-13. The item of note in this listing is the _callBack instance variable. The Carbon application provides the callback function that’s assigned to this variable.

Listing 3-13 Declaring the interface for the controller

#import #import "CocoaBundle.h" typedef OSStatus (*CallBackType)(int);

@interface Controller : NSObject

Using a Cocoa User Interface in a Carbon Application 33 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

{ id window; id textField; CallBackType _callBack; }

- (void)setCallBack:(CallBackType)callBack; - (void)showWindow; + (id)sharedController; @end

Writing C-Callable Wrapper Functions

Listing 3-14 shows the C-callable wrapper functions that are part of the Cocoa bundle. These functions are in the Controller.m file in the CocoaInCarbon project. Each function in Listing 3-14 wraps around one of the methods shown in Listing 3-12 (page 32). After the Carbon application loads the Cocoa bundle, the application can obtain function pointers to these C-callable wrapper functions.

One of the critical items in this code is the use of NSAutoreleasePool. For a Cocoa bundle loaded by a Carbon application, you must set up autorelease pools as needed. A detailed explanation for each numbered line of code appears following the listing.

Listing 3-14 C-callable wrapper functions for the Cocoa bundle

OSStatus initializeBundle (OSStatus (*callBack)(int)) // 1 { Controller *controller; NSAutoreleasePool *localPool;

localPool = [[NSAutoreleasePool alloc] init]; // 2 controller = [[Controller alloc] init]; // 3 [controller setCallBack:callBack]; // 4 [localPool release]; // 5 return noErr; }

OSStatus orderWindowFront(void) // 6 { NSAutoreleasePool *localPool;

localPool = [[NSAutoreleasePool alloc] init]; [[Controller sharedController] showWindow]; [localPool release]; return noErr; }

OSStatus changeText (CFStringRef message) // 7 { NSAutoreleasePool *localPool;

localPool = [[NSAutoreleasePool alloc] init]; [[Controller sharedController] changeText:(NSString *)message]; // 8 [localPool release];

34 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

return noErr; }

Here’s what the code does:

1. Defines a C-callable wrapper function that must be called by the Carbon application to initialize Cocoa. 2. Allocates and initializes NSAutoreleasePool, as required. You must do this for each C- callable wrapper function. See “C-Callable Wrapper Functions” (page 12) for more information.

3. Calls the init method. Recall that this method makes the required call to NSApplicationLoad. 4. Assigns the callback function provided by the Carbon application to the controller’s callback instance variable. 5. Releases the local autorelease pool.

6. Defines a C-callable wrapper function for the showWindow method.

7. Defines a C-callable wrapper function for the changeText: method.

8. Casts a CFStringRef to NSString*. Recall from Chapter 2, “Carbon and Cocoa Integration Concepts”, that the CFString data type is toll-free bridged to the NSString class.

Creating the Cocoa Window in Interface Builder

You must use Interface Builder to create the Cocoa window and connect targets to the appropriate actions. When the button in the sample application (see Figure 3-4) is pressed, it triggers the buttonPressed action method. Recall from Listing 3-12 (page 32) that the buttonPressed method calls the callback function assigned to the controller. In the CocoaInCarbon application, the callback is provided by the Carbon application.

Using a Cocoa User Interface in a Carbon Application 35 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Figure 3-4 Connecting a button to a button-pressed action

For more information on creating a Cocoa window in Interface Builder, see the Currency Converter Tutorial:

http://developer.apple.com/macosx/gettingstarted/

Setting Up the Carbon Application to Use the Cocoa Interface

Your Carbon application must perform a number a tasks to use the interface provided by the Cocoa bundle. These tasks are described in the following sections:

■ “Including the Common Header File” (page 36) ■ “Loading the Cocoa Bundle” (page 37) ■ “Obtaining Function Pointers and Initializing Cocoa” (page 37) ■ “Writing a Bundle Command Handler” (page 38)

Including the Common Header File

The Cocoa bundle and the Carbon application that loads the bundle each need to have a copy of the header file that declares any constants used to communicate events with each other. Listing 3-11 (page 32) shows the header file (CocoaBundle.h) that needs to be included with both the Cocoa bundle and the Carbon application for the CocoaInCarbon project. See “Writing a Common Header File” (page 31) for details.

36 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Loading the Cocoa Bundle

You need to obtain the bundle reference (CFBundleRef) to your Cocoa bundle in order to load the bundle and obtain function pointers to the C-callable wrapper functions needed by your Carbon application. Listing 3-4 (page 21) shows a utility function (MyLoadPrivateFrameworkBundle) that loads a bundle from an application’s Frameworks folder.

For the CocoaInCarbon application, the point at which you’d call the function MyLoadPrivateFrameworkBundle is when your Carbon application responds to the event to open the Cocoa window. This event occurs when the user chooses Open Cocoa Window from the Test menu. (See “About the CocoaInCarbon Application” (page 29).)

Obtaining Function Pointers and Initializing Cocoa

After you have loaded the Cocoa bundle in your Carbon application, you can obtain function pointers to the C-callable wrapper functions that are in the Cocoa bundle. You need the Core Foundation bundle reference (CFBundleRef) to the Cocoa bundle to obtain these function pointers. The bundleRef parameter to the function MyLoadPrivateFrameworkBundle shown in Listing 3-4 (page 21) is, on return, a reference to the bundle.

To obtain function pointers to the C-callable wrapper functions in the Cocoa bundle, you pass the bundle reference to the Core Foundation Bundle Services functions CFBundleGetFunctionPointerForName or CFBundleGetFunctionPointersForNames. The first function obtains a pointer for one function, while the other obtains a table of function pointers.

The function CFBundleGetFunctionPointerForName takes two parameters: a bundle reference to the bundle you want to search and a Core Foundation string reference that specifies the name of the function you want to locate.

Once you have obtained a function pointer to the initialization function, you must call the function, as shown in Listing 3-15. It’s important to check for errors and take the appropriate action should an error occur.

The initialization function takes a callback function as a parameter. In Listing 3-15, the function handleBundleCommand is passed to the initialization function. The handleBundleCommand function is described in “Writing a Bundle Command Handler” (page 38).

Listing 3-15 Calling the initializeBundle C-wrapper function using a function pointer err = (*funcPtr)(handleBundleCommand); require_noerr(err, CantInitializeBundle);

After the initialization function has executed successfully, you can obtain and call function pointers to other C-wrapper functions that are in the Cocoa bundle. There are two additional C-wrapper functions in the CocoaInCarbon application—orderWindowFront and changeText.

Listing 3-16 shows the code that obtains a function pointer to the orderWindowFront function and then calls the function. The function is called when the user chooses Open Cocoa Window from the Test menu in the Carbon portion of the interface. Note the code checks for errors and takes appropriate action.

Using a Cocoa User Interface in a Carbon Application 37 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Listing 3-16 Calling the C-wrapper function to open the Cocoa window

showPtr = CFBundleGetFunctionPointerForName (bundleRef, CFSTR ("orderWindowFront")); require (showPtr, CantFindFunction);

err = (*showPtr)(); require_noerr(err, CantCallFunction);

Writing a Bundle Command Handler

The Carbon code in the CocoaInCarbon application provides a function (handleBundleCommand) that is passed as a parameter to the C-wrapper function that initializes Cocoa. When the user presses the button in the Cocoa window, the action method associated with the button invokes the handleBundleCommand function. In the CocoaInCarbon application, this function just sends a string back to the Cocoa bundle, so it is not really necessary. However, the handleBundleCommand function does illustrate how a more complex application could be notified of user actions in the Cocoa bundle.

The handleBundleCommand function is shown in Listing 3-17. A detailed explanation for each numbered line of code appears following the listing.

Listing 3-17 A Carbon function that handles a Cocoa button press

static OSStatus handleBundleCommand (int commandID) { OSStatus osStatus = noErr; if (commandID == kEventButtonPressed) // 1 { OSStatus (*funcPtr)(CFStringRef message);

funcPtr = CFBundleGetFunctionPointerForName (bundleRef, CFSTR("changeText")); // 2 require (funcPtr, CantFindFunction); osStatus = (*funcPtr)(CFSTR("Button pressed!")); // 3 require_noerr (osStatus, CantCallFunction); } CantFindFunction: CantCallFunction: return osStatus; }

Here’s what the code does:

1. Checks to make sure it’s a button-press event, the only event handled by the function.

2. Obtains a function pointer to the C-callable wrapper function changeText.

3. Calls the changeText C-wrapper function with the string “Button pressed!”

38 Using a Cocoa User Interface in a Carbon Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Summary

This section demonstrated how you can write a Carbon application that uses a Cocoa user interface, possible only in the Jaguar and later versions of Mac OS X. The Cocoa user interface and all methods needed to handle the interface must be provided in a Cocoa bundle. In addition, the Cocoa bundle must call the NSLoadApplication function to initialize the bundle for use in a Carbon application.The Cocoa bundle supplies C-callable wrapper functions for any method whose functionality is needed by the Carbon application.

The Carbon application must load the Cocoa bundle at runtime and obtain function pointers to any C-callable wrapper functions the application needs. One of the C-wrapper function must be an initialization function. After the initialization function has executed successfully, the Carbon application can (through using function pointers) call any of the C-wrapper functions it needs.

The code in this section is taken from the sample application CocoaInCarbon. Although most of the code from the CocoaInCarbon application is shown in the listings in this section, not all of the code is included. You should download the CocoaInCarbon Project Builder project to see how the Carbon and Cocoa pieces fit together. You can get the sample code from the ADC Member Site Download Software area:

http://connect.apple.com

Using a Carbon User Interface in a Cocoa Application

This section describes how you can use a Carbon user interface in a Cocoa application. In the Jaguar and later versions of Mac OS X, the system automatically provides code that allows Cocoa and Carbon to communicate user events to each other. Communication between the two application environments is what enables a Cocoa application to use a Carbon user interface.

Using a Carbon interface in a Cocoa application requires you to perform the following major tasks:

■ “Creating the Carbon User Interface” (page 41). You use Interface Builder to create a Carbon window and add controls and other items to the window. ■ “Setting Up the Cocoa Application to Use the Carbon User Interface” (page 41). You load the nib file that specifies the Carbon window, create an NSWindow to allow management of the Carbon window with Cocoa methods, and show the window. The tasks described in the following sections are illustrated using sample code taken from a working application—CarbonInCocoa. See “About the CarbonInCocoa Application” (page 40) for a description of the application. You can download the code for this application from the ADC Member Site Download Software area:

http://connect.apple.com

Using a Carbon User Interface in a Cocoa Application 39 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

About the CarbonInCocoa Application

As you read the subsequent sections it may be helpful to have an idea of how the CarbonInCocoa application behaves and what the user interface looks like. When the Cocoa application is launched, the window shown in Figure 3-5 appears. This window is a Cocoa window created with Interface Builder.

Figure 3-5 The Cocoa user interface for the CarbonInCocoa application

When the user clicks the Show Carbon Window button, the window shown in Figure 3-6 opens and becomes the frontmost and active window. The window in Figure 3-6 is a Carbon window created with Interface Builder. When the user clicks one of the windows, that window becomes the active window. The user can type text in the text field in either window, copy the text from one window to the other, or cut the text from either window.

Figure 3-6 The Carbon user interface for the CarbonInCocoa application

The CarbonInCocoa application is simple. Its purpose is to show how little code you need to provide to use a Carbon user interface in a Cocoa application.

40 Using a Carbon User Interface in a Cocoa Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Creating the Carbon User Interface

You should use Interface Builder to create the Carbon user interface. Follow these steps to create a Carbon window:

1. Open Interface Builder. 2. Under Carbon in the Starting Point select Window and click New. 3. When the window appears, drag items form the Carbon palette to the window to create the interface.

See Creating a User Interface With Interface Builder for details on how to use Interface Builder, available from this website: http://developer.apple.com/macosx/gettingstarted/

See Inside Mac OS X: Aqua Human Interface Guidelines for information on making an Aqua-compliant interface, available from this website: http://developer.apple.com/documentation/UserExperience/Conceptual/AquaHIGuidelines/index.html

4. Save the file. Interface Builder saves the interface in a nib file. (The “ib” in “nib” represents Interface Builder.) A nib file contains an archive of the interface. When you want to show the interface, you need to unarchive the nib file. You’ll see how to do this in “Loading the Nib File” (page 42).

Setting Up the Cocoa Application to Use the Carbon User Interface

You must perform the following tasks to enable your Cocoa application to use the Carbon user interface:

■ “Adding the Nib File that Specifies the Carbon Interface” (page 41) ■ “Declaring the Interface for the Controller” (page 42) ■ “Loading the Nib File” (page 42) ■ “Creating an NSWindow Object for the Carbon Window” (page 43) ■ “Showing the Carbon Window” (page 43)

Adding the Nib File that Specifies the Carbon Interface

To add the nib fie that specifies the Carbon interface, do the following:

1. Open the Project Builder project for your Cocoa application. 2. Choose Add Files from the Project menu. 3. Locate the nib file and double-click its name.

Using a Carbon User Interface in a Cocoa Application 41 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

4. Click Add in the dialog that appears. If your Cocoa application has more than one target, you need to choose the appropriate target before you click Add.

Declaring the Interface for the Controller

Your controller for the CarbonInCocoa application has two instance variables: a WindowRef for the Carbon window and an NSWindow object. The NSWindow object allows management of the Carbon window using Cocoa methods. (See “Showing the Carbon Window” (page 43).)

The sample application’s controller has no other instance variables, but your application would need to declare any other instance variables that are appropriate. Listing 3-18 shows the declaration for the controller in the CarbonInCocoa application.

Listing 3-18 The declaration for the controller

@interface MyController : NSObject { WindowRef window; NSWindow *cocoaFromCarbonWin; } @end

Loading the Nib File

The Cocoa nib file is loaded automatically when the Cocoa application launches. But you must explicitly load the nib file that contains the Carbon interface. For the CarbonInCocoa application, the nib file is loaded in response to the user clicking the Show Carbon Window button in the Cocoa window.

Listing 3-19 shows the code needed to load the Carbon nib file at runtime. An explanation for each numbered line of code appears following the listing.

Listing 3-19 Loading a nib file for a Carbon window

CFBundleRef bundleRef; IBNibRef nibRef; OSStatus err;

bundleRef = CFBundleGetMainBundle(); // 1 err = CreateNibReferenceWithCFBundle (bundleRef, CFSTR("SampleWindow"), &nibRef); // 2 if (err!=noErr) NSLog(@"failed to create nib reference");

CFRelease(bundleRef); // 3

err = CreateWindowFromNib (nibRef,

42 Using a Carbon User Interface in a Cocoa Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

CFSTR("CarbonWindow"), &window); // 4 if (err!=noErr) NSLog(@"failed to create carbon window from nib");

DisposeNibReference (nibRef); // 5

Here’s what the code does:

1. Calls the Core Foundation Bundle Services function CFBundleGetMainBundle to obtain an instance of the application's main bundle. You need this reference for the call.

2. Calls the Interface Builder (IB) Services function CreateNibReferenceWithCFBundle to create a reference to the Carbon window’s nib file. The Core Foundation string you provide must be the name of the nib file, but without the .nib extension. 3. Releases the bundle reference, as it is no longer needed.

4. Calls the IB Services function CreateWindowFromNib to unarchive the Carbon window from the nib file. On return, window is a WindowRef to the Carbon window. Recall from “Declaring the Interface for the Controller” (page 42) that window is a controller instance variable.

5. Calls the IB Services function DisposeNibReference to dispose of the reference to the nib file. You should call this function immediately after you have finished unarchiving an object.

Creating an NSWindow Object for the Carbon Window

You do not need to create an NSWindow object for the Carbon window. However, doing so lets you manage the Carbon window using Cocoa methods rather than Carbon functions, which may be desirable in some applications and is what the CarbonInCocoa application does.

You can create an NSWindow object for a Carbon window by allocating an NSWindow object, then initializing the object using the initWithWindowRef: method, as shown in the following line of code: cocoaFromCarbonWin = [[NSWindow alloc] initWithWindowRef:window];

Recall from “Loading the Nib File” (page 42) that window is a reference to the Carbon window.

You can find more information about the NSWindow object and the initWithWindowRef: method from the NSWindow documentation.

Showing the Carbon Window

Because you created an NSWindow object for the Carbon window, you can call the makeKeyAndOrderFront: method to show the window instead of calling the Carbon function ShowWindow. You show the Carbon window with the following line of code:

[cocoaFromCarbonWin makeKeyAndOrderFront:nil];

Using a Carbon User Interface in a Cocoa Application 43 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. CHAPTER 3 Carbon and Cocoa Integration Tasks

Summary

This section demonstrated how you can write a Cocoa application that uses a Carbon user interface, which is possible only in Jaguar and later versions of Mac OS X. The Carbon user interface (a window with a variety of controls in it) is defined in an Interface Builder nib file that is included as a resource in the Cocoa application. At runtime, the Cocoa application must load the Interface Builder file that contains the interface and then show the Carbon window. The sample code also shows how to create an NSWindow object for the Carbon window. Doing this provides the option of managing the Carbon window using Cocoa methods.

The code in this section is taken from the sample application CarbonInCocoa. Although most of the code from the CarbonInCocoa application is shown in the listings in this section, not all of the code is included. You should download the CarbonInCocoa Project Builder project for a more complete picture of how the Cocoa and Carbon pieces fit together. You can get the sample code from the ADC Member Site Download Software area:

http://connect.apple.com

44 Using a Carbon User Interface in a Cocoa Application Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. REVISION HISTORY

Document Revision History

Table RH-1 describes revisions to Inside Mac OS X: Integrating Carbon and Cocoa in Your Application.

Table RH-1 Document revision history

Date

Jan. 3, 2003 Where possible, links to documentation are now to the local version of the document. Corrected some capitalization errors in Cocoa class names.

Dec. 11, 2002 Updated formatting.

May 2002 Preliminary version of this document.

45 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved. REVISION HISTORY Document Revision History

46 Preliminary © 2003, 2004 Apple Computer, Inc. All Rights Reserved.