Last week we looked at menu drag/drop. Another little-used menu feature added in Windows 2000 is the ability to show context menus on menus. The message is WM_MENU­RBUTTON­UP and the flag is TPM_RECURSE. Let's demonstrate with a simple program.

Start with the scratch program, and add the Move­Menu­Item function just so our context menu can do something.

// resource header file
#define IDM_MAIN 1
#define IDM_POPUP 2
#define IDC_MOVEUP 200
#define IDC_MOVEDOWN 201

// resource file
1 MENU PRELOAD
BEGIN
    POPUP "&Test"
    BEGIN
        MENUITEM "&Red",    100
        MENUITEM "&Orange", 101
        MENUITEM "&Yellow", 102
        MENUITEM "&Green",  103
        MENUITEM "&Blue",   104
        MENUITEM "&Violet", 105
    END
END

2 MENU PRELOAD
BEGIN POPUP ""
    BEGIN
        MENUITEM "Move &Up",   IDC_MOVEUP
        MENUITEM "Move &Down", IDC_MOVEDOWN
        MENUITEM SEPARATOR
        MENUITEM "&Cancel",    IDCANCEL
    END
END

// scratch.cpp
#define HANDLE_WM_MENURBUTTONUP(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (UINT)(wParam), (HMENU)(lParam)), 0L)

void OnMenuRButtonUp(HWND hwnd, UINT uPos, HMENU hmenu)
{
 if (hmenu == GetSubMenu(GetMenu(hwnd), 0)) {
  HMENU hmenuPopup = LoadMenu(g_hinst, MAKEINTRESOURCE(IDM_POPUP));
  if (hmenuPopup) {
   if (uPos == 0) {
    EnableMenuItem(hmenuPopup, IDC_MOVEUP, MF_DISABLED | MF_GRAYED);
   }
   if (uPos == GetMenuItemCount(hmenu) - 1) {
    EnableMenuItem(hmenuPopup, IDC_MOVEDOWN, MF_DISABLED | MF_GRAYED);
   }
   DWORD dwPos = GetMessagePos();
   UINT idCmd = TrackPopupMenuEx(GetSubMenu(hmenuPopup, 0),
                 TPM_RECURSE | TPM_RETURNCMD,
                 GET_X_LPARAM(dwPos),
                 GET_Y_LPARAM(dwPos), hwnd, NULL);
   switch (idCmd) {
    case IDC_MOVEUP:
     MoveMenuItem(hmenu, uPos, uPos - 1);
     break;
    case IDC_MOVEDOWN:
     MoveMenuItem(hmenu, uPos, uPos + 2);
     break;
   }
   DestroyMenu(hmenuPopup);
  }
 }
}

    HANDLE_MSG(hwnd, WM_MENURBUTTONUP, OnMenuRButtonUp);

// InitApp function
    wc.lpszMenuName = MAKEINTRESOURCE(IDM_MAIN);

When we receive the WM_MENU­RBUTTON­UP message and confirm that the menu is the one we support, we create the popup menu and display it at the mouse location (obtained via Get­Message­Pos) with the TPM_RECURSE flag, indicating that this is a pop-up menu for a pop-up menu. (We also use TPM_RETURN­CMD, but that's nothing new.) If the user chose to move the item up or down, we move it up or down.

That's all. There really isn't much here, but I figured I'd just write a sample program just to show how it's done.