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.

2485 lines
75 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mndraw.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Menu Painting 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. #define SRCSTENCIL 0x00B8074AL
  15. #define MENU_STRLEN 255
  16. /***************************************************************************\
  17. * MNIsCachedBmpOnly
  18. *
  19. * 04/02/97 GerardoB Created
  20. \***************************************************************************/
  21. __inline BOOL MNIsCachedBmpOnly(
  22. PITEM pItem)
  23. {
  24. return TestMFS(pItem, MFS_CACHEDBMP) && (pItem->lpstr == NULL);
  25. }
  26. /***************************************************************************\
  27. * MNDrawHilite
  28. *
  29. * Don't draw the hilite if:
  30. * The insertion bar is on (MFS_GAPDROP)
  31. * or this is a cached bitmap (close, min, max, etc) with no text
  32. *
  33. * 08/12/96 GerardoB Ported From Memphis.
  34. \***************************************************************************/
  35. BOOL MNDrawHilite(
  36. PITEM pItem)
  37. {
  38. return TestMFS(pItem, MFS_HILITE)
  39. && !TestMFS(pItem, MFS_GAPDROP)
  40. && !MNIsCachedBmpOnly(pItem);
  41. }
  42. /***************************************************************************\
  43. * MNDrawMenu3DHotTracking
  44. *
  45. * 03/10/97 yutakas created
  46. * 04/07/97 vadimg ported from Memphis
  47. \***************************************************************************/
  48. VOID MNDrawMenu3DHotTracking(
  49. HDC hdc,
  50. PMENU pMenu,
  51. PITEM pItem)
  52. {
  53. HBRUSH hbrTopLeft, hbrBottomRight;
  54. BOOL fDrawEdge;
  55. UserAssertMsg0(!MNIsFlatMenu(), "3D Hot Tracking not valid for flat menus");
  56. if (pItem->hbmp && TestMFS(pItem, MFS_CACHEDBMP)) {
  57. return;
  58. }
  59. fDrawEdge = FALSE;
  60. if (!TestMF(pMenu, MFISPOPUP)) {
  61. if (TestMFS(pItem, MFS_HILITE)) {
  62. hbrTopLeft = SYSHBR(3DSHADOW);
  63. hbrBottomRight = SYSHBR(3DHILIGHT);
  64. SetMFS(pItem, MFS_HOTTRACKDRAWN);
  65. fDrawEdge = TRUE;
  66. } else if (TestMFS(pItem, MFS_HOTTRACK)) {
  67. hbrTopLeft = SYSHBR(3DHILIGHT);
  68. hbrBottomRight = SYSHBR(3DSHADOW);
  69. SetMFS(pItem, MFS_HOTTRACKDRAWN);
  70. fDrawEdge = TRUE;
  71. } else if (TestMFS(pItem, MFS_HOTTRACKDRAWN)) {
  72. if (pMenu->hbrBack == NULL) {
  73. hbrTopLeft = SYSHBR(MENU);
  74. hbrBottomRight = SYSHBR(MENU);
  75. } else {
  76. hbrTopLeft = pMenu->hbrBack;
  77. hbrBottomRight = pMenu->hbrBack;
  78. }
  79. ClearMFS(pItem, MFS_HOTTRACKDRAWN);
  80. fDrawEdge = TRUE;
  81. }
  82. }
  83. if (fDrawEdge) {
  84. int x = pItem->xItem, y = pItem->yItem;
  85. int cx = pItem->cxItem, cy = pItem->cyItem;
  86. HBRUSH hbrOld = GreSelectBrush(hdc, hbrTopLeft);
  87. GrePatBlt(hdc, x, y, cx - CXMENU3DEDGE, CYMENU3DEDGE, PATCOPY);
  88. GrePatBlt(hdc, x, y, CXMENU3DEDGE, cy - CYMENU3DEDGE, PATCOPY);
  89. GreSelectBrush(hdc, hbrBottomRight);
  90. GrePatBlt(hdc, x, y + cy - CYMENU3DEDGE, cx - CXMENU3DEDGE, CYMENU3DEDGE, PATCOPY);
  91. GrePatBlt(hdc, x + cx - CYMENU3DEDGE, y, CXMENU3DEDGE, cy, PATCOPY);
  92. GreSelectBrush(hdc, hbrOld);
  93. }
  94. }
  95. /***************************************************************************\
  96. * MNDrawArrow
  97. *
  98. * Redraws the specified arrow (uArrow) in a scrollable menu (ppopup) to reflect
  99. * its current state of enabled or disabled, drawing it in HOTLIGHT if fOn is
  100. * TRUE.
  101. *
  102. * 08/12/96 GerardoB Ported From Memphis.
  103. \***************************************************************************/
  104. VOID MNDrawArrow(
  105. HDC hdcIn,
  106. PPOPUPMENU ppopup,
  107. UINT uArrow)
  108. {
  109. PWND pwnd = ppopup->spwndPopupMenu;
  110. HDC hdc;
  111. int x, y;
  112. DWORD dwBmp;
  113. DWORD dwAtCheck;
  114. DWORD dwState;
  115. if (ppopup->spmenu->dwArrowsOn == MSA_OFF) {
  116. return;
  117. }
  118. if (hdcIn == NULL) {
  119. hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE);
  120. } else {
  121. hdc = hdcIn;
  122. }
  123. x = SYSMET(CXFIXEDFRAME);
  124. if (!TestMF(ppopup->spmenu, MNS_NOCHECK)) {
  125. /*
  126. * Win9x: x += MNByteAlignItem(oemInfo.bm[OBI_MENUCHECK].cx);
  127. */
  128. x += gpsi->oembmi[OBI_MENUCHECK].cx;
  129. } else {
  130. x += SYSMET(CXEDGE) * 2;
  131. }
  132. if (uArrow == MFMWFP_UPARROW) {
  133. y = SYSMET(CXFIXEDFRAME);
  134. dwBmp = OBI_MENUARROWUP;
  135. dwAtCheck = MSA_ATTOP;
  136. dwState = DFCS_MENUARROWUP;
  137. } else {
  138. y = pwnd->rcWindow.bottom - pwnd->rcWindow.top - SYSMET(CYFIXEDFRAME) - gcyMenuScrollArrow;
  139. dwBmp = OBI_MENUARROWDOWN;
  140. dwAtCheck = MSA_ATBOTTOM;
  141. dwState = DFCS_MENUARROWDOWN;
  142. }
  143. if (ppopup->spmenu->dwArrowsOn == dwAtCheck) {
  144. /*
  145. * go 2 ahead to inactive state bitmap
  146. */
  147. dwBmp += 2;
  148. dwState |= DFCS_INACTIVE;
  149. }
  150. if (ppopup->spmenu->hbrBack != NULL) {
  151. /*
  152. * For menus with background brushes, we can't do a straight blt
  153. * of the scroll arrows 'cause the background wouldn't be right;
  154. * need to call DrawFrameControl with DFCS_TRANSPARENT instead.
  155. */
  156. RECT rc;
  157. rc.top = y;
  158. rc.left = x;
  159. rc.right = x + gpsi->oembmi[OBI_MENUARROWUP].cx;
  160. rc.bottom = y + gpsi->oembmi[OBI_MENUARROWUP].cy;
  161. DrawFrameControl(hdc, &rc, DFC_MENU, dwState | DFCS_TRANSPARENT);
  162. } else {
  163. BitBltSysBmp(hdc, x, y, dwBmp);
  164. BitBltSysBmp(hdc, x, y, dwBmp);
  165. }
  166. if (hdcIn == NULL) {
  167. _ReleaseDC(hdc);
  168. }
  169. }
  170. /***************************************************************************\
  171. * DrawOutline
  172. *
  173. * Draws an outline of a specified thickness using a given brush.
  174. *
  175. * History:
  176. * 03-03-00 JStall - Created
  177. \***************************************************************************/
  178. VOID DrawOutline(
  179. HDC hdc,
  180. int x,
  181. int y,
  182. int w,
  183. int h,
  184. int nThick,
  185. HBRUSH hbrFill)
  186. {
  187. POLYPATBLT rgPPB[4];
  188. /* Left */
  189. rgPPB[0].x = x;
  190. rgPPB[0].y = y;
  191. rgPPB[0].cx = nThick;
  192. rgPPB[0].cy = h;
  193. rgPPB[0].BrClr.hbr = hbrFill;
  194. /* Top */
  195. rgPPB[1].x = x;
  196. rgPPB[1].y = y;
  197. rgPPB[1].cx = w;
  198. rgPPB[1].cy = nThick;
  199. rgPPB[1].BrClr.hbr = hbrFill;
  200. /* Right */
  201. rgPPB[2].x = x + w - nThick;
  202. rgPPB[2].y = y;
  203. rgPPB[2].cx = nThick;
  204. rgPPB[2].cy = h;
  205. rgPPB[2].BrClr.hbr = hbrFill;
  206. /* Bottom */
  207. rgPPB[3].x = x;
  208. rgPPB[3].y = y + h - nThick;
  209. rgPPB[3].cx = w;
  210. rgPPB[3].cy = nThick;
  211. rgPPB[3].BrClr.hbr = hbrFill;
  212. GrePolyPatBlt(hdc, PATCOPY, rgPPB, 4, PPB_BRUSH);
  213. }
  214. /***************************************************************************\
  215. * MNDrawEdge
  216. *
  217. * Draws the edge of the menu for flat-menus. The given rectangle can
  218. * optionally be modified to exclude the drawn area.
  219. *
  220. * History:
  221. * 03-03-00 JStall - Created
  222. \***************************************************************************/
  223. VOID MNDrawEdge(
  224. PMENU pmenu,
  225. HDC hdc,
  226. RECT *prcDraw,
  227. UINT nFlags)
  228. {
  229. int nWidth, nHeight, nBorder, nTemp;
  230. HBRUSH hbr;
  231. UserAssertMsg0(MNIsFlatMenu(), "Should only be called for flat menus");
  232. nWidth = prcDraw->right - prcDraw->left;
  233. nHeight = prcDraw->bottom - prcDraw->top;
  234. nBorder = SYSMET(CXBORDER);
  235. /*
  236. * Draw the flat outline around the menu
  237. */
  238. DrawOutline(hdc, 0, 0, nWidth, nHeight, nBorder, SYSHBR(BTNSHADOW));
  239. /*
  240. * Draw inside the menu
  241. */
  242. nTemp = 2 * nBorder;
  243. if (pmenu->hbrBack == NULL) {
  244. hbr = SYSHBR(MENU);
  245. } else {
  246. hbr = pmenu->hbrBack;
  247. }
  248. DrawOutline(hdc, nBorder, nBorder, nWidth - nTemp, nHeight - nTemp, nTemp, hbr);
  249. /*
  250. * Adjust the given rectangle from the equivalent calls for 3D-menus
  251. * when the non-client area is drawn by DefWindowProc. DrawEdge will
  252. * inset the rectangle 2 times. (once for the inner and once for the
  253. * outer) With the additional InflateRect() the rectangle gets
  254. * insetted 3 times total.
  255. */
  256. if (nFlags == BF_ADJUST) {
  257. nTemp = -3 * nBorder;
  258. InflateRect(prcDraw, nTemp, nTemp);
  259. }
  260. }
  261. /***************************************************************************\
  262. * MNDrawFullNC
  263. *
  264. * Performs the custom nonclient painting needed for scrollable menus.
  265. * Assumes that the given menu is a scrollable menu.
  266. *
  267. * History:
  268. * 08-14-96 GerardoB - Ported from Memphis
  269. \***************************************************************************/
  270. VOID MNDrawFullNC(
  271. PWND pwnd,
  272. HDC hdcIn,
  273. PPOPUPMENU ppopup)
  274. {
  275. RECT rc;
  276. HDC hdc;
  277. HBRUSH hbrOld;
  278. int yTop, yBottom;
  279. POINT ptOrg;
  280. if (hdcIn == NULL) {
  281. hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE);
  282. } else {
  283. hdc = hdcIn;
  284. }
  285. rc.left = rc.top = 0;
  286. rc.right = pwnd->rcWindow.right - pwnd->rcWindow.left;
  287. rc.bottom = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
  288. if (MNIsFlatMenu()) {
  289. MNDrawEdge(ppopup->spmenu, hdcIn, &rc, BF_ADJUST);
  290. } else {
  291. DrawEdge(hdc, &rc, EDGE_RAISED, (BF_RECT | BF_ADJUST));
  292. DrawFrame(hdc, &rc, 1, DF_3DFACE);
  293. InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  294. }
  295. yTop = rc.top;
  296. yBottom = rc.bottom - gcyMenuScrollArrow;
  297. GreGetBrushOrg(hdc, &ptOrg);
  298. if (ppopup->spmenu->hbrBack != NULL) {
  299. GreSetBrushOrg(hdc, 0,
  300. -(int)MNGetToppItem(ppopup->spmenu)->yItem, NULL);
  301. hbrOld = GreSelectBrush(hdc, ppopup->spmenu->hbrBack);
  302. } else {
  303. hbrOld = GreSelectBrush(hdc, SYSHBR(MENU));
  304. }
  305. rc.right -= rc.left;
  306. GrePatBlt(hdc, rc.left, yTop, rc.right, gcyMenuScrollArrow, PATCOPY);
  307. MNDrawArrow(hdc, ppopup, MFMWFP_UPARROW);
  308. GrePatBlt(hdc, rc.left, yBottom, rc.right, gcyMenuScrollArrow, PATCOPY);
  309. MNDrawArrow(hdc, ppopup, MFMWFP_DOWNARROW);
  310. GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, NULL);
  311. GreSelectBrush(hdc, hbrOld);
  312. if (hdcIn == NULL) {
  313. _ReleaseDC(hdc);
  314. }
  315. }
  316. /***************************************************************************\
  317. * MNEraseBackground
  318. *
  319. * Erases the background making sure that the background pattern (i.e, watermark)
  320. * aligns with the pattern in the nonclient area.
  321. *
  322. * History:
  323. * 08-23-96 GerardoB - Created
  324. \***************************************************************************/
  325. VOID MNEraseBackground(
  326. HDC hdc,
  327. PMENU pmenu,
  328. int x,
  329. int y,
  330. int cx,
  331. int cy)
  332. {
  333. BOOL fSetOrg;
  334. HBRUSH hbrOld;
  335. POINT ptOrg;
  336. UserAssert(pmenu->hbrBack != NULL);
  337. fSetOrg = TRUE;
  338. GreGetBrushOrg(hdc, &ptOrg);
  339. /*
  340. * If we have scrollbars
  341. */
  342. if (pmenu->dwArrowsOn != MSA_OFF) {
  343. /*
  344. * If not drawing on the client area only
  345. */
  346. if (TestMF(pmenu, MFWINDOWDC)) {
  347. ptOrg.x = 0;
  348. ptOrg.y = -(int)MNGetToppItem(pmenu)->yItem;
  349. } else {
  350. ptOrg.x = -MNXBORDER;
  351. ptOrg.y = -MNYBORDER - gcyMenuScrollArrow - MNGetToppItem(pmenu)->yItem;
  352. }
  353. } else {
  354. if (TestMF(pmenu, MFWINDOWDC)) {
  355. ptOrg.x = MNXBORDER;
  356. ptOrg.y = MNYBORDER;
  357. } else {
  358. fSetOrg = FALSE;
  359. }
  360. }
  361. if (fSetOrg) {
  362. GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, &ptOrg);
  363. }
  364. hbrOld = GreSelectBrush(hdc, pmenu->hbrBack);
  365. GrePatBlt(hdc, x, y, cx, cy, PATCOPY);
  366. if (fSetOrg) {
  367. GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, NULL);
  368. }
  369. GreSelectBrush(hdc, hbrOld);
  370. }
  371. /***************************************************************************\
  372. * MNAnimate
  373. *
  374. * If fIterate is TRUE, then perform the next iteration in the menu animation
  375. * sequence. If fIterate is FALSE, terminate the animation sequence.
  376. *
  377. * History:
  378. * 07-23-96 GerardoB - fixed up for 5.0
  379. \***************************************************************************/
  380. VOID MNAnimate(
  381. PMENUSTATE pMenuState,
  382. BOOL fIterate)
  383. {
  384. DWORD dwTimeElapsed;
  385. int x, y, xOff, yOff, xLast, yLast;
  386. if (TestFadeFlags(FADE_MENU)) {
  387. if (!fIterate) {
  388. StopFade();
  389. }
  390. return;
  391. }
  392. /*
  393. * If we're not animating, bail
  394. */
  395. if (pMenuState->hdcWndAni == NULL) {
  396. return;
  397. }
  398. /*
  399. * The active popup must be visible. It's supposed to be the
  400. * window we're animating
  401. */
  402. UserAssert(TestWF(pMenuState->pGlobalPopupMenu->spwndActivePopup, WFVISIBLE));
  403. /*
  404. * End animation if asked to do so, if it's taking too long
  405. * or someone is waiting for the critical section
  406. */
  407. dwTimeElapsed = NtGetTickCount() - pMenuState->dwAniStartTime;
  408. if (!fIterate
  409. || (dwTimeElapsed > CMS_QANIMATION)
  410. || (ExGetExclusiveWaiterCount(gpresUser) > 0)
  411. || (ExGetSharedWaiterCount(gpresUser) > 0)) {
  412. GreBitBlt(pMenuState->hdcWndAni, 0, 0, pMenuState->cxAni, pMenuState->cyAni, pMenuState->hdcAni,
  413. 0, 0, SRCCOPY | NOMIRRORBITMAP, 0xFFFFFF);
  414. goto AnimationCompleted;
  415. }
  416. /*
  417. * Remember current animation point and calculate new one
  418. */
  419. xLast = pMenuState->ixAni;
  420. yLast = pMenuState->iyAni;
  421. if (pMenuState->iAniDropDir & PAS_HORZ) {
  422. pMenuState->ixAni = MultDiv(gcxMenuFontChar, dwTimeElapsed, CMS_QANIMATION / 20);
  423. if (pMenuState->ixAni > pMenuState->cxAni) {
  424. pMenuState->ixAni = pMenuState->cxAni;
  425. }
  426. }
  427. if (pMenuState->iAniDropDir & PAS_VERT) {
  428. pMenuState->iyAni = MultDiv(gcyMenuFontChar, dwTimeElapsed, CMS_QANIMATION / 10);
  429. if (pMenuState->iyAni > pMenuState->cyAni) {
  430. pMenuState->iyAni = pMenuState->cyAni;
  431. }
  432. }
  433. /*
  434. * if no change -- bail out
  435. */
  436. if ((pMenuState->ixAni == xLast) && (pMenuState->iyAni == yLast)) {
  437. return;
  438. }
  439. /*
  440. * Calculate source and dest coordinates
  441. */
  442. if (pMenuState->iAniDropDir & PAS_LEFT) {
  443. x = pMenuState->cxAni - pMenuState->ixAni;
  444. xOff = 0;
  445. } else {
  446. xOff = pMenuState->cxAni - pMenuState->ixAni;
  447. x = 0;
  448. }
  449. if (pMenuState->iAniDropDir & PAS_UP) {
  450. y = pMenuState->cyAni - pMenuState->iyAni;
  451. yOff = 0;
  452. } else {
  453. yOff = pMenuState->cyAni - pMenuState->iyAni;
  454. y = 0;
  455. }
  456. /*
  457. * Do it
  458. */
  459. GreBitBlt(pMenuState->hdcWndAni, x, y, pMenuState->ixAni, pMenuState->iyAni,
  460. pMenuState->hdcAni, xOff, yOff, SRCCOPY | NOMIRRORBITMAP, 0xFFFFFF);
  461. /*
  462. * Check if we're done
  463. */
  464. if ((pMenuState->cxAni == pMenuState->ixAni)
  465. && (pMenuState->cyAni == pMenuState->iyAni)) {
  466. AnimationCompleted:
  467. MNDestroyAnimationBitmap(pMenuState);
  468. _ReleaseDC(pMenuState->hdcWndAni);
  469. pMenuState->hdcWndAni = NULL;
  470. _KillTimer(pMenuState->pGlobalPopupMenu->spwndActivePopup, IDSYS_MNANIMATE);
  471. }
  472. }
  473. /***************************************************************************\
  474. * DrawMenuItemCheckMark() -
  475. *
  476. * Draws the proper check mark for the given item. Note that ownerdraw
  477. * items should NOT be passed to this procedure, otherwise we'd draw a
  478. * checkmark for them when they are already going to take care of it.
  479. *
  480. * Returns TRUE is a bitmap was drawn (or at least we attempted to draw it).
  481. *
  482. * History:
  483. \***************************************************************************/
  484. BOOL DrawMenuItemCheckMark(
  485. HDC hdc,
  486. PITEM pItem,
  487. int xPos)
  488. {
  489. int yCenter;
  490. HBITMAP hbm;
  491. DWORD textColorSave;
  492. DWORD bkColorSave;
  493. BOOL fChecked;
  494. POEMBITMAPINFO pOem;
  495. BOOL fRet = TRUE;
  496. DWORD dwFlags = BC_INVERT;
  497. UserAssert(hdc != ghdcMem2);
  498. pOem = gpsi->oembmi + OBI_MENUCHECK;
  499. yCenter = pItem->cyItem - pOem->cy;
  500. if (yCenter < 0)
  501. yCenter = 0;
  502. yCenter /= 2;
  503. fChecked = TestMFS(pItem, MFS_CHECKED);
  504. if (hbm = (fChecked) ? pItem->hbmpChecked : pItem->hbmpUnchecked) {
  505. HBITMAP hbmSave;
  506. // Use the app supplied bitmaps.
  507. if (hbmSave = GreSelectBitmap(ghdcMem2, hbm)) {
  508. textColorSave = GreSetTextColor(hdc, 0x00000000L);
  509. bkColorSave = GreSetBkColor (hdc, 0x00FFFFFFL);
  510. if (TestMFT(pItem, MFT_RIGHTORDER))
  511. xPos = pItem->cxItem - pOem->cx;
  512. GreBitBlt(hdc,
  513. xPos,
  514. yCenter,
  515. pOem->cx,
  516. pOem->cy,
  517. ghdcMem2,
  518. 0,
  519. 0,
  520. SRCSTENCIL,
  521. 0x00FFFFFF);
  522. GreSetTextColor(hdc, textColorSave);
  523. GreSetBkColor(hdc, bkColorSave);
  524. GreSelectBitmap(ghdcMem2, hbmSave);
  525. }
  526. } else if (fChecked) {
  527. if (TestMFT(pItem, MFT_RADIOCHECK))
  528. pOem = gpsi->oembmi + OBI_MENUBULLET;
  529. if (TestMFT(pItem, MFT_RIGHTORDER))
  530. xPos = pItem->cxItem - pOem->cx;
  531. // 389917: Mirror active menu's check mark if hdc is mirroed.
  532. if ((GreGetLayout(hdc) & LAYOUT_RTL) && (hdc != gpDispInfo->hdcGray)) {
  533. dwFlags |= BC_NOMIRROR;
  534. }
  535. BltColor(hdc,
  536. NULL,
  537. HDCBITS(),
  538. xPos,
  539. yCenter,
  540. pOem->cx,
  541. pOem->cy,
  542. pOem->x,
  543. pOem->y,
  544. dwFlags);
  545. } else {
  546. fRet = FALSE;
  547. }
  548. return fRet;
  549. }
  550. /***************************************************************************\
  551. * xxxDrawItemtUnderline
  552. *
  553. * Draws or hides an underline for a menu item
  554. *
  555. * 07/23/96 vadimg separated into a separate routine
  556. \***************************************************************************/
  557. VOID xxxDrawItemUnderline(
  558. PITEM pItem,
  559. HDC hdc,
  560. int xLeft,
  561. int yTop,
  562. LPWSTR pszMenu,
  563. LONG lResLo)
  564. {
  565. int cx;
  566. PTHREADINFO ptiCurrent = PtiCurrentShared();
  567. //
  568. // LOWORD of result is 0xFFFF if there is no underlined character.
  569. // Therefore ulX must be valid or be UNDERLINE_RECALC because the item
  570. // or menu mode changed.
  571. //
  572. // Bail out if there isn't one.
  573. //
  574. if (lResLo == 0xFFFF) {
  575. return;
  576. }
  577. //
  578. // For proportional fonts, or if an LPK is installed, find starting
  579. // point of underline.
  580. //
  581. if ((pItem->ulX == UNDERLINE_RECALC) || (PpiCurrent()->dwLpkEntryPoints & LPK_INSTALLED)) {
  582. if (lResLo != 0) {
  583. SIZE size;
  584. if (CALL_LPK(ptiCurrent)) {
  585. xxxClientGetTextExtentPointW(hdc, pszMenu, lResLo, &size);
  586. } else {
  587. GreGetTextExtentW(hdc, pszMenu, lResLo, &size, GGTE_WIN3_EXTENT);
  588. }
  589. pItem->ulX = size.cx - gcxMenuFontOverhang;
  590. } else
  591. pItem->ulX = 0;
  592. }
  593. xLeft += pItem->ulX;
  594. //
  595. // Adjust for proportional font when setting the length of the underline
  596. // and height of text.
  597. //
  598. // Calculate underline width.
  599. if (!pItem->ulWidth) {
  600. SIZE size;
  601. if (CALL_LPK(ptiCurrent)) {
  602. xxxClientGetTextExtentPointW(hdc, pszMenu + lResLo, 1, &size);
  603. } else {
  604. GreGetTextExtentW(hdc, pszMenu + lResLo, 1, &size, GGTE_WIN3_EXTENT);
  605. }
  606. pItem->ulWidth = size.cx - gcxMenuFontOverhang;
  607. }
  608. cx = pItem->ulWidth;
  609. // Get ascent of text (units above baseline) so that underline can be drawn
  610. // below the text
  611. yTop += gcyMenuFontAscent;
  612. // Proper brush should be selected into dc.
  613. GrePatBlt(hdc, xLeft, yTop, pItem->ulWidth, SYSMET(CYBORDER), PATCOPY);
  614. }
  615. /***************************************************************************\
  616. * xxxDrawMenuItemText
  617. *
  618. * Draws menu text with underline.
  619. \***************************************************************************/
  620. VOID xxxDrawMenuItemText(
  621. PITEM pItem,
  622. HDC hdc,
  623. int xLeft,
  624. int yTop,
  625. LPWSTR lpsz,
  626. int cch,
  627. BOOL fShowUnderlines)
  628. {
  629. LONG result;
  630. WCHAR szMenu[MENU_STRLEN], *pchOut;
  631. PTHREADINFO ptiCurrent = PtiCurrentShared();
  632. TL tl;
  633. if (cch > MENU_STRLEN) {
  634. pchOut = (WCHAR*)UserAllocPool((cch+1) * sizeof(WCHAR), TAG_RTL);
  635. if (pchOut == NULL)
  636. return;
  637. ThreadLockPool(ptiCurrent, pchOut, &tl);
  638. } else {
  639. pchOut = szMenu;
  640. }
  641. result = GetPrefixCount(lpsz, cch, pchOut, cch);
  642. if (CALL_LPK(ptiCurrent)) {
  643. xxxClientExtTextOutW(hdc, xLeft, yTop, 0, NULL, pchOut, cch - HIWORD(result), NULL);
  644. } else {
  645. GreExtTextOutW(hdc, xLeft, yTop, 0, NULL, pchOut, cch - HIWORD(result), NULL);
  646. }
  647. if (fShowUnderlines || TEST_BOOL_ACCF(ACCF_KEYBOARDPREF) || TestEffectInvertUP(KEYBOARDCUES)
  648. || (GetAppCompatFlags2(VER40) & GACF2_KCOFF)) {
  649. if (CALL_LPK(ptiCurrent)) {
  650. xxxPSMTextOut(hdc, xLeft, yTop, lpsz, cch, DT_PREFIXONLY);
  651. } else{
  652. xxxDrawItemUnderline(pItem, hdc, xLeft, yTop, pchOut, LOWORD(result));
  653. }
  654. }
  655. if (pchOut != szMenu) {
  656. ThreadUnlockAndFreePool(ptiCurrent, &tl);
  657. }
  658. }
  659. /***************************************************************************\
  660. * xxxSendMenuDrawItemMessage
  661. *
  662. * Sends a WM_DRAWITEM message to the owner of the menu (pMenuState->hwndMenu).
  663. * All state is determined in this routine so HILITE state must be properly
  664. * set before entering this routine..
  665. *
  666. * Revalidation notes:
  667. * This routine must be called with a valid and non-NULL pwnd.
  668. * Revalidation is not required in this routine: no windows are used after
  669. * potentially leaving the critsect.
  670. *
  671. * History:
  672. \***************************************************************************/
  673. VOID xxxSendMenuDrawItemMessage(
  674. HDC hdc,
  675. UINT itemAction,
  676. PMENU pmenu,
  677. PITEM pItem,
  678. BOOL fBitmap,
  679. int iOffset)
  680. {
  681. DRAWITEMSTRUCT dis;
  682. TL tlpwndNotify;
  683. int y;
  684. CheckLock(pmenu);
  685. dis.CtlType = ODT_MENU;
  686. dis.CtlID = 0;
  687. dis.itemID = pItem->wID;
  688. dis.itemAction = itemAction;
  689. dis.itemState =
  690. ((pItem->fState & MF_GRAYED) ? ODS_GRAYED : 0) |
  691. ((pItem->fState & MFS_DEFAULT) ? ODS_DEFAULT : 0) |
  692. ((pItem->fState & MFS_CHECKED) ? ODS_CHECKED : 0) |
  693. ((pItem->fState & MFS_DISABLED) ? ODS_DISABLED : 0) |
  694. (MNDrawHilite(pItem) ? ODS_SELECTED : 0) |
  695. ((pItem->fState & MFS_HOTTRACK) ? ODS_HOTLIGHT : 0) |
  696. (TestMF(pmenu, MFINACTIVE) ? ODS_INACTIVE : 0) |
  697. (!TestMF(pmenu, MFUNDERLINE) ? ODS_NOACCEL : 0);
  698. dis.hwndItem = (HWND)PtoH(pmenu);
  699. dis.hDC = hdc;
  700. y = pItem->yItem;
  701. if (fBitmap) {
  702. y = (pItem->cyItem - pItem->cyBmp) / 2;
  703. }
  704. dis.rcItem.left = iOffset + pItem->xItem;
  705. dis.rcItem.top = y;
  706. dis.rcItem.right = iOffset + pItem->xItem + (fBitmap ? pItem->cxBmp : pItem->cxItem);
  707. dis.rcItem.bottom = y + (fBitmap ? pItem->cyBmp : pItem->cyItem);
  708. dis.itemData = pItem->dwItemData;
  709. if (pmenu->spwndNotify != NULL) {
  710. ThreadLockAlways(pmenu->spwndNotify, &tlpwndNotify);
  711. xxxSendMessage(pmenu->spwndNotify, WM_DRAWITEM, 0, (LPARAM)&dis);
  712. ThreadUnlock(&tlpwndNotify);
  713. }
  714. }
  715. /***************************************************************************\
  716. * CalcbfExtra
  717. *
  718. * History:
  719. * 08-09-96 GerardoB Made into an inline function (code from xxxMenuDraw)
  720. \***************************************************************************/
  721. __inline UINT CalcbfExtra(
  722. VOID)
  723. {
  724. if (SYSRGB(3DHILIGHT) == SYSRGB(MENU) && SYSRGB(3DSHADOW) == SYSRGB(MENU)) {
  725. return BF_FLAT | BF_MONO;
  726. } else {
  727. return 0;
  728. }
  729. }
  730. /***************************************************************************\
  731. * MNDrawInsertionBar
  732. *
  733. * History:
  734. * 11/21/96 GerardoB Created
  735. \***************************************************************************/
  736. VOID MNDrawInsertionBar(
  737. HDC hdc,
  738. PITEM pItem)
  739. {
  740. BOOL fTop;
  741. POLYPATBLT PolyData [3], *ppd;
  742. /*
  743. * If no insertion bar for this item, bail
  744. */
  745. fTop = TestMFS(pItem, MFS_TOPGAPDROP);
  746. if (!fTop && !TestMFS(pItem, (MFS_BOTTOMGAPDROP))) {
  747. return;
  748. }
  749. /*
  750. * Vertical line on the left
  751. */
  752. ppd = PolyData;
  753. ppd->x = pItem->xItem + SYSMET(CXDRAG);
  754. ppd->cx = SYSMET(CXDRAG);
  755. ppd->cy = SYSMET(CYDRAG);
  756. if (fTop) {
  757. ppd->y = pItem->yItem;
  758. } else {
  759. ppd->y = pItem->yItem + pItem->cyItem - ppd->cy;
  760. }
  761. ppd->BrClr.hbr = SYSHBR(HIGHLIGHT);
  762. /*
  763. * Horizontal line in the middle
  764. */
  765. ppd++;
  766. ppd->x = pItem->xItem + (2 * SYSMET(CXDRAG));
  767. ppd->cx = pItem->cxItem - (4 * SYSMET(CXDRAG));
  768. ppd->cy = SYSMET(CYDRAG) / 2;
  769. if (fTop) {
  770. ppd->y = pItem->yItem;
  771. } else {
  772. ppd->y = pItem->yItem + pItem->cyItem - ppd->cy;
  773. }
  774. ppd->BrClr.hbr = PolyData->BrClr.hbr;
  775. /*
  776. * Vertical line on the right
  777. */
  778. ppd++;
  779. ppd->x = pItem->xItem + pItem->cxItem - (2 * SYSMET(CXDRAG));
  780. ppd->cx = PolyData->cx;
  781. ppd->cy = PolyData->cy;
  782. ppd->y = PolyData->y;
  783. ppd->BrClr.hbr = PolyData->BrClr.hbr;
  784. GrePolyPatBlt(hdc, PATCOPY, PolyData, 3, PPB_BRUSH);
  785. }
  786. /***************************************************************************\
  787. * xxxDrawMenuItem
  788. *
  789. * !
  790. *
  791. * History:
  792. \***************************************************************************/
  793. VOID xxxDrawMenuItem(
  794. HDC hdc,
  795. PMENU pMenu,
  796. PITEM pItem,
  797. DWORD dwFlags)
  798. {
  799. BOOL fHilite;
  800. BOOL fFlatMenuBar;
  801. BOOL fFlatMenu;
  802. HFONT hfnOld;
  803. int tcExtra;
  804. UINT uFlags;
  805. int iBkSave;
  806. hfnOld = NULL;
  807. uFlags = DST_COMPLEX;
  808. CheckLock(pMenu);
  809. fFlatMenu = MNIsFlatMenu();
  810. /*
  811. * If the insertion bar is on (MFS_GAPDROP), don't draw the item hilited
  812. */
  813. if (fFlatMenu) {
  814. fHilite = MNDrawHilite(pItem) || TestMFS(pItem, MFS_HOTTRACK) || TestMFS(pItem, MFS_HOTTRACKDRAWN);
  815. fFlatMenuBar = !TestMF(pMenu, MFISPOPUP);
  816. } else {
  817. fHilite = MNDrawHilite(pItem);
  818. fFlatMenuBar = FALSE;
  819. }
  820. if (TestMFS(pItem, MFS_DEFAULT))
  821. {
  822. if (ghMenuFontDef != NULL)
  823. hfnOld = GreSelectFont(hdc, ghMenuFontDef);
  824. else
  825. {
  826. uFlags |= DSS_DEFAULT;
  827. tcExtra = GreGetTextCharacterExtra(hdc);
  828. GreSetTextCharacterExtra(hdc, tcExtra + 1 + (gcxMenuFontChar / gpsi->cxSysFontChar));
  829. }
  830. }
  831. if (TestMFT(pItem, MFT_OWNERDRAW)) {
  832. /*
  833. * If ownerdraw, just set the default menu colors since the app is
  834. * responsible for handling the rest.
  835. */
  836. GreSetTextColor(hdc, SYSRGB(MENUTEXT));
  837. GreSetBkColor(hdc, fFlatMenuBar ? SYSRGB(MENUBAR) : SYSRGB(MENU));
  838. /*
  839. * Send drawitem message since this is an ownerdraw item.
  840. */
  841. xxxSendMenuDrawItemMessage(hdc,
  842. (UINT)((dwFlags & DMI_INVERT) ? ODA_SELECT : ODA_DRAWENTIRE),
  843. pMenu, pItem,FALSE,0);
  844. // Draw the hierarchical arrow for the cascade menu.
  845. if (TestMF(pMenu, MFISPOPUP) && (pItem->spSubMenu != NULL))
  846. {
  847. POEMBITMAPINFO pOem;
  848. HBRUSH hbr = fHilite ? SYSHBR(HIGHLIGHTTEXT) : SYSHBR(MENUTEXT);
  849. pOem = gpsi->oembmi + (TestMFT(pItem, MFT_RIGHTORDER)
  850. ? OBI_MENUARROW_L : OBI_MENUARROW);
  851. // This item has a hierarchical popup associated with it. Draw the
  852. // bitmap dealy to signify its presence. Note we check if fPopup is set
  853. // so that this isn't drawn for toplevel menus that have popups.
  854. BltColor(hdc,
  855. hbr,
  856. HDCBITS(),
  857. TestMFT(pItem, MFT_RIGHTORDER)
  858. ? pItem->xItem + pOem->cx :
  859. pItem->xItem + pItem->cxItem - pOem->cx,
  860. pItem->yItem + max((INT)(pItem->cyItem - 2 - pOem->cy) / 2,
  861. 0),
  862. pOem->cx,
  863. pOem->cy,
  864. pOem->x,
  865. pOem->y,
  866. BC_INVERT);
  867. }
  868. } else {
  869. COLORREF crBack;
  870. COLORREF crFore;
  871. GRAYMENU gm;
  872. HBRUSH hbrBack;
  873. HBRUSH hbrFore;
  874. //
  875. // Setup colors and state
  876. //
  877. if (fHilite) {
  878. if (fFlatMenu) {
  879. crBack = SYSRGB(MENUHILIGHT);
  880. hbrBack = SYSHBR(MENUHILIGHT);
  881. crFore = SYSRGB(HIGHLIGHTTEXT);
  882. hbrFore = SYSHBR(HIGHLIGHTTEXT);
  883. } else {
  884. /*
  885. * If we are not using flat menu's, only draw the highlight if
  886. * the item is a popup menu. If it is in the menubar, don't
  887. * hilight it since it will be beveled.
  888. */
  889. if (TestMF(pMenu, MFISPOPUP)) {
  890. crBack = SYSRGB(HIGHLIGHT);
  891. hbrBack = SYSHBR(HIGHLIGHT);
  892. crFore = SYSRGB(HIGHLIGHTTEXT);
  893. hbrFore = SYSHBR(HIGHLIGHTTEXT);
  894. } else {
  895. goto NormalMenuItem;
  896. }
  897. }
  898. } else if (fFlatMenuBar) {
  899. UserAssertMsg0(fFlatMenu, "fFlatMenuBar should only be set for flat menus");
  900. crFore = SYSRGB(MENUTEXT);
  901. hbrFore = SYSHBR(MENUTEXT);
  902. if (pMenu->hbrBack != NULL) {
  903. crBack = SYSRGB(MENUBAR);
  904. hbrBack = pMenu->hbrBack;
  905. } else {
  906. crBack = SYSRGB(MENUBAR);
  907. hbrBack = SYSHBR(MENUBAR);
  908. }
  909. } else {
  910. NormalMenuItem:
  911. crBack = SYSRGB(MENU);
  912. hbrBack = SYSHBR(MENU);
  913. crFore = SYSRGB(MENUTEXT);
  914. hbrFore = SYSHBR(MENUTEXT);
  915. }
  916. // B#4157 - Lotus doesn't like it if we draw
  917. // its disabled menu items in GRAY, t-arthb
  918. // MAKE SURE MF_GRAYED stays 0x0001 NOT 0x0003 for this to fix
  919. /*
  920. * System bitmaps are already grayed so don't draw them disabled
  921. * if the menu is inactive
  922. */
  923. if (!MNIsCachedBmpOnly(pItem)
  924. && (TestMFS(pItem, MF_GRAYED) || TestMF(pMenu, MFINACTIVE))) {
  925. //
  926. // Only do embossing if menu color is same as 3D color. The
  927. // emboss uses 3D hilight & 3D shadow, which doesn't look cool
  928. // on a different background.
  929. //
  930. if ((fHilite) ||
  931. (crBack != SYSRGB(3DFACE)) || SYSMET(SLOWMACHINE)) {
  932. //
  933. // If gray text won't show up on background, then dither.
  934. //
  935. if (SYSRGB(GRAYTEXT) == crBack) {
  936. uFlags |= DSS_UNION;
  937. } else {
  938. crFore = SYSRGB(GRAYTEXT);
  939. hbrFore = SYSHBR(GRAYTEXT);
  940. }
  941. } else {
  942. if ((SYSRGB(3DSHADOW) == crBack) && (SYSRGB(3DHILIGHT) == crBack)) {
  943. uFlags |= DSS_UNION;
  944. } else {
  945. uFlags |= TestMF(pMenu, MFINACTIVE) ? DSS_INACTIVE : DSS_DISABLED;
  946. }
  947. }
  948. }
  949. GreSetBkColor(hdc, crBack);
  950. GreSetTextColor(hdc, crFore);
  951. if (((dwFlags & DMI_INVERT) && (pMenu->hbrBack == NULL))
  952. || fHilite || fFlatMenuBar) {
  953. POLYPATBLT PolyData;
  954. /*
  955. * Only fill the background if we're being called on behalf of
  956. * MNInvertItem. This is so that we don't waste time filling
  957. * the unselected rect when the menu is first pulled down.
  958. * If the menu has a background brush and we were called by
  959. * MNInvertItem, that function will have already taken care of
  960. * filling the background
  961. */
  962. PolyData.x = pItem->xItem;
  963. PolyData.y = pItem->yItem;
  964. PolyData.cx = pItem->cxItem;
  965. PolyData.cy = pItem->cyItem;
  966. PolyData.BrClr.hbr = hbrBack;
  967. GrePolyPatBlt(hdc, PATCOPY, &PolyData, 1, PPB_BRUSH);
  968. if (fHilite && fFlatMenu) {
  969. DrawOutline(hdc, pItem->xItem, pItem->yItem,
  970. pItem->cxItem, pItem->cyItem, 1, SYSHBR(HIGHLIGHT));
  971. }
  972. }
  973. if (pMenu->hbrBack != NULL) {
  974. iBkSave = GreSetBkMode(hdc, TRANSPARENT);
  975. }
  976. GreSelectBrush(hdc, hbrFore);
  977. //
  978. // Draw the image
  979. //
  980. gm.pItem = pItem;
  981. gm.pMenu = pMenu;
  982. xxxDrawState(hdc,
  983. hbrFore,
  984. (LPARAM)(PGRAYMENU)&gm,
  985. pItem->xItem,
  986. pItem->yItem,
  987. pItem->cxItem,
  988. pItem->cyItem,
  989. uFlags);
  990. if (! fFlatMenu) {
  991. MNDrawMenu3DHotTracking(hdc, pMenu, pItem);
  992. }
  993. }
  994. /*
  995. * Draw the drop insertion bar, if any
  996. */
  997. MNDrawInsertionBar (hdc, pItem);
  998. if (pMenu->hbrBack != NULL)
  999. GreSetBkMode(hdc, iBkSave);
  1000. if (TestMFS(pItem, MFS_DEFAULT))
  1001. {
  1002. if (hfnOld)
  1003. GreSelectFont(hdc, hfnOld);
  1004. else
  1005. GreSetTextCharacterExtra(hdc, tcExtra);
  1006. }
  1007. }
  1008. extern void SetupFakeMDIAppStuff(PMENU lpMenu, PITEM lpItem);
  1009. /***************************************************************************\
  1010. *
  1011. * xxxRealDrawMenuItem()
  1012. *
  1013. * Callback from xxxDrawState() to draw the menu item, either normally or into
  1014. * an offscreen bitmp. We don't know where we're drawing, and shouldn't
  1015. * have to.
  1016. *
  1017. \***************************************************************************/
  1018. BOOL CALLBACK xxxRealDrawMenuItem(
  1019. HDC hdc,
  1020. PGRAYMENU pGray,
  1021. int cx,
  1022. int cy)
  1023. {
  1024. PMENU pMenu;
  1025. PITEM pItem;
  1026. BOOL fPopup;
  1027. int cch;
  1028. int xLeft;
  1029. int yTop;
  1030. int tp;
  1031. int rp;
  1032. LPWSTR lpsz;
  1033. int cyTemp;
  1034. int xHilite = 0;
  1035. int yHilite = 0;
  1036. TL tlpwndChild;
  1037. PTHREADINFO ptiCurrent = PtiCurrent();
  1038. BOOL fCheckDrawn = FALSE;
  1039. int xFarLeft;
  1040. //
  1041. // BOGUS
  1042. // Use cx and cy instead of lpItem->cxItem, lpItem->cyItem to
  1043. // speed stuff up.
  1044. //
  1045. pMenu = pGray->pMenu;
  1046. CheckLock(pMenu);
  1047. pItem = pGray->pItem;
  1048. fPopup = TestMF(pMenu, MFISPOPUP);
  1049. if (fPopup) {
  1050. xLeft = MNLEFTMARGIN;
  1051. if (TestMF(pMenu, MNS_NOCHECK)) {
  1052. xLeft += MNXSPACE;
  1053. } else {
  1054. fCheckDrawn = DrawMenuItemCheckMark(hdc, pItem, xLeft);
  1055. if (!TestMF(pMenu, MNS_CHECKORBMP)
  1056. || ((pItem->hbmp == NULL) || fCheckDrawn)) {
  1057. xLeft += TestMFT(pItem, MFT_RIGHTORDER)
  1058. ? 0 : (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE);
  1059. }
  1060. }
  1061. } else {
  1062. xLeft = 0;
  1063. /*
  1064. * If not drawing flat menus in the menubar, "depress" the menu item
  1065. * when it becomes highlighted.
  1066. */
  1067. if ((! MNIsFlatMenu()) && TestMFS(pItem, MFS_HILITE)) {
  1068. xHilite = CXMENU3DEDGE;
  1069. yHilite = CYMENU3DEDGE;
  1070. }
  1071. }
  1072. /*
  1073. * If there is not bitmap or we don't to draw it, go draw the text
  1074. */
  1075. if ((pItem->hbmp == NULL)
  1076. || (fCheckDrawn
  1077. && TestMF(pMenu, MNS_CHECKORBMP))) {
  1078. goto RealDrawMenuItemText;
  1079. }
  1080. /*
  1081. * Draw the bitmap
  1082. */
  1083. if (TestMFS(pItem, MFS_CACHEDBMP)) {
  1084. if (pItem->hbmp == HBMMENU_SYSTEM) {
  1085. /*
  1086. * Drawing app icon (system menu)
  1087. */
  1088. PWND pwndChild;
  1089. PICON pIcon = NULL;
  1090. UINT cyUse, cxUse;
  1091. AintNothingLikeTheRealMDIThing:
  1092. if (!(pItem->dwItemData))
  1093. SetupFakeMDIAppStuff(pMenu, pItem);
  1094. pwndChild = HMValidateHandleNoRip((HWND)(pItem->dwItemData),TYPE_WINDOW);
  1095. if (!pwndChild)
  1096. {
  1097. //
  1098. // Oops, child window isn't valid anymore. Go find
  1099. // the new one.
  1100. //
  1101. if (pItem->dwItemData)
  1102. {
  1103. pItem->dwItemData = 0;
  1104. goto AintNothingLikeTheRealMDIThing;
  1105. }
  1106. pIcon = NULL;
  1107. }
  1108. else {
  1109. ThreadLock(pwndChild, &tlpwndChild);
  1110. pIcon = xxxGetWindowSmIcon(pwndChild, FALSE);
  1111. ThreadUnlock(&tlpwndChild);
  1112. }
  1113. if (!pIcon)
  1114. pIcon = SYSICO(WINLOGO);
  1115. cyUse = cy - SYSMET(CYEDGE);
  1116. cxUse = cx - (SYSMET(CXEDGE) * 2);
  1117. /*
  1118. * If this is a popup, make sure that no weird
  1119. * width/height stretch takes place.
  1120. */
  1121. if (fPopup && (cyUse < cxUse)) {
  1122. cxUse = cyUse;
  1123. }
  1124. _DrawIconEx(hdc, xLeft + (SYSMET(CXEDGE) * 2),
  1125. SYSMET(CYBORDER), pIcon, cxUse,
  1126. cyUse, 0, SYSHBR(MENU), DI_NORMAL | DI_NOMIRROR);
  1127. } else {
  1128. /*
  1129. * This is a cached bitmap
  1130. */
  1131. UINT wBmp;
  1132. int xBmpLeft = xLeft;
  1133. int y;
  1134. POEMBITMAPINFO pOem;
  1135. switch ((ULONG_PTR)pItem->hbmp) {
  1136. case (ULONG_PTR)HBMMENU_MBAR_RESTORE:
  1137. wBmp = OBI_RESTORE_MBAR;
  1138. goto DrawSysBmp;
  1139. case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE:
  1140. wBmp = OBI_REDUCE_MBAR;
  1141. xBmpLeft += SYSMET(CXEDGE);
  1142. goto DrawSysBmp;
  1143. case (ULONG_PTR)HBMMENU_MBAR_CLOSE:
  1144. wBmp = OBI_CLOSE_MBAR;
  1145. goto DrawSysBmp;
  1146. case (ULONG_PTR)HBMMENU_MBAR_CLOSE_D:
  1147. wBmp = OBI_CLOSE_MBAR_I;
  1148. goto DrawSysBmp2;
  1149. case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE_D:
  1150. wBmp = OBI_REDUCE_MBAR_I;
  1151. xBmpLeft += SYSMET(CXEDGE);
  1152. goto DrawSysBmp2;
  1153. DrawSysBmp:
  1154. /*
  1155. * Select proper bitmap based on the item state
  1156. */
  1157. if (TestMFS(pItem, MFS_HILITE)) {
  1158. wBmp += DOBI_PUSHED;
  1159. }
  1160. DrawSysBmp2:
  1161. BitBltSysBmp(hdc, xBmpLeft, SYSMET(CYEDGE), wBmp);
  1162. break;
  1163. default:
  1164. UserAssert((pItem->hbmp >= HBMMENU_POPUPFIRST)
  1165. && (pItem->hbmp <= HBMMENU_POPUPLAST));
  1166. wBmp = OBI_POPUPFIRST + HandleToUlong(pItem->hbmp) - HandleToUlong(HBMMENU_POPUPFIRST);
  1167. UserAssert(wBmp < OBI_COUNT);
  1168. pOem = gpsi->oembmi + wBmp;
  1169. y = (pItem->cyItem - pOem->cy) / 2;
  1170. if (y < 0) {
  1171. y = 0;
  1172. }
  1173. BltColor(hdc, NULL, HDCBITS(), xLeft, y,
  1174. pOem->cx, pOem->cy, pOem->x, pOem->y, BC_INVERT);
  1175. break;
  1176. }
  1177. } /* if (pItem->hbmp == HBMMENU_SYSTEM) */
  1178. } else if (pItem->hbmp == HBMMENU_CALLBACK) {
  1179. /*
  1180. * Owner draw bitmap
  1181. */
  1182. xxxSendMenuDrawItemMessage(hdc,ODA_DRAWENTIRE, pMenu, pItem, TRUE, xLeft);
  1183. } else {
  1184. /*
  1185. * Drawing a regular bitmap.
  1186. */
  1187. int dx, dy;
  1188. HBITMAP hbmSave;
  1189. //
  1190. // Is this the zero'th item in a menu bar that's not all
  1191. // bitmaps? Hmm, sounds like it could be a fake MDI dude.
  1192. // If it is, use the windows icon instead
  1193. //
  1194. /*
  1195. * Let's fail this for > 4.0 apps so we can get rid of
  1196. * this horrible hack someday. The HBMMENU_ constants
  1197. * have been made public so people can use them freely.
  1198. *
  1199. * Note: even if the app is marked as 4.0, he could be
  1200. * recompiled and may utilizes the new feature in NT5 menu.
  1201. * So just in case, we have to check both dwItemData and lpstr
  1202. * so that the menu could have bitmap, dwItemData and a menu string.
  1203. *
  1204. */
  1205. if (LOWORD(ptiCurrent->dwExpWinVer) <= VER40) {
  1206. if (pItem->dwItemData && pItem->lpstr == NULL)
  1207. goto AintNothingLikeTheRealMDIThing;
  1208. else if (!fPopup &&
  1209. (pItem == pMenu->rgItems) &&
  1210. (pMenu->cItems > 1) &&
  1211. !(pMenu->rgItems[1].hbmp) &&
  1212. (pItem->spSubMenu)) {
  1213. RIPMSG0(RIP_WARNING, "Fake MDI detected, using window icon in place of bitmap");
  1214. goto AintNothingLikeTheRealMDIThing;
  1215. }
  1216. }
  1217. UserAssert(hdc != ghdcMem2);
  1218. dx = pItem->cxBmp;
  1219. if (fPopup) {
  1220. dy = pItem->cyBmp;
  1221. //
  1222. // Center bitmap in middle of item area
  1223. //
  1224. cyTemp = (pItem->cyItem - dy);
  1225. if (cyTemp > 0)
  1226. cyTemp = cyTemp / 2;
  1227. else
  1228. cyTemp = 0;
  1229. } else {
  1230. dy = max(pItem->cyBmp, SYSMET(CYMENUSIZE));
  1231. cyTemp = 0;
  1232. if (pItem->lpstr != NULL) {
  1233. xLeft += gcxMenuFontChar;
  1234. }
  1235. }
  1236. if (hbmSave = GreSelectBitmap(ghdcMem2, pItem->hbmp)) {
  1237. BITMAP bmp;
  1238. //
  1239. // Draw the bitmap leaving some room on the left for the
  1240. // optional check mark if we are in a popup menu. (as opposed
  1241. // to a top level menu bar).
  1242. //
  1243. // We can do cool stuff with monochrome bitmap itmes
  1244. // by merging with the current colors.
  1245. //
  1246. // If the item is selected and the bitmap isn't monochrome,
  1247. // we just invert the thing when we draw it. We can't do
  1248. // anything more clever unless we want to convert to
  1249. // monochrome.
  1250. //
  1251. GreExtGetObjectW(pItem->hbmp, sizeof(bmp), (LPSTR)&bmp);
  1252. GreBitBlt(hdc, xLeft + xHilite, cyTemp + xHilite, dx, dy, ghdcMem2, 0, 0,
  1253. (bmp.bmPlanes * bmp.bmBitsPixel == 1) ?
  1254. SRCSTENCIL :
  1255. (MNDrawHilite(pItem) ? NOTSRCCOPY : SRCCOPY),
  1256. 0x00FFFFFF);
  1257. GreSelectBitmap(ghdcMem2, hbmSave);
  1258. } else {
  1259. RIPMSG3(RIP_WARNING, "Menu 0x%08X, item 0x%08X: Tried to draw invalid bitmap 0x%08X", pMenu, pItem, pItem->hbmp) ;
  1260. }
  1261. }
  1262. RealDrawMenuItemText:
  1263. if (pItem->lpstr != NULL) {
  1264. /*
  1265. * We want the text in all popup menu items to be aligned
  1266. * if an alignment offset is available.
  1267. */
  1268. if (fPopup && (pMenu->cxTextAlign != 0)) {
  1269. xLeft = pMenu->cxTextAlign;
  1270. } else if (pItem->hbmp != NULL) {
  1271. xLeft += pItem->cxBmp + SYSMET(CXEDGE);
  1272. }
  1273. // This item is a text string item. Display it.
  1274. yTop = gcyMenuFontExternLeading;
  1275. cyTemp = pItem->cyItem - (gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYBORDER));
  1276. if (cyTemp > 0)
  1277. yTop += (cyTemp / 2);
  1278. if (!fPopup && (pItem->hbmp == NULL)) {
  1279. xLeft += gcxMenuFontChar;
  1280. }
  1281. lpsz = TextPointer(pItem->lpstr);
  1282. if (lpsz!=NULL) {
  1283. cch = pItem->cch;
  1284. // Even though we no longer support any funky handling of the
  1285. // help prefix character, we still need to eat it up if we run
  1286. // across it so that the menu item is drawn correctly
  1287. if ((*lpsz == CH_HELPPREFIX) && !fPopup) {
  1288. // Skip help prefix character.
  1289. lpsz++;
  1290. cch--;
  1291. }
  1292. // tp will contain the character position of the \t indicator
  1293. // in the menu string. This is where we add a tab to the string.
  1294. //
  1295. // rp will contain the character position of the \a indicator
  1296. // in the string. All text following this is right aligned.
  1297. tp = FindCharPosition(lpsz, TEXT('\t'));
  1298. rp = FindCharPosition(lpsz, TEXT('\t') - 1);
  1299. xFarLeft = pItem->cxItem - (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE);
  1300. if (rp && (rp != cch)) {
  1301. // Display all the text up to the \a
  1302. if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
  1303. SIZE extent;
  1304. xxxPSMGetTextExtent(hdc, lpsz, rp, &extent);
  1305. xLeft = xFarLeft - extent.cx;
  1306. }
  1307. xxxDrawMenuItemText(pItem, hdc, xLeft + xHilite, yTop + xHilite, lpsz, rp,
  1308. TestMF(pMenu, MFUNDERLINE));
  1309. // Do we also have a tab beyond the \a ??
  1310. if (tp > rp + 1) {
  1311. SIZE extent;
  1312. if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
  1313. xLeft = xFarLeft - pItem->dxTab ;
  1314. } else {
  1315. xxxPSMGetTextExtent(hdc, lpsz + rp + 1,
  1316. (UINT)(tp - rp - 1), &extent);
  1317. xLeft = (int)(pItem->dxTab - extent.cx);
  1318. }
  1319. //
  1320. // lotus in Hebrew make their menus by putting the
  1321. // accelerator on the left and the text on the right
  1322. //
  1323. xxxPSMTextOut(hdc, xLeft, yTop, (LPWSTR)(lpsz + rp + 1), tp - rp - 1,
  1324. TestMF(pMenu, MFUNDERLINE) ? 0 : DT_HIDEPREFIX);
  1325. }
  1326. } else if (tp && rp == cch) {
  1327. // Display text up to the tab position
  1328. if (TestMFT(pItem, MFT_RIGHTORDER)) {
  1329. SIZE extent;
  1330. xxxPSMGetTextExtent(hdc, lpsz, tp, &extent);
  1331. xLeft = xFarLeft - extent.cx;
  1332. if (!fPopup && (pItem->hbmp == NULL)) {
  1333. xLeft += gcxMenuFontChar;
  1334. }
  1335. }
  1336. xxxDrawMenuItemText(pItem, hdc, xLeft + xHilite, yTop + xHilite, lpsz, tp,
  1337. TestMF(pMenu, MFUNDERLINE));
  1338. }
  1339. // Any text left to display (like after the tab) ??
  1340. if (tp < cch - 1) {
  1341. if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
  1342. SIZE extent;
  1343. xxxPSMGetTextExtent(hdc, lpsz + tp + 1, (int)cch - tp - 1, &extent);
  1344. xLeft = pItem->cxItem - pItem->dxTab - extent.cx;
  1345. } else {
  1346. xLeft = pItem->dxTab + gcxMenuFontChar;
  1347. }
  1348. xxxPSMTextOut(hdc, xLeft, yTop, lpsz + tp + 1, cch - tp - 1,
  1349. TestMF(pMenu, MFUNDERLINE) ? 0 : DT_HIDEPREFIX);
  1350. }
  1351. }
  1352. }
  1353. //
  1354. // Draw the hierarchical arrow for the cascade menu.
  1355. //
  1356. if (fPopup && (pItem->spSubMenu != NULL)) {
  1357. POEMBITMAPINFO pOem;
  1358. pOem = gpsi->oembmi + (TestMFT(pItem, MFT_RIGHTORDER)
  1359. ? OBI_MENUARROW_L : OBI_MENUARROW);
  1360. // This item has a hierarchical popup associated with it. Draw the
  1361. // bitmap dealy to signify its presence. Note we check if fPopup is set
  1362. // so that this isn't drawn for toplevel menus that have popups.
  1363. BltColor(hdc,
  1364. NULL,
  1365. HDCBITS(),
  1366. TestMFT(pItem, MFT_RIGHTORDER)
  1367. ? pOem->cx :
  1368. pItem->cxItem - pOem->cx,
  1369. max((INT)(pItem->cyItem - 2 - pOem->cy) / 2, 0),
  1370. pOem->cx,
  1371. pOem->cy,
  1372. pOem->x,
  1373. pOem->y,
  1374. BC_INVERT);
  1375. }
  1376. return TRUE;
  1377. }
  1378. /***************************************************************************\
  1379. * xxxMenuBarDraw
  1380. *
  1381. * History:
  1382. * 11-Mar-1992 mikeke From win31
  1383. \***************************************************************************/
  1384. int xxxMenuBarDraw(
  1385. PWND pwnd,
  1386. HDC hdc,
  1387. int cxFrame,
  1388. int cyFrame)
  1389. {
  1390. UINT cxMenuMax;
  1391. UINT cyMenu;
  1392. int yTop;
  1393. PMENU pMenu;
  1394. BOOL fClipped = FALSE;
  1395. TL tlpMenu;
  1396. HBRUSH hbrT;
  1397. CheckLock(pwnd);
  1398. pMenu = pwnd->spmenu;
  1399. if (pMenu == NULL) {
  1400. return SYSMET(CYBORDER);
  1401. }
  1402. /*
  1403. * NT5 menus are drawn inactive when the window is not active.
  1404. */
  1405. if (TestwndFrameOn(pwnd) || (GetAppCompatFlags2(VER40) & GACF2_ACTIVEMENUS)) {
  1406. ClearMF(pMenu, MFINACTIVE);
  1407. } else {
  1408. SetMF(pMenu, MFINACTIVE);
  1409. }
  1410. /*
  1411. * Lock the menu so we can poke around.
  1412. */
  1413. ThreadLockMenuNoModify(pMenu, &tlpMenu);
  1414. yTop = cyFrame;
  1415. yTop += GetCaptionHeight(pwnd);
  1416. /*
  1417. * Calculate maximum available horizontal real estate
  1418. */
  1419. cxMenuMax = (pwnd->rcWindow.right - pwnd->rcWindow.left) - cxFrame * 2;
  1420. /*
  1421. * If the menu has switched windows, or if either count is 0,
  1422. * then we need to recompute the menu width.
  1423. */
  1424. if (pwnd != pMenu->spwndNotify ||
  1425. pMenu->cxMenu == 0 ||
  1426. pMenu->cyMenu == 0) {
  1427. xxxMenuBarCompute(pMenu, pwnd, yTop, cxFrame, cxMenuMax);
  1428. }
  1429. /*
  1430. * If the menu rectangle is wider than allowed, or the
  1431. * bottom would overlap the size border, we need to clip.
  1432. */
  1433. if (pMenu->cxMenu > cxMenuMax ||
  1434. (int)(yTop + pMenu->cyMenu) > (int)((pwnd->rcWindow.bottom - pwnd->rcWindow.top)
  1435. - cyFrame)) {
  1436. /*
  1437. * Lock the display while we're playing around with visrgns. Make
  1438. * a local copy of the saved-visrgn so it can be restored in case
  1439. * we make a callback (i.e. WM_DRAWITEM).
  1440. */
  1441. GreLockDisplay(gpDispInfo->hDev);
  1442. fClipped = TRUE;
  1443. GreIntersectVisRect(hdc, pwnd->rcWindow.left + cxFrame,
  1444. pwnd->rcWindow.top,
  1445. pwnd->rcWindow.left + cxFrame + cxMenuMax,
  1446. pwnd->rcWindow.bottom - cyFrame);
  1447. GreUnlockDisplay(gpDispInfo->hDev);
  1448. }
  1449. {
  1450. // Draw menu background in MENU color
  1451. // Draw border under menu in proper BORDER color
  1452. POLYPATBLT PolyData[2];
  1453. BOOL fFlatMenu;
  1454. /*
  1455. * We can't use MFISPOPUP to determine if we are drawing a menu bar.
  1456. * This is because a menu can be created as a popup, but then attached
  1457. * to a window using SetMenu(), making it a menubar.
  1458. */
  1459. fFlatMenu = MNIsFlatMenu();
  1460. PolyData[0].x = cxFrame;
  1461. PolyData[0].y = yTop;
  1462. PolyData[0].cx = pMenu->cxMenu;
  1463. PolyData[0].cy = pMenu->cyMenu;
  1464. PolyData[0].BrClr.hbr = (pMenu->hbrBack) ? pMenu->hbrBack : (fFlatMenu ? SYSHBR(MENUBAR) : SYSHBR(MENU));
  1465. PolyData[1].x = cxFrame;
  1466. PolyData[1].y = yTop + pMenu->cyMenu;
  1467. PolyData[1].cx = pMenu->cxMenu;
  1468. PolyData[1].cy = SYSMET(CYBORDER);
  1469. PolyData[1].BrClr.hbr = (TestWF(pwnd, WEFEDGEMASK) && !TestWF(pwnd, WFOLDUI))? SYSHBR(3DFACE) : SYSHBR(WINDOWFRAME);
  1470. GrePolyPatBlt(hdc,PATCOPY,&PolyData[0],2,PPB_BRUSH);
  1471. }
  1472. /*
  1473. * Finally, draw the menu itself.
  1474. */
  1475. hbrT = GreSelectBrush(hdc, (TestWF(pwnd, WEFEDGEMASK) && !TestWF(pwnd, WFOLDUI))? SYSHBR(3DFACE) : SYSHBR(WINDOWFRAME));
  1476. xxxMenuDraw(hdc, pMenu);
  1477. GreSelectBrush(hdc, hbrT);
  1478. if (fClipped) {
  1479. /*
  1480. * Recalculate the DC visrgn after calling back.
  1481. */
  1482. PDCE pdce;
  1483. if ((pdce = LookupDC(hdc)) != NULL) {
  1484. InvalidateDce(pdce);
  1485. }
  1486. }
  1487. cyMenu = pMenu->cyMenu + SYSMET(CYBORDER);
  1488. ThreadUnlockMenuNoModify(&tlpMenu);
  1489. return cyMenu;
  1490. }
  1491. /***************************************************************************\
  1492. * xxxMenuDraw
  1493. *
  1494. * Draws the menu
  1495. *
  1496. * Revalidation notes:
  1497. * This routine must be called with a valid and non-NULL pwnd.
  1498. *
  1499. * History:
  1500. \***************************************************************************/
  1501. VOID xxxMenuDraw(
  1502. HDC hdc,
  1503. PMENU pmenu)
  1504. {
  1505. PITEM pItem;
  1506. UINT i, cy;
  1507. RECT rcItem;
  1508. HFONT hFontOld;
  1509. UINT bfExtra;
  1510. PTHREADINFO ptiCurrent = PtiCurrent();
  1511. UINT oldAlign;
  1512. int iBkSave;
  1513. POINT ptOrg;
  1514. CheckLock(pmenu);
  1515. if (pmenu == NULL) {
  1516. RIPERR0(ERROR_INVALID_HANDLE,
  1517. RIP_WARNING,
  1518. "xxxMenuDraw: Invalid menu handle (NULL)");
  1519. return;
  1520. }
  1521. GreGetViewportOrg(hdc, &ptOrg);
  1522. hFontOld = GreSelectFont(hdc, ghMenuFont);
  1523. oldAlign = GreGetTextAlign(hdc);
  1524. if (pmenu->rgItems && TestMFT(pmenu->rgItems, MFT_RIGHTORDER)) {
  1525. GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
  1526. }
  1527. bfExtra = CalcbfExtra();
  1528. if (pmenu->hbrBack != NULL) {
  1529. iBkSave = GreSetBkMode(hdc, TRANSPARENT);
  1530. }
  1531. if (pmenu->dwArrowsOn != MSA_OFF) {
  1532. pItem = MNGetToppItem(pmenu);
  1533. GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y - ((int)pItem->yItem), NULL);
  1534. i = pmenu->iTop;
  1535. } else {
  1536. pItem = (PITEM)pmenu->rgItems;
  1537. i = 0;
  1538. }
  1539. cy = 0;
  1540. for (; i < pmenu->cItems; i++, pItem++) {
  1541. if (TestMFT(pItem, MFT_MENUBARBREAK) &&
  1542. TestMF(pmenu, MFISPOPUP)) {
  1543. //
  1544. // Draw a vertical etch. This is done by calling DrawEdge(),
  1545. // sunken, with BF_LEFT | BF_RIGHT.
  1546. //
  1547. if(TestMFT(pItem, MFT_RIGHTORDER) && i) {
  1548. //
  1549. // going backwards, so the correct place is just before the
  1550. // _previous_ item.
  1551. //
  1552. PITEM pi;
  1553. pi = pItem - 1;
  1554. rcItem.left = pi->xItem - SYSMET(CXFIXEDFRAME);
  1555. rcItem.top = 0;
  1556. rcItem.right = pi->xItem - SYSMET(CXBORDER);
  1557. rcItem.bottom = pmenu->cyMenu;
  1558. } else {
  1559. rcItem.left = pItem->xItem - SYSMET(CXFIXEDFRAME);
  1560. rcItem.top = 0;
  1561. rcItem.right = pItem->xItem - SYSMET(CXBORDER);
  1562. rcItem.bottom = pmenu->cyMenu;
  1563. }
  1564. DrawEdge(hdc, &rcItem, BDR_SUNKENOUTER, BF_LEFT | BF_RIGHT | bfExtra);
  1565. }
  1566. /*
  1567. * If this is a separator, draw it and return.
  1568. * If version is less than 4.0 don't test the MFT_OWNERDRAW
  1569. * flag. Bug 21922; App MaxEda has both separator and Ownerdraw
  1570. * flags on. In 3.51 we didn't test the OwnerDraw flag
  1571. */
  1572. if (TestMFT(pItem, MFT_SEPARATOR)
  1573. && (!TestMFT(pItem, MFT_OWNERDRAW)
  1574. || (LOWORD(ptiCurrent->dwExpWinVer) < VER40))) {
  1575. /*
  1576. * Draw a horizontal etch.
  1577. */
  1578. int yT = pItem->yItem + (pItem->cyItem / 2) - SYSMET(CYBORDER);
  1579. RECT rcItem;
  1580. rcItem.left = pItem->xItem + 1;
  1581. rcItem.top = yT;
  1582. rcItem.right = pItem->xItem + pItem->cxItem - 1;
  1583. rcItem.bottom = yT + SYSMET(CYEDGE);
  1584. DrawEdge(hdc, &rcItem, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM | bfExtra);
  1585. /*
  1586. * Draw drop insertion bar, if any.
  1587. */
  1588. MNDrawInsertionBar (hdc, pItem);
  1589. } else {
  1590. xxxDrawMenuItem(hdc, pmenu, pItem, 0);
  1591. }
  1592. if (pmenu->dwArrowsOn != MSA_OFF) {
  1593. cy += pItem->cyItem;
  1594. if (cy > pmenu->cyMenu) {
  1595. /*
  1596. * This is a scrollable menu and the item just drawn falls below
  1597. * the bottom of the visible menu -- no need to draw any further
  1598. */
  1599. break;
  1600. }
  1601. }
  1602. }
  1603. if (pmenu->hbrBack != NULL) {
  1604. GreSetBkMode(hdc, iBkSave);
  1605. }
  1606. GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y, NULL);
  1607. GreSetTextAlign(hdc, oldAlign);
  1608. GreSelectFont(hdc, hFontOld);
  1609. }
  1610. /***************************************************************************\
  1611. * xxxDrawMenuBar
  1612. *
  1613. * Forces redraw of the menu bar
  1614. *
  1615. * History:
  1616. \***************************************************************************/
  1617. BOOL xxxDrawMenuBar(
  1618. PWND pwnd)
  1619. {
  1620. CheckLock(pwnd);
  1621. if (!TestwndChild(pwnd)) {
  1622. xxxRedrawFrame(pwnd);
  1623. }
  1624. return TRUE;
  1625. }
  1626. /***************************************************************************\
  1627. * xxxMenuInvert
  1628. *
  1629. * Invert menu item
  1630. *
  1631. * Revalidation notes:
  1632. * This routine must be called with a valid and non-NULL pwnd.
  1633. *
  1634. * fOn - TRUE if the item is selected thus it needs to be inverted
  1635. * fNotify - TRUE if the parent should be notified (as appropriate), FALSE
  1636. * if we are just redrawing the selected item.
  1637. *
  1638. * History:
  1639. \***************************************************************************/
  1640. PITEM xxxMNInvertItem(
  1641. PPOPUPMENU ppopupmenu,
  1642. PMENU pmenu,
  1643. int itemNumber,
  1644. PWND pwndNotify,
  1645. BOOL fOn)
  1646. {
  1647. PITEM pItem = NULL;
  1648. HDC hdc;
  1649. int y, iNewTop;
  1650. RECT rcItem;
  1651. BOOL fSysMenuIcon = FALSE;
  1652. PMENU pmenusys;
  1653. BOOL fClipped = FALSE;
  1654. HFONT hOldFont;
  1655. PWND pwnd;
  1656. POINT ptOrg;
  1657. TL tlpwnd;
  1658. UINT oldAlign;
  1659. CheckLock(pmenu);
  1660. CheckLock(pwndNotify);
  1661. /*
  1662. * If we are in the middle of trying to get out of menu mode, hMenu
  1663. * and/or pwndNotify will be NULL, so just bail out now.
  1664. */
  1665. if ((pmenu == NULL) || (pwndNotify == NULL)) {
  1666. return NULL;
  1667. }
  1668. /*
  1669. * If ppopupmenu is NULL, we're not in menu mode (i.e, call from
  1670. * HiliteMenuItem).
  1671. */
  1672. if (ppopupmenu == NULL) {
  1673. pwnd = pwndNotify;
  1674. } else {
  1675. pwnd = ppopupmenu->spwndPopupMenu;
  1676. }
  1677. if (pwnd != pwndNotify) {
  1678. ThreadLock(pwnd, &tlpwnd);
  1679. }
  1680. if (itemNumber < 0) {
  1681. if (ppopupmenu != NULL) {
  1682. if ((itemNumber == MFMWFP_UPARROW) || (itemNumber == MFMWFP_DOWNARROW)) {
  1683. MNDrawArrow(NULL, ppopupmenu, itemNumber);
  1684. }
  1685. }
  1686. xxxSendMenuSelect(pwndNotify, pwnd, pmenu, itemNumber);
  1687. goto SeeYa;
  1688. }
  1689. if (!TestMF(pmenu, MFISPOPUP)) {
  1690. pmenusys = xxxGetSysMenuHandle(pwndNotify);
  1691. if (pmenu == pmenusys) {
  1692. MNPositionSysMenu(pwndNotify, pmenusys);
  1693. fSysMenuIcon = TRUE;
  1694. }
  1695. }
  1696. if ((UINT)itemNumber >= pmenu->cItems)
  1697. goto SeeYa;
  1698. pItem = &pmenu->rgItems[itemNumber];
  1699. if (!TestMF(pmenu, MFISPOPUP) && TestWF(pwndNotify, WFMINIMIZED)) {
  1700. /*
  1701. * Skip inverting top level menus if the window is iconic.
  1702. */
  1703. goto SeeYa;
  1704. }
  1705. /*
  1706. * Is this a separator?
  1707. */
  1708. if (TestMFT(pItem, MFT_SEPARATOR)) {
  1709. goto SendSelectMsg;
  1710. }
  1711. if ((BOOL)TestMFS(pItem, MFS_HILITE) == (BOOL)fOn) {
  1712. /*
  1713. * Item's state isn't really changing. Just return.
  1714. */
  1715. goto SeeYa;
  1716. }
  1717. if (fOn && (ppopupmenu != NULL) && (pmenu->dwArrowsOn != MSA_OFF)) {
  1718. /*
  1719. * when selecting an item, ensure that the item is fully visible
  1720. * NOTE -- For mouse use, partially visible should be acceptable
  1721. * -- Can we get mouse info down this far ?
  1722. */
  1723. if (itemNumber < pmenu->iTop) {
  1724. iNewTop = itemNumber;
  1725. goto NewTop;
  1726. } else {
  1727. PITEM pWalk = MNGetToppItem(pmenu);
  1728. int dy = pItem->yItem + pItem->cyItem - pWalk->yItem - pmenu->cyMenu;
  1729. iNewTop = pmenu->iTop;
  1730. while ((dy > 0) && (iNewTop < (int)pmenu->cItems)) {
  1731. dy -= pWalk->cyItem;
  1732. pWalk++;
  1733. iNewTop++;
  1734. }
  1735. if (iNewTop >= (int)pmenu->cItems) {
  1736. iNewTop = pmenu->cItems;
  1737. }
  1738. NewTop:
  1739. if (xxxMNSetTop(ppopupmenu, iNewTop)) {
  1740. xxxUpdateWindow(pwnd);
  1741. }
  1742. }
  1743. }
  1744. rcItem.left = pItem->xItem;
  1745. rcItem.top = pItem->yItem;
  1746. rcItem.right = pItem->xItem + pItem->cxItem;
  1747. rcItem.bottom = pItem->yItem + pItem->cyItem;
  1748. y = pItem->cyItem;
  1749. if (TestMF(pmenu, MFISPOPUP)) {
  1750. hdc = _GetDC(pwnd);
  1751. } else {
  1752. hdc = _GetWindowDC(pwnd);
  1753. if (TestWF(pwnd, WFSIZEBOX) && !fSysMenuIcon) {
  1754. /*
  1755. * If the window is small enough that some of the menu bar has been
  1756. * obscured by the frame, we don't want to draw on the bottom of the
  1757. * sizing frame. Note that we don't want to do this if we are
  1758. * inverting the system menu icon since that will be clipped to the
  1759. * window rect. (otherwise we end up with only half the sys menu
  1760. * icon inverted)
  1761. */
  1762. int xMenuMax = (pwnd->rcWindow.right - pwnd->rcWindow.left) - SYSMET(CXSIZEFRAME);
  1763. if (rcItem.right > xMenuMax ||
  1764. rcItem.bottom > ((pwnd->rcWindow.bottom -
  1765. pwnd->rcWindow.top) - SYSMET(CYSIZEFRAME))) {
  1766. /*
  1767. * Lock the display while we're playing around with visrgns.
  1768. * Make a local copy of the visrgn, so that it can be
  1769. * properly restored on potential callbacks (i.e. WM_DRAWITEM).
  1770. */
  1771. GreLockDisplay(gpDispInfo->hDev);
  1772. fClipped = TRUE;
  1773. GreIntersectVisRect(hdc,
  1774. pwnd->rcWindow.left + rcItem.left,
  1775. pwnd->rcWindow.top + rcItem.top,
  1776. pwnd->rcWindow.left + xMenuMax,
  1777. pwnd->rcWindow.bottom - SYSMET(CYSIZEFRAME));
  1778. GreUnlockDisplay(gpDispInfo->hDev);
  1779. }
  1780. }
  1781. }
  1782. oldAlign = GreGetTextAlign(hdc);
  1783. if (pItem && TestMFT(pItem, MFT_RIGHTORDER))
  1784. GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
  1785. hOldFont = GreSelectFont(hdc, ghMenuFont);
  1786. GreGetViewportOrg(hdc, &ptOrg);
  1787. if (fOn) {
  1788. SetMFS(pItem, MFS_HILITE);
  1789. } else {
  1790. ClearMFS(pItem, MFS_HILITE);
  1791. }
  1792. if (!fSysMenuIcon
  1793. && ((pItem->hbmp != HBMMENU_SYSTEM)
  1794. || (TestMF(pmenu, MFISPOPUP)))) {
  1795. if (pmenu->dwArrowsOn != MSA_OFF) {
  1796. GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y - ((int)MNGetToppItem(pmenu)->yItem), NULL);
  1797. }
  1798. if ((pmenu->hbrBack != NULL)
  1799. && !MNDrawHilite(pItem)
  1800. && !TestMFT(pItem, MFT_OWNERDRAW)) {
  1801. /*
  1802. * fill the background here so xxxDrawMenuItem doesn't have to fool
  1803. * around with hbrBack
  1804. */
  1805. int iBkSave = GreSetBkMode(hdc, TRANSPARENT);
  1806. MNEraseBackground (hdc, pmenu,
  1807. pItem->xItem, pItem->yItem,
  1808. pItem->cxItem, pItem->cyItem);
  1809. GreSetBkMode(hdc, iBkSave);
  1810. }
  1811. xxxDrawMenuItem(hdc, pmenu, pItem, DMI_INVERT);
  1812. }
  1813. if (fClipped) {
  1814. /*
  1815. * Recalculate the DC visrgn after calling back.
  1816. */
  1817. PDCE pdce;
  1818. if ((pdce = LookupDC(hdc)) != NULL) {
  1819. InvalidateDce(pdce);
  1820. }
  1821. }
  1822. GreSelectFont(hdc, hOldFont);
  1823. GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y, NULL);
  1824. GreSetTextAlign(hdc, oldAlign);
  1825. _ReleaseDC(hdc);
  1826. SendSelectMsg:
  1827. /*
  1828. * send select msg only if we are selecting an item.
  1829. */
  1830. if (fOn) {
  1831. xxxSendMenuSelect(pwndNotify, pwnd, pmenu, itemNumber);
  1832. }
  1833. SeeYa:
  1834. if (pwnd != pwndNotify) {
  1835. ThreadUnlock(&tlpwnd);
  1836. }
  1837. return pItem;
  1838. }
  1839. /***************************************************************************\
  1840. * xxxDrawMenuBarTemp
  1841. *
  1842. * This is so the control panel can let us do the work -- and make their
  1843. * preview windows that much more accurate. The only reason I put the hwnd in
  1844. * here is because, in the low level menu routines, we assume that an hwnd is
  1845. * associated with the hmenu -- I didn't want to slow that code down by adding
  1846. * NULL checks.
  1847. *
  1848. * The SYSMET(CYMENU) with regard to the given font is returned -- this
  1849. * way control panel can say, "The user wants this menu font (hfont) with this
  1850. * menu height (lprc)", and we can respond "this is the height we ended up
  1851. * using."
  1852. *
  1853. * NOTE: It's OK to over-write lprc because this function receives a pointer
  1854. * to the rectangle captured in NtUserDrawMenuBarTemp.
  1855. *
  1856. * History:
  1857. * 20-Sep-95 BradG Ported from Win95 (inctlpan.c)
  1858. \***************************************************************************/
  1859. int xxxDrawMenuBarTemp(
  1860. PWND pwnd,
  1861. HDC hdc,
  1862. LPRECT lprc,
  1863. PMENU pMenu,
  1864. HFONT hfont)
  1865. {
  1866. int cyMenu;
  1867. HFONT hOldFont;
  1868. HFONT hFontSave;
  1869. int cxCharSave;
  1870. int cxOverhangSave;
  1871. int cyCharSave;
  1872. int cyLeadingSave;
  1873. int cyAscentSave;
  1874. int cySizeSave;
  1875. PWND pwndNotify;
  1876. TL tlpwndNotify;
  1877. hFontSave = ghMenuFont;
  1878. cxCharSave = gcxMenuFontChar;
  1879. cxOverhangSave = gcxMenuFontOverhang;
  1880. cyCharSave = gcyMenuFontChar;
  1881. cyLeadingSave = gcyMenuFontExternLeading;
  1882. cyAscentSave = gcyMenuFontAscent;
  1883. cySizeSave = SYSMET(CYMENUSIZE);
  1884. CheckLock(pwnd);
  1885. CheckLock(pMenu);
  1886. ThreadLock(pMenu->spwndNotify, &tlpwndNotify);
  1887. pwndNotify = pMenu->spwndNotify;
  1888. cyMenu = lprc->bottom - lprc->top;
  1889. if (hfont) {
  1890. TEXTMETRIC textMetrics;
  1891. /*
  1892. * Compute the new menu font info if needed
  1893. */
  1894. ghMenuFont = hfont;
  1895. hOldFont = GreSelectFont(HDCBITS(), ghMenuFont);
  1896. gcxMenuFontChar = GetCharDimensions(
  1897. HDCBITS(), &textMetrics, &gcyMenuFontChar);
  1898. gcxMenuFontOverhang = textMetrics.tmOverhang;
  1899. GreSelectFont(HDCBITS(), hOldFont);
  1900. gcyMenuFontExternLeading = textMetrics.tmExternalLeading;
  1901. gcyMenuFontAscent = textMetrics.tmAscent + SYSMET(CYBORDER);
  1902. }
  1903. cyMenu -= SYSMET(CYBORDER);
  1904. cyMenu = max(cyMenu, (gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYEDGE)));
  1905. SYSMET(CYMENUSIZE) = cyMenu;
  1906. SYSMET(CYMENU) = cyMenu + SYSMET(CYBORDER);
  1907. /*
  1908. * Compute the dimensons of the menu (hope that we don't leave the
  1909. * USER critical section)
  1910. */
  1911. xxxMenuBarCompute(pMenu, pwnd, lprc->top, lprc->left, lprc->right);
  1912. /*
  1913. * Draw menu border in menu color
  1914. */
  1915. lprc->bottom = lprc->top + pMenu->cyMenu;
  1916. FillRect(hdc, lprc, MNIsFlatMenu() ? SYSHBR(MENUBAR) : SYSHBR(MENU));
  1917. /*
  1918. * Finally, draw the menu itself.
  1919. */
  1920. xxxMenuDraw(hdc, pMenu);
  1921. /*
  1922. * Restore the old state
  1923. */
  1924. ghMenuFont = hFontSave;
  1925. gcxMenuFontChar = cxCharSave;
  1926. gcxMenuFontOverhang = cxOverhangSave;
  1927. gcyMenuFontChar = cyCharSave;
  1928. gcyMenuFontExternLeading = cyLeadingSave;
  1929. gcyMenuFontAscent = cyAscentSave;
  1930. SYSMET(CYMENUSIZE) = cySizeSave;
  1931. cyMenu = SYSMET(CYMENU);
  1932. SYSMET(CYMENU) = cySizeSave + SYSMET(CYBORDER);
  1933. Lock(&pMenu->spwndNotify, pwndNotify);
  1934. ThreadUnlock(&tlpwndNotify);
  1935. return cyMenu;
  1936. }
  1937. /***************************************************************************\
  1938. * xxxDrawMenuBarUnderlines
  1939. *
  1940. * Description: Shows or hides all underlines on a menu bar.
  1941. *
  1942. * History:
  1943. * 07/23/96 vadimg created
  1944. \***************************************************************************/
  1945. VOID xxxDrawMenuBarUnderlines(
  1946. PWND pwnd,
  1947. BOOL fShow)
  1948. {
  1949. HDC hdc;
  1950. PMENU pmenu;
  1951. PITEM pitem;
  1952. ULONG i, yTop, cyTemp;
  1953. LPWSTR psz;
  1954. WCHAR szMenu[MENU_STRLEN], *pchOut;
  1955. LONG result;
  1956. HBRUSH hbr;
  1957. TL tlpMenu;
  1958. PTHREADINFO ptiCurrent = PtiCurrentShared();
  1959. int xLeft;
  1960. LPWSTR lpsz;
  1961. SIZE extent;
  1962. BOOL fFlatMenuBar;
  1963. CheckLock(pwnd);
  1964. /*
  1965. * Bail if menu underlines are always on.
  1966. */
  1967. if (TEST_BOOL_ACCF(ACCF_KEYBOARDPREF) || TestEffectInvertUP(KEYBOARDCUES)
  1968. || (GetAppCompatFlags2(VER40) & GACF2_KCOFF)) {
  1969. return;
  1970. }
  1971. // if there is no menu, bail out right away
  1972. pwnd = GetTopLevelWindow(pwnd);
  1973. if (pwnd == NULL || !TestWF(pwnd, WFMPRESENT)) {
  1974. return;
  1975. }
  1976. /*
  1977. * We don't clear WFMPRESENT when the menu is unlocked so make sure we have
  1978. * one.
  1979. */
  1980. pmenu = pwnd->spmenu;
  1981. if (pmenu == NULL) {
  1982. return;
  1983. }
  1984. if (MNIsFlatMenu()) {
  1985. fFlatMenuBar = !TestMF(pmenu, MFISPOPUP);
  1986. } else {
  1987. fFlatMenuBar = FALSE;
  1988. }
  1989. /*
  1990. * set/clear the underline state. There are cases when the
  1991. * menu loop doesn't remove the keys from the queue; so after
  1992. * exiting we might get here but nothing needs to be drawn
  1993. */
  1994. if (fShow) {
  1995. if (TestMF(pmenu, MFUNDERLINE)) {
  1996. return;
  1997. }
  1998. hbr = SYSHBR(MENUTEXT);
  1999. SetMF(pmenu, MFUNDERLINE);
  2000. } else {
  2001. if (!TestMF(pmenu, MFUNDERLINE)) {
  2002. return;
  2003. }
  2004. if (pmenu->hbrBack != NULL) {
  2005. hbr = pmenu->hbrBack;
  2006. } else if (fFlatMenuBar) {
  2007. hbr = SYSHBR(MENUBAR);
  2008. } else {
  2009. hbr = SYSHBR(MENU);
  2010. }
  2011. ClearMF(pmenu, MFUNDERLINE);
  2012. }
  2013. pitem = (PITEM)pmenu->rgItems;
  2014. hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
  2015. // select in the correct brush and font
  2016. GreSelectFont(hdc, ghMenuFont);
  2017. ThreadLockMenuNoModify(pmenu, &tlpMenu);
  2018. for (i = 0; i < pmenu->cItems; i++, pitem++) {
  2019. if (((psz = TextPointer(pitem->lpstr)) == NULL)
  2020. && !TestMFT(pitem, MFT_OWNERDRAW)) {
  2021. continue;
  2022. }
  2023. if (TestMFT(pitem, MFT_OWNERDRAW)) {
  2024. GreSetViewportOrg(hdc, 0, 0, NULL);
  2025. } else {
  2026. GreSetViewportOrg(hdc, pitem->xItem, pitem->yItem, NULL);
  2027. }
  2028. // this funky xLeft and yTop calculation stolen from RealDrawMenuItem
  2029. yTop = gcyMenuFontExternLeading;
  2030. cyTemp = pitem->cyItem - (gcyMenuFontChar + gcyMenuFontExternLeading +
  2031. SYSMET(CYBORDER));
  2032. if (cyTemp > 0) {
  2033. yTop += (cyTemp / 2);
  2034. }
  2035. if (fShow && TestMFS(pitem, MFS_HOTTRACK)) {
  2036. GreSelectBrush(hdc, SYSHBR(HOTLIGHT));
  2037. } else {
  2038. GreSelectBrush(hdc, hbr);
  2039. }
  2040. if (TestMFT(pitem, MFT_OWNERDRAW)) {
  2041. xxxSendMenuDrawItemMessage(hdc, ODA_DRAWENTIRE, pmenu, pitem, FALSE, 0);
  2042. } else {
  2043. TL tl;
  2044. if (pitem->cch > MENU_STRLEN) {
  2045. pchOut = (WCHAR*)UserAllocPool((pitem->cch+1) * sizeof(WCHAR), TAG_RTL);
  2046. if (pchOut == NULL) {
  2047. break;
  2048. }
  2049. ThreadLockPool(ptiCurrent, pchOut, &tl);
  2050. } else {
  2051. pchOut = szMenu;
  2052. }
  2053. xLeft = gcxMenuFontChar;
  2054. if (TestMFT(pitem, MFT_RIGHTORDER) &&
  2055. ((lpsz = TextPointer(pitem->lpstr)) != NULL))
  2056. {
  2057. xxxPSMGetTextExtent(hdc, lpsz, pitem->cch, &extent);
  2058. xLeft += (pitem->cxItem - (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE) - extent.cx);
  2059. }
  2060. if (CALL_LPK(ptiCurrent)) {
  2061. if (!fShow) {
  2062. //Becuase PSMTextOut does not use PatBlt it uses ExtTextOut.
  2063. GreSetTextColor(hdc, fFlatMenuBar ? SYSRGB(MENUBAR) : SYSRGB(MENU));
  2064. }
  2065. xxxPSMTextOut(hdc, xLeft, yTop, psz, pitem->cch, DT_PREFIXONLY);
  2066. } else {
  2067. result = GetPrefixCount(psz, pitem->cch, pchOut, pitem->cch);
  2068. xxxDrawItemUnderline(pitem, hdc, xLeft, yTop, pchOut,
  2069. LOWORD(result));
  2070. }
  2071. if (pchOut != szMenu) {
  2072. ThreadUnlockAndFreePool(ptiCurrent, &tl);
  2073. }
  2074. }
  2075. }
  2076. ThreadUnlockMenuNoModify(&tlpMenu);
  2077. _ReleaseDC(hdc);
  2078. }
  2079. /***************************************************************************\
  2080. * xxxPaintMenuBar
  2081. *
  2082. * 3/8/2000 vadimg created
  2083. \***************************************************************************/
  2084. UINT xxxPaintMenuBar(PWND pwnd, HDC hdc, int iLeftOffset, int iRightOffset,
  2085. int iTopOffset, DWORD dwFlags)
  2086. {
  2087. PMENU pMenu;
  2088. UINT cyMenu, cxMenuMax;
  2089. HBRUSH hbrOld, hbr;
  2090. TL tlpMenu;
  2091. CheckLock(pwnd);
  2092. pMenu = (PMENU)pwnd->spmenu;
  2093. if (pMenu == NULL) {
  2094. return 0;
  2095. }
  2096. ThreadLockMenuNoModify(pMenu, &tlpMenu);
  2097. /*
  2098. * Figure out whether to paint it in active or inactive colors.
  2099. */
  2100. if (dwFlags & PMB_ACTIVE) {
  2101. ClearMF(pMenu, MFINACTIVE);
  2102. } else {
  2103. SetMF(pMenu, MFINACTIVE);
  2104. }
  2105. /*
  2106. * Calculate maximum available horizontal real estate.
  2107. */
  2108. cxMenuMax = ((pwnd->rcWindow.right - iRightOffset) -
  2109. (pwnd->rcWindow.left + iLeftOffset));
  2110. /*
  2111. * If the menu has switched windows, or if either count is 0,
  2112. * then we need to recompute the menu width.
  2113. */
  2114. if (pwnd != pMenu->spwndNotify || pMenu->cxMenu == 0 ||
  2115. pMenu->cyMenu == 0) {
  2116. xxxMenuBarCompute(pMenu, pwnd, iTopOffset, iLeftOffset, cxMenuMax);
  2117. }
  2118. if (pMenu->hbrBack) {
  2119. hbr = pMenu->hbrBack;
  2120. } else {
  2121. hbr = SYSHBR(MENUBAR);
  2122. }
  2123. hbrOld = GreSelectBrush(hdc, hbr);
  2124. GrePatBlt(hdc, iLeftOffset, iTopOffset, pMenu->cxMenu, pMenu->cyMenu, PATCOPY);
  2125. /*
  2126. * Finally draw the menu itself.
  2127. */
  2128. xxxMenuDraw(hdc, pMenu);
  2129. GreSelectBrush(hdc, hbrOld);
  2130. cyMenu = pMenu->cyMenu;
  2131. ThreadUnlockMenuNoModify(&tlpMenu);
  2132. return cyMenu;
  2133. }