Positioning popup menus
I'm not a big fan of right click menus. In many applications they are implemented incorrectly and cannot be triggered with the context menu key or the standard Shift+F10 combination. They also seem to be sort of a dump bin for many developers. Instead of thinking how to incorporate a new feature in a useful way, it's just added to all right click menus on the form. Lastly, right click menus are hardly discoverable for the end user. Having to click on every pixel just to discover whether there's some hidden feature is great for games, but not really appropriate for any productive application.
There are uses for right click menus. One should keep in mind, though, that the name "shortcut menu" comes from the fact that these menus originally provided a quicker way to access features with a mouse that would be available in the menu or on buttons, as well. Features which are only available through a shortcut or multi-level popup menus that merely open a dialog box somewhere else on the screen hardly fall into that category.
Back to the topic. There are times when it makes sense to provide less frequently used features on a form. For instance, a useful feature for a name textbox is to open a browser window with a web search. For such features I use a small button close to the textbox that displays a down arrow ("u" using the Marlett font). This button opens a small popup menu just below the button much in the same way you see this all over in Microsoft Office.
Positioning popup menus can be a little difficult in Visual FoxPro. The Menu Designer produces code that uses MCOL() and MROW() to position the menu at the mouse cursor. This works fine unless you click somewhere on the window title. In this case, the two functions return -1 moving the popup somewhere outside the window.
When you try to move the popup elsewhere you quickly realize that you have to give the position in Foxels instead of pixels. Foxels are in Visual FoxPro what Twips are in Visual Basic. A Foxel depends on the size of the current font of a form. In other words, changing Thisform.FontName or any other font related property changes the measurement for Foxels.
The rarely used ScaleMode property allows you to specify form coordinates in Foxels or pixels. As a side note: Switching a form to Foxels is a good way to break generic third party components. Most developers don't deal very well with positions that have decimal places and add pixels to Left, Top, Width, and Height without checking the scale mode before.
Hence, to position a popup control just below or above a command button, you switch the form scale mode, read the button position in Foxels and switch back to pixels. This works fine for buttons that are placed on the form directly. With nested controls, though, you have to use OBJTOCLIENT(), a function that completely ignores the ScaleMode property. To get around this, I add an invisible button right to the form:
Local lnRow, lnColumn
If Vartype(Thisform.PosHelp) != "O"
Thisform.ScaleMode = 0
lnRow = Thisform.PosHelp.Top
lnColumn = Thisform.PosHelp.Left
Thisform.ScaleMode = 3
With the position in Foxels I can now define a popup menu like this:
DEFINE POPUP shortcut ;
SHORTCUT RELATIVE ;
From m.lnRow, m.lnColumn ;
Font Thisform.FontName, This.FontSize
Define bar 1 of Shortcut Prompt "Item 1"
Define bar 2 of Shortcut Prompt "Item 2"
Define bar 3 of Shortcut Prompt "Item 3"
Activate Popup Shortcut
Just in case you wonder about the FONT clause used here. Some clients insist on a resizer control on the form. That is, when they resize the form, the content becomes larger. With this clause the popup menu has the same size as everything else on the form.
ActiveX controls don't work well in VFP with modal forms. Well, that's not exactly a big surprise for anyone who tried that. If you contact the creator of an ActiveX control you usually end up with one of two replies:
"Our controls work in all supported environment. What is Visual FoxPro, anyway?" or "Go away! We don't write unmanaged code, anymore." Even controls that usually behave well with VFP, like the controls from dbi exhibit this behaviour.
Why can't the vendors fix this? Admittedly, it's not really their bug; it's a Visual FoxPro bug. Not really a bug, more like a design issue. One of the problems that the Visual FoxPro team had to solve was that modal windows simply don't exist in the Windows API. OK, you see them, but that doesn't mean they are real.
Windows creates a modal window by disabling the parent window. That works fine with just a single level of modal windows like it's the case with a dialog box. However, with multiple windows (or forms) that can be modal, maybe with multiple forms in a form set that remain accessible while other windows are not, things get more confusing.
So here's what happens with ActiveX controls in Visual FoxPro: When you add an ActiveX control you actually add two windows to the form. The outermost is the OLE host window which is the one that Visual FoxPro controls. Inside the host window, the ActiveX control creates its own window(s) that remain under control of the ActiveX control. This inner window (or windows) is what we know as "the ActiveX control".
When you show a modal window, Visual FoxPro disables all OLE host windows on all other forms. The ActiveX control inside remains active, but doesn't receive any user input related messages anymore, because the parent window is disabled. As a result, the control does not respond to mouse or keyboard events, nor does it receive the focus. It continues to run, though. For instance, it can update itself, respond to other events, and the like.
Once the user closes the modal form, Visual FoxPro enables all previously disabled host windows. My bet (well, more a guess, actually) is that Visual FoxPro stores the previous state when it disables the OLE host window and restores that state when upon enabling them. The control receives messages and responds to user input. Well, that's what happens with just one level of modal forms.
It seems, though, that there's only one variable for keeping the previous enabled state of each OLE host window. So when the second modal form is launched, Visual FoxPro stores the state of the already disabled window and disables it again. When the form is closed, this disabled state becomes what is restored. In the end, the host window remains disabled even when all modal forms have been closed.
There is actually an easy fix that - unfortunately - is not generic. When the Activate event of a form fires you know for sure that this form should respond to user input. At this point none of the ActiveX controls on the current form should be disabled. You can ensure this by running the following code for every ActiveX control that is on the form:
Declare Long GetParent in Win32API Long
Declare Long EnableWindow in Win32API Long, Long
EnableWindow( GetParent(oleControl.Hwnd), 1 )
oleControl is a regular reference to the ActiveX control on the form (e.g. Thisform.oleControl1). The HWND property is not a standard property that VFP provides. It's completely up to the ActiveX control to provide the window handle and to pick the name for the property or method. You need to refer to the documentation of the ActiveX control.