/***************************************************************************\ * * 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; }