1 /* 2 * PROJECT: ReactOS msctfime.ime 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Input Context of msctfime.ime 5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "msctfime.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime); 11 12 /*********************************************************************** 13 * CInputContextOwner 14 */ 15 16 /// @unimplemented 17 CInputContextOwner::CInputContextOwner(FN_IC_OWNER_CALLBACK fnCallback, LPVOID pCallbackPV) 18 { 19 m_dwCookie = -1; 20 m_fnCallback = fnCallback; 21 m_cRefs = 1; 22 m_pCallbackPV = pCallbackPV; 23 } 24 25 /// @implemented 26 CInputContextOwner::~CInputContextOwner() 27 { 28 } 29 30 /// @implemented 31 HRESULT CInputContextOwner::_Advise(IUnknown *pContext) 32 { 33 ITfSource *pSource = NULL; 34 35 m_pContext = NULL; 36 37 HRESULT hr = E_FAIL; 38 if (SUCCEEDED(m_pContext->QueryInterface(IID_ITfSource, (LPVOID*)&pSource)) && 39 SUCCEEDED(pSource->AdviseSink(IID_ITfContextOwner, 40 static_cast<ITfContextOwner*>(this), &m_dwCookie))) 41 { 42 m_pContext = pContext; 43 m_pContext->AddRef(); 44 hr = S_OK; 45 } 46 47 if (pSource) 48 pSource->Release(); 49 50 return hr; 51 } 52 53 /// @implemented 54 HRESULT CInputContextOwner::_Unadvise() 55 { 56 ITfSource *pSource = NULL; 57 58 HRESULT hr = E_FAIL; 59 if (m_pContext) 60 { 61 if (SUCCEEDED(m_pContext->QueryInterface(IID_ITfSource, (LPVOID*)&pSource)) && 62 SUCCEEDED(pSource->UnadviseSink(m_dwCookie))) 63 { 64 hr = S_OK; 65 } 66 } 67 68 if (m_pContext) 69 { 70 m_pContext->Release(); 71 m_pContext = NULL; 72 } 73 74 if (pSource) 75 pSource->Release(); 76 77 return hr; 78 } 79 80 /// @implemented 81 STDMETHODIMP CInputContextOwner::QueryInterface(REFIID riid, LPVOID* ppvObj) 82 { 83 static const QITAB c_tab[] = 84 { 85 QITABENT(CInputContextOwner, ITfContextOwner), 86 QITABENT(CInputContextOwner, ITfMouseTrackerACP), 87 { NULL } 88 }; 89 return ::QISearch(this, c_tab, riid, ppvObj); 90 } 91 92 /// @implemented 93 STDMETHODIMP_(ULONG) CInputContextOwner::AddRef() 94 { 95 return ++m_cRefs; 96 } 97 98 /// @implemented 99 STDMETHODIMP_(ULONG) CInputContextOwner::Release() 100 { 101 if (--m_cRefs == 0) 102 { 103 delete this; 104 return 0; 105 } 106 return m_cRefs; 107 } 108 109 /// @unimplemented 110 STDMETHODIMP 111 CInputContextOwner::GetACPFromPoint( 112 const POINT *ptScreen, 113 DWORD dwFlags, 114 LONG *pacp) 115 { 116 return E_NOTIMPL; 117 } 118 119 /// @unimplemented 120 STDMETHODIMP 121 CInputContextOwner::GetTextExt( 122 LONG acpStart, 123 LONG acpEnd, 124 RECT *prc, 125 BOOL *pfClipped) 126 { 127 return E_NOTIMPL; 128 } 129 130 /// @implemented 131 STDMETHODIMP CInputContextOwner::GetScreenExt(RECT *prc) 132 { 133 return m_fnCallback(2, &prc, m_pCallbackPV); 134 } 135 136 /// @implemented 137 STDMETHODIMP CInputContextOwner::GetStatus(TF_STATUS *pdcs) 138 { 139 return m_fnCallback(6, &pdcs, m_pCallbackPV); 140 } 141 142 /// @unimplemented 143 STDMETHODIMP CInputContextOwner::GetWnd(HWND *phwnd) 144 { 145 return m_fnCallback(7, &phwnd, m_pCallbackPV); 146 } 147 148 /// @unimplemented 149 STDMETHODIMP CInputContextOwner::GetAttribute(REFGUID rguidAttribute, VARIANT *pvarValue) 150 { 151 return E_NOTIMPL; 152 } 153 154 struct MOUSE_SINK_ARGS 155 { 156 ITfRangeACP *range; 157 ITfMouseSink *pSink; 158 DWORD *pdwCookie; 159 }; 160 161 /// @implemented 162 STDMETHODIMP CInputContextOwner::AdviseMouseSink( 163 ITfRangeACP *range, 164 ITfMouseSink *pSink, 165 DWORD *pdwCookie) 166 { 167 MOUSE_SINK_ARGS args = { range, pSink, pdwCookie }; 168 return m_fnCallback(9, &args, m_pCallbackPV); 169 } 170 171 /// @implemented 172 STDMETHODIMP CInputContextOwner::UnadviseMouseSink(DWORD dwCookie) 173 { 174 return m_fnCallback(10, &dwCookie, m_pCallbackPV); 175 } 176 177 /*********************************************************************** 178 * CicInputContext 179 */ 180 181 /// @unimplemented 182 CicInputContext::CicInputContext( 183 _In_ TfClientId cliendId, 184 _Inout_ PCIC_LIBTHREAD pLibThread, 185 _In_ HIMC hIMC) 186 { 187 m_hIMC = hIMC; 188 m_dwQueryPos = 0; 189 m_cRefs = 1; 190 } 191 192 /// @implemented 193 STDMETHODIMP CicInputContext::QueryInterface(REFIID riid, LPVOID* ppvObj) 194 { 195 static const QITAB c_tab[] = 196 { 197 QITABENT(CicInputContext, ITfCleanupContextSink), 198 QITABENT(CicInputContext, ITfContextOwnerCompositionSink), 199 { NULL } 200 }; 201 return ::QISearch(this, c_tab, riid, ppvObj); 202 } 203 204 /// @implemented 205 STDMETHODIMP_(ULONG) CicInputContext::AddRef() 206 { 207 return ::InterlockedIncrement(&m_cRefs); 208 } 209 210 /// @implemented 211 STDMETHODIMP_(ULONG) CicInputContext::Release() 212 { 213 if (::InterlockedDecrement(&m_cRefs) == 0) 214 { 215 delete this; 216 return 0; 217 } 218 return m_cRefs; 219 } 220 221 /// @implemented 222 STDMETHODIMP 223 CicInputContext::OnStartComposition( 224 ITfCompositionView *pComposition, 225 BOOL *pfOk) 226 { 227 if ((m_cCompLocks <= 0) || m_bReconverting) 228 { 229 *pfOk = TRUE; 230 ++m_cCompLocks; 231 } 232 else 233 { 234 *pfOk = FALSE; 235 } 236 return S_OK; 237 } 238 239 /// @implemented 240 STDMETHODIMP 241 CicInputContext::OnUpdateComposition( 242 ITfCompositionView *pComposition, 243 ITfRange *pRangeNew) 244 { 245 return S_OK; 246 } 247 248 /// @implemented 249 STDMETHODIMP 250 CicInputContext::OnEndComposition( 251 ITfCompositionView *pComposition) 252 { 253 --m_cCompLocks; 254 return S_OK; 255 } 256 257 /// @implemented 258 HRESULT 259 CicInputContext::GetGuidAtom( 260 _Inout_ CicIMCLock& imcLock, 261 _In_ BYTE iAtom, 262 _Out_opt_ LPDWORD pdwGuidAtom) 263 { 264 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCompStr); 265 if (FAILED(imeContext.m_hr)) 266 return imeContext.m_hr; 267 268 HRESULT hr = E_FAIL; 269 if (iAtom < m_cGuidAtoms) 270 { 271 *pdwGuidAtom = m_adwGuidAtoms[iAtom]; 272 hr = S_OK; 273 } 274 275 return hr; 276 } 277 278 /// @unimplemented 279 HRESULT 280 CicInputContext::CreateInputContext( 281 _Inout_ ITfThreadMgr *pThreadMgr, 282 _Inout_ CicIMCLock& imcLock) 283 { 284 //FIXME 285 return E_NOTIMPL; 286 } 287 288 /// @unimplemented 289 HRESULT 290 CicInputContext::DestroyInputContext() 291 { 292 ITfSourceSingle *pSource = NULL; 293 294 if (m_pContext && m_pContext->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) 295 pSource->UnadviseSingleSink(m_clientId, IID_ITfCleanupContextSink); 296 297 //FIXME: m_dwUnknown5 298 299 if (m_pTextEventSink) 300 { 301 m_pTextEventSink->_Unadvise(); 302 m_pTextEventSink->Release(); 303 m_pTextEventSink = NULL; 304 } 305 306 if (m_pCompEventSink2) 307 { 308 m_pCompEventSink2->_Unadvise(); 309 m_pCompEventSink2->Release(); 310 m_pCompEventSink2 = NULL; 311 } 312 313 if (m_pCompEventSink1) 314 { 315 m_pCompEventSink1->_Unadvise(); 316 m_pCompEventSink1->Release(); 317 m_pCompEventSink1 = NULL; 318 } 319 320 if (m_pInputContextOwner) 321 { 322 m_pInputContextOwner->_Unadvise(); 323 m_pInputContextOwner->Release(); 324 m_pInputContextOwner = NULL; 325 } 326 327 if (m_pDocumentMgr) 328 m_pDocumentMgr->Pop(1); 329 330 if (m_pContext) 331 { 332 ClearCompartment(m_clientId, m_pContext, GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0); 333 m_pContext->Release(); 334 m_pContext = NULL; 335 } 336 337 if (m_pContextOwnerServices) 338 { 339 m_pContextOwnerServices->Release(); 340 m_pContextOwnerServices = NULL; 341 } 342 343 // FIXME: m_pICOwnerCallback 344 345 if (m_pDocumentMgr) 346 { 347 m_pDocumentMgr->Release(); 348 m_pDocumentMgr = NULL; 349 } 350 351 if (pSource) 352 pSource->Release(); 353 354 return S_OK; 355 } 356 357 /// @implemented 358 STDMETHODIMP 359 CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) 360 { 361 return S_OK; 362 } 363 364 /// @implemented 365 STDMETHODIMP 366 CicInputContext::OnCleanupContext( 367 _In_ TfEditCookie ecWrite, 368 _Inout_ ITfContext *pic) 369 { 370 TLS *pTLS = TLS::PeekTLS(); 371 if (!pTLS || !pTLS->m_pProfile) 372 return E_OUTOFMEMORY; 373 374 LANGID LangID; 375 pTLS->m_pProfile->GetLangId(&LangID); 376 377 IMEINFO IMEInfo; 378 WCHAR szPath[MAX_PATH]; 379 if (Inquire(&IMEInfo, szPath, 0, (HKL)UlongToHandle(LangID)) != S_OK) 380 return E_FAIL; 381 382 ITfProperty *pProp = NULL; 383 if (!(IMEInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)) 384 return S_OK; 385 386 HRESULT hr = pic->GetProperty(GUID_PROP_COMPOSING, &pProp); 387 if (FAILED(hr)) 388 return S_OK; 389 390 IEnumTfRanges *pRanges = NULL; 391 hr = pProp->EnumRanges(ecWrite, &pRanges, NULL); 392 if (SUCCEEDED(hr)) 393 { 394 ITfRange *pRange = NULL; 395 while (pRanges->Next(1, &pRange, 0) == S_OK) 396 { 397 VARIANT vari; 398 V_VT(&vari) = VT_EMPTY; 399 pProp->GetValue(ecWrite, pRange, &vari); 400 if (V_VT(&vari) == VT_I4) 401 { 402 if (V_I4(&vari)) 403 pProp->Clear(ecWrite, pRange); 404 } 405 pRange->Release(); 406 pRange = NULL; 407 } 408 pRanges->Release(); 409 } 410 pProp->Release(); 411 412 return S_OK; 413 } 414 415 /// @unimplemented 416 HRESULT CicInputContext::SetupDocFeedString(CicIMCLock& imcLock, UINT uCodePage) 417 { 418 return E_NOTIMPL; 419 } 420 421 /// @unimplemented 422 HRESULT CicInputContext::EscbClearDocFeedBuffer(CicIMCLock& imcLock, BOOL bFlag) 423 { 424 return E_NOTIMPL; 425 } 426 427 /// @unimplemented 428 HRESULT CicInputContext::EscbCompComplete(CicIMCLock& imcLock) 429 { 430 return E_NOTIMPL; 431 } 432 433 /// @unimplemented 434 HRESULT CicInputContext::EscbCompCancel(CicIMCLock& imcLock) 435 { 436 return E_NOTIMPL; 437 } 438 439 /// @unimplemented 440 HRESULT CicInputContext::OnSetCandidatePos(TLS *pTLS, CicIMCLock& imcLock) 441 { 442 return E_NOTIMPL; 443 } 444 445 /// @unimplemented 446 HRESULT CicInputContext::DelayedReconvertFuncCall(CicIMCLock& imcLock) 447 { 448 return E_NOTIMPL; 449 } 450 451 /// @unimplemented 452 HRESULT 453 CicInputContext::MsImeMouseHandler( 454 DWORD dwUnknown58, 455 DWORD dwUnknown59, 456 UINT keys, 457 CicIMCLock& imcLock) 458 { 459 return E_NOTIMPL; 460 } 461 462 /// @unimplemented 463 HRESULT 464 CicInputContext::SetupReconvertString( 465 CicIMCLock& imcLock, 466 ITfThreadMgr_P *pThreadMgr, 467 UINT uCodePage, 468 UINT uMsg, 469 BOOL bUndo) 470 { 471 return E_NOTIMPL; 472 } 473 474 void CicInputContext::ClearPrevCandidatePos() 475 { 476 m_dwUnknown8 = 0; 477 ZeroMemory(&m_rcCandidate1, sizeof(m_rcCandidate1)); 478 ZeroMemory(&m_CandForm, sizeof(m_CandForm)); 479 ZeroMemory(&m_rcCandidate2, sizeof(m_rcCandidate2)); 480 m_dwQueryPos = 0; 481 } 482 483 /// @unimplemented 484 HRESULT CicInputContext::EndReconvertString(CicIMCLock& imcLock) 485 { 486 return E_NOTIMPL; 487 } 488 489 /// @unimplemented 490 BOOL CicInputContext::SetCompositionString( 491 CicIMCLock& imcLock, 492 ITfThreadMgr_P *pThreadMgr, 493 DWORD dwIndex, 494 LPCVOID lpComp, 495 DWORD dwCompLen, 496 LPCVOID lpRead, 497 DWORD dwReadLen, 498 UINT uCodePage) 499 { 500 return FALSE; 501 } 502