This HOWTO guide explains how to use Clozure CL (formerly
- OpenMCL) to create a Cocoa application that is functionally
- identical to Apple's
-
- Currency Converter example. The most important
- difference between Apple's example and this one is that this
- one is implemented in Common Lisp instead of Objective-C. It
- uses Clozure CL's Objective-C bridge to provide communication
- between the Lisp code that you write and Apple's Cocoa
- frameworks. The resulting application looks and acts just
- like any other Cocoa application.
-
-
This HOWTO doesn't discuss all the background information
- that Apple's tutorial covers. Instead, we assume that you have
- Apple's document handy for reference, and we just describe the
- specific steps needed to build the example using
- Apple's InterfaceBuilder application and Clozure CL.
-
-
An observant programmer will look at the code in this example
- and say, "well, that's trivial! Why create all those classes and
- connections and so forth just to perform a multiplcation?" That
- observation is correct: the actual work done by the Currency
- Converter application is trivial—both in the Lisp and the
- Objective-C versions. The point of this example (and Apple's) is
- not to show you how to perform a multiplication. The point is to
- show you how Apple's frameworks implement and support the
- Model-View-Controller paradigm, and how you can use that support
- to build Cocoa applications. In fact, the work done by the
- application is intentionally trivial, to emphasize the
- frameworks rather than the particulars of the application.
-
-
This HOWTO has the additional purpose of showing you how
- Clozure CL makes it possible to do exactly the same thing in
- Lisp that you can do with Objective-C, so that you will
- understand how to use Lisp with Apple's frameworks.
-
-
The current version of the Clozure CL Objective-C bridge
- includes code that was formerly distributed separately as the
- "Bosco" application framework. Because that framework has been
- integrated with Clozure CL proper, it no longer exists as a
- separate project.
-
-
-
-
Apple's Currency Converter Example
-
-
-
-
It will be helpful in understanding this example if you can
- easily refer to
- Apple's
- Currency Converter tutorial while working through this
- HOWTO. You might consider opening a separate window or tab, and
- keeping the Apple example handy while you work.
-
-
In some ways, the Lisp version of the example is simpler
- than the Objective-C example, but the basic concepts are the
- same. In particular, the Lisp example follows the same
-
- Model-View-Controller paradigm that the Apple tutorial
- uses. If you are new to Cocoa programming, or if you are not
- familiar with how it uses the Model-View-Controller paradigm,
- it's probably a good idea to read through the Apple example in
- full, paying special attention to the Model-View-Controller
- section. Once you've done that, keep the Apple pages handy in
- a window for easy reference.
-
-
This Common Lisp version of the Currency Converter example
- uses Apple's InterfaceBuilder application to build a window and
- main menu, and then uses Common Lisp code to load and operate
- that user interface. The Common Lisp code relies on Clozure CL's
- Objective-C bridge to provide communication between the running
- Lisp code and Apple's Cocoa frameworks. Once the code is
- complete, we use the BUILD-APPLICATION function to save a
- working Cocoa application bundle. That bundle looks and acts
- just like any other Cocoa application.
-
-
-
-
-
Requirements Before You Start
-
-
-
-
In order to build this example you will need:
-
-
-
Mac OS X Leopard (version 10.5.x) or Mac OS X Tiger
- (version 10.4.x)
-
Apple's XCode development tools
-
Apple's InterfaceBuilder application (included with XCode)
-
-
-
-
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/bosco.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/bosco.jpg
deleted file mode 100644
index c7b33f661..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/bosco.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/cc1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/cc1.jpg
deleted file mode 100644
index a4875c5b1..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/cc1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/cc2.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/cc2.jpg
deleted file mode 100644
index a5a08cce5..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/cc2.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/finder-win1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/finder-win1.jpg
deleted file mode 100644
index 4335b9312..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/finder-win1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/finder-win2.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/finder-win2.jpg
deleted file mode 100644
index d8f79010a..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/finder-win2.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard1.jpg
deleted file mode 100644
index 0bfb12724..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard2.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard2.jpg
deleted file mode 100644
index 6622b5ce9..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard2.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard3.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard3.jpg
deleted file mode 100644
index 9e3f726a9..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard3.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard4.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard4.jpg
deleted file mode 100644
index 3843b64a9..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard4.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard5.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard5.jpg
deleted file mode 100644
index 284ec5f36..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard5.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard6.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard6.jpg
deleted file mode 100644
index b6792ea00..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard6.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard7.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard7.jpg
deleted file mode 100644
index 678eb2c3c..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard7.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard8.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard8.jpg
deleted file mode 100644
index 5e153c221..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-leopard8.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger1.jpg
deleted file mode 100644
index 39d598471..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger10.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger10.jpg
deleted file mode 100644
index 6c998cf4c..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger10.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger11.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger11.jpg
deleted file mode 100644
index 441c6e41a..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger11.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger12.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger12.jpg
deleted file mode 100644
index d74310fca..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger12.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger13.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger13.jpg
deleted file mode 100644
index 2a3ff481e..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger13.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger14.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger14.jpg
deleted file mode 100644
index d9b184e6d..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger14.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger15.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger15.jpg
deleted file mode 100644
index dd5002e57..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger15.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger16.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger16.jpg
deleted file mode 100644
index aff537e45..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger16.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger17.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger17.jpg
deleted file mode 100644
index 25aaa52f4..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger17.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger18.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger18.jpg
deleted file mode 100644
index a74dcf85a..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger18.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger19.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger19.jpg
deleted file mode 100644
index dd1b4bb68..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger19.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger2.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger2.jpg
deleted file mode 100644
index 94636dc35..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger2.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger20.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger20.jpg
deleted file mode 100644
index 09bbe5174..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger20.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger21.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger21.jpg
deleted file mode 100644
index df6b25659..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger21.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger22.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger22.jpg
deleted file mode 100644
index 613a73dda..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger22.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger3.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger3.jpg
deleted file mode 100644
index 7eec55e43..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger3.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger4.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger4.jpg
deleted file mode 100644
index aed4e712f..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger4.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger5.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger5.jpg
deleted file mode 100644
index 4523ca897..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger5.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger6.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger6.jpg
deleted file mode 100644
index 03d9716db..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger6.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger7.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger7.jpg
deleted file mode 100644
index 2a1e4a56c..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger7.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger8.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger8.jpg
deleted file mode 100644
index e4dcd7449..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger8.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger9.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger9.jpg
deleted file mode 100644
index 54abadee6..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin-tiger9.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/ibwin1.jpg
deleted file mode 100644
index f819554a7..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/ibwin1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/listener1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/listener1.jpg
deleted file mode 100644
index 92592e740..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/listener1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/images/term1.jpg b/examples/cocoa/currency-converter/HOWTO_files/images/term1.jpg
deleted file mode 100644
index 3508829f1..000000000
Binary files a/examples/cocoa/currency-converter/HOWTO_files/images/term1.jpg and /dev/null differ
diff --git a/examples/cocoa/currency-converter/HOWTO_files/pages/build_app.html b/examples/cocoa/currency-converter/HOWTO_files/pages/build_app.html
deleted file mode 100644
index 3bcbc78af..000000000
--- a/examples/cocoa/currency-converter/HOWTO_files/pages/build_app.html
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
- CurrencyConverter HOWTO
-
-
-
-
-
-
-
Building the Application
-
-
-
-
Both the user interface and the behavior of the
- CurrencyConverter are complete now. All that remains for us to
- do is to build the application executable into a Cocoa
- application bundle. Apple's tutorial relies on XCode to build
- the application from Objective-C source files; we will use the
- Clozure CL IDE to build it from our Lisp source file.
-
-
We build the application using the optional
- BUILD-APPLICATION feature, distributed as part of Clozure CL. The
- steps to build the Cocoa application are:
-
-
-
Load the application code into the IDE
-
Load the BUILD_APPLICATION feature
-
Run BUILD_APPLICATION with the proper arguments
-
-
-
This sequence of steps causes Clozure CL to construct a Cocoa
- application bundle and write out the application executable to
- it, then quit. If all goes well, you should be able to run the
- application by double-clicking it, and use the UI you built in
- InterfaceBuilder to convert currencies.
-
-
-
-
Building the Application, Step-by-Step
-
-
-
-
-
Launch the Clozure CL IDE. It's safest to build the
- application with a fresh IDE session, so if you have it
- running, you may wish to quit and relaunch before following
- the rest of the steps.
-
-
For convenience, set the working directory to your
- "currency-converter" folder. For example, you can do
- something like this (using your pathnames in place of mine, of
- course:):
Run BUILD-APPLICATION (be sure to correct the pathname
- to your CurrencyConverter nibfile. It is safest to use a full,
- absolute pathname—not the relative pathname you see
- below):
By default, BUILD-APPLICATION constructs the application
- bundle in the current working directory. If you followed the
- instructions here, that means it will build
- CurrencyConverter.app in your currency-converter folder. You
- can control where BUILD-APPLICATION puts the application bundle
- by passing a pathname as the value of the keyword argument
- :DIRECTORY, like so:
If all goes well, BUILD-APPLICATION constructs an
- application bundle, copies "CurrencyConverter.nib" into it,
- writes the application executable, and quits. You should now
- be able to launch CurrencyConverter.app by double-clicking
- the application icon:
-
-
-
-
-
-
CurrencyConverter.app launches and displays your user
- interface, which you can then use to convert currencies:
-
-
-
-
-
-
-
-
-
-
Correcting the Application Name
-
-
-
-
You'll notice when you run the application that, even though
- you named it CurrencyConverter, the name in the main menu
- appears as "Clozure CL". That's because OS X takes the
- application's name, not from the application bundle's name, nor
- from the running code, but from an InfoPlist.strings file hidden
- inside the application bundle. To make the name appear
- correctly in the running application, you need to edit the file
Find the entry named "CFBundleName" and change its value
- from "Clozure CL" to "CurrencyConverter". The application's name
- in the main menu bar should now appear correctly, as
- "CurrencyConverter". You may also want to change the other
- strings in the "InfoPlist.strings" file.
The next step in creating a Lisp version of the currency
- converter application is to construct the user
- interface. Apple's
- tutorial
- describes in detail how to do this.
-
-
-
Apple's Tutorial
-
-
-
Apple's tutorial explains how to use InterfaceBuilder to create the
- user interface, and how to use XCode to create project files and
- write Objective-C code. Our project uses Lisp instead of
- Objective-C, and does not use XCode project files, so you can skip
- the part of the tutorial that explains how to use XCode.
-
-
-
Using InterfaceBuilder to Create the UI
-
-
-
We'll begin by using Apple's InterfaceBuilder application to
- create a nibfile. The nibfile contains
- archived versions of the Objective-C objects that define the
- application's user interface. When you launch an application,
- Mac OS X uses the archived objects in the nibfile to create the
- windows and menus you see on the screen.
-
-
Start by locating Apple's InterfaceBuilder application. If
- you installed Apple's Developer Tools, InterfaceBuilder should
- be in the folder "/Developer/Applications/":
-
-
-
-
-
-
-
NOTE: If you have not installed Apple's Developer Tools, you should
- do that now. You will not be able to build the CurrencyConverter
- example without them. The Developer Tools are distributed as an
- optional install with Mac OS X 10.5 ("Leopard"). Look for the
- "XCode Tools" package in the "Optional Installs" folder on the
- Mac OS 10.5 install disk.
-
-
Once you have located InterfaceBuilder, double-click to launch
- the application. InterfaceBuilder presents a window you can use
- to choose a template for the nibfile you are going to create.
-
-
-
-
-
-
Click the "Application" icon and then click the "Choose" button to
- create an application nibfile. InterfaceBuilder creates a new
- application nibfile, but doesn't immediately save it. The
- Objective-C objects that represent the new application's
- interface appear in a new untitled window:
-
-
-
-
-
-
The intial window and menubar also appear on the screen. The
- new application's name appears in the menus as
- "NewApplication". Save the new nibfile into the
- "currency-converter" folder that you created earlier
- (on this
- page). InterfaceBuilder 3.0 gives you a choice of file
- formats when you save a new nibfile; use the "NIB 3.x"
- format—the "XIB 3.x" format works fine for editing your
- user interface, but will not work correctly if you try to use it
- in a working application. Give the new file the name
- "CurrencyConverter.nib".
-
-
-
NOTE: Most Objective-C application projects use a main
- nibfile called "MainMenu.nib", and if you use XCode to create
- a new application project, it creates a nibfile with that
- name. Apple's CurrencyConverter tutorial assumes that the
- name of the main nibfile is "MainMenu.nib".
-
-
So, why do we tell you to use a different name? Clozure CL
- has a main nibfile built into it, whose name is
- "MainMenu.nib". Normally you don't see it, and don't even
- need to know that it exists. But the Clozure CL
- application-building tools create a new application by
- copying resources from the Clozure CL application, so that
- your new application has available to it all the built-in
- Clozure CL tools. We ask you to name your nibfile
- "CurrencyConverter.nib" so that it can coexist with the
- Clozure CL main nibfile without causing any problems.
-
-
This difference between a Lisp project and an Objective-C
- project might be a little confusing at first. Just try to keep
- in mind that whenever Apple's tutorial refers to the
- "MainMenu.nib" file, it means the file we have just created
- and named "CurrencyConverter.nib". In a Clozure CL project,
- "MainMenu.nib" is the name of the main Lisp nibfile, not your
- application's main nibfile.
-
-
-
-
Skip straight to the part of Apple's tutorial
-called Defining
-the View: Building the User Interface. Read the introduction to
-nibfiles, and follow the instructions to create the Currency Converter
-interface. (Remember that when the tutorial tells you to open and edit
-"MainMenu.nib", you will instead open and edit your
-"CurrencyConverter.nib".) When you reach the end of the section
-called Test
-the Interface, and move on to the short section afterward
-called What's
-Next, you are done creating the interface for your
-application. Save your nibfile and continue with the next section of
-this HOWTO.
-
-
-
What if You Need to Use InterfaceBuilder 2.x?
-
-
-
If you are still using Mac OS X 10.4.x ("Tiger"), you can still
- create a working nibfile and you can still follow the instructions
- in this HOWTO to create a Cocoa application with Clozure CL. The
- main obstacle to doing so is that the earlier versions of
- InterfaceBuilder have a significantly different user interface, and
- so you may find it hard to follow Apple's tutorial when working with
- InterfaceBuilder.
-
-
If you are working with Mac OS X 10.4.x ("Tiger"), you can
- look here to find a description
- of how to build the user interface files with the earlier version of
- InterfaceBuilder. When you have finished building your user
- interface, you can continue with the next
- section, "Creating a Lisp File".
-
-
One other thing: if you are using Mac OS X 10.4.x ("Tiger"), you
- will be able to build Cocoa applications only on PPC Macs. The
- Clozure CL Objective-C support for Intel systems works only on Mac
- OS X 10.5.x ("Leopard").
-
-
-
-
Adding Custom Classes to the nibfile
-
-
-
Once the user interface for your application looks right, there is
- still one important task to complete before you can use it. You must
- record some information in the nibfile about the classes of the
- objects, so that the application can create them with the right
- connections in place.
-
-
When you use XCode to write an Objective-C application,
- InterfaceBuilder can read the Objective-C header files and use the
- information in them to create descriptions of the classes in the
- Objective-C code. When the application is written in Lisp,
- InterfaceBuilder can't read the class descriptions from the code,
- and so we'll have to manually tell the nibfile about any classes
- that we use in the user interface.
-
-
As you will see in the following sections, we'll use Lisp code to
- define two Objective-C classes: Converter, and
- ConverterController. The Converter class implements the method that
- performs the actual currency conversion for our application; the
- ConverterController class provides communication between the user
- interface and the Converter object. We need a way to create
- instances of these two classes in the nibfile, so that launching the
- application sets up all the objects correctly.
-
-
-
Create Instances of Custom Classes
-
-
-
In InterfaceBuilder's Library window, select the Cocoa Objects and
- Controllers view:
-
-
-
-
-
-
Drag an Object from the Library window and drop it into the main
- CurrencyConverter window:
-
-
-
-
-
-
Now tell InterfaceBuilder the name of the new object's class. With
- the Object icon selected in the main CurrencyConverter window,
- choose the Identity tab of the Inspector. At the top of the
- Identity view is a "Class" field; type the name of your custom
- class (in this case, "Converter") into the "Class" field and save
- the nibfile:
-
-
-
-
-
-
Repeat the previous steps to create an instance of the
- ConverterController class: drag an "Object" icon and drop it in the
- main CurrencyConverter window. Then, change the name of the
- Object's class to "ConverterController".
-
-
That's all it takes to add an instance of a custom class to the
- nibfile. We do still have to add the names of instance variables and
- actions, and we need to create the connections between the
- instances.
-
-
-
Add Outlets and Actions
-
-
-
Now, using the "+" button below the "Class Outlets" section of the
- Inspector, add outlets to the ConverterController class. The
- outlets you need to add are named "amountField", "converter",
- "dollarField", and "rateField".
-
-
-
-
-
-
We'll connect each of the "field" outlets to one of the text fields
- in the CurrencyConverter UI, and we'll connect the "converter"
- outlet to the Converter instance that we created before. When the
- application launches, it creates the Converter and
- ConverterController instances and establishes the connections that
- we've specified in the nibfile.
-
-
-
First, though, we need to tell the nibfile about actions as well as
- outlets. With the "ConverterController" instance selected, use the
- "+" button below the "Class Actions" section to add a new
- action. Name the action "convert:":
-
-
-
-
-
-
In this application, the "convert:" action is the only action
- defined for the user interface, so we are done with actions now. In
- more complex applications you may need to define many actions and
- outlets.
-
-
Now we'll connect outlets and actions to objects.
-
-
-
Add Connections
-
-
-
InterfaceBuilder enables you to connect objects by
- "Control-dragging" from one to another. To "Control-drag", you hold
- down the Control key while dragging from one object to the next.
-
-
Select the "ConverterController" instance in the nibfile's main
- window, and Control-drag a connection to the "Exchange rate" text
- field in the application's main window. (Be sure to connect to the
- text field, not to its label!) When you release the mouse button,
- InterfaceBuilder pops up a menu that lists the available
- outlets. Choose "rateField" from the menu. The "rateField" outlet of
- the "ConverterController" instance is now connected to the "Exchange
- rate" text field.
-
-
Repeat the same steps for the "Dollars" field and the "Amount"
- field, connecting them to the "dollarField" and "amountField"
- outlets, respectively.
-
-
Finally, Control-drag a connection from the "ConverterController"
- instance to the "Converter" instance. Choose "converter" from the
- popup menu to connect the "converter" field of the
- "ConverterController" instance to the "Converter" instance.
-
-
To confirm that the connections are correct, you can use the
- Connections view in the inspector. With the "ConverterController"
- instance selected, click the blue arrow icon at the top of the
- Inspector window to display connections. You should see a list of
- outlets and the types of objects they are connected to:
-
-
-
-
-
-
We need to add one more connection: from the "Convert" button in
- the application window to the "ConverterController"
- instance. Control drag a connection from the "Convert" button in the
- application window to the "ConverterController" instance in the
- nibfile's main window. InterfaceBuilder pops up a menu; choose the
- "convert:" action from the menu to connect the button to the
- action.
-
-
The nibfile now contains descriptions of the needed cusstom
- classes and their connections. You can continue with the next
- section, which explains how to write the Lisp code that implements
- the application's behavior.
If you are using Mac OS X 10.4.x ("Tiger") to build your
- application, then the Apple tutorial's section on building the
- UI may be somewhat confusing. Apple's tutorial uses
- InterfaceBuilder 3.x to show how to build an interface, and
- there were many interface changes between versions 2.x and 3.x
- of InterfaceBuilder. In this section we see how to build the UI
- using InterfaceBuilder 2.x.
-
-
-
Launch InterfaceBuilder
-
-
-
Start by locating Apple's InterfaceBuilder application. If
- you installed Apple's Developer Tools, InterfaceBuilder should
- be in the folder "/Developer/Applications/":
-
-
-
-
-
-
NOTE: If you have not
- installed Apple's Developer Tools, you should do that now. You
- will not be able to build the CurrencyConverter example
- without them. The Developer Tools are distributed as an
- optional install with Mac OS X 10.4 ("Tiger"). Look for the
- "XCode Tools" package in the "Optional Installs" folder on the
- Mac OS 10.4 install disk.
-
-
Once you have located InterfaceBuilder, double-click to launch
- the application. InterfaceBuilder presents a window you can use
- to choose a template for the nibfile you are going to create.
-
-
-
-
-
-
Make sure the "Application" option is selected in the "Cocoa"
- section and click the "New" button to create a new
- nibfile. InterfaceBuilder creates a new application nibfile, but
- doesn't immediately save it. The Objective-C objects that
- represent the new application's interface appear in a new
- untitled window:
-
-
-
-
-
-
The intial window and menubar also appear on the screen. The
- new application's name appears in the menus as
- "NewApplication". Save the new nibfile into the
- "currency-converter" folder that you created earlier
- (on this page). Give the new
- file the name "CurrencyConverter.nib"
-
-
-
NOTE: Most Objective-C application projects use a main
- nibfile called "MainMenu.nib", and if you use XCode to create
- a new application project, it creates a nibfile with that
- name. Apple's CurrencyConverter tutorial assumes that the
- name of the main nibfile is "MainMenu.nib".
-
-
So, why do we tell you to use a different name? Clozure CL
- has a main nibfile built into it, whose name is
- "MainMenu.nib". Normally you don't see it, and don't even
- need to know that it exists. But the Clozure CL
- application-building tools create a new application by
- copying resources from the Clozure CL application, so that
- your new application has available to it all the built-in
- Clozure CL tools. We ask you to name your nibfile
- "CurrencyConverter.nib" so that it can coexist with the
- Clozure CL main nibfile without causing any problems.
-
-
This difference between a Lisp project and an Objective-C
- project might be a little confusing at first. Just try to keep
- in mind that whenever Apple's tutorial refers to the
- "MainMenu.nib" file, it means the file we have just created
- and named "CurrencyConverter.nib". In a Clozure CL project,
- "MainMenu.nib" is the name of the main Lisp nibfile, not your
- application's main nibfile.
-
-
-
-
Resize the Window
-
-
-
Make the window smaller by dragging the bottom-right corner
- of the window inward.
-
-
-
-
-
-
-
Change the Title of the Window
-
-
-
InterfaceBuilder creates the initial window with the title
- "Window". Change the title to the more appropriate "Currency
- Converter":
-
-
-
Click the Window object in the "Currency Converter"
- window.
-
-
-
-
-
Choose "Attributes" from the drop-down menu in the
- Inspector window:
-
-
-
-
-
Change the "Window Title" field to read "Currency Converter":
-
-
-
-
-
-
-
-
Add Text Fields
-
-
-
In InterfaceBuilder's Palettes window, select the "Cocoa
- Text" view, and find the NSTextView object:
-
-
-
-
-
-
Drag an NSTextView object and drop it into the "Currency
- Converter" window:
-
-
-
-
-
-
If you drag a view near the edges of a window,
- InterfaceBuilder displays blue guide lines that show the
- standard placement of a view near the edge of the window. Drag
- the text view to the right and upward until the guide lines
- appear, and then let go. The text view is then positioned in
- the standard way.
-
-
Now add two more text fields. You can drag them from the
- palette as you did the first one, or you can duplicate the
- first one. To duplicate, select the first text view and then
- choose "Duplicate" from the "Edit" menu. Alternatively, you can
- option-drag the text field to duplicate it.
-
-
-
-
-
-
-
Label the Text Fields
-
-
-
Now add labels to the text fields, to identify their
- purposes for the user. For each text field, drag a Label object
- from the palette and drop it next to the field. (Alternatively,
- you can drop one Label and then duplicate it, just as you can
- duplicate the text fields.)
-
-
-
-
-
-
Just as InterfaceBuilder displayed guidelines to help you
- position the text field near the edge of the window, it also
- displays guide lines to help you position the labels near the
- text fields. Just drag each label until the blue guide lines
- appear, then release the label.
-
-
Now change the text of the labels. Click a label to select
- it. Then show the Inspector by choosing the "Show Inspector"
- item from the "Tools" menu. Select the "Attributes" item from
- the pull-down menu at the top of the Inspector window, and type
- the correct text into the Title field. For example, here is how
- to enter the text for the top label:
-
-
-
-
-
-
Here's how the labels should look after you have entered the
- text for all three:
-
-
-
-
-
-
When you first enter the text for a label, the label may not
- be wide enough to show it all. In that case, you'll see only
- part of the text in the label. You can resize the label to make
- the full text visible. Click the label to select it. Notice the
- small blue dots that surround it. Grab a dot on the left side
- and drag it to the left to make the label wider, until you can
- see the entire text.
-
-
-
Change Text Field Attributes
-
-
-
The first two text fields accept user input; the last
- displays the result of the conversion. We want the first two
- text fields to be editable, so users can enter the values to use
- in the conversion. We don't want the last field to be editable,
- but we do want users to be able to copy text from it.
-
-
We can control all this behavior using text-field
- attributes, configurable in the Inspector.
-
-
-
Select the first text field
-
Choose "Show Inspector" from the "Tools" menu
-
Make sure "Attributes" is selected in the pull-down
- menu at the top of the Inspector window
-
Ensure that the "Editable" and "Enabled" boxes are
- checked in the "Attributes" display of the Inspector window
-
Repeat this process for the second text field
-
Finally, repeat it again for the last text field, but
- this time make sure the "Editable" box is unchecked
-
-
-
-
Add a Button
-
-
-
Now we add a button that activates the currency conversion.
-
-
-
Drag a Button object from the palette and drop it on
- the window
-
-
-
-
-
Double-click the button and change its title to "Convert"
-
-
-
-
-
Select the button and then choose "Attributes" from
- the pull-down menu at the top of the Inspector window. Almost
- halfway down the "Attributes" view of the Inspector window,
- find the "Key Equiv" field. Choose "Return" from the pulldown
- menu in that field.
-
-
-
-
-
When you choose "Return", InterfaceBuilder enters "\R" in
- the text field for the Key Equivalent. Now when a user hits
- the "Return" key, your application will behave as if they had
- clicked the "Convert" button.
-
-
-
-
Add a Separator
-
-
-
Now add a separator line to visually group the text fields
- together. Drag a separator line from the palette and drop it
- above the button.
-
-
-
-
-
-
-
Drag the ends of the separator line until it spans a
- visually pleasing width. As always, you can use the blue
- guidelines that InterfaceBuilder displays to adjust the size
- and position of the line and other elements to conform to
- Apple's Human Interface Guidelines.
-
-
-
Set Up the Menus
-
-
-
InterfaceBuilder creates the standard menus for a new
- application, but it doesn't know the name of the
- application. Consequently, the Application menu and several menu
- items use the name "NewApplication" where they should use the
- name of your application. Change the text of these items so that
- they read "Currency Converter" instead of "NewApplication".
-
-
-
Double-click the text "NewApplication" in the
- application menu of your application's menubar. Change the
- text to "Currency Converter".
-
-
-
-
-
NOTE: This change isn't really enough to get your
- application to display the right name for the Application menu
- when it's launched; the section
- on building the application explains how to make sure the
- correct name appears.
-
Repeat this process for each menu item where the name
- "NewApplication" appears. Using the same method you used to
- change the name of the application menu, edit the "About
- NewApplication" item, the "Hide NewApplication" item, and the
- "Quit NewApplication" item in the application menu. Then edit
- the "NewApplication Help" item in the "Help" menu.
-
-
-
-
Tighten Up the Window Layout
-
-
-
InterfaceBuilder provides layout tools with which you can
- easily clean up the layout of a UI window and ensure it
- conforms to Apple's user interface guidelines.
-
-
-
Select the "Exchange Rate" text label. Then
- Shift-click the other two labels to include them in the
- selection (actually, it doesn't matter which label you select first).
-
Choose "Layout" > "Size to Fit" to shrink the labels
- to the smallest sizes that still show all the text
-
Choose "Layout" > "Alignment" > "Align Right Edges" to
- line up the right sides of the labels
-
With all three labels still selected, drag them up and
- to the left. Release them when the blue guidelines show at
- the top and left side of the window.
-
Now select all three text fields. You can click one of
- them, then Shift-click to add the others to the
- selection. Drag them up and left, toward the labels. Again,
- release them when the blue guide line appears to show you the
- proper distance from the labels. A guide line also appears to
- show you when the fields are vertically aligned with the
- center label.
-
Next, grab the separator line and move it up and to the
- left. Release it when its left edge is aligned with the left
- edge of the bottom label, and its top is the recommended
- distance from the bottom label and its text field. Then drag
- the right end of the separator line to resize it until its
- right edge is aligned with the right edge of the bottom text
- field. Again, guide lines show when you have found the proper
- distances.
-
Grab the button and move it up and left, again using
- the guide lines to help you find a good position.
-
Finally, resize the window. When the blue guide lines
- appear on the right and bottom of the window, it's the right
- size for its contents.
-
-
-
Now your application window should look something like the
- one in the illustration:
-
-
-
-
-
-
-
-
Enable Tabbing Between Text Fields
-
-
-
Users generally expect to be able to use the Tab key to move
- from one text field to the next in a dialog
- box. InterfaceBuilder enables you to specify the tabbing order
- in text fields.
-
-
-
Choose "Layout" > "Keyboard Navigation" > "Show
- Keyboard Check". InterfaceBuilder displays a set of small
- icons that identify UI elements that can respond to key
- events.
-
Select the "Exchange Rate" text field (the field, not
- the label) and then choose "Layout" > "Keyboard Navigation" >
- "Make Initial First Responder". A small "1" icon appears in
- the text field to show that when the application launches,
- that field receives keyboard events.
-
Control-drag from the "Exchange Rate" field to the
- "Dollars" field. InterfaceBuilder shows the "Connections"
- Inspector, and, because Keyboard Check is enabled,
- automatically selects the "nextKeyView" outlet. Click the
- "Connect" button in the Inspector window to confirm.
-
Repeat the previous steps to connect the "Dollars"
- field back to the "Exchange Rate" field. That way, tabbing
- moves the insertion point from the "Exchange Rate" field to
- the "Dollars" field, and then back to the "Exchange Rate"
- field. Control-drag from the "Dollars" field to the "Exchange
- Rate" field, then click "Connect" to confirm.
-
-
-
We don't enable tabbing into the "Amount" field because it's
- not an editable field; it's used only to show the result of a
- conversion.
-
-
-
Set Up the Classes Used In the User Interface
-
-
-
The visual elements of your application's user interface are
- all ready now. The last thing you must do is create descriptions
- of any custom classes used by the application when users
- interact with the interface.
-
-
When a user clicks the "Convert" button, it should send a
- message to a custom class that causes the conversion to take
- place. In order for the application to connect the user
- interface to classes that perform these actions, you must add
- descriptions of your classes to the nibfile. Fortunately,
- InterfaceBuilder can create class descriptions and save them in
- the nibfile for you.
-
-
-
ConverterController
-
-
-
ConverterController is the controller class that the user
- interface communicates with directly when the "Convert" button
- is pressed. Create a description of the ConverterController
- class, and then create an instance of it.
-
-
-
In InterfaceBuilder's "CurrencyConverter.nib" window,
- click the "Classes" tab. The window shows a browser view of
- all available Objective-C classes:
-
-
-
-
-
Control-click the "NSObject" entry in the browser, and
- choose "Subclass NSObject" from the popup
- menu. InterfaceBuilder creates a new entry initially called
- "MyObject". Change the name from "MyObject" to "ConverterController".
-
Select the "ConverterController" class in the browser,
- then activate the Inspector window and choose "Attributes"
- from the popup menu at the top of the Inspector. At the
- bottom of the "Attributes" view is a list of actions or
- outlets. Select "Outlets", and use the "Add" button to add
- four fields:
-
-
-
-
Rename these four fields to: "amountField", "dollarField",
- "rateField", and "converter":
-
-
-
-
Now add the action that is triggered when the
- "Convert" button is pressed: switch to the Actions view and
- use the "Add" button to add a new action:
-
-
-
-
Change the name of the action from "myAction:" to "convert:"
-
Now create an instance of the ConverterController
- class. In the browser, Right-click the ConverterController
- class and choose "Instantiate ConverterController". The
- browser view automatically switches to the Instances view to
- show you the newly-created instance of ConverterController as
- a blue box icon. There is a small yellow flag next to the
- ConverterController instances to show that it has outlets
- that are not connected to anything. In our final step, we'll
- create the correct connections for the instance's outlets,
- which will enable the application to send messages correctly
- to the objects that implement its behavior.
-
-
-
-
Converter
-
-
-
Converter is
- the model
- class that implements the actual conversion code. Create a
- description of the Converter class, and then create an
- instance of it. Repeat the steps you used to create the
- ConverterController class and instance to create a Converter
- class and instance:
-
-
-
Switch to the browser view in the
- "CurrencyConverter.nib" window.
-
Control-click NSObject and choose "Subclass NSObject"
- from the resulting popup menu.
-
Change the name of the newly-created class from
- "MyObject" to "Converter"
-
Control-click the "Converter" class and choose
- "Instantiate Converter" to create an instance of the
- Converter class.
-
-
-
The model class, "Converter", has no outlets or actions, so
- you don't need to add anything to it before instantiating
- it. Your code will implement a conversion method, but
- InterfaceBuilder doesn't need to know about it; the "convert:"
- method in your code will know everything it needs to about the
- "Converter" class. You just need to create the class
- description and the instance so that your application will
- start up with the correct objects created and connected.
-
-
-
Connecting the Outlets
-
-
-
The final step in setting up the user interface is
- establishing connections between the outlets and objects in the
- interface, so that messages are sent from the user interface to
- the correct objects.
-
-
-
Connect the "Convert" button to the
- "ConverterController" instance. Control-drag from the
- "Convert" button to the "ConverterController" instance. Make
- sure the "convert:" action is selected in the "Target/Action"
- view of the Inspector window, then click the "connect" button
- to confirm.
-
Connect the "ConverterController" instance to the text
- fields. Control-drag from the "ConverterController" instance
- to the "Exchange Rate" field. Select "rateField" in the
- "Outlets" view of the Inspector window and click "connect" to
- confirm. Then repeat this process, connecting "dollarField" to
- the "Dollars" text field, and "amountField" to the "Amount"
- field.
-
Finally, connect the "ConverterController" to the
- "Converter" instance. Control-drag from the
- "ConverterController" instance to the "Converter"
- instance. Select the "converter" outlet in the Inspector
- window and click "connect" to confirm.
-
-
-
The nibfile now contains descriptions of the custom classes
- that your code will implement, including connections between
- their outlets and the objects with which they must
- communicate. You can save the nibfile and proceed to write the
- code that implements their behavior.
This concludes our HOWTO on building the Apple
- CurrencyConverter example in Lisp with Clozure CL. Your own Lisp
- applications are likely to be considerably more complex than the
- Currency Converter, which, after all, just does a simple
- multiplication. You can, however, use exactly the same steps to
- build a much richer and more full-featured Cocoa
- application.
-
-
A more complex application will still consist of one or more
- nibfiles and one or more Lisp source files. You will still use
- the Objective-C bridge to define Objective-C classes and
- methods, and to use Cocoa library features. And you will still
- use BUILD-APPLICATION to turn your source and nibfiles into
- standalone Cocoa applications.
-
-
You should now be able to use Clozure CL to accomplish anything
- that an Objective-C user can accomplish with Cocoa. Good luck!
Now that you have created the nibfile that defines your
- application's user interface, it's time to create the Lisp
- source file that defines its behavior. When you work with
- Objective C, as in Apple's tutorial, you use Apple's XCode
- application to create and manage projects, to edit Objective C
- source files, and to build the final application. In this HOWTO,
- the Clozure CL application takes the place of XCode. The Lisp
- project structure is much simpler than the XCode project
- structure: to build the Lisp application we need only the
- nibfile created in the previous section, and a single Lisp
- source file.
-
-
Double-click Clozure CL to launch it. Clozure CL displays a Listener window:
-
-
-
-
-
-
Choose "New" from the "File" menu to create a new Lisp source
- window. Save it with the name "CurrencyConverter.lisp" into the
- same "currency-converter" folder where you saved your nibfile in
- the earlier section. You should now have a "currency-converter"
- folder that contains a "CurrencyConverter.lisp" item and a
- "CurrencyConverter.nib" item.
-
-
Now you're ready to continue, and write the Lisp code that
- implements the application's behavior.
This HOWTO is distributed with example files that include a
- working nibfile (a file of user-interface objects,
- named "CurrencyConverter.nib") and a Lisp source file (named
- "CurrencyConverter.lisp"). You can build a working copy of the
- example application by using these files, but you probably
- shouldn't. If you want to understand how to build your own Lisp
- application projects, you should follow the instructions here to
- create your own source file and nibfile, and use the example
- files only for reference in case something goes wrong.
-
-
-
-
Create the Project Folder
-
-
-
-
First, create a project folder to hold the files you are
- going to create. When your project is complete, the folder will
- contain a nibfile that defines the user interface, and
- a Lisp source file that defines the behavior of the
- application. Those two files are really all there is to a Lisp
- application, though not all applications are as simple as this
- currency converter. For more complex applications it makes sense
- to split your UI into several nibfiles, and to split your
- implementation into several source files. The basic principle
- remains the same, however: nibfiles define your user interface,
- and Lisp files define your application's behavior.
-
-
Create a folder somewhere convenient, and name it
- "currency-converter". Next we will use Apple's InterfaceBuilder
- application to create the user interface The next page tells you
- how to do that; when you create your nibfile, save it into your
- "currency-converter" folder.
In this section we'll write Lisp code that duplicates the
- features provided by the Objective-C code in Apple's
- tutorial. In Apple's tutorial, the explanation of the Objective
- C code begins with the
- section Bridging
- the Model and View: The Controller.
-
-
The Lisp code in this section of the HOWTO is considerably
- simpler than the corresponding Objective-C code, in part
- because we can ignore the conventions that XCode uses for
- laying out source files. We can just write all our definitions
- into a single Lisp source file, and load that file into Clozure CL
- when we are ready to build the application.
-
-
-
First Things First
-
-
-
-
Place the following line at the top of your Lisp source file:
-
-
(in-package "CCL")
-
-
Clozure CL's Objective-C bridge code is defined in the "CCL"
- package. Usually, when building an application, you'll create a
- package for that application and import the definitions you need
- to use. In order to keep the discussion short in this simple
- example, we just place all our definitions in the "CCL"
- package.
-
-
-
-
-
Defining the Converter Class
-
-
-
-
We begin by defining the Converter class. Recall from Apple's
- tutorial that this is the Model class that implements the
- conversion between dollars and other currencies. Here is the
- Lisp definition that implements the class you created in
- InterfaceBuilder:
This is an ordinary CLOS class definition, with a couple of
- simple wrinkles. First, the superclass it inherits from is the
- NS-OBJECT class in the "NS" package. NS-OBJECT is an Objective-C
- class, the ancestor of all Objective-C objects. This CLOS
- definition actually creates a new Objective-C class named
- "Converter".
-
-
We tell Clozure CL how to build the right kind of class object
- by including the :METACLASS option in the definition:
-
-
- (:metaclass ns:+ns-object)
-
-
-
The Objective-C bridge knows that when the metaclass
- is ns:+ns-object, it must lay out the class object
- in memory as an Objective-C class, rather than a normal CLOS
- STANDARD-CLASS.
-
-
Next, we define the method "convertCurrency:atRate:":
This is the method that actually does the currency
- conversion. It's a Lisp method that will be called when the
- AppKit sends the Objective-C message "convertCurrency:atRate:"
- It's very simple—really, it just multiples
- currency times rate. Most of the text in the definition is
- Objective-C bridge code that links the definition to the right
- class with the right argument and return types.
-
-
objc:defmethod is a version of DEFMETHOD that
- creates methods that can execute in response to Objective-C
- message-sends.
-
-
The syntax #/convertCurrency:atRate: uses the
- "#/" reader macro to read a symbol with case preserved, so that
- you can see in your code the same name that Objective-C uses for
- the method, without worrying about how the name might be
- converted between Lisp and Objective-C conventions.
-
-
The number of arguments to an Objective-C method is the
- number of colons in the name, plus one. Each colon indicates an
- argument, and there is always an extra "self" argument that
- refers to the object that receives the message. These are normal
- Objective-C conventions, but we perhaps need to emphasize the
- details, since we are using Lisp code to call the Objective-C
- methods.
-
-
We indicate the return type and the types of arguments in
- the method definition by surrounding parameters and the method
- name with parentheses, and appending the type name.
-
-
Thus, for example,
-
-
-(#/convertCurrency:atRate: :float)
-
-
-
means that the return type of the method is :FLOAT, and
-
-
-(self converter)
-
-
-
means that the type of the receiving object is Converter.
-
-
You will see these same conventions repeated in the next
- section.
-
-
-
-
Defining the ConverterController Class
-
-
-
-
The previous section defined the Model class, Converter. All
- we need now is a definition for the ConverterController
- class. Recall from your reading of Apple's Tutorial that the
- CurrencyConverter example uses the Model-View-Controller
- paradigm. You used InterfaceBuilder to construct the
- application's views. The Converter class provides the model
- that represents application data. Now we define the controller
- class, ConverterController, which connects the View and the
- Model.
-
-
Here's the definition of the ConverterController class:
Once again we use the Objective-C bridge to define an
- Objective-C class. This time, we provide several
- instance-variable definitions in the class, and name accessors
- for each of them explicitly. The :FOREIGN-TYPE
- initargs enable us to specify the type of each field in the
- foreign (Objective-C) class.
-
-
Each field in the definition of the ConverterController class
- is an outlet that will be used to store a reference to one of
- the text fields that you created in InterfaceBuilder. For
- example, amount-field will be connected to the
- "Amount" text field.
-
-
Why did we spell the name "amount-field" in Lisp code, and
- "amountField" when creating the outlet in InterfaceBuilder? The
- Objective-C bridge automatically converts Lisp-style field names
- (like "amount-field") to Objective-C-style field names (like
- "amountField"), when handling class definitions.
-
-
The converter field at launch time contains a
- reference to the Converter object, whose class definition is in
- the previous section.
-
-
The final piece of the implementation is a definition of the
- "convert:" method. This is the method that is called when a
- user clicks the "Convert" button in the user interface.
Just as in the Apple example, this method reads the dollar
- and rate values, and passes them to the
- "convertCurrency:atRate:" method of the Converter class. It then
- sets the text of the amount-field to reflect the result of the
- conversion. The only significant difference between this
- implementation and Apple's is that the code is written in Lisp
- rather than Objective-C.
-
-
This completes the definition of the CurrencyConverter's
- behavior. All that remains is to actually build the Cocoa
- application. The next section shows how to do that.
This HOWTO shows how you can load nibfiles
- into a running copy of Clozure CL by evaluating Lisp forms. You
- might want to load nibfiles this way to test user-interface
- elements that you are working on for an application project, or
- to enable an application to dynamically load optional
- user-interface elements.
-
-
-
-
-
Nibfiles
-
-
-
-
A large part of developing Cocoa applications is creating
- user-interface elements using the Cocoa frameworks. Although
- it's perfectly possible to create any user-interface element
- just by making method calls against the frameworks, the more
- standard way to design a user interface is to use Apple's
- InterfaceBuilder application to
- create nibfiles—files of archived
- Objective-C objects that implement the user-interface
- elements.
-
-
InterfaceBuilder is an application that ships with Apple's
- Developer Tools. The Developer Tools are an optional install
- that comes with Mac OS X. Before you can use this HOWTO, you'll
- need to make sure that Apple's Developer Tools are installed on
- your system. Members of Apple's developer programs may download
- the tools for free from
- Apple's developer
- website, but normally there is no need. You can simply
- use the optional Developer Tools installer on the Mac OS X
- system disks to install the tools.
-
-
-
-
Using Nibfiles
-
-
-
-
Using InterfaceBuilder, you can quickly and easily create
- windows, dialog boxes, text fields, buttons, and other
- user-interface elements. The elements you create with
- InterfaceBuilder have the standard appearance and behavior
- specified by Apple's Human Interface Guidelines.
-
-
InterfaceBuilder saves descriptions of these objects
- in nibfiles. These files contain archived
- representations of Objective-C classes and objects. When you
- launch an application and it loads a nibfile, the Cocoa runtime
- creates these Objective-C objects in memory, complete with any
- instance-variable references to other objects that might have
- been saved in the nibfile. In short, a nibfile is an archived
- collection of user-interface objects that Cocoa can quickly and
- easily revive in memory.
-
-
The normal way that Objective-C programmers use nibfiles is
- by storing them in an application bundle. The application's
- Info.plist file (also stored in the bundle) specifies which
- nibfile is the application's main nibfile, and that file is
- loaded automatically when the application starts up. The
- application can dynamically load other nibfiles from the bundle
- by making method calls.
-
-
Lisp applications written with Clozure CL can also use
- nibfiles in this same fashion (see the "currency-converter"
- HOWTO in the "cocoa" examples folder), but Lisp programmers are
- accustomed to highly interactive development, and might want to
- simply load an arbitrary nibfile into a running Clozure CL
- session. Fortunately, this is easy to do.
-
-
-
-
How To Load a Nibfile
-
-
-
-
Let's start by loading a very simple nibfile from the Clozure
- CL Listener window. Start by launching the Clozure CL
- application.
-
-
In the same directory as this HOWTO file, you'll find a
- nibfile named "hello.nib". This is an extremely simple nibfile
- that creates a single Cocoa window with a greeting in it. We'll
- use forms typed into the Listener window to load it.
-
-
We're going to call the Objective-C class
- method loadNibFile:externalNameTable:withZone: to
- load the nibfile into memory, creating the window that is
- described in the file. First, though, we need to set up some
- data structures that we'll pass to this method.
-
-
The arguments
- to loadNibFile:externalNameTable:withZone: are a
- pathname, a dictionary object, and a memory zone. As with every
- Objective-C method call, we also pass the object that receives
- the message, which in this case is the class NSBundle.
-
-
The pathname is just a reference to the nibfile we want to
- load. The dictionary holds references to objects. In this
- first simple example, we'll use it only to identify the
- nibfile's owner, which in this case is the application
- itself. The zone is a reference to the area of memory where
- the nibfile objects will be allocated.
-
-
Don't worry if none of this makes sense to you; the code to
- create these objects is simple and straightforward, and should
- help clarify what's going on.
-
-
-
1. Get the Zone
-
-
-
First, we'll get a memory zone. We'll tell Cocoa to allocate
- the nibfile objects in the same zone that the application
- uses, so getting a zone is a simple matter of asking the
- application for the one it's using.
-
-
Before we can ask the application anything, we need a
- reference to it. When the Clozure CL application starts up,
- it stores a reference to the Cocoa application object into
- the special variable *NSApp*.
-
-
Start by changing to the CCL package; most of the utility
- functions we'll use are defined in that package:
-
-
- ? (in-package :ccl)
- #<Package "CCL">
-
-
-
We have a reference to the running Clozure CL application
- object in the special variable *NSApp*. We can ask it for its
- zone, where it allocates objects in memory:
Now we have a reference to the application's zone, which is
- one of the parameters we need to pass
- to loadNibFile:externalNameTable:withZone:.
-
-
-
2. Make a Dictionary
-
-
-
The dictionary argument
- to loadNibFile:externalNameTable:withZone: is
- used for two purposes: to identify the nibfile's owner, and
- to collect toplevel objects.
-
-
The nibfile's owner becomes the owner of all the toplevel
- objects created when the nibfile is loaded, objects such as
- windows, buttons, and so on. A nibfile's owner manages the
- objects created when the nibfile is loaded, and provides a
- way for your code to get references to those objects. You
- supply an owner object in the dictionary, under the
- key "NSNibOwner".
-
-
The toplevel objects are objects, such as windows, that are
- created when the nibfile is loaded. To collect these, you
- can pass an NSMutableArray object under the
- key NSNibTopLevelObjects.
-
-
For this first example, we'll pass an owner object (the
- application object), but we don't need to collect toplevel
- objects, so we'll omit
- the NSNibTopLevelObjects key.
The window defined in the "hello.nib" file should appear
- on the
- screen. The loadNibFile:externalNameTable:withZone:
- method returns T to indicate it loaded the
- nibfile successfully; if it had failed, it would have
- returned NIL.
-
-
At this point we no longer need the pathname and
- dictionary objects. The *nib-path* we must release:
The *my-dict* instance was not created with #/alloc (or with
- MAKE-INSTANCE), so it is already autoreleased, and we don't need
- to release it again.
-
-
-
Making a Nib-loading Function
-
-
-
Loading a nibfile seems like something we might want to do
- repeatedly, and so it makes sense to make it as easy as possible
- to do. Let's make a single function we can call to load a nib as
- needed.
-
-
The nib-loading function can take the file to be loaded as a
- parameter, and then perform the sequence of steps covered in the
- previous section. If we just literally do that, the result will
- look something like this:
The trouble with this function is that it leaks a string
- every time we call it. We need to release the
- nib-name before returning. So how about this
- version instead?
This version solves the leaking problem by binding the result
- of the load call to result, then releasing the
- nib-name before returning the result of the
- load.
-
-
There's just one more problem: what if we want to use the
- dictionary to collect the nibfile's toplevel objects, so that we
- can get access to them from our code? We'll need another version
- of our function.
-
-
In order to collect toplevel objects, we'll want to pass an
- NSMutableArray object in the dictionary, stored under the key
- NSNibTopLevelObjects. So we first need to create such an
- array object in the let form:
Now that we have the array in which to store the nibfile's
- toplevel objects, we need to change the code that creates the
- dictionary, so that it contains not only the owner object, but
- also the array we just created:
We now want to collect the objects in it. We'll do that by
- making a local variable to store them, then iterating over the
- array object to get them all. (Normally, when we want to keep
- an object from an array, we have to retain it. Top-level nib
- objects are a special case: they are created by the nib loading
- process with a retain count of 1, and we are responsible for releasing
- them when we're through with them.)
After collecting the objects, we can release the array, then
- return the list of objects. It's still possible we might want
- to know whether the load succeeded, so we
- use values to return both the toplevel objects and
- the success or failure of the load.
-
-
The final version of the nib-loading code looks like
- this:
The "Hello!" window appears on the screen, and two values are
- returned. The first value is the list of toplevel objects that
- were loaded. The second value, T indicates that the
- nibfile was loaded successfully.
-
-
-
What About Unloading Nibfiles?
-
-
-
Cocoa provides no general nibfile-unloading API. Instead, if
- you want to unload a nib, the accepted approach is to close all
- the windows associated with a nibfile and release all the
- toplevel objects. This is one reason that you might want to use
- the "NSNibTopLevelObjects" key with the dictionary
- object that you pass
- to loadNibFile:externalNameTable:withZone:—to
- obtain a collection of toplevel objects that you release when
- the nibfile is no longer needed.
-
-
In document-based Cocoa applications, the main nibfile is
- usually owned by the application object, and is never unloaded
- while the application runs. Auxliliary nibfiles are normally
- owned by controller objects, usually instances of
- NSWindowController subclasses. When you
- use NSWindowController objects to load nibfiles,
- they take responsibility for loading and unloading nibfile
- objects.
-
-
When you're experimenting interactively with nibfile loading,
- you may not start out by
- creating NSWindowController objects to load
- nibfiles, and so you may need to do more of the object
- management yourself. On the one hand, loading nibfiles by hand
- is not likely to be the source of major application problems. On
- the other hand, if you experiment with nib-loading for a long
- time in an interactive session, it's possible that you'll end up
- with numerous discarded objects cluttering memory, along with
- various references to live and possibly released objects. Keep
- this in mind when using the Listener to explore Cocoa. You can
- always restore your Lisp system to a clean state by restarting
- it, but of course you then lose whatever state you have built up
- in your explorations. It's often a good idea to work from a text
- file rather than directly in the Listener, so that you have a
- record of the experimenting you've done. That way, if you need
- to start fresh (or if you accidentally cause the application to
- crash), you don't lose all the information you gained.