MenuStuff
Volume Number: 4
Issue Number: 10
Column Tag: Pascal Proceedures
MenuStuff for Handling Menus
By Mark Shirley, Paris, France
MenuStuff
Menus are an essential part of the Macintosh interface, and the resource formats
for creating menus and the ToolBox routines for implementing them and reacting to
user choices are some of the most straight forward areas of Macintosh programming.
But menus have two faces : the logical aspect, and what I will call the “housekeeping”
aspect. Reacting to choice i,j by selecting font F is an example of the logical aspect;
knowing that item j in menu i must be checked when chosen and that items j-m..j-1
and items j+1..j+n must be unchecked (to say nothing of the repercussions this might
have on the display characteristics of Font Size Menu Z) is an example of the
housekeeping aspect. This latter aspect is neither conceptually difficult nor
algorithmically rewarding, but it generates an awful lot of extra code which is
essentially similar from one program to another yet usually sufficiently
program-specific that the routines end up being rewritten every time. Furthermore,
the algorithmic triteness creates a paradox : programmer disinterest invites bugs, and
it is usually late in the development cycle before these “visual feedback” features
work correctly, whereas more difficult parts of the program, by capturing the
programmer’s interest, frequently are bug-free (sic) much earlier.
My solution to this problem(contrary to Beethoven, if you will excuse the
presumption, I hate writing variations!), has been to :
1) list the different ways menu items behave and extract common types,
2) devise a custom resource format that codes the menu’s “housekeeping” behavior;
this custom resource complements (without modifying or replacing) the standard
menu resource,
3) write a set of routines which, driven by the custom resource, carry out all
housekeeping activities for the application program in a completely transparent
manner, i.e., the program needs only call the front end after MenuSelect and then
carry out the logical aspects of choice i,j in blissful ignorance of the
mechanico-visual aspects of the User choice.
Obviously my menu types are not exhaustive. It seems that almost every new
program that hits the market finds a new variant on the menu theme. Nevertheless the
basic types are covered and new types are easy to create. The types work equally well
whether the menu be straight pull-down, hierarchical, or pop-up (and presumably
tear-off); dynamic item addition/suppression are supported (though with certain
restrictions on the types); a mechanism is provided for storing/restoring a snapshot
of a given menu state; dynamically allocated zones containing runtime information can
be resource-triggered; and finally PROC type resources can be associated with menus
and transparently executed on menu selection, thereby providing a mechanism for
interactions between menus.
Excuse the drum-beating : it’s really quite straightforward. There are no neat
ToolBox tricks to be learned from this article, its only purpose is to relieve you of
some of the drudge-work of menu handling.
The sources in this article are written in Consulair Mac C (having started it a
long time ago, I updated in Mac C; it will be last thing I write in Mac C). Adapting it to
the C language of your choice should be easy, insofar as no specific features are used
(other than 32 bit int’s).
Just as menu housekeeping in an application program takes up an amount of space
disproportionate to its conceptual interest, so these routines are rather long. In order
to fit it into the space graciously allotted by the Editor (let us not dwell on his
displeasure with the original size), I had to cut out quite a bit of code, and compact the
remaining code to the detriment of its legibility. The source available with this issue
contains the full length code along with a very thorough test program that allows
complete testing of menus before integrating them into the final application. This is
very important, because the custom nature of the resource makes coding it very
error-prone, and the source code presented has had the data-validation part amputated
and the error reporting routine severely watered down. In the perfect leisure world of
my fantasy, rather than using RMaker to create the custom resource it would be
generated by an interactive utility.
Name Calling
Not surprisingly, the key data structure is called MenuStuff, and this structure
will be referred to as MS (that these also happen to be my initials is of course pure
coincidence). Its complete description may be found in “MS.h”.
Item Types
The basic item types recognized by MS routines are the following :
1) Normal : straight-Joe items requiring no special processing.
2) Check Toggled : an individual item which is either checked or not checked, and
toggles between these states with each successive selection. Example : Gremlin
Alert in QUED.
3) Text Toggled : an individual item whose text exists in two variants; successive
selections toggle the display of these alternative text variants. Example : a
Hide/Show item.
4) Enabled Pair : a pair of menu items one of which one is enabled and the other
disabled at any given time. Selecting the enabled member disables the selected
member and enables the other member of the pair. Example : Open/Close items in
MacWrite.
5) Checked Range : a set of items for which one and only one element must be checked
at any given time. Example : a Font Size menu.
6) Cumulative Range with Reset : a set of items with one singular element (the
parent) and one or more non-singular elements (members); at any given time at
least one element must be checked. Several members may be con currently
checked, but the parent and members are never checked con currently; selecting a
member unchecks the parent, and unchecking the last checked member checks the
parent (no, this is not a chess journal, you did pick up the right magazine).
Individually, members behave like check toggled items. Archetypical example :
the Style Menu.
7) Enabled Set : a set of items comprised of two disjoint subsets. Each subset contains
a singular element (the parent) and one or more non-singular elements
(members); at any given time all elements of one of the disjoint subsets are
enabled and all members of the other subset are disabled. Selecting the enabled
parent of the enabled subset disables all elements of the selected subset and
enables all elements of the other disjoint subset. Individually, the member
elements behave like normal items.
8) Text Toggled Set : a set of items with one singular element (the parent) and one or
more non-singular elements (members); each element’s display text exists in
two variants, and successive selections of the parent toggle the display of these
alternative text variants for all elements in the set. Variant display is coherent,
i.e, at any given time the same variant level is displayed by all elements of the
set. Individually, member elements behave like normal types.
9) Disabled Menu Lines : self-explanatory; used to “parse” set membership when
set membership is defaulted in the MS resource, otherwise ignored by MS
routines (more on this point later).
I am not trying to dictate menu behavior, I am describing the predefined types
this version of MS knows how to handle. Any type of behavior differing from the
predefined types may either be tagged as Normal (hence ignored by MS routines and
fielded by the application), or else new types may be added to the MS routines.
Functional Overview
Menu creation per se is totally independent of MS. Once menus are created, if the
menu contains items the programmer wants MS routines to handle, a complementary
MS resource must be created using RMaker or equivalent. At this point linking the test
program (MST.Rel) with the menu and MS resources will immediately show not only
what the menus look like, but also how they react to user choices. Not a line of
application code has so far been written, yet the menu interface is a faithful presage of
the real thing.
In the application, a call to MSMake should be followed by a call to MSInsert.
Then, in the doMenu section of the application, before the Menu/ItemNumber Switch, a
call to MSDispatch should be inserted. To delete a menu from the MenuBar without
disposing of memory structures (both standard and MS), the application should call
the ToolBox DeleteMenu routine. To dispose of associated memory structures (again,
both standard and MS) the application should call MSDispose. To add/delete items
to/from existing menus MSInsMenuItem/MSDelMenuItem should be called. See below
for snapshots and inter-menu interactions.
In its present form MS routines do not act on MenuBars, but adding a front end to
MSMake, MSInsert, and MSDispose is trivial. Actually MS itself is trivial, and the the
point of this whole article is to prove the usefulness of coordinated triteness.
Underlying Mechanics
More on name calling : up until now I have used MS for MenuStuff and
consistently qualified it with either “routines” or “ resources”. The main MS
resources type is ‘MST ‘ (MST blank); therefore I will henceforth use MST to
designate the resource and MS to generically designate MS routines.
MST is a non-purgeable, variable length resource that MS never rewrites to
disk. As you will see shortly, menu states can be faithfully memorized and restored
using a small, masked flag structure. MST is composed of constant header fields
affecting the entire menu, a variable size Menu Verb Table (MVT) coding behavior at
the item level, and an optional ‘PROC’ function parameter zone following the MVT
(more on this later).
Each MVT entry is a MenuVerb structure, composed of an opCode component and a
5-element argument table (argT) component. OpCodes and argTs are typedef’ed to byte,
which is adequate for the predefined types but may be changed if longer argTs should be
necessary for new types. MVT[0] contains bit flags (referred to as Menu Flags) which
affect the way menus are set up by MSMake, and govern certain aspects of PROC
function behavior. The opCode identifies the item type, and the argTs supply positional
information necessary to implement the type’s behavior. Not all types use all the
argTs, and argT[4] (the last one) is never used by any predefined type; it pads to word
length boundary, and may be used for new types, or it can hold any item information
the calling application might wish to store there. In general, argT[0] through argT[4]
correspond to “first”, “last”, “parent” and “other” positional parameters, but in
the case of TextToggled items, for example, “other” (argT[4]) is the index of the first
text variant in the associated STR# resource (the second variant is other+1). These
positional arguments may be expressed either as absolute itemNumbers or may be
calculated by MS based on the previous and the next disabled line positions in the menu.
Using direct references allows interspersing different members of a given group
anywhere in the menu (as long as there are no intervening types with the same
opCode), whereas using disabled line defaulting makes coding the resources a lot
easier. A concrete example : for an Enabled Set, first and last are the position of the
first and last element of THIS disjoint set, parent is the master item controlling THIS
disjoint set’s enabling/disabling, and other is the itemNumber of the OTHER disjoint
set’s parent.
Another essential part of the MST resource is the ToggleFlag component (not to be
confused with the menu flags in MVT[0]). In the present version ToggleFlag is
typedef’ed to MenuVerb, giving a 48 bit field, but this can be increased to handle more
items (but have mercy on the poor User!). Every time an item different from Normal
or Disabled Line is passed to MSDispatch, the associated flag is toggled from its
previous state. This new value determines the transition processing. For example,
toggling a text toggled item causes the other+ToggleFlag[i] variant to be displayed.
Certain transitions, such as a 1->0 on a Checked Range are not allowed and the toggle
flag is reset to its original value (otherwise there would be no checked member in the
range). With one exception (Expanded Ranges, see below), the initial state of
ToggleFlags must be CORRECTLY coded into the resource; bits are numbered from left
to right to allow for compatibility should ToggleFlags need to be lengthened. Bit zero is
not used.
Menu flags, occupying the first MVT entry, control the overall set up of the
menu. There is a flag indicating if AddResMenu should be called, and if it is set, the
field sysRes in the constant header should contain a resource type. Since the number of
resource items cannot be determined at resource creation time, it is impossible to code
MVT entries for these items. To solve this problem, another flag enables MVT
expansion, and yet another enables ToggleFlag set up and initial menu checking. In the
present version only Checked Ranges may be expanded using this mechanism, and this
is adequate for most applications. Two fields in the constant header govern this
expansion : staticCount and allocCount. StaticCount is the itemNumber of the last MVT
explicitly coded, and allocCount is the total number of entries allocated in the MVT.