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.
394 lines
12 KiB
394 lines
12 KiB
/***************************************************************************\
|
|
*
|
|
* DMMNEM.C -
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Mnemonic Character Processing Routines
|
|
*
|
|
* ??-???-???? mikeke Ported from Win 3.0 sources
|
|
* 12-Feb-1991 mikeke Added Revalidation code
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/*
|
|
* There are several loops that we could get stuck in that we just forcibly
|
|
* break by a max iteration count. Ugly, but its legacy DialogManager
|
|
* issues.
|
|
*/
|
|
#define INFINITE_LOOP_CURE 1024
|
|
|
|
/***************************************************************************\
|
|
* FindMnemChar
|
|
*
|
|
* Returns: 0x00 if no matching char,
|
|
* 0x01 if menmonic char is matching,
|
|
* 0x80 if first char is matching
|
|
*
|
|
* History:
|
|
* 11-18-90 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
int FindMnemChar(
|
|
LPWSTR lpstr,
|
|
WCHAR ch,
|
|
BOOL fFirst,
|
|
BOOL fPrefix)
|
|
{
|
|
WCHAR chc;
|
|
WCHAR chFirst;
|
|
|
|
while (*lpstr == TEXT(' '))
|
|
lpstr++;
|
|
|
|
ch = (WCHAR)(ULONG_PTR)CharLowerW((LPWSTR)ULongToPtr( (DWORD)(UTCHAR)ch ));
|
|
chFirst = (WCHAR)(ULONG_PTR)CharLowerW((LPWSTR)ULongToPtr( (DWORD)(UTCHAR)(*lpstr) ));
|
|
|
|
if (fPrefix) {
|
|
WORD wvch, xvkey;
|
|
//
|
|
// get OEM-dependent virtual key code
|
|
//
|
|
if (IS_DBCS_ENABLED() && (wvch = VkKeyScanW(ch)) != -1)
|
|
wvch &= 0x00FF;
|
|
|
|
while (chc = *lpstr++) {
|
|
//
|
|
// This should think about KOREA & TAIWAN case. But probably OK.
|
|
//
|
|
if ((chc == CH_PREFIX) || (chc == CH_ENGLISHPREFIX && IS_DBCS_ENABLED())) {
|
|
|
|
WORD chnext = (WCHAR)(ULONG_PTR)CharLowerW((LPWSTR)ULongToPtr( (DWORD)(UTCHAR)*lpstr ));
|
|
|
|
if (chnext == CH_PREFIX) {
|
|
//
|
|
// Two CH_PREFIX in the resrc string results in one "&" in the text displayed
|
|
//
|
|
lpstr++;
|
|
} else {
|
|
if (chnext == ch) {
|
|
return 0x01;
|
|
}
|
|
if (IS_DBCS_ENABLED()) {
|
|
//
|
|
// Compare should be done with virtual key in Kanji menu mode
|
|
// in order to accept Digit shortcut key and save English
|
|
// windows applications!
|
|
//
|
|
xvkey = VkKeyScanW(chnext);
|
|
if (xvkey != 0xFFFF && ((xvkey & 0x00FF) == wvch)) {
|
|
return 0x01;
|
|
}
|
|
}
|
|
return 0x00;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if 0 // the original US code on NT4
|
|
if (fPrefix) {
|
|
while (chc = *lpstr++) {
|
|
if (((WCHAR)CharLower((LPWSTR)(DWORD)(UTCHAR)chc) == CH_PREFIX)) {
|
|
chnext = (WCHAR)CharLowerW((LPWSTR)(DWORD)(UTCHAR)*lpstr);
|
|
|
|
if (chnext == CH_PREFIX)
|
|
lpstr++;
|
|
else if (chnext == ch)
|
|
return 0x01;
|
|
else {
|
|
return 0x00;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // FE_SB
|
|
|
|
if (fFirst && (ch == chFirst))
|
|
return 0x80;
|
|
|
|
return 0x00;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxFindNextMnem
|
|
*
|
|
* This function returns NULL if no control with the specified mnemonic
|
|
* can be found.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
PWND xxxGNM_FindNextMnem(
|
|
PWND pwndDlg,
|
|
PWND pwnd,
|
|
WCHAR ch)
|
|
{
|
|
PWND pwndStart;
|
|
PWND pwndT;
|
|
WCHAR rgchText[256];
|
|
int i = 0;
|
|
TL tlpwndStart;
|
|
TL tlpwnd;
|
|
DWORD dwDlgCode;
|
|
|
|
CheckLock(pwndDlg);
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Check if we are in a group box so we can find local mnemonics.
|
|
*/
|
|
|
|
pwndStart = _GetChildControl(pwndDlg, pwnd);
|
|
ThreadLock(pwndStart, &tlpwndStart);
|
|
|
|
while (TRUE) {
|
|
|
|
pwndT = _GetNextDlgGroupItem(pwndDlg, pwndStart, FALSE);
|
|
|
|
ThreadUnlock(&tlpwndStart);
|
|
|
|
i++;
|
|
if (pwndT == NULL || pwndT == pwnd || i > INFINITE_LOOP_CURE) {
|
|
|
|
/*
|
|
* If we have returned to our starting window or if we have gone
|
|
* through INFINITE_LOOP_CURE iterations, let's exit. There are
|
|
* no local mnemonics that match. We have to check for
|
|
* INFINITE_LOOP_CURE iterations (or so) because we run into
|
|
* problems with WS_GROUP not being properly defined in rc files
|
|
* that we never reach this same starting window again....
|
|
*/
|
|
break;
|
|
}
|
|
|
|
pwndStart = pwndT;
|
|
|
|
/*
|
|
* Only check for matching mnemonic if control doesn't want characters
|
|
* and control isn't a static control with SS_NOPREFIX
|
|
*/
|
|
ThreadLock(pwndStart, &tlpwndStart);
|
|
|
|
dwDlgCode = (DWORD)SendMessage(HWq(pwndT), WM_GETDLGCODE, 0, 0L);
|
|
if (!(dwDlgCode & DLGC_WANTCHARS) &&
|
|
(!(dwDlgCode & DLGC_STATIC) || !(pwndT->style & SS_NOPREFIX))) {
|
|
GetWindowText(HWq(pwndT), rgchText, sizeof(rgchText)/sizeof(WCHAR));
|
|
if (FindMnemChar(rgchText, ch, FALSE, TRUE) != 0) {
|
|
ThreadUnlock(&tlpwndStart);
|
|
return pwndT;
|
|
}
|
|
}
|
|
}
|
|
|
|
pwnd = pwndStart = _GetChildControl(pwndDlg, pwnd);
|
|
i = 0;
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
while (TRUE) {
|
|
|
|
/*
|
|
* Start with next so we see multiples of same mnemonic.
|
|
*/
|
|
pwnd = _NextControl(pwndDlg, pwnd, TRUE);
|
|
ThreadUnlock(&tlpwnd);
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
/*
|
|
* Only check for matching mnemonic if control doesn't want characters
|
|
* and control isn't a static control with SS_NOPREFIX
|
|
*/
|
|
dwDlgCode = (DWORD)SendMessage(HW(pwnd), WM_GETDLGCODE, 0, 0L);
|
|
if (!(dwDlgCode & DLGC_WANTCHARS) &&
|
|
(!(dwDlgCode & DLGC_STATIC) || !(pwnd->style & SS_NOPREFIX))) {
|
|
GetWindowText(HW(pwnd), rgchText, sizeof(rgchText)/sizeof(WCHAR));
|
|
if (FindMnemChar(rgchText, ch, FALSE, TRUE) != 0)
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
if (pwnd == pwndStart || i > INFINITE_LOOP_CURE) {
|
|
pwnd = NULL;
|
|
break;
|
|
}
|
|
}
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
return pwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxGotoNextMnem
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
PWND xxxGotoNextMnem(
|
|
PWND pwndDlg,
|
|
PWND pwnd,
|
|
WCHAR ch)
|
|
{
|
|
UINT code;
|
|
PWND pwndFirstFound = NULL;
|
|
int count = 0;
|
|
TL tlpwnd;
|
|
PWND pwndT;
|
|
HWND hwnd;
|
|
|
|
CheckLock(pwndDlg);
|
|
CheckLock(pwnd);
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
/*
|
|
* Loop for a long time but not long enough so we hang...
|
|
*/
|
|
while (count < INFINITE_LOOP_CURE) {
|
|
|
|
/*
|
|
* If the dialog box doesn't has the mnemonic specified, return NULL.
|
|
*/
|
|
if ((pwnd = xxxGNM_FindNextMnem(pwndDlg, pwnd, ch)) == NULL) {
|
|
ThreadUnlock(&tlpwnd);
|
|
return NULL;
|
|
}
|
|
hwnd = HWq(pwnd);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
code = (UINT)SendMessage(hwnd, WM_GETDLGCODE, 0, 0L);
|
|
|
|
/*
|
|
* If a non-disabled static item, then jump ahead to nearest tabstop.
|
|
*/
|
|
if (code & DLGC_STATIC && !TestWF(pwnd, WFDISABLED)) {
|
|
pwndT = _GetNextDlgTabItem(pwndDlg, pwnd, FALSE);
|
|
|
|
/*
|
|
* If there is no other tab item, keep looking
|
|
*/
|
|
if (pwndT == NULL)
|
|
continue;
|
|
pwnd = pwndT;
|
|
hwnd = HWq(pwnd);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
/*
|
|
* I suppose we should do a getdlgcode here, but who is going to
|
|
* label a button with a static control? The setup guys, that's
|
|
* who... Also, generally useful for ownerdraw buttons which are
|
|
* labeled with a static text item.
|
|
*/
|
|
code = (UINT)SendMessage(hwnd, WM_GETDLGCODE, 0, 0L);
|
|
}
|
|
|
|
if (!TestWF(pwnd, WFDISABLED)) {
|
|
|
|
/*
|
|
* Is it a Pushbutton?
|
|
*/
|
|
if (!(code & DLGC_BUTTON)) {
|
|
|
|
/*
|
|
* No, simply give it the focus.
|
|
*/
|
|
DlgSetFocus(hwnd);
|
|
} else {
|
|
|
|
/*
|
|
* Yes, click it, but don't give it the focus.
|
|
*/
|
|
if ((code & DLGC_DEFPUSHBUTTON) || (code & DLGC_UNDEFPUSHBUTTON)) {
|
|
|
|
/*
|
|
* Flash the button.
|
|
*/
|
|
SendMessage(hwnd, BM_SETSTATE, TRUE, 0L);
|
|
|
|
/*
|
|
* Delay
|
|
*/
|
|
#ifdef LATER
|
|
// JimA - 2/19/92
|
|
// There oughta be a better way of doing this...
|
|
for (i = 0; i < 10000; i++)
|
|
;
|
|
#else
|
|
Sleep(1);
|
|
#endif
|
|
|
|
/*
|
|
* Un-Flash it.
|
|
*/
|
|
SendMessage(hwnd, BM_SETSTATE, FALSE, 0L);
|
|
|
|
/*
|
|
* Send the WM_COMMAND message.
|
|
*/
|
|
pwndT = REBASEPWND(pwnd, spwndParent);
|
|
SendMessage(HW(pwndT), WM_COMMAND,
|
|
MAKELONG(PTR_TO_ID(pwnd->spmenu), BN_CLICKED), (LPARAM)hwnd);
|
|
ThreadUnlock(&tlpwnd);
|
|
return (PWND)1;
|
|
} else {
|
|
|
|
/*
|
|
* Because BM_CLICK processing will result in BN_CLICK msg,
|
|
* xxxSetFocus must be prevented from sending the same msg;
|
|
* Otherwise, it will notify parent twice!
|
|
* Fix for Bug #3024 -- SANKAR -- 09-22-89 --
|
|
*/
|
|
BOOL fIsNTButton;
|
|
PBUTN pbutn;
|
|
|
|
fIsNTButton = IS_BUTTON(pwnd);
|
|
if (fIsNTButton) {
|
|
pbutn = ((PBUTNWND)pwnd)->pbutn;
|
|
BUTTONSTATE(pbutn) |= BST_DONTCLICK;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "xxxGotoNextMnem: fnid != FNID_BUTTON");
|
|
}
|
|
|
|
DlgSetFocus(hwnd);
|
|
|
|
if (fIsNTButton) {
|
|
BUTTONSTATE(pbutn) &= ~BST_DONTCLICK;
|
|
}
|
|
|
|
/*
|
|
* Send click message if button has a UNIQUE mnemonic
|
|
*/
|
|
if (xxxGNM_FindNextMnem(pwndDlg, pwnd, ch) == pwnd) {
|
|
SendMessage(hwnd, BM_CLICK, TRUE, 0L);
|
|
}
|
|
}
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
return pwnd;
|
|
} else {
|
|
|
|
/*
|
|
* Stop if we've looped back to the first item we checked
|
|
*/
|
|
if (pwnd == pwndFirstFound) {
|
|
ThreadUnlock(&tlpwnd);
|
|
return NULL;
|
|
}
|
|
|
|
if (pwndFirstFound == NULL)
|
|
pwndFirstFound = pwnd;
|
|
}
|
|
|
|
count++;
|
|
|
|
} /* Loop for a long time */
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
return NULL;
|
|
}
|