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.
570 lines
16 KiB
570 lines
16 KiB
//
|
|
// ic.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "common.h"
|
|
#include "korimx.h"
|
|
#include "icpriv.h"
|
|
#include "ipointcic.h"
|
|
#include "cleanup.h"
|
|
#include "helpers.h"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnStartCleanupContext
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CKorIMX::OnStartCleanupContext()
|
|
{
|
|
// nb: a real tip, for performace, should skip input contexts it knows
|
|
// it doesn't need a lock and callback on. For instance, kimx only
|
|
// cares about ic's with ongoing compositions. We could remember which ic's
|
|
// have compositions, then return FALSE for all but the ic's with compositions.
|
|
// It is really bad perf to have the library make a lock request for every
|
|
// ic!
|
|
m_fPendingCleanup = fTrue;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnEndCleanupContext
|
|
//
|
|
// Called after all ic's with cleanup sinks have been called.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CKorIMX::OnEndCleanupContext()
|
|
{
|
|
// our profile just changed or we are about to be deactivated
|
|
// in either case we don't have to worry about anything interrupting ic cleanup
|
|
// callbacks anymore
|
|
m_fPendingCleanup = fFalse;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnCleanupContext
|
|
//
|
|
// This method is a callback for the library helper CleanupAllContexts.
|
|
// We have to be very careful here because we may be called _after_ this tip
|
|
// has been deactivated, if the app couldn't grant a lock right away.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CKorIMX::OnCleanupContext(TfEditCookie ecWrite, ITfContext *pic)
|
|
{
|
|
// all kimx cares about is finalizing compositions
|
|
CleanupAllCompositions(ecWrite, pic, CLSID_KorIMX, _CleanupCompositionsCallback, this);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ITfActiveLanguageProfileNotifySink::OnActivated
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
STDAPI CKorIMX::OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL bActivated)
|
|
{
|
|
// our profile just changed or we are about to be deactivated
|
|
// in either case we don't have to worry about anything interrupting ic cleanup
|
|
// callbacks anymore
|
|
m_fPendingCleanup = fFalse;
|
|
|
|
//if (IsSoftKbdEnabled())
|
|
// OnActivatedSoftKbd(bActivated);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _CleanupCompositionsCallback
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
void CKorIMX::_CleanupCompositionsCallback(TfEditCookie ecWrite, ITfRange *rangeComposition, void *pvPrivate)
|
|
{
|
|
CKorIMX* pKorTip = (CKorIMX*)pvPrivate;
|
|
ITfContext *pic;
|
|
|
|
if (rangeComposition->GetContext(&pic) != S_OK)
|
|
return;
|
|
|
|
if (pKorTip)
|
|
pKorTip->MakeResultString(ecWrite, pic, rangeComposition);
|
|
// _FinalizeRange(ecWrite, pic, rangeComposition);
|
|
|
|
pic->Release();
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
CKorIMX::_InitICPriv
|
|
|
|
Init IC private data
|
|
---------------------------------------------------------------------------*/
|
|
HRESULT CKorIMX::_InitICPriv(ITfContext *pic)
|
|
{
|
|
CICPriv *picp;
|
|
CCompartmentEventSink* pCompartmentSink;
|
|
ITfSourceSingle *pSourceSingle;
|
|
TF_STATUS dcs;
|
|
|
|
// Check pic
|
|
if (pic == NULL)
|
|
return E_FAIL;
|
|
//
|
|
// check enable/disable (Candidate stack)
|
|
//
|
|
if (IsDisabledIC(pic) || IsEmptyIC(pic))
|
|
return S_OK;
|
|
|
|
// Initialize Private data members
|
|
if ((picp = GetInputContextPriv(pic)) == NULL)
|
|
{
|
|
IUnknown *punk;
|
|
|
|
if ((picp = new CICPriv) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// IC
|
|
picp->RegisterIC(pic);
|
|
// IMX
|
|
picp->RegisterIMX(this);
|
|
|
|
if (picp->IsInitializedIPoint() == FALSE)
|
|
{
|
|
//struct _GUID RefID={0}; // dummy id
|
|
IImeIPoint1 *pIP;
|
|
LPCIPointCic pCIPointCic = NULL;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Create IImeIPoint1 instance
|
|
//////////////////////////////////////////////////////////////////////
|
|
if ((pCIPointCic = new CIPointCic(this)) == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// This increments the reference count
|
|
if (FAILED(pCIPointCic->QueryInterface(IID_IImeIPoint1, (VOID **)&pIP)))
|
|
{
|
|
delete pCIPointCic;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// initialize kernel
|
|
pCIPointCic->Initialize(pic);
|
|
|
|
// register ic depended objects.
|
|
picp->RegisterIPoint(pIP);
|
|
picp->InitializedIPoint(fTrue);
|
|
}
|
|
|
|
//
|
|
// text edit sink/edit transaction sink
|
|
//
|
|
ITfSource *pSource;
|
|
DWORD dwCookieForTextEditSink = 0;
|
|
//DWORD dwCookieForTransactionSink = 0;
|
|
if (pic->QueryInterface(IID_ITfSource, (void **)&pSource ) == S_OK)
|
|
{
|
|
pSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &dwCookieForTextEditSink);
|
|
//pSource->AdviseSink(IID_ITfEditTransactionSink, (ITfEditTransactionSink *)this, &dwCookieForTransactionSink);
|
|
|
|
pSource->Release();
|
|
|
|
picp->RegisterCookieForTextEditSink(dwCookieForTextEditSink);
|
|
//picp->RegisterCookieForTransactionSink(dwCookieForTransactionSink);
|
|
}
|
|
|
|
// compartment event sink
|
|
if ((pCompartmentSink = new CCompartmentEventSink(_CompEventSinkCallback, picp)) != NULL )
|
|
{
|
|
picp->RegisterCompartmentEventSink(pCompartmentSink);
|
|
|
|
// On/Off - compartment
|
|
pCompartmentSink->_Advise(GetTIM(), GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, FALSE);
|
|
|
|
// Conversion mode - compartment
|
|
pCompartmentSink->_Advise(GetTIM(), GUID_COMPARTMENT_KORIMX_CONVMODE, FALSE);
|
|
|
|
// SoftKeyboard Open/Close
|
|
pCompartmentSink->_Advise(GetTIM(), GUID_COMPARTMENT_KOR_SOFTKBD_OPENCLOSE, FALSE);
|
|
|
|
// Soft Keyboard layout change
|
|
pCompartmentSink->_Advise(GetTIM(), GUID_COMPARTMENT_SOFTKBD_KBDLAYOUT, FALSE);
|
|
}
|
|
|
|
Assert(pCompartmentSink != NULL);
|
|
|
|
if (pic->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle) == S_OK)
|
|
{
|
|
// setup a cleanup callback
|
|
// nb: a real tip doesn't need to be this aggressive, for instance
|
|
// kimx probably only needs this sink on the focus ic.
|
|
pSourceSingle->AdviseSingleSink(GetTID(), IID_ITfCleanupContextSink, (ITfCleanupContextSink *)this);
|
|
pSourceSingle->Release();
|
|
}
|
|
|
|
// Initialized kernel
|
|
picp->Initialized(fTrue);
|
|
|
|
// Set to compartment GUID
|
|
GetCompartmentUnknown(pic, GUID_IC_PRIVATE, &punk);
|
|
if (!punk)
|
|
{
|
|
SetCompartmentUnknown(GetTID(), pic, GUID_IC_PRIVATE, picp);
|
|
picp->Release();
|
|
}
|
|
else
|
|
{
|
|
// Praive data already exist.
|
|
punk->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
}
|
|
|
|
// Set AIMM1.2
|
|
picp->SetAIMM(fFalse);
|
|
pic->GetStatus(&dcs);
|
|
|
|
if (dcs.dwStaticFlags & TF_SS_TRANSITORY)
|
|
picp->SetAIMM(fTrue);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
CKorIMX::_DeleteICPriv
|
|
|
|
Delete IC private data
|
|
---------------------------------------------------------------------------*/
|
|
HRESULT CKorIMX::_DeleteICPriv(ITfContext *pic)
|
|
{
|
|
CICPriv *picp;
|
|
IUnknown *punk;
|
|
CCompartmentEventSink* pCompartmentSink;
|
|
ITfSource *pSource;
|
|
ITfSourceSingle *pSourceSingle;
|
|
|
|
if (pic == NULL)
|
|
return E_FAIL;
|
|
|
|
picp = GetInputContextPriv(pic);
|
|
|
|
#ifdef DBG
|
|
Assert(IsDisabledIC(pic) || picp != NULL );
|
|
#endif
|
|
|
|
if (picp == NULL)
|
|
return S_FALSE;
|
|
|
|
//
|
|
// Compartment event sink
|
|
//
|
|
pCompartmentSink = picp->GetCompartmentEventSink();
|
|
if (pCompartmentSink)
|
|
{
|
|
pCompartmentSink->_Unadvise();
|
|
pCompartmentSink->Release();
|
|
}
|
|
|
|
//
|
|
// text edit sink
|
|
//
|
|
if (pic->QueryInterface( IID_ITfSource, (void **)&pSource) == S_OK)
|
|
{
|
|
pSource->UnadviseSink(picp->GetCookieForTextEditSink());
|
|
//pSource->UnadviseSink(picp->GetCookieForTransactionSink());
|
|
pSource->Release();
|
|
}
|
|
picp->RegisterCookieForTextEditSink(0);
|
|
|
|
// Clear ITfCleanupContextSink
|
|
if (pic->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle) == S_OK)
|
|
{
|
|
pSourceSingle->UnadviseSingleSink(GetTID(), IID_ITfCleanupContextSink);
|
|
pSourceSingle->Release();
|
|
}
|
|
|
|
// UnInitialize IPoint
|
|
IImeIPoint1 *pIP = GetIPoint(pic);
|
|
// IImeIPoint
|
|
if (pIP)
|
|
{
|
|
pIP->Release();
|
|
}
|
|
picp->RegisterIPoint(NULL);
|
|
picp->InitializedIPoint(fFalse); // reset
|
|
|
|
// Reset init flag
|
|
picp->Initialized(fFalse);
|
|
|
|
// We MUST clear out the private data before cicero is free
|
|
// to release the ic
|
|
GetCompartmentUnknown(pic, GUID_IC_PRIVATE, &punk);
|
|
if (punk)
|
|
punk->Release();
|
|
ClearCompartment(GetTID(), pic, GUID_IC_PRIVATE, fFalse);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
CKorIMX::GetInputContextPriv
|
|
|
|
Get IC private data
|
|
---------------------------------------------------------------------------*/
|
|
CICPriv *CKorIMX::GetInputContextPriv(ITfContext *pic)
|
|
{
|
|
IUnknown *punk;
|
|
|
|
if (pic == NULL)
|
|
return NULL;
|
|
|
|
GetCompartmentUnknown(pic, GUID_IC_PRIVATE, &punk);
|
|
|
|
if (punk)
|
|
punk->Release();
|
|
|
|
return (CICPriv *)punk;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
CKorIMX::OnICChange
|
|
---------------------------------------------------------------------------*/
|
|
void CKorIMX::OnFocusChange(ITfContext *pic, BOOL fActivate)
|
|
{
|
|
BOOL fReleaseIC = fFalse;
|
|
BOOL fDisabledIC = IsDisabledIC(pic);
|
|
BOOL fEmptyIC = IsEmptyIC(pic);
|
|
BOOL fCandidateIC = IsCandidateIC(pic);
|
|
|
|
BOOL fInEditSession;
|
|
HRESULT hr;
|
|
|
|
if (fEmptyIC)
|
|
{
|
|
if (m_pToolBar)
|
|
m_pToolBar->SetCurrentIC(NULL);
|
|
|
|
if (IsSoftKbdEnabled())
|
|
SoftKbdOnThreadFocusChange(fFalse);
|
|
return; // do nothing
|
|
}
|
|
|
|
if (fDisabledIC == fTrue && fCandidateIC == fFalse )
|
|
{
|
|
if (m_pToolBar)
|
|
m_pToolBar->SetCurrentIC(NULL);
|
|
|
|
if (IsSoftKbdEnabled())
|
|
SoftKbdOnThreadFocusChange(fFalse);
|
|
return; // do nothing
|
|
}
|
|
|
|
// O10 #278261: Restore Soft Keyboard winfow after switched from Empty Context to normal IC.
|
|
if (IsSoftKbdEnabled())
|
|
SoftKbdOnThreadFocusChange(fActivate);
|
|
|
|
// Notify focus change to IME Pad svr
|
|
if (m_pPadCore)
|
|
{
|
|
m_pPadCore->SetFocus(fActivate);
|
|
}
|
|
|
|
// Terminate
|
|
if (fActivate == fFalse)
|
|
{
|
|
if (!fDisabledIC && pic && GetIPComposition(pic))
|
|
{
|
|
if (SUCCEEDED(pic->InWriteSession(GetTID(), &fInEditSession)) && !fInEditSession)
|
|
{
|
|
CEditSession2 *pes;
|
|
ESSTRUCT ess;
|
|
|
|
ESStructInit(&ess, ESCB_COMPLETE);
|
|
|
|
if ((pes = new CEditSession2(pic, this, &ess, _EditSessionCallback2)))
|
|
{
|
|
pes->Invoke(ES2_READWRITE | ES2_SYNC, &hr);
|
|
pes->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close cand UI if opened.
|
|
if (m_fCandUIOpen)
|
|
CloseCandidateUIProc();
|
|
|
|
return;
|
|
}
|
|
|
|
// fActivate == TRUE
|
|
if (fDisabledIC)
|
|
{
|
|
pic = GetRootIC();
|
|
fReleaseIC = fTrue;
|
|
}
|
|
|
|
if (m_pToolBar)
|
|
m_pToolBar->SetCurrentIC(pic);
|
|
|
|
if (m_pPadCore)
|
|
{
|
|
IImeIPoint1* pIP = GetIPoint(pic);
|
|
m_pPadCore->SetIPoint(pIP);
|
|
}
|
|
|
|
if (pic && !fDisabledIC)
|
|
{
|
|
CICPriv *picp;
|
|
|
|
// Sync GUID_COMPARTMENT_KEYBOARD_OPENCLOSE with GUID_COMPARTMENT_KORIMX_CONVMODE
|
|
// This for Word now but looks not good since we don't sync On/Off status with conv mode.
|
|
// In future Apps should set GUID_MODEBIAS_HANGUL on boot and should be Korean specific code.
|
|
if (GetConvMode(pic) == TIP_NULL_CONV_MODE) // if this is first boot.
|
|
{
|
|
if (IsOn(pic))
|
|
SetCompartmentDWORD(GetTID(), GetTIM(), GUID_COMPARTMENT_KORIMX_CONVMODE, TIP_HANGUL_MODE, fFalse);
|
|
else
|
|
SetCompartmentDWORD(GetTID(), GetTIM(), GUID_COMPARTMENT_KORIMX_CONVMODE, TIP_ALPHANUMERIC_MODE, fFalse);
|
|
}
|
|
else
|
|
{
|
|
// Reset ModeBias
|
|
picp = GetInputContextPriv(pic);
|
|
if (picp)
|
|
picp->SetModeBias(NULL);
|
|
}
|
|
|
|
// Modebias check here
|
|
CheckModeBias(pic);
|
|
}
|
|
|
|
if (fReleaseIC)
|
|
SafeRelease(pic);
|
|
}
|
|
|
|
|
|
// REVIEW::
|
|
// tmp solution
|
|
ITfContext* CKorIMX::GetRootIC(ITfDocumentMgr* pDim)
|
|
{
|
|
if (pDim == NULL)
|
|
{
|
|
pDim = m_pCurrentDim;
|
|
if( pDim == NULL )
|
|
return NULL;
|
|
}
|
|
|
|
IEnumTfContexts *pEnumIc = NULL;
|
|
if (SUCCEEDED(pDim->EnumContexts(&pEnumIc)))
|
|
{
|
|
ITfContext *pic = NULL;
|
|
while (pEnumIc->Next(1, &pic, NULL) == S_OK)
|
|
break;
|
|
pEnumIc->Release();
|
|
|
|
return pic;
|
|
}
|
|
|
|
return NULL; // error case
|
|
}
|
|
|
|
IImeIPoint1* CKorIMX::GetIPoint(ITfContext *pic)
|
|
{
|
|
CICPriv *picp;
|
|
|
|
if (pic == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
picp = GetInputContextPriv(pic);
|
|
|
|
if (picp)
|
|
{
|
|
return picp->GetIPoint();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
BOOL CKorIMX::IsDisabledIC(ITfContext *pic)
|
|
{
|
|
DWORD dwFlag;
|
|
|
|
if (pic == NULL)
|
|
return fFalse;
|
|
|
|
GetCompartmentDWORD(pic, GUID_COMPARTMENT_KEYBOARD_DISABLED, &dwFlag, fFalse);
|
|
|
|
if (dwFlag)
|
|
return fTrue; // do not create any kernel related info into ic.
|
|
else
|
|
return fFalse;
|
|
}
|
|
|
|
/* I S E M P T Y I C */
|
|
BOOL CKorIMX::IsEmptyIC(ITfContext *pic)
|
|
{
|
|
DWORD dwFlag;
|
|
|
|
if (pic == NULL)
|
|
return fFalse;
|
|
|
|
GetCompartmentDWORD(pic, GUID_COMPARTMENT_EMPTYCONTEXT, &dwFlag, fFalse);
|
|
|
|
if (dwFlag)
|
|
return fTrue; // do not create any kernel related info into ic.
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
/* I S C A N D I D A T E I C */
|
|
/*------------------------------------------------------------------------------
|
|
|
|
Check if the input context is one of candidate UI
|
|
|
|
------------------------------------------------------------------------------*/
|
|
BOOL CKorIMX::IsCandidateIC(ITfContext *pic)
|
|
{
|
|
DWORD dwFlag;
|
|
|
|
if (pic == NULL)
|
|
return fFalse;
|
|
|
|
GetCompartmentDWORD( pic, GUID_COMPARTMENT_KEYBOARD_DISABLED, &dwFlag, fFalse);
|
|
|
|
if (dwFlag)
|
|
return fTrue; // do not create any kernel related info into ic.
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
HWND CKorIMX::GetAppWnd(ITfContext *pic)
|
|
{
|
|
ITfContextView* pView;
|
|
HWND hwndApp = 0;
|
|
|
|
if (pic == NULL)
|
|
return 0;
|
|
|
|
pic->GetActiveView(&pView);
|
|
if (pView == NULL)
|
|
return 0;
|
|
|
|
pView->GetWnd(&hwndApp);
|
|
pView->Release();
|
|
|
|
return hwndApp;
|
|
|
|
}
|