Welcome to Foxite.COM Community Weblog Sign in | Join | Help

Creating Data Driven Pop-Up menus in VFP

One of the little tools that Marcia and I use all the time when working in VFP is a pop-up menu generator that allows us to select a development environment easily and quickly. Yes, we know all about the Task Pane but personally I have always found that a real pain (pardon the pun) to use. All we wanted was a quick and easy way to be able to switch between my various environments.

It seemed to us that the easiest way to do that is to have a pop-up menu that could be invoked from a hot key. Then we realized that there are all sorts of situations in which it would be useful to have a pop-up menu (like right-click options on forms and controls) and so we came up with a simple menu generator to build a pop-up menu on the fly from simple tables. In order to make this generic we set up a relational structure using three tables as shown in Table 1.

These tables are all contained in their own DBC named (imaginateively) "PopMenu". So how do we use the tables?

Well, first we create the records in the names table, currently I have two menus defined, one named "Projects" and another named "EditOptions". The first is the one that I run when I want to select a project environment, and the second is a generic editing options popup. Of course there is no real limit to the number of menus you can define but for the purposes of this article let's stick to these two.

Now we need to define our menu bars for these options and this is really simple. The first thing we need is a horizontal menu divider (grouped options always look better) so that gets defined as the first bar #1 as follows:

cBarText     = "\-"
cBarDesc     = "Divider Line"

Nothing else is needed for this entry. Now we need a bar for each option that we want to include, but for these bars we also need to include the action (and Skip For commands if needed). For my default development environment I use the following:

cBarText     = "\-"
cBarDesc     = "Divider Line"
mBarAction  = " CLOSE ALL
                       CLEAR
                       SET PATH TO (HOME() + ";D:\VFP90\RUN\;LIBS;FORMS;DATA;PROGS;UTILS;BMPS" )
                       SET DEFAULT TO D:\VFP90\RUN\"

And for the "Paste" option on the EditOptions menu we need to include a SKIP FOR condition too:

cBarText     = "Paste"
cBarDesc     = "Paste From Clipboard"
mBarAction  = "SYS(1500, '_MED_PASTE', '_MEDIT')"
mSkipFor     = "EMPTY( _ClipText )"

 

The code to generate the tables is in the attached zip file as GenBars.prg. You can use this file as the template to set up your own version of this little utility, and Figure 1 shows how the data looks for the result:

 

Now all we need is the code to use this. We created this as a class that does all the work in its INIT() event so that it runs itself when called. By returning .F. on completion of the code we prevent the object from actually being instantiated so that there is no lasting impact on the system at run time. The class is based on the session base class and this means that it creates a transient data session of its own – again, to avoid any environmental impact.

However, this also means that the class cannot be defined visually and so, if you really wanted to have this object defined as a visual class you could have to use either a Toolbar, Form or FormSet – these being the three visual classes that can create a Private Datasession. (Note: Of the three, the formset is actually the one with the smallest memory footprint – so this may be the only time you might actually use a formset in VFP. Personally I don't care if the definition is visual or not and so I just use the Session base class).

The Init() method is very simple indeed, as follows:

PROCEDURE INIT( tcMenuName )
  LOCAL lcScript
  *** Have we got this menu definition
  IF This.GetMenuDef( tcMenuName )
    lcScript = This.BuildMenu( tcMenuName )
    EXECSCRIPT( lcScript )
  ENDIF
  RETURN .F.
ENDPROC

If the passed in menu name is found by GetMenuDef(), the BuildMenu() method is called. This creates a temporary MPR file using the data from the metadata tables and the file is then executed to display the menu. O)n completion of the menu action, the method returns false preventing the object from actually instantiating.

The GetMenuDef() method simply executes a SQL query, creating a cursor that contains the relevant data to generate the required menu:

LOCAL lcMenuName
lcMenuName = UPPER( ALLTRIM( tcMenuName ))
*** Populate the cursor
SELECT PB.cbartext, PB.mbaraction, PB.mbarskip, PL.ilnkseq ;
   FROM popnames PN, popbars PB, poplink PL ;
 WHERE PB.ibarpk = PL.ilnkbarfk ;
      AND PL.ilnknamfk = PN.imenupk ;
      AND UPPER( PN.cmenuname ) = lcMenuName ;
      AND NOT DELETED( 'poplink' ) ;
     INTO CURSOR curMenu ;
   ORDER BY PL.ilnkseq
 *** Did we get anything?
RETURN (_TALLY > 0)

This cursor is then used by the subordinate methods (GetBars() and GetActions()) that are called by the BuildMenu() method. Here is the script generated for the EditOptions pop-up and, as you can see it is a perfectly standard MPR file that uses ExecScript in the ON SELECTION clause to execute whatever action has been defined:

DEFINE POPUP editoptions SHORTCUT RELATIVE FROM MROW(),MCOL()
DEFINE BAR 1 OF editoptions PROMPT [Copy]
DEFINE BAR 2 OF editoptions PROMPT [\-]
DEFINE BAR 3 OF editoptions PROMPT [Paste]SKIP FOR EMPTY( _ClipText )
DEFINE BAR 4 OF editoptions PROMPT [\-]
DEFINE BAR 5 OF editoptions PROMPT [Cut]
ON SELECTION BAR 1 OF editoptions EXECSCRIPT( [SYS(1500, '_MED_COPY', '_MEDIT')])
ON SELECTION BAR 3 OF editoptions EXECSCRIPT( [SYS(1500, '_MED_PASTE', '_MEDIT')])
ON SELECTION BAR 5 OF editoptions EXECSCRIPT( [SYS(1500, '_MED_CUT', '_MEDIT')])
ACTIVATE POPUP editoptions

So how do we use this class? The PopMenu.prg is in my VFP root directory and the class name is "xMenuPop". My VFP startup program, called from config.fpw, includes the following two lines:

*** Set Projects menu Hotkey and Run the Projects menu
ON KEY LABEL CTRL+F12 NEWOBJECT( 'xMenuPop', 'D:\vfp90\popmenu.prg', NULL, 'Projects' )
NEWOBJECT( 'xMenuPop', 'D:\vfp90\popmenu.prg', NULL, 'startup' )

The first assigns the projects menu to my CTRL+F12 key, and the second, which is the last line in startup program,  runs it immediately. Similarly to use the EditOptions menu we simply add one line of code to the right-click of any control that we want to invoke the menu from:

NEWOBJECT( 'xMenuPop', 'D:\vfp90\popmenu.prg', NULL, 'EditOptions' )

The class definition, the DBC, tables and the sample data generation program are all included in the downloadable zip file attached to this blog, I hope you find it as useful as we do.
Published Saturday, June 20, 2009 4:38 PM by andykr
Filed Under:
Attachment(s): PopMenu.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

What do you think?

(required) 
required 
(required)