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.
2443 lines
67 KiB
2443 lines
67 KiB
//
|
|
// hotkey.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "tim.h"
|
|
#include "dim.h"
|
|
#include "ic.h"
|
|
#include "hotkey.h"
|
|
#include "nuictrl.h"
|
|
#include "nuihkl.h"
|
|
#include "cregkey.h"
|
|
#include "ime.h"
|
|
#include "ctffunc.h"
|
|
#include "profiles.h"
|
|
|
|
|
|
#define TF_MOD_ALL (TF_MOD_ALT | \
|
|
TF_MOD_CONTROL | \
|
|
TF_MOD_SHIFT | \
|
|
TF_MOD_RALT | \
|
|
TF_MOD_RCONTROL | \
|
|
TF_MOD_RSHIFT | \
|
|
TF_MOD_LALT | \
|
|
TF_MOD_LCONTROL | \
|
|
TF_MOD_LSHIFT | \
|
|
TF_MOD_ON_KEYUP | \
|
|
TF_MOD_IGNORE_ALL_MODIFIER | \
|
|
TF_MOD_WIN | \
|
|
TF_MOD_LWIN | \
|
|
TF_MOD_RWIN)
|
|
|
|
static const TCHAR c_szKbdToggleKey[] = TEXT("Keyboard Layout\\Toggle");
|
|
static const TCHAR c_szHotKey[] = TEXT("Control Panel\\Input Method\\Hot Keys");
|
|
static const TCHAR c_szModifiers[] = TEXT("Key Modifiers");
|
|
static const TCHAR c_szVKey[] = TEXT("Virtual Key");
|
|
|
|
UINT g_uLangHotKeyModifiers = 0;
|
|
UINT g_uLangHotKeyVKey[2] = {0,0};
|
|
UINT g_uKeyTipHotKeyModifiers = 0;
|
|
UINT g_uKeyTipHotKeyVKey[2] = {0,0};
|
|
UINT g_uModifiers = 0;
|
|
|
|
#define CHSLANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
|
|
#define CHTLANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
|
|
|
|
//
|
|
// default IMM32 hotkeys.
|
|
//
|
|
// we use these default hotkey values if there is no entry in
|
|
// HKCU\Control Panel\Input Method\Hot Keys.
|
|
//
|
|
//
|
|
|
|
IMM32HOTKEY g_ImmHotKeys411[] = {
|
|
{IME_JHOTKEY_CLOSE_OPEN , VK_KANJI, TF_MOD_IGNORE_ALL_MODIFIER, FALSE},
|
|
{0 , 0, 0, FALSE}
|
|
};
|
|
|
|
IMM32HOTKEY g_ImmHotKeys412[] = {
|
|
{IME_KHOTKEY_SHAPE_TOGGLE , -1, -1, FALSE},
|
|
{IME_KHOTKEY_HANJACONVERT , -1, -1, FALSE},
|
|
{IME_KHOTKEY_ENGLISH , -1, -1, FALSE},
|
|
{0 , 0, 0, FALSE}
|
|
};
|
|
|
|
IMM32HOTKEY g_ImmHotKeys804[] = {
|
|
{IME_CHOTKEY_IME_NONIME_TOGGLE , VK_SPACE, TF_MOD_CONTROL, FALSE},
|
|
{IME_CHOTKEY_SHAPE_TOGGLE , VK_SPACE, TF_MOD_SHIFT, FALSE},
|
|
{IME_CHOTKEY_SYMBOL_TOGGLE , -1, -1, FALSE},
|
|
{0 , 0, 0, FALSE}
|
|
};
|
|
|
|
IMM32HOTKEY g_ImmHotKeys404[] = {
|
|
{IME_THOTKEY_IME_NONIME_TOGGLE , VK_SPACE, TF_MOD_CONTROL, FALSE},
|
|
{IME_THOTKEY_SHAPE_TOGGLE , VK_SPACE, TF_MOD_SHIFT, FALSE},
|
|
{IME_THOTKEY_SYMBOL_TOGGLE , -1, -1, FALSE},
|
|
{IME_ITHOTKEY_RESEND_RESULTSTR , -1, -1, FALSE},
|
|
{IME_ITHOTKEY_PREVIOUS_COMPOSITION , -1, -1, FALSE},
|
|
{IME_ITHOTKEY_UISTYLE_TOGGLE , -1, -1, FALSE},
|
|
{IME_ITHOTKEY_RECONVERTSTRING , -1, -1, FALSE},
|
|
{0 , 0, 0, FALSE}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CAsyncProcessHotKeyQueueItem
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CAsyncProcessHotKeyQueueItem : public CAsyncQueueItem
|
|
{
|
|
public:
|
|
CAsyncProcessHotKeyQueueItem(WPARAM wParam, LPARAM lParam, TimSysHotkey tsh, BOOL fTest, BOOL fSync) : CAsyncQueueItem(fSync)
|
|
{
|
|
_wParam = wParam;
|
|
_lParam = lParam;
|
|
_tsh = tsh;
|
|
_fTest = fTest;
|
|
}
|
|
|
|
HRESULT DoDispatch(CInputContext *pic)
|
|
{
|
|
CThreadInputMgr *ptim = CThreadInputMgr::_GetThis();
|
|
if (!ptim)
|
|
{
|
|
Assert(0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
ptim->_SyncProcessHotKey(_wParam, _lParam, _tsh, _fTest);
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
WPARAM _wParam;
|
|
LPARAM _lParam;
|
|
TimSysHotkey _tsh;
|
|
BOOL _fTest;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MSCTF default hotkeys.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct tag_DEFAULTHOTKEY
|
|
{
|
|
const GUID *pguid;
|
|
UINT uId;
|
|
TF_PRESERVEDKEY prekey;
|
|
TfGuidAtom guidatom;
|
|
} DEFAULTHOTKEY;
|
|
|
|
/* 61847d8e-29ff-11d4-97a9-00105a2799b5 */
|
|
const GUID GUID_DEFHOTKEY_CORRECTION = {
|
|
0x61847d8e,
|
|
0x29ff,
|
|
0x11d4,
|
|
{0x97, 0xa9, 0x00, 0x10, 0x5a, 0x27, 0x99, 0xb5}
|
|
};
|
|
|
|
/* 61847d8f-29ff-11d4-97a9-00105a2799b5 */
|
|
const GUID GUID_DEFHOTKEY_VOICE = {
|
|
0x61847d8f,
|
|
0x29ff,
|
|
0x11d4,
|
|
{0x97, 0xa9, 0x00, 0x10, 0x5a, 0x27, 0x99, 0xb5}
|
|
};
|
|
|
|
/* 61847d90-29ff-11d4-97a9-00105a2799b5 */
|
|
const GUID GUID_DEFHOTKEY_TOGGLE = {
|
|
0x61847d90,
|
|
0x29ff,
|
|
0x11d4,
|
|
{0x97, 0xa9, 0x00, 0x10, 0x5a, 0x27, 0x99, 0xb5}
|
|
};
|
|
|
|
/* 61847d91-29ff-11d4-97a9-00105a2799b5 */
|
|
const GUID GUID_DEFHOTKEY_HANDWRITE = {
|
|
0x61847d91,
|
|
0x29ff,
|
|
0x11d4,
|
|
{0x97, 0xa9, 0x00, 0x10, 0x5a, 0x27, 0x99, 0xb5}
|
|
};
|
|
|
|
#define DHID_CORRECTION 0
|
|
#define DHID_VOICE 1
|
|
#define DHID_TOGGLE 2
|
|
#define DHID_HANDWRITE 3
|
|
#define DEFHOTKEYNUM 4
|
|
|
|
DEFAULTHOTKEY g_DefHotKeys[] = {
|
|
{&GUID_DEFHOTKEY_CORRECTION, DHID_CORRECTION, {'C',TF_MOD_WIN}, TF_INVALID_GUIDATOM},
|
|
{&GUID_DEFHOTKEY_VOICE, DHID_VOICE, {'V',TF_MOD_WIN}, TF_INVALID_GUIDATOM},
|
|
{&GUID_DEFHOTKEY_TOGGLE, DHID_TOGGLE, {'T',TF_MOD_WIN}, TF_INVALID_GUIDATOM},
|
|
{&GUID_DEFHOTKEY_HANDWRITE, DHID_HANDWRITE, {'H',TF_MOD_WIN}, TF_INVALID_GUIDATOM},
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InitDefaultHotkeys
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::InitDefaultHotkeys()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < DEFHOTKEYNUM; i++)
|
|
{
|
|
CHotKey *pHotKey;
|
|
HRESULT hr;
|
|
hr = InternalPreserveKey(NULL,
|
|
*g_DefHotKeys[i].pguid,
|
|
&g_DefHotKeys[i].prekey,
|
|
NULL, 0, 0, &pHotKey);
|
|
|
|
if (SUCCEEDED(hr) && pHotKey)
|
|
g_DefHotKeys[i].guidatom = pHotKey->_guidatom;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// UninitDefaultHotkeys
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::UninitDefaultHotkeys()
|
|
{
|
|
int i;
|
|
for (i = 0; i < DEFHOTKEYNUM; i++)
|
|
{
|
|
UnpreserveKey(*g_DefHotKeys[i].pguid,
|
|
&g_DefHotKeys[i].prekey);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// PreserveKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::PreserveKey(TfClientId tid, REFGUID rguid, const TF_PRESERVEDKEY *pprekey, const WCHAR *pchDesc, ULONG cchDesc)
|
|
{
|
|
CTip *ctip;
|
|
|
|
if (!_GetCTipfromGUIDATOM(tid, &ctip))
|
|
return E_INVALIDARG;
|
|
|
|
return InternalPreserveKey(ctip, rguid, pprekey, pchDesc, cchDesc, 0, NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// PreserveKeyEx
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::PreserveKeyEx(TfClientId tid, REFGUID rguid, const TF_PRESERVEDKEY *pprekey, const WCHAR *pchDesc, ULONG cchDesc, DWORD dwFlags)
|
|
{
|
|
CTip *ctip;
|
|
|
|
if (!_GetCTipfromGUIDATOM(tid, &ctip))
|
|
return E_INVALIDARG;
|
|
|
|
return InternalPreserveKey(ctip, rguid, pprekey, pchDesc, cchDesc, dwFlags, NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InternalPreserveKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::InternalPreserveKey(CTip *ctip, REFGUID rguid, const TF_PRESERVEDKEY *pprekey, const WCHAR *pchDesc, ULONG cchDesc, DWORD dwFlags, CHotKey **ppHotKey)
|
|
{
|
|
CHotKey *pHotKey = NULL;
|
|
int nCnt;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!pprekey)
|
|
return E_INVALIDARG;
|
|
|
|
if (pprekey->uVKey > 0xff)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
if (ctip && _IsThisHotKey(ctip->_guidatom, pprekey))
|
|
{
|
|
hr = TF_E_ALREADY_EXISTS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!(pHotKey = new CHotKey()))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!pHotKey->Init(ctip ? ctip->_guidatom : g_gaSystem, pprekey, rguid, dwFlags))
|
|
{
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!pHotKey->SetDesc(pchDesc, cchDesc))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!_rgHotKey[pprekey->uVKey])
|
|
{
|
|
if (!(_rgHotKey[pprekey->uVKey] = new CPtrArray<CHotKey>))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert this to VKey list
|
|
//
|
|
nCnt = _rgHotKey[pprekey->uVKey]->Count();
|
|
if (!_rgHotKey[pprekey->uVKey]->Insert(nCnt, 1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
_rgHotKey[pprekey->uVKey]->Set(nCnt, pHotKey);
|
|
|
|
//
|
|
// Insert this to CTip list
|
|
//
|
|
if (ctip)
|
|
{
|
|
nCnt = ctip->_rgHotKey.Count();
|
|
if (!ctip->_rgHotKey.Insert(nCnt, 1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
ctip->_rgHotKey.Set(nCnt, pHotKey);
|
|
}
|
|
|
|
hr = S_OK;
|
|
_OnPreservedKeyUpdate(pHotKey);
|
|
|
|
Exit:
|
|
if (pHotKey && (hr != S_OK))
|
|
{
|
|
delete pHotKey;
|
|
}
|
|
|
|
if (ppHotKey)
|
|
*ppHotKey = (hr == S_OK) ? pHotKey : NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _IsThisHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_IsThisHotKey(TfClientId tid, const TF_PRESERVEDKEY *pprekey)
|
|
{
|
|
int nCnt;
|
|
int i;
|
|
|
|
if (!_rgHotKey[pprekey->uVKey])
|
|
return FALSE;
|
|
|
|
nCnt = _rgHotKey[pprekey->uVKey]->Count();
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
CHotKey *pHotKey;
|
|
pHotKey = _rgHotKey[pprekey->uVKey]->Get(i);
|
|
Assert(pHotKey);
|
|
Assert(pHotKey->_prekey.uVKey == pprekey->uVKey);
|
|
|
|
if (!pHotKey->IsValidTID(tid))
|
|
continue;
|
|
|
|
if (pHotKey->_prekey.uModifiers == pprekey->uModifiers)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// UnregisterHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::UnpreserveKey(REFGUID rguid, const TF_PRESERVEDKEY *pprekey)
|
|
{
|
|
int i;
|
|
BOOL bFound = FALSE;
|
|
HRESULT hr = CONNECT_E_NOCONNECTION;
|
|
CTip *ctip = NULL;
|
|
TfGuidAtom guidatom;
|
|
CHotKey *pHotKey = NULL;
|
|
int nCnt;
|
|
|
|
if (FAILED(MyRegisterGUID(rguid, &guidatom)))
|
|
return E_INVALIDARG;
|
|
|
|
if (!_rgHotKey[pprekey->uVKey])
|
|
return hr;
|
|
|
|
nCnt = _rgHotKey[pprekey->uVKey]->Count();
|
|
Assert(nCnt); // this should not be 0.
|
|
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
pHotKey = _rgHotKey[pprekey->uVKey]->Get(i);
|
|
|
|
if (pHotKey->_guidatom == guidatom)
|
|
{
|
|
//
|
|
// Remove this from VKey list.
|
|
//
|
|
_rgHotKey[pprekey->uVKey]->Remove(i, 1);
|
|
|
|
if (!ctip && (pHotKey->GetTID() != g_gaSystem))
|
|
_GetCTipfromGUIDATOM(pHotKey->GetTID(), &ctip);
|
|
|
|
//
|
|
// Remove this from CTip list.
|
|
//
|
|
if (ctip)
|
|
{
|
|
int nCntTid = ctip->_rgHotKey.Count();
|
|
int k;
|
|
for (k = 0; k < nCntTid; k++)
|
|
{
|
|
if (pHotKey == ctip->_rgHotKey.Get(k))
|
|
{
|
|
ctip->_rgHotKey.Remove(k, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if there is no hotkey in this vkey, delete ptrary.
|
|
//
|
|
if (!_rgHotKey[pprekey->uVKey]->Count())
|
|
{
|
|
delete _rgHotKey[pprekey->uVKey];
|
|
_rgHotKey[pprekey->uVKey] = NULL;
|
|
}
|
|
|
|
//
|
|
// make a notification.
|
|
//
|
|
_OnPreservedKeyUpdate(pHotKey);
|
|
|
|
//
|
|
// delete it.
|
|
//
|
|
delete pHotKey;
|
|
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _ProcessHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_ProcessHotKey(WPARAM wParam, LPARAM lParam, TimSysHotkey tsh, BOOL fTest, BOOL fSync)
|
|
{
|
|
UINT uVKey = (UINT)wParam & 0xff;
|
|
CAsyncProcessHotKeyQueueItem *pAsyncProcessHotKeyQueueItem;
|
|
CHotKey *pHotKey;
|
|
BOOL bRet;
|
|
HRESULT hr;
|
|
|
|
if (!_rgHotKey[uVKey])
|
|
return FALSE;
|
|
|
|
if (!_FindHotKeyByTID(TF_INVALID_GUIDATOM,
|
|
wParam,
|
|
lParam,
|
|
&pHotKey,
|
|
tsh,
|
|
g_uModifiers))
|
|
return FALSE;
|
|
|
|
if (!pHotKey)
|
|
return FALSE;
|
|
|
|
if (!pHotKey->IsNoDimNeeded() && !_pFocusDocInputMgr)
|
|
return FALSE;
|
|
|
|
if (!_pFocusDocInputMgr || (_pFocusDocInputMgr->_GetCurrentStack() < 0))
|
|
{
|
|
//
|
|
// we may need to invoke system hotkey under Empty DIM.
|
|
//
|
|
BOOL fEaten = FALSE;
|
|
|
|
if (fTest)
|
|
fEaten = TRUE;
|
|
else
|
|
{
|
|
GUID guid;
|
|
if (SUCCEEDED(MyGetGUID(pHotKey->_guidatom, &guid)))
|
|
_CallSimulatePreservedKey(pHotKey, NULL, guid, &fEaten);
|
|
}
|
|
|
|
return fEaten;
|
|
}
|
|
|
|
//
|
|
// Issue:
|
|
//
|
|
// We don't know which IC in the focus DIM will handle the hotkey yet.
|
|
// because the selection is changed by the application so we need to get ec
|
|
// to update the current selection pos. We do call GetSelection
|
|
// inside the root IC's lock. So it might be failed if hotkey's target
|
|
// is TOP IC.
|
|
//
|
|
CInputContext *pic = _pFocusDocInputMgr->_GetIC(0);
|
|
|
|
pAsyncProcessHotKeyQueueItem = new CAsyncProcessHotKeyQueueItem(wParam, lParam, tsh, fTest, fSync);
|
|
if (!pAsyncProcessHotKeyQueueItem)
|
|
return FALSE;
|
|
|
|
hr = S_OK;
|
|
|
|
bRet = TRUE;
|
|
if ((pic->_QueueItem(pAsyncProcessHotKeyQueueItem->GetItem(), FALSE, &hr) != S_OK) || FAILED(hr))
|
|
{
|
|
Assert(0);
|
|
bRet = FALSE;
|
|
}
|
|
|
|
pAsyncProcessHotKeyQueueItem->_Release();
|
|
return bRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _SyncProcessHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_SyncProcessHotKey(WPARAM wParam, LPARAM lParam, TimSysHotkey tsh, BOOL fTest)
|
|
{
|
|
CHotKey *pHotKey;
|
|
CInputContext *pic;
|
|
UINT uVKey = (UINT)wParam & 0xff;
|
|
BOOL fEaten = FALSE;
|
|
|
|
if (!_pFocusDocInputMgr)
|
|
return FALSE;
|
|
|
|
if (!_rgHotKey[uVKey])
|
|
return FALSE;
|
|
|
|
if (_FindHotKeyAndIC(wParam, lParam, &pHotKey, &pic, tsh, g_uModifiers))
|
|
{
|
|
if (fTest)
|
|
fEaten = TRUE;
|
|
else
|
|
{
|
|
GUID guid;
|
|
if (SUCCEEDED(MyGetGUID(pHotKey->_guidatom, &guid)))
|
|
_CallSimulatePreservedKey(pHotKey, pic, guid, &fEaten);
|
|
}
|
|
}
|
|
|
|
return fEaten;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _FindHotKeyByTiD
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_FindHotKeyByTID(TfClientId tid, WPARAM wParam, LPARAM lParam, CHotKey **ppHotKey, TimSysHotkey tsh, UINT uModCurrent)
|
|
{
|
|
UINT uVKey = (UINT)wParam & 0xff;
|
|
int nCnt;
|
|
int i;
|
|
CHotKey *pHotKey;
|
|
|
|
Assert(_rgHotKey[uVKey]);
|
|
|
|
nCnt = _rgHotKey[uVKey]->Count();
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
pHotKey = _rgHotKey[uVKey]->Get(i);
|
|
Assert(pHotKey);
|
|
Assert(pHotKey->_prekey.uVKey == uVKey);
|
|
|
|
if ((tid != TF_INVALID_GUIDATOM) && !pHotKey->IsValidTID(tid))
|
|
continue;
|
|
|
|
switch (tsh)
|
|
{
|
|
case TSH_SYSHOTKEY:
|
|
if (!pHotKey->IsSysHotkey())
|
|
continue;
|
|
break;
|
|
|
|
case TSH_NONSYSHOTKEY:
|
|
if (pHotKey->IsSysHotkey())
|
|
continue;
|
|
break;
|
|
|
|
case TSH_DONTCARE:
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
if ((pHotKey->_prekey.uModifiers & TF_MOD_ON_KEYUP) !=
|
|
((lParam & 0x80000000) ? (UINT)TF_MOD_ON_KEYUP : 0))
|
|
continue;
|
|
|
|
if (ModifiersCheck(uModCurrent, pHotKey->_prekey.uModifiers))
|
|
{
|
|
if (ppHotKey)
|
|
*ppHotKey = pHotKey;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _FindHotkeyIC
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_FindHotKeyAndIC(WPARAM wParam, LPARAM lParam, CHotKey **ppHotKey, CInputContext **ppic, TimSysHotkey tsh, UINT uModCurrent)
|
|
{
|
|
int iStack;
|
|
|
|
Assert(_pFocusDocInputMgr);
|
|
|
|
iStack = _pFocusDocInputMgr->_GetCurrentStack();
|
|
if (iStack < 0)
|
|
return FALSE;
|
|
|
|
while (iStack >= 0)
|
|
{
|
|
CInputContext *pic = _pFocusDocInputMgr->_GetIC(iStack);
|
|
if (_FindHotKeyInIC(wParam, lParam, ppHotKey, pic, tsh, uModCurrent))
|
|
{
|
|
if (ppic)
|
|
*ppic = pic;
|
|
return TRUE;
|
|
}
|
|
iStack--;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _FindHotkey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_FindHotKeyInIC(WPARAM wParam, LPARAM lParam, CHotKey **ppHotKey, CInputContext *pic, TimSysHotkey tsh, UINT uModCurrent)
|
|
{
|
|
TfClientId tid;
|
|
|
|
pic->_UpdateKeyEventFilter();
|
|
|
|
//
|
|
// try left side of the selection.
|
|
//
|
|
if ((tid = pic->_gaKeyEventFilterTIP[LEFT_FILTERTIP]) != TF_INVALID_GUIDATOM)
|
|
{
|
|
if (_FindHotKeyByTID(tid, wParam, lParam, ppHotKey, tsh, uModCurrent))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// try right side of the selection.
|
|
//
|
|
if ((tid = pic->_gaKeyEventFilterTIP[RIGHT_FILTERTIP]) != TF_INVALID_GUIDATOM)
|
|
{
|
|
if (_FindHotKeyByTID(tid, wParam, lParam, ppHotKey, tsh, uModCurrent))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// try foreground tip.
|
|
//
|
|
if ((_tidForeground != TF_INVALID_GUIDATOM) || (tsh == TSH_SYSHOTKEY))
|
|
{
|
|
if (_FindHotKeyByTID(_tidForeground, wParam, lParam, ppHotKey, tsh, uModCurrent))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we may have a system hotkey that matched with the wParm and lParam.
|
|
//
|
|
if (_FindHotKeyByTID(TF_INVALID_GUIDATOM, wParam, lParam, ppHotKey, TSH_SYSHOTKEY, uModCurrent))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CallKeyEventSink
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::_CallSimulatePreservedKey(CHotKey *pHotKey, CInputContext *pic, REFGUID rguid, BOOL *pfEaten)
|
|
{
|
|
ITfKeyEventSink *pSink;
|
|
CTip *ctip;
|
|
|
|
//
|
|
// This is tip's Preserved key.
|
|
//
|
|
if (pHotKey->GetTID() != g_gaSystem)
|
|
{
|
|
if (!pHotKey->IsNoDimNeeded() && !pic)
|
|
return S_FALSE;
|
|
|
|
if (!_GetCTipfromGUIDATOM(pHotKey->GetTID(), &ctip))
|
|
return E_INVALIDARG;
|
|
|
|
if (!(pSink = ctip->_pKeyEventSink))
|
|
return S_FALSE;
|
|
|
|
return pSink->OnPreservedKey(pic, rguid, pfEaten);
|
|
}
|
|
|
|
|
|
UINT uId = -1;
|
|
int i;
|
|
HRESULT hr = S_OK;
|
|
|
|
for (i = 0; i < DEFHOTKEYNUM; i++)
|
|
{
|
|
if (g_DefHotKeys[i].guidatom == pHotKey->_guidatom)
|
|
{
|
|
uId = g_DefHotKeys[i].uId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (g_DefHotKeys[i].uId)
|
|
{
|
|
case DHID_CORRECTION:
|
|
//
|
|
// simulate Reconversion Button.
|
|
//
|
|
hr = AsyncReconversion();
|
|
break;
|
|
|
|
case DHID_VOICE:
|
|
hr = MyToggleCompartmentDWORD(g_gaSystem,
|
|
GetGlobalComp(),
|
|
GUID_COMPARTMENT_SPEECH_OPENCLOSE,
|
|
NULL);
|
|
if (hr == S_OK)
|
|
*pfEaten = TRUE;
|
|
|
|
break;
|
|
|
|
case DHID_HANDWRITE:
|
|
hr = MyToggleCompartmentDWORD(g_gaSystem,
|
|
this,
|
|
GUID_COMPARTMENT_HANDWRITING_OPENCLOSE,
|
|
NULL);
|
|
if (hr == S_OK)
|
|
*pfEaten = TRUE;
|
|
|
|
break;
|
|
|
|
case DHID_TOGGLE:
|
|
|
|
DWORD dwMicOn;
|
|
|
|
if (FAILED(MyGetCompartmentDWORD(GetGlobalComp(),
|
|
GUID_COMPARTMENT_SPEECH_OPENCLOSE,
|
|
&dwMicOn)))
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
if ( dwMicOn )
|
|
{
|
|
DWORD dwSpeechStatus;
|
|
|
|
if (FAILED(MyGetCompartmentDWORD(GetGlobalComp(),
|
|
GUID_COMPARTMENT_SPEECH_GLOBALSTATE,
|
|
&dwSpeechStatus)))
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
if ((dwSpeechStatus & (TF_DICTATION_ON | TF_COMMANDING_ON)) == 0 )
|
|
{
|
|
// Both dictation and voice command are OFF
|
|
// After toggled, we set dictation ON.
|
|
dwSpeechStatus |= TF_DICTATION_ON;
|
|
}
|
|
else
|
|
{
|
|
dwSpeechStatus ^= TF_DICTATION_ON;
|
|
dwSpeechStatus ^= TF_COMMANDING_ON;
|
|
}
|
|
|
|
hr = MySetCompartmentDWORD(g_gaSystem,
|
|
GetGlobalComp(),
|
|
GUID_COMPARTMENT_SPEECH_GLOBALSTATE,
|
|
dwSpeechStatus);
|
|
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
*pfEaten = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetPreservedKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CThreadInputMgr::GetPreservedKey(ITfContext *pic, const TF_PRESERVEDKEY *pprekey, GUID *pguid)
|
|
{
|
|
CHotKey *pHotKey;
|
|
CInputContext *pcic;
|
|
HRESULT hr = S_FALSE; // we return S_FALE, if there is no proper keys.
|
|
|
|
if (!pguid)
|
|
return E_INVALIDARG;
|
|
|
|
*pguid = GUID_NULL;
|
|
|
|
if (!pprekey)
|
|
return E_INVALIDARG;
|
|
|
|
if (pprekey->uVKey >= ARRAYSIZE(_rgHotKey))
|
|
return E_INVALIDARG;
|
|
|
|
if (pprekey->uModifiers & ~TF_MOD_ALL)
|
|
return E_INVALIDARG;
|
|
|
|
if (!pic)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(pcic = GetCInputContext(pic)))
|
|
return E_INVALIDARG;
|
|
|
|
if (!_rgHotKey[pprekey->uVKey])
|
|
goto Exit;
|
|
|
|
//
|
|
// we always get KeyUp preserve key first.
|
|
//
|
|
if (_FindHotKeyInIC(pprekey->uVKey, 0x80000000, &pHotKey, pcic, TSH_DONTCARE, pprekey->uModifiers))
|
|
{
|
|
hr = MyGetGUID(pHotKey->_guidatom, pguid);
|
|
}
|
|
else if (_FindHotKeyInIC(pprekey->uVKey, 0x0, &pHotKey, pcic, TSH_DONTCARE, pprekey->uModifiers))
|
|
{
|
|
hr = MyGetGUID(pHotKey->_guidatom, pguid);
|
|
}
|
|
|
|
Exit:
|
|
SafeRelease(pcic);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// isPreservedKeyInfo
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CThreadInputMgr::IsPreservedKey(REFGUID rguid, const TF_PRESERVEDKEY *pprekey, BOOL *pfRegistered)
|
|
{
|
|
TfGuidAtom guidatom;
|
|
int i;
|
|
int nCnt;
|
|
|
|
if (!pfRegistered)
|
|
return E_INVALIDARG;
|
|
|
|
*pfRegistered = FALSE;
|
|
|
|
if (!pprekey)
|
|
return E_INVALIDARG;
|
|
|
|
if (pprekey->uVKey >= ARRAYSIZE(_rgHotKey))
|
|
return E_INVALIDARG;
|
|
|
|
if (pprekey->uModifiers & ~TF_MOD_ALL)
|
|
return E_INVALIDARG;
|
|
|
|
if (FAILED(MyRegisterGUID(rguid, &guidatom)))
|
|
return E_FAIL;
|
|
|
|
if (!_rgHotKey[pprekey->uVKey])
|
|
return S_FALSE;
|
|
|
|
nCnt = _rgHotKey[pprekey->uVKey]->Count();
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
CHotKey *pHotKey = _rgHotKey[pprekey->uVKey]->Get(i);
|
|
if ((guidatom == pHotKey->_guidatom) &&
|
|
(pprekey->uModifiers == pHotKey->_prekey.uModifiers))
|
|
{
|
|
*pfRegistered = TRUE;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetPreservedKeyInfoInternal
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CThreadInputMgr::_GetFirstPreservedKey(REFGUID rguid, CHotKey **ppHotKey)
|
|
{
|
|
UINT uVKey;
|
|
TfGuidAtom guidatom;
|
|
|
|
if (FAILED(MyRegisterGUID(rguid, &guidatom)))
|
|
return FALSE;
|
|
|
|
for (uVKey = 0; uVKey < 256; uVKey++)
|
|
{
|
|
int nCnt;
|
|
int i;
|
|
|
|
if (!_rgHotKey[uVKey])
|
|
continue;
|
|
|
|
nCnt = _rgHotKey[uVKey]->Count();
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
CHotKey *pHotKey = _rgHotKey[uVKey]->Get(i);
|
|
if (guidatom == pHotKey->_guidatom)
|
|
{
|
|
*ppHotKey = pHotKey;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// SimulatePreservedKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CThreadInputMgr::SimulatePreservedKey(ITfContext *pic, REFGUID rguid, BOOL *pfEaten)
|
|
{
|
|
CInputContext *pcic;
|
|
CHotKey *pHotKey;
|
|
HRESULT hr;
|
|
|
|
if (!pfEaten)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(pcic = GetCInputContext(pic)))
|
|
return E_INVALIDARG;
|
|
|
|
hr = S_OK;
|
|
*pfEaten = FALSE;
|
|
|
|
if (_GetFirstPreservedKey(rguid, &pHotKey))
|
|
{
|
|
//
|
|
// we always get KeyUp preserve key first.
|
|
//
|
|
if (_FindHotKeyInIC(pHotKey->_prekey.uVKey,
|
|
0x80000000,
|
|
NULL,
|
|
pcic,
|
|
TSH_DONTCARE,
|
|
pHotKey->_prekey.uModifiers))
|
|
{
|
|
hr = _CallSimulatePreservedKey(pHotKey, pcic, rguid, pfEaten);
|
|
}
|
|
else if (_FindHotKeyInIC(pHotKey->_prekey.uVKey,
|
|
0x0,
|
|
NULL,
|
|
pcic,
|
|
TSH_DONTCARE,
|
|
pHotKey->_prekey.uModifiers))
|
|
{
|
|
hr = _CallSimulatePreservedKey(pHotKey, pcic, rguid, pfEaten);
|
|
}
|
|
|
|
}
|
|
|
|
SafeRelease(pcic);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _OnPreservedKeyUpdate
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CThreadInputMgr::_OnPreservedKeyUpdate(CHotKey *pHotKey)
|
|
{
|
|
CStructArray<GENERICSINK> *pSinks = _GetPreservedKeyNotifySinks();
|
|
int i;
|
|
|
|
//
|
|
// we don't make a notification for system default hotkeys.
|
|
//
|
|
if (pHotKey->GetTID() == g_gaSystem)
|
|
return S_OK;
|
|
|
|
for (i = 0; i < pSinks->Count(); i++)
|
|
{
|
|
((ITfPreservedKeyNotifySink *)pSinks->GetPtr(i)->pSink)->OnUpdated(&pHotKey->_prekey);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// SetPreservedKeyDescription
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CThreadInputMgr::SetPreservedKeyDescription(REFGUID rguid, const WCHAR *pchDesc, ULONG cchDesc)
|
|
{
|
|
CHotKey *pHotKey;
|
|
if (!_GetFirstPreservedKey(rguid, &pHotKey))
|
|
return E_INVALIDARG;
|
|
|
|
if (!pHotKey->SetDesc(pchDesc, cchDesc))
|
|
return E_FAIL;
|
|
|
|
_OnPreservedKeyUpdate(pHotKey);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetPreservedKeyDescription
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CThreadInputMgr::GetPreservedKeyDescription(REFGUID rguid, BSTR *pbstrDesc)
|
|
{
|
|
CHotKey *pHotKey;
|
|
if (!_GetFirstPreservedKey(rguid, &pHotKey))
|
|
return E_INVALIDARG;
|
|
|
|
if (!pHotKey->GetDesc(pbstrDesc))
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Static Functions
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define IsAlt(u) ((u & TF_MOD_ALT) ? 1 : 0)
|
|
#define IsShift(u) ((u & TF_MOD_SHIFT) ? 1 : 0)
|
|
#define IsControl(u) ((u & TF_MOD_CONTROL) ? 1 : 0)
|
|
#define IsWin(u) ((u & TF_MOD_WIN) ? 1 : 0)
|
|
|
|
|
|
#define CheckMod(m0, m1, mod) \
|
|
if (m1 & TF_MOD_ ## mod ##) \
|
|
{ \
|
|
if (!(m0 & TF_MOD_ ## mod ##)) \
|
|
return FALSE; \
|
|
} \
|
|
else \
|
|
{ \
|
|
if ((m1 ^ m0) & TF_MOD_RL ## mod ##) \
|
|
return FALSE; \
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ModifiersCheck
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL ModifiersCheck(UINT uModCurrent, UINT uMod)
|
|
{
|
|
uMod &= ~TF_MOD_ON_KEYUP;
|
|
|
|
if (uMod & TF_MOD_IGNORE_ALL_MODIFIER)
|
|
return TRUE;
|
|
|
|
if (uModCurrent == uMod)
|
|
return TRUE;
|
|
|
|
if (uModCurrent && !uMod)
|
|
return FALSE;
|
|
|
|
CheckMod(uModCurrent, uMod, ALT);
|
|
CheckMod(uModCurrent, uMod, SHIFT);
|
|
CheckMod(uModCurrent, uMod, CONTROL);
|
|
CheckMod(uModCurrent, uMod, WIN);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// InitLangChangeHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL InitLangChangeHotKey()
|
|
{
|
|
CMyRegKey key;
|
|
TCHAR sz[2] = TEXT("3");
|
|
TCHAR sz2[2] = TEXT("3");
|
|
|
|
if (key.Open(HKEY_CURRENT_USER, c_szKbdToggleKey, KEY_READ) == S_OK)
|
|
{
|
|
if (key.QueryValueCch(sz, TEXT("Language Hotkey"), ARRAYSIZE(sz)) != S_OK)
|
|
{
|
|
if (key.QueryValueCch(sz, IsOnNT() ? TEXT("Hotkey") : NULL, ARRAYSIZE(sz)) != S_OK)
|
|
{
|
|
sz[0] = TEXT('1');
|
|
sz[1] = TEXT('\0');
|
|
}
|
|
|
|
if (PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) == LANG_CHINESE)
|
|
{
|
|
sz[0] = TEXT('1');
|
|
sz[1] = TEXT('\0');
|
|
}
|
|
}
|
|
if (key.QueryValueCch(sz2, TEXT("Layout Hotkey"), ARRAYSIZE(sz)) != S_OK)
|
|
{
|
|
if (lstrcmp(sz, TEXT("2")) == 0)
|
|
{
|
|
sz2[0] = TEXT('1');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
else
|
|
{
|
|
sz2[0] = TEXT('2');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
|
|
if (GetSystemMetrics(SM_MIDEASTENABLED))
|
|
{
|
|
sz2[0] = TEXT('3');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if lang and layout hotkey is the same key, let's disable the layout hotkey
|
|
//
|
|
if (lstrcmp(sz, sz2) == 0)
|
|
{
|
|
if (lstrcmp(sz, TEXT("1")) == 0)
|
|
{
|
|
sz2[0] = TEXT('2');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
else if (lstrcmp(sz, TEXT("2")) == 0)
|
|
{
|
|
sz2[0] = TEXT('1');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
else
|
|
{
|
|
sz2[0] = TEXT('3');
|
|
sz2[1] = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
CicEnterCriticalSection(g_csInDllMain);
|
|
|
|
switch (sz[0])
|
|
{
|
|
case ( TEXT('1') ) :
|
|
default:
|
|
{
|
|
g_uLangHotKeyModifiers = TF_MOD_ALT | TF_MOD_SHIFT;
|
|
g_uLangHotKeyVKey[0] = VK_SHIFT;
|
|
g_uLangHotKeyVKey[1] = VK_MENU;
|
|
break;
|
|
}
|
|
case ( TEXT('2') ) :
|
|
{
|
|
g_uLangHotKeyModifiers = TF_MOD_CONTROL | TF_MOD_SHIFT;
|
|
g_uLangHotKeyVKey[0] = VK_SHIFT;
|
|
g_uLangHotKeyVKey[1] = VK_CONTROL;
|
|
break;
|
|
}
|
|
case ( TEXT('3') ) :
|
|
{
|
|
g_uLangHotKeyModifiers = 0;
|
|
g_uLangHotKeyVKey[0] = 0;
|
|
g_uLangHotKeyVKey[1] = 0;
|
|
break;
|
|
}
|
|
case ( TEXT('4') ) :
|
|
{
|
|
g_uLangHotKeyModifiers = 0;
|
|
g_uLangHotKeyVKey[0] = CHAR_GRAVE;
|
|
g_uLangHotKeyVKey[1] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the layout switch hotkey.
|
|
//
|
|
switch (sz2[0])
|
|
{
|
|
case ( TEXT('1') ) :
|
|
default:
|
|
{
|
|
g_uKeyTipHotKeyModifiers = TF_MOD_LALT | TF_MOD_SHIFT;
|
|
g_uKeyTipHotKeyVKey[0] = VK_SHIFT;
|
|
g_uKeyTipHotKeyVKey[1] = VK_MENU;
|
|
break;
|
|
}
|
|
case ( TEXT('2') ) :
|
|
{
|
|
g_uKeyTipHotKeyModifiers = TF_MOD_CONTROL | TF_MOD_SHIFT;
|
|
g_uKeyTipHotKeyVKey[0] = VK_SHIFT;
|
|
g_uKeyTipHotKeyVKey[1] = VK_CONTROL;
|
|
break;
|
|
}
|
|
case ( TEXT('3') ) :
|
|
{
|
|
g_uKeyTipHotKeyModifiers = 0;
|
|
g_uKeyTipHotKeyVKey[0] = 0;
|
|
g_uKeyTipHotKeyVKey[1] = 0;
|
|
break;
|
|
}
|
|
case ( TEXT('4') ) :
|
|
{
|
|
g_uKeyTipHotKeyModifiers = 0;
|
|
g_uKeyTipHotKeyVKey[0] = VK_GRAVE;
|
|
g_uKeyTipHotKeyVKey[1] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CicLeaveCriticalSection(g_csInDllMain);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// UpdateModifiers
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
SHORT sksMenu = GetKeyState(VK_MENU);
|
|
SHORT sksCtrl = GetKeyState(VK_CONTROL);
|
|
SHORT sksShft = GetKeyState(VK_SHIFT);
|
|
SHORT sksLWin = GetKeyState(VK_LWIN);
|
|
SHORT sksRWin = GetKeyState(VK_RWIN);
|
|
|
|
CicEnterCriticalSection(g_cs);
|
|
switch (wParam & 0xff)
|
|
{
|
|
case VK_MENU:
|
|
if (sksMenu & 0x8000)
|
|
{
|
|
if (lParam & 0x01000000)
|
|
g_uModifiers |= (TF_MOD_RALT | TF_MOD_ALT);
|
|
else
|
|
g_uModifiers |= (TF_MOD_LALT | TF_MOD_ALT);
|
|
}
|
|
break;
|
|
|
|
case VK_CONTROL:
|
|
if (sksCtrl & 0x8000)
|
|
{
|
|
if (lParam & 0x01000000)
|
|
g_uModifiers |= (TF_MOD_RCONTROL | TF_MOD_CONTROL);
|
|
else
|
|
g_uModifiers |= (TF_MOD_LCONTROL | TF_MOD_CONTROL);
|
|
}
|
|
break;
|
|
|
|
case VK_SHIFT:
|
|
if (sksShft & 0x8000)
|
|
{
|
|
if (((lParam >> 16) & 0x00ff) == 0x36)
|
|
g_uModifiers |= (TF_MOD_RSHIFT | TF_MOD_SHIFT);
|
|
else
|
|
g_uModifiers |= (TF_MOD_LSHIFT | TF_MOD_SHIFT);
|
|
}
|
|
break;
|
|
|
|
case VK_LWIN:
|
|
if (sksLWin & 0x8000)
|
|
g_uModifiers |= (TF_MOD_LWIN | TF_MOD_WIN);
|
|
break;
|
|
|
|
case VK_RWIN:
|
|
if (sksRWin & 0x8000)
|
|
g_uModifiers |= (TF_MOD_RWIN | TF_MOD_WIN);
|
|
break;
|
|
}
|
|
|
|
if (!(sksMenu & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_ALLALT;
|
|
if (!(sksCtrl & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_ALLCONTROL;
|
|
if (!(sksShft & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_ALLSHIFT;
|
|
if (!(sksRWin & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_RWIN;
|
|
if (!(sksLWin & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_LWIN;
|
|
if (!(sksRWin & 0x8000) && !(sksLWin & 0x8000))
|
|
g_uModifiers &= ~TF_MOD_WIN;
|
|
|
|
CicLeaveCriticalSection(g_cs);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// IsInLangChangeHotkeyStatus
|
|
//
|
|
// This function check the current keyboard status is in LangChange hotkey.
|
|
// This will be a trigger to eat WM_INPUTLANGUAGECHANGEREQUEST that was
|
|
// genereated by System. This is a fallback code because sometimes we
|
|
// could not eat the message in CheckLangChangeHotKey() (inside keyboard hook).
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL IsInLangChangeHotkeyStatus()
|
|
{
|
|
//
|
|
// we don't need this hack on NT.
|
|
//
|
|
if (IsOnNT())
|
|
return FALSE;
|
|
|
|
|
|
//
|
|
// this Modifiers patch works for only Key-Down time hotkey.
|
|
// this hack does not work for Key-Up time hotkey.
|
|
//
|
|
#if 0
|
|
//
|
|
// patch Shift status for g_uModifiers.
|
|
// we might not be able to catch up the current keystatus because
|
|
// sytstem could eat Shift key and no keyboard hook was called.
|
|
//
|
|
if (GetKeyState(VK_SHIFT) & 0x8000)
|
|
g_uModifiers |= TF_MOD_SHIFT;
|
|
else
|
|
g_uModifiers &= ~TF_MOD_ALLSHIFT;
|
|
#endif
|
|
|
|
if (g_uLangHotKeyModifiers &&
|
|
ModifiersCheck(g_uModifiers, g_uLangHotKeyModifiers))
|
|
return TRUE;
|
|
|
|
if (g_uKeyTipHotKeyModifiers &&
|
|
ModifiersCheck(g_uModifiers, g_uKeyTipHotKeyModifiers))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CheckLangChangeHotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CheckLangChangeHotKey(SYSTHREAD *psfn, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL fLangHotKeys;
|
|
BOOL fKeyTipHotKeys;
|
|
|
|
if (psfn == NULL)
|
|
return FALSE;
|
|
|
|
//
|
|
// we don't care about reperted key down.
|
|
//
|
|
if ((lParam & 0xffff) > 1)
|
|
return FALSE;
|
|
|
|
//
|
|
// If we are not interested in the VKey (wParam),
|
|
// clear bToggleReady up and don't eat it.
|
|
//
|
|
if ((g_uLangHotKeyVKey[0] != wParam) &&
|
|
(g_uLangHotKeyVKey[1] != wParam))
|
|
{
|
|
if (psfn->bLangToggleReady)
|
|
{
|
|
psfn->bLangToggleReady = FALSE;
|
|
}
|
|
|
|
fLangHotKeys = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fLangHotKeys = TRUE;
|
|
}
|
|
|
|
if ((g_uKeyTipHotKeyVKey[0] != wParam) &&
|
|
(g_uKeyTipHotKeyVKey[1] != wParam))
|
|
{
|
|
if (psfn->bKeyTipToggleReady)
|
|
{
|
|
psfn->bKeyTipToggleReady = FALSE;
|
|
}
|
|
|
|
fKeyTipHotKeys = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fKeyTipHotKeys = TRUE;
|
|
}
|
|
|
|
if (fLangHotKeys && !psfn->bLangToggleReady)
|
|
{
|
|
if (!(lParam & 0x80000000))
|
|
{
|
|
if (g_uLangHotKeyModifiers &&
|
|
ModifiersCheck(g_uModifiers, g_uLangHotKeyModifiers))
|
|
{
|
|
//
|
|
// we will change assembly at next key up.
|
|
//
|
|
psfn->bLangToggleReady = TRUE;
|
|
|
|
//
|
|
// we always eat Language change hotkey to stop system
|
|
// to change hKL.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fKeyTipHotKeys && !psfn->bKeyTipToggleReady)
|
|
{
|
|
if (!(lParam & 0x80000000))
|
|
{
|
|
if (g_uKeyTipHotKeyModifiers &&
|
|
ModifiersCheck(g_uModifiers, g_uKeyTipHotKeyModifiers))
|
|
{
|
|
if (GetKeyboardItemNum() >= 2)
|
|
{
|
|
//
|
|
// we will change assembly at next key up.
|
|
//
|
|
psfn->bKeyTipToggleReady = TRUE;
|
|
|
|
//
|
|
// we don't want to eat KeyTip change hotkey if there is
|
|
// only one keyboard item in this language.
|
|
// Ctrl+Shift is used by apps.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (wParam == VK_GRAVE && g_uKeyTipHotKeyVKey[0] == wParam)
|
|
{
|
|
//
|
|
// we will change assembly at next key up.
|
|
//
|
|
psfn->bKeyTipToggleReady = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(lParam & 0x80000000))
|
|
{
|
|
//
|
|
// want to eat Grave Accent if it is a layout switching hotkey for ME.
|
|
//
|
|
if (g_uKeyTipHotKeyVKey[0] == wParam && wParam == VK_GRAVE)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bLangToggleReady = psfn->bLangToggleReady;
|
|
BOOL bKeyTipToggleReady = psfn->bKeyTipToggleReady;
|
|
psfn->bLangToggleReady = FALSE;
|
|
psfn->bKeyTipToggleReady = FALSE;
|
|
|
|
if (bLangToggleReady)
|
|
{
|
|
|
|
//
|
|
// remove all WM_INPUTLANGCHANGEREQUEST message.
|
|
//
|
|
MSG msg;
|
|
while(PeekMessage(&msg, NULL,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
PM_REMOVE));
|
|
|
|
if (g_uLangHotKeyVKey[0] == VK_SHIFT)
|
|
{
|
|
BOOL bRightShift = FALSE;
|
|
if ((((wParam & 0xff) == VK_SHIFT) &&
|
|
(((lParam >> 16) & 0x00ff) == 0x36)) ||
|
|
(g_uModifiers & TF_MOD_RSHIFT))
|
|
bRightShift = TRUE;
|
|
|
|
PostThreadMessage(GetCurrentThreadId(),
|
|
g_msgPrivate,
|
|
TFPRIV_LANGCHANGE,
|
|
bRightShift);
|
|
return TRUE;
|
|
}
|
|
else if (g_uLangHotKeyVKey[0] == CHAR_GRAVE)
|
|
{
|
|
//
|
|
// Issue: we need to do something for Thai.
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (bKeyTipToggleReady)
|
|
{
|
|
//
|
|
// remove all WM_INPUTLANGCHANGEREQUEST message.
|
|
//
|
|
MSG msg;
|
|
while(PeekMessage(&msg, NULL,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
PM_REMOVE));
|
|
|
|
if (g_uKeyTipHotKeyVKey[0] == VK_SHIFT)
|
|
{
|
|
BOOL bRightShift = FALSE;
|
|
if ((((wParam & 0xff) == VK_SHIFT) &&
|
|
(((lParam >> 16) & 0x00ff) == 0x36)) ||
|
|
(g_uModifiers & TF_MOD_RSHIFT))
|
|
bRightShift = TRUE;
|
|
|
|
PostThreadMessage(GetCurrentThreadId(),
|
|
g_msgPrivate,
|
|
TFPRIV_KEYTIPCHANGE,
|
|
bRightShift);
|
|
return TRUE;
|
|
}
|
|
else if (g_uKeyTipHotKeyVKey[0] == VK_GRAVE)
|
|
{
|
|
// checking for Middle East(Arabic or Hebrew) layout hotkey to
|
|
// support the third hotkey value(Grave Accent) instead of Ctrl+Shift
|
|
// or Alt+Shift.
|
|
|
|
PostThreadMessage(GetCurrentThreadId(),
|
|
g_msgPrivate,
|
|
TFPRIV_KEYTIPCHANGE,
|
|
FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Imm32ModtoCicMod
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
UINT Imm32ModtoCicMod(UINT uImm32Mod)
|
|
{
|
|
UINT uMod = 0;
|
|
if ((uImm32Mod & (MOD_LEFT | MOD_RIGHT)) == (MOD_LEFT | MOD_RIGHT))
|
|
uImm32Mod &= ~(MOD_LEFT | MOD_RIGHT);
|
|
|
|
if (uImm32Mod & MOD_LEFT)
|
|
{
|
|
if (uImm32Mod & MOD_ALT) uMod |= TF_MOD_LALT;
|
|
if (uImm32Mod & MOD_CONTROL) uMod |= TF_MOD_LCONTROL;
|
|
if (uImm32Mod & MOD_SHIFT) uMod |= TF_MOD_LSHIFT;
|
|
}
|
|
else if (uImm32Mod & MOD_RIGHT)
|
|
{
|
|
if (uImm32Mod & MOD_ALT) uMod |= TF_MOD_RALT;
|
|
if (uImm32Mod & MOD_CONTROL) uMod |= TF_MOD_RCONTROL;
|
|
if (uImm32Mod & MOD_SHIFT) uMod |= TF_MOD_RSHIFT;
|
|
}
|
|
else
|
|
{
|
|
if (uImm32Mod & MOD_ALT) uMod |= TF_MOD_ALT;
|
|
if (uImm32Mod & MOD_CONTROL) uMod |= TF_MOD_CONTROL;
|
|
if (uImm32Mod & MOD_SHIFT) uMod |= TF_MOD_SHIFT;
|
|
}
|
|
|
|
if (uImm32Mod & MOD_ON_KEYUP) uMod |= TF_MOD_ON_KEYUP;
|
|
if (uImm32Mod & MOD_IGNORE_ALL_MODIFIER) uMod |= TF_MOD_IGNORE_ALL_MODIFIER;
|
|
|
|
return uMod;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// LoadImmHotkeyFromReg
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL LoadImmHotkeyFromReg(IMM32HOTKEY *pHotKey)
|
|
{
|
|
CMyRegKey key;
|
|
UINT uMod;
|
|
DWORD dw;
|
|
TCHAR szKey[256];
|
|
|
|
pHotKey->fInit = TRUE;
|
|
|
|
StringCchPrintf(szKey, ARRAYSIZE(szKey),"%s\\%08x", c_szHotKey, pHotKey->dwId);
|
|
if (key.Open(HKEY_CURRENT_USER, szKey, KEY_READ) != S_OK)
|
|
goto Exit;
|
|
|
|
pHotKey->uVKey = (UINT)-2;
|
|
dw = sizeof(DWORD);
|
|
key.QueryBinaryValue(&uMod, dw, c_szModifiers);
|
|
dw = sizeof(DWORD);
|
|
key.QueryBinaryValue(&pHotKey->uVKey, dw, c_szVKey);
|
|
|
|
pHotKey->uModifiers = Imm32ModtoCicMod(uMod);
|
|
|
|
Exit:
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetImmHotKeyTable()
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMM32HOTKEY *GetImmHotKeyTable(LANGID langid)
|
|
{
|
|
IMM32HOTKEY *pHotKeys;
|
|
|
|
switch (langid)
|
|
{
|
|
case 0x0411: pHotKeys = g_ImmHotKeys411; break;
|
|
case 0x0412: pHotKeys = g_ImmHotKeys412; break;
|
|
case 0x0404: pHotKeys = g_ImmHotKeys404; break;
|
|
case 0x0804: pHotKeys = g_ImmHotKeys804; break;
|
|
default:
|
|
switch (g_uACP)
|
|
{
|
|
case 932: pHotKeys = g_ImmHotKeys411; break;
|
|
case 936: pHotKeys = g_ImmHotKeys804; break;
|
|
case 949: pHotKeys = g_ImmHotKeys412; break;
|
|
case 950: pHotKeys = g_ImmHotKeys404; break;
|
|
}
|
|
return NULL;
|
|
}
|
|
return pHotKeys;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// IsImmHotkey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMM32HOTKEY *IsImmHotkey(UINT uVKey, BOOL fUp, UINT uModifiers, LANGID langid)
|
|
{
|
|
int i = 0;
|
|
BOOL bRet = FALSE;
|
|
IMM32HOTKEY *pHotKeys;
|
|
IMM32HOTKEY *pHotKeyRet = NULL;
|
|
|
|
pHotKeys = GetImmHotKeyTable(langid);
|
|
if (!pHotKeys)
|
|
return NULL;
|
|
|
|
CicEnterCriticalSection(g_cs);
|
|
|
|
while (pHotKeys[i].dwId)
|
|
{
|
|
if (!pHotKeys[i].fInit)
|
|
LoadImmHotkeyFromReg(&pHotKeys[i]);
|
|
|
|
if ((pHotKeys[i].uVKey == uVKey) &&
|
|
pHotKeys[i].uModifiers &&
|
|
ModifiersCheck(uModifiers, pHotKeys[i].uModifiers))
|
|
{
|
|
pHotKeyRet = &pHotKeys[i];
|
|
|
|
if ((pHotKeyRet->uModifiers & TF_MOD_ON_KEYUP) && !fUp)
|
|
pHotKeyRet = NULL;
|
|
else if (!(pHotKeyRet->uModifiers & TF_MOD_ON_KEYUP) && fUp)
|
|
pHotKeyRet = NULL;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
Exit:
|
|
CicLeaveCriticalSection(g_cs);
|
|
return pHotKeyRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// IsInImmHotkeyStatus
|
|
//
|
|
//
|
|
// This function check the current keyboard status is in IMM32's hotkey.
|
|
// This will be a trigger to eat WM_INPUTLANGUAGECHANGEREQUEST that was
|
|
// genereated by IMM32. This is a fallback code because sometimes we
|
|
// could not eat the message in CheckImm32HotKey() (inside keyboard hook).
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
IMM32HOTKEY *IsInImmHotkeyStatus(SYSTHREAD *psfn, LANGID langid)
|
|
{
|
|
int i = 0;
|
|
IMM32HOTKEY *pHotKeys;
|
|
IMM32HOTKEY *pHotKeyRet = NULL;
|
|
BYTE bkey[256];
|
|
UINT uModifiers;
|
|
|
|
if (!psfn)
|
|
return NULL;
|
|
|
|
if (psfn->fRemovingInputLangChangeReq)
|
|
return NULL;
|
|
|
|
if (!psfn->ptim)
|
|
return NULL;
|
|
|
|
if (!psfn->ptim->_GetFocusDocInputMgr())
|
|
return NULL;
|
|
|
|
if (!GetKeyboardState(bkey))
|
|
return NULL;
|
|
|
|
pHotKeys = GetImmHotKeyTable(langid);
|
|
if (!pHotKeys)
|
|
return NULL;
|
|
|
|
uModifiers = 0;
|
|
if (bkey[VK_MENU] & 0x80)
|
|
uModifiers |= TF_MOD_ALT;
|
|
|
|
if (bkey[VK_CONTROL] & 0x80)
|
|
uModifiers |= TF_MOD_CONTROL;
|
|
|
|
if (bkey[VK_SHIFT] & 0x80)
|
|
uModifiers |= TF_MOD_SHIFT;
|
|
|
|
CicEnterCriticalSection(g_cs);
|
|
|
|
while (pHotKeys[i].dwId)
|
|
{
|
|
if (!pHotKeys[i].fInit)
|
|
LoadImmHotkeyFromReg(&pHotKeys[i]);
|
|
|
|
if ((bkey[pHotKeys[i].uVKey & 0xff] & 0x80) &&
|
|
pHotKeys[i].uModifiers &&
|
|
ModifiersCheck(uModifiers, pHotKeys[i].uModifiers))
|
|
{
|
|
pHotKeyRet = &pHotKeys[i];
|
|
|
|
if (pHotKeyRet->uModifiers & TF_MOD_ON_KEYUP)
|
|
pHotKeyRet = NULL;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
Exit:
|
|
CicLeaveCriticalSection(g_cs);
|
|
return pHotKeyRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CancelImmHotkey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef SIMULATE_EATENKEYS
|
|
BOOL CancelImmHotkey(SYSTHREAD *psfn, HWND hwnd, IMM32HOTKEY *pHotKey)
|
|
{
|
|
UINT uMsg;
|
|
|
|
if (pHotKey->uModifiers & TF_MOD_ON_KEYUP)
|
|
uMsg = WM_KEYUP;
|
|
else
|
|
uMsg = WM_KEYDOWN;
|
|
|
|
PostMessage(hwnd, uMsg, (WPARAM)pHotKey->uVKey, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// void ToggleCHImeNoIme
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL ToggleCHImeNoIme(SYSTHREAD *psfn, LANGID langidCur, LANGID langid)
|
|
{
|
|
int i;
|
|
LANGID langidPrev;
|
|
GUID guidPrevProfile;
|
|
HKL hklPrev;
|
|
BOOL fCiceroClient= FALSE;
|
|
|
|
CAssemblyList *pAsmList;
|
|
CAssembly *pAsm;
|
|
ASSEMBLYITEM *pItem;
|
|
|
|
pAsmList = EnsureAssemblyList(psfn);
|
|
if (!pAsmList)
|
|
return FALSE;
|
|
|
|
langidPrev = psfn->langidPrevForCHHotkey;
|
|
guidPrevProfile = psfn->guidPrevProfileForCHHotkey;
|
|
hklPrev = psfn->hklPrevForCHHotkey;
|
|
psfn->guidPrevProfileForCHHotkey = GUID_NULL;
|
|
psfn->langidPrevForCHHotkey = 0;
|
|
psfn->hklPrevForCHHotkey = 0;
|
|
|
|
pAsm = pAsmList->FindAssemblyByLangId(langidCur);
|
|
if (!pAsm)
|
|
return FALSE;
|
|
|
|
if (psfn->ptim && psfn->ptim->_GetFocusDocInputMgr())
|
|
fCiceroClient = TRUE;
|
|
|
|
if (fCiceroClient)
|
|
{
|
|
pItem = pAsm->FindActiveKeyboardItem();
|
|
if (!pItem)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
pItem = pAsm->FindKeyboardLayoutItem(GetKeyboardLayout(0));
|
|
if (!pItem)
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsEqualGUID(pItem->clsid, GUID_NULL) || IsPureIMEHKL(pItem->hkl))
|
|
{
|
|
//
|
|
// Not the current active keyboard item is TIP or IME.
|
|
//
|
|
|
|
psfn->guidPrevProfileForCHHotkey = pItem->guidProfile;
|
|
psfn->hklPrevForCHHotkey = pItem->hkl;
|
|
psfn->langidPrevForCHHotkey = langid;
|
|
|
|
for (i = 0; i < pAsm->Count(); i++)
|
|
{
|
|
pItem = pAsm->GetItem(i);
|
|
if (!pItem)
|
|
continue;
|
|
|
|
if (IsEqualGUID(pItem->catid, GUID_TFCAT_TIP_KEYBOARD) &&
|
|
IsEqualGUID(pItem->clsid, GUID_NULL) &&
|
|
!IsPureIMEHKL(pItem->hkl))
|
|
{
|
|
ActivateAssemblyItem(psfn,
|
|
langidCur,
|
|
pItem,
|
|
AAIF_CHANGEDEFAULT);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
PostThreadMessage(GetCurrentThreadId(),
|
|
g_msgPrivate,
|
|
TFPRIV_ACTIVATELANG,
|
|
0x0409);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
BOOL fActivateFirstIME = FALSE;
|
|
BOOL fCheckItem = FALSE;
|
|
|
|
if (langidCur != langid)
|
|
ActivateAssembly(langid, ACTASM_NONE);
|
|
|
|
pAsm = pAsmList->FindAssemblyByLangId(langid);
|
|
if (!pAsm)
|
|
return FALSE;
|
|
|
|
if ((langidPrev == langid) &&
|
|
(!IsEqualGUID(guidPrevProfile, GUID_NULL) ||
|
|
IsPureIMEHKL(hklPrev)))
|
|
{
|
|
fCheckItem = TRUE;
|
|
}
|
|
else if (!langidPrev &&
|
|
IsEqualGUID(guidPrevProfile, GUID_NULL) &&
|
|
!hklPrev)
|
|
{
|
|
fActivateFirstIME = TRUE;
|
|
}
|
|
|
|
if (fActivateFirstIME)
|
|
{
|
|
return ActivateNextKeyTip(FALSE);
|
|
}
|
|
|
|
for (i = 0; i < pAsm->Count(); i++)
|
|
{
|
|
pItem = pAsm->GetItem(i);
|
|
if (!pItem)
|
|
continue;
|
|
|
|
if (!IsEqualGUID(pItem->catid, GUID_TFCAT_TIP_KEYBOARD))
|
|
continue;
|
|
|
|
//
|
|
// Bug#494617 - Check the item is enabled or not.
|
|
//
|
|
if (!pItem->fEnabled)
|
|
continue;
|
|
|
|
if (fCheckItem)
|
|
{
|
|
if ((!IsEqualGUID(guidPrevProfile, GUID_NULL) &&
|
|
IsEqualGUID(pItem->guidProfile, guidPrevProfile)) ||
|
|
(IsPureIMEHKL(hklPrev) && (hklPrev == pItem->hkl)))
|
|
{
|
|
ActivateAssemblyItem(psfn,
|
|
langid,
|
|
pItem,
|
|
AAIF_CHANGEDEFAULT);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (!IsEqualGUID(pItem->guidProfile, GUID_NULL))
|
|
{
|
|
ActivateAssemblyItem(psfn,
|
|
langid,
|
|
pItem,
|
|
AAIF_CHANGEDEFAULT);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CAsyncOpenKeyboardTip
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CAsyncOpenKeyboardTip : public CAsyncQueueItem
|
|
{
|
|
public:
|
|
CAsyncOpenKeyboardTip(CThreadInputMgr *ptim, BOOL fSync) : CAsyncQueueItem(fSync)
|
|
{
|
|
_ptim = ptim;
|
|
}
|
|
|
|
HRESULT DoDispatch(CInputContext *pic)
|
|
{
|
|
if (!_ptim)
|
|
{
|
|
Assert(0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
MySetCompartmentDWORD(g_gaSystem,
|
|
_ptim,
|
|
GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
|
|
TRUE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
CThreadInputMgr *_ptim;
|
|
};
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// void ToggleJImeNoIme
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL ToggleJImeNoIme(SYSTHREAD *psfn)
|
|
{
|
|
int i;
|
|
CAssemblyList *pAsmList;
|
|
CAssembly *pAsm;
|
|
ASSEMBLYITEM *pItem;
|
|
|
|
if (!psfn)
|
|
return FALSE;
|
|
|
|
if (!psfn->ptim)
|
|
return FALSE;
|
|
|
|
//
|
|
// if there is no Focus DIM, we don't have to do this.
|
|
//
|
|
if (!psfn->ptim->_GetFocusDocInputMgr())
|
|
return FALSE;
|
|
|
|
pAsmList = EnsureAssemblyList(psfn);
|
|
if (!pAsmList)
|
|
return FALSE;
|
|
|
|
pAsm = pAsmList->FindAssemblyByLangId(0x0411);
|
|
if (!pAsm)
|
|
return FALSE;
|
|
|
|
pItem = pAsm->FindActiveKeyboardItem();
|
|
if (!pItem)
|
|
return FALSE;
|
|
|
|
if (!IsEqualGUID(pItem->clsid, GUID_NULL))
|
|
return FALSE;
|
|
|
|
if (IsPureIMEHKL(pItem->hkl))
|
|
return FALSE;
|
|
|
|
|
|
ASSEMBLYITEM *pItemNew = NULL;
|
|
for (i = 0; i < pAsm->Count(); i++)
|
|
{
|
|
ASSEMBLYITEM *pItemTemp;
|
|
pItemTemp = pAsm->GetItem(i);
|
|
if (!pItemTemp)
|
|
continue;
|
|
|
|
if (pItemTemp == pItem)
|
|
continue;
|
|
|
|
if (!IsEqualGUID(pItemTemp->catid, GUID_TFCAT_TIP_KEYBOARD))
|
|
continue;
|
|
|
|
if (!IsEqualGUID(pItemTemp->clsid, GUID_NULL))
|
|
{
|
|
pItemNew = pItemTemp;
|
|
break;
|
|
}
|
|
|
|
if (IsPureIMEHKL(pItemTemp->hkl))
|
|
{
|
|
pItemNew = pItemTemp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pItemNew)
|
|
{
|
|
ActivateAssemblyItem(psfn,
|
|
0x0411,
|
|
pItemNew,
|
|
AAIF_CHANGEDEFAULT);
|
|
|
|
|
|
//
|
|
// Open Keyboard TIP.
|
|
//
|
|
CInputContext *pic = NULL;
|
|
pic = psfn->ptim->_GetFocusDocInputMgr()->_GetIC(0);
|
|
if (pic)
|
|
{
|
|
CAsyncOpenKeyboardTip *pAsyncOpenKeyboardTip;
|
|
pAsyncOpenKeyboardTip = new CAsyncOpenKeyboardTip(psfn->ptim, FALSE);
|
|
if (pAsyncOpenKeyboardTip)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ((pic->_QueueItem(pAsyncOpenKeyboardTip->GetItem(), FALSE, &hr) != S_OK) || FAILED(hr))
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
pAsyncOpenKeyboardTip->_Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CheckImm32HotKey
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CheckImm32HotKey(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HKL hKL;
|
|
SYSTHREAD *psfn = GetSYSTHREAD();
|
|
IMM32HOTKEY *pHotKey;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (psfn == NULL)
|
|
return FALSE;
|
|
|
|
//
|
|
// If there is no tim, let system change hKL.
|
|
//
|
|
if (!psfn->ptim)
|
|
return FALSE;
|
|
|
|
//
|
|
// If there is focus dim, we need to handle it.
|
|
// If there is no focus dim, but msctfime can eat the hotkey,
|
|
// we need to do this instead of system.
|
|
//
|
|
if (!psfn->ptim->_GetFocusDocInputMgr())
|
|
{
|
|
if (!CtfImmIsCiceroStartedInThread())
|
|
return FALSE;
|
|
}
|
|
|
|
hKL = GetKeyboardLayout(NULL);
|
|
|
|
pHotKey = IsImmHotkey((UINT)wParam & 0xff,
|
|
(HIWORD(lParam) & KF_UP) ? TRUE : FALSE,
|
|
g_uModifiers,
|
|
(LANGID)LOWORD((UINT_PTR)hKL));
|
|
|
|
if (!pHotKey)
|
|
{
|
|
//
|
|
// Chinese IME-NONIME toggle Hack for NT.
|
|
//
|
|
// On NT, we're using non IME as a dummy hKL of CH-Tips.
|
|
// we need to simulate HotKey.
|
|
//
|
|
LANGID langidPrev = psfn->langidPrev;
|
|
|
|
//
|
|
// If the Chinese IME-NONIME toggle has never been done in this thread
|
|
// and the current thread locale is Chinese, let's try to do
|
|
// IME-NONIME toggle.
|
|
//
|
|
if ((langidPrev != CHTLANGID) &&
|
|
(langidPrev != CHSLANGID) &&
|
|
!psfn->langidPrevForCHHotkey)
|
|
{
|
|
LANGID langidThread = LANGIDFROMLCID(GetThreadLocale());
|
|
if ((langidThread == CHTLANGID) || (langidThread == CHSLANGID))
|
|
langidPrev = langidThread;
|
|
}
|
|
|
|
if (IsOnNT() &&
|
|
((langidPrev == CHTLANGID) || (langidPrev == CHSLANGID)))
|
|
{
|
|
pHotKey = IsImmHotkey((UINT)wParam & 0xff,
|
|
(HIWORD(lParam) & KF_UP) ? TRUE : FALSE,
|
|
g_uModifiers,
|
|
langidPrev);
|
|
|
|
if (pHotKey)
|
|
{
|
|
//
|
|
// if it is a IME-NONIME toggle hotkey
|
|
// we need to simulate it.
|
|
//
|
|
if ((pHotKey->dwId == IME_CHOTKEY_IME_NONIME_TOGGLE) ||
|
|
(pHotKey->dwId == IME_THOTKEY_IME_NONIME_TOGGLE))
|
|
{
|
|
bRet = ToggleCHImeNoIme(psfn,
|
|
LANGIDFROMHKL(hKL),
|
|
langidPrev);
|
|
|
|
//
|
|
// On CUAS, Imm32's Hotkey is simulated in ImmProcessKey
|
|
// So this function is called there.
|
|
// We don't need this Toggle status hack.
|
|
//
|
|
if (!CtfImmIsCiceroStartedInThread())
|
|
psfn->bInImeNoImeToggle = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// remove all WM_INPUTLANGCHANGEREQUEST message.
|
|
//
|
|
// sometimes, this can not catch the IMM32's language change. We
|
|
// fallback hack for them in default.cpp's WM_INPUTLANGCHANGEREQUEST
|
|
// handler. Check IsInImmHotKeyStatus() and CancelImmHotkey().
|
|
//
|
|
MSG msg;
|
|
ULONG ulQuitCode;
|
|
BOOL fQuitReceived = FALSE;
|
|
|
|
psfn->fRemovingInputLangChangeReq = TRUE;
|
|
|
|
while(PeekMessage(&msg, NULL,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
WM_INPUTLANGCHANGEREQUEST,
|
|
PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
ulQuitCode = (ULONG)(msg.wParam);
|
|
fQuitReceived = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fQuitReceived)
|
|
PostQuitMessage(ulQuitCode);
|
|
|
|
psfn->fRemovingInputLangChangeReq = FALSE;
|
|
|
|
//
|
|
// Chinese IME-NONIME toggle Hack for NT.
|
|
//
|
|
// On NT, we're using non IME as a dummy hKL of CH-Tips.
|
|
// we need to simulate HotKey.
|
|
//
|
|
if (IsOnNT() && !psfn->bInImeNoImeToggle)
|
|
{
|
|
if ((pHotKey->dwId == IME_CHOTKEY_IME_NONIME_TOGGLE) ||
|
|
(pHotKey->dwId == IME_THOTKEY_IME_NONIME_TOGGLE))
|
|
{
|
|
bRet = ToggleCHImeNoIme(psfn, LANGIDFROMHKL(hKL), LANGIDFROMHKL(hKL));
|
|
}
|
|
}
|
|
psfn->bInImeNoImeToggle = FALSE;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CAsyncProcessDBEKeyQueueItem
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CAsyncProcessDBEKeyQueueItem : public CAsyncQueueItem
|
|
{
|
|
public:
|
|
CAsyncProcessDBEKeyQueueItem(CThreadInputMgr *ptim, WPARAM wParam, LPARAM lParam, BOOL fTest, BOOL fSync) : CAsyncQueueItem(fSync)
|
|
{
|
|
_wParam = wParam;
|
|
_lParam = lParam;
|
|
_fTest = fTest;
|
|
_ptim = ptim;
|
|
}
|
|
|
|
HRESULT DoDispatch(CInputContext *pic)
|
|
{
|
|
if (!_ptim)
|
|
{
|
|
Assert(0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
BOOL fEaten;
|
|
|
|
return _ptim->_KeyStroke((_lParam & 0x80000000) ? KS_UP : KS_DOWN,
|
|
_wParam,
|
|
_lParam,
|
|
&fEaten,
|
|
_fTest,
|
|
TF_KEY_INTERNAL);
|
|
|
|
}
|
|
|
|
private:
|
|
WPARAM _wParam;
|
|
LPARAM _lParam;
|
|
BOOL _fTest;
|
|
CThreadInputMgr *_ptim;
|
|
};
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// HandleDBEKeys
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL HandleDBEKeys(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
SYSTHREAD *psfn = GetSYSTHREAD();
|
|
CThreadInputMgr *ptim;
|
|
LANGID langid;
|
|
|
|
//
|
|
// only Japanese layout has DBE keys.
|
|
//
|
|
langid = GetCurrentAssemblyLangId(psfn);
|
|
if (langid != 0x0411)
|
|
return FALSE;
|
|
|
|
//
|
|
// no need to forward this on non Cicero apps.
|
|
//
|
|
if (!(ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn)))
|
|
return FALSE;
|
|
|
|
if (!ptim->_GetFocusDocInputMgr())
|
|
return FALSE;
|
|
|
|
//
|
|
// if ALT is not held, app can forward this to TIPs.
|
|
//
|
|
if (!(g_uModifiers & TF_MOD_ALT))
|
|
return FALSE;
|
|
|
|
UINT uVKey = (UINT)wParam & 0xff;
|
|
BOOL fRet = FALSE;
|
|
|
|
switch (uVKey)
|
|
{
|
|
case VK_DBE_ALPHANUMERIC:
|
|
case VK_DBE_KATAKANA:
|
|
case VK_DBE_HIRAGANA:
|
|
case VK_DBE_SBCSCHAR:
|
|
case VK_DBE_DBCSCHAR:
|
|
case VK_DBE_ROMAN:
|
|
case VK_DBE_NOROMAN:
|
|
case VK_DBE_CODEINPUT:
|
|
case VK_DBE_NOCODEINPUT:
|
|
case VK_DBE_ENTERWORDREGISTERMODE:
|
|
case VK_DBE_ENTERIMECONFIGMODE:
|
|
case VK_DBE_ENTERDLGCONVERSIONMODE:
|
|
case VK_DBE_DETERMINESTRING:
|
|
case VK_DBE_FLUSHSTRING:
|
|
case VK_CONVERT:
|
|
case VK_KANJI:
|
|
|
|
|
|
//
|
|
// Issue:
|
|
//
|
|
// We don't know which IC in the focus DIM will handle the hotkey yet.
|
|
// because the selection is changed by the application so we need to get ec
|
|
// to update the current selection pos. We do call GetSelection
|
|
// inside the root IC's lock. So it might be failed if hotkey's target
|
|
// is TOP IC.
|
|
//
|
|
CInputContext *pic = ptim->_GetFocusDocInputMgr()->_GetIC(0);
|
|
|
|
if (!pic)
|
|
return FALSE;
|
|
|
|
CAsyncProcessDBEKeyQueueItem *pAsyncProcessDBEKeyQueueItem;
|
|
pAsyncProcessDBEKeyQueueItem = new CAsyncProcessDBEKeyQueueItem(ptim, wParam, lParam, FALSE, FALSE);
|
|
if (!pAsyncProcessDBEKeyQueueItem)
|
|
return FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
fRet = TRUE;
|
|
if ((pic->_QueueItem(pAsyncProcessDBEKeyQueueItem->GetItem(), FALSE, &hr) != S_OK) || FAILED(hr))
|
|
{
|
|
Assert(0);
|
|
fRet = FALSE;
|
|
}
|
|
|
|
pAsyncProcessDBEKeyQueueItem->_Release();
|
|
break;
|
|
}
|
|
|
|
return fRet;
|
|
}
|