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

mnpopup.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: mnpopup.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Popup Menu Support 00007 * 00008 * History: 00009 * 10-10-90 JimA Cleanup. 00010 * 03-18-91 IanJa Window revalidation added 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 #define RECT_ONLEFT 0 00017 #define RECT_ONTOP 1 00018 #define RECT_ONRIGHT 2 00019 #define RECT_ONBOTTOM 3 00020 #define RECT_ORG 4 00021 00022 BOOL TryRect( 00023 UINT wRect, 00024 int x, 00025 int y, 00026 int cx, 00027 int cy, 00028 LPRECT prcExclude, 00029 LPPOINT ppt, 00030 PMONITOR pMonitor); 00031 00032 /***************************************************************************\ 00033 * xxxTrackPopupMenuEx (API) 00034 * 00035 * Process a popup menu 00036 * 00037 * Revalidation Notes: 00038 * o if pwndOwner is always the owner of the popup menu windows, then we don't 00039 * really have to revalidate it: when it is destroyed the popup menu windows 00040 * are destroyed first because it owns them - this is detected in MenuWndProc 00041 * so we would only have to test pMenuState->fSabotaged. 00042 * o pMenuState->fSabotaged must be cleared before this top-level routine 00043 * returns, to be ready for next time menus are processed (unless we are 00044 * currently inside xxxMenuLoop()) 00045 * o pMenuState->fSabotaged should be FALSE when we enter this routine. 00046 * o xxxMenuLoop always returns with pMenuState->fSabotaged clear. Use 00047 * a UserAssert to verify this. 00048 * 00049 * History: 00050 \***************************************************************************/ 00051 00052 int xxxTrackPopupMenuEx( 00053 PMENU pMenu, 00054 UINT dwFlags, 00055 int x, 00056 int y, 00057 PWND pwndOwner, 00058 CONST TPMPARAMS *lpTpm) 00059 { 00060 PMENUSTATE pMenuState; 00061 PWND pwndHierarchy; 00062 PPOPUPMENU ppopupMenuHierarchy; 00063 LONG sizeHierarchy; 00064 int cxPopup, 00065 cyPopup; 00066 BOOL fSync; 00067 int cmd; 00068 BOOL fButtonDown; 00069 TL tlpwndHierarchy; 00070 TL tlpwndT; 00071 RECT rcExclude; 00072 PTHREADINFO ptiCurrent, 00073 ptiOwner; 00074 PMONITOR pMonitor; 00075 POINT pt; 00076 00077 CheckLock(pMenu); 00078 CheckLock(pwndOwner); 00079 00080 /* 00081 * Capture the things we care about in case lpTpm goes away. 00082 */ 00083 if (lpTpm != NULL) { 00084 if (lpTpm->cbSize != sizeof(TPMPARAMS)) { 00085 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "TrackPopupMenuEx: cbSize is invalid"); 00086 return(FALSE); 00087 } 00088 rcExclude = lpTpm->rcExclude; 00089 } 00090 00091 ptiCurrent = PtiCurrent(); 00092 ptiOwner = GETPTI(pwndOwner); 00093 00094 /* 00095 * Win95 compatibility: pwndOwner must be owned by ptiCurrent. 00096 */ 00097 if (ptiCurrent != ptiOwner) { 00098 RIPMSG0(RIP_WARNING, "xxxTrackPopupMenuEx: pwndOwner not owned by ptiCurrent"); 00099 return FALSE; 00100 } 00101 00102 UserAssert(pMenu != NULL); 00103 if (ptiCurrent->pMenuState != NULL) { 00104 00105 if (dwFlags & TPM_RECURSE) { 00106 /* 00107 * Only allow recursion if: 00108 * -The current menu mode is not about to exit 00109 * -Both menus notify the same window 00110 * -Only one thread is involved in the current menu mode 00111 * This will prevent us from getting into some random 00112 * scenarios we don't want to deal with 00113 */ 00114 ppopupMenuHierarchy = ptiCurrent->pMenuState->pGlobalPopupMenu; 00115 pwndHierarchy = ppopupMenuHierarchy->spwndNotify; 00116 if (ExitMenuLoop(ptiCurrent->pMenuState, ppopupMenuHierarchy) 00117 || (pwndHierarchy == NULL) 00118 || (pwndHierarchy != pwndOwner) 00119 || (ptiCurrent->pMenuState->ptiMenuStateOwner != GETPTI(pwndHierarchy))) { 00120 00121 RIPMSG0(RIP_WARNING, "xxxTrackPopupMenuEx: Failing TPM_RECURSE request"); 00122 return FALSE; 00123 } 00124 /* 00125 * Terminate any animation 00126 */ 00127 MNAnimate(ptiCurrent->pMenuState, FALSE); 00128 /* 00129 * Cancel pending show timer if any. ie, the app wants to 00130 * pop up a context menu on a popup before we drop it. 00131 */ 00132 ppopupMenuHierarchy = ((ppopupMenuHierarchy->spwndActivePopup != NULL) 00133 ? ((PMENUWND)(ppopupMenuHierarchy->spwndActivePopup))->ppopupmenu 00134 : NULL); 00135 if ((ppopupMenuHierarchy != NULL) && ppopupMenuHierarchy->fShowTimer) { 00136 00137 _KillTimer(ppopupMenuHierarchy->spwndPopupMenu, IDSYS_MNSHOW); 00138 ppopupMenuHierarchy->fShowTimer = FALSE; 00139 } 00140 /* 00141 * If we're currently on a modal menu, let's unlock the capture 00142 * so the recursive menu can get it. 00143 */ 00144 if (!ptiCurrent->pMenuState->fModelessMenu) { 00145 ptiCurrent->pq->QF_flags &= ~QF_CAPTURELOCKED; 00146 } 00147 } else { 00148 /* 00149 * Allow only one guy to have a popup menu up at a time... 00150 */ 00151 RIPERR0(ERROR_POPUP_ALREADY_ACTIVE, RIP_VERBOSE, ""); 00152 return FALSE; 00153 } 00154 } 00155 00156 // Is button down? 00157 00158 if (dwFlags & TPM_RIGHTBUTTON) 00159 { 00160 fButtonDown = (_GetKeyState(VK_RBUTTON) & 0x8000) != 0; 00161 } else { 00162 fButtonDown = (_GetKeyState(VK_LBUTTON) & 0x8000) != 0; 00163 } 00164 00165 /* 00166 * Create the menu window. 00167 */ 00168 pwndHierarchy = xxxCreateWindowEx( 00169 WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 00170 (PLARGE_STRING)MENUCLASS, 00171 NULL, 00172 WS_POPUP | WS_BORDER, 00173 x, y, 100, 100, 00174 TestMF(pMenu, MNS_MODELESS) ? pwndOwner : NULL, 00175 NULL, (HANDLE)pwndOwner->hModule, 00176 NULL, 00177 WINVER); 00178 00179 if (pwndHierarchy == NULL) { 00180 return FALSE; 00181 } 00182 00183 #ifdef USE_MIRRORING 00184 if (TestWF(pwndOwner, WEFLAYOUTRTL)) 00185 SetWF(pwndHierarchy, WEFLAYOUTRTL); 00186 #endif 00187 00188 // 00189 // Do this so that old apps don't get weird borders on tracked popups due 00190 // to the app hack used in CreateWindowEx32. 00191 // 00192 ClrWF(pwndHierarchy, WFOLDUI); 00193 00194 ThreadLockAlways(pwndHierarchy, &tlpwndHierarchy); 00195 00196 #ifdef HAVE_MN_GETPPOPUPMENU 00197 ppopupMenuHierarchy = (PPOPUPMENU)xxxSendMessage(pwndHierarchy, 00198 MN_GETPPOPUPMENU, 0, 0); 00199 #else 00200 ppopupMenuHierarchy = ((PMENUWND)pwndHierarchy)->ppopupmenu; 00201 #endif 00202 00203 00204 ppopupMenuHierarchy->fDelayedFree = TRUE; 00205 Lock(&(ppopupMenuHierarchy->spwndNotify), pwndOwner); 00206 LockPopupMenu(ppopupMenuHierarchy, &ppopupMenuHierarchy->spmenu, pMenu); 00207 Lock(&(ppopupMenuHierarchy->spwndActivePopup), pwndHierarchy); 00208 ppopupMenuHierarchy->ppopupmenuRoot = ppopupMenuHierarchy; 00209 ppopupMenuHierarchy->fIsTrackPopup = TRUE; 00210 ppopupMenuHierarchy->fFirstClick = fButtonDown; 00211 ppopupMenuHierarchy->fRightButton = ((dwFlags & TPM_RIGHTBUTTON) != 0); 00212 if (SYSMET(MENUDROPALIGNMENT) || TestMF(pMenu, MFRTL)) { 00213 // 00214 // popup's below this one need to follow the same direction as 00215 // the other menu's on the desktop. 00216 // 00217 ppopupMenuHierarchy->fDroppedLeft = TRUE; 00218 } 00219 ppopupMenuHierarchy->fNoNotify = ((dwFlags & TPM_NONOTIFY) != 0); 00220 00221 if (fSync = (dwFlags & TPM_RETURNCMD)) 00222 ppopupMenuHierarchy->fSynchronous = TRUE; 00223 00224 ppopupMenuHierarchy->fIsSysMenu = ((dwFlags & TPM_SYSMENU) != 0); 00225 00226 // Set the GlobalPopupMenu variable so that EndMenu works for popupmenus so 00227 // that WinWart II people can continue to abuse undocumented functions. 00228 // This is nulled out in MNCancel. 00229 /* 00230 * This is actually needed for cleanup in case this thread ends 00231 * execution before we can free the popup. (See xxxDestroyThreadInfo) 00232 * 00233 * Note that one thread might own pwndOwner and another one might call 00234 * TrackPopupMenu (pretty normal if the two threads are attached). So 00235 * properly setting (and initializing) pMenuState is a must here. 00236 */ 00237 pMenuState = xxxMNAllocMenuState(ptiCurrent, ptiOwner, ppopupMenuHierarchy); 00238 if (pMenuState == NULL) { 00239 /* 00240 * Get out. The app never knew we were here so don't notify it 00241 */ 00242 dwFlags |= TPM_NONOTIFY; 00243 goto AbortTrackPopupMenuEx; 00244 } 00245 00246 /* 00247 * Notify the app we are entering menu mode. wParam is 1 since this is a 00248 * TrackPopupMenu. 00249 */ 00250 00251 if (!ppopupMenuHierarchy->fNoNotify) 00252 xxxSendMessage(pwndOwner, WM_ENTERMENULOOP, 00253 (ppopupMenuHierarchy->fIsSysMenu ? FALSE : TRUE), 0); 00254 00255 /* 00256 * Send off the WM_INITMENU, set ourselves up for menu mode etc... 00257 */ 00258 if (!xxxMNStartMenu(ppopupMenuHierarchy, MOUSEHOLD)) { 00259 /* 00260 * ppopupMenuHierarchy has been destroyed already; let's bail 00261 */ 00262 goto AbortTrackPopupMenuEx; 00263 } 00264 00265 /* 00266 * If drag and drop, register the window as a target. 00267 */ 00268 if (pMenuState->fDragAndDrop) { 00269 if (!SUCCEEDED(xxxClientRegisterDragDrop(HW(pwndHierarchy)))) { 00270 RIPMSG0(RIP_ERROR, "xxxTrackPopupMenuEx: xxxClientRegisterDragDrop failed"); 00271 } 00272 } 00273 00274 if (!ppopupMenuHierarchy->fNoNotify) { 00275 ThreadLock(ppopupMenuHierarchy->spwndNotify, &tlpwndT); 00276 xxxSendMessage(ppopupMenuHierarchy->spwndNotify, WM_INITMENUPOPUP, 00277 (WPARAM)PtoHq(pMenu), MAKELONG(0, (ppopupMenuHierarchy->fIsSysMenu ? 1: 0))); 00278 ThreadUnlock(&tlpwndT); 00279 ppopupMenuHierarchy->fSendUninit = TRUE; 00280 } 00281 00282 /* 00283 * Size the menu window if needed... 00284 */ 00285 sizeHierarchy = (LONG)xxxSendMessage(pwndHierarchy, MN_SIZEWINDOW, MNSW_SIZE, 0); 00286 00287 if (!sizeHierarchy) { 00288 00289 AbortTrackPopupMenuEx: 00290 if (FWINABLE()) { 00291 xxxWindowEvent(EVENT_SYSTEM_MENUEND, pwndOwner, OBJID_WINDOW, INDEXID_CONTAINER, 0); 00292 } 00293 /* 00294 * Release the mouse capture we set when we called StartMenuState... 00295 */ 00296 xxxMNReleaseCapture(); 00297 00298 /* Notify the app we have exited menu mode. wParam is 1 for real 00299 * tracked popups, not sys menu. Check wFlags since ppopupHierarchy 00300 * will be gone. 00301 */ 00302 if (!(dwFlags & TPM_NONOTIFY)) 00303 xxxSendMessage(pwndOwner, WM_EXITMENULOOP, ((dwFlags & TPM_SYSMENU) ? 00304 FALSE : TRUE), 0L); 00305 00306 /* 00307 * Make sure we return failure 00308 */ 00309 fSync = TRUE; 00310 cmd = FALSE; 00311 goto CleanupTrackPopupMenuEx; 00312 } 00313 00314 if (glinp.dwFlags & LINP_KEYBOARD) { 00315 pMenuState->fUnderline = TRUE; 00316 SetMF(pMenu, MFUNDERLINE); 00317 } 00318 00319 // 00320 // Setup popup window dimensions 00321 // 00322 cxPopup = LOWORD(sizeHierarchy) + 2*SYSMET(CXFIXEDFRAME); 00323 cyPopup = HIWORD(sizeHierarchy) + 2*SYSMET(CYFIXEDFRAME); 00324 00325 // 00326 // Calculate the monitor BEFORE we adjust the point. Otherwise, we might 00327 // move the point offscreen. In which case, we will end up pinning the 00328 // popup to the primary display, which is wrong. 00329 // 00330 pt.x = x; 00331 pt.y = y; 00332 pMonitor = _MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); 00333 00334 // 00335 // Horizontal alignment 00336 // 00337 #ifdef USE_MIRRORING 00338 if (TestWF(pwndOwner, WEFLAYOUTRTL) && !(dwFlags & TPM_CENTERALIGN)) { 00339 dwFlags = dwFlags ^ TPM_RIGHTALIGN; 00340 } 00341 #endif 00342 if (dwFlags & TPM_RIGHTALIGN) { 00343 #if DBG 00344 if (dwFlags & TPM_CENTERALIGN) { 00345 RIPMSG0(RIP_WARNING, "TrackPopupMenuEx: TPM_CENTERALIGN ignored"); 00346 } 00347 #endif // DBG 00348 00349 x -= cxPopup; 00350 ppopupMenuHierarchy->iDropDir = PAS_LEFT; 00351 } else if (dwFlags & TPM_CENTERALIGN) { 00352 x -= (cxPopup / 2); 00353 } else { 00354 ppopupMenuHierarchy->iDropDir = (ppopupMenuHierarchy->fDroppedLeft ? PAS_LEFT : PAS_RIGHT); 00355 } 00356 00357 // 00358 // Vertical alignment 00359 // 00360 if (dwFlags & TPM_BOTTOMALIGN) { 00361 #if DBG 00362 if (dwFlags & TPM_VCENTERALIGN) { 00363 RIPMSG0(RIP_WARNING, "TrackPopupMenuEx: TPM_VCENTERALIGN ignored"); 00364 } 00365 #endif // DBG 00366 00367 y -= cyPopup; 00368 ppopupMenuHierarchy->iDropDir |= PAS_UP; 00369 } else if (dwFlags & TPM_VCENTERALIGN) { 00370 y -= (cyPopup / 2); 00371 } else { 00372 ppopupMenuHierarchy->iDropDir |= PAS_DOWN; 00373 } 00374 /* 00375 * If the caller provided an animation direction, use that instead 00376 */ 00377 if (dwFlags & TPM_ANIMATIONBITS) { 00378 ppopupMenuHierarchy->iDropDir = ((dwFlags >> TPM_FIRSTANIBITPOS) & (PAS_VERT | PAS_HORZ)); 00379 } 00380 // 00381 // Get coords to move to. 00382 // 00383 sizeHierarchy = FindBestPos( 00384 x, 00385 y, 00386 cxPopup, 00387 cyPopup, 00388 ((lpTpm != NULL) ? &rcExclude : NULL), 00389 dwFlags, 00390 ppopupMenuHierarchy, 00391 pMonitor); 00392 00393 #ifdef USE_MIRRORING 00394 if (TestWF(pwndOwner, WEFLAYOUTRTL) && (ppopupMenuHierarchy->iDropDir & PAS_HORZ)) { 00395 ppopupMenuHierarchy->iDropDir ^= PAS_HORZ; 00396 } 00397 #endif 00398 00399 /* 00400 * If we have an animation direction and the caller wants animation, 00401 * set the bit to get it going. 00402 */ 00403 if ((ppopupMenuHierarchy->iDropDir != 0) && !(dwFlags & TPM_NOANIMATION)) { 00404 ppopupMenuHierarchy->iDropDir |= PAS_OUT; 00405 } 00406 00407 /* 00408 * Show the window. Modeless menus are not topmost and get activated. 00409 * Modal menus are topmost but don't get activated. 00410 */ 00411 PlayEventSound(USER_SOUND_MENUPOPUP); 00412 xxxSetWindowPos(pwndHierarchy, 00413 (pMenuState->fModelessMenu ? PWND_TOP : PWND_TOPMOST), 00414 GET_X_LPARAM(sizeHierarchy), GET_Y_LPARAM(sizeHierarchy), 0, 0, 00415 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOOWNERZORDER 00416 | (pMenuState->fModelessMenu ? 0 : SWP_NOACTIVATE)); 00417 00418 if (FWINABLE()) { 00419 xxxWindowEvent(EVENT_SYSTEM_MENUPOPUPSTART, pwndHierarchy, OBJID_CLIENT, INDEXID_CONTAINER, 0); 00420 } 00421 // 00422 // We need to return TRUE for compatibility w/ async TrackPopupMenu(). 00423 // It is conceivable that a menu ID could have ID 0, in which case just 00424 // returning the cmd chosen would return FALSE instead of TRUE. 00425 // 00426 00427 // 00428 // If mouse is in client of popup, act like clicked down 00429 // 00430 pMenuState->fButtonDown = fButtonDown; 00431 00432 cmd = xxxMNLoop(ppopupMenuHierarchy, pMenuState, 0, FALSE); 00433 00434 /* 00435 * If this is a modeless menu, return without clenning up because 00436 * the menu is up. 00437 */ 00438 if (pMenuState->fModelessMenu) { 00439 ThreadUnlock(&tlpwndHierarchy); 00440 goto ReturnCmdOrTrue; 00441 } 00442 00443 CleanupTrackPopupMenuEx: 00444 00445 if (ThreadUnlock(&tlpwndHierarchy)) { 00446 if (!TestWF(pwndHierarchy, WFDESTROYED)) { 00447 xxxDestroyWindow(pwndHierarchy); 00448 } 00449 } 00450 00451 if (pMenuState != NULL) { 00452 xxxMNEndMenuState (TRUE); 00453 } 00454 00455 /* 00456 * Capture must be unlocked if no menu is active. 00457 */ 00458 UserAssert(!(ptiCurrent->pq->QF_flags & QF_CAPTURELOCKED) 00459 || ((ptiCurrent->pMenuState != NULL) 00460 && !ptiCurrent->pMenuState->fModelessMenu)); 00461 00462 00463 ReturnCmdOrTrue: 00464 return(fSync ? cmd : TRUE); 00465 } 00466 00467 /***************************************************************************\ 00468 * 00469 * FindBestPos() 00470 * 00471 * Gets best point to move popup menu window to, given exclusion area and 00472 * screen real estate. Note that for our purposes, we consider center 00473 * alignment to be the same as left/top alignment. 00474 * 00475 * We try to pin the menu to a particular monitor, to avoid having it 00476 * cross. 00477 * 00478 * We try four possibilities if the original position fails. The order of 00479 * these is determined by the alignment and "try" flags. Basically, we try 00480 * to move the rectangle out of the exclusion area by sliding it horizontally 00481 * or vertically without going offscreen. If we can't, then we know that 00482 * sliding it in both dimensions will also fail. So we use the original 00483 * point, clipping on screen. 00484 * 00485 * Take the example of a top-left justified popup, which should be moved 00486 * horizontally before vertically. We'll try the original point. Then 00487 * we'll try to left-justify with the right edge of the exclude rect. Then 00488 * we'll try to top-justify with the bottom edge of the exclude rect. Then 00489 * we'll try to right-justify with the left edge of the exclude rect. Then 00490 * we'll try to bottom-justify with the top edge of the exclude rect. 00491 * Finally, we'll use the original pos. 00492 * 00493 \***************************************************************************/ 00494 00495 LONG 00496 FindBestPos( 00497 int x, 00498 int y, 00499 int cx, 00500 int cy, 00501 LPRECT prcExclude, 00502 UINT wFlags, 00503 PPOPUPMENU ppopupmenu, 00504 PMONITOR pMonitor) 00505 { 00506 int iRect; 00507 int iT; 00508 UINT awRect[4]; 00509 POINT ptT; 00510 RECT rcExclude; 00511 // 00512 // Clip our coords on screen first. We use the same algorithm to clip 00513 // as in Win3.1 for dudes with no exclude rect. 00514 // 00515 00516 if (prcExclude!=NULL) { 00517 // Clip exclude rect to monitor! 00518 CopyRect(&rcExclude, prcExclude); 00519 IntersectRect(&rcExclude, &rcExclude, &pMonitor->rcMonitor); 00520 } else { 00521 SetRect(&rcExclude, x, y, x, y); 00522 } 00523 00524 00525 /* 00526 * Make sure popup fits completely on the screen 00527 * At least the x,y point will be on the screen. 00528 */ 00529 if (x + cx > pMonitor->rcMonitor.right) { 00530 if ((wFlags & TPM_CENTERALIGN) 00531 || (x - cx < pMonitor->rcMonitor.left) 00532 || (x >= pMonitor->rcMonitor.right)) { 00533 x = pMonitor->rcMonitor.right - cx; 00534 } else { 00535 x -= cx; 00536 } 00537 if (ppopupmenu->iDropDir & PAS_HORZ) { 00538 COPY_FLAG(ppopupmenu->iDropDir, PAS_LEFT, PAS_HORZ); 00539 } 00540 } 00541 00542 if (x < pMonitor->rcMonitor.left) { 00543 x += cx; 00544 if ((wFlags & TPM_CENTERALIGN) 00545 || (x >= pMonitor->rcMonitor.right) 00546 || (x < pMonitor->rcMonitor.left)) { 00547 x = pMonitor->rcMonitor.left; 00548 } 00549 if (ppopupmenu->iDropDir & PAS_HORZ) { 00550 COPY_FLAG(ppopupmenu->iDropDir, PAS_RIGHT, PAS_HORZ); 00551 } 00552 } 00553 00554 00555 if (y + cy > pMonitor->rcMonitor.bottom) { 00556 if ((wFlags & TPM_VCENTERALIGN) 00557 || (y - cy < pMonitor->rcMonitor.top) 00558 || (y >= pMonitor->rcMonitor.bottom)) { 00559 y = pMonitor->rcMonitor.bottom - cy; 00560 } else { 00561 y -= cy; 00562 } 00563 if (ppopupmenu->iDropDir & PAS_VERT) { 00564 COPY_FLAG(ppopupmenu->iDropDir, PAS_UP, PAS_VERT); 00565 } 00566 } 00567 00568 if (y < pMonitor->rcMonitor.top) { 00569 y += cy; 00570 if ((wFlags & TPM_VCENTERALIGN) 00571 || (y >= pMonitor->rcMonitor.bottom) 00572 || (y < pMonitor->rcMonitor.top)) { 00573 y = pMonitor->rcMonitor.top; 00574 } 00575 if (ppopupmenu->iDropDir & PAS_VERT) { 00576 COPY_FLAG(ppopupmenu->iDropDir, PAS_DOWN, PAS_VERT); 00577 } 00578 } 00579 00580 // 00581 // Try first point 00582 // 00583 if (TryRect(RECT_ORG, x, y, cx, cy, &rcExclude, &ptT, pMonitor)) 00584 goto FOUND; 00585 00586 // 00587 // Sort possibilities. Get offset of horizontal rects. 00588 // 00589 iRect = (wFlags & TPM_VERTICAL) ? 2 : 0; 00590 00591 // 00592 // Sort horizontally. Note that we treat TPM_CENTERALIGN like 00593 // TPM_LEFTALIGN. 00594 // 00595 // 00596 // If we're right-aligned, try to right-align on left side first. 00597 // Otherwise, try to left-align on right side first. 00598 // 00599 iT = (wFlags & TPM_RIGHTALIGN) ? 0 : 2; 00600 00601 awRect[0 + iRect] = RECT_ONLEFT + iT; 00602 awRect[1 + iRect] = RECT_ONRIGHT - iT; 00603 00604 // 00605 // Sort vertically. Note that we treat TPM_VCENTERALIGN like 00606 // TPM_TOPALIGN. 00607 // 00608 // If we're bottom-aligned, try to bottom-align with top of rect 00609 // first. Otherwise, try to top-align with bottom of exclusion first. 00610 // 00611 iT = (wFlags & TPM_BOTTOMALIGN) ? 0 : 2; 00612 00613 awRect[2 - iRect] = RECT_ONTOP + iT; 00614 awRect[3 - iRect] = RECT_ONBOTTOM - iT; 00615 00616 // 00617 // Loop through sorted alternatives. Note that TryRect fails immediately 00618 // if an exclusion coordinate is too close to screen edge. 00619 // 00620 00621 for (iRect = 0; iRect < 4; iRect++) { 00622 if (TryRect(awRect[iRect], x, y, cx, cy, &rcExclude, &ptT, pMonitor)) { 00623 switch (awRect[iRect]) 00624 { 00625 case RECT_ONTOP: 00626 ppopupmenu->iDropDir = PAS_UP; 00627 break; 00628 case RECT_ONLEFT: 00629 ppopupmenu->iDropDir = PAS_LEFT; 00630 break; 00631 case RECT_ONBOTTOM: 00632 ppopupmenu->iDropDir = PAS_DOWN; 00633 break; 00634 case RECT_ONRIGHT: 00635 ppopupmenu->iDropDir = PAS_RIGHT; 00636 break; 00637 } 00638 00639 x = ptT.x; 00640 y = ptT.y; 00641 break; 00642 } 00643 } 00644 00645 FOUND: 00646 return MAKELONG(x, y); 00647 } 00648 00649 00650 00651 /***************************************************************************\ 00652 * 00653 * TryRect() 00654 * 00655 * Tries to fit rect on screen without covering exclusion area. Returns 00656 * TRUE if success. 00657 * 00658 \***************************************************************************/ 00659 00660 BOOL 00661 TryRect( 00662 UINT wRect, 00663 int x, 00664 int y, 00665 int cx, 00666 int cy, 00667 LPRECT prcExclude, 00668 LPPOINT ppt, 00669 PMONITOR pMonitor) 00670 { 00671 RECT rcTry; 00672 00673 switch (wRect) { 00674 case RECT_ONRIGHT: 00675 x = prcExclude->right; 00676 if (x + cx > pMonitor->rcMonitor.right) 00677 return FALSE; 00678 break; 00679 00680 case RECT_ONBOTTOM: 00681 y = prcExclude->bottom; 00682 if (y + cy > pMonitor->rcMonitor.bottom) 00683 return FALSE; 00684 break; 00685 00686 case RECT_ONLEFT: 00687 x = prcExclude->left - cx; 00688 if (x < pMonitor->rcMonitor.left) 00689 return FALSE; 00690 break; 00691 00692 case RECT_ONTOP: 00693 y = prcExclude->top - cy; 00694 if (y < pMonitor->rcMonitor.top) 00695 return FALSE; 00696 break; 00697 00698 // 00699 // case RECT_ORG: 00700 // NOP; 00701 // break; 00702 // 00703 } 00704 00705 ppt->x = x; 00706 ppt->y = y; 00707 00708 rcTry.left = x; 00709 rcTry.top = y; 00710 rcTry.right = x + cx; 00711 rcTry.bottom = y + cy; 00712 00713 return(!IntersectRect(&rcTry, &rcTry, prcExclude)); 00714 }

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