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