
user: menu item client changes
Implement menu item abstraction functions and begin converting menu item
references to use these functions

Thomas Kho

---

 dlls/user/menu.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 212 insertions(+), 31 deletions(-)

diff --git a/dlls/user/menu.c b/dlls/user/menu.c
index c688931..557d2af 100644
--- a/dlls/user/menu.c
+++ b/dlls/user/menu.c
@@ -374,6 +374,128 @@ static NTSTATUS MENU_UpdateMenu(HMENU hM
 }
 
 /***********************************************************************
+ *           MENU_GetAllMenuItems
+ *
+ * Get a local copy of all menu items for given hMenu
+ * TODO: slow, implement reference counting
+ */
+static NTSTATUS MENU_GetAllMenuItems(HMENU hMenu, MENUITEM **item)
+{
+    int i;
+    unsigned int allocsize;
+    char *tmp;
+    POPUPMENU menu;
+    NTSTATUS ret;
+    if ((ret = MENU_GetMenu(hMenu, &menu)))
+        return ret;
+    if (menu.nItems == 0)
+    {
+        *item = NULL;
+        return 0;
+    }
+    allocsize = sizeof(MENUITEM) * menu.nItems;
+    for (i = 0; i < menu.nItems; i++)
+        if (menu.items[i].text)
+            allocsize += sizeof(WCHAR)*(strlenW(menu.items[i].text)+1);
+    if (!(*item = HeapAlloc(GetProcessHeap(), 0, allocsize)))
+        return STATUS_NO_MEMORY;
+    memcpy(*item, menu.items, sizeof(MENUITEM) * menu.nItems);
+
+    tmp = (char *) *item + menu.nItems * sizeof(MENUITEM);
+    for (i = 0; i < menu.nItems; i++)
+    {
+        if (menu.items[i].text)
+        {
+            memcpy(tmp, menu.items[i].text,
+                   sizeof(WCHAR)*(strlenW(menu.items[i].text)+1));
+            (*item)[i].text = (WCHAR *) tmp;
+            tmp += sizeof(WCHAR)*(strlenW(menu.items[i].text)+1);
+        }
+    }
+    return 0;
+}
+
+/***********************************************************************
+ *           MENU_GetMenuItem
+ *
+ * Get a local copy of menu item for given hMenu
+ */
+static NTSTATUS MENU_GetMenuItem(HMENU hMenu, unsigned int pos, MENUITEM **item)
+{
+    unsigned int allocsize;
+    POPUPMENU menu;
+    NTSTATUS ret;
+    if ((ret = MENU_GetMenu(hMenu, &menu)))
+        return ret;
+    if (pos >= menu.nItems)
+    {
+        *item = NULL;
+        return 0;
+    }
+    allocsize = sizeof(MENUITEM);
+    if (menu.items[pos].text)
+        allocsize += sizeof(WCHAR)*(strlenW(menu.items[pos].text)+1);
+    if (!(*item = HeapAlloc(GetProcessHeap(), 0, allocsize)))
+        return STATUS_NO_MEMORY;
+    memcpy(*item, &menu.items[pos], sizeof(MENUITEM));
+    if (menu.items[pos].text)
+    {
+        char *dest = (char *) *item + sizeof(MENUITEM);
+        memcpy(dest, menu.items[pos].text,
+               sizeof(WCHAR)*(strlenW(menu.items[pos].text)+1));
+        (*item)->text = (WCHAR *) dest;
+    }
+    return 0;
+}
+
+/***********************************************************************
+ *           MENU_UpdateMenuItem
+ *
+ * Get a local copy of menu item for given hMenu
+ */
+static NTSTATUS MENU_UpdateMenuItem(HMENU hMenu, unsigned int pos,
+                                    MENUITEM *item)
+{
+    POPUPMENU menu;
+    NTSTATUS ret;
+    LPWSTR oldtext;
+    if ((ret = MENU_GetMenu(hMenu, &menu)))
+        return ret;
+    oldtext = menu.items[pos].text;
+    memcpy(&menu.items[pos], item, sizeof(MENUITEM));
+    if (!item->text)
+    {
+        if (oldtext)
+            HeapFree(GetProcessHeap(), 0, oldtext);
+        return 0;
+    }
+    if (oldtext && !strcmpW(oldtext, item->text))
+    {
+        menu.items[pos].text = oldtext;
+        return 0;
+    }
+    if (oldtext)
+        HeapFree(GetProcessHeap(), 0, oldtext);
+    menu.items[pos].text = HeapAlloc(GetProcessHeap(), 0,
+                                     sizeof(WCHAR)*(strlenW(item->text)+1));
+    if (!menu.items[pos].text)
+        return STATUS_NO_MEMORY;
+    memcpy(menu.items[pos].text, item->text,
+           sizeof(WCHAR)*(strlenW(item->text)+1));
+    return 0;
+}
+
+/***********************************************************************
+ *           MENU_ReleaseMenuItem
+ *
+ * Get a local copy of menu item for given hMenu
+ */
+static NTSTATUS MENU_ReleaseMenuItem(MENUITEM *item)
+{
+    return HeapFree(GetProcessHeap(), 0, item);
+}
+
+/***********************************************************************
  *           get_win_sys_menu
  *
  * Get the system menu of a window
@@ -536,15 +658,18 @@ HMENU MENU_GetSysMenu( HWND hWnd, HMENU 
 
 	if (hPopupMenu)
 	{
+            MENUITEM *item;
             if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
                 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
 
 	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
                          (UINT_PTR)hPopupMenu, NULL );
 
-            MENU_GetMenu(hMenu, menu);
-            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
-            menu->items[0].fState = 0;
+            MENU_GetMenuItem(hMenu, 0, &item);
+            item->fType = MF_SYSMENU | MF_POPUP;
+            item->fState = 0;
+            MENU_UpdateMenuItem(hMenu, 0, item);
+            MENU_ReleaseMenuItem(item);
             if (!MENU_GetMenu(hPopupMenu, menu))
             {
                 menu->wFlags |= MF_SYSMENU;
@@ -601,6 +726,7 @@ static UINT  MENU_GetStartOfNextColumn(
     POPUPMENU tmpmenu;
     POPUPMENU *menu = &tmpmenu;
     UINT i;
+    MENUITEM *items;
 
     if(MENU_GetMenu(hMenu, menu))
 	return NO_SELECTED_ITEM;
@@ -609,11 +735,16 @@ static UINT  MENU_GetStartOfNextColumn(
     if( i == NO_SELECTED_ITEM )
 	return i;
 
+    MENU_GetAllMenuItems(hMenu, &items);
     for( ; i < menu->nItems; ++i ) {
-	if (menu->items[i].fType & MF_MENUBARBREAK)
+	if (items[i].fType & MF_MENUBARBREAK)
+        {
+            MENU_ReleaseMenuItem(items);
 	    return i;
+        }
     }
 
+    MENU_ReleaseMenuItem(items);
     return NO_SELECTED_ITEM;
 }
 
@@ -631,6 +762,7 @@ static UINT  MENU_GetStartOfPrevColumn(
     POPUPMENU tmpmenu;
     POPUPMENU *menu = &tmpmenu;
     UINT  i;
+    MENUITEM *items;
 
     if( MENU_GetMenu(hMenu, menu) )
 	return NO_SELECTED_ITEM;
@@ -641,17 +773,22 @@ static UINT  MENU_GetStartOfPrevColumn(
 
     /* Find the start of the column */
 
+    MENU_GetAllMenuItems(hMenu, &items);
     for(i = MENU_GetLocalMenu(hMenu)->FocusedItem; i != 0 &&
-        !(menu->items[i].fType & MF_MENUBARBREAK);
+        !(items[i].fType & MF_MENUBARBREAK);
         --i) ; /* empty */
 
     if(i == 0)
+    {
+        MENU_ReleaseMenuItem(items);
 	return NO_SELECTED_ITEM;
+    }
 
     for(--i; i != 0; --i) {
-	if (menu->items[i].fType & MF_MENUBARBREAK)
+	if (items[i].fType & MF_MENUBARBREAK)
 	    break;
     }
+    MENU_ReleaseMenuItem(items);
 
     TRACE("ret %d.\n", i );
 
@@ -683,7 +820,10 @@ static UINT MENU_FindItem( HMENU *hmenu,
     }
     else
     {
-        MENUITEM *item = menu->items;
+        MENUITEM *items;
+        MENUITEM *item;
+        MENU_GetAllMenuItems(*hmenu, &items);
+        item = items;
 	for (i = 0; i < menu->nItems; i++, item++)
 	{
 	    if (item->fType & MF_POPUP)
@@ -693,6 +833,7 @@ static UINT MENU_FindItem( HMENU *hmenu,
 		if (subitem != ITEM_NOT_FOUND)
 		{
 		    *hmenu = hsubmenu;
+                    MENU_ReleaseMenuItem(items);
 		    return subitem;
 		}
 		else if (item->wID == *nPos)
@@ -705,9 +846,11 @@ static UINT MENU_FindItem( HMENU *hmenu,
 	    else if (item->wID == *nPos)
 	    {
 		*nPos = i;
+                MENU_ReleaseMenuItem(items);
 		return i;
 	    }
 	}
+        MENU_ReleaseMenuItem(items);
     }
 
     if (fallback)
@@ -728,14 +871,16 @@ UINT MENU_FindSubMenu( HMENU *hmenu, HME
     POPUPMENU tmpmenu;
     POPUPMENU *menu = &tmpmenu;
     UINT i;
-    MENUITEM *item;
+    MENUITEM *item, *items;
     if (((*hmenu)==(HMENU)0xffff) ||
             (MENU_GetMenu(*hmenu, menu)))
         return NO_SELECTED_ITEM;
-    item = menu->items;
+    MENU_GetAllMenuItems(*hmenu, &items);
+    item = items;
     for (i = 0; i < menu->nItems; i++, item++) {
         if(!(item->fType & MF_POPUP)) continue;
         if (item->hSubMenu == hSubTarget) {
+            MENU_ReleaseMenuItem(items);
             return i;
         }
         else  {
@@ -743,10 +888,12 @@ UINT MENU_FindSubMenu( HMENU *hmenu, HME
             UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
             if (pos != NO_SELECTED_ITEM) {
                 *hmenu = hsubmenu;
+                MENU_ReleaseMenuItem(items);
                 return pos;
             }
         }
     }
+    MENU_ReleaseMenuItem(items);
     return NO_SELECTED_ITEM;
 }
 
@@ -791,7 +938,7 @@ MENU_AdjustMenuItemRect(HMENU hmenu, LPR
  */
 static void MENU_FindItemByCoords( HMENU hmenu, POINT pt, UINT *pos )
 {
-    MENUITEM *item;
+    MENUITEM *item, *items;
     UINT i;
     RECT wrect;
     RECT rect;
@@ -803,7 +950,8 @@ static void MENU_FindItemByCoords( HMENU
 
     if (!GetWindowRect(menu->hWnd,&wrect)) return;
     pt.x -= wrect.left;pt.y -= wrect.top;
-    item = menu->items;
+    MENU_GetAllMenuItems(hmenu, &items);
+    item = items;
     for (i = 0; i < menu->nItems; i++, item++)
     {
         rect = item->rect;
@@ -812,9 +960,11 @@ static void MENU_FindItemByCoords( HMENU
 	    (pt.y >= rect.top) && (pt.y < rect.bottom))
 	{
 	    if (pos) *pos = i;
+            MENU_ReleaseMenuItem(items);
 	    return;
 	}
     }
+    MENU_ReleaseMenuItem(items);
     return;
 }
 
@@ -836,15 +986,17 @@ static UINT MENU_FindItemByKey( HWND hwn
     {
         POPUPMENU tmpmenu;
         POPUPMENU *menu = &tmpmenu;
-	MENUITEM *item;
+	MENUITEM *item, *items;
 	LRESULT menuchar;
         MENU_GetMenu(hmenu, menu);
-        item = menu->items;
 
 	if( !forceMenuChar )
 	{
 	     UINT i;
 
+             MENU_GetAllMenuItems(hmenu, &items);
+             item = items;
+
 	     for (i = 0; i < menu->nItems; i++, item++)
 	     {
 		if( item->text)
@@ -855,9 +1007,14 @@ static UINT MENU_FindItemByKey( HWND hwn
 		    	p = strchrW (p + 2, '&');
 		    }
 		    while (p != NULL && p [1] == '&');
-		    if (p && (toupperW(p[1]) == toupperW(key))) return i;
+		    if (p && (toupperW(p[1]) == toupperW(key)))
+                    {
+                        MENU_ReleaseMenuItem(items);
+                        return i;
+                    }
 		}
 	     }
+             MENU_ReleaseMenuItem(items);
 	}
 	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
                                  MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
@@ -1209,7 +1366,7 @@ MENU_GetMaxPopupHeight(LPPOPUPMENU lppop
  */
 static void MENU_PopupMenuCalcSize( HMENU hmenu, HWND hwndOwner )
 {
-    MENUITEM *lpitem;
+    MENUITEM *lpitem, *items;
     HDC hdc;
     int start, i;
     int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
@@ -1234,9 +1391,10 @@ static void MENU_PopupMenuCalcSize( HMEN
     lppop->maxBmpSize.cx = 0;
     lppop->maxBmpSize.cy = 0;
 
+    MENU_GetAllMenuItems(hmenu, &items);
     while (start < lppop->nItems)
     {
-	lpitem = &lppop->items[start];
+	lpitem = &items[start];
 	orgX = maxX;
         if( lpitem->fType & MF_MENUBREAK)
             orgX += MENU_COL_SPACE; 
@@ -1250,6 +1408,7 @@ static void MENU_PopupMenuCalcSize( HMEN
 		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
 
 	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
+            MENU_UpdateMenuItem(hmenu, i, lpitem);
 
 	    if (lpitem->fType & MF_MENUBARBREAK) orgX++;
 	    maxX = max( maxX, lpitem->rect.right );
@@ -1263,15 +1422,17 @@ static void MENU_PopupMenuCalcSize( HMEN
 
 	  /* Finish the column (set all items to the largest width found) */
 	maxX = max( maxX, maxTab + maxTabWidth );
-	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
+	for (lpitem = &items[start]; start < i; start++, lpitem++)
 	{
 	    lpitem->rect.right = maxX;
 	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
 		lpitem->xTab = maxTab;
+            MENU_UpdateMenuItem(hmenu, start, lpitem);
 
 	}
 	lppop->Height = max( lppop->Height, orgY );
     }
+    MENU_ReleaseMenuItem(items);
 
     lppop->Width  = maxX;
 
@@ -1441,7 +1602,7 @@ static void draw_popup_arrow( HDC hdc, R
 /***********************************************************************
  *           MENU_DrawMenuItem
  *
- * Draw a single menu item.
+ * Draw a single menu item. Updates lpitem->rect.
  */
 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
 			       UINT height, BOOL menuBar, UINT odaction )
@@ -1822,13 +1983,18 @@ static void MENU_DrawPopupMenu( HWND hwn
                 /* draw menu items */
                 if( menu->nItems)
                 {
-                    MENUITEM *item;
+                    MENUITEM *item, *items;
                     UINT u;
 
-                    item = menu->items;
-                    for( u = menu->nItems; u > 0; u--, item++)
+                    MENU_GetAllMenuItems(hmenu, &items);
+                    item = items;
+                    for( u = 0; u < menu->nItems; u++, item++)
+                    {
                         MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
                                 item, menu->Height, FALSE, ODA_DRAWENTIRE );
+                        MENU_UpdateMenuItem(hmenu, u, item);
+                    }
+                    MENU_ReleaseMenuItem(items);
                 }
                 /* draw scroll arrows */
                 if (MENU_GetLocalMenu(hmenu)->bScrolling)
@@ -1899,7 +2065,11 @@ static BOOL MENU_ShowPopup( HWND hwndOwn
     if (MENU_GetMenu( hmenu, menu )) return FALSE;
     if (lpm->FocusedItem != NO_SELECTED_ITEM)
     {
-	menu->items[lpm->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
+        MENUITEM *item;
+        MENU_GetMenuItem(hmenu, lpm->FocusedItem, &item);
+	item->fState &= ~(MF_HILITE|MF_MOUSESELECT);
+        MENU_UpdateMenuItem(hmenu, lpm->FocusedItem, item);
+        MENU_ReleaseMenuItem(item);
 	lpm->FocusedItem = NO_SELECTED_ITEM;
     }
 
@@ -1967,12 +2137,13 @@ MENU_EnsureMenuItemVisible(HMENU hmenu, 
 
     if (lpm->bScrolling)
     {
-        MENUITEM *item = &lppop->items[wIndex];
         UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
         UINT nOldPos = lpm->nScrollPos;
         RECT rc;
         UINT arrow_bitmap_height;
         BITMAP bmp;
+        MENUITEM *item;
+        MENU_GetMenuItem(hmenu, wIndex, &item);
         
         GetClientRect(lppop->hWnd, &rc);
 
@@ -1996,6 +2167,7 @@ MENU_EnsureMenuItemVisible(HMENU hmenu, 
             ScrollWindow(lppop->hWnd, 0, nOldPos - lpm->nScrollPos, &rc, &rc);
             MENU_DrawScrollArrows(hmenu, hdc);
         }
+        MENU_ReleaseMenuItem(item);
     }
 }
 
@@ -2009,6 +2181,7 @@ static void MENU_SelectItem( HWND hwndOw
     POPUPMENU tmppop;
     LPPOPUPMENU lppop = &tmppop;
     LOCALPOPUPMENU *lpm;
+    MENUITEM *items;
     HDC hdc;
 
     TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
@@ -2024,30 +2197,33 @@ static void MENU_SelectItem( HWND hwndOw
 
     SelectObject( hdc, get_menu_font(FALSE));
 
+    MENU_GetAllMenuItems(hmenu, &items);
       /* Clear previous highlighted item */
     if (lpm->FocusedItem != NO_SELECTED_ITEM)
     {
-	lppop->items[lpm->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
+	items[lpm->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
 	MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,
-                          &lppop->items[lpm->FocusedItem],
+                          &items[lpm->FocusedItem],
                           lppop->Height, !(lppop->wFlags & MF_POPUP),
 			  ODA_SELECT );
+        MENU_UpdateMenuItem(hmenu, lpm->FocusedItem, &items[lpm->FocusedItem]);
     }
 
       /* Highlight new item (if any) */
     lpm->FocusedItem = wIndex;
     if (lpm->FocusedItem != NO_SELECTED_ITEM)
     {
-        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
-            lppop->items[wIndex].fState |= MF_HILITE;
+        if(!(items[wIndex].fType & MF_SEPARATOR)) {
+            items[wIndex].fState |= MF_HILITE;
             MENU_EnsureMenuItemVisible(hmenu, wIndex, hdc);
             MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
-                    &lppop->items[wIndex], lppop->Height,
+                    &items[wIndex], lppop->Height,
                     !(lppop->wFlags & MF_POPUP), ODA_SELECT );
+            MENU_UpdateMenuItem(hmenu, wIndex, &items[wIndex]);
         }
         if (sendMenuSelect)
         {
-            MENUITEM *ip = &lppop->items[lpm->FocusedItem];
+            MENUITEM *ip = &items[lpm->FocusedItem];
 	    SendMessageW( hwndOwner, WM_MENUSELECT,
                      MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
                      ip->fType | ip->fState |
@@ -2062,7 +2238,7 @@ static void MENU_SelectItem( HWND hwndOw
                 POPUPMENU *ptm = &tmpptm;
                 MENUITEM *ip;
                 MENU_GetMenu( topmenu, ptm );
-                ip = &ptm->items[pos];
+                ip = &items[pos];
                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
                          ip->fType | ip->fState |
                          (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
@@ -2070,6 +2246,7 @@ static void MENU_SelectItem( HWND hwndOw
         }
     }
     ReleaseDC( lppop->hWnd, hdc );
+    MENU_ReleaseMenuItem(items);
 }
 
 
@@ -2374,9 +2551,13 @@ static HMENU MENU_GetSubPopup( HMENU hme
     if (MENU_GetMenu( hmenu, menu )
         || (lpm->FocusedItem == NO_SELECTED_ITEM)) return 0;
 
-    item = &menu->items[lpm->FocusedItem];
+    MENU_GetMenuItem(hmenu, lpm->FocusedItem, &item);
     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
+    {
+        MENU_ReleaseMenuItem(item);
         return item->hSubMenu;
+    }
+    MENU_ReleaseMenuItem(item);
     return 0;
 }
 

