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.

720 lines
23 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mnpopup.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Popup Menu Support
  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 RECT_ONLEFT 0
  15. #define RECT_ONTOP 1
  16. #define RECT_ONRIGHT 2
  17. #define RECT_ONBOTTOM 3
  18. #define RECT_ORG 4
  19. BOOL TryRect(
  20. UINT wRect,
  21. int x,
  22. int y,
  23. int cx,
  24. int cy,
  25. LPRECT prcExclude,
  26. LPPOINT ppt,
  27. PMONITOR pMonitor);
  28. /***************************************************************************\
  29. * xxxTrackPopupMenuEx (API)
  30. *
  31. * Process a popup menu
  32. *
  33. * Revalidation Notes:
  34. * o if pwndOwner is always the owner of the popup menu windows, then we don't
  35. * really have to revalidate it: when it is destroyed the popup menu windows
  36. * are destroyed first because it owns them - this is detected in MenuWndProc
  37. * so we would only have to test pMenuState->fSabotaged.
  38. * o pMenuState->fSabotaged must be cleared before this top-level routine
  39. * returns, to be ready for next time menus are processed (unless we are
  40. * currently inside xxxMenuLoop())
  41. * o pMenuState->fSabotaged should be FALSE when we enter this routine.
  42. * o xxxMenuLoop always returns with pMenuState->fSabotaged clear. Use
  43. * a UserAssert to verify this.
  44. *
  45. * History:
  46. \***************************************************************************/
  47. int xxxTrackPopupMenuEx(
  48. PMENU pMenu,
  49. UINT dwFlags,
  50. int x,
  51. int y,
  52. PWND pwndOwner,
  53. CONST TPMPARAMS *lpTpm)
  54. {
  55. PMENUSTATE pMenuState;
  56. PWND pwndHierarchy;
  57. PPOPUPMENU ppopupMenuHierarchy;
  58. LONG sizeHierarchy;
  59. int cxPopup,
  60. cyPopup;
  61. BOOL fSync;
  62. int cmd;
  63. BOOL fButtonDown;
  64. TL tlpwndHierarchy;
  65. TL tlpwndT;
  66. RECT rcExclude;
  67. PTHREADINFO ptiCurrent,
  68. ptiOwner;
  69. PMONITOR pMonitor;
  70. POINT pt;
  71. CheckLock(pMenu);
  72. CheckLock(pwndOwner);
  73. /*
  74. * Capture the things we care about in case lpTpm goes away.
  75. */
  76. if (lpTpm != NULL) {
  77. if (lpTpm->cbSize != sizeof(TPMPARAMS)) {
  78. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "TrackPopupMenuEx: cbSize is invalid");
  79. return(FALSE);
  80. }
  81. rcExclude = lpTpm->rcExclude;
  82. }
  83. ptiCurrent = PtiCurrent();
  84. ptiOwner = GETPTI(pwndOwner);
  85. /*
  86. * Win95 compatibility: pwndOwner must be owned by ptiCurrent.
  87. */
  88. if (ptiCurrent != ptiOwner) {
  89. RIPMSG0(RIP_WARNING, "xxxTrackPopupMenuEx: pwndOwner not owned by ptiCurrent");
  90. return FALSE;
  91. }
  92. UserAssert(pMenu != NULL);
  93. if (ptiCurrent->pMenuState != NULL) {
  94. if (dwFlags & TPM_RECURSE) {
  95. /*
  96. * Only allow recursion if:
  97. * -The current menu mode is not about to exit
  98. * -Both menus notify the same window
  99. * -Only one thread is involved in the current menu mode
  100. * This will prevent us from getting into some random
  101. * scenarios we don't want to deal with
  102. */
  103. ppopupMenuHierarchy = ptiCurrent->pMenuState->pGlobalPopupMenu;
  104. pwndHierarchy = ppopupMenuHierarchy->spwndNotify;
  105. if (ExitMenuLoop(ptiCurrent->pMenuState, ppopupMenuHierarchy)
  106. || (pwndHierarchy == NULL)
  107. || (pwndHierarchy != pwndOwner)
  108. || (ptiCurrent->pMenuState->ptiMenuStateOwner != GETPTI(pwndHierarchy))) {
  109. RIPMSG0(RIP_WARNING, "xxxTrackPopupMenuEx: Failing TPM_RECURSE request");
  110. return FALSE;
  111. }
  112. /*
  113. * Terminate any animation
  114. */
  115. MNAnimate(ptiCurrent->pMenuState, FALSE);
  116. /*
  117. * Cancel pending show timer if any. ie, the app wants to
  118. * pop up a context menu on a popup before we drop it.
  119. */
  120. ppopupMenuHierarchy = ((ppopupMenuHierarchy->spwndActivePopup != NULL)
  121. ? ((PMENUWND)(ppopupMenuHierarchy->spwndActivePopup))->ppopupmenu
  122. : NULL);
  123. if ((ppopupMenuHierarchy != NULL) && ppopupMenuHierarchy->fShowTimer) {
  124. _KillTimer(ppopupMenuHierarchy->spwndPopupMenu, IDSYS_MNSHOW);
  125. ppopupMenuHierarchy->fShowTimer = FALSE;
  126. }
  127. /*
  128. * If we're currently on a modal menu, let's unlock the capture
  129. * so the recursive menu can get it.
  130. */
  131. if (!ptiCurrent->pMenuState->fModelessMenu) {
  132. ptiCurrent->pq->QF_flags &= ~QF_CAPTURELOCKED;
  133. }
  134. } else {
  135. /*
  136. * Allow only one guy to have a popup menu up at a time...
  137. */
  138. RIPERR0(ERROR_POPUP_ALREADY_ACTIVE, RIP_VERBOSE, "");
  139. return FALSE;
  140. }
  141. }
  142. // Is button down?
  143. if (dwFlags & TPM_RIGHTBUTTON)
  144. {
  145. fButtonDown = (_GetKeyState(VK_RBUTTON) & 0x8000) != 0;
  146. } else {
  147. fButtonDown = (_GetKeyState(VK_LBUTTON) & 0x8000) != 0;
  148. }
  149. /*
  150. * Create the menu window.
  151. */
  152. pwndHierarchy = xxxNVCreateWindowEx(
  153. WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE,
  154. (PLARGE_STRING)MENUCLASS,
  155. NULL,
  156. WS_POPUP | WS_BORDER,
  157. x, y, 100, 100,
  158. TestMF(pMenu, MNS_MODELESS) ? pwndOwner : NULL,
  159. NULL, (HANDLE)pwndOwner->hModule,
  160. NULL,
  161. WINVER);
  162. if (pwndHierarchy == NULL) {
  163. return FALSE;
  164. }
  165. if (TestWF(pwndOwner, WEFLAYOUTRTL) || (dwFlags & TPM_LAYOUTRTL)) {
  166. SetWF(pwndHierarchy, WEFLAYOUTRTL);
  167. }
  168. //
  169. // Do this so that old apps don't get weird borders on tracked popups due
  170. // to the app hack used in CreateWindowEx32.
  171. //
  172. ClrWF(pwndHierarchy, WFOLDUI);
  173. ThreadLockAlways(pwndHierarchy, &tlpwndHierarchy);
  174. #ifdef HAVE_MN_GETPPOPUPMENU
  175. ppopupMenuHierarchy = (PPOPUPMENU)xxxSendMessage(pwndHierarchy,
  176. MN_GETPPOPUPMENU, 0, 0);
  177. #else
  178. ppopupMenuHierarchy = ((PMENUWND)pwndHierarchy)->ppopupmenu;
  179. #endif
  180. ppopupMenuHierarchy->fDelayedFree = TRUE;
  181. Lock(&(ppopupMenuHierarchy->spwndNotify), pwndOwner);
  182. LockPopupMenu(ppopupMenuHierarchy, &ppopupMenuHierarchy->spmenu, pMenu);
  183. Lock(&(ppopupMenuHierarchy->spwndActivePopup), pwndHierarchy);
  184. ppopupMenuHierarchy->ppopupmenuRoot = ppopupMenuHierarchy;
  185. ppopupMenuHierarchy->fIsTrackPopup = TRUE;
  186. ppopupMenuHierarchy->fFirstClick = fButtonDown;
  187. ppopupMenuHierarchy->fRightButton = ((dwFlags & TPM_RIGHTBUTTON) != 0);
  188. if (SYSMET(MENUDROPALIGNMENT) || TestMF(pMenu, MFRTL)) {
  189. //
  190. // popup's below this one need to follow the same direction as
  191. // the other menu's on the desktop.
  192. //
  193. ppopupMenuHierarchy->fDroppedLeft = TRUE;
  194. }
  195. ppopupMenuHierarchy->fNoNotify = ((dwFlags & TPM_NONOTIFY) != 0);
  196. if (fSync = (dwFlags & TPM_RETURNCMD))
  197. ppopupMenuHierarchy->fSynchronous = TRUE;
  198. ppopupMenuHierarchy->fIsSysMenu = ((dwFlags & TPM_SYSMENU) != 0);
  199. // Set the GlobalPopupMenu variable so that EndMenu works for popupmenus so
  200. // that WinWart II people can continue to abuse undocumented functions.
  201. // This is nulled out in MNCancel.
  202. /*
  203. * This is actually needed for cleanup in case this thread ends
  204. * execution before we can free the popup. (See xxxDestroyThreadInfo)
  205. *
  206. * Note that one thread might own pwndOwner and another one might call
  207. * TrackPopupMenu (pretty normal if the two threads are attached). So
  208. * properly setting (and initializing) pMenuState is a must here.
  209. */
  210. pMenuState = xxxMNAllocMenuState(ptiCurrent, ptiOwner, ppopupMenuHierarchy);
  211. if (pMenuState == NULL) {
  212. /*
  213. * Get out. The app never knew we were here so don't notify it
  214. */
  215. dwFlags |= TPM_NONOTIFY;
  216. goto AbortTrackPopupMenuEx;
  217. }
  218. /*
  219. * Notify the app we are entering menu mode. wParam is 1 since this is a
  220. * TrackPopupMenu.
  221. */
  222. if (!ppopupMenuHierarchy->fNoNotify)
  223. xxxSendMessage(pwndOwner, WM_ENTERMENULOOP,
  224. (ppopupMenuHierarchy->fIsSysMenu ? FALSE : TRUE), 0);
  225. /*
  226. * Send off the WM_INITMENU, set ourselves up for menu mode etc...
  227. */
  228. if (!xxxMNStartMenu(ppopupMenuHierarchy, MOUSEHOLD)) {
  229. /*
  230. * ppopupMenuHierarchy has been destroyed already; let's bail
  231. */
  232. goto AbortTrackPopupMenuEx;
  233. }
  234. /*
  235. * If drag and drop, register the window as a target.
  236. */
  237. if (pMenuState->fDragAndDrop) {
  238. if (!SUCCEEDED(xxxClientRegisterDragDrop(HW(pwndHierarchy)))) {
  239. RIPMSG0(RIP_ERROR, "xxxTrackPopupMenuEx: xxxClientRegisterDragDrop failed");
  240. }
  241. }
  242. if (!ppopupMenuHierarchy->fNoNotify) {
  243. ThreadLock(ppopupMenuHierarchy->spwndNotify, &tlpwndT);
  244. xxxSendMessage(ppopupMenuHierarchy->spwndNotify, WM_INITMENUPOPUP,
  245. (WPARAM)PtoHq(pMenu), MAKELONG(0, (ppopupMenuHierarchy->fIsSysMenu ? 1: 0)));
  246. ThreadUnlock(&tlpwndT);
  247. ppopupMenuHierarchy->fSendUninit = TRUE;
  248. }
  249. /*
  250. * Size the menu window if needed...
  251. */
  252. sizeHierarchy = (LONG)xxxSendMessage(pwndHierarchy, MN_SIZEWINDOW, MNSW_SIZE, 0);
  253. if (!sizeHierarchy) {
  254. AbortTrackPopupMenuEx:
  255. xxxWindowEvent(EVENT_SYSTEM_MENUEND, pwndOwner, OBJID_WINDOW, INDEXID_CONTAINER, 0);
  256. /*
  257. * Release the mouse capture we set when we called StartMenuState...
  258. */
  259. xxxMNReleaseCapture();
  260. /* Notify the app we have exited menu mode. wParam is 1 for real
  261. * tracked popups, not sys menu. Check wFlags since ppopupHierarchy
  262. * will be gone.
  263. */
  264. if (!(dwFlags & TPM_NONOTIFY))
  265. xxxSendMessage(pwndOwner, WM_EXITMENULOOP, ((dwFlags & TPM_SYSMENU) ?
  266. FALSE : TRUE), 0L);
  267. /*
  268. * Make sure we return failure
  269. */
  270. fSync = TRUE;
  271. cmd = FALSE;
  272. goto CleanupTrackPopupMenuEx;
  273. }
  274. if (glinp.dwFlags & LINP_KEYBOARD) {
  275. pMenuState->fUnderline = TRUE;
  276. SetMF(pMenu, MFUNDERLINE);
  277. } else {
  278. ClearMF(pMenu, MFUNDERLINE);
  279. }
  280. //
  281. // Setup popup window dimensions
  282. //
  283. cxPopup = LOWORD(sizeHierarchy) + 2*SYSMET(CXFIXEDFRAME);
  284. cyPopup = HIWORD(sizeHierarchy) + 2*SYSMET(CYFIXEDFRAME);
  285. //
  286. // Calculate the monitor BEFORE we adjust the point. Otherwise, we might
  287. // move the point offscreen. In which case, we will end up pinning the
  288. // popup to the primary display, which is wrong.
  289. //
  290. pt.x = x;
  291. pt.y = y;
  292. pMonitor = _MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  293. //
  294. // Horizontal alignment
  295. //
  296. if (TestWF(pwndOwner, WEFLAYOUTRTL) && !(dwFlags & TPM_CENTERALIGN)) {
  297. dwFlags = dwFlags ^ TPM_RIGHTALIGN;
  298. }
  299. if (dwFlags & TPM_RIGHTALIGN) {
  300. #if DBG
  301. if (dwFlags & TPM_CENTERALIGN) {
  302. RIPMSG0(RIP_WARNING, "TrackPopupMenuEx: TPM_CENTERALIGN ignored");
  303. }
  304. #endif // DBG
  305. x -= cxPopup;
  306. ppopupMenuHierarchy->iDropDir = PAS_LEFT;
  307. } else if (dwFlags & TPM_CENTERALIGN) {
  308. x -= (cxPopup / 2);
  309. } else {
  310. ppopupMenuHierarchy->iDropDir = (ppopupMenuHierarchy->fDroppedLeft ? PAS_LEFT : PAS_RIGHT);
  311. }
  312. //
  313. // Vertical alignment
  314. //
  315. if (dwFlags & TPM_BOTTOMALIGN) {
  316. #if DBG
  317. if (dwFlags & TPM_VCENTERALIGN) {
  318. RIPMSG0(RIP_WARNING, "TrackPopupMenuEx: TPM_VCENTERALIGN ignored");
  319. }
  320. #endif // DBG
  321. y -= cyPopup;
  322. ppopupMenuHierarchy->iDropDir |= PAS_UP;
  323. } else if (dwFlags & TPM_VCENTERALIGN) {
  324. y -= (cyPopup / 2);
  325. } else {
  326. ppopupMenuHierarchy->iDropDir |= PAS_DOWN;
  327. }
  328. /*
  329. * If the caller provided an animation direction, use that instead
  330. */
  331. if (dwFlags & TPM_ANIMATIONBITS) {
  332. ppopupMenuHierarchy->iDropDir = ((dwFlags >> TPM_FIRSTANIBITPOS) & (PAS_VERT | PAS_HORZ));
  333. }
  334. //
  335. // Get coords to move to.
  336. //
  337. sizeHierarchy = FindBestPos(
  338. x,
  339. y,
  340. cxPopup,
  341. cyPopup,
  342. ((lpTpm != NULL) ? &rcExclude : NULL),
  343. dwFlags,
  344. ppopupMenuHierarchy,
  345. pMonitor);
  346. if (TestWF(pwndOwner, WEFLAYOUTRTL) && (ppopupMenuHierarchy->iDropDir & PAS_HORZ)) {
  347. ppopupMenuHierarchy->iDropDir ^= PAS_HORZ;
  348. }
  349. /*
  350. * If we have an animation direction and the caller wants animation,
  351. * set the bit to get it going.
  352. */
  353. if ((ppopupMenuHierarchy->iDropDir != 0) && !(dwFlags & TPM_NOANIMATION)) {
  354. ppopupMenuHierarchy->iDropDir |= PAS_OUT;
  355. }
  356. /*
  357. * Show the window. Modeless menus are not topmost and get activated.
  358. * Modal menus are topmost but don't get activated.
  359. */
  360. PlayEventSound(USER_SOUND_MENUPOPUP);
  361. xxxSetWindowPos(pwndHierarchy,
  362. (pMenuState->fModelessMenu ? PWND_TOP : PWND_TOPMOST),
  363. GET_X_LPARAM(sizeHierarchy), GET_Y_LPARAM(sizeHierarchy), 0, 0,
  364. SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOOWNERZORDER
  365. | (pMenuState->fModelessMenu ? 0 : SWP_NOACTIVATE));
  366. xxxWindowEvent(EVENT_SYSTEM_MENUPOPUPSTART, pwndHierarchy, OBJID_CLIENT, INDEXID_CONTAINER, 0);
  367. /*
  368. * We need to return TRUE for compatibility w/ async TrackPopupMenu().
  369. * It is conceivable that a menu ID could have ID 0, in which case just
  370. * returning the cmd chosen would return FALSE instead of TRUE.
  371. */
  372. /*
  373. * If mouse is in client of popup, act like clicked down
  374. */
  375. pMenuState->fButtonDown = fButtonDown;
  376. cmd = xxxMNLoop(ppopupMenuHierarchy, pMenuState, 0, FALSE);
  377. /*
  378. * If this is a modeless menu, return without clenning up because
  379. * the menu is up.
  380. */
  381. if (pMenuState->fModelessMenu) {
  382. ThreadUnlock(&tlpwndHierarchy);
  383. goto ReturnCmdOrTrue;
  384. }
  385. CleanupTrackPopupMenuEx:
  386. if (ThreadUnlock(&tlpwndHierarchy)) {
  387. if (!TestWF(pwndHierarchy, WFDESTROYED)) {
  388. xxxDestroyWindow(pwndHierarchy);
  389. }
  390. }
  391. if (pMenuState != NULL) {
  392. xxxMNEndMenuState (TRUE);
  393. }
  394. /*
  395. * Capture must be unlocked if no menu is active.
  396. */
  397. UserAssert(!(ptiCurrent->pq->QF_flags & QF_CAPTURELOCKED)
  398. || ((ptiCurrent->pMenuState != NULL)
  399. && !ptiCurrent->pMenuState->fModelessMenu));
  400. ReturnCmdOrTrue:
  401. return(fSync ? cmd : TRUE);
  402. }
  403. /***************************************************************************\
  404. *
  405. * FindBestPos()
  406. *
  407. * Gets best point to move popup menu window to, given exclusion area and
  408. * screen real estate. Note that for our purposes, we consider center
  409. * alignment to be the same as left/top alignment.
  410. *
  411. * We try to pin the menu to a particular monitor, to avoid having it
  412. * cross.
  413. *
  414. * We try four possibilities if the original position fails. The order of
  415. * these is determined by the alignment and "try" flags. Basically, we try
  416. * to move the rectangle out of the exclusion area by sliding it horizontally
  417. * or vertically without going offscreen. If we can't, then we know that
  418. * sliding it in both dimensions will also fail. So we use the original
  419. * point, clipping on screen.
  420. *
  421. * Take the example of a top-left justified popup, which should be moved
  422. * horizontally before vertically. We'll try the original point. Then
  423. * we'll try to left-justify with the right edge of the exclude rect. Then
  424. * we'll try to top-justify with the bottom edge of the exclude rect. Then
  425. * we'll try to right-justify with the left edge of the exclude rect. Then
  426. * we'll try to bottom-justify with the top edge of the exclude rect.
  427. * Finally, we'll use the original pos.
  428. *
  429. \***************************************************************************/
  430. LONG
  431. FindBestPos(
  432. int x,
  433. int y,
  434. int cx,
  435. int cy,
  436. LPRECT prcExclude,
  437. UINT wFlags,
  438. PPOPUPMENU ppopupmenu,
  439. PMONITOR pMonitor)
  440. {
  441. int iRect;
  442. int iT;
  443. UINT awRect[4];
  444. POINT ptT;
  445. RECT rcExclude;
  446. //
  447. // Clip our coords on screen first. We use the same algorithm to clip
  448. // as in Win3.1 for dudes with no exclude rect.
  449. //
  450. if (prcExclude!=NULL) {
  451. // Clip exclude rect to monitor!
  452. CopyRect(&rcExclude, prcExclude);
  453. IntersectRect(&rcExclude, &rcExclude, &pMonitor->rcMonitor);
  454. } else {
  455. SetRect(&rcExclude, x, y, x, y);
  456. }
  457. /*
  458. * Make sure popup fits completely on the screen
  459. * At least the x,y point will be on the screen.
  460. */
  461. if (x + cx > pMonitor->rcMonitor.right) {
  462. if ((wFlags & TPM_CENTERALIGN)
  463. || (x - cx < pMonitor->rcMonitor.left)
  464. || (x >= pMonitor->rcMonitor.right)) {
  465. x = pMonitor->rcMonitor.right - cx;
  466. } else {
  467. x -= cx;
  468. }
  469. if (ppopupmenu->iDropDir & PAS_HORZ) {
  470. COPY_FLAG(ppopupmenu->iDropDir, PAS_LEFT, PAS_HORZ);
  471. }
  472. }
  473. if (x < pMonitor->rcMonitor.left) {
  474. x += cx;
  475. if ((wFlags & TPM_CENTERALIGN)
  476. || (x >= pMonitor->rcMonitor.right)
  477. || (x < pMonitor->rcMonitor.left)) {
  478. x = pMonitor->rcMonitor.left;
  479. }
  480. if (ppopupmenu->iDropDir & PAS_HORZ) {
  481. COPY_FLAG(ppopupmenu->iDropDir, PAS_RIGHT, PAS_HORZ);
  482. }
  483. }
  484. /*
  485. * Make sure popup fits completely on the screen
  486. * At least the x+cx,y point will be on the screen
  487. * for right aligned menus.
  488. */
  489. if ((wFlags & TPM_RIGHTALIGN)
  490. && (x + cx > pMonitor->rcMonitor.right)) {
  491. x = pMonitor->rcMonitor.right - cx;
  492. }
  493. if (y + cy > pMonitor->rcMonitor.bottom) {
  494. if ((wFlags & TPM_VCENTERALIGN)
  495. || (y - cy < pMonitor->rcMonitor.top)
  496. || (y >= pMonitor->rcMonitor.bottom)) {
  497. y = pMonitor->rcMonitor.bottom - cy;
  498. } else {
  499. y -= cy;
  500. }
  501. if (ppopupmenu->iDropDir & PAS_VERT) {
  502. COPY_FLAG(ppopupmenu->iDropDir, PAS_UP, PAS_VERT);
  503. }
  504. }
  505. if (y < pMonitor->rcMonitor.top) {
  506. y += cy;
  507. if ((wFlags & TPM_VCENTERALIGN)
  508. || (y >= pMonitor->rcMonitor.bottom)
  509. || (y < pMonitor->rcMonitor.top)) {
  510. y = pMonitor->rcMonitor.top;
  511. }
  512. if (ppopupmenu->iDropDir & PAS_VERT) {
  513. COPY_FLAG(ppopupmenu->iDropDir, PAS_DOWN, PAS_VERT);
  514. }
  515. }
  516. //
  517. // Try first point
  518. //
  519. if (TryRect(RECT_ORG, x, y, cx, cy, &rcExclude, &ptT, pMonitor))
  520. goto FOUND;
  521. //
  522. // Sort possibilities. Get offset of horizontal rects.
  523. //
  524. iRect = (wFlags & TPM_VERTICAL) ? 2 : 0;
  525. //
  526. // Sort horizontally. Note that we treat TPM_CENTERALIGN like
  527. // TPM_LEFTALIGN.
  528. //
  529. //
  530. // If we're right-aligned, try to right-align on left side first.
  531. // Otherwise, try to left-align on right side first.
  532. //
  533. iT = (wFlags & TPM_RIGHTALIGN) ? 0 : 2;
  534. awRect[0 + iRect] = RECT_ONLEFT + iT;
  535. awRect[1 + iRect] = RECT_ONRIGHT - iT;
  536. //
  537. // Sort vertically. Note that we treat TPM_VCENTERALIGN like
  538. // TPM_TOPALIGN.
  539. //
  540. // If we're bottom-aligned, try to bottom-align with top of rect
  541. // first. Otherwise, try to top-align with bottom of exclusion first.
  542. //
  543. iT = (wFlags & TPM_BOTTOMALIGN) ? 0 : 2;
  544. awRect[2 - iRect] = RECT_ONTOP + iT;
  545. awRect[3 - iRect] = RECT_ONBOTTOM - iT;
  546. //
  547. // Loop through sorted alternatives. Note that TryRect fails immediately
  548. // if an exclusion coordinate is too close to screen edge.
  549. //
  550. for (iRect = 0; iRect < 4; iRect++) {
  551. if (TryRect(awRect[iRect], x, y, cx, cy, &rcExclude, &ptT, pMonitor)) {
  552. switch (awRect[iRect])
  553. {
  554. case RECT_ONTOP:
  555. ppopupmenu->iDropDir = PAS_UP;
  556. break;
  557. case RECT_ONLEFT:
  558. ppopupmenu->iDropDir = PAS_LEFT;
  559. break;
  560. case RECT_ONBOTTOM:
  561. ppopupmenu->iDropDir = PAS_DOWN;
  562. break;
  563. case RECT_ONRIGHT:
  564. ppopupmenu->iDropDir = PAS_RIGHT;
  565. break;
  566. }
  567. x = ptT.x;
  568. y = ptT.y;
  569. break;
  570. }
  571. }
  572. FOUND:
  573. return MAKELONG(x, y);
  574. }
  575. /***************************************************************************\
  576. *
  577. * TryRect()
  578. *
  579. * Tries to fit rect on screen without covering exclusion area. Returns
  580. * TRUE if success.
  581. *
  582. \***************************************************************************/
  583. BOOL
  584. TryRect(
  585. UINT wRect,
  586. int x,
  587. int y,
  588. int cx,
  589. int cy,
  590. LPRECT prcExclude,
  591. LPPOINT ppt,
  592. PMONITOR pMonitor)
  593. {
  594. RECT rcTry;
  595. switch (wRect) {
  596. case RECT_ONRIGHT:
  597. x = prcExclude->right;
  598. if (x + cx > pMonitor->rcMonitor.right)
  599. return FALSE;
  600. break;
  601. case RECT_ONBOTTOM:
  602. y = prcExclude->bottom;
  603. if (y + cy > pMonitor->rcMonitor.bottom)
  604. return FALSE;
  605. break;
  606. case RECT_ONLEFT:
  607. x = prcExclude->left - cx;
  608. if (x < pMonitor->rcMonitor.left)
  609. return FALSE;
  610. break;
  611. case RECT_ONTOP:
  612. y = prcExclude->top - cy;
  613. if (y < pMonitor->rcMonitor.top)
  614. return FALSE;
  615. break;
  616. //
  617. // case RECT_ORG:
  618. // NOP;
  619. // break;
  620. //
  621. }
  622. ppt->x = x;
  623. ppt->y = y;
  624. rcTry.left = x;
  625. rcTry.top = y;
  626. rcTry.right = x + cx;
  627. rcTry.bottom = y + cy;
  628. return(!IntersectRect(&rcTry, &rcTry, prcExclude));
  629. }