STDAPI CTextService::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { BOOL fSelectionChanged; IEnumTfRanges *pEnumTextChanges; ITfRange *pRange; // // did the selection change? // The selection change includes the movement of caret as well. // The caret position is represent as the empty selection range when // there is no selection. // if (pEditRecord->GetSelectionStatus(&fSelectionChanged) == S_OK && fSelectionChanged) { } // text modification? if (pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, &pEnumTextChanges) == S_OK) { if (pEnumTextChanges->Next(1, &pRange, NULL) == S_OK) { // // pRange is the updated range. // pRange->Release(); } pEnumTextChanges->Release(); } return S_OK; }
STDAPI touchmind::control::DWriteEditControlTextEditSink::OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { std::shared_ptr<control::DWriteEditControl> pEditControl = m_pEditControl.lock(); DisplayAttributeProperties dispAttrProps; HRESULT hr = m_displayAttribute.GetDisplayAttributeProperties(dispAttrProps); if (SUCCEEDED(hr)) { IEnumTfRanges *pEnum; hr = pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, dispAttrProps.GetPropTablePointer(), static_cast<ULONG>(dispAttrProps.Count()), &pEnum); if (SUCCEEDED(hr) && pEnum) { ITfRange *pRange; if (pEnum->Next(1, &pRange, nullptr) == S_OK) { pRange->Release(); pEditControl->ClearCompositionRenderInfo(); ITfRange *pRangeEntire = nullptr; ITfRange *pRangeEnd = nullptr; if (SUCCEEDED(pic->GetStart(ecReadOnly, &pRangeEntire)) && SUCCEEDED(pic->GetEnd(ecReadOnly, &pRangeEnd)) && SUCCEEDED(pRangeEntire->ShiftEndToRange(ecReadOnly, pRangeEnd, TF_ANCHOR_END))) { IEnumTfRanges *pEnumRanges; ITfReadOnlyProperty *pProp = nullptr; m_displayAttribute.GetDisplayAttributeTrackPropertyRange(pic, dispAttrProps, &pProp); if (SUCCEEDED(pProp->EnumRanges(ecReadOnly, &pEnumRanges, pRangeEntire))) { while (pEnumRanges->Next(1, &pRange, nullptr) == S_OK) { TF_DISPLAYATTRIBUTE da; ZeroMemory(&da, sizeof(da)); TfGuidAtom guid; if (m_displayAttribute.GetDisplayAttributeData(ecReadOnly, pProp, pRange, &da, &guid) == S_OK) { ITfRangeACP *pRangeACP; if (pRange->QueryInterface(IID_ITfRangeACP, (void **)&pRangeACP) == S_OK) { LONG nStart; LONG nEnd; pRangeACP->GetExtent(&nStart, &nEnd); pEditControl->AddCompositionRenderInfo(nStart, nStart + nEnd, &da); pRangeACP->Release(); } } } } } if (pRangeEntire) { pRangeEntire->Release(); } if (pRangeEnd) { pRangeEnd->Release(); } } pEnum->Release(); } } return S_OK; }
STDAPI CCaseTextService::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { BOOL fSelectionChanged; IEnumTfRanges *pEnumTextChanges; ITfRange *pRange; // we'll use the endedit notification to update the snoop window // did the selection change? if (pEditRecord->GetSelectionStatus(&fSelectionChanged) == S_OK && fSelectionChanged) { _pSnoopWnd->_UpdateText(ecReadOnly, pContext, NULL); return S_OK; } // text modification? if (pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, &pEnumTextChanges) == S_OK) { if (pEnumTextChanges->Next(1, &pRange, NULL) == S_OK) { // arbitrary update the snoop window with the first change // there may be more than one in the enumerator, but we don't care here _pSnoopWnd->_UpdateText(ecReadOnly, pContext, pRange); pRange->Release(); } pEnumTextChanges->Release(); } // if we get here, only property values changed return S_OK; }
STDAPI CTextService::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { BOOL fSelectionChanged; IEnumTfRanges *pEnumTextChanges; ITfRange *pRange; // // did the selection change? // The selection change includes the movement of caret as well. // The caret position is represent as the empty selection range when // there is no selection. // if (pEditRecord->GetSelectionStatus(&fSelectionChanged) == S_OK && fSelectionChanged) { // If the selection is moved to out side of the current composition, // we terminate the composition. This TextService supports only one // composition in one context object. if (_IsComposing()) { TF_SELECTION tfSelection; ULONG cFetched; if (pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) == S_OK && cFetched == 1) { ITfRange *pRangeComposition; // is the insertion point covered by a composition? if (_pComposition->GetRange(&pRangeComposition) == S_OK) { if (!IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition)) { _EndComposition(pContext); } pRangeComposition->Release(); } } } } // text modification? if (pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, &pEnumTextChanges) == S_OK) { if (pEnumTextChanges->Next(1, &pRange, NULL) == S_OK) { // // pRange is the updated range. // pRange->Release(); } pEnumTextChanges->Release(); } return S_OK; }
BOOL CSampleIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange) { if (ppRange == nullptr) { return FALSE; } *ppRange = nullptr; // find GUID_PROP_COMPOSING ITfProperty* pPropComp = nullptr; IEnumTfRanges* enumComp = nullptr; /* GetProperty(GUID_PROP_COMPOSING, ...) -- nonzero if text is part of a composition */ HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); if (FAILED(hr) || pPropComp == nullptr) { return FALSE; } /* EnumRanges: Obtains an enumeration of ranges that contain unique values of the property GUID_PROP_COMPOSING within the given range */ hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); if (FAILED(hr) || enumComp == nullptr) { pPropComp->Release(); return FALSE; } BOOL isCompExist = FALSE; VARIANT var; ULONG fetched = 0; while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) { hr = pPropComp->GetValue(ec, *ppRange, &var); if (hr == S_OK) { if (var.vt == VT_I4 && var.lVal != 0) { isCompExist = TRUE; break; } } (*ppRange)->Release(); *ppRange = nullptr; } pPropComp->Release(); enumComp->Release(); return isCompExist; }
BOOL CDIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange) { debugPrint(L"CDIME::_FindComposingRange()"); if (pContext == nullptr || ppRange == nullptr) { debugPrint(L"CDIME::_FindComposingRange() failed with null pContext or pRange"); return FALSE; } *ppRange = nullptr; // find GUID_PROP_COMPOSING ITfProperty* pPropComp = nullptr; IEnumTfRanges* enumComp = nullptr; HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); if (FAILED(hr) || pPropComp == nullptr) { if (FAILED(hr)) debugPrint(L"CDIME::_FindComposingRange() pContext->GetProperty() failed"); else debugPrint(L"CDIME::_FindComposingRange() failed with null pPropComp"); return FALSE; } hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); if (FAILED(hr) || enumComp == nullptr) { if (FAILED(hr)) debugPrint(L"CDIME::_FindComposingRange() pPropComp->EnumRanges failed"); else debugPrint(L"CDIME::_FindComposingRange() failed with null enumComp"); pPropComp->Release(); return FALSE; } BOOL isCompExist = FALSE; VARIANT var; ULONG fetched = 0; while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) { debugPrint(L"CDIME::_FindComposingRange() enumComp->Next() "); hr = pPropComp->GetValue(ec, *ppRange, &var); if (hr == S_OK) { if (var.vt == VT_I4 && var.lVal != 0) { WCHAR rangeText[4096]; rangeText[0] = NULL; fetched = 0; hr = (*ppRange)->GetText(ec, 0, rangeText, 4096, &fetched); if (SUCCEEDED(hr)) { if (lastReadingString.GetLength() > 0) // The last reading string is null when composing is just started and the range should be empty. { debugPrint(L"CDIME::_FindComposingRange() text in range = %s with %d character, last reading string = %s with %d characters", rangeText, fetched, lastReadingString.Get(), lastReadingString.GetLength()); UINT cmpCount = (int)lastReadingString.GetLength(); BOOL rangeMatchReadingString = FALSE; if (fetched > 0 && fetched > cmpCount) { LONG shifted = 0; // range contains other characters than last reading string, and these characters should be skipped with shifting the start anchor (*ppRange)->ShiftStart(ec, fetched - cmpCount, &shifted, NULL); debugPrint(L"CDIME::_FindComposingRange() shift start anchor with %d characters", shifted); rangeText[0] = NULL; hr = (*ppRange)->GetText(ec, 0, rangeText, 4096, &fetched); if (SUCCEEDED(hr)) debugPrint(L"CDIME::_FindComposingRange() text in range with shited anchor = %s with %d character", rangeText, fetched); } // The range we are composing should always matched with the last reading string. rangeMatchReadingString = CompareString(GetLocale(), 0, rangeText, cmpCount, lastReadingString.Get(), cmpCount) == CSTR_EQUAL; if (rangeMatchReadingString) { isCompExist = TRUE; debugPrint(L"CDIME::_FindComposingRange() text in this range mathced with last reading string. isCompExist = TRUE"); break; } } } else { debugPrint(L"CDIME::_FindComposingRange() cannot getText from range isCompExist = TRUE"); } } } debugPrint(L"CDIME::_FindComposingRange() release *ppRange"); if(*ppRange) (*ppRange)->Release(); *ppRange = nullptr; } pPropComp->Release(); enumComp->Release(); return isCompExist; }
STDAPI CMarkTextService::OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { ITfRange *pRangeComposition; IEnumTfRanges *pEnumRanges; ITfRange *pRange; ITfContext *pCompositionContext; TF_SELECTION tfSelection; BOOL fResult; BOOL fCancelComposition; ULONG cFetched; if (_pComposition == NULL) return S_OK; // are we responsible for the edit? if (pContext->InWriteSession(_tfClientId, &fResult) == S_OK && fResult) return S_OK; // is this the context our composition lives in? if (_pComposition->GetRange(&pRangeComposition) != S_OK) return S_OK; if (pRangeComposition->GetContext(&pCompositionContext) != S_OK) goto Exit; fResult = IsEqualUnknown(pCompositionContext, pContext); pCompositionContext->Release(); if (!fResult) goto Exit; // different context fCancelComposition = FALSE; // we're composing in this context, cancel the composition if anything suspicious happened // did the selection move outside the composition? if (pEditRecord->GetSelectionStatus(&fResult) == S_OK && fResult) { if (pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) == S_OK && cFetched == 1) { if (_pComposition->GetRange(&pRangeComposition) == S_OK) { fResult = IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition); pRangeComposition->Release(); if (!fResult) { fCancelComposition = TRUE; } } tfSelection.range->Release(); } } if (fCancelComposition) goto CancelComposition; // did someone else edit the document text? if (pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, &pEnumRanges) == S_OK) { // is the enumerator empty? if (pEnumRanges->Next(1, &pRange, NULL) == S_OK) { pRange->Release(); fCancelComposition = TRUE; } pEnumRanges->Release(); } if (fCancelComposition) { CancelComposition: // we need a write edit session to cancel the composition _TerminateCompositionInContext(pContext); } Exit: pRangeComposition->Release(); return S_OK; }
STDAPI CTextEditSink::OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { CDispAttrProps *pDispAttrProps = GetDispAttrProps(); if (pDispAttrProps) { IEnumTfRanges *pEnum; if (SUCCEEDED(pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, pDispAttrProps->GetPropTablePointer(), pDispAttrProps->Count(), &pEnum)) && pEnum) { ITfRange *pRange; if (pEnum->Next(1, &pRange, NULL) == S_OK) { // We check if there is a range to be changed. pRange->Release(); _pEditor->ClearCompositionRenderInfo(); // We read the display attribute for entire range. // It could be optimized by filtering the only delta with ITfEditRecord interface. ITfRange *pRangeEntire = NULL; ITfRange *pRangeEnd = NULL; if (SUCCEEDED(pic->GetStart(ecReadOnly, &pRangeEntire)) && SUCCEEDED(pic->GetEnd(ecReadOnly, &pRangeEnd)) && SUCCEEDED(pRangeEntire->ShiftEndToRange(ecReadOnly, pRangeEnd, TF_ANCHOR_END))) { IEnumTfRanges *pEnumRanges; ITfReadOnlyProperty *pProp = NULL; GetDisplayAttributeTrackPropertyRange(ecReadOnly, pic, pRangeEntire, &pProp, pDispAttrProps); if (SUCCEEDED(pProp->EnumRanges(ecReadOnly, &pEnumRanges, pRangeEntire))) { while (pEnumRanges->Next(1, &pRange, NULL) == S_OK) { TF_DISPLAYATTRIBUTE da; TfGuidAtom guid; if (GetDisplayAttributeData(ecReadOnly, pProp, pRange, &da, &guid) == S_OK) { ITfRangeACP *pRangeACP; if (pRange->QueryInterface(IID_ITfRangeACP, (void **)&pRangeACP) == S_OK) { LONG nStart; LONG nEnd; pRangeACP->GetExtent(&nStart, &nEnd); _pEditor->AddCompositionRenderInfo(nStart, nStart + nEnd, &da); pRangeACP->Release(); } } } } } if (pRangeEntire) pRangeEntire->Release(); if (pRangeEnd) pRangeEnd->Release(); } pEnum->Release(); } delete pDispAttrProps; } return S_OK; }
void CTSFEditWnd::_Save(IStream *pStream) { if(pStream) { HRESULT hr; //write the plain UNICODE text into the stream LPWSTR pwsz; LONG cch; ULONG uWritten; LARGE_INTEGER li; ULONG uSize; //set the stream pointer to the start of the stream li.QuadPart = 0; pStream->Seek(li, STREAM_SEEK_SET, NULL); //get the text if(SUCCEEDED(_GetText(&pwsz, &cch))) { TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader; //write the size, in BYTES, of the text uSize = cch * sizeof(WCHAR); hr = pStream->Write(&uSize, sizeof(ULONG), &uWritten); //write the text, including the NULL_terminator, into the stream hr = pStream->Write(pwsz, uSize, &uWritten); //free the memory allocated by _GetText GlobalFree(pwsz); //enumerate the properties in the context IEnumTfProperties *pEnumProps; hr = m_pContext->EnumProperties(&pEnumProps); if(SUCCEEDED(hr)) { ITfProperty *pProp; ULONG uFetched; while(SUCCEEDED(pEnumProps->Next(1, &pProp, &uFetched)) && uFetched) { //enumerate all the ranges that contain the property IEnumTfRanges *pEnumRanges; hr = pProp->EnumRanges(m_EditCookie, &pEnumRanges, NULL); if(SUCCEEDED(hr)) { IStream *pTempStream; //create a temporary stream to write the property data to hr = CreateStreamOnHGlobal(NULL, TRUE, &pTempStream); if(SUCCEEDED(hr)) { ITfRange *pRange; while(SUCCEEDED(pEnumRanges->Next(1, &pRange, &uFetched)) && uFetched) { //reset the temporary stream's pointer li.QuadPart = 0; pTempStream->Seek(li, STREAM_SEEK_SET, NULL); //get the property header and data for the range hr = m_pServices->Serialize(pProp, pRange, &PropHeader, pTempStream); /* Write the property header into the primary stream. The header also contains the size of the property data. */ hr = pStream->Write(&PropHeader, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uWritten); //reset the temporary stream's pointer li.QuadPart = 0; pTempStream->Seek(li, STREAM_SEEK_SET, NULL); //copy the property data from the temporary stream into the primary stream ULARGE_INTEGER uli; uli.QuadPart = PropHeader.cb; hr = pTempStream->CopyTo(pStream, uli, NULL, NULL); pRange->Release(); } pTempStream->Release(); } pEnumRanges->Release(); } pProp->Release(); } pEnumProps->Release(); } //write a property header with zero size and guid into the stream as a terminator ZeroMemory(&PropHeader, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP)); hr = pStream->Write(&PropHeader, sizeof(TF_PERSISTENT_PROPERTY_HEADER_ACP), &uWritten); } } }