04.08.2013 Views

FoxTalk - dFPUG-Portal

FoxTalk - dFPUG-Portal

FoxTalk - dFPUG-Portal

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>FoxTalk</strong><br />

Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers<br />

Menus – A Data-<br />

Driven Approach<br />

Chad J. Lemmer<br />

Like many other Visual FoxPro developers, I long for the day<br />

when Microsoft updates the Menu Designer to be objectoriented.<br />

Some developers have gone to great lengths and<br />

have come up with excellent ways to make the current Menu<br />

Designer object-oriented. As an alternative to object-oriented<br />

menus, Chad Lemmer presents another approach—a tabledriven<br />

menu system.<br />

RECENTLY, I was working on an application where I<br />

needed a menu system that was easy to use<br />

and maintain, and that also had built-in security. If<br />

a user didn’t have access to a certain task in the system, I<br />

didn’t want the option to even show up on the menu.<br />

“Out of sight, out of mind.” Let’s face it, who likes<br />

looking at an interesting menu option, only to select it and<br />

receive a hostile message informing them that they’ve<br />

done something terribly wrong. If the user doesn’t belong<br />

there, then I don’t like giving them the option to go there.<br />

In all of my experiences using FoxPro, there’s one<br />

thing that I’ve learned—to make things table-driven<br />

wherever possible. It might take a little more work up<br />

front to get the ball rolling, but the extra time and effort is<br />

almost always gained back if changes need to be made<br />

later. My initial idea was to create a menu class that<br />

would use the DEFINE PAD, DEFINE POPUP, and<br />

DEFINE BAR commands to build a standard menu<br />

driven by a menu table and a user access table. After<br />

looking into the DEFINE commands a bit further, I<br />

concluded that the class code could get pretty hairy. In<br />

addition, the system was going to be used in a<br />

manufacturing environment with touch screens, and if<br />

you’ve ever used a touch screen, you know that selecting<br />

items from a standard Windows menu bar is a nuisance<br />

because the text is too small.<br />

After a few more brainstorming sessions, I decided<br />

to create a menu form that would be table-driven to<br />

This is an exclusive supplement for<br />

<strong>FoxTalk</strong> subscribers. For more<br />

information about <strong>FoxTalk</strong>, call us at<br />

1-800-788-1900 or visit our Web site<br />

at www.pinnaclepublishing.com/ft.<br />

Extended Article<br />

simplify the adding, modifying, and removing of<br />

menu items. The menu class excludes items by referring<br />

to a user security table that stores the menu items to<br />

which a user has access. The menu class also takes<br />

care of the touch screen problem because the menu<br />

form would use a ListBox to display the menu items<br />

with a font big enough for even Sampson’s<br />

index finger!<br />

How it works<br />

The clMenu class consists of a form with a ListBox to<br />

display menu options, along with an EditBox to display<br />

a more detailed description of the menu option. To<br />

execute a menu item, the user selects it from the ListBox<br />

and clicks on the “Run” button or presses the Enter key<br />

(see Figure 1).<br />

http://www.pinnaclepublishing.com/ft <strong>FoxTalk</strong> Extended Article: October 2000<br />

1<br />

6.0<br />

Figure 1. An example of a menu form created by the<br />

clMenu class.


The clSysMenu table<br />

The clSysMenu table is used by the clMenu class to<br />

populate the ListBox with menu items. Table 1 shows the<br />

structure of the clSysMenu.<br />

Each record in the clSysMenu table defines either<br />

another menu to display or a menu item to add to the<br />

ListBox. To instantiate another menu, you’d populate the<br />

cRunMenu field with a value from the cModuleID field that<br />

represents the menu you want to show. This setup provides<br />

an unlimited number of submenus. Each menu in the table<br />

must have a header record that specifies various menu<br />

properties such as the caption, icon, and color of the menu<br />

name. To create a menu header record, set the cMenuID<br />

field equal to the cModuleID field. When the clMenu class<br />

instantiates, it SEEKs the menu header record and sets<br />

the menu various form properties accordingly.<br />

To execute a FoxPro program, application, or form,<br />

you’d populate the cPath and cRunFile fields with the path<br />

and filename to execute, respectively. The cMenuID field<br />

stores a unique key to each menu item. The unique keys are<br />

used in conjunction with a clUser table’s mAllowAcc field<br />

that defines the menu items to which a user has access.<br />

Table 1. The clSysMenu structure.<br />

Field Description<br />

cModuldID Used to group menu items together. All items that<br />

belong to a menu have the same value in this field.<br />

cMenuID A unique menu item ID used for security and to<br />

uniquely identify each menu item.<br />

nOrder Specifies the order in which the menu items<br />

should appear.<br />

cMenuName The name of the menu or menu item to display in<br />

the ListBox.<br />

cRunMenu If specified, displays the specified submenu.<br />

cIconFile The file name for the icon to display on the menu<br />

form’s caption bar. Used in header records only.<br />

cPath Specifies the path for the file to execute in the<br />

cRunFile field or the icon specified in the<br />

cIconFile field.<br />

cRunFile If specified, the file in this field will be executed<br />

when the menu item is selected.<br />

cParms Additional parameters to pass to the file specified<br />

in the cRunFile field.<br />

mComment A longer description of the menu item to be<br />

displayed on the menu form class.<br />

nRed Passed to the RGB() function to allow different<br />

menu captions to have different colors. Used in<br />

header records only.<br />

nGreen Passed to the RGB() function to allow different<br />

menu captions to have different colors. Used in<br />

header records only.<br />

nBlue Passed to the RGB() function to allow different<br />

menu captions to have different colors. Used in<br />

header records only<br />

The clUser table<br />

The clUser table contains one record per user and has a<br />

memo field that contains a list of values from the<br />

clSysMenu.cMenuID field to which the user has access.<br />

Table 2 shows the structure for the clUser.<br />

Table 2. The clUser structure.<br />

Field Description<br />

cLoginName The name of the current user that’s logged into<br />

the application.<br />

mAllowAcc A list of values from the clSysMenu.cMenuID field<br />

that the user has access to.<br />

lAutoClose If set to .T., the previous menu will close<br />

automatically when a new menu is created,<br />

except for the main menu.<br />

When the clMenu class spins through the clSysMenu<br />

table to add items to the ListBox, it only adds the menu<br />

item if the item’s cMenuID is present in the clUser<br />

security table record. Another field, lAutoClose, tells the<br />

clMenu class to close the previous menu after running a<br />

submenu, unless they’re calling a submenu from the main<br />

menu. The main menu will always stay open.<br />

The clMenu class<br />

The clMenu class spins through the records in the<br />

clSysMenu table to populate the ListBox with menu<br />

options that the user has access to. The main menu for<br />

your application can be created by using the NewObject()<br />

function, passing it “MAIN” as the module ID and the<br />

user name of the user logged into the application. Once<br />

the main menu is instantiated, the clMenu class takes over<br />

from there. You probably want to add the line<br />

SYSMENU=OFF in your CONFIG.FPW file to remove the<br />

Visual FoxPro system menu from your application. The<br />

following is an example of how to instantiate the main<br />

menu for your application:<br />

public oMainMenu<br />

oMainMenu = NewObject("clMenu", "clMenu", "", ;<br />

"MAIN", lcLoginName)<br />

if type("oMainMenu") = "O" and ;<br />

!isnull(oMainMenu)<br />

oMainMenu.Show<br />

read events<br />

else<br />

wait window "Unable to create main menu."<br />

endif<br />

The ListBox on the clMenu form is populated by a<br />

temporary cursor called curMenu. This increases the<br />

menu’s overall performance because the ListBox only<br />

contains the values it needs—as opposed to using a<br />

filtering technique on the clSysMenu table to eliminate the<br />

menu items that you don’t want the user to see. The<br />

clMenu class accepts the following parameters as shown<br />

in Table 3.<br />

2 <strong>FoxTalk</strong> Extended Article: October 2000<br />

http://www.pinnaclepublishing.com/ft


Table 3. The clMenu class parameters.<br />

Parameter Description<br />

tcModuleID Specifies which menu items from the clSysMenu<br />

table to retrieve. Records from the clSysMenu table<br />

who’s cModuleID field value matches this<br />

parameter will be included in the ListBox, if the<br />

user has access to it.<br />

tcLoginName The name of the current user logged into<br />

the application.<br />

In order to create multiple instances of the clMenu<br />

class for each submenu that’s produced, I devised a<br />

method called GetNewMenuObject that returns the next<br />

available menu object name. This method spins through<br />

all of the objects in memory whose Name property starts<br />

with “oMenu” and appends a sequential number on the<br />

end of it until it comes up with an object name that isn’t<br />

being used. Notice the code for the GetNewMenuObject<br />

method in the following example:<br />

lparameters tnMenu<br />

local lnI, lcMenu<br />

lnI = 1<br />

lcMenu = "oMenu" + allt(str(lnI))<br />

do while .t.<br />

if type(lcMenu) "O" or isnull(&lcMenu)<br />

exit<br />

else<br />

* If the menu already exists, then show it<br />

* and don't make another copy of it<br />

if &lcMenu..cModuleID = curMenu.cRunMenu<br />

&lcMenu..show<br />

return<br />

endif<br />

lnI = lnI + 1<br />

lcMenu = "oMenu" + allt(str(lnI))<br />

endif<br />

enddo<br />

tnMenu = lnI<br />

return lcMenu<br />

In addition to finding a unique object name for each<br />

menu that’s created, this code also accepts a parameter<br />

(passed by reference) that tells the calling method what<br />

menu number was returned. The calling method uses the<br />

number to control where the new menu appears on the<br />

screen. By taking the menu number, multiplying it by a<br />

constant, and setting the new form’s Left and Top<br />

properties to that value, it gives the menus a cascading<br />

effect. Otherwise, the submenus would appear directly<br />

over the top of the previous menu (see Figure 2). Also, if<br />

the submenu selected by the user already exists as an<br />

object in memory, this method will show the existing<br />

menu instead of instantiating another one. It does this by<br />

comparing the new menu object’s cModuleID property<br />

with the value in the cRunMenu field, as you can see in<br />

the preceding code.<br />

Conclusion<br />

Because the menu class is table-driven, it’s very easy to<br />

make changes to an application’s menu. Most importantly,<br />

it eliminates the need to modify source code and install<br />

system updates when we need to change the menu. Once<br />

the menu option is added and the user is assigned access<br />

to the menu option, it appears in the menu automatically.<br />

The main disadvantage to this approach is that the menu<br />

isn’t a standard Windows menu that most other<br />

applications use. It comes down to the fact that the<br />

“standard way” might not always be the “practical way”<br />

to solve a problem.<br />

A complete working demo of the clMenu class is<br />

included in this month’s Source Code files. Questions,<br />

comments, and new ideas are always welcome! ▲<br />

10LEMMSC.ZIP at www.pinnaclepublishing.com/ft<br />

Chad J. Lemmer is a full-time I.S. programmer/analyst at Wausau Coated<br />

Products in Wausau, WI, specializing in Visual FoxPro application<br />

development. He also develops Visual FoxPro applications for the clients<br />

of his own consulting business. clemmer@dwave.net.<br />

Figure 2. The clMenu class cascades submenus.<br />

http://www.pinnaclepublishing.com/ft <strong>FoxTalk</strong> Extended Article: October 2000<br />

3

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!