GenMenuX: How and Why It Works

Andrew Ross MacNeill
AKSEL Solutions


Introduction

Why change FoxPro's menu system? One of the great things about FoxPro is its power tools; the menu builder allows developers and even casual users to design menus that all have a similar look and feel. In designing a FoxPro menu, we create the top row (known as the menu bar) and add pull-down menus, making an application feel much like a Windows application.

A FoxPro menu item can be one of four options: a submenu (which calls another menu), a procedure (in which program code can be written), a command (in which the menu can directly execute a FoxPro command) and a bar (one of FoxPro's built-in menu items). In building a menu, we identify our menu pad names and add functionality by choosing one of these four types. As we build our menu, FoxPro stores the information in a database file with the MNX extension. This database file follows a specific structure and order. If even one record gets deleted, it can mean the entire menu file is corrupted!

When it comes time to build our menu into an application, FoxPro uses GENMENU.PRG which reads the MNX file and generates the Menu Program File (MPR) using the native Text Merge capabilities of FoxPro. The GENMENU Program handles all of the coding details, ensuring that popups are uniquely named and the generated MPR file doesn't contain any syntax errors that may occur when defining a menu. The Menu Builder also offers several options but keeps developers limited to a standard GUI-style menu. Within the FoxPro menus, we can choose to SKIP a menu item based on a logical condition (using the SKIP FOR clause) and we can add a hot key to a menu option (using the Key Label clause). We can identify a MARK character for marking menu options. We can do all this without writing having to write specific code to create the menu.

Unfortunately, the FoxPro Menu builder does not take full advantage of the FoxPro language. We are unable, for example, to place the menu at a line lower than row 0 even though there is an AT LINE clause in the DEFINE MENU command. And although we are able to define a MESSAGE in Windows, the DOS menu builder does not provide this functionality.

As a developer, I have noticed many applications that do not use the FoxPro Menu Builder for precisely those reasons. Instead, the developer has to open up the Language Reference Guide and type in necessary commands to place the menu on line 5, to create a popup of database fields, or to place the menu within a window. The result, is that the menu builder as a Power Tool is virtually ignored because it cannot handle the requirements of many developers.


Possible Solutions

To solve these problems, we have a variety of solutions: we can roll up our sleeves and dissect GENMENU.PRG, which Microsoft provides to us or even modify the generated MPR file, but these two solutions leave us with a heavy maintenance headache. Once a new patch for FoxPro becomes available, it is our responsibility to update the new GENMENU with our changes. If we have modified the MPR file, then we have to rely on our knowledge of the FoxPro language to make future changes which defeats the purpose of using the FoxPro Menu Builder in the first place.

If you have ever felt cheated by the FoxPro Menu Builder and the limits it places on you, GENMENUX offers a perfect solution.

Just as GENSCRNX extends the FoxPro Screen Builder in ways that the original developers never imagined, GENMENUX attempts to extend the Menu Builder. The idea behind the "X" series of generators is that there are times when FoxPro does not provide everything required in the Power Tools. The "X" series provides the additional functionality that can add incredible power to these tools.

It enhances the FoxPro Menu Builder with the following features :

ability to control default menu positioning, colours and actions without manually changing the MPR file.
ability to remove menu pads based on logical conditions instead of using SKIP FOR.
ability to add Messages to DOS Menus
ability to automatically add hot keys to menu pads
ability to call menu "drivers" at various points in the Menu Generation process.
ability to define Menu Templates that contain standard menu objects that can be inserted at any time into an existing menu.


Installing GENMENUX

GENMENUX can be easily added to your development environment by making a small change in the CONFIG.FP file (CONFIG.FPW for Windows) that tells FoxPro to use GENMENUX as your Menu Generation program. This is done by adding the line :

_GENMENU="GENMENUX.PRG"

to the file. If you don't always work directly from your FoxPro directory, specify the full path so FoxPro knows where to find GENMENUX. Ensure that GENMENUX has been moved into the FoxPro directory. The next time you start FoxPro, GENMENUX will be the Menu Generator program! Now isn't that easier than learning all the possible combinations of the DEFINE PAD command.

Technically, GENMENUX and GENSCRNX operate in a similar fashion. They take copy of a screen or menu and make modifications to the copy prior to building. After changes have been made to the copy, the original generator (GENMENU or GENSCRN) is called on the copy. Additional changes can then be made to the generated SPR or MPR file. After all changes have been made, the generated program file is renamed to what the original program file was to be called. This way, the original screen remains the same and still creates the same program file but the contents of the program file may be completely different. As with GENSCRNX, GENMENUX is a Public Domain tool, with no charges against using it.

In order to use GENMENUX, you must specify it as a replacement for GENMENU with the following line :

_GENMENU="GENMENUX.PRG"

If you have added FoxPro into your DOS path, you should put the entire path into the GENMENUX line.

How does GENMENUX extend the screen builder?

GENMENUX offers built-in directives (or statements) that the developer can place in a menu comment snippet. When the menu generation process takes place, GENMENUX reads this directive and makes specific changes to the menu file. These changes result in a menu program file that offers the same functionality that a developer can hard-code into a program but through the use of the FoxPro Power tools.


Calling GENMENUX directives

GENMENUX directives may be called in four different locations :

CONFIG.FP/CONFIG.FPW

The CONFIG.FP is the startup configuration file that FoxPro uses. Any directive that GENMENUX uses begins with an underscore character (_).

Setup/Menu Procedure Snippet

Any Setup directives may be called from either the Setup snippet or the Menu Procedure snippet. You can access the Menu Procedure snippet easier than the Setup snippet as it is immediately visible when choosing Menu options from the Menu pad.

Comment Snippet

The comment snippet is available by selecting Options for any particular menu.

Directives that appear in either the CONFIG.FP or Setup/Procedure snippets are usually global directives, that is, they affect an entire menu by changing default colors, positioning or shadows, etc. Comment directives affect only the menu option that the comment is specified in.

Notes for FoxPro Windows

FoxPro for Windows handles Menus slightly differently than DOS in order to conform to the Windows standard. One of the differences is that the main system menu _MSYSMENU is not allowed to be moved from the top of the desktop. To overcome this, add the directive *:MENUNAME to your menu and rename it to something other than _MSYSMENU. This will result in a Menu that will use the default FoxPro for Windows font which is FoxFont Size 10. If you want to have a more "Windows" like menu, DEFINE your window using the FONT clause of "MS Sans Serif",10.


GENMENUX Drivers

GENMENUX also supports menu drivers. A menu driver is a program or procedure that is run through the temporary menu or generated MPR file making changes as directed by the developer. To identify a driver, place the required driver directive in the Setup/Menu Procedure snippet of the menu and GENMENUX will call the procedure after it has completed its changed.

The driver specified by any of the DRV directives does not have to be highlighted in Quotes. In fact, GENMENUX will strip the quotes if there are any. In addition, if you do not put an extension on the driver, GENMENUX will look for a driver with a PRG extension to run.

*:MNXDRV1 - MNXDRV4

Calls different Drivers at various times before GENMENUX actually generates the MPR file.

*:MNXDRV5 <expr1> / _MNDRV5

Calls a GENMENUX Driver that completely REPLACES the standard FoxPro GENMENU. This driver must be a complete driver in the sense that when it starts, the menu MNX file is open and at the first record. Whatever gets created here should be placed in the MPR file.

The MNXDRV5 driver is passed the same two parameters as the regular GENMENU.

*:MPRDRV1 - MPRDRV2

Calls Drivers that will update the SPR file within the temporary project file.

*:MNXDRV0 <expr1>

Attaches additional code to the GENMENU program to provide advanced code within the GENMENU process. The original GENMENU is saved.


GENMENUX Template or Library Directives

Since Version 1.1, GENMENUX allows a menu template or library to be created, similar to GENSCRNX. Templates allow developers to create default menu objects only once and reference them in new menus without having to rewrite any code. For example, if every custom application you build uses a Special menu pad that contains items such as Help, About, System Information, then when you design a menu, you have to add these items in every time. By defining the Special menu pad in a Menu Library, you can call this menu pad and it will be added to any menu automatically. A library is contained in a template file.

This file is referenced by FOXMNX. Libraries have objects within them. The Special menu, mentioned above, may be in a library called STANDARD and referenced as an object called SPECIAL. This library may be created and referenced using the commands below.

*:FOXMNX <cFileName> / _FOXMNX

In order for a menu library to exist, the library file must be created using the FOXMNX directive. This directive may define a library table in every application directory or it may reference a single one for different applications. The table created will have the identical structure to an MNX file with some additional fields that identify the library and objects within.

*:DEFLIB <cLibrary>

Since every object must belong to a library, DEFLIB allows you to define the library for an entire menu. This is useful if you create a single menu that contains all of your library objects and want to place each object into a single library.

*:DEFOBJ [<cLibrary>.]<cObject>

To define a library object, place the DEFOBJ directive in the comment field for the menu pad, submenu or procedure to be defined. The object will be inserted into the FOXMNX table as belonging to the library specified by cLibrary. If cLibrary is not specified, then the library will be the one identified by the DEFLIB directive.


Example of How to Define Library Objects

In the CONFIG.FP, specify the template file with the command :

_FOXMNX='FOXMNX.DBF'

This identifies the FOXMNX table as the container for any libraries.

In addition, specify the default library for any menu objects to belong to with the line :

_DEFLIB="TOOLS"

This identifies any objects in the menu as belonging to the Tools library.

On any menu pad, submenu, command or procedure that should be defined in the Library, add the DEFOBJ directive in the Comment snippet. If the About... menu pad contains a procedure that is the same between different applications, add the following line to the Comment snippet :

*:DEFOBJ About

This inserts a menu object into the FOXMNX table for the About menu pad, identifying it as belonging to the Tools library and naming the object About.

If the File Reindex menu pad contains a procedure that is the same between different applications but you want to place it in the File library, add the following line to the Comment snippet :

*:DEFOBJ FILE.REINDEX

This inserts a menu object into the FOXMNX table for the Reindex menu pad, identifying it as belonging to the File library and naming the object Reindex.


Referencing Library Objects

*:INCLIB

To define the default library for referencing library objects, place the INCLIB directive in the Setup or Menu Procedure snippet of the menu.

*:INSOBJ <cLibrary>.<cObject>

To reference a library object and have it replace a menu option in a menu, place the INSOBJ directive in the comment field for the menu pad, submenu or procedure to be replaced. The object will be replaced by the object <cObject> from the FOXMNX table belonging to the library specified by cLibrary. If cLibrary is not specified, then the library will be the one identified by the INCLIB directive.


GENMENUX Directives

CONFIG.FP Directives - all CONFIG.FP directives are preceded with a _

 

 

AUTOACT

Automatically Activates the menu after creating it.

AUTOHOT

Automatically adds Hot keys to top menu bar without labels. (ALT+ only)

AUTOPOS

Allows the developer to place the menu on a line when it is being compiled.

AUTORUN

Automatically runs the menu upon completion.

FOUNDATION

Automatically creates a FOUNDATION READ at the bottom of the menu file.

FOXMNX

Defines the Menu Template File.

GENMENUX

Runs driver specified instead of standard GENMENU.

HIDE

Hides the Menu while the MPR file is being run so menu pads won't distract the user.

MNXDRV1-5

Identifies Driver x for the MNX file

MPRDRV1-2

Identifies MPR drive for the MPR object file

NOXTHERM

Removes the GENMENUX Extended Thermometer in favour of the traditional FoxPro.

REFPRG

Identifies Refresh Program that GENMENUX will create to help in the refreshing of conditional CASE and ARRAY menus.

SYSPOP

Wraps all procedures with PUSH MENU _MSYSMENU ,POP MENU _MSYSMENU statements to preserve menu settings before a procedure is called.

 

 

 

Setup/Menu Procedure Directives - all setup directives are preceded with a *:

{{}}

Identical functionality to {{}} in GENSCRNX.

AUTOACT

Automatically Activates the menu after creating it.

AUTOHOT

Automatically adds Hot keys to top menu bar without labels. (ALT+ only)

AUTOPOS

Allows the developer to place the menu on a line when it is being compiled.

AUTORUN

Automatically runs the menu upon completion.

BARHOT

Automatically adds CTRL hot keys for a menu item or submenu.

DEFLIB

Defines the Default Library to be used when defining menu template objects.

FOUNDATION

Automatically creates a FOUNDATION READ at the bottom of the menu file.

FOXMNX

Defines the Menu Template File.

HIDE

Hides the Menu while the MPR file is being run so menu pads won't distract the user.

INCLIB

Defines the Default Library to be used when retrieving menu template objects.

INSSCX

Inserts a DO .MPR call within a screen file. Also allows you to call the menu from within a MODAL screen and preserve the original menu settings.

LINE

Places the menu at line specified. This should be a numeric value. Be warned under Windows!

LOCATION

Allows you to set the location of the menu in relation to the current menu (REPLACE, APPEND, BEFORE or AFTER) instead of having to identify it under Menu Options.

MENUCOLOR

Adds the COLOR clause setting to the DEFINE MENU statement if one is present in the MPR file.

MENUNAME

Renames the default _MSYSMENU menu name in the MPR file to whatever is specified. This does not have to be in quotes. If it is, the quotes are stripped from the word.

MNXDRV0-5

Identifies Driver x for the MNX file

MPRDRV1-2

Identifies MPR drive for the MPR object file

NOACT

Removes the statement ACTIVATE MENU _MSYSMENU from the MPR file.

NOAUTO

Removes the statement SET SYSMENU AUTOMATIC from the MPR file.

NOBAR

Removes the BAR statement. Usually only required if renaming menu. Be warned under Windows!

NOGEN

Does not generate the MPR file

NOMARGIN

Removes the MARGIN clause from the MPR file.

NOSHADOW

Removes the SHADOW clause from the MPR file.

NOXGEN

Does not process any GENMENUX directives.

NOXTHERM

Removes the GENMENUX Extended Thermometer in favour of the traditional FoxPro.

PADCOLOR

Changes the Default Menu Pad color from color Scheme 4 to whatever number is specified. No quotes please!

POPCOLOR

Changes the Default Menu Popup color from color Scheme 4 to whatever number is specified. No quotes please!

REFPRG

Identifies Refresh Program that GENMENUX will create to help in the refreshing of conditional CASE and ARRAY menus.

SELECTBAR

Changes the ON BAR statements to ON SELECTION BAR.

SELECTPAD

Changes the ON PAD statements to ON SELECTION PAD.

SYSDEFAULT

Makes the Menu become the FoxPro default by adding the line SET SYSMENU NOSAVE to the bottom of the MPR file.

SYSPOP

Wraps all procedures with PUSH MENU _MSYSMENU ,POP MENU _MSYSMENU statements to preserve menu settings before a procedure is called.

VERTICAL

Makes a menu orientation vertical instead of horizontal starting at a particular location.

WINDOW

Allows you to place a menu within a window and define the window within the MPR with specific window definition clauses.

 

Comment Directives - all Comment directives are preceded with a *:

 

 

{{}}

Identical functionality to {{}} in GENSCRNX.

ARRAY

Makes a menu popup definable by an array instead of within the MPR file. Good for dynamic menus.

BARHOT

Automatically adds CTRL hot keys for a menu item or submenu.

CASE

Adds or removes a menu bar when a general condition is not met.

COLOR

Allows you to designate a Color Pair setting for a particular menu item. Under Windows and Macintosh, allows you to identify color by name.

COLORSET

Allows a specific color scheme setting for a particular menu item.

DEFOBJ

Defines the current menu pad/procedure as a menu object in the menu template file.

DEFPOPIF

Makes the definition of a menu popup conditional if the menu popup already exists. This has no effect on the options of the popup but will speed up the running of the MPR file.

DELETE

Removes menu pad prior to calling GENMENU

DELOBJ

Removes menu pad prior to calling GENMENU but AFTER initial GENMENUX directives has processed.

GENIF

Removes a menu pad or bar from the MNX file during compilation if condition is not met.

IF

RELEASES a menu PAD or BAR if condition is not met

IGNORE

Tells GENMENUX to ignore the menu or the pad for GENMENUX processing.

INSOBJ

Inserts a menu object from a menu template file.

MESSAGE

Adds a message clause to the menu definition. This replaces the FPW's message clause. The message must be enclosed in quotes unless it is a variable.

PADNAME

Identifies a particular name to a menu pad without having to go into Menu Options.

PADPOS

Defines the menu pad at a particular row and column.

POPCOMMAND

Adds the line ON SELECTION POPUP for each popup using POPFILES or POPFIELD, the expression must be a valid FoxPro command. Quotes ARE NOT STRIPPED from the expression.

POPFIELD

Allows for a popup of fields matching the file spec matching the expression. Quotes are not needed and if used, will return the text instead of the field.

POPFILES

Allows for a popup of files matching the file spec matching the expression. Quotes are ignored in the expression as per standard FoxPro behaviour

POPNAME

Identifies a particular name to a menu popup without having to go into Menu Options.

POPPOS

Defines the menu popup at a particular row and column.

POPPRECOMMAND

Identifies a particular command to be run before a popup is defined. For use with the POPFIELD and POPFILES directives only.

POPTITLE

Identifies a particular title for a menu popup.

SYSPOP

Wraps all procedures with PUSH MENU _MSYSMENU ,POP MENU _MSYSMENU statements to preserve menu settings before a procedure is called.

TRNTXT

Translates Text within a procedure file.

 

 



Session 121
Session 224
GENMENUX Drivers

Andrew Ross MacNeill
PC Edge Inc.


GENMENUX Drivers

Both GENMENUX and GENSCRNX offer developers the ability to extend the power of the screen builder in their own ways. This is done through the use of drivers, FoxPro programs that are called at different times during the generation process. However, the implementation between the two products differs slightly. GENSCRNX uses two kinds of drivers : table drivers, which must go through every record in the screen file on its own and object drivers, which make changes to the final generated code. In GENMENUX, there are three kinds of drivers : table drivers, object drivers and line drivers.

To understand how to write a driver, it is important to first understand how GENMENUX calls drivers. GENMENUX calls drivers in six different locations. The following table shows where these drivers are called in version 2.0:


Definition of Drivers

A line driver is a program that is called for each record in a menu file. GENMENUX opens the menu file and uses a SCAN...ENDSCAN statement to process each record. For each record, GENMENUX calls the driver. GENMENUX uses line drivers for MNXDRV2 and MNXDRV3.

A table driver is a program that is called at the top of a menu file. It is the responsibility of the program to process the entire menu file. When the driver returns control of GENMENUX, GENMENUX goes on to the next process. GENMENUX uses table drivers for MNXDRV1 and MNXDRV4.

An object driver is a program that is called when the currently selected table contains a memo field containing the menu MPR file. It is the responsibility of the driver to make changes to the driver. GENMENUX uses object drivers for MPRDRV1 and MPRDRV2.


Driver Potential

Since drivers are called at different times, there is different potential for each kind of driver. If you want to update particular menu options with GENMENUX directives such as *:DELETE, *:MESSAGE and *:GENIF, you can fill these values using a MNXDRV1 driver. The MNXDRV1 driver is also useful if you want to add or remove specific menu options that use normal GENMENUX directives. The following is an example of an MNXDRV1 driver :

This driver automatically adds messages to each menu option if one doesn't already exist.

  SCAN
    IF EMPTY(message)
      REPLACE comment WITH [*:MESSAGE "Message for :"]+ALLTRIM(prompt)+["]
    ENDIF
  ENDSCAN

The above driver is a very basic driver but it shows that drivers do not have to be complex in order to be useful. It demonstrates how a table driver should go through the entire MNX table.

If you want to use the *:IF directive to create conditional menu options, a driver can update the menu comment snippet as either at the MNXDRV1 or MNXDRV2 levels.

The following is an example of an MNXDRV4 driver that goes through the entire MNX file and adds each prompt to a table. Such a table could be used as a security measure or for documentation to show every menu prompt available to a user.

  IF NOT FILE("PROMPTS.DBF")
         PRIVATE jnArea
         jnArea=SELECT()
         SELECT 0
         CREATE TABLE PROMPTS (CPROMPT C(50))
         SELECT (jnArea)
  ENDIF
  SCAN
         IF NOT EMPTY(prompt)
                 INSERT INTO Prompts FIELDS cPrompt VALUES ALLTRIM(prompt)
         ENDIF
  ENDSCAN

Because GENMENUX calls each driver, it is possible to use functions that are available from within GENMENUX in the driver. For example, if you need to know what a particular setting is in the user's CONFIG.FP file, you can use the native GENMENUX CONFIGFP function instead of writing your own. Listed below are the native functions within GENMENUX that may be of use when writing drivers :

CONFIGFP CONFIGFP is a function that returns a setting in the user's CONFIG.FP or CONFIG.FPW file. When either GENMENUX or GENSCRNX runs, it creates a cursor that holds the CONFIG.FP file in a memo field. The function then reads the memo field, looking for the particular setting requested.

WORDSEARCH WORDSEARCH returns the remainder of the line based on the parameters passed.

STRTRANC This function performs the same string translation as FoxPro's native STRTRAN with one exception, it's case-insensitive. STRTRAN requires that the string found is in the exact case as the string being searched ; STRTRANC doesn't.

NOHOT This function returns a string with all of the characters that can be used to identify hotkeys removed.

SAYTHERM This function updates the second message on the thermometer.

UPDTHERM This function updates the thermometer to the specified percentage point.

ADDCLEANUP This function adds the passed string to the menu file's cleanup snippet.

ADDSETUP This function adds the passed string to the menu file's setup snippet.

 


Using the MNX File

The Menu file created by the FoxPro Menu Power Tool is a fairly complex file, not only because of its structure but also because of its order. With FoxPro Screen files (SCX), the order of the records is not vital to ensure the SPR file is built properly. The order of the records simply dictates the order of the screen objects. With the Menu file, things are implemented a little differently. The MNX file is saved in the order which the menu appears. Whenever a change is made in a menu, even a simple reordering of menu pads, the MNX file is resorted in its appropriate order. The menu is also in the order that the code is generated. The _MSYSMENU pads are identified first, then followed by the records for each menu item from left to right, from top to bottom.

An interesting point is that the MNX file was built with several fields that seem to indicate a great expandability (such as the SCHEME and PROCTYPE fields); however, the Menu Builder doesn’t make use of these fields.

Working with the MNX file involves a number of issues.

Under DOS, the level name of the main menu pad is "_MSYSMENU"; under Windows, it is "_msysmenu". Even though menus do not contain platform-specific code, this represents a difference when you save a menu under a platform.

Submenus have two components to them: one record identifies the menu pad prompt, the other identifies the popup name. This is similar to the code GENMENU creates: one line to identify the pad; another to DEFINE the popup statement.

The MNX file has a field for SCHEME. In 2.5 versions of GENMENU, this SCHEME field was ignored and GENMENU was hardcoded to color schemes 3 and 4. In 2.6, Microsoft included support for this field, even though the user does not have direct access to this field. (GENMENUX 2.0b provides 2.6 level support)

The NUMITEMS and ITEMNUM fields are vital to the MNX file order. If the number of records identified for a particular menu level do not equal the NUMITEMS field for the menu pad, the MNX file will be corrupted.


GenMenuX
(c)1994 Andrew Ross MacNeill