Reactos
1/*
2 * PROJECT: ReactOS CTF
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: ITfContext implementation
5 * COPYRIGHT: Copyright 2009 Aric Stewart, CodeWeavers
6 * Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9#include "precomp.h"
10
11#include <wine/debug.h>
12WINE_DEFAULT_DEBUG_CHANNEL(msctf);
13
14////////////////////////////////////////////////////////////////////////////
15
16class CContext
17 : public ITfContext
18 , public ITfSource
19 // , public ITfContextComposition
20 , public ITfContextOwnerCompositionServices
21 // , public ITfContextOwnerServices
22 , public ITfInsertAtSelection
23 // , public ITfMouseTracker
24 // , public ITfQueryEmbedded
25 , public ITfSourceSingle
26 , public ITextStoreACPSink
27 , public ITextStoreACPServices
28{
29public:
30 CContext();
31 virtual ~CContext();
32
33 static HRESULT CreateInstance(
34 TfClientId tidOwner,
35 IUnknown *punk,
36 ITfDocumentMgr *mgr,
37 ITfContext **ppOut,
38 TfEditCookie *pecTextStore);
39 HRESULT Initialize(ITfDocumentMgr *manager);
40 HRESULT Uninitialize();
41
42 // ** IUnknown methods **
43 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override;
44 STDMETHODIMP_(ULONG) AddRef() override;
45 STDMETHODIMP_(ULONG) Release() override;
46
47 // ** ITfContext methods **
48 STDMETHODIMP RequestEditSession(
49 _In_ TfClientId tid,
50 _In_ ITfEditSession *pes,
51 _In_ DWORD dwFlags,
52 _Out_ HRESULT *phrSession) override;
53 STDMETHODIMP InWriteSession(
54 _In_ TfClientId tid,
55 _Out_ BOOL *pfWriteSession) override;
56 STDMETHODIMP GetSelection(
57 _In_ TfEditCookie ec,
58 _In_ ULONG ulIndex,
59 _In_ ULONG ulCount,
60 _Out_ TF_SELECTION *pSelection,
61 _Out_ ULONG *pcFetched) override;
62 STDMETHODIMP SetSelection(
63 _In_ TfEditCookie ec,
64 _In_ ULONG ulCount,
65 _In_ const TF_SELECTION *pSelection) override;
66 STDMETHODIMP GetStart(
67 _In_ TfEditCookie ec,
68 _Out_ ITfRange **ppStart) override;
69 STDMETHODIMP GetEnd(
70 _In_ TfEditCookie ec,
71 _Out_ ITfRange **ppEnd) override;
72 STDMETHODIMP GetActiveView(_Out_ ITfContextView **ppView) override;
73 STDMETHODIMP EnumViews(_Out_ IEnumTfContextViews **ppEnum) override;
74 STDMETHODIMP GetStatus(_Out_ TF_STATUS *pdcs) override;
75 STDMETHODIMP GetProperty(
76 _In_ REFGUID guidProp,
77 _Out_ ITfProperty **ppProp) override;
78 STDMETHODIMP GetAppProperty(
79 _In_ REFGUID guidProp,
80 _Out_ ITfReadOnlyProperty **ppProp) override;
81 STDMETHODIMP TrackProperties(
82 _In_ const GUID **prgProp,
83 _In_ ULONG cProp,
84 _In_ const GUID **prgAppProp,
85 _In_ ULONG cAppProp,
86 _Out_ ITfReadOnlyProperty **ppProperty) override;
87 STDMETHODIMP EnumProperties(_Out_ IEnumTfProperties **ppEnum) override;
88 STDMETHODIMP GetDocumentMgr(_Out_ ITfDocumentMgr **ppDm) override;
89 STDMETHODIMP CreateRangeBackup(
90 _In_ TfEditCookie ec,
91 _In_ ITfRange *pRange,
92 _Out_ ITfRangeBackup **ppBackup) override;
93
94 // ** ITfSource methods **
95 STDMETHODIMP AdviseSink(
96 _In_ REFIID riid,
97 _In_ IUnknown *punk,
98 _Out_ DWORD *pdwCookie) override;
99 STDMETHODIMP UnadviseSink(_In_ DWORD dwCookie) override;
100
101 // ** ITfContextOwnerCompositionServices methods **
102 STDMETHODIMP StartComposition(
103 _In_ TfEditCookie ecWrite,
104 _In_ ITfRange *pCompositionRange,
105 _In_ ITfCompositionSink *pSink,
106 _Out_ ITfComposition **ppComposition) override;
107 STDMETHODIMP EnumCompositions(_Out_ IEnumITfCompositionView **ppEnum) override;
108 STDMETHODIMP FindComposition(
109 _In_ TfEditCookie ecRead,
110 _In_ ITfRange *pTestRange,
111 _Out_ IEnumITfCompositionView **ppEnum) override;
112 STDMETHODIMP TakeOwnership(
113 _In_ TfEditCookie ecWrite,
114 _In_ ITfCompositionView *pComposition,
115 _In_ ITfCompositionSink *pSink,
116 _Out_ ITfComposition **ppComposition) override;
117 STDMETHODIMP TerminateComposition(_In_ ITfCompositionView *pComposition) override;
118
119 // ** ITfInsertAtSelection methods **
120 STDMETHODIMP InsertTextAtSelection(
121 _In_ TfEditCookie ec,
122 _In_ DWORD dwFlags,
123 _In_ const WCHAR *pchText,
124 _In_ LONG cch,
125 _Out_ ITfRange **ppRange) override;
126 STDMETHODIMP InsertEmbeddedAtSelection(
127 _In_ TfEditCookie ec,
128 _In_ DWORD dwFlags,
129 _In_ IDataObject *pDataObject,
130 _Out_ ITfRange **ppRange) override;
131
132 // ** ITfSourceSingle methods **
133 STDMETHODIMP AdviseSingleSink(
134 _In_ TfClientId tid,
135 _In_ REFIID riid,
136 _In_ IUnknown *punk) override;
137 STDMETHODIMP UnadviseSingleSink(
138 _In_ TfClientId tid,
139 _In_ REFIID riid) override;
140
141 // ** ITextStoreACPSink methods **
142 STDMETHODIMP OnTextChange(
143 _In_ DWORD dwFlags,
144 _In_ const TS_TEXTCHANGE *pChange) override;
145 STDMETHODIMP OnSelectionChange() override;
146 STDMETHODIMP OnLayoutChange(
147 _In_ TsLayoutCode lcode,
148 _In_ TsViewCookie vcView) override;
149 STDMETHODIMP OnStatusChange(_In_ DWORD dwFlags) override;
150 STDMETHODIMP OnAttrsChange(
151 _In_ LONG acpStart,
152 _In_ LONG acpEnd,
153 _In_ ULONG cAttrs,
154 _In_ const TS_ATTRID *paAttrs) override;
155 STDMETHODIMP OnLockGranted(_In_ DWORD dwLockFlags) override;
156 STDMETHODIMP OnStartEditTransaction() override;
157 STDMETHODIMP OnEndEditTransaction() override;
158
159 // ** ITextStoreACPServices methods **
160 STDMETHODIMP Serialize(
161 _In_ ITfProperty *prop,
162 _In_ ITfRange *range,
163 _Out_ TF_PERSISTENT_PROPERTY_HEADER_ACP *header,
164 _In_ IStream *stream) override;
165 STDMETHODIMP Unserialize(
166 _In_ ITfProperty *prop,
167 _In_ const TF_PERSISTENT_PROPERTY_HEADER_ACP *header,
168 _In_ IStream *stream,
169 _In_ ITfPersistentPropertyLoaderACP *loader) override;
170 STDMETHODIMP ForceLoadProperty(_In_ ITfProperty *prop) override;
171 STDMETHODIMP CreateRange(
172 _In_ LONG start,
173 _In_ LONG end,
174 _Out_ ITfRangeACP **range) override;
175
176protected:
177 LONG m_cRefs;
178 BOOL m_connected;
179
180 // Aggregation
181 ITfCompartmentMgr *m_CompartmentMgr;
182
183 TfClientId m_tidOwner;
184 TfEditCookie m_defaultCookie;
185 TS_STATUS m_documentStatus;
186 ITfDocumentMgr *m_manager;
187
188 ITextStoreACP *m_pITextStoreACP;
189 ITfContextOwnerCompositionSink *m_pITfContextOwnerCompositionSink;
190 ITfEditSession *m_currentEditSession;
191
192 // kept as separate lists to reduce unnecessary iterations
193 struct list m_pContextKeyEventSink;
194 struct list m_pEditTransactionSink;
195 struct list m_pStatusSink;
196 struct list m_pTextEditSink;
197 struct list m_pTextLayoutSink;
198};
199
200////////////////////////////////////////////////////////////////////////////
201
202typedef struct tagEditCookie
203{
204 DWORD lockType;
205 CContext *pOwningContext;
206} EditCookie;
207
208////////////////////////////////////////////////////////////////////////////
209
210CContext::CContext()
211 : m_cRefs(1)
212 , m_connected(FALSE)
213 , m_CompartmentMgr(NULL)
214 , m_manager(NULL)
215 , m_pITextStoreACP(NULL)
216 , m_pITfContextOwnerCompositionSink(NULL)
217 , m_currentEditSession(NULL)
218{
219 list_init(&m_pContextKeyEventSink);
220 list_init(&m_pEditTransactionSink);
221 list_init(&m_pStatusSink);
222 list_init(&m_pTextEditSink);
223 list_init(&m_pTextLayoutSink);
224}
225
226CContext::~CContext()
227{
228 EditCookie *cookie;
229 TRACE("destroying %p\n", this);
230
231 if (m_pITextStoreACP)
232 m_pITextStoreACP->Release();
233
234 if (m_pITfContextOwnerCompositionSink)
235 m_pITfContextOwnerCompositionSink->Release();
236
237 if (m_defaultCookie)
238 {
239 cookie = (EditCookie *)remove_Cookie(m_defaultCookie);
240 cicMemFree(cookie);
241 m_defaultCookie = 0;
242 }
243
244 free_sinks(&m_pContextKeyEventSink);
245 free_sinks(&m_pEditTransactionSink);
246 free_sinks(&m_pStatusSink);
247 free_sinks(&m_pTextEditSink);
248 free_sinks(&m_pTextLayoutSink);
249
250 if (m_CompartmentMgr)
251 m_CompartmentMgr->Release();
252}
253
254STDMETHODIMP CContext::QueryInterface(REFIID riid, void **ppvObj)
255{
256 *ppvObj = NULL;
257
258 IUnknown *pUnk = NULL;
259 if (riid == IID_IUnknown || riid == IID_ITfContext)
260 pUnk = static_cast<ITfContext *>(this);
261 else if (riid == IID_ITfSource)
262 pUnk = static_cast<ITfSource *>(this);
263 else if (riid == IID_ITfContextOwnerCompositionServices)
264 pUnk = static_cast<ITfContextOwnerCompositionServices *>(this);
265 else if (riid == IID_ITfInsertAtSelection)
266 pUnk = static_cast<ITfInsertAtSelection *>(this);
267 else if (riid == IID_ITfCompartmentMgr)
268 pUnk = m_CompartmentMgr;
269 else if (riid == IID_ITfSourceSingle)
270 pUnk = static_cast<ITfSourceSingle *>(this);
271 else if (riid == IID_ITextStoreACPSink)
272 pUnk = static_cast<ITextStoreACPSink *>(this);
273 else if (riid == IID_ITextStoreACPServices)
274 pUnk = static_cast<ITextStoreACPServices *>(this);
275
276 if (pUnk)
277 {
278 pUnk->AddRef();
279 *ppvObj = pUnk;
280 return S_OK;
281 }
282
283 WARN("unsupported interface: %s\n", debugstr_guid(&riid));
284 return E_NOINTERFACE;
285}
286
287STDMETHODIMP_(ULONG) CContext::AddRef()
288{
289 return ::InterlockedIncrement(&m_cRefs);
290}
291
292STDMETHODIMP_(ULONG) CContext::Release()
293{
294 ULONG ret = ::InterlockedDecrement(&m_cRefs);
295 if (!ret)
296 delete this;
297 return ret;
298}
299
300STDMETHODIMP CContext::RequestEditSession(
301 _In_ TfClientId tid,
302 _In_ ITfEditSession *pes,
303 _In_ DWORD dwFlags,
304 _Out_ HRESULT *phrSession)
305{
306 HRESULT hr;
307 DWORD dwLockFlags = 0x0;
308
309 TRACE("(%p) %i %p %x %p\n", this, tid, pes, dwFlags, phrSession);
310
311 if (!(dwFlags & TF_ES_READ) && !(dwFlags & TF_ES_READWRITE))
312 {
313 *phrSession = E_FAIL;
314 return E_INVALIDARG;
315 }
316
317 if (!m_pITextStoreACP)
318 {
319 FIXME("No ITextStoreACP available\n");
320 *phrSession = E_FAIL;
321 return E_FAIL;
322 }
323
324 if (!(dwFlags & TF_ES_ASYNC))
325 dwLockFlags |= TS_LF_SYNC;
326
327 if ((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE)
328 dwLockFlags |= TS_LF_READWRITE;
329 else if (dwFlags & TF_ES_READ)
330 dwLockFlags |= TS_LF_READ;
331
332 if (!m_documentStatus.dwDynamicFlags)
333 m_pITextStoreACP->GetStatus(&m_documentStatus);
334
335 if (((dwFlags & TF_ES_READWRITE) == TF_ES_READWRITE) &&
336 (m_documentStatus.dwDynamicFlags & TS_SD_READONLY))
337 {
338 *phrSession = TS_E_READONLY;
339 return S_OK;
340 }
341
342 hr = pes->QueryInterface(IID_ITfEditSession, (LPVOID *)&m_currentEditSession);
343 if (FAILED(hr))
344 {
345 *phrSession = E_FAIL;
346 return E_INVALIDARG;
347 }
348
349 return m_pITextStoreACP->RequestLock(dwLockFlags, phrSession);
350}
351
352STDMETHODIMP CContext::InWriteSession(
353 _In_ TfClientId tid,
354 _Out_ BOOL *pfWriteSession)
355{
356 FIXME("STUB:(%p)\n", this);
357 return E_NOTIMPL;
358}
359
360STDMETHODIMP CContext::GetSelection(
361 _In_ TfEditCookie ec,
362 _In_ ULONG ulIndex,
363 _In_ ULONG ulCount,
364 _Out_ TF_SELECTION *pSelection,
365 _Out_ ULONG *pcFetched)
366{
367 ULONG count, i;
368 ULONG totalFetched = 0;
369 HRESULT hr = S_OK;
370
371 if (!pSelection || !pcFetched)
372 return E_INVALIDARG;
373
374 *pcFetched = 0;
375
376 if (!m_connected)
377 return TF_E_DISCONNECTED;
378
379 if (get_Cookie_magic(ec) != COOKIE_MAGIC_EDITCOOKIE)
380 return TF_E_NOLOCK;
381
382 if (!m_pITextStoreACP)
383 {
384 FIXME("Context does not have a ITextStoreACP\n");
385 return E_NOTIMPL;
386 }
387
388 count = (ulIndex == (ULONG)TF_DEFAULT_SELECTION) ? 1 : ulCount;
389
390 for (i = 0; i < count; i++)
391 {
392 DWORD fetched;
393 TS_SELECTION_ACP acps;
394
395 hr = m_pITextStoreACP->GetSelection(ulIndex + i, 1, &acps, &fetched);
396 if (hr == TS_E_NOLOCK)
397 return TF_E_NOLOCK;
398 else if (FAILED(hr))
399 break;
400
401 pSelection[totalFetched].style.ase = (TfActiveSelEnd)acps.style.ase;
402 pSelection[totalFetched].style.fInterimChar = acps.style.fInterimChar;
403 Range_Constructor(this, acps.acpStart, acps.acpEnd, &pSelection[totalFetched].range);
404 totalFetched++;
405 }
406
407 *pcFetched = totalFetched;
408 return hr;
409}
410
411STDMETHODIMP
412CContext::SetSelection(
413 _In_ TfEditCookie ec,
414 _In_ ULONG ulCount,
415 _In_ const TF_SELECTION *pSelection)
416{
417 TS_SELECTION_ACP *acp;
418 ULONG i;
419 HRESULT hr;
420
421 TRACE("(%p) %i %i %p\n", this, ec, ulCount, pSelection);
422
423 if (!m_pITextStoreACP)
424 {
425 FIXME("Context does not have a ITextStoreACP\n");
426 return E_NOTIMPL;
427 }
428
429 if (get_Cookie_magic(ec) != COOKIE_MAGIC_EDITCOOKIE)
430 return TF_E_NOLOCK;
431
432 acp = (TS_SELECTION_ACP *)cicMemAlloc(sizeof(TS_SELECTION_ACP) * ulCount);
433 if (!acp)
434 return E_OUTOFMEMORY;
435
436 for (i = 0; i < ulCount; i++)
437 {
438 if (FAILED(TF_SELECTION_to_TS_SELECTION_ACP(&pSelection[i], &acp[i])))
439 {
440 TRACE("Selection Conversion Failed\n");
441 cicMemFree(acp);
442 return E_FAIL;
443 }
444 }
445
446 hr = m_pITextStoreACP->SetSelection(ulCount, acp);
447
448 cicMemFree(acp);
449
450 return hr;
451}
452
453STDMETHODIMP
454CContext::GetStart(
455 _In_ TfEditCookie ec,
456 _Out_ ITfRange **ppStart)
457{
458 TRACE("(%p) %i %p\n", this, ec, ppStart);
459
460 if (!ppStart)
461 return E_INVALIDARG;
462
463 *ppStart = NULL;
464
465 if (!m_connected)
466 return TF_E_DISCONNECTED;
467
468 if (get_Cookie_magic(ec) != COOKIE_MAGIC_EDITCOOKIE)
469 return TF_E_NOLOCK;
470
471 return Range_Constructor(this, 0, 0, ppStart);
472}
473
474STDMETHODIMP
475CContext::GetEnd(
476 _In_ TfEditCookie ec,
477 _Out_ ITfRange **ppEnd)
478{
479 LONG end;
480
481 TRACE("(%p) %i %p\n", this, ec, ppEnd);
482
483 if (!ppEnd)
484 return E_INVALIDARG;
485
486 *ppEnd = NULL;
487
488 if (!m_connected)
489 return TF_E_DISCONNECTED;
490
491 if (get_Cookie_magic(ec) != COOKIE_MAGIC_EDITCOOKIE)
492 return TF_E_NOLOCK;
493
494 if (!m_pITextStoreACP)
495 {
496 FIXME("Context does not have a ITextStoreACP\n");
497 return E_NOTIMPL;
498 }
499
500 m_pITextStoreACP->GetEndACP(&end);
501
502 return Range_Constructor(this, end, end, ppEnd);
503}
504
505STDMETHODIMP CContext::GetActiveView(_Out_ ITfContextView **ppView)
506{
507 FIXME("STUB:(%p)\n", this);
508 return E_NOTIMPL;
509}
510
511STDMETHODIMP CContext::EnumViews(_Out_ IEnumTfContextViews **ppEnum)
512{
513 FIXME("STUB:(%p)\n", this);
514 return E_NOTIMPL;
515}
516
517STDMETHODIMP CContext::GetStatus(_Out_ TF_STATUS *pdcs)
518{
519 TRACE("(%p) %p\n", this, pdcs);
520
521 if (!m_connected)
522 return TF_E_DISCONNECTED;
523
524 if (!pdcs)
525 return E_INVALIDARG;
526
527 if (!m_pITextStoreACP)
528 {
529 FIXME("Context does not have a ITextStoreACP\n");
530 return E_NOTIMPL;
531 }
532
533 m_pITextStoreACP->GetStatus(&m_documentStatus);
534
535 *pdcs = m_documentStatus;
536
537 return S_OK;
538}
539
540STDMETHODIMP CContext::GetProperty(
541 _In_ REFGUID guidProp,
542 _Out_ ITfProperty **ppProp)
543{
544 FIXME("STUB:(%p)\n", this);
545 return E_NOTIMPL;
546}
547
548STDMETHODIMP CContext::GetAppProperty(
549 _In_ REFGUID guidProp,
550 _Out_ ITfReadOnlyProperty **ppProp)
551{
552 FIXME("STUB:(%p)\n", this);
553 return E_NOTIMPL;
554}
555
556STDMETHODIMP CContext::TrackProperties(
557 _In_ const GUID **prgProp,
558 _In_ ULONG cProp,
559 _In_ const GUID **prgAppProp,
560 _In_ ULONG cAppProp,
561 _Out_ ITfReadOnlyProperty **ppProperty)
562{
563 FIXME("STUB:(%p)\n", this);
564 return E_NOTIMPL;
565}
566
567STDMETHODIMP CContext::EnumProperties(_Out_ IEnumTfProperties **ppEnum)
568{
569 FIXME("STUB:(%p)\n", this);
570 return E_NOTIMPL;
571}
572
573STDMETHODIMP CContext::GetDocumentMgr(_Out_ ITfDocumentMgr **ppDm)
574{
575 TRACE("(%p) %p\n", this, ppDm);
576
577 if (!ppDm)
578 return E_INVALIDARG;
579
580 *ppDm = m_manager;
581 if (!m_manager)
582 return S_FALSE;
583
584 m_manager->AddRef();
585 return S_OK;
586}
587
588STDMETHODIMP CContext::CreateRangeBackup(
589 _In_ TfEditCookie ec,
590 _In_ ITfRange *pRange,
591 _Out_ ITfRangeBackup **ppBackup)
592{
593 FIXME("STUB:(%p)\n", this);
594 return E_NOTIMPL;
595}
596
597STDMETHODIMP CContext::AdviseSink(
598 _In_ REFIID riid,
599 _In_ IUnknown *punk,
600 _Out_ DWORD *pdwCookie)
601{
602 TRACE("(%p) %s %p %p\n", this, debugstr_guid(&riid), punk, pdwCookie);
603
604 if (cicIsNullPtr(&riid) || !punk || !pdwCookie)
605 return E_INVALIDARG;
606
607 if (riid == IID_ITfTextEditSink)
608 return advise_sink(&m_pTextEditSink, IID_ITfTextEditSink, COOKIE_MAGIC_CONTEXTSINK, punk, pdwCookie);
609
610 FIXME("(%p) Unhandled Sink: %s\n", this, debugstr_guid(&riid));
611 return E_NOTIMPL;
612}
613
614STDMETHODIMP CContext::UnadviseSink(_In_ DWORD dwCookie)
615{
616 TRACE("(%p) %x\n", this, dwCookie);
617
618 if (get_Cookie_magic(dwCookie) != COOKIE_MAGIC_CONTEXTSINK)
619 return E_INVALIDARG;
620
621 return unadvise_sink(dwCookie);
622}
623
624STDMETHODIMP CContext::StartComposition(
625 _In_ TfEditCookie ecWrite,
626 _In_ ITfRange *pCompositionRange,
627 _In_ ITfCompositionSink *pSink,
628 _Out_ ITfComposition **ppComposition)
629{
630 FIXME("STUB:(%p) %#x %p %p %p\n", this, ecWrite, pCompositionRange, pSink, ppComposition);
631 return E_NOTIMPL;
632}
633
634STDMETHODIMP CContext::EnumCompositions(_Out_ IEnumITfCompositionView **ppEnum)
635{
636 FIXME("STUB:(%p) %p\n", this, ppEnum);
637 return E_NOTIMPL;
638}
639
640STDMETHODIMP CContext::FindComposition(
641 _In_ TfEditCookie ecRead,
642 _In_ ITfRange *pTestRange,
643 _Out_ IEnumITfCompositionView **ppEnum)
644{
645 FIXME("STUB:(%p) %#x %p %p\n", this, ecRead, pTestRange, ppEnum);
646 return E_NOTIMPL;
647}
648
649STDMETHODIMP CContext::TakeOwnership(
650 _In_ TfEditCookie ecWrite,
651 _In_ ITfCompositionView *pComposition,
652 _In_ ITfCompositionSink *pSink,
653 _Out_ ITfComposition **ppComposition)
654{
655 FIXME("STUB:(%p) %#x %p %p %p\n", this, ecWrite, pComposition, pSink, ppComposition);
656 return E_NOTIMPL;
657}
658
659STDMETHODIMP CContext::TerminateComposition(_In_ ITfCompositionView *pComposition)
660{
661 FIXME("STUB:(%p) %p\n", this, pComposition);
662 return E_NOTIMPL;
663}
664
665STDMETHODIMP CContext::InsertTextAtSelection(
666 _In_ TfEditCookie ec,
667 _In_ DWORD dwFlags,
668 _In_ const WCHAR *pchText,
669 _In_ LONG cch,
670 _Out_ ITfRange **ppRange)
671{
672 EditCookie *cookie;
673 LONG acpStart, acpEnd;
674 TS_TEXTCHANGE change;
675 HRESULT hr;
676
677 TRACE("(%p) %i %x %s %p\n", this, ec, dwFlags, debugstr_wn(pchText,cch), ppRange);
678
679 if (!m_connected)
680 return TF_E_DISCONNECTED;
681
682 if (get_Cookie_magic(ec) != COOKIE_MAGIC_EDITCOOKIE)
683 return TF_E_NOLOCK;
684
685 cookie = (EditCookie *)get_Cookie_data(ec);
686
687 if ((cookie->lockType & TS_LF_READWRITE) != TS_LF_READWRITE)
688 return TS_E_READONLY;
689
690 if (!m_pITextStoreACP)
691 {
692 FIXME("Context does not have a ITextStoreACP\n");
693 return E_NOTIMPL;
694 }
695
696 hr = m_pITextStoreACP->InsertTextAtSelection(dwFlags, pchText, cch, &acpStart, &acpEnd, &change);
697 if (SUCCEEDED(hr))
698 Range_Constructor(this, change.acpStart, change.acpNewEnd, ppRange);
699
700 return hr;
701}
702
703STDMETHODIMP CContext::InsertEmbeddedAtSelection(
704 _In_ TfEditCookie ec,
705 _In_ DWORD dwFlags,
706 _In_ IDataObject *pDataObject,
707 _Out_ ITfRange **ppRange)
708{
709 FIXME("STUB:(%p)\n", this);
710 return E_NOTIMPL;
711}
712
713STDMETHODIMP CContext::AdviseSingleSink(
714 _In_ TfClientId tid,
715 _In_ REFIID riid,
716 _In_ IUnknown *punk)
717{
718 FIXME("STUB:(%p) %i %s %p\n", this, tid, debugstr_guid(&riid), punk);
719 return E_NOTIMPL;
720}
721
722STDMETHODIMP CContext::UnadviseSingleSink(
723 _In_ TfClientId tid,
724 _In_ REFIID riid)
725{
726 FIXME("STUB:(%p) %i %s\n", this, tid, debugstr_guid(&riid));
727 return E_NOTIMPL;
728}
729
730STDMETHODIMP CContext::OnTextChange(
731 _In_ DWORD dwFlags,
732 _In_ const TS_TEXTCHANGE *pChange)
733{
734 FIXME("STUB:(%p)\n", this);
735 return S_OK;
736}
737
738STDMETHODIMP CContext::OnSelectionChange()
739{
740 FIXME("STUB:(%p)\n", this);
741 return S_OK;
742}
743
744STDMETHODIMP CContext::OnLayoutChange(
745 _In_ TsLayoutCode lcode,
746 _In_ TsViewCookie vcView)
747{
748 FIXME("STUB:(%p)\n", this);
749 return S_OK;
750}
751
752STDMETHODIMP CContext::OnStatusChange(_In_ DWORD dwFlags)
753{
754 HRESULT hr, hrSession;
755
756 TRACE("(%p) %x\n", this, dwFlags);
757
758 if (!m_pITextStoreACP)
759 {
760 FIXME("Context does not have a ITextStoreACP\n");
761 return E_NOTIMPL;
762 }
763
764 hr = m_pITextStoreACP->RequestLock(TS_LF_READ, &hrSession);
765
766 if(SUCCEEDED(hr) && SUCCEEDED(hrSession))
767 m_documentStatus.dwDynamicFlags = dwFlags;
768
769 return S_OK;
770}
771
772STDMETHODIMP CContext::OnAttrsChange(
773 _In_ LONG acpStart,
774 _In_ LONG acpEnd,
775 _In_ ULONG cAttrs,
776 _In_ const TS_ATTRID *paAttrs)
777{
778 FIXME("STUB:(%p)\n", this);
779 return E_NOTIMPL;
780}
781
782STDMETHODIMP CContext::OnLockGranted(_In_ DWORD dwLockFlags)
783{
784 HRESULT hr;
785 EditCookie *cookie, *sinkcookie;
786 TfEditCookie ec;
787 struct list *cursor;
788
789 TRACE("(%p) %x\n", this, dwLockFlags);
790
791 if (!m_currentEditSession)
792 {
793 FIXME("OnLockGranted called for something other than an EditSession\n");
794 return S_OK;
795 }
796
797 cookie = (EditCookie *)cicMemAlloc(sizeof(EditCookie));
798 if (!cookie)
799 return E_OUTOFMEMORY;
800
801 sinkcookie = (EditCookie *)cicMemAlloc(sizeof(EditCookie));
802 if (!sinkcookie)
803 {
804 cicMemFree(cookie);
805 return E_OUTOFMEMORY;
806 }
807
808 cookie->lockType = dwLockFlags;
809 cookie->pOwningContext = this;
810 ec = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, cookie);
811
812 hr = m_currentEditSession->DoEditSession(ec);
813
814 if ((dwLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE)
815 {
816 ITfTextEditSink *sink;
817 TfEditCookie sc;
818
819 sinkcookie->lockType = TS_LF_READ;
820 sinkcookie->pOwningContext = this;
821 sc = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, sinkcookie);
822
823 /*TODO: implement ITfEditRecord */
824 SINK_FOR_EACH(cursor, &m_pTextEditSink, ITfTextEditSink, sink)
825 {
826 sink->OnEndEdit(static_cast<ITfContext *>(this), sc, NULL);
827 }
828 sinkcookie = (EditCookie *)remove_Cookie(sc);
829 }
830 cicMemFree(sinkcookie);
831
832 m_currentEditSession->Release();
833 m_currentEditSession = NULL;
834
835 /* Edit Cookie is only valid during the edit session */
836 cookie = (EditCookie *)remove_Cookie(ec);
837 cicMemFree(cookie);
838
839 return hr;
840}
841
842STDMETHODIMP CContext::OnStartEditTransaction()
843{
844 FIXME("STUB:(%p)\n", this);
845 return E_NOTIMPL;
846}
847
848STDMETHODIMP CContext::OnEndEditTransaction()
849{
850 FIXME("STUB:(%p)\n", this);
851 return E_NOTIMPL;
852}
853
854STDMETHODIMP CContext::Serialize(
855 _In_ ITfProperty *prop,
856 _In_ ITfRange *range,
857 _Out_ TF_PERSISTENT_PROPERTY_HEADER_ACP *header,
858 _In_ IStream *stream)
859{
860 FIXME("stub: %p %p %p %p %p\n", this, prop, range, header, stream);
861 return E_NOTIMPL;
862}
863
864STDMETHODIMP CContext::Unserialize(
865 _In_ ITfProperty *prop,
866 _In_ const TF_PERSISTENT_PROPERTY_HEADER_ACP *header,
867 _In_ IStream *stream,
868 _In_ ITfPersistentPropertyLoaderACP *loader)
869{
870 FIXME("stub: %p %p %p %p %p\n", this, prop, header, stream, loader);
871 return E_NOTIMPL;
872}
873
874STDMETHODIMP CContext::ForceLoadProperty(_In_ ITfProperty *prop)
875{
876 FIXME("stub: %p %p\n", this, prop);
877 return E_NOTIMPL;
878}
879
880STDMETHODIMP CContext::CreateRange(
881 _In_ LONG start,
882 _In_ LONG end,
883 _Out_ ITfRangeACP **range)
884{
885 FIXME("stub: %p %d %d %p\n", this, start, end, range);
886 return S_OK;
887}
888
889HRESULT CContext::CreateInstance(
890 TfClientId tidOwner,
891 IUnknown *punk,
892 ITfDocumentMgr *mgr,
893 ITfContext **ppOut,
894 TfEditCookie *pecTextStore)
895{
896 CContext *This = new(cicNoThrow) CContext();
897 if (!This)
898 return E_OUTOFMEMORY;
899
900 EditCookie *cookie = (EditCookie *)cicMemAlloc(sizeof(EditCookie));
901 if (!cookie)
902 {
903 delete This;
904 return E_OUTOFMEMORY;
905 }
906
907 TRACE("(%p) %x %p %p %p\n", This, tidOwner, punk, ppOut, pecTextStore);
908
909 This->m_tidOwner = tidOwner;
910 This->m_manager = mgr;
911
912 CompartmentMgr_Constructor(static_cast<ITfContext *>(This), IID_IUnknown, (IUnknown **)&This->m_CompartmentMgr);
913
914 cookie->lockType = TF_ES_READ;
915 cookie->pOwningContext = This;
916
917 if (punk)
918 {
919 punk->QueryInterface(IID_ITextStoreACP, (LPVOID*)&This->m_pITextStoreACP);
920 punk->QueryInterface(IID_ITfContextOwnerCompositionSink, (LPVOID*)&This->m_pITfContextOwnerCompositionSink);
921
922 if (!This->m_pITextStoreACP && !This->m_pITfContextOwnerCompositionSink)
923 FIXME("Unhandled pUnk\n");
924 }
925
926 This->m_defaultCookie = generate_Cookie(COOKIE_MAGIC_EDITCOOKIE, cookie);
927 *pecTextStore = This->m_defaultCookie;
928
929 list_init(&This->m_pContextKeyEventSink);
930 list_init(&This->m_pEditTransactionSink);
931 list_init(&This->m_pStatusSink);
932 list_init(&This->m_pTextEditSink);
933 list_init(&This->m_pTextLayoutSink);
934
935 *ppOut = static_cast<ITfContext *>(This);
936 TRACE("returning %p\n", *ppOut);
937
938 return S_OK;
939}
940
941HRESULT CContext::Initialize(ITfDocumentMgr *manager)
942{
943 if (m_pITextStoreACP)
944 {
945 m_pITextStoreACP->AdviseSink(IID_ITextStoreACPSink,
946 static_cast<ITextStoreACPSink *>(this),
947 TS_AS_ALL_SINKS);
948 }
949 m_connected = TRUE;
950 m_manager = manager;
951 return S_OK;
952}
953
954HRESULT CContext::Uninitialize()
955{
956 if (m_pITextStoreACP)
957 m_pITextStoreACP->UnadviseSink(static_cast<ITextStoreACPSink *>(this));
958 m_connected = FALSE;
959 m_manager = NULL;
960 return S_OK;
961}
962
963////////////////////////////////////////////////////////////////////////////
964
965EXTERN_C
966HRESULT
967Context_Constructor(
968 TfClientId tidOwner,
969 IUnknown *punk,
970 ITfDocumentMgr *mgr,
971 ITfContext **ppOut,
972 TfEditCookie *pecTextStore)
973{
974 return CContext::CreateInstance(tidOwner, punk, mgr, ppOut, pecTextStore);
975}
976
977EXTERN_C
978HRESULT Context_Initialize(ITfContext *iface, ITfDocumentMgr *manager)
979{
980 CContext *This = static_cast<CContext *>(iface);
981 return This->Initialize(manager);
982}
983
984EXTERN_C
985HRESULT Context_Uninitialize(ITfContext *iface)
986{
987 CContext *This = static_cast<CContext *>(iface);
988 return This->Uninitialize();
989}