0% found this document useful (0 votes)
118 views240 pages

OpenSTEP Developers Tutorial 4.0 Mach 1996

This document introduces OPENSTEP application development. It provides tutorials to help developers get started creating OPENSTEP applications by learning the basics of the object-oriented OPENSTEP framework. Tutorials cover currency conversion and travel advisor applications to demonstrate key concepts like the model-view-controller paradigm, classes, objects, and the advantages of the OPENSTEP development environment.

Uploaded by

Matias Kottwitz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
118 views240 pages

OpenSTEP Developers Tutorial 4.0 Mach 1996

This document introduces OPENSTEP application development. It provides tutorials to help developers get started creating OPENSTEP applications by learning the basics of the object-oriented OPENSTEP framework. Tutorials cover currency conversion and travel advisor applications to demonstrate key concepts like the model-view-controller paradigm, classes, objects, and the advantages of the OPENSTEP development environment.

Uploaded by

Matias Kottwitz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 240

DISCOVERING OPENSTEP:

A DEVELOPER TUTORIAL

Object-Oriented Software
DISCOVERING OPENSTEP:
A Developer Tutorial

Release 4.0 for Mach


NeXT Software, Inc.
900 Chesapeake Drive
Redwood City, CA 94063
U.S.A.

We at NeXT have tried to make the information contained in this publication as accurate and reliable as
possible. Nevertheless, NeXT disclaims any warranty of any kind, whether express or implied, as to any
matter whatsoever relating to this publication, including without limitation the merchantability or fitness
for any particular purpose. NeXT will from time to time revise the software described in this publication
and reserves the right to make such changes without the obligation to notify the purchaser. In no event
shall NeXT be liable for any indirect, special, incidental, or consequential damages arising out of purchase
or use of this publication or the information contained herein.

Restricted Rights Legend: Use, duplication, or disclosure by the Government is subject to restrictions as
set forth in subparagraph (c)(1 )(ij) of the Rights in Technical Data and Computer Software clause at DFARS
252.227-7013 (or, if applicable, similar clauses at FAR 52.227-19 or NASA FAR Supp. 52.227-86).

Copyright 1993-1996 NeXT Software, Inc. All Rights Reserved.


[6863.00]

NeXT, the NeXT logo, NEXTSTEp, Netlnfo, and Objective-C are registered trademarks of NeXT Software,
Inc. The NEXTSTEP logo, Application Kit, Enterprise Object, Enterprise Objects Framework, Interface
Builder, OPENSTEp, the OPENSTEP logo, PDO, Portable Distributed Objects, WebObjects, and
Workspace Manager are trademarks of NeXT Software, Inc. Use in commerce other than as "fair use" is
prohibited by law except by express license from NeXT Software, Inc.

PostScript is a registered trademark of Adobe Systems, Incorporated. Unix is a registered trademark of


UNIX Systems Laboratories, Inc. All other trademarks mentioned belong to their respective owners.

U.S. and foreign patents are pending on NeXT products.


Netlnfo: U.S. Patent No. 5,410,691
NEXTSTEP: U.S. Patent Nos. 5,184,124; 5,355,483; 5,388,201; 5,423,039; 5,432,937.
Cryptography: U.S. Patent Nos. 5,159,632; 5,271,061.

Address inquiries concerning usage of NeXT trademarks, designs, or patents to General Counsel, NeXT
Software, Inc., 900 Chesapeake Drive, Redwood City, CA 94063 USA.}

Written by: Terry Donoghue


Tutorial applications by: Terry Donoghue
Art and Production management: Terri FitzMaurice
Book design: Karin Stroud
Publications management: Ron Hayden
With help from: Trey Matteson, Ron Hayden, Jean Ostrem, Lynn Cox, Derek Clegg, and Kelly Toshach
Cover design: CKS Partners, San Francisco, California
Table of Contents

1 Introduction 111 To Do Tutorial


6 What is OPENSTEP? 117 The Design of To Do
8 Power Programming with OPEN STEP Developer 121 Setting up the To Do Project
10 The Advantage of Objects 122 Creating the Model Class (ToDoltem)
11 The Advantage of OPENSTEP 128 Subclass Example: Adding Data and Behavior
(CalendarMatrix)
Why NSMatrix? 128
13 Currency Converter Tutorial
139 The Basics of a Multi-Document Application
19 Creating the Currency Converter Project 150 Managing Documents Through Delegation
21 Creating the Currency Converter Interface 153 Managing the Data and Coordinating its Display
34 Designing the Currency Converter Application (ToDoDoc)
37 Defining the Classes of Currency Converter 160 Subclass Example: Overriding Behavior
Connecting ConverterControlier to the Interface 42 (SelectionNotifMatrix)
46 Implementing the Classes of Currency Converter 164 Creating and Managing an Inspector (ToDolnspector)
51 Building the Currency Converter Project 181 Subclass Example: Overriding and Adding Behavior
(ToDoCell)
56 Run Currency Converter
187 Setting Up Timers for Notification Messages
190 Build, Run, and Extend the Application
57 Travel Advisor Tutorial
Optional Exercises 191
62 Creating the Travel Advisor Interface World Wide Web 197
73 The Design of Travel Advisor
Model Objects 73
193 Where To Go From Here
Controller 74
198 Programming Tools and Resources
76 Defining the Classes of Travel Advisor
201 Information
82 Implementing the Country Class
203 Professional Services
90 Implementing the TAController Class
Data Mediation 92 205 Ordering NeXT Products and Services
Getting the Table View to Work 95
Adding and Deleting Records 100
Field Formatting and Validation 102
Application Management 105
109 Building and Running Travel Advisor
Table of Contents

209 Appendix A: Object-Oriented


Programming
214 Objects
Encapsulation 214
Messages 215
An Object-Oriented Program 216
Polymorphism and Dynamic Binding 217.
219 Classes
Object Creation 219
Inheritance 220
Defining a Class 222
224 Categories and Protocols
Concepts

13 Currency Converter Tutorial 88 Object Ownership, Retention, and Disposal


91 Turbo Coding With Project Builder
20 Project Indexing
22 A Window in OpenStep
94 Finding Information Within Your Project

28 Aligning on a Grid
97 Getting in on the Action: Delegation and Notification

32 An OpenStep Application - What You Get "For Free"


101 Abstract Classes and Class Clusters
103 Behind "Click Here": Controls, Cells, and Formatters
33 An OpenStep Application - The Possibilities
34 Why an Object is Like a Jelly Donut
106 Flattening the Object Network: Coding and Archiving

36 The Model-View-Controller Paradigm


108 Using the Graphical Debugger
109 lips for Eliminating Deallocation Bugs
37 Class Versus Object
40 Paths for Object Communication: Outlets, Targets, and
Actions 111 To Do Tutorial
50 Objective-C Quick Reference 116 Starting Up - What Happens in NSApplicationMainO
52 What Happens When You Build an Application 118 Only When Needed: Dynamically Loading Resources and
54 Where To Go For Help Code
134 Dates and limes in OpenStep
57 Travel Advisor Tutorial 141 The Structure of Multi-Document Applications
143 Coordinate Systems in OpenStep
63 Varieties of Buttons
148 The Application Quartet: NSResponder, NSApplication,
64 More About Forms
NSWindow, and NSView
66 More About Table Views
162 Events and the Event Cycle
74 The Collection Classes
178 A Short Guide to Drawing and Compositing
79 Checking Connections in Outline Mode
180 Making a Custom View
80 File's Owner
181 Why Chose NSButtonCell as Superclass?
81 Just Add a Smock: Compiled and Dynamic Palettes
189 lick Tock Brrrring: Run Loops and limer
82 NSString: A String for All Countries
84 The Foundation Framework: Capabilities, Concepts, and
Paradigms

iii
Table of Contents

iv
tjciono Projects :b;1j

Delete

ry

Jill
~24-Hour
05,.:,5.2,; ,~~tJ
~
Chapter 1
Introduction

Sections

What is OPENSTEP?

Power Programming With


OPENSTEP Developer

The Advantage of Objects

The Advantage of OPENSTEP

3
Introduction

4
When you begin any enterprise, you must find a starting
point. You set out from that starting point and acquire a basic
vocabulary, a notion of boundaries and techniques, a sense
of how things fit together and what is possible. For those
who want to learn how to create OPENSTEP applications,
this book provides a starting point.
With this book you become familiar with OPENSTEP
application development not merely by reading but by
doing. The book guides you through the creation of three
applications of increasing complexity. Along the way it
explains related concepts and issues. The techniques and
concepts you learn in one tutorial lay the foundation for the
more advanced techniques and concepts in the next
tutorial.
The final chapter of the book tells you where to go for
further information and where and how to find things, such
as tools and documentation. It also tells you how to get
NeXT products and services.
This book covers a lot of ground, although sometimes at
only a summary level. Finishing this book makes you much
better prepared to take on serious application development
with OPENSTEP in general and the Enterprise Object
Framework in particular.
Although the aim is primarily to educate, this book is also
intended-for those interested in programming-to be fun.

Some of you might be new to OPENSTEP. To learn more


about OPENSTEp, the standard on which it's based, and
OPENSTEP Developer, turn the page.

5
Chapter 1 Introduction to OPENSTEP

OPENSTEP is NeXT Software's graphical, object-oriented user OpenStep


and development environment. It is based on the OpenStep
standard and available on a variety of platforms. OPENSTEP is OpenStep is the software industry's first open standard for object-
earning a growing reputation in the corporate world as the oriented software development. It is an application programming
premier environmentfor developing and deploying mission- interface (API) based on the fundamental NEXTSTEP object layer:
critical custom applications. the Application Kit, the Foundation Kit, and Display PostScript.

The two core components ofthe product are OPENSTEP User and
OPENSTEP Developer.
. Applications
Application Kit
OPENSTEP User is a user environment Display Foundation
acclaimed for its intuitively navigable desktop Postscript Kit
0IifHSfBi and file manager. On it you can easily deploy
your own OPENSTEP applications as well as those OpenStep
supplied by NeXT and third-party vendors. Intelligent networking,
particularly Netlnfo, makes it possible to install and upgrade
OPENSTEP in a fraction of the time it takes other systems.
The OpenStep object layer allows corporate customers to create, evolve
OPENSTEP Developer, NeXT's software- and deploy multi-tier; client/server business applications in a fraction of
development environment, provides time it takes other methods.
seamlessly integrated set of tools for building
• Application Kit: APls for user-interface objects and for essential
0IifHSfBi complex applications that can be deployed on application behavior, such as event handling
heterogeneous client/server networks running not only
OPENSTEP, but Portable Distributed Objects, Enterprise Objects • Foundation Kit APls that define basic object behavior, that support
Framework, and OpenStep-based software developed by other object persistence and distribution, and that "objectify" collections,
vendors. Unicode strings, and many other programmatic entities
• Display PostScript: APls for PostScript drawing

Whats in a Name?
"0PENSTEP" refers to the software product. "0penStep" refers to the standard or specification on which the product is based, and by extension to the
concepts expressed by the specification.
The OpenStep specification is available via anonymous ftp at ftp. next. com.

6
The OPENSTEP user
environment includes File
!newer (a file-system browser),
Mail, Preferences, Edit, and
other applications in/NextApps.

Other Products from NeXT

• With WebObjects you can easily create dynamic websites. application to be distributed among a heterogeneous network
WebObjects applications provide a smart, interactive of OpenStep clients and a broad range of servers.
connection between corporate databases and customers on
"The Web." • D'OLE brings the PD~ object model to the Windows platform,
giving Windows application developers the ability to make use
• Enterprise Objects Framework enables you to construct of distributed-object technology.
applications that use (and reuse) enterprise business objects,
storing them in relational databases such as those from Oracle In addition to software products, NeXT provides developer
and Sybase. support and professional services, in particular the Object
Experts program, an innovative on-site support and training
• Portable Distributed Objects (PDO) allow objects in a single program.

OPENSTEP- Platforms and Interoperability


Supported Platforms Intel-based PCs, Sun SPARC workstations, NeXT's Motorola 68040-based computers.

CORBA NeXT has licensed SunSoft's implementation of the CORBA standard (from the Object Management
Group) and is committed to CORBA interoperability.

7
Chapter 1 Introduction to OPENSTEP

;: <,' ,,/,:.'}:J)" ,.';.:;. ..J; '. .,:.


;\rOwer~r~gramming~ith OPEI\I~-r~PDeveloperHjt;' : <:'/' ;'
~ __ ",~>~;.;,,~.,~',_,_.....>4>"~~,;:::<""_.;.,._'.>'h....,....,.,'''''~;;.,,«,;.,~ __'' __..,.,~,,\,;,.,......"<>'.1_."",,,..';"'~"~' v.,o'.<,c,,,,,,_',",L,,,,,,~~ _,~..,.....,.v.~"",m.. • ,"" *,,*<.&&.,~~ ......~,,,,,, """ci""~_"<.".".=_, .......,,,,.,,,,,,,,,.,,.:..,, "'.".,N>"'M""'''..,.;:...,:."", ...<'''.... ~~.~.,,_v~''''>.i.'''',._·_·~,
,,.,,,,_·'c

OPENSTEP Developer 4.0 is a programming environment ideally Interface Builder makes it easy to create application
suited for the rapid development of custom object-oriented interfaces by dragging objects from palettes.
applications deployable on a variety of computer architectures. It Standard palettes hold an assortment of Application
comprises an integrated set of development software, libraries, Kit objects. Custom palettes can include third-party
header files, tools, documentation, and other resources. objects as well as the developer's own objects. Interface Builder
archives and restores elements of a user interface as objects-it
doesn't "hardwire" them into the interface. Interface Builder
helps to connect objects for messaging, and it assists in the
::::::'::-I1!IiIL~
Project Builder is an application that manages
definition of custom classes.
software-development projects, and that
orchestrates and streamlines the development
process. It integrates a project browser, a full-
featured code editor, language-savvy symbol recognition,
sophisticated project search capabilities, header file and
documentation access, build and debugging support, and a host
of other features.

The main window ofProject Builder iii SimlllePiayer - lIIextDeveloper/ExampleslllEXTIME Ll . ~~


combines a project browser with a
code editor.
Iconic buttons above the browser
let you access the application's
Build, Project Find, and Loaded Rles
panels, as well as the project
inspector and the graphical
debugger.
When projects are indexed, Project
Builder caches all symbols in
memory and makes them instantly
available upon request.
#import .. Controller. h"
#import .. Document. h"
#import .. SimplePlayer _Globals . h"

static NSString *curDirectory = nil; 1* Directory for open panel *1

@implementation Controller

8
, .

Interface Builder lets you craft user


interfaces from palettes of ready-
made objects, then store the
interface in a file.
A palettes window contains
assortments of standard
Application Kit and (if installed)
Enterprise Objects Framework
objects as well as custom objects
that you or third-party developers
create.
A nib file window enables the initial
definition of custom classes and
facilitates the connection of these
classes to objects on the interface.
It also catalogs the image and
sound resources used in the
interface.

• OpenStep Class Libraries. Includes NeXT's implementation of • Compiler and Library Technology. Two important features of
the Application Kit, the Foundation Kit, and Display PostScript, OPENSTEP Developer 4.0 for Mach are dynamic shared
with some extensions. libraries and frameworks. Programs linked with a dynamic
shared library share one copy ofthat library's routines, and are
• Objective-C. An object-oriented programming language that is linked with only those modules they currently need.
an extension of standard ANSI C, Dbjective-C is a simple and Frameworks assemble all library components in one place:
powerful language. It is easy to learn, yet elegant in its executable code, header files, resources, and documentation.
application to the problem domain. OPENSTEP projects can The executable code is in the form of a dynamic shared library.
include Objective-C, C, and C++ source code. The Application Kit, Foundation, and Display PostScript are
In addition, OPENSTEP Developer 4.0 offers a new version of the installed as frameworks.
GNU Ccompiler, enhancements to C++ compilation, and GNU
make technology.

9
Chapter 1 Introduction to OPENSTEP

Objects are the software equivalent of the Industrial Revolution. An Example


In the same way that modern factories assemble products out of
Object-oriented programming delivers its greatest benefits to
prefabricated components ratherthan manufacture every
large and complex programs. But its advantages can also be
product from scratch, object-orientation allows programmers to
demonstrated with a simple data structure such as might be used
build complex software by reusing software components called
in any application.
objects. Specifically, objects lead to several measurable
advantages:
count dataPointer
Greater reliability. By breaking complex software projects into
small, self-contained, and modular objects, object-orientation
ensures that changes to one part of a software project will not
adversely affect other portions ofthe software. Being small, each
With procedural programming techniques, the application is
ofthese objects is a well-tested module of code, and so the directly responsible for data manipulation. One problem with this
overall reliability of the software increases. is illustrated in the picture above: It shows a data structure
consisting of a' count variable and a data pointer. Since the
application directly manipulates the data, it has the opportunity to
introduce inconsistencies. Here, it has added an item to the data,
but has forgotten to incrementthe count;the count variable says
there are still only two data elements when in factthere are three.
The structure has become inconsistent and unreliable.
Another problem is that all parts of the application must have
intimate knowledge about the structure of the data. If the
allocation of data elements were changed from a statically
allocated array to a dynamically allocated linked list, it would
affect every part of the application that accesses, adds, or
deletes elements from the list.

--+- Messages

Greater maintainability. Since objects are modular and usually


small (in terms of the overall code size of a project), bugs in code
are easier to locate. Developers can also change the
With an object-oriented programming paradigm, the application
implementation of an object without causing havoc to other parts
as a whole wouldn't directly manipulate the data structure;
of an application.
rather, thattask is entrusted to a particular object. Since the
application doesn't directly access the data, it can't introduce
Greater productivity through reuse. One of the principal benefits inconsistencies. Note also that it's possible to change the
of object-orientation is reuse. Objects can be integrated into implementation of the object without breaking other parts of the
many applications. And through subclassing you can create application. For example, the data storage method could be
specialized objects merely by adding the code unique to the new changedto optimize performance. So long asthe object responds
object. Objects of the new subclass inherit functionality from the to the same messages, other parts of the application are
superclass, reducing coding and promoting greater reliability. unaffected by internal implementation details.

10
The Advantage of OPEN STEP

Proven Technology. NeXT Software's technology has been Don't Take Our Word For It
evolving through 10 years and four major releases. During that
time, it has been rigorously tested and iteratively refined. NeXT Here are a few comments on OPENSTEP and its predecessor,
has an established track record in object technology, while it will NEXTSTEP:
be years before its major competitors can offer comparable • Booz Allen & Hamilton's study of OPENSTEP development
technology of comparable maturity. suggests that experienced developers could increase their
True Objects. OPENSTEP objects are truly objects-modular, productivity five to ten times.
autonomous, persistent, and distributable. They are not static • "Information is our business. That's why OPENSTEP succeeds
entities, but can be bound dynamically atrun time. When you drag an here. The most important product we have is the quality of the
object from an Interface Builder palette, you're getting a real object service we provide: our timeliness, the effectiveness of our
and not an area painted on the screen with some code attached. analysis and planning."
Portability. OPENSTEP is designed to foster both hardware Director, Software Engineering
portability and operating-system portability. Fannie Mae

Simplified Client/Server Development. OPENSTEP Developer's • "We would never have be able to do what we did on time or on
integrated tool-set simplifies the complex process of building budget if we had chosen any other solution but NeXT."
distributed client/server applications. Manager, IS Branch Automation
Chrysler Financial Corporation
Substantial Business Benefits. OPENSTEP's object-orientation
helps managers to accelerate the introduction of new products • "The greatthing about object-oriented programming is thatthe
and services that depend on new software. With OPENSTEP longer you're at the game, the more benefits you derive. You
programmers can modify software quickly and assuredly to take can reuse objects you've created or add to objects to make
advantage of evolving business opportunities. Through reusable them more robust. And NEXTSTEP is the best integrated
object libraries, systems integrators can quickly customize a computer platform on the market."
generic productto produce an individualized software solution for MIS Manager,
each client. UBS Securities

In three years, Nicholas-Applegate


Capital Management reengineered
its business systems, enabling the
company to manage its business
growth from $4 billion to $14 billion
in assets. Using object technology
from NeXT, Nicholas-Applegate
was able to develop an investment
and trading environment that was
flexible and able to expand as the
company grew.

11
Chapter 1 Introduction to OPENSTEP

12
Rle Vievn~r

:dona Projects Intro f···J SPra!~ ra ... Pu~)sDev

r;y Converter

ToDo
Exchange R ate per $1: 3.45~

aunt in C)ther Currency:


CurrencyConverter.a.pp

Convert (.J'l
Chapter 2
Currency Converter Tutorial

Sections Concepts

Creating the Currency Project Indexing


Converter Project
A Window in OpenStep
Creating the Currency
Converter Interface An OpenStep Application - What You Get For Free

Designing the Currency An OpenStep Application - the Possibilities


Converter Application
Why an Object is like a Jelly Donut
Defining the Classes of
The Model-View-Controller Paradigm
Currency Converter
Class Versus Object
Implementing the Classes of
Currency Converter Paths for Object Communication: Outlets, Targets, and Actions
Building the Currency What Happens When You Build an Application
Converter Project
Where to Go For Help
Run Currency Converter

15
Currency Converter Tutorial

16
The application that you are going to create in this tutorial is called Currency
Converter. It is a simple application, yet it exemplifies much of what software
development with OpenStep is about. As you'll discover, Currency Converter is
amazingly easy to create, but it's equally amazing how many features you get
"for free" - as with all OpenStep applications.

Currency Converter converts a dollar amount to an amount in another currency,


given the rate of that currency relative to the dollar. Here's what it looks like:

CUrrency Converter ; r:J

Exchange R ate per $1 :I.~.~~_. ".~ . ~ ... #.: Enter an exchange rate and a dollar amount

Dollars to Convert: I;;~~=",-=,.)i


Am ount in Other Currency: t~P.8:~:;:;;;;:~.~;~;.:;~;.~:;;:.H
When you click the Convert button, the
converted amount appears in the Amount in
Other Currency field.

Instead of clicking the button, you can also press the Return key. You can
double-click the converted amount, copy it (with the Edit menu's Copy
command) and paste it in another application that takes text. You can tab
between the first two fields. You can do many other things common to OpenStep
applications.

In this tutorial you 'Illearn the basic things you must do to create a OpenStep
application. You will discover how to:

• Create a project.
• Create an interface.
• Create a custom subclass.
• Connect an instance of the custom subclass to the interface.
• Design an application using a common object-oriented design paradigm.

You can find the


CurrencyConverter project in the
AppKit subdirectory of
/NextDeveloper/Examples.

17
Chapter 2 Currency Converter Tutorial

By following the steps of this chapter, you will become more familiar with the
two most important OpenStep applications for program development: Interface
Builder and Project Builder. You will also learn the typical work flow of
OpenStep application development:

1. Note: Although this chapter.


Designing the
Application discusses the design of the
application midway through the
tutorial, application design can take
place anytime in the early stages of
a project, and in fact is often
recommended as the first stage.

18
Creating the Currency Converter Project

Creating the Currency Converter Proiect

Every OpenStep application starts out as aproject. A project is a repository for all
the elements that go into the application, such as source code files, makefiles,
frameworks, libraries, the application's user interface, sounds, and images. You
use the Project Builder application to create and manage projects.

Launch Project Builder.

In File Viewer navigate to the


/NextDeveloper/Apps directory.
Select ProjectBuilder.app and When Project Builder starts up, only its main menu appears on the screen. You
double-click its icon (at right).
must create or open a project to get Project Builder's main window. The New
Project panel allows you to specify a new project's name and location.

2 Make a new project. [-- -- - ---- - ------ - - - ------- -- - --- --- -- ---------

Choose New from the Project


menu (Project ~ New).
Often projects are kept in a common
In the New Project panel, directory.
select the project location.
Enter "CurrencyConverter" as the
project name.
The name specified here becomes the
Click OK to create the project. name of the project directory and the
default name of the application itself.

~~~~,___ Make sure Application is the project


ProjectType: . Application -.- ..~ : :::::<;5'1 type.

Project Builder creates a project directory named after the project-in this case
CurrencyConverter-and populates this directory with an assortment of ready-
made files and directories. It then displays its main window.

Note: Here's a variation on project creation: Create a project directory using File
Viewer and then, in the New Project panel, navigate to that directory, type
"PB.project" in the Name field, and click OK.

19
Chapter 2 Currency Converter Tutorial

Command panel: Build,


Rnd, Loaded Rles, Project
Inspector, Launcher/
Classes Graphical Debugger.
Headers
Other Sources
Interfaces·
Images J~ ~-..o._-..o.-..o.~_ _ _ Project browser. Each
Other Resource f'
SUbprOjects "suitcase" is a project
Context Help. r;; resource category.
Supporting FUe t;
Frameworks f~
Li braries f'.

----:-t--- Code editor

Go ahead and click an item in the left column of the project browser (a grouping
of project resources sometimes called a "suitcase"); see what some of these
A makefile specifies file suitcases contain already:
dependency relations and
compiler and linker instructions • Other Sources: This suitcase contains CurrencyConvertecmain.m, the mainO routine
for building the project. See that loads the initial set of resources and runs the application. (Do not modify
OPENSTEP Development: Tips this file!)
and Techniques for common
changes to Makefile.preamble and • Interfaces: This suitcase contains CurrencyConverter.nib, the file that contains the
Makefile.postamble. application's user interface. More on this file in the next step.

• Supporting Files: This suitcase contains the project's default makefiles and
template source-code files. You can modify the preamble and postamble
makefiles, but you must leave Makefile unchanged.

Project Indexing

When you create or open a project, after Usually indexing happens automatically
some seconds you may notice triangular when you create or open a project. You can
"branch" buttons appearing after source turn off this option if you wish. Choose
code files in the browser. Project Builder has Preferences from the Info menu and then
indexed these files. choose the Indexing display. Turn off the
"Index when project is opened" switch.
During indexing Project Builder stores all
symbols of the project (classes, methods,
globals, etc.) in virtual memory. This allows You can also index a project at any time by
Project Builder to access project-wide choosing Index Source Code from the
information quickly. Indexing is Project menu. If you want to do without
indispensable to such features as name indexing (maybe you have memory
completion and Project Find. (More on these constraints), choose Purge Indices from the
features later.) Project menu.

20
Creating the Currency Converter Interface

Creating the Currency Converter Interface

When you create an application project, Project Builder puts the maillllib jile in
the Interfaces suitcase. A nib file is primarily a description of a user interface (or
part of a user interface). The main nib file contains the main menu and any
windows and panels you want to appear when your application starts up; at start-
up time, each application loads the main nib file.

At the beginning of a project, the main nib file is like a blank canvas, ready for
you to craft the interface. Look in the Interfaces suitcase for nib files.

Open the main nib file.


To open, double-click the nib file name ...or double-click the icon Palette window
Locate CurrencyConverter.nib in
the project browser.
Double-click to open.

A nib file contains user-interface


objects, definitions of custom
classes, the connections between
objects, and sounds and images
that are used in the interface.
Besides the main nib file, you can
have nib files that you can load
whenever you need them. These
auxiliary nib files, and the When you first open the application's main nib file,
Nib file window Interface Builder displays a blank window.
techniques related to using them,
are described in the "To Do
Tutorial," page 118. See By default, the window entitled "My Window" will appear when the application
OPENSTEP Developmem: Tools is launched.
and Techniques for an overview of
nib files. Note: The Interface Builder application is located in/NextDeveloper/Apps. The icon
for the application is this:

21
Chapter 2 Currency Converter Tutorial

A window in Open Step looks very similarto windows in other user its windows and tracks the current status of each. Each window,
environments such as Windows or Macintosh. It is a rectangular on the other hand, manages a hierarchy of views in addition to its
area on the screen in which an application displays controls, PostScript window.
fields, text, and graphics. Windows can be moved around the
At the "top" of this hierarchy is the content view, which fits just
screen and stacked on top of each other.like pieces of paper. A
within the window's content rectangle. The content view
typical OpenStep window has a title bar, a content area, and
encloses all other view (its subviews), which come below itinthe
several control objects.
hierarchy. The NSWindow distributes events to views in the
hierarchy and regulates coordinate transformations among them.

Miniaturize and close


buttons

resize bar

Many user-interface objects other than the standard window


depicted above are windows. Menus, pop-up lists, and pull-down
lists are primarilywind9ws, as are all varieties of panels: attention
panels, inspectors, and tool palettes, to name a few. In fact,
anything drawn on the screen must appear in a window. Another rectangle, the frame rectangle, defines the outer
boundary of the window and includes the title bar and the
NSWindow and the Window Server window's controls. The lower-left corner of the frame rectangle
defines the window's location relative to the screen's coordinate
Two interacting systems create and manage OpenStep windows. system and establishes the base coordinate system for the views
On the one hand, a window is created by the Window Server. The of the window. Views draw themselves in coordinate systems
Window Server is a process integrating the NeXT Window transformed from (and relative to) this base coordinate system.
System and Display Postscript. The Window Server draws,
See page 149 for more on the view hierarchy.
resizes, hides, and moves windows using Postscript primitives.
The Window Server also detects user events (such as mouse
clicks) and forwards them to applications. Key and Main Windows

The window that the Window Server creates is paired with an Windows have numerous characteristics. They can be on-screen
object supplied by the Application Kit: an instance of the or off-screen. On-screen windows are "Iayered" on the screen in
NSWindow class. Each physical window in an object-oriented tiers managed by the Window Server. On-screen windows also
program is managed by an instance of NSWindow (or subclass). can carry a status: keyor main.

When you create an NSWindow object, the Window Server Key windows respond to key presses for an application and are
creates the physical window that the NSWindow object will the primary recipient of action messages from menus and panels.
manage. The Window Server references the window by its Usually a window is made key when the user clicks it Key
window number, the NSWindow by its own identifier. windows have black title bars. Each application can have only
one key window.
Application, Window, View An application has one main window, which can often have key
status as well. The main window is the principal focus of user
In a running OpenStep application, NSWindow objects occupy a
actions for an application. Often user actions in a modal key
middle position between an instance of NSApplication and the
window (typically a panel such as the Font panel or an inspector)
views of the application. (A view is an object that can draw itself
have a direct effect on the main window. In this case, the title bar
and detect user events.) The NSApplication object keeps a list of
of the main window (when it is not key) is a dark gray.

22
Creating the Currency Converter Interface

2 Resize the window.

Make the window smaller by


dragging an edge of the window
inward from a resize handle.
Between the two resize handles
is the resize bar, which permits
only vertical resizing.

Most objects on an interface have attributes that you can set in the Inspector
panel's Attributes display.

3 Set the window's title and


attributes.

Click the window to select it.


Choose Tools ~ Inspector.

C
The title of the major window in
an application is often the
Selectthe Attributes display from
application name.
the pop-up list.
Backlng - Controls -
Enter the window title. C Nonretained Miniaturize.:kl
C Retained Close .:J When this option is turned off,
Turn off the resize option. []
C Buffered R~e~si~ze~b~a~lf=lt::tt_ _ _- the windows's resize bar
'""" , c " w' " ""

" 'w ,wOptions, disappears.


Release when closedD
11 Hide on daactivatel.:J
Ii Visible at launchtim~21
Ii Deferredl21
One shot',:J "
Dynamic depth limif.:J
Wants to be color.:J

23
Chapter 2 Currency Converter Tutorial

Put palette objects on the window using the "drag and drop" technique.

4 Put a text field on the interface


and resize and initialize it.
Click this icon to select the Views
Select the Views palette. palette. This palette contains an
assortment of commonly used objects.
Drag a text field from the palette
onto the window.

Drag a text field and drop it (that is,


release the mouse button) over the
''surface'' of the window.

To initialize the text field, double- You must get rid of the word "Text" in this field; otherwise, that's what the field
click "Text" and press Delete. will show when the nib file is loaded.

The text field should be longer so it can hold more digits (you're dealing with
millions here):

Lengthen the text field.

Drag a resize handle in the direction


you want the object to grow.

Currency Converter needs two more text fields, both the same size as the first.
You have two options: you can drag another object from the palette and make it
the same size; or you can duplicate the first object

24
Creating the Currency Converter Interface

5 Duplicate an object.

Select the text field. ..II The new text field appears slightly offset
from the original field. Reposition it under the
.Q
Choose Edit ~ Copy. first text field.
Choose Edit ~ Paste.

Get the third field from the palette and make it the same size as the first field.

6 Make objects the same size.


The first object you select should have the
Drag a text field onto the window. dimensions you want the other objects in the
selection to take.
Delete "Text" from the text field.
Select the first text field.
Shift-click to select the new text r
Ii "~\lI=-----L-- Shift-click multiple objects to include them in
--;-;11
7
- the same selection.
!2

field.
Choose Format ~ Size ~ Same
Size
You're not done yet with these text fields. The bottom text field displays the
result of the computation. It should not be editable and therefore should, by
convention, have a gray background.

7 Change the attributes of a text


field.

Select the third text field.


Choose Tools ~ Colors. Drag the gray color into this well to set
the background color.
Selectthe grayscale palette of
the Color panel.
Select the gray color that is the
same as the window background. Click to get the color that blends the text
field into the window background.
Choose Tools ~ Inspector.
Selectthe Inspector panel's
Attributes display.
Drag the gray colorfromthe Color With the Editable attribute turned off,
+--+----~--- users cannot alter the contents of the
panel into the Background Color
text field.
well.
• ,~. 1·;~. !II Keep Selectable as an option so the user
Turn off the Editable and can select copy, and paste the result to
Scrollable options. other applications.

The Views palette provides a "Title" object that you can easily adapt to be a
text-field label. (The title object is actually a text field, set to have a gray
background and no border, and to be non-editable and non-selectable.) Text in

25
Chapter 2 Currency Converter Tutorial

the title object is centered by default, but labels are usually aligned from the
right.

8 Assign labels to the fields.

Drag a title object onto the


window.
The text is highlighted when it is selected.
Double-click to select the text
"Title".
Choose Format ~ Text ~ Align
Right to align the text from the
right.
The size of the text is rather large for a label, so change it. You set font family,
typeface, and size with the standard Open Step Font panel.

Make sure the object's text is


selected.
Choose Format ~ Font ~ Font
Panel.
Setthe label text to 16 points.
Make two copies of the label.
Position all labels to the left of
their text fields.

The font of this object is 18 point Helvetica.


Click here and then click the Set button to set
the font size to 16 points.

When you cut and paste objects that contain text, like these labels, the object
should be selected and not the text the object contains; if the text is selected,
de-select it by clicking outside the text, then click the object again to select it.

Type the text of each label.

Double-click to select title, then type the text


of the label in place of the selection.

26
Creating the Currency Converter Interface

9 Add a button to the interface and


initialize it.

Drag the button object from the


Exchange Rate per$1 :1--:-----:
Views palette and put it on the Dollars to Convert: L________ I
lower-right corner ofthe window. Amount in Other Currency: t.,;. You can resize buttons the same way you resize
text fields or any other object on a window.
Make the button the same size as
a text field. Double-click the title of the button to select it.

Change the title ofthe button to


"Convert".
You can easily give the button the capacity for responding to carriage returns in
addition to mouse clicks.

Selectthe Images display ofthe


nib file window. Select a display of the nib file window
Drag the NSReturnSign image to by clicking a tab. Exchange Rate per$1: '.--;-;;~.=c;-.-;;-;;=<.c;-d;
the main window and drop it over Dollars to Convert:
!~~=~~"
the button.

After you drop the image


over the button, the image
appears (by default) to the
right of the button title.

If you check the attributes of the button in the Inspector panel, you'll notice two
things have been added: NSReturnSign is now listed as the button's icon, and the
Key field contains the escape sequence for a carriage return (\r).

You've probably noticed that the final interface for Currency Converter (shown
on the first page of this chapter) has a decorative line between the text fields and
the button. This line is easy to make.

27
Chapter 2 Currency Converter Tutorial

10 Create a horizontal decorative


line.

Drag a box object from the Views


palette onto the interface.
Bring up the Attributes display for Qlrrency Converter ,.. ; X
the box (Command-l), select No
Title, and setthe Vertical Offsetto
zero. Exchan g~. Rate, pe r,$i1~ .'; '; ;!~i'C; !w; ; ;' '; ' ' ' ' ' ' ' '; ' ;' ; ; ;'; ;' '}'

Drag the bottom-middle resize Dollars toC onve.~t: !c~==~,,-==.!;.


handle of the box upward until the
horizontal lines meet.
Position the line above the button.
Drag the end points of the line
until the line extends across the Drag upward until lines merge into one line.
window.

For a black line (instead of white) click here.

As you might have noticed, the Currency Converter has a main menu that holds,
by default, the commands Info, Hide, and Quit, and the Edit, Services, and
Windows menus. The menus contain ready-made sets of commands. The Edit
menu includes commands for cutting, copying, and pasting text. The Windows
menu lists the titles of open windows as well as common window commands.
The Services menu allows your application to communicate with other
applications, often with no work on the part of your application. For example, if
your application handles text, you can use the Services menu to transfer
information to other applications that accept text.

Aligning on a Grid

You can align objects on a window by nearest grid intersection like nails to a
imposing a grid on the window. When you magnet. You set the edges of alignment and
move objects in this grid, they "snap" to the the spacing of the grid (in pixels) in the
Alignment panel. Choose Format ~Align ~
Alignment to display this panel.
Be sure the grid is turned on before you move
objects (Format ~Align ~ Turn Grid On).
You can move selected user-interface
objects in Interface Builder by pressing an
arrow key. When the grid is turned onthe unit
of movement is whatever the grid is set to (in
pixels). When the grid is turned off, the unit of
movement is one pixel.

28
Creating the Currency Converter Interface

Currency Converter's interface is almost complete. One finishing touch might


be to align the text fields and labels in neat rows and columns. Interface Builder
gives you several ways to align selected objects on a window.

• Dragging objects with the mouse


• Pressing arrow keys (with the grid off, the selected objects move one pixel)
• Using a reference object to put selected objects in rows and columns
• Specifying origin points in the Size display of the Inspector panel
• Using a grid (see preceding side bar)

For Currency Converter, use the columns-and-rows technique.

11 Align the text fields and labels in COLUMNS


rows and columns.
First select the object whose vertical position
the other objects should adopt (the reference
Select the three text fields and object).
choose Format ~ Align ~ Make
Column.
Amount in Other Currency: ~ --:...;-.--- Shift-click the other objects to include them in
Select the first text field and its the selection.
label and choose Format ~
Convert Making a column evens the spacing between
Align ~ Make Row. objects in the selection.
Repeat the last step for the
second and third text fields and ROWS
their labels. When you make a row, the selected objects
rest on a common horizontal baseline.
Dollars to Convert:
tl U :r-~
:Amount in Ot~er CurrencY:Ql ':"~;;; u :::::

The nextKeyView variable is an The final step in composing the Currency Converter interface has more to do
outlet. An outlet is the identifier of with behavior than appearance. You want the user to be able to tab from the first
an object that another object
editable field to the second, and back again to the first. Many objects on
stores as an instance variable.
Outlets enable communication
Interface Builder's palettes have an instance variable named nextKeyView. This
between objects. See page 40 for variable identifies the next object to receive keyboard events when the user
more information on outlets. presses the Tab key (or the previous object if Shift-Tab is pressed). If you want
inter-field tabbing you must connect fields through the nextKeyView variable.

29
Chapter2 Currency Converter Tutorial

12 Enable tabbing between text


fields.
When you press Control and drag the mouse
Selectthe first text field. from an object a connection line is drawn.
Control-drag a connection line When a line encloses the destination object
from itto the second text field. release the mouse button.
In the Inspector panel
(Connections display) select
nextKeyView and click Connect.
Repeat the same procedu"re, When you make a visual connection such as this, Interface Builder brings up the
going from the second to the first Connections display of the Inspector panel:
field.

The nextKeyView outltet identifies the next object to


respond to events after the Tab key is pressed.

Be sure to click the Connect button to confirm the


connection (the button title then changes to Disconnect).

Don't connect the nextKeyView outlet of the "Amount in Other Currency" field;
this field is not supposed to be editable.

30
Creating the Currency Converter Interface

13 Testthe interface. The CurrencyConverter interface is now complete. Interface Builder lets you
test an interface without having to write one line of code.
Choose Document ~ Save to
save the interface to the nib file.
Note: You can also exit from test mode by double-clicking the Interface Buildfer
Choose Document ~ Test icon, which changes to the following image to represent test mode:
Interface.
Try various operations in the
interface (see suggestions on the
following pagel.
When finished, choose Quit from
the main menu.

31
Chapter 2 Currency Converter Tutorial

The simplest OpenStep application, even one without a line of could also have setthe auto-resizing attributes of the window and
code added to it, includes a wealth of features that you get "for its views so thatthe window's objects would resize proportionally
free": You do not have to program these features yourself. You can to the resized window or would retain their initial size (see
see this when you test an interface in Interface Builder. OPENSTEP Programming: Tools and Techniques for details on
auto-resizing).
To enter test mode, choose Test Interface from the Document
menu. Interface Builder simulates how your application (in this
case, Currency Converter) would run, minus the behavior added Controls and Text
by custom classes. Go ahead and try things out: move your
windows, type in fields, click buttons. The buttons and textfields of Currency Converter come with many
built-in behaviors. Click the Convert button. Notice how the button
is highlighted momentarily.
Application and Window Behavior
In test mode Currency Converter behaves almost like any other
application on the screen. Click elsewhere on the screen, and
Currency Converter is deactivated, becoming totally or partially
obscured by the windows of other applications. If you had buttons of a different style, such as radio buttons, they
would also respond in characteristic ways to mouse clicks.

Now click in one of the text fields. See how the cursor blinks in
place. Type some text and select it. Use the commands in the Edit
menu to copy it and paste it in the other text field.

Do you recall the nextKeyView connections we made between


Currency Converter's text fields? While a cursor is in a text field,
press the Tab key and watch the cursor jump from field to field.

When You Add Menu Commands

An application you design in Interface Builder can acquire extra


functionality with the simple addition of a menu command or
submenu. You've already seen what you getwith the Services and
Reactivate Currency Converter by clicking on its window or by
Windows menu, both included by default. You can add other
double-clicking its icon (the defaultterminal icon) in the
commands and submenus to the main menu for "free"
workspace. Move the window around by its title bar.
functionality without compilation. For example:
Here's some other tests you can make:
• The Font submenu adds behavior for applying fonts to text in
• Click the Edit submenu in Currency Converter's main menu. It NSText objects, such as the one in the scroll view object in the
expands and contracts as in any application. DataViews palette. Your application gets the Font panel and a
font-manager object "for free."
• Click the miniaturize button or choose the Hide command.
Double-click the document icon to getthe window back. • The Text submenu allows you to align text anywhere there is
• Click the close box and the Currency Converter window editable text, and to display a ruler in the NSText object for
disappears. (Choose Quit from the main menu and re-entertest tabbing, indentation, and alignment.
mode to get the window back.)
Many objects that display text or images can print their contents
If we had configured Currency Converter's window in Interface as PostScript data. Later you'll learn how to add the Print menu
Builder to retain the resize bar, we could also resize it now. We command and have it invoke this capability.

32
Creating the Currency Converter Interface

An OpenStep Application - The Possibilities

An OpenStep application can do an impressive range of things management), the Color panel (and color management), and,
without a formidable programming effort on your part. although it's not a panel, the text ruler and the tabbing and
indentation capabilities it provides.
Doc·ument Management
Formatter classes enable your application to format numbers,
Many applications create and manage semi-autonomous objects dates, and other types of field values. Support for validating the
called documents. Documents contain discrete sets of
contents of fields is also available.
information and support the entry and maintenance of that
information. A word-processing document is a typical example.
The application coordinates with the user and communicates Printing and Faxing
with its documents to create, open, save, close and otherwise
manage them. With just a simple Interface Builder procedure, OpenStep
The final tutorial in this book describes how to create an automates simple printing and faxing of views that contain text or
application based on a multi-document architecture. graphics. When a user clicks the control, an appropriate panel
helps to configure the print or fax process. The output is
File and Account Management WYSIWYG.

An application can use the Open panel of the Application Kit to Several Application Kit classes give you greater control over the
help the user locate files in the file system and open them. It can printing of documents and forms, including features such as
also make the Save panel available for saving information in files. pagination and page orientation.
NeXT's version of OpenStep also provides classes for managing
files in the file system (creating, comparing, copying, moving, and
so forth) and for managing system-account information and user Help
defaults.
You can create a help system for your application using Interface
Communicating With Other Applications Builder, Project Builder, and an RTF text editor (such as Edit). The
Application Kit includes an class for context-sensitive help. If the
OpenStep gives an application several ways of communicating
user clicks an object on the application's interface while pressing
information to and from other applications:
a Help key, a small window is displayed containing concise
• Pasteboard: The pasteboard is a global facility for sharing information on the object.
information among applications. Applications can use the
pasteboard to hold data that the user has cut or copied and
may paste into another application. Custom Drawing and Animation

• Services: Any application can avail itself ofthe services OpenStep lets you create your own custom views that draw their
provided by another application, based on the type of the own content and respond to user actions. To assist you in this,
selected data (such as text). An application can also provide OpenStep provides image-compositing and event-handling API
services to other applications such as encryption, language as well as PostScript operators, operator functions, and client
translation, or record-fetching. library functions.
• Drag-and-drop: If your application implements the proper
protocol, users can drag objects to and from the interfaces of
Plug and Play
other applications.

You can design some applications so that users can incorporate


Editing Support
new modules later on. For example, a drawing program could
You can get several panels (and associated functionality) when have a tools palette: pencil, brush, eraser, and so on. You could
you add a submenu to your application's main menu in Interface create a new tool and have users install it. When the application
Builder. These "add-ons" includes the Font panel (and font is next started, this tool appears in the palette.

33
Chapter 2 Currency Converter Tutorial

Designing the Currency Converter Application

An object-oriented application should be based on a design that identifies the


objects of the application and clearly defines their roles and responsibilities. You
normally work on a design before you write a line of code. You don't need any
fancy tools for designing many applications; a pencil and a pad of paper will do.

Currency Converter is an extremely simple application, but there's still a design


behind it. This design is based upon the Model-View-Controller paradigm, a
model behind many designs for object-oriented programs (see "The Model-
View-Controller Paradigm" on page 36). This design paradigm aids in the
development of maintainable, extensible, and understandable systems. But
first, you might want to read the sidebar below to understand the symbol used
in the design diagram.

Note: This design for Currency Converter is intended to illustrate a few points,
and so is perhaps overly designed for something so simple. It is quite possible to
have the application's controller class, ConverterController, do the computation
and do without the Converter class.

Why an Object is Like a Jelly Donut

This book depicts objects as filled and segmented "donuts." Why and may return data to the requesting object. As the symbol
this unlikely shape? suggests, an object's methods do the encapsulating, in effect
mediating access to the object's data. An object's methods are
also its interface, articulating the ways in which the object
communicates with the world outside it.

The donut symbol also helps to convey the modularity of objects.


Because an object encapsulates a defined set of data and logic,
you can easily assign it to particular duties within a program.
Conceptually, it is like a functional unit-for instance, "Customer
Record"-thatyou can move around on a design board; you can
This symbol illustrates data encapsulation, the essential then plot communication paths to and from other objects based
characteristic of objects. An object consists of both data and on their interfaces.
procedures for manipulating that data. Other objects or external
code cannot access that data directly, but must send messages
See the appendix "Object Oriented Programming," for a fuller
to the object requesting its data.
description of data encapsulation, messages, methods, and other
An object's procedures (called methods) respond to the message properties of objects.

34
Designing the Currency Converter Application

You can divide responsibility within Currency Converter among two custom
objects and the user interface, taken as a collection of ready-made Application
Kit objects. The Converter object is responsible for computing a currency
amount and returning that value. Between the user interface'and the Converter
object is a cOlltrol/erobject, ConverterController. ConverterControl1er coordinates
the activity between the Converter object and the ur objects.

Exchange Rate per $1 :1,45

Dollars to Convert: '~ooR,


Am ount In Other Currency: ,"-90-.9---

----------.-.---.--,-~I " " ~

tn\ ConverierConlrolier

/.:::sJ
·~dnve;i"
I/'''''-·~''''''''' "
r
\
yI Converter

The ConverterController class assumes a central role. Like all controller objects,
it communicates with the interface and with model objects, and it handles tasks
specific to the application, such as managing the cursor. ConverterController
gets the values users enter into fields, passes these values to the Converter
object, gets the result back from Converter, and puts this result in a field in the
interface.

The Converter class merely computes a value from two arguments passed into
it and returns the result. As with any model object, it could also hold data as well
as provide computational services. Thus, objects that represent customer
records (for example) are akin to Converter. By insulating the Converter class
from application-specific details, the design for Currency Converter makes it
more reusable, as you'll see in the Travel Advisor tutorial.

35
Chapter 2 Currency Converter Tutorial

A common and useful paradigm for object-oriented applications, Controller Object


particularly business applications, is Model-View-Controller
(MVC). Derived from Smalltalk-80, MVC proposes three types of Acting as a mediator between Model objects and View objects in
objects in an application, separated by abstract boundaries and an application is a Controller object. There is usually one per
communicating with each other across those boundaries.
application or window. A Controller object communicates data
back and forth between the Model objects and the View objects.
It also performs all the application-specific chores, such as
loading nib files and acting as window and application delegate.
Since what a Controller does is very specific to an application, it
is generally not reusable even though it often comprises much of
an application's code. (This last statement does not mean,
however, that Controller objects cannotbe reused; with a good
design, they can.)

Because of the Controller's central, mediating role, Model


objects need not know about the state and events of the user
interface, and View objects need not know aboutthe
programmatic interfaces of the Model objects. You can make your
View and Model objects available to others from a palette in
Interface Builder.
Model Objects

This type of object represents special knowledge and expertise. Hybrid Models
Model objects hold a company's data and define the logic
that manipulates that data. For example, a Customer object, MVC, strictly observed, is not advisable in all circumstances.
common in business applications, is a Model object. It holds data
Sometimes its bestto combine roles. For instance, in a graphics-
describing the salient facts of a customer and has access to
algorithms that access and calculate new data from those facts. intensive application, such as an arcade game, you might have
A more specialized Model class might be one in a meteorological several View objects that merge the roles of View and Model.
system called Front; objects of this class would contain the data In some applications, especially simple ones, you can combine
and intelligence to represent weather fronts. Model objects are the roles of Controller and Model; these objects join the special
not displayable. They often are reusable, distributed, persistent, data structures and logic of Model objects with the Controller's
and portable to a variety of platforms.
hooks to the interface.

View Objects
A Note on Terminology
A View object in the paradigam represents something visible on
the user interface (a window, for example, or a button). A View The Application Kit and Enterprise Objects Framework reserve
object is "ignorant" of the data it displays. The Application Kit special meanings for "vew object" and "model." A view object in
usually provides all the View objects you need: windows, text the Application Kit denotes a user-interface object that inherits
fields, scroll views, buttons, browsers, and so on. But you might from NSView. In the Enterprise Objects Framework, a model
wantto create your own View objects to show or represent your
establishes and maintains a correspondence between an
data in a novel way (for example, a graph view). You can also
group View objects within a window in novel ways specific to an enterprise object class and data stored in a relational database.
application. View objects, especially those in kits, tend to be very This book uses "model object" only within the context ofthe
reusable and so provide consistency between applications. ModelNiew-Controlier paradigm.

36
Defining the Classes of Currency Converter

Defining the Classes of Currency Converter

Interface Builder is a versatile tool for application developers. It enables you not
only to compose the application's graphical user interface, but it gives you a way
to define much of the programmatic interface of the application's classes and to
connect the objects eventually created from those classes.

You must go to the Classes display of the nib file window to define a class. Once
there, the first thing you must do is select the superclass, the class your new
subclass will inherit from. Let's start with the ConverterController class.

Specify a subclass.
Click to select the Classes display.
Go to the Classes display ofthe
nib file window.
Select NSObject, the superclass
of your custom classes. NSObject the root class, is the class that
Choose Subclass from the pull- ConverterController will inherit from.
down Operations menu.
The Subclass command in this pull-down
menu generates a new subclass.

After you choose the Subclass command, "MyNSObject" appears under


"NSObject" highlighted.

Class Versus Object

To newcomers to the subject, explanations of object-oriented What especially differentiates a class from its instance is data. A
programming might seem to use the terms "object" and "class" instance has its own unique set of data but its class, strictly
interchangeably. Are an object and a class the same thing? And if speaking, does not. The class defines the structure of the data its
not, how are they different? How are they related? instances will have, but only instances can hold data.
An object and a class are both programmatic units. They are A class, on the other hand, implements the behavior of all of its
closely related, but serve quite different purposes in a program. instances in a running program. The donut symbol used to
represent objects is a bit misleading here, because it suggests
First, classes provide a taxonomy of objects, a useful way of
that each object contains its own copy of code. This is fortunately
categorizing them. Just as you can say a particular tree is a pine
not the case; instead of being duplicated, this code is shared
tree, you can identify a particular object by its class. You can among all current instances in the program.
thereby know its purpose and what messages you can send it. In
other words, a class describes the type of an object. Implicit in the notion of a taxonomy is inheritance, a key property
of classes. Classes exist in a hierarchical relationship to one
Second, you use classes to generate instances-or objects.
another, with a subclass inheriting behavior and data structures
Classes define the data structures and behavior of their
from its superclass, which in turn inherits from its superclass.
instances, and at run time create and initialize these instances.
In a sense, a class is like a factory, stamping out instances of itself See the appendix, "Object-Oriented Programming," for more on
when requested. these and other aspects of classes.

37
Chapter 2 Currency Converter Tutorial

Enter the name of the subclass:


"ConverterController:" After you name the class, it appears
Press Return. indented under its superclass in
alphabetical order.

To see subclasses of a class, click a filled


button (if the button is unfilled, there are no
subclasses).
NSCell, for example, has several levels of
subclasses; each level is indicated by
indentation.

Now your class is established in the hierarchy of classes within the nib file. Next,
specify the paths for messages travelling between the ConverterController
object and other objects. In Interface Builder you specify these paths as outlets
and actions.

Before You Go On - - - - - - - - - - - - - - - - - - - - -
Here's some basic terminology:

Outlet An object held as an instance variable and typed as id. Objects in


See Paths for Object applications often hold outlets as part of their data so they can send messages to
Commullicatioll: Out/ets, Targets,
the objects referenced by the outlets. An outlet lets you keep track of or
alld Actions on page 40. for a more
detailed description of outlets manipulate something in the interface.
and actions. See page 103 for
more on control objects and their id The generic (or dynamic) type of objects (technically the address of an
relation to cells and formatters. object).

Action Refers both to a message sent to an object when the user clicks a button
or manipulates some other control object and to the method that is invoked.

Control object A user-interface object (a device) with which users can interact to
affect events in the application. Control objects include buttons, text fields,
forms, sliders, and browsers. All control objects inherit from NSControl.

38
Defining the Classes of Currency Converter

2 Define your class's outlets.

In the nib file window, click the -~~~~~~:"""'lT~~~~ ---- Click here to begin specifying outlets.
electrical-outlet icon to the right
ofthe class. "Outlets" appears indented underneath,
highlighted (not shown).
Choose Add Outlet from the
Operations pull-down menu Instead of choosing Add Outlets from the
Operations menu, you can press Return
Type the name of the outlet over when "Outlets" is highlighted to add an
the highlighted "myOutlet." Name outlet.
the first outlet rateField.
Press Return.
Repeat the last three steps to
define two other outlets:
doliarField
totalField

ConverterController has one action method, convert:. When the user clicks the
Convert button, a convert: message is sent to the target object, an instance of
ConverterController.

3 Define your class's actions.

In the Classes display of the nib


file window, click the crosshairs The crosshairs suggest the "target" in the
icon. target/action paradigm.
Choose the Add Action command After you chose Add Action "myAction"
from the Operations pull-down appears indented under "Actions. "
menu.
You only need to type convert here-
Type the name of the action Interface Builder adds the colon.
method, convert:.
Press Return.
Be/ore You Go On - - - - - - - - - - - - - - - - - - - - -
Add an outlet: ConverterController needs to access the text fields of the interface,
so you've just provided outlets for that purpose. But ConverterController must
also communicate with the Converter class (yet to be defined). To enable this
communication, add an outlet named converter to ConverterController.

39
Chapter 2 Currency Converter Tutorial

Outlets When You Make a Connection in Interface Builder


An outlet is an instance variable that identifies an object. As with any instance variable, outlets must be initialized at run
timeto some reasonable value-in this case, an object's identifier
(id value). Because of Interface Builder, an application can
initialize outlets when it loads a nib file.
When you make a connection in Interface Builder, a special
connector object holds information on the source and destination
objects of the connection. (The source object is the object with
the outlet.) This connector object is then stored in the nib file.
When a nib file is loaded, the application uses the connector
object to set the source object's outlet to the identifier of the
destination object.
You can communicate with other objects in an application by It might help to understand connections by imagining an electrical
sending messages to outlets. outlet (as used in the Classes display of the nib file window)
embedded in the destination object. Also picture an electrical
An outlet can reference any object in an application: user- cord extending from the outlet in the source object Before the
interface objects such as text fields and buttons, windows and connection is made the cord is unplugged and the value of
panels, instances of custom classes, and even the application destination is undefined; afterthe connection is made (the cord is
object itself. plugged in), the id value ofthe de~tination object is assigned to
the destination outlet

source destination

Outlets are declared as:

id anObjecti

You can use id as the type for any object; objects with id as their
type are dynamically typed, meaning thatthe class ofthe object is
determined at run time. You can statically type an object as a
pointer to a class name; you can declare these objects as
instance variables, butthey are not outlets. What distinguishes
outlets is their relationship to Interface Builder.

Interface Builder can "recognize" outlets in code by their


declarations, and it can initialize outlets. You usually set an
outlet's value in Interface Builder by drawing connection lines
between objects. There are ways other than outlets to reference
objects in an application, but outlets and Interface Builder's
facility for initializing them are a great convenience.

40
Defining the Classes of Currency Converter

Target/Action in Interface Builder-What's Going On Which Direction to Connect?


Usually the outlets and actions that you connect belong to a
As you'll soon find out, you can view (and complete) target/action custom subclass of NSObject Forthese occasions, you need only
connections in Interface Builder's Connections inspector. This follow a couple simple rules to know which way to draw a
inspector is easy to use, but the relation of target and action in it connection line in Interface Builder.
might not be apparent First, target is an outlet of a cell objectthat
identifies the recipient of an action message. Well (you say) • To make an action connection, draw a line to the custom
what's a cell object and what does it have to do with a button?- instance from a control object in the user interface, such as a
that's what I'm making the connection from. button or a text field.

One or more cell objects are always associated with a control • To make an outlet connection, draw a line from the custom
object (that is, an object inheriting from NSControl, such as a instance to another object in the application.
button). Control objects "drive" the invocation of action methods,
Another way to clarify connections is to consider who needs to
but they get the target and action from a cell. NSActionCell
find whom. With outlets, the custom object needs to find some
defines the target and action outlets, and most kinds of cells in the
other object, so the connection is from the custom objectto the
Application Kit inherit these outlets.
other object With actions, the control object needs to find the
custom object, so the connection is from the control object
These are only rules of thumb for the common case, and do not
.MiOg. apply in all circumstances. For instance, many OpenStep objects
have a delegate outlet; to connect these, you draw a connection
line from the OpenStep object to your custom object

i
I
action r.::'~------
.. :I Convert ~ ¢!ll
_________ .J, / . ---
", ....,.--_.... ,.............. ". ..,. ............... -.. - .. ".-... .. NSActionCell
ConnecUons
myController

1I Ir'" ............
' ......•.1....
·Rcvc,t ..
",. . . . . . . . . . . . . . . . . . . .11II
·r··lr···conneci··~~····-

For example, when a user clicks the Convert button of Currency


Converter, the button gets the required information from its cell
and sends the message convert to the target outlet, which is an
instance of your custom class ConverterController.
In the Actions column of the Connections inspector are all action
methods defined by the class of the target object and known by
Interface Builder. Interface Builder identifies action methods
because their declarations follow the syntax:
- (void)doThis: (id) sender;
It looks in particular for the argument sender.

41
Chapter 2 Currency Converter Tutorial

Connecting ConverterController to the Interface


As the final step of defining a class in Interface Builder, you create an instance
of your class and connect its outlets and actions.

Generate an instance of the


class.

In the Classes display, select the


ConverterControlier class. n-r:~~~*i~~~~L===~~fr:~~t--- Click the class name to col/apse outlets and
actions. If they are already collapsed, make
sure your subclass is selected.
Choose the Instantiate command
from the Operations pull-down
menu.

~~~~~~!~!!!~~~~~~--- Choose this command to generate an


instance of your custom class.

Note: The Instantiate command does not generate a true instance of


ConverterController, but creates a stand-in object used for establishing
connections. When the nib file's contents are unarchived, Interface Builder will
create true instances of these classes and use the proxy objects to establish the
outlet and action connections.

When you instantiate a class (that is, create an instance of it), Interface Builder
switches to the Instances display and highlights the new instance, which is
named after the class.

Now you can connect this ConverterController object to the user interface. By
connecting it to specific objects in the interface, you initialize your outlets.
ConverterController will use these outlets to get and set values in the interface.

42
Defining the Classes of Currency Converter

5 Connect the custom class to the


interface via its outlets.

In the Instances display ofthe nib


file window, Control-drag a
connection line from the Control-drag from an object with defined
ConverterControlier instance to outlets )often an instance of a custom
class).
the first text field.
Whenthefield is outlined in black,
release the mouse button.

Exch an 9e Rate per $1 :1F==t==:=+-~--- When a black line encloses an object it will
be selected as the destination object of the
connection if you release the mouse
button.
Amount in Other Currency:

Convet1 'PI

Interface Builder brings up the Connections display of the Inspector panel. This
display shows the outlets you have defined for ConverterController.

In the Connections display, select CUstllm Object Inspectllr IX


the outlet that corresponds to the Connections
first field (rateFieldl.
Outlets of the destination object appear
Click the Connect button. under this column of the Connections
display.
Following the same steps,
connect ConverterControlier's
dollarField and totalField outlets
to the appropriate text fields.

I~~~~~~~~~~~~~l--- When you click Connect the connection


appears here, including the class of the
destination object.

To receive action messages from the user interface-to be notified, for example,
when users click a button-you must connect the control objects that emit those
messages to CurrencyConverter. The procedure for connecting actions is similar
to that for outlets, but with one major difference. When you connect an action,
always start the connection line from a control object (such as a button, text field,

43
Chapter 2 Currency Converter Tutorial

or form) that sends an action message; you usually end the connection at an
instance of your custom class. That instance is the target outlet of the control
object.

6 Connect the interface's controls


to the custom class via its
actions.

Control-drag a connection line


from the Convert button to the
ConverterControlier instance in
the nib file window.
The source object of an action connection
When the instance is outlned in must be a control object
black, release the mouse button ..

When a black line encloses an object, it will


be selected as the destination object of the
connection if you release the mouse
button.

The Connections display of the Inspector panel shows the action methods you
have specified for ConverterController.

In the Connections display, make


sure target in the Outlets column
is selected.
-1---- /fyou had defined other actions for
Select convert: in the Actions ConverterController, they would have
column. appeared in this column.

Click the Connect button.


Save the CurrencyConverter nib Interface Builder allows you to set these
file !Document ~ Savel. outlets directly for buttons.

Make sure that you click here to establish


the connection.

You've finished defining the classes of Currency Converter-almost.

44
Defining the Classes of Currency Converter

Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Define the Converter Class: While connecting ConverterController's outlets, you
probably noticed that one outlet remains unconnected: converter. This outlet
identifies the instance of the Converter class in the Currency Converter
application, which doesn't exist yet.

Define the Converter class. This should be pretty easy because Converter, as
you might recall, is a model class within the lVlodel-View-Controller paradigm.
Since instances of this type of class don't communicate directly with the
interface, there is no need for outlets or actions. Here are the steps to be
completed:

1. In the Classes display, make Converter a subclass of NSObject.


2. Instantiate the Converter class.
3. lVlake an outlet connection between ConverterController and Converter.

When you are finished, save CurrencyConverter.nib.

Optional Exercise - - - - - - - - - - - - - - - - - - - - - - -
Text fields and action messages: The NSReturnsign image that you embedded earlier in
the Convert button indicates that users can activate this button by pressing the
Return key. In Currency Converter this key event occurs when the cursor is in
a text field. Text fields are control objects just as buttons are; when the user
presses the Return key and the cursor is in a text field, an action message is sent
to a target object if the action is defined and the proper connection is made.

Connect the second text field (that is, the one with the "Dollars to Convert"
label) to the convert: action method of ConverterController. You won't be
disconnecting the prior action connection because multiple control objects in an
interface can invoke the same action method.

45
Chapter 2 Currency Converter Tutorial

Implementing the Classes of Currency Converter

Interface Builder generates source code files from the (partial) class definitions
you've made. These files are "skeletal," in the sense that they contain little
more than essential Objective-C directives and the class-definition information.
You'll usually need to supplement these files with your own code.

In Interface Builder, generate


header and implementation files.
Click here to bring up the Classes display.
Go to the Classes display ofthe
nib file window. Make sure your class is selected before
choosing Create Files.
Select the ConverterControlier
class.
Choose Create Files from the
Operations pull-down menu.

Interface Builder then displays two attention panels, one after the other:

When a Create Files panel is Click Yes to confirm that you want the
displayed, click Yes. header and implementation files for the
class created. Interface Builder files have
A second Create Files panel is an extension of.h and implementation files
displayed; click Yes again. an extension of.m.
Repeat for the Converter class. Create
INeVseaporVhomes/delawareMonolProjects/Currenc
Save the nib file. yConverterfConverterController,Ihm]? '
> <' .
d , ~; "

Click Yes to confirm that you want the


source code files added to the project in
Project Builder. If, for example, you wanted
to add the files to another project you
would click No.

Now we leave Interface Builder for this application. You'll compl~te the
application using Project Builder.

46
Implementing the Classes of Currency Converter

2 Examine an interface (header) Project Builder imports the


file in Project Builder. Application Kit header files,
which import the Foundation
header files.
Hide Interface Builder and
(#import includes files only if
activate Project Builder. they haven't already been
included.)
Click Headers in the project
browser.
Select ConverterController.h.

Interface definitions begin


with @interface and the class
name. The superclass appears
after the colon.
eintertoce ConverterController : NSObject
(
1d
1d
converter;
dollerF1eld; 1---------------;.;;4__ Instance variables (here the
outlets defined in Interface
1d reteF1eld; Builder) go between the
1d totelF1eld; braces.
}
_ (vo1d)convert:(1d)=xler;
@end
---------------""t-- Method declarations follow the
second brace. The declaration
of the action method you
specified in Interface Builder is
inserted. The definition ends
with@end.

You can add instance variables or method declarations to a header file generated
by Interface Builder. This is commonly done, but it isn't necessary in
ConverterController's case. But we do need to add a method to the Converter
class that the ConverterController object can invoke to get the result of the
computation. Let's start with by declaring the method in Converter.h.

3 Add a method declaration. #import <AppKit/AppKit.h>


#import <Foundation/Foundation.h>
Select Converter.h in the project
browser.
@interface Converter:NSObject
Insert a declaration for
convertAmount:byRate:.
- (float)convertAmount: (float)rate byRate: (float)amti

@end

This declaration states that convertAmount:byRate: takes two arguments of type float,
and returns a float value. When parts of a method name have colons, such as
convertAmount: and byRate:, they are keywords which introduce arguments. (These
are keywords in a sense different from keywords in the C language.) Most
method declarations begin with a dash (-), followed by a space.

Now you need to update both implementation files. First examine Converter.m.

47
Chapter 2 Currency Converter Tutorial

4 Examine an implementation file. mOJrrencyCOnverter - -/Projects 1:3 !:~

Click Classes in the project


browser. I'iii
Converter,m· ~

Select Converter.m.

For this class, implement the method declared in Converter.h. Between


@implementation Converter and @end add the following code:

5 Implement the classes.


- (float)convertAmount: (float)amt byRate: (float) rate
Type the code at right between
@implementation and @end in return (amt * rate);
Converter.m.

The method simply multiplies the two arguments and returns the result. Simple
enough. Next update the "empty" implementation of the convert: method that
Interface Builder generated.

Select ConverterController.m in
- (void) convert: (id) sender
the project browser.
Update the convert: method as float rate, amt, totali
shown by the example.
Import Converter.h. amt = [dollarField floatValueli /* 1 */
rate = [rateField floatValueli
total = [converter convertAmount:amt byRate:rateli /* 2 */
[totalField setFloatValue:totall; /* 3 */
[rateField selectText:selfli /* 4 */

The convert: method does the following:

1. Gets the floating-point values typed into the rate and dollar-amount fields

48
Implementing the Classes of Currency Converter

2. Invokes the convertAmount:byRate: method and gets the returned value.

3. Uses setFloatValue: to write the returned value in the Amount in Other


Currency text field (totaIField).

4. Sends selectText: to the rate field; this puts the cursor in the rate field so the
user begin another calculation.

Be sure to #import "Converter.h" -ConverterController invokes a method defined


in the Converter class, so it needs to be aware of the method's declaration.

Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Each line of the convert: method shown above, excluding the declaration offloats,
is a message. The "word" on the left side of a message expression identifies the
object receiving the message ( called the "receiver"). These objects are
identified by the outlets you defined and connected. After the receiver comes
the name of the method that the sending object (called the "sender") wants to
invoke. Messages often result in values being returned; in the above example,
the local variables rate, amt, and total hold these values.

Before you build the project, add a small bit of code to ConverterController.m that
will make life a little easier for your users. When the application starts up, you
want Currency Converter's window to be selected and the cursor to be in the
Exchange Rate per $1 field. We can do this only after the nib file is unarchived,
which establishes the connection to the text field rateField. To enable set-up
operations like this, awakeFromNib is sent to all objects when unarchiving
concludes. Implement this method to take appropriate action.

6 Implement the awakeFromNib


method. - (void)awakeFrornNib

Type the code shown at right. [rateField selectText:self]; /* 1 */


[[rateField window] makeKeyAndOrderFront:self]; /* 2 */

1. You've seen the selectText: message before, in the convert: implementation; it


selects the text in the text field that receives the message, inserting the cursor
if there is no text.

2. The makeKeyAndOrderFront: message does as it says: It makes the receiving


window the key window and puts it before all other windows on the screen.
This message also nests another message; [rateField window] returns the window
to which the text field belongs, and the makeKeyAndOrderFront: method is then
sent to this returned object.

49
Chapter 2 Currency Converter Tutorial

The Objective-C language is a superset of ANSI Cwith special Messages and Method Implementations
syntax and run-time extensions that make object-oriented
programming possibie. Objective-C syntax is uncomplicated, but • Methods are procedures implemented by a class for its objects
powerful in its simplicity. You can mix standard Cand even C++ (or, in the case of class methods, to provide functionality not
code with Objective-C code. tied to a particular instance). Methods can be public or private;
public methods are declared in the class's header file (see
The following summarizes some of the more basic aspects of the above). Messages are invocations of an object's method that
language. See Object-Oriented Programming and the Objective-C identify the method by name.
Language for complete details. Also, see "Object-Oriented
Programming" in the appendix for explanations of terms that are • Message expressions consist of a variable identifying the
italicized. receiving objectfollowed by the name ofthe method you want
to invoke; enclose the expression in brackets.
Declarations [anObject doSomethingWithArg:this]i
• Dynamically type objects by declaring them as id: receiver method to invoke

id myObjecti As in standard C, terminate statements with a semicolon.


Since the class of dynamically typed objects is resolved at run • Messages often get values returned from the invoked method;
time, you can refer to them in your code without knowing you must have a variable of the proper type to receive this
beforehand what class they belong to. Type outlets in this way value on the left side of an assignment.
as well as objects that are likely to be involved in polymorphism
and dynamic binding. int result = [anObj calcTotalJi
• You can nest message expressions inside other message
• Statically type objects as a pointer to a class:
expressions. This example gets the window of a form object
NSString *mystringi and makes it the receiving object of another message.
You statically type objects to obtain better compile-time type [[form window] makeKeyAndOrderFront:selfJi
checking and to make code easierto understand.
• A method is structured like a function: After the full declaration
• Declarations of instance methods begin with a minus sign H of the method comes the body of the implementing code
and, for class methods, with a plus sign (+): enclosed by braces.

- (NSString *)countryNamei • Use nil to specify a null object; this is analogous to a null
+ (NSDate *)calendarDatei pointer. Note that some OpenStep methods do not accept nil
objects as arguments.
• Put the type of value returned by a method in parentheses
between the minus sign (or plus sign) and the beginning ofthe • A method can usefully refer to two implicit identifiers: self and
method name. (See above example.) Methods returning no super. Both identify the object receiving a message, but they
explicit type are assumed to return id. affect differently how the method implementation is located:
self starts the search in the receiver's class whereas super
• Method argument types are in parentheses and go between starts the search in the receiver's superclass. Thus
the argument's keyword and the argument itself:
[super init] i
- initWithName: (NSString *)name
andType: (int)typei causes the init method of the superclass to be invoked.
Be sure to terminate all declarations with a semicolon. • In methods you can directly access the instance variables of
your class's instances. However, accessor methods are
• By default, the scope of an instance variable is protected,
recommended instead of direct access, except in cases where
making that variable directly accessible only to objects of the
performance is of paramount importance. Chapter 4, "Travel
class that declares it or of a subclass of that class. To make an
Advisor Tutorial," describes accessor methods in greater
instance variable private (accessible only within the declaring
detail.
class), insert the @private directive before the declaration.

50
Building the Currency Converter Project

Building the Currency Converter Proiect


The Build process in Project Builder compiles and links the application guided
by the information stored in the project's makefiles. You must begin builds from
the Project Build panel.

Build the project.

Save source code files and any


changes to the project.
Click the Build button on the main When you click the Build button on the main window, the Project Build panel
window /icon at right). is displayed.
Click the Build button on the
Project Build panel (same icon). QlrrencyConverter - Project Build C X
Target app
Status: QlmmcyC0nv8ffer.app - &l1Id suCCf!l6ded

II
-------------~.;jI II.I+--
. Build, Clean, and Build
Options buttons.

---~-- Build error browser.

Jdynamlc':-obYms8IVculTencyConverter_main.o
Iblnlcc -9 ·Wall-plpe -0 -dynamic .fno-coMmon·llProJectHeader.... ---H+-- Detailed build results.
-lJdel1vscUrc -arch m6sk -ObJC -$ectcreafe _ICON _header
CurrencyConver1erJconheader-segpro1_ICON t r ·sectcreate ...:-,ICON app
applicatlon,t!1f-o JCurrencyConverler.applCurrencyConverter.m68k.
r" Jdynamlc_obyms8IVConverter.o Jdynamlc_obyms8k!Convertercontroller.o
t JdynamIC_obYmS8!rJCurrencyConvertecmaln.o ·framewott AppKlt
I -framewort Foundation
;. Iblnlln JCurrencyconvsrter.appICurrencyConverle':MSSk
1: CurrencyConverter.appICurrenClICOnYsrter
;y

You don't have to maintain


makefiles in Project Builder. It
When you click the Build button on the Project Build panel, the build process
updates Makefile according to the
variables specified through its begins; Project Builder logs the build's progress in the lower split view. When
user interface. You can customize Project Builder finishes-and encounters no errors along the way-it displays
the build process by modifying "Build succeeded."
the Makefile.preamble and
Makefile.postamble files. For Of course, rare is the project that is flawless from the start. Project Builder is
more information on customizing likely to catch some errors when you first build your project. To see the error-
these files, see OPENSTEP
checking features of Project Builder, introduce a mistake into the code.
Developmellt: Tools aJld Techlliqlles

51
Chapter2 Currency Converter Tutorial

WhafHappensWhen You Buil "an


: { ,,<,' '/ . .> " • ,<: .~ ~

By clicking the Build button in Project Builder,


you run the build tool. By default, the build tool is
gnumake, but it can be any build utility that you ~
specify as a project default in Project Builder.
The build tool coordinates the compilation and ~c.c
linking process that results in an executable file.
It also performs other tasks needed to build an
application.
The build tool manages and updates files based
on the dependencies and other information
specified in the project's makefiles. Every
application project has three makefiles:
Makefile, Makefile.preamble, and
Makefile.postamble. Makefile is maintained by
Project Builder-don't edit it directly-but you
can modify the other two to customize your
build.
The build tool invokes the compiler tool cc,
passing it the source code files of the project.
Compilation ofthese files (Objective-C, C++, and
standard C) produces machine-readable object
files forthe architecture (or architectures)
specified for the build. It puts these files in an
architecture-specific subdirectory of
dynamic_obi· libraries

In the linking phase ofthe build, the build tool frameworks


executes the link editor Id (via cc), passing itthe
libraries and frameworks to link against the application wrapper
object files. Frameworks and libraries contain (".app" extension)
precompiled code that can be used by any
application. Linking integrates the code in
libraries, frameworks, and object files to
produce the application executable file.lf there application
are multiple architecture-specific object files, executable
linking also combines these into a single "fat"
Resources
executable.
The build tool also copies nib files, sound,
English.lproj
images, and other resources from the projectto
the appropriate localized or non-localized
locations in the application wrapper.

An application wrapper is a file package with an extension of the Workspace Managerthatthe application wrapper
".app". A file package is a directorythatthe Workspace contains an executable that can be run ("Iaunched") by
Manager presents to users as a simple file; in other words, it double-clicking.
hides the contents of the directory. The ".app" extension tells

52
Building the Currency Converter Project

2 Build the project after correcting


errors, To navigate to an
error in a code file,
click the line
Delete a s'emicolon in the code, describing the error.
Cpnyerterm
creating an error. ... Illegal statement missing ';' aIIer ?~_'_ _ _ _ _ _ _ __

Click the Build button on the Ii) CUrreflcyCOnverter - -/Projects C X


Project Build panel.
::' . .
, ~-
~m
Click the error-notification line COnverter.m - -
that appears in the build error •• Making Cu
browser (upper split view). Exportlnghea
...1or Currency
Done exporttn
Fix the error in the code. Iblnlcc -9-w
-IJderlved_src
Re-build. Jdynamlc_ob ~ISut)prollecls

gnumalte(I): ••
Converter.m:8:
gnumaka:'" (
!!~~~~~~~~~~~~!!!!!!!!!!!!!!!!!
illegal statement: missing ';' after ')'
1111lport. "Converter .h"

(l1mplcmentot1al ~verter
:1

i (tloot)convertAmcult:(tloot)o!llt byRote:(tloot)rote I
~~::!2m'm::I(i!.!!o~t~!Jr]]ilte~?I:!================::1,/
(lend
~ I
Project Builder highlights thslins that
contains the error.

53
Chapter 2 Currency Converter Tutorial

Context-Sensitive Application Help


Project Builder and Interface Builder provide context-sensitive
help on the details of their use. To activate context-sensitive help,

Help-c~cka contro~field,menu
app~cation. A small window appears that briefly describes the
selected object. command,or other areas of the I~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Help key varies by computer architecture. Consult user
documentation for the Help key on your machine.

W'' ' /' __~''':';.'"'_~':'''_':«WW,*W''''~''''_'~'W'''''''_~'''W=''_V_''»''''_.~. >7""»»>_"""*'«<!~'/''''''»'

~'!r~4'~ '!!Zii:, . : . .' .. :


~ ... ~ . . . . . . • .
Digital Librarian NextDeveloper
Examples
Digital Librarian is an application that quickly searches for a word
(or other lexical unit) in an on-line manual (or other target) and
lists the documents that contain the word. You c~ck a ~sted item
and the document is displayed at the point where the word
occurs. The contents of documents are indexed, making
searching very fast.
OpenStep includes NextDeveloper.bshlf, a Digital Librarian
bookshelf for developers in /NextLibrary/Bookshelves. This II CompHerTools.rtf
bookshelf contains most of the targets you are likely to want, and
includes (as the topmosttarget) instructions on creating your own
bookshelf and customizing it to your needs. When you choose
Help from Project Builder or Interface Builder, a Digital Librarian
bookshelf is opened that contains the on-line version of
OPENSTEP Development: Tools and Techniques.
You can find Digital Librarian as Librarian.app in /NextApps.

54
Building the Currency Converter Project

NeXT's Technical Documentation


~~ Most OpenStep programming documentation is located on-line in
~~l Project Builder
NeXTLibrary/Documentation/NextDev. The document files are in
RTF format, so you can open them in Project Builder, Edit, or in
most word processors. NeXT includes the following manuals
Project Builder gives you several ways to get the information you under the /NextDev directory:
need when developing an application.
Reference
Project Find: The Project Find panel allows you to search for • API Reference Documentation (specifications of classes,
definitions of, and references to, classes, methods, functions, protocols, functions, types, and constants). This
constants, and other symbols in your project. Since it is based on documentation is divided among, and located in, the
project indexing, searching is quick and thorough and leads frameworks NeXT provides, except for information that is
directly to the relevant code. See OPENSTEP Development: Tools common to all frameworks (/Reference).
and Techniques for a complete description of Project Find.
• Development Tools Reference covering the compiler, the
Reference Documentation Lookup: If the results of a search using debugger, and other tools (Reference DevTools).
Project Find includes OpenStep symbols, you can easily get • NeXT Assembler Manual
related reference documentation that describes that symbol. See
"Finding Information Within Your Project" on page 94 for Tasks and Concepts
instructions on the use of this feature. • Discovering OPENSTEP: A Developer Tutorial (this manual)

Frameworks: Under Frameworks in the project browser, you can • Object-Oriented Programming and the Objective-C Language
browse the header files related to OpenStep frameworks within • Topics in OPENSTEP Programming (concepts and
Project Builder. The Application Kit and Foundation frameworks programming procedures)
always are included by default for application projects. See
chapter 5, "Where to Go From Here," for a fuller description • OPENSTEP Development: Tools and Techniques (a task-
oriented approach to using the development tools)
• OPENSTEP Conversion Guide (step-by-step instructions for
converting 3.x NEXTSTEP applications to run on OPENSTEP 4.0.
The /NextDev directory also includes release notes. It also
contains documentation on the following products, if they're
installed: Enterprise Objects Framework, Distributed Objects
(DO), Portable Distributed Objects (PDO).
See chapter 5, "Where to Go From Here," for more information on
NeXT's technical publications.

55
Chapter 2 Currency Converter Tutorial

Run Currency Converter

Congratulations. You've just created your first OpenStep application. Find


CurrencyConverter.app in the Workspace, launch it, and try it out. Enter some rates
and dollar amounts and click Convert. Also, select the text in a field and choose
the Services menu; this menu now lists the other applications that can do
something with the selected text.

You can use Project Builder's Of course, the more complex an application is, the more thoroughly you will
graphical debugger or gdb to track need to test it. You might discover errors or shortcomings that necessitate a
bugs down. See "Using the change in overall design, in the interface, in a custom class definition, or in the
Graphical Debugger" on page
104 for an overview of the
implementation of methods and functions.
graphical debugger.
Although it's a simple application, Currency Converter still introduced you to
many of the concepts, tools, and skills you'll need to develop OpenStep
applications. Let's review what you've learned:

• Composing a graphical user interface (GUI) with Interface Builder


• Testing the interface
• Designing an application using the Model-View-Controller paradigm
• Specifying a class's outlets and actions
• Connecting the class instance to the interface via its outlets and actions
• Class implementation basics
• Building an application and error resolution

Optional Exercise - - - - - - - - - - - - - - - - - - - - - -
Nesting Messages:You can nest message expressions; in other words, you can use
the value returned by a message as the receiver of another message or as a
message argument. It is thus possible to rewrite the first three messages of the
ConverterController's convert: method as one statement:

total = [converter convertAmount: [dollarsField floatValue]


byRate: [rateField floatValue]];

It is possible to go even further. Try to incorporate the fourth message ([totaIField


setFloatValue:total]) of the convert: method into the above statement.

56
y: ~.ustralia

'alia
::e ,-------..,----- logistics -------:--1

.~'.~)~ ........ .. ", ....... ..... ........ .. ....................... ................... .


" , "." " " " .6. i rp 0 rts: S~_qtJ.~~.lt}J.~EQ?!iQQ§l:1 ....... _... _... ,_ ..
~.irlines:

Transportation: !axi $~5 e.~~~_~~,.~.~~:~~!Q~!J. §y'£~!,!.~.I{_,


H ote Is: ~.Y.9Q ~.~'.!jqilt~t~J?~..e.,~.~{E!! RQL . ". __....
'ld Itinerary fot' Australia ,-----..,---..,---..,----..,---- Other - - - - - : - - - - - 1
I _. roo '"
25/95 11 :35 SFO Quantas Cu rre n C1:/ : I~~.~.~~_l? g.I.I.~~ .......1 Rate: ,,~_: ?.~.~., J
ydney 6/274: 14 A~/1
ing John Crollen, Sr. VP, Lan!~ uaft es: E_.~,gl.i sh.......... ,.. ,..... ,," ,q" __ '" ........ " . . . . " . . . . . . . . . . . , •••••

of ~.ustralial 4th Floor, 2 P~1


presentation slides
zj Englistl \-videl~' spoken
~---:--:----:--:--~ Conversions -----:--1

Dollars: 17.=;;;;-:;;;1 Local: J.3576.50

Celsius: Farenheit: ro----


Add ·1 Delete I
Chapter 3
Travel Advisor Tutorial

Sections Concepts

Creating the Travel Advisor Varieties of Buttons


Interface
More About Forms
The Design of Travel Advisor
More About Table Views
Defining the Classes of Travel
The Collection Classes
Advisor
Files Owner
Implementing the Country
Class Static and Dynamic Palettes
Implementing the TAControlier NSString: A String for all Countries
Class
The Foundation Framework: Capabilities, Concepts, and Paradigms
Data Mediation
Object Ownership, Retention, and Disposal
Implementing the Table View
Turbo Coding With Project Builder
Adding and Deleting Records
Finding Information Within Your Project
Field Formatting and
Validation Getting in on the Action: Delegation and Notification

Application Management Behind "Click Here": Controls, Cells, and Formatters

Building and Running Travel Using the Graphical Debugger


Advisor

59
Travel Advisor Tutorial

60
In this chapter you create Travel Advisor, an application that is considerably
more complex than the Currency Converter application you built in the first
tutorial. Travel Advisor is a forms-based application used for entering, viewing,
and maintaining records on countries that the user travels to. Users enter a
country name and information associated with that country. When they click
Add, the country appears in the table below the country name. They can select
countries in the table, and the information on that country appears in the forms.
The application also performs temperature and currency conversions.

Country: 1.tI~str<l:li<l:

I·.··· Australia
I France . - - - - - - - Logistics ----:-:---:--,--'-"-:"
1 Germa.ny Airp orts: I.~. Y~~? yl~tern<ltig~<ll
Airlines: 19~<l~t<l~
Tra.nsp ortati on :1!<P<:ig~,II,~~~9\':1~t9\':1~§Y~~~Y
Molels: 1.~.~~n~Y}'ilt9~$76~~S{QLqDt .~.w~,
Notes and Itinerary for Australia , - - - - - , - - - - - Other -,-------,--.....,
Lv 6/26/95 11:36 SFO Quantas 'currencY:I~Q~l Rate:~! per$1
I:,. Arr Sydney 6/274: 14 AM
I.' Meeting John Crotten, Sr. VP, Languages:I~~.~I}i~M'c,.. ,' '. "'U '"

I:' Bank of Australia, 4th Floor, 2 PM ij English widely spok.en


I": Bring presentation slides ..
,....-----:------ Conversions - - - - - - - ,
I:
Ib; Doliars; t~~gg:gg"J; Local: t~~~J)'SO; .' ~ !: C~nv~rt I
I:
Celsius: 13·0~1 Farenhelt:/ ~ ;;.• !k~~v~rtl
lii'j\
I~;:'
11~~

This chapter presents a lot of information on OpenStep programming. Among


other things, you '11 learn how to:

• Use several new objects on Interface Builder's palettes.


• Assign an icon to an application.
• Print the contents of a view.
Collection objects allow you to
• Use collection objects (NSArray and NSDictionary).
store, organize, and access data in
different ways. For more • Use string objects (NSString).
information, see "The Collection • Archive and unarchive object data.
Classes" on page 74. • Format and validate field contents.
String objects represent textual • Manage events through delegation.
strings in various encodings. See • Quickly find information related to your project.
page 82 for more information. • Use Project Builder's graphical debugger.
You can find the TravelAdvisor
project in the AppKit subdirectory
Perhaps most interestingly, you will reuse the Converter class you implemented
of /NextDeveloper!Examples. in the previous tutorial.

61
Chapter 3 Travel Advisor Tutorial

Creating the Travel Advisor Interface

Create the application project. You should be familiar with many of the objects on the Travel Advisor interface
because you've encountered them in the Currency Converter tutorial. The
Start Project Builder.
following illustration points out the objects that are new to you in this tutorial.
Choose New from the Project
menu.
Name the application
"TraveIAdvisor."

2 Open the application's nib file.

Click Interfaces in the project


browser, select
TraveIAdvisor.nib, and double-
click its icon.

3 Customize the application's


window.

Resize the window, using the


example at right as a guide.
In the Attributes display ofthe
Inspector panel, entitle the
window "Travel Advisor."
Turn off the resize bar.
Scrol/view Table view Image view Switch (button) Groups
(containing an (Boxes)
NSText object)

The following pages describe the purpose of each new object found on
Interface Builder's palettes and explain how to set these objects up for Travel
Advisor. Before getting to these new objects, start with the familiar ones:
buttons and text fields.

62
Creating the Travel Advisor Interface

4 Put the text fields, labels, and


buttons on the window. Country: I
Position, re-size, and initialize the
objects as shown.
,.----------------------1--- Be sure this label
contains enough
"padding" for the
longest country
name.
Notes and Itlnerary for
currency:I_~____ Rate:C=- per$1

Languages:1

English widely spoken


"'----------t--- Drag the Switch
Dollars: 1r - - - LOCal:~ 'convert I object from the
Celsius: Le: FaJenheil: r 'Convert I
Views palette and
drop here.

Add

You might think the "English widely spoken" object is a new kind of object.
It's actually a button, a special style of button called a switch.

Set up the switch. -+-_ _ _ Double-click to select text, then type new label.

Click to set the initial state of this toggled button (no checkmark).

Varieties of Buttons

If in Interface Builder you select the "English


widely spoken" switch and bring up the
Attributes inspector, you can see that the
switch is a button set up in a special way.
Titl e: IE~ gli~~",i~~lysp()~~~
Buttons are two-state control objects. They
AIL Title:1
are either off or on, and this state can be set by ~======~
the user or programmatically (setState:). For !con:li'i?~"'it~~_'_m_ .._.. ______ ,~__ ,_

certain types of buttons (especially standard AIL leo n: 1f\J?J::ii g~lig~~~~?""itch

buttons like Currency Converter's Convert


button), when the state is switched, the button
sends an action message to a target object.
Toggle-type buttons-such as switches and
radio buttons- visually reflecttheir state.
Applications can learn ofthis state with the
state message. You can make your own
buttons, associating icons and titles with a
button's off and on states, and positioning title
and icon relative to each other.

63
Chapter 3 Travel Advisor Tutorial

Construct the "Logistics" section of the interface using a form object.

5 Place a form on the interface and


prepare it.

Drag the form object from the


Views palette.

Increase the size ofthe form's


fields by dragging the middle ----~. Drag to lengthen the fields.
resize handle sideways.

Create two more form fields by


Alternate-dragging the bottom- As you Alternate-drag, new form fields
middle resize handle downward. appear underneath the cursor.

Rename the field labels.


Double-click to select label text.
Type the new label text and click outside
the form to set the text.
11Field: I;:'

More About Forms

Forms are labelled fields bound vertically in a Form Attributes


matrix. The fields are the same size and each
In addition to the obvious controls in the
label is to the left of its field. Forms are ideal
Forms inspector, there's the "Cell tags =
objects for applications that display and
positions" attribute. Switching this on
capture multiple rows of data, as do many
assigns tags to each NSFormCeli that
corporate client-server applications.
correspond to the cells' indices. (A tag is a
The editable fields in aform are actually cells number assigned to an object that is used to
that you programmatically identify through idenitfy and access that object. You'll use
zero-based indexing; the first cell is at index tags extensively in the next tutorial.)
oofthe matrix, the second cell at index 1, and The Scrollable option, turned on by default,
so on. NSForm defines the behavior of forms;
enables the userto type long entries in fields,
individual cells are instances of NSFormCe11.
scrolling contents to the left as characters
Access these cells with NSForm's
are entered.
cellAtlndex: method.

64
Creating the Travel Advisor Interface

To make titled sections of the fields, forms, and buttons on the Travel Advisor
interface, group selected objects. By grouping them, you put them in a box.

6 Group the objects on the To select the objects as a group, drag


interface. a selection rectangle around them or
Shift-click each object. (To make a
selection rectangle, start dragging from
Selectthe two Convert buttons an empty spot on the window.)
and the Dollars, Local, Celsius,
Fahrenheit labels and text fields. After you choose the Group in Box
command, the objects are enclosed by
Choose Format ~ Group ~ Group a titled box.
in Box.
Double-click "Tltle" to select it.
Boxes are a useful way to organize and name sections of an interface. In
Choose Format ~ Font ~ Bold to
make the title bold face. Interface Builder you can move, copy, paste, and do other operations with the
box as a unit. For Travel Advisor, you don't need to change the default box
Rename "Tltle" to "Conversions."
attributes.
Repeat for the next two groups:
"Logistics" and "Other."
Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Programmatically, the box is the superview of all of its grouped objects. (A view,
simply put, is any object visible on a window.) A superview encloses its subviews
and is the next in line to respond to user actions if none of its subviews cannot
handle them.

The scroll view on the DataViews palette encloses a text object (an instance of
NSText). This object allows users to enter, edit, and format text with minimal
programmatic involvement on your part.

65
Chapter 3 Travel Advisor Tutorial

7 Put the scroll view on the


window and resize it.

Drag the scroll view from the


DataViews palette and drop it on
the lower-left corner of the
window.
Resize the scroll view..

You don't need to change any of the default attributes of the scroll view (but you
might want to look at the attributes you can set, if you're curious).

Next, add a table view for displaying the list of countries.

More About Table Views

A table view is an objectfor displaying and editing tabular data. Later in this tutorial you will learn some basic techniques for
Often that data consists of a set of related records, with rows for accessing and managing the data in a table view. Here's a quick
individual records and columns for the common fields (attributes) preview of the essential pieces:
ofthose records, Table views are ideal for applications that have
• Data source. The data source is any object in your application
a database component, such as Enterprise Objects Framework
that supplies the NSTableView with data. The elements of data
applications.
(usually records) must be identifiable through zero-based
The table view on Interface Builder's TabulationViews palette is indexing. The data source must implement some or all of the
actually several objects, bound together in a scroll view. Inside methods of the NSTableDataSources informal protocol.
the scroll view is an instance of NSTableView in which data is
• Column identifier. Each column (NSTableColumn) of a table
displayed and edited. At the top of the table view is an
view has an identifier associated with it, which can be either
NSTableHeaderView object, which contains one or more column
an NSString or a number. You use the identifier as a key to
headers (instance of NSTableColumn).
obtain the value of a record field .

• iiif- NSTableColumn • Delegate methods. NSTableView sends several messagesto


its delegate, giving itthe opportunity to control the appearance
Serol/view { and accessibility of individual cells, and to validate or deny
(NSSeroI/View) ~~- NSTableView editing in fields.

66
Creating the Tra"~1 Advisor Interface

8 Place and configure the table


view.

Drag the table view object from


the TabulationViews palette. Click to
select the
Resize the table view. Tabulation
Views
palette.

. . , - - - - - - - Conversions ----:;--"-:;--"---;
Dollars: 1_.~J Local:j 2: :;:" '/ /co'nv~rt'l
Farenheit:C ;\coC:n\;e~n '

Set the title of the first column to


"Countries."
1;,'<1
•.~87=~~~~~+-- second
<"0',";,.,;,.":,"';"');'),;<;".,,,;.;
Double-click column twice (first to select the column,
to insert the cursor), Type "Countries" then click
anywhere outside the column.

Make the table header only one


column. When this cursor appears over the line separating
columns, drag the line so that it's flush with the right
edge.

The other object on the TabulationViews palette is a browser. It is just as suitable


for the Travel Advisor application as a table view. Browsers are ideal for
displaying hierarchically structured information (such as is found in the UNIX
file system) as well as single-level views of data such as the list of countries in
Travel Advisor. A table view can also handle single-column rows of data easily;
it is used instead because it is designed for displaying and editing records from
relational databases, something that Enterprise Objects Framework (EOF)
programmers find very useful.

To configure the table view, you must set attributes of two component objects:
the NSTabieView object and the NSTableColumn object.

67
Chapter 3 Travel Advisor Tutorial

Select the NSTableView by


double-clicking the interior ofthe
table view.
Set the attributes as shown at
right.

""'---+---- Since this is a single-column view and country names are


of limited length, you need only the verticle scroller, in case
there's no more countries that can be shown at once.

F~=,Optlons'-~~~~""-t--- Whether to show the grid is a matter of personal


preference, but turn off resizing and reordering. The user
shouldn't be able to affect the contents of the column
directly.

The Attributes display for NSTableView is the same as that for NSScrollView.

Select the column by double-


clicking once Of this inserts the
cursor, click outside the column,
then click the column once).
Setthe NSTableColumn attributes
as shown at right.

The Travel Advisor window is nearly complete. For a decorative touch, you're
next going to add an image to the interface.

68
Creating the Travel Advisor Interface

9 Add an image to the interface. Click here to get the Data Views palette.

Drag the image view onto the


window, as shown at right.
In Project Builder:
Double-click Images in the

r-T ra-n sp-: -: ;~:- ~.-!:-· _~: LD:_~;'.~; C;-~ -~~~_=__. ._~~J~..ll~11 ~Ic"",=
project browser.
In the Open panel, select the file
Airline.eps from the __ .- __ . _ _=
. __ __
=....
_

/AppKit/TraveIAdvisor
subdirectory of
/NextDeveloper/Examples Hotels:

Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
Sometimes buttons are the preferred objects for holding images-for instance
when you want a different image for either state of a button. But when buttons
are disabled, any image they display is dimmed. So for decorative images, use
image views (NSImageView) instead of buttons.

When you drop a sound or image over a button or image view, it is added to the
nib file. When you add an image or a sound to a nib file, Interface Builder asks
if you also want to add the resource to the project. Nib files are localized and
their resources are only accessible when the nib file has been loaded. Resources
that are associated with a project can be localized and are always accessible.

In the Attributes inspector for the


image view, type the name ofthe Enter the name of the image file, minus the extension.
image and set the NSlmageView The image can be in TIFF of EPS format and must be
attributes. part of the project.
Make the image view (and the You can also add an image by dragging it from the
enclosed image) small enough to Images display of the nib file window and dropping it
fit between the title bar and the over the image view.
Logistics group.
Add a "velocity" line behind the ~~.t;....d"'"4-:----:r---- The border of the image should not be visible.
airplane.

~~~:-:-----ll---- Since the image is larger than the image view, have it
scale proportionally.

Uncheck if you don't want users to affect the image in


anyway.

69
Chapter 3 Travel Advisor Tutorial

Tip: To make the "velocity" line behind the airplane, make a title-less black box
with a vertical offset of zero, and run the top and bottom lines together.
Travel Advisor's main menu has a submenu and a command that do not come
ready-made on the Menus palette. You use the Submenu and the Item cells to
create customized submenus and menu commands, respectively.

10 Add commands to the main menu.

Select the Menus palette.


Drag the Item command and drop
it between Edit and Services.
Change "Item" to "Print Notes ... ".
Drag the Submenu item and drop
it between Info and Edit.
Double-click Submenu to select
the item text; change the name to
"Records".
Add three Items to the Records
submenu (making four
altogether).
Change the command names to
those shown at right.
Double-click the area to the right of the command and type a letter.
Add key equivalents to the right of This letter is the Command key equivalent to teh menu command
the last two commands. (Command-r here because Command-p is often reserved for a print
command).

Three dots after a menu command indicates that the command opens a panel:
"Print Notes ... " means that clicking this command displays the Print panel.

You can now connect many of the objects on the Travel Advisor interface
through outlets and actions defined by the Application Kit. As you might recall,
text fields have a nextKeyView outlet that you connect so that users can tab from
field to field. Forms also have a nextKeyView outlet for tabbing. (The fields within
a form are already interconnected, so you don't need to connect them.)

70
Creating the Travel Advisor Interface

11 Connect Application Kit outlets


for inter-field tabbing and
printing. I .L1
In top-to-bottom sequence,
connect the fields and the form
.- ...1
a»'~
"" ..
-
L,9.!li,~tlcs
through their nextKeyView Airports:!
outlets. ;: Airlines:! -- ------- - ---
When you reach the Languages Transportation: I
field, connect it with the Country Hotels:!

field, making a loop. Itinerary for


i'
'"~.O_
---t::.l .~
...
,~;.n
....J
When a gray line borders the form, I°t is selected.
Release the mouse button and set the nextkeyview
outlet connection.

The Application Kit also has "pre-set" actions that you can connect your
application to. The NSText object in the scroll view can print its contents as can
all objects that inherit from NSView. To take advantage of this capability, "hook
up" the menu command with the NSText action method for printing.

Connect the Print Notes menu


command to the text object in the
scroll view.
Selectthe print: action method in
the Connections display of the
Inspector panel.
Click the Connect button in the
Inspector's Connection display.

Make sure the text


object (the white
rectangle) is
selected and not the
scroll view that
encloses it.

The final step in crafting the Travel Advisor interface has nothing to do with the
main window, but with what users see of your application when they encounter
it in the File Manager: the application's icon. '

71
Chapter 3 Travel Advisor Tutorial

12 Add the application icon.

In Project Builder:
Open the Project Inspector.
Go to the Project Attributes
display of the inspector.
Click in the Application Icon field. )JJ~lrlUilL~~~~ed.4-- Make sure the cursor is in
this field before dragging.
In File Manager
After you drag the image
Locate TravelAdvisor.tiff in the into the well, the icon is
/AppKit/TraveIAdvisor displayed in the well and
subdirectory of the image file is
automatically added to the
/NextDeveloper/Examples.
project.
Drag TravelAdvisor.tiff into the
icon well in the Project Attributes
display.

13 Test the interface. You're finished with the Travel Advisor interface. Test it by choosing Test
Interface from Interface Builder's Document menu.Try the following:

• Press the Tab key repeatedly. Notice how the cursor jumps between the
fields of the form, and how it loops from the Languages field to the Country
field. Press Shift-Tab to make the cursor go in the reverse direction .

• Enter some text in the scroll view, then click the Print Notes menu item. The
print panel is displayed. Print the text object's contents.

• Also in the scroll view, press the Return key repeatedly until a slider appears
in the scroBer.

72
The Design of Travel Advisor

The Design of Travel Advisor


Travel Advisor is much like Currency Converter in its basic design. Like
Currency Converter, it's based on the Model-View-Controller paradigm. A
controller object (TAController) manages a user interface comprised of
Application Kit objects. Also as before, the controller sends a message to the
Converter object to get the result of a computation. In other words, the
Converter object is reused.

Converter

Key

Value

Country Country Country NSDictionary


~------------------------------~

Travel Advisor's view objects, in terms of Model-View-Controller, are all off-the-


palette Application Kit objects, so the following discussion concentrates on
those parts of the design distinctive to Travel Advisor.

Model Obiects
Travel Advisor's design is more interesting and dynamic than Currency
Converter's because it must display a unique set of data depending on the
country the user selects. To make this possible, the data for each country is
stored in a Country object. These objects encapsulate data on a country (in a
sense, they're like records in a relational database). The application can manage
potentially hundreds of these objects, tracking each without recourse to a
"hardwired" connection.

Another model object in the application is the instance of the Converter class.
This instance does not hold any data, but does provide some specialized
behavior.

73
Chapter 3 Travel Advisor Tutorial

Controller
The controller object for the application is TAController. Like all controller
objects, TAController is responsible for mediating the flow of data between the
user interface (the View part of the paradigm) and the model objects that
encapsulate that data: the Country objects. Based on user choices in the
interface, TAController can find and display the requested Country object; it
can also save changes made by users to the appropriate Country'object.

What makes this possible is an NSDictionary object (called a dictionary from


here on). A dictionary is a container that stores objects and permits their retrieval
through key-value associations. The key is some identifier paired with an object
in the dictionary (the object often holds the identifier as one of its instance
variables). To get the object, you send a message to the dictionary using the key
as an argument (objectForKey:).

NSColor *aColor = [aDictionary objectForKey:@IBackgroundColor"]i

A Country object holds the name of a country as an instance variable; this


country name also functions as the dictionary key. When you store a Country
object in the dictionary, you also store the country name (in the form of an
NSString) as the object's key. Later you retrieve the object by sending the
dictionary the message objectForKey: with the country name as argument.

The Collection Classes

Several classes in OpenStep's Foundation Framework create created (see "Abstract Classes and Class Clusters" on page 101).
objects whose purpose is to hold other objects. These col.lection
Collection objects also provide a valuable way to store data.
classes are very useful. Instances of them can store and locate
their contents through a number of mechanisms. When you store (or archive) a collection object in the file system,
its constituent objects are also stored.
• Arrays (NSArray) store and retrieve objects in an ordered
fashion through zero-based indexing.
• Dictionaries (NSDictionary) store and quickly retrieve objects NSObject
using key/value pairs. For example, the key "red" might be
associated with an NSCoior object representing red. I
NSArray NSDictionary NSSet
• Sets (NSSet) are unordered collections of distinct elements.
Counted sets (NSCountedSet) are sets that can contain I I
duplicate (non-distinct) elements; these duplicates are tracked NSMutableArray NSMutableSet
through a counter. Use sets when the speed of membership-
NSMutableDictionary
testing is important.
The mutable versions of these classes allow you to add and NSCountedSet
remove objects programmatically after the collection object is

74
The Design of Travel Advisor

Storing Data Source Information


See "Implementing the TAController also manages the data source for the table view on the interface.
TAController Class" on page 90 It stores the keys of the dictionary in an array object (NSArray), sorted
for a diagram that depicts the data alphabetically. When the table view requests data, the TAController "feeds" it
relationships ofTAController as
data source. See page 66 for more
the objects in the array.
on NSTableView's data source.
Creation of Country Objects
Another important point of design is the manner in which the Country objects
are created. Instead of Interface Builder creating them, the TAController object
creates Country objects in response to users clicking the Add button.

Delegation and Notification


See "Getting in on the Action: An essential aspect of design not evident from the diagram are the roles
Delegation and Notification" on de/egatioll and notification play. The TAController object is the delegate of the
page 97 for more on delegation. application object and thereby receives messages that enable it to manage the
application, which includes tracking the edited status of Country objects,
initiating object archival upon application termination, and setting up the
application at launch time.

75
Chapter 3 Travel Advisor Tutorial

Defining the Classes of Travel Advisor


Travel Advisor has three classes: Country, Converter, and TAController. Only
TAController has outlets and actions. And, rather than defining the Converter
class, you are simply going to add it to the project from the CurrencyConverter
project and reuse it.

Specify the Country and


TAController classes.

In Interface Builder, bring up the


Classes display of the nib file
window.
For each class, select NSObject
as the superclass.
Choose Subclass from the
Operations menu.
Type the class name.

2 Specify TAController's outlets.

Add the outlets shown in the nib


file window at right.
Through this outlet, the TAController
object establishes a connection with
the instance of the Converter class.
You will reuse this class later in this
section.
'iCO
: >country~jeld; t;i!l
> curr~mcypolla:r$Fleld
currencyLocalFleld
currencyNameFielcl
ClJrr~~cYRateFj13Ic1~>
eY!9 lis,hSpokenSwitcp
fahrenheit > ,;

l~ri)9uagesFjeld >

,logistic

76
. Defining the Classes of Travel Advisor

3 Specify TAControlier's actions.

Define the action methods shown


in the nib file window at right. ."__. . '".........,!y;!Lq!tf."L.:...."........~_.".~,.'"__...... _...._""'"
. ..-___..._•.. _.•..§.9.~,B,e.g.g!q.:.- •..•• _"_ ........_......·'m'"''
blankFlelds;
. convettCelsius:
. convertCurrency:
, deleteRecord:
haridleTVClick:

In OpenStep there are many ways to reuse objects through their classes. For
example, subclassing an existing class to obtain slightly different behavior is one
way to reuse the functionality of the superclass. Another way is to integrate an
existing class-like the Converter class-into your project.

4 Reuse the Converter class.

In Interface Builder:
Open CurrencyConverter.nib in n£J~~E[=======r.::;~3E+-- Make sure to select the
superclass before pasting.
the English.lproj subdirectory of
the CurrencyConverter project
directory.
In the Classes display of the nib
file window, select the Converter
class.
Choose Edit ~ Copy.
Select the nib file window for
TraveIAdvisor.nib.
In the Classes display, select the
Headers
superclass (NSObject). When you add a
~ g~~s__~_~ CCmonwV9rert;ea~h~~~~========~~========~-­ class to a project
Choose Edit ~ Paste. Headers t> Project BuNder
OtT,erSOllfces"',;:' adds the
In Project Builder: Interfaces r-
Images ,..
associated header
Launch Project Builder. Other Resource r- file too.

Select Classes in the project It copies both files


browser. from the source
location.
Choose Project ~ Add Files.
In the Add Classes panel,
navigate to the
CurrencyConverter project
directory and select Converter.m.
When asked if you want to
include the header file, click OK.

77
Chapter3 Travel Advisor Tutorial

5 Generate instances of the


TAControlier and Converter
classes.

" NSDictionary
NSFontManager
(j NSlmage

" NSRespondef
NSTableCoJumn

Instantiate
'Edit Cla~s

Read File
, Create Files

You don't need to instantiate the Country class in the nib file because it is not
involved in any outlet or action connections. TAController interacts behind the
scenes with users as they manipulate the application's interface. It therefore
needs access to interface objects and to be made the target of action messages.

6 Connect the TAControlier Outlet Make Connection To


instance to its outlets.
celsius Text field labelled "Celsius"

commentsLa bel Label that reads "Notes and Itinerary for"

commentsField Text object within scroll view

converter Instance of Converter class (cube in Instances display)

countryField Text field labelled "Country"

currencyDoliarsField Text field labelled "Dollars"

currencyLocalField Text field labelled "Local"

currencyNameField Text field labelled "Currency"

currencyRateField Text field labelled "Rate"

englishSpokenSwitch Switch (button) labelled "English widely spoken"

fahrenheit Text field labelled "Fahrenheit"

languagesField Text field labelled "Languages"

logisticsForm Form in group (box) labelled "Logistics"; the form is selected when a
gray line borders it.

tableView The area underneath the "Countries" column

78
Defining the Classes of Travel Advisor

Connect the TAControlier Action Make Connection From


instance to the interface via its
actions. addRecord: "Add" button

blankFields: "Clear" button

convertCelsius: "Convert" button to the right of the "Fahrenheit" field

convertCurrency: "Convert" button to the right of the "Local" field

deleteRecord: "Delete" button

handleTVClick: The table view(the area beneath the "Countries" column header)

nextRecord: The "Next Record" menu command on the Records submenu

prevRecord: The "Prior Record" menu command on the Records submenu

switchChecked: The "English widely spoken" switch

You can assign delegates Before You Go On - - - - - - - - - - - - - - - - - - - - - - -


programmatically or by using
Interface Builder. For more
information, see "Getting in on
You're next going to connect objects through an outlet defined by several
the Action: Delegation and OpenStep classes. The value of this outlet, named delegate, is the id of a custom
Notification" on page 97. object. As the delegate ofNSApp (the NSApplication object), TAControllerwill
receive messages from it as certain events happen.

Checking Connections in Outline Mode

The nib file window of Interface Builder gives you two modes in Outline mode, as the phrase suggests, represents objects in a
which to view the objects in a nib file and to make connections hierarchical list: an outline. The advantages of outline mode are
between those objects. So far you've been working in the icon that it represents all objects and graphically indicates the
mode of the Instances display, which pictorially represents connections between them. You can connect objects through
objects such as windows and custom objects. their outlets and actions in outline mode, as well as disconnect
them by Control-clicking a connection line.

Click here for icon mode.


Click here for outline --+-~I
,...-+-- Click a right-pointing triangle to see connections
out; click a left-pointing triangle to see
mode.
connections into the object.
Connect objects in outline
mode just as you do in icon
...;,..-----+----+-- Move the vertical line left or right to see details
(this is a vertical split view).
mode: Control-drag a

l
~~~~~~~~~~~;;;~;~~rr-- Atypeconnection
connection line between
objects. (electricalisoutlet
identified by name
for outlet, and iconfor
cross-hairs for
action)

79
Chapter 3 Travel Advisor Tutorial

Every application has a global NSApplication object (called NSApp) that


coordinates events specific to the application. Among many other messages,
NSApp sends a message to its delegate notifying it that the application is about
to terminate. Later, you will implement TAController so that, when it receives
this message, it archives (saves) the dictionary containing the Country objects.
8 Connect the delegate outlet.

Drag a connection line from File's


Owner to the TAController object. Notice that the direction of the
connection is from the File's
In the Connections display of the Owner object (which is the
Inspector panel select delegate application object) to the
TAController object.
and click OK.

TravelAdlllsor

9 Generate source code files for


When you generate the header and implementation files for all classes of
the TAController and Country
classes. Currency Converter, you are finished with the Interface Builder portion of
development. Be sure you save the nib file before you switch over to Project
Save TraveIAdvisor.nib. Builder.
Select the class in the Classes
display ofthe nib file window.
Choose Create Files from the
Operations pull-down menu.

File's Owner

Every nib file has one owner, represented by Nib files other than the main nib file-
the File's Owner icon in a nib file window. The auxiliary nib files-contain objects and
owner is an object, external to the nib file, resources that an application may load only
that relays messages between the objects when it needs them (for example, an Info
unarchived from the nib file and the other panel). You must specify the owner of
objects in your application. auxiliary nib files.
You can specify a file's owner in Interface
Builder or programmatically, with You can determine or set the class of the
NSBundle's loadNibNamed:owner:. The current nib file's owner in Interface Builder
File's Owner icon for the main nib file always by selecting the File's Owner icon in the nib
represents NSApp, the global NSApplication file window and then displaying the Custom
constant. The main nib file is automatically Class inspector view. You'll get to practice
created when you create an application this technique when you learn howto create
project; it is loaded in main() when an multi-document applications in the next
application is launched. tutorial.

80
Defining the Classes of Travel Advisor

Just Add a Smock: Compiled and Dynamic Palettes

A palette is a display on the Palettes window that holds one or • Select objects singly or in groups on the interface or in the nib
more reusable objects. You can add these objects to your file window (either icon or outline mode)
application's interface using the drag-and-drop technique. There
are two types of palettes: dynamic and compiled (also called • Alternate-drag these objects and drop them on the blank
"static palettes"). To the user, they seem identical, butthe palette.
differences are many.
Alternate-drag to
Static palettes are built as a project and have code defining their move objects
objects; dynamic palettes include no special code-they're onto palettes, to
unique configurations of objects found on static palettes. move objects
Consequently, static palettes must be compiled, but you can around palettes,
create dynamic palettes on the fly, without writing and compiling and to remove
objects from
code. Objects on static palettes can have inspectors and editors, palettes.
which dynamic-palette objects cannot.

You usually create a static palette as a way to distribute your


objects-and the logic informing these objects' behavior-to
potential users. Many developers of commercial OpenStep
objects make use of static palettes as a distribution media. You can use dynamic palettes to:
Creating static palettes (and their inspectors and editors) is a
• Store collections of often-used View objects configured with
more complex process than creating dynamic palettes, but the
specific sizes and other attributes. For instance, you could
resulting product has more value added to it.
have a "standard" text field of a certain length, font, and
background color stored on a dynamic palete.
Using Dynamic Palettes
• Hold windows and panels that are replicated in your projects
Dynamic palettes are a big convenience. You can save groups of (such as Info panels).
objects, with or withouttheir interconnections, to a dynamic
• Store versions of interfaces.
palette at anytime. You can save dynamic palettes and store them
in the file system, just as you do with the traditional compiled • Keep interconnected objects as a template that you can later
palette. You can remove the palette from the Palette viewer and, use as-is or modify for particular circumstances. For instance,
when you need it again, load it back into Interface Builder. you could store a group oftextfields and their delegate, or a set
of controls and their connections to a controller object
To store objects on a dynamic palette:
• Assist in prototyping and group work. For example, you could
• Choose Tools" Palettes" New to create a blank palette. mail a palette file containing an interface to interested parties.

81
Chapter 3 Travel Advisor Tutorial

Implementing the Country Class

Although it has no outlets, the Country class defines a number of instance


variables that correspond to the fields of Travel Advisor.

Declare instance variables. @interface Country : NSObject <NSCoding> /* 1 */

In Project Builder, click Headers


NSString *namei /* 2 */
in the project browser, then select
Country.h. NSString *airportsi
NSString *airlinesi
Add the declarations shown NSString *transportationi
between the braces at right.
NSString *hotelsi
NSString *languagesi
BOOL englishSpokeni
NSString *currencyNamei
float currencyRatei /* 3 */
NSString *commentsj

When a class adopts a protocol, it


asserts that it implements the 1. Declares that the Country class adopts the NSCoding protocol
methods the protocol declares.
Classes that archive or serialize 2. Explicitly types the instance variable as "a pointer to class NSString" -or a
their data must adopt the NSString object. See below for more about the NSString class.
NSCoding protocol. See Object-
Oriellted Prograllllllillg and the 3. Declare non-object instance variables the same way you declare them in C
Objective-C Language for more on programs. In this case, currencyRate is of type float.
protocols.

NSString: A String for All Countries

NSString objects represent character strings. They're behind character set. Hence they can represent words in Chinese,
almost all text in an application, from labels to spreadsheet entries Japanese, Arabic,and many other languages,
to word-processing documents. NSStrings (or string objects)
supplant that familiar C programming data type, char *, The N'SString and NSMutableString classes provide APlto create
static and dynamic strings, respectively, and to perform string
"But why?" you might be saying. "Why not stick with the tried and
operations such as substring searching, string comparison, and
true?" By representing strings as objects, you confer on them all
concatenation.
the advantages that belong to objects, such as persistency and
distributability. Moreover, thanks to data encapsulation, string
objects can use whatever encoding is needed and can choose the None of this prevents you from using char * strings, and there are
most efficient storage for themselves. occasions where for performance or other reasons you should.
However, the public interfaces of OpenStep classes now use
The most important rationale for string objects is the role they play string objects almost exclusively. A number of NSString methods
in internationalization. String objects contain Unicode characters enable you to convert string objects to char * strings and back
rather than the narrow range of characters afforded by the ASCII again.

82
Implementing the Country Class

Country.h also declares a dozen or more methods. Most of these are accessor
methods. Accessor methods fetch and set the values of instance variables. They
are a critical part of an object's interface.

2 Declare methods. /* initializtion and de-allocation */


- (id)init; /* 1 */
After the instance variables, add
- (void)dealloc;
the declarations listed here.
/* archiving and unarchiving */
- (void)encodeWithCoder: (NSCoder *)coder; /* 2 */
- (id)initWithCoder: (NSCoder *)coder;
/* accessor methods */
- (NSString *)name; /* 3 */
- (void)setName: (NSString *)str;
- (NSString *)airports;
- (void)setAirports: (NSString *)stri
- (NSString *)airlines;
- (void)setAirlines: (NSString *)stri
/* ... other accessor method declarations follow ... */

1. Object initialization and deallocation. In OpenStep you usually create an object by


allocating it (alloc) and then initializing it (init or init... variant):

Country *aCountry = [[Country alloc] init];

When Country's init method is invoked, it initializes its instance variables to


known values and completes other start-up tasks. Similarly, when an object
is deallocated, its dealloc method is invoked, giving it the opportunity to
release objects it's created, free malloc'd memory, and so on. You'll learn more
about init and dealloc shortly.

2. Object archiving and unarchiving. The encodeWithCoder: declaration indicates that


objects of this class are to be archived. Archiving encodes an object's class and
state (typically instance variables) in a file that is often stored within the
application wrapper (that is, the "hidden" application directory).
U narchiving, through initWithCoder:, reads the encoded class and state data and
restores the object to its previous state. There's more on this topic in the
following pages.

3. Accessor methods. The declaration for accessor methods that return val ues is, by
convention, the name of the instance variable preceded by the type of the
returned value in parentheses. Accessor methods that set the value of instance
variables begin with "set" prepended to the name of the instance variable
(initial letter capitalized). The "set" method's argument takes the type of the
instance variable and the method itself returns void.

83
Chapter 3 Travel Advisor Tutorial

he Foundatio~ Framework: Ca'pabhitie~,;Concepts, '~'it:\


~_=>_,~"~ ,>_,~, >:~i~~-.~m,,~l>~~~~,r :~ ':~ \'~>~,i~~% ,~_~:.<~~l:: c::' ::;~ ;f;<~ ffi_~,~.~L',~~~,.~~,~,.~"i~,~~.,*":»l ~"~, ~~~>~W"~_H ~~~~~i; " _~£, _"~~'N,~,~'::>l::li.~,~~,~li~,i~,u_~~'-"~'~~~~~~_'<M. ~~~i

The Foundation Framework consists of a base layer of classes methods for searching, combining, and comparing strings.
that specify fundamental object behavior plus a number of utility NSCharacterSet represents various groupings of characters
classes. It also introduces several paradigms that define which are used by NSString. An NSScanner object scans
functionality not covered by the Objective-C language. Notably, numbers and words from an NSString object. For more
the Foundation Framework: information, see "NSString: A String for All Countries" on page 82.
• Makes software development easier by introducing consistent You use NSBundle objects to load code and localized resources
conventions for things such as object deallocation dynamically (see "Only When Needed: Dynamically Loading
Resources and Code" on page 118). The NSUserDefaults class
• Supports Unicode strings, object persistence, and object
enables you to store and access default values based on locale.
distribution
• Provides a level of operating-system independence, Object Persistence and Distribution
enhancing application portability
NSSerializer makes it possible to representthe data that an object
contains in an architecture-dependent way. NSCoder and its
Root Class subclasses take this process a step further by storing class
NSObject, the principal root class, provides the fundamental information along with the data, thereby enabling archiving and
behavior and interface for objects. It includes methods for distribution. Archiving (NSArchiver) stores encoded objects and
creating, initializing, deallocating, copying, comparing, and other data in files. Distribution denotes the transmission of
querying objects. Almost all OpenStep objects inherit ultimately encoded object data between different processes and threads
from NSObject. (NSPortCoder, NSConnection, NSDistantObject, and others).

Deallocation of Objects Other Functionality

The Foundation Framework introduces a mechanism for ensuring Date and time. The NSDate, NSCalendarDate, and NSlimeZone
that objects are properly deallocated when they're no longer classes generate objects that represent dates and times. They
needed. This mechanism, which depends on general offer methods for calculating temporal differences, for displaying
conformance to a policy of object ownership, automatically dates and times in any desired format, and for adjusting times and
tracks objects that are marked for release within a loop and dates based on location in the world.
deallocates them atthe close ofthe loop. See "Object Ownership,
Application coordination. NSNotification, NSNotificationCenter,
Retention, and Disposal" on page 88 for more information.
and NSNotificationQueue implement a system for broadcasting
notifications of changes within an application. Any object can
Data Storage and Access specify and post a notification, and any other object can register
The Foundation Framework provides object-oriented storage for itself as an observer of that notification. You can use an NSlimer
object to send a message to another object at specific intervals.
• Arrays of raw bytes (NSData) and characters (NSString)
Operating system services. Many Foundation classes help to
• Simple Cdata values (NSValue and NSNumber) insulate your code from the peculiarities of disparate operating
systems.
• Objective-C objects of any class (NSArray, NSDictionary,
NSSet, and NSPPL) • NSFileManager provides a consistent interface for file-system
operations such as creating files and directories, enumerating
NSArray, NSDictionary, and NSSet (and related mutable classes)
directory contents, and moving, copying, and deleting files.
are collection classes that also allow you to organize and access
objects in certain ways (see "The Collection Classes" on page 74). • NSThread and NSProcesslnfo let you create multi-threaded
applications and query the environment in which an
Text and Internationalization application runs.

NSString internally represents text in various encodings, most • NSUserDefaults allows applications to query, update, and
importantly Unicode, making applications inherently capable of manipulate a user's default settings across several domains:
expressing a variety of written languages. NSString also provides globally, per application, and per language.

84
Implementing the Country Class

Before You Go On - - - - - - - - - - - - - - - - - - - - - - -

If you don't want to allow an instance variable's value to be changed by anyone outside of your
class, dOll 't provide a set method for the instance variable. If you do provide a set method, make
sure objects of your own class use it when specifying a value for the instance variables. This has
important implications for subclasses of your class.

Exercise: The previous example shows the declarations for only a few accessor
methods. Every instance variable of the Country class should have an accessor
method that returns a value and one that sets a value. Complete the remaining
declarations.

Now that you've declared the Country class's accessor methods, implement
them.

3 Implement the accessor


methods. - (NSString *)name /* 1 */

Select Country.m in the project return name;


browser.
Write the code that obtains and
sets the values of instance - (void)setName: (NSString *)str /* 2 */
variables.
[name autorelease];
name = [str copy];

/* more accessor method implementations follow */

1. For "get" accessor methods (at least when the instance variables, like Travel
Advisor's, hold immutable objects) simply return the instance variable.

2. For accessor methods that set objec/values, first send autorelease to the current
instance variable, then copy (or retain) the passed-in value to the variable. The
autorelease message causes the previously assigned object to be released at the
In many situations you can send end of the current event loop, keeping current references to the object valid
retain instead of copy to keep an until then.
object around. But for "value"
type objects, such as Country's
If the instance variable has a non-object value (such as an integer or float
instance variables, copy is better.
For the reason why, and for more
value), you don't need to autorelease and copy; just assign the new value.
on autorelease, retain, copy, and
related messages for object Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
disposal and object retention, see
"Object Ownership, Retention, Exercise: The example above shows the implementation of the accessor methods
and Disposal" on page 88. for the name instance variable. Implement the remaining accessor methods.

85
Chapter 3 Travel Advisor Tutorial

4 Write the object-initialization


and object-deallocation code. - (id)init

Implement the init method, as [super initl; /* 1 */


shown here.
Implement the dealloc method, name = @""; /* 2 */
following the suggestions in the airports = @"";
Required Exercise, below. airlines = @"";
transportation = @"";
hotels = @"";
languages = @"";
currencyName = @"";
comments = @"";

return self; /* 3 */

1. Invokes super's (the supe~lass's) init method to have inherited instance


variables initialized. Always do this first in an init method.

Don't substitute nil when empty 2. Initializes an NSString instance variable to an empty string. @"" is a compiler-
objects are expected, and vice supported construction that creates an immutable NSString object from the
versa.The Objective-C keyword
text enclosed by the quotes. You could have just as well typed:
nil represents an "object" with an
id (value) ofzero. An empty
object (such as @'''') is a true name = @"Howdy Doody";
object; it just has no content of its
given type. To learn more about But that wouldn't have been practical as an initial value. You don't need to
Objective-C keywords, see
Object-OrieJlted ProgrammiJlg aJld
initialize instance variables to null values because the run-time system does
the Objective-C Lallguage. it for you; it assigns nil to objects, zeroes to integers and floats, and NULL to
char *'s if they're not explicitly initialized. However, you should initialize
instance variables that take other starting values.

3. By returning self you're returning a true instance of your object; up until this
point, the instance is considered undefined.

Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
Note that release itself doesn't Implement the dealloc method. In this method you release (that is, send release
deallocate objects, but it leads to or autorelease to) objects that you've created, copied, or retained (which don't
their deallocation. For more on
have an impending autorelease). For the Country class, release all objects held as
release and autorelease, see
"Object Ownership, Retention,
instance variables. If you had other retained objects, you would release them,
and Disposal" on page 88. and if you had dynamically allocated data, you would free it. 'Vhen this method
completes, the Country object is deallocated. The dealloc method should send
dealloc to super as the last thing it does, so that the Country object isn't released
by its superclass before it's had the chance to release all objects it owns.

86
Implementing the Country Class

You want the Country objects created by the Travel Advisor application to be
persistellt. That is, you want them to "remember" their state between sessions.
Archiving lets you do this by encoding the state of application objects in a file
along with their class membership. The NSCoding protocol defines two
methods that enable archiving for a class: encodeWithCoder: and initWithCoder:.

5 Implement the methods that


archive and unarchive the object. - (void)encodeWithCoder: (NSCoder *)coder

Implement the [coder encodeObject:name]i /* 1 */


encodeWithCoder: method, as [coder encodeObject:airports]i
shown at right. [coder encodeObject:airlines];
[coder encodeObject:transportation];
[coder encodeObject:hotels] i
[coder encodeObject:languages]i
[coder encodeValueOfObjCType: s" at:&englishSpoken]i /* 2 */
l

[coder encodeObject:currencyName]i
[coder encodeValueOfObjCType: f" at:&currencyRate]i
l

[coder encodeObject:comments]i

1. The encodeObject: method encodes a single object in the archival file.

2. For both object and non-object types, you can use encodeValueOfObjCType:at:.

Implement the initWithCoder:


- (id)initWithCoder: (NSCoder *)coder
method, as shown at right.

name = [[coder decodeObject] COPY]i /* 1 */


airports = [[coder decodeObject] COPY]i
airlines = [[coder decodeObject] COPY]i
transportation = [[coder decodeObject] COPY]i
hotels = [[coder decodeObject] COPY]i
languages = [[coder decodeObject] copy];
[coder decodeValueOfObjCType: s" at:&englishSpoken]i
l

The NSCoder class provides a


currencyName = [[coder decodeObject] COPY]i
number of methods for encoding
[coder decodeValueOfObjCType: f" at:&currencyRate]i
l

and decoding objects and data of


comments = [[coder decodeObject] COPY]i
standard C types. Sec the
specification of the NSCoder
return selfi /* 2 */
class in the Foundation
framework reference
documentation.
1. The order of decoding should be the same as the order of encoding; since
name is encoded first it should be decoded first. Use copy when you assign
value-type objects to instance variables (see "Object Ownership, Retention,
and Disposal" on page 88 ). NSCoder defines decode ... methods that
correspond the encode ... methods, which you should use.

2. As in any init... method, end by returning self-an initialized instance.

87
Chapter 3 Travel Advisor Tutorial

Object ~~nershipl Retenti~~~"andDispos~d!t 1:03:


~:.J.""",~~_._.,_.."",,,,,_..,,,,~w.,,-"::;:-',,,,.. .w,~....,,,,_~,,,,,,~v,,,,,,,,,>~;:..l,M~,,,,;,~~~,,,,,,,~~~~_~_'""",.....,'-'.,.,;.,."'' '....".,.__ ~''''''''_~,~>".''',.,><'''.'"''<"''...,..,.-~~_''''_~'''''~''"'~v"""""''''''...''''''',.....,.,.,->;:»»-*.::", ~A.<»",,,",,,,,_,,,,,,,,,,md,,~J:l:,,\_,>~,,,,,~. . ,>,,,..,.,,, "'_""~~"'''''-''''''''<--_ _ ''»''''>''»»-''''<_

The problem of object ownership and disposal is a natural allocating or copying them. You also own (or share ownership in)
concern in object-oriented programming. When an object is objects that you retain, since retain increments an object's
created and passed around various "consumer' objects in an reference count (see facing page). The flip side of this rule is: If
application, which object is responsible for disposing of it? And you don't own an object, you need not worry about releasing it.
when? If the object is not deallocated, memory leaks. If the object
is deallocated too soon, problems may occur in other objects that OK, but now another question arises. If the owner of an object
assume its existence, and the application may crash. must release the object within its programmatic scope, how can
it give that object to other objects? The short answer is: the
The Foundation Framework introduces a mechanism and a policy autorelease method, which marks the receiver for later release,
that helps to ensure that objects are deallocated when-and only enabling it to live beyond the scope of the owning object so that
when-they are no longer needed. other objects can use it.

Who Owns Which Object? The autorelease method must be understood in a larger context
ofthe autorelease mechanism for object deallocation. Through
The policy is quite simple: You are responsible for disposing of all this programmatic mechanism, you implementthe policy of object
objects that you own. You own objects that you create, either by ownership and disposal.

myObj yourObj Autorelease


How Autorelease pool
Pools Work:
An Example

r--

.J
retention count

A. myObj creates an object: yourObj


Autorelease pool
anObj = [[MyClass alloe] init];
B. myObj returns the object to yourObj, autoreleased:
return [anObj autorelease];
2
The object is "put" in the autorelease pool; that is, the autorelease
pool starts tracking the object.
C. yourObj retains the object:
yourObj o
Autorelease pool
[anObj retain];
If the object wasn't retained itwould be deallocated atthe end ofthe
current event cycle.
1
D. Atthe end ofthe event cycle, the autorelease pool sends release to
all of its objects, thereby decrementing their reference counts. Now
with a reference count of 1, anObj stays in the autorelease pool. yourObj
Autorelease pool
E. yourObj sends autorelease to the object. At the end of the event
cycle, the autorelease pool sends release to its objects; since
anObi's reference count is now zero, it's deallocated.
For a fuller description of object ownership and disposal, see the o
introduction to the Foundation Framework reference documentation.

88
Implementing the Country Class

Reference Counts, Autorelease Pools, and Deallocation Implications of Retained Objects

Each object in the Foundation Framework has an associated When you retain an object you're sharing it with its owner and
reference count. When you allocate or copy an object, its other objects that have retained it. While this might be what you
reference count is set at 1. You send release to an object to want, it can lead to some undesirable consequences. Ifthe owner
decrement its reference count. When the reference count is released, any object you received from it and retained is usually
reaches zero, NSObject invokes the object's dealloc method, and invalid. If you had retained an instance variable of the owning
the object is destroyed. However, successive consumers of the object, and that instance variable is reassigned, your reference
object can delay its destruction by sending it retain, which would also become invalid.
increments the reference count. You retain objects to ensure that
they won't be deallocated until you're done with them.
copy Versus retain
Each application has an autorelease pool. An autorelease pool When deciding whether to retain or copy objects, it helps to
tracks objects marked for eventual release and releases them at categorize them as value objects or entity objects. Value objects
the appropriate time. You put an object in the pool by sending the are objects such as NSNumbers or NSStrings that encapsulate a
object an autorelease message. When your code finishes discrete, limited set of data. Entity objects, such as NSViews and
executing and control returns to the application object (typically NSWindows, tend to be larger objects that manage and
atthe end ofthe event cycle), the application object sends coordinate subordinate objects. Forvalue objects, use copy when
release to the autorelease pool, and the pool releases each you want your own "snapshot" ofthe object; use retain when you
object it contains. If afterwards the reference count of an object intend to share it. Always retain entity objects.
in the pool is zero, the object is deallocated.
In accessor methods that set value-object instance variables, you
Putting the Policy Into Practice usually (but not always) wantto make your own copy ofthe object
and not share it. (Otherwise it might change without your
When an object is used solely within the scope of the method that knowing.) Send autorelease to the old object and then send
creates it, you can deallocate it immediately by sending it release. copy-not retain-to the new one:
Otherwise, send autorelease to all created objects that you no
longer need but will return or pass to other objects. - (void)setTitle: (NSString *)newTitle

You shouldn't release objects that you receive from other objects
[title autorelease]i
(unless you precede the release or autorelease with aretain). You
don't own these objects, and can assume that their owner has title = [newTitle copy];
seen to their eventual deallocation. You can also assume that
(with some exceptions, described below) a received object
remains valid within the method it was received in. That method OpenStep framework classes can, for reasons of efficiency,
can also safely return the object to its invoker. return objects cast as immutable when to the owner (the
framework class) they are mutable. Thus there is no guarantee
You should send release or autorelease to an object only as many that a vended framework object won't change, even if it is of an
times as are allowed by its creation (one) plus the number of immutable type. The precaution you should take is evident: copy
retain messages you have sent it. You should never send free to a objects obtained from framework classes if it's important the
OpenStep object. object shouldn't change from under you.

89
Chapter 3 Travel Advisor Tutorial

Implementing the TAController Class

The TAController class plays a central role in the Travel Advisor application. As
the application's controller object, it transfers data from the model objects
(Country instances) to the fields ofthe interface and, when users enter or modify
data, back to the correct Country object. The TAController must also coordinate
the data displayed in the table view with the current object, and it must do the
right thing when users select an item in the table view or click the Add or Delete
button. All custom code specific to the user interface resides in TAController.

The mechanics of this activity require an array (NSMutableArray) and a


dictionary (NSMutableDictionary) for storing and accessing Country data. The
following diagram illustrates the relationship among interface components,
TAController, and the sources of data.

Key Value
o _~~fJl~~9___ _
1 --_
France
.. ---_ .. _--
2 _G~!~~!)y___
3
4 I

----------~----------
5'---_ _ _---'
Array TAControlier Dictionary

The dictionary contains Country objects (values) that are identified by the
names of countries (keys). The dictionary is the source of data for the fields of
Travel Advisor. The array derives from the dictionary and is sorted. It is the
source of data for the table view.

After describing what other instance variables you must add to TAController,
this section covers the following implementation tasks:

• Getting the data from Country objects to the interface and back
• Getting the table view to work, including updating Country records
• Adding and deleting "records" (Country objects)
• Formatting and validating field values
• "Housekeeping" tasks (application management)

90
Implementing the TAControlier Class

Update TAController.h.
NSMutableDictionary *countryDict;
Import Country.h. NSMutableArray *countryKeys;
Add the instance-variable BOOL recordNeedssaving;
declarations shown at right.

The variables countryDict and countryKevs identify the array and the dictionary
discussed on the previous page. The boolean recordNeedsSaving flags that record
if the user modifies the information in any field.

Add the enum declaration shown


at right between the last#import enum LogisticsFormTags
directive and the @interface LGairports=O,
directive. LGairlines,
LGtransportation,
LGhotels
};

This declaration is not essential, but the enum constants provide a clear and
convenient way to identify the cells in the Logistics form. Methods such as
cellAtlndex: identify the editable cells in a form through zero-based indexing.
This declaration gives each cell in the Logistics form a meaningful designation.

Turbo Coding With Project Builder

When you write code with Project Builder you have a set of by pressing Escape (or Tab, ifthat key is bound in Preferences).
"workbench" tools at your disposal, among them: You can use name completion in the code editor andin all panels
where you are finding information or searching for files to open.
Indentation As an example: you know there's a certain constant to use with
In Preferences you can set the characters at which indentation fonts, but you cannot remember it. In your code, type NSFont.
automatically occurs, the number of spaces per indentation, and Then press the Escape key several times. These symbols appear
other global indentation characteristics. The Edit menu includes in succession (the found portion is underlined):
the Indentation submenu, which allows you to indent lines or NSFontldentityMatrix
blocks of code on a case-by-case basis. NSFontManager
NSFontPanel
Brace and Bracket Checking
Emacs Bindings
Double-click a brace (left or right, it doesn't matter) to locate the
matching brace;the code in-between the braces is highlighted. In You can issue the most common Emacs commands in Project
an identical fashion, double-click a square bracket in a message Builder's code editor. (Emacs is a popular editor for writing code.)
expression to locate the matching bracket. For example, there are the commands page-forward (Control-v),
word-forward (Meta-f), delete-word (Meta-d), kill-forward
Name completion (Control-k), and yank from kill ring (Control-y). You can also
perform an incremental search by pressing Control-s; this
Name completion is a facility that, given a partial name, command displays a small search panel and takes you to the next
completes itfrom all symbols known by the project. You activate it occurrence of whatever you type.

91
Chapter 3 Travel Advisor Tutorial

Data Mediation
TAController acts as the mediator of data exchanged between a source of data
and the display of that data. Data mediation involves taking data from fields,
storing it somewhere, and putting it back into the fields later. TAController has
two methods related to data mediation: populateFields: puts Country instance data
into the fields of Travel Advisor and extractFields: updates a Country object with
the information in the fields.

2 Implement the methods that


- (void)populateFields: (Country *)aRec
transfer data to and from the
application's fields.
[countryField setStringValue: [aRec name]]; /* 1 */
Implement the populateFields:
method as shown at right. [[logisticsForm cellAtIndex:LGairports] setStringValue:
[aRec airports]]; /* 2 */
[[logisticsForm cellAtIndex:LGairlines] setStringValue:
[aRec airlines]];
[[logisticsForm cellAtIndex:LGtransportation] setStringValue:
[aRec transportation]];
[[logisticsForm cellAtIndex:LGhotels] setStringValue:
[aRec hotels]];

[currencyNameField setStringValue: [aRec currencyName]];


[currencyRateField setFloatValue: [aRec currencyRate]];
[languagesField setStringValue: [aRec languages]];
[englishSpokenSwitch setState: [aRec englishSpoken]];

[commentsField setString: [aRec comments]];

[countryField selectText:self]i /* 3 */

1. Causes the Country field to display the value of the name instance variable of
the Country record (aRec) passed into the method. Since [aRec name] is nested,
the object it returns is used as the argument of setStringValue:, which sets the
textual content of the receiver (in this case, an NSTextFieldCell).

2. The cellAtlndex: message is sent to the form and returns the cell identified by
the enum constant LGairports.

3. Selects the text in the Country field or, if there is no text, inserts the cursor.

Although it doesn't do anything with data, the blankFields: method is similar in


structure to populateFields:. The blankFields: method clears whatever appears in
Travel Advisor's fields by inserting empty string objects and zeros.

92
Implementing the TAController Class

Implement the blankFields: - (void)blankFields: (id) sender


method as shown at right.
[countryField setStringValue:@III1];

[[logisticsForm cellAtlndex:LGairports] setStringValue:@III1];


[[logisticsForm cellAtlndex:LGairlines] setStringValue:@III1];
[[logisticsForm cellAtlndex:LGtransportation] setStringValue:@"
[[logisticsForm cellAtlndex:LGhotels] setStringValue:@III1];

[currencyNameField setStringValue:@III1];
[currencyRateField setFloatValue:O.OOO];
[languagesField setStringValue:@"I];
[englishSpokenSwitch setState:NO]; /* 1 */

[currencyDollarsField setFloatValue:O.OO];
[currencyLocalField setFloatValue:O.OO];
[celsius setlntValue:O];

[commentsField setString:@"" ]; /* 2 */
[countryField selectText:self];

1. The setState: message affects the appearance of two-state toggled controls,


such as a switch button. With an argument of YES, the checkmark appears;
with an argument of NO, the checkmark is removed.

2. The setString: message sets the textual contents ofNSText objects (such as the
one enclosed by the scroll view).

Before You Go On - - - - - - - - - - - - - - - - - - - - - - -

Exercise: Implement the extractFields: method. In this method set the values of the
passed-in Country record's instance variables with the contents of the associated
fields.

Tip: Use the stringValue method to get field contents and use Country's accessor
methods to set the values of instance variables.

93
Chapter 3 Travel Advisor Tutorial

The Project Find Panel


The Project Find panel lets you find any symbol defined or referenced in your project. It
also allows you to look up related reference documentation, search for text project-
wide using regular expressions, and replace symbols or strings of text. To use the full
power of Project Find, your project must be indexed; once it is, you have access to all
symbols that the project references, including symbols defined in the frameworks and
libraries linked into the project.

. , . - - - - - - - - Search for: symbol definition,


symbol reference, textual
strings (with or without
regular expressions)

Lists the targets of recent Cun'e1'ltofiMoo~-- Find and replace buttons.


find operations; selecting one
re-displays the results in the
browser.

Search results.

Click a book icon to see thp--++-4I Click an item to display


related reference the relevant code
documentation.

Symbol Definition Search Syntax Other Ways of Finding Information


You can narrow your search for definitions of symbols by Project Builder includes other facilities for finding information:
indicating type in the Find field ofthe Project Find panel along with
• Incremental search:Control-s brings up the incremental-
the symbol name. Once the symbol items are listed in the browser,
search panel for the currently edited file. As you type, the
you can click an item to navigate to the definition in the header
cursor advances to the next sequence of characters in the file
file, or click a book icon to display the relevant reference
that match what you type. Click Next (or press Control-s) to go
documentation.
to the next occurrence; click Prev (or press Control-r) to go to
The following table lists examples of searching for symbol the previous occurrence.
definitions by type:
• Man pages: Choose Edit ~ Find ~ Man Page to bring up the
"Show man page" panel. Enter the name of a tool in the panel
Example Finds Definition For
to getthe man page on thattool.
@NSArray NSArray class
• Librarian via Services: Select a symbol or any word (for
<NSCoding> NSCoding protocol example, "fonts") in Project Builder, then choose Services ~
-objectAtl ndex: Instance method Librarian ~ Search to have Digital Librarian find related
+stringWithFormat: Class method documentation.
[NSBox controlView] Method specific to class • Help: Project Builder and Interface Builder also feature
NSRunAlertPanelO Function context-sensitive help and task-related help. See "Where to
NSApp Type or constant Go For Help" in chapter 2, JJCurre~ncy Converter Tutorial" for
details.

94
Implementing the TAControlier Class

Getting the Table View to Work


Table views are objects that display data as records (rows) with attributes
(columns). The table view in Travel Advisor displays the simplest kind of
record, with each record having only one attribute: a country name.

Table views get the data they display from a data source. A data source is an
object that implements the informal NSTableDataSource protocol to respond to
NSTableView requests for data. Since the NSTableView organizes records by
zero-based indexing, it is essential that the data source organizes the data it
provides to the NSTabie View similarly: in an array.

3 Implement the behavior of the


table view's data source. - (void)awakeFromNib

In TAControlier's awakeFromNib NSArray *tmpArray = [[eountryDiet allKeys] /* 1 */


method, create and sort the array sortedArrayUsingSeleetor:@seleetor(eompare:)]i
of country names. eountryKeys = [[NSMutableArray alloe] initWithArray:tmpArraY]i

In the same method, designate


[tableView setDataSouree:self]i /* 2 */
self as the data source.
[[[tableView tableColumns] objeetAtlndex:O] /* 3 */
setldentifier:@"Countries"]i
[tableView sizeLastColumnToFit]i

1. The [countryDictaIlKeys] message returns an array of keys (country names) from


the unarchived dictionary that contains Country objects as values. The
sortedArrayUsingSelector: message sorts the items in this "raw" array using the
compare: method defined by the class of the objects in the array, in this case
If users are supposed to edit the NSString (this is an example of polymorphism and dynamic binding). The
cells of the table view, you would sorted names go into a temporary NSArray-since that is the type of the
also make TAController the returned value-and this temporary array is used to create a mutable array,
delegate of the table view at this
point (with setDelegate:).The
which is then assigned to countryKeys. A mutable array is necessary because
delegate receives messages users may add or delete countries from the application.
relating to the editing and
validation of cell contents. For 2. The [tableView setDataSource:self] message identifies the TAController object as
details, see the specification on the table view's data source. The table view will commence sending
NSTableView in the Application NSTableDataSource messages to TAController. (You can effect the same
Kit reference documentation. thing by setting the NSTableView's dataSource outlet in Interface Builder.)

3. Every column has an identifier to associate it with a column, which is itself


usually associated with an attribute. By default, the identifier is a number: the
first column is 0, the second column is 1, and so on. This compound message
makes the identifier a string object and thus binds it semantically to the
attribute. The tableColumns method returns all NSTableColumns in a array; in
this case, only the single column of this table view. The setldentifier: message
sets the value.

95
Chapter 3 Travel Advisor Tutorial

To fulfill its role as data source, TAController must implement two methods of
the NSTableDataSource informal protocol.

Implementtwo methods of the


- (int)numberOfRowslnTableview: (NSTableView *)theTableView
NSTableDataSource informal
protocol: /* 1 */
return [countryKeys count];
- numberOfRowslnTableView:
- tableView:
objectValueForTableColumn:
row:
- (id)tableView: (NSTableView *)theTableView /* 2 * /
objectValueForTableColumn: (NSTableColumn *)theColumn
row: (int)rowlndex

if ([[theColumn identifier] isEqualToString:@"Countries"])


return [countryKeys objectAtlndex:rowlndex];
else
return nil;

1. Returns the number of country names in the countryKeys array.

If you had an application with multiple table views, each would invoke this
NSTableView delegation method (as well as the others). By evaluating the
theTableView argument, you could distinguish which table view was involved.

2. This method first evaluates the column identifier to determine if it's the right
column (it should always return "Countries"). If it is, the method returns the
country name from the countryKeys array that is associated with rowlndex. This
name is then displayed at rowlndex of the column. (Remember, the array and
the cells of the column are synchronized in terms of their indexing.)

The NSTableDataSource informal protocol has a another method,


tableView:setObjectValue:forTableColumn:row~ that you won't implement in this
tutorial. This method allows the data source to extract data entered by users into
table-view cells; since Travel Advisor's table view is read-only, there is no need
to implement it.

96
Implementing the TAControlier Class

in on the Action: Delegation and Notification

A lot goes on in a running application: events are being Notification


interpreted, files are being read, views are being drawn. Because
A notification is a message that is broadcast to all objects in an
your custom objects might be interested in any ofthese activities,
application that are interested in the event the notification
OpenStep offer two mechanisms through which your objects can
represents. As does the informational delegation message, the
participate or be kept informed of events going on in the
notification informs these observers that this event took place. It
application: delegation and notification.
can also pass along relevant data about the event.

Delegation notification center

posts
Many OpenStep framework objects hold a delegate as an
OddBallDidActSillyNotification
instance variable. A delegate is a object that receives messages
from the framework object when specific events occur.
Delegation messages are of several types, depending on the oddBall
expected role of the delegate:

• Some messages are purely informational, occurring after an


event has happened. They allow a delegate to coordinate its
actions with the other object.
A c
• Some messages are sent before an action will occur, allowing
the delegate to veto or permitthe action.

• Other delegation messages assign a specific task to a


delegate, like filling a browser with cells.
Here's the way the notification process works:
• Objects who are interested in an event that happens
elsewhere in the application - say the addition of a record to
a database - register themselves with a notification center
(an instance of NSNotificationCenter) as observers of that
event. Delegates of an objectthat posts notifications are
automatically registered as observers of those notifications.
• The object that adds the objectto the database (or some such
event) posts a notification (an instance of NSNotification) to a
notification center. The notification contains a tag identifying
the notification, the id ofthe associated object, and, optionally,
a dictionary of supplemental data.
• The notification center then sends a message to each
observer, invoking the method specified by each, and passing
in the notification.
Notifications hold some advantages over delegation messages as
a means of inter-application communication. They allow an object
You can set your custom objectto be the delegate of a framework to synchronize its behavior and state with multiple objects in an
object programmatically or in Interface Builder. Your custom application, and without having to know the identity of those
classes can also define their own delegate variables and objects. With notification queues, it is also possible to post
delegation protocols for client objects. notifications asynchronously and coalesce similar notifications.

97
Chapter 3 Travel Advisor Tutorial

The final thing you need to do to get the table view working is to respond to
mouse clicks in it. As you recall, you defined in Interface Builder the
handleTVClick: action for this purpose. This method must do a number of things:

• Save the current Country object or create a new one.


• If there's a new record, re-sort the array providing data to the table view.
• Display the selected record.

4 Update records.
- (void)handleTVClick: (id) sender
Implement the method that
responds to user selections in the Country *aRec, *newRec, *newerRecj
table view. int index;

/* does current obj need to be saved? */


if (recordNeedsSaving) { /* 1 */
/* is current object already in dictionary? */
if (aRec=[eountryDict objectForKey: [countryField stringValue]])

/* remove if it's been changed */


if (aRec) {
NSString *country = [aRee name];
[eountryDict removeObjectForKey:countrY]i
[countryKeys removeObject:countrY]i

/* Create Country obj, add to dict, add name to keys array */


newRec = [[Country alloe] init];
[self extractFields:newRee]i
[countryDictsetObject:newReeforKey: [countryFieldstringValue]]j
[countryKeys addObject: [countryField stringValue]]j

/* sort array here */


[countryKeys sortUsingSelector:@selector(compare:)];
[tableView tile];

index = [sender selectedRow]j


if (index >= 0 && index < [eountryKeys count]) /* 2 */
newerRec = [countryDict objectForKey:
[countryKeys objeetAtIndex:index]];
[self populateFields:newerRec];
[commentsLabel setStringValue: [NSString stringWithFormat:
@"Notes and Itinerary for %@", [countryField stringValue]]]j
recordNeedsSaving=NOj

This method has two major sections, each introduced by an if statement.

98
Implementing the TAControlier Class

1. When any Country-object data is added or altered, Travel Advisor sets the
recordNeedsSaving flag to YES (you'll learn how to do this on later on). If
recordNeedsSaving is YES, first delete any existing Country record for that
country from the dictionary and also remove the country name from the table
view's array. (Upon removal, the objects are automatically released by the
array.) Then create a new Country instance and initialize it with the values
currently on the screen; add the instance to the dictionary, add the country
name to the table view's array, sort the array, and reset the recordNeedsSaving
flag. At the end, invoke thetile method, which (among other things) causes
the table view to request data from its data source.

2. The selectedRow message queries the table view for the row index of the cell
that was clicked. If this index is within expected bounds, use it to get the
country name from the array, and then use the country name as the key to get
the associated Country instance. Write the instance-variable values of this
instance to the fields of the application, update the "Notes and Itinerary for"
label.

Optional Exercise - - - - - - - - - - - - - - - - - - - - - - -
Application developers often like to have key alternatives to mouse actions such
as clicking a table view. One way of acquiring a key alternative is to add a menu
cell in Interface Builder, specify a key as an attribute of the cell, define an action
method that will be invoked, and then implement that method.

The methods nextRecord: and prevRecord: should be invoked when users chose
N..ext Record and Prev Record or type the key equivalents Command-n and
Command-r. In TAController.m, implement these methods, keeping the following
hints in mind:

1. Get the index of the selected row (selectedRow).

2. Increment or decrement this index, according to which key is pressed (or


which command is clicked).

3. If the start or end of the table view is encountered, "wrap" the selection.
(Hint: Use the index of the last object in the countryKeys array.)

4. Using the index, select the new row, but don't extend the selection.

5. Simulate a mouse click on the new row by sending handleTVClick: to self.

99
Chapter 3 Travel Advisor Tutorial

Adding and Deleting Records


When users click Add Record to enter a Country "record," the addRecord:
method is invoked. You want this method to do a few things besides adding a
Country object to the application's dictionary:

• Ensure that a country name has been entered.


• Make the table view reflect the new record.
• If the record already exists, update it (but only if it's been modified).

5 Implement the method that adds - (void)addRecord: (id) sender


a Country object to the
NSDictionary "database."
Country *aCountry;
NSString *countryName = [countryField stringValue];
/* 1 */
if (countryName && (! [countryName isEqualToString:@"I]))
aCountry = [countryDict objectForKey:countryName]i
if (aCountry && recordNeedsSaving) {
/* remove old Country object from dictionary */
[countryDict removeObjectForKey:countryName];
[countryKeys removeObject:countryName];
aCountry = nil;

if (!aCountry) /* record is new or has been removed */


aCountry = [[Country alloc] init];
else /* record already exists and hasn't changed */
return;
/* 2 */
[self extractFields:aCountry];
[countryDict setObject:aCountry forKey: [aCountry name]];
[countryKeys addObject: [aCountry name]];
·[countryKeys sortUsingSelector:@selector(compare:)];
/* 3 */
recordNeedsSaving=NO;.
[commentsLabel setStringValue: [NSString stringWithFormat:
@"Notes and Itinerary for %@", [countryField stringValue]
[countryField selectText:self];
/* 4 */
[tableView tile];
[tableView selectRow: [countryKeys indexOfObject:
[aCountry name]].byExtendingSelection:NO];

1. This section of code verifies that a country name has been entered and sees
if there is a Country object in the dictionary. If there's no object for the key,
objectForKey: returns nil. If the object exists and it's flagged as modified, the
code removes it from the dictionary and removes the country name from the

100
Implementing the TAControlier Class

countryKeys array. Note that removing an object from a dictionary or array also
releases it, so the code sets aCountry to nil. It then tests aCountry and, if it's nil,
creates a new object; otherwise it just returns, because an object already exists
for this country and it hasn't been modified.

2. After updating the new Country object with the information on the
application's fields (extractFields:), this code adds the Country object to the
dictionary and the country name to the countryKeys array.

3. This section of code performs some things that have to be done, such as
resetting the recordNeedsSaving flag and updating the label over the scroll view
to reflect the just-added country.

4. The tile message forces the table view to update its contents. The
selectRow:byExtendingSelection: message highlights the new record in the table
VIew.

Before You Go On - - - - - - - - - - - - - - - - - - - - -
Exercise: Implement the deleteRecord: method. Although similar in structure to
addRecord: this method is much simpler, because you don't need to worry about
whether a Country record has been modified. Once you've deleted the record,
remember to update the table view and clear the fields of the application.

Abstract Classes and Class Clusters

Many of the classes in the Foundation appropriate private subclass. What's


Framework fall into functional constellations appropriate depends on the creation
of public and private classes called class method, which indicates the type of storage
clusters. Class clusters simplify the required. The class membership of the
programming interface and permit more returned object is hidden, but its interface, as
efficient storage of data. declared by the abstract superclass, is
An abstract class (such as NSArray) defines public.
the public interface for objects vended from
class clusters. Abstract classes declare Many OpenStep class clusters have two or
methods common to private, concrete more abstract classes. Usually one class
subclasses, but do not declare any instance provides the interface for obtaining
variables to hold data-that's done by the immutable objects (for example, NSArray)
private classes. When you send an object- and another class, which inherits from the
creation message to an abstract class, it mutable class, vends mutable versions of the
instantiates and returns an instance of the same type of object (NSMutableArray).

101
Chapter 3 Travel Advisor Tutorial

Field Formatting and Validation


Travel Advisor has several numeric fields. Some display temperatures while
others display currency amounts. In this stage, you'll enable these fields to
format their contents by using a formatting API defined in the Application Kit.

6 Format and validate numeric - (void)awakeFromNib


fields.
[[currencyRateField cell] setEntryType:NSFloatType];
Set the entry type and floating-
[[currencyRateField cell] setFloatingPointFormat:YES
pointformat of some TAControlier
fields in the awakeFromNib left:2 right:l];
method. [[currencyDollarsField cell] setEntryType:NSFloatType];
[[currencyDollarsField cell] setFloatingPointFormat:YES left:5
right:2];
[[currencyLocaIField cell] setEntryType:NSFloatType];
[[currencyLocaIField cell] setFloatingPointFormat:YES left:5
right:2];
[[celsius cell] setEntryType:NSFloatType];
[[celsius cell] setFloatingPointFormat:YES left:2 right:l];

The NSCell class provides methods for specifying how cell values are
formatted. In this instance, setEntryType: sets the type of value as a float and
setFloatingPointFormat:left:right: specifies the number of digits on each side of the
decimal point.

The NSControrclass gives you an API for validating the contents of cells.
Validation verifies that the values of cells fall within certain limits or meet
certain criteria. In Travel Advisor, we want to make sure that the user does not
enter a negative value in the Rate field.

The request for validation is a message-control:isValidObject:-that a control


sends to its delegate. The control, in this case, is the Rate field.

In awakeFromNib, make
[currencyRateField setDelegate:self];
TAControlier a delegate of the
field to be validated.
Implementthe - (BOOL) control: (NSControl *)control isValidObject: (id)obj
control:isValidObject: method to
validate the value of the field. if (control == currencyRateField) { /* 1 */
if ([obj floatValue] < 0.0) {
NSRunAlertPanel(@"Travel Advisor", /* 2 */
@"Rate cannot be negative.", nil, nil, nil);
return NO;

return YES;

102
Implementing the TAControlier Class

Behind "Click Here": Controls, Cells, and Formatters 1


"1
Controls and cells lie behind the appearance and behavior of A control does not have to have a cell associated with it, but most
most user-interface objects in OpenStep, including buttons, text user-interface objects available on Interface Builder's standard
fields, sliders, and browsers. Although they are quite different palettes are cell-control combinations. Even a simple button-
types of objects-controls inherit from NSControl while cells from Interface Builder or programmatically created-is a control
inherit from NSCell-they interact closely. (an NSButton instance) associated with an NSButtonCel1. The
cells in a control such as a matrix must be the same size, butthey
Controls enable users to signal their intentions to an application, can be of different classes. More complex controls, such as table
and thus to contra/what is happening. By interpreting mouse and
views and browsers, can incorporate various types of cells.
keyboard events and asking another objectto respond to them,
controls implement the target/action paradigm described in
"Paths for Object Communication: Outlets, Targets, and Actions" Cells and Formatters
on page 38. Controls themselves can hold targets and actions as
instance variables, but usually they getthis data from the affected When one thinks of the contents of cells, it's natural to consider
cell (which must inherit from NSActionCell). onlytext(NSString) and images (NSlmage). The contentseemsto
Cells are rectangular areas "embedded" within a control. A be whatever is displayed. However, cells can hold other kinds of
control can hold multiple cells as a way to partition its surface into objects, such as dates (NSDate), numbers (NSNumber), and
active areas. Cells can draw their own contents either as text or custom objects (say, phone-number objects).
image (and sometimes as both), and they can respond individually
to user actions. Since cells are typically more frugal consumers of Formatter objects handle the textual representation ofthe objects
memory than controls, they help applications be more efficient. associated with cells and translate what is typed into a cell into
the underlying object. Using NSCel\'s setFormatter:, you must
programmatically associate a formatter with a cell to getthis
behavior.
cell
(NSButtonCel/)
control
(NSMatrix)

- - - - - - control
cell (NSTextField)
(NSTextFieldCel/}

Controls act as managers of their cells, telling them when and


where to draw, and notifying them when a user event (mouse
clicks, keystrokes) occurs in their areas. This division of labor,
given the relative "weight" of cells and controls, provides a great
boost to application performance. ... andvice
versa

tracking messages
The Foundation Framework provides the NSDateFormatter class
to generate date formatters and will release other specialized
formatter classes in the future. You can make a custom subclass
Cell of NSFormatter to derive your own formatters.

drawing messages

103
Chapter 3 Travel Advisor Tutorial

1. Because you might have more than one field's value to validate, this example
first determines which field is sending the message. It then checks the field's
value (passed in as the second object); if it is negative, it displays an attention
panel and returns NO, blocking the entry of the value. Otherwise, it returns
YES and the field accepts the value.
For more information on
2. The NSRunAlertPanelO function allows you to display a modal attention panel
NSRunAlertPanel(), see the
"Functions" section of the from any point in your code. The above example calls this function simply to
Application Kit (framework) inform the user why the value cannot be accepted.
reference documentation.

Rate cannotbenegatlve.

Although Travel Advisor doesn't evaluate it, the NSRunAlertPanelO function


returns a constant indicating which button the user clicks on the panel. The
logic of your code could therefore branch according to user input. In
addition, the function allows you to insert variable information (using printfO-
style conversion specifiers) into the body of the message.

104
Implementing the TAControlier Class

Application Management
By now you've finished the major coding tasks for Travel Advisor. All that
remains to implement are a half dozen or so methods. Some of these methods
perform tasks that every application should do. Others provide bits of
functionality that Travel Advisor requires. In this section you'll:

• Archive and unarchive the TAController object.


• Implement TAController's init and dealloc methods.
• Save data when the application terminates.
• Mark the current record when users make a change.
• Obtain and display converted currency values.

The data that users enter into Travel Advisor should be saved in the file system,
or archived. The best time to initiate archiving in Travel Advisor is when the
application is about to terminate. Earlier you made TAController the delegate
of the application object (NSApp). Now respond to the delegate message
applicationShouldTerminate:, which is sent just before the application terminates.

7 Archive the application's objects


- (BOOL)applicationShouldTerminate: (id) sender
when it terminates.
/* 1 */

Implement the delegate method NSString *storePath = [[[NSBundle mainBundle] bundlePath]


applicationShouldTerminate:, as stringByAppendingPathComponent:@"TravelData"]j
shown at right. /* save current record if it is new or changed */
[self addRecord:self]j
/* 2 */
if (countryDict && [countryDict count])
[NSArchiver archiveRootObject:countryDict toFile:storePath]

return YESj

1. Constructs a pathname to the application wrapper in which to store the


archive file "TravelData." The application wrapper-the "hidden" directory
holding the application executable and required resources-is a bundle, so
NSBundle methods are used to get the bundle path.

2. If the countryDict dictionary holds Country objects, TAController archives it


with the NSArchiver class method archiveRootObject:toFile:. Since the dictionary
is designated as the root object for archiving, all objects that the dictionary
references (that is, the Country objects it contains) will be archived too.

105
Chapter 3 Travel Advisor Tutorial

8 Implement TAControlier's
- (id)init
methods for initializing and
{
deallocating itself.
/* 1 */
Implementthe init method, as NSString *storePath = [[NSBundle mainBundle]
shown at right. pathForResouree:@ITravelData" ofType:nilli
[super init];
Implement the dealloc method to
/* 2 */
release object instance variables.
eountryDiet = [NSUnarehiver unarehiveObjeetWithFile:storePath];
/* 3 */
if (!eountryDiet)
eountryDiet [[NSMutableDietionaryalloe] init];
eountryKeys [[NSMutableArrayalloe] initWithCapaeity:l0];
else
eountryDiet [eountryDiet retain];
reeordNeedsSaving=NOj

return self;

1. Using NSBundle methods, locates the archive file "TraveIData" in the applicati l

wrapper and returns the path to it.

2. The unarchiveObjectWithFile: message unarchives (that is, restores) the object whos(
attributes are encoded in the specified file. The object that is unarchived and
returned is the NSDictionary of Country objects (countryDict).

3. If no NSDictionary is unarchived, the countryDict instance variable remains nil. If tl


is the case, TAController creates an empty countryDict dictionary and an empty
countryKeys array. Otherwise, it retains the instance variable.

Flattening the Object Network: Coding and Archiving

Coding, as implemented by NSCoder, takes the encodeWithCoder: and initWithCoder:


a network of objects such as exist in an methods.
application and serializes that data,
capturing the state, structure, relationships, Thus sending archiveRootObject:toFile: to
and class memberships ofthe objects. As a NSArchiver leads to the invocation of
subclass of NSCoder, NSArchiver extends encodeWithCoder: in the root object and in
this behavior by storing the serialized data all referenced objects that implement it.
in a file. Similarly, sending unarchiveObjectWithFile:
to NSUnarchiver results in initWithCoder:
When you archive a root object, you archive being invoked in those objects referenced in
not only that object but all other objects the the archive file. These objects reconstitute
root object references, all objects those themselves from the instance data in the file.
second-level objects reference, and so on. In this way, the network of objects, three-
To be archived, however, objects must dimensional in abstraction, is converted to
conform to the NSCoding protocol. This atwo-dimensional stream of data and
conformance requires that they implement back again.

106
Implementing the TAControlier Class

When users modify data in fields of Travel Advisor, you want to mark the current
record as modified so later you'll know to save it. The Application Kit broadcasts
a notification whenever text in the application is altered. To receive this
notification, add TAController to the list of the notification's observers.

9 Write the code that marks


[[NSNotificationCenter defaultCenter] addObserver:self
records as modified.
selector:@selector(textDidChange:)
In the awakeFromNib method, name:NSControlTextDidChangeNotification object:nil];
make TAControlier an observer of
NSControlTextDidChangeNotification.
Next, implement the method that you indicated would respond to the
Implement textDidChange: to set notification; this method sets a flag, thereby marking the record as changed.
the recordNeedsSaving flag.

- (void)textDidChange: (NSNotification *)notification

You post notifications and add if ([notification object] currencyDollarsField II


objects as observers of [notification object] celsius) return;
notifications with methods
defined in the recordNeedsSaving=YES;
NSNotificationCenter class.
NSNotification defines methods
for creating notification objects
and for accessing their attributes. Two of the editable fields of Travel Advisor hold temporary values used in
See the specifications of these conversions and so are not saved. This statement checks if these fields are the
classes in the Foundation ones originating the notification and, if they are, returns without setting the flag.
Framework reference (The object message obtains the object associated with the notification.)
documentation.
The final method to implement is almost identical to the one you wrote for
Currency Converter to display the results of a currency conversion when the
user clicks the Convert button for currency conversion.

10 Implement the method that


- (void)convertCurrency: (id) sender
responds to a request for a
currency conversion.
[currencyLocalField setFloatValue:
[converter convertAmount: [currencyDollarsField floatValue]
byRate: [currencyRateField floatValue]]];

Optional Exercise - - - - - - - - - - - - - - - - - - - - - - - -
Convert Celsius to Fahrenheit: Implement the convertCelsius: method. You've already
specified and connected the necessary outlets (celsius, fahrenheit) and action
(convertCelsius:), so all that remains is the method implementation. The formula
you'll need is:

107
Chapter 3 Travel Advisor Tutorial

Using the Graphical Debugger'


fI------------------~--

LIiI TravelAdvisor - INetJseaportJJlrojects/Pubs Staff,lJostremlDev Env ~


Project Builder's graphical debugger provides an
easy-to-use, intuitive user interface to gdb, the
GNU debugger.

~iiiiiijji:=:..J-_-- Launch program; run debugger; inspect


task (breakpoints, stack, etc.)
~mf"ll:Ei'l'Srnr-t---- Run the application being debugged;
interrupt and continue the application.

Print value, print referenced value, print


object description (select variable first).

ibrary,/Fn3me\IOr~;s/~'oul'lIti(Kit. fr'amework/Vlers:Lom~'io!!II!MIoOM-~--~ Step over, step into statement.


ile~~IitIOIi _ _ _ _ _ _ _ _ _ _~_ Launch options (see below)

(sel f=Elx14848c ,

1I~~g~§§§§§§§EEEEEEEEEE§~~~r You can also issue gdb commands on the


t command line.
- (BOOl) applicationShouldTerminate : (id) sender
{
NSString *storePath = [[ [NSBundle mainBundle] bundlePath]
stringByAppendingPathComponent:@"TravelData"];

Launch options affect both


launched and debugged programs.
The inspector displays allow you to
set target executables,
environment variables, and source
directories.

108
Building and Running Travel Advisor

Building and Running Travel Advisor


When Travel Advisor is built, start it up by double-clicking the icon in the File
lVianager. Then put the application through the following tests:

• Enter a few records. lViake up geographical information if you have to-


you're not trusting your future travels to this application. Not yet, anyway.

• Click the items in the table view and notice how the selected records are
displayed. Press Command-n and Command-r and observe what happens.

• Enter values in the conversion fields to see how they're automatically


formatted. Try to enter a negative value in the Rate field.

• Quit the application and then start it up again. Notice how the application
displays the same records that you entered.

Tips for Eliminating Deallocation Bugs

Problems in object deallocation are not unusual in OpenStep • The AnalyzeAllocation tool compiles statistics on memory
applications under development. You might release an objecttoo allocation during the time a program executes.
many times or you might not release an object as many times as
is needed to deallocate it. Both situations lead to nasty problems See the man pages on these tools for more information.
-in the first case, to run-time errors when your code references
non-existent objects; the second case leads to memory leaks. Avoiding Deallocation Errors
If you're releasing an object too many times, you'll get run-time Here's a fewthings to remember that might help you avoid
error messages telling you that a message was sent to a freed deallocation bugs in OpenStep code:
object. To find which methods were releasing the object, in gdb or
the graphical debugger:: • Make sure there's an alloc, copy, mutableCopy, or retain
message sent to an object for each release or autorelease
Send enableFreedObjectCheck: to NSAutoreleasePool with sentto it.
an argument of YES.
• When you release a collection object (such as an NSArray),
2 Set a breakpoint on _NSAutoreleaseFreedObject. you release all objects stored in it as well. When you request
an object stored in a collection object, it's returned to you
3 Run the program under the debugger.
autoreleased.
4 When the program hits the breakpoint, do a backtrace and
check the stack to find the method releasing the object. • Superviews retain subviews as you add them to the view
hierarchy and release subviews as you release them. If you
Other tools help you track down problems related to release and want to keep swapped-out views, you should retain them.
autorelease: Similarly, when you replace a window's or box's content view,
the old view is released and the new view is retained.
• The oh command records allocation and deallocation events
related to a specific process. It produces a report showing the • To avoid retain cycles, objects should not retain their
stack frame for an object each time the object is allocated, delegates. Objects also should not retain their outlets, since
copied, retained or released. they do not own them.

109
Chapter 3 Travel Advisor Tutorial

110
(Juliets
calenciaJ February 1996
da~,,'Label
ite rn t',,1 atri;:.~

lielions

To Do on Tue February' 13 1996


--> 1

If~~;~~i~~Ef~~~c~i~!~~l~~~~l_-..-.t1 ~-----~-
_~l.""""o""","O""o",oOO"OO"
4
Chapter 4
To Do Tutorial

Sections Concepts

The design of To Do Starting up - what happens in NSApplicationMainO

Setting up the project Dynamically loading resources and code

Creating the model class Dates and times in OpenStep

Subclass example: adding The structure of multi-document applications


data and behavior
The application quartet: NSResponder, NSApplication, NSWindow, and NSView
The basics of a multi-
document application Coordinate systems in OpenStep

Managing documents through Events and the event cycle


delegation
A short guide to drawing and compositing
Managing the data and
Making a custom NSView
coordinating its display
Run loops and timers
Subclass example: overriding
behavior

Creating and managing an


inspector

Subclass example: overriding


and adding behavior

Setting up timers for


notification messages

Build, run, and extend the


application

113
To Do Tutorial

114
Niany kinds of applications-word processors and spreadsheets, to name a
couple-are designed with the notion of a document in mind. A document is a
body of information, usually contained by a window, that is self-contained and
repeatable. Users can create, modify, store, and access a document as a discrete
unit. Multi-document applications (as these programs are called) can generate
an almost unlimited number of documents.

The To Do application presented in this chapter is a multi-document


application. It is a fairly simple personal information manager (PIN!). Each To
Do document captures the daily "must-do" items for a particular purpose. For
instance, one could have a To Do list for work and another one for home. To Do
allows users to:

• Enter appointments or actions that they must complete on particular days.


• Specify the times those items are due.
• Receive notifications at a specified interval before the due time.
• Associate notes with to-do items. '
• Nlark items as complete or deferred.

I II Work Schedule.td - -/Misc ILl Ix


January 1996
Sun Mon Tue Wed Thu Fri Ss.!

~L:J~...:J~~
.-J~ ~~_~=-L~:J 9 Inspector [X

~~16 17~~~ Dat.e: Tue, Jan 30 1996

~-==-J 23 iJ~ 27 Item: board meeting

~iJ~~ Notification ... 1

To Do on Tue Januarj 30 1886


C>tal.U.:,,o,uuc
Time: 11O~i l!l._ (" AI\·1 r Ptvl
X II
10:00AM board meeting ----:-- When to Notify - -
..JDO not notify
12:30At'y'111Iunc~.with SenatorF. .d15 minutes before
..,; •• > II phonetvl°m ..J 1 hour before
____ ----------------
~I~I ..J 1 day before
..JOther ~ hours
_ _-,II
----~II~--------------­
_ _-,II

As with Travel Advisor, you're going to cover a lot of OpenStep territory by


completing this tutorial. It explores two major areas:

• Multi-document architecture: The design of applications that can create


multiple documents, save and restore those documents, and do the right
thing on certain events, such as application termination.

115
Chapter 4 To Do Tutorial

• Strategies for subclassing: Reuse of existing classes by adding behavior and


data, by overriding existing behavior, or by doing both things.

You will also learn about other aspects of OpenStep programming:

• Opening and saving files


• Loading nib files (and other bundles) programmatically
• Creating and managing inspectors
• Programmatic creation and manipulation of user-interface objects
• Time and date manipulation
• Declaring informal protocols
• Using timers

And you'll be introduced to these important OpenStep concepts:

• Event handling
• The core program framework
• Drawing and image composition

When you complete this tutorial, you should be ready to tackle OpenStep
programming on your own.

Starting Up - What Happens in NSApplicationMainO

Every OpenStep application project created through Project 2 Gets the Class object for NSApplication and invokes its
Builder has the same mainO function (in the file sharedApplication class method, creating an instance of
ApplicationName_main.m). When users double-click an NSApplication, which is stored in the global variable, NSApp.
application or document icon in the File Viewer, mainO (the entry Creating the NSApplication object connects the application to
point) is called first; mainO, in turn, calls NSApplicationMain()- the window system and the Display PostScript server, and
and that's all it does. initializes its PostScript environment.

3 Loads the main nib file, specifying NSApp as the owner.


The NSApplicationMainO function does what's necessary to get Loading unarchives and re-creates application objects and
an OpenStep application up and running-responding to events, restores the connections between objects.
coordinating the activity of its objects, and so on. The function
starts the network of objects in the application sending messages 4 Runs the application by starting the main event loop. Each time
to each other. Specifically, NSApplicationMainO: through the loop, the application object gets the next available
event from the Window Server and dispatches it to the most
appropriate object in the application. The loop continues until
Gets the application's attributes, which are stored in the
the application object receives a stop: or terminate: message,
application wrapper as a property list. From this property list,
after which the application is released and the program exits.
it gets the names ofthe main nib file and the principal class (for
applications, this is NSApplication or a custom subclass of You can add your own code tomainO to customize application
NSApplication). start-up or termination behavior.

116
The Design of To Do

The Design of To Do
The To Do application vaults past Travel Advisor in terms of complexity.
Instead of Travel Advisor's one nib file, To Do has three nib files. Instead of
three custom classes, To Do has seven. This diagram shows the
interrelationships among instances of some of those classes and the nib files that
they load:

ToDoDoc.nib
~=-_.a ToDolnspector.nib

---II~~ Creates

ToDoltem
(Model)

q ToDo.nib

Some of the objects in this diagram are familiar, fitting as they do into the
Model-View-Controller paradigm. The ToOoltem class provides the model
objects for the application; instances of this class encapsulate the data associated
with the items appearing in documents. They also offer functions for computing
subsets of that data. And then there's the controller object... actually, there is
The ToDoInspector instance in
the above diagram is an offshoot more than one controller object.
of the application controller,
ToDoController. By breaking To Do's Multi-Document Design
down a problem domain into
distinct areas of responsibility, Two types of controller objects are at the heart of multi-document application
and assigning cenain types of design. They claim different areas of responsibility within an application.
objects to each area, you increase ToOoController is the application cOlltroller, it manages events that affect the
the modularity and reusability of application as a whole. Each ToOoDoc object is a document cOlltroller, and
the object, and make
manages a single document, including all the ToOoItems that belong to the
maintenance and trouble-
shooting easier. See "Object-
document. Naturally, it's essential that the application controller be able to
Oriented Programming" in the communicate with its (potentially) numerous document controllers, and they
appendix for more on this. with it.

117
Chapter 4 To Do Tutorial

Netaded: Dynamically Loading Res~ui'ces an~

As any developer knows well, performance is a key consideration nib files. The File's Owner object is not really appearing twice; it's
in program design. One factor is the timing of resource allocation. created in one file and referenced in the other.
If an application loads all code and resources that it might use
The File's Owner object dynamically loads a nib file and makes
when it starts up, it will probably be a sluggish, bloated
itself the owner of that file by sending loadNibNamed:owner: to
application-and one that takes awhile to launch.
NSBundle, specifying self as the second argument.
You can strategically store the resources of an application
(including user-interface objects) in several nib files. You can also NSBundle and Bundles
put code that might be used among one or more loadable bundles.
When the application needs a resource or piece of code, it loads A bundle is a location in the file system that stores code and the
the nib file or loadable bundle that contains it. This technique of resources that go with that code, including images, sounds, and
deferred allocation benefits an application greatly. By conserving archived objects. A bundle is also identified with an instance of
memory, it improves program efficiency.ltalso speeds upthetime NSBundle, which makes the contents of the bundle available to
it takes to launch the application. other objects that request it.
The generic notion of bundles is pervasive throughout OpenStep.
Auxiliary Nib Files Applications are bundles, as are frameworks and palettes. Every
application has at least one bundle-its main bundle-which is
When more sophisticated applications start up, they load only a
the ".app" directory (or application wrappen where its
minimum of resources in the main nib file-the main menu and
executable file is located. This file is loaded into memory when
perhaps a window. They display other windows (and load other
the application is launched.
nib files) only when users request it or when conditions warrant it.
Nib files other than an application's main nib file are sometimes Loadable Bundles
called auxiliary nib files. There are two general types of auxiliary
nib files: special-use and document. You can organize an application into any number of other bundles
in addition to the main bundle and the bundles of linked-in
Special-use nib files contain objects (and other resources) frameworks. Although these loadable bundles usually reside
that might be used in the normal operation ofthe application. inside the application wrapper, they can be anywhere in the file
Examples of special-use nib files are those containing inspector system. Project Builder allows you to build Loadable Bundle
panels and Info panels. projects.
Document nib files contain objects that represent some Loadable bundles differ from nib files in thatthey don't require you
repeatable entity, such as a word-processor document. A to use Interface Builderto build them. Instead of containing
document nib file is a template for documents: it contains the UI mostly archived objects, they usually contain mostly code.
objects and other resources needed to make a document. Loadable bundles are especially useful for incorporating extra
behavior into an application upon demand. An economic-forecast
The Owner of an Auxiliary Nib File application, for example, might load a bundle containing the code
defining an economic model, but only when users request that
The object that loads a nib file is usually the object that owns
model. You could also use loadable bundlesto integrate "plug and
it. A nib file's owner must be external to the file. Objects play" components into an existing framework.
unarchived from the nib file communicate with other objects in
the application only through the owner. Loadable bundles usually have an extension of ".bundle"
(although that's a convention, not a requirement). Each loadable
In Interface Builder, the File's Owner icon represents this external bundle must have a principal class that mediates between bundle
object. The File's Owner is typically the application controller for
objects and external objects.
special-use nib files, and the document controller for document

118
The Design of To Do

As multi-document applications typically do, To Do includes the Document


menu found on Interface Builder's Menus palette. When users choose New
from the Document menu, the application controller allocates and initializes an
instance of the ToDoDoc class. When the ToDoDoc instance initializes itself, it
loads the ToDoDoc.nib file. When the user has finished entering items into the
document, and chooses Save from the Document menu, a Save panel appears
and the user saves the document in the file system under an assigned name.
Later, the user can open the document using the Open menu command, which
causes the Open panel to be displayed.

The rationale behind, and The controller objects of To Do respond to a variety of delegation messages sent
process of, constructing when certain events occur-primarily from windows and NSApp-in order to
multi-document applications is save and store object state. One example of such an event is when the user
discussed in "The Structure of
Multi-Document Applications"
closes a document window; another is when data is entered into a document.
on page 141. Often when these events happen, one controller sends a message to the other
controller to keep it informed.

How To Do Stores and Accesses its Data


The data elements of a To Do document (ToDoDoc) are ToDoltems. When a
user enters an item in a document's list, the ToDoDoc creates a ToDoItem and
inserts that object in a mutable array (NSMutableArray); the ToDoItem
occupies the same position in the array as the item in the matrix's text field. This
positional correspondence of objects in the array and items in the matrix is an
essential part of the design. For instance, when users delete the first entry in the
document's list, the document removes the corresponding ToDoItem (at index
0) from the array.

iii Work Sehedule.W - -/Mise II:l :::


... February 1996 ...
Sun Mon Tue Wed Thu Frl

ToDo/tern (*itern one*)

ToDo/tern (*itern one*)

ToDo/tern (*itern one*)

119
Chapter 4 To Do Tutorial

The array of ToDoItems is associated with a particular day. Thus the data for a
document consists of a (mutable) dictionary with arrays of To Do Items for values
and dates for keys.

NSMutableDictionary
15 Nov 1996 16 Nov 1996 17 Nov 1996
Key
ToDoltem ToDoltem ToDoltem
ToDoltem ToDoltem
ToDoltem ToDoltem ToDoltem
Value ToDoltem

ToDoltem

When users select a day in the calendar, the application computes the date,
which it then uses as the key to locate an array of ToDoltems in the dictionary.

To Do's Custom Views


The discussion so far has touched on model objects and controller objects, but
has said nothing about the second member of the Model-View-Controller triad:
view objects. Unlike Travel Advisor, which uses only "off-the-shelf' views, To
Do's interface features objects from three custom Application Kit subclasses.

II Work Schedule.td - -IMisc ,I:l Ix


January 1996
CalendarMatrix: a
subclass of NSMatrix,
this is a dynamic
calendar that notifies
the delegate about
selected dates.

SelectionNotifMatrix:
__-+___ modifies NSMatrix to
notify observers when a
ToDoCell: a subclass of selection in a text field
NSButtonCell, this is a tri- occurs.
state control with --+--~':""':""':""--:-:i:::====':':""::============; I
different images for each
state. It also displays
times items are due.

You'll learn much more about these custom subclasses in the pages that follow.

120
Setting up the To Do Project

Setling Up the 10 Do IProject


Create the application project. Create the To Do project almost in the same way you created the Travel Advisor
application. There are a few differences; each, of course, has a different name
Start Project Builder.
and icon. But the most important difference is that To Do has its own document
Choose New from the Project type.
menu.
Name the application "ToDo."

Project Name:, !_o~q ______ u

Langua.ge: L~_~g~~~__ _
; __~;;';P;:;;;P1;:; ;_iC;';'~';';'_ig;';'; _~_;__';'";;;:;';;;;";;;;';;";;;;"
APpile ali 0 n Clas s: N;;'__S- r.;;,

2 Add the application icon.


Main Nib Fjle:l!~.~_~____ _
The ToDo icon (ToDo.tiffl is
located in the ToDo project in the You can have different icons and other project
attributes for Open Step for Mach and
AppKit subdirectory of OpenStep for Windows.
/NextDeveloper/Examples.

Instead of dragging the image-file icon into


the well, you can add the image file to the
project and then just type the name of the
image here.

3 Specify the To Do document type.

Click Add.
Double-click the new cell under
the Extension column.
Type the extension of To Do
documents: "td". Document types specify the kinds of files the
application can open and "understand." They
Drag into the image well the file lIUt"'t:N~It:t' for ~1acti appear in the workspace with the assigned icon
calendar.tiff from the ToDo and may be opened by double-clicking.
project in Ii Help File
/NextDeveloper/Examples/
As with the application icon, when you drag
AppKit. the document icon into the image well, the image
file is added to the project.

I . . td I caiendar.tiIT I Before Project Builder accepts the document icon,


you must assign the extension (if the type is new)
I! and select the row.

.
".,. ..
'"
-"'
IV ·,,1 If the document type is well-known (for example,
".c"), just drag a document of that type into the well.

121
Chapter 4 To Do Tutorial

Creating the Model Class (ToDoltem)

The ToDoItem class provides the model objects for the To Do application. Its
instance variables hold the data that defines tasks that should be done or
appointments that have to be kept. Its methods allow access to this data. In
addition, it provides functions that perform helpful calculations with that data.
ToDoItem thus encapsulates both data andbehavior that goes beyond accessing
data.

Since ToDoItem is a model class, it has no user-interface duties and so the


expedient course is to create the class without using Interface Builder. We first
add the class to the project; Project Builder helps out by generating template
source-code files.

Add the ToDoltem class to the


project.

Select Classes in the project


browser.
Choose New In Project from the
File menu.
Other Supporting
Class Header Help
In the New File In ToDo panel, Source File
type "ToDoltem" in the Name
field. Name: II~7~~I,!,~I!l",-~,-~,",,-w"--"'~'0m7"-""w:,-"--;:"'
'11 Create header ca~~'~t 'II OK
Make sure the "Create header"
switch is checked.
Click the OK button. As you've done before with Travel Advisor, start by declaring instance variables
and methods in the header file, ToDoltem.h.

2 Declare ToDoltem's instance @interface ToDoItern:NSObject<NSCoding, NSCopying>


variables and methods.
NSCalendarDate *daYi
Type the instance variables as
NSString *iternNarnei
shown at right.
NSString *notes;
Indicate the protocols adopted by NSTirner *iternTirneri
this class. long secsUntilDuei
long secsUntilNotif;
ToDoIternStatus itemStatusi

You are adopting the NSCopying protocol in addition to the NSCoding protocol
because you are going to implement a method that makes "snapshot" copies of
ToDoItem instances.

122
Creating the Model Class (ToDoltem)

Instance Variable What it Holds

day The day (a date resolved to 12:00 AM) ofthe to-do item

itemName The name of the to-do item (the content's of a document text field)

notes The contents of the inspector's Notes display; this could be any
information related to the to-do item, such as an agenda to discuss at
a meeting.

itemlimer A timer for notification messages.

secsUntilDue The seconds after day at which the item comes due

secsUntilNotif The seconds after day at which a notification is sent (before


secsUntilDue)

itemStatus Either "incomplete," "complete," or "deferToNextDay"

3 Define enum constants for use in


typedef enum _ToDoltemStatus
ToDoltem's methods.
incomplete=O,
complete,
Definethese constants before the
@interface directive. deferToNextDay
ToDoltemStatusj

enum {
minlnSecs = 60,
hrlnSecs = (minlnSecs * 60),
daylnSecs = (hrlnSecs * 24),
weeklnSecs = (daylnSecs * 7)
}i

The first set of constants are values for the itemStatus instance variable. The
second set of constants are for convenience and clarity in the methods that deal
with temporal values.

4 Declare two time-conversion BOOL ConvertSecondsToTime(long sees, int *hour, int *minute);
functions.
long ConvertTimeToSeconds(int hr, int min, BOOL flag);

These functions provide computational services to clients of this class,


converting time in seconds to hours and minutes (as required by the user
interface), and back again to seconds (as stored by ToDoItem).

123
Chapter 4 To Do Tutorial

Type the method declarations - (id)initWithName: (NSString *)name andDate: (NSCalendarDate *)date;
shown at right.
- (void)dealloc;
- (BOOL)isEqual: (id)anObjecti
- (id)copyWithZone: (NSZone *)zone;
- (id)initWithCoder: (NSCoder *)coder;
- (void)encodeWithCoder: (NSCoder *)coderi
- (void)setDay: (NSCalendarDate *)newDaYi
- (NSCalendarDate *)daYi
- (void)setltemName: (NSString *)newNamei
- (NSString *)itemNamei
- (void)setNotes: (NSString *)notesi
- (NSString *)notesi
- (void)setltemTimer: (NSTimer *)aTimeri
- (NSTimer *)itemTimeri
- (void)setSecsUntilDue:(long)secs;
- (long)secsUntilDue;
- (void)setSecsuntilNotif: (long)secsi
- (long)secsUntilNotif;
- (void)setltemStatus: (ToDoltemStatus)newStatus;
- (ToDoltemStatus)itemStatus;

Most of these declarations are for accessor methods. You know what to do.

5 Implement accessor methods. - (void)setltemTimer: (NSTimer *)aTimer

Open ToDoltem.m in the code


if (itemTimer)
editor.
[itemTimer invalidate];
Implement methods that get and [itemTimer autorelease]i
setthe values of ToDoltem's
instance variables. itemTimer = [aTimer retain];
Implement the setltemTimer:
method as shown at right.

The setltemTimer: method is slightly different from the other "set" accessor
methods. It sends invalidate to itemTimer to disable the timer before it autoreleases
Timers (instances of NSTImer)
are always associated with a run it.
loop (an instance of
NSRunLoop). See "Tick Tock In this application, you want client objects to be able to copy your ToDoltem
Brrrring: Run Loops and Timer" objects and test them for equality. You must define this behavior yourself.
on page 190 for more on timers
and run loops.

124
Creating the Model Class (ToDoltem)

6 Implement the isEqual: method. - (BOOL)isEqual: (id)anObj

if ([anObj isKindOfClass:[ToDoltem class]] &&


[itemName isEqualToString: [anObj itemName]] &&
[day isEqualToDate: [anObj day]])
return YES;
else
return NO;

The default implementation of isEqual: (in NSObject) is based on pointer


equality. However, ToDoItem has a different basis for equality; any two
ToDoItem objects for the same calendar day and having the same item name are
considered equal. The implementation of isEqual: overrides NSObject to make
these tests. (Note that it invokes NSString's and NSDate's own isEqual...
methods for the specific tests.)

Before You Go On - - - - - - - - - - - - - - - - - - - - - -

There is a specific as well as a general need for the isEqual: override. In the To
Do application, an NSArray contains a day's ToDoItems. To access them, other
objects in the application invoke several NSArray methods that, in turn, invoke
the isEqual: method of each object in the array.

7 Implement the copyWithZone: - (id)copyWithZone: (NSZone *)zone


method.
ToDoltem *newobj =
[[ToDoltem alloc] initWithName:itemName
andDate:day];
[newobj setNotes:notes);
[newobj setltemStatus:itemStatus]i
[newobj setSecsUntilDue:secsUntilDue]i
[newobj setSecsUntilNotif:secsUntilNotif]i

return newobji

Copies of objects can be either


deep or shallow. In deep copies
This implementation of the copyWithZone: protocol method makes a copy of a
(like ToDoltem's) every copied
instance variable is an . ToDoItem instance that is an independent replicate of the original (self). It does
independent replicate, including this by allocating a new ToDoItem object and initializing it with the essential
the values referenced by instance variables held by self. Copying is often implemented for value objects-
pointers. In shallow copies, objects that represent attributes such as numbers, dates, and to-do items.
pointers are copied but the
referenced objects are the same. The next method you'll implement-description-assists you and other
F or more on this topic, see the
developers in debugging the To Do application with gdb. When you enter the po
description of the NSCopying
protocol in the Foundation (print object) command in gdb with a ToDoItem as the argument, this description
reference documentation. me~hod is invoked and essential debugging information is printed.

125
Chapter 4 To Do Tutorial

8 Implement the description - (NSString *)description


method.
NSString *desc = [NSString stringWithFormat:@"%@\n\tName: %@\n\tDate:
%@\n\tNotes: %@\n\tCompleted: %@\n\tSecs until Due: %d\n\tSecs Until
Notif: %d",
[super description],
[self itemName],
[self day],
[self notes] I

·(([self itemStatus]==complete)?@IYes :@INo"), l

[self secsUntilDue],
[self secsUntilNotif]]i

return (desc) i

9 ImplementToDoltem's initializing Here are some things to remember as you implement initWithName:andDate: and
and deallocation methods. dealloc:

• If the first argument of initWithName:andDate: (the item name) is not a valid


string, return nil. If the second argument (the date) is nil, set the related
instance variable to some reasonable value (such as today's date). Also, be sure
to invoke super's init method.

• The instance variables to initialize are day, itemName, notes, and itemStatus (to
"incomplete").

• In dealloc, release those object instance variables initialized in


initWithName:andDate: plus any object instance variables that were initialized
later. Also invalidate any timer before you release it.

10 Implement ToDoltem's archiving When you implement encodeWithCoder: and initWithCoder:, keep the following in
and unarchiving methods. mind:

• Encode and decode instance variables in the same order.

• Copy the object instance variables after you decode them.

• You don't need to archive the itemTimer instance variable since timers are re-
set when a document is opened.

The final step in creating the ToDoltem class is to implement the functions that
furnish "value-added" behavior.

126
Creating the Model Class (ToDoltem)

11 Implement ToDoltem's time- long ConvertTimeToSeeonds(int hr, int min, BaaL flag) /* 1 */
conversion functions. {
if (flag) { /* PM * /
if (hr >= 1 && hr < 12)
hr += 12;
else
if (hr 12)
hr = 0;

return «hr * hrlnSees) + (min * minlnSees));

BaaL ConvertSeeondsToTime(long sees, int *hour, int *minute) /* 2 */


{
int hr=O;
BaaL pm=NO;

if (sees) {
hr = sees / hrlnSees;
if (hr > 12) {
*hour = (hr -= 12);
pm YES;
else
pm NO;
if (hr == 0)
hr = 12;
*hour = hr;

*minute «sees%hrlnSees) / minlnSees);

return pm;

1. This expression, as well as others in these two methods, uses the enum constants
for time-values-as seconds that you defined earlier.

2. The ConvertSecondsToTimeO function uses indirection as a means for returning


multiple values and directly returns a Boolean to indicate AM or PM.

127
Chapter 4 To Do Tutorial

Subclass Example: Adding Data and Behavior (CalendarMatrix)

The calendar on To Do's interface is an instance of a custom subclass of


NSMatrix. CalendarMatrix dynamically updates itself as users select new
months, notifies a delegate when users select a day, and reflects the current day
(today) and the current selection by setting button attributes.

Creating a subclass of a class that is farther down the inheritance tree poses more
of a challenge for a developer than a simple sublcass of NSObject. A class such
as NSMatrix is more specialized than NSObject and carries with it more
baggage: It inherits from NSResponder, NSView, and NSControl, all fairly
complex Application Kit classes. And since CalendarMatrix inherits from
NSView, it appears on the user interface; it is an example of a view object in the
Model-View-Controller paradigm, and as such it is highly reusable.

Why NSMatrix?
When you select a specialized superclass as the basis for your subclass, it is
important to consider what your requirements are and to understand what the
superclass has to offer. To Do's dynamic calendar should:

• Arrange numbers (days) sequentially in rows and columns.


• Respond to and communicate selections of days.
• Understand dates.
• Enable navigation between months.

If you then started to peruse the reference documentation on Application Kit


classes, and looked at the section on NSMatrix, you'd read this:

NSMatrix is a class usedfor creating groups ofNSCells that work together in various
ways. It includes methods for arranging NSCells in rows and columns.... An
NSMatrix adds to NSControl's targetlaction paradigm by allowing a separate target
and action for each of its NSCells in addition to its own target and action.

128
Subclass Example: Adding Data and Behavior (CalendarMatrix)

So NSMatrix has an inherent capability for the first of the requirements listed
above, and part of the second (responding to selections). Our CalendarMatrix
subclass thus does not need to alter anything in its superclass. It just needs to
supplement NSMatrix with additional data and behavior so it can understand
dates (and update itself appropriately), navigate between months, and notify a
delegate that a selection was made.

Define the CalendarMatrix class


in Interface Builder.

From Project Builder, open


ToDo.nib.
16 10W
In Interface Builder, choose o NSVieVl 26 2W
Document ~ New Module ~ t::) NSBo;-.:: 26 2W
New Empty to create a new nib o NSControl 26
file. t::) NSBrow'ser 36
fJ NSButton 26
Save the nib file as ToDoDoc.nib.
Locate NSMatrix several levels down in the
In the Classes display ofthe nib class hierarchy.
file window, select NSMatrix.
Choose Subclass from the pull-
down list.
Name the new class
"CalendarMatrix".
Select the new class. Outlets and actions already defined by the
superclass (or its superclasses) appear in
gray text. Add the outlets and actions shown
Add the outlets and actions in black text.
shown in the example at right.

choseDay:
monthChang ed:
fax:

When you created subclasses ofNSObject in the previous two tutorials, the next
step was to instantiate the subclass. Because CalendarMatrix is a view (that is, it
inherits from NSView), the procedure for generating an instance for making
connections is different.

129
Chapter 4 To Do Tutorial

2 Put a custom NSView object


(CalendarMatrix) on the user
interface. .,-+---- representing
The Custom View object is a "proxy" object,
any custom NSView object on
the interface.
Drag awindow from the Windows
palette. Assign a class to the Custom View by
selecting a class here. Custom classes
Resize the window, using the must be defined in the nib file.
example at right as a guide.
Turn off the window's resize
handle.
Drag a CustomView from the
Views palette onto the window.
Resize and position the
Custom View, using the example
at right as a guide.
In the Attributes display of the
inspector, select CalendarMatrix
from the list of available classes.

The selection of the class for the CustomView creates an instance of it that you
can connect to other objects in the nib file. Now put the controls and fields
associated with CalendarMatrix on the window.

130
Subclass Example: Adding Data and Behavior ICalendarMatrix)

3 Put the objects related to ,.-,---,-----:-----,-".,--...,...-,--:---:-:----:--:--..,..-~=;:=;:=i'--- This label contains the month and year:. Initialize by
CalendarMatrix on the window. typing "September 9999" (the longest possible string).
set text to Helvetica 18, center it, then delete it.

Drag a label object for the month-


year from the Views palette and ::1--+--- Type the days of the week as individual labels, arrangE
as a row, then distribute the fields evenly over columm
put it over the CalendarMatrix. (this may take some trial and error).

Make seven small labels for each


day ofthe week. ~--II--- To make the button enclose the image as tightly as
possible, select the button and choose
Format pSize pSize To Fit.
Drag a button onto the interface
and set its attributes to
unbordered and image only.
Drag lefCarrow.tiff from
/NextDeveloper/Examples
/AppKit/ToDo and drop it overthe
button.
To the attention panel that asks
"Insert image left_arrow in
project?" click Yes.
Repeat the same button
procedure for righcarrow.tiff.

Next connect CalendarMatrix to its satellite objects.

4 Connect CalendarMatrix to its Name Connection Type


outlet and to the controls sending
monthName From CalendarMatrix to the label field above it outlet
action messages.
leftButton From CalendarMatrix to the left-pointing arrow outlet
5 Finish up in Interface Builder.
rightButton From CalendarMatrix to the right-pointing arrow outlet
Save ToDoDoc.nib.
Select CalendarMatrix and in the monthChanged: From both arrows to CalendarMatrix action
Classes display and choose
Create Files from the Operations
pull-down menu.
You might have noticed that there's an action message left unconnected:
Confirm that you wantthe source- choseDay:. Because it is impossible in Interface Builder to connect an object with
code files added to the project.
itself, you will make this connection programmatically.

131
Chapter 4 To Do Tutorial

6 Add declarations to the header @interface CalendarMatrix NSMatrix


file CalendarMatrix.h.
/* ... * /
(Existing declarations are NSCalendarDate *selectedDaYi
indicted by ellipsis.) short startOffsetj /* 1 */

/ * ... * /
- (void)refreshCalendar;
- (id)initWithFrame: (NSRect)frameRecti
- (void)dealloc;
- (void)setSelectedDay: (NSCalendarDate *)newDaYi
- (NSCalendarDate *)selectedDaYi
@end

@interface NSObject(CalendarMatrixDelegate) /* 2 */
- (void)calendarMatrix: (CalendarMatrix *)obj
didChangeToDate: (NSDate *)datei
- (void)calendarMatrix: (CalendarMatrix *)obj
didChangeToMonth: (int)mo year: (int)yr;
@end

There are a couple of interesting things to note about these declarations:

1. The cells in CalendarMatrix are sequentially ordered by tag number, left to right,
going downward. startOffset marks the cell (by its tag) on which the first day of the
month falls.

2. CalendarMatrixDelegate is a category on NSObject that declares the


methods to be implemented by the delegate. This technique creates what is
called an informal protocol, which is commonly used for delegation methods.

132
Subclass Example: Adding Data and Behavior /CalendarMatrix)

7 Implement CalendarMatrix's - (id)initWithFrame: (NSRect)frameRect


initialization methods.
int i, j, cnt=Oi
Select CalendarMatrix.m in the id cell = [[NSButtonCell alloc] initTextCell:@""];
project browser. NSCalendarDate *now = [NSCalendarDate date] i /* 1 *1
Write the implementation of
initWithFrame: (at right). [super initWithFrame:frarneRect 1* 2 *1
mode:NSRadioModeMatrix
Implement dealloc.
prototype:cell
numberOfRows:6
numberOfColumns:7] i
II set cell tags 1* 3 *1
for (i=Oi i<6i i++) {
for (j=Oi j<7i j++)
[[self cellAtRow:i column:j] setTag:cnt++];

[cell release];
selectedDay = [[NSCalendarDate dateWithYear: [now yearOfCommonEra]
month: [now monthOfYear] 1* 4 *1
day: [now dayOfMonth]
hour:O minute:O second:O
timeZone: [NSTimeZone locaITimeZone]] copy];

return self;

The initWithFrame: method is an initializer of NSMatrix, NSControl and NSView.

1. This invocation of date, a class method declared by NSDate, returns the current
date ("today") as an NSCalendarDate. (NSCalendarDate is a subclass ofNSDate.)

2. This message to super (NSMatrix) sets the physical and cell dimensions of the
matrix, identifies the type of cell using a prototype (an NSButtonCell), and
specifies the general behavior of the matrix: radio mode, which means that
only one button can be selected at any time.

3. Set the tag number of each cell sequentially left to right and down. Tags are
the mechanism by which CalendarMatrix sets and retrieves the day numbers
of cells.

4. This NSCalendarDate class method initializes the selectedDay instance


variable to midnight of the current day, using the year, month, and day
elements of the current date. The localTimeZone message obtains an
NSTimeZone object with an suitable offset from Greenwich Mean Time.

133
Chapter 4 To Do Tutorial

Implement awakeFromNib as - (void)awakeFromNib


shown at right.
[monthName setAlignment:NSCenterTextAlignmentli
[self setTarget:selfli
[self setAction:@selector(choseDay:)li
[self setAutosizesCells:YES]i
[self refre~hCalendar];

The awakeFromNib method performs additional initializations (some of which


could just have easily been done in initWithFrame:). Most importantly, it sets self as
its own target object and specifies an action method for this target, choseOay:,
something that couldn't be done in Interface Builder. Other methods to note:

• setAutosizesCells: causes the matrix to resize its cells on every redraw.


• refreshCalendar (which you'll write next) updates the calendar.

The refreshCalendar method is fairly long and complex-it is the workhorse of the
class-so you'll approach it in sections.

Dates and limes in OpenStep

In Open Step you represent dates and times as objects that inherit The NSCalendarDate class, which inherits from NSDate,
from NSDate. The major advantage of dates and times as objects generates objects that represent dates conforming to western
is common to all objects that represent basic values: they yield calendrical systems. NSCalendarDate objects also adjust the
functionality that, although commonly found in most operating representations of dates to reflect their associated time zones.
systems, is not tied to the internals of any particular operating-
Because of this, you can track an NSCalendarDate object across
system.
differenttime zones. You can also present date information from
NSDates hold dates and times as values of type NSlimelnterval time-zone viewpoints other than the one for the current locale.
and express these values as seconds. The NSlimelnterval type
makes possible a wide and fine-grained range of date and time Each NSCalendarDate object also has a calendar format string
values, giving accuracy within milliseconds for dates 10,000 years bound to it. This format string contains date-conversion specifiers
apart.
that are very similar to those used in the standard Clibrary
NSDate and its subclasses compute time as seconds relative to function strftimeO. NSCalendarDate can interpret user-entered
an absolute reference date (the first instant of January 1, 2001). dates that conform to this format string.
NSDate converts all date and time representations to and from
NSlimelnterval values that are relative to this reference date. NSCalendar has methods for creating NSCalendarDate objects
from formatted strings and from component time values (such as
NSDate provides methods for obtaining NSDate objects
(including date, which returns the current date and time as an minutes, hours, day of week, and year). It also supplements
NSDate), for comparing dates, for computing relative time values, NSDate with methods for accessing componenttime values and
and for representing dates as strings. for representing dates in various formats, locales, and time zones.

134
Subclass Example: Adding Data and Behavior ICalendarMatrix)

8 Implement the code that updates static short MonthDays[] =


the calendar. {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }i
#define isLeap (year) (( ( ((year) % 4) == 0 && (( (year) % 100) ! = 0))
Initialize the MonthDays[] array II ((year) % 400) == 0))
and write the isLeapO macro.
Determine the day of the week at
the start of the month and the
- (void)refreshCalendar
number of days in the month.
NSCalendarDate *firstOfMonth, *selDate = [self selectedDay],
*now = [NSCalendarDate date] i
int i, j, currentMonth = [selDate monthOfYear] i
unsigned int currentYear = [selDate yearOfCommonEra] i
short dayslnMonthi
id celli

firstOfMonth [NSCalendarDate dateWithYear:currentYear /* 1 */


month:currentMonth
day:1 hour:O minute:O second:O
timeZone: [NSTimeZone locaITimeZone]]i
[monthName setStringValue: [firstOfMonth /* 2 */
descriptionWithCalendarFormat:@"%B %Y"]] i
dayslnMonth = MonthDays[currentMonth-1]+li /* 3 */
/* correct Feb for leap year */
if ((currentMonth == 2) && (isLeap(currentYear))) dayslnMonth++i
startOffset = [firstOfMonth dayOfWeek]i /* 4 */

Before it can start writing day numbers to the calendar for a given month,
CalendarMatrix must know what cell to start with and how many cells to fill with
numbers. The refreshCalendar method begins by calculating these values.

1. Creates an NSCalendarDate for the first day of the currently selected month and
year (computed from the selectedDay instance variable).

2. Writes the month and year (for example, "February 1997") to the label above
the calendar.

3. Gets from the MonthDays static array the number of days for that month; if the
month is February and it is a leap year, this number is adjusted.

4. Gets the day of the week for the first day of the month and stores this in the
startOffset instance variable.

135
Chapter 4 To Do Tutorial

Write the refreshCalendar code for (i=Oi i<startOffseti i++)


that writes day numbers to the cell = [self cellWithTag:i]i
cells and sets cell attributes. [cell setBordered:NO] i
[cell setEnabled:NO] i
[cell setTitle:@""];
[cell setCellAttribute:NSCellHighlighted to:NO);

for (j=l; j < dayslnMonth; i++, j++)


cell = [self cellWithTag:i]i
[cell setBordered:YES] i
[cell setEnabled:YES] i
[cell setFont: [NSFont systernFontOfSize:12]];
[cell setTitle: [NSString stringWithForrnat:@"%d", j]]i
[cell setCellAttribute:NSCellHighlighted to:NO)i

for (ii<42ii++) {
cell = [self cellWithTag:i] i
[cell setBordered:NO] i
[cell setEnabled:NO] i
[cell setTitle:@""]i
[cell setCellAttribute:NSCellHighlighted to:NOJi

The first and third for-loops in this section of code clear the leading and trailing
cells that aren't part of the month's days. Because the current day is indicated by
highlighting, they also turn off the highlighted attribute. The second for-loop
writes the day numbers of the month, starting at startOffset and continuing until
dayslnMonth, and resets the font (since the selected day is in bold face) and other
cell attributes.

Complete the refreshCalendar if ((currentYear == [now yearOfCornrnonEra])


method implementation by && (currentMonth == [now rnonthOfYear]»
resetting the "today" cell [[self cellWithTag: ([now dayOfMonth]+startOffset)-l]
attribute.
setCellAttribute:NSCellHighlighted to:YES]i
[[self cellWithTag: ([now dayOfMonth]+startOffset)-l]
setHighlightsBy:NSMornentaryChangeButton];
}

This final section of refreshCalendar determines if the newly selected month and
year are the same as today's, and if so highlights the cell corresponding to today.

136
Subclass Example: Adding Data and Behavior (CalendarMatrix)

9 Implement the monthChanged: - (void)monthChanged:sender


action method.
NSCalendarDate *thisDate = [self selectedDay] i
int currentYear = [thisDate yearOfCommonEra] i
unsigned int currentMonth = [thisDate monthOfYear]i

if (sender == rightButton) { /* 1 */
if (currentMonth == 12) {
currentMonth = 1i
currentYear++i
else {
currentMonth++i

else
if (currentMonth == 1)
currentMonth 12i
currentYear--i
else {
currentMonth--i

/* 2 */
[self setSelectedDay: [NSCalendarDate dateWithYear:currentYear
month:currentMonth
day:1 hour:O minute:O second:O
timeZone: [NSTimeZone localTimeZone]]]i
[self refreshCalendar]i
[[self delegate] calendarMatrix:self /* 3 */
didChangeToMonth:currentMonth year:currentYear]i

The arrow buttons above CalendarMatrix send it the monthChanged: message


when they are clicked. This method causes the calendar to go forward or
backward a month.

1. Determines which button is sending the message, then increments or decrements


the month accordingly. If it goes past the end or beginning of the year, it
increments or decrements the year and adjusts the month.

2. Resets the selectedDay instance variable with the new month (and perhaps
year) numbers and invokes refreshCalendar to display the new month.

3. Sends the calendarMatrix:didChangeToMonth:year: message to its delegate (which


in this application, as you'll soon see, is a ToDoDoc controller object).

137
Chapter 4 To Do Tutorial

10 Implement the choseDay: action - (void)choseDay:sender


method.
NSCalendarDate *selDate, *thisDate = [self selectedDaY]i
/* 1 */
unsigned int selDay = [[self selectedCell] tag]-startOffset+l;
/* 2 */
selDate = [NSCalendarDate dateWithYear: [thisDate yearOfCommonEra]
month: [thisDate monthOfYear]
day:selDay
hour:O
minute:O
second:O
timeZone: [NSTimeZone localTimeZone]]i
/* 3 */
[[self cellWithTag: [thisDate dayOfMonth]+startOffset-l]
~etFont: [NSFont systemFontOfSize:12]];
[[self cellWithTag:selDay+startOffset-l] setFont:
[NSFont boldSystemFontOfSize:12]]i
/* 4 */
[self setSelectedDay:selDate]i
[[self delegate] calendarMatrix:self didChangeToDate:selDate];

This method is invoked when users click a day of the calendar.

1. Gets the tag number of the selected cell and subtracts the offset from it (plus one
to adjust for zero-based indexing) to find the number of the selected day.

2. Derives an NSCalendarDate that represents the selected date.

3. Sets the font of the previously selected cell to the normal system font
(removing the bold attribute) and puts the number of the currently selected
cell in bold face.

4. Sets the selectedDay instance variable to the new date and sends the
11 Implement accessor methods for calendarMatrix:didChangeToDate: message to the delegate.
the selectedDay instance
variable. You are finished with CalendarMatrix. If you loaded ToDoDoc.nib right now, the
calendar would work, up to a point. If you clicked the arrow buttons,
CalendarMatrix would display the next or previous months. The days of the
month would be properly set out on the window, and the current day would be
highlighted.

But not much else would happen. That's because CalendarMatrix has not yet
been hooked up to its delegate.

138
The Basics of a Multi-Document Application

The Basics of a Multi-Document Application


A multi-document application, as described on page 141, has at least one
application controller and a document controller for each document opened.
The application controller also responds to user commands relating to
documents and either creates, opens, closes, or saves a document.

Customize the application's main Customize the Document submenu by deleting


menu. the Save As, Save To, Save AI/, and Revert To
Saved commands.
Open ToDo.nib in Interface
Builder.
Drag the Document item from the
Menus palette and drop it
' - - - - - - - Append an ellipsis (three dots) to the command
between the Info and the Edit name to indicate that the command displays a
submenus. panel. Also enter "i" as the key equivalent.
, Drag the Item item from the
Menus palette and drop it
Note: The Info submenu, which you get by default, includes the Info Panel,
between the Edit and Windows
menus. Preferences, and Help commands. Although this tutorial does not cover
implementing Info and Preferences panels specifically, it does give you enough
Change the title of "Item" to
"Inspector." information (which it will supplement with tips) so that you can try to
implement these panels on your own. You may delete the Help command from
the Info submenu if you wish; if you leave it in and users click it, they get a
message informing them that Help is not available.

2 Define the application-controller


class.

Create ToDoControlier as a
subclass of NSObject. o To Do Controller
OUtlets
Add the outlet and actions (listed inspector
at right) to the class.
Make the action connections
from the appropriate Document openDoc:
menu commands. saveDoc:
. showlnfo:
showlnspector;
showPreferences:

Now that you've defined the application-controller class, define the document-
controller class, ToDoDoc. Remember, since the ToDoDoc controller must own
the nib file containing the document, it must be external to it; although it is
defined in the main nib file (ToDo.nib) and in ToDoDoc.nib, it's instantiated before
its nib file is loaded.

139
Chapter 4 To Do Tutorial

3 Define the document-controller


class.

Create ToDoDoc as a subclass of


NSObject.
Add to the class the outlets and
action listed at right.
Instantiate ToDoControlier and
ToDoDoc.
Save ToDo.nib.

Now add the remaining objects to the document interface.

4 Complete the document


interface.

Open ToDoDoc.nib.
Add the matrices oftext fields.
Add the labels above the
matrices.
Make the labels 14 points in the
user's application font.
Make the item text 12 points in the
Set the text color of this
user's application font. label to dark gray.
Add padding to this label,
Save ToDoDoc.nib. extending it acrss the
column.

To assist alignment, these

II';ill!~:::::~~~~~~r Remember,
cells are text fields of the
same size as the cells of the
other matrix. However, you create a matrix
by Alternate-dragging a
will at run time substitute handle of a suitable object.
your own custom cell Before Alternate-dragging,
(ToDoCell). make the initial text field
scrollable.

5 Connect the outlets and actions Name Connection Type


of ToDoDoc.
calendar From File's Owner to the CalendarMatrix object outlet
Select File's Owner in the
Instances display of dayLabel From File's Owner to label "To Do on" outlet
ToDoDoc.nib.
itemMatrix From File's Owner 1T0DoDoc) to matrix of long text fields outlet
Choose ToDoDoc from the list of
classes in the Attributes display markMatrix From File's Owner to matrix of short text fields outlet
of the inspector.
itemChecked: From matrix of short text fields to File's Owner action
Make the connections described
in the table at right.

140
The Basics of a Multi-Document Application

The Structure of Multi-Document Applications

From a user's perspective, a document is a unique body of controller delegates ofthe application (NSApp) and the document
information usually contained by its own window. Users can window, they can receive messages sent at critical moments of a
create an unlimited number of documents and save each to a file. running application. These moments include the closure of
Common documents are word-processing documents and windows (windowShouldClose:), window selection
spreadsheets. (windowDidResignMain:), application start-up
(applicationWiIIFinishLaunching:) and application termination
From a programming perspective, a document comprises the
(applicationShouldTerminate:). In the methods handling these
objects and resources unarchived from an auxiliary nib file and
messages, the controllers can then do the appropriate thing, such
the controller object that loads and manages these things. This
as saving a document's data or displaying an empty document.
document controller is the owner ofthe auxiliary nib file
containing the document interface and related resources.To Several NSViews also have delegation messages that facilitate
manage a document, the document controller makes itselfthe document management, particularly text fields, forms, and other
delegate of its window and its "content" objects. Ittracks edited controls with editable text (controIText... ) and NSText objects
status, handles window-close events, and responds to other (text... ). One important such message is textDidChange: (or
conditions. controITextDidChange:), which signals that the document's
textual content was modified. In responding to this message,
When users choose the New (or equivalent) command, a method
controllers can setthe window's close button to have a "broken"
is invoked in the application's controller object. In this method, the
X with the setDocumentEdited: message; later, they can
application controller creates a document-controller object,
determine whether the document needs to be saved by sending
which loads the document nib file in the course of initializing itself.
isDocumentEdited to the window.
A documentthus remains independent of the application's" core"
objects, storing state data in the document controller. If the Document controllers often need to communicate with the
application needs information about a document's state, it can application controller or other objects in the application. One way
query the document controller. to do this is by posting notifications. Another way is to use the key
relationships within the core program framework (see page 149)
When users chose the Save command, the application displays a
to find the other object (assuming it's a delegate of an Application
Save panel and enables users to save the document in the file
Kit object). For example, the application controller can send the
system. When users chose the Open command, the application
following message to locate the current document controller:
displays an Open panel, allowing users to select a document file
and open it. [[NSApp mainWindow] delegate]
The document controller can find the application controller with:
Document Management Techniques
[NSApp delegate]
When you make the application controller and the document

Document Creation Sequence

creates loads

Sav,fAS':,
C!ose~ AppController DocController Doc.nib

141
Chapter 4 To Do Tutorial

Text fields in a matrix, just like a form's cells, are connected for inter-field
tabbing when you create the matrix. But you must also connect ToDoDoc and
ToDoController to the delegate outlets of other objects in the application-this
step is critical to the multi-document design.

Connect ToDoDoc and Name Connection


ToDoControlier to other objects
as their delegates. textDelegate From the CalendarMatrix object to File's Owner (ToDoDoc)

delegate From the document window's title bar to File's Owner (ToDoDoc)

delegate In ToDo.nib, from File's Owner (NSApp) to the ToDoController instance

6 Create source-code files for The ToDoDoc class needs supplemental data and behavior to get the multi-
ToDoDoc and ToDoControlier. document mechanism working right.
In Project Builder:
@interface ToDoDoc:NSObject
7 Add declarations of methods and
instance variables to the /* ... * /
ToDoDoc class. NSMutableDictionary *activeDays;
NSMutableArray *currentltemsi
. SelectToDoDoc.h in the project
browser. /* ... * /
Add the declarations at right. - (NSMutableArray *)currentltems;
- (void)setCurrentltems: {NSMutableArray *)newltems;
(Ellipses indicate existing {NSMatrix *)itemMatrix;
declarations.)
- (NSMatrix *)markMatrixi
- (NSMutableDictionary *)activeDaYSi
- (void)saveDoCi
- (id)initwithFile: (NSString *)aFilei
- (void)dealloc;
- (void)activateDoCi
- (void)selectltem: (int) item;
@end

The active Days and currentltems instance variables hold the collection objects that
store and organize the data of the application. (You'll deal with these instance
variables much more in the next section of this tutorial.) Many of the methods
declared are accessor methods that set or return these instance variables or one
of the matrices of the document.

You'11 be ~witching between ToDoDoc.m and ToDoController.m in the next few tasks.
The intent is not to confuse, but to show the close interaction between these
two clas~es.

142
The Basics of a Multi-Document Application

8 Write the code that creates - (void)newDoc: (id) sender


documents.

Select ToDoController.m in the


id currentDoc =
[[ToDoDoc alloc] initWithFile:nil] i
[currentDoc activateDoc]i
project browser.
Implement ToDoControlier's
newDoc: method.
The newDoc: method is invoked when the user chooses New from the
Document menu. The method allocates and initializes an instance of the
document controller, ToDoDoc, thereby creating a document. (See the
implementation of initWithFile: on the following page to see what happens in this
process.) It then updates the document interface by invoking activateDoc ..

Coordinate Systems in OpenStep

The screen's coordinate system is the basis for all other The reference coordinate system for a window is known as the
coordinate systems used for positioning, sizing, drawing, and base coordinate system. It differs from the screen coordinate
event handling. You can think of the entire screen as occupying system in only two ways:
the upper-right quadrant of a two-dimensional coordinate grid.
• It applies only to a particular window, each window has its own
The other three quadrants, which are invisible to users, take
base coordinate system.
negative values along their x-axis, their y-axis, or both axes. The
screen's quadrant has its origin in the lower left corner; the • Its origin is at the lower left corner of the window, rather than
positive x-axis extends horizontally to the right and the positive y- the lower left comer of the screen. If the window moves, the
axis extends vertically upward. A unit along either axis is origin and the entire coordinate system move with it
expressed as a pixel.
For drawing, each NSView uses a coordinate system transformed
from the base coordinate system or from the coordinate system of
The screen coordinate system has just one function: to position its superview. This coordinate system also has itorigin pointatthe
windows on the screen. When your application creates a new lower-left corner of the NSView, making it more convenient for
window, it must specify the window's initial size and location in drawing operations. NSView has several methods for converting
screen coordinates.You can "hide" windows by specifying their between base and local coordinate systems. When you draw,
origin points well within one of the invisible quadrants. This coordinates are expressed in the application's currentcoordinate
technique is often used in off-screen rendering in buffered system, the system reflecting the last coordinate transformations
windows. to have taken place within the current window.

.~. . . . ..&....... &a... &aa."'''.''A'' ••''' •• ''&'''''''.''''''.&A''--:. A view's location is specified


=
..
.~~ ;:
':
relative to the coordinate
system of its window or
• ~ ·c
; . superview. The coordinate
· ::..
origin for drawing begins at

·::• ,:•
this point.
The location of the window is
expressed relative to the

··•
=

=
• x-axis
..
::

:

screen's origin, and its coord-
inate system begins here too.

ll(~·o. ! The origins and dimensions of ' "


windows and panels are based
on the screen origin.
; -200.0) : 0,0
• " " " . . . . . . . . , . . " . . . y.................,."..... ............................................. ~

143
Chapter 4 To Do Tutorial

Select ToDoDoc.m in the project - initWithFile: (NSString *)aFile


browser.
Implement ToDoDoc's NSEnumerator *dayenum;
initWithFile: method. NSDate *itemDate;

[super init];
if (aFile) { /* 1 */
activeDays = [NSUnarchiver unarchiveObjectWithFile:aFile];
if (activeDays)
activeDays = [activeDays retain];
else
NSRunAlertPanel(@"To Do", @"Couldn't unarchive file %@",
nil, nil, nil, aFile);
else /* 2 */
activeDays = [[NSMutableDictionary alloc] init];
[self setCurrentltems:nil];

if (! [NSBundle loadNibNamed:@"ToDoDoc.nib" owner:self] ) /* 3 */


return nil;
if (aFile) /* 4 */
[[itemMatrix window] setTitleWithRepresentedFilename:aFile];
else
[[itemMatrix window] setTitle:@"UNTITLED"];
[[itemMatrix window] makeKeyAndOrderFront:self];
return self;

This method, which initializes and loads the document, has the following steps:

1. Restores the document's archived objects if the aFile argument is the pathname of
a file containing the archived objects (that is, the document is opened). If objects
are unarchived, it retains the activeDays dictionary; otherwise it displays an
attention panel.

2. Initializes the activeDays and currentltems instance variables. A aFile argument


with a nil value indicates that the user is requesting a new document.

3. Loads the nib file containing the document interface, specifying self as owner.

4. Sets the title of the window; this is either the file name on the left of the title
bar and the pathname on the right, or "UNTITLED" if the document is new.

Be/ore You Go On - - - - - - - - - - - - - - - - - - - - - -
Note the [itemMatrix window] message nested in the last message. Every object
that inherits from NSView "knows" its window and will return that NSWindow
object if you send it a window message.

144
The Basics of a Multi-Document Application

9 Implement the document- - (void)openDoc: (id) sender


opening method.
int result;
Select ToDoController.m in the
NSString *selected, *startDir;
project browser.
NSArray *fileTypes = [NSArray arrayWithObject:@"td"];
Write the code for open Doc:. NSOpenPanel *oPanel = [NSOpenPanel openPanel]; /* 1 */

[oPanel setAllowsMultipleSelection:YES]i
if ([[[NSApp keyWindow] delegate] isKindOfClass: [ToDoDoc class]])
startDir = [[[NSApp keyWindow] representedFilename] /* 2 */
stringByDeletingLastPathComponent];
else
startDir = NSHomeDirectory();
resul t = [,oPanel runModalForDirectory: startDir file: nil /* 3 */
types:fileTypes] ;
if (result == NSOKButton) {
NSArray *filesToOpen = [oPanel filenames];
int i, count = [filesToOpen count];
for (i=O; i<count; i++) { /* 4 */
NSString *aFile = [filesToOpen objectAtIndex:i];
id currentDoc = [[ToDoDoc alloc] initWithFile:aFile];
[currentDoc activateDoc];

The open Doc: method displays the modal Open panel, gets the user's response
(which can be multiple selections) and opens the file (or files) selected.

1. Creates or gets the NSOpenPanel instance (an instance shared among objects of
an application). The previous message specifies the file types (that is, the
extensions) of the files that will appear in the Open panel browser. The next
message enables selection of multiple file in the panel's browser.

2. Sets the directory at which the NSOpenPanel starts displaying files either to
the directory of any document window currently key or , if there is none, to
the user's home directory.

3. Runs the NSOpenPanel and obtains the key clicked.

4. If the key is NSOKButton, cycles through the selected files and, for each,
creates a document by allocating and initializing a ToDoDoc instance,
passing in a file name.

The methods invoked by the Document menu's Close and Save commands
both simply send a message to another object. How they locate these objects
exemplify important techniques using the core program framework.

145
Chapter 4 To Do Tutorial

10 Write the code that closes - (void)closeDoc: (id) sender


documents.
[[NSApp mainWindow] performClose:self];
In ToDoController.m, implement
the closeDoc: method.

NSApp, the global NSApplication instance, keeps track of the application's


windows, including their status. Because only one window can have main status,
the mainWindow message returns that NSWindow object- which is, of course,
the one the user chose the Close command for. The closeDoc: method sends
performClose: to that window to simulate a mouse click in the window's close
button. (See the following section, "Managing Documents Through
Delegation," to learn how the document handles this user event.)

11 Write the code that saves - (void)saveDoc: (id) sender


documents.

In ToDoController.m, implement
id currentDoc =[[NSApp mainWindow] delegate];
if (currentDoc)
the saveDoc: method.
[currentDoc saveDoc];

As did closeDoc:, this method sends mainWindow to NSApp to get the main
window, but then it sends delegate to the returned window to get its delegate, the
ToDoDoc instance that is managing the document. It then sends the ToDoDoc-
defined message saveDoc to this instance.

Note: You could implement closeDoc: and saveDoc: in the ToDoDoc class, but the
ToDoController approach was chosen to make the division of responsibility
clearer.

146
The Basics of a Multi-Document Application

Select ToDoDoc.m in the project - (void)saveDoc


browser.
Implement the saveDoc: method. NSString *fn;

if (! [[[itemMatrix window] title] hasPrefix:@"UNTITLED"])


fn = [[itemMatrix window] representedFilename]; /* 1 */
else {
int result; /* 2 */
NSSavePanel *sPanel = [NSSavePanel savePanel];
[sPanel setRequiredFileType:@"td"];
result = [sPanel runModalForDirectory:NSHomeDirectory() file:nil];
if (result == NSOKButton) {
fn = [sPanel filename];
[[itemMatrix window] setTitleWithRepresentedFilename:fn];
else
return;

if (! [NSArchiver archiveRootObject:activeDays toFile:fn]) /* 3 */


NSRunAlertPanel(@"To Do", @"Couldn't archive file %@",
nil, nil, nil, fn);
else
[[itemMatrix window] setDocumentEdited:NO];

ToDoDoc's saveDoc method complements ToDoController's openDoc: method in


that it runs the modal Save panel for users.

1. The title method returns the text that appears in the window's title bar. If the title
doesn't begin with "UNTITLED" (what new document windows are initialized
with), then a file name and directory location has already been chosen, and is
stored as the represented Filename.

2. If the window title begins with "UNTITLED" then the document needs to
be saved under a user-specified file name and directory location. This part of
the code creates or gets the shared NSSavePanel instance and sets the file
type, which is the extension that's automatically appended. Then it runs the
Save panel, specifying the user's home directory as the starting location.

3. Archives the document under the chosen directory path and file name and,
with the setDocumentEdited: message, changes the window's close button to an
"unbroken X" image (more on this in the next section).

12 Implementthe accessor methods Don't implement setCurrentltems: yet. This method does something special for
for ToDoController and ToDoDoc. the application that will be covered in "Managing the Data and Coordinating its
Display (ToDoDoc)" on page 154.

147
Chapter 4 To Do Tutorial

SAP~lic,ati~n~~~\Nin~~YI';an~ .~;S,Vie"!i;'.~1
t.--h"""''''''~_''_.~~_._._' ' • ..,...""...~_,,<,",._._.,..,,,~,J:,~,,,,,."»,,,,J..._,,,~~-.~/-:'~,. __,,,,,,,;.;;L~~,~:~•. ~_ __

Many classes ofthe Application Kit stand out in terms of relative NSResponder
importance. NSControl, for example, is the superclass of all user-
interface devices, NSText underlies all text operations, and NSResponder is an abstract class, but it enables event handling
NSMenu has obvious significance. But four classes are at the in all classes that inherit from it. It defines the set of messages
core of a running application: NSResponder, NSApplication, invoked when different mouse and keyboard events occur. It also
NSWindow, and NSView. Each of these classes plays a critical defines the mechanics of event processing among objects in an
role in the two primary activities of an application: drawing the application, especially the passing of events up the responder
user interface and responding to events. The structure of their chain to each next responder until the event is handled. See the
interaction is sometimes called the core program framework. "Events and the Event Cycle" on page 163 for more on the
responder chain and a description of first responder.
NSWindow
NSApplication
An NSWindow object manages each physical window (that is,
each window created by the Window Server) on the screen. It Every application must have one NSApplication object to act as
draws the title bar and window frame and responds to user its interface with the Window Server and to supervise and
actions that close, move, resize, and otherwise manipulate the coordinate the overall behavior of the application. This object
window. receives events from the Window Server and dispatches them to
the appropriate NSWindows (which, in turn, distribute them to
The main purpose of an NSWindow is to display an application's their NSViews). The NSApplication object manages its windows
user interface (or part of it) in its content area: that space below and detects and handles changes in their status as well as in its
the title bar and within the window frame. A window's content is own status: hidden and unhidden, active and inactive. The
the NSViews it encloses, and at the root of this view hierarchy is NSApplication object is represented in each application by the
the content view, which fills the content area. Based on the global variable NSApp. To coordinate your own code with NSApp,
location of a user event, NSWindows assigns an NSView in its you can assign your own custom object as its delegate.
content area to act as first responder.
An NSWindow allows you to assign a custom object as its NSView
delegate and so participate in its activities. Any object you see in a window's content area is an NSView.
(Actually, since NSView is an abstract class, these objects are
instances of NSView subclasses.) NSView objects are
responsible for drawing and for responding to mouse and
keyboard events Each NSView owns a rectangular region
The NSEvent class is also
involved in event processing. For associated with a particular window; it produces images within
more about NSEvent and the this region and responds to events occurring within the rectangle.
NSObject
event cycle, see "Events and the
NSViews in a window are logically arranged in a view hierarchy,
Event Cycle" on page 163.
with t~e content viewatthe top of the hierarchy (see facing page
for more information). An NSView references its window, its
superview, and its subviews. It can be the first responder for
events orthe next responder in the responder chain. An NSView's
NSResponder frame and bounds are rectangles that define its location on the
screen, its dimension, and its coordinate system for drawing.

I
NSWindow NSApplication NSView

148
The Basics of a Multi-Document Application

The View Hierarchy Fitting Your Application In


Just inside each window's content area-the area enclosed by The core program framework provides ways for your application
the title bar and the other three sides of the frame-lies the to access the participating objects and so to enter into the action.
content view. The content view is the root (or top) NSView in the
• The global variable NSApp identifies the NSApplication object.
window's view hierarchy. Conceptually like a tree, one or more
By sending the appropriate message to NSApp, you can obtain
NSViews may branch from the content view, one one or more
the application's NSWindow objects (windows), the key and
other NSViews may branch from these subordinate NSViews, and
main windows (keyWindow and mainWindow), the current
so on. Except for the content view, each NSView has one (and
event (currentEvent), the main menu (mainMenu), and the
only one) NSView above it in the hierarchy. An NSView's
application's delegate (delegate).
subordinate views are called its subviews; its superior view is
known as the superview. • Once you've identified an NSWindow object, you can get its
contentview(bysending itcontentView) and from that you can
On the screen enclosure determines the relationship between
get all subviews of the window. By sending messages to the
superview and subview: a superview encloses its subviews. This
NSWindow object you can also get the current event
relationship has several implications for drawing:
(currentEvent), the current first responder (firstResponder),
• It permits construction of a superview simply by arrangement and the delegate (delegate).
of subviews. (An NSBrowser is an instance of a compound
NSView.) • You can obtain from an NSView most objects it references. You
can discover its window, its superview, and its subviews.
• Subviews are positioned in the coordinates of their superview, Some NSView subclasses can also have delegates, which you
so when you move an NSView or transform its coordinate can access with delegate.
system, all subviews are moved and transformed in concert.
By making your custom objects delegates ofthe NSApplication
• Because an NSView has its own coordinate system for object, your application's NSWindows, and NSViews that have
drawing, its drawing instructions remain constant regardless delegates, you can integrate your application into the core
of any change in position in itself or of its superview. program framework and participate in what's going on.

NSApp
NSApplication
windows .. r+ NSView(B)
~
NSWindow
delegate
~
.... window
contentView ..
po NSView(A) ...
~
superview
delegate subviews
.... window
superview (nil)
subviews

-. NSWindow L..-+ NSView.(C)

conte ntVi ew window


A superview

~
delegate
subviews

149
Chapter 4 To Do Tutorial

Managing Documents Through Delegation

At certain points while an application is running you want to ensure that a


document's data is preserved or that a document's edited status is tracked.
These events occur when users:

• Edit a document.
• Close a window.
• Quit the application.
• Hide the application.
• Switch to another application or window.

Several classes of the Application Kit send messages to their delegates when
these events occur, givi~g the delegate the opportunity to do the appropriate
thing, whether that be saving a document to the file system or marking a
document as edited.

Mark a document as edited.


- (void)controlTextDidChange: (NSNotification *)notif

Open ToDoDoc.m.
[[itemMatrix window] setDocumentEdited:YES];
Implement the
controlTextDidChange: method
to mark the document.
When a control that contains editable text-such as a text field or a matrix of text
fields-detects editing in a field, it posts the controlTextDidChange: notification
which, like all notifications, is sent to the control's delegate as well as to all
observers. The setDocumentEdited: message causes the document's window to
change the image in its close button to a broken X.

~ [window setDocumentEdited:NO] i

~ [window setDocumentEdited:YES];

Note: The ToDo object that, by notification, invokes the controlTextDidChange:


method is itemMatrix, the matrix of to-do items (text fields). You will
programmatically set ToDoDoc to be the delegate of this object later in this
tutorial.

150
Managing Documents Through Delegation

2 Save edited documents when - (BOOL)windowShouldClose: (id) sender


windows are closed.
int result;
Implementthe delegation method
/* 1 */
windowShouldClose:.
if (! [[itemMatrix windowl isDocumentEditedl) return YES;
/* 2 */
[[itemMatrix windowl makeFirstResponder: [itemMatrix windowll;
result = NSRunAlertPanel(@"Close", @"Document has been edited.
Save changes before closing?", @"Save", @"Don't Save",
@"Cancel") ;
/* 3 */
switch(result) {
case NSAlertDefaultReturn:
[self saveDocltemsl;
[self saveDocli
return YES;

case NSAlertAlternateReturn:
return YES;

case NSAlertOtherReturn:
return NO;

return NO;

When users click a window's close button, the window sends windowShouldClose:
to its delegate. It expects a response directing it either to close the window or
leave it open.

1. Returns YES (meaning: go ahead, close the window) if the document hasn't been
edited.

2. Makes the window its own first responder. This has the effect of forcing the
validation of cells, flushing currently entered text to the method that handles
it (more on this in the next section).

3. Identifies the clicked button by evaluating the constant returned from


NSRunAlertPanelO and returns the appropriate boolean value; If the user clicks
the Save button, this method also updates internal storage with the currently
displayed items (saveDocltems) and then sends saveDoc to itself to archive
application data to a file. (saveDocltems is described in the following section.)

Note: Do you recall the performClose: method that ToDoController sends the
document window when the user chooses the Close command? This method

151
Chapter 4 To Do Tutorial

simulates a mouse click on the window's close button, causing windowShouldClose:


to be invoked.

3 Save edited documents when the - (BOOL)applicationShouldTerminate: (id) sender


user quits the application.
while ([NSApp keyWindow]) {
In ToDoController.m, implement
int result;
the delegation method
applicationShouldTerminate:. id doc = [[NSApp keyWindow] delegate];

if (! [[NSApp keyWindow] isDocumentEdited])


[[NSApp keyWindow] close];
if (doc) [doc autorelease];
continue;

if ([doc isKindOfClass: [ToDoDoc class]]) {


NSString *repfile = [[NSApp keyWindow] representedFilename];
result = NSRunAlertPanel(@"To Do", @"Save %@?", @"Save",
@"Don't Save", @"Cancel",
( [repfi Ie isEqual ToString: @" " ] ?@"UNTITLED" : repfile) ) ;
switch(result) {
case NSAlertDefaultReturn:
[doc saveDocItems];
(doc saveDoc];
break;
case NSAlertAlternateReturn:
[[NSApp keyWindow] close];
break;
case NSAlertOtherReturn:
return NO;

if (doc) [doc autorelease];

else
[[NSApp keyWindow] close];

return YES;

NSApplication sends several message to its delegate. One of these messages-


applicationShouldTerminate:-notifies the delegate that the application is about to
terminate. The implementation of this method is similar to that for
windowShouldClose:. What's different is that this method cycles through all
windows of the application and, if the window is managed by To Do Doc, puts
up an attention panel and responds according to the user's choice.

152
Managing the Data and Coordinating its Display (ToDoDoc)

Managing the Data and Coordinating its Display (ToDoDoc)


If you recall the discussion on To Do's design earlier in this chapter ("How To
Do Stores and Accesses its Data" on page 119), you'll remember that the
application's real data consists of instances of the model class, ToDoItem. To Do
stores these objects in arrays and stores the arrays in a dictionary; it uses dates as
the keys for accessing specific arrays. (Both the dictionary and its arrays are
mutable, of course.) You might also recall that this design depends on a
positional correspondence between the text fields of the document interface
and the "slots" of the arrays.

To lend clarity to this design's implementation, this section follows the process
from start to finish through which the ToDoDoc class handles entered data, and
organizes, displays, and stores it. It also shows how the display and manipulation
of data is driven by the selections made in the CalendarMatrix object.

Start by revisiting a portion of code you wrote earlier for ToDoDoc's initWithFile:
method.

- initWithFile: {NSString *)aFile

/ * ... * /
if (aFile)
activeDays = [NSUnarchiver unarchiveObjectWithFile:aFilel i
if (activeDays)
activeDays = [activeDays retainJ;
else
NSRunAlertPanel{@"To Do", @IICouldn't unarchive file %@",
nil, nil, nil, aFile)i
else
activeDays = [[NSMutableDictionary allocl initJi
[self setCurrentItems:nilJi

/* */

Assume the user has chosen the New command from the Document menu.
Since there is no archive file (aFile is nil), the active Days dictionary is created but
is left empty. Then initWithFile: invokes its own setCurrentltems: method, passing in
nil.

153
Chapter 4 To Do Tutorial

Set the current items or, if - (void)setCurrentItems: (NSMutableArray *)newItems


necessary, create and prepare
the array that holds them. if (currentItems) [currentItems autorelease]i

Implement setCurrentltems:.
if (newItems)
currentItems = [newItems mutableCopy] i
else {
int numRows = [[itemMatrix cells] count]i
currentltems = [[NSMutableArray alloc]
initWithCapacity:numRows] i
while (--numRows >= 0)
[currentItems addObject:@""]i

This "set" accessor method is like other such methods, except in how it handles
a nil argument. In this case, nil signifies that the array does not exist, and so it
must be created. Not only does setCurrentltems: create the array, but it "initializes"
it with empty s.tring objects. It does this because NSMutableArray's methods
cannot tolerate nil objects within the bounds of the array.

So there's now a currentltems array ready to accept ToDoItems. Imagine yourself


using the application. What are the user events that cause a ToDoItem to be
added to the currentltems array? To Do allows entry of items "on the fly," and thus
does not require the user to click a button to add a ToDoItem to the array.
Specifically,items are added when users type something and then:

• Press the Tab key.


• Press the Enter key.
• Click outside the text field.

The controlTextDidEndEditing: delegation method makes these scenarios possible.


The matrix of editable text fields (itemMatrix) invokes this method when the
cursor leaves a text field that has been edited.

154
Managing the Data and Coordinating its Display (ToDoDocl

2 As items are entered in the - (void)controITextDidEndEditing: (NSNotification *)notif


interface, add ToDoltems to
internal storage, delete them, or id curltem, newltem;
modify them, as appropriate. int row = [itemMatrix selectedRow];
NSString *selName = [[itemMatrix selectedCell] stringValue];
Implement
/* 1 */
controITextDidEndEditing:.
if (! [[itemMatrix window] isDocumentEdited] II
(row>= [currentltems count])) return;
if (!currentltems)
[self setCurrentltems:nil];
/* 2 */
if ([seIName isEquaIToString:@""] &&
([[currentltems objectAtlndex:row] isKindOfClass:
[ToDoltem class]]) &&
(! [[[currentltems objectAtlndex:row] itemName]
isEquaIToString:@""]))
[currentltems replaceObjectAtlndex:row withObject:@""];
/* 3 */
else if ([[currentltems objectAtlndex:row] isKindOfClass:
[ToDoltem class]] &&
(! [[[currentltems objectAtlndex:row] itemName]
isEquaIToString:seIName]) )
[[currentltems objectAtlndex:row] setltemName:seIName];
/* 4 */
else if (! [selName isEqualToString:@""]) {
newltem = [[ToDoltem alloc] initWithName:selName
andDate: [calendar selectedDay]];
[currentltems replaceObjectAtlndex:row withObject:newltem] ;
[newltem release] ;

/* 5 */
[self updateMatrix] ;

A control sends controlTextDidEndEditing: to its delegate when the cursor leaves a text
field. In addition to creating new ToDoItems, this implementation of
controlTextDidEndEditing: removes ToDoltems from arrays and modifies item text.
What it does is appropriate to what the user does.

1. If the document hasn't been edited (see controITextDidChange:) or if the selected row
exceeds the array bounds, it returns because there's no reason to proceed. It
initializes a currentltems array if one doesn't exist.

2. If the user deletes the text of an existing item, it removes the ToDoltem that
positionally corresponds to the row of that deleted text.

3. It changes the name of an item if the text entered in a field doesn't match the
name of the corresponding item in the currentltems array.

155
Chapter 4 To Do Tutorial

4. If either of the two previous conditions don't apply, and text has been
entered, it creates a new ToDoltem and inserts it in the currentltems array.

5. Updates the list of items in the document interface.

3 Update the document interface


with the current items.

Implement updateMatrix:.
int it cnt =
[currentltems count], rows [[itemMatrix cells] count];
ToDoltem *thisltem;

for (i=Oi i<cnt, i<rows; i++) {


NSDate *due;
thisltem = [currentltems objectAtlndex:i];
if ([thisltem isKindOfClass: [ToDoltem class]]) /* 1 */
if ( [thisltem secsUntilDue] )
due [[thisltem day] addTimelnterval:
[thisltem secsUntilDue]];
else
due nil;
[[itemMatrix cellAtRow:i column:O] setStringValue:
[thisltem itemName]];
£ [markMatrix cellAtRow:i column:O] setTimeDue:due] i
[[markMatrix cellAtRow:i column:O] setTriState:
[thisltem itemStatus]]i

else /* 2 */
[[itemMatrix cellAtRow:i column:O] setStringValue:@IIII]i
[[markMatrix cellAtRow:i column:O] setTitle:@"I)i
[[markMatrix cellAtRow:i column:O] setlmage:nil);

The update Matrix method writes the names of the items (ToDoItems) in the
currentltems array to the text fields of itemMatrix. It also updates the visual
appearance of the cells in the matrix (markMatrix) next to itemMatrix. These cells
are instances of a custom subclass of NSButtonCell that you will create later in
this tutorial. For now, just type all the code above; later, when you create the cell
class, ToDoCell, you can refer back to this example to see what is happening.

Basically, this method cycles through the array of items, doing the following:

1. If an object in the array is a ToDoltem, it writes the item name to the text field
corresponding to the array slot and updates the button cell next to the field.

2. If an object isn't a ToDoltem, it blanks the corresponding text field and cell.

156
Managing the Data and Coordinating its Display (ToDoDoc)

4 Respond to user actions in the - {void)calendarMatrix: {CalendarMatrix *)matrix /* 1 */


calendar. didChangeToDate: {NSDate *)date

Implement CalendarMatrix's [[itemMatrix window] makeFirstResponder: [itemMatrix window]] i


delegation methods.
[self saveDocltems]i

[self setCurrentltems: [activeDays objectForKey:date]]i


[dayLabel setStringValue: [date descriptionWithCalendarFormat:
@"To Do on %a %B %d %Y" timeZone: [NSTimeZone defaultTimeZone]
locale:nil]]i
[self updateMatrix]i

- {void)calendarMatrix: {CalendarMatrix *)matrix /* 2 */


didChangeToMonth: {int)mo year: {int)yr

[self saveDocltems]i
[self setCurrentltems:nil]i
[self updateMatrix]i

As you recall, CalendarMatrix declared two methods to allow delegates to "hook


into" its behavior. Its delegate for this application is ToDoDoc.

1. The calendar sends calendarMatrix:didChangeToOate: when users click a new day of


the month. This implementation saves the current items to the activeOays
dictionary. It then sets the current items to be those corresponding to the selected
date (if there are no items for that date, the objectForKey: message returns nil and the
currentltems array is initialized with empty strings). Finally it updates the matrix
with the new data.

2. The calendar sends calendarMatrix:didChangeToMonth:year:when users go to a new


month and (possibly) a new year. This implementation responds by saving
the current items to internal storage and presenting a blank list of items.

157
Chapter 4 To Do Tutorial

5 Save the data to internal storage. - (void)saveDocltems

Implement saveDocltems:. ToDoltem *anltemj


int i, cnt = [currentltems count] j
6 Archive and unarchive the
II save day's current items (array) to document dictionary
document's data.
for (i=Oi i<cntj i++) {
Implement encodeWithCoder: if ( (anltem = [currentltems objectAtlndex:i]) &&
and initWithCoder:to archive and ([anltem isKindOfClass: [ToDoltem class]]) ) {
unarchive the dictionary holding [activeDays setObject:currentltems forKey:
the arrays of ToDoltems. [anltem day]]j
break;

This method inspects the currentltems array and, if it contains at least one
ToDoltem, puts the array in the activeDays dictionary with a key corresponding
to the date.

Now that you've completed the methods for saving and archiving the collection
objects holding ToDoltems, assume that the user has saved his document and
then opens it.

158
Managing the Data and Coordinating its Display lToDoDocl

7 Perform set-up tasks when the - (void)awakeFromNib


document's nib file is unarchived.
int i;
Implement awakeFromNib as NSDate *date;
shown at right.
date = [calendar selectedDay];
[self setCurrentltems: [activeDays objectForKey:date]];
/* set up self as delegates */
[[itemMatrix window] setDelegate:self];
[itemMatrix setDelegate:self];
[[itemMatrix window] makeKeyAndOrderFront:self];

When the ToDoDoc.nib file is completely unarchived, awakeFromNib is invoked. It


sets the current items for today, sets a couple of delegates, and puts the
document window in front of all other windows.

Note: This method sets some delegates programmatically, which is redundant


since you set these delegates in Interface Builder. However, this code
demonstrates the programmatic route-and no harm done.

8 Set up the document once it's - (void)activateDoc


created or opened.
if ([currentltems count]) [self updateMatrix];
ImplementactivateDoc as shown
[dayLabel setStringValue:[[calendar selectedDay]
at right.
descriptionWithCalendarFormat:@"To Do on %a %B %d %Y"
timeZone: [NSTimeZone defaultTimeZone] locale:nil]];

The activateDoc method is invoked right after a ToOo document is created or


opened. It starts the ball rolling by updating the list matrices of the document
and writing the current date to the "To Do on <dote>" label.

159
Chapter 4 To Do Tutorial

Subclass Example: Overriding Behavior (Selection NotifMatrix)


You can often achieve significant gains in object behavior by making a subclass
that adds only a small amount of code to its superclass. Such is the case with the
subclass you'll create in this section: SelectionNotifMatrix.

The need for this class is this: An instance of NSMatrix is a control and thus can
send action messages to its cell's targets; but when it contains
NSTextFieldCells, action messages are sent only when users press the Return
key in a cell. You want the inspector to synchronize its displays when the user
selects a new item by clicking a text field. To do this, you will override the
method in NSMatrix that is invoked when users click the matrix; in your
implcmc~tation, you'll invoke the superclass method, detect the selected row,
and then post a notification to interested observers.

Create template source-code #import <AppKit/AppKit.h>


files and add to the project.
extern NSString *SelectionlnMatrixNotification /* 1 */
Choose File ~ New In Project. @IS e l ec tionlnMatrixNotification";
In the New File In ToDo panel,
selectthe Class suitcase, turn on @interface SelectionNotifMatrix : NSMatrix
the Create header switch, and
type "SelectionNotifMatrix" after
Name.
- (void)mouseDown: (NSEvent *)theEvent; /* 2 */
2 Add declarations to the header
file. @end

1. Declares a string constant identifying the notification that will be posted.

2. Declares mouseDown:, the method implemented by the superclass that


SelectionN otifMatrix overrides.

3 Override mouseDown: - (void)mouseDown: (NSEvent *)theEvent

In SelectionNotifMatrix.m,
int row;
implement mouseD own: as
[super mouseDown:theEvent]; /* 1 */
shown here.

row = [self selectedRow]; /* 2 */


if (row ! = -1) {
[[NSNotificationCenter defaultCenter]
postNotificationName:@"SelectionlnMatrixNotification"
object:self userlnfo: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithlnt:row], @"Itemlndex", nil]];

160
Subclass Example: Overriding Behavior (SelectionNotifMatrix)

This override of mouseDown: does the following:

1. Invokes NSMatrix's implementation of mouseDown: to allow the normal


processing of this event.

2. Gets the row of the cell clicked and, if it's a valid row, creates a userlnfo
dictionary containing the clicked row, and posts the
SelectionInMatrixNotification.

Now that you've created the SelectionNotifMatrix class, you must re-assign the
class membership of the object in the interface. You can do this easily in
Interface Builder.

4 Replace the class of the matrix SelectionUotifMatrix Inspector X


object.
The Custom Classes browser lists the original
class of the selected object and all compatible
In Interface Builder: custom subclasses.

Open ToDoDoc.nib. CalendarMatrix


t:l§M~ri(L~__ ,_~_ _3t
Select the matrix of editable text ~~~I ~S!i9D!'l.9!~~_~t!i~. ,-.~--,~---~J;
cells.
Open the inspector and choose
Custom Class from the pop-up
menu.
Select SelectionNotifMatrix in
the browser of compatible
classes.

161
Chapter 4 To Do Tutorial

Events and the Event Cycle;

You can depictthe interaction between a user and an OpenStep codes identifying characters and keys, and various other kinds of
application as a cyclical process, with the Window Server playing information.
an intermediary role (see illustration below). This cycle-the
event cycle-usually starts at launch time when the application An NSEvent also divulges the type of event it represents. There
(which includes all the OpenStep frameworks it's linked to) sends are many event types (NSEventType); theyfall into five categories:
a stream of PostScript code to the Window Server to have it draw
the application interface. • Keyboard events Generated when a key is pressed down, a
pressed key is released, or a modifier key changes. Of these,
Then the application begins its main event loop and begins
key-down events are the most useful. When you handle a key-
accepting inputfrom the user (see facing page). When users click
down event, you often determine the character or characters
or drag the mouse or type on the keyboard, the Window Server
associated with the event by sending the NSEvent a
detects these actions and processes them, passing them to the
characters message.
application as events. Often the application, in response to these
events, returns another stream of PostScript code to the Window
• Mouse events Mouse events are generated by changes in
Server to have it redraw the interface.
the state of the mouse buttons (that is, down and up) for both
In addition to events, applications can respond to other kinds of left and right mouse buttons and during mouse dragging.
input, particularly timers, data received at a port, and data waiting Events are also generated when the mouse simply moves,
at a file descriptor. But events are the most important kind of input. without any button pressed.

• Tracking-rectangle events If the application has asked the


Events
window system to set a tracking rectangle in a window, the
The Window Server treats each user action as an event; it window system creates mouse-entered and mouse-exit
associates the event with a window and reports it to the events when the cursor enters the rectangle or leaves it.
application that created the window. Events are objects:
instances of NSEvent composed from information derived from • Periodic events A periodic event notifies an application that
the user action. a certain time interval has elapsed. An application can request
that periodic events be placed in its event queue at a certain
All event methods defined in NSResponder (such as mouseD own: frequency. They are usually used during atracking loop. (These
and keyDown:) take an NSEvent as their argument. You can query events aren't passed to an NSWindow.)
an NSEvent to discover its window, the location of the event
within the window, and the time the event occurred (relative to • Cursor-update events An cursor-update event is generated
system start-up). You can also find out which (if any) modifier keys when the cursor has crossed the boundary of a predefined
were pressed (such as Command, Alternate, and Controll, the rectangular area.

Events

Other
Applications
Window
Server
User

Monitored Port
or File
PostScript
Code
Entries

162
Subclass Example: Overriding Behavior (SelectionNotifMatrixl

The Event Queue and Event Dispatching First Responder and the Responder Chain

When an application starts up, the NSApplication object (NSAppl Each NSWindow in an application keeps track ofthe object in its
starts the main event loop and begins receiving events from the view hierarchy that has first responderstatus. This is the NSView
Window Server (see page 1161. As NSEvents arrive, they're put in that currently receives keyboard events for the window. By
the event queue in the order they're received. On each cycle of default, an NSWindow is its own first responder, but any NSView
the loop, NSApp gets the topmost event, analyzes it, and sends an within the window can become first responder when the user
event message to the appropriate object. (Event messages are clicks it with the mouse.
defined by NSResponder and correspond to particular events.l
When NSApp finishes processing the event, it gets the next event, You can also set the first responder programmatically with the
and repeats the process again and again until the application NSWindow's makeFirstResponder: method. Moreover, the first-
terminates. responder object can be a target of an action message sent by an
NSControl, such as a button or a matrix. Programmatically, you do
The objectthat is "appropriate" for an event depends on the type this by sending setTarget: to the NSControl (or its cellI with an
of event. NSApp sends most event messages to the NSWindow in argument of nil. You can do the same thing in Interface Builder by
which the user action occurred. If the event is a keyboard or making a target/action connection between the NSControl and
mouse event, the NSWindow forwards the message to one ofthe the First Responder icon in the Instances display of the nib file
objects in its view hierarchy: the NSView within which the mouse window.
was clicked or the key was pressed. If the NSView can respond
to the event-that is, it accepts first responder status and defines Recall that all NSViews of the application, as well as all
an NSResponder method corresponding to the event message- NSWindows and the application object itself, inheritfrom
it handles the event. NSResponder, which defines the default message-handling
behavior: events are passed up the responder chain. Many
Application Kit objects, of course, override this behavior, so
events are passed up the chain until they reach an objectthat
does respond.

The series of next responders in the responder chain is


determined by the interrelationships between the application's
NSView, NSWindow, and NSApplication objects (see page 1491.
For an NSView, the next responder is usually its superview; the
Ifthe NSView cannot handle an event, itforwards the message to
content view's next responder is the NSWindow. From there, the
the next responder in the responder chain (see belowl. It travels event is passed to the NSApplication object.
up the responder chain until an object handles it.
For action messages sent to the first responder, the trail back
NSWindow handles some events itself, and doesn'tforward them through possible respondents is even more detailed. The
to an NSView, such as window-moved, window-resized, and messages are first passed up the responder chain to the
window-exposed events. (Since these are handled by NSWindow NSWindow and then to the NSWindow's delegate. Then, if the
itself, they are not defined in NSResponder.l NSApp also previous sequence occurred in the key window the same path is
processes a few kinds of events itself; these include cursor- followed for the main window. Then the NSApplication object
update, and application-activate and -deactivate events. tries to respond, and failing that, it goes to NSApp's delegate.

163
Chapter 4 To Do Tutorial

Creating and Managing an Inspector (ToDolnspector)


An inspector is a panel of fields and controls that enable users to examine and
set an object's attributes. Because objects often have many attributes and
because you want to make it easy for users to set those attributes, inspectors
usually have more than one display; users typically access these multiple
displays using a pop-up list.

The ToDo application has an inspector panel that allows users to inspect and set
the attributes of the currently selected ToDoItem. The inspector panel has its
own controller: ToDolnspector. While showing you how to create the inspector
panel and ToDolnspector, this section focuses on four things:

• Managing displays according to user selections


• Getting the current ToDoItem
• Updating the currently selected display
• Updating the current ToDoltem as users make changes to it

In Interface Builder

Create a new nib file named


~-:"-"'-!+-- The text fields after the labels should have a light
ToDolnspector.nib and add it to gray background and should not be editable. The
the ToDD project. lower of these fields should be large enough to

2 Create the inspector panel. 1~~ti~~i~i~- hold the text of an item.

Drag a panel object from the


fI Double-click to display the three default cel/s
(Item 1, Item2, and Item3). Then, for each cel/,
Windows palette. double-click its title to select it and type the new
title. Assign tags (0 to 2) to the cells.
Make the title ofthe panel
"Inspector."
Turn off the title attribute and resize the box object
Resize the panel, using the so it fits just inside the lower part of the panel.
example at right as a guide. To provide a guide for resizing, this example
shows the box having a border; turn the border
Put labels and fields on the panel off after resizing.
and set their attributes (as
shown).
Put a pop-up button on the panel
and set cell titles (as shown). BeforeyouGoOn------------------------------------------
Assign tags to the pop-up button You might be wondering about the empty box object in the lower part of the
cells. panel. This box by itself may not seem a promising thing for displaying object
Create a separator line just below attributes, but it is critical to the workings of the inspector panel. A box that you
the pop-up button. drag from the Views palette contains one subview, called the content view.
Put an empty box object in the NSBox's content view fits entirely within the bounds of the box. NSBox
lower part of the panel. provides methods for obtaining and changing the content view of boxes. You'll
use these methods to change what the inspector panel displays.

164
Creating and Managing an Inspector (ToDolnspector)

3 Create an off-screen panel


holding the inspector's displays.

Drag a panel object from the


Windows palette. JDTas~ Completed Time: cr=:i• r'AM GPM
.- When to Reschedule - . - '--.::.--_ When to Notify ~
Resize the panel, using the ;1£1 Don' reschedule . \£1 Do notnotlfy
example at right as a guide. IU Next day :B 15 minutes before

Put the labels, text fields, scroll


!t:JWeek from now
fljMonlh from now
• ItJ
1.:.1
1 hour before
1 day before
view, and switch and radio-button :t]speCifiC date IIJolher ~f hours
matrices on the panel shown in
the example at right. L__ . ____ .___:. mmlyy/dd
Make the When to Reschedule • •
and When to Notify groupings Turn off the border attributes of each outer box.
(boxes).
Make three other groupings for
the three displays: Notes, Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Reschedule, and Notification.
You probably now see where the inspector panel gets its displays and how it puts
Resize the resulting boxes to the
same dimensions as the
them in place. \Vhen the inspector panel is first opened (and ToDolnspector.nib is
"dummy" view in the inspector loaded) the inspector controller, ToDolnspector, replaces the content view of
panel. the inspector's empty box (dummyView) with the content view of the Notification
box in the off-screen panel. Thereafter, every time the user chooses a new pop-
up button in the inspector panel, ToDolnspector replaces the currently
displayed content view with the content view of the associated off-screen box.

L~~~~t:::d--f-- When users choose a new display,


ToDolnspector replaces the current
content view of dummyView with
the appropriate view on the off-
screen window of inspector views.
dummyView - - ! - -

165
Chapter 4 To Do Tutorial

4 Define the ToDolnspector class. Outlet Connection From ToDolnspector To ...

dummyView The empty box object in the inspector panel


Create a subclass of NSObject
and name it "ToDolnspector."
inspectorViews The title bar ofthe off-screen panel
Add the outlets and actions in the
tables at right to the new class. notesView The box in the off-screen panel containing the scroll view

Instantiate ToDolnspector. notiNiew The box in the off-screen panel containing the fields and controls
Connect the ToDolnspector related to notification of impending items
object to its outlets and as the
target of action messages (see reschedView The box in the off-screen panel containing the fields and controls
tables at right). related to rescheduling items

Connect ToDolnspector and the inspPopUp The pop-up button on the inspector panel
inspector panel via the panel's
delegate outlet. inspDate The uneditable text field next to the "Date" label
Close both panels. inspltem The uneditable text field next to the "Item" label
Save ToDolnspector.nib.
inspNotifHour The first field after the "lime" label
Create source-code files for
ToDolnspector and add them to inspNotifMinute The second field after the "lime" label
the project.
inspNotifAMPM The matrix holding the "AM" and "PM" radio buttons

inspNotifOtherHours The text field in the "When to Notify" box

inspNotifSwitchMatrix The matrix of switches in the "When to Notify" box

inspSchedComplete The "Task Completed" switch

inspSchedDate The text field in the "When to Reschedule" box

inspSchedMatrix The matrix of switches in the "When to Reschedule" box

inspNotes The text object inside the scroll view

Action Connection To ToDolnspector From ...

newlnspectorView: The pop-up button on the inspector panel

switchChecked: The matrix of switches inthe "When to Notify" box, the AM-PM matrix,
the "Task Completed" switch, and the matrix of switches in the "When
to Reschedule" switches.

166
Creating and Managing an Inspector (ToDolnspector)

In Project Builder @interface ToDolnspector : NSObject

5 Add declarations to ToDoltem *currentltem;


ToDolnspector.h. /* */

Open ToDolnspector.h. /* */
Type the declarations shown at - (void)setCurrentltem: (ToDoltem *)newltem;
right (ellipses indicate existing - (ToDoltem *)currentltem;
declarations). - (void)updatelnspector: (ToDoItem *)item;
@end
Import ToDoltem.h and
ToDoDoc.h.
The ToDoInspector class has a utility function for clearing switches set in a
matrix and defines constants for the tags assigned to the pop-up buttons.

Open ToDolnspector.m. static void clearButtonMatrix(id matrix) ;


Forward-declare enum { notifTag = 0, reschedTag, notesTag };
clearButtonMatrixO at the
beginning of the file.
Using tags to identify cells rather than cell titles is a better localization strategy.
Define enum constants for the
pop-up button tags.
ToDoInspector has two accessor methods, one that gives out the current item
and one that sets the current item.

6 Implement the accessor methods - (void)setCurrentltem: (ToDoltem *)newltem


for the class.
if (currentltem) [currentItem autorelease] ;
Implement currentltem to return if (newltem)
the instance variables it names. currentltem [newltem retain]; /* 1 */
Implement setCurrentltem: as else
shown at right. currentltem nil;
[self updatelnspector:currentltem]; /* 2 */

This implementation of a "set" accessor method probably seems familiar to


you-except for a couple of things:

1. Instead of copying the new value, this implementation retains it. By retaining, it
shares the current ToDoltem with the document controller (ToDoDoc) that has
sent the setCurrentltem: message, enabling both objects to update the same
ToDoltem simultaneously.

Note: Later in this section, you'll invoke ToDoInspector's setCurrentltem:


method in various places in ToDoDoc.m.

2. Updates the current display of the inspector with the appropriate values of
the new ToDoltem.

167
Chapter 4 To Do Tutorial

7 Switch inspector displays based - (void)newlnspectorView: (id) sender


on user selections.
NSBox *newview=nil;
Implement newlnspectorView:. NSView *cView = [[inspPopUp window] contentView]; /* 1 */
int selected = [[inspPopUp selectedltem] tag];
switch(selected) { /* 2 */
case notifTag:
newView = notifView;
break;
case reschedTag:
newView = reschedView;
break;
case notesTag:
newView = notesView;

if ([[cView subviews] containsObject:newView]) return; /* 3 */


[dummyView setContentView:newView]; /* 4 */
if (newView == notifView) [inspNotifHour selectText:self];
if (newView == notesView) [inspNotes
setSelectedRange:NSMakeRange(O,O)];
[self updatelnspector:currentltem] ; /* 5 */
[cView display] ;

This method switches the current inspector display according to the pop-up
button users select; it does this switching by replacing the dummyView's content
view. Toward this end, the method:

1. Gets the panel's content view and the tag of the selected pop-up button.

2. Assigns to the newView local variable the off-screen box object corresponding
to the tag of the selected pop-up button.

3. Returns if the selected display is already on the inspector panel. The subviews
message returns an array of all su bviews of the inspector panel's control view,
and the containsObject: message determines if the chosen display is among
these subviews.

4. Replaces the content view of the inspector panel's dummyView. In awakeFromNib


(which you'll soon implement) you'll retain each original content view. The
setContentView: method replaces the new view and releases the old one;
because it's been retained, the replaced view doesn't disappear.

5. Updates the inspector with the current item; this item hasn't changed, but the
display is new and so the set of instance variables to be displayed is different.
The display message forces a re-draw of the inspector panel's views.

168
Creating and Managing an Inspector (ToDolnspector)

8 Update the current inspector - (void)updatelnspector: (ToDoltem *)newltem


display with the new ToDoltem.
int minute=O, hour=O, selected=O;
Write the first part of the selected = [[inspPopUp selectedltem] tag]; /* 1 */
update Inspector: method shown [[inspPopUp window] orderFront:self];
at right. if (newltem && [newltem isKindOfClass: [ToDoltem class]]) /* 2 */
T
[inspltem setStringValue: [newltem itemName]];
[inspDate setStringValue: [[newltem day]
descriptionWithCalendarFormat:@"%a, %b %d %Y"
timeZone: [NSTimeZone localTimeZone] locale:nil]];
switch(selected) {
case notifTag: { /* 3 */
long notifSecs, dueSecs = [newltem secsUntilDue] ;
BaaL ampm = ConvertSecondsToTime(dueSecs, &hour, &minute);
[[inspNotifAMPM cellAtRow:O column:O] setState: lampm];
[[inspNotifAMPM cellAtRow:O column:1] setState:ampm];
[inspNotifHour setlntValue:hour];
[inspNotifMinute setlntValue:minute];
notifSecs = dueSecs - [newltem secsUntilNotif];
if (notifSecs == dueSecs) notifSecs = 0;
clearButtonMatrix(inspNotifSwitchMatrix);
switch(notifSecs) { /* 4 */
case 0:
[[inspNotifSwitchMatrix cellAtRow:O column: 0]
setState:YES];
break;
case (hrlnSecs/4):
[[inspNotifSwitchMatrix cellAtRow:1 column:O]
setState:YES];
break;
case (hrlnSecs):
[[inspNotifSwitchMatrix cellAtRow:2 column:O]
setState:YES];
break;
case (daylnSecs):
[[inspNotifSwitchMatrix cellAtRow:3 column:O]
setState:YES];
break;
default: /* Other */
[[inspNotifSwitchMatrix cellAtRow:4 column:O]
setState:YES];
[inspNotifOtherHours setlntValue:
((dueSecs-notifSecs)/hrlnSecs)];
break;

break;

case reschedTag:
break;

169
Chapter 4 To Do Tutorial

The updatelnspector: method is a long one, so we'll approach it in stages. This first part updat(
the common data elements (item name and date) and, if the selected display is for notification
updates that display.

1. Gets the tag assigned to the selected pop-up button.

2. Tests the argument newltem to see if it is a ToDoItem. This test is important


because if the argument is nil, the method clears the display of existing data
(next example).

If newltem is a ToDoltem, updatelnspector: first updates the Item and Date


fields.

3. If the tag of the selected pop-up button is notifTag, updates the associated
inspector display. This task starts by converting the due time from seconds to
hour, minute, and PM boolean values and then setting the appropriate fields
and button matrix with these values.

4. Sets the appropriate switch in the "When to Notify" matrix. It starts with the
difference (in seconds) between the time the item is due and the time the
item notification is sent. It calls clearButtonMatrixO to turn all switches off and
then, in a switch statement, sets the switch corresponding to the difference in
value between seconds from midnight before due and before notification.

Before You Go On - - - - - - - - - - - - - - - - - - - - -
Update the Notes display: Add code to update the inspector's Notes display from the
information in the ToDoltem passed into updatelnspector:. (Check the
documentation on NSText to see what method is suitable for this.) The selected
pop-up button must have notesTag assigned to it. Also put the cursor at the start
of the text object by selecting a "null" range.

Note that tutorial omits the rescheduling logic of the ToDo application,
including the code in this method that would update the "Reschedule" display.
Rescheduling of ToDoltems is reserved as an optional exercise for you at the
end of this tutorial.

170
Creating and Managing an Inspector IToDolnspector)

Finish the implementation of


update Inspector: by resetting all else if (!newltem) { /* newltem is nil */
displays if the argument is nil. [inspltem setStringValue:@""];
[inspDate setStringValue:@""];
[inspNotifHour setStringValue:@""];
[inspNotifMinute setStringValue:@""];
[[inspNotifAMPM cellAtRow:O column:O] setState:YES];
[[inspNotifAMPM cellAtRow:O column:1] setState:NO];
clearButtonMatrix(inspNotifSwitchMatrix);
[[inspNotifSwitchMatrix cellAtRow:O column:O]
setState:YES];
[inspNotifOtherHours setStringValue:@""];
[inspNotes setString:@""];

As you've most likely noticed, the updatelnspector: method calls the function
clearButtonMatrixO, which resets the states of all button cells in a switch matrix to
NO. This function has a counterpart, indexOfSetCellO, that returns the index of the
currently selected switch.

Implementthe void clearButtonMatrix(id matrix)


clearButtonMatrixO utility
function. int i, cnt=[[matrix cells] count];
for(i=O; i<cnt; i++)
[[matrix cellAtRow:i column: 0] setState:NO];

The cells message returns the cells of the matrix as an array; the count message
determines the number of cells.

171
Chapter 4 To Do Tutorial

9 Update the current item with new - (void)switchChecked: (id) sender


values entered in the inspector.
long tmpSecs=O;
Implement switchChecked: to int idx= 0;
apply changes made through id doc = [[NSApp mainWindow) delegate);
switches and other controls. if (sender == inspNotifAMPM) { 1* 1 *1
if ([inspNotifHour intValue)) {
tmpSecs = ConvertTimeToSeconds([inspNotifHour intValue),
[inspNotifMinute intValue),
[[sender cellAtRow:O column:1] state]);
[currentltem setSecsUntilDue:tmpSecs);
[[NSApp'mainWindow] setDocumentEdited:YES];
[doc updateMatrix];

else if (sender == inspNotifSwitchMatrix) 1* 2 *1


idx = [inspNotifswitchMatrix selectedRow);
tmpSecs = [currentltem secsUntilDue]i
switch (idx) {
case 0:
[currentltem setSecsUntilNotif:O);
break;
case 1:
[currentltem setSecsUntilNotif:tmpSecs-(hrlnSecs/4)];
break;
case 2:
[currentltem setSecsUntilNotif:tmpSecs-hrlnSecs] ;
break;
case 3:
[currentltem setSecsUntilNotif:tmpSecs-daylnSecs);
break;
case 4: II Other
[currentltem setSecsUntilNotif: ([inspNotifOtherHours intValu
* hrlnSecs)];
break;
default:
NSLog(@"Error in selectedRow");
break;

[[NSApp mainWindow] setDocumentEdited:YES];


else if (sender == inspSchedComplete) { 1* 3 *1
[currentltem setltemStatus:complete];
[[NSApp mainWindow] setDocumentEdited:YES];
[doc updateMatrix];

else if (sender== inspSchedMatrix) 1* 4 *1

172
Creating and Managing an Inspector (ToDolnspector)

When users click a switch button on any inspector display, or when they click
one of the AM-PM radio buttons, the switchChecked: method is invoked. This
method works by evaluating the sender argument: the sending object.

1. If sender is the radio-button matrix (AM-PM), gets the new time due by calling the
utility function ConvertTimeToSeconds(j, sets the current item to have this new value,
marks the document as edited, and then sends updateMatrix to the document
controller to have it display this new time.

2. If sender is the "When to Notify" matrix, gets the index of the selected cell
and the seconds until the item is due. It evaluates the first value in a switch
statement and uses the second value to set the current item's new secsUntilNotif
value. It also sets the window to indicate an edited document.

3. If sender is the "Task Completed" switch, sets the status of the current item
to "complete," sets the window to indicate an edited document, and has the
document controller update its matrices.

4. As before, implementation of this rescheduling block is left as a final exercise.

Since text fields are controls that send target/action messages, you could also
have switchChecked: respond when data is entered in the fields. However, users
might not press Return in a text field so you can't assume the action message will
be sent. Therefore, it's better to rely upon delegation messages.

173
Chapter 4 To Do Tutorial

Update the current item if - (void)textDidEndEditing: (NSNotification *)notif /* 1 */


changes are made to the
contents of text fields or the text if ([notif object] == inspNotes)
object ofthe inspector panel. [currentltem setNotes: [inspNotes string]];
[[NSApp mainWindow] setDocumentEdited:YES];

- (void)controlTextDidEndEditing: (NSNotification *)notif

long tmpSecs=O;
if ([notif object] == inspNotifHour I I /* 2 */
[notif object] == inspNotifMinute)
tmpSecs = ConvertTimeToSeconds([inspNotifHour intValue],
[inspNotifMinute intValue],
[[inspNotifAMPM cellAtRow:O column: 1] state]);
[currentltem setSecsUntilDue:tmpSecs];
[[[NSApp mainWindow] delegate] updateMatrix];
[[NSApp mainWindow] setDocumentEdited:YES];
else if ([notif object] == inspNotifOtherHours) /* 3 */
if ([inspNotifSwitchMatrix selectedRow] == 4)
[currentltem setSecsUntilNotif: ([inspNotifOtherHours
intValue] * hrlnSecs)];
[[NSApp mainWindow] setDocumentEdited:YES];

else if ([notif object] == inspSchedDate) { /* 4 */

The textDidEndEditing: and controlTextDidEndEditing: notification messages are sent to


the delegate (and all other observers) when the cursor leaves a text object or text
field (respectively) after editing has occurred.

1. After editing takes place in the "Notes" text object, this method is invoked, and it
responds by resetting the notes instance variable of the ToDoItem with the
contents of the text object.

2. If the object behind the notification is the hour or minute field of the
"Notifications" display, controlTextDidEndEditing: computes the new due time,
sets the current item to have this new value, and then sends update Matrix to the
document controller to have it display this new time. (This code is almost the
same as that for the AM-PM matrix in the switch Checked: method.)

174
Creating and Managing an Inspector lToDolnspectorl

3. If the object behind the notification is the "Other... hours" text field in the
"When to Notify" box, the method verifies that the "Other" switch is
checked and, if it is, sets the ToDoltem with the new value.

4. Here is another empty rescheduling block of code that you can fill out in a
later exercise.

Now it's time to address two related problems in synchronizing displays of data.
The first is the requirement for the inspector to display the ToDoItem currently
selected in the document. In ToDoDoc.m write code that communicates this object
to ToDoInspector through notification.

10 Synchronize the items displayed id curltem;


in the document with the
/* */
inspector.
if (curltem = [currentltems objectAtlndex:row])
if (! [curltem isKindOfClass: [ToDoltem class]])
Open ToDoDoc.m.
curltem = nil;
Import ToDolnspector.h. [[NSNotificationCenter defaultCenter] postNotificationName:
Add the code at rightto the end of ToDoltemChangedNotification object:curltem
the controlTextDidEndEditing: userlnfo:nil] ;
method.
Post identical notifications in the
otherToDoDoc methods listed in The controlTextDidEndEditing: method is where ToDoItems are added, removed, or
the table below.
modified, so it's especially important here to let ToDoInspector know when
In ToDoDoc.h declare as extern there's a change in the current ToDoltem. The fragment of code above gets the
the string constant
ToDoltemChangedNotification.
current item (row holds the index of the selected row); if the returned object isn't
a ToDoltem, curltem is set to nil. Then the code posts a
In ToDoDoc.m, declare and
initialize the same constant.
ToDoltemChangedNotification, passing in curltem as the object related to the
notification.

Post an identical notification in other ToDoDoc methods that select a


ToDoItem or that require the removal of the currently displayed ToDoltem
from the inspector's display. In methods of this second type, there is no need to
get the current item because the object argument of the notification should
always be nil. This argument is eventually passed to ToDoInspector's
updatelnspector:, to which nil means "clear the display."

Other Methods Posting Notifications to ToDolnspector object: Argument

calendarMatrix:didChangeToDate: nil

calendarMatrix:didChangeToMonth:year: nil

windowShouldClose: Ifor both "Save" and "Close"l nil

selectionlnMatrix: current item or nil

175
Chapter 4 To Do Tutorial

The second data-synchronization problem involves the selection and display of


11 Open the inspector panel when initial values in the document and the inspector when the user:
users choose the Inspector
command. • Opens the inspector
Implement ToDoController's • Opens a document
showlnspector: method to load • Selects a new day from the calendar
ToDolnspector.nib and make the
inspector panel the key window. You must return to ToDoDoc.m to write code that implements this behavior.
12 Update the document and
inspector to display initial - (void)selectltem: (int) item
values.
id thisltem = [currentltems objectAtlndex:iteml i
In ToDoDoc.m, implement [itemMatrix selectCellAtRow:item column:Oli
selectltem:. if (thisltem) {
if (! [thisltem isKindOfClass: [ToDoltem classll) thisltem nil;
Invoke this method at the
[[NSNotificationCenter defaultCenterl
appropriate places (see below).
postNotificationName:ToDoltemChangedNotification
object:thisltem
userlnfo:nill;

The selectltem: method selects the text field identified in the argument and posts
a notification to the inspector with the associated ToDoltem as argument (or nil
if the text field is empty). Next, invoke selectltem: in these methods:

Method Comment

calendarMatrix:didChangeToDate: Make it the final message, with an argument of 0 (ToDoDoc.m).

The use of notifications to open Doc: Invoke after opening a document, with an argument of 0
communicate changes in one (ToDoController.m)
object to another object in an
application is a good design showl nspector: Invoke afer opening the inspector panel, passing in the index of the
strategy because it removes the selected row in the document. (ToDoController.m). Hint: Get the
current document by querying for the delegate of the main window,
need for the objects to have
then obtain the selected row from this object.
specific knowledge of each other.
It also makes the application
more extensible, because any
number of objects can also
become observers of the changes. Before You Go On - - - - - - - - - - - - - - - - - - - - -
However, there is a way for
ToDoDoc to locate Make ToDolnspector respond to the notification. Declare a notification method
ToDolnspector reliably using the named currentltemChanged: and implement it to set the current item with the object
various relationships established
value of the notification. Then, in init or awakeFromNib, add ToDolnspector as an
within the program framework.
See page 189 to see how this is observer of the ToDoltemChangedNotification, identifying currentltemChanged:
done. as the method to be invoked.

176
Creating and Managing an Inspector (ToDolnspector)

13 Format and validate the contents - (void)awakeFromNib


of inspector text fields.
NSDateFormatter *dateFmt;
In ToOo/nspector.m:
Implement awakeFromNib as [[inspNotifHour cell] setEntryType:NSPositivelntType]; /* 1 */
shown at right. [[inspNotifMinute cell] setEntryType:NSPositivelntType];
dateFmt = [[NSDateFormatter alloc] /* 2 */
Implement control:isValidObject:
initWithDateFormat:@"%m/%d/%y" allowNaturalLanguage:YES] i
to ensure that users can only
[[inspSchedDate cell] setFormatter:dateFmt];
enter the proper range of
[dateFmt release];
numbers in the hour and minute
[inspPopUp selectltemAtlndex:O]; /* 3 */
text fields.
[inspNotes setDelegate:self];

[[notifView contentView] removeFromSuperview]; /* 4 */


notifView = [[notifView contentView] retain];
[[reschedView contentView] removeFromSuperview];
reschedView = [[reschedView contentView] retain];
[[notesView contentView] removeFromSuperview]i
notesView = [[notesView contentView] retain);
[inspectorViews release);
[self newlnspectorView:self];

ToDolnspector's awakeFromNib method sets up formatters for the inspector's


hour, minute, and date fields. It also performs some necessary "housekeeping"
tasks.

1. Sets the hour and minute fields to accept only positive integer values.

2. Creates a date formatter (an instance of NSDateFormatter) that accepts and


formats dates as (for example) "12/25/96." After associating the formatter with
the date text-field cell, it releases it (setFormatter: retains the formatter).

3. Makes the Notification display the start-up default, using the index of the
"Notification" cell rather than its title to improve localization. Then it sets self
to be the delegate of the text object.

4. Each of the three inspector displays in the off-screen panel (inspectorViews) is


the content view of an NSBox. This section of code extracts and retains each
of those content views, reassigning each to its original NSBox instance
variable in the process. This explicit retaining is necessary because, in
newlnspectorView:, each currrent content view is released when it's swapped
out. Once all content views are retained, the code releases the off-screen
window and invokes newlnspectorView: to put up the default display.

177
Chapter 4 To Do Tutorial

Drawirlo and Compositing

Besides responding to events, all objects that inherit from programmatically move, scale, and rotate the NSView by
NSView can render themselves on the screen. They do this reference to its frame (setFrameOrigin:, setFrameSize:, and so
rendering through image composition and PostScript drawing. on).
NSViews draw themselves as an indirect result of receiving the
display message (or a variant of display); this message is sent To draw efficiently, the NSView must have its frame rectangle
explicitly or through conditions that cause automatic display. The translated into its own coordinate system. This translated
display message leads to the invocation of an NSView's rectangle, suitable for drawing, is called the bounds. The bounds
drawRect: method and the drawRect: methods of all subviews of rectangle usually specifies exactly the same area as the frame
that NSView. The drawRect: method should contain all code rectangle, but it specifies that area in a different coordinate
needed to redraw the NSView completely. system. In the default coordinate system, an NSView's bounds is
An NSView can be automatically displayed when: the same as its frame, except that the point locating the frame
becomes the origin of the bounds (x = 0.0, y = 0.0). The x- and y-
• Users scroll it (assuming it supports scrolling). axes of the default coordinate system run parallel to the sides of
• Users resize or expose the NSView's window. the frame so, for example, if you rotate the frame the default
coordinate system rotates with it.
• The window receives a display message or is automatically
updated. This relationship between frame and bounds has several
• For some Application Kit objects, when an attribute changes. implications important in drawing and compositing.

An NSView represents a context within which PostScript drawing • Each NSView's coordinate system is a transformation of its
can take place. This context has three components:
superview's.
• A rectangular frame within a window to which drawing is
clipped. • Drawing instructions don't have to account for an NSView's
location on the screen or its orientation.
• A coordinate system
• The current PostScript graphics state • Changes in a superview's coordinate system are propagated to
its subviews.
Frame and Bounds
NSView allows you to flip coordinate systems (so the positive y-
An NSView's frame specifies the location and dimensions of the
axis runs downward) and to otherwise alter coordinate systems.
NSView in terms of the coordinate system of the NSView's
superview. It is a rectangle that encloses the NSView. You can
~t:::.:/<··';:·:::·:::·:::·:::·:::·:::·:::··';:·:::·-':'·· Focusing
Frame ,:otated within its l~..····· . . . ..

'" '~"W <:.~ : li.,..,.,. ,. ::::::::>


Before an NSView can draw it must lock focus to ensure that it
draws in the correct window, place, and coordinate system. It
locks focus by invoking NSView's lockFocus method. Focusing
modifies the PostScript graphics state by:

• Making the NSView's window the current device


Flipped coordinate
system 1: ........~....... 0.0, 0,0 • Creating a clipping path around the NSView's frame
:1
~1
h Bounds origin • Making the PostScript coordinate system match the NSView's
~I
~ .. .. .. ..(0.0,..':':'0,0)
::' ':":' ':":' ':":' ..':':'.. ..::'..::'..::'.. .. .. .. ....
':":' "::' ':":' ::' ':":'
coordinate system
Location of frame
within its superview
(200,300)
After drawing, the NSView should unlock focus (unlockFocus).

178
Creating and Managing an Inspector !ToDolnspector)

PostScript Drawing NSViews can simply display an image within their frame. You
usually composite an image using NSlmage's
In OpenStep, NSViews draw themselves by sending binary- compositeToPoint:operation: (or a related method).
encoded PostScript code to the Window Server. The Application
Kit and the Display PostScript frameworks provide a number of C- NSlmage allows you to copy images into your user interface. It
language functions that send PostScript code to perform common uses various subclasses of NSlmageRep to store the multiple
drawing tasks. You can use these functions in combinations to representations of the same image-color, grayscale, TIFF, EPS,
accomplish fairly elaborate drawing. and so on-and chosing the representation appropriate for a
given type or display. NSlmage can read image data from a
The Application Kit has functions and constants, declared in
bundle (including the application's main bundle), from the
NSGraphics.h, for (among other things):
pasteboard, or from an NSData object.
• Drawing, filling, highlighting, clipping and erasing rectangles
Compositing allows you to do more than simply copy images.
• Drawing buttons, bezels, and bitmaps Compositing builds a new image by overlaying images that were
• Computing window depth and related display information previously drawn. It's like a photographer printing a picture from
two negatives, one placed on top of the other. Various
You also call OpenStep-compliant drawing routines defined in compositing operators (NSCompositingOperation, defined in
dpsOpenStep.h. These routines (such as DPSDoUserPath()) draw dpsOpenStep.h) determine how the source and destination
a specified path. In addition, you can call the functions declared images merge.
in psops.h. These functions correspond to single PostScript
operators, such as PSsetgrayO and PSfilIO.
Source Image Destination Image
You can also write and send your own custom PostScript code.
pswrap is a program (in /usrlbin) that converts PostScript code
into C-Ianguage functions that you can call within your
applications. It is an efficient way to send PostScript code to the
Window Server. The following pswrap function draws grid lines:
00
0
Operation Destination After
defineps DrawGrid(float width, height, every)
5 6 div setgray
o every width { Copy Source image overlays
o move to 0 height rlineto stroke
} for
o every height {

O
o exch move to width 0 rlineto stroke Source image wherever
} for Source it is opaque, and
endps Over destination image
elsewhere.
Compose the function in a file with a .psw extension and add itto
Destination image
the Other Source project "suitcase" in Project Builder. When you
next build your project, Project Builder runs the pswrap program,
generating an object file and a header file (matching the file name
of the .psw) file, and links these into the application. To use the
code, import the header file and call the function when you want
D
Out
l' r
es Ina Ion
O wherever it is opaque but
source image is
transparent, and
transparent elsewhere.

to do the drawing: You can achieve interesting effects with compositing when the
initial images are drawn with partially transparent paint.
DrawGrid(5.0, 5.0, 1.0);
(Transparency is specified by coverage, a PostScript indicator of
paint opacity.lln a typical compositing operation, paint that's
Compositing Images
partiallytransparentwon'tcompletely coverthe image it's placed
The other technique NSViews use to render their appearance is on top of; some of the other image will show through. The more
image compositing. By compositing (with the SOVER operator) transparent the paint is, the more of the other image you'll see.

179
Chapter 4 To Do Tutorial

If you want an object that draws itself differently than any other Handling Events
Application Kit object, or responds to events in a special way, you
should make a custom subclass of NSView. Your custom subclass In the next section, you'll make a subclass of NSButtonCell that
should complete at least the steps outlined below. uniquely responds to mouse clicks. The way custom NSViews
handle events is different. If you intend your custom NSView to
Note: If you make a custom subclass of any class that inherits from respond to user actions you must do a couple of things:
NSView, and you wantto do custom drawing or event handling, 4 Override acceptsFirstResponderto return YES ifthe NSView is
the basic procedure presented here still applies. to handle selections. (The default NSView behavior is to return
NO.)
Interface Builder 5 Override the desired NSResponder event methods
(mouseDown:, mouseD ragged:, keyDown:, etc.)
Define a subclass of NSView in Interface Builder. Then
generate header and implementation files. - (void)mouseDown: (NSEvent *)event
if (([event modifierFlags] &
2 Drag a CustomView object from the Views palette onto a NSControlKeyMask){
window and resize it. Then, with the CustomView object still
doSomething();
selected, choose the Custom Class display ofthe Inspector
panel and select the custom class. Connect any outlets and
actions.
You can query the NSEvent argument for the location of the user
action in the window, modifier keys pressed, character and key
codes, and other information.

Drawing

When you send display to an NSView, its drawRect: method and


each of its subview's drawRect: are invoked. This method is .
where an NSView renders its appearance.

6 Override drawRect:. The argument is usually the frame


rectangle in which drawing is to occur. This tells the Window
Server where the NSView's coordinate system is located. To
draw the NSView, you can do one or more of the following:

• Composite an NSlmage.

• Call Application Kit functions such as NSRectFiIIO and


NSFrameRect () (NSGraphics.h).
Initializing Instances
• Call Cfunctions that correspond to single PostScript
operations, such as PSsetgrayO and PSfilIO.
3 Override the designated initializer, initWithFrame: to return an
initialized instance of self. The argument of this method is the • Call custom drawing functions created with pswrap.
frame rectangle of the NSView, usually as set in Interface
Builder (see step 2). You mightwantto display the custom view See "A Short Guide to Drawing and Compositing" on page 179 for
at this point. more information on drawing techniques and requirements.

180
Subclass Example: Overriding and Adding Behavior (ToDoCell)

Subclass Example: Overriding and Adding Behavior (ToDoCell)


Buttons in the Application Kit are two-state controls. They have two-and only
two--states: 1 and 0 (often expressed as Boolean YES and NO, or ON and
OFF). For the To Do application, a three-state button is preferable. You want
the button to indicate, with an image, three possible states: notOone (no image),
done (an "X"), and deferred (a check mark). These states correspond to the
possible statues of a ToOoItem.

The ToOoCell class, which you will implement in this section, generates cells
that behave as three-state buttons. These buttons also display the time an item
is due.

Item status. --I~ 05:45 PM}-- Time item is due.

The superclass of ToOoCell is NSButtonCel1. In creating ToOoCell you will


add data and behavior to NSButtonCell, and you will override some existing
behavior.

Why Chose NSButtonCell as Superclass?

ToDoCeWs superclass is NSButtonCel1. This NSButtonCell is ToDoCeWs superclass


choice prompts two questions: because button cells already have much of
the behavior you want By virtue of
• Why a button cell and notthe button itself? inheritance from NSActionCell, button cells
can hold target and action information.
• Why this particular superclass? Button cells also have the unique capability
to display an image and text simultaneously.
NSCell defines state as an instance variable, These are all aspects of behavior needed for
and thus all cells inherit it Cells instead of ToDoCe11.
controls hold state information for reasons of
efficiency-one control (a matrix) can When you think that you need a specialized
manage a collection of cells, each cell with subclass of an OpenStep class, you should
its own state setting. NSButton does provide first spend some time examining the header
methods for getting and setting state values, files and referen'ce documentation on not
but it accesses the state value of the cell only that class, but its superclasses and any
(usually NSButtonCell) that it contains. "sibling" classes.

181
Chapter 4 To Do Tutorial

Add header and implementation enum _ToDoButtonState {notDone=O, done, deferred} ToDoButtonState;
files to the project.
@interfaee ToDoCe11 NSButtonCel1
Chose New in Project from the
File menu. ToDoButtonState triState;
In the New File In ToDo panel, NSlmage *doneImage, *deferredImage;
select the Class suitcase, click NSDate *timeDue;
Create header, type "ToDoCeil"
after Name, and click OK. - (void)setTriState: (ToDoButtonState)newState;
- (ToDoButtonState) triState;
2 Complete ToDoCell.h. - (void)setTimeDue: (NSDate *)newTime;
- (NSDate *)timeDue;
Make the superclass @end
NSButtonCel1.
Add the instance-variable and
method declarations shown at The triState instance variable will be assigned ToDoButtonState constants as
right. values. The NSlmage variables hold the "X" and check mark images that
Add the enum constants for state represent statuses of completed and deferred (that is, rescheduled for the next
values (as shown). day). The timeDue instance variable carries the time the item is due as an
NSDate; for display, this object will be converted to a string.

3 Initialize the allocated ToDoCell - (id)init


instance (and deallocate it).
Nsstring *path;
Select ToDoCell.m in the project [super initTextCell:@""];
browser.
Implement init as shown at right. triState = notDone;
[self setType:NSToggleButton]; /* 1 */
Implement dealloc.
[self setImagePosition:NSImageLeft];
[self setBezeled:YES];
[self setFont: [NSFont userFontOfSize:12]];
[self setAlignment:NSRightTextAlignment];
/* 2 * /
path = [[NSBundle mainBundle] pathForImageResouree:@"X.tiff"];
done Image = [[NSImage alloe] initByRefereneingFile:path];
path = [[NSBundle mainBundle]
pathForImageResouree:@"eheekMark.tiff"] ;
deferredImage = [[NSImage alloe] initByRefereneingFile:path];

return self;

1. Sets some superc1ass (NSButtonCell) attributes, such as button type, image and
text position, font of text, and border.

2. Through NSBundle's pathForlmageResource:, gets the path name for the cell
images and creates and stores the images using the pathname.

182
Subclass Example: Overriding and Adding Behavior (ToDoeelll

4 Implementthe accessor methods - (void)setTriState: (ToDoButtonState)newState /* 1 */


related to state.
if (newState == deferred+l)
Write the methods that get and triState notDone;
set the triState instance variable. else
Override the superclass methods triState newState;
that get and set state. [self _setlmage:triState] i

- (ToDoButtonState)triState {return triState;}

- (void)setState: (int)val /* 2 */

- (int)state /* 3 */

if (triState == deferred)
return (int)done;
else
return (int)triState;

Accessing state information is a dual-path task in ToDoCell. It involves not only


setting and getting the new state instance variable, triState, but properly handling
the inherited instance variable by overriding the superclass accessor methods for
state.

1. If the new value for triState is one greater than the limit (deferred), reset it to zero
(notDone); otherwise, assign the value. The reason behind this logic is that (as
you'll soon learn) when users click a ToDoCell, setTriState: is invoked with an
argument one more than the current value. This way users can cycle through the
three states of ToDoCell.

2. Overrides setState: to be a null method. The reason for this override is that
NSCell intervenes when a button is clicked, resetting state to zero (NO).
This override nullifies that effect.

3. Overrides state to return a reasonable value to client objects that invoke this
accessor method.

183
Chapter 4 To Do Tutorial

5 Set the cell image. @interface ToDoCell (PrivateMethods)


- (void)_setlmage: (ToDoButtonState)aState; /* 1 */
Declare the private method @end
_setlmage:. /* ... * /
Implement the _setlmage: - (void)_setlmage: (ToDoButtonState) as tate
method.
switch(aState) { /* 2 */
case notDone:
[self setlmage:nil];
break;

case done:
[self setlmage:donelmage]i
break;

case deferred:
[self setlmage:deferredlmage];
break;

[(NSControl *) [self controlView] updateCell:self]; /* 3 */

This portion of code handles the display of the cell's image by doing the
following:

1. In a category of ToDoCell in ToDoCell.m, it declares the private method _setlmage:.


Private methods, which by convention begin with an underscore, are methods that
you don't want clients of your object to invoke. In this case, you don't want the
image to be set independently from the cell's triState value.

2. In a switch statement, evaluates the tri-state argument and sets the cell's
image appropriately (setlmage: is an NSButtonCell method).

3. Sends updateCell: to the control view of the cell's control (a matrix) to force a
re-draw of the cell.

184
Subclass Example: Overriding and Adding Behavior IloDoCell1

6 Track mouse clicks on a - (BOOL)startTrackingAt: (NSPoint)startPoint inView:


ToDoCell and reset state. (NSView *)controlView

Override two NSCell mouse- return YES;


tracking methods as shown in this
example.
- (void)stopTracking: (NSPoint)lastPoint at: (NSPoint)stopPoint
inView: (NSView *)controlView mouselsUp: (BOOL) flag

if (flag == YES) {
[self setTriState: ([self triState] +1) ] ;

When you create your own cell subclass, you might want to override some
methods that are intrinsic to the behavior of the cell. Mouse-tracking methods,
inherited from NSCell, are among these. You can override these methods to
incorporate specialized behavior when the mouse clicks the cell or drags over it.
ToDoCell overrides these methods to increment the value of triState.

• Overrides startTrackingAt:inView: to return YES, thus signalling to the control


that the ToDoCell will track the mouse.

• Overrides stopTracking:at:inView:mouselsUp: to evaluate flag and, if it's YES, to


increment the triState instance variable. (The setTriState: method "wraps" the
incremented value to zero (notDone) if it is greater than 2 (deferred».

7 Get and set the time due, - (void)setTimeDue: (NSDate *)newTime


displaying the time in the
process. if (timeDue)
[timeDue autorelease];
ImplementsetlimeDue: as shown if (newTime)
in this example. timeDue = [newTime copy];
Implement timeDue to return the [self setTitle: [timeDue descriptionWithCalendarFormat:
NSDate. @"%I:%M %p" timeZone: [NSTimeZone localTimeZone]
locale:nil]];

else
timeDue = nil;
[self setTitle:@"-->"];

The setlimeDue: method is similar to other "set" accessor methods, except that it
handles interpretation and display of the NSDate instance variable it stores. If
newlime is a valid object, it uses NSDate's
descriptionWithCalendarFormat:timeZone:locale: method to interpret and format the

185
Chapter 4 To Do Tutorial

date object, then displays the result with setTitle:. If newTime is nil, no due time has
been specified, and so the method sets the title to "-->".

You've now completed all code required for ToDoCell. However, you must now
"install" instances of this class in the To Do interface.

8 At launch time, create and install - (void)awakeFromNib


your custom cells in the matrix.
int i;
SelectToDoDoc.m in the project / * ... * I
browser. i = [[markMatrix cells] count];
Insert the code at right in while (i--) {
awakeFromNib. ToDoCe11 *aCell = [[ToDoCell alloc] init];
[aCell setTarget:self];
[aCell setAction:@selector(itemChecked:)];
[markMatrix putCell:aCell atRow:i column:O]i
[aCell release];

This block of code substitutes a ToDoCell for each cell in the left matrix
(markMatrix) you created for the To Do interface. It creates a ToDoCell, sets its
target and action message, then inserts it into the markMatrix by invoking
NSMatrix's putCe":atRow:column: method.

Finally, you must implement the action message sent when the matrix of
ToDoCells is clicked. (This response to mouse-down is for objects external to
To DoC ell, while the mouse-tracking response sets state internally.)

9 Respond to mouse clicks on the - (void)itemChecked:sender


matrix of ToDoCe"'s.
int row = [sender selectedRow];
In ToDoDoc.m, implement ToDoCe11 *cell = [sender cellAtRow:row column:Ol;
item Checked:. if (cell && [currentltems countl) {
id item = [currentltems objectAtlndex:row];
if (item && [item isKindOfClass: [ToDoltem class]])
[item setltemStatus: [cell triState]];
[[sender window] setDocumentEdited:YES];

This method gets the ToDoCell that was clicked and the object in the
corresponding text field. If that object is a ToDoltem, the method updates its
status to reflect the state of the ToDoCell. It then marks the window as
containing an edited document.

186
Setting Up limers for Notification Messages

Setting Up nmers for Notification Messages


The To Do application includes as a feature the capability for notifying users of
items with impending due times. Users can specify various intervals before the
due time for these notifications, which take the form of a message in an attention
panel. In this section you will implement the notification feature of To Do. In
the process you'll learn the basics of creating, setting, and responding to timers.

Here's how it works: Each ToDoltem with a "When to Notify" switch (other
than "Do not notify") selected in the inspector panel-and hence has a positive
secsUntilNotif value-has a timer set for it. If a user cancels a notification by
selecting "Do not notify," the document controller invalidates the timer. When
a timer fires, it invokes a method that displays the attention panel, selects the
"Do not notify" switch, and sets secsUntilNotif to zero.

Implementing the timer feature takes place entirely in Project Builder, but
extends across several classes.

Add the timer as an instance - (void)setTimerForItem: (ToDoItem *)anItem


variable to ToDoltem.
NSDate *notifDatei
Open ToDoltem.h. NSTimer *aTimer;
Add the instance variable if ([anItem secsUntilNotif)) /* 1 */
itemTimer of class NSlimer. notifDate = [[anItem day] addTimeInterval: [anItem
secsUntilNotif))i
Write accessor methods to get
aTimer = [NSTimer scheduledTimerWithTimeInterval: /* 2 */
and set this instance variable.
[notifDate timeIntervalSinceNow]
target:self
2 Create and set the timer, or
invalidate it. selector:@selector(itemTimerFired:)
userInfo:anItem
Open ToDoDoc.m. repeats:NO];
[anItem setItemTimer:aTimer]i
Implement the setlimerForltem: else
method, which is shown at right.
[[anItem itemTimer] invalidate]; /* 3 */

This method sets or invalidates a timer, depending on whether the ToDoltem


passed in has a positive secsUntilNotif value.

1. Tests the ToDoltem to see if it has a positive secsUntilNotif value and, if it has,
composes the time the notification should be sent.

2. Creates a timer and schedules it to fire at the notification time, and instructs
it to invoke itemTimerFired: when it fires. It also sets the ~imer in the ToDoltem.

3. If the secsUntilNotif variable is zero, invalidates the item's timer.

187
Chapter 4 To Do Tutorial

3 Respond to timers firing. - (void)itemTimerFired:(id)timer

Implement itemTimerFired: as id anltem = [timer userlnfo];


shown at right. ToDolnspector *inspController [[[NSApp delegate) /* 1 */
inspector) delegate];
NSDate *dueDate = [[anltem day) addTimelnterval: /* 2 */
[anltem secsUntilDue]):
NSBeep():
NSRunAlertPanel(@"To Do", @"%@ on %@", nil, nil, nil,
[anltem itemName], [dueDate
descriptionWithCalendarFormat:@"%b %d, %Y at %I:%M %p"
timeZone: [NSTimeZone defaultTimeZone] locale:nil));
[anltem setSecsUntilNotif:O];
[inspController resetNotifSwitch]:

When a ToDoItem's timer goes off, it invokes the itemTimerFired: method


(remember, you designated this method when you scheduled the timer).

1. This method communicates with ToDolnspector in a more direct manner than


notification. It gets the ToDolnspector object through this chain of association: the
delegate of the application object is ToDoController, which holds the idof the
inspector panel as an instance variable, and the delegate of the inspector panel is
ToDolnspector.

2. Composes the notification time (as an NSDate), beeps, and displays an


attention panel specifying the name of a ToDoItem and the time it is due. It
then sets the ToDoItem's secsUntiiNotif instance variable to zero, and sends
resetNotifSwitch to ToDoInspector to have it reset the "When to Notify"
switches to "Do not Notify."

Before You Go On - - - - - - - - - - - - - - - - - - - - -
Implement resetNotifSwitch: You haven't written ToDoInspector's resetNotifSwitch
method yet, so do it now as an exercise. It should select the "Do not Notify"
switch after turning off all switches in the matrix, and then force a redisplay of
the switch matrix~

Next you must send setTim~rForltem: at the right place and time, which is
ToDoInspector, when the user alters a "When to Notify" value.

188
Setting Up Timers for Notification Messages

4 Send the message that sets the [[[NSApp mainWindow] delegate] setTimerForltem:currentltem];
timer at the right times

Open ToDolnspector.m.
Instead of archiving an item's NSTimer, To Do re-creates and resets it when the
In switch Checked:, insertthe
application is launched.
setTimerForltem: message at
right afterthe switch statement
evaluating which "When to if ([self activeDays])
Notify" switch was checked. dayenum = [[self activeDays] keyEnumerator];
In controITextDidEndEditing:, while (itemDate = [dayenum nextObject])
insert the same message atthe NSEnumerator *itemenum;
end of the block related to the ToDoltem *anltem=nil;
inspNotifOtherHours variable. NSArray *itemArray = [[self activeDays]
objectForKey:itemDate] ;
5 When the application is itemenum = [itemArray objectEnumerator];
launched, reset item timers. while ((anltem = [itemenum nextObject]) &&
[anltem isKindOfClass: [ToDoltem class]] &&
Add the code at right, below, to [anltem secsUntilNotif]) {
ToDoDoc's initWithFile: method. [self setTimerForltem:anltem];

This block of code traverses the activeDays dictionary, evaluating each ToOoItem
within the dictionary. If the ToOoItem has a positive secsUntilNotif value, it
invokes setTimerForltem: to have a timer set for it.

lick Tock Brrrring: Run Loops and Timer

A run loop-an instance of NSRunLoop- elapsed and then fires, sending a specified
manages and processes sources of input. message to a specified object. For example,
These sources include mouse and keyboard you could create an NSTimer that
events from the window system, file periodically sends messages to an object,
descriptor, inter-thread connections asking it to respond if an attribute changes.
(NSConnection), and timers (NSTimer).

Applications typically won't need to either NSTimer objects work in conjunction with
create or explicitly manage NSRunLoop NSRunLoop objects. NSRunLoops control
objects. When a thread is created, an loops that wait for input, and they use
NSRunLoop object is automatically created NSTimers to help determine the maximum
for it. The NSApplication object creates a amount of time they should wait. When the
. default thread and therefore creates a NSTimer's time limit has elapsed, the
default run loop.
NSRunLoop fires the NSTimer (causing its
NSTimer creates timer objects. A timer message to be sent), then checks for new
object waits until a certain time interval has input.

189
Chapter 4 To Do Tutorial

Build, Run, and Extend the Application


Although you probably have been building the ToDo project frequently now, as
it's been taking shape, build it one more time and check out what you have
wrought. Go through the following sequence and observe To Do's behavior.

1. When you choose New from the Document menu, the application creates a new
To Do document and selects the current day.

2. Enter a few items. Click a new day on the calendar and enter a few more
items. Click the previous day and notice how the items you entered reappear.

3. Choose Inspector from the main menu. When the inspector appears, click an
item and notice how the name and date of the item appears in the top part of
the inspector. Enter due times for a couple items, and some associated notes.
Note how the times, as you enter them, appear in the Status/Due column of
the To Do document. Click among a few items again and note how the
Notifications and Notes displays change.

4. Click a Status/Due button; the image toggles among the three states. Then,
with an item that has a due time, select a notification time- that has already
passed. The application immediately displays an attention panel with a
notification message. When you dismiss this panel, To Do sets the
notification option to "Do not notify."

s. Click the document window and respond to the attention panel by clicking
Save. In the Save panel, give the document a location and name. When the
window has closed, chose Open from the Document menu and open the
same document. Observe how the items you entered are redisplayed.

Optional Exercises
You should be able now to supplement the To Do application with other
features and behaviors. Try some of the following suggestions.

Make Your Own Info Panel


Make your own Info panel. Define a method that responds to a click on the Info
panel button by loading a nib file containing the panel. The owner of the panel
can be the application controller. You can customize this panel however you
wish. For instance, put the application icon in a toggled button (the main image)
and make the alternate image a photo (yourself, your significant other, your
dog). When users click the button, the image changes between the two.

190
Build, Run, and Extend the Application

Implement Application Preferences


Make a Preferences panel for the application, with a new controller object (or
the application controller) as the owner of the nib file containing the panel.
Follow what you've done for ToDoInspector, especially if the panel has
multiple displays. Some ideas for Preferences: how long to keep expired
ToDoItems before logging and purging them (see below); the default document
to open upon launch; the default rescheduling interval (see below). Store and
retrieve specified preferences as user defaults; for more information, see the
NSUserDefaults specification.

Implement Rescheduling
ToDo's Inspector pane has a Rescheduling display that does almost nothing
now. Implement the capability for rescheduling items by the period specified.

Implement Logging and Purging


After certain period (set via Preferences), append expired ToDoItems (as
formatted text) to a log, and expunge the ToDoItems from the application.

191
Chapter 4 To Do Tutorial

192
~
,:"'
ll",,~
'." ....,"',',"'.' .. ',:,",'"
,'I, II'"
Chapter 5
Where To Go From Here

Sections

World Wide Web

Programming Tools and


Resources

Information

Professional Services

Ordering NeXT Products and


Services

195
Where To Go From Here

196
If you've completed the tutorials in this book, you're no longer a novice in
OpenStep development. You should be able to attempt an OpenStep
application on your own, and should expect to carry it off successfully. However,
there is much more for you to learn (or to learn in greater detail). There will
probably be times when you'll need information or help. You might want to
pursue a training course, you might want to order a NeXT product, or you might
have questions you'll need answered, or problems you'll need resolved.

This section points you toward these sources of information and help. It also
includes descriptions of tools, resources, and documentation frequently used in
application development.

World Wide Web


NeXT's corporate home page is at: http://www.next.com.

From there you can navigate to summaries of products, such asWebObjects®,


Enterprise Objects Framework®, and D'OLE®-and order the products on-
line. You can learn about NeXT programs in education, consulting and technical
support. You can also use the NeXTanswers document retrieval system. For
WebObjects, you can download versions of the product, try out demonstration
WebObjects applications, and access related documentation.

197
Chapter 5 Where To Go From Here

Programming Tools and Resources

Other Development Applications


OPENSTEP Developer for Mach includes applications other than Project
Builder and Interface Builder. Except where noted, these applications are
installed in /NextDeveloper/Apps.

Name Description

FileMerge Visually compares the contents of two files or two directories. You can use
FileMerge, for example, to determine the differences between versions of the
same source code file or between two project directories. You can also use it
to merge changes.

MallocDebug Measures the dynamic-memory usage of applications, finds memory leaks,


analyzes all allocated memory in an application, and measures the memory
allocated since a given time.

Icon Builder A simple graphics program for creating application and document icons.

Yap A utility for editing and previewing PostScript code.

Sampler Analyzes performance problems with your application by sampling the call
stack of your program over a period. (In /NextDeveloper/Demos)

Other Installed Frameworks


A framework contains a dynamic shared library, related header files, and
resources (including nib files, images, sounds, documentation, and localized
strings) used by the library. All frameworks are installed in/NextLibrary/Frameworks.
OPENSTEP Developer for Mach provides these other frameworks in addition
to the Application Kit and the Foundation frameworks:

Name Description

System Operating-system and low-level Objective-C runtime APls

SoundKit Sound recording, playback, and editing capabilities.

InterfaceBuilder Creation of custom static (compiled) palletes for use in Interface Builder

NEXTIME Real-time video imaging

NIAccess Netlnfo's access layer

Nllnterface Netlnfo's interface layer

198
Programming Tools and Resources

Useful Command-Line Tools


NeXT has created or modified several tools for compilation, debugging,
performance analysis, and so on. The following table lists some of the more
useful of these tools. You can get further information using the man pages
system.

Name Description Location

cc Compiles C, Objective-C, CH, and Objective-CH /bin


source code files.

gdb Source-level symbolic debugger for C, extended by /bin


NeXT to support Objective-C, CH, Mach, Windows NT,
and (by late 1996) Windows 95.

gnumake Utility for making programming projects. /bin

as Assembler; translates assembly code into object code. /bin

defaults Reads, writes, searches, and deletes user defaults. /usr/bin


The defaults system records user preferences that
persist when the application isn't running. When users
specify defaults in an application's Preferences panel,
NSUserDefaults methods are used to write the defaults.

pswrap Creates Cfunctions that "wrap" PostScript code and /usr/bin


send it to the Window Server for interpretation.

nibTool Reads the contents of an Interface Builder nib file. /usr/bin


Prints classes, the hierarchy, objects, connections, and
localizable strings.

libtool Creates static or dynamic libraries from specified


object bin files for one or multiple architectures.

otool Displays specified parts of object files or libraries. /bin

nm Displays the symbol table, in whole or in part, of the /bin


specified object file or files.

oh Records allocation and deallocation events. /usr/bin

AnalyzeAliocation Analyzes program memory allocation. /usr/bin

fixPrecomps Creates a precompiled header file for each of the major /usr/bin
frameworks.

strip Removes or modifies the symbol table attached to /bin


assembled and linked output.

lipo Creates, lists, and manipulates multi-architecure /bin


object files

199
ChapterS Where To Go From Here

Converting NEXTSTEP Code to OPENSTEPp


You can take advantage of an automated conversion process to convert
NEXTSTEP® Release 3.x code to OPENSTEP Release 4.0. By completing
this process you'll make your application an OpenStep application (that is, an
application conforming to the OpenStep specification). An OpenStep
application-one without any specific operation-system dependencies-
should run on any OpenStep system.

The TOPS scripts you run to perform the conversion process, along with 3.3
header files and intermediate frameworks, are located at
/NextDeveloper/OpenStepConversion. The OPENSTEP Conversion Guide provides
instructions on using the scripts as well as summaries of API changes and
conversion tips.

Other Programming Resources


You can find programming resources-such as fonts, sounds, and palettes-in
various subdirectories of /NextLibrary.

Name Comments

System Resources Character-set information and location of headers for automatic


precompilation (fixPrecomps)

Colors Bundles containing the default set of color binaries for the Colors panel

Fonts Default set of system fonts"including AFM, bitmap, and outline versions

PS2Resources PostScriptfiles containing calibrated color space and color rendering, printing
halftones, and gray-shading patterns

Rulebooks Glyph generators for various string encodings

Sounds Default sound files (".snd") such as Cricket, Ping, and Rooster

200
Information

Information
NeXT publishes documentation for users, developers, and system
administrators. It also, through its Professional Services, offers customers access
to NeXTanswers, an automated document retrieval system.

To order documentation, call 1-800-TRY-NEXT.

Accessing Documentation
On OPENSTEP for Mach, several tools help you access documentation:

• Digital Librarian's NextDeveloper bookshelf includes most of the documents


described in "Developer Documentation" below, as search targets. This
For more information on Digital bookshelf, which located at /NextLibrary/Bookshelves, also includes instructions
Librarian and on other means for for creating your own custom bookshelf.
accessing documentation, see
"\Vhere To Go For Help" on • In Project Builder, you can use the Project Find panel to display reference
page 54. documentation on classes, protocols, methods, functions, and other types.
For details of using the Project
Find panel, see "Finding • In Project Builder or in a Terminal window, you can issue commands to
Information Within Your Project" UNIX's man pages system to display information on command-line tools.
on page 94.

Developer Documentation
The core set of documentation for OPENSTEP is the reference specifications
describing OpenStep classes, protocols, methods, functions, types, and
constants.The reference documentation for the two major OpenStep
frameworks is stored within the frameworks:

/NextLibrary/Frameworks/AppKit.framework!Resources/English.1proj/Documentation
/NextLibrary/Frameworks/Foundation.framework/Resources/English.lproj/Documentation

Several documents-some printed but most only in on-line form-supplement


this core set of documentation. The following table describes these materials
directly related to OPENSTEP for Mach; all documents are located at
/NextLibrary/Documentation/NextDeu

201
Chapter 5 Where To Go From Here

Book/Document Description /NexDev Directory

Object-Oriented Title says it all TasksAndConcepts


Programming and the (as ObjectiveC)
Objective-C Language

OPENSTEP Development: Using Project Builder, Interface Builder, and other TasksAndConcepts
Tools and Techniques tools in program development (as DevEnvGuide)

Programming Topics Contains conceptual background and step-by-step TasksAndConcepts


instructions for common programming tasks.
(Note: the numbered of covered topics will grow in
the months following release 4.0.)

General Reference Display PostScript reference documentation plus Reference


information pertinent to all OpenStep frameworks

Release Notes Current release notes on frameworks, development ReleaseNotes


applications, and tools

Other Developer Documentation


If you've installed Enterprise Objects Framework or Portable Distributed
Objects documentation for these products will also be installed in
/NextLibrary/Documentation/NextDeu This includes the API reference and Developer's
Guide for Enterprise Objects Framework.

You will also find third-party documentation installed in


/NextLibrary/Documentation, including reference documentation for the GNU
libg++ (C++) library. .

System Administration Documentation


Documentation for system administrators of OPENSTEP for Mach networks
can find a helpful manual in /NextLibrary/Documentation/NextAdmin. This manual
describes planning and setting up networks, as well as the creation of user
accounts. It also explains details ofNFS, discusses administrative tasks such as
backups and security, and provides troubleshooting guidelines.

In /NextLibrary/Documentation, you'll also find back issues of the magazine for


system administrators, NEXTSTEP In Focus.

NeXT's Professional Services


also distributes OpenStep
programming information
through NeXTanswers, an
automated document retrieval
system. See page 204 for details.

202
Professional Services

Professional Services
NeXT provides training, consulting, and technical support for its customers. For
more information on the programs described below, call1-800-955-NEXT
(U.S.), +1415-780-2922 (elsewhere in North America), +44 181-565-0005
(Europe). You can also visit the Professional Services section of the NeXT
website at http://www.next.com/Services for up-to-date information on current
programs in technical education, consulting, and support.

Education
Courses offered by NeXT's Training department give developers of all
backgrounds a strong foundation in the fundamentals of OpenStep application
development. This background is critical to the successful implementation of
OpenStep programs by development teams.

Customers can choose from three training formats:

• Open enrollment classes, held at NeXT's training facilities in Redwood City,


Washington, D.C., and Chicago

• On-site classes at the customer's location

• On-site Object Learning Solutions, which over periods of several weeks


provide customers with training and tailored development of skills.

Obiect Expert Consulting


The Object Expert program assigns an expert in OpenStep development 'to
assist customers in their projects on a full-time basis. The committment can be
from two months to as many months as necessary. The Object Expert can help
with developing a prototype (including project planning, requirements,
integration, and testing) or can provide analysis, design, planning, programming,
integration, and testing expertise for full-fledged application-development
projects.

Software Maintenance and Technical Support


With the Software Maintenance program customers can get one copy of each
covered release of software and documentation as well as major and minor
software upgrades. They can select from four levels of technical support and
software maintenance offered by NeXT.

Support includes a range of offerings, from installation assistance to


NeXTanswers. Developers receive debugging assistance and problem

203
Chapter 5 Where To Go From Here

investigation, memory management and performance tuning, portability advice,


and help with converting NEXTSTEP code to OpenStep. System
administrators can obtain help with problems related to network connectivity,
NetInfo domain requirements analysis and implementation, hardware selection
and configuration questions, and other areas.

NeXTanswers
NeXTanswers is an automated retrieval system that gives customers access to
the latest product information, technical documents, drivers, and other software.
You can access NeXTanswers through NeXT's website (http://www.next.com) and
by:

• Electronic mail: Send requests to [email protected] with a subject line of


HELP to receive instructions on how to proceed.

• Fax: Call 415-780-3990 from a touch-tone phone and follow instructions


(you'll need to know the ID numbers of the files you want).

• Anonymous FTP: Connect to FTP.NEXT.COM and read


pub/NeXTanswers/README for further instructions.

• BBS: Call 415-780-Z965, log in as "guest", and go to the Files section. From
there you can download NeXTanswer documents.

Requests sent to NeXTanswers are answered electronically, and are not read or
handled by a person. It does not answer your questions or forward your requests.

204
Ordering NeXT Products and Services

Ordering NeXT Products and Services

To order NeXT products and services, you can:

1. Call1-800-TRY-NEXT (U.S. only); a sales representative will assist you.

2. Send electronic mail to [email protected].

3. Fill out the on-line form on the World Wide Web at


http://www.next.com/AboutNext/Feedback.htmland a sales representative will
promptly contact you.

4. Contact one of the sales offices below, which can also furnish you with
product brochures, data sheets, and other information.

Worldwide Headquarters
900 Chesapeake Drive
Redwood City, CA 94063
Tel: (415) 366-0900
Fax: (415) 780-3714

North American Field Sales OHices


Washington DC Office
1650 Tysons Boulevard, Suite 650
McLean, VA 22102
Tel: (703) 938-6398
Fax: (703) 506-3990

New York Office


One Park Avenue, Sixth Floor
New York, NY 10016
Tel: (212) 503-4750
Fax: (212) 503-4751

New Jersey Office


90 Washington Valley Road
Bedminister, NJ 07921
Tel: (908) 719-8905
Fax: (908) 719-8903

205
Chapter 5 Where To Go From Here

Chicago Office
311 South Wacker Drive, 22nd Floor
Suite 2250
Chicago, IL 60606
Tel: (312) 697-4500
Fax: (312) 697-4501

Canadian Office
4370 Dominion Street
Suite 400
Burnaby, British Columbia
Canada V5G - 4L7
Tel: (604) 451-1877
Fax: (604) 451-1819

Mexico (MeXT)
Tel: 011 525-530-7278

European and Asian Sales OHices


NeXT Computer UK Limited
Technology House
Meadow Bank
Furlong Road·
Bourne End
Bucks
SL85A]
Tel:+ 44(0) 1628535222
Fax:+44(0) 1628535200

NeXT Computer Paris, France


TourCBC
8rue Felix Pyat
F -92800 Puteaux la Defense
Tel: (+33) 146932782
Fax: (+33) 1 46932928

NeXT Software K.K. (Asia Pacific)


Tennoz Central Tower 7F
2-2-24 Higashi-Shinagawa
Shinagawa-ku, Tokyo

206
Ordering NeXT Products and Services

140 Japan
Tel: +81-3-5461-7161
Fax: +81-3-5461-7170

NeXT Software Deutschland GmbH


Gruenwalder Weg 13a
D-82008 Unterhaching
Germany
Tel: +49896145290
Fax: +4989614529 12

207
Chapter 5 Where To Go From Here

208
Appendix A
Obiect-Oriented Programming

Objects

Classes

Categories and Protocols

211
Object-Oriented Programming

212
"Object-oriented programming" has become one of the premier buzzwords in
the computer industry. To understand why, it's important to cut through the
hype and focus on the problem that engendered the object-oriented approach.

In classic procedural programmil1g (used with COBOL, Fortran, C, and other


languages), programs are made of two fundamental components: data and code.
The data represents what the user needs to manipulate, while the code does the
manipulation. To improve project management and maintenance, procedural
programs compartmentalize code into procedures. However, much of the data is
global, and each procedure may manipulate any part of that global data directly.

procedure' data
data
data
data
data
data

With the procedural approach, the network of interaction between procedures


and data becomes increasingly complex as an application grows. Inevitably, the
interrelationships become a hard-to-maintain tangle-spaghetti code. A simple
change in a data structure can affect many procedures, many lines of code-a
nightmare for those who must maintain and enhance applications. Procedural
programming also leads to nasty, hard-to-find bugs in which one function
inadvertently changes data that another function relies on.

Objects change all that.

213
Appendix A Object-Oriented Programming

Obiects
An object is a self-contained programmatic unit that combines data and the
procedures that operate on that data. In the Objective-C language, an object's
data comprises its instance variables, and its procedures, the functions that affect
or make use of the data, are known as methods.

Like objects in the physical world, objects in a program have identifying


characteristics and behavior. Often programmatic objects are modelled on real
objects. For example, an object such as a button has an analog in the buttons on
control devices, such stereo equipment and telephones. A button object
includes the data and code to generate an appearance on the screen that
simulates a "real" button and to respond in a familiar way to user actions.

A button object highlights its on-screen representation


when the user clicks it.

Encapsulation
Just as procedures compartmentalize code, objects compartmentalize both code
and data. This results in data encapsulation, effectively surrounding data with the
procedures for manipulating that data.

Typically, an object is regarded as a "black box," meaning that a program never


directly accesses an object's variables. Indeed, a program shouldn't even need to
know what variables an object has in order to perform its functions. Instead, the
program accesses the object only through its methods. In a sense, the methods
surround the data, not only shielding an object's instance variables but
mediating access to them:

Objects are the basic building blocks of Objective-C applications. By


representing a responsibility in the problem domain, each object encapsulates a
particular area of functionality that the program needs. The object's methods
provide the interface to this functionality. For example, an object representing
a database record both stores data and provides well-defined ways to access that
data.

214
Using this modularity, object-oriented programs can be divided into distinct
objects for specific data and specific tasks. Programming teams can easily parcel
out areas of responsibility among them, agreeing on interfaces to the distinct
objects while implementing data structures and code in the most efficient way
for their specific area of functionality.

Messages
To invoke one of the object's methods you send it a message. A message requests
an object to perform some functionality or to return a value. In Objective-C, a
message expression is enclosed in square brackets, like this:

celsius = [converter convertTemp:fahrenheit]


• '----v---' '---------v---- '......-----..,----'
returned value receiver method name argument

In this example converter is the object that receives the message, the receiver.
Everything to the right of this term is the message itself; it consists of a method
name and any arguments the method requires. The message received by
converter tells it to convert a temperature in Fahrenheit to Celsius and return that
value .

. In Objective-C, every message argument is identified with a label. Arguments


follow colon-terminated keywords, which are considered part of the method
name. One argument per keyword is allowed. If a method has more than one
argument-as NSString's rangeOfString:options: method does, for example-
the name is broken apart to accept the arguments:

range = [string rangeOfString:@"OPENSTEP" options:NSLiteralSearch];

Often, but not always, messages return values to the sender of the message.
Returned values must be received in a variable of an appropriate type. In the
above example, the variable range must be of type NSRange. Messages that
return values can be nested, especially if those returned values are objects. By
enclosing one message expression within another, you can use a returned value
as an argument or as a receiver without having to declare a variable for it.

newString = [stringOne stringByAppendingString:


[substringFromRange:
[stringTwo rangeOfString:@"OPENSTEP" at:NSAnchoredSearch]]];

The above message nests two other messages, each of which returns a value
used as an argument. The inmost message expressions is resolved first, then the
next nested message expression is resolved, then the third message is sent and
a value is returned to newString.

215
Appendix A Object-Oriented Programming

An Obiect-Oriented Program
Object-oriented programming is more than just another way of organizing data
and functions. It permits application programmers to conceive and construct
solutions to complex programs using a model that resembles-much more so
than traditional programs-the way we organize the world around us. The
object-oriented model for program structure simplifies problem resolution by
clarifying roles and relationships.

You can think of an object-oriented program as a network of objects with well-


defined behavior and characteristics, objects that interact through messages.

-+- Messages

Different objects in the network play different roles. Some correspond to


graphical elements in the user interface. The elements that you can drag from
an Interface Builder palette are all objects. In an application, each window is
represented by a separate object, as is each button, menu item, or display of text.

Applications also assign to objects functionality that isn't directly apparent in the
interface, giving each object a different area of responsibility. Some of these
objects might perform very specific computational tasks while others might
manage the display and transfer of data, mediating the interaction between
user-interface objects and computational objects.

Once you've defined your objects, creating a program is largely a matter of


"hooking up" these objects: creating the connections that objects will use to
communicate with each other.

216
Polymorphism and Dynamic Binding
Although the purpose of a message is to invoke a method, a message isn't the
same as a function call. An object "knows about" only those methods that were
defined for it or that it inherits. It can't confuse its methods with another object's
methods, even if the methods are identically named.

Each object is a self-contained unit, with its own name space (an name space
being an area of the program where it is uniquely recognized by name). Just as
local variables within a C function are isolated from other parts of a program, so
too are the variables and methods of an object. Thus if two different kinds of
objects have the same names for their methods, both objects could receive the
same message, but each would respond to it differently. The ability of one
message to cause different behavior in different receivers is referred to as
polymorphism.

Polymorphism

The advantage polymorphism brings to application developers is significant. It


helps improve program flexibility while maintaining code simplicity. You can
write code that might have an effect on a variety of objects without having to
know at the time you write the code what objects they might be. For example,
The example of display highlights
the role of inheritance in
most user-interface objects respond to the message display; you can send display
polymorphism: a subclass often to any of these objects in your interface and it ~ill draw itself, in its own way.
implements an identically named
method (that is, overrides the Dynamic binding is perhaps even more useful than polymorphism. It means both
method) of its superclass to achieve the object receiving a message and the message that an object receives can be
more specialized behavior. See the set within your program as it runs. This is particularly important in a graphical,
following section, "Classes," for user-driven environment, where one user command-say Copy or Paste-may
details.
apply to any number of user-interface objects.

217
Appendix A Object-Oriented Programming

Dynamic Binding

~""....
........
........
........
........ ,,
...... :1..
< _________ :,

In dynamic binding, a run-time process finds the method implementation


appropriate for the receiver of the message; it then invokes (or calls, in a sense)
this implementation and passes it the receiver's data structure. This mechanism
makes it easier to structure programs that respond to selections and actions
chosen by users at run time. For example, either or both parts of a message
expression-the receiver and the method name-can be variables whose values
are determined by user actions. A simple message expression can deliver a Cut,
Copy, or Paste menu command to whatever object controls the current
selection.

Dynamic binding even enables applications to deal with new kinds of objects,
ones that were not envisioned when the application itself was built. For
example, it lets Interface Builder send messages to objects such as EOModeler
when it is loaded into the application by means of custom palettes.

Polymorphism and dynamic binding depend on two other features: dynamic


typing and introspection. The Objective-C language allows you to identify objects
generically with the data type of id. This type defines a pointer to an object and
its data structure (that is, instance variables) which, by inheritance from the root
class NSObject, include a pointer to the object's class. What this means is that
you don't have to type objects strictly by class in your code: the class for the
object can be determined at run time through introspection.

Introspection means that an object, even one typed as id, can reveal its class and
divulge other characteristics at run time. Several introspection methods allow
you to ascertain the inheritance relationships of an object, the methods it
responds to, and the protocols that it conforms to.

218
Classes
Some of the objects networked together in an applications are of different kinds,
and some might be of the same kind. Objects of the same kind belong to the
same class. A class is a programmatic entity that creates instances of itself-
objects. A class defines the structure and interface of its instances and specifies
their behavior.

When you want a new kind of object, you define a new class. You can think of a
class definition as a type definition for a kind of object. It specifies the data
structure that all objects belonging to the class will have and the methods they
will use to respond to messages. Any number of objects can be created from a
single class definition. In this sense, a class is like a factory for a particular kind
of object.

In terms of lines of code, an object-oriented program consists mainly of class


definitions. The objects the program will use to do its work are created at run
time from class definitions (or, if pre-built with Interface Builder, are loaded at
run time from the files where they are stored).

A class is more than just an object "factory," however. It can be assigned


methods and receive messages just as an object can. As such it acts as a class
object.

Obiect Creation
One of the primary functions of a class is to create new objects of the type the
class defines. For example, the NSButton class creates new NSButton objects
and the NSArray class creates new NSArrays. Objects are created at run time in
a two-step process that first allocates memory for the instance variables of the
new object and then initializes those variables. The following code creates a
new Country object:

id newCountry = [[Country alloe] init];


You can create objects in your code
with the alloc and init methods The receiver for the alIoc message is the Country class (from the Travel Advistor
described here. But when you application in the next tutorial). The alIoc method dynamically allocates
define a class in Interface Builder,
that class definition is stored in a
memory for a new instance of the receiving class and returns the new object.
nib file. When an application loads . The receiver for the init message is the new Country object that was
that nib file, Interface Builder dynamically allocated by alIoc. Once allocated and initialized, this new record is
causes an instance of that class to assigned to the variable new Country.
be created.
After being allocated and initialized, a new object is a fully functional member
of its class with its own set of variables. The newCountry object has all the
behavior of any Country object, so it can receive messages, store values in its

219
Appendix A Object-Oriented Programming

instance variables, and do all the other things a Country object does. If you need
other Country objects, you create them in the same way from the same class
definition.

Objects can be typed as id, as in the above example, or can be more restrictively
typed, based on their class. Here, newCountry is typed as a Country object:

country *newCountry = [[Country alloc] init];

The more restrictive typing by class enables the compiler to perform type-
checking in assignment statements.

Inheritance
Inheritance is one of the most powerful aspects of object-oriented programming.
Just as people inherit traits from their forbearers, instances of a class inherit
attributes and behavior from that class's "ancestors." An object's total
complement of instance variables and methods derives not only from the class
that creates it, but from all the classes that class inherits from.

Because of inheritance, an Objective-C class definition doesn't have to specify


every method and variable. If there's a class that does almost everything you
want, but you need some additional features, you can define a new class that
inherits from the existing class. The new class is called a subclass of the original
class; the class it inherits from is its superclass.

Inheritance . . New
. . Inherited

- - - - - - - - Instance of superclass

, . - - - - - Instance of subclass

Creating a new class is often a matter of specialization. Since the new class
inherits all its superclass's behavior, you don't need to reimplement the things
that work as you want them to. The subclass merely extends the inherited
behavior by adding new methods and any variables needed to support the
additional methods. All the methods and variables defined for-or inherited
by-the superclass are inherited by the subclass. A subclass can also alter

220
superclass behavior by overriding an inherited method, reimplementing the
method to achieve a behavior different from the superclass's implementation.
(The technique for doing this is discussed later.)

The Class Hierarchy and the Root Class


A class can have any number of subclasses, but only one superclass. This means
that classes are arranged in a branching hierarchy, with one class at the top-the
root class-that has no superclass:

Part of the OpenStep class


hierarchy.

NSArray NSString

NSWindow NSApplication

NSTextField NSButton

Other root classes are possible. In NSObject is the root class of this hierarchy, as it is of most Objective-C class
fact, OPENSlEP's Distributed hierarchies. From NSObject, other classes inherit the basic functionality that
Objects makes use of another root
makes messaging work, enables objects to work together, and otherwise invests
class, NSProxy.
objects with the ability to behave as objects. Among other things, the root class
creates a framework for the creation, initialization, de allocation, introspection,
and storage of objects.

As noted earlier, you often create a subclass of another class because that
superclass provides most, but not all, the behavior you require. But a subclass
can have its own unique purpose that does not build on the role of an existing
class. To define a new class that doesn't need to inherit any special behavior
other than the default behavior of objects, you make it a subclass of the
NSObject class. Subclasses of NSObject, because of their general-purpose
nature as objects, are very common in OpenStep applications. They often
perform computational or application-specific functions.

221
Appendix A Object-Oriented Programming

Advantages of Inheritance
Inheritance makes it easy to bundle functionality common to a group of classes
into a single class definition. For example, every object that draws on the
screen-whether it draws an image of a button, a slider, a text display, or a graph
of points-must keep track of which window it draws in and where in the
window it draws. It must also know when it's appropriate to draw and when to
respond to a user action. The code that handles all these details is part of a single
class definition (the NSView class in the Application Kit). The specific work of
drawing a button, a slider, or a text display can then be entrusted to a subclass.

This bundling of functionality both simplifies the organization of the code that
needs to be written for an application and makes it easier to define objects that
do complicated things. Each subclass need only implement the things it does
differently from its superclass; there's no need to reimplement anything that's
already been done.

What's more, hierarchical design assures more robust code. By building on a


widely used, well-tested class such as NSView, a subclass inherits a proven
foundation of functionality. Because the new code for a subclass is limited to
implementing unique behavior, it's easier to test and debug that code.

Any class can be the superclass for a new subclass. Thus inheritance makes
every class easily extensible-those provided by OpenStep, those you create,
and those offered by third party vendors.

Defining a Class
You define classes in two parts: One part declares the instance variables and the
interface, principally the methods that can be invoked by messages sent to
objects belonging to the class, and the other part actually implements those
methods. The interface is public. The implementation is private, and can
change without affecting the interface or the way the class is used.

The basic procedure for defining a class (using Interface Builder) is covered in
the Currency Converter tutorial. However, here is a supplemental list of
conventions and other points to remember when you define a class:

• The public interface for a class is usually declared in a header file (.h
extension), the name of which is the name of the class. This header file can
be imported into any program that makes use of the class.

• The code implementing a class is usually in a file taking the name of the class
and having an extension of .m. This code must be present-in the form of a
framework, dynamic shared library, static library, or the implementation file
itself-when the project containing the class is compiled.

222
• ~lethod declarations and implementations must begin with and - sign or a +
sign. The dash indicates that these methods are used by instances of the class;
a + sign precedes methods that the class object itself uses.

• ~lethod definitions are much like function definitions. Note that methods not
only respond to messages, they often initiate messages of their own-just as
one function might call another.

• In a method implementation you can refer directly to an object's instance


variables, as long as that object belongs to the class the method is defined in.
There's no extra syntax for accessing variables or passing the object's data
structure. The language keeps all that hidden.

• A method can also refer to the receiving object as self. This variable makes it
possible for an object, in its method definitions, to send messages to itself.

Overriding a Method
A subclass can not only add new methods to the ones it inherits, it can also
replace an inherited method with a new implementation. No special syntax is
required; all you do is reimplement the method.

Overriding methods doesn't alter the set of messages that an object can receive;
it alters the method implementations that will be used to respond to those
messages. As mentioned earlier, this ability of each class to implement its own
version of a method is referred to as polymorphism.

It's also possible to extend an inherited method, rather than replace it outright.
To do this you override the method but invoke the superclass's same method in
the new implementation. This invocation occurs with a message to super, which
is a special receiver in the Objective-C language. The term super indicates that
an inherited method should be performed, rather than one defined in the
current class.

223
Appendix A Object-Oriented Programming

Categories and Protocols


In addition to subclassing, you can expand an object and make it fit with other
classes using two Objective-C mechanisms: categories and protocols.

Categories provide a way to extend classes defined by other implementors-for


example, you can add methods to the classes defined in the OPENSTEP
frameworks. The added methods are inherited by subclasses and are
indistinguishable at run-time from the original methods of the class. Categories
can also be used as a way to distribute the implementation of a class into groups
of related methods and to simplify the management of large classes where more
than one developer is responsible for components of the code.

Protocols provide a way to declare groups of methods independent of a specific


class-methods which any class, and perhaps many classes, might implement.
Protocols declare interfaces to objects, leaving the programmer free to choose
the implementation most appropriate to a specific class. Protocols free method
declarations from dependency on the class hierarchy, so they can be used in
ways that subclasses and categories cannot. They allow objects of any class to
communicate with each other for a specific purpose.

OpenStep provides a number of protocols. For example, the spell-checking


protocols and the object-dragging protocols enable other developers to
seamlessly integrate their spell-checking and object-dragging implementations
into an existing system.

224
Index
Index

A application controller 109,110,131, 133, switch 57, 162


abstract class 95 182, See also controller object types 57
acceptsFirstResponder 172 Application Kit 4,7,67,142,155,170,
,accessing
172 c
application wrapper 48, 99, 108, 110 C 48
data 78
applicationShouldTerminate: 99, 144 C++ 7,48,192
information SO-51
architecture 48 calendar format 126
accessor method 46, 77, 79, 175
multi-document 107 category 124,212
implementing 116
archiveRootObject:toFile: 99, 139 cc 48,189
retaining object 159
archiving 68, 74, 77, 99, 100, 118, 139 cell 37,97
action 34, 73,92, 123, 164
argument 203 enabling and disabling 128
connecting 40
array 145 highlighing 128
defining 35, 36
arrayWithObject: 137 installing 178
implementing 130
ASCII 76 prototype 125
setting programmatically 126
assembler 189 setting state 176.
action message, See action
assigning the class 153 setting title 128
action method, See action
attention panel 180 cellAtIndex: 85, 86
adding
attribute, setting 156 cellAtRow:column: 162
action 35
Attributes display 21 cellWithTag: 130
application icon 66
autorelease class 33
menu item 64
mechanism 82 abstract 95
outlet 35
pool 82,83 adding to project 174
submenu 64
autorelease 79,80,82,83, 103 and object creation 207
addObject: 146
auxiliary nib file 17, 110, 133 assigning in Interface Builder 153
addTimelnterval: 148
awakeFromNib 45,96, 126, 151 cluster 95
alignment 25
creating 208,209
of text 22 B
definition 121,207,210
Alignment command 24 background color 21
principal 108
alloc 103, 207 base coordinate system 18, 135
relation to object 33
AnalyzeAllocation command 103, 189 bounds 170
reusing 71, 108
animation 29 box object 24, 59, 156
specifying 33, 70
ANSIC 7 breakpoint
testing membership in 117, 147
application 144 setting 102
class hierarchy 209
attributes 66, 108 browser 61
class method 46
behavior 28 Build panel 47
class object 207
creating 15 building a project 47,48
client/server 4, 9
design 32, 168 and errors 49
Close command 138
icon 66 bundle 99, 110
closing a document 138
multi-document 107, 131, 133 accessing resources 174
cluster, class 95
NSApplication 154 load able 110
coding 100
possibilities 29 main 110
collection classes 68
resources 11 0, 133 button 23, 87
color 190
standard features 28 and images 63, 123
Color panel 21
start-up routine 108 custom 173
column identifier 60
state 173
columns, of objects 25

226
Index

compare: 89 customizing menus 64 dictionary 68, 99


compiler 48 CustomView object 172 Digital Librarian application 50,88, 191
GNUC 7 display 160, 170, 172
o
compositeToPoint:operation: 171 Display PostScript 4, 7, 18, 108, 171
. D'OLE 5, 187
compositing images 170, 171, 172 documentation 192
documentation 192
connecting objects 39 displays
data
direction of 37 synchronizing 168
at port 154
Connections display 26, 39 distributed computing 4
mediating 86
consulting 193 document 107, 133
serializing 100
containsObject: 160 and nib file 133
storage 78
content area 140, 141 closing 138
synchronizing displays of 167, 168
content view 18, 140, 141, 160 creating 135
data encapsulation 30, 202
box 156, 157 icon 113
data source 60, 69, 84, 89
replacing 160 initial values 168
DataViews palette 60
contentView 141, 169 management 29
date 125
context-sensitive Help 50 marking as edited 139, 142
date and time 126, 130
control object 34,37,97 opening 137
creating object 127
control:isValidObject~ 96, 169 saving 138, 143, 144
formatting 127
controller object 31,32,32,68, 109 setting type of 113
date With Year:month:day:hour:minute:
application 109 document controller 109, 110, 131, 132,
second:timeZone: 130
document 109 133, See also controller object
day of the week 127
controlTextDidChange: 142 Document menu 131
dealloc 80,83, 100, 118
controlTextDidEndEditing: 146-148, Interface Builder 111
deallocation 77, 78, 80, 82-83, 100, 103,
166, 167 document type 113
118,209
converting code,to OpenStep 190 documentation 50, 51, 88, 191
debugger 102, 103
coordinate system 18, 135, 141, 170 accessing 191
debugging 117
flipping 170 reference 51, 191
declaration 46, 134
copy 79, 83, 103 system administration 192
method 43, 116
copying objects 21, 117 drag-and-drop 20, 29
deep copy 117
and reference count 83 drawing 29,140,141,170-172
defaults 189
copyWithZone: 117 functions 171
delegate 37, 73, 74, 91, 99, 129, 133, 140,
CORBA5 141, 149, 155 drawRect: 170,172
core program framework 140-141, 168 method implemented by 60, 111, 142, duplicating object 21
coverage 171 165 dynamic binding 46, 89, 205-206
creating delegate 141 dynamic loading 110
class 33 delegation 69,91, 144 dynamic shared libraries 7
custom view object 172 delimiter checking 85 dynamic typing 36, 46, 206
document 135 description 117, 118
E
form 58 descriptionWithCalendarF ormat:
timeZone:locale: 149 Editmenu 24
object 207
design editable text 21
panel 156
hierarchical 210 Emacs key bindings 85
currentEvent 141
hybrid 32 enableDoubleReleaseCheck: 103
custom palette 206
of application 32 encapsulation 30, 202
custom view 112
determining class membership 206 encodeObject: 81
and Interface Builder 122

227
Index

encodeValueOfObjCType:at: 81 Foundation framework 4, 7, 78 indentation 85


encodeWithCoder: 77, 81, 100, 118 frame 170 Info panel 131, 182
Enterprise Objects Framework 4, 5, 60, framework 7,48, 110, 188 informal protocol 89, 124, 129
187 documentation 191 inheritance 8, 33, 208, 209
documentation 192 Foundation 78 advantage of 210
entity object 83 function 119 init 80, 100, 118,207
enum constant 85 initialization 77,80, 100, 118, 125,209
EOModeler 206 G
default 80
event 18, 154-155, 181 gdb 52, 102, 103, 117, 189
initializing text 20
and custom NSView 172 generating
initWithCoder: 77,81, 100, 118
dispatching 140, 155 instances 72
initWithFrame: 125, 172
handling 140, 172 source-code files 42, 74
input source 181
keyboard 155 generating code files 114
inspector panel 19, 156
message 140 "get" method 77, 79
creating 156
event cycle 154-155 gnumake 48,189
display of 157
event queue 155 graphical debugger 102
managing 156
extensibility 210 grid, aligning on 24
instance 33, 207
extension, file 113 grouping
generating 38
objects 59
F instance method 46
grouping methods 212
instance variable 91, 114,202,207,211
fat files 48
H declaring 76, 114,210
faxing 29
header file 42, 43, 210 inheriting 208
field, formatting 96, 169
Help 29,50,88, 131 scope 46
file
hierarchical data 61 setting 83
extension 113
hierarchy Instantiate command 38, 72
management 29
of classes 209 interface 17
opening 137
creating with Interface Builder 17-27
saving 138-139
testing 27, 28, 66
type 137
icon Interface Builder 6, 17,97,207
file descriptor 154, 181
application 66 and custom NSView 172
file package 48
document 113 inspector 19
File's Owner 74, 110
icon mode (Interface Builder) 73 palettes 7, 20
FileMerge application 188
lconBuilder application 188 interface file 210
finding information 50, 51
id 34,206 Objective-C 43
first responder 140, 155
identifier 89 internationalization 76
firstResponder 141
column 60 interoperability 5
focusing 170
image introspection 206, 209
font 190
adding to button 123 invalidate 116, 179
setting 130
adding to interface 63 isDocumentEdited: 143, 147
Font panel 22
compositing 171 isEqual: 117
Font submenu 28
image view 63 isEqualToString: 147
form 58
implementation file 42,44,210 isKindOfClass: 117,147
formatter 29, 97
importing header files 45
setting 169 K
incremental search 88
formatting, of fields 96 key 68,69,145

228
Index

key equivalent 93 customizing 131 NeXTanswers 187, 194


key window 18, 155 default 24 NextDeveloper bookshelf 191
keyboard event 154, 155 Document 111 NEXTIME 188
keyWindow 141 Menus palette 64, 111 nextKeyView 25, 28, 64
keyword 46, 80, 203 message 45, 46, 203, 211 nib file 16, 182,207
action and localization 63
l
nesting 45, 46, 52, 203 auxiliary 74, 110, 133
label 21,22
,method 30, 46, 114, 202 creating 156
ld 48
accessor 46, 77, 79 definition 17
libg++ 192
class 46 document 110
library 48
declaring 43, 77, 116,210 loading 108, 136
dynamic 7
delegation 73,111,129, 142, 146,149, main 17,108
line on interface 165 sound and images 63
creating 24 difference from function 205 nibTool189
link editor 48 extending 211 nil 46, 80, 146
linking 48 inheriting 208 nm command 189
lipo command 189 instance 46 notification 69, 91, 167, 168, 179
loadNibNamed: 136 invoking the superclass 153 adding an observer 101
localization, and nib files 63 overriding 152, 175, 208, 211 advantages 168
localTimeZone 125 private 176 identifying 152
locating project symbols 88 syntax 211 posting 153
model object 31,32,67, 109, 145 notification center 91
M
Model-View-Controller paradigm 30, 32, notification queue 91
main bundle 110 67,109,120
main menu 131 NSActionCell 97
modifier key 154
main nib file 17, 108 NSApp 74, ro8, 138, 141, 155
modularity 8, 30, 109, 203
main window 18, 138, 155 NSApplication 18, 108, 140, 141, 155
mouse click, simulating 138
mainO 16, 108 NSApplicationMainO 108
mouse event 154
mainMenu 141 NSArchiver 99, 100
mouseDown: 152, 172
mainWindow 138,141 NSArray 68, 78, 89, 95, 117
multi-document application 107, 131
make 7 NSBox 156,169
design 133
make, See also gnumake 7 NSBrowser 141
multi-document architecture 107
Makefile 16 NSBundle 99, 100, 110, 174
mutableCopy 103
makefile 47,48 NSButtonCell 125,148,173,174
Makefile.postamble 16 N NSCalendarDate 125
Makefile.preamble 16 name completion 85 NSCell 97
makeFirstResponder: 143, 149, 155 nesting messages 45 NSCoder 81, 100
makeKeyAndOrderFront: 45 Netlnfo 4, 188 NSCoding protocol 100, 114
making a connection 40 network management 192 NSCompositingOperation 171
MallocDebug application 188 New command 133, 135 NSConnection 181
man pages 88 NeXT NSControl 96,97, 120, 125, 155
matrix 125 ordering products 195 NSCopying protocol 114, 117
and tabbing 134 publications 51, 191 NSCountedSet 68
menu 18,64 website 187 NSData 78, 171
and Interface Builder 24 next responder 140, 155 NSDate 97, 125, 174, 180

229
Index

NSDateFormatter 97, 169 o reusing 109


NSDictionary 68, 78, 99, 100 object 8, 9, 33 scroll view 60
inserting objects 150 aligning 24, 25 sizing 20
NSEvent 154 allocation 78 text 59, 166
NSEventType 154 analog to 202 text field 165, 166
NSFormatter 97 and dynamic binding 206 unarchiving 136
NSHomeDirectoryO 139 and name space 205 value 83
NSlmage 171,174 archiving 77, 139, 150 view 32, 112, 120
NSlmageRep 171 array 111 objectAtIndex: 137, 147
NSlmageView 63 attribute 156 objectForKey: 68, 151
NSMatrix 120,121,125 box 24,59, 156, 160 94
NSMutableArray 84,95, 111 button 23 . Objective-C 7,48,202,203,206,208,
NSMutableDictionary 84 class membership 206 211, 212
NSMutableString 76 communication 180 documentation 192
NSNotification 91, 101, 167 comparison 117 header file 43
NSNotificationCenter 91, 101, 167 connecting 36,39,40 summary 46
NSNumber 78, 97 controller 31,32,68,84, 109 object-oriented program 204
NSObject 33, 78, 83, 124, 209 copying 21, 117 design 30
NSOpenPanel 137 creation 207 object-oriented programming 201
NSProcesslnfo 78 deallocation 77, 78, 80, 103, 118 documenation 192
NSResponder 120, 140, 154, 155 definition 202 objects
NSRunAlertPanelO 98, 143 dictionary 68 connecting 37, 73
NSRunLoop 116, 181 disposal 82, 83 grouping 59, 157
NSSavePanel 139 duplicating 21 making same size 21
NSSet 68 dynamic typing 34 sharing 159
NSString 76, 78, 89 entity 83 observer 91
NSTableColumn 60, 89 form 58 oh command 103,189
NSTableDataSource 89 formatter 169 Open command 133, 137
NSTableView 60, 89 initialization 77,80, 100, 118 Open panel 111, 133, 137
NSText 87 initializing text 20 opening a document 137
NSTextFieldCell 86 inspector 156 openPanel 137
NSThread 78 interface 30, 203 OPENSTEP 4
NSTimeInterval introspection 206 application 28, 29
NSTimer 116, 179, 181 matrix 111, 125, 134 development applications 188
NSTimeZone 125 model 31,32,67,109 OpenStep 7
NSUnarchiver 100 modularity 109 converting code to 190
NSValue 78 ownership policy 78, 82, 83 specification 4, 190
NSView 120, 125, 135, 140, 141, 155, placing 20 OPENSTEP Developer 4, 6
170,210 pop-up list 162 OPENSTEP User 4
custom 172 putting in NSDictionary 150 ordering products 195
focusing 170 relation to class 33 origin point 135
NSWindow 18, 138, 140, 141, 154, 155 resizing 20 outlet 25,34, 70, 72, 123
numberofRowslnTableView: 90 retaining 82, 169 connecting 39
retention 83 defining 35, 36
outline mode 73

230
Index

overriding a method 152, 173, 175, building 47 S


208,211 directory 15 sales offices 195
invoking superclass 153 project browser 16 Same Size command 21
ownership, of objects 82 Project Builder 6, 110 Sampler application 188
p checking delimiters 85 Save command 133, 138
indentation 85 Save panel 111, 133, 139
palette 20, 110, 190
launching 15 savePancl 139
panel 18
searching 88 saving a document 138, 143
creating 156
Project Find panel 51,88, 191 screen, coordinate system 135
inspector 157
project symbols 88 scroll view 60
off-screen 157
protocol 212 scrolling 170
pasteboard 29
adopting 76, 114 searching code 88
pathForImageResource: 174
informal 124 selectable text 21
PDO, See Portable Distributed Objects
pswrap 171, 172, 189 selectedCell 130
performClose: 138
putCell:atRow:column: 178 selected Row 93
periodic event 154
persistence 81 selectText: 45,86
R
placing objects 20 self 46,211
radio mode 125
platforms, supported 5 sender 45
receiver 45, 203
plug-and-play 29 services 29
reference documentation 51,88
po command 117 Services menu 24
release 80, 83, 103
polymorphism 46,89, 205, 211 "set" method 77, 79
release notes 192
pop-up list 18 setAutosizesCells: 126
reliability 8
Portable Distributed Objects 4, 5 setContentView: 160
removeFromSuperView 169
documentation 192 setDataSource: 89
replaceObjectAtlndex:withObject: 147
posting, a notification 91 setDocumentEdited: 139, 142
representedFilename 137
PostScript 154, 170 setEntryType: 96
resizing
and drawing 171 setFloatingPointF ormat:left:right: 96
view object 20
Preferences panel 131, 183 setFormatter: 97, 169
window 19
principal class 108 setFrameOrigin: 170
resource
print: 65 setFrameSize: 170
for programming 190
printing 29,65 setIdentifier: 89
resources
procedural programming 201 setlmage: 148
application 174
procedures 201 setObject:forKey: 150
responder chain 140, 155
professional services 5 setState: 57,87
retain 79, 83, 103, 159
program development setString : 87
and content view 160
command-line tools 189 setStringValue: 86
retaining object
resources 190 setTarget: 155
implications of 83
work flow 14 setting the font 22, 130
reusability 109
program, object-oriented 204 setTitle: 136, 148
reuse 8,108
programming setTi tieWithRepresentedFilen<J.me:
reusing object 71 136
procedural 201 root class 209 shallow copy 117
work flow 14 root object 78, 99, 100 . sharedApplication 108
project 15 rows, of objects 25 sizing objects 21
adding class to 114 runModalF orDirectory:fiIe:types: 137

231
Index

sortedArray UsingSelector: 89 tag 124, 125, 128, 160, 162 V


sound 190 tag 130 validation, of fields 96, 169
Sound Kit 188 target 36, 40 value object 68,83
source-code files setting programmatically 126 view hierarchy 18, 140, 141
generating 42, 74, 114 target/action paradigm 37, 155, 165 view object 18,32, 120
specialization 209 techncial documentation 51 custom 112, 172
standard window 18 technical support 193 printing 65
startTrackingAt:inView: 177 testing an interface 27, 66 removing from superview 169
state 57 text Views palette 20, 172
static typing 46 aligning 22
W
stopTracking:at:inView:mouseIsUp: 177 background color 21
string object WebObjects 5, 136, 187
editable 21
and character strings 76 selectable 21 window 18, 154
empty 80 text color 21 attributes 19
stringValue 87 text field 165, 166 behavior 28
strip command 189 closing 143
attributes 21
subclass 8, 33, 208, 209 depth 171
font 22
creating 33, 152, 173, 209 formatting 169 events 155
custom view 172 placing and resizing 20 key 18
making 108 tabbing between 26 locating 136
Subclass command 70 main 18
validation 169
subclassing 108 text object 59, 166 making first responder 143
subview 59, 141, 160 Text submenu 28 positioning 135
suitcase 16 resizing 19
textDidChange: 101
Sun Solaris 4 setting title 136
textDidEndEditing: 166
super46,80, 125,211 thread 181 status 138
superclass 33, 70, 121, 152,208,209 Window Server 18, 108, 140, 154
tile 93
accessor method 175 time zone 126 Windows. menu 24
superview 59, 141, 155 time, See date and time Windows operating system 4
support, technical 193 timer 116, 154, 179, 181 Windows palette 157
switch 57, 87, 162 windowShouldClose: 143
firing 180
symbols, definitions and references 88 scheduling 179 World Wide Web 187, 195
system administration 192 Title object 22 y
tools, command-line 189 Yap application 188
T
TOPS 190
tabbing, between fields 26
tracking-rectangle event 154
table view 60, 69, 89
training 193
configuring 61
transparency 171
identifier 89
typing, static and dynamic 208
tableView:
objectValueForTableColumn: row: U
90
unarchiveObjectWithFile: 100, 136
tableView:setObjectValue:
forTableColumn:row: 90 unarchiving 77, 118, 136
TabulationViews palette 60 Unicode 76
userInfo dictionary 153

232
Prillted 011 recycled paper
6863.00

You might also like