Commit in reactos on MAIN
include/messages.h+11.11 -> 1.12
       /structs.h+61.84 -> 1.85
lib/user32/windows/menu.c+778-1551.48 -> 1.49
subsys/win32k/ntuser/menu.c+83-451.46 -> 1.47
ChangeLog+41.216 -> 1.217
+872-200
5 modified files
Implement keyboard navigation for menus

reactos/include
messages.h 1.11 -> 1.12
diff -u -r1.11 -r1.12
--- messages.h	15 Dec 2003 00:44:52 -0000	1.11
+++ messages.h	22 Feb 2004 18:04:51 -0000	1.12
@@ -1054,6 +1054,7 @@
 #define WM_NCXBUTTONUP	(172)
 #define WM_NCXBUTTONDBLCLK	(173)
 #define WM_NEXTDLGCTL	(40)
+#define WM_NEXTMENU	(531)
 #define WM_NOTIFY	(78)
 #define WM_NOTIFYFORMAT	(85)
 #define WM_NULL         (0)

reactos/include
structs.h 1.84 -> 1.85
diff -u -r1.84 -r1.85
--- structs.h	14 Feb 2004 21:27:35 -0000	1.84
+++ structs.h	22 Feb 2004 18:04:51 -0000	1.85
@@ -3136,6 +3136,12 @@
 typedef_tident(MDICREATESTRUCT)
 typedef_tident(LPMDICREATESTRUCT)
 
+typedef struct tagMDINEXTMENU {
+	HMENU hmenuIn;
+	HMENU hmenuNext;
+	HWND hwndNext;
+} MDINEXTMENU,*PMDINEXTMENU,*LPMDINEXTMENU;
+
 typedef struct tagMEASUREITEMSTRUCT {
   UINT  CtlType;
   UINT  CtlID;

reactos/lib/user32/windows
menu.c 1.48 -> 1.49
diff -u -r1.48 -r1.49
--- menu.c	15 Feb 2004 07:39:12 -0000	1.48
+++ menu.c	22 Feb 2004 18:04:52 -0000	1.49
@@ -21,7 +21,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: menu.c,v 1.48 2004/02/15 07:39:12 gvg Exp $
+/* $Id: menu.c,v 1.49 2004/02/22 18:04:52 gvg Exp $
  *
  * PROJECT:         ReactOS user32.dll
  * FILE:            lib/user32/windows/menu.c
@@ -33,12 +33,15 @@
 
 /* INCLUDES ******************************************************************/
 
+#define __NTAPP__
+
 #include <windows.h>
 #include <user32.h>
 #include <string.h>
 #include <draw.h>
 #include <window.h>
 #include <strpool.h>
+#include <ntos/rtl.h>
 
 #include <user32/callback.h>
 #include "user32/regcontrol.h"
@@ -332,6 +335,41 @@
 }
 
 /***********************************************************************
+ *           MenuGetAllRosMenuItemInfo
+ *
+ * Get full information about all menu items
+ */
+static INT FASTCALL
+MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
+{
+  DWORD BufSize;
+
+  BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
+  if (BufSize <= 0)
+    {
+      return -1;
+    }
+  *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
+  if (NULL == *ItemInfo)
+    {
+      return -1;
+    }
+
+  return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
+}
+
+/***********************************************************************
+ *           MenuCleanupAllRosMenuItemInfo
+ *
+ * Cleanup after use of MenuGetAllRosMenuItemInfo
+ */
+static VOID FASTCALL
+MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
+{
+  HeapFree(GetProcessHeap(), 0, ItemInfo);
+}
+
+/***********************************************************************
  *           MenuDrawMenuItem
  *
  * Draw a single menu item.
@@ -941,8 +979,8 @@
 }
 
 
-static BOOL
-MeasureMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str)
+static BOOL FASTCALL
+MeasureMenuItem(HWND hWnd, HMENU mnu, HDC hDC, PROSMENUITEMINFO mii, LPWSTR str)
 {
   BOOL res = FALSE;
   MEASUREITEMSTRUCT mis;
@@ -960,14 +998,14 @@
     res = (BOOL)SendMessageW(hWnd, WM_MEASUREITEM, 0, (LPARAM)&mis);
     if(res)
     {
-      mir->right = mir->left + mis.itemWidth;
-      mir->bottom = mir->top + mis.itemHeight;
+      mii->Rect.right = mii->Rect.left + mis.itemWidth;
+      mii->Rect.bottom = mii->Rect.top + mis.itemHeight;
     }
     else
     {
       /* FIXME calculate size internally assuming the menu item is empty */
-      mir->right = mir->left + 1;
-      mir->bottom = mir->top + 1;
+      mii->Rect.right = mii->Rect.left + 1;
+      mii->Rect.bottom = mii->Rect.top + 1;
     }
     return res;
   }
@@ -975,14 +1013,14 @@
   {
     GetTextExtentPoint32W(hDC, str, mii->cch, &sz);
     /* FIXME calculate the size of the menu item */
-    mir->right = mir->left + sz.cx + 6;
-    mir->bottom = mir->top + max(sz.cy, GetSystemMetrics(SM_CYMENU));
+    mii->Rect.right = mii->Rect.left + sz.cx + 6;
+    mii->Rect.bottom = mii->Rect.top + max(sz.cy, GetSystemMetrics(SM_CYMENU));
     return TRUE;
   }
 }
 
 static BOOL
-DrawMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str)
+DrawMenuItem(HWND hWnd, HMENU mnu, HDC hDC, PROSMENUITEMINFO mii, LPWSTR str)
 {
   BOOL res = FALSE;
   DRAWITEMSTRUCT dis;
@@ -997,7 +1035,7 @@
     dis.itemState = 0; /* FIXME */
     dis.hwndItem = (HWND)mnu;
     dis.hDC = hDC;
-    RtlCopyMemory(&dis.rcItem, mir, sizeof(RECT));
+    RtlCopyMemory(&dis.rcItem, &mii->Rect, sizeof(RECT));
     dis.itemData = mii->dwItemData;
     res = (BOOL)SendMessageW(hWnd, WM_DRAWITEM, 0, (LPARAM)&dis);
     return res;
@@ -1007,7 +1045,7 @@
     /* FIXME draw the menu item */
     SetTextColor(hDC, COLOR_MENUTEXT);
     SetBkMode(hDC, TRANSPARENT);
-    DrawTextW(hDC, str, mii->cch, mir, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
+    DrawTextW(hDC, str, mii->cch, &mii->Rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
   }
   return res;
 }
@@ -1392,10 +1430,10 @@
   HANDLE hHeap;
   PVOID Buf, hBuf;
   DWORD BufSize, Items, Items2, hItems;
-  MENUITEMINFOW *mii;
+  ROSMENUITEMINFO *mii;
   PVOID milist, *mih;
   SETMENUITEMRECT smir;
-  RECT *omir, *mir = NULL;
+  RECT omir;
   LPWSTR str;
 
   mnu = GetMenu(hWnd); /* Fixme - pass menu handle as parameter */
@@ -1421,50 +1459,46 @@
       hItems = 0;
       while(Items > 0)
       {
-        omir = mir;
-        mii = (LPMENUITEMINFOW)Buf;
-        Buf += sizeof(MENUITEMINFOW);
-        mir = (LPRECT)Buf;
-        Buf += sizeof(RECT);
+        mii = (PROSMENUITEMINFO)Buf;
+        Buf += sizeof(ROSMENUITEMINFO);
         if(mii->cch)
         {
-          str = (LPWSTR)Buf;
-          Buf += (mii->cch + 1) * sizeof(WCHAR);
+          str = (LPWSTR)mii->dwTypeData;
         }
         else
           str = NULL;
-        if(omir)
+        if(Items != Items2)
         {
-          mir->left = omir->right + 1;
-          mir->top = omir->top;
-          mir->right += mir->left;
-          mir->bottom += mir->top;
+          mii->Rect.left = omir.right + 1;
+          mii->Rect.top = omir.top;
+          mii->Rect.right += mii->Rect.left;
+          mii->Rect.bottom += mii->Rect.top;
         }
         else
         {
-          mir->left = Rect->left;
-          mir->top = Rect->top;
+          mii->Rect.left = Rect->left;
+          mii->Rect.top = Rect->top;
         }
         if((mii->fType & MFT_RIGHTJUSTIFY) && !milist)
         {
-          milist = mih = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Items * sizeof(MENUITEMINFOW*));
+          milist = mih = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Items * sizeof(PROSMENUITEMINFO));
           hItems = Items;
         }
         
-        MeasureMenuItem(hWnd, mnu, hDC, mii, mir, str);
+        MeasureMenuItem(hWnd, mnu, hDC, mii, str);
         
-        if((mir->right > Rect->right) || (mii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK)))
+        if((mii->Rect.right > Rect->right) || (mii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK)))
         {
-          mir->right -= (mir->left - Rect->left);
-          mir->left = Rect->left;
-          mir->top += line;
-          mir->bottom += line;
-          line = mir->bottom - mir->top;
+          mii->Rect.right -= (mii->Rect.left - Rect->left);
+          mii->Rect.left = Rect->left;
+          mii->Rect.top += line;
+          mii->Rect.bottom += line;
+          line = mii->Rect.bottom - mii->Rect.top;
         }
         
         if(!milist)
         {
-          smir.rcRect = *mir;
+          smir.rcRect = mii->Rect;
           NtUserSetMenuItemRect(mnu, &smir);
           smir.uItem++;
         }
@@ -1473,8 +1507,9 @@
           *(mih++) = mii;
         }
         
-        bottom = max(bottom, mir->bottom);
-        line = max(line, mir->bottom - mir->top);
+        bottom = max(bottom, mii->Rect.bottom);
+        line = max(line, mii->Rect.bottom - mii->Rect.top);
+        omir = mii->Rect;
         Items--;
       }
       /* align help menus to the right */
@@ -1487,23 +1522,22 @@
         while(hItems > 0)
         {
           LONG wd;
-          MENUITEMINFOW *omii;
+          PROSMENUITEMINFO omii;
           
           omii = mii;
-          mii = (MENUITEMINFOW*)(*(--mih));
-          mir = (LPRECT)(mii + 1);
+          mii = (PROSMENUITEMINFO)(*(--mih));
           
-          if(omii && ((mir->right > x) || (omii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK))))
+          if(omii && ((mii->Rect.right > x) || (omii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK))))
           {
             x = Rect->right;
           }
           
-          wd = (mir->right - mir->left);
-          mir->right = x - 1;
-          mir->left = mir->right - wd;
-          x = mir->left;
+          wd = (mii->Rect.right - mii->Rect.left);
+          mii->Rect.right = x - 1;
+          mii->Rect.left = mii->Rect.right - wd;
+          x = mii->Rect.left;
           
-          smir.rcRect = *mir;
+          smir.rcRect = mii->Rect;
           
           NtUserSetMenuItemRect(mnu, &smir);
           smir.uItem--;
@@ -1528,19 +1562,16 @@
       /* draw menu items */
       while (Items2 > 0)
       {
-        mii = (LPMENUITEMINFOW)Buf;
-        Buf += sizeof(MENUITEMINFOW);
-        mir = (LPRECT)Buf;
-        Buf += sizeof(RECT);
+        mii = (PROSMENUITEMINFO)Buf;
+        Buf += sizeof(ROSMENUITEMINFO);
         if(mii->cch)
         {
-          str = (LPWSTR)Buf;
-          Buf += (mii->cch + 1) * sizeof(WCHAR);
+          str = (LPWSTR)mii->dwTypeData;
         }
         else
           str = NULL;
         /* DbgPrint("Draw menu item %ws at (%d, %d, %d, %d)\n", str, mir->left, mir->top, mir->right, mir->bottom); */
-        DrawMenuItem(hWnd, mnu, hDC, mii, mir, str);
+        DrawMenuItem(hWnd, mnu, hDC, mii, str);
         Items2--;
       }
     }
@@ -1908,6 +1939,7 @@
 {
   RECT Rect;
   ROSMENUITEMINFO ItemInfo;
+  ROSMENUINFO SubMenuInfo;
   HDC Dc;
   HMENU Ret;
 
@@ -2012,9 +2044,9 @@
 
   MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem,
                 Rect.left, Rect.top, Rect.right, Rect.bottom );
-  if (SelectFirst)
+  if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
     {
-      MenuMoveSelection(WndOwner, ItemInfo.hSubMenu, ITEM_NEXT);
+      MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
     }
 
   Ret = ItemInfo.hSubMenu;
@@ -2376,6 +2408,545 @@
   return TRUE;
 }
 
+/******************************************************************************
+ *
+ *   UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
+ */
+static UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
+{
+  UINT i;
+  PROSMENUITEMINFO MenuItems;
+
+  i = MenuInfo->FocusedItem;
+  if (NO_SELECTED_ITEM == i)
+    {
+      return i;
+    }
+
+  if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
+    {
+      return NO_SELECTED_ITEM;
+    }
+
+  for (i++ ; i < MenuInfo->MenuItemCount; i++)
+    {
+      if (0 != (MenuItems[i].fType & MF_MENUBARBREAK))
+        {
+          return i;
+        }
+    }
+
+  return NO_SELECTED_ITEM;
+}
+
+/******************************************************************************
+ *
+ *   UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
+ */
+static UINT FASTCALL
+MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
+{
+  UINT i;
+  PROSMENUITEMINFO MenuItems;
+
+  if (0 == MenuInfo->FocusedItem || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
+    {
+      return NO_SELECTED_ITEM;
+    }
+
+  if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
+    {
+      return NO_SELECTED_ITEM;
+    }
+
+  /* Find the start of the column */
+
+  for (i = MenuInfo->FocusedItem;
+       0 != i && 0 == (MenuItems[i].fType & MF_MENUBARBREAK);
+       --i)
+    {
+      ; /* empty */
+    }
+
+  if (0 == i)
+    {
+      MenuCleanupAllRosMenuItemInfo(MenuItems);
+      return NO_SELECTED_ITEM;
+    }
+
+  for (--i; 0 != i; --i)
+    {
+      if (MenuItems[i].fType & MF_MENUBARBREAK)
+        {
+          break;
+        }
+    }
+
+  MenuCleanupAllRosMenuItemInfo(MenuItems);
+  DPRINT("ret %d.\n", i );
+
+  return i;
+}
+
+/***********************************************************************
+ *           MenuGetSubPopup
+ *
+ * Return the handle of the selected sub-popup menu (if any).
+ */
+static HMENU FASTCALL
+MenuGetSubPopup(HMENU Menu)
+{
+  ROSMENUINFO MenuInfo;
+  ROSMENUITEMINFO ItemInfo;
+
+  if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
+      || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
+    {
+      return NULL;
+    }
+
+  MenuInitRosMenuItemInfo(&ItemInfo);
+  if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
+    {
+      MenuCleanupRosMenuItemInfo(&ItemInfo);
+      return NULL;
+    }
+  if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
+    {
+      MenuCleanupRosMenuItemInfo(&ItemInfo);
+      return ItemInfo.hSubMenu;
+    }
+
+  MenuCleanupRosMenuItemInfo(&ItemInfo);
+  return NULL;
+}
+
+/***********************************************************************
+ *           MenuDoNextMenu
+ *
+ * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
+ */
+static LRESULT FASTCALL
+MenuDoNextMenu(MTRACKER* Mt, UINT Vk)
+{
+#if 0 /* FIXME */
+  ROSMENUINFO TopMenuInfo;
+  ROSMENUINFO MenuInfo;
+
+  if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
+    {
+      return (LRESULT) FALSE;
+    }
+
+  if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
+      || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
+    {
+      MDINEXTMENU NextMenu;
+      HMENU NewMenu;
+      HWND NewWnd;
+      UINT Id = 0;
+
+      NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
+      NextMenu.hmenuNext = NULL;
+      NextMenu.hwndNext = NULL;
+      SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
+
+      DPRINT("%p [%p] -> %p [%p]\n",
+             Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
+
+      if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
+        {
+          DWORD Style = GetWindowLongW(Mt->OwnerWnd, GWL_STYLE);
+          NewWnd = Mt->OwnerWnd;
+          if (IS_SYSTEM_MENU(&TopMenuInfo))
+            {
+              /* switch to the menu bar */
+
+              if (0 != (Style & WS_CHILD)
+                  || NULL == (NewMenu = GetMenu(NewWnd)))
+                {
+                  return FALSE;
+                }
+
+              if (VK_LEFT == Vk)
+                {
+                  if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
+                    {
+                      return FALSE;
+                    }
+                  Id = MenuInfo.MenuItemCount - 1;
+                }
+            }
+          else if (0 != (Style & WS_SYSMENU))
+            {
+#if 0 /* FIXME */
+              /* switch to the system menu */
+              NewMenu = get_win_sys_menu(NewWnd);
+#endif
+            }
+          else
+            {
+              return FALSE;
+            }
+        }
+      else    /* application returned a new menu to switch to */
+        {
+          NewMenu = NextMenu.hmenuNext;
+          NewWnd = NextMenu.hwndNext;
+
+          if (IsMenu(NewMenu) && IsWindow(NewWnd))
+            {
+              DWORD Style = GetWindowLongW(NewWnd, GWL_STYLE);
+
+#if 0 /* FIXME */
+              if (0 != (Style & WS_SYSMENU)
+                  && GetSubMenu(get_win_sys_menu(NewWnd), 0) == NewMenu)
+                {
+                  /* get the real system menu */
+                  NewMenu =  get_win_sys_menu(NewWnd);
+                }
+              else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
+#else
+              if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
+#endif
+                {
+                  /* FIXME: Not sure what to do here;
+                   * perhaps try to track NewMenu as a popup? */
+
+                  DPRINT(" -- got confused.\n");
+                  return FALSE;
+                }
+            }
+          else
+            {
+              return FALSE;
+            }
+        }
+
+      if (NewMenu != Mt->TopMenu)
+        {
+          MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
+                         FALSE, 0 );
+          if (Mt->CurrentMenu != Mt->TopMenu)
+            {
+              MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
+            }
+        }
+
+      if (NewWnd != Mt->OwnerWnd)
+        {
+          Mt->OwnerWnd = NewWnd;
+          SetCapture(Mt->OwnerWnd);
+        }
+
+      Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
+      if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
+        {
+          MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
+        }
+
+      return TRUE;
+    }
+#endif
+
+  return FALSE;
+}
+
+/***********************************************************************
+ *           MenuSuspendPopup
+ *
+ * The idea is not to show the popup if the next input message is
+ * going to hide it anyway.
+ */
+static BOOL FASTCALL
+MenuSuspendPopup(MTRACKER* Mt, UINT Message)
+{
+  MSG Msg;
+
+  Msg.hwnd = Mt->OwnerWnd;
+
+  PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
+  Mt->TrackFlags |= TF_SKIPREMOVE;
+
+  switch (Message)
+    {
+      case WM_KEYDOWN:
+        PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
+        if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
+          {
+            PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
+            PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
+            if (WM_KEYDOWN == Msg.message
+                && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
+              {
+                Mt->TrackFlags |= TF_SUSPENDPOPUP;
+                return TRUE;
+              }
+          }
+        break;
+    }
+
+  /* failures go through this */
+  Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
+
+  return FALSE;
+}
+
+/***********************************************************************
+ *           MenuKeyEscape
+ *
+ * Handle a VK_ESCAPE key event in a menu.
+ */
+static BOOL FASTCALL
+MenuKeyEscape(MTRACKER *Mt, UINT Flags)
+{
+  BOOL EndMenu = TRUE;
+  ROSMENUINFO MenuInfo;
+  HMENU MenuTmp, MenuPrev;
+
+  if (Mt->CurrentMenu != Mt->TopMenu)
+    {
+      if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
+          && 0 != (MenuInfo.Flags & MF_POPUP))
+        {
+          MenuPrev = MenuTmp = Mt->TopMenu;
+
+          /* close topmost popup */
+          while (MenuTmp != Mt->CurrentMenu)
+            {
+              MenuPrev = MenuTmp;
+              MenuTmp = MenuGetSubPopup(MenuPrev);
+            }
+
+          if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
+            {
+              MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE);
+            }
+          Mt->CurrentMenu = MenuPrev;
+          EndMenu = FALSE;
+        }
+    }
+
+  return EndMenu;
+}
+
+/***********************************************************************
+ *           MenuKeyLeft
+ *
+ * Handle a VK_LEFT key event in a menu.
+ */
+static void FASTCALL
+MenuKeyLeft(MTRACKER* Mt, UINT Flags)
+{
+  ROSMENUINFO MenuInfo;
+  ROSMENUINFO TopMenuInfo;
+  ROSMENUINFO PrevMenuInfo;
+  HMENU MenuTmp, MenuPrev;
+  UINT PrevCol;
+
+  MenuPrev = MenuTmp = Mt->TopMenu;
+
+  if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
+    {
+      return;
+    }
+
+  /* Try to move 1 column left (if possible) */
+  if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
+    {
+      if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
+        {
+          MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
+        }
+      return;
+    }
+
+  /* close topmost popup */
+  while (MenuTmp != Mt->CurrentMenu)
+    {
+      MenuPrev = MenuTmp;
+      MenuTmp = MenuGetSubPopup(MenuPrev);
+    }
+
+  if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
+    {
+      return;
+    }
+  MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE);
+  Mt->CurrentMenu = MenuPrev;
+
+  if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
+    {
+      return;
+    }
+  if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
+    {
+      /* move menu bar selection if no more popups are left */
+
+      if (! MenuDoNextMenu(Mt, VK_LEFT))
+        {
+          MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
+        }
+
+      if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
+        {
+          /* A sublevel menu was displayed - display the next one
+           * unless there is another displacement coming up */
+
+          if (! MenuSuspendPopup(Mt, WM_KEYDOWN))
+            {
+              Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
+                                                 TRUE, Flags);
+            }
+        }
+    }
+}
+
+/***********************************************************************
+ *           MenuKeyRight
+ *
+ * Handle a VK_RIGHT key event in a menu.
+ */
+static void FASTCALL
+MenuKeyRight(MTRACKER *Mt, UINT Flags)
+{
+  HMENU MenuTmp;
+  ROSMENUINFO MenuInfo;
+  ROSMENUINFO CurrentMenuInfo;
+  UINT NextCol;
+
+  DPRINT("MenuKeyRight called, cur %p, top %p.\n",
+         Mt->CurrentMenu, Mt->TopMenu);
+
+  if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
+    {
+      return;
+    }
+  if (0 != (MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
+    {
+      /* If already displaying a popup, try to display sub-popup */
+
+      MenuTmp = Mt->CurrentMenu;
+      if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
+        {
+          Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
+        }
+
+      /* if subpopup was displayed then we are done */
+      if (MenuTmp != Mt->CurrentMenu)
+        {
+          return;
+        }
+    }
+
+  if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
+    {
+      return;
+    }
+
+  /* Check to see if there's another column */
+  if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
+    {
+      DPRINT("Going to %d.\n", NextCol);
+      if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
+        {
+          MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
+        }
+      return;
+    }
+
+  if (0 == (MenuInfo.Flags & MF_POPUP))	/* menu bar tracking */
+    {
+      if (Mt->CurrentMenu != Mt->TopMenu)
+        {
+          MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE );
+          MenuTmp = Mt->CurrentMenu = Mt->TopMenu;
+        }
+      else
+        {
+          MenuTmp = NULL;
+        }
+
+      /* try to move to the next item */
+      if (! MenuDoNextMenu(Mt, VK_RIGHT))
+        {
+          MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
+        }
+
+      if (NULL != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
+        {
+          if (! MenuSuspendPopup(Mt, WM_KEYDOWN))
+            {
+              Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
+                                                 TRUE, Flags);
+            }
+        }
+    }
+}
+
+/***********************************************************************
+ *           MenuFindItemByKey
+ *
+ * Find the menu item selected by a key press.
+ * Return item id, -1 if none, -2 if we should close the menu.
+ */
+static UINT FASTCALL
+MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
+                  WCHAR Key, BOOL ForceMenuChar)
+{
+  PROSMENUITEMINFO Items, ItemInfo;
+  LRESULT MenuChar;
+  UINT i;
+
+  DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
+
+#if 0 /* FIXME */
+  if (! IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
+#endif
+
+  if (NULL != MenuInfo)
+    {
+      if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
+        {
+          return -1;
+        }
+      if (! ForceMenuChar)
+        {
+          Key = towupper(Key);
+          ItemInfo = Items;
+          for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
+            {
+              if (IS_STRING_ITEM(ItemInfo->fType) && NULL != ItemInfo->dwTypeData)
+                {
+                  WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
+                  do
+                    {
+                      p = wcschr(p + 2, '&');
+		    }
+                  while (NULL != p && L'&' == p[1]);
+                  if (NULL != p && (towupper(p[1]) == Key))
+                    {
+                      return i;
+                    }
+                }
+            }
+        }
+
+      MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
+                              MAKEWPARAM(Key, MenuInfo->Flags), (LPARAM) MenuInfo->Self);
+      if (2 == HIWORD(MenuChar))
+        {
+          return LOWORD(MenuChar);
+        }
+      if (1 == HIWORD(MenuChar))
+        {
+          return (UINT) (-2);
+        }
+    }
+
+  return (UINT)(-1);
+}
+
 /***********************************************************************
  *           MenuTrackMenu
  *
@@ -2387,6 +2958,7 @@
 {
   MSG Msg;
   ROSMENUINFO MenuInfo;
+  ROSMENUITEMINFO ItemInfo;
   BOOL fRemove;
   INT ExecutedMenuId = -1;
   MTRACKER Mt;
@@ -2537,106 +3109,148 @@
 	}
       else if (WM_KEYFIRST <= Msg.message && Msg.message <= WM_KEYLAST)
 	{
-#if 0 /* FIXME */
-            fRemove = TRUE;  /* Keyboard messages are always removed */
-	    switch(msg.message)
-	    {
-	    case WM_KEYDOWN:
-		switch(msg.wParam)
-		{
-		case VK_HOME:
-		case VK_END:
-		    MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
-				     NO_SELECTED_ITEM, FALSE, 0 );
-		/* fall through */
-		case VK_UP:
-		    MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
-				       (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
-		    break;
-
-		case VK_DOWN: /* If on menu bar, pull-down the menu */
-
-		    menu = MENU_GetMenu( mt.hCurrentMenu );
-		    if (!(menu->wFlags & MF_POPUP))
-			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
-		    else      /* otherwise try to move selection */
-			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
-		    break;
-
-		case VK_LEFT:
-		    MENU_KeyLeft( &mt, wFlags );
-		    break;
-
-		case VK_RIGHT:
-		    MENU_KeyRight( &mt, wFlags );
-		    break;
-
-		case VK_ESCAPE:
-                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
-		    break;
-
-		case VK_F1:
-		    {
-			HELPINFO hi;
-			hi.cbSize = sizeof(HELPINFO);
-			hi.iContextType = HELPINFO_MENUITEM;
-			if (menu->FocusedItem == NO_SELECTED_ITEM)
-			    hi.iCtrlId = 0;
-		        else
-			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
-			hi.hItemHandle = hmenu;
-			hi.dwContextId = menu->dwContextHelpID;
-			hi.MousePos = msg.pt;
-			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
-			break;
-		    }
+          fRemove = TRUE;  /* Keyboard messages are always removed */
+          switch(Msg.message)
+            {
+              case WM_KEYDOWN:
+                switch(Msg.wParam)
+                  {
+                    case VK_HOME:
+                    case VK_END:
+                      if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
+                        {
+                          MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
+                                         FALSE, 0 );
+                        }
+                      /* fall through */
+
+                    case VK_UP:
+                      if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
+                        {
+                          MenuMoveSelection(Mt.OwnerWnd, &MenuInfo,
+                                            VK_HOME == Msg.wParam ? ITEM_NEXT : ITEM_PREV);
+                        }
+                      break;
+
+                    case VK_DOWN: /* If on menu bar, pull-down the menu */
+                      if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
+                        {
+                          if (0 == (MenuInfo.Flags & MF_POPUP))
+                            {
+                              if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
+                                {
+                                  Mt.CurrentMenu = MenuShowSubPopup(Mt.OwnerWnd, &MenuInfo,
+                                                                    TRUE, Flags);
+                                }
+                            }
+                          else      /* otherwise try to move selection */
+                            {
+                              MenuMoveSelection(Mt.OwnerWnd, &MenuInfo, ITEM_NEXT);
+                            }
+                        }
+                      break;
+
+                    case VK_LEFT:
+                      MenuKeyLeft(&Mt, Flags);
+                      break;
+
+                    case VK_RIGHT:
+                      MenuKeyRight(&Mt, Flags);
+                      break;
+
+                    case VK_ESCAPE:
+                      fEndMenu = MenuKeyEscape(&Mt, Flags);
+                      break;
+
+                    case VK_F1:
+                      {
+                        HELPINFO hi;
+                        hi.cbSize = sizeof(HELPINFO);
+                        hi.iContextType = HELPINFO_MENUITEM;
+                        if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
+                          {
+                            if (NO_SELECTED_ITEM == MenuInfo.FocusedItem)
+                              {
+                                hi.iCtrlId = 0;
+                              }
+                            else
+                              {
+                                MenuInitRosMenuItemInfo(&ItemInfo);
+                                if (MenuGetRosMenuItemInfo(MenuInfo.Self,
+                                                           MenuInfo.FocusedItem,
+                                                           &ItemInfo))
+                                  {
+                                    hi.iCtrlId = ItemInfo.wID;
+                                  }
+                                else
+                                  {
+                                    hi.iCtrlId = 0;
+                                  }
+                                MenuCleanupRosMenuItemInfo(&ItemInfo);
+                              }
+                          }
+                        hi.hItemHandle = Menu;
+			hi.dwContextId = MenuInfo.dwContextHelpID;
[truncated at 1000 lines; 152 more skipped]

reactos/subsys/win32k/ntuser
menu.c 1.46 -> 1.47
diff -u -r1.46 -r1.47
--- menu.c	19 Feb 2004 21:12:09 -0000	1.46
+++ menu.c	22 Feb 2004 18:04:52 -0000	1.47
@@ -16,7 +16,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: menu.c,v 1.46 2004/02/19 21:12:09 weiden Exp $
+/* $Id: menu.c,v 1.47 2004/02/22 18:04:52 gvg Exp $
  *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
@@ -888,58 +888,96 @@
 {
   DWORD res = 0;
   UINT sz;
-  MENUITEMINFOW mii;
+  ROSMENUITEMINFO mii;
   PVOID Buf;
   PMENU_ITEM CurItem = MenuObject->MenuItemList;
-  if(nMax)
-  {
-    sz = sizeof(MENUITEMINFOW) + sizeof(RECT);
-    Buf = Buffer;
-    mii.cbSize = sz;
-    mii.fMask = 0;
-    while(CurItem && (nMax >= sz))
-    {
-      mii.cch = CurItem->Text.Length / sizeof(WCHAR);
-      mii.dwItemData = CurItem->dwItemData;
-      if(CurItem->Text.Length)
-        mii.dwTypeData = NULL;
-      else
-        mii.dwTypeData = (LPWSTR)CurItem->Text.Buffer;
-      mii.fState = CurItem->fState;
-      mii.fType = CurItem->fType;
-      mii.hbmpChecked = CurItem->hbmpChecked;
-      mii.hbmpItem = CurItem->hbmpItem;
-      mii.hbmpUnchecked = CurItem->hbmpUnchecked;
-      mii.hSubMenu = CurItem->hSubMenu;
-      RtlCopyMemory(Buf, &mii, sizeof(MENUITEMINFOW));
-      Buf += sizeof(MENUITEMINFOW);
-      RtlCopyMemory(Buf, &CurItem->Rect, sizeof(RECT));
-      Buf += sizeof(RECT);      
-      nMax -= sz;
+  PWCHAR StrOut;
+  NTSTATUS Status;
+  WCHAR NulByte;
+
+  if (0 != nMax)
+    {
+      if (nMax < MenuObject->MenuInfo.MenuItemCount * sizeof(ROSMENUITEMINFO))
+        {
+          return 0;
+        }
+      StrOut = (PWCHAR)((char *) Buffer + MenuObject->MenuInfo.MenuItemCount
+                                          * sizeof(ROSMENUITEMINFO));
+      nMax -= MenuObject->MenuInfo.MenuItemCount * sizeof(ROSMENUITEMINFO);
+      sz = sizeof(ROSMENUITEMINFO);
+      Buf = Buffer;
+      mii.cbSize = sizeof(ROSMENUITEMINFO);
+      mii.fMask = 0;
+      NulByte = L'\0';
+
+      while (NULL != CurItem)
+        {
+          mii.cch = CurItem->Text.Length / sizeof(WCHAR);
+          mii.dwItemData = CurItem->dwItemData;
+          if (0 != CurItem->Text.Length)
+            {
+              mii.dwTypeData = StrOut;
+            }
+          else
+            {
+              mii.dwTypeData = NULL;
+            }
+          mii.fState = CurItem->fState;
+          mii.fType = CurItem->fType;
+          mii.hbmpChecked = CurItem->hbmpChecked;
+          mii.hbmpItem = CurItem->hbmpItem;
+          mii.hbmpUnchecked = CurItem->hbmpUnchecked;
+          mii.hSubMenu = CurItem->hSubMenu;
+          mii.Rect = CurItem->Rect;
+          mii.XTab = CurItem->XTab;
+
+          Status = MmCopyToCaller(Buf, &mii, sizeof(ROSMENUITEMINFO));
+          if (! NT_SUCCESS(Status))
+            {
+              SetLastNtError(Status);
+              return 0;
+            }
+          Buf += sizeof(ROSMENUITEMINFO);
       
-      if(CurItem->Text.Length && (nMax >= CurItem->Text.Length + sizeof(WCHAR)))
-      {
-        /* copy string */
-        RtlCopyMemory(Buf, (LPWSTR)CurItem->Text.Buffer, CurItem->Text.Length);
-        Buf += CurItem->Text.Length + sizeof(WCHAR);
-        nMax -= CurItem->Text.Length + sizeof(WCHAR);
-      }
-      else
-        break;
+          if (0 != CurItem->Text.Length
+              && (nMax >= CurItem->Text.Length + sizeof(WCHAR)))
+            {
+              /* copy string */
+              Status = MmCopyToCaller(StrOut, CurItem->Text.Buffer,
+                                      CurItem->Text.Length);
+              if (! NT_SUCCESS(Status))
+                {
+                  SetLastNtError(Status);
+                  return 0;
+                }
+              StrOut += CurItem->Text.Length / sizeof(WCHAR);
+              Status = MmCopyToCaller(StrOut, &NulByte, sizeof(WCHAR));
+              if (! NT_SUCCESS(Status))
+                {
+                  SetLastNtError(Status);
+                  return 0;
+                }
+              StrOut++;
+              nMax -= CurItem->Text.Length + sizeof(WCHAR);
+            }
+          else if (0 != CurItem->Text.Length)
+            {
+              break;
+            }
 
-      CurItem = CurItem->Next;
-      res++;
+          CurItem = CurItem->Next;
+          res++;
+        }
     }
-  }
   else
-  {
-    while(CurItem)
     {
-      res += sizeof(MENUITEMINFOW) + sizeof(RECT);
-      res += CurItem->Text.Length + sizeof(WCHAR);
-      CurItem = CurItem->Next;
+      while (NULL != CurItem)
+        {
+          res += sizeof(ROSMENUITEMINFO) + CurItem->Text.Length + sizeof(WCHAR);
+          CurItem = CurItem->Next;
+        }
     }
-  }
+
   return res;
 }
 

reactos
ChangeLog 1.216 -> 1.217
diff -u -r1.216 -r1.217
--- ChangeLog	22 Feb 2004 17:30:32 -0000	1.216
+++ ChangeLog	22 Feb 2004 18:04:52 -0000	1.217
@@ -1,3 +1,7 @@
+2004-02-22  Ge van Geldorp <ge@gse.nl>
+
+	* Implement keyboard navigation for menus
+
 2004-02-22  Casper S. Hornstrup  <chorns@users.sourceforge.net>
 
 	* Makefile (all): Put REGTESTS dependency earlier in the list.
CVSspam 0.2.8