Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

imehotky.c

Go to the documentation of this file.
00001 /****************************** Module Header ******************************\ 00002 * Module Name: imehotky.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Contents: Manage IME hotkey 00007 * 00008 * There are the following two kind of hotkeys defined in the IME specification. 00009 * 00010 * 1) IME hotkeys that changes the mode/status of current IME 00011 * 2) IME hotkeys that causes IME (keyboard layout) change 00012 * 00013 * History: 00014 * 10-Sep-1995 takaok Created for NT 3.51. 00015 * 15-Mar-1996 takaok Ported to NT 4.0 00016 \***************************************************************************/ 00017 00018 #include "precomp.h" 00019 #pragma hdrstop 00020 00021 PIMEHOTKEYOBJ DeleteImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete); 00022 VOID AddImeHotKey(PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd); 00023 PIMEHOTKEYOBJ FindImeHotKeyByKey(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey); 00024 PIMEHOTKEYOBJ FindImeHotKeyByID(PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID); 00025 PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang(PIMEHOTKEYOBJ pHead, UINT uModifyKeys, UINT uRL, UINT uVKey, LANGID langId); 00026 00027 00028 #define L_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) 00029 #define L_JPN MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT) 00030 #define L_KOR MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT) 00031 #define L_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) 00032 00033 enum { 00034 ILANG_NO_MATCH = 0, // 0: does not match. 00035 ILANG_MATCH_SYSTEM, // 1: matches the system locale 00036 ILANG_MATCH_THREAD, // 2: matches the thread locale 00037 ILANG_MATCH_PERFECT, // 3: matches the current HKL, or direct KL switching hotkey. 00038 }; 00039 00040 00041 // Make sure constants are within the range we expect 00042 #if IME_CHOTKEY_FIRST != 0x10 || IME_JHOTKEY_FIRST != 0x30 || IME_KHOTKEY_FIRST != 0x50 || IME_THOTKEY_FIRST != 0x70 00043 #error unexpected IME_xHOTKEY range ! 00044 #endif 00045 00046 LANGID GetHotKeyLangID(DWORD dwHotKeyID) 00047 { 00048 LANGID langId = -1; 00049 static CONST LANGID aLangId[] = { 00050 ~0, // 0x00 - 0x0f: illegal 00051 L_CHS, L_CHS, // 0x10 - 0x2f 00052 L_JPN, L_JPN, // 0x30 - 0x4f 00053 L_KOR, L_KOR, // 0x50 - 0x6f 00054 L_CHT, L_CHT, // 0x70 - 0x8f 00055 }; 00056 00057 if (dwHotKeyID >= IME_CHOTKEY_FIRST && dwHotKeyID <= IME_THOTKEY_LAST) { 00058 langId = aLangId[dwHotKeyID >> 4]; 00059 } 00060 else { 00061 langId = LANG_NEUTRAL; 00062 } 00063 00064 // Because KOR IME does not want IME hot key handling 00065 UserAssert(langId != L_KOR); 00066 00067 return langId; 00068 } 00069 00070 BOOL 00071 GetImeHotKey( 00072 DWORD dwHotKeyID, 00073 PUINT puModifiers, 00074 PUINT puVKey, 00075 HKL *phKL ) 00076 { 00077 PIMEHOTKEYOBJ ph; 00078 00079 ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID ); 00080 if ( ph == NULL ) { 00081 RIPERR0(ERROR_HOTKEY_NOT_REGISTERED, RIP_VERBOSE, "No such IME hotkey"); 00082 return (FALSE); 00083 } 00084 00085 // 00086 // it is OK for NULL phKL, if the target hKL is NULL 00087 // 00088 if ( phKL ) { 00089 *phKL = ph->hk.hKL; 00090 } else if ( ph->hk.hKL != NULL ) { 00091 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "phKL is null"); 00092 return (FALSE); 00093 } 00094 00095 *puModifiers = ph->hk.uModifiers; 00096 *puVKey = ph->hk.uVKey; 00097 00098 return (TRUE); 00099 } 00100 00101 // 00102 // Insert/remove the specified IME hotkey into/from 00103 // the IME hotkey list (gpImeHotKeyListHeader). 00104 // 00105 BOOL 00106 SetImeHotKey( 00107 DWORD dwHotKeyID, 00108 UINT uModifiers, 00109 UINT uVKey, 00110 HKL hKL, 00111 DWORD dwAction ) 00112 { 00113 PIMEHOTKEYOBJ ph; 00114 00115 switch ( dwAction ) { 00116 case ISHK_REMOVE: 00117 ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID ); 00118 if ( ph != NULL ) { 00119 if ( DeleteImeHotKey( &gpImeHotKeyListHeader, ph ) == ph ) { 00120 UserFreePool( ph ); 00121 return ( TRUE ); 00122 } else { 00123 RIPMSG0( RIP_ERROR, "IME hotkey list is messed up" ); 00124 return FALSE; 00125 } 00126 } else { 00127 RIPERR0( ERROR_INVALID_PARAMETER, 00128 RIP_WARNING, 00129 "no such IME hotkey registered"); 00130 return FALSE; 00131 } 00132 break; 00133 00134 case ISHK_INITIALIZE: 00135 ph = gpImeHotKeyListHeader; 00136 while ( ph != NULL ) { 00137 PIMEHOTKEYOBJ phNext; 00138 00139 phNext = ph->pNext; 00140 UserFreePool( ph ); 00141 ph = phNext; 00142 } 00143 gpImeHotKeyListHeader = NULL; 00144 return TRUE; 00145 00146 case ISHK_ADD: 00147 if (dwHotKeyID >= IME_KHOTKEY_FIRST && dwHotKeyID <= IME_KHOTKEY_LAST) { 00148 // Korean IME does not want any IMM hotkey handling. 00149 // We should not register any Korean IME hot keys. 00150 return FALSE; 00151 } 00152 00153 if ((WORD)uVKey == VK_PACKET) { 00154 // 00155 // VK_PACKET should not be a IME hot key. 00156 // 00157 return FALSE; 00158 } 00159 00160 ph = FindImeHotKeyByKeyWithLang(gpImeHotKeyListHeader, 00161 uModifiers & MOD_MODIFY_KEYS, 00162 uModifiers & MOD_BOTH_SIDES, 00163 uVKey, 00164 GetHotKeyLangID(dwHotKeyID)); 00165 if ( ph != NULL ) { 00166 if ( ph->hk.dwHotKeyID != dwHotKeyID ) { 00167 RIPERR0( ERROR_HOTKEY_ALREADY_REGISTERED, 00168 RIP_WARNING, 00169 "There is an IME hotkey that has the same vkey/modifiers/Lang Id"); 00170 return FALSE; 00171 } 00172 // So far we found a hotkey that has the 00173 // same vkey and same ID. 00174 // But because modifiers may be slightly 00175 // different, so go ahead and change it. 00176 } else { 00177 // 00178 // the specified vkey/modifiers combination cound not be found 00179 // in the hotkey list. The caller may want to change the key 00180 // assignment of an existing hotkey or add a new hotkey. 00181 // 00182 ph = FindImeHotKeyByID( gpImeHotKeyListHeader, dwHotKeyID ); 00183 } 00184 00185 if ( ph == NULL ) { 00186 // 00187 // adding a new hotkey 00188 // 00189 ph = (PIMEHOTKEYOBJ)UserAllocPool( sizeof(IMEHOTKEYOBJ), TAG_IMEHOTKEY ); 00190 if ( ph == NULL ) { 00191 RIPERR0( ERROR_OUTOFMEMORY, 00192 RIP_WARNING, 00193 "Memory allocation failed in SetImeHotKey"); 00194 return FALSE; 00195 } 00196 ph->hk.dwHotKeyID = dwHotKeyID; 00197 ph->hk.uModifiers = uModifiers; 00198 ph->hk.uVKey = uVKey; 00199 ph->hk.hKL = hKL; 00200 ph->pNext = NULL; 00201 AddImeHotKey( &gpImeHotKeyListHeader, ph ); 00202 00203 } else { 00204 // 00205 // changing an existing hotkey 00206 // 00207 ph->hk.uModifiers = uModifiers; 00208 ph->hk.uVKey = uVKey; 00209 ph->hk.hKL = hKL; 00210 00211 } 00212 return TRUE; 00213 } 00214 00215 return FALSE; 00216 } 00217 00218 00219 PIMEHOTKEYOBJ DeleteImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pDelete ) 00220 { 00221 PIMEHOTKEYOBJ ph; 00222 00223 if ( pDelete == *ppHead ) { 00224 *ppHead = pDelete->pNext; 00225 return pDelete; 00226 } 00227 00228 for ( ph = *ppHead; ph != NULL; ph = ph->pNext ) { 00229 if ( ph->pNext == pDelete ) { 00230 ph->pNext = pDelete->pNext; 00231 return pDelete; 00232 } 00233 } 00234 return NULL; 00235 } 00236 00237 VOID AddImeHotKey( PIMEHOTKEYOBJ *ppHead, PIMEHOTKEYOBJ pAdd ) 00238 { 00239 PIMEHOTKEYOBJ ph; 00240 00241 if ( *ppHead == NULL ) { 00242 *ppHead = pAdd; 00243 } else { 00244 ph = *ppHead; 00245 while( ph->pNext != NULL ) 00246 ph = ph->pNext; 00247 ph->pNext = pAdd; 00248 } 00249 return; 00250 } 00251 00252 VOID FreeImeHotKeys(VOID) 00253 { 00254 PIMEHOTKEYOBJ phk; 00255 00256 while (gpImeHotKeyListHeader != NULL) { 00257 phk = gpImeHotKeyListHeader->pNext; 00258 UserFreePool(gpImeHotKeyListHeader); 00259 gpImeHotKeyListHeader = phk; 00260 } 00261 } 00262 00263 00264 LCID glcidSystem; 00265 00266 int GetLangIdMatchLevel(HKL hkl, LANGID langId) 00267 { 00268 00269 if (langId == LANG_NEUTRAL) { 00270 // 00271 // If langId is LANG_NEUTRAL, the hot key does not depend on 00272 // the current HKL. Make it perfect match always. 00273 // 00274 return ILANG_MATCH_PERFECT; 00275 } 00276 00277 { 00278 LCID lcid; 00279 00280 if (LOWORD(HandleToUlong(hkl)) == langId) { 00281 // langId matches the current KL locale 00282 return ILANG_MATCH_PERFECT; 00283 } 00284 00285 lcid = NtCurrentTeb()->CurrentLocale; 00286 if (LANGIDFROMLCID(lcid) == langId) { 00287 // langId matches the current thread's locale 00288 return ILANG_MATCH_THREAD; 00289 } 00290 00291 if (glcidSystem == 0) { 00292 // If we've not got system default locale yet, get it here. 00293 ZwQueryDefaultLocale(FALSE, &glcidSystem); 00294 } 00295 if (LANGIDFROMLCID(glcidSystem) == langId) { 00296 // langId matches the system locale. 00297 return ILANG_MATCH_SYSTEM; 00298 } 00299 } 00300 00301 return ILANG_NO_MATCH; 00302 } 00303 00305 // FindImeHotKeyByKey() 00306 // Return Value: 00307 // pHotKey - IMEHOTKEY pointer with the key, 00308 // else NULL - failure 00309 // 00310 // Finds the best matching of IME hot keys considering the current 00311 // input locale. 00312 // 00314 00315 PIMEHOTKEYOBJ FindImeHotKeyByKey( // Finds pHotKey with this input key 00316 PIMEHOTKEYOBJ pHead, 00317 UINT uModifyKeys, // the modify keys of this input key 00318 UINT uRL, // the right and left hand side 00319 UINT uVKey) // the input key 00320 { 00321 PTHREADINFO ptiCurrent = PtiCurrent(); 00322 PIMEHOTKEYOBJ phResult = NULL; 00323 PIMEHOTKEYOBJ ph; 00324 HKL hkl = GetActiveHKL(); 00325 WORD langPrimary = PRIMARYLANGID(LOWORD(HandleToUlong(hkl))); 00326 int iLevel = ILANG_NO_MATCH; 00327 00328 for (ph = pHead; ph != NULL; ph = ph->pNext) { 00329 00330 if (ph->hk.uVKey == uVKey) { 00331 BOOL fDoCheck = FALSE; 00332 00333 // Check if the modifiers match 00334 if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) { 00335 fDoCheck = TRUE; 00336 } else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) { 00337 continue; 00338 } 00339 00340 if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL || 00341 (ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) { 00342 fDoCheck = TRUE; 00343 } 00344 00345 if (fDoCheck) { 00346 LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID); 00347 int iMatch = GetLangIdMatchLevel(hkl, langId); 00348 00349 #if 0 // Test only 00350 if (iMatch != ILANG_NO_MATCH) { 00351 DbgPrint("GetIdMatchLevel(%X, %X)=%d\n", hkl, langId); 00352 } 00353 #endif 00354 00355 if (iMatch == ILANG_MATCH_PERFECT) { 00356 // Perfect match ! 00357 return ph; 00358 } 00359 00360 // If the hotkey is DSWITCH, GetLangIdMatchLevel() must return 3. 00361 UserAssert(ph->hk.dwHotKeyID < IME_HOTKEY_DSWITCH_FIRST || 00362 ph->hk.dwHotKeyID > IME_HOTKEY_DSWITCH_LAST); 00363 00364 if (langPrimary == LANG_KOREAN) { 00365 // Korean IME wants no hotkeys except the direct 00366 // keyboard layout switching hotkeys. 00367 continue; 00368 } 00369 00370 if (iMatch == ILANG_NO_MATCH) { 00371 // Special case for CHT/CHS toggle 00372 if (ph->hk.dwHotKeyID == IME_CHOTKEY_IME_NONIME_TOGGLE || 00373 ph->hk.dwHotKeyID == IME_THOTKEY_IME_NONIME_TOGGLE) { 00374 // 00375 // If the key is for CHT/CHS toggle and the previous 00376 // hkl is either CHT/CHS, it is a IME hotkey. 00377 // 00378 if (LOWORD(HandleToUlong(ptiCurrent->hklPrev)) == langId) { 00379 #if 0 // Test only 00380 DbgPrint("FindImeHotKeyByKey() found CHT/CHS hotkey.\n"); 00381 #endif 00382 return ph; 00383 } 00384 } 00385 } 00386 else if (iMatch > iLevel) { 00387 // Current ph is the strongest candidate so far. 00388 iLevel = iMatch; 00389 phResult = ph; 00390 } 00391 } 00392 } 00393 } 00394 00395 return phResult; 00396 } 00397 00398 /**********************************************************************/ 00399 /* FindImeHotKeyByID() */ 00400 /* Return Value: */ 00401 /* pHotKey - IMEHOTKEY pointer with the dwHotKeyID, */ 00402 /* else NULL - failure */ 00403 /**********************************************************************/ 00404 PIMEHOTKEYOBJ FindImeHotKeyByID( PIMEHOTKEYOBJ pHead, DWORD dwHotKeyID ) 00405 { 00406 PIMEHOTKEYOBJ ph; 00407 00408 for ( ph = pHead; ph != NULL; ph = ph->pNext ) { 00409 if ( ph->hk.dwHotKeyID == dwHotKeyID ) 00410 return (ph); 00411 } 00412 return (PIMEHOTKEYOBJ)NULL; 00413 } 00414 00415 /**********************************************************************/ 00416 /* FindImeHotKeyByKeyWithLang() */ 00417 /* Return Value: */ 00418 /* pHotKey - IMEHOTKEY pointer with the key, */ 00419 /* else NULL - failure */ 00420 /**********************************************************************/ 00421 PIMEHOTKEYOBJ FindImeHotKeyByKeyWithLang( // Finds pHotKey with this input key 00422 PIMEHOTKEYOBJ pHead, 00423 UINT uModifyKeys, // the modify keys of this input key 00424 UINT uRL, // the right and left hand side 00425 UINT uVKey, // the input key 00426 LANGID langIdKey) // the language id 00427 { 00428 PIMEHOTKEYOBJ ph; 00429 00430 for (ph = pHead; ph != NULL; ph = ph->pNext) { 00431 00432 if (ph->hk.uVKey == uVKey) { 00433 BOOL fDoCheck = FALSE; 00434 00435 // Check if the modifiers match 00436 if ((ph->hk.uModifiers & MOD_IGNORE_ALL_MODIFIER)) { 00437 fDoCheck = TRUE; 00438 } else if ((ph->hk.uModifiers & MOD_MODIFY_KEYS) != uModifyKeys) { 00439 continue; 00440 } 00441 00442 if ((ph->hk.uModifiers & MOD_BOTH_SIDES) == uRL || 00443 (ph->hk.uModifiers & MOD_BOTH_SIDES) & uRL) { 00444 fDoCheck = TRUE; 00445 } 00446 00447 if (fDoCheck) { 00448 LANGID langId = GetHotKeyLangID(ph->hk.dwHotKeyID); 00449 00450 if (langIdKey == langId || langId == LANG_NEUTRAL) { 00451 return ph; 00452 } 00453 } 00454 } 00455 } 00456 00457 return NULL; 00458 } 00459 00460 PIMEHOTKEYOBJ 00461 CheckImeHotKey( 00462 PQ pq, // input queue 00463 UINT uVKey, // virtual key 00464 LPARAM lParam // lparam of WM_KEYxxx message 00465 ) 00466 { 00467 static UINT uVKeySaved = 0; 00468 PIMEHOTKEYOBJ ph; 00469 UINT uModifiers = 0; 00470 BOOL fKeyUp; 00471 00472 // 00473 // early return for key up message 00474 // 00475 fKeyUp = ( lParam & 0x80000000 ) ? TRUE : FALSE; 00476 if ( fKeyUp ) { 00477 // 00478 // if the uVKey is not same as the vkey 00479 // we previously saved, there is no chance 00480 // that this is a hotkey. 00481 // 00482 if ( uVKeySaved != uVKey ) { 00483 uVKeySaved = 0; 00484 return NULL; 00485 } 00486 uVKeySaved = 0; 00487 // 00488 // If it's same, we still need to check 00489 // the hotkey list because there is a 00490 // chance that the hotkey list is modified 00491 // between the key make and break. 00492 // 00493 } 00494 00495 // 00496 // Current specification doesn't allow us to use a complex 00497 // hotkey such as LSHIFT+RMENU+SPACE 00498 // 00499 00500 // 00501 // Setup the shift, control, alt key states 00502 // 00503 uModifiers |= TestKeyStateDown(pq, VK_LSHIFT) ? (MOD_SHIFT | MOD_LEFT) : 0; 00504 uModifiers |= TestKeyStateDown(pq, VK_RSHIFT) ? (MOD_SHIFT | MOD_RIGHT) : 0; 00505 00506 uModifiers |= TestKeyStateDown(pq, VK_LCONTROL) ? (MOD_CONTROL | MOD_LEFT) : 0; 00507 uModifiers |= TestKeyStateDown(pq, VK_RCONTROL) ? (MOD_CONTROL | MOD_RIGHT) : 0; 00508 00509 uModifiers |= TestKeyStateDown(pq, VK_LMENU) ? (MOD_ALT | MOD_LEFT) : 0; 00510 uModifiers |= TestKeyStateDown(pq, VK_RMENU) ? (MOD_ALT | MOD_RIGHT) : 0; 00511 00512 ph = FindImeHotKeyByKey( gpImeHotKeyListHeader, 00513 uModifiers & MOD_MODIFY_KEYS, 00514 uModifiers & MOD_BOTH_SIDES, 00515 uVKey ); 00516 00517 if ( ph != NULL ) { 00518 if ( fKeyUp ) { 00519 if ( ph->hk.uModifiers & MOD_ON_KEYUP ) { 00520 return ph; 00521 } 00522 } else { 00523 if ( ph->hk.uModifiers & MOD_ON_KEYUP ) { 00524 // 00525 // save vkey for next keyup message time 00526 // 00527 // when ALT+Z is a hotkey, we don't want 00528 // to handle #2 as the hotkey sequence. 00529 // 1) ALT make -> 'Z' make -> 'Z' break 00530 // 2) 'Z' make -> ALT make -> 'Z' break 00531 // 00532 uVKeySaved = uVKey; 00533 } else { 00534 return ph; 00535 } 00536 } 00537 } 00538 00539 return NULL; 00540 }

Generated on Sat May 15 19:40:20 2004 for test by doxygen 1.3.7