Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

mnchange.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: mnchange.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Change Menu Routine 00007 * 00008 * History: 00009 * 10-10-90 JimA Cleanup. 00010 * 03-18-91 IanJa Window revalidation added (none required) 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 /* 00017 * Allocation/deallocation increments. Make them 00018 * different to avoid possible thrashing when an item 00019 * is repeatedly added and removed. 00020 */ 00021 #define CMENUITEMALLOC 8 00022 #define CMENUITEMDEALLOC 10 00023 00024 BOOL xxxSetLPITEMInfo(PMENU pMenu, PITEM pItem, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstr); 00025 typedef BOOL (*MENUAPIFN)(PMENU, UINT, BOOL, LPMENUITEMINFOW); 00026 00027 00028 #if DBG 00029 VOID RelocateMenuLockRecords( 00030 PITEM pItem, 00031 int cItem, 00032 LONG_PTR cbMove) 00033 { 00034 while (cItem > 0) { 00035 if (pItem->spSubMenu != NULL) { 00036 HMRelocateLockRecord(&(pItem->spSubMenu), cbMove); 00037 } 00038 pItem++; 00039 cItem--; 00040 } 00041 } 00042 #endif 00043 00044 /***************************************************************************\ 00045 * UnlockSubMenu 00046 * 00047 * Unlocks the pSubMenu and removes the MENULIST element corresponding to pMenu 00048 * 00049 * History: 00050 * Nov-20-98 MCostea 00051 \***************************************************************************/ 00052 PMENU UnlockSubMenu( 00053 PMENU pMenu, 00054 PMENU* ppSubMenu) 00055 { 00056 PMENULIST* pp; 00057 PMENULIST pMLFound; 00058 00059 if (*ppSubMenu == NULL) { 00060 return NULL; 00061 } 00062 /* 00063 * Remove the item from pMenu's pParentsList 00064 */ 00065 for (pp = &(*ppSubMenu)->pParentMenus; *pp != NULL; pp = &(*pp)->pNext) { 00066 if ((*pp)->pMenu == pMenu) { 00067 pMLFound = *pp; 00068 *pp = (*pp)->pNext; 00069 DesktopFree(pMenu->head.rpdesk, pMLFound); 00070 break; 00071 } 00072 } 00073 return Unlock(ppSubMenu); 00074 } 00075 00076 #define NESTED_MENU_LIMIT 25 00077 /***************************************************************************\ 00078 * GetMenuDepth 00079 * 00080 * Returns the menu depth (how many nested submenus this menu has). 00081 * This helps catch loops in the menu hierarchy or deep evil apps. 00082 * 00083 * History: 00084 * Sept-22-98 MCostea 00085 \***************************************************************************/ 00086 CHAR GetMenuDepth(PMENU pMenu, UINT uMaxAllowedDepth) 00087 { 00088 UINT uItems, uMaxDepth = 0, uSubMenuDepth; 00089 PITEM pItem; 00090 00091 /* 00092 * This will prevent us from getting trapped in loops 00093 */ 00094 if (uMaxAllowedDepth == 0) { 00095 return NESTED_MENU_LIMIT; 00096 } 00097 pItem = pMenu->rgItems; 00098 for (uItems = pMenu->cItems; uItems--; pItem++) { 00099 if (pItem->spSubMenu != NULL) { 00100 uSubMenuDepth = GetMenuDepth(pItem->spSubMenu, uMaxAllowedDepth-1); 00101 if (uSubMenuDepth > uMaxDepth) { 00102 /* 00103 * Don't walk the other submenus if a deep branch was found 00104 */ 00105 if (uSubMenuDepth >= NESTED_MENU_LIMIT) { 00106 return NESTED_MENU_LIMIT; 00107 } 00108 uMaxDepth = uSubMenuDepth; 00109 } 00110 } 00111 } 00112 return uMaxDepth + 1; 00113 } 00114 00115 /***************************************************************************\ 00116 * GetMenuAncestors 00117 * 00118 * Returns the maximum number of levels above pMenu in the menu hierarchy. 00119 * Walking the "parent" tree should not be expensive as it is pretty unusual 00120 * for menus to appear in different places in the hierarchy. The tree is 00121 * usualy a simple linked list. 00122 * 00123 * History: 00124 * Nov-10-98 MCostea 00125 \***************************************************************************/ 00126 CHAR GetMenuAncestors(PMENU pMenu) 00127 { 00128 PMENULIST pParentMenu; 00129 CHAR uParentAncestors; 00130 CHAR retVal = 0; 00131 00132 for (pParentMenu = pMenu->pParentMenus; pParentMenu; pParentMenu = pParentMenu->pNext) { 00133 uParentAncestors = GetMenuAncestors(pParentMenu->pMenu); 00134 if (uParentAncestors > retVal) { 00135 retVal = uParentAncestors; 00136 } 00137 } 00138 return retVal+1; 00139 } 00140 00141 /********************************************** 00142 * Global Insert/Append/Set client/server interface 00143 * 00144 * 01-13-94 FritzS Created 00145 ***********************************************/ 00146 BOOL xxxSetMenuItemInfo( 00147 PMENU pMenu, 00148 UINT wIndex, 00149 BOOL fByPosition, 00150 LPMENUITEMINFOW lpmii, 00151 PUNICODE_STRING pstrItem) 00152 { 00153 00154 PITEM pItem; 00155 00156 CheckLock(pMenu); 00157 00158 pItem = MNLookUpItem(pMenu, wIndex, fByPosition,NULL); 00159 if (pItem == NULL) { 00160 /* 00161 * Word doesn't like not finding SC_TASKLIST -- so it that's what 00162 * they're looking for, let's pretend we changed it. 00163 */ 00164 if (!fByPosition && (wIndex == SC_TASKLIST)) 00165 return TRUE; 00166 00167 /* 00168 * Item not found. Return false. 00169 */ 00170 RIPERR0(ERROR_MENU_ITEM_NOT_FOUND, RIP_WARNING, "ModifyMenu: Menu item not found"); 00171 return FALSE; 00172 } 00173 /* 00174 * we need to treat MFT_RIGHTORDER separately as this is propogated down 00175 * to the entire menu not just to this item so that we stay in ssync. This 00176 * is pretty similar to the use of MFT_RIGHTJUST, we actually do the 00177 * propogation because we need the flag in all sorts of places, not just 00178 * in MBC_RightJustifyMenu() 00179 */ 00180 00181 /* 00182 * See ValidateMENUITEMINFO in client\clmenu.c will add more flags to fMask if it use to be MIIM_TYPE 00183 * Then fMask will not be any more == MIIM_TYPE. 00184 */ 00185 00186 if (lpmii->fMask & MIIM_TYPE) { 00187 BOOL bRtoL = (lpmii->fType & MFT_RIGHTORDER) ? TRUE : FALSE; 00188 00189 if (bRtoL || TestMF(pMenu, MFRTL)) { 00190 MakeMenuRtoL(pMenu, bRtoL); 00191 } 00192 } 00193 return xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem); 00194 } 00195 00196 /***************************************************************************\ 00197 * xxxSetMenuInfo (API) 00198 * 00199 * 00200 * History: 00201 * 12-Feb-1996 JudeJ Ported from Memphis 00202 * 23-Jun-1996 GerardoB Fixed up for 5.0 00203 \***************************************************************************/ 00204 BOOL xxxSetMenuInfo(PMENU pMenu, LPCMENUINFO lpmi) 00205 { 00206 PPOPUPMENU ppopup; 00207 BOOL fRecompute = FALSE; 00208 BOOL fRedraw = FALSE; 00209 UINT uFlags = MNUS_DEFAULT; 00210 PITEM pItem; 00211 UINT uItems; 00212 TL tlSubMenu; 00213 00214 CheckLock(pMenu); 00215 00216 if (lpmi->fMask & MIM_STYLE) { 00217 pMenu->fFlags ^= (pMenu->fFlags ^ lpmi->dwStyle) & MNS_VALID; 00218 fRecompute = TRUE; 00219 } 00220 00221 if (lpmi->fMask & MIM_MAXHEIGHT) { 00222 pMenu->cyMax = lpmi->cyMax; 00223 fRecompute = TRUE; 00224 } 00225 00226 if (lpmi->fMask & MIM_BACKGROUND) { 00227 pMenu->hbrBack = lpmi->hbrBack; 00228 fRedraw = TRUE; 00229 if (pMenu->dwArrowsOn != MSA_OFF) { 00230 uFlags |= MNUS_DRAWFRAME; 00231 } 00232 } 00233 00234 if (lpmi->fMask & MIM_HELPID) { 00235 pMenu->dwContextHelpId = lpmi->dwContextHelpID; 00236 } 00237 00238 if (lpmi->fMask & MIM_MENUDATA) { 00239 pMenu->dwMenuData = lpmi->dwMenuData; 00240 } 00241 00242 /* 00243 * Do we need to set this for all submenus? 00244 */ 00245 if (lpmi->fMask & MIM_APPLYTOSUBMENUS) { 00246 pItem = pMenu->rgItems; 00247 for (uItems = pMenu->cItems; uItems--; pItem++) { 00248 if (pItem->spSubMenu != NULL) { 00249 ThreadLock(pItem->spSubMenu, &tlSubMenu); 00250 xxxSetMenuInfo(pItem->spSubMenu, lpmi); 00251 ThreadUnlock(&tlSubMenu); 00252 } 00253 } 00254 } 00255 00256 00257 if (fRecompute) { 00258 // Set the size of this menu to be 0 so that it gets recomputed with this 00259 // new item... 00260 pMenu->cyMenu = pMenu->cxMenu = 0; 00261 } 00262 00263 if (fRecompute || fRedraw) { 00264 if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) { 00265 // this menu is currently being displayed -- redisplay the menu, 00266 // recomputing if necessary 00267 xxxMNUpdateShownMenu(ppopup, NULL, uFlags); 00268 } 00269 } 00270 00271 return TRUE; 00272 } 00273 /***************************************************************************\ 00274 * MNDeleteAdjustIndex 00275 * 00276 * History: 00277 * 11/19/96 GerardoB Created 00278 \***************************************************************************/ 00279 void NNDeleteAdjustIndex (UINT * puAdjustIndex, UINT uDelIndex) 00280 { 00281 if (*puAdjustIndex == uDelIndex) { 00282 *puAdjustIndex = MFMWFP_NOITEM; 00283 } else if ((int)*puAdjustIndex > (int)uDelIndex) { 00284 (*puAdjustIndex)--; 00285 } 00286 } 00287 /***************************************************************************\ 00288 * MNDeleteAdjustIndexes 00289 * 00290 * This function is called when an item on an active menu is about 00291 * to be deleted. It makes sure that other indexes like posSelectedItem, 00292 * uButtonDownIndex and uDraggingIndex are adjusted to reflect the change 00293 * It "clears" the index if it is AT the point of deletion or 00294 * decrements it if it is after the point of deletion 00295 * 00296 * History: 00297 * 01/16/97 GerardoB Created 00298 \***************************************************************************/ 00299 void MNDeleteAdjustIndexes (PMENUSTATE pMenuState, PPOPUPMENU ppopup, UINT uiPos) 00300 { 00301 /* 00302 * Adjust the index of the selected item and the dropped popup, if needed. 00303 */ 00304 NNDeleteAdjustIndex(&ppopup->posSelectedItem, uiPos); 00305 if (ppopup->fHierarchyDropped) { 00306 NNDeleteAdjustIndex(&ppopup->posDropped, uiPos); 00307 } 00308 00309 /* 00310 * Adjust uButtonDownIndex and uDraggingIndex if needed 00311 */ 00312 if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { 00313 NNDeleteAdjustIndex(&pMenuState->uButtonDownIndex, uiPos); 00314 } 00315 if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { 00316 NNDeleteAdjustIndex(&pMenuState->uDraggingIndex, uiPos); 00317 } 00318 } 00319 /***************************************************************************\ 00320 * xxxInsertMenuItem 00321 * 00322 \***************************************************************************/ 00323 BOOL xxxInsertMenuItem( 00324 PMENU pMenu, 00325 UINT wIndex, 00326 BOOL fByPosition, 00327 LPMENUITEMINFOW lpmii, 00328 PUNICODE_STRING pstrItem) 00329 { 00330 BOOL fRet = TRUE; 00331 PITEM pItem; 00332 PMENU pMenuItemIsOn; 00333 PMENUSTATE pMenuState; 00334 PITEM pNewItems; 00335 PPOPUPMENU ppopup = NULL; 00336 TL tlMenu; 00337 UINT uiPos; 00338 00339 CheckLock(pMenu); 00340 00341 // Find out where the item we are inserting should go. 00342 if (wIndex != MFMWFP_NOITEM) { 00343 pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn); 00344 00345 if (pItem != NULL) { 00346 pMenu = pMenuItemIsOn; 00347 } else { 00348 wIndex = MFMWFP_NOITEM; 00349 } 00350 } else { 00351 pItem = NULL; 00352 } 00353 /* 00354 * keep normal menu items between the MDI system bitmap items 00355 */ 00356 if (!TestMF(pMenu, MFISPOPUP) 00357 && (pMenu->cItems != 0) 00358 && (!(lpmii->fMask & MIIM_BITMAP) 00359 || (lpmii->hbmpItem > HBMMENU_MBARLAST) 00360 || (lpmii->hbmpItem == 0) 00361 )) { 00362 00363 UINT wSave, w; 00364 PITEM pItemWalk; 00365 wSave = w = wIndex; 00366 00367 if (pItem && !fByPosition) { 00368 w = MNGetpItemIndex(pMenu, pItem); 00369 w = (UINT)((PBYTE)pItem - (PBYTE)(pMenu->rgItems)) / sizeof(ITEM); 00370 } 00371 00372 if (!w) { 00373 pItemWalk = pMenu->rgItems; 00374 if ((pItemWalk->hbmp == HBMMENU_SYSTEM)) { 00375 wIndex = 1; 00376 } 00377 } else { 00378 if (w == MFMWFP_NOITEM) { 00379 w = pMenu->cItems; 00380 } 00381 00382 w--; 00383 pItemWalk = pMenu->rgItems + w; 00384 while (w && (pItemWalk->hbmp) && (pItemWalk->hbmp < HBMMENU_MBARLAST)) { 00385 wIndex = w--; 00386 pItemWalk--; 00387 } 00388 } 00389 00390 if (wIndex != wSave) { 00391 pItem = pMenu->rgItems + wIndex; 00392 } 00393 } 00394 00395 // LATER -- we currently realloc every 10 items. investigate the 00396 // performance hit/gain we get from this and adjust accordingly. 00397 if (pMenu->cItems >= pMenu->cAlloced) { 00398 if (pMenu->rgItems) { 00399 pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, 00400 (pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM), 00401 DTAG_MENUITEM); 00402 if (pNewItems) { 00403 RtlCopyMemory(pNewItems, pMenu->rgItems, 00404 pMenu->cAlloced * sizeof(ITEM)); 00405 #if DEBUGTAGS 00406 if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { 00407 RelocateMenuLockRecords(pNewItems, pMenu->cItems, 00408 ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems)); 00409 } 00410 #endif 00411 DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); 00412 } 00413 } else { 00414 pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, 00415 sizeof(ITEM) * CMENUITEMALLOC, DTAG_MENUITEM); 00416 } 00417 00418 if (pNewItems == NULL) 00419 return(FALSE); 00420 00421 pMenu->cAlloced += CMENUITEMALLOC; 00422 pMenu->rgItems = pNewItems; 00423 00424 /* 00425 * Now look up the item again since it probably moved when we realloced the 00426 * memory. 00427 */ 00428 if (wIndex != MFMWFP_NOITEM) 00429 pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn); 00430 00431 } 00432 00433 00434 /* 00435 * If this menu is being displayed right now and we're not appending 00436 * an item, then we need to adjust the positions we keep track of. 00437 * We want to do this before moving the items to accomodate the 00438 * new one, in case we need to clear the insertion bar 00439 */ 00440 if ((pItem != NULL) 00441 && (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState))) { 00442 /* 00443 * This menu is active. Adjust the index the selected 00444 * item and the dropped popup, if needed 00445 */ 00446 uiPos = MNGetpItemIndex(pMenu, pItem); 00447 if (ppopup->posSelectedItem >= (int)uiPos) { 00448 ppopup->posSelectedItem++; 00449 } 00450 if (ppopup->fHierarchyDropped && (ppopup->posDropped >= (int)uiPos)) { 00451 ppopup->posDropped++; 00452 } 00453 00454 /* 00455 * Adjust uButtonDownIndex and uDraggingIndex if needed 00456 */ 00457 if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { 00458 if ((int)pMenuState->uButtonDownIndex >= (int)uiPos) { 00459 pMenuState->uButtonDownIndex++; 00460 } 00461 } 00462 if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { 00463 /* 00464 * Check to see if an item is inserted right on the insertion 00465 * bar. If so, clean up any present insertion bar state 00466 */ 00467 if (((int)pMenuState->uDraggingIndex == (int)uiPos) 00468 && (pMenuState->uDraggingFlags & MNGOF_TOPGAP)) { 00469 00470 xxxMNSetGapState(pMenuState->uDraggingHitArea, 00471 pMenuState->uDraggingIndex, 00472 pMenuState->uDraggingFlags, 00473 FALSE); 00474 } 00475 00476 if ((int)pMenuState->uDraggingIndex >= (int)uiPos) { 00477 pMenuState->uDraggingIndex++; 00478 } 00479 } 00480 } 00481 00482 pMenu->cItems++; 00483 if (pItem != NULL) { 00484 // Move this item up to make room for the one we want to insert. 00485 RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) * 00486 sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems)); 00487 #if DEBUGTAGS 00488 if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { 00489 RelocateMenuLockRecords(pItem + 1, 00490 (int)(&(pMenu->rgItems[pMenu->cItems]) - (pItem + 1)), 00491 sizeof(ITEM)); 00492 } 00493 #endif 00494 } else { 00495 00496 // If lpItem is null, we will be inserting the item at the end of the 00497 // menu. 00498 pItem = pMenu->rgItems + pMenu->cItems - 1; 00499 } 00500 00501 // Need to zero these fields in case we are inserting this item in the 00502 // middle of the item list. 00503 pItem->fType = 0; 00504 pItem->fState = 0; 00505 pItem->wID = 0; 00506 pItem->spSubMenu = NULL; 00507 pItem->hbmpChecked = NULL; 00508 pItem->hbmpUnchecked = NULL; 00509 pItem->cch = 0; 00510 pItem->dwItemData = 0; 00511 pItem->xItem = 0; 00512 pItem->yItem = 0; 00513 pItem->cxItem = 0; 00514 pItem->cyItem = 0; 00515 pItem->hbmp = NULL; 00516 pItem->cxBmp = MNIS_MEASUREBMP; 00517 pItem->lpstr = NULL; 00518 00519 /* 00520 * We might have reassigned pMenu above, so lock it 00521 */ 00522 ThreadLock(pMenu, &tlMenu); 00523 if (!xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem)) { 00524 00525 /* 00526 * Reset any of the indexes we might have adjusted above 00527 */ 00528 if (ppopup != NULL) { 00529 MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos); 00530 } 00531 00532 MNFreeItem(pMenu, pItem, TRUE); 00533 00534 00535 // Move things up since we removed/deleted the item 00536 RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) + 00537 (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1))); 00538 #if DEBUGTAGS 00539 if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { 00540 RelocateMenuLockRecords(pItem, 00541 (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem), 00542 -(int)sizeof(ITEM)); 00543 } 00544 #endif 00545 pMenu->cItems--; 00546 fRet = FALSE; 00547 } else { 00548 /* 00549 * Like MFT_RIGHTJUSTIFY, this staggers down the menu, 00550 * (but we inherit, to make localisation etc MUCH easier). 00551 * 00552 * MFT_RIGHTORDER is the same value as MFT_SYSMENU. We distinguish 00553 * between the two by also looking for MFT_BITMAP. 00554 */ 00555 if (TestMF(pMenu, MFRTL) || 00556 (pItem && TestMFT(pItem, MFT_RIGHTORDER) && !TestMFT(pItem, MFT_BITMAP))) { 00557 pItem->fType |= (MFT_RIGHTORDER | MFT_RIGHTJUSTIFY); 00558 if (pItem->spSubMenu) { 00559 MakeMenuRtoL(pItem->spSubMenu, TRUE); 00560 } 00561 } 00562 } 00563 00564 ThreadUnlock(&tlMenu); 00565 return fRet; 00566 00567 } 00568 00569 /***************************************************************************\ 00570 * FreeItemBitmap 00571 * 00572 * History: 00573 * 07-23-96 GerardoB - Added header and Fixed up for 5.0 00574 \***************************************************************************/ 00575 void FreeItemBitmap(PITEM pItem) 00576 { 00577 // Free up hItem unless it's a bitmap handle or nonexistent. 00578 // Apps are responsible for freeing their bitmaps. 00579 if ((pItem->hbmp != NULL) && !TestMFS(pItem, MFS_CACHEDBMP)) { 00580 /* 00581 * Assign ownership of the bitmap to the process that is 00582 * destroying the menu to ensure that bitmap will 00583 * eventually be destroyed. 00584 */ 00585 GreSetBitmapOwner((HBITMAP)(pItem->hbmp), OBJECT_OWNER_CURRENT); 00586 } 00587 00588 // Zap this pointer in case we try to free or reference it again 00589 pItem->hbmp = NULL; 00590 } 00591 /***************************************************************************\ 00592 * FreeItemString 00593 * 00594 * History: 00595 * 07-23-96 GerardoB - Added header and Fixed up for 5.0 00596 \***************************************************************************/ 00597 00598 void FreeItemString(PMENU pMenu, PITEM pItem) 00599 { 00600 // Free up Item's string 00601 if ((pItem->lpstr != NULL)) { 00602 DesktopFree(pMenu->head.rpdesk, pItem->lpstr); 00603 } 00604 // Zap this pointer in case we try to free or reference it again 00605 pItem->lpstr = NULL; 00606 } 00607 00608 /***************************************************************************\ 00609 * FreeItem 00610 * 00611 * Free a menu item and its associated resources. 00612 * 00613 * History: 00614 * 10-11-90 JimA Translated from ASM 00615 \***************************************************************************/ 00616 00617 void MNFreeItem( 00618 PMENU pMenu, 00619 PITEM pItem, 00620 BOOL fFreeItemPopup) 00621 { 00622 PMENU pSubMenu; 00623 00624 FreeItemBitmap(pItem); 00625 FreeItemString(pMenu, pItem); 00626 00627 pSubMenu = UnlockSubMenu(pMenu, &(pItem->spSubMenu)); 00628 if (pSubMenu) { 00629 if (fFreeItemPopup) { 00630 _DestroyMenu(pSubMenu); 00631 } 00632 } 00633 } 00634 /***************************************************************************\ 00635 * RemoveDeleteMenuHelper 00636 * 00637 * This removes the menu item from the given menu. If 00638 * fDeleteMenuItem, the memory associted with the popup menu associated with 00639 * the item being removed is freed and recovered. 00640 * 00641 * History: 00642 \***************************************************************************/ 00643 00644 BOOL xxxRemoveDeleteMenuHelper( 00645 PMENU pMenu, 00646 UINT nPosition, 00647 DWORD wFlags, 00648 BOOL fDeleteMenu) 00649 { 00650 PITEM pItem; 00651 PITEM pNewItems; 00652 PMENU pMenuSave; 00653 PMENUSTATE pMenuState; 00654 PPOPUPMENU ppopup; 00655 UINT uiPos; 00656 00657 CheckLock(pMenu); 00658 00659 pMenuSave = pMenu; 00660 00661 pItem = MNLookUpItem(pMenu, nPosition, (BOOL) (wFlags & MF_BYPOSITION), &pMenu); 00662 if (pItem == NULL) { 00663 00664 /* 00665 * Hack for apps written for Win95. In Win95 the prototype for 00666 * this function was with 'WORD nPosition' and because of this 00667 * the HIWORD(nPosition) got set to 0. 00668 * We are doing this just for system menu commands. 00669 */ 00670 if (nPosition >= 0xFFFFF000 && !(wFlags & MF_BYPOSITION)) { 00671 nPosition &= 0x0000FFFF; 00672 pMenu = pMenuSave; 00673 pItem = MNLookUpItem(pMenu, nPosition, FALSE, &pMenu); 00674 00675 if (pItem == NULL) 00676 return FALSE; 00677 } else 00678 return FALSE; 00679 } 00680 00681 if (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState)) { 00682 /* 00683 * This menu is active; since we're about to insert an item, 00684 * make sure that any of the positions we've stored are 00685 * adjusted properly 00686 */ 00687 uiPos = MNGetpItemIndex(pMenu, pItem); 00688 MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos); 00689 } 00690 MNFreeItem(pMenu, pItem, fDeleteMenu); 00691 00692 /* 00693 * Reset the menu size so that it gets recomputed next time. 00694 */ 00695 pMenu->cyMenu = pMenu->cxMenu = 0; 00696 00697 if (pMenu->cItems == 1) { 00698 DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); 00699 pMenu->cAlloced = 0; 00700 pNewItems = NULL; 00701 } else { 00702 00703 /* 00704 * Move things up since we removed/deleted the item 00705 */ 00706 00707 RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) + 00708 (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1))); 00709 #if DEBUGTAGS 00710 if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { 00711 RelocateMenuLockRecords(pItem, 00712 (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem), 00713 -(int)sizeof(ITEM)); 00714 } 00715 #endif 00716 00717 /* 00718 * We're shrinking so if localalloc fails, just leave the mem as is. 00719 */ 00720 UserAssert(pMenu->cAlloced >= pMenu->cItems); 00721 if ((pMenu->cAlloced - pMenu->cItems) >= CMENUITEMDEALLOC - 1) { 00722 pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, 00723 (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM), 00724 DTAG_MENUITEM); 00725 if (pNewItems != NULL) { 00726 00727 RtlCopyMemory(pNewItems, pMenu->rgItems, 00728 (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM)); 00729 #if DEBUGTAGS 00730 if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { 00731 RelocateMenuLockRecords(pNewItems, pMenu->cItems - 1, 00732 ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems)); 00733 } 00734 #endif 00735 DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); 00736 pMenu->cAlloced -= CMENUITEMDEALLOC; 00737 } else 00738 pNewItems = pMenu->rgItems; 00739 } else 00740 pNewItems = pMenu->rgItems; 00741 } 00742 00743 pMenu->rgItems = pNewItems; 00744 pMenu->cItems--; 00745 00746 if (ppopup != NULL) { 00747 /* 00748 * this menu is currently being displayed -- redisplay the menu with 00749 * this item removed 00750 */ 00751 xxxMNUpdateShownMenu(ppopup, pMenu->rgItems + uiPos, MNUS_DELETE); 00752 } 00753 return TRUE; 00754 } 00755 00756 /***************************************************************************\ 00757 * RemoveMenu 00758 * 00759 * Removes and item but doesn't delete it. Only useful for items with 00760 * an associated popup since this will remove the item from the menu with 00761 * destroying the popup menu handle. 00762 * 00763 * History: 00764 \***************************************************************************/ 00765 00766 BOOL xxxRemoveMenu( 00767 PMENU pMenu, 00768 UINT nPosition, 00769 UINT wFlags) 00770 { 00771 return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, FALSE); 00772 } 00773 00774 /***************************************************************************\ 00775 * DeleteMenu 00776 * 00777 * Deletes an item. ie. Removes it and recovers the memory used by it. 00778 * 00779 * History: 00780 \***************************************************************************/ 00781 00782 BOOL xxxDeleteMenu( 00783 PMENU pMenu, 00784 UINT nPosition, 00785 UINT wFlags) 00786 { 00787 return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, TRUE); 00788 } 00789 00790 /***************************************************************************\ 00791 * xxxSetLPITEMInfo 00792 * 00793 * History: 00794 * 07-23-96 GerardoB - Added header and Fixed up for 5.0 00795 \***************************************************************************/ 00796 BOOL NEAR xxxSetLPITEMInfo( 00797 PMENU pMenu, 00798 PITEM pItem, 00799 LPMENUITEMINFOW lpmii, 00800 PUNICODE_STRING pstrItem) 00801 { 00802 00803 HANDLE hstr; 00804 UINT cch; 00805 BOOL fRecompute = FALSE; 00806 BOOL fRedraw = FALSE; 00807 PPOPUPMENU ppopup; 00808 00809 CheckLock(pMenu); 00810 00811 if (lpmii->fMask & MIIM_FTYPE) { 00812 pItem->fType &= ~MFT_MASK; 00813 pItem->fType |= lpmii->fType; 00814 if (lpmii->fType & MFT_SEPARATOR ) { 00815 pItem->fState |= MFS_DISABLED ; 00816 } 00817 fRecompute = TRUE; 00818 fRedraw = (lpmii->fType & MFT_OWNERDRAW); 00819 } 00820 00821 if (lpmii->fMask & MIIM_STRING) { 00822 if (pstrItem->Buffer != NULL) { 00823 hstr = (HANDLE)DesktopAlloc(pMenu->head.rpdesk, 00824 pstrItem->Length + sizeof(UNICODE_NULL), DTAG_MENUTEXT); 00825 00826 if (hstr == NULL) { 00827 return FALSE; 00828 } 00829 00830 try { 00831 RtlCopyMemory(hstr, pstrItem->Buffer, pstrItem->Length); 00832 } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { 00833 DesktopFree(pMenu->head.rpdesk, hstr); 00834 return FALSE; 00835 } 00836 cch = pstrItem->Length / sizeof(WCHAR); 00837 /* 00838 * We don't need to null terminate the string, since DesktopAlloc 00839 * zero-fills for us. 00840 */ 00841 } else { 00842 cch = 0; 00843 hstr = NULL; 00844 } 00845 FreeItemString(pMenu,pItem); 00846 pItem->cch = cch; 00847 pItem->lpstr = hstr; 00848 fRecompute = TRUE; 00849 fRedraw = TRUE; 00850 } 00851 00852 if (lpmii->fMask & MIIM_BITMAP) { 00853 FreeItemBitmap(pItem); 00854 pItem->hbmp = lpmii->hbmpItem; 00855 fRecompute = TRUE; 00856 fRedraw = TRUE; 00857 pItem->cxBmp = MNIS_MEASUREBMP; 00858 /* 00859 * If this is one of the special bitmaps, mark it as such 00860 */ 00861 if ((pItem->hbmp > HBMMENU_MIN) && (pItem->hbmp < HBMMENU_MAX)) { 00862 SetMFS(pItem, MFS_CACHEDBMP); 00863 } else { 00864 ClearMFS(pItem, MFS_CACHEDBMP); 00865 } 00866 } 00867 00868 if (lpmii->fMask & MIIM_ID) { 00869 pItem->wID = lpmii->wID; 00870 } 00871 00872 if (lpmii->fMask & MIIM_DATA) { 00873 pItem->dwItemData = lpmii->dwItemData; 00874 } 00875 00876 if (lpmii->fMask & MIIM_STATE) { 00877 /* 00878 * Preserve private bits (~MFS_MASK). 00879 * Also preserve MFS_HILITE | MFS_DEFAULT if already set; if not set, 00880 * let the caller turn them on. 00881 */ 00882 UserAssert(!(lpmii->fState & ~MFS_MASK)); 00883 pItem->fState &= ~MFS_MASK | MFS_HILITE | MFS_DEFAULT; 00884 pItem->fState |= lpmii->fState; 00885 if (pItem->fType & MFT_SEPARATOR) 00886 pItem->fState |= MFS_DISABLED; 00887 fRedraw = TRUE; 00888 } 00889 00890 if (lpmii->fMask & MIIM_CHECKMARKS) { 00891 pItem->hbmpChecked = lpmii->hbmpChecked; 00892 pItem->hbmpUnchecked = lpmii->hbmpUnchecked; 00893 fRedraw = TRUE; 00894 } 00895 00896 if (lpmii->fMask & MIIM_SUBMENU) { 00897 PMENU pSubMenu = NULL; 00898 00899 if (lpmii->hSubMenu != NULL) { 00900 pSubMenu = ValidateHmenu(lpmii->hSubMenu); 00901 } 00902 00903 // Free the popup associated with this item, if any and if needed. 00904 if (pItem->spSubMenu != pSubMenu) { 00905 if (pItem->spSubMenu != NULL) { 00906 _DestroyMenu(pItem->spSubMenu); 00907 } 00908 if (pSubMenu != NULL) { 00909 00910 BOOL bMenuCreated = FALSE; 00911 /* 00912 * Fix MSTest that sets a submenu to itself by giving it a different handle. 00913 * So the loop is broken and we won't fail their call later 00914 * MCostea #243374 00915 */ 00916 if (pSubMenu == pMenu) { 00917 pSubMenu = _CreateMenu(); 00918 if (!pSubMenu) { 00919 return FALSE; 00920 } 00921 bMenuCreated = TRUE; 00922 } 00923 /* 00924 * Link the submenu and then check for loops 00925 */ 00926 Lock(&(pItem->spSubMenu), pSubMenu); 00927 SetMF(pItem->spSubMenu, MFISPOPUP); 00928 /* 00929 * We just added a submenu. Check to see if the menu tree is not 00930 * unreasonable deep and there is no loop forming. 00931 * This will prevent us from running out of stack 00932 * MCostea #226460 00933 */ 00934 if (GetMenuDepth(pSubMenu, NESTED_MENU_LIMIT) + GetMenuAncestors(pMenu) >= NESTED_MENU_LIMIT) { 00935 FailInsertion: 00936 RIPMSG1(RIP_WARNING, "The menu hierarchy is very deep or has a loop %#p", pMenu); 00937 ClearMF(pItem->spSubMenu, MFISPOPUP); 00938 Unlock(&(pItem->spSubMenu)); 00939 if (bMenuCreated) { 00940 _DestroyMenu(pSubMenu); 00941 } 00942 return FALSE; 00943 } 00944 /* 00945 * Add pMenu to the pSubMenu->pParentMenus list 00946 */ 00947 { 00948 PMENULIST pMenuList = DesktopAlloc(pMenu->head.rpdesk, 00949 sizeof(MENULIST), 00950 DTAG_MENUITEM); 00951 if (!pMenuList) { 00952 goto FailInsertion; 00953 } 00954 pMenuList->pMenu = pMenu; 00955 pMenuList->pNext = pSubMenu->pParentMenus; 00956 pSubMenu->pParentMenus = pMenuList; 00957 } 00958 } else { 00959 UnlockSubMenu(pMenu, &(pItem->spSubMenu)); 00960 } 00961 fRedraw = TRUE; 00962 } 00963 } 00964 00965 // For support of the old way of defining a separator i.e. if it is not a string 00966 // or a bitmap or a ownerdraw, then it must be a separator. 00967 // This should prpbably be moved to MIIOneWayConvert -jjk 00968 if (!(pItem->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) 00969 && (pItem->lpstr == NULL) 00970 && (pItem->hbmp == NULL)) { 00971 00972 pItem->fType = MFT_SEPARATOR; 00973 pItem->fState|=MFS_DISABLED; 00974 } 00975 00976 if (fRecompute) { 00977 pItem->dxTab = 0; 00978 pItem->ulX = UNDERLINE_RECALC; 00979 pItem->ulWidth = 0; 00980 00981 // Set the size of this menu to be 0 so that it gets recomputed with this 00982 // new item... 00983 pMenu->cyMenu = pMenu->cxMenu = 0; 00984 00985 00986 if (fRedraw) { 00987 if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) { 00988 // this menu is currently being displayed -- redisplay the menu, 00989 // recomputing if necessary 00990 xxxMNUpdateShownMenu(ppopup, pItem, MNUS_DEFAULT); 00991 } 00992 } 00993 00994 } 00995 00996 return TRUE; 00997 } 00998 00999 BOOL _SetMenuContextHelpId(PMENU pMenu, DWORD dwContextHelpId) 01000 { 01001 01002 // Set the new context help Id; 01003 pMenu->dwContextHelpId = dwContextHelpId; 01004 01005 return TRUE; 01006 } 01007 01008 BOOL _SetMenuFlagRtoL(PMENU pMenu) 01009 { 01010 01011 // This is a right-to-left menu being created; 01012 SetMF(pMenu, MFRTL); 01013 01014 return TRUE; 01015 } 01016 01017 /***************************************************************************\ 01018 * MNGetPopupFromMenu 01019 * 01020 * checks to see if the given hMenu is currently being shown in a popup. 01021 * returns the PPOPUPMENU associated with this hMenu if it is being shown; 01022 * NULL if the hMenu is not currently being shown 01023 * 01024 * History: 01025 * 07-23-96 GerardoB - Added header & fixed up for 5.0 01026 \***************************************************************************/ 01027 PPOPUPMENU MNGetPopupFromMenu(PMENU pMenu, PMENUSTATE *ppMenuState) 01028 { 01029 PPOPUPMENU ppopup; 01030 PMENUSTATE pMenuState; 01031 01032 /* 01033 * If this menu doesn't have a notification window, then 01034 * it cannot be in menu mode 01035 */ 01036 if (pMenu->spwndNotify == NULL) { 01037 return NULL; 01038 } 01039 01040 /* 01041 * If no pMenuState, no menu mode 01042 */ 01043 pMenuState = GetpMenuState(pMenu->spwndNotify); 01044 if (pMenuState == NULL) { 01045 return NULL; 01046 } 01047 01048 /* 01049 * If not in the menu loop, not yet or no longer in menu mode 01050 */ 01051 if (!pMenuState->fInsideMenuLoop) { 01052 return NULL; 01053 } 01054 01055 /* 01056 * return pMenuState if requested 01057 */ 01058 if (ppMenuState != NULL) { 01059 *ppMenuState = pMenuState; 01060 } 01061 01062 01063 /* 01064 * Starting from the root popup, find the popup associated to this menu 01065 */ 01066 ppopup = pMenuState->pGlobalPopupMenu; 01067 while (ppopup != NULL) { 01068 /* 01069 * found? 01070 */ 01071 if (ppopup->spmenu == pMenu) { 01072 if (ppopup->fIsMenuBar) { 01073 return NULL; 01074 } 01075 /* 01076 * Since the menu is being modified, let's kill any animation. 01077 */ 01078 MNAnimate(pMenuState, FALSE); 01079 return ppopup; 01080 } 01081 /* 01082 * If no more popups, bail 01083 */ 01084 if (ppopup->spwndNextPopup == NULL) { 01085 return NULL; 01086 } 01087 01088 /* 01089 * Next popup 01090 */ 01091 ppopup = ((PMENUWND)ppopup->spwndNextPopup)->ppopupmenu; 01092 } 01093 01094 return NULL; 01095 } 01096 01097 /***************************************************************************\ 01098 * xxxMNUpdateShownMenu 01099 * 01100 * updates a given ppopup menu window to reflect the inserting, deleting, 01101 * or altering of the given lpItem. 01102 * 01103 * History: 01104 * 07-23-96 GerardoB - Added header & fixed up for 5.0 01105 \***************************************************************************/ 01106 void xxxMNUpdateShownMenu(PPOPUPMENU ppopup, PITEM pItem, UINT uFlags) 01107 { 01108 RECT rc; 01109 PWND pwnd = ppopup->spwndPopupMenu; 01110 PMENU pMenu = ppopup->spmenu; 01111 TL tlpwnd; 01112 TL tlpmenu; 01113 01114 /* 01115 * The popup might get destroyed while we callback, so lock this pwnd. 01116 */ 01117 ThreadLock(pwnd, &tlpwnd); 01118 ThreadLock(ppopup->spmenu, &tlpmenu); 01119 01120 _GetClientRect(pwnd, &rc); 01121 01122 /* 01123 * If we need to resize menu window 01124 */ 01125 if (pMenu->cxMenu == 0) { 01126 RECT rcScroll = rc; 01127 int cySave = rc.bottom; 01128 int cxSave = rc.right; 01129 DWORD dwSize; 01130 DWORD dwArrowsOnBefore; 01131 01132 dwArrowsOnBefore = pMenu->dwArrowsOn; 01133 UserAssert(uFlags != 0); 01134 dwSize = (DWORD)xxxSendMessage(pwnd, MN_SIZEWINDOW, uFlags, 0L); 01135 uFlags &= ~MNUS_DRAWFRAME; 01136 /* 01137 * If scroll arrows appeared or disappeared, redraw entire client 01138 */ 01139 if (dwArrowsOnBefore ^ pMenu->dwArrowsOn) { 01140 goto InvalidateAll; 01141 } 01142 01143 rc.right = LOWORD(dwSize); 01144 if (pItem != NULL) { 01145 if (rc.right != cxSave) { 01146 // width changed, redraw everything 01147 // BUGBUG -- this could be tuned to just redraw items with 01148 // submenus and/or accelerator fields 01149 goto InvalidateAll; 01150 } else { 01151 rc.bottom = pMenu->cyMenu; 01152 if (pMenu->dwArrowsOn != MSA_OFF) { 01153 if (rc.bottom <= cySave) { 01154 rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; 01155 goto InvalidateRest; 01156 } 01157 01158 _GetClientRect(pwnd, &rcScroll); 01159 } 01160 01161 rc.top = rcScroll.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; 01162 if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) { 01163 xxxScrollWindowEx(pwnd, 0, rc.bottom - cySave, &rcScroll, &rc, NULL, NULL, SW_INVALIDATE | SW_ERASE); 01164 } 01165 } /* else of if (rc.right != cxSave) */ 01166 } /* if (pItem != NULL) */ 01167 } /* if (pMenu->cxMenu == 0) */ 01168 01169 if (!(uFlags & MNUS_DELETE)) { 01170 if (pItem != NULL) { 01171 rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; 01172 rc.bottom = rc.top + pItem->cyItem; 01173 InvalidateRest: 01174 if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) { 01175 xxxInvalidateRect(pwnd, &rc, TRUE); 01176 } 01177 } else { 01178 InvalidateAll: 01179 xxxInvalidateRect(pwnd, NULL, TRUE); 01180 } 01181 if (uFlags & MNUS_DRAWFRAME) { 01182 xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0, 01183 SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE 01184 | SWP_NOACTIVATE | SWP_NOOWNERZORDER); 01185 } 01186 } 01187 01188 ThreadUnlock(&tlpmenu); 01189 ThreadUnlock(&tlpwnd); 01190 }

Generated on Sat May 15 19:40:51 2004 for test by doxygen 1.3.7