Getting to know HyperCard
HyperCard is like a deck of playing cards ...
...that you can manipulate on screen and build programs in and around. A single
HyperCard program is called a "stack", and essentially comprises the deck of cards
available to a user at any given time. Such a deck of cards (or stack) might contain
as few as a single card, or as many as thousands. The author of a particular stack
or deck of cards assumes the role of 'dealer' for that deck/stack, pretty much making
up the rules of the game as they go along. The rules for a HyperCard stack can be
wildly different from one stack to another-- its all up to the author of the stack.
The card deck model HyperCard's based on actually works pretty good for many purposes,
since practically all programs often need several different screens available for
presentation to users (cards), and you might want to access them in a variety of
ways (sequentially, randomly, via particular button or menu command), all of which you
can do with HyperCard's screens.
Hiding cards and why
The author may elect to keep some cards forever hidden from users, in order to
provide the stack with its own internal, protected memory space. Such memory space
can allow a stack to be more intelligent than would otherwise be the case. For instance,
this memory might be used to remember user preferences from a previous session. Why
hidden cards for memory space? Because the only non-volatile memory a HyperCard stack
enjoys lies within its scripts, its artwork, and its text fields. This means if you
have variables that might change in value from one session to another, based on something
'learned' by your stack over time, the only practical place to store them is in text
fields; and text fields require a card to reside on. Ergo, the hidden memory card
(or cards-- you might need more than one to contain all the fields required).
The main three HyperCard modes of operation
Browse mode is the default.
Many HyperCard users may never exit this mode to another, as this is the one you're
typically in to move through a stack, read and scroll through text, type in information,
click buttons, and more.
Button mode is really a development tool for authoring and scripting HyperCard stacks.
Button mode allows you to move and resize buttons, as well as access via double-clicks
their Info dialog boxes, and from there their scripts.
Field mode is another development tool, except in this case for text fields.
In this mode you can move and resize text fields, and edit their scripts or other
properties.
Using Fonts in HyperCard
Theres two kinds of font usage in HyperCard-- paint and text.
Paint font usage
Paint font images pose little problem for distribution in stacks, as they are simple
bitmap-style imagery, and don't require that an end-user's system possess the font
resource with which the image was created. This allows you, the author, great latitude in how your stack looks font-wise, so long as you can adequately get by with simple,
paint-type images. However, there are some disadvantages to using fonts in this manner:
1. As you develop your stack, it can be difficult and time consuming to revise
text embedded in your stack as paint images. Because it's often necessary to manually
move individual words or phrases about to get the revisions to fit in properly, as
well as spend substantial time trying to figure out which font you originally used to create
the text, so that you can use it again to create new text that matches the old. If
this text were instead within a text field, you could simply type in the new text,
with HyperCard automatically rearranging the rest to accept it, and select a section
of text and examine the font menu to see which font was being used, as necessary.
2. Paint-style font images can consume a lot more disk space than their text field
counterparts. The amount of extra space required varies with many factors, but in
general paint fonts require maybe a third more disk space than text field fonts.
Text field font usage
When you use fonts in text fields of stacks you plan to distribute widely, you
should limit your usage to fonts generally available on most Macs. Otherwise when
someone else looks at your stack, and their system doesn't contain the same font,
HyperCard will make the best choice it can for a match from what's available-- usually making
your text look rather garish as a result. From the few near universal fonts available
on Macs these days, something like 12 point Geneva seems the best overall choice
for legibility, compatibility, and easy-to-stomach appearance. Unfortunately, there are
times that more latitude in font usage seems essential for a presentation. And since
the Mac's few universal fonts are lacking in such flexibility, the only other choice
is to use non-universal fonts in the creation of paint-style text in your screens.
HyperCard shorthand or abbreviations
The more you script the more you'll use and appreciate HyperCard's shorthand,
or abbreviations for its commands.
Abbreviations you're likely to use most often are:
bg = background
cd = card
btn = button
fld = field
loc = location
char = character
chars = characters
Command shorthand you might often use:
lock screen = set the lockscreen to true
unlock screen = set the lockscreen to false
lock messages = set the lockmessages to true
unlock messages = set the lockmessages to false
HyperCard comments, notes, and asides...
...can come in handy. During development or debug of fairly complex scripts
you'll often want to make notes to yourself about what's happening in a particular
statement, or perhaps place an informative comment for the benefit of your future
self who might not look at the same section of code again for years. Or, you might wish to
remove a statement from the executing script temporarily as a test, without having
to remove it entirely from the listing. All these things can be done with comments
in HyperCard scripts. HyperCard will usually ignore any statement with two dashes (--) in
front of it; so you can safely park your comments or temporarily out-of-the-running
script behind these dashes, as you pursue other matters.
Other ways to get scripts out of your face temporarily while still keeping them
relatively handy include storing them elsewhere, like in the script of another object.
You can even place them inside a handler so named that it will never be activated
by any command from HyperCard or your own scripts-- a handler devoted purely to storage,
but maintaining your statements in their original format (any scripts you store without
a proper handler name or comment dashes (--) will not retain their format structure, and may also cause a script error under some conditions, if HyperCard tries to act
on them).
Below is an example of using comment dashes:
on mouseUp
-- here I've placed a comment on its own separate line in a handler
go to next cd -- here I've placed a comment after a command
end mouseUp
Playing with the pointer
Showing a busy cursor is no more difficult than typing
set the cursor to busy
in a script. This changes the cursor to the beachball shape many Mac users will be
familiar with from past incarnations of MacDraw and other programs.
However, a single statement of this type won't make the beachball cursor move or
rotate as you might expect, to indicate the stack is working (as opposed to being
crashed or hung up).
To make the beachball cursor spin or rotate, simply repeat the original statement
elsewhere in your script. One great way to accomplish this with a single line of
code is to place the statement inside a repeating loop. This will spin the cursor
like we're talking about, as the statement will be executed anew with each cycle of the loop.
The beachball is the default busy cursor. But what if you want the wristwatch cursor
instead? And you want its hand to spin? Sorry, but you'll have to create your own
custom icons to depict a spinning watch. HyperCard has a static (unmoving) watch
cursor you can use, but doesn't supply the fodder for animating its hands like it does
for animating the beachball.
HyperCard sleight-of-hand
If you're a magic aficionado you'll appreciate HyperCard's own bag of tricks
for fooling stack users. First and foremost, HyperCard lets you freeze time for the
end user, while it (time) continues running normally for you and your program internally. A seventies-style movie about a magic watch sometimes appears on TV illustrating
how powerful this principle would be if applicable to the entire world. When you
freeze time in HyperCard, all the normal screen updates don't occur-- so you can
perform amazing manipulations of your stack without the user seeing them-- moving between cards,
cutting and pasting, deleting cards, etc. When you UNfreeze time again, all the user
sees are the end results of your machinations-- not the messy intermediate stage.
The command for freezing time in HyperCard is lock screen; the command to unfreeze it is
unlock screen.
The long winded ways to say these things are:
set the lockscreen to true
set the lockscreen to false
Other sleight-of-hand tricks are illustrated in HyperSavvy HOT v1.0, the companion
stack to this article series, published as part of the Summer 94 issue. Therein are
given functional examples of the
lock screen
described above, as well as other techniques, such as pre-loading text fields to
make them appear to be rapidly changing on-the-fly, and how to use a combination
of buttons and fields to mimic the functionality of a PopUp menu in your stack. There's
also examples of using button icons and outlines to create animation effects, in various
progress gauge dialogs in the stack.
And while we're on the subject of object handling sometimes being faster than the
eye...
Each card can have many layers of objects, some visible, some invisible, some shared
with other cards, some not.
The pure graphic imagery seen on each card in HyperCard can possess both a background
and foreground aspect, much like a painting. The background is referred to inside
HyperCard by the name you'd expect ("background"), while the foreground is not--
usually being referred to as a "card".
Whatever artwork exists in the card layer will usually take precedence over images
in the background layer; that is, if you have a picture of a clown in the card layer,
and an elephant in the background layer, youll see only the clown if they're both
located in the same two dimensional region of the HyperCard window. This will be the
case where the clown graphic is opaque; you might make it transparent, in which case
a user will see the elephant through the clown. Many cards may share the same background. So on another card the user might see the elephant through transparent balloons
rather than the clown.
Text fields have a layer of their own, which is always in front of any graphics
they share a card or background with. A background text field will always be in front
of a background graphic, and a card text field always in front of a card graphic.
A card text field will always be in front of a background text field.
Buttons too have their own layer, which is in front of any text fields they share
a card or background with. So a background button is always in front of any background
text field or graphic, and a card button always atop a card field or graphic.
A card button can cover up a background button on screen, and all the buttons and
fields in a background layer can be hidden by an opaque graphic in the card layer.
Often you'll have several buttons and text fields occupying the same card or background
layer.
In such a case as this, you may shuffle the buttons or text fields amongst themselves,
within their respective groups and layers, like a deck of cards. The way you do this
is use the Bring Closer and Send Farther commands in HyperCard's Objects menu, after you've selected the particular button or field you want to move. These
commands are incremental-- that is, you may have to use them several times to get
the object where you want it. If you're moving a text field from behind three others
to the front of its field layer you'll need to use Bring Closer three times.
How does HyperCard determine where a button or field is in its respective layer
and object group? By order of creation (until you change the order by a Bring Closer or Send Farther command). The first button created in a layer will be the farthest back, the latest
button the one in front. The same goes for text fields.
Scripting in HyperCard's object oriented environment
Great, humongous chunks of a HyperCard stack are programmable by the user. And
each chunk is pretty much an independent object, so there's not as much to worry
about in programming as there would be in a language like C or BASIC.
If you're used to certain other languages, you've seen program listings that were
tens or even hundreds of pages long, since often much or all the programming is done
in one place, creating something like a book worth of text you can scroll through
for hours, and in which it can be very difficult to locate bugs or other items, simply
because everything's all in one, contiguous form.
In HyperCard, each piece of a stack is a separate individual, with its own programming.
This means each button, each card, each background, each text field, all possess
their own potential programming text, in their own editing window.
This fact can seem intimidating to the beginning scripter, even if they are otherwise
experienced with a variety of languages. At least it was for me, and I did possess
considerable experience with other languages before arriving at HyperCard.
I guess the best advice is: don't worry so much. As long as you keep to a fairly
reasonable backup routine for whatever serious work you're attempting with HC, you
should feel free to try just about anything with the program. After all, that's one
big reason kids usually learn stuff faster than we adults-- they worry less, and experiment
more.
HyperCard object names, and other remote identification matters
One great thing about HyperCard is you can name everything with as descriptive
a title as you wish in a stack. And then refer to that name in your programming rather
than using the ID numbers or other references available for the object in question--
because HyperCard will recognize the name!
You can have a card named "H.G. Wells", where it's the screen in your database
that holds his biography in a text field. Also sitting there on your "H.G. Wells"
card you can have two buttons, one named War of the Worlds,
and the other Time Machine,
because each button leads, respectively, to cards detailing those two works by the
author.
Naming your objects this way is a good habit to get into, as it'll make it more
convenient for you to cut and paste objects from one stack into another-- because
that way you won't have to change ID or sequence numbers in your scripts in a following
clean-up procedure.
However, you don't want to name objects indiscriminately, as this can lead to problems
on a par with not naming them at all; mainly because you'll be unable to remember
the names, or tend to use the same name more than once. So be careful when naming
your objects to make the name something meaningful to you, that you'll easily remember
in later script references, and be unlikely to use again with a different object.
In this way object names are similar to variable names-- for you don't want to
name your variables indiscriminately either, for pretty much the same reasons. Fortunately
however, HyperCard's usage of global and local variables gives you much more slack
in this regard than you tend to enjoy with object naming.
All this extra consideration required for naming objects can often slow up a project--
so many times you might not go to the trouble. That's OK, since HyperCard doesn't
usually require a name for an object. But remember that copying and pasting your
buttons, fields, and cards into another stack later will entail lots of ID number changes,
if formal names are lacking from your objects' descriptions.
the target
is a HyperCard synonym for whatever object received the last message. For example,
let's say you had six radio buttons you wanted to use the same handler for in their
shared card script. Your goal might be to have only one button hilited at a time.
If you're to use the same handler for all the buttons, you type its name into the
mouseUp
handler of each button's script. Then in the card script, where the special wholesale
button handler is, you first turn off the hilites of all the buttons, so as to prevent
more than one ever being seen hilited at the same time-- that's easy. The harder
part is next turning on the hilite of the single button that the user just clicked.
Or, at least it would be harder if HyperCard didn't offer
the target
as a remedy. Since
the target
always contains the identity of the last object to receive a message from the user,
it'll hold the identity of the button clicked upon. So after turning off the hilites
of all the various buttons, we can turn on the hilite of
the target
in our script, and that's that.
Users of HyperCard v2.2/2.3 may know that the above work is redundant for them,
since HyperCard takes care of such things automatically, with button families. However,
the target
can still be used in this way with other HyperCard objects by 2.2/2.3 users, and
users of older HyperCard versions will still require it for radio buttons too.
Names are optional for objects-- but addresses aren't. If you don't name an
object, HyperCard will still recognize it by a unique ID number HyperCard assigns
to it when you first create the object. But providing HyperCard with the proper address
of the object, or where it lives in the stack, is mandatory where you're referring to
anything not present on the card where the acting script is running from. For example,
if your script is inside a button on card ID 342, and it needs to refer to a field
existing on the same card, then your script may get by just referring to the field as
cd fld ID 23
. But if the field in question were on a different card entirely from the button whose
script is calling on it, you'd have to give the field's address too when identifying
it.
When you add the field's address to its identification,
cd fld ID 23
changes to something like
cd fld ID 23 of cd ID 8946
. If the field and card have been assigned names by you, this expression might instead
look like
cd fld "George" of cd "Carlsons".
Actually, the prefix
cd
in the field's identification is also a part of its address, since this informs HyperCard
you're talking about a field in the card layer, and not the background layer; two
very different locations in terms of objects.
this [object], me, cousin it, and the message box.
Though I mention elsewhere how HyperCard understands names given to objects like
buttons and cards, it's often unnecessary to use a name in your scripts. If you write
a line inside a button program that refers to the same button housing the script,
rather than using the button's name you can just say
me
, and the button program will know you mean the button itself. Just remember though
that
me
works as a reference only inside the program of the very object you're referring
to.
A variation on
me
is
this [object]
. However, the two can't always be used interchangeably. If you say
this card
in a script, the script will interpret this as the card on-screen
, or the card that's supposed
to be onscreen at this time (if the lock screen command has been invoked, HyperCard could have been ordered to move to a particular
card by script command, but the lock screen order prevented that card from replacing the last one displayed on screen).
this stack, this card,
and
this background
may be the full range of possible uses for this expression.
me
might be applied to the full range of HyperCard objects.
The Addams Family on TV had a cousin "it", and HyperCard does too, sort of.
If you do much of any HyperCard programming at all you'll become very familiar with
cousin "it". In HyperCard
it
is a temporary variable you'll often use in your scripts, and which HyperCard uses
automatically for its own use, in the course of its operations. You'll often put
it
into a variable of your own to get free info from HyperCard, and other useful tasks,
sort of like drawing water from a public well.
The
message box
is sort of the opposite of cousin
it
, in that it's dedicated wholly to your own use, rather than the way
it
may be used casually either by HyperCard or a scripter. Whenever you put something
into the
message box
by script, like the value of a variable or a character string, the message box appears
on-screen, displaying what you put into it. Sometimes the
message box
is useful for checking a certain variable inside a program, and more convenient and
useful than things like the "variable watcher" which came with HyperCard 2.1. Let's
say you suspected your variable "
thinGie
" contained an erroneous value at a critical point in your script. You can check this
by displaying its value in the
message box
from the very spot suspect in your script. Just type the line
put thinGie into message box
immediately following the line that sets its value, or immediately after your program
has made a decision based on "
thinGie
", and the next time your program runs it'll display
thinGie's
value in the
message box,
allowing you to verify if anything is amiss.
Another great thing about the message box is you can type in one-line programs
or commands to get immediate results, rather than having to set up a button to do
the same thing. If you type into the message box the following...
go to cd 3
...and then press your return key, you're immediately transported to card 3
in the stack (if there is one).
HyperCard variables and lists (known in HyperTalk as containers)
Given the previous information about objects and object-naming, it might seem difficult
to get all the parts of a stack to work together. But it's easier than it appears.
For one thing, in something like a BASIC program you have to be pretty careful about assigning variable names, since using the same one for two different things could
really screw up your entire program. In older BASIC languages the problem was made
even worse by the fact that you were restricted to using only very short variable
names; so, as in the recent history of DOS with filenames, it was very easy to become redundant
or too cursory in the names used, to the point that you either didn't know what a
particular abbreviated name stood for, or ruined a lot of work by mistakenly using
the wrong one at the wrong time. In HyperCard though, you can often use the same variable
name in all of your different program listings throughout a stack, for lots of different
purposes, with no problem at all. This is because HyperCard has two kinds of variables: local and global. Local variables are used and forgotten by HyperCard immediately,
being remembered only long enough to perform the task which fathered them. So using
"
ButtFlip
" as a local variable in the program for card 113 doesn't interfere at all with using
"
ButtFlip
" again in the next program you run, that might exist in the background of the very
same card!
Of course, sometimes you'll want
variables HyperCard remembers from one object's program to another: global variables.
Whenever you use these in a program, you tell HyperCard your intentions by putting
a statement like
global johnHenry,Henrietta,stopBit,whoA
as the first (or near first) line in your program listing (where
johnHenry,Henrietta,stopBit,whoA
are all global variables with names you yourself made up).
Any variable in HyperCard can be made into a list by simply distributing various
punctuation marks or other signals inside it. Inside lists such marks are called
itemDelimiters,
and any words or characters inbetween the marks are called
items.
The delimiter used most often and by default is the comma. So the usual HyperCard
list variable will contain something like
Peggy,Anne,Sue,Terri,Melinda
(commas included). To order HyperCard to take note of
Peggy
in the list, you'd say in a script
item 1 of listOfGirls
(if listOfGirls was the name of the variable).
You can change the
itemDelimiter
if you wish, to something like a colon for special purposes, like interpreting file
pathnames obtained from the Mac operating system. For example,
Hard disk:Applications:HyperCard:FLUX Summer 94
might be the pathname you'd get for FLUX residing on your hard drive, if the magazine
is inside a folder named "HyperCard", inside another folder named "Applications"--
and your hard drive is simply named "Hard disk". Pathnames are really just one of
the ways the Mac keeps track of where things are, and you might never fool around with
them much in your scripting. But you can if you want, by telling HyperCard to recognize
colons as itemDelimiters, with the statement
set itemDelimiter to colon
.
Like lots of HyperCard elements, the identity of the
itemDelimiter
changes back automatically to the default value the next time HyperCard gets to idle.
This means you can change the
itemDelimiter
in your script to a
colon
or something else out of the ordinary, use it to do whatever it is you need, then
forget about it-- and HyperCard will put that toy away for you when you're done.
But remember HyperCard won't reset this while your script's still busy. So if your
script first does work with a colon
delimited list, and then switches to work on a comma
delimited list, you must reset the itemDelimiter yourself inbetween lists-- or face
the wrath of the mighty Script Error.
HyperCard's nervous system, chain of command, and message hierarchy
You might be surprised to know HyperCard possesses something eerily similar to
a living nervous system, that constantly pulses with messages streaming from HyperCard's
extremities to its brain.
By typing "mw" into HyperCard's message box and pressing your return key, you can bring onscreen
a small window that displays this pulse of information always traveling through an
open and active HyperCard stack.
As you'll see in the message watcher window, most of these pulses are
idle
messages, meaning that nothing is happening, that HyperCard is waiting for the user
(or a user-written script) to tell it something to do.
Message passing, handlers, and interception
Trickle up information flow; the natural or default message flow in HyperCard (bottom
up).
Typically a system message will begin its journey in an encounter with a button
or text field, and if not intercepted along the way by a handler, proceed to the
card underlying that button or text field, go on to the background behind the card,
then to the host stack. If the message still finds no handler designed to cope with it, it
will go on to the local Home stack, and from there, to the HyperCard application
itself. If HyperCard has no built-in predilection for actively responding to this
particular message in some way, the message will then simply drop out of the HyperCard universe,
into a sort of 'black hole', from which nothing ever returns.
One big reason handlers are named as they are is because the main thing they do
is intercept and handle messages as such signals travel through the HyperCard system.
The majority of messages intercepted by the handlers of a typical stack are standard
HyperCard system messages, consisting of things like
mouseUp, openCard,
and
openBackground
. But handlers can be written to intercept custom messages too. In fact, you could
write handlers to intercept almost any message you might care to create-- and this
is exactly what you'll do in order to create your own custom capabilities in a stack.
You'll want to use HyperCard's standard system messages as a skeleton for your
efforts, as they conveniently cover most of the events you'd want to respond to in
your stack, such as the end-user clicking on a button, making a menu selection, or
moving to a new card. If you had a custom handler you'd written for a fantastic animated display
inside your stack, named
SpectacularEffects
, you might utter just the name of the handler inside the
mouseUp
handler of a trigger button, to invoke the special display. Then when the user clicked
the button, the name would move through the stack as a message, looking for your
handler to intercept it.
Where would be a good place for your
SpectacularEffects
handler to be, in order to intercept the message? Well, the most straightforward
place for it would be inside the script of the trigger button itself, just after
the
mouseUp
handler which calls upon it. Everything would work fine with this arrangement.
But the very best placement of the
SpectacularEffects
handler would depend on several factors. For instance, what if you wanted another
button on the same card to use the
SpectacularEffects
handler too? In that case, you might want to place the
SpectacularEffects
handler not in any one button's script, but inside the script of the card which all
the buttons shared. This way all the buttons could easily access the handler, and
you'd save space in your stack by not having several redundant copies of the handler
spread among several different scripts.
What if there were other buttons, on different cards, that you wanted to use the
handler with? Well, if all or most of the cards in question shared the same background,
you could place the handler in that single background's script, rather than the scripts of multiple cards, saving space by bumping the
SpectacularEffects
handler up from a card script to a background script.
Note that this technique does more than just save space in your script; it also
helps speed development and reduce the potential for bugs. How? It can speed development
because you can improve the functionality of all the buttons dependent on the same
handler by modifying that single handler in its single script, rather than having to
edit several different scripts to accomplish the same purpose. Plus, the fewer copies
of the handler in your stack you must edit, the less chance for making mistakes
or mistyping within any one of them and not catching your error in testing-- which makes
for a reduction in potential bug creation. Further, the fewer examples of the handler
you must hunt down in your stack when a correction is deemed necessary, the less
likely you'll accidentally overlook one for revision, creating a bug for your end-users
to find later.
HyperCard's Chain of Command; Top-Down and Horizontal Messaging.
Sending a message down or sideways through a stack is accomplished, logically enough,
with the
send
command. This command is quite powerful, allowing you to (among other things) get
around HyperCard's limitations on the size of individual object scripts. The 30,000
character or roughly 6000 word limit can hit developers especially hard in the area
of stack scripts, because so much of a complex stack's functionality can be dependent on
that single script, since it's often the only one which many important features of
your stack can all share simultaneously.
One example is FLUX magazine itself, which is capable of so many different and
strange things, compared to most other stacks you might encounter. First off, each
issue [from the second year of publication, was] essentially two entirely different
programs in one: a magazine with educational, speculative, and/or entertaining articles and
imagery, AND a Super Stack, which amounts pretty much to a full-blown application
program, dedicated to creating complex, interactive presentations to be published
in future issues of the magazine. The magazine transforms itself to a Super Stack at the
click of a button, thereby deleting and rearranging huge chunks of its functionality,
sort of like a hero from one of those Japanese transformer kid shows. As if this
wasn't enough, the transformed Super Stack then offers users a wide array of tool palettes,
above and beyond those provided by the basic HyperCard application. Not least among
these palettes is the new Magic Palette, which pretty much gives HyperCard Player
users a big piece of the full-blown HyperCard application program, within the environment
of the Super Stack (providing much of the functionality of userLevels 4 and 5).
It's no surprise that many experienced HyperCard scripters might find all this
to be quite amazing, considering what they know of HyperCard's limitations. Especially
when they discover that no custom XCMDs or XFCNs are involved in the majority of
these capabilities.
Much of the credit for all this has to go to HyperCard's
send
command. It allows me to send all the dozens of different commands originating from
the wealth of palettes to a single card script in the stack, rather than burdening
the stack script with them. Other tasks too are offloaded from the stack script via
this method. In other words, FLUX as we know it today could not exist without the
send
command.
Believe it or not, all the above represents only a small part of the power of HyperCard's
send
command-- and not even the part you're most likely to use in your own efforts!
No, the way you'll probably use the
send
command most is in things like sending a
mouseUp
message to a button on a particular card. Yes, the
send
command can be used to send HyperCard's own system messages to objects! At least
sometimes. Some messages work better with
send
than others. Anyway, why might you want to send a
mouseUp
to a button this way? Well, in v2.1 of HyperCard and earlier, there was no way to
set up a default button automatically-- you know, the kind with extra lines drawn
around it, that signify it will be the button activated if you press your return
key? In these older HyperCards, you had to set such things up manually. One way was to place
a
returnKey
handler in the card script that looked like this:
on returnKey
send "mouseUp" to cd btn ID 3
end returnKey
This script (along with the appropriate lines drawn around the button onscreen)
makes card button ID 3 a default button, activated automatically by the press of
the return key.
There's still other facets to the
send
command, but as they pertain to highly advanced subjects like inter-stack communications,
and even network communications, they're not really appropriate in this article.
Most scripters are unlikely to need or want to use those advanced functions in their own efforts for the first two or three years of stack development, as it could easily
take that long for them to become proficient enough in other, more practical Hyper
matters, to even want to exploit such things.
Turning off the message system
Sometimes you'll want to turn off HyperCard's normal message broadcasting system.
I've personally found myself in the situation of needing a script to:
1. Move to a particular card to get something.
2. Return to the original card to deposit the object retrieved.
However, an
openCard
handler in the interim destination card was programmed to do something else, and
that something interfered with the above described process, which I was adding later.
The interfering script was there for a good reason, unrelated to the new process
I was creating, and so I didn't want to remove or disable the older script. The solution
was to temporarily turn off HyperCard's messaging system while my newer script was
running, so that the
openCard
handler never got the signal to act on its own script. Turning off the message system
is always a temporary thing, as it automatically turns itself back on as soon as
the active stack is idling again.
The command to turn off HyperCard's messaging system is
lock messages
. Its opposite is
unlock messages
, but the system automatically goes back on-line anyway as soon as your scripted processes
are finished, and HyperCard's able to return to an 'idling' condition.
HyperCard's subroutines (called "handlers" in HyperTalk)
"Handlers" is the term HyperCard uses for subroutines or message trapping routines.
Many handlers can be named whatever you want. Maybe you need to access the same subroutine
from the scripts of several different objects, so you decide to have just one subroutine in a place accessible to all, to save space in your stack. Maybe you have
six cards, all with a button called "Order Form", which your customers will click
to go to a screen designed for accepting orders. In this case, you might want all
six buttons to use the same subroutine. Here, each button's program might look something
like this:
on mouseUp
orderForm
end mouseUp
In this handler, the phrase
on mouseUp
tells the button to grab the
mouseUp
system message that shoots through the HyperCard program when a user lets go of his
mouse button, just after the user has clicked on the screen button with his pointer.
on mouseUp
makes the button object stand up and pay attention when the
mouseUp
message comes its way. The button then looks at whatever instructions you've listed
inside the handler-- here it's just
orderForm
. The button object sees
orderForm
, but doesn't understand it, since you've put the handler for
orderForm
somewhere else in the stack. So the button just passes the message
orderForm
on up the chain of command in the stack, to see if anybody else knows what you're
talking about.
The message
orderForm
next travels to the script of the card the button lives on. The card object checks
its its own script to see if you put something there about
orderForm
, and finding nothing, just passes the message on again.
orderForm
is next passed to the card's background, which also knows nothing about it, and so
passes it on up to the stack object.
Here, finally, the stack object recognizes the command, as you've placed the
handler below in its program listing (please excuse the formatting below; it's slightly inaccurate):
on orderForm
global howManyTimes
add 1 to howManyTimes
put howManyTimes into cd fld "Order Form Check Out" of cd "Surveillance"
go to cd "Form"
end orderForm
The
on orderForm
and
end orderForm
expressions tell HyperCard where your instructions for this particular message begin
and end.
global howManyTimes
tells the stack you want to use the global variable
howManyTimes
here.
add 1 to howManyTimes
adds one to the number already in the global variable.
put howManyTimes into cd fld "Order Form Check Out" of cd "Surveillance"
tells the stack to put the number that's inside the global variable
howManyTimes
into a text field named
Order Form Check Out
, that exists on the card you've named
Surveillance
, hidden somewhere in your stack.
go to cd "Form"
tells the stack to move the user from the present card where he clicked the button
to the order form card, which is named
Form
.
In the
orderForm
handler I did several things:
I updated a global variable to keep track of how many times the user looked
at the order form. If I can ever get my hands on this information later I can analyze
it statistically, along with other data, to find out things like the percentage of
users who looked at the order form, that actually ordered something-- you know, customer
surveys sort of, except subtle. It's kind of like a telephone survey except without
the inconvenience of a ringing phone to get the information-- it all happens automatically and transparently. This tidbit provides a real world example of the greater
marketing power and consumer research potential available in interactive media over
the old forms.
But back to the
orderForm
handler....I told the stack to remember the global variable by storing it in a text
field on a hidden data card I have in my stack. This is necessary because otherwise
my stack will forget the updated number when the user quits out of the stack, and
start from scratch when the stack is reopened. The only permanent way to get a stack to
remember things is to either install a fixed value inside the stack's scripts somewhere,
or store values inside text fields inside the stack-- global variables have to be
reset each time the stack is opened, and are RAM based, while the contents of text fields
are saved to disk as part of the stack file.
HyperCard now returns to the original buttons
mouseUp
handler, to perform any other tasks that might be listed after
orderForm
. In this case however, there are no other tasks-- just the phrase
end mouseUp.
end mouseUp
informs the button that this is the end of its instructions for this particular event,
and so HyperCard starts twiddling its thumbs by pulsing with
idle
messages, waiting for something to happen...
Text Processing in Scripts
Dealing with user cursoring and typing, scrolling, justification, fonts, styles, re-formatting,
and word wrap in the text fields inside your stack
Surprise! You don't have to deal with these things at all in most cases, because
HyperCard will do it for you automatically! Anyone who's been forced to program text
processing from scratch in C or other languages will really appreciate the way you
can just plunk down a HyperCard text field and expect it to pretty much take care of itself
most of the time. Of course, sooner or later you'll get into fairly advanced stack
building, where playing with the text fields may become necessary after all-- but
you'll be starting your work from a much better position than most programmers throughout
history enjoyed in this area.
Translating text into HyperTalk strings
Literal strings
. If you place a quote before and after a string of alphanumeric characters, HyperCard
will see the string not as a programming instruction, but as a linear list of characters
meant to be held in storage, displayed in an object, compared to another string, or installed somewhere in the stack.
The quoted phrase "The ship sank with all hands." is an example of the material
HyperCard will treat in the manner just described.
If you want HyperCard to do more with a bit of text than what's discussed above,
such as make dynamic changes in a text string based on current circumstances, you
have to get more complicated.
In those cases the literal strings described before will be only a small part of
the text processing you and HyperCard will be working on together.
Displaying quote marks in text fields.
Let's say you wanted to display quotes in a text field, both before and after a text
string you place there. Using the script statement...
put "The ship sank with all hands" into cd fld ID 4
...will NOT include quotes in what's placed inside the field. In fact, it won't put
any quotes at all into the field. It'll only place the words between
the quotes into the field.
To add visible quotes to the text, you'd have to tell HyperCard to place something
like
quote&"The ship sank with all hands.""e
into the field. You see? You have to actually spell the word q-u-o-t-e for HyperCard
to understand you want the punctuation mark added to the sentence. Why is it so complicated
a process to add a simple quote? Well, to answer that question I'd have to get into all the trade offs involved in designing any programming language, and especially
those involved in creating HyperCard's own scripting language. Suffice it to say
that HyperCard's developers tried their best to make everything as easy and straightforward as possible for scripters-- but at some point you always get backed into a corner,
and have to nail down a compromise to pay for everything else. To my mind this quote
thing is one of the compromises made by the developers. Actually, considering the
power and ease of use the developers bought with the handful of compromises they made,
I believe they did a great job.
The ampersand symbol; HyperCard Glue for Strings.
Anyway, now we come to the ampersand (&) symbol. The ampersand is like smart glue
in HyperCard. You use it to tie together text strings and their modifiers so that
HyperCard will understand what you're talking about. In the example above I used
ampersands to tie together my literal text string and the quote marks I wanted to appear to
either side of the string. If you place two ampersands side-by-side (&&), they not
only glue together any string and modifier you put into a statement, but insert a
space character smack between the two string pieces so joined, too.
Inserting changing values into a string.
Sometimes you'll want to display a message to your user showing a changeable value.
Maybe you want to keep them up-to-date on some lengthy process they've initiated
in your stack. You might wish to display something to the effect of "14 items remaining
to be processed". How would you make a single text string allow a regular update of
the value here expressed as "14"?
Well, let's say "14" is a momentary value contained in a variable named
whatsLeft
. If
whatsLeft
can be depended upon to always hold the value you want the user to see in the string,
you can use ampersand glue to attach it to your string, like so:
whatsLeft&&"items remaining to be processed."
Place the statement displaying this string into the same repeat loop performing
your process, and it will update the value displayed regularly onscreen-- as long
as the variable
whatsLeft
is updated in the script too, somewhere.
How hard is it to display more than one changing value in a text string? Not hard
at all, with ampersand glue. Let's say we wanted to display a bit more information
than the previous example did. Maybe say "14 items of a total of 28" remaining to
be processed.
The way we'd build the string is to begin by breaking the literal part into two,
inserting the appropriate extra variable, and then gluing it all back again with
ampersands.
If the variable containing "28" is named
total
, the end result of our rearranging would look like so:
whatsLeft&&"items of a total of"&&total&&"remaining to be processed."
You may have noticed that we actually have a variable here with the same spelling
as part of the literal text string it follows. Those of you with programming experience
might ask if this could cause a problem for HyperCard. The answer is no. The "total" inside the literal text string is regarded as completely different from the variable
named
total
, because of the placing of the quotation marks in the expression.
Storing, changing, and deleting text
Characters, chunks, words, and lines.
Characters are individual letters or numbers in a text field or variable/container.
"2" and "e" are considered single characters or chars by HyperCard.
Words, to HyperCard, are pretty much the same thing they are to you and me-- groups
of characters separated from others by spaces or punctuation marks. However, there
is one difference between HyperCard's perceptions and ours when it comes to words:
HyperCard will accept things like "gffpt" as a word, so long as it meets the previous
criteria, where you and I wouldn't.
When might you use the term
words
in your scripts? Well you might have HyperCard count the number of words in a text
field for you, like so:
put the number of words in cd fld ID 3 into totalWrds
Chunks are ranges of characters, like
char 23 to 45 of cd fld ID 5
(translated: the 23rd through 45th characters in text field ID 5) which can include
any or all of the other types named here. The term
chunk
is usually used in scripts as part of functions like
the foundChunk,
which provides you with information about the location of text inside a text field,
after you've initiated a search for same. Things like
the foundChunk
come in handy when you want do custom search and replace operations in a field.
Lines are usually words, phrases, or numbers separated by a return, which will
tend to arrange them vertically in a container like a text field.
Just one thing
lines
are useful for are creating clickable vertical lists in scrolling text fields, like
those seen in the Preferences screen of Pathfinder HC v1.0. Because HyperCard will
return to your scripts information about which
line
was clicked on, via
the clickLine,
allowing you to hilite that line, and carry out commands based on what the line displays
onscreen to the user.
the clickChunk
and
the clickText functions,
and grouped text properties all are related to this.
Specifying a single character in a container
.
put char 5 of "red hair" into shoThis
...puts the character "h"
into the variable "shoThis".
Deleting a single character in a container.
put "red hair" into shoThis
delete char 5 of shoThis
...puts the words "red hair" into the variable "shoThis", then deletes the "h" inside the variable, leaving only "red air" remaining.
Specifying a single word in a container
.
put word 2 of "flaming red hair" into shoThis
...puts the word "red" into the variable "shoThis".
Deleting a single word in a container.
put "flaming red hair" into shoThis
delete word 2 of shoThis
...puts the words "flaming red hair" into the variable "shoThis", then deletes the word "red" inside the variable, leaving only "flaming hair" remaining.
Designating a range of text
Elsewhere in this article I talk about giving HyperCard an address for a
text field-- informing HyperCard about whether it exists on a card or background,
and which one.
When working with text, you not only often require similar address information,
but must add still more-- such as the address of a particular character within the
text of a field or variable.
word 1 to 3 of myStory
identifies to HyperCard the first three words contained in the variable
myStory
.
line 3 of cd fld ID 14 of cd ID 342677
identifies to HyperCard the text between the second and third return characters in
card field ID 14, which is located on cd ID 342677.
HyperCard functions
Some folks will say that functions are the real subroutines in HyperCard, because
they act a little more like the BASIC gosub command lots of folks are used to, often
returning a new value to the handler which called upon them.
HyperCard functions can use both the global and local variables discussed before,
as well as store and retrieve data in text fields. But functions also make use of
a different form of variable, called parameters, to make them a bit more flexible
than the standard handler in a script.
Parameter variables
Between local variables which you want forgotten immediately after usage, and global
variables which you want to remember always, are parameter values.
By parameter values I mean values passed from one handler to another, or from a
handler to an function, or other routine, and maybe back again.
Unlike global variables, you can declare parameters 'on-the-fly' in a script, without
any prior warning to HyperCard, much as you can local variables. But there are a
couple of rules you must subscribe to in their usage:
One, you must list any parameters used in your handler or function upfront, in
the
on [handler name]
header, or very first line of a function script. For example, a
doMenu
handler often uses a parameter containing the menu option a user has chosen, by its
header being written something like
on doMenu command
, where
command
may then be used within the body of the handler script as a container holding the
specific option a user indicated with their pointer. The name you use for the parameter
doesnt matter much-- HyperCard will accept just about whatever you feel like using.
Two, in functions that you want to return a value to the handler which called them,
you must instruct HyperCard as to this desire. For example, the last statement before
the closing line of a function might say
return aardvark,
and whatever value had been placed in
aardvark
within the function will be made available to the handler which called upon the function
in the first place. An example would be the passing of a simple "true" or "false"
value from a function back to the handler which called it, like below:
In a
closeStack
handler I have the statement...
if FLUXDiff() then
...which calls upon the function below...
function FLUXDiff
return not(there is a card "Comic Relief Init")
end FLUXDiff
In this case the call upon the function in the
closeStack
handler will cause HyperCard to read
FLUXDiff()
as the function's returned result rather than the word
FLUXDiff()
. As the function returns a true or false value only, one of those values is what
HyperCard sees when it looks at the function call in the
closeStack
script.
Here,
FLUXDiff()
returns "true" if there's NOT a card named "Comic Relief Init" in the current stack.
And returns "false" if there IS such a card. This may seem confusing, and I must
admit I myself have to think about it a little each time I examine it, to fully understand it. It's the Boolean
not
that's included in the function statement, that throws us. If you don't know what
"Boolean" means, don't fret; it's just a label used in mathematics and programming
for the use of logical ideas like "and, not", and "or",which all come in handy when you're talking to computers, since they readily understand
such things. For example, in the statement
if tryVegas = 10 and tryLA = 12 then go next cd,
the computer will understand you only want to go to the next card if BOTH variables
contain the stated values; which here means both variables must contain the correct
value for anything to happen. Change the statement by replacing
and
with
or,
and the computer will take you to the next card if either
of the variables contain the desired value. As for the Boolean
not,
an example was given previously in the explanation of the
FLUXDiff()
function.
But getting back to passing parameters:
What if you need to pass something more substantial than "true" or "false" from
a function to a handler? What then?
Essentially you do the same thing, only substituting a variable name for the expression
we used above of
not(there is a card "Comic Relief Init").
Heres an example:
Let's say I'm going to use a button script to change the script of another object.
In the button script I have the line...
put scripObject() into bullsEye
...and so somewhere else I must have a function
scripObject
, which performs some work concerning the variable
bullsEye
, returning the results of that work to the commanding button script. Near the end
of the
scripObject
function I say...
return interMediateValue
...which contains the result of the functions calculations..
...and so when HyperCard sees the line...
put scripObject() into bullsEye
... it sees the returned value of
interMediateValue
when it looks at the word
scripObject()
. And thereby places this value into the variable
bullsEye
, there inside the handler which called upon the function in the first place.
Escapes and aborts; extricating yourself from messy situations and avoiding unnecessary
scripting
Often inside a handler you may find yourself figuratively painted into a corner,
in that you've reached a circumstance where you'd like the routine to stop, but there's
still another apparent 25 script lines following it to be gone through by HyperCard. How can you just say to HyperCard "Never mind the rest of the script if such and
such happens"? With an
exit
command. The
exit
command is flexible, allowing a scripter several options: it can be used to escape
only a repeat loop inside a handler, but still allow the execution of any remaining
commands in that handler; it can also provide escape from the handler altogether,
immediately and completely. The first two examples below illustrate those two actions. The
third example is an
exit to HyperCard
, and is used when the doo-doo is extra deep; your script is very complex, execution
might be inside a handler called by another handler called by another handler, or
you've got HyperCard jumping through some loops at the time that seem particularly
difficult to get the program to break away from cleanly. Though the
exit to HyperCard
may be the most powerful version of the command, you should be careful where you
use it, as it effectively will stop any and all on-going processes in your stack
at the time-- and only an
idle
handler or manual user action can start them up again.
exit repeat
exit [handler name]
exit to HyperCard
Warning Signs
When you're first starting out programming in HyperCard, one warning sign that
you're getting off track could be excessive complexity and length in your scripts.
For often these things will signal that you're laboring under some gross misconceptions
in HyperCard syntax, variable usage, program loops, or available commands. If your script
begins to get intimidating looking (or starts running noticeably slower), here's
some things to check for that might allow you to improve matters:
Are you using several repeat loops when one would do?
Is there a lot of redundant statements in your script? Where you do the same thing
many times, with only an ID number or object name changing between statements? If
so, you might be able to drastically shorten your script with a simple repeat loop.
If the numbers that change are sequential, you can easily let the repeat loop's counter
variable stand for them in a single statement. You can use a modified version of
the counter value if it's larger or smaller than what you require, by subtracting
or adding a constant value to it before inserting it into a statement (Just be careful that
your repeating loop still gets the right counter value for its own work!) If you're
using names for your objects, or you require random, non-sequential ID numbers, you
can put these into a comma-delimited container, or item list, and feed them to the repeating
statement using the loop counter as an index to the values.
Many times you can replace an entire section of code in a script with a single
handler name, and then install that script under that new handler name elsewhere
in your stack. In some cases this maneuver will allow you to substitute the new handler
name for the identical script at many places in your original listing, making the whole
thing much easier to examine and understand-- not to mention much more compact on
disk.
Making use of scripts found in other stacks
Both novice and expert scripters should do this on a pretty regular basis. Though
you must avoid outright plagiarizing of someone else's copyrighted code, this will
be an unlikely scenario for most scripters, for the following reasons:
One, rarely will you find a script that does exactly what you want it to, as written.
Perhaps the found script performs on a text field what you'd rather do on a button.
Maybe your idea requires a lot more functionality than the found script provides,
necessitating considerable rewriting and experimentation to make it work. Maybe the
found script is far too complex, and you need only a small piece of it. Any of these
possibilities, as well as innumerable others, will force you to change a found script
so extensively that the best HyperCard experts in the world would have a tough time
saying your finished script infringed on the original.
Two, in order to make a found script work with scripts you've already created,
it will often be necessary to change all or many of the variables in the found script
to match your other code elements. This change alone can make for substantial modifications.
Three, major changes in the repetitive loops of a script will often be in order.
You may have to remove nested loops, or add some, in order to get the effects you
want. You may have to adjust counters and high end or low end limits on the number
of times a loop repeats.
Four, you might want to make a lot of changes simply to make the look of the found
script consistent with how you personally do things, in order to avoid later confusion
for yourself. For instance, many scripters make variable names stand out in scripts by capitalizing a central letter-- by typing "doThis
" rather than "dothis". Or by using "repeat until" rather than "repeat while" or "repeat with";
others may use only two letter handler names like "cs, nm, vc"
,
and so on, rather than more descriptive identifications.
Five, you'll probably want to add some comments as you work.
Six, many stack authors may welcome your use of their code-- as long as you add
substantially to its quality, speed, and/or flexibility-- and make it available to
them to continue tweaking themselves....it all adds to the strength of the brighter
side of the Force, Luke...
What if you DO find a piece of script that's already "just right"?
In many cases the original author will have stated in his stack that it's OK to
use his scripts in this manner, so long as you subscribe to certain conditions, like
maybe mentioning his name in your stack's credits or whatever (it's good manners
to do this even if they don't specifically request it). Whatever the stated conditions are,
be sure to follow them-- otherwise you could end up like those guys "Milli Vinilli",
who were revealed to be lip synching their songs, instead of really singing. Nobody
wants to become the Milli Vinilli of HyperCard scripting.
So what if the original author's requirements are too stiff? Say, they demand
a $1000 licensing fee for you to use their stuff? Or a $10 commission on each stack
you sell with their script excerpt included? Ouch!
In this case you have only two choices: bite the bullet and pay them as they demand--
or give it up, and try to replace their script with your own work, or a script available
from a more economical source. Of course, if your project is destined to be the next Myst-style CD ROM hit, you can easily afford to pay someone a hefty licensing
fee upfront. But few folks will be in that position, and so will want a more balanced
arrangement with the author.
Copyright © 1993,
1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 by J.R. Mooneyham. All rights reserved.
Back to the Table of Contents of the Signposts Timeline
Back to J.R.'s
WebFLUX Page (the magazine)
Back to J.R.'s
WebWork Page (A hefty catalog of links to almost everything)
Site Map for the
WebFLUX and WebWork pages