[comp.sys.atari.st] Professional GEM 5-8 +sources Part 01 of 04

exodus@uop.UUCP (Freddy Kreuger) (10/09/87)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 4)."
# Contents:  README gem5.asc gem7.asc
# Wrapped by exodus@uop on Thu Oct  8 16:43:02 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(466 characters\)
sed "s/^X//" >README <<'END_OF_README'
XProfessional GEM Parts 5-8 + Sources
X
XThe two source files must be concatenated and uudecoded the unarced.
XAll sources for the rest of the Professional GEM series are include.
XParts 5-8 are in this posting also.
X
XGreg Onufer
X
X--
X
XGreg Onufer   		GEnie: G.ONUFER		University of the Pacific
XUUCP:	 ...!ucbvax!\				**POSTMASTER @ UOP**
X		     -ucdavis!\
X	...!lll-crg!/	       -uop.edu!{exodus, exodusr, postmaster, root}
X	     ...!ptsfa!cogent!/
X	     ...!cepu!retix!/
END_OF_README
if test 466 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f gem5.asc -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"gem5.asc\"
else
echo shar: Extracting \"gem5.asc\" \(17318 characters\)
sed "s/^X//" >gem5.asc <<'END_OF_gem5.asc'
X                        *PROFESSIONAL GEM*
X                Part 5 -- Resource Tree Structures
X                           By Tim Oren
X
X     This is the fifth issue of ST PROFESSIONAL GEM, concluding our
Xtrek through GEM dialogs and resources with a look at the internal
Xstructure of object trees.  Also, I'll answer a number of questions
Xof general interest which have been received via the ANTIC ONLINE
XFEEDBACK.  As always, there is a download file associated with this
Xcolumn: GEMCL5.C, which you will find in DL3 of the new Atari 16-bit
XSIG (type GO PCS-58 or GO ATARI16).
X
X     Even if you have no immediate use for this issue's code, be sure
Xto take the download anyway; some of the routines will be used in
Xlater articles.
X
X     In the last installment, we established that resources trees are
Xpointed to by the tree index, and that they are composed of objects
Xwhich contain pointers onward to other structures.  However, we
Xpassed over the issue of linkage among the objects within a tree.  It
Xis now time to go back and cure this omission.
X
X     The technical term for the linkage scheme of an object tree is a
X"right-threaded binary tree".   If you already know what this is, you
Xcan skim over the next few paragraphs.  If you happen to have access
Xto a copy of the book "FUNDAMENTAL ALGORITHMS", which is part of the
Xseries THE ART OF COMPUTER PROGRAMMING by Donald E. Knuth, you might
Xwant to read his excellent discussion of binary trees beginning on
Xpage 332.
X
X     For the following discussion, you should have a listing of the C
Ximage of a resource tree in front of you.  For those who do not have
Xthe listing from the last column, I have included a fragment at the
Xbeginning of the download.  Before we begin, I should warn you of one
Xpeculiarity of "computer trees":  They grow upside-down!  That is,
Xwhen they are diagrammed or described, their root is at the top, and
Xthe  "leaves" grow downward.  You will see this both in the listing,
Xand in the way the following discussion talks about moving through
Xtrees.
X
X     Each GEM object tree begins at its ROOT object, numbered zero,
Xwhich is the object pointed at by the tree index.  There are three
Xlink  fields at the beginning of each object.  They are called
XOB_NEXT, OB_HEAD,  and OB_TAIL, which is the order in which they
Xappear.
X
X     Each of the links is shown as an index relative to the root of
Xthe current tree.  This means that the link '0' would refer to the
Xroot of the tree, while '2' would indicate the object two lines below
Xit. The special link -1 is called NIL, and means that there is no
Xlink in  the given direction.
X
X     Each object, or node, in a tree may have "offspring" or nodes
Xwhich are nested below it.  If it does, then its OB_HEAD will point
Xto its first (or "leftmost") "child", while the OB_TAIL will point to
Xthe last ("rightmost") of its offspring.  The OB_NEXT pointer links
Xthe  children together, with the OB_NEXT of the first pointing to the
Xsecond, and so on, until the OB_NEXT of the last finally points back
Xto its parent, the object at which we started.
X
X     Remember that each of these children may in turn have offspring
Xof their own, so that the original "parent" may have a large and
Xcomplex collection of "descendents".
X
X     Let's look at the first tree in the download to see an example
Xof this structure.  The very first object is the ROOT.  Note that its
XOB_NEXT is NIL, meaning that there are no more objects in the tree:
Xthe ROOT is both the beginning and the end of the tree.  In this
Xcase, the OB_HEAD is 1 and the OB_TAIL is 3, showing that there are
Xat least two different children.
X
X     Following OB_HEAD down to the next line, we can trace through
Xthe OB_NEXT links (2, 3, 0) as they lead through a total of three
Xchildren and back to the ROOT.  You will notice that the first two
Xchildren have NIL for the OB_HEAD and OB_TAILs, indicating that they
Xhave no further offspring.
X
X     However, node three, the last child of the ROOT, does have the
Xvalue 4 for both its OB_HEAD and OB_TAIL.  By this we can tell that
Xit  has one, and only one, offspring.  Sure enough, when we look at
Xnode four, we see that its OB_NEXT leads immediately back to node
Xthree. Additionally, it has no further offspring because its OB_HEAD
Xand OB_TAIL are NIL.
X
X     You will find that object trees are always written out by the
XResource Construction Set in "pre-order".  (Again, see Knuth if you
Xhave a copy.)  This means that the ROOT is always written first, then
Xits offspring left to right.  This rule is applied recursively, that
Xis, we go down to the next level and write out each of these nodes,
Xthen THEIR children left to right, and so on.
X
X     For a further example, look at the next tree in rs_object in the
Xdownload.  You will see that the ROOT has an OB_HEAD of 1 and an
XOB_TAIL of 6, but that it actually has only three offspring (nodes 1,
X2 and 6).  We see that node 2 itself had children, and applying the
Xrule given above, they were written out before continuing with the
Xnext child of the ROOT.
X
X     Why was this seemingly complex structure chosen for GEM?  The
Xreason has do with the tasks of drawing objects in their proper
Xlocations on the screen, and determining which object was "hit" when
Xa mouse click is detected.
X
X     To find out how this works, we must look at four more fields
Xfound in each object: OB_X, OB_Y, OB_WIDTH, and OB_HEIGHT.  These
Xfields are the last four on each line in the sample trees.
X
X     Each object in a tree "owns" a rectangle on the screen.  These
Xfields define that rectangle.  When a resource is stored "outside"
Xthe  program the fields are in character units, so that an object
Xwith OB_WIDTH  of 10 and OB_HEIGHT of 2 (for instance) would define a
Xscreen area 10  characters wide and 2 high.
X
X     When the resource is read into memory with an rsrc_load call,
XGEM multiplies the appropriate character dimension in pixels into
Xeach of these fields.  In this way portability is achieved: the same
Xresource file works for any of the ST's three resolutions.  Knowing
Xhow rsrc_load works, your code should treat these fields as pixel
Xcoordinates.
X
X     (I have committed one oversimplification above.  If an object is
Xnot created on a character boundary in the RCS, then the external
Xstorage method described will not work.  In this case, the lower byte
Xof each rectangle field is used to store the nearest character
Xposition, while the upper byte stores the pixel remainder to be added
Xafter the character size is multiplied in.
X
X     Non-character-boundary objects may only be created in the "FREE"
Xtree mode of the Resource Construction Set (also called "PANEL" in
XRCS 2.0). You should use them only in programs which will run in a
Xsingle ST screen  mode, because pixel coordinates are not portable
Xbetween resolutions.)
X
X     The first real secret of object rectangles is that each OB_X and
XOB_Y is specified RELATIVE to the X and Y coordinate of its parent
Xobject  within the tree.  This is the first property we have seen
Xthat is actually "inherited" from level to level within the tree.
X
X     The second secret is more subtle:  Every object's rectangle must
Xbe entirely contained within the rectangle of its parent.  This
Xprinciple goes by the names "bounding rectangles" or "visual
Xhierarchy".  We'll see in a moment how useful it is when detecting
Xmouse/object collisions.
X
X                         HOW GEM DOES IT.
X
X     Knowing these secrets, and the linkage structure  of object
Xtrees, we can deduce how a number of the GEM operations must work.
XFor instance, consider objc_offset,  which returns the actual screen
XX and Y  of an object.  We can see now that simply loading the OB_X
Xand OB_Y fields of  the object does not suffice: they only give the
Xoffset relative to the parent  object.  So, objc_offset must BEGIN
Xwith these values, and then work its way  back up to the ROOT of the
Xtree, adding in the offsets found at each level.
X
X     This can be done by following the OB_NEXT links from the chosen
Xobject.  Whenever OB_NEXT points to an object whose OB_TAIL points
Xright back  to the same location, then the new node is another level,
Xor "parent" in the  tree, and objc_offset adds its OB_X and OB_Y into
Xthe running totals.  When OB_NEXT becomes NIL, then the ROOT has been
Xreached and the totals are the values to return.  (By the way,
Xremember that the OB_X and OB_Y of the ROOT are undefined until
Xform_center has been called for the tree.  They are shown as zeroes
Xin the sample trees.)
X
X     We can also figure out objc_draw.  It works its way DOWN the
Xtree, drawing each object as it comes to it.  It, too, must keep a
Xrunning X and Y variable, adding in object offsets as it descends
Xtree levels (using OB_HEAD), and subtracting them again as it returns
Xfrom each level.   Since the larger objects are nearer the ROOT, we
Xcan now see why they are  drawn first, with smaller objects drawn
Xlater or "on top of" them.
X
X     (If you write an application which needs to move portions of a
Xdialog or screen with respect to each other, you can take advantage
Xof inheritance of screen position in objc_draw.  Simply by changing
Xthe OB_X and/or OB_Y of an object, you can move it and its entire
Xsub-tree to a new location in the dialog.  For instance, changing the
Xcoordinates of the parent box of a set of radio buttons will cause
Xall of the buttons to move along with it.)
X
X     Objc_draw also gives us an example of the uses of visual
Xhierarchy. Recall that a clipping rectangle is specified when calling
Xobjc_draw. At each level of the tree we know that all objects below
Xare contained in the screen rectangle of the current object.  If the
Xcurrent rectangle falls completely outside the specified clipping
Xrectangle, we know  immediately that we need not draw the object, or
Xany of its descendents! This ability to ignore an entire subtree is
Xcalled "trivial rejection".
X
X     Now it's rather easy to figure out objc_find.  It starts out by
Xsetting its "object found" variable to NIL.  It begins a "walk"
Xthrough the entire object tree, following OB_HEAD and OB_NEXT links,
Xand keeping  a current X and Y, just like objc_draw.
X
X     At each node visited, it simply checks to see if the "mouse" X,Y
Xspecified in the call are inside the current object's rectangle.  If
Xthey  are, that object becomes the found object, and the tree walk
Xcontinues with the object's offspring, and then siblings.  Notice how
Xthis checking of offspring makes sure that a smaller object nested
Xwithin, i.e., below, a larger object is found correctly.
X
X     If the mouse X,Y position is not within the object being
Xchecked, then by visual hierarchy it cannot be within any of its
Xoffspring, either.   Trivial rejection wins again, and the entire
Xsub-tree is skipped!  Objc_find  moves on to the OB_NEXT of the
Xrejected object.
X
X                       THOUGHT EXPERIMENTS.
X
X     Thinking about the objc_find algorithm  reveals some information
Xabout its performance, and a few tricks we may  use in improving the
Xappearance of dialogs and other object trees.
X
X     First consider the problem of a dialog which contains many
Xobjects. If we lay them all out "side-by-side", then they will all be
Ximmediate offspring of the ROOT object.  In this situation, the
Xtrivial rejection method will gain nothing.  The time objc_find takes
Xto complete will vary linearly with the total number of objects.
XThis is called an "Order N" process.
X
X     Suppose that instead we broke up the dialog into two areas with
Xinvisible boxes, then broke up each of these areas in a like fashion,
Xand so on until we got down to the size of the individual selectable
Xobjects.  The number of bottom level objects in this scheme is a
Xpower  of two equal to the depth of the tree. Trivial rejection is
Xused to its  fullest in this case.  It is called an "Order Log N"
Xprocess, and is much  more efficient for large numbers of objects.
X
X     In practice, the speed of the ST will allow you to ignore this
Xdistinction for most dialogs and other trees.  But if you get into a
Xsituation when speed is critical in searching a large tree, remember
Xthat nesting objects can improve performance dramatically.
X
X     If you have been following closely, you may have also noticed a
Xhole in the visual hierarchy rule.  It says that all of a node's
Xchildren must lie within its rectangle, but it does NOT guarantee
Xthat the children's  rectangles will be disjoint, that is, not
Xoverlap one another.  This peculiarity is the basis of several useful
Xtricks.
X
X     First, remember that objc_find always tries to scan the entire
Xtree.  That is, it doesn't quit when it finds the first object on the
Xgiven coordinates.  As mentioned above, this normally guarantees that
Xnested objects will be found.  Consider, however, what happens when
Xthe mouse  coordinates are on a point where two or more objects AT
XTHE SAME LEVEL overlap: they will replace one another as the "found
Xobject" until  objc_find returns with the one which is "last", that
Xis, rightmost in  the tree.
X
X     This quirk can be used to advantage in a number of cases.
XSuppose that you have in a dialog an image and a string which you
Xwould  like to be selected together when either is clicked.  Nesting
Xwithin a common parent achieves nothing in this case.  Instead,
Xknowing that  form_do must use objc_find, you could use our trick.
X
X     You have to know that the Resource Construction Set normally
Xadds  objects in a tree left to right, in the order in which you
Xinserted them.  You proceed to build the dialog in the following
Xorder: insert the image  first, the string next, then carefully add
Xan invisible box which is not  nested within either, and size it to
Xcover them both.  Set the SELECTABLE  attribute for the box, and the
Xdialog manager will find it, and invert  the whole area, when either
Xthe image or string is clicked.
X
X     By the way, remember that the SORT option in the RCS will change
Xthe order of an object's offspring.  If you are going to try this
Xtrick, don't use SORT!  It will undo all of your careful work.
X
X     A TREEWALKER OF OUR OWN.
X
X     Since the GEM system gets so much mileage  out of walking object
Xtrees, it seems reasonable that the same method should  be useful in
Xapplication programs.  In the download you will find map_tree(). As
Xmany LISP veterans might guess from the name, this code will traverse
Xall or part of an object tree, applying a function to each node.  It
Xalso allows the function to return a true/false value specifying
Xwhether  the sub-tree below a particular node should be ignored.
XLet's examine map_tree() in more detail as a final review of object
Xtree structure.
X
X     First, look at the parameters.  "tree" is the long address of
Xthe object tree of interest, as retrieved by rsrc_gaddr.  "this" is
Xthe node at which to begin the traverse, and "last" is the node at
Xwhich to terminate.
X
X     In most cases, the beginning node will be ROOT, and the final
Xvalue will be NIL.  This will result in the entire tree being
Xtraversed. You may use other values, but be sure that you CAN get to
X"last" from "this" by following tree links!  Although map_tree()
Xincludes a safety  check to prevent "running off" the tree, you could
Xget some very strange  results from incorrect parameters.
X
X     The declaration for the final parameter, "routine", makes use of
XC construct which may be new to some.  It is a pointer to a
Xsubroutine which returns a WORD as a result.
X
X     Map_tree() begins by initializing a temporary variable, tmp1,
Xwhich is used to store the number of the last node visited.  Since no
Xnode will follow itself, setting tmp1 to the starting node is safe.
X
X     The main loop of the routine simply repeats visiting a new node
Xuntil the last value is reached, or the safety check for end of tree
Xis satisfied.
X
X     At any node visited, we can be in one of two conditions. Either
Xwe are at a node which is "new", that is, not previously visited, or
Xelse we are returning to a parent node which has already been
Xprocessed. We can detect the latter condition by comparing the last
Xnode visited (tmp1) with the OB_TAIL pointer of the current node.  If
Xthe node is "old", it is not processed a second time, we simply
Xupdate tmp1 and  continue.
X
X     If the node is new, we call "routine" to process it, sending the
Xtree address and object number as parameters.  If a FALSE is
Xreturned,  we will ignore any subtree below this node.  On a TRUE
Xreturn, we load up  the OB_HEAD pointer and follow it if a subtree
Xexists.  (If you don't care  about rejecting subtrees, simply remove
Xthe if condition.)  Finally, if  the new node had no subtree, or was
Xrejected by "routine", we follow along  its OB_NEXT link to the next
Xnode.
X
X     A simple application of our new tool shows its power.  From a
Xprevious column you may recall the tedium of deselecting every button
Xinside a dialog after it was completed.  Using map_tree(), you can
Xdeselect EVERY OBJECT in the tree by using map_tree(tree, ROOT, NIL,
Xdesel_obj); You must use a slightly modified version of desel_obj()
X(included in the download) which always returns TRUE.  Be sure to
Xdefine or declare desel_obj() in your code BEFORE making the map_tree
Xcall!
X
X     In future columns, I will return to map_tree() and show how it
Xcan be used for advanced techniques such as animated dialogs.  In the
Xmeantime, experiment and enjoy!
END_OF_gem5.asc
if test 17318 -ne `wc -c <gem5.asc`; then
    echo shar: \"gem5.asc\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f gem7.asc -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"gem7.asc\"
else
echo shar: Extracting \"gem7.asc\" \(16958 characters\)
sed "s/^X//" >gem7.asc <<'END_OF_gem7.asc'
X                        *PROFESSIONAL GEM*
X                           By Tim Oren
X                   Column #7 - Menu Structures
X
X    HAPPY NEW YEAR!  This is article number seven in the ST
XPRO GEM series, and the first for 1986.  In this installment,
XI will be discussing GEM menu structures and how to use them
Xin your application.  There is  also a short Feedback
Xresponse section.  You will find the download file containing
Xthe code for this column in the file GEMCL7.C in DL3 of the
XATARI16 SIG (PCS-58).
X
X    MENU BASICS.  In ST GEM, the menu consists of a bar
Xacross the top of the screen which displays several sub-menu
Xtitles.  Touching one of the titles causes it to highlight,
Xand an associated "drop-down" to be drawn  directly below on
Xthe screen.  This drop-down may be dismissed by moving  to
Xanother title, or by clicking the mouse off of the drop-down.
X
X
X    To make a selection, the mouse is moved over the
Xdrop-down.  Each  valid selection is highlighted when the
Xmouse touches it.  Clicking the mouse  while over one of
Xthese selections picks that item.  GEM then undraws the
Xdrop-down, and sends a message to your application giving the
Xobject number  of the title bar entry, and the object number
Xof the drop-down item which  were selected by the user.  The
Xselected title entry is left highlighted  while your code
Xprocesses the request.
X
X    MENU STRUCTURES.  The data structure which defines a GEM
Xmenu is (surprise!) an object tree, just like the dialogs and
Xpanels which we have discussed before.  However, the
Xoperations of the GEM menu manager are quite different from
Xthose of the form manager, so the internal design of the menu
Xtree has some curious constraints.
X
X    The best way to understand these constraints is to look
Xat an  example.  The first item in the download is the object
Xstructure (only) of the menu tree from the GEM Doodle/Demo
Xsample application.
X
X    The ROOT of a menu tree is sized to fit the entire
Xscreen.  To satisfy the visual hierarchy principle (see
Xarticle #5), the screen is divided into two parts: THE BAR,
Xcontaining the menu titles, and THE SCREEN, while contains
Xthe drop-downs when they are drawn.  Each of these areas is
Xdefined by an object of the same name, which are the only two
Xobjects linked directly below the ROOT of a menu tree.  You
Xwill notice an important implication of this structure:  The
Xmenu titles and their associated drop-downs are stored in
Xentirely different subtrees of the menu!
X
X    While examining THE BAR in the example listing, you may
Xnotice that its OB_HEIGHT is very large (513).  In
Xhexadecimal this is 0x0201.  This defines a height for THE
XBAR of one character plus two pixels used for spacing.  THE
XBAR and its subtree are the only objects which are drawn on
Xthe screen in the menu's quiescent state.
X
X    The only offspring object of THE BAR is THE ACTIVE.  This
Xobject defines the part of THE BAR which is covered by menu
Xtitles.  The screen rectangle belonging to THE ACTIVE is used
Xby the GEM screen manager when it  waits for the mouse to
Xenter an active menu title.  Notice that THE ACTIVE and its
Xoffspring also have OB_HEIGHTs with pixel residues.
X
X    The actual menu titles are linked left to right in order
Xbelow THE ACTIVE.  Their OB_Xs and OB_WIDTHs are arranged so
Xthat they  completely cover THE ACTIVE.  Normally, the title
Xobjects are typed  G_TITLE, a special type which assures that
Xthe title bar margins are correctly drawn.
X
X    THE SCREEN is the parent object of the drop-down boxes
Xthemselves. They are linked left to right in an order
Xidentical with their titles, so  that the menu manager can
Xmake the correct correspondence at run-time.  The OB_X of
Xeach drop-down is set so that it is positioned below its
Xtitle on the screen.
X
X    Notice that it is safe to overlap the drop-downs within a
Xmenu,  since only one of them will be displayed at any time.
XThere is one constraint on the boxes however:  They must be
Xno greater than a quarter screen in total size.  This is the
Xsize of the off-screen blit buffer which is used by GEM to
Xstore the screen contents when the drop-down is drawn.  If
Xyou exceed this size, not all the screen under the drop-down
Xwill be restored, or the ST may crash!
X
X    The entries within a drop-down are usually G_STRINGs,
Xwhich are optimized for drawing speed.  The rectangles of
Xthese entries must  completely cover the drop-down, or the
Xentire drop-down will be inverted  when the mouse touches an
Xuncovered area!  Techniques for using objects  other than
XG_STRINGs are discussed later in this column.
X
X    The first title and its corresponding drop-down are
Xspecial.  The title name, by custom, is set to DESK.  The
Xdrop-down must contain exactly  eight G_STRING objects.  The
Xfirst (again by custom) is the INFO entry,  which usually
Xleads to a dialog displaying author and copyright information
Xfor your application.  The next is a separator string of
Xdashes with the DISABLED flag set.  The following six objects
Xare dummy strings which GEM fills in with the names of desk
Xaccessories when your menu is loaded.
X
X    The purpose of this description of menu trees is to give
Xyou an understanding of what lies "behind the scenes" in the
Xnext section, which describes the run-time menu library
Xcalls.  In practice, the Resource Construction Set provides
X"blank menus" which include all of the required elements, and
Xit also enforces the constraints on internal structure.  You
Xonly need to worry about these if you modify the menu tree
X"on-the-fly".
X
X    USING THE MENU.  Once you have loaded the application's
Xresource, you can ask the AES to install your menu.  You must
Xfirst get the address of the menu tree within the resource
Xusing:
X
X    rsrc_gaddr(R_TREE, MENUTREE, &ad_menu);
X
X assuming that MENUTREE is the name you gave the menu in the
XRCS, and that ad_menu is a LONG which will receive the
Xaddress.  Then you call the AES to establish the menu:
X
X    menu_bar(ad_menu, TRUE);
X
X At this point, the AES draws your menu bar on the screen and
Xanimates it when the user moves the mouse into the title
Xarea.
X
X    The AES indicates that the user has made a menu selection
Xby sending your application a message.  The message type is
XMN_SELECTED,  which will be stored in msg[0], the first
Xlocation in the message  returned by evnt_multi().
X
X    The AES also stores the object number of the selected
Xmenu's  title in msg[3], and the object number of the
Xselected menu item in msg[4].  Generally, your application
Xwill process menu messages with nested C switch statements.
XThe outer switch will have one case for each menu title, and
Xthe inner switch statements will have a case for each entry
Xwithin the selected menu.  (This implies that you must give a
Xname to each title and to each menu entry when you create the
Xmenu in the RCS.)
X
X    After the user has made a menu selection, the AES leaves
Xthe title of the chosen menu in reverse video to indicate
Xthat your application is busy processing the message.  When
Xyou done with whatever action is indicated, you need to
Xreturn the title to a normal state. This is done with
X
X    menu_tnormal(ad_menu, msg[3], TRUE);
X
X (Remember that msg[3] is the title's object number.)
X
X    When your application is ready to terminate, it should
Xdelete its menu bar.  Do this with the call:
Xmenu_bar(ad_menu, FALSE);
X
X    GETTING FANCY.  The techniques above represent the bare
Xminimum to handle menus.  In most cases, however, you will
Xwant your menus to be more "intelligent" in displaying the
Xuser's options.  For instance, you can prevent many user
Xerrors by disabling inappropriate choices, or you can save
Xspace on drop-downs by showing only one line for a toggle and
Xaltering its text or placing and removing a check mark when
Xthe state is changed.  This section discusses these and other
Xadvanced techniques.
X
X    It is a truism of user interface design that the best way
Xto deal with an error is not to let it happen in the first
Xplace.  It  many cases, you can apply this principle to GEM
Xmenus by disabling  choices which should not be used.  If
Xyour application uses a "selection  precedes action" type of
Xinterface, the type of object selected may  give the
Xinformation needed to do this.   Alternately, the state of
Xthe  underlying program may render certain menu choices
Xillegal.
X
X    GEM provides a call to disable and re-enable menu
Xoptions. The call is:
X
X    menu_ienable(ad_menu, ENTRY, FALSE);
X
X to disable a selection.  The entry will be grayed out when
Xit is drawn, and will not invert under the mouse and will not
Xbe selected by the user. Substituting TRUE for FALSE
Xre-enables the option.  ENTRY is the name  of the object
Xwhich is being affected, as assigned in the RCS.
X
X    Note that menu_ienable() will not normally affect the
Xappearance or operation of menu TITLE entries.  However,
Xthere is an undocumented feature which allows this.  If ENTRY
Xis replaced by the object number of a title bar entry with
Xits top bit set, then the entire associated drop-down will be
Xdisabled or re-enabled as requested, and the title's
Xappearance will be changed.  But, be warned that this feature
Xdid not work reliably in some early versions of GEM.  Test it
Xon your copy of ST GEM, and use it with caution when you
Xcannot control the version under which your application may
Xrun.
X
X    It is also possible to disable menu entries by directly
Xaltering the DISABLED attribute within the OB_STATE word.
XThe routines enab_obj() and disab_obj() in the download show
Xhow this is done.  They are also used in set_menu(), which
Xfollows them immediately.
X
X    Set_menu() is a utility which is useful when you wish to
Xsimultaneously enable or disable many entries in the menu
Xwhen the program's state changes or a new object is selected
Xby the user.   It is called with
X
X    set_menu(ad_menu, vector);
X
X where vector is a pointer to an array of WORDs.  The first
Xword of the array determines the default state of menu
Xentries.  If it is TRUE, then set_menu() enables all entries
Xin every drop-down of the menu tree, except that the DESK
Xdrop-down is unaffected.  If it is FALSE, then every menu
Xentry is disabled.
X
X    The following entries in the array are the numbers of
Xmenu entries which are to be toggled to the reverse of the
Xdefault state. This list is terminated by a zero entry.
X
X    The advantage of set_menu() is that it allows you to
Xbuild a collection of menu state arrays, and associate one
Xwith each type of user-selected object, program state, and so
Xon.  Changing the status of the menu tree may then be
Xaccomplished with a single call.
X
X    CHECK, PLEASE?  One type of state indicator which may
Xappear  within a drop-down is a checkmark next to an entry.
XYou can add the  checkmark with the call:
X
X    menu_icheck(ad_menu, ENTRY, TRUE);
X
X and remove it by replacing the TRUE with FALSE.  As above,
XENTRY is the name of the menu entry of interest.  The
Xcheckmark appears inside the left boundary of the entry
Xobject, so leave some space for it.
X
X    The menu_icheck() call is actually changing the state of
Xthe CHECKED flag within the entry object's OB_STATE word.  If
Xnecessary, you may alter the flag directly using do_obj() and
Xundo_obj() from the download.
X
X    NOW YOU SEE IT, NOW YOU DON'T.  You can also alter the
Xtext  which appears in a particular menu entry (assuming that
Xthe entry is  a G_STRING object).  The call
X
X    menu_text(ad_menu, ENTRY, ADDR(text));
X
X will substitute the null-terminated string pointed to by
Xtext for whatever is currently in ENTRY.  Remember to make
Xthe drop-down wide enough to handle the largest text string
Xwhich you may substitute.  In the interests of speed,
XG_STRINGs drawn within drop-downs are not clipped, so you may
Xget garbage characters on the desktop if you do not size the
Xdrop-down properly!
X
X    The menu_text() call actually alters the OB_SPEC field of
Xthe menu entry object to point to the string which you
Xspecify.  Since the menu tree is a static data structure
Xwhich may be directly accessed by the AES at any time, be
Xsure that the string is also statically allocated and that it
Xis not modified without first being delinked from the menu
Xtree. Failure to do this may result in random crashes when
Xthe user accesses the drop-down!
X
X    LUNCH AND DINNER MENUS.  Some applications may have such
Xa wide  range of operations that they need more than one menu
Xbar at different  times.  There is no problem with having
Xmore than one menu tree in a  resource, but the AES can only
Xkeep track of one at a time.  Therefore,  to switch menus you
Xneed to use menu_bar(ad_menu1, FALSE); to release  the first
Xmenu, then use menu_bar(ad_menu2, TRUE); to load the second
Xmenu tree.
X
X    Changing the entire menu is a drastic action.  Out of
Xconsideration for your user, it should be associated with
Xsome equally obvious change in the application which has just
Xbeen manually requested.  An example  might be changing from
Xspreadsheet to data graphing mode in a multi-function
Xprogram.
X
X    DO IT YOURSELF.  In a future column, I will discuss how
Xto set up user-defined drawing objects.  If you have already
Xdiscovered them  on your own, you can use them within a
Xdrop-down or as a title entry.
X
X    If the user-defined object is within a drop-down, its
Xassociated drawing code will be called once when the
Xdrop-down is first drawn. It will then be called in
X"state-change" mode when the entry is highlighted (inverted).
XThis allows you to use non-standard methods to show
Xselection, such as outlines.
X
X    If you try to insert a user-defined object within the
Xmenu title area, remember that the G_TITLE object which you
Xare replacing includes part of the dark margin of the bar.
XYou will need to experiment with your object drawing code to
Xreplicate this effect.
X
X    MAKE PRETTY.  There are a number of menu formatting
Xconventions which have become standard practice.  Using these
Xgives your application a recognizable "look-and-feel" and
Xhelps users learn it.  The following section reviews these
Xconventions, and supplies a few hints and tricks to obtain a
Xbetter appearance for you menus.
X
X    The second drop-down is customarily used as the FILE
Xmenu.  It  contains options related to loading and saving the
Xfiles used by the  application, as well as entries for
Xclearing the workspace and  terminating the program.
X
X    You should avoid crowding the menu bar.  Leave a couple
Xof spaces between each entry, and try not to use more than
X70% of the bar.  Not only does this look better, but you will
Xhave space for longer words if you translate your application
Xto a foreign language.
X
X    Similarly, avoid cluttering menu drop-downs.  Try to keep
Xthe number of options to no more than ten unless they are
Xclearly related, such as colors.  Separate off dissimilar
Xentries with the standard disabled dashes line.  (If you are
Xusing set_menu(), remember to consider the separators when
Xsetting up the state vectors.)
X
X    If the number of options grows beyond this bound, it may
Xbe time to move them to a dialog box.  If so, it is a
Xconvention to put three dots following each menu entry which
Xleads to a dialog. Also, allow a margin on the menu entries.
XTwo leading blanks and a minimum of one trailing blank is
Xstandard, and allows room for checkmarks if they are used.
X
X    Dangerous menu options should be far away from common
Xused entries, and are best separated with dashed lines.  Such
Xoptions should either lead to a confirming go/no-go alert, or
Xshould have associated "undo" options.
X
X    After you have finished defining a menu drop-down with
Xthe RCS,  be sure that its entries cover the entire box.
XThen use ctrl-click to  select the drop-down itself, and SORT
Xthe entries top to bottom.  This  way the drop-down draws in
Xsmoothly top to bottom.
X
X    Finally, it is possible to put entries other than
XG_STRINGs into drop-downs.  In the RCS, you will need to
Ximport them via the clipboard from the Dialog mode.
X
X    Some non-string object, such as icons and images, will
Xlook odd  when they are inverted under the mouse.  There is a
Xstandard trick for  dealing with this problem.  Insert the
Xicon or whatever in the drop-down first.  Then get a G_IBOX
Xobject and position and size it so that it covers the first
Xobject as well as the extra area you would like to be
Xinverted.
X
X    Edit the G_IBOX to remove its border, and assign the
Xentry name  to it.  Since the menu manager uses objc_find(),
Xit will detect and invert this second object when the mouse
Xmoves into the drop-down.  (To see why, refer to article #5.)
XFinally, DO NOT SORT a drop-down which has been set  up this
Xway!
X
X    THAT'S IT FOR NOW!  The next column will discuss some of
Xthe principles of designing GEM interfaces for applications.
XThis topic is irreverantly known as GEM mythology or
Xinterface religion.  The subject for the following column is
Xundecided.  I am considering mouse and keyboard messages, VDI
Xdrawing primitives, and the file selector as topics.  Let me
Xknow your preferences in the Feedback!
END_OF_gem7.asc
if test 16958 -ne `wc -c <gem7.asc`; then
    echo shar: \"gem7.asc\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 4\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0