|
Jonathan
Cooper |
What's the Hype about HyperCard? (Part II)
Originally published in MACinations (Club Mac, Sydney, Australia) , August 1992
In Part I (MACinations, June-July 1992), we looked at what HyperCard is, what it can do and how it compares with other Macintosh applications. In this second part, I will attempt to explain how it works.
Many will regard this as very ambitious (after all, Danny Goodman doesn't attempt this until page 313 of his Complete HyperCard Handbook!), but I believe that if you have a mental picture of the way that processes interact, you will better appreciate the real potential of HyperCard; you will see that it is more than just the sum total of various sample stacks.
Hopefully, this approach will be of help not just to complete HyperCard novices but even to those who have been using HyperCard for a while but still don't "get it".
Stacks are just documents
While a HyperCard document, or stack, can be made to look and act like an application, it is still a document; it still needs HyperCard to run. [HyperCard 2.3 allows you to create stand-alone applications.] While this may occasionally be inconvenient, it does have two advantages:
1) It means that most routines, functions, dialog boxes, windows and the like are not duplicated unnecessarily in each stack but are "inherited" from elsewhere (not just from the HyperCard application -- more about that in a moment). You might call this "information conservation".
2) It makes HyperCard development much easier and so much more immediate. After all, you do not have to be side-tracked with routine things like drawing dialog boxes or saving data, so you can concentrate on functionality.
(Incidentally, these principles are just an extension of what the Toolbox -- in the Macintosh ROMs -- and the System file mean for application developers.)
Inheritance
As stated above, a stack inherits resources (1) from more than just the HyperCard application. It also inherits them from a special stack called "Home" and the Mac system. In addition, up to 10 other stacks can be inserted into the "inheritance chain" if desired (either by the user or the creator of the stack) simply with the command "Start using stack <Stack Name>". The hierarchy of inheritance is illustrated below ("Lower" files inherit from all "higher" files):

Fig. 1: HyperCard inheritance
Home and user levels
The stack "Home" has a special place in HyperCard. In fact, HyperCard will not even run unless it can find a stack called "Home". (So that Home can be found automatically, it should be placed either in the System Folder, in the same folder as the HyperCard application or not inside any folder -- that is, at the "root" level of the disk that HyperCard is on.)
Home is a preferences document and a convenient "launch pad" for opening stacks, applications and documents. By far the most important preference is the user level, which can be one of five settings: 1 (Browsing: for just looking), 2 (Typing: for entering and editing text), 3 (Painting: level 2 + creating and editing pictures), 4 (Authoring: level 3 + creating and editing fields and buttons), 5 (Scripting: level 4 + creating and editing scripts (2)). Normally, you would set the user level to 5 while creating a stack and possibly lower it to 1 or 2 before giving it to others to use, to make it easier to use, but harder to corrupt accidentally.
To change the user level, go to the last card of the Home stack (choose Last from the Go menu or preferences from the Home menu) then click on the level you want:

Fig. 2: Preferences card of the Home stack (HyperCard 2.x)
If you are on this card but level 5 seems to be missing, it means that your copy of HyperCard [version 2.0 or 2.1] came bundled with your Mac. To set the user level to 5, show the message box, if it is not already visible (Message from the Go menu, or command-M), type "magic" (without the quotes) and press return. (What ancient Nordic ritual is this??) Better still, get a copy of HyperCard 2.0 or 2.1 from Club Mac and you will get the online help system and all the sample stacks.
Objects and properties
The basic metaphor of HyperCard is, not surprisingly, that of cards (like index cards), grouped together in stacks. Cards that are related to each other (that is, that come from the same basic template) are said to share a common background. Finally, on each card you usually find fields (which hold text) and buttons (which are the triggers for initiating actions).
Buttons, fields, cards, backgrounds and stacks are the five basic HyperCard "objects":

Fig. 3: Five types of HyperCard objects
Each object has its own set of properties (for example name -- all objects; font -- buttons and fields; icon -- buttons; number -- buttons, fields, cards and backgrounds).
To see the properties of an object, use the Objects menu. For stacks, backgrounds and cards, choose "Stack Info...", "Background Info..." and "Card Info..." respectively. For fields and buttons, select the appropriate tool from the Tools menu, click on the object concerned to select it and then choose "Button Info..." or "Field Info..." from the Objects menu. (Shortcut: Just double-click the object with the appropriate tool selected.)

Fig. 4: The three non-painting tools. From L to R: browse,
button, field
Scripts
One special property that any object can have is a script. To see a simple script, find a stack that has a button that looks like a house. (Most stacks have at least one.) Select the button tool (Tools menu). Double-click on the Home button. In the Button Info dialog box that appears, click the "Script..." button. You should see something like this:
on mouseUp
go home
end mouseUp
(Don't worry if it's a bit more complicated; this is really all
that's necessary for the button to work.)
This script has only three lines (although a script can contain up to 30,000
characters!):
| on mouseUp | When the mouse button is clicked (down then up) on me... |
| go home | ... go to the Home stack. |
| end mouseUp | (That's the end of these "mouseUp" instructions.) |
This is a single "message handler". Scripts can contain many of these (to handle various messages). "MouseUp" is regarded as a message in the HyperCard world, in this case a message that a certain event has occurred. These messages, and the events that trigger them are happening all the time.
Apart from "mouseUp" (and "mouseDown") there are "openCard" (when a card is "opened" or shown), "closeField" (when a field has been changed and the insertion point leaves the field), "returnKey" (when you press the return key), "newCard" (when a new card is created) and so on. (There is even an "idle" message when nothing else is happening!) What handlers do is "catch" messages, respond to them, generating their own messages, which in turn get caught by other handlers.
Messages and hierarchy
Fortunately, messages don't just fly all over the place; they travel along set paths. Many messages go no further than the objects they were aimed at originally. For example, the "mouseUp" message produced when you click on a Home button: because the mouse-click happened over the button -- that is, the "mouseUp" message was aimed at it -- and because the button's scrip contains a "mouseUp" handler, the response ("go home") is carried out and the message travels no further.
However, what happens when the objects that a message is aimed at doesn't have a handler for that message in its script? For example, imagine a card where every field is supposed to contain a number. Instead of putting a "closeField" handler in the script of every field, you can put the following handler in the card script:
| on closeField | When the text in any field is changed by the user... |
| if the value of the target is not a number then | ("The target" is the full name of the field; its "value" in this context means its content.) |
| beep | ... beep, ... |
| answer "This should be a number." | ... display a dialog box with this text in it, ... |
| put 0 into the target | ... put 0 into the field ... |
| select text of the target | ... and select the 0 ready for editing. |
| end if | (This is the end of the instructions to be followed if the field does not contain a number.) |
| end closeField | (This is the end of the closeField handler.) |
Now if the card script contained no "closeField" handler, the "closeField" message would travel even further. To know where messages travel, it's important to understand the hierarchy of HyperCard objects:

Fig. 5: Message hierarchy -- arrows represent directions
of messages
Figure 5 (above) is an extension of the inheritance diagram in Figure 1. Any object in the stack (within the grey rectangle in the diagram) can be an "entry point" for a message, as can the message box. If the object's script contains a handler for that message, then the message is responded to and travels no further. Otherwise, it travels up the hierarchy "looking" for a handler.
If the message passes through all the objects (including the Home stack) without finding a matching handler, it continues on to the HyperCard application where (assuming it is a valid HyperTalk (3) message) it's either ignored (for example "mouseUp" -- because HyperCard has no built-in response to this message) or acted upon (for example "tabKey" -- which moves the insertion point or selection to the next field).
So, getting back to our "closeField" handler: If the "closeField" message got past the card script without being caught, it would go "higher", to the background script. If it still got past that, it would try the stack script, and so on (4).
Do it yourself
Even if HyperTalk only allowed its own "official" messages to be sent, it would still be very powerful. However, it allows you to create, name and send your own as well. Why would you want to create your own messages? Well, let me illustrate with an example from my own experience:
Some time ago, I needed to print out the text from the fields on any card in a particular stack (rather than printing each card -- buttons, graphics and all). So, I created a button with a script something like this:
| on mouseUp | |
| repeat with fieldNumber = 1 to the number of fields | Starts an instruction-loop, so that this will work for every field; the variable fieldNumber increases by 1 every time the loop is executed. |
| put
the short name of field fieldNumber & ":" |
"&" joins 2 bits of text
together; " |
| field fieldNumber & return after theText | "return": makes sure that
each new field starts a new line on the printout; "theText" is a temporary data-holder (or variable). |
| end repeat | This line causes the computer to keep looping back until all fields have been processed |
| print theText | Now, print it! |
| end mouseUp |
Then I realised that I could use this routine in many different stacks. All I had to do was to turn it into a message. I gave it a name -- "printAllFields" (it has to be a single word) -- and wrote the same handler as above but with "printAllFields" in the place of "mouseUp". By putting this handler into the stack script of the stack "Home", I could create a button in any stack with the following very simple script:
on mouseUp
printAllFields
end mouseUp
Clicking on such a button would send the message "printAllFields" from the button to the card to the background to the stack (to any other stacks I may have "inserted" or started using) to the Home stack, where it would have been caught by my handler. This handler would then perform its actions on the fields on the current card. (Despite the extra "distance" that messages like these have to travel, there is no noticeable loss of performance, even on a Mac Plus!)
I don't even have to create a button to invoke this message. The "message box" (summoned from the Go menu) is not called that by accident. I can send my message ("printAllFields" in this case) via the current card by typing it into the message box and pressing the return key.
I can even simulate a system event (for example the user issuing a menu command, such as "New Card") by typing its corresponding message (in this case doMenu "New Card") into the message box and pressing return. If the message is to be directed at a particular object (e.g. a button) rather than the current card, it can be specifically addressed to it. For example: send "mouseUp" to button "Calculate".
Externals
Sometimes even HyperTalk with it built-in commands and functions (5) and those you create yourself may not be enough. In these cases, pieces of programming code called external commands (XCMDs) and external functions (XFCNs) can be installed as resources (6) into your stack and then used just as if they were native HyperTalk commands and functions.
These may be necessary because of the greater speed at which they execute and/or because they provide useful interfaces that are not a normal part of HyperCard or because they access parts of the Macintosh system not normally accesses by HyperTalk. Interestingly, many of the new features of HyperCard 2.0 were originally externals in HyperCard 1.2.5.
Conclusion
HyperCard is much more than an application for storing and displaying data and it's much more than a programming language (such as BASIC, most people's first introduction to micro-computers). It's a total software environment: a world of related objects -- cards, fields, buttons etc. -- that send and respond to messages according to your wishes.
Once this important concept is grasped, especially the last part, HyperCard starts to suggest itself as the solution to just about any problem or task that can involve a computer. Suddenly the user-friendly computer just became a whole lot friendlier!
Footnotes
1. I am using "resources" here in its common sense -- since a HyperCard script actually resides in the data fork rather than the resource fork of its stack.
2. Scripts: The ordered and inter-related lists of instructions that actually make stacks do things.
3. HyperTalk: The language of HyperCard scripts.
4. It's possible to put a "closeField" handler in a stack's script, but then it would respond to all the [untrapped] "closeField" messages sent on by every field in the stack, unless the script had some way of weeding out those it did not want.
5. Commands and functions are essentially the same -- that is, messages. The main difference is that functions "return" data (e.g. "the stacks" returns a list of the currently opened stacks) whereas commands usually just perform actions (e.g. "help" opens the Help stack)
6. Now I am using "resources" in its technical sense (cf Footnote 1; sorry if this confuses you!). To copy external resources from one stack to another, you can use the Resource Mover card in the Power Tools stack that comes with the "full" version of HyperCard 2.0 and above.