/*++ Copyright (c) Microsoft Corporation. Licensed under the MIT license. Module Name: TfEditSession.cpp Abstract: This file implements the CEditSessionObject Class. Author: Revision History: Notes: --*/ #include "precomp.h" #include "TfConvArea.h" #include "TfCatUtil.h" #include "TfDispAttr.h" #include "TfEditSession.h" //+--------------------------------------------------------------------------- // // CEditSessionObject::IUnknown::QueryInterface // CEditSessionObject::IUnknown::AddRef // CEditSessionObject::IUnknown::Release // //---------------------------------------------------------------------------- STDAPI CEditSessionObject::QueryInterface(REFIID riid, void** ppvObj) { *ppvObj = nullptr; if (IsEqualIID(riid, IID_ITfEditSession)) { *ppvObj = static_cast(this); } else if (IsEqualIID(riid, IID_IUnknown)) { *ppvObj = static_cast(this); } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CEditSessionObject::AddRef() { return ++m_cRef; } STDAPI_(ULONG) CEditSessionObject::Release() { long cr; cr = --m_cRef; FAIL_FAST_IF(!(cr >= 0)); if (cr == 0) { delete this; } return cr; } //+--------------------------------------------------------------------------- // // CEditSessionObject::GetAllTextRange // //---------------------------------------------------------------------------- // static [[nodiscard]] HRESULT CEditSessionObject::GetAllTextRange(TfEditCookie ec, ITfContext* ic, ITfRange** range, LONG* lpTextLength, TF_HALTCOND* lpHaltCond) { HRESULT hr; // // init lpTextLength first. // *lpTextLength = 0; // // Create the range that covers all the text. // wil::com_ptr_nothrow rangeFull; if (FAILED(hr = ic->GetStart(ec, &rangeFull))) { return hr; } LONG cch = 0; if (FAILED(hr = rangeFull->ShiftEnd(ec, LONG_MAX, &cch, lpHaltCond))) { return hr; } if (FAILED(hr = rangeFull->Clone(range))) { return hr; } *lpTextLength = cch; return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionObject::SetTextInRange // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::SetTextInRange(TfEditCookie ec, ITfRange* range, __in_ecount_opt(len) LPWSTR psz, DWORD len) { HRESULT hr = E_FAIL; if (g_pConsoleTSF) { g_pConsoleTSF->SetModifyingDocFlag(TRUE); hr = range->SetText(ec, 0, psz, len); g_pConsoleTSF->SetModifyingDocFlag(FALSE); } return hr; } //+--------------------------------------------------------------------------- // // CEditSessionObject::ClearTextInRange // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::ClearTextInRange(TfEditCookie ec, ITfRange* range) { // // Clear the text in Cicero TOM // return SetTextInRange(ec, range, nullptr, 0); } //+--------------------------------------------------------------------------- // // CEditSessionObject::_GetCursorPosition // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::_GetCursorPosition(TfEditCookie ec, CCompCursorPos& CompCursorPos) { ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; if (pic == nullptr) { return E_FAIL; } HRESULT hr; ULONG cFetched; TF_SELECTION sel; sel.range = nullptr; if (SUCCEEDED(hr = pic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &cFetched))) { wil::com_ptr_nothrow start; LONG ich; TF_HALTCOND hc; hc.pHaltRange = sel.range; hc.aHaltPos = (sel.style.ase == TF_AE_START) ? TF_ANCHOR_START : TF_ANCHOR_END; hc.dwFlags = 0; if (SUCCEEDED(hr = GetAllTextRange(ec, pic, &start, &ich, &hc))) { CompCursorPos.SetCursorPosition(ich); } SafeReleaseClear(sel.range); } return hr; } //+--------------------------------------------------------------------------- // // CEditSessionObject::_GetTextAndAttribute // //---------------------------------------------------------------------------- // // Get text and attribute in given range // // ITfRange::range // TF_ANCHOR_START // |======================================================================| // +--------------------+ #+----------+ // |ITfRange::pPropRange| #|pPropRange| // +--------------------+ #+----------+ // | GUID_ATOM | # // +--------------------+ # // ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^# // ITfRange::gap_range gap_range # // # // V // ITfRange::no_display_attribute_range // result_comp // +1 <- 0 -> -1 // [[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttribute(TfEditCookie ec, ITfRange* rangeIn, std::wstring& CompStr, std::vector& CompGuid, std::wstring& ResultStr, BOOL bInWriteSession, CicCategoryMgr* pCicCatMgr, CicDisplayAttributeMgr* pCicDispAttr) { HRESULT hr; ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; if (pic == nullptr) { return E_FAIL; } // // Get no display attribute range if there exist. // Otherwise, result range is the same to input range. // LONG result_comp; wil::com_ptr_nothrow no_display_attribute_range; if (FAILED(hr = rangeIn->Clone(&no_display_attribute_range))) { return hr; } const GUID* guids[] = { &GUID_PROP_COMPOSING }; const int guid_size = sizeof(guids) / sizeof(GUID*); if (FAILED(hr = _GetNoDisplayAttributeRange(ec, rangeIn, guids, guid_size, no_display_attribute_range.get()))) { return hr; } wil::com_ptr_nothrow propComp; if (FAILED(hr = pic->TrackProperties(guids, guid_size, // system property NULL, 0, // application property &propComp))) { return hr; } wil::com_ptr_nothrow enumComp; if (FAILED(hr = propComp->EnumRanges(ec, &enumComp, rangeIn))) { return hr; } wil::com_ptr_nothrow range; while (enumComp->Next(1, &range, nullptr) == S_OK) { VARIANT var; BOOL fCompExist = FALSE; hr = propComp->GetValue(ec, range.get(), &var); if (S_OK == hr) { wil::com_ptr_nothrow EnumPropVal; if (wil::try_com_query_to(var.punkVal, &EnumPropVal)) { TF_PROPERTYVAL tfPropertyVal; while (EnumPropVal->Next(1, &tfPropertyVal, nullptr) == S_OK) { for (int i = 0; i < guid_size; i++) { if (IsEqualGUID(tfPropertyVal.guidId, *guids[i])) { if ((V_VT(&tfPropertyVal.varValue) == VT_I4 && V_I4(&tfPropertyVal.varValue) != 0)) { fCompExist = TRUE; break; } } } VariantClear(&tfPropertyVal.varValue); if (fCompExist) { break; } } } } VariantClear(&var); ULONG ulNumProp; wil::com_ptr_nothrow enumProp; wil::com_ptr_nothrow prop; if (FAILED(hr = pCicDispAttr->GetDisplayAttributeTrackPropertyRange(ec, pic, range.get(), &prop, &enumProp, &ulNumProp))) { return hr; } // use text range for get text wil::com_ptr_nothrow textRange; if (FAILED(hr = range->Clone(&textRange))) { return hr; } // use text range for gap text (no property range). wil::com_ptr_nothrow gap_range; if (FAILED(hr = range->Clone(&gap_range))) { return hr; } wil::com_ptr_nothrow pPropRange; while (enumProp->Next(1, &pPropRange, nullptr) == S_OK) { // pick up the gap up to the next property gap_range->ShiftEndToRange(ec, pPropRange.get(), TF_ANCHOR_START); // // GAP range // no_display_attribute_range->CompareStart(ec, gap_range.get(), TF_ANCHOR_START, &result_comp); LOG_IF_FAILED(_GetTextAndAttributeGapRange(ec, gap_range.get(), result_comp, CompStr, CompGuid, ResultStr)); // // Get display attribute data if some GUID_ATOM exist. // TF_DISPLAYATTRIBUTE da; TfGuidAtom guidatom = TF_INVALID_GUIDATOM; LOG_IF_FAILED(pCicDispAttr->GetDisplayAttributeData(pCicCatMgr->GetCategoryMgr(), ec, prop.get(), pPropRange.get(), &da, &guidatom, ulNumProp)); // // Property range // no_display_attribute_range->CompareStart(ec, pPropRange.get(), TF_ANCHOR_START, &result_comp); // Adjust GAP range's start anchor to the end of property range. gap_range->ShiftStartToRange(ec, pPropRange.get(), TF_ANCHOR_END); // // Get property text // LOG_IF_FAILED(_GetTextAndAttributePropertyRange(ec, pPropRange.get(), fCompExist, result_comp, bInWriteSession, da, guidatom, CompStr, CompGuid, ResultStr)); } // while // the last non-attr textRange->ShiftStartToRange(ec, gap_range.get(), TF_ANCHOR_START); textRange->ShiftEndToRange(ec, range.get(), TF_ANCHOR_END); BOOL fEmpty; while (textRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) { WCHAR wstr0[256 + 1]; ULONG ulcch0 = ARRAYSIZE(wstr0) - 1; textRange->GetText(ec, TF_TF_MOVESTART, wstr0, ulcch0, &ulcch0); TfGuidAtom guidatom; guidatom = TF_INVALID_GUIDATOM; TF_DISPLAYATTRIBUTE da; da.bAttr = TF_ATTR_INPUT; try { CompGuid.insert(CompGuid.end(), ulcch0, guidatom); CompStr.append(wstr0, ulcch0); } CATCH_RETURN(); } textRange->Collapse(ec, TF_ANCHOR_END); } // out-most while for GUID_PROP_COMPOSING // // set GUID_PROP_CONIME_TRACKCOMPOSITION // wil::com_ptr_nothrow PropertyTrackComposition; if (SUCCEEDED(hr = pic->GetProperty(GUID_PROP_CONIME_TRACKCOMPOSITION, &PropertyTrackComposition))) { VARIANT var; var.vt = VT_I4; var.lVal = 1; PropertyTrackComposition->SetValue(ec, rangeIn, &var); } return hr; } //+--------------------------------------------------------------------------- // // CEditSessionObject::_GetTextAndAttributeGapRange // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttributeGapRange(TfEditCookie ec, ITfRange* gap_range, LONG result_comp, std::wstring& CompStr, std::vector& CompGuid, std::wstring& ResultStr) { TfGuidAtom guidatom; guidatom = TF_INVALID_GUIDATOM; TF_DISPLAYATTRIBUTE da; da.bAttr = TF_ATTR_INPUT; BOOL fEmpty; WCHAR wstr0[256 + 1]; ULONG ulcch0; while (gap_range->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) { wil::com_ptr_nothrow backup_range; if (FAILED(gap_range->Clone(&backup_range))) { return E_FAIL; } // // Retrieve gap text if there exist. // ulcch0 = ARRAYSIZE(wstr0) - 1; if (FAILED(gap_range->GetText(ec, TF_TF_MOVESTART, // Move range to next after get text. wstr0, ulcch0, &ulcch0))) { return E_FAIL; } try { if (result_comp <= 0) { CompGuid.insert(CompGuid.end(), ulcch0, guidatom); CompStr.append(wstr0, ulcch0); } else { ResultStr.append(wstr0, ulcch0); LOG_IF_FAILED(ClearTextInRange(ec, backup_range.get())); } } CATCH_RETURN(); } return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionObject::_GetTextAndAttributePropertyRange // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::_GetTextAndAttributePropertyRange(TfEditCookie ec, ITfRange* pPropRange, BOOL fCompExist, LONG result_comp, BOOL bInWriteSession, TF_DISPLAYATTRIBUTE da, TfGuidAtom guidatom, std::wstring& CompStr, std::vector& CompGuid, std::wstring& ResultStr) { BOOL fEmpty; WCHAR wstr0[256 + 1]; ULONG ulcch0; while (pPropRange->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty) { wil::com_ptr_nothrow backup_range; if (FAILED(pPropRange->Clone(&backup_range))) { return E_FAIL; } // // Retrieve property text if there exist. // ulcch0 = ARRAYSIZE(wstr0) - 1; if (FAILED(pPropRange->GetText(ec, TF_TF_MOVESTART, // Move range to next after get text. wstr0, ulcch0, &ulcch0))) { return E_FAIL; } try { // see if there is a valid disp attribute if (fCompExist == TRUE && result_comp <= 0) { if (guidatom == TF_INVALID_GUIDATOM) { da.bAttr = TF_ATTR_INPUT; } CompGuid.insert(CompGuid.end(), ulcch0, guidatom); CompStr.append(wstr0, ulcch0); } else if (bInWriteSession) { // if there's no disp attribute attached, it probably means // the part of string is finalized. // ResultStr.append(wstr0, ulcch0); // it was a 'determined' string // so the doc has to shrink // LOG_IF_FAILED(ClearTextInRange(ec, backup_range.get())); } else { // // Prevent infinite loop // break; } } CATCH_RETURN(); } return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionObject::_GetNoDisplayAttributeRange // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionObject::_GetNoDisplayAttributeRange(TfEditCookie ec, ITfRange* rangeIn, const GUID** guids, const int guid_size, ITfRange* no_display_attribute_range) { ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; if (pic == nullptr) { return E_FAIL; } wil::com_ptr_nothrow propComp; HRESULT hr = pic->TrackProperties(guids, guid_size, // system property nullptr, 0, // application property &propComp); if (FAILED(hr)) { return hr; } wil::com_ptr_nothrow enumComp; hr = propComp->EnumRanges(ec, &enumComp, rangeIn); if (FAILED(hr)) { return hr; } wil::com_ptr_nothrow pRange; while (enumComp->Next(1, &pRange, nullptr) == S_OK) { VARIANT var; BOOL fCompExist = FALSE; hr = propComp->GetValue(ec, pRange.get(), &var); if (S_OK == hr) { wil::com_ptr_nothrow EnumPropVal; if (wil::try_com_query_to(var.punkVal, &EnumPropVal)) { TF_PROPERTYVAL tfPropertyVal; while (EnumPropVal->Next(1, &tfPropertyVal, nullptr) == S_OK) { for (int i = 0; i < guid_size; i++) { if (IsEqualGUID(tfPropertyVal.guidId, *guids[i])) { if ((V_VT(&tfPropertyVal.varValue) == VT_I4 && V_I4(&tfPropertyVal.varValue) != 0)) { fCompExist = TRUE; break; } } } VariantClear(&tfPropertyVal.varValue); if (fCompExist) { break; } } } } if (!fCompExist) { // Adjust GAP range's start anchor to the end of property range. no_display_attribute_range->ShiftStartToRange(ec, pRange.get(), TF_ANCHOR_START); } VariantClear(&var); } return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionCompositionComplete::CompComplete // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionCompositionComplete::CompComplete(TfEditCookie ec) { ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; RETURN_HR_IF_NULL(E_FAIL, pic); // Get the whole text, finalize it, and set empty string in TOM wil::com_ptr_nothrow spRange; LONG cch; RETURN_IF_FAILED(GetAllTextRange(ec, pic, &spRange, &cch)); // Check if a part of the range has already been finalized but not removed yet. // Adjust the range appropriately to avoid inserting the same text twice. long cchCompleted = g_pConsoleTSF->GetCompletedRangeLength(); if ((cchCompleted > 0) && (cchCompleted < cch) && SUCCEEDED(spRange->ShiftStart(ec, cchCompleted, &cchCompleted, NULL))) { FAIL_FAST_IF(!((cchCompleted > 0) && (cchCompleted < cch))); cch -= cchCompleted; } else { cchCompleted = 0; } // Get conversion area service. CConversionArea* conv_area = g_pConsoleTSF->GetConversionArea(); RETURN_HR_IF_NULL(E_FAIL, conv_area); // If there is no string in TextStore we don't have to do anything. if (!cch) { // Clear composition LOG_IF_FAILED(conv_area->ClearComposition()); return S_OK; } HRESULT hr = S_OK; try { auto wstr = std::make_unique(cch + 1); // Get the whole text, finalize it, and erase the whole text. if (SUCCEEDED(spRange->GetText(ec, TF_TF_IGNOREEND, wstr.get(), (ULONG)cch, (ULONG*)&cch))) { // Make Result String. hr = conv_area->DrawResult({ wstr.get(), static_cast(cch) }); } } CATCH_RETURN(); // Update the stored length of the completed fragment. g_pConsoleTSF->SetCompletedRangeLength(cchCompleted + cch); return hr; } //+--------------------------------------------------------------------------- // // CEditSessionCompositionCleanup::EmptyCompositionRange() // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionCompositionCleanup::EmptyCompositionRange(TfEditCookie ec) { if (!g_pConsoleTSF) { return E_FAIL; } if (!g_pConsoleTSF->IsPendingCompositionCleanup()) { return S_OK; } HRESULT hr = E_FAIL; ITfContext* pic = g_pConsoleTSF->GetInputContext(); if (pic != nullptr) { // Cleanup (empty the context range) after the last composition. hr = S_OK; long cchCompleted = g_pConsoleTSF->GetCompletedRangeLength(); if (cchCompleted != 0) { wil::com_ptr_nothrow spRange; LONG cch; hr = GetAllTextRange(ec, pic, &spRange, &cch); if (SUCCEEDED(hr)) { // Clean up only the completed part (which start is expected to coincide with the start of the full range). if (cchCompleted < cch) { spRange->ShiftEnd(ec, (cchCompleted - cch), &cch, nullptr); } hr = ClearTextInRange(ec, spRange.get()); g_pConsoleTSF->SetCompletedRangeLength(0); // cleaned up all completed text } } } g_pConsoleTSF->OnCompositionCleanup(SUCCEEDED(hr)); return hr; } //+--------------------------------------------------------------------------- // // CEditSessionUpdateCompositionString::UpdateCompositionString // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionUpdateCompositionString::UpdateCompositionString(TfEditCookie ec) { HRESULT hr; ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; if (pic == nullptr) { return E_FAIL; } // Reset the 'edit session requested' flag. g_pConsoleTSF->OnEditSession(); // If the composition has been cancelled\finalized, no update necessary. if (!g_pConsoleTSF->IsInComposition()) { return S_OK; } BOOL bInWriteSession; if (FAILED(hr = pic->InWriteSession(g_pConsoleTSF->GetTfClientId(), &bInWriteSession))) { return hr; } wil::com_ptr_nothrow FullTextRange; LONG lTextLength; if (FAILED(hr = GetAllTextRange(ec, pic, &FullTextRange, &lTextLength))) { return hr; } wil::com_ptr_nothrow InterimRange; BOOL fInterim = FALSE; if (FAILED(hr = _IsInterimSelection(ec, &InterimRange, &fInterim))) { return hr; } CicCategoryMgr* pCicCat = nullptr; CicDisplayAttributeMgr* pDispAttr = nullptr; // // Create Cicero Category Manager and Display Attribute Manager // hr = _CreateCategoryAndDisplayAttributeManager(&pCicCat, &pDispAttr); if (SUCCEEDED(hr)) { if (fInterim) { hr = _MakeInterimString(ec, FullTextRange.get(), InterimRange.get(), lTextLength, bInWriteSession, pCicCat, pDispAttr); } else { hr = _MakeCompositionString(ec, FullTextRange.get(), bInWriteSession, pCicCat, pDispAttr); } } if (pCicCat) { delete pCicCat; } if (pDispAttr) { delete pDispAttr; } return hr; } //+--------------------------------------------------------------------------- // // CEditSessionUpdateCompositionString::_IsInterimSelection // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_IsInterimSelection(TfEditCookie ec, ITfRange** pInterimRange, BOOL* pfInterim) { ITfContext* pic = g_pConsoleTSF ? g_pConsoleTSF->GetInputContext() : nullptr; if (pic == nullptr) { return E_FAIL; } ULONG cFetched; TF_SELECTION sel; sel.range = nullptr; *pfInterim = FALSE; if (pic->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &sel, &cFetched) != S_OK) { // no selection. we can return S_OK. return S_OK; } if (sel.style.fInterimChar && sel.range) { HRESULT hr; if (FAILED(hr = sel.range->Clone(pInterimRange))) { SafeReleaseClear(sel.range); return hr; } *pfInterim = TRUE; } SafeReleaseClear(sel.range); return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionUpdateCompositionString::_MakeCompositionString // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_MakeCompositionString(TfEditCookie ec, ITfRange* FullTextRange, BOOL bInWriteSession, CicCategoryMgr* pCicCatMgr, CicDisplayAttributeMgr* pCicDispAttr) { std::wstring CompStr; std::vector CompGuid; CCompCursorPos CompCursorPos; std::wstring ResultStr; BOOL fIgnorePreviousCompositionResult = FALSE; RETURN_IF_FAILED(_GetTextAndAttribute(ec, FullTextRange, CompStr, CompGuid, ResultStr, bInWriteSession, pCicCatMgr, pCicDispAttr)); if (g_pConsoleTSF && g_pConsoleTSF->IsPendingCompositionCleanup()) { // Don't draw the previous composition result if there was a cleanup session requested for it. fIgnorePreviousCompositionResult = TRUE; // Cancel pending cleanup, since the ResultStr was cleared from the composition in _GetTextAndAttribute. g_pConsoleTSF->OnCompositionCleanup(TRUE); } RETURN_IF_FAILED(_GetCursorPosition(ec, CompCursorPos)); // Get display attribute manager ITfDisplayAttributeMgr* dam = pCicDispAttr->GetDisplayAttributeMgr(); RETURN_HR_IF_NULL(E_FAIL, dam); // Get category manager ITfCategoryMgr* cat = pCicCatMgr->GetCategoryMgr(); RETURN_HR_IF_NULL(E_FAIL, cat); // Allocate and fill TF_DISPLAYATTRIBUTE try { // Get conversion area service. CConversionArea* conv_area = g_pConsoleTSF ? g_pConsoleTSF->GetConversionArea() : nullptr; RETURN_HR_IF_NULL(E_FAIL, conv_area); if (!ResultStr.empty() && !fIgnorePreviousCompositionResult) { return conv_area->DrawResult(ResultStr); } if (!CompStr.empty()) { const size_t cchDisplayAttribute = CompGuid.size(); std::vector DisplayAttributes; DisplayAttributes.reserve(cchDisplayAttribute); for (size_t i = 0; i < cchDisplayAttribute; i++) { TF_DISPLAYATTRIBUTE da; ZeroMemory(&da, sizeof(da)); da.bAttr = TF_ATTR_OTHER; GUID guid; if (SUCCEEDED(cat->GetGUID(CompGuid.at(i), &guid))) { CLSID clsid; wil::com_ptr_nothrow dai; if (SUCCEEDED(dam->GetDisplayAttributeInfo(guid, &dai, &clsid))) { dai->GetAttributeInfo(&da); } } DisplayAttributes.emplace_back(da); } return conv_area->DrawComposition(CompStr, // composition string DisplayAttributes, // display attributes CompCursorPos.GetCursorPosition()); // cursor position } } CATCH_RETURN(); return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionUpdateCompositionString::_MakeInterimString // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_MakeInterimString(TfEditCookie ec, ITfRange* FullTextRange, ITfRange* InterimRange, LONG lTextLength, BOOL bInWriteSession, CicCategoryMgr* pCicCatMgr, CicDisplayAttributeMgr* pCicDispAttr) { LONG lStartResult; LONG lEndResult; FullTextRange->CompareStart(ec, InterimRange, TF_ANCHOR_START, &lStartResult); RETURN_HR_IF(E_FAIL, lStartResult > 0); FullTextRange->CompareEnd(ec, InterimRange, TF_ANCHOR_END, &lEndResult); RETURN_HR_IF(E_FAIL, lEndResult < 0); if (lStartResult < 0) { // Make result string. RETURN_IF_FAILED(FullTextRange->ShiftEndToRange(ec, InterimRange, TF_ANCHOR_START)); // Interim char assume 1 char length. // Full text length - 1 means result string length. lTextLength--; FAIL_FAST_IF(!(lTextLength > 0)); if (lTextLength > 0) { try { auto wstr = std::make_unique(lTextLength + 1); // Get the result text, finalize it, and erase the result text. if (SUCCEEDED(FullTextRange->GetText(ec, TF_TF_IGNOREEND, wstr.get(), (ULONG)lTextLength, (ULONG*)&lTextLength))) { // Clear the TOM LOG_IF_FAILED(ClearTextInRange(ec, FullTextRange)); } } CATCH_RETURN(); } } // Make interim character std::wstring CompStr; std::vector CompGuid; std::wstring _tempResultStr; RETURN_IF_FAILED(_GetTextAndAttribute(ec, InterimRange, CompStr, CompGuid, _tempResultStr, bInWriteSession, pCicCatMgr, pCicDispAttr)); // Get display attribute manager ITfDisplayAttributeMgr* dam = pCicDispAttr->GetDisplayAttributeMgr(); RETURN_HR_IF_NULL(E_FAIL, dam); // Get category manager ITfCategoryMgr* cat = pCicCatMgr->GetCategoryMgr(); RETURN_HR_IF_NULL(E_FAIL, cat); // Allocate and fill TF_DISPLAYATTRIBUTE try { // Get conversion area service. CConversionArea* conv_area = g_pConsoleTSF ? g_pConsoleTSF->GetConversionArea() : nullptr; RETURN_HR_IF_NULL(E_FAIL, conv_area); if (!CompStr.empty()) { const size_t cchDisplayAttribute = CompGuid.size(); std::vector DisplayAttributes; DisplayAttributes.reserve(cchDisplayAttribute); for (size_t i = 0; i < cchDisplayAttribute; i++) { TF_DISPLAYATTRIBUTE da; ZeroMemory(&da, sizeof(da)); da.bAttr = TF_ATTR_OTHER; GUID guid; if (SUCCEEDED(cat->GetGUID(CompGuid.at(i), &guid))) { CLSID clsid; wil::com_ptr_nothrow dai; if (SUCCEEDED(dam->GetDisplayAttributeInfo(guid, &dai, &clsid))) { dai->GetAttributeInfo(&da); } } DisplayAttributes.emplace_back(da); } return conv_area->DrawComposition(CompStr, // composition string (Interim string) DisplayAttributes); // display attributes } } CATCH_RETURN(); return S_OK; } //+--------------------------------------------------------------------------- // // CEditSessionUpdateCompositionString::_CreateCategoryAndDisplayAttributeManager // //---------------------------------------------------------------------------- [[nodiscard]] HRESULT CEditSessionUpdateCompositionString::_CreateCategoryAndDisplayAttributeManager( CicCategoryMgr** pCicCatMgr, CicDisplayAttributeMgr** pCicDispAttr) { HRESULT hr = E_OUTOFMEMORY; CicCategoryMgr* pTmpCat = nullptr; CicDisplayAttributeMgr* pTmpDispAttr = nullptr; // // Create Cicero Category Manager // pTmpCat = new (std::nothrow) CicCategoryMgr; if (pTmpCat) { if (SUCCEEDED(hr = pTmpCat->InitCategoryInstance())) { ITfCategoryMgr* pcat = pTmpCat->GetCategoryMgr(); if (pcat) { // // Create Cicero Display Attribute Manager // pTmpDispAttr = new (std::nothrow) CicDisplayAttributeMgr; if (pTmpDispAttr) { if (SUCCEEDED(hr = pTmpDispAttr->InitDisplayAttributeInstance(pcat))) { *pCicCatMgr = pTmpCat; *pCicDispAttr = pTmpDispAttr; } } } } } if (FAILED(hr)) { if (pTmpCat) { delete pTmpCat; } if (pTmpDispAttr) { delete pTmpDispAttr; } } return hr; }