TweetFollow Us on Twitter

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 concurrently checked, but the parent and members are never checked concurrently; 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. Entries beyond staticCount should be coded as 6 zero bytes. After the resource is added, the staticCount entry is copied from the staticCount to the dynamicCount-staticCount last entries (dynamicCount is determined by a CountMItem call). If the number of added items should exceed allocCount, the menu is trimmed to allocCount. A flag indicating whether the user should be warned of this trimming is set aside but the warning is not implemented in this version. Increasing the maximum toggle flag “addressing” capacity only adds 1 bit of overhead per item, so it is easy to allow for lots of fonts, or whatever other resources the application needs. Only the size of menus actually concerned with this resource will be increased beyond this 1 bit overhead.

The Expanded Check Range mechanism, though strictly necessary only for resource additions, can also be used to expand any Checked Range. The staticCount entry must have a Check Range opCode, and if the first argT is less than or equal to zero, the StaticCount entry will be copied into the dynamicCount-staticCount last entries, as explained above, and the “first” argT will be set to staticCount and the “last” argT will be set to dynamicCount. If however, the first argT is different from zero, the staticCount entry will still be copied, but from “first” to “last” only, without modification.

There are three ways to specify which entry in the Expanded Range should initially be checked and have its toggle flag set :

• argT[4] (“other”) is not normally used in Checked Range types; if the Expanded Range flag is set, however, and this argT is non-zero, it will be interpreted as the default item relative to staticCount, e.g., 3 would mean StaticCount + 2,

• if this argT is zero, then if the MST has an associated MIVT (Menu Item Value Table), and if the table contains an entry whose value is equal to the MIVTdefVal field, then the position of this value in the table gives the item to be checked,

• if these schemes fail, item number staticCount is checked and set.

Special Features

In order for the application to dynamically add items to an installed MST menu, extra space must follow the staticCount entry in the MVT, i.e, staticCount must be strictly less than allocCount, and as items are added/deleted, dynamicCount must never exceed allocCount. Notice that MSInsMenuItem does not exit via MSErrExit on error, it returns a negative error code instead. Items are added by passing a menuID, an item string pointer (which is passed directly directly to the ToolBox InsMenuItem routine, so it may include metacharacters), an after and a MenuVerb parameter. If the after parameter is non-null, it is taken as relative to StaticCount-1, e.g., both zero and 1 give staticCount and 5 gives staticCount + 4; out of range values are clipped to either staticCount or dynamicCount. Normal return values give the absolute itemNumber of the added item. Toggle flags are not set and the menu is neither checked nor enabled as a result of calling MSInsMenuItem, so the addition of singular elements should be followed by a call to MSDispatch, passing it the absolute itemNumber returned by MSInsMenuItem. TextToggled and TextToggled Sets MAY NOT BE ADDED IN THE PRESENT VERSION. Different groups should either be separated by a disabled line insertion, or exact argT parameters MUST be supplied, though interesting effects can be obtained by mixing different types, so caveat emptor.

The MIVT/PROC mechanisms are extremely useful. Though they can be used independently, they complement each other and the dynamic menu addition mechanism to provide a powerful way of writing resource-driven processing that is independent of the main application.

A menu flag governs the allocation of an MIVT (Menu Item Value Table). The table consists of allocCount+1 longs allocated by NewHandle and initially set to MIVTnullVal. A handle to the table is stored in the MST. Another flag determines whether this table will be left alone or whether a PROC resource, whose ID is stored in procID[0], will be called to set it up. In either case a later call to MSGetMIVT, passing menuID and itemNumber, retrieves the entry.

A second PROC may be associated with the menu by storing its ID in procID[1]; this second PROC will automatically be called every time MSDispatch is entered. If the appropriate menu flag is set, the short integer following the MVT contains the number of shorts needed to store any additional parameters the second PROC might need to carry out its task (such as the ID of a or the secondary menus the selected menu might interact with).

Using PROC parameters allows for reusable code. Appropriate functions can be written and stored, and used by different applications. On the other hand, you do not have to use PROC functions. In addition to the blessed menuID, MSMake takes two additional parameters which will override the PROC IDs: they should be function addresses set up to receive the standard PROC parameters. These function addresses will be stored in the procID table of MST, and appropriate menu flags will be set by MSMake to indicate that the “PROC” functions are application functions and not really PROC resources.

The test files included contain three sample PROCs. PROC ID 1 calculates the font IDs for installed fonts, and stores them in the Font menu MIVT. PROC ID 3 scans the text of the size menu and converts the first numeric sequence it finds to a long integer and stores it in the Size menu MIVT. PROC ID 2 is associated with the Font menu, and is called every time this menu is selected. It retrieves the font number for the selected item from the Font MIVT, gets the ID of the Size menu from the optional PROC parameters following the MVT, and then scans the Size menu entries, retrieving the font size from its MIVT. Any size entry that matches a RealFont gets its text outlined. The calling application is never aware of any of this visual processing, and simply retrieves font IDs and numeric sizes for logical actions associated with the menu choice without having to declare tables, make conversions, etc.

The PROC mechanism and the dynamic menu addition features provide a convenient solution to the problem of displaying open window titles in a menu, with the front window item typically checked. When the user chooses a window menu item, the corresponding window is brought to the front. This is a very common feature of most multiple window programs.

Initially no windows are open, and no window items appear in the “Window” menu. When a window is opened, MSInsMenuItem should be called, passing a menuID,a Checked Range type Menu Verb having zero first and last parameters (the staticCount item is assumed for convenience to be a disabled menu line), the Window Title string pointer, and either zero or a large number as the after parameter. Then the windowPtr should be stored in the MIVT, in the entry whose index is the itemNumber returned by MSInsMenuItem (the short version of MS presented here lacks the MSSetMIVT and MSFindMIVT functions, but they are easy to code, and are present in the longer version included in the source disquette). Next a call to MSDispatch will check this item.

When a window is closed, find its windowPtr in the MIVT, and the corresponding index is the itemNumber to delete by calling MSDelMenuItem.

The Toggle Flags provide the means by which menu states may be memorized throughout without ever changing the main MST disk resource. The MSAdjTog function takes a menuID and two ToggleFlag pointers as parameters. The first ToggleFlag pointer is a ToggleFlag, and the second is a ToggleFlag mask. For every bit for which the corresponding mask bit is not zero, if the corresponding installed Toggle bit is different from the passed Toggle bit, MSDispatch is called for this item, effectively setting the menu to the snapshot state implicitly contained in the passed ToggleFlag/Mask pair. Processes whose states are reflected in menus need only store the appropriate masked toggle flags before deactivation, and call MSAdjTog passing this stored information when reactivated.

A special resource, of type ‘MST1’, consists of just such a pair. These resources are optional, and their existence is determined by the state of one of the Menu Flags in MVT[0] (see above). When menus are originally set up by MSMake, if this flag is set , the ‘MST1’ resource will automatically be retrieved and MSAdjTog called. The application may update this resource and re-write it to disk by calling MSAdjPREV, which examines the mask contained in the appropriate ‘MST1’ resource and updates the ToggleFlag part of the disk resource according to the value of installed Toggle bits.

Extensions

An easy extension would allow the application to set up but never insert a special MST menu the user would never see. This invisible menu would serve to code multiple menu interactions provoked by non-menu events (program choices, state changes, etc.). Code for new types would need to be included in MSDispatch1 & 2. These new types would never actually be accessed by User menu choices : the applications simulates MSDispatch calls on the dummy menu. The dummy menu may contain opCodes for which the argTs would be interpreted differently . For example, to implement a mass disabling, suppose new type for which :

• [first] item number action applied to

• [last] item number action applied to

• [parent] is the singular element for the group

• [other] is menuID to other alternate set (forces menuIDs to 0-255, or need to change argT type to short)

• [ (currently unused) argT[4] ] is the number of entries following this one.

The MSDispatch code could be set for example to enable/disable (depending on the dummy menus own toggle flag transition), the specified range of the specified menu, for each item verb in the list. Therefore a parenthetical call to MSDispatch with the dummy i,j could trigger enabling/disabling over a multiple-menu set of items, independently of their primary MS coding.

A far less obvious extension would be to develop an MST like structure controlling DITLs and other controls. Hmm...

A better mousetrap ?

Obviously for small programs using few menus, MS is shooting mice with elephant guns. For large programs, relying extensively on menus, MS offers a significant savings in programming time, it provides a way of rapidly visualizing the functioning menu interface, and may be easily adapted to provide for non-menu generated events having multiple-menu visual display repercussions. But is MST fun ?

{1}
Listing:  MS.h

/*MS.h = metavariables and typedefs for MS routines*/
#include“stdio.h”
#include“MacDefs.h”
#include“Resource.h”
#include“Menu.h”
// MS.h = MenuStuff metavariables
/*_______________ Menu Verb Codes  ________________________*/
#define _MVNORMAL0 /* NORMAL ITEM  */
#define _MVCHKTOG1 /* CHECK TOGGLED*/
#define _MVTEXTOG2 /* TEXT TOGGLED */
#define _MVENABPR3 /* ENABLED PAIR */
#define _MVNZEXRG4 /* NON ZERO EXCL RANGE    */
#define _MVCMRGRS5 /* CUMULATIVE RNG WITH RESET*/ 
#define _MVENBSET6 /* ENABLED  SET */
#define _MVTTGSET7 /* TEXT TOGGLED SET */
#define _MVMENLIN-128/* DISABLED LINE*/

/*_______________Menu Flags ____________________________*/
#define _MFSYSLOD0 /* LOAD SYSTEM RESOURCE   */
#define _MFERRTRM1 /* ALERT USER TO TRIM     */
#define _MFEXPRNG2 /* EXPAND RANGE */
#define _MFALOCTB3 /* ALLOCATE MIVT TABLE    */
#define _MFBLDTAB4 /* BUILD MIVT TABLE */
#define _MFSETXRG5 /* SET EXP. RANGE TOGGLEFLG & MENU*/
#define _MFSETPRE6 /* SET TO PREVious STATE  */
#define _MFSUBMEN7 /* IS A SUB-MENU*/
#define _MFHASSTR8 /* HAS TXTTOG STR# RESOURCE     */
#define _MFPRCPAR9 /* PROC PARAMETERS FOLLOW MVT*/
#define _MFPRCFLG14/* USED INTERNALLY FOR PROC FUNCTS*/

/*_______________Resource Strings _______________________*/
#define _MSMSTTYP‘MST ‘ /* MENUSTUFF TYPE          */
#define _MSSTRTYP‘STR#’ /* TXTTOG  STR# RESOURCE   TYPE*/
#define _MSPRCTYP‘PROC’ /* MIVT FUNCTION TYPE      */
#define _MSPRVTYP‘PREV’ /* RES FOR RESET TO PREV STATE*/

/*__________________ TypeDefs __________________________*/
typedef charMVOpCode;
typedef charMVArgT;
#define _MVARGDIM5

typedef struct
{MVOpCode opCode;
 MVArgT argT[_MVARGDIM];
}MenuVerb;

typedef MenuVerb ToggleFlag;
#define _TFBITSZE(sizeof(ToggleFlag)*8-1)

typedef struct
{
 MenuHandle MH;
 long   sysRes;
 long   **MIVTH;
 long   procID[2];
 long   MIVTdefVal;
 long   MIVTnullVal;
 short  staticCount;
 short  allocCount;
 short  beforeID;
 short  ttgStrID;
 ToggleFlag TF;
 MenuVerb MVT[_TFBITSZE+1];
}MenuStuff;
#define _MSFIXLEN42
typedef MenuStuff*MSPtr;
typedef MSPtr    *MSHdl;

/*_______________ Non-int Externals______________________*/
extern  MSHdl    MSGetMSH();
{2}
Listing MS.C

/*MS.C = source code for MenuStuff Routines */
#include“MS.h”

int MSMake(menuID, MIVTFct1,MIVTFct2)
short   menuID;
  int   MIVTFct1;
  int   MIVTFct2;
{/*Main Set Up routine - must be called before using menu*/
 MSHdl  theMSH;
 MSPtr  theMSP;
 MenuVerb theMV, theMF;
 Handle theHdl;
 long   defVal;
 int    i,j;
 short  dynamCount,staticCount,allocCount;
 MVArgT defPos;
 MVOpCode opCode;
 short  progNum=01;

 HLock((theMSH=MSGetMSH(menuID,progNum)));
 theMSP=*theMSH;
 theMF=*theMSP->MVT;
 /* Get Menus */
 if (! (theMSP->MH=GetMenu(menuID)) )
 MSErrExit(progNum,02,menuID,0); 
 /* Load System resources */
 if (BitTst(&theMF, _MFSYSLOD))
 AddResMenu(theMSP->MH,theMSP->sysRes);
 /* Trim to allocCount */
 dynamCount=CountMItems(theMSP->MH);
 staticCount=theMSP->staticCount;
 allocCount=theMSP->allocCount;
 while (dynamCount > allocCount)
 { DelMenuItem(theMSP->MH,dynamCount);dynamCount--;}
 /* Minimal validation */
 if (staticCount > allocCount)
 MSErrExit(progNum,18,menuID,0);
 /* If TEXT TOG, check STR# & set Indx */
 if (BitTst(&theMF, _MFHASSTR))
 { if (! GetResource(_MSSTRTYP,theMSP->ttgStrID) )
 MSErrExit(progNum,07,menuID,0);
 /*  */
 j=0;
 for (i=1;i <= allocCount;i++)
 { opCode=theMSP->MVT[i].opCode;
 if (opCode == _MVTEXTOG || opCode == _MVTTGSET)
 { if (theMSP->MVT[i].argT[3] <= 0)
 {theMSP->MVT[i].argT[3]=j*2+1;j++;}
 /* Set TXTOG items to TF state  */
 theMV.opCode=theMSP->MVT[i].opCode;
 theMSP->MVT[i].opCode=_MVTEXTOG;
 MSTogBit(menuID,i);
 MSDispatch1(theMSP,menuID,i);
 theMSP->MVT[i].opCode=theMV.opCode;
 } }  } 
 /* Expand  Range for added resources */
 if (BitTst(&theMF, _MFEXPRNG))
 { theMV=theMSP->MVT[staticCount];
 if (theMV.opCode != _MVNZEXRG)
 MSErrExit(progNum,09,menuID,staticCount);
 if (theMV.argT[0] <= 0)
 { theMV.argT[0]=staticCount;
 theMV.argT[1]=dynamCount;
 if (theMV.argT[3])
 theMV.argT[3]+=staticCount-1;
 }
 for (i=theMV.argT[0];i <= theMV.argT[1];i++)
 theMSP->MVT[i]=theMV;    
 } 
 /* Allocate MIVT (Menu Item Values Table*/
 if (BitTst(&theMF, _MFALOCTB))
 if(!(theMSP->MIVTH=
 (long **)NewHandle((allocCount+1)*sizeof(long))))
 MSErrExit(progNum,03,menuID,0);
 else for (i=0;i <= allocCount;i++)
 *((*theMSP->MIVTH)+i)=theMSP->MIVTnullVal;
 /* MIVT Prep  */
 if (MIVTFct1)
 { theMSP->procID[0]=MIVTFct1;
 BitSet(theMSP->MVT,_MFPRCFLG);
 }
 if (MIVTFct2)
 { theMSP->procID[1]=MIVTFct2;
 BitSet(theMSP->MVT, _MFPRCFLG+1);
 }
 /* Build MIVT (Menu Item Values Table*/
 if (BitTst(&theMF, _MFBLDTAB))
 if (! theMSP->procID[0])
 MSErrExit(progNum,10,menuID,0);
 else MSCallProc (theMSP,menuID,0,0);
 /* Set Menu and TogFlgs for expanded range */
 if (BitTst(&theMF, _MFSETXRG))
 if (!(BitTst(&theMF, _MFEXPRNG)))
 MSErrExit(progNum,06,menuID,0);
 else 
 { defPos=theMV.argT[3];
 if (! defPos)
 { defVal=theMSP->MIVTdefVal;
 defPos=staticCount;
 if (defVal)
 for (i=defPos;i <= dynamCount;i++)
 if (defVal == MSGetMIVT(menuID,i))
 { defPos=i;break;}
 }
 CheckItem(theMSP->MH,defPos,1);
 MSTogBit(menuID,defPos);
 }
 /* Set Menu and TogFlgs to PREVious state */
 if (BitTst(&theMF, _MFSETPRE))
 if ( ! (theHdl=GetResource(_MSPRVTYP,menuID)) )
 MSErrExit(progNum,12,menuID,0);
 else
 { HLock(theHdl);
 MSAdjTog(menuID,*theHdl,*theHdl+sizeof(ToggleFlag));
 HUnlock(theHdl);
 }
 /* Execute PROC2 if attached */
 MSCallProc (theMSP,menuID,0,1);
 HUnlock(theMSH);
 return ((int)theMSP->MH);
} 

MSInsert(menuID, beforeID)
 short  menuID,beforeID;
{/* Call after MSMake, inserts MST menu in MenuBar*/
 MSHdl  theMSH;
 MenuHandle theMH;
 short  progNum=04;

 HLock((theMSH=MSGetMSH(menuID,progNum)));
 theMH=(*theMSH)->MH;
 if (! theMH)  MSErrExit(progNum,02,menuID,0);
 if(BitTst((*theMSH)->MVT,_MFSUBMEN))
 InsertMenu(theMH,-1);
 else if (beforeID != 0)  InsertMenu(theMH,beforeID);
 else   InsertMenu(theMH,(*theMSH)->beforeID);     
 HUnlock(theMSH);
}

MSDispatch(menuID, itemNumber)
 short  menuID;
 short  itemNumber;
{/*Call after MenuSelect, dispatches to MSDispatch1, which     is recursive, 
and therefore needs a locked MSPtr */
 MSHdl  theMSH;
 MSPtr  theMSP;
 short  progNum=02;
 int    MIVTFct1;
 Handle theHandle;

 if(!(theMSH=(MSHdl)GetResource(_MSMSTTYP,menuID)) )return;
 HLock(theMSH);
 theMSP=*theMSH;
 MSDispatch1(theMSP,menuID, itemNumber);
 MSCallProc (theMSP,menuID,itemNumber,1);
 HUnlock(theMSH);
}
intMSInsMenuItem(menuID,menuStr,after,theMV)
 short  menuID;
 char   *menuStr;
 short  after;
 MenuVerb *theMV;
{/* Inserts a dynamic, typed item beyond staticCount, if
 enough space has been allocated*/
 short  progNum=12;
 short  staticCount, dynamicCount,allocCount;
 MSHdl  theMSH;
 MSPtr  theMSP;
 long   menLineTxt=0x02282D00;/* (- pascal string  */

 HLock((theMSH=MSGetMSH(menuID,progNum)));
 theMSP=*theMSH;
 MSParseAfter(theMSP,&after,
 &staticCount,&dynamicCount,&allocCount);
 if (dynamicCount >= allocCount) return (-17);
 if (theMV->opCode == _MVTEXTOG 
 || theMV->opCode == _MVTEXTOG)
 return (-24); /* not implemented */
 MSShift(theMSP,after,1);
 theMSP->MVT[after+1] = *theMV;
 if (theMV->opCode == _MVMENLIN)
 menuStr=(char *)(&menLineTxt);
 InsMenuItem(theMSP->MH,menuStr,after);
 HUnlock(theMSH);
 return (after+1);
}  /* _____________________ End of MSDispatch2     _________*/

MSDelMenuItem(menuID,itemNumber)
 short  menuID;
 short  itemNumber;
{/* Deletes dynamic menu item beyond staticCount*/
 short  progNum=13;
 short  staticCount, dynamicCount,allocCount;
 MSHdl  theMSH;
 MSPtr  theMSP;

 HLock((theMSH=MSGetMSH(menuID,progNum)));
 theMSP=*theMSH;
 MSParseAfter(theMSP,&itemNumber,
 &staticCount,&dynamicCount,&allocCount);
 if (itemNumber == staticCount)
 if (staticCount == dynamicCount)  return (-17);
 else itemNumber++;
 MSShift(theMSP,itemNumber,-1);
 DelMenuItem(theMSP->MH,itemNumber);
 HUnlock(theMSH);
 return (0);
}

MSDispose(menuID)
 short  menuID;
{/* Disposes of menu and associated MST structures*/
 MSHdl  theMSH;
 short  progNum=05;

 theMSH=MSGetMSH(menuID,progNum);
 ReleaseResource((*theMSH)->MH);
 if (BitTst((*theMSH)->MVT, _MFALOCTB))
 DisposHandle((*theMSH)->MIVTH );  
 ReleaseResource(theMSH);
}

MSAdjTog(menuID,theTF,theMSK)
 short  menuID;
 ToggleFlag *theTF,*theMSK;
{/* Sets menus to Toggle Flag snapshot, masked*/
 MSHdl  theMSH;
 MSPtr  theMSP;
 int    i;
 short  allocCount;
 short  progNum=08;

 HLock((theMSH=MSGetMSH(menuID,progNum)));
 theMSP=*theMSH;
 allocCount=(*theMSH)->allocCount;
 for (i=1; i <= allocCount;i++)
 if (BitTst(theMSK,i))
 if (   BitTst(&(*theMSH)->TF,i)
 !=   BitTst(theTF,i))
 { MSDispatch1(theMSP,menuID,i);
 MSCallProc (theMSP,menuID,i,1);
 }
 HUnlock(theMSH);
}

MSAdjPREV(menuID)
 short  menuID;
{/* Sets stored MST1 resource to current toggle state*/
 MSHdl  theMSH;
 Handle thePREVH;
 Ptr    thePREVP;
 short  progNum=10;
 int    i;
 short  theErr,allocCount;

 theMSH=MSGetMSH(menuID,progNum);
 if ( ! (thePREVH=GetResource(_MSPRVTYP,menuID))  )
 MSErrExit(progNum,12,menuID,0);
 allocCount=(*theMSH)->allocCount;
 HLock(thePREVH);
 thePREVP=*thePREVH;
 for (i=1; i <= allocCount;i++)
 if (BitTst(thePREVP+sizeof(ToggleFlag),i))
 if (   BitTst(&(*theMSH)->TF,i)
 !=   BitTst(thePREVP,i)  )
 if (BitTst(&(*theMSH)->TF,i))BitSet(thePREVP,i);
 else   BitClr(thePREVP,i);
 ChangedResData(thePREVH);
 if (theErr=ResError())
 MSErrExit(progNum,13,menuID,theErr);
 WriteResource(thePREVH);
 if (theErr=ResError())
 MSErrExit(progNum,13,menuID,theErr);
 HUnlock(thePREVH);
 ReleaseResource(thePREVH);
}

MSErrExit(progNum,errNum, menuID, itemNumber) 
 short progNum,errNum, menuID, itemNumber;
{/* Puts up an Alert and exits to Finder*/
 Str255 txtStr[3];
 int    i;

 sprintf(txtStr[0],”%d”,progNum);
 sprintf(txtStr[1],”%d”,errNum);
 sprintf(txtStr[2],”%d”,menuID);
 sprintf(txtStr[3],”%d”,itemNumber);
 for (i=0;i < 4;i++) CtoPstr(&txtStr[i]);
 ParamText(txtStr[0],txtStr[1],txtStr[2],txtStr[3]);
 StopAlert(500,0);
 ExitToShell();
}

int MSTogBit(menuID,itemNumber)
 short  menuID;
 short  itemNumber;
 /* toggles and returns current value   */
{/* Toggles flag and returns value */
 MSHdl  theMSH;
 MSPtr  theMSP;
 short  progNum=09;

 theMSH=MSGetMSH(menuID,progNum);
 theMSP=*theMSH;
 if (itemNumber > theMSP->allocCount)
 MSErrExit(progNum,17,menuID,itemNumber);
 if (BitTst(&theMSP->TF,itemNumber))
 { BitClr(&theMSP->TF,itemNumber);return(0); }
 else { BitSet(&theMSP->TF,itemNumber);return(1);  }
}

MSHdl MSGetMSH(menuID,progNum)
 short  menuID;
 short  progNum;
{/* Gets MST resource handle, on error calls MSErrExit*/
 MSHdl  theMSH;
 if ( (theMSH=(MSHdl)GetResource(_MSMSTTYP,menuID)) )
 return (theMSH );
 else MSErrExit(progNum,01,menuID,0);
}

int MSGetTog(menuID,itemNumber)
 short  menuID;
 short  itemNumber;
{/* returns current value of toggleflag*/
 MSHdl  theMSH;
 short  progNum=06;

 theMSH=MSGetMSH(menuID,progNum);
 return(BitTst(&(*theMSH)->TF,itemNumber));
}

int MSGetMIVT(menuID,itemNumber)
 short  menuID, itemNumber;
{/* Retrieves the itemnumber entry from MIVTable*/
 MSHdl  theMSH;
 short  progNum=07;

 theMSH=MSGetMSH(menuID,progNum);
 if (itemNumber > (*theMSH)->allocCount)
 MSErrExit(progNum,17,menuID,itemNumber);
 return (*((*(*theMSH)->MIVTH)+itemNumber));
}


/*__________________ STATIC FCTS   ____________________*/
static MSDispatch1(theMSP,menuID, itemNumber)
 MSPtr  theMSP;
 short  menuID;
 short  itemNumber;
{/* core type-dispatching routine - recursive, therefore must not lock/unlock 
MST - calls MSDispatch2 for calculating argT default values*/
 MenuHandle theMH;
 MenuVerb theMV;
 MVOpCode opCode;
 MVArgT first, last, parent, other;
 short  progNum=03;
 Str255 theSTR;
 int    theTVal, flag,i;

 if (itemNumber > theMSP->allocCount) return;
 theMH=theMSP->MH;
 theMV=theMSP->MVT[itemNumber];
 opCode=theMV.opCode;
 if (opCode <= _MVNORMAL) return;
 if (itemNumber > theMSP->allocCount)
 MSErrExit(progNum,17,menuID,itemNumber);
 first=theMV.argT[0];
 last=theMV.argT[1];
 parent=theMV.argT[2];
 other=theMV.argT[3];
 theTVal=MSTogBit(menuID,itemNumber);
 MSDispatch2(theMSP,menuID,itemNumber,
 opCode,&first,&last,&parent,&other);
 switch (opCode)
 {
 case _MVCHKTOG :
 CheckItem(theMH,itemNumber,theTVal);
 break;
 case _MVTEXTOG :
 if (MSGetIndStr(&theSTR,menuID, other+theTVal) < 0)
 MSErrExit(progNum,08,menuID,itemNumber);
 else SetItem(theMH,itemNumber,&theSTR);
 break;
 case (_MVENABPR) :
 MSTogBit(menuID,other);
 MSEnable(theMH,itemNumber,theTVal);
 MSEnable(theMH,other,! theTVal);
 break;
 case (_MVNZEXRG) :
 MSClear(theMSP,first,last,opCode);
 CheckItem(theMH,itemNumber,1);
 MSTogBit(menuID, itemNumber);
 break;
 case (_MVCMRGRS) :
 switch (itemNumber == parent)
 { 
 case(1): /* Parent */
 if (! theTVal)
 MSTogBit(menuID,itemNumber);
 else
 { MSClear(theMSP,first,last,opCode);
 CheckItem(theMH,itemNumber,1);
 if((itemNumber >= first) 
 &&(itemNumber <= last))
 MSTogBit(menuID,itemNumber); 
 }
 break;
 case (0) : /* Member */
 CheckItem(theMH,itemNumber,theTVal);
 if (theTVal)
 flag=BitTst(&theMSP->TF,parent);
 else
 flag = ! MSBitOr(theMSP,first,last,opCode);
 if (flag)
 { CheckItem(theMH,parent,! theTVal);
 MSTogBit(menuID,parent);
 }
 break;
 }
 break;
 case (_MVENBSET) :
 if (itemNumber != parent)
 MSTogBit(menuID,itemNumber);
 else 
 { for (i=first;i <= last; i++)
 if(theMSP->MVT[i].opCode == opCode)
 {MSEnable(theMH,i,theTVal);MSTogBit(menuID,i);}
 if ((itemNumber >= first) && (itemNumber <= last))
 MSTogBit(menuID,itemNumber);
 else MSEnable(theMH,itemNumber, theTVal);                     
 itemNumber=other;
 if (MSGetTog(menuID,itemNumber) == theTVal)
 MSDispatch1(theMSP ,menuID,itemNumber)
 }
 break;
 case (_MVTTGSET) :
 if (itemNumber != parent)
 MSTogBit(menuID,itemNumber);
 else 
 { for (i=first;i <= last; i++)
 if (theMSP->MVT[i].opCode == opCode)
 if (MSGetIndStr(&theSTR,menuID,
 theMSP->MVT[i].argT[3]+theTVal) < 0)
 MSErrExit(progNum,08,menuID,i);
 else
 { SetItem(theMH,i,&theSTR);
 MSTogBit(menuID,i);
 }
 if ((itemNumber >= first) && (itemNumber <= last))
 MSTogBit(menuID,itemNumber);
 else 
 if (MSGetIndStr(&theSTR,menuID,other+theTVal)<0)
 MSErrExit(progNum,08,menuID,itemNumber);
 else SetItem(theMH,itemNumber,&theSTR);
 }
 break;
 }
}

static MSDispatch2(theMSP,menuID,itemNumber,
 opCode,first,last,parent,other)
 MSPtr  theMSP;
 short  menuID,itemNumber;
 MVOpCode opCode;
 MVArgT *first,*last,*parent,*other;
{/* calculates values for argTs when not specified,i.e. first&last zero, 
etc.*/
 int    i,j;
 MenuVerb fakeVerb;
 short  dynamCount;
 short  progNum=11;

 dynamCount=CountMItems(theMSP->MH);
 switch (opCode)
 { 
 case (_MVENABPR) :
 if (*other <= 0)
 *other=itemNumber+(*other?-1:1);
 break;
 case (_MVNZEXRG) :
 case (_MVCMRGRS) :
 case (_MVENBSET) :
 case (_MVTTGSET) :
 if (*first && *last) return;
 else if (*first || *last)
 MSErrExit(progNum,14,menuID,itemNumber);
 for (i=itemNumber;i > 0;i--)
 if (theMSP->MVT[i].opCode == _MVMENLIN) break;
 for (j=itemNumber;j <= dynamCount;j++)
 if (theMSP->MVT[j].opCode == _MVMENLIN) break;

 *first=++i;
 *last=--j;
 if (opCode == _MVNZEXRG) break;
 if (*parent <= 0)
 *parent=*first;
 else 
 { *parent+=*first-1;
 if (*parent < *first || *parent > *last)
 MSErrExit(progNum,16,menuID,itemNumber);
 }
 if (opCode != _MVENBSET) break;
 if (*other > 0) break;
 itemNumber=(*other)?*first-2:*last+2;
 if (itemNumber <= 0 || itemNumber > dynamCount)
 MSErrExit(progNum,15,menuID,itemNumber);
 for (i=0;i < _MVARGDIM;i++)
 fakeVerb.argT[i]=theMSP->MVT[itemNumber].argT[i];
 MSDispatch2(theMSP,menuID,itemNumber,_MVCMRGRS,
 &fakeVerb.argT[0],&fakeVerb.argT[1],
 &fakeVerb.argT[2],&fakeVerb.argT[3]);
 *other=fakeVerb.argT[2];
 break;
 }
}

static MSParseAfter(theMSP,after,
 staticCount,dynamicCount,allocCount)
 MSPtr  theMSP;
 short  *after,*staticCount,*dynamicCount,*allocCount;
{/*Translates itemNumber defaults for Del/InsMenItem */
 *dynamicCount=CountMItems(theMSP->MH);
 *staticCount=theMSP->staticCount;
 *allocCount=theMSP->allocCount;
 *after+=*staticCount;
 if (*after <= *staticCount)
 *after=*staticCount;
 else if (*after >= *dynamicCount)
 *after=*dynamicCount;
}

static MSShift(theMSP,itemNumber,direction)
 MSPtr  theMSP;
 short  itemNumber, direction;
{/* Del/InsMenItem utility = shifts MVT by +/- 1 */
 short  progNum=14;
 MenuVerb *MVTP;
 long     *MIVTP;
 ToggleFlag *TFP;
 int    i,k,first,last,dynamicCount;

 MVTP=theMSP->MVT;
 TFP=&theMSP->TF;
 dynamicCount=CountMItems(theMSP->MH);
 if (BitTst(theMSP->MVT, _MFALOCTB))
 MIVTP=(*theMSP->MIVTH);
 else MIVTP=0;
 if(direction>0) {first=dynamicCount+1;last=itemNumber+1;}
 else   { first=itemNumber;last=dynamicCount;}
 direction*=-1;
 for (i=first;i-last;i+=direction)
 { *(MVTP+i)=*(MVTP+i+direction);
 k=BitTst(TFP,i+direction);
 if (k) BitSet(TFP,i);
 else   BitClr(TFP,i);
 if (MIVTP) *(MIVTP+i)=*(MIVTP+i+direction);
 }
 for(i=0;i < _MVARGDIM;i++) *((char *)(MVTP+last)+i)=’\0';
 if (MIVTP) *(MIVTP+last)=theMSP->MIVTnullVal;
 BitClr(TFP,last);
}

static MSCallProc (theMSP,menuID,itemNumber,indx)
 MSPtr  theMSP;
 short  menuID,itemNumber,indx;
{/* Gets Handle to and calls MST->procID[indx] PROC parameter using standard 
interface (menuID,itemNumber, indx)  - for MSCallProc itself, indx is 
the procID entry number */
 int    MIVTFct;
 Handle theHdl;
 int    retVal;
 Str255 theStr;
 short  progNum=16;

 if (! theMSP->procID[indx]) return;
 MIVTFct=theMSP->procID[indx];
 if (! BitTst(theMSP->MVT, _MFPRCFLG+indx))
 if (! (theHdl=GetResource(_MSPRCTYP,MIVTFct)) )
 MSErrExit(progNum,04,menuID,indx+1);
 else {HLock(theHdl);MIVTFct=(int)(*theHdl);}
 retVal=(*((int (*)())(MIVTFct)))
 (theMSP ,menuID,itemNumber,indx);
 if (retVal < 0)
 MSErrExit(progNum,05,menuID,
 (-retVal*256)+indx);
 if (! BitTst(theMSP->MVT,_MFPRCFLG+indx))
 HUnlock(theHdl);
}

static MSEnable(theMH,itemNumber,flag)
 MenuHandle theMH;
 short  itemNumber,flag;
{/* Boolean parameter added to ToolBox routine*/
 if (flag)EnableItem(theMH,itemNumber);
 else   DisableItem(theMH,itemNumber);


}
static MSClear(theMSP,first, last,opCode)
 MenuStuff*theMSP;
 short  first,last;
 MVOpCode opCode;
 /*  Clears Toggle Flags and unchecks menu items for 
 MenuVerbs in range having same opCode */
{
 short  i,theMark;
 for(i=first;i <= last;i++)
 if(theMSP->MVT[i].opCode == opCode)
 { BitClr(&theMSP->TF,i);
 GetItemMark(theMSP->MH,i,&theMark);
 if (theMark != noMark)
 CheckItem(theMSP->MH,i,0);
 }
}
static MSPcat(s1,s2)
char *s1,*s2;
{int i; BlockMove(s2+1,s1+*s1+1,*s2); *s1+=*s2;}

static int MSBitOr(theMSP,first, last,opCode)
 MSPtr  theMSP;
 short  first,last;
 MVOpCode opCode;
 /*_______ returns (i..j) togbit k   ________*/
{
 short  i;
 for (i=first;i <= last;i++)
 if (theMSP->MVT[i].opCode == opCode)
 if (BitTst(&theMSP->TF,i))
 return(1);
 return(0);
}

static int MSGetIndStr(theStrP,menuID,strIndx)
 char   *theStrP;
 short  menuID,strIndx;
{/* Like NOT IN ROM GetIndStr, except returns length,
 or -1 if Indx out of bounds */
 MSHdl  theMSH;
 Handle theSTRH;
 int    i,maxIndx,len;
 char   *s;
 short  progNum=19;

 theMSH=MSGetMSH(menuID,progNum);
 menuID=(*theMSH)->ttgStrID;
 if( !(theSTRH=GetResource(_MSSTRTYP,menuID))) return (-1);
 maxIndx=*((short *)(*theSTRH));
 if ((strIndx > maxIndx) || (strIndx <= 0)) return (-1);
 s=*theSTRH + sizeof(short);
 for (i=1; i < strIndx; i++) s+=(unsigned char)(*s)+1;   
 len=(unsigned char)(*s)+1;
 *theStrP=0;
 BlockMove(s,theStrP,len);
 return(len);
}
{3}
Listing:  MST.C

/*  MST.C = minimal MenuStuff Test Program*/
#Options +J +K +Z
#include“MS.h”
#include “Events.h”
#include “Window.h”
#define _QuitMenNum2
#define _QuitItemNum 1
main()
{
 EventRecordtheEvent;
 char   c;
 WindowPtrtheWindow;
 short  windowCode;
 long   menuResult;
 int    i,j;
 Handle theHandle;
 Str255 theStr;

 InitMenus();
 InitCursor();
 i=CountResources(_MSMSTTYP);
 for (j=1;j <= i;j++)
 { theHandle=GetIndResource(_MSMSTTYP,j);
 GetResInfo(theHandle,&windowCode,&menuResult,&theStr);
 MSMake(windowCode, 0,0);
 MSInsert(windowCode, 0);
 }
 DrawMenuBar();
 FlushEvents(everyEvent);
 while (1)
 { if (GetNextEvent(everyEvent,&theEvent))
 switch (theEvent.what)
 { case (keyDown) :
 c=theEvent.message & 0x000000FFL;
 if (theEvent.modifiers & cmdKey)
 if (c == ‘q’)
 ExitToShell();
 break;
 case (mouseDown) :
 windowCode=FindWindow(&theEvent.where,&theWindow);
 if (windowCode == inMenuBar)
 { menuResult=MenuSelect(&theEvent.where);
 MSDispatch(HiWord(menuResult),
 LoWord(menuResult));
 if(HiWord(menuResult) == _QuitMenNum)
 if(LoWord(menuResult) == _QuitItemNum)
 ExitToShell();
 HiliteMenu(0);
 }
 break;
}} }
{4}
Listing:  MST.Link

;MSTst.link
;
/Start QuickStart
/Output MST
MST.Rel
MS.Rel
Standard Library.Rel
/Include MS.rsrc
/Include MSTtst1.rsrc
/Include MSPROC.rsrc
/End
{5}
Listing:  MSProc1.C

/*MSPROC1.c = PROC Resource 1,sets MIVT[i] to fontNum */
#Options +J +K +Z
#include “MS.h”
MSPROC1(theMSP,menuID,itemNumber,flg)
 MSPtr  theMSP;
 short  menuID,itemNumber,flg;
{
 short  dynamCount,staticCount,fontNum,i;
 Str255 fontName;
 staticCount=theMSP->staticCount;
 dynamCount=CountMItems(theMSP->MH);
 for (i=staticCount;i <= dynamCount;i++)
 { GetItem(theMSP->MH,i,&fontName);
 GetFNum(&fontName,&fontNum);
 *((long *)(*theMSP->MIVTH)+i)=fontNum;
 }
 return (0);
}
main()
{}
{6}
Listing:  MSProc1.Link

;MSPROC.link
/Output MSPROC1
MSPROC1.Rel
/End
{7}
Listing:  MSProc2.C
/* MSPROC2.c =  PROC Resource 2 : when Font menu selected, sets Font 
Size to outline if size is RealFont*/
#Options +J +K +Z
#include “MS.h”
#include “Font.h”
MSPROC2(theMSP,menuID,itemNumber,flg)
 MSPtr  theMSP;
 short  menuID;
 short  itemNumber,flg;
{
 int    i;
 short  dynamCount,staticCount,allocCount,
 fontNum,fontSize,byteOffSet,otherMenuID;
 MSHdl  otherMSH;
 long   **MIVTH;
 MenuHandle otherMH;
 Str255 fontName;

 if (! BitTst(theMSP->MVT, _MFPRCPAR)) return(-1);
 if (itemNumber < NULL)   return(-2);
 dynamCount=CountMItems(theMSP->MH);
 if(itemNumber == NULL) /* may be called at set up */
 { for (i=1;i <= dynamCount;i++)
 if (BitTst(&theMSP->TF,i))
 break;
 itemNumber=(i > dynamCount)?1:i;
 }
 MIVTH=theMSP->MIVTH;
 allocCount=theMSP->allocCount;
 byteOffSet=_MSFIXLEN+(allocCount+1)*sizeof(MenuVerb);
 if ( *(short *)((char *)theMSP+byteOffSet) < 1)
 return(-3); /* must find font menu ID somewhere! */
 fontNum=*(*MIVTH+itemNumber);
 otherMenuID=*((short *)((char *)theMSP+byteOffSet)+1);
 otherMSH=(MSHdl)GetResource(_MSMSTTYP,otherMenuID);
 if (otherMSH == 0) return (-4);
 if (!(otherMH=(*otherMSH)->MH)) return (-4);
 staticCount=(*otherMSH)->staticCount;
 MIVTH=(*otherMSH)->MIVTH;
 dynamCount=CountMItems((*otherMSH)->MH);

 for (i=staticCount;i <= dynamCount;i++)
 { fontSize=*(*MIVTH+i);
 if (RealFont(fontNum,fontSize))
 SetItemStyle(otherMH,i,outlineStyle);
 else
 SetItemStyle(otherMH,i,0);
 }
 return (0);
}
main()
{}

{8}
Listing:  MSProc2.Link

;MSPROC2.link - link files for other PROCS similar
/Start QuickStart
/Output MSPROC2
/Strip
MSPROC2.Rel
Standard Library.Rel
/End
{9}
Listing:  MSProc3.C

/*MSPROC3.c =  PROC Resource 3, converts Font size text to numeric in 
MIVT */
#Options +J +K +Z
#include “MS.h”

MSPROC3(theMSP,menuID,itemNumber,flg)
 MSPtr  theMSP;
 short  menuID;
 short  itemNumber,flg;
{
 int    i,j,k;
 short  dynamCount;
 char   s1[256],s2[256];

 dynamCount=CountMItems(theMSP->MH);

 for (i=1;i <= dynamCount;i++)
 { if (theMSP->MVT[i].opCode == _MVMENLIN)
 continue;
 GetItem(theMSP->MH,i,s1);
 PtoCstr(s1);
 j=0;
 while (! isdigit(*(s1+j)) && j < strlen(s1)) j++;
 if (j < strlen(s1))
 { k=0;
 while (isdigit(*(s1+j)) && j < strlen(s1))
 { *(s2+k)=*(s1+j);
 j++;k++;
 }
 *(s2+k)=’\0';
 k=atoi(s2);
 *((*theMSP->MIVTH)+i)=k;
 }
 else *((*theMSP->MIVTH)+i)=0;
 }
 return (0);
}
main()
{}
{10}
Listing:  MSProc3.Link

;MSPROC3.link
/Start QuickStart
/Output MSPROC3
MSPROC3.Rel
Standard Library.Rel
/End
{11}
Listing:  MSPROC.R

* MSPROC.R = creates PROC Resources

MSPROC.rsrc

TYPE PROC

 ,1
MSPROC1

 ,2
MSPROC2

 ,3
MSPROC3

{12}
Listing:  MS.R

*   Alert resources for MSErrExit
*

MS.rsrc

Type ALRT

     ,500
66 68 266 427
500
4444

Type DITL

     ,500
10
*   1
BtnItem Enabled
161 262 185 328
Finder

*   2
BtnItem Enabled
159 173 185 244
Restart 

*   3
StatText Enabled
11 98 28 304
Menu Stuff Problems !!

*   4
StatText Enabled
54 98 73 342
^0

*   5
StatText Enabled
131 98 152 333
^2

*   6
StatText Enabled
80 98 117 352
^1
{13}
Listing:  MSTtst1.R

* Sample Test resources
MSTtst1.rsrc

TYPE MENU

 ,1
\14

 ,2
File
Quit/Q

,3
Test
Font/\1B!\4
Size/\1B!\5
Style/\1B!\6
!\12CheckTog
TextTog 0
EnabPair 1
(EnabPair 2
(-
Enabled Parent 1
Enabled Member 1
(-
(Enabled Parent 2
(Enabled Member 2
(-
TextTog Master 
TextTog Member

 ,4
Font

 ,5
Size
9 
!\1212 
18 
20 
24 

 ,6
Style
!\12Plain
Bold<B
Italic<I
Underlined<U
Outlined<O
Shadowed<S


*File Menu
TYPE MST  = GNRL
,2
* Menu Handle place holder
.H
0000 0000
* Resource type for AddResMenu
.H
0000 0000
* MIVT handle place holder
.H
0000 0000
* procID table 
.H
0000 0000 0000 0000
*MIVT default and null values
.H
0000 0000 0000 0000
* static, allocCount, beforeID, STR# ID
.H
0001 0001 0000 0000
*Toggle Flags
.H
0000 0000 0000
* MVT - zeroeth entry contains Menu Flag
.H
0000 0000 0000
*StaticCount Menu Verbs follow :
.H
0000 0000 0000


*Desk Menu
TYPE MST  = GNRL
,1
.H
0000 0000 4452 5652
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 000F
0002 0000 0000 0000
0000 8000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
.H
0000



*Test Menu 3
TYPE MST  = GNRL
,3
.H
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0010 0010
0000 0003 0A60 0000
0000 0080 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0100 0000 0000
0200 0000 0000 0300
0000 0000 0300 0000
FF00 8000 0000 0000
0600 0000 0000 0600
0000 0000 8000 0000
0000 0600 0000 FF00
0600 0000 FF00 8000
0000 0000 0700 0000
0000 0700 0000 0000


*Size Menu
TYPE MST  = GNRL
,5
.H
0000 0000 0000 0000
0000 0000 0000 0003
0000 0000 0000 0000
FFFF FFFF 0001 0005
0000 0000 0000 0000
0000 3D00 0000 0000
0400 0000 0200 0000
0000 0000 0000 0000
0000 0000 0000 0000
.H
0000 0000 0000

*Font Menu
TYPE MST  = GNRL
,4
.H
0000 0000 464F 4E54
0000 0000 0000 0001
0000 0002 0000 0000
FFFF FFFF 0001 0006
0000 0000 0000 0000
0000 FD40 0000 0000
0400 0000 0100 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
.H
 0000 0000
*****   PROC PARAM
.H
0001 0005



*STYLE Menu
TYPE MST  = GNRL
,6
.H
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0006 0006
0000 0000 4000 0000
0000 0100 0000 0000
0500 0000 0000 0500
0000 0000 0500 0000
0000 0500 0000 0000
0500 0000 0000 0500
.H
0000 0000

TYPE STR#

* Text Toggled strings
     ,3 (32)
6
TextTog 1 - 0
TextTog 1 - 1
TextTog Master - 0
TextTog Master - 1
TextTog Member - 0
TextTog Member - 1
 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
Carbon Copy Cloner 4.0.3 - Easy-to-use b...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
ForeverSave 2.1.3 - Universal auto-save...
ForeverSave auto-saves all documents you're working on while simultaneously doing backup versioning in the background. Lost data can be quickly restored at any time. Losing data, caused by... Read more

Latest Forum Discussions

See All

Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »
You Can Play the Final Chapter of Lone W...
You Can Play the Final Chapter of Lone Wolf: Dawn Over V’taag Right Now Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Swords of Anima (Games)
Swords of Anima 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A new tactical turn-based RPG experience. Command the Savior Rex Squad in an epic journey of courage and deception. Can you... | Read more »
Audio Defence: Zombie Arena
Audio Defence: Zombie Arena By Lee Hamlet on November 26th, 2014 Our Rating: :: DRAGS ITS FEETUniversal App - Designed for iPhone and iPad From the makers of Papa Sangre comes a defense game that forces players to listen carefully... | Read more »
Tales from the Borderland​s Will be Comi...
Tales from the Borderland​s Will be Coming to iOS by the End of the Year Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Telltale Games has announced | Read more »
Sunburn! Review
Sunburn! Review By Campbell Bird on November 26th, 2014 Our Rating: :: DON'T DIE ALONEUniversal App - Designed for iPhone and iPad Platform through the depths of space to make sure your entire crew dies together in this satisfying... | Read more »

Price Scanner via MacPrices.net

2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Early Black Friday sale prices on iPad Air 2,...
 MacMall is discounting iPad Air 2s by up to $75 off MSRP as part of their Black Friday sale. Shipping is free: - 16GB iPad Air WiFi: $459 $40 off - 64GB iPad Air WiFi: $559 $40 off - 128GB iPad Air... Read more
Early Black Friday MacBook Air sale prices, $...
 MacMall has posted early Black Friday MacBook Air sale prices. Save $101 on all models for a limited time: - 11″ 1.4GHz/128GB MacBook Air: $798 - 11″ 1.4GHz/256GB MacBook Air: $998 - 13″ 1.4GHz/... Read more
Why iPhone 6 Tablet/Laptop Cannibalization Is...
247wallst.com blogger Douglas A. McIntyre noted last week that according to research posted on the Applovin blog site the iPhone 6 is outselling the iPhone 6 Plus by a wide margin . Hardly a surprise... Read more
Worldwide Tablet Growth Expected to Slow to 7...
The global tablet market is expected to record massive deceleration in 2014 with year-over-year growth slowing to 7.2%, down from 52.5% in 2013, according to a new forecast from International Data... Read more
Touchscreen Glove Company Announces New Produ...
Surrey, United Kingdom based TouchAbility specializes in design and manufacture of a wide variety of products compatible with touchscreen devices including smartphones, tablets and computers. Their... Read more
OtterBox Alpha Glass Screen Protectors for iP...
To complement the bigger, sharper displays on the latest Apple devices, OtterBox has introduced Alpha Glass screen protectors to the iPhone 6 and iPhone 6 Plus. The fortified glass screen protectors... Read more
Early Black Friday Mac Pro sale, 6-Core 3.5GH...
 B&H Photo has the 6-Core 3.5GHz Mac Pro on sale today for $3499 including free shipping plus NY sales tax. Their price is $500 off MSRP, and it’s the lowest price available for this model from... Read more
Early Black Friday sale price: 15-inch 2.2GHz...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale today for $1699.99. Shipping is free, and B&H charges NY sales tax only. Their price is $300 off MSRP, equalling Best Buy’s price... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.