Popup (context) menus

Started by Art Araya, May 31, 2005, 08:54:08 PM

Previous topic - Next topic

Art Araya

In VB to display a popup menu when the user right clicked on some control this is what I had to do:

1) Create the menu in the menu editor
2) Hide this menu by setting its Visible property to False
3) Then call a single line function to display a popup in the appropriate control's event. (Like the button down or button up event of the desired control)

What's the procedure in FF/PB?  I don't see any way to hide a menu so I don't think I create it in the menu editor first and then hide it.  I also see no function in PB or FF to display a popup (context) menu.

Does this have to be handled via pure Win32 stuff?  Does anyone have a handy code snippet or library that makes this easier?

TIA
Art

Roger Garstang

If you just want a popup, it would probably be easier to use the APIs.  I usually create them in the WM_CREATE function and destroy them in WM_DESTROY.  You just setup a MENUITEMINFO structure/type and use CreatePopupMenu and InsertMenuItem to create the menu.  Might be easiest to make up some Global DWORDS or even an array to store the menu handles for each menu you want.  Then use TrackPopupMenu on the click events to show the menu.  Then on WM_DESTROY use DestroyMenu on all the menu handles.

You could also make them up on the fly which is better if you want dynamic options in the menu, but may show the menu a little slower.  On Click do the whole process of Creating the menu, Inserting the items, Show the menu, then after the selection is made Destroy it.

Roger Garstang


%WM_MENURESTORE = %WM_USER + 501
%WM_MENUABOUT   = %WM_USER + 502
%WM_MENUSEPLINE = %WM_USER + 503
%WM_MENUEXIT    = %WM_USER + 504

'------------------------------------------------------------------------------------
Function FORM_WM_CREATE (hWndForm As Dword, ByVal UserData As Long) As Long
Local mi As MENUITEMINFO
Local tempsz As Asciiz * 10

   ' Setup Menu Structure
   mi.cbSize=SizeOf(mi)
   mi.fMask= %MIIM_TYPE Or %MIIM_STATE Or %MIIM_ID
   mi.fType= %MF_STRING
   mi.dwTypeData= VARPTR(tempsz)
   
   ' Create Menu
   gMenu= CreatePopupMenu()
   
   mi.wID= %WM_MENURESTORE
   mi.fState= %MFS_DEFAULT
   tempsz= "&Restore"
   InsertMenuItem(gMenu, &HFFFFFFFF, %TRUE, mi)

   mi.wID= %WM_MENUABOUT
   mi.fState= %MF_ENABLED
   tempsz= "&About"
   InsertMenuItem(gMenu, &HFFFFFFFF, %TRUE, mi)

   mi.fType= %MF_SEPARATOR
   mi.wID= %WM_MENUSEPLINE
   InsertMenuItem(gMenu, &HFFFFFFFF, %TRUE, mi)

   mi.fType= %MF_STRING
   mi.wID= %WM_MENUEXIT
   tempsz= "E&xit"
   InsertMenuItem(gMenu, &HFFFFFFFF, %TRUE, mi)
End Function

'------------------------------------------------------------------------------------
Function FORM_WM_DESTROY (hWndForm As Dword) As Long
   DestroyMenu(gMenu) 'cleanup menu handle  
End Function

Art Araya

Thanks Roger - again, you've been a big help to me.  I am having a problem displaying the popup menu over the list control.  I am using TrackPopupMenu in the WM_RBUTTONUP of the list control and am getting a popup menu but it is not positioned at the location of the cursor.  Here is the code I am using.  Perhaps you can point out my error?


FUNCTION FRMMAIN_LSTNAMES_WM_RBUTTONUP (parameters omitted for brevity) AS LONG

'display the context menu
TRACKPOPUPMENU gListPopupMenu, %TPM_LEFTALIGN OR %TPM_LEFTBUTTON, xPos, yPos, 0, hWndForm, BYVAL %NULL
   
END FUNCTION



TIA,

Art

Art Araya

OK, solved this myself.  I thought that I could use the xPos and yPos values that are being passed in to WM_RBUTTONUP but apparently I can't.  Maybe these are in client coordinates?  But TrackPopupMenu requires screen coordinates.  So I am now using GETCURSORPOS to get the coordinates.  Here is the revised code that works properly.

FUNCTION FRMMAIN_LSTNAMES_WM_RBUTTONUP (parameters omitted for brevity) AS LONG

'display the context menu
LOCAL lngRetVal AS LONG
LOCAL ptScreenCursorPos AS POINTAPI

lngRetVal = GETCURSORPOS(ptScreenCursorPos)

   IF lngRetVal <> 0 THEN
    TRACKPOPUPMENU gListPopupMenu, %TPM_LEFTALIGN OR %TPM_LEFTBUTTON, ptScreenCursorPos.x, ptScreenCursorPos.y, 0, hWndForm, BYVAL %NULL

   END IF
 
END FUNCTION


Art

TechSupport

Quote from: Art ArayaI thought that I could use the xPos and yPos values that are being passed in to WM_RBUTTONUP but apparently I can't.  Maybe these are in client coordinates?  But TrackPopupMenu requires screen coordinates.  So I am now using GETCURSORPOS to get the coordinates.  Here is the revised code that works properly.
Art, you are definately correct. All of the mouse button type messages use the client area coordinates rather than screen coordinates. This is just how Windows has designed those messages. You did the right thing by grabbing the mouse position via GetCursorPos and displaying the menu based on those values.

TechSupport

Oh, and one other thing, don't forget to use DestroyMenu to free the resources held by the menu handle, especially if you are dynamically creating the menu many times during the life of your program (via CreateMenu). Each time you create the menu you should first test to see if the gListPopupMenu variable is valid and destroy it.


If gListPopupMenu then DestroyMenu gListPopupMenu
' continue to create the new menu here....


You can also DestroyMenu at the end of the program as well just to ensure that the resources are released.  i.e. via the WM_DESTROY message handler for the main form.

Art Araya

Thanks for the pointer Paul.  I am creating the popup menu once during WM_CREATE.  And am therefore destroying the popup menu once during WM_DESTROY.  Is this correct?

TechSupport

Yes, that is correct. Even though Windows will normally release all memory held by your application when it terminates, it is considered good programming practice to explicitly destroy your used handles prior to the app ending.

Larry Burford

From the code examples above:

Roger -

   ' Create Menu
   gMenu= CreatePopupMenu()

Art -

'display the context menu
TRACKPOPUPMENU gListPopupMenu, ...

===

When I added Roger's pop up menu to the Calculator example in the Samples folder, I had to change Art's variable "gListPopupMenu" to Roger's variable "gMenu" to get the menu to display

===

Am I right in assuming that this is just two people using two different names for the menu handle, or is there really another variable involved but this is such a simple example that the second variable doesn't come into play?

TechSupport

Quote from: Larry BurfordAm I right in assuming that this is just two people using two different names for the menu handle, or is there really another variable involved but this is such a simple example that the second variable doesn't come into play?
You're right - it's just two people using two different names for the menu handle.