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.
466 lines
13 KiB
466 lines
13 KiB
/****************************** 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;
|
|
}
|