/****************************** Module Header ******************************\ * Module Name: dlgmgrc.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains client side dialog functionality * * History: * 15-Dec-1993 JohnC Pulled functions from user\server. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /***************************************************************************\ * UT_PrevGroupItem * * History: \***************************************************************************/ PWND UT_PrevGroupItem( PWND pwndDlg, PWND pwndCurrent) { PWND pwnd, pwndPrev; if (pwndCurrent == NULL || !TestWF(pwndCurrent, WFGROUP)) return _PrevControl(pwndDlg, pwndCurrent, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); pwndPrev = pwndCurrent; while (TRUE) { pwnd = _NextControl(pwndDlg, pwndPrev, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); if (TestWF(pwnd, WFGROUP) || pwnd == pwndCurrent) return pwndPrev; pwndPrev = pwnd; } } /***************************************************************************\ * UT_NextGroupItem * * History: \***************************************************************************/ PWND UT_NextGroupItem( PWND pwndDlg, PWND pwndCurrent) { PWND pwnd, pwndNext; pwnd = _NextControl(pwndDlg, pwndCurrent, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); if (pwndCurrent == NULL || !TestWF(pwnd, WFGROUP)) return pwnd; pwndNext = pwndCurrent; while (!TestWF(pwndNext, WFGROUP)) { pwnd = _PrevControl(pwndDlg, pwndNext, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); if (pwnd == pwndCurrent) return pwndNext; pwndNext = pwnd; } return pwndNext; } /***************************************************************************\ * _PrevControl * * History: \***************************************************************************/ PWND _PrevControl( PWND pwndRoot, PWND pwndStart, UINT uFlags) { BOOL fFirstFound; PWND pwndNext; PWND pwnd, pwndFirst; if (!pwndStart) return(NULL); UserAssert(pwndRoot != pwndStart); UserAssert(!TestWF(pwndStart, WEFCONTROLPARENT)); pwnd = _NextControl(pwndRoot, NULL, uFlags); pwndFirst = pwnd; fFirstFound = FALSE; while (pwndNext = _NextControl(pwndRoot, pwnd, uFlags)) { if (pwndNext == pwndStart) break; if (pwndNext == pwndFirst) { if (fFirstFound) { RIPMSG0(RIP_WARNING, "_PrevControl: Loop Detected"); break; } else { fFirstFound = TRUE; } } pwnd = pwndNext; } return pwnd; } /***************************************************************************\ * * GetChildControl() * * Gets valid ancestor of given window. * A valid dialog control is a direct descendant of a "form" control. * \***************************************************************************/ PWND _GetChildControl(PWND pwndRoot, PWND pwndChild) { PWND pwndControl = NULL; while (pwndChild && TestwndChild(pwndChild) && (pwndChild != pwndRoot)) { pwndControl = pwndChild; pwndChild = REBASEPWND(pwndChild, spwndParent); if (TestWF(pwndChild, WEFCONTROLPARENT)) break; } return(pwndControl); } /***************************************************************************\ * * _NextSibblingOrAncestor * * Called by _NextControl. It returns the next control to pwndStart. If there * is a next window (pwndStart->spwndNext), then that is it. * Otherwise, the next control is up the parent chain. However, if it's already * at the top of the chain (pwndRoot == pwndStart->spwndParent), then the next * control is the first child of pwndRoot. But if it's not at the top of the chain, * then the next control is pwndStart->spwndParent or an ancestor. * \***************************************************************************/ PWND _NextSibblingOrAncestor (PWND pwndRoot, PWND pwndStart) { PWND pwndParent; #if DBG PWND pwndNext; #endif // If there is a sibbling, go for it if (pwndStart->spwndNext != NULL) { return (REBASEALWAYS(pwndStart, spwndNext)); } // If it cannot go up the parent chain, then return the first sibbling. pwndParent = REBASEALWAYS(pwndStart, spwndParent); if (pwndParent == pwndRoot) { // Note that if pwndStart doesn't have any sibblings, // this will return pwndStart again return (REBASEALWAYS(pwndParent, spwndChild)); } // Otherwise walk up the parent chain looking for the first window with // a WS_EX_CONTROLPARENT parent. #if DBG pwndNext = #else return #endif _GetChildControl(pwndRoot, pwndParent); #if DBG if ((pwndNext != pwndParent) || !TestWF(pwndParent, WEFCONTROLPARENT)) { // Code looping through the controls in a dialog might go into an infinite // loop because of this (i.e., xxxRemoveDefaultButton, _GetNextDlgTabItem,..) // We've walked up the parent chain but will never walk down the child chain again // because there is a NON WS_EX_CONTROLPARENT parent window somewhere in the chain. RIPMSG0 (RIP_ERROR, "_NextSibblingOrAncestor: Non WS_EX_CONTROLPARENT window in parent chain"); } return pwndNext; #endif } /***************************************************************************\ * * _NextControl() * * It searches for the next NON WS_EX_CONTROLPARENT control following pwndStart. * If pwndStart is NULL, the search begins with pwndRoot's first child; * otherwise, it starts with the control next to pwndStart. * This is a depth-first search that can start anywhere in the window tree. * uFlags determine what WS_EX_CONTROLPARENT windows should be skipped or recursed into. * If skipping a window, the search moves to the next control (see _NextSibblingOrAncestor); * otherwise, the search walks down the child chain (recursive call). * If the search fails, it returns pwndRoot. * \***************************************************************************/ PWND _NextControl( PWND pwndRoot, PWND pwndStart, UINT uFlags) { BOOL fSkip, fAncestor; PWND pwndLast, pwndSibblingLoop; /* Bug 272874 - joejo * * Stop infinite loop by only looping a finite number of times and * then bailing. */ int nLoopCount = 0; UserAssert (pwndRoot != NULL); if (pwndStart == NULL) { // Start with pwndRoot's first child pwndStart = REBASEPWND(pwndRoot, spwndChild); pwndLast = pwndStart; fAncestor = FALSE; } else { UserAssert ((pwndRoot != pwndStart) && _IsDescendant(pwndRoot, pwndStart)); // Save starting handle and get next one pwndLast = pwndStart; pwndSibblingLoop = pwndStart; fAncestor = TRUE; goto TryNextOne; } // If no more controls, game over if (pwndStart == NULL) { return pwndRoot; } // Search for a non WS_EX_CONTROLPARENT window; if a window should be skipped, // try its spwndNext; otherwise, walk down its child chain. pwndSibblingLoop = pwndStart; do { //If not WS_EX_CONTROLPARENT parent, done. if (!TestWF(pwndStart, WEFCONTROLPARENT)) { return pwndStart; } // Do they want to skip this window? fSkip = ((uFlags & CWP_SKIPINVISIBLE) && !TestWF(pwndStart, WFVISIBLE)) || ((uFlags & CWP_SKIPDISABLED) && TestWF(pwndStart, WFDISABLED)); // Remember the current window pwndLast = pwndStart; // Walk down child chain? if (!fSkip && !fAncestor) { pwndStart = _NextControl (pwndStart, NULL, uFlags); // If it found one, done. if (pwndStart != pwndLast) { return pwndStart; } } TryNextOne: // Try the next one. pwndStart = _NextSibblingOrAncestor (pwndRoot, pwndStart); if (pwndStart == NULL) { break; } // If parents are the same, we are still in the same sibbling chain if (pwndLast->spwndParent == pwndStart->spwndParent) { // If we had just moved up the parent chain last time around, // mark this as the beginning of the new sibbling chain. // Otherwise, check if we've looped through all sibblings already. if (fAncestor) { // Beggining of new sibbling chain. pwndSibblingLoop = pwndStart; } else if (pwndStart == pwndSibblingLoop) { // Already visited all sibblings, so done. break; } fAncestor = FALSE; } else { // We must have moved up the parent chain, so don't // walk down the child chain right away (try the next window first) // Eventhough we are on a new sibbling chain, we don't update // pwndSibblingLoop yet; this is because we must walk down this // child chain again to make sure we visit all the descendents fAncestor = TRUE; } /* Bug 272874 - joejo * * Stop infinite loop by only looping a finite number of times and * then bailing. */ } while (nLoopCount++ < 256 * 4); // It couldn't find one... return pwndRoot; } /***************************************************************************\ * GetNextDlgTabItem * * History: * 19-Feb-1991 JimA Added access check \***************************************************************************/ FUNCLOG3(LOG_GENERAL, HWND, WINAPI, GetNextDlgTabItem, HWND, hwndDlg, HWND, hwnd, BOOL, fPrev) HWND WINAPI GetNextDlgTabItem( HWND hwndDlg, HWND hwnd, BOOL fPrev) { PWND pwnd; PWND pwndDlg; PWND pwndNext; pwndDlg = ValidateHwnd(hwndDlg); if (pwndDlg == NULL) return NULL; if (hwnd != (HWND)0) { pwnd = ValidateHwnd(hwnd); if (pwnd == NULL) return NULL; } else { pwnd = (PWND)NULL; } pwndNext = _GetNextDlgTabItem(pwndDlg, pwnd, fPrev); return (HW(pwndNext)); } PWND _GetNextDlgTabItem( PWND pwndDlg, PWND pwnd, BOOL fPrev) { PWND pwndSave; if (pwnd == pwndDlg) pwnd = NULL; else { pwnd = _GetChildControl(pwndDlg, pwnd); if (pwnd && !_IsDescendant(pwndDlg, pwnd)) return(NULL); } // // BACKWARD COMPATIBILITY // // Note that the result when there are no tabstops of // IGetNextDlgTabItem(pwndDlg, NULL, FALSE) was the last item, now // will be the first item. We could put a check for fRecurse here // and do the old thing if not set. // // We are going to bug out if we hit the first child a second time. pwndSave = pwnd; pwnd = (fPrev ? _PrevControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) : _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED)); if (!pwnd) goto AllOver; while ((pwnd != pwndSave) && (pwnd != pwndDlg)) { UserAssert(pwnd); if (!pwndSave) pwndSave = pwnd; if ((pwnd->style & (WS_TABSTOP | WS_VISIBLE | WS_DISABLED)) == (WS_TABSTOP | WS_VISIBLE)) // Found it. break; pwnd = (fPrev ? _PrevControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) : _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED)); } AllOver: return pwnd; } /***************************************************************************\ * * _GetNextDlgGroupItem() * \***************************************************************************/ FUNCLOG3(LOG_GENERAL, HWND, DUMMYCALLINGTYPE, GetNextDlgGroupItem, HWND, hwndDlg, HWND, hwndCtl, BOOL, bPrevious) HWND GetNextDlgGroupItem( HWND hwndDlg, HWND hwndCtl, BOOL bPrevious) { PWND pwndDlg; PWND pwndCtl; PWND pwndNext; pwndDlg = ValidateHwnd(hwndDlg); if (pwndDlg == NULL) return 0; if (hwndCtl != (HWND)0) { pwndCtl = ValidateHwnd(hwndCtl); if (pwndCtl == NULL) return 0; } else { pwndCtl = (PWND)NULL; } if (pwndCtl == pwndDlg) pwndCtl = pwndDlg; pwndNext = _GetNextDlgGroupItem(pwndDlg, pwndCtl, bPrevious); return (HW(pwndNext)); } PWND _GetNextDlgGroupItem( PWND pwndDlg, PWND pwnd, BOOL fPrev) { PWND pwndCurrent; BOOL fOnceAround = FALSE; pwnd = pwndCurrent = _GetChildControl(pwndDlg, pwnd); do { pwnd = (fPrev ? UT_PrevGroupItem(pwndDlg, pwnd) : UT_NextGroupItem(pwndDlg, pwnd)); if (pwnd == pwndCurrent) fOnceAround = TRUE; if (!pwndCurrent) pwndCurrent = pwnd; } while (!fOnceAround && ((TestWF(pwnd, WFDISABLED) || !TestWF(pwnd, WFVISIBLE)))); return pwnd; }