|
|
/***************************************************************************\
* * DLGMGR2.C * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Dialog Management Routines * * ??-???-???? mikeke Ported from Win 3.0 sources * 12-Feb-1991 mikeke Added Revalidation code \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* xxxRemoveDefaultButton * * Scan through all the controls in the dialog box and remove the default * button style from any button that has it. This is done since at times we * do not know who has the default button. * * History: * * Bug 19449 - joejo * * Stop infinite loop when pwnd != pwndStart but pwnd == pwnd after calling * _NextControl! \***************************************************************************/
void xxxRemoveDefaultButton( PWND pwndRoot, PWND pwndStart) { UINT code; PWND pwnd; PWND pwndDup; TL tlpwnd;
CheckLock(pwndRoot); CheckLock(pwndStart);
if (!pwndStart || TestWF(pwndStart, WEFCONTROLPARENT)) pwndStart = _NextControl(pwndRoot, NULL, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); else pwndStart = _GetChildControl(pwndRoot, pwndStart);
if (!pwndStart) return;
pwnd = pwndStart; do { pwndDup = pwnd; ThreadLock(pwnd, &tlpwnd);
code = (UINT)SendMessage(HWq(pwnd), WM_GETDLGCODE, 0, 0L);
if (code & DLGC_DEFPUSHBUTTON) { SendMessage(HWq(pwnd), BM_SETSTYLE, BS_PUSHBUTTON, (LONG)TRUE); }
pwnd = _NextControl(pwndRoot, pwnd, 0);
ThreadUnlock(&tlpwnd);
} while (pwnd && (pwnd != pwndStart) && (pwnd != pwndDup)); #if DBG
if (pwnd && (pwnd != pwndStart) && (pwnd != pwndDup)) { RIPMSG0(RIP_WARNING, "xxxRemoveDefaultButton bailing potential infinite loop!"); } #endif
}
/***************************************************************************\
* xxxCheckDefPushButton * * History: \***************************************************************************/
void xxxCheckDefPushButton( PWND pwndDlg, HWND hwndOldFocus, HWND hwndNewFocus) { PWND pwndNewFocus; PWND pwndOldFocus; TL tlpwndT; PWND pwndT; UINT codeNewFocus = 0; UINT styleT; LONG lT; int id;
if (hwndNewFocus) pwndNewFocus = ValidateHwnd(hwndNewFocus); else pwndNewFocus = NULL;
if (hwndOldFocus) pwndOldFocus = ValidateHwnd(hwndOldFocus); else pwndOldFocus = NULL;
CheckLock(pwndDlg); CheckLock(pwndNewFocus); CheckLock(pwndOldFocus);
if (pwndNewFocus) { // Do nothing if clicking on dialog background or recursive dialog
// background.
if (TestWF(pwndNewFocus, WEFCONTROLPARENT)) return;
codeNewFocus = (UINT)SendMessage(hwndNewFocus, WM_GETDLGCODE, 0, 0L); }
if (SAMEWOWHANDLE(hwndOldFocus, hwndNewFocus)) { //
// NEW FOR 4.0:
//
// There is a very common frustrating scenario for ISVs who try to
// set the default ID. Our dialog manager assumes that if a push
// button has the focus, it is the default button also. As such
// it passes in the focus window to this routine. If someone tries
// to change the focus or set the def ID such that they reside with
// two different push buttons, the double-default-push button case
// will result shortly.
//
// As such, for 4.0 dialogs, we will go check the def ID and see if
// is the same as hwndOldFocus' ID. If not, then we will find IT
// and use that dude as hwndOldFocus
//
if (codeNewFocus & DLGC_UNDEFPUSHBUTTON) { if (TestWF(pwndDlg, WFWIN40COMPAT) && hwndOldFocus) { lT = (LONG)SendMessage(HWq(pwndDlg), DM_GETDEFID, 0, 0L); id = (HIWORD(lT) == DC_HASDEFID ? LOWORD(lT) : IDOK); lT = MAKELONG(id, 0);
if (lT != PtrToLong(pwndNewFocus->spmenu)) { if (pwndOldFocus = _FindDlgItem(pwndDlg, lT)) { hwndOldFocus = HW(pwndOldFocus); if (SendMessage(hwndOldFocus, WM_GETDLGCODE, 0, 0L) & DLGC_DEFPUSHBUTTON) { xxxRemoveDefaultButton(pwndDlg, pwndOldFocus); goto SetNewDefault; } } } }
SendMessage(hwndNewFocus, BM_SETSTYLE, BS_DEFPUSHBUTTON, (LONG)TRUE); } return; }
/*
* If the focus is changing to or from a pushbutton, then remove the * default style from the current default button */ if ((hwndOldFocus != NULL && (SendMessage(hwndOldFocus, WM_GETDLGCODE, 0, 0) & (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON))) || (hwndNewFocus != NULL && (codeNewFocus & (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON)))) { xxxRemoveDefaultButton(pwndDlg, pwndNewFocus); }
SetNewDefault: /*
* If moving to a button, make that button the default. */ if (codeNewFocus & DLGC_UNDEFPUSHBUTTON) { SendMessage(hwndNewFocus, BM_SETSTYLE, BS_DEFPUSHBUTTON, (LONG)TRUE); } else {
/*
* Otherwise, make sure the original default button is default * and no others. */
/*
* Get the original default button handle */ lT = (LONG)SendMessage(HWq(pwndDlg), DM_GETDEFID, 0, 0L); id = (HIWORD(lT) == DC_HASDEFID ? LOWORD(lT) : IDOK); pwndT = _FindDlgItem(pwndDlg, id);
if (pwndT == NULL) return; ThreadLockAlways(pwndT, &tlpwndT);
/*
* If it already has the default button style, do nothing. */ if ((styleT = (UINT)SendMessage(HWq(pwndT), WM_GETDLGCODE, 0, 0L)) & DLGC_DEFPUSHBUTTON) { ThreadUnlock(&tlpwndT); return; }
/*
* Also check to make sure it is really a button. */ if (!(styleT & DLGC_UNDEFPUSHBUTTON)) { ThreadUnlock(&tlpwndT); return; }
if (!TestWF(pwndT, WFDISABLED)) { SendMessage(HWq(pwndT), BM_SETSTYLE, BS_DEFPUSHBUTTON, (LONG)TRUE); } ThreadUnlock(&tlpwndT); } }
/***************************************************************************\
* IsDialogMessage (API) * * History: \***************************************************************************/
FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsDialogMessageA, HWND, hwndDlg, LPMSG, lpmsg) BOOL IsDialogMessageA( HWND hwndDlg, LPMSG lpmsg) { WPARAM wParamSaved = lpmsg->wParam; BOOL bRet;
switch (lpmsg->message) { #ifdef FE_SB // IsDialogMessageA()
case WM_CHAR: case EM_SETPASSWORDCHAR: /*
* BUILD_DBCS_MESSAGE_TO_CLIENTW_FROM_CLIENTA() macro will return TRUE * for DBCS leadbyte message everytime, then we check there is some * possibility the return value become FALSE, here. * * These code originally come from IsDialogMessageW(). */ if (IS_DBCS_ENABLED()) { PWND pwndDlg, pwnd; TL tlpwndDlg; BOOL fLockDlg = FALSE;
if ((pwndDlg = ValidateHwndNoRip(hwndDlg)) == NULL) { return FALSE; }
if (lpmsg->hwnd == NULL) { return FALSE; }
pwnd = ValidateHwnd(lpmsg->hwnd); //
// THIS IS FOR MFC.
//
// This solves many problems with apps that use MFC but want to take
// advantage of DS_CONTROL. MFC blindly passes in child dialogs sometimes
// to IsDialogMessage, which can mess up tabbing etc.
//
if (TestWF(pwndDlg, WEFCONTROLPARENT) && TestWF(pwndDlg, WFCHILD)) { pwndDlg = GetParentDialog(pwndDlg); ThreadLock(pwndDlg, &tlpwndDlg); fLockDlg = TRUE; hwndDlg = HWq(pwndDlg); }
if (pwnd != pwndDlg && !_IsChild(pwndDlg, pwnd)) { if (fLockDlg) ThreadUnlock(&tlpwndDlg); return FALSE; }
/*
* Build DBCS-aware message. */ BUILD_DBCS_MESSAGE_TO_CLIENTW_FROM_CLIENTA(lpmsg->message,lpmsg->wParam,TRUE);
/*
* Fall through..... */ } #else
case WM_CHAR: case EM_SETPASSWORDCHAR: #endif // FE_SB
case WM_CHARTOITEM: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: #ifdef FE_IME // IsDialogMessageA()
case WM_IME_CHAR: case WM_IME_COMPOSITION: #endif // FE_IME
RtlMBMessageWParamCharToWCS(lpmsg->message, &lpmsg->wParam); }
bRet = IsDialogMessageW(hwndDlg, lpmsg);
/*
* Restore the original ANSI char. */ lpmsg->wParam = wParamSaved; return bRet; }
FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsDialogMessageW, HWND, hwndDlg, LPMSG, lpMsg) BOOL IsDialogMessageW( HWND hwndDlg, LPMSG lpMsg) { PWND pwndDlg; PWND pwnd; PWND pwnd2; HWND hwnd2; HWND hwndFocus; int iOK; BOOL fBack; UINT code; LONG lT; TL tlpwnd; TL tlpwndDlg; BOOL fLockDlg = FALSE; TL tlpwnd2; WORD langID;
langID = PRIMARYLANGID(LANGIDFROMLCID(GetUserDefaultLCID()));
if ((pwndDlg = ValidateHwndNoRip(hwndDlg)) == NULL) { return FALSE; }
CheckLock(pwndDlg);
/*
* If this is a synchronous-only message (takes a pointer in wParam or * lParam), then don't allow this message to go through since those * parameters have not been thunked, and are pointing into outer-space * (which would case exceptions to occur). * * (This api is only called in the context of a message loop, and you * don't get synchronous-only messages in a message loop). */ if (TESTSYNCONLYMESSAGE(lpMsg->message, lpMsg->wParam)) { /*
* Fail if 32 bit app is calling. */ if (!(GetClientInfo()->dwTIFlags & TIF_16BIT)) { RIPERR0(ERROR_MESSAGE_SYNC_ONLY, RIP_WARNING, "IsDialogMessage: must be sync only"); return FALSE; }
/*
* For wow apps, allow it to go through (for compatibility). Change * the message id so our code doesn't understand the message - wow * will get the message and strip out this bit before dispatching * the message to the application. */ lpMsg->message |= MSGFLAG_WOW_RESERVED; }
if (CallMsgFilter(lpMsg, MSGF_DIALOGBOX)) return TRUE;
if (lpMsg->hwnd == NULL) { return FALSE; }
pwnd = ValidateHwnd(lpMsg->hwnd); //
// THIS IS FOR MFC.
//
// This solves many problems with apps that use MFC but want to take
// advantage of DS_CONTROL. MFC blindly passes in child dialogs sometimes
// to IsDialogMessage, which can mess up tabbing etc.
//
if (TestWF(pwndDlg, WEFCONTROLPARENT) && TestWF(pwndDlg, WFCHILD)) { pwndDlg = GetParentDialog(pwndDlg); ThreadLock(pwndDlg, &tlpwndDlg); fLockDlg = TRUE; hwndDlg = HWq(pwndDlg); }
if (pwnd != pwndDlg && !_IsChild(pwndDlg, pwnd)) { if (fLockDlg) ThreadUnlock(&tlpwndDlg); return FALSE; } ThreadLock(pwnd, &tlpwnd);
fBack = FALSE; iOK = IDCANCEL; switch (lpMsg->message) { case WM_LBUTTONDOWN:
/*
* Move the default button styles around on button clicks in the * same way as TABs. */ if ((pwnd != pwndDlg) && ((hwndFocus = GetFocus()) != NULL)) { xxxCheckDefPushButton(pwndDlg, hwndFocus, lpMsg->hwnd); } break;
case WM_SYSCHAR:
/*
* If no control has focus, and Alt not down, then ignore. */ if ((GetFocus() == NULL) && (GetKeyState(VK_MENU) >= 0)) { if (lpMsg->wParam == VK_RETURN && TestWF(pwnd, WFMINIMIZED)) {
/*
* If this is an iconic dialog box window and the user hits * return, send the message off to DefWindowProc so that it * can be restored. Especially useful for apps whose top * level window is a dialog box. */ goto CallDefWindowProcAndReturnTrue; } else { NtUserMessageBeep(0); }
ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; }
/*
* If alt+menuchar, process as menu. */ if (lpMsg->wParam == MENUSYSMENU) { DefWindowProcWorker(pwndDlg, lpMsg->message, lpMsg->wParam, lpMsg->lParam, FALSE); ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; }
/*
*** FALL THRU ** */
case WM_CHAR:
/*
* Ignore chars sent to the dialog box (rather than the control). */ if (pwnd == pwndDlg) { ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; }
code = (UINT)SendMessage(lpMsg->hwnd, WM_GETDLGCODE, lpMsg->wParam, (LPARAM)lpMsg);
/*
* If the control wants to process the message, then don't check for * possible mnemonic key. */ if ((lpMsg->message == WM_CHAR) && (code & (DLGC_WANTCHARS | DLGC_WANTMESSAGE))) break;
/* If the control wants tabs, then don't let tab fall thru here
*/ if ((lpMsg->wParam == VK_TAB) && (code & DLGC_WANTTAB)) break;
/*
* HACK ALERT * * If ALT is held down (i.e., SYSCHARs), then ALWAYS do mnemonic * processing. If we do away with SYSCHARS, then we should * check key state of ALT instead. */
/*
* Space is not a valid mnemonic, but it IS the char that toggles * button states. Don't look for it as a mnemonic or we will * beep when it is typed.... */ if (lpMsg->wParam == VK_SPACE) { ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; }
if (!(pwnd2 = xxxGotoNextMnem(pwndDlg, pwnd, (WCHAR)lpMsg->wParam))) {
if (code & DLGC_WANTMESSAGE) break;
/*
* No mnemonic could be found so we will send the sys char over * to xxxDefWindowProc so that any menu bar on the dialog box is * handled properly. */ if (lpMsg->message == WM_SYSCHAR) { CallDefWindowProcAndReturnTrue: DefWindowProcWorker(pwndDlg, lpMsg->message, lpMsg->wParam, lpMsg->lParam, FALSE);
ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; } NtUserMessageBeep(0); } else {
/*
* pwnd2 is 1 if the mnemonic took us to a pushbutton. We * don't change the default button status here since doing this * doesn't change the focus. */ if (pwnd2 != (PWND)1) { ThreadLockAlways(pwnd2, &tlpwnd2); xxxCheckDefPushButton(pwndDlg, lpMsg->hwnd, HWq(pwnd2)); ThreadUnlock(&tlpwnd2); } }
ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE;
case WM_SYSKEYDOWN: /*
* If Alt is down, deal with keyboard cues */ if ((HIWORD(lpMsg->lParam) & SYS_ALTERNATE) && TEST_KbdCuesPUSIF) { if (TestWF(pwnd, WEFPUIFOCUSHIDDEN) || (TestWF(pwnd, WEFPUIACCELHIDDEN))) { SendMessageWorker(pwndDlg, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0, FALSE); } } break;
case WM_KEYDOWN: code = (UINT)SendMessage(lpMsg->hwnd, WM_GETDLGCODE, lpMsg->wParam, (LPARAM)lpMsg); if (code & (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE)) break;
switch (lpMsg->wParam) { case VK_TAB: if (code & DLGC_WANTTAB) break; pwnd2 = _GetNextDlgTabItem(pwndDlg, pwnd, (GetKeyState(VK_SHIFT) & 0x8000));
if (TEST_KbdCuesPUSIF) { if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) { SendMessageWorker(pwndDlg, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0, FALSE); } }
if (pwnd2 != NULL) { hwnd2 = HWq(pwnd2); ThreadLockAlways(pwnd2, &tlpwnd2); DlgSetFocus(hwnd2); xxxCheckDefPushButton(pwndDlg, lpMsg->hwnd, hwnd2); ThreadUnlock(&tlpwnd2); } ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE;
/*
* For Arabic and Hebrew locales the arrow keys are reversed. Also reverse them if * the dialog is RTL mirrored. */ case VK_LEFT: if ((((langID == LANG_ARABIC) || (langID == LANG_HEBREW)) && TestWF(pwndDlg,WEFRTLREADING)) ^ (!!TestWF(pwndDlg, WEFLAYOUTRTL))) {
goto DoKeyStuff; } case VK_UP: fBack = TRUE; goto DoKeyStuff;
/*
*** FALL THRU ** */ case VK_RIGHT: if ((((langID == LANG_ARABIC) || (langID == LANG_HEBREW)) && TestWF(pwndDlg,WEFRTLREADING)) ^ (!!TestWF(pwndDlg, WEFLAYOUTRTL))) {
fBack = TRUE; } case VK_DOWN: DoKeyStuff: if (code & DLGC_WANTARROWS) break;
if (TEST_KbdCuesPUSIF) { if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) { SendMessageWorker(pwndDlg, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0, FALSE); } }
pwnd2 = _GetNextDlgGroupItem(pwndDlg, pwnd, fBack); if (pwnd2 == NULL) { ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; } hwnd2 = HWq(pwnd2); ThreadLockAlways(pwnd2, &tlpwnd2);
code = (UINT)SendMessage(hwnd2, WM_GETDLGCODE, lpMsg->wParam, (LPARAM)lpMsg);
/*
* We are just moving the focus rect around! So, do not send * BN_CLICK messages, when WM_SETFOCUSing. Fix for Bug * #4358. */ if (code & (DLGC_UNDEFPUSHBUTTON | DLGC_DEFPUSHBUTTON)) { PBUTN pbutn; BOOL fIsNTButton = IS_BUTTON(pwnd2); if (fIsNTButton) { pbutn = ((PBUTNWND)pwnd2)->pbutn; BUTTONSTATE(pbutn) |= BST_DONTCLICK; } DlgSetFocus(hwnd2); if (fIsNTButton) { BUTTONSTATE(pbutn) &= ~BST_DONTCLICK; } xxxCheckDefPushButton(pwndDlg, lpMsg->hwnd, hwnd2); } else if (code & DLGC_RADIOBUTTON) { DlgSetFocus(hwnd2); xxxCheckDefPushButton(pwndDlg, lpMsg->hwnd, hwnd2); if (TestWF(pwnd2, BFTYPEMASK) == LOBYTE(BS_AUTORADIOBUTTON)) {
/*
* So that auto radio buttons get clicked on */ if (!SendMessage(hwnd2, BM_GETCHECK, 0, 0L)) { SendMessage(hwnd2, BM_CLICK, TRUE, 0L); } } } else if (!(code & DLGC_STATIC)) { DlgSetFocus(hwnd2); xxxCheckDefPushButton(pwndDlg, lpMsg->hwnd, hwnd2); } ThreadUnlock(&tlpwnd2); ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE;
case VK_EXECUTE: case VK_RETURN:
/*
* Guy pressed return - if button with focus is * defpushbutton, return its ID. Otherwise, return id * of original defpushbutton. */ if (!(hwndFocus = GetFocus())) code = 0; else { code = (WORD)(DWORD)SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0L); }
if (code & DLGC_DEFPUSHBUTTON) { iOK = GetDlgCtrlID(hwndFocus); pwnd2 = ValidateHwnd(hwndFocus); goto HaveWindow; } else { lT = (LONG)SendMessage(hwndDlg, DM_GETDEFID, 0, 0L); iOK = MAKELONG( (HIWORD(lT)==DC_HASDEFID ? LOWORD(lT) : IDOK), 0); } // FALL THRU
case VK_ESCAPE: case VK_CANCEL:
/*
* Make sure button is not disabled. */ pwnd2 = _FindDlgItem(pwndDlg, iOK); HaveWindow: if (pwnd2 != NULL && TestWF(pwnd2, WFDISABLED)) { NtUserMessageBeep(0); } else { SendMessage(hwndDlg, WM_COMMAND, MAKELONG(iOK, BN_CLICKED), (LPARAM)HW(pwnd2)); }
ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg); return TRUE; } break; }
ThreadUnlock(&tlpwnd); if (fLockDlg) ThreadUnlock(&tlpwndDlg);
TranslateMessage(lpMsg); DispatchMessage(lpMsg);
return TRUE; }
/***************************************************************************\
* * FindDlgItem32() * * Given a dialog, finds the window with the given ID anywhere w/in the * descendant chain. * \***************************************************************************/
PWND _FindDlgItem(PWND pwndParent, DWORD id) { PWND pwndChild; PWND pwndOrig;
// QUICK TRY:
pwndChild = _GetDlgItem(pwndParent, id); if (pwndChild || !TestWF(pwndParent, WFWIN40COMPAT)) return(pwndChild);
pwndOrig = _NextControl(pwndParent, NULL, CWP_SKIPINVISIBLE); if (pwndOrig == pwndParent) return(NULL);
pwndChild = pwndOrig;
// VerifyWindow(pwndChild);
do { if (PtrToUlong(pwndChild->spmenu) == id) return(pwndChild);
pwndChild = _NextControl(pwndParent, pwndChild, CWP_SKIPINVISIBLE); } while (pwndChild && (pwndChild != pwndOrig));
return(NULL); }
|