April 91 - Approaching MacApp 3.0
Approaching MacApp 3.0
Chris Knepper, Apple Computer
MacApp 3.0 boasts many enhancements over version 2.0. This article surveys these
enhancements, and highlights those areas of interest to the developer. This article
assumes familiarity with MacApp 2.0.
System 7 Support
Enhancements for System 7 are the principle features of MacApp 3.0, including full
support for the AppleEvents, Edition, Help, and Alias Managers. MacApp provides a
powerful object-oriented interface to these Managers, in most cases eliminating the
need for developers to access these Managers directly. For example, MacApp 3.0
enables applications to fully support System 7's Edition Manager without ever making
a call to any Edition Manager routines.
MacApp 3.0 also supports the new services provided in System 7 by the Gestalt,
TextEdit, Resource, and Window Managers. Implementing support for these System 7
managers helped to illuminate many areas where MacApp was deficient. The
framework was extended to support the new managers, and it was also improved in
many places to address the newly illuminated deficiencies.
AppleEvents
MacApp 3.0 supports the required AppleEvents, and provides a framework for
applications to support additional AppleEvents. To do this, MacApp 3.0 includes several
new classes, TAppleEvent and TAppleCommand, as well as extensions to TEvtHandler.
MacApp internally represents an AppleEvent as an instance of the TAppleEvent class.
The class helps to encapsulate AppleEvent behavior, such as writing to and reading
from the AppleEvent message data structure, and retrieving AppleEvent attributes,
such as the AppleEvent address and priority.
When MacApp receives an AppleEvent, it creates an instance of TAppleEvent, asks
TAppleEvent to internalize the AppleEvent, and then creates a TAppleCommand to serve
as a courier of the TAppleEvent. MacApp then propagates the TAppleCommand down the
MacApp command handling chain by way of TEvtHandler's DoAppleCommand method.
Edition Manager
To support the Edition Manager, MacApp 3.0 includes various specialized classes for
supporting the Edition Manager's menu items: section events (which are Edition
Manager AppleEvents) and editions. The TEditionDocument class, a subclass of
TFileBasedDocument (described below), encapsulates most of this support. If you
subclass your application's document class from TEditionDocument, you'll get support
for the Edition Manager menu items, and management of your document's publishers
and subscribers.
Although MacApp's support of the Edition Manager required only a few specialized new
classes, it illuminated the need for several more-a generalized view adornment for
supporting Edition Manager borders, and a mechanism for specifying subranges of
data. Also, MacApp now unifies the protocol to support the Edition Manager and the
Scrap Manager by way of a flexible stream interface (see below).
A MacApp 3.0 application dispatches Edition Manager AppleEvents, called section
events, using its AppleEvent dispatching scheme described above. The TSectionMgr
class, a subclass of TEvtHandler, encapsulates handling of the section events. An
instance of TSectionMgr is installed at the end of the target chain when an application
initializes MacApp's Edition Manager support. TSectionMgr implements a poor-man's
AppleEvents Object Model mechanism for targeting the section events to the
appropriate section object.
Publishers and Subscribers
To support publishers and subscribers, MacApp 3.0 includes TSection, an abstract
class that encapsulates common publisher and subscriber behavior. TSection objects
can save themselves to and create themselves from resources which are saved with the
document. Each TSection owns a TDesignator (described below) so that each section is
associated with a portion of the document. Each section's TDesignator is saved with the
section. TPublisher is a specialized subclass of TSection that can create and delete its
edition container file (see Inside Macintosh volume VI, chapter 4), write to the
container file, and maintain the modification date of the data that is published.
TSubscriber is a specialized subclass of TSection that can read its data from an edition
container file, and go to its publisher.
View adornment
MacApp 3.0 includes a powerful framework for view adornment that is used to image
edition borders. Since adorners influence a view's appearance, edition adorners frame
a portion of a view to indicate that the portion is a published or subscribed section.
Creating the borders is the responsibility of the document that "owns" the publisher
or subscriber, working in cooperation with the view in which the section is imaged.
The generalized view adornment in MacApp 3.0 takes the form of the TAdorner class
hierarchy. Adorners, a flexible way of modifying how a view renders, were initially
motivated by the need to implement borders, which are required to provide user
feedback on editions. Adorners are also used for other purposes, such as adorning
controls (as determined by the fAdornment field of TControl), drawing a window's
resize icon, and for drawing print feedback.
TView owns a list of TAdorner objects over which the view iterates while rendering.
Adorners get a crack at imaging either before or after the view's Draw method is
called, depending on the adorner's priority (which is assigned when the adorner is
initialized). When adorners get called to Draw, they can assume that their view is
focused. This means that there is little overhead to support imaging of adorners since
no extra focus is required.
Adorners can be used to augment a view's appearance in a number of ways: giving it a
drop shadow, framing it, or (in the case of the Edition Manager) imaging a border for
publishers and subscribers imaged in the view.
Interestingly, adding adorners addressed a criticism of MacApp 2.0. They eliminated
the need for a Focus call in DrawContents after drawing a view's subviews. This was
necessary in MacApp 2.0 to call the view's HighlightSelection method. MacApp 3.0
includes a specialized adorner that simply calls its view's HighlightSelection method.
Any view that requires its HighlightSelection method to be called (e.g., TGridView)
must attach this adorner. Thus, the overhead for focusing after drawing only occurs if
the view has any adorners over which it must iterate. For the common case in which a
view has no adorners, the view won't need to focus after drawing.
Designators
MacApp's Edition Manager support leverages heavily off a new feature of MacApp
3.0-designators. It is important to understand and implement support for designators
before attempting to implement Edition Manager support. Designators are implemented
as a TDesignator class hierarchy.
TDesignator provides support for user selection and sub-range specification in the
document. Instances of this class designate a "chunk" of the data managed by the
document. For example, a subclass of TDesignator, TLinearDesignator, might
represent a linear selection in a word processing document, consisting of a beginning
and ending offset into the text. Designators help MacApp implement a simple protocol
for talking about the user selection. This is necessary to support the Edition Manager
in its mapping of a publisher or subscriber to a chunk of the document.
Help Manager
Extensions to TView to support the Help Manager are another way in which MacApp 3.0
has been enhanced to support System 7. TView now has an instance variable called
fHelpID that contains the ID of the help resource for that view. Whenever the mouse is
over a view and "Show Balloons" is on, the Help Manager displays a balloon containing
the text in the help resource that corresponds to the view's help id.
The sleep region that is computed for the call to WaitNextEvent takes into account any
view that handles help. This ensures that if the mouse strays outside the view that
handles help, MultiFinder will wake up the application so that MacApp can hide that
Help Balloon.
Great New Features
There are several new features in MacApp 3.0 that weren't primarily motivated by the
need to support System 7.
Change notification
MacApp 3.0 includes a powerful change notification architecture. This architecture is
tightly integrated with TObject, so that any object can register itself as a dependent of
any other object and receive update notification when any of its dependents are
changed.
The heart of this change notification architecture is the UDependency unit, which
implements the TDependencies class (a TSortedDynamicArray), and maintains the
dependencies between various objects. For example, you might use this change
notification architecture if your application has multiple views on the same data. You
would set the views to be dependent on the data objects managed by the application.
Whenever any one of the data objects changes, all the views get automatic notification
and can render using the new data.
In general, whenever an object changes, a three-phase process occurs:
• The first phase marks all dependents of the changed object.
• The second phase updates all dependents by calling HandleUpdate on each
dependent. HandleUpdate will do nothing if the object is unmarked. If the object
is marked as needing updating, HandleUpdate then calls all the object's
notifiers to update themselves if they are in need of updating. HandleUpdate
then calls DoUpdate to update the object. Last, HandleUpdate unmarks the
object, and asks all dependents to update themselves.
• The third phase unmarks all dependents.
Using this change notification architecture increased the recalculation speed of the
MacApp example spreadsheet, Calc, by several orders of magnitude for a complex
spreadsheet.
Behaviors
Another new feature in MacApp 3.0 is TBehavior, which can be attached to any
TEvtHandler or subclass to modify the behavior of the TEvtHandler. TBehavior objects
get a crack at any user action before the TEvtHandler to which they are attached.
TViewBehavior, a specialized TBehavior, modifies the behavior of views. A subclass of
TViewBehavior might intercept a mouse-down on a view to implement selection and
dragging of the view. Attaching this behavior to any view would enable the user to drag
any view without the view itself having any knowledge of the dragging behavior.
For example, in MacApp 3.0 TPrintHandler descends from TViewBehavior. This allows
printing to be thought of as a specialized behavior added to views to augment view
behavior with printing capabilities.
Streams
After many years of gestation in the lab, MacApp finally includes a stream unit, thanks
to Larry Rosenstein. There is even protocol at the TObject level for writing an object
to and reading an object from a stream. At this point, streams are implemented for
reading and writing files, editions, and the scrap. Enhancements
New library organization
The most immediately obvious change in MacApp 3.0 may be the new library
organization. Each major class now has its own unit. In contrast to MacApp 2.0, which
has a public interface consisting of 19 units, 65 classes, and 1404 methods and global
routines, MacApp 3.0's public interface currently consists of 59 units, 137 classes,
and 2383 methods and global routines. Yet, in spite of this increase to the MacApp
libraries, the size of applications that use MacApp 3.0 should not grow by more than a
small percentage over the size of comparable 2.0 applications. (The operative word
here is "comparable.")
The libraries were reorganized in order to add significant new features to MacApp
(such as System 7 support and a flexible change notification mechanism), to
incorporate many building blocks (e.g., UTearOffMenu, UFloatWindow,
USynchScroller), and to break out classes into their own units (to speed up build
times and maintenance).
The result of this new organization is better modularity, allowing classes and methods
to be more specialized. It also allows faster access to classes of interest, and reduces
build times. For example, a change to TView's DrawContents method simply requires a
quick recompile of the UView unit. In MacApp 2.0, a similar change caused a lengthy
recompile of the entire UMacApp unit. This change is primarily a benefit to the
MacApp team in the development of MacApp, since most developers rarely-if
ever-change MacApp source. (Yeah, right.)
Totally revamped TDocument
For those of you who have grown tired of working around TDocument's file-based
nature, there is good news. TDocument in MacApp 3.0 is little more than a view
manager. TFileBasedDocument, a subclass of TDocument, now contains most of the
file-based nature of TDocument. In fact, a new class called TFile encapsulates the
knowledge of how to write to and read from disk-based files.
TFileBasedDocument interacts with TFile by means of a new class called TFileHandler.
TFileHandler manages the process by which a document writes to or reads from a file.
TFileHandler decides which of several algorithms to use when saving the file, and
defers to the document when it comes to actually reading a data stream from the file.
So, subclasses of TFileBasedDocument can map their data to one or more files by means
of several TFileHandler instances, each of which interacts with a given file. This new
document architecture has allowed each of these classes to be specialized for their
particular task, and for TDocument to be as lean as possible.
TEvent
A new class, TEvent, now encapsulates common behavior for all kinds of events. This
unifies all types of events, whether commands posted to the system-such as subclasses
of TCommand-or toolbox events, now encapsulated in a new class TDeviceEvent.
TCommand has been split into TCommand and TTracker, a descendant of TCommand.
This division clarifies the purpose of TCommand.
Any subclass of TCommand that requires mouse tracking should now descend from
TTracker. TTracker contains all knowledge of mouse tracking, thus cleaning up
TCommand significantly and ensuring that simple subclasses of TCommand don't need to
carry around the knowledge of mouse tracking.
TView and floating windows
The change to TView's focusing is a major change that provides significant performance
benefits to MacApp 3.0 applications. Essentially, where in MacApp 2.0 views were
required to focus before performing any geometric operations (e.g., ViewToQDPt,
ValidVRect), MacApp 3.0 views can perform these geometric operations without
focusing.
This was accomplished by splitting up TView's Focus into two methods,
UpdateCoordinates and Focus. The former is called whenever a view's geometry
changes, such as when a view is moved, resized, or added as a subview of another view.
The latter is called when a view renders. Consequently, the work required to Focus is
reduced, as is the need to focus for a variety of common operations.
Floating windows are now fully integrated.
Attention to detail
Detailed improvements throughout MacApp include: allocators now signal failure, all
classes implement Initialize, and many classes implement deep-cloning.
Since allocators now signal failure, it's possible to remove many calls to FailNIL from
your code. For example, you no longer have to call FailNIL after creating an instance of
a class; the allocator routine that created the instance of that class calls FailNIL for
you. This should help to reduce code size.
Since Initialize is now called from the object allocator routine, and all classes
implement Initialize, it's now possible to put a newly created object into a known
state. This helps significantly in failure handling, so that a newly created object can
immediately be freed if a failure occurs. Since the Initialize method isn't allowed to
allocate memory, its use is somewhat restricted. However, it is generally useful to
modularize initialization of instance variables.
Finally, many classes implement deep-cloning in their Clone method. Deep-cloning is
essentially a shallow clone plus special treatment of references to owned objects-in a
deep clone, owned objects are also cloned. An object's reference to another object is
considered an "owned" reference if the object will be freed in the class' Free method,
or if it was created during the class' initialization.
Implementation in C++
MacApp 3.0 is implemented in C++, but will continue to provide complete access from
Object Pascal by a set of interfaces. However, C++ users will benefit from powerful
new capabilities that use features only available in the C++ language. These
capabilities include list iterators and full-blown classes for Rect, Point, VRect,
VPoint, and String.
For example, the Rect class contains methods to perform Rect addition, equality tests,
and intersections. The String class contains methods to perform string concatenation
and character access.
In general, using these classes improves code readability and performance. Where
possible, the methods in these classes are declared inline, which eliminates the
overhead required for a subroutine call.
Summary
At press time, MacApp 3.0 is in its early alpha stage. All the features and
enhancements documented in this article have been implemented. The principle feature
in MacApp 3.0 is System 7 support. The other added features were either motivated by
developer requests (such as the change to TDocument), or by the need to make up for
deficiencies that were illuminated during the implementation of System 7 support.
We hope that MacApp 3.0 will provide developers with a solid basis for development of
ever more powerful and attractive applications.