1 /* 2 * PROJECT: ReactOS msctfime.ime 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: The sinks 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 /// @implemented 13 CCompartmentEventSink::CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData) 14 : m_array() 15 , m_cRefs(1) 16 , m_fnEventSink(fnEventSink) 17 , m_pUserData(pUserData) 18 { 19 } 20 21 /// @implemented 22 CCompartmentEventSink::~CCompartmentEventSink() 23 { 24 } 25 26 /// @implemented 27 STDMETHODIMP CCompartmentEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) 28 { 29 static const QITAB c_tab[] = 30 { 31 QITABENT(CCompartmentEventSink, ITfCompartmentEventSink), 32 { NULL } 33 }; 34 return ::QISearch(this, c_tab, riid, ppvObj); 35 } 36 37 /// @implemented 38 STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef() 39 { 40 return ::InterlockedIncrement(&m_cRefs); 41 } 42 43 /// @implemented 44 STDMETHODIMP_(ULONG) CCompartmentEventSink::Release() 45 { 46 if (::InterlockedDecrement(&m_cRefs) == 0) 47 { 48 delete this; 49 return 0; 50 } 51 return m_cRefs; 52 } 53 54 /// @implemented 55 STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid) 56 { 57 return m_fnEventSink(m_pUserData, rguid); 58 } 59 60 /// @implemented 61 HRESULT 62 CCompartmentEventSink::_Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread) 63 { 64 CESMAP *pCesMap = m_array.Append(1); 65 if (!pCesMap) 66 return E_OUTOFMEMORY; 67 68 ITfSource *pSource = NULL; 69 70 HRESULT hr = GetCompartment(pUnknown, rguid, &pCesMap->m_pComp, bThread); 71 if (FAILED(hr)) 72 { 73 hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); 74 if (FAILED(hr)) 75 { 76 hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &pCesMap->m_dwCookie); 77 if (FAILED(hr)) 78 { 79 if (pCesMap->m_pComp) 80 { 81 pCesMap->m_pComp->Release(); 82 pCesMap->m_pComp = NULL; 83 } 84 m_array.Remove(m_array.size() - 1, 1); 85 } 86 else 87 { 88 hr = S_OK; 89 } 90 } 91 } 92 93 if (pSource) 94 pSource->Release(); 95 96 return hr; 97 } 98 99 /// @implemented 100 HRESULT CCompartmentEventSink::_Unadvise() 101 { 102 CESMAP *pCesMap = m_array.data(); 103 size_t cItems = m_array.size(); 104 if (!cItems) 105 return S_OK; 106 107 do 108 { 109 ITfSource *pSource = NULL; 110 HRESULT hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); 111 if (SUCCEEDED(hr)) 112 pSource->UnadviseSink(pCesMap->m_dwCookie); 113 114 if (pCesMap->m_pComp) 115 { 116 pCesMap->m_pComp->Release(); 117 pCesMap->m_pComp = NULL; 118 } 119 120 if (pSource) 121 pSource->Release(); 122 123 ++pCesMap; 124 --cItems; 125 } while (cItems); 126 127 return S_OK; 128 } 129 130 /***********************************************************************/ 131 132 /// @implemented 133 CTextEventSink::CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV) 134 { 135 m_cRefs = 1; 136 m_pUnknown = NULL; 137 m_dwEditSinkCookie = (DWORD)-1; 138 m_dwLayoutSinkCookie = (DWORD)-1; 139 m_fnLayoutChange = NULL; 140 m_fnEndEdit = fnEndEdit; 141 m_pCallbackPV = pCallbackPV; 142 } 143 144 /// @implemented 145 CTextEventSink::~CTextEventSink() 146 { 147 } 148 149 /// @implemented 150 STDMETHODIMP CTextEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) 151 { 152 static const QITAB c_tab[] = 153 { 154 QITABENT(CTextEventSink, ITfTextEditSink), 155 QITABENT(CTextEventSink, ITfTextLayoutSink), 156 { NULL } 157 }; 158 return ::QISearch(this, c_tab, riid, ppvObj); 159 } 160 161 /// @implemented 162 STDMETHODIMP_(ULONG) CTextEventSink::AddRef() 163 { 164 return ::InterlockedIncrement(&m_cRefs); 165 } 166 167 /// @implemented 168 STDMETHODIMP_(ULONG) CTextEventSink::Release() 169 { 170 if (::InterlockedDecrement(&m_cRefs) == 0) 171 { 172 delete this; 173 return 0; 174 } 175 return m_cRefs; 176 } 177 178 struct TEXT_EVENT_SINK_END_EDIT 179 { 180 TfEditCookie m_ecReadOnly; 181 ITfEditRecord *m_pEditRecord; 182 ITfContext *m_pContext; 183 }; 184 185 /// @implemented 186 STDMETHODIMP CTextEventSink::OnEndEdit( 187 ITfContext *pic, 188 TfEditCookie ecReadOnly, 189 ITfEditRecord *pEditRecord) 190 { 191 TEXT_EVENT_SINK_END_EDIT Data = { ecReadOnly, pEditRecord, pic }; 192 return m_fnEndEdit(1, m_pCallbackPV, (LPVOID)&Data); 193 } 194 195 /// @implemented 196 STDMETHODIMP CTextEventSink::OnLayoutChange( 197 ITfContext *pContext, 198 TfLayoutCode lcode, 199 ITfContextView *pContextView) 200 { 201 switch (lcode) 202 { 203 case TF_LC_CREATE: 204 return m_fnLayoutChange(3, m_fnEndEdit, pContextView); 205 case TF_LC_CHANGE: 206 return m_fnLayoutChange(2, m_fnEndEdit, pContextView); 207 case TF_LC_DESTROY: 208 return m_fnLayoutChange(4, m_fnEndEdit, pContextView); 209 default: 210 return E_INVALIDARG; 211 } 212 } 213 214 /// @implemented 215 HRESULT CTextEventSink::_Advise(IUnknown *pUnknown, UINT uFlags) 216 { 217 m_pUnknown = NULL; 218 m_uFlags = uFlags; 219 220 ITfSource *pSource = NULL; 221 HRESULT hr = pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); 222 if (SUCCEEDED(hr)) 223 { 224 ITfTextEditSink *pSink = static_cast<ITfTextEditSink*>(this); 225 if (uFlags & 1) 226 hr = pSource->AdviseSink(IID_ITfTextEditSink, pSink, &m_dwEditSinkCookie); 227 if (SUCCEEDED(hr) && (uFlags & 2)) 228 hr = pSource->AdviseSink(IID_ITfTextLayoutSink, pSink, &m_dwLayoutSinkCookie); 229 230 if (SUCCEEDED(hr)) 231 { 232 m_pUnknown = pUnknown; 233 pUnknown->AddRef(); 234 } 235 else 236 { 237 pSource->UnadviseSink(m_dwEditSinkCookie); 238 } 239 } 240 241 if (pSource) 242 pSource->Release(); 243 244 return hr; 245 } 246 247 /// @implemented 248 HRESULT CTextEventSink::_Unadvise() 249 { 250 if (!m_pUnknown) 251 return E_FAIL; 252 253 ITfSource *pSource = NULL; 254 HRESULT hr = m_pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); 255 if (SUCCEEDED(hr)) 256 { 257 if (m_uFlags & 1) 258 hr = pSource->UnadviseSink(m_dwEditSinkCookie); 259 if (m_uFlags & 2) 260 hr = pSource->UnadviseSink(m_dwLayoutSinkCookie); 261 262 pSource->Release(); 263 } 264 265 m_pUnknown->Release(); 266 m_pUnknown = NULL; 267 268 return E_NOTIMPL; 269 } 270 271 /***********************************************************************/ 272 273 /// @implemented 274 CThreadMgrEventSink::CThreadMgrEventSink( 275 _In_ FN_INITDOCMGR fnInit, 276 _In_ FN_PUSHPOP fnPushPop, 277 _Inout_ LPVOID pvCallbackPV) 278 { 279 m_fnInit = fnInit; 280 m_fnPushPop = fnPushPop; 281 m_pCallbackPV = pvCallbackPV; 282 m_cRefs = 1; 283 } 284 285 /// @implemented 286 STDMETHODIMP CThreadMgrEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) 287 { 288 static const QITAB c_tab[] = 289 { 290 QITABENT(CThreadMgrEventSink, ITfThreadMgrEventSink), 291 { NULL } 292 }; 293 return ::QISearch(this, c_tab, riid, ppvObj); 294 } 295 296 /// @implemented 297 STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef() 298 { 299 return ::InterlockedIncrement(&m_cRefs); 300 } 301 302 /// @implemented 303 STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release() 304 { 305 if (::InterlockedDecrement(&m_cRefs) == 0) 306 { 307 delete this; 308 return 0; 309 } 310 return m_cRefs; 311 } 312 313 INT CALLBACK 314 CThreadMgrEventSink::DIMCallback( 315 UINT nCode, 316 ITfDocumentMgr *pDocMgr1, 317 ITfDocumentMgr *pDocMgr2, 318 LPVOID pUserData) 319 { 320 return E_NOTIMPL; 321 } 322 323 STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim) 324 { 325 if (!m_fnInit) 326 return S_OK; 327 return m_fnInit(0, pdim, NULL, m_pCallbackPV); 328 } 329 330 STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim) 331 { 332 if (!m_fnInit) 333 return S_OK; 334 return m_fnInit(1, pdim, NULL, m_pCallbackPV); 335 } 336 337 STDMETHODIMP 338 CThreadMgrEventSink::OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) 339 { 340 if (!m_fnInit) 341 return S_OK; 342 return m_fnInit(2, pdimFocus, pdimPrevFocus, m_pCallbackPV); 343 } 344 345 STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic) 346 { 347 if (!m_fnPushPop) 348 return S_OK; 349 return m_fnPushPop(3, pic, m_pCallbackPV); 350 } 351 352 STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic) 353 { 354 if (!m_fnPushPop) 355 return S_OK; 356 return m_fnPushPop(4, pic, m_pCallbackPV); 357 } 358 359 void CThreadMgrEventSink::SetCallbackPV(_Inout_ LPVOID pv) 360 { 361 if (!m_pCallbackPV) 362 m_pCallbackPV = pv; 363 } 364 365 HRESULT CThreadMgrEventSink::_Advise(ITfThreadMgr *pThreadMgr) 366 { 367 m_pThreadMgr = NULL; 368 369 HRESULT hr = E_FAIL; 370 ITfSource *pSource = NULL; 371 if (pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && 372 pSource->AdviseSink(IID_ITfThreadMgrEventSink, this, &m_dwCookie) == S_OK) 373 { 374 m_pThreadMgr = pThreadMgr; 375 pThreadMgr->AddRef(); 376 hr = S_OK; 377 } 378 379 if (pSource) 380 pSource->Release(); 381 382 return hr; 383 } 384 385 HRESULT CThreadMgrEventSink::_Unadvise() 386 { 387 HRESULT hr = E_FAIL; 388 ITfSource *pSource = NULL; 389 390 if (m_pThreadMgr) 391 { 392 if (m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && 393 pSource->UnadviseSink(m_dwCookie) == S_OK) 394 { 395 hr = S_OK; 396 } 397 398 if (pSource) 399 pSource->Release(); 400 } 401 402 if (m_pThreadMgr) 403 { 404 m_pThreadMgr->Release(); 405 m_pThreadMgr = NULL; 406 } 407 408 return hr; 409 } 410 411 /***********************************************************************/ 412 413 /// @implemented 414 CActiveLanguageProfileNotifySink::CActiveLanguageProfileNotifySink( 415 _In_ FN_COMPARE fnCompare, 416 _Inout_opt_ void *pUserData) 417 { 418 m_dwConnection = (DWORD)-1; 419 m_fnCompare = fnCompare; 420 m_cRefs = 1; 421 m_pUserData = pUserData; 422 } 423 424 /// @implemented 425 CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink() 426 { 427 } 428 429 /// @implemented 430 STDMETHODIMP CActiveLanguageProfileNotifySink::QueryInterface(REFIID riid, LPVOID* ppvObj) 431 { 432 static const QITAB c_tab[] = 433 { 434 QITABENT(CActiveLanguageProfileNotifySink, ITfActiveLanguageProfileNotifySink), 435 { NULL } 436 }; 437 return ::QISearch(this, c_tab, riid, ppvObj); 438 } 439 440 /// @implemented 441 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef() 442 { 443 return ::InterlockedIncrement(&m_cRefs); 444 } 445 446 /// @implemented 447 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::Release() 448 { 449 if (::InterlockedDecrement(&m_cRefs) == 0) 450 { 451 delete this; 452 return 0; 453 } 454 return m_cRefs; 455 } 456 457 /// @implemented 458 STDMETHODIMP 459 CActiveLanguageProfileNotifySink::OnActivated( 460 REFCLSID clsid, 461 REFGUID guidProfile, 462 BOOL fActivated) 463 { 464 if (!m_fnCompare) 465 return 0; 466 467 return m_fnCompare(clsid, guidProfile, fActivated, m_pUserData); 468 } 469 470 /// @implemented 471 HRESULT 472 CActiveLanguageProfileNotifySink::_Advise( 473 ITfThreadMgr *pThreadMgr) 474 { 475 m_pThreadMgr = NULL; 476 477 ITfSource *pSource = NULL; 478 HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); 479 if (FAILED(hr)) 480 return E_FAIL; 481 482 hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, this, &m_dwConnection); 483 if (SUCCEEDED(hr)) 484 { 485 m_pThreadMgr = pThreadMgr; 486 pThreadMgr->AddRef(); 487 hr = S_OK; 488 } 489 else 490 { 491 hr = E_FAIL; 492 } 493 494 if (pSource) 495 pSource->Release(); 496 497 return hr; 498 } 499 500 /// @implemented 501 HRESULT 502 CActiveLanguageProfileNotifySink::_Unadvise() 503 { 504 if (!m_pThreadMgr) 505 return E_FAIL; 506 507 ITfSource *pSource = NULL; 508 HRESULT hr = m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); 509 if (SUCCEEDED(hr)) 510 { 511 hr = pSource->UnadviseSink(m_dwConnection); 512 if (SUCCEEDED(hr)) 513 hr = S_OK; 514 } 515 516 if (pSource) 517 pSource->Release(); 518 519 if (m_pThreadMgr) 520 { 521 m_pThreadMgr->Release(); 522 m_pThreadMgr = NULL; 523 } 524 525 return hr; 526 } 527