VanGogh Smalltalk
Volume Number: 13
Issue Number: 3
Column Tag: Tools Of The Trade
Making Smalltalk with the Macintosh Toolbox
By Lee Ann Rucker, Sunnyvale, CA
Using VanGogh and the next version of VisualWorks to build
multiplatform applications with an Object-Oriented Smalltalk
Interface to the Macintosh Toolbox
VisualWorks is a Smalltalk application development environment from
ParcPlace-Digitalk providing a very high degree of portability. Applications developed
in VisualWorks run unchanged on a variety of platforms including Macintosh,
Windows, OS/2 and unix. Unfortunately, it achieves this portability at the expense of
platform compliance, by emulating all user interface elements ("widgets") using a
small set of platform-specific graphic primitives.
This article discusses the VanGogh platform architecture - the reasons for its
creation, how it manages cross-platform compatibility while providing native
look-and-feel, how it works with the VisualWorks framework, and how it can be used
to write Macintosh-specific code.
In the early versions of Smalltalk, the user interface on all platforms used a variation
of the original Smalltalk look. The release of VisualWorks 1.0 added the concept of
"LookPolicy". Each user interface had its own LookPolicy - MacLookPolicy,
WinLookPolicy, etc. The emulation allowed cross-platform developers the opportunity
to preview how their applications would look on the deployment platform. However,
the benefits of this were overshadowed by the increasing liabilities - the challenge of
faithfully rendering a given platform's look and especially feel and the maintenance
cost to keep up with changes to the interfaces, which became even more difficult when
customizable looks were introduced for Macintosh and Windows. The Macintosh look
used the pre-System 7 black-and-white controls exclusively, the menu bars were a
part of the window, scroll bars alongside the edge of a window were not constrained to
their proper position, and the grow box was obscured by the window contents (Figure
1).
Figure 1. VisualWorks Macintosh window containing the menu bar.
The objective of the VanGogh project is to provide a VisualWorks product that
maintains the portability of its predecessors, but adds host compliance by connecting
directly to host widgets. In addition, it allows platform-specific codethat can coexist
with writing applications that use the portable VisualWorks framework.
VanGogh Architecture
One of the goals of VanGogh was to maintain cross-platform portability. To that end, we
implemented a layered architecture, shown in Figure 2. It divides objects into three
layers: in the top-most layer are portable objects that are able to draw on the services
provided by two lower layers. The middle layer implements a layered arch consisting
of Bridge objects that supply a platform-independent API to the portable objects using
capabilities drawn from the bottom-most layer. This bottom-most layer is shown in
Figure 2.
Figure 2. Layered Architecture.
Portable layer
The Portable layer is used by a HostLookPolicy and has exactly the same interface, so
existing applications already accustomed to using a LookPolicy for user interface
elements run unchanged.
VanGogh allows the new framework to retain the snapshot portability of traditional
VisualWorks, which allowed a system to be restarted in exactly the same state as when
it was saved, a "snapshot" in Smalltalk terminology. When the snapshot occurs, the
portable objects query their bridges for the current state. On restart, new Bridges and
Proxies, appropriate for the platform, are created using the state stored in the
snapshot. Thus a system can be shut down on a Macintosh and restarted on Windows,
and it picks up precisely where it left off.
Bridge layer
The Bridge layer provides the portable layer with a platform-independent interface to
the host. The interface between the Bridge and Portable layers is identical for all
platforms. Each class in the Portable layer is matched with a class in the Bridge layer.
However, the implementation and actual class hierarchy in the Bridge layer differs on
each platform to take advantage of variations in implementations of the platform
objects.
Platform access layer
The purpose of a platform access layer is two-fold. To provide support for the portable
objects ("Proxies") and to furnish convenient direct access to the platforms facilities
for discretionary use by the application developer. Proxy objects provide an interface
to Toolbox functions, methods for accessing structure fields, utilities to support
changes in the Macintosh user interface, and finalization. We discuss these in greater
detail in the section "Features of Proxies.
Many of the proxy classes (see Figure 3) correspond fairly directly to the various
Macintosh Toolbox Managers. Other classes support user interface elements not
provided by the Macintosh, such as dividers and notebooks.
Event handling
One of the features provided by the Bridge is event dispatching and processing. VanGogh
provides standard event processors for every component. Low-level Macintosh events
are dispatched to the Bridges that use the proxies to handle them, then creates
high-level VisualWorks events such as value or focus change.
Figure 3. Proxy Class Hierarchy.
Features of Proxies
Proxy classes are used to implement an object-oriented Toolbox. Its structure
parallels as closely as possible to the Toolbox Managers, along with additional classes
to support user interface elements not provided by the Toolbox (Figure 3). The
methods are derived from the Toolbox API following a standard convention, and utility
methods are added to standardize the API, allowing the abstraction of common
functionality.
Toolbox methods
Toolbox methods are Smalltalk versions of functions provided by the Toolbox. To allow
easy conversion from the Toolbox, we have adopted a consistent naming convention for
Toolbox methods. According to this convention, Toolbox methods are created from
Toolbox function names by removing the identifier for the corresponding Manager. The
initial letter is lowercase for compliance with Smalltalk. Though this may seem more
confusing than simply keeping the names of the Toolbox functions intact, it provides
benefits in an object-oriented environment.
Consider the following: DisposeControl, TEDispose, DisposeWindow, DisposeMenu,
DisposeDialog, LDispose, DisposeRgn, DisposeGWorld. Following the convention, all of
these are implemented in the Proxy as dispose. We now have a uniform means of
disposing of these Toolbox elements, while still keeping the names similar enough to
the Mac Toolbox so that a Toolbox programmer will have little trouble converting
existing Toolbox code.
The naming convention for methods with more than one parameter is to use the
parameter name in the function specification as the Smalltalk keyword. (In Smalltalk,
keyword messages are methods whose names consist of words followed by arguments,
and the words themselves are called keywords.) For example, DragControl is defined in
the Toolbox Assistant as follows:
void DragControl(ControlHandle theControl, Point *startPt,
const Rect *limitRect, const Rect *slopRect,
short axis);
We know that theControl corresponds to the proxy, so startPt is the first parameter of
the corresponding Toolbox method. Therefore the method name is composed of the
names of the remaining parameters, and when sent to a control object is used as
follows:
aControlProxy drag: startPt limitRect: limitRect slopRect: slopRect
axis:
axis
Finalization
When the Proxy is finalized as part of Smalltalk's automatic garbage collection, the
handle is automatically disposed of using the dispose method as described in the Toolbox
Methods section. This helps prevent resource leaks and double-disposing, both common
problems in Macintosh programming.
Toolbox extension methods
The Apple Human Interface Guidelines specify some behaviors and graphical elements
which the Toolbox does not provide: typing selections in Lists, the keyboard focus
border on Lists, the default border on default Push Buttons, the border and scroll bars
for a TextEdit and the window grow box. Inside Macintosh and Apple DTS provide
sample implementations of these facilities, which we include in the Proxies as Apple
extensions to the Toolbox.
Shortcut methods
Proxies provide shortcut methods that simplify calling certain Toolbox methods.
• Methods with parameters that are ignored, either by the MacOS or by the
programmer. Shortcut methods provide default values or placeholders where needed.
• Accessor methods which obtain values by passing a pointer to a parameter.
Shortcuts for these methods create a temporary pointer and return the pointer
contents.
The following example of a shortcut for GetDItem demonstrates both kinds of shortcuts
- it provides placeholders for the unused itemType and box, and returns the contents
of the pointer to the item. The section "Using the toolbox directly" discussed below,
shows how to use this method.
MacDialogProxy
getItem: itemNo
"Answer a handle to an item so that you can manipulate it
| itemType item box |
"Allocate temporary pointers for the parameters
itemType := Host api short malloc.
item := Host api Handle malloc.
box := Host api Rect malloc.
"Call the Toolbox
self getItem: itemNo
itemType: itemType
item: item
box: box.
^item contents
Enhanced methods
As the Macintosh User Interface evolved, Apple sometimes had to overload existing
functions to provide new capabilities. One example of this is submenus - these did not
exist in early System versions. A submenu is added by overloading SetMenuItemCmd,
and passing in a special constant for the cmdChar. We conceal the clumsiness of these
overloaded functions by providing enhanced methods like this one that takes the item
and submenuID and calls SetMenuItemCmd with the appropriate constant.
MacMenuProxy
setSubmenu: item submenuID: aMenuID
"Utility to set itemCmd and itemMark correctly for a submenu with
the given id
self setItemCmd: item
cmdChar: #hMenuCmd;
setItemMark: item
markChar: aMenuID
Data conversion
Toolbox methods provide data conversion when calling the Toolbox.
• Smalltalk Booleans are converted to integers.
• Smalltalk Strings are converted to Pascal Strings, which begin with a length
byte.
• Toolbox flag constants are passed in a Smalltalk Set and automatically "or"ed
together.
The following example from the MacAppleEventProxy calls the Toolbox method
representing AESend.
"Send an AppleEvent without waiting for a reply
self send: theEvent
reply: reply
sendMode: #(#kAENoReply #kAENeverInteract
#kAECanSwitchLayer)
sendPriority: #kAENormalPriority
timeOutInTicks: #kAEDefaultTimeout
idleProc: nil
filterProc: nil.
Complex types like Points and Rects are not converted in place by Toolbox methods,
though there are utility routines that will convert complex types to and from their