Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1096 lines
32 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mncomput.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Menu Layout Calculation Routines
  7. *
  8. * History:
  9. * 10-10-90 JimA Cleanup.
  10. * 03-18-91 IanJa Window revalidation added
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. DWORD MNRecalcTabStrings(HDC hdc, PMENU pMenu, UINT iBeg, UINT iEnd,
  15. DWORD xTab, DWORD hCount);
  16. /***************************************************************************\
  17. * xxxMNGetBitmapSize
  18. *
  19. * Returns TRUE if measureitem was sent and FALSE if not
  20. *
  21. * History:
  22. * 07-23-96 GerardoB - Added header & fixed up for 5.0
  23. \***************************************************************************/
  24. BOOL xxxMNGetBitmapSize(
  25. LPITEM pItem,
  26. PWND pwndNotify)
  27. {
  28. MEASUREITEMSTRUCT mis;
  29. if (pItem->cxBmp != MNIS_MEASUREBMP) {
  30. return FALSE;
  31. }
  32. // Send a measure item message to the owner
  33. mis.CtlType = ODT_MENU;
  34. mis.CtlID = 0;
  35. mis.itemID = pItem->wID;
  36. mis.itemWidth = 0;
  37. // After scrollable menus
  38. // mis32.itemHeight= gcyMenuFontChar;
  39. mis.itemHeight= (UINT)gpsi->cySysFontChar;
  40. mis.itemData = pItem->dwItemData;
  41. xxxSendMessage(pwndNotify, WM_MEASUREITEM, 0, (LPARAM)&mis);
  42. pItem->cxBmp = mis.itemWidth;
  43. pItem->cyBmp = mis.itemHeight;
  44. return TRUE;
  45. }
  46. /***************************************************************************\
  47. * xxxItemSize
  48. *
  49. * Calc the dimensions of bitmaps and strings. Loword of returned
  50. * value contains width, high word contains height of item.
  51. *
  52. * History:
  53. * 07-23-96 GerardoB - fixed up for 5.0
  54. \***************************************************************************/
  55. BOOL xxxMNItemSize(
  56. PMENU pMenu,
  57. PWND pwndNotify,
  58. HDC hdc,
  59. PITEM pItem,
  60. BOOL fPopup,
  61. LPPOINT lppt)
  62. {
  63. BITMAP bmp;
  64. int width = 0;
  65. int height = 0;
  66. DWORD xRightJustify;
  67. LPWSTR lpMenuString;
  68. HFONT hfnOld;
  69. int tcExtra;
  70. UNREFERENCED_PARAMETER(pMenu);
  71. CheckLock(pMenu);
  72. CheckLock(pwndNotify);
  73. if (!fPopup) {
  74. /*
  75. * Save off the height of the top menu bar since we will used this often
  76. * if the pItem is not in a popup. (ie. it is in the top level menu bar)
  77. */
  78. height = SYSMET(CYMENUSIZE);
  79. }
  80. hfnOld = NULL;
  81. if (TestMFS(pItem, MFS_DEFAULT)) {
  82. if (ghMenuFontDef)
  83. hfnOld = GreSelectFont(hdc, ghMenuFontDef);
  84. else {
  85. tcExtra = GreGetTextCharacterExtra(hdc);
  86. GreSetTextCharacterExtra(hdc, tcExtra + 1 + (gcxMenuFontChar / gpsi->cxSysFontChar));
  87. }
  88. }
  89. /*
  90. * Compute bitmap dimensions if needed
  91. */
  92. if (pItem->hbmp != NULL) {
  93. if (pItem->hbmp == HBMMENU_CALLBACK) {
  94. xxxMNGetBitmapSize(pItem, pwndNotify);
  95. } else if (pItem->cxBmp == MNIS_MEASUREBMP) {
  96. if (TestMFS(pItem, MFS_CACHEDBMP)) {
  97. pItem->cxBmp = SYSMET(CXMENUSIZE);
  98. pItem->cyBmp = SYSMET(CYMENUSIZE);
  99. if (pItem->hbmp == HBMMENU_SYSTEM) {
  100. pItem->cxBmp += SYSMET(CXEDGE);
  101. /*
  102. * Chicago/Memphis only stretch the width,
  103. * not the height. NT Bug 124779. FritzS
  104. */
  105. // pItem->cyBmp += SYSMET(CXEDGE);
  106. }
  107. } else {
  108. if (GreExtGetObjectW(pItem->hbmp, sizeof(BITMAP), (LPSTR)&bmp)) {
  109. pItem->cxBmp = bmp.bmWidth;
  110. pItem->cyBmp = bmp.bmHeight;
  111. } else {
  112. /*
  113. * If the bitmap is not useable, this is as good a default
  114. * as any.
  115. */
  116. pItem->cxBmp = SYSMET(CXMENUSIZE);
  117. pItem->cyBmp = SYSMET(CYMENUSIZE);
  118. }
  119. }
  120. }
  121. width = pItem->cxBmp;
  122. /*
  123. * Remember the max bitmap width to align the text in all items.
  124. */
  125. pMenu->cxTextAlign = max(pMenu->cxTextAlign, (DWORD)width);
  126. /*
  127. * In menu bars, we force the item to be at least CYMNSIZE.
  128. * Fixes many, many problems w/ apps that fake own MDI.
  129. */
  130. if (fPopup) {
  131. height = pItem->cyBmp;
  132. } else {
  133. height = max((int)pItem->cyBmp, height);
  134. }
  135. } else if (TestMFT(pItem, MFT_OWNERDRAW)) {
  136. // This is an ownerdraw item -- the width and height are stored in
  137. // cxBmp and cyBmp
  138. xxxMNGetBitmapSize(pItem, pwndNotify);
  139. width = pItem->cxBmp;
  140. //
  141. // Ignore height with menu bar now--that's set by user.
  142. //
  143. if (fPopup) {
  144. height = pItem->cyBmp;
  145. // If this item has a popup (hierarchical) menu associated with it, then
  146. // reserve room for the bitmap that tells the user that a hierarchical
  147. // menu exists here.
  148. // B#2966, t-arthb
  149. UserAssert(fPopup == (TestMF(pMenu, MFISPOPUP) != 0));
  150. width = width + (gcxMenuFontChar << 1);
  151. }
  152. }
  153. if ((pItem->lpstr != NULL) && (!TestMFT(pItem, MFT_OWNERDRAW)) ) {
  154. SIZE size;
  155. /*
  156. * This menu item contains a string
  157. */
  158. /*
  159. * We want to keep the menu bar height if this isn't a popup.
  160. */
  161. if (fPopup) {
  162. /* The thickness of mnemonic underscore is CYBORDER and the gap
  163. * between the characters and the underscore is another CYBORDER
  164. */
  165. height = max(height, gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYEDGE));
  166. }
  167. lpMenuString = TextPointer(pItem->lpstr);
  168. xRightJustify = FindCharPosition(lpMenuString, TEXT('\t'));
  169. xxxPSMGetTextExtent(hdc, lpMenuString, xRightJustify, &size);
  170. if (width) {
  171. width += MNXSPACE + size.cx;
  172. } else {
  173. width = size.cx;
  174. }
  175. }
  176. if (fPopup && !TestMFT(pItem, MFT_OWNERDRAW)) {
  177. /*
  178. * Add on space for checkmark, then horz spacing for default & disabled
  179. * plus some left margin.
  180. */
  181. if (TestMF(pMenu, MNS_CHECKORBMP) || !TestMF(pMenu, MNS_NOCHECK)) {
  182. width += gpsi->oembmi[OBI_MENUCHECK].cx;
  183. }
  184. width += MNXSPACE + MNLEFTMARGIN + 2;
  185. height += 2;
  186. }
  187. if (TestMFS(pItem, MFS_DEFAULT)) {
  188. if (hfnOld)
  189. GreSelectFont(hdc, hfnOld);
  190. else
  191. GreSetTextCharacterExtra(hdc, tcExtra);
  192. }
  193. /*
  194. * Loword contains width, high word contains height of item.
  195. */
  196. lppt->x = width;
  197. lppt->y = height;
  198. return(TestMFT(pItem, MFT_OWNERDRAW));
  199. }
  200. /***************************************************************************\
  201. * xxxMNCompute
  202. *
  203. * !
  204. *
  205. * History:
  206. \***************************************************************************/
  207. int xxxMNCompute(
  208. PMENU pMenu,
  209. PWND pwndNotify,
  210. DWORD yMenuTop,
  211. DWORD xMenuLeft,
  212. DWORD cxMax,
  213. LPDWORD lpdwMenuHeight)
  214. {
  215. UINT cItem;
  216. DWORD cxItem;
  217. DWORD cyItem;
  218. DWORD cyItemKeep;
  219. DWORD yPopupTop;
  220. INT cMaxWidth;
  221. DWORD cMaxHeight;
  222. UINT cItemBegCol;
  223. DWORD temp;
  224. PITEM pCurItem;
  225. POINT ptMNItemSize;
  226. BOOL fOwnerDrawItems;
  227. BOOL fMenuBreak;
  228. LPWSTR lpsz;
  229. BOOL fPopupMenu;
  230. DWORD menuHeight = 0;
  231. HDC hdc;
  232. HFONT hOldFont;
  233. PTHREADINFO ptiCurrent = PtiCurrent();
  234. BOOL fStringAndBitmapItems;
  235. CheckLock(pMenu);
  236. CheckLock(pwndNotify);
  237. /*
  238. * Whoever computes the menu, becomes the owner.
  239. */
  240. if (pwndNotify != pMenu->spwndNotify) {
  241. Lock(&pMenu->spwndNotify, pwndNotify);
  242. }
  243. if (lpdwMenuHeight != NULL) {
  244. menuHeight = *lpdwMenuHeight;
  245. }
  246. /*
  247. * Empty menus have a height of zero.
  248. */
  249. if (pMenu->cItems == 0) {
  250. return 0;
  251. }
  252. hdc = _GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE);
  253. hOldFont = GreSelectFont(hdc, ghMenuFont);
  254. /*
  255. * Try to make a non-multirow menu first.
  256. */
  257. pMenu->fFlags &= (~MFMULTIROW);
  258. fPopupMenu = TestMF(pMenu, MFISPOPUP);
  259. if (fPopupMenu) {
  260. /*
  261. * Reset the menu bar height to 0 if this is a popup since we are
  262. * being called from menu.c MN_SIZEWINDOW.
  263. */
  264. menuHeight = 0;
  265. } else if (pwndNotify != NULL) {
  266. pMenu->cxMenu = cxMax;
  267. }
  268. /*
  269. * Initialize the computing variables.
  270. */
  271. cMaxWidth = cyItemKeep = 0L;
  272. cItemBegCol = 0;
  273. cyItem = yPopupTop = yMenuTop;
  274. cxItem = xMenuLeft;
  275. pCurItem = (PITEM)&pMenu->rgItems[0];
  276. /*
  277. * cxTextAlign is used to align the text in all items; this is useful
  278. * in popup menus that mix text only items with bitmap-text items. It's
  279. * set to the max bitmap width plus some spacing.
  280. * Do this for new menus wich use string AND bitmaps on the same item
  281. */
  282. fStringAndBitmapItems = FALSE;
  283. pMenu->cxTextAlign = 0;
  284. /*
  285. * Process each item in the menu.
  286. */
  287. fOwnerDrawItems = FALSE;
  288. for (cItem = 0; cItem < pMenu->cItems; cItem++) {
  289. /*
  290. * If it's not a separator, find the dimensions of the object.
  291. */
  292. if (TestMFT(pCurItem, MFT_SEPARATOR) &&
  293. ( !TestMFT(pCurItem, MFT_OWNERDRAW) ||
  294. (LOWORD(ptiCurrent->dwExpWinVer) < VER40)) ) {
  295. /*
  296. * If version is less than 4.0 don't test the MFT_OWNERDRAW
  297. * flag. Bug 21922; App MaxEda has both separator and Ownerdraw
  298. * flags on. In 3.51 we didn't test the OwnerDraw flag
  299. */
  300. //
  301. // This is a separator. It's drawn as wide as the menu area,
  302. // leaving some space above and below. Since the menu area is
  303. // the max of the items' widths, we set our width to 0 so as not
  304. // to affect the result.
  305. //
  306. pCurItem->cxItem = 0;
  307. pCurItem->cyItem = SYSMET(CYMENUSIZE) / 2;
  308. } else {
  309. /*
  310. * Are we using NT5 strings and bitmaps?
  311. */
  312. fStringAndBitmapItems |= ((pCurItem->hbmp != NULL) && (pCurItem->lpstr != NULL));
  313. /*
  314. * Get the item's X and Y dimensions.
  315. */
  316. if (xxxMNItemSize(pMenu, pwndNotify, hdc, pCurItem, fPopupMenu, &ptMNItemSize))
  317. fOwnerDrawItems = TRUE;
  318. pCurItem->cxItem = ptMNItemSize.x;
  319. pCurItem->cyItem = ptMNItemSize.y;
  320. if (!fPopupMenu && ((pCurItem->hbmp == NULL) || (pCurItem->lpstr != NULL))) {
  321. pCurItem->cxItem += gcxMenuFontChar * 2;
  322. }
  323. }
  324. if (menuHeight != 0)
  325. pCurItem->cyItem = menuHeight;
  326. /*
  327. * If this is the first item, initialize cMaxHeight.
  328. */
  329. if (cItem == 0)
  330. cMaxHeight = pCurItem->cyItem;
  331. /*
  332. * Is this a Pull-Down menu?
  333. */
  334. if (fPopupMenu) {
  335. /*
  336. * If this item has a break or is the last item...
  337. */
  338. if ((fMenuBreak = TestMFT(pCurItem, MFT_BREAK)) ||
  339. pMenu->cItems == cItem + (UINT)1) {
  340. /*
  341. * Keep cMaxWidth around if this is not the last item.
  342. */
  343. temp = cMaxWidth;
  344. if (pMenu->cItems == cItem + (UINT)1) {
  345. if ((int)(pCurItem->cxItem) > cMaxWidth)
  346. temp = pCurItem->cxItem;
  347. }
  348. /*
  349. * Get new width of string from RecalcTabStrings.
  350. */
  351. temp = MNRecalcTabStrings(hdc, pMenu, cItemBegCol,
  352. (UINT)(cItem + (fMenuBreak ? 0 : 1)), temp, cxItem);
  353. /*
  354. * If this item has a break, account for it.
  355. */
  356. if (fMenuBreak) {
  357. //
  358. // Add on space for the etch and a border on either side.
  359. // NOTE: For old apps that do weird stuff with owner
  360. // draw, keep 'em happy by adding on the same amount
  361. // of space we did in 3.1.
  362. //
  363. if (fOwnerDrawItems && !TestWF(pwndNotify, WFWIN40COMPAT))
  364. cxItem = temp + SYSMET(CXBORDER);
  365. else
  366. cxItem = temp + 2 * SYSMET(CXEDGE);
  367. /*
  368. * Reset the cMaxWidth to the current item.
  369. */
  370. cMaxWidth = pCurItem->cxItem;
  371. /*
  372. * Start at the top.
  373. */
  374. cyItem = yPopupTop;
  375. /*
  376. * Save the item where this column begins.
  377. */
  378. cItemBegCol = cItem;
  379. /*
  380. * If this item is also the last item, recalc for this
  381. * column.
  382. */
  383. if (pMenu->cItems == (UINT)(cItem + 1)) {
  384. temp = MNRecalcTabStrings(hdc, pMenu, cItem,
  385. (UINT)(cItem + 1), cMaxWidth, cxItem);
  386. }
  387. }
  388. /*
  389. * If this is the last entry, supply the width.
  390. */
  391. if (pMenu->cItems == cItem + (UINT)1)
  392. pMenu->cxMenu = temp;
  393. }
  394. pCurItem->xItem = cxItem;
  395. pCurItem->yItem = cyItem;
  396. cyItem += pCurItem->cyItem;
  397. if (cyItemKeep < cyItem) {
  398. cyItemKeep = cyItem;
  399. }
  400. } else {
  401. /*
  402. * This a Top Level menu, not a Pull-Down.
  403. */
  404. /*
  405. * Adjust right aligned items before testing for multirow
  406. */
  407. if (pCurItem->lpstr != NULL) {
  408. lpsz = TextPointer(pCurItem->lpstr);
  409. if ((lpsz != NULL) && (*lpsz == CH_HELPPREFIX)) {
  410. pCurItem->cxItem -= gcxMenuFontChar;
  411. }
  412. }
  413. /*
  414. * If this is a new line or a menu break.
  415. */
  416. if ((TestMFT(pCurItem, MFT_BREAK)) ||
  417. (((cxItem + pCurItem->cxItem + gcxMenuFontChar) >
  418. (xMenuLeft + pMenu->cxMenu)) && (cItem != 0))) {
  419. cyItem += cMaxHeight;
  420. cxItem = xMenuLeft;
  421. cMaxHeight = pCurItem->cyItem;
  422. pMenu->fFlags |= MFMULTIROW;
  423. }
  424. pCurItem->yItem = cyItem;
  425. pCurItem->xItem = cxItem;
  426. cxItem += pCurItem->cxItem;
  427. }
  428. if (cMaxWidth < (int)(pCurItem->cxItem)) {
  429. cMaxWidth = pCurItem->cxItem;
  430. }
  431. if (cMaxHeight != pCurItem->cyItem) {
  432. if (cMaxHeight < pCurItem->cyItem)
  433. cMaxHeight = pCurItem->cyItem;
  434. if (!fPopupMenu) {
  435. menuHeight = cMaxHeight;
  436. }
  437. }
  438. if (!fPopupMenu) {
  439. cyItemKeep = cyItem + cMaxHeight;
  440. }
  441. pCurItem++;
  442. }
  443. /*
  444. * Determine where the strings should be drawn so they are aligned.
  445. * The alignment is only for popup (vertical) menus (see
  446. * xxxRealDrawMenuItem). The actual space depends on the MNS_NOCHECK
  447. * and MNS_CHECKORBMP styles Multicolumn menus don't get aligment (now
  448. * that we have scrollbars, multicolumn is out).
  449. */
  450. if (!fStringAndBitmapItems || (cItemBegCol != 0)) {
  451. pMenu->cxTextAlign = 0;
  452. } else if (TestMF(pMenu, MNS_NOCHECK)) {
  453. pMenu->cxTextAlign += MNXSPACE;
  454. } else if (TestMF(pMenu, MNS_CHECKORBMP)) {
  455. pMenu->cxTextAlign = max(pMenu->cxTextAlign, (UINT)gpsi->oembmi[OBI_MENUCHECK].cx);
  456. pMenu->cxTextAlign += MNXSPACE;
  457. } else {
  458. pMenu->cxTextAlign += gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE;
  459. }
  460. /*
  461. * Add the left margin
  462. */
  463. if (pMenu->cxTextAlign != 0) {
  464. pMenu->cxTextAlign += MNLEFTMARGIN;
  465. }
  466. if (cItemBegCol && pMenu->cItems &&
  467. TestMFT(((PITEM)&pMenu->rgItems[0]), MFT_RIGHTJUSTIFY)) {
  468. //
  469. // multi-column, if we are in RtoL mode, reverse the columns
  470. //
  471. pCurItem = &pMenu->rgItems[0];
  472. for (cItem = 0; cItem < pMenu->cItems; cItem++, pCurItem++) {
  473. pCurItem->xItem = pMenu->cxMenu -
  474. (pCurItem->xItem + pCurItem->cxItem);
  475. }
  476. }
  477. GreSelectFont(hdc, hOldFont);
  478. _ReleaseDC(hdc);
  479. pMenu->cyMenu = cyItemKeep - yMenuTop;
  480. if (lpdwMenuHeight != NULL) {
  481. *lpdwMenuHeight = menuHeight;
  482. }
  483. return pMenu->cyMenu;
  484. }
  485. /***************************************************************************\
  486. * MBC_RightJustifyMenu
  487. *
  488. * !
  489. *
  490. * History:
  491. \***************************************************************************/
  492. VOID MBC_RightJustifyMenu(
  493. PMENU pMenu)
  494. {
  495. PITEM pItem;
  496. int cItem;
  497. int iFirstRJItem = MFMWFP_NOITEM;
  498. DWORD xMenuPos;
  499. DWORD yPos;
  500. DWORD xPosStart;
  501. DWORD xPosEnd;
  502. int cItemEnd;
  503. int cItemStart;
  504. BOOL bIsWin95;
  505. //
  506. // Need to compensate for MDI menus. Need to do all here as Win31/Hebrew did
  507. // this. Also messed up computation, anything non-text was not moved.
  508. //
  509. if (pMenu->cItems == 0) {
  510. return;
  511. }
  512. pItem = (PITEM)&pMenu->rgItems[0];
  513. cItemStart = 0;
  514. if (TestMF(pMenu,MFRTL)) {
  515. bIsWin95 = TestWF(pMenu->spwndNotify, WFWIN40COMPAT);
  516. while (cItemStart < (int)pMenu->cItems) {
  517. if (bIsWin95) {
  518. //
  519. // deal with fake MDI dude.
  520. //
  521. if (!cItemStart && IsMDIItem(pItem)) {
  522. goto StillFindStart;
  523. } else {
  524. break;
  525. }
  526. }
  527. if (TestMFT(pItem, MFT_BITMAP)) {
  528. if (pItem->hbmp > HBMMENU_MAX) {
  529. break;
  530. } else {
  531. goto StillFindStart;
  532. }
  533. }
  534. if (!TestMFT(pItem, MFT_OWNERDRAW)) {
  535. break;
  536. }
  537. StillFindStart:
  538. cItemStart++;
  539. pItem = pMenu->rgItems + cItemStart;
  540. }
  541. //
  542. // Anything before cItems should be left where it is. Now need to find
  543. // the last item to fool with.
  544. //
  545. cItemEnd = pMenu->cItems - 1;
  546. pItem = pMenu->rgItems + cItemEnd;
  547. while (cItemEnd > cItemStart) {
  548. if (bIsWin95) {
  549. //
  550. // fake mdi dudes
  551. //
  552. if (IsMDIItem(pItem)) {
  553. goto StillFindEnd;
  554. } else {
  555. break;
  556. }
  557. }
  558. if (!TestMFT(pItem, MFT_BITMAP) && !TestMFT(pItem, MFT_OWNERDRAW)) {
  559. break;
  560. }
  561. StillFindEnd:
  562. cItemEnd--;
  563. pItem = pMenu->rgItems + cItemEnd;
  564. }
  565. yPos = pMenu->rgItems[0].yItem;
  566. xMenuPos = pMenu->cxMenu ;
  567. xPosStart = xMenuPos; // for 2nd row onward
  568. xPosEnd = pMenu->rgItems[cItemStart].xItem ;
  569. for (cItem = pMenu->cItems-1; cItem > cItemEnd; cItem--) {
  570. //
  571. // Force any MDI dudes back to the top line again.
  572. //
  573. pItem = pMenu->rgItems + cItem;
  574. xMenuPos = pItem->xItem = xMenuPos - pItem->cxItem;
  575. pItem->yItem = yPos;
  576. }
  577. for (cItem = cItemStart; cItem <= cItemEnd; cItem++) {
  578. pItem = pMenu->rgItems + cItem;
  579. if (xMenuPos - pItem->cxItem > xPosEnd) {
  580. xMenuPos -= pItem->cxItem;
  581. } else {
  582. xMenuPos = xPosStart - pItem->cxItem;
  583. yPos += pItem->cyItem;
  584. xPosEnd = 0;
  585. }
  586. pItem->xItem = xMenuPos;
  587. pItem->yItem = yPos;
  588. }
  589. } else {
  590. // B#4055
  591. // Use signed arithmetic so comparison cItem >= iFirstRJItem won't
  592. // cause underflow.
  593. for (cItem = 0; cItem < (int)pMenu->cItems; cItem++) {
  594. // Find the first item which is right justified.
  595. if (TestMFT((pMenu->rgItems + cItem), MFT_RIGHTJUSTIFY)) {
  596. iFirstRJItem = cItem;
  597. xMenuPos = pMenu->cxMenu + pMenu->rgItems[0].xItem;
  598. for (cItem = (int)pMenu->cItems - 1; cItem >= iFirstRJItem; cItem--) {
  599. pItem = pMenu->rgItems + cItem;
  600. xMenuPos -= pItem->cxItem;
  601. if (pItem->xItem < xMenuPos)
  602. pItem->xItem = xMenuPos;
  603. }
  604. return;
  605. }
  606. }
  607. }
  608. }
  609. /***************************************************************************\
  610. * xxxMenuBarCompute
  611. *
  612. * Returns the height of the menubar menu. yMenuTop, xMenuLeft, and
  613. * cxMax are used when computing the height/width of top level menu bars in
  614. * windows.
  615. *
  616. *
  617. * History:
  618. \***************************************************************************/
  619. int xxxMenuBarCompute(
  620. PMENU pMenu,
  621. PWND pwndNotify,
  622. DWORD yMenuTop,
  623. DWORD xMenuLeft,
  624. int cxMax)
  625. {
  626. int size;
  627. /* menuHeight is set by MNCompute when dealing with a top level menu and
  628. * not all items in the menu bar have the same height. Thus, by setting
  629. * menuHeight, MNCompute is called a second time to set every item to the
  630. * same height. The actual value stored in menuHeight is the maximum
  631. * height of all the menu bar items
  632. */
  633. DWORD menuHeight = 0;
  634. CheckLock(pwndNotify);
  635. CheckLock(pMenu);
  636. size = xxxMNCompute(pMenu, pwndNotify, yMenuTop, xMenuLeft, cxMax, &menuHeight);
  637. if (!TestMF(pMenu, MFISPOPUP)) {
  638. if (menuHeight != 0) {
  639. /*
  640. * Add a border for the multi-row case.
  641. */
  642. size = xxxMNCompute(pMenu, pwndNotify, yMenuTop, xMenuLeft,
  643. cxMax, &menuHeight);
  644. }
  645. /*
  646. * Right justification of HELP items is only needed on top level
  647. * menus.
  648. */
  649. MBC_RightJustifyMenu(pMenu);
  650. }
  651. /*
  652. * There's an extra border underneath the menu bar, if it's not empty!
  653. */
  654. return(size ? size + SYSMET(CYBORDER) : size);
  655. }
  656. /***************************************************************************\
  657. * xxxMNRecomputeBarIfNeeded
  658. *
  659. * !
  660. *
  661. * History:
  662. \***************************************************************************/
  663. VOID xxxMNRecomputeBarIfNeeded(
  664. PWND pwndNotify,
  665. PMENU pMenu)
  666. {
  667. int cxFrame;
  668. int cyFrame;
  669. UserAssert(!TestMF(pMenu, MFISPOPUP));
  670. CheckLock(pwndNotify);
  671. CheckLock(pMenu);
  672. if (!TestMF(pMenu, MFSYSMENU)
  673. && ((pMenu->spwndNotify != pwndNotify) || !pMenu->cxMenu || !pMenu->cyMenu)) {
  674. int cBorders;
  675. cBorders = GetWindowBorders(pwndNotify->style, pwndNotify->ExStyle, TRUE, FALSE);
  676. cxFrame = cBorders * SYSMET(CXBORDER);
  677. cyFrame = cBorders * SYSMET(CYBORDER);
  678. cyFrame += GetCaptionHeight(pwndNotify);
  679. // The width passed in this call was larger by cxFrame;
  680. // Fix for Bug #11466 - Fixed by SANKAR - 01/06/92 --
  681. xxxMenuBarCompute(pMenu, pwndNotify, cyFrame, cxFrame,
  682. (pwndNotify->rcWindow.right - pwndNotify->rcWindow.left) - cxFrame * 2);
  683. }
  684. }
  685. /***************************************************************************\
  686. * RecalcTabStrings
  687. *
  688. * !
  689. *
  690. * History:
  691. * 10-11-90 JimA Translated from ASM
  692. \***************************************************************************/
  693. DWORD MNRecalcTabStrings(
  694. HDC hdc,
  695. PMENU pMenu,
  696. UINT iBeg,
  697. UINT iEnd,
  698. DWORD xTab,
  699. DWORD hCount)
  700. {
  701. UINT i;
  702. UINT cOwnerDraw;
  703. int adx;
  704. int maxWidth = 0;
  705. int cx;
  706. PITEM pItem;
  707. CheckLock(pMenu);
  708. xTab += hCount;
  709. if ((iBeg >= pMenu->cItems) || (iBeg > iEnd))
  710. goto SeeYa;
  711. cOwnerDraw = 0;
  712. for (i = iBeg, pItem = pMenu->rgItems + iBeg; i < iEnd; pItem++, i++) {
  713. adx = 0;
  714. /*
  715. * Subtract hCount to make dxTab relative to start of column for
  716. * multi-column menus.
  717. */
  718. pItem->dxTab = xTab - hCount;
  719. // Skip non-string or empty string items
  720. if ((pItem->lpstr != NULL) && !TestMFT(pItem, MFT_OWNERDRAW)) {
  721. LPWSTR lpString = TextPointer(pItem->lpstr);
  722. int tp;
  723. SIZE size;
  724. // Are there any tabs?
  725. tp = FindCharPosition(lpString, TEXT('\t'));
  726. if (tp < (int) pItem->cch) {
  727. PTHREADINFO ptiCurrent = PtiCurrentShared();
  728. if (CALL_LPK(ptiCurrent)) {
  729. xxxClientGetTextExtentPointW(hdc, lpString + tp + 1,
  730. pItem->cch - tp - 1, &size);
  731. } else {
  732. GreGetTextExtentW(hdc, lpString + tp + 1,
  733. pItem->cch - tp - 1, &size, GGTE_WIN3_EXTENT);
  734. }
  735. adx = gcxMenuFontChar + size.cx;
  736. }
  737. } else if (TestMFT(pItem, MFT_OWNERDRAW))
  738. cOwnerDraw++;
  739. adx += xTab;
  740. if (adx > maxWidth)
  741. maxWidth = adx;
  742. }
  743. /*
  744. * Add on space for hierarchical arrow. So basically, popup menu items
  745. * can have 4 columns:
  746. * (1) Checkmark
  747. * (2) Text
  748. * (3) Tabbed text for accel
  749. * (4) Hierarchical arrow
  750. *
  751. * But, we only do this if at least one item isn't ownerdraw
  752. * and if there's at least one submenu in the popup.
  753. */
  754. if (cOwnerDraw != (iEnd - iBeg)) {
  755. maxWidth += gcxMenuFontChar + gpsi->oembmi[OBI_MENUCHECK].cx;
  756. }
  757. cx = maxWidth - hCount;
  758. for (i = iBeg, pItem = pMenu->rgItems + iBeg; i < iEnd; pItem++, i++)
  759. pItem->cxItem = cx;
  760. SeeYa:
  761. return(maxWidth);
  762. }
  763. /***************************************************************************\
  764. * GetMenuPwnd
  765. *
  766. * This function is used by xxxGetMenuItemRect and xxxMenuItemFromPoint
  767. * which expect a pointer to the menu window for popup menus.
  768. *
  769. * In 4.0, apps had to go the extra mile to find the menu window; but this
  770. * is bogus since menu windows are an internally thing not directly exposed
  771. * to applications.
  772. *
  773. * 08/19/97 GerardoB Created
  774. \***************************************************************************/
  775. PWND GetMenuPwnd(
  776. PWND pwnd,
  777. PMENU pmenu)
  778. {
  779. if (TestMF(pmenu, MFISPOPUP)) {
  780. if ((pwnd == NULL) || (GETFNID(pwnd) != FNID_MENU)) {
  781. PPOPUPMENU ppopup = MNGetPopupFromMenu(pmenu, NULL);
  782. if (ppopup != NULL) {
  783. UserAssert(ppopup->spmenu == pmenu);
  784. pwnd = ppopup->spwndPopupMenu;
  785. }
  786. }
  787. }
  788. return pwnd;
  789. }
  790. /***************************************************************************\
  791. * GetMenuItemRect
  792. \***************************************************************************/
  793. BOOL xxxGetMenuItemRect(
  794. PWND pwnd,
  795. PMENU pMenu,
  796. UINT uIndex,
  797. LPRECT lprcScreen)
  798. {
  799. PITEM pItem;
  800. int dx, dy;
  801. BOOL fRTL;
  802. CheckLock(pwnd);
  803. CheckLock(pMenu);
  804. SetRectEmpty(lprcScreen);
  805. if (uIndex >= pMenu->cItems) {
  806. return FALSE;
  807. }
  808. /*
  809. * Raid #315084: Compatiblity with NT4/Win95/98
  810. *
  811. * WordPerfect does a long complex way to calc the menu rect
  812. * by calling this API. It calls GetMenuItemRect() with the app's
  813. * window.
  814. */
  815. if (pwnd == NULL || TestWF(pwnd, WFWIN50COMPAT)) {
  816. pwnd = GetMenuPwnd(pwnd, pMenu);
  817. }
  818. /*
  819. * If no pwnd, no go.
  820. * IMPORTANT: For MFISPOPUP we might get a different pwnd but we don't lock
  821. * it because we won't call back.
  822. */
  823. if (pwnd == NULL) {
  824. return FALSE;
  825. }
  826. fRTL = TestWF(pwnd, WEFLAYOUTRTL);
  827. if (TestMF(pMenu, MFISPOPUP)) {
  828. if (fRTL) {
  829. dx = pwnd->rcClient.right;
  830. } else {
  831. dx = pwnd->rcClient.left;
  832. }
  833. dy = pwnd->rcClient.top;
  834. } else {
  835. xxxMNRecomputeBarIfNeeded(pwnd, pMenu);
  836. if (fRTL) {
  837. dx = pwnd->rcWindow.right;
  838. } else {
  839. dx = pwnd->rcWindow.left;
  840. }
  841. dy = pwnd->rcWindow.top;
  842. }
  843. if (uIndex >= pMenu->cItems) {
  844. return FALSE;
  845. }
  846. pItem = pMenu->rgItems + uIndex;
  847. lprcScreen->right = pItem->cxItem;
  848. lprcScreen->bottom = pItem->cyItem;
  849. if (fRTL) {
  850. dx -= (pItem->cxItem + pItem->xItem);
  851. } else {
  852. dx += pItem->xItem;
  853. }
  854. dy += pItem->yItem;
  855. OffsetRect(lprcScreen, dx, dy);
  856. return TRUE;
  857. }
  858. /***************************************************************************\
  859. * xxxMenuItemFromPoint
  860. \***************************************************************************/
  861. int xxxMenuItemFromPoint(
  862. PWND pwnd,
  863. PMENU pMenu,
  864. POINT ptScreen)
  865. {
  866. CheckLock(pwnd);
  867. CheckLock(pMenu);
  868. /*
  869. * If no pwnd, no go.
  870. *
  871. * IMPORTANT: For MFISPOPUP we might get a different pwnd but we don't lock
  872. * it because we won't call back.
  873. */
  874. pwnd = GetMenuPwnd(pwnd, pMenu);
  875. if (pwnd == NULL) {
  876. return MFMWFP_NOITEM;
  877. }
  878. if (!TestMF(pMenu, MFISPOPUP)) {
  879. xxxMNRecomputeBarIfNeeded(pwnd, pMenu);
  880. }
  881. return MNItemHitTest(pMenu, pwnd, ptScreen);
  882. }
  883. PMENU MakeMenuRtoL(
  884. PMENU pMenu,
  885. BOOL bRtoL)
  886. {
  887. PITEM pItem;
  888. int cItem;
  889. if (bRtoL) {
  890. SetMF(pMenu,MFRTL);
  891. } else {
  892. ClearMF(pMenu,MFRTL);
  893. }
  894. for (cItem = 0; cItem < (int)pMenu->cItems; cItem++) {
  895. pItem = pMenu->rgItems + cItem;
  896. if (bRtoL) {
  897. SetMFT(pItem, MFT_RIGHTJUSTIFY);
  898. SetMFT(pItem, MFT_RIGHTORDER);
  899. } else {
  900. ClearMFT(pItem, MFT_RIGHTJUSTIFY);
  901. ClearMFT(pItem, MFT_RIGHTORDER);
  902. }
  903. if (pItem->spSubMenu) {
  904. MakeMenuRtoL(pItem->spSubMenu, bRtoL);
  905. }
  906. }
  907. return pMenu;
  908. }
  909. /***************************************************************************\
  910. * xxxCalcMenuBar
  911. *
  912. * 3/8/2000 vadimg created
  913. \***************************************************************************/
  914. UINT xxxCalcMenuBar(
  915. PWND pwnd,
  916. int iLeftOffset,
  917. int iRightOffset,
  918. int iTopOffset,
  919. LPCRECT prcWnd)
  920. {
  921. PMENU pMenu;
  922. UINT cyMenu, cxMenuMax;
  923. TL tlpMenu;
  924. CheckLock(pwnd);
  925. pMenu = pwnd->spmenu;
  926. if (TestwndChild(pwnd) || pMenu == NULL) {
  927. return 0;
  928. }
  929. ThreadLockMenuNoModify(pMenu, &tlpMenu);
  930. cxMenuMax = (prcWnd->right - iRightOffset) - (prcWnd->left + iLeftOffset);
  931. xxxMenuBarCompute(pMenu, pwnd, iTopOffset, iLeftOffset, cxMenuMax);
  932. cyMenu = pMenu->cyMenu;
  933. ThreadUnlockMenuNoModify(&tlpMenu);
  934. return cyMenu;
  935. }