Quadratic Plotters
Volume Number: 5
Issue Number: 8
Column Tag: MacApp Workshop
A Tale of Two Quadratic Plotters 
By Chuck McMath, Carl Nelson, MacApp Developer's Assoc.
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
[Author’s Note: This article is NOT about MacApp. We do not cover the basics
which you can get elsewhere. For example, this article does not discuss the concepts
behind Object programming and Object Pascal syntax, nor do we explain the
organization of all the MacApp classes. The December 1987 MacTutor, Kurt
Schmucker’s excellent book Object Oriented Programming for the Macintosh, the
January 1989 APDALog, the newsletter of the MacApp Developer’s Association
(Frameworks ) or the MacApp documentation itself are good reference sources. You
may understand some of this article without knowing the basics behind Object Pascal
or MacApp, but don’t count on it! The programs discussed in this article were written
using MacApp 2.0B8. By the time this is published, the final MacApp 2.0 should be
available. As far as we know, no changes to this code should be necessary to compile,
link, and run it under the final version of MacApp 2.0, but one never knows]
This article is about the conversion of Dave Kelly’s and Dave Smith’s Plot
Program into a MacApp program. The program originally appeared as part of the
Multifinder Friendly MacDraw Plotter article in the February 1988 MacTutor.
Actually, we will discuss two conversions, one written by Chuck McMath (this month)
which attempts to literally translate the original program into MacApp and the other a
conversion (next month), by Carl Nelson, ports the intent of the original program into
a MacApp program. We review the original article and program, then expose the
mechanics and decisions involved in doing both conversions. Along the way, we will
show you the basics of physically creating a MacApp program and give you some of our
reasoning and insight as to why we did what we did. Hopefully you can use this wisdom
to do the same as you start writing programs using MacApp. Most of us using MacApp
have learned “the hard way” - hours of working on projects until we reach
‘enlightment’ on the techniques needed to take advantage of object programming
languages.
The motive of this article is not to present the blinding vision of enlightenment
on how to learn MacApp but rather a pragmatic approach, an exploration of two ways
that might work. We know the starting point (the original program) and the ending
point (the original program rewritten using MacApp). Let’s travel along the path
from start to end with two programs, one a simple clean translation and the other a
program that produces the same effect but is internally quite different -- the low road
(direct translation) and the high road (conversion and expansion). Two points of
view, and two results. From both of these you can gain insight into the mechanics of
MacApp and a brief look at how Objects and MacApp change the way you look at writing
programs.
Introduction Revisited
While Carl’s viewpoint is the elegance and power that MacApp gives you in terms
of code reusability, my perspective is somewhat different. The main impression that I
had when I looked at the ‘Multifinder Friendly Plotter’ was “hey -- this program is
all user-interface.” Although it does spend some time solving a quadratic equation and
fooling around with picture comments, most of the code is involved with the plot
parameters dialog box, putting the window up, handling events correctly, etc. Of
course, you might say those features are necessary in any Macintosh program, right?
Absolutely! These ‘common user interface’ features are what MacApp is all about. My
point of view is that MacApp provides a full-blown user interface, freeing you from
having to worry about those details, and letting you concentrate on what makes your
application different. You can turn your application out more quickly, and have it be of
higher quality and greater reliability. The bottom line: less work!
I took the design of the plotter application and decided to implement it just as it
was. I didn’t make any changes to the overall design to take advantage of MacApp, but
(and this is an important but) I also did not limit the program to take away any of
MacApp’s benefits. For instance, the original plotter program puts up a dialog box,
waits for you to enter parameters, and then plots the quadratic equation in a new
window. If you replot the equation, you get a plot in the same window -- i.e., only one
window is on the screen at a time. MacApp, however, implements multiple windows as
a given; instead of throwing out MacApp’s multiple window feature, I allowed the new
plotter application to take advantage of that capability. The result is that the new
plotter application allows you to produce as many plot windows as memory allows --
and better yet -- this was done with very little work on my part.
The Original Program
The original plotting program was written just after the new Macintosh ROMs
became widely available. Since MacTutor likes to keep on the cutting edge, the two
Daves decided to write a program which implements as many features as they could
cram into a coherent whole. While the explicit purpose of the program is to solve
quadratic equations, the real purpose was to showcase some of the new features in the
Mac ][/SE ROMs. The new features being exhibited included:
- calling SysEnvirons to determine if the machine supported the required new
features
- resources to specify colors in menus/ windows
- hierarchical menus used to select colors for parts of the plot
- Multifinder support through calling WaitNextEvent and acting on Multi finder
events
- capability to save plots as PICT files, and the use of PicComments to group
logically related items
- hairlines on the LaserWriter through using PicComments.
The operation of the program is fairly simple: when Plot is selected from the File
menu, a modal dialog box appears. The user types numbers in for the quadratic
equation parameters and some plot parameters, then clicks the OK button (there is no
Cancel button). The dialog goes away, and a plot window appears, with the solution to
the quadratic equation plotted in the window. The plot is sized so as to always fill the
entire window (minus allowance for the scroll bars, of course). A Graph menu lets the
user change the color of the axis, the plot, and the background items. A Print Options
menu allows the user to specify whether the printed output should be actual size (i.e.,
the window size) or a full page size. The Graph menu features three hierarchical
menus displaying the colors which may be chosen for the three elements of the plot
which can be colored (the axes, the plot itself, and the background). The colors
available are the 8 supported in the so-called ‘Old QuickDraw model.’ The window can
be resized, dragged, zoomed, and closed. The contents of the window may be saved, and
if saved, the file written out is a PICT file which can be opened in MacDraw. If the file
is opened in MacDraw, individual items can be manipulated, as if the plot were drawn
in MacDraw itself. Selecting Plot again while a plot window is up brings up the modal
dialog again, but when it is dismissed, the plot is redrawn in the existing window (only
one window is allowed on the screen).
This is only an overview of the original program and this short description
cannot do it justice. If you’d like to go read the original article, we won’t be offended
(just come back to this one.) And now, back to Carl.
MacApp Provides Features
As Chuck said, the original article and program were written to show new
features introduced into the Macintosh environment in early 1988 and not as the best
or even the right way to do plotting of equations. The original article and the example
program show a style for teaching and exploring programming. In order to accomplish
their task, the two Daves drew source code from Professional Programmers Extender,
past issues of MacTutor, Dave Wilson’s Examples and Steve Sheet’s Code, Inside
Macintosh, and no less than 13 Apple Tech Notes. Several times in the article you can
find a Dave saying: “It is much faster to cut and paste source code from someone else,
than to reinvent the wheel yourself.” The theme of this article, and many MacTutor
articles, could just as well have been learning through reuse and extension.
Programming by extending and reusing code is one of premises of an application
framework and a key feature of languages containing Objects. We show how reuse and
extension can occur when bringing the MacTutor Plot program into the MacApp
framework. Writing this a year later and having version 2.0B8 of MacApp in hand,
many of the concerns presented in the article are non-issues. To be specific:
• Update Events & Growing the Window
MacApp users in general (and specifically for a program the size and complexity
of Plot) never worry about update events. The MacApp architecture nicely handles
setting things up for you so that all you have to do is draw when asked to by MacApp.
MacApp programmers seldom worry about events, the event loop, Desk Accessories or
switching in and out under Multi finder. There is a very robust event handling
architecture provided by MacApp and it has been our experience that as the MacOS
changes, Apple has enhanced MacApp to match the changes.
• Multi finder
Being Multifinder friendly and having to deal with WaitNextEvent are
non-issues. MacApp properly handles all of it as long as the ‘SIZE’ resource is
properly set up. If background processing is needed the right objects should be
overridden.
• Palette Manager
The MacApp DrawShapes Example program provides us with an example of how to
setup a custom Palette color with a single call to the toolbox GetColor routine.
• Menus (Color, hierarchical or otherwise)
MacApp has a very interesting and useful scheme for handling menus and the
actions to be taken when menu selections are made. MacApp uses a resource named
‘cmnu’ which is the same as a regular ‘MENU’ but adds an integer to the end. This
integer serves as a unique command number for that entry. The cmnu resources are
post-processed by a utility called PostRez. It splits apart cmnus and creates a regular
MENU and a table that maps these command numbers to menu items for use in your
program. MacApp uses these unique command numbers to inform you of menu
selections as well as handling the standard kind of menu functions like New, Open and
Save. Command numbers 0 through 1200 are reserved for use by MacApp; the rest
are available for use in your program. The nice side effect of this is that you can
rearrange menus without regard to where they reside in the MENU BAR or their item
number. All you ever deal with is the unique command number. Of course there are
good examples of custom Menus in both the MacApp Developer’s Association ‘Goodies
Disk’ and the MacApp samples shipped by Apple.
• Saving
MacApp provides all the code surrounding New, Save and Save As. If all of your
data fits into memory at once, you will have to implement only two procedures,
DoNeedDiskSpace and DoWrite, to save your data. You do not have to worry about temp
files, disk space and errors, as MacApp provides a powerful error handling mechanism
to handle general case failures so you do not have to.
• Printing
By creating a standard print handler object and assigning it to a view (objects
which display things in MacApp) a view can be made can be made to print as well as
display on the screen. Because of this mechanism, you usually do not have to worry if
you are drawing to the screen or a printer port.
• Machine/System Determination
MacApp provides compile time variables (qNeedsScriptManager,
qNeedsROM128K, qNeedsHierarchialMenus, qNeedsStyleTextEdit,
qNeedsWaitNextEvent, qNeedsColorQD, qNeedsMC68020, qNeedsMC68030,
qNeedsFPU) that you can use to configure your application so that if your combination
of features is not present, your application will safely exit.
With all of the above support, I will not dwell on these features of MacApp
[Chuck’s note: I will dwell on them to some extent.]. We will take this support as
given and refer to them as appropriate in our discussions of our conversions. Again,
you can find ample documentation as to how these work by reading the MacApp 2.0 final
documentation, browsing the sources which are supplied with MacApp or looking
through the 1988 and 1989 issues of Frameworks which discuss the MacApp 2.0
Architecture.
With all this out of the way we can move on to discuss our conversion efforts.
Chuck gets to go first and discuss his efforts then I will talk about my conversion.
Overall Program Design
The crucial point of creating a MacApp program comes before any code has been
written: design is the key, for if you have a good object design the code becomes obvious
(well, if not ‘obvious,’ then maybe ‘more clear’). A good design can be understood by
many people. A bad design is usually not even understood by its creator after a short
time passes. The major design of our objects is easy. This is because we are using
MacApp. By using MacApp, you ‘sign a pact’ to become a member of the MacApp
community. This agreement says that you will accept all of the wonderful features
MacApp provides for your application; in exchange, you agree to be a good citizen and
write your application according to a particular structure. This structure enables you
to take advantage of the MacApp code, and in a sense, drop your code into MacApp and
have it work. The difficult part of object design is deciding where the things you are
familiar with fit in. When you start out, you will often be asking yourself which
feature and parameters belong to which object. As we’ll see, determining this is
usually easy, once you understand the objects MacApp provides for us, and how they
relate to the pieces of your application. In this particular case, the design is made
easier in that we are simply converting an existing program to MacApp, and I am
following the original design as much as possible. This means that many of the
decisions have been made for us.
MacApp has a number of predefined object classes which equate to the different
portions of an application. These predefined objects have the ‘generic Mac thing’
behavior already implemented. For instance, there is an object which corresponds to
the overall application -- TApplication (incidentally, MacApp object types all begin
with a capital T. This is, I suppose, to remind you that an object is simply a declared
type in Pascal). An object of type TApplication ‘knows’ how to do all of the things that
a good application must -- handle desk accessories, put up an about box, work under
MultiFinder, launch when double-clicked upon in the Finder, open a document when
the document is double-clicked, etc. Therefore, you don’t have to do anything to your
application to have it exhibit this behavior. Instead, you worry about the part of your
application that is different from a standard application. One aspect of MacApp
programming that you will become accustomed to is that you don’t do all of the work --
you let MacApp do it for you. This can make you somewhat confused, for in many cases,
you only implement a few methods for an object -- yet it seems to be quite a
sophisticated object. This is because you are taking advantage of the predefined MacApp
object behavior. Don’t worry, you will quickly get used to not doing all of the work
yourself -- and you might even like it.
MacApp Function [TOKEN:21365]bclass
TApplication Manages Documents TQPlotApplication
launch, open, etc.
TDocument holds data ÂTQPlotDocument
stored on disk
TView Displays data in TQPlotView
windows
Table 1: MacApp Objects and QPlot Descendents
Table 1 shows a simplified description of the predefined MacApp objects, what
they are responsible for, and the descendent objects defined in the plotter application.
Keep in mind that when you define an object and modify its behavior, you can be doing
one of two things: adding some behavior that does not exist, or changing some existing
behavior. Our TQPlotApplication is an example object which was created for both of
these reasons. Behavior has to be added to it to handle the colorful hierarchical menus,
for example. And some behavior also has to be changed -- and this is where experience
with MacApp becomes necessary -- because the generic MacApp behavior of an
application is to open a blank document when you double-click on the application in the
Finder. In our case, this behavior is not what we want to happen, so, I decided to
override the method which produces the blank document. This requires ‘behavior
modification’ for our TApplication object.
As you can see from the table, this application requires only a few simple objects
(remember, my goal was to do a quick conversion and not to make the application into
the ultimate MacApp program). There is, of course, an application object, since every
MacApp program has to have an application object -- TQPlotApplication. There is a
document, since we are concerned with storing data on the disk -- TQPlotDocument.
There is a view, since we are displaying data in a window -- TQPlotView. And, last,
but not least, there is the dialog view, since we have a dialog which requests the plot
parameters -- TDialogView. Note that the dialog view is not a specialized view we
defined. Why is that? Well, when you look at what a typical Macintosh dialog view lets
you do, you will realize that MacApp’s dialog functions are ample to do what we want,
e specially since our dialog is a simple one. The other objects, though, warrant some
explanation to show what they provide outside of the generic MacApp structure. Which
leads us to the question:
How does anything get done?
Good question, Chuck! Things get done in MacApp by having one of the
conceptual‘control chains’ take over. The most prevalent ‘chain’ used in MacApp is
the inheritance chain. Many parts of MacApp use this sort of chain to pass control on
from child (subclass) to parent (superclass). Knowing where to hook in by
OVERRIDEing is the key to using this and the other chains. The other chains are:
1) The command chain routes system generated events to objects that want to handle
them.
2) The idle chain is traversed during idle to give selected objects periodic chances to
execute.
3) The choice chain informs a view object’s parent views that its state has changed.
Knowing these chains exist help you to determine where as well as who does the
work and gives you conceptual frameworks around which to build your application.
Now what were you going to say about the plotter application, Chuck?
In the plotter application, nothing happens unless the user selects something
using the mouse, so we will only consider mouse down events, which are part of the
command chain (incidentally, you don’t need to know much about these ‘chains’ in
order to program in MacApp, although knowing what is going on under the hood helps
you to understand when things turn out differently from what you expect. But back to
mouse downs) In addition, I’ll classify the mouse downs into either menu bar hits or
‘other.’ Initially I will discuss menu bar hits and how you translate from a menu
selection to some activity.
In a non-MacApp program menu enabling and disabling is tied quite closely in
with activate and deactivate events. Of course, this makes perfect sense, because when
you activate a window you want that window’s menu items enabled, and vice versa when
the window is being deactivated. There are a few inherent problems with this
approach, however. First, you need to have special code in your activate or deactivate
event handling procedure to eat up successive activate/deactivate events (the way
Chernikoff’s MiniEdit does it) or else you will end up in the wrong state -- with the
wrong items enabled. Second, this technique is not easy to extend or modify, and if you
end up adding a new window type, then you add volumes to the code.
MacApp and Menus
In MacApp, menus are handled quite differently, as Carl has described above.
MacApp tries to ‘unhook’ the connection between an item in a menu and an object which
handles that item. Instead of hard-coding menu processing in a gigantic procedure,
MacApp introduces the concept of commands and command numbers. Think of each
menu item as representing a different command, and each command having its unique
number. Some commands will best be handled by the application -- New, Open, and
Quit for example. Other command are the domain of the document -- Save, SaveAs and
Close. Still other commands should be handled by the object which draws the data --
turning grids on and off, aligning objects, perhaps cutting and pasting. So you can see
that instead of a central processing repository for all ‘menu actions,’ MacApp farms
the commands out the different objects. In fact, this is a crucial tenet of object
programming: an object will only deal with information it knows how to process.
Therefore, in MacApp, each object is given a stab at a menu selection. If the object
‘knows’ how to handle that command, it does whatever it is supposed to. If, however,
the object doesn’t recognize that command as something it knows about, the object just
passes the request along. Eventually either some object somewhere handled the menu
choice, or MacApp senses that no object handled the menu choice, and that choice is
simply thrown away.
Menu enabling is handled in the same distributed way as menu selection: there is
a special method defined for objects in MacApp where they enable or disable the menu
items they are concerned with. This ensures your program will never see a menu item
enabled if it shouldn’t be, because the only way that item is enabled is if its
corresponding object exists. In fact, whenever MacApp determines that the menu bar
could have changed, it disables every item in all of the menus. Only after all items
have been disabled do the individual existing objects get a chance to enable their
functions.
Using a Command to Do Work
When a selection is made from a menu, the object that chooses to handle the menu
selection can do one of two things: go ahead and perform whatever action was called for
(in easy or trivial cases), or create a command object to perform the command and
pass it back to MacApp to be executed (more difficult cases). Command objects are the
technique MacApp uses to implement an action and its associated Undo and Redo. Since
all of the actions in the plotting program were simple and could be undone easily, my
version of the plotter application does not have any command objects. [Carl’s Note: My
version does]
What about mouse downs that aren’t in the menu bar? They are normally handled
by a method called DoMouseCommand (pretty obvious name, eh? Most MacApp methods
attempt to be as obvious) but since the plotter application is a simple one, we don’t
have anything to do in this method. All of the normal Macintosh behavior is already
provided for us by MacApp: dragging the window around; clicking and dragging in the
grow box, and resizing the window when we release the button; clicking in the close
box to indicate that we want to put the window away; clicking in the zoom box to zoom
or unzoom the window -- all of these normal, everyday Macintosh features are there
-- yet we didn’t do a thing about them. This is the power of MacApp.
The Application Object
The application object, as we have mentioned before, is responsible for the things
a good application does -- handling chores at startup and quitting time, creating and
opening documents, handling desk accessories, and of course handling and sending
events to the appropriate object. What does the object we created for this program do?
Remember, that we can either add functionality to our object, or override existing
functionality to change the behavior of our customized application object. Outside of
initialization code, my application does one thing: handle choices from the Graph menu,
which allows the user to choose the color of the axis, plot, and background. Since we
want to be able to set a default for the next plot, the application object has three
variables which will correspond to those colors. Selecting from these menus only
changes the application’s variable. When a plot is created it uses the current set of
default colors. Once a plot is created, its colors cannot be changed.
The Document Object
The document object is principally responsible for only one thing: saving and
restoring its data. While there are certainly other things the document must be
concerned with, most of the document’s methods will involve either saving and
restoring, or manipulating its data structure -- the document is the object which is
responsible for maintaining the data that our application requires. In addition, each
document will usually have a window associated with it, and it will be used to display
data from the document.
The standard document knows that it must be able to save and retrieve itself from
disk, however, there’s no way the generic document can know how you want to store
your data. So, if you were to look in the MacApp source code, in the section that dealt
with saving the document, you would find that the DoWrite method is called. However,
looking at the DoWrite methods would not help you, since the TDocument.DoWrite
method is empty. So what’s going on here? Well, this design takes advantage of the
inheritance chain Carl spoke of earlier. When you define your document class, you
will implement your save routine for the DoWrite method that OVERRIDEs (replaces)
TDocument’s DoWrite (this is where you need to understand the concept of OVERRIDE).
When Save is selected from the File menu, DoWrite is called for the document (your
document) and its DoWrite method gets called -- all because of the hook put in by
MacApp. You don’t have to write any code to handle disk errors, not enough space, etc.,
because MacApp does it for you by calling DoWrite at the right time and under the right
conditions. MacApp is full of these hooks -- calling a placeholder procedure that
serves only to occupy space until you OVERRIDE it to provide the needed functionality.
In many cases you are only defining these needed override procedures. Sometimes it is
frustrating and confusing or seems quite magical as to where to find these magic place
holders that you can override to get the work done. The only advice we offer here is
patience. Reading the documentation sometimes helps, looking at source code of sample
programs that behave the way you want is also a good way to discover these
placeholders and of course reading articles like this one.
So what does the plotter document do? It has its own initialization code where it
clears its copy of the quadratic equation parameters, and creates the window and view.
Since the document does this work in my application, it has a method which will
present the dialog box to the user and allow the user to type in parameters for the
quadratic equation. Solving the equation is another function the document performs
(note: the application object could have been chosen to solve the equation; I chose the
document object since each document carries its own set of equation variables). And of
course we need methods to write the plot out to disk. Since my plotter application does
not read its data back in, there are no methods to read the PICT file, only ones to write
it.
The View Object
The view exists to display the data in a window. Since the application draws
quadratic equations, this is the main function of the view. However, there are some
other functions our view must perform. Since good object design says data should be
encapsulated, the view is the only object which knows its size, it is responsible for
calculating how large it is. In the plotter application, the view can be one of two sizes:
either the view is the entire size of the window or it is the size of a full page. When
the window changes size, MacApp sends a message to the view, telling it to recalculate
its size. Our view must know whether it is full page size or not, and depending on that
status, must calculate its size. Since the view size can be changed by a menu choice,
the view must have the necessary code to handle that menu choice. And of course, there
must be code to enable the menu choices that the view can handle.
One big difference between the MacApp and the original version of the plotter
program is that the original plotter explicitly passed which device was being drawn on
-- the screen or the LaserWriter -- while the MacApp program doesn’t know. The
original program did this to optimize operations for the LaserWriter (as all good
Macintosh programmers do): for instance, when drawing on the LaserWriter, the
original program did not fill rectangles with white (since on the LaserWriter this
operation is just a waste of time). My MacApp version did not follow this approach
because in the MacApp structure, printing is just the same as drawing on the screen
-- the view doesn’t know and shouldn’t care which is being done. Although, I could
have queried the global variable gPrinting to find out if the view was printing. MacApp
takes care of all the device-specific setup, and our view just draws.
The Dialog Object
The dialog which initially appears is a simple one, but it serves to illustrate just
how much MacApp does for us, and how much we don’t have to worry about. Think
about all the steps required to put up a good-looking dialog that simply requests some
values: after the resources have been created we need to get the dialog (it’s invisible),
install a procedure for the UserItem (to outline the default button), stuff initial
values, make the dialog visible, drop into a ModalDialog loop, if the default button was
pressed then grab values from the dialog items and convert them back into numbers.
The design of dialogs in MacApp incorporates much of this tedious process into a
complete package that again gets done for us. Sure, MacApp dialogs aren’t free, but as
they get more complex, MacApp’s contribution becomes more and more significant.
Just what is a MacApp dialog?
While in the ‘normal’ Macintosh environment a dialog is a special item, in
MacApp, a dialog is simply a view (i.e., a place where data gets drawn) with a bunch of
views within it. Every item in the DITL that can handle some interaction is a view, and
these view types correspond pretty intuitively to the DITL types: TCheckBox,
TRadioButton, TButton, TEditText, and TNumberText are the major types. As you may
by now assume, these classes respond to mouse clicks in the way we’d want them to: for
instance, when the check box is clicked on, the TCheckBox class flips an internal
variable and checks the control. So we don’t have to do anything special in this dialog
to get the kind of interaction we want (in fact, MacApp also does the little things like
highlighting the default button, so we have even less work to do).
Our dialog box is pretty simple, since the only items in it that can respond to
events are the six parameter EditText items. Each of these items is represented as a
TEditText object in MacApp. By now you should realize that each of these ‘dialog object
types’ has default behavior that corresponds to what we expect when we are in a dialog
-- in our case, the TEditText objects respond to clicks and keystrokes. Hitting a tab
while in a field causes the succeeding field (in DITL order) to be highlighted. This
behavior is what we want, for the most part.
One important sidelight is that MacApp 2.0 does not use the Dialog Manager for
its dialogs. This has a number of implications: dialogs are handled like other windows
-- in fact, you may have noticed that when a modal dialog comes up, the menus all get
disabled except for the Edit menu. With MacApp, you can use the Edit menu while your
modal dialog is up. Another interesting feature is that since these modal dialogs are not
real dialogs, your application can be switched out under MultiFinder while a modal
dialog is on top. But back to our description
You may have noted that there is a TNumberText object, yet we used TEditText
objects for numeric fields. Why? Well, TNumberText is only used for LongInts, and
we want real numbers, so we will just take the strings and convert them to real
numbers ourselves (Unlike Carl, I wasn’t aware of Calvin Cock’s TExtendedText unit
that handles Reals, which he discusses later, so I (ugh) reinvented the wheel. Talk
about an advertisement for being in touch.).
What goes where?
One of the decisions you must make when writing a MacApp program is which
object a method belongs to. In many cases, it’s just not clear which object should have
the responsibility. Take cut and paste for example. On the one hand, you could
consider that cut and paste change the data structure associated with the document, so
they should be document-level methods. On the other hand, you could say that cutting
and pasting change the appearance of the items on the screen, so they should be
view-level methods. One way to tell that a method is in the wrong place is that you are
constantly (i.e., more than two or three times) having to refer to another objects’s
fields to accomplish even a simple task. You probably have the method in the wrong
class. Putting a method in the wrong class makes the method look messier or more
complicated than it is. You will usually find yourself doing something like:
a := TMyDoc.fMyView.fMyViewData.fGetaValue;
Another way to tell if a method is “misassigned” is to see if you rely on
information that isn’t plainly available to your object to enable or disable the menu
item associated with that command. If, for instance, you have cut and paste associated
with the document, and you need to ask the view if any items are highlighted so you can
tell if you should enable the menu items, then you either have a bad design or you have
the method in the wrong class.
Discussion of the program
At this point, I’d like to move through the source files and point out where you
will find the pieces I have described. The source files consist of three types of files:
Pascal (MacApp) source, resources, and the makefile. The source files will merit
most of the discussion, so let’s start with them.
Main program file (MQPlot.p)
All MacApp main programs look pretty much alike. After the necessary junk at
the beginning at the file, the main program starts. Main programs are boring, because
so much of the Macintosh functionality is rolled up into the application object. A
MacApp main program does the following: initialize the Toolbox Managers and other
needed functions, create the application object, call its initializing method, then call
its Run method. That’s it, and that’s usually always it; when Run returns the program
execution ends.
Unit file (UQPlot.p and UQPlot.inc1.p)
MacApp source files have a strange arrangement forced upon them by MacApp.
The .inc1 source file is organized by object hierarchy: first the application, then the
document, and last the view (remember, the dialog view does not have a customized
object because the standard dialog handling was sufficient for our purposes).
As we mentioned before, the application object is a simple one, since there is not
too much to do. In order to handle the menu choices relating to the color selections, we
need to override DoSetupMenus and DoMenuCommand, and both of those methods are
pretty easy to understand. Remember, the DoSetupMenus method is called by MacApp
frequently (you won’t believe just how frequently) so there shouldn’t be any lengthy
calculations here. If you need to do some complicated calculations to determine if some
menu item should be enabled, you should do it somewhere else (like in the idle chain)
and set a flag or value in your object; that flag can then be used for the menu enabling.
The only other method of interest is a real short one: HandleFinderRequest. MacApp’s
default behavior for applications is that when an application is launched from the
Finder by double clicking on the application icon, a blank document opens. Well, we
don’t want that to happen, since our document is the plot, and we wouldn’t want a blank
plot window on the screen. So, to modify this behavior, we just override
HandleFinderRequest and do nothing. Now when the application is launched, our
HandleFinderRequest is called instead of the default one, and we don’t get a empty
document. This method is a demonstration of the small things you sometimes have to do
to make your application ‘perfect’ -- and note that it’s not difficult, it just requires
knowing what’s going on and where to look. [Carl’s Note: In many ways this typifies the
MacApp design - the methods are there to do the kind of things you will need to do - you
just have to look for them.]
The document methods can be characterized into three categories: initialization,
solving the quadratic equation, and saving the PICT data. Initialization methods are not
very interesting, except to note that the DoMakeViews and DoMakeWindows methods
are some more of the methods which you are required to OVERRIDE or else your
program will not work (Actually that’s not really the truth -- with MacApp 2.0 you
can omit these overrides and your program will work, it’s just that you’ll get the
default view and window size/location). The method titles are fairly self explanatory.
Solving the quadratic equation involves a few methods which contain a lot of code lifted
directly from the original plotter application. All I did for these methods was put the
method syntax before and after them, and change the variable references to refer to the
document object’s variables instead of some global variables. The last few methods
deal with saving the data to disk. As mentioned before, there are really two methods
necessary to save data on disk: DoNeedDiskSpace -- which calculates if the file will fit
on the disk, and DoWrite -- which actually writes out the data. If there seems to be a
lot missing from the implementation of the disk handling, remember that MacApp does
most of the work for us -- opening the file and checking for errors. Again, all we have
to do is handle our application’s specific needs, which in this case is saving the PICT.
The view object contains some initialization methods (which should be quite
familiar by now). After that is a method called CalcMinSize. This method is called (by
MacApp of course) when the size of the view needs to be determined. Remember, the
view can be either the size of the window or the size of an entire page. So we need to
have a flag (fOnePage) which tells us which is the case. If we are an entire page, we
tell MacApp that our size is the size of our print handler’s page (since we want the
printout to be one page). If we are not the size of a page, we tell MacApp our size is the
size of our superview (our superview is the view which we are inside. In the case of a
simple view, our superview is the window itself). After the menu handling routines
(DoSetupMenus and DoMenuCommand) we have a large method which prints the plot on
the LaserWriter (again, this method was stolen directly from the original plotter
application and had some method syntax wrapped around it). The next two methods
were written to handle the case where the window is resized. In
SuperViewChangedSize, we check to see if we are displaying the plot in full page mode.
If not, when the window changes size we need to change the size of the view (since the
view mirrors the window size). The next method, Resize, has been overridden because
when the view size changes we want to force the view to be redrawn (i.e., when
switching between full page and window size display modes). The last view method just
draws the view, and it is also pretty simple.
Resource file (QPlot.r)
Resources for a MacApp program are almost identical to those for a non-MacApp
program. There are only two differences (and they are big differences): ‘cmnu’
instead of ‘MENU’ and ‘view’ resources. ‘cmnu’ resources (probably stands for
‘Command Menu’) are used to associate menu items with command numbers. Carl and I
have both spoken of the usefulness of command numbers, and I don’t need to reiterate it
here. The view resource is different, and is a crucial resource in your MacApp
program. Carl will speak about view resources in greater detail, but let me say that
the view resource allows you to specify the contents of your windows (in functional
areas). You can (and will) use views to specify your dialogs in MacApp, because these
dialogs are build out of views, as we mentioned earlier. The benefit of using a view
resource is the same as you get anywhere else in using resources: you can change the
resource without having to recompile the source code. While it may seem that this
benefit is limited with regard to views, I’m sure you can attest to the value of
resources in other areas, and isn’t it nice to be able to ‘tweak’ a resource quickly?
One other thing you should know about MacApp resource files is that MacApp has
a default number for the about box. If you can live with a boring about box (i.e., an
ALRT) then you should create it with id 201. That’s because as part of the generic
Macintosh application that MacApp implements, when you select the About
Application from the Apple menu, you get ALRT 201 displayed. If you like that, it’s great, and as always, if you don’t like it, you can easily override it. In fact, I override
it in my program because I want to calculate some system parameters. I’m sure that
you realize the proper method is in the Application level object (since of course the
application handles requests to display the about box).
Makefile (QPlot.MAmake)
The makefile is something many Lightspeed users are unfamiliar with. MacApp
2.0’s MABuild process eliminates the need for a makefile in simple cases. I have
provided a simple make file, it’s not much, as you can see from the listing. When your
program becomes more complicated the makefile usually grows proportionally in
complexity. For small projects made up of two or three files, the example make files
in the MacApp samples folder can be used as guides (and of course, you can consult the
manual for more explanation).
Trouble In Paradise
One last parenthetical note before we move over to Carl’s description of his
program. When we both did the conversions, we independently had problems printing
the plots on the LaserWriter -- PostScript errors. I tore my hair out about it for a
few weeks, then decided to really dig down and see what was happening. By
strategically inserting debugging statements into my code I finally isolated my bug to a
PicComment call. The PicComment in question sets the linewidth, and looks like this:
PicComment(SetLineWidth,2,Handle(Width));
However, the real call should look like this:
PicComment(SetLineWidth,4,Handle(Width));
The problem was that the second parameter to the PicComment is supposed to be
the size (in bytes) of the data. The original specification was wrong, yet it worked (we
assume) because Lightspeed Pascal must be more forgiving than MPW. Not only was
this line a bug, but the Daves even featured the bug in their article (top of page 21)!!
Let this be a lesson that just because someone gives you code that works you can’t
assume that it is completely bug-free.
That just about covers my program; Next month, we’ll hear about Carl’s.
Continued in next frame