void InsertTextAtSelection(TfEditCookie ec, ITfContext *pContext, const WCHAR *pchText, ULONG cchText) { ITfInsertAtSelection *pInsertAtSelection; ITfRange *pRange; TF_SELECTION tfSelection; // we need a special interface to insert text at the selection if (pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK) return; // insert the text if (pInsertAtSelection->InsertTextAtSelection(ec, 0, pchText, cchText, &pRange) != S_OK) goto Exit; // update the selection, we'll make it an insertion point just past // the inserted text. pRange->Collapse(ec, TF_ANCHOR_END); tfSelection.range = pRange; tfSelection.style.ase = TF_AE_NONE; tfSelection.style.fInterimChar = FALSE; pContext->SetSelection(ec, 1, &tfSelection); pRange->Release(); Exit: pInsertAtSelection->Release(); }
HRESULT COVTSF::_HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction) { ITfRange* pRangeComposition = nullptr; TF_SELECTION tfSelection; ULONG fetched = 0; // get the selection if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) { // no selection, eat the keystroke return S_OK; } // get the composition range if (FAILED(_pComposition->GetRange(&pRangeComposition))) { goto Exit; } // For incremental candidate list if (_pCandidateListUIPresenter) { _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); } pContext->SetSelection(ec, 1, &tfSelection); pRangeComposition->Release(); Exit: tfSelection.range->Release(); return S_OK; }
HRESULT CDIME::_CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) { debugPrint(L"CDIME::_CreateAndStartCandidate(), _candidateMode = %d", _candidateMode); HRESULT hr = S_OK; if ((_candidateMode == CANDIDATE_NONE) && (_pUIPresenter)) { // we don't cache the document manager object. So get it from pContext. ITfDocumentMgr* pDocumentMgr = nullptr; if (SUCCEEDED(pContext->GetDocumentMgr(&pDocumentMgr))) { // get the composition range. ITfRange* pRange = nullptr; if (SUCCEEDED(_pComposition->GetRange(&pRange))) { hr = _pUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, pCompositionProcessorEngine->GetCandidateWindowWidth()); pRange->Release(); } pDocumentMgr->Release(); } } return hr; }
STDAPI WeaselTSF::DoEditSession(TfEditCookie ec) { ITfInsertAtSelection *pInsertAtSelection; ITfRange *pRange; TF_SELECTION tfSelection; if (_pEditSessionContext->QueryInterface(IID_ITfInsertAtSelection, (LPVOID *) &pInsertAtSelection) != S_OK) return E_FAIL; /* insert the text */ if (pInsertAtSelection->InsertTextAtSelection(ec, 0, _pEditSessionText, _cEditSessionText, &pRange) != S_OK) { pInsertAtSelection->Release(); return E_FAIL; } /* update the selection to an insertion point just past the inserted text. */ pRange->Collapse(ec, TF_ANCHOR_END); tfSelection.range = pRange; tfSelection.style.ase = TF_AE_NONE; tfSelection.style.fInterimChar = FALSE; _pEditSessionContext->SetSelection(ec, 1, &tfSelection); pRange->Release(); pInsertAtSelection->Release(); return S_OK; }
// callback from edit session for ending composition HRESULT TextService::doEndCompositionEditSession(TfEditCookie cookie, EndCompositionEditSession* session) { if(composition_) { // move current insertion point to end of the composition string ITfRange* compositionRange; if(composition_->GetRange(&compositionRange) == S_OK) { // clear display attribute for the composition range ComPtr<ITfProperty> dispAttrProp; if(session->context()->GetProperty(GUID_PROP_ATTRIBUTE, &dispAttrProp) == S_OK) { dispAttrProp->Clear(cookie, compositionRange); } TF_SELECTION selection; ULONG selectionNum; if(session->context()->GetSelection(cookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { selection.range->ShiftEndToRange(cookie, compositionRange, TF_ANCHOR_END); selection.range->Collapse(cookie, TF_ANCHOR_END); session->context()->SetSelection(cookie, 1, &selection); selection.range->Release(); } compositionRange->Release(); } // end composition and clean up composition_->EndComposition(cookie); // do some cleanup in the derived class here onCompositionTerminated(false); composition_->Release(); composition_ = NULL; } return S_OK; }
// callback from edit session for starting composition HRESULT TextService::doStartCompositionEditSession(TfEditCookie cookie, StartCompositionEditSession* session) { ITfContext* context = session->context(); ITfContextComposition* contextComposition; if(context->QueryInterface(IID_ITfContextComposition, (void**)&contextComposition) == S_OK) { // get current insertion point in the current context ITfRange* range = NULL; ITfInsertAtSelection* insertAtSelection; if(context->QueryInterface(IID_ITfInsertAtSelection, (void **)&insertAtSelection) == S_OK) { // get current selection range & insertion position (query only, did not insert any text) insertAtSelection->InsertTextAtSelection(cookie, TF_IAS_QUERYONLY, NULL, 0, &range); insertAtSelection->Release(); } if(range) { if(contextComposition->StartComposition(cookie, range, (ITfCompositionSink*)this, &composition_) == S_OK) { // according to the TSF sample provided by M$, we need to reset current // selection here. (maybe the range is altered by StartComposition()? // So mysterious. TSF is absolutely overly-engineered! TF_SELECTION selection; selection.range = range; selection.style.ase = TF_AE_NONE; selection.style.fInterimChar = FALSE; context->SetSelection(cookie, 1, &selection); // we did not release composition_ object. we store it for use later } range->Release(); } contextComposition->Release(); } return S_OK; }
BOOL CTextService::_SetCompositionDisplayAttributes(TfEditCookie ec, ITfContext *pContext, TfGuidAtom gaDisplayAttribute) { ITfRange *pRangeComposition; ITfProperty *pDisplayAttributeProperty; HRESULT hr; // we need a range and the context it lives in if (_pComposition->GetRange(&pRangeComposition) != S_OK) return FALSE; hr = E_FAIL; // get our the display attribute property if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) == S_OK) { VARIANT var; // set the value over the range // the application will use this guid atom to lookup the acutal rendering information var.vt = VT_I4; // we're going to set a TfGuidAtom var.lVal = gaDisplayAttribute; hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var); pDisplayAttributeProperty->Release(); } pRangeComposition->Release(); return (hr == S_OK); }
// check if current insertion point is in the range of composition. // if not in range, insertion is now allowed bool TextService::isInsertionAllowed(EditSession* session) { TfEditCookie cookie = session->editCookie(); TF_SELECTION selection; ULONG selectionNum; if(isComposing()) { if(session->context()->GetSelection(cookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { ITfRange* compositionRange; if(composition_->GetRange(&compositionRange) == S_OK) { bool allowed = false; // check if current selection is covered by composition range LONG compareResult1; LONG compareResult2; if(selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_START, &compareResult1) == S_OK && selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_END, &compareResult2) == S_OK) { if(compareResult1 == -1 && compareResult2 == +1) allowed = true; } compositionRange->Release(); } if(selection.range) selection.range->Release(); } } return false; }
HRESULT CTextService::_HandleCharacterKey(TfEditCookie ec, ITfContext *pContext, WPARAM wParam) { if (!_IsComposing()) _StartComposition(pContext); // // create an instance of the candidate list class. // if (_pCandidateList == NULL) _pCandidateList = new CCandidateList(this); // // The document manager object is not cached. Get it from pContext. // ITfDocumentMgr *pDocumentMgr; if (pContext->GetDocumentMgr(&pDocumentMgr) == S_OK) { // // get the composition range. // ITfRange *pRange; if (_pComposition->GetRange(&pRange) == S_OK) { _pCandidateList->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange); pRange->Release(); } pDocumentMgr->Release(); } if (_IsComposing()) _TerminateComposition(ec, pContext); 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) { } // 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; }
void CMarkTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec) { ITfRange *pRangeComposition; ITfContext *pContext; ITfProperty *pDisplayAttributeProperty; // we need a range and the context it lives in if (_pComposition->GetRange(&pRangeComposition) != S_OK) return; if (pRangeComposition->GetContext(&pContext) != S_OK) { pContext = NULL; goto Exit; } // get our the display attribute property if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK) goto Exit; // clear the value over the range pDisplayAttributeProperty->Clear(ec, pRangeComposition); pDisplayAttributeProperty->Release(); Exit: pRangeComposition->Release(); SafeRelease(pContext); }
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; }
HRESULT COVTSF::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext) { ITfRange* pRangeComposition = nullptr; TF_SELECTION tfSelection; ULONG fetched = 0; BOOL isCovered = TRUE; // Start the new (std::nothrow) compositon if there is no composition. if (!_IsComposing()) { return S_OK; } // first, test where a keystroke would go in the document if we did an insert if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) { return S_FALSE; } // is the insertion point covered by a composition? if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) { isCovered = _IsRangeCovered(ec, tfSelection.range, pRangeComposition); pRangeComposition->Release(); if (!isCovered) { goto Exit; } } // // Add virtual key to composition processor engine // CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; pCompositionProcessorEngine = _pCompositionProcessorEngine; DWORD_PTR vKeyLen = pCompositionProcessorEngine->GetVirtualKeyLength(); if (vKeyLen) { pCompositionProcessorEngine->RemoveVirtualKey(vKeyLen - 1); if (pCompositionProcessorEngine->GetVirtualKeyLength()) { _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); } else { _HandleCancel(ec, pContext); } } Exit: tfSelection.range->Release(); return S_OK; }
HRESULT COVTSF::_HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCandidateList) { HRESULT hr = S_OK; if (isCandidateList && _pCandidateListUIPresenter) { // Finalize selected candidate string from CCandidateListUIPresenter DWORD_PTR candidateLen = 0; const WCHAR *pCandidateString = nullptr; candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); CStringRange candidateString; candidateString.Set(pCandidateString, candidateLen); if (candidateLen) { // Finalize character hr = _AddCharAndFinalize(ec, pContext, &candidateString); if (FAILED(hr)) { return hr; } } } else { // Finalize current text store strings if (_IsComposing()) { ULONG fetched = 0; TF_SELECTION tfSelection; if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) { return S_FALSE; } ITfRange* pRangeComposition = nullptr; if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) { if (_IsRangeCovered(ec, tfSelection.range, pRangeComposition)) { _EndComposition(pContext); } pRangeComposition->Release(); } tfSelection.range->Release(); } } _HandleCancel(ec, pContext); 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; }
HRESULT CTextService::_HandleCharacterKey(TfEditCookie ec, ITfContext *pContext, WPARAM wParam) { ITfRange *pRangeComposition; TF_SELECTION tfSelection; ULONG cFetched; WCHAR ch; BOOL fCovered; // Start the new compositon if there is no composition. OutputDebugString("test compositing"); if (!_IsComposing()) _StartComposition(pContext); OutputDebugString("after test compositing"); // // Assign VK_ value to the char. So the inserted the character is always // uppercase. // ch = (WCHAR)wParam; // first, test where a keystroke would go in the document if an insert is done if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK || cFetched != 1) return S_FALSE; // is the insertion point covered by a composition? if (_pComposition->GetRange(&pRangeComposition) == S_OK) { fCovered = IsRangeCovered(ec, tfSelection.range, pRangeComposition); pRangeComposition->Release(); if (!fCovered) { goto Exit; } } // insert the text // use SetText here instead of InsertTextAtSelection because a composition was already started //Don't allow to the app to adjust the insertion point inside the composition if (tfSelection.range->SetText(ec, 0, &ch, 1) != S_OK) goto Exit; // update the selection, make it an insertion point just past // the inserted text. tfSelection.range->Collapse(ec, TF_ANCHOR_END); pContext->SetSelection(ec, 1, &tfSelection); // // set the display attribute to the composition range. // _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); Exit: tfSelection.range->Release(); return S_OK; }
STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) { ITfInsertAtSelection *pInsertAtSelection = NULL; ITfRange *pRangeInsert = NULL; ITfContextComposition *pContextComposition = NULL; ITfComposition *pComposition = NULL; HRESULT hr = E_FAIL; // we need a special interface to insert text at the selection if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK) { goto Exit; } // insert the text if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert) != S_OK) { goto Exit; } // get an interface on the context we can use to deal with compositions if (_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition) != S_OK) { goto Exit; } // start the new composition if ((pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition) == S_OK) && (pComposition != NULL)) { // Store the pointer of this new composition object in the instance // of the CTextService class. So this instance of the CTextService // class can know now it is in the composition stage. _pTextService->_SetComposition(pComposition); // // set selection to the adjusted range // TF_SELECTION tfSelection; tfSelection.range = pRangeInsert; tfSelection.style.ase = TF_AE_NONE; tfSelection.style.fInterimChar = FALSE; _pContext->SetSelection(ec, 1, &tfSelection); } Exit: if (pContextComposition != NULL) pContextComposition->Release(); if (pRangeInsert != NULL) pRangeInsert->Release(); if (pInsertAtSelection != NULL) pInsertAtSelection->Release(); return S_OK; }
STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) { ITfInsertAtSelection* pInsertAtSelection = nullptr; ITfRange* pRangeInsert = nullptr; ITfContextComposition* pContextComposition = nullptr; ITfComposition* pComposition = nullptr; if (FAILED(_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection))) { goto Exit; } if (FAILED(pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert))) { goto Exit; } if (FAILED(_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition))) { goto Exit; } if (SUCCEEDED(pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition)) && (nullptr != pComposition)) { _pTextService->_SetComposition(pComposition); // set selection to the adjusted range TF_SELECTION tfSelection; tfSelection.range = pRangeInsert; tfSelection.style.ase = TF_AE_NONE; tfSelection.style.fInterimChar = FALSE; _pContext->SetSelection(ec, 1, &tfSelection); _pTextService->_SaveCompositionContext(_pContext); } Exit: if (nullptr != pContextComposition) { pContextComposition->Release(); } if (nullptr != pRangeInsert) { pRangeInsert->Release(); } if (nullptr != pInsertAtSelection) { pInsertAtSelection->Release(); } return S_OK; }
STDAPI CEndCompositionEditSession::DoEditSession(TfEditCookie ec) { /* Clear the dummy text we set before, if any. */ ITfRange *pCompositionRange; if (_pComposition->GetRange(&pCompositionRange) == S_OK) pCompositionRange->SetText(ec, 0, L"", 0); _pComposition->EndComposition(ec); _pTextService->OnCompositionTerminated(ec, _pComposition); return S_OK; }
HRESULT CDIME::_ProbeCompositionRangeNotification(_In_ TfEditCookie ec, _In_ ITfContext *pContext) { debugPrint(L"CDIME::_ProbeCompositionRangeNotification(), \n"); HRESULT hr = S_OK; if(!_IsComposing()) _StartComposition(pContext); hr = E_FAIL; ULONG fetched = 0; TF_SELECTION tfSelection; if ( pContext == nullptr || (hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) != S_OK || fetched != 1 ||tfSelection.range==nullptr) { _TerminateComposition(ec,pContext); return hr; } tfSelection.range->Release(); ITfRange *pRange; ITfContextView* pContextView; ITfDocumentMgr* pDocumgr; if (_pComposition&&SUCCEEDED(_pComposition->GetRange(&pRange)) && pRange) { if(pContext && SUCCEEDED(pContext->GetActiveView(&pContextView)) && pContextView) { if(pContext && SUCCEEDED( pContext->GetDocumentMgr(&pDocumgr)) && pDocumgr && _pThreadMgr &&_pUIPresenter) { ITfDocumentMgr* pFocusDocuMgr; _pThreadMgr->GetFocus(&pFocusDocuMgr); if(pFocusDocuMgr == pDocumgr) { _pUIPresenter->_StartLayout(pContext, ec, pRange); _pUIPresenter->_MoveUIWindowsToTextExt(); } else _pUIPresenter->ClearNotify(); pDocumgr->Release(); } pContextView->Release(); } pRange->Release(); } _TerminateComposition(ec,pContext); return hr; }
STDAPI CSampleIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly, __RPC__in_opt ITfEditRecord *pEditRecord) { BOOL isSelectionChanged; // // 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 == nullptr) { return E_INVALIDARG; } if (SUCCEEDED(pEditRecord->GetSelectionStatus(&isSelectionChanged)) && isSelectionChanged) { // 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 fetched = 0; if (pContext == nullptr) { return E_INVALIDARG; } if (FAILED(pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) { return S_FALSE; } ITfRange* pRangeComposition = nullptr; if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) { if (!_IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition)) { _EndComposition(pContext); } pRangeComposition->Release(); } tfSelection.range->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; }
HRESULT CTextService::_HandleArrowKey(TfEditCookie ec, ITfContext *pContext, WPARAM wParam) { ITfRange *pRangeComposition; LONG cch; BOOL fEqual; TF_SELECTION tfSelection; ULONG cFetched; // get the selection if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK || cFetched != 1) { // no selection? return S_OK; // eat the keystroke } // get the composition range if (_pComposition->GetRange(&pRangeComposition) != S_OK) goto Exit; // adjust the selection, we won't do anything fancy if (wParam == VK_LEFT) { if (tfSelection.range->IsEqualStart(ec, pRangeComposition, TF_ANCHOR_START, &fEqual) == S_OK && !fEqual) { tfSelection.range->ShiftStart(ec, -1, &cch, NULL); } tfSelection.range->Collapse(ec, TF_ANCHOR_START); } else { // VK_RIGHT if (tfSelection.range->IsEqualEnd(ec, pRangeComposition, TF_ANCHOR_END, &fEqual) == S_OK && !fEqual) { tfSelection.range->ShiftEnd(ec, +1, &cch, NULL); } tfSelection.range->Collapse(ec, TF_ANCHOR_END); } pContext->SetSelection(ec, 1, &tfSelection); pRangeComposition->Release(); Exit: tfSelection.range->Release(); return S_OK; // eat the keystroke }
HRESULT COVTSF::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch) { ITfRange* pRangeComposition = nullptr; TF_SELECTION tfSelection; ULONG fetched = 0; BOOL isCovered = TRUE; CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; pCompositionProcessorEngine = _pCompositionProcessorEngine; if ((_pCandidateListUIPresenter != nullptr) && (_candidateMode != CANDIDATE_INCREMENTAL)) { _HandleCompositionFinalize(ec, pContext, FALSE); } // Start the new (std::nothrow) compositon if there is no composition. if (!_IsComposing()) { _StartComposition(pContext); } // first, test where a keystroke would go in the document if we did an insert if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched != 1) { return S_FALSE; } // is the insertion point covered by a composition? if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) { isCovered = _IsRangeCovered(ec, tfSelection.range, pRangeComposition); pRangeComposition->Release(); if (!isCovered) { goto Exit; } } // Add virtual key to composition processor engine pCompositionProcessorEngine->AddVirtualKey(wch); _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); Exit: tfSelection.range->Release(); return S_OK; }
HRESULT COVTSF::_CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) { HRESULT hr = S_OK; if (((_candidateMode == CANDIDATE_PHRASE) && (_pCandidateListUIPresenter)) || ((_candidateMode == CANDIDATE_NONE) && (_pCandidateListUIPresenter))) { // Recreate candidate list _pCandidateListUIPresenter->_EndCandidateList(); delete _pCandidateListUIPresenter; _pCandidateListUIPresenter = nullptr; _candidateMode = CANDIDATE_NONE; _isCandidateWithWildcard = FALSE; } if (_pCandidateListUIPresenter == nullptr) { _pCandidateListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, CATEGORY_CANDIDATE, pCompositionProcessorEngine->GetCandidateListIndexRange(), FALSE); if (!_pCandidateListUIPresenter) { return E_OUTOFMEMORY; } _candidateMode = CANDIDATE_INCREMENTAL; _isCandidateWithWildcard = FALSE; // we don't cache the document manager object. So get it from pContext. ITfDocumentMgr* pDocumentMgr = nullptr; if (SUCCEEDED(pContext->GetDocumentMgr(&pDocumentMgr))) { // get the composition range. ITfRange* pRange = nullptr; if (SUCCEEDED(_pComposition->GetRange(&pRange))) { hr = _pCandidateListUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, pCompositionProcessorEngine->GetCandidateWindowWidth()); pRange->Release(); } pDocumentMgr->Release(); } } return hr; }
HRESULT CDIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString) { debugPrint(L"CDIME::_AddComposingAndChar() reading string = %s", pstrAddString->Get()); HRESULT hr = S_OK; ULONG fetched = 0; TF_SELECTION tfSelection; if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) return S_FALSE; // // make range start to selection // ITfRange* pAheadSelection = nullptr; hr = pContext->GetStart(ec, &pAheadSelection); if (SUCCEEDED(hr) && pAheadSelection) { debugPrint(L"CDIME::_AddComposingAndChar() SUCCEEDED( pContext->GetStart( &pAheadSelection))"); hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); if (SUCCEEDED(hr)) { debugPrint(L"CDIME::_AddComposingAndChar() SUCCEEDED( pAheadSelection->ShiftEndToRange())"); ITfRange* pRange = nullptr; BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); if (FAILED(_SetInputString(ec, pContext, pRange, pstrAddString, exist_composing))) debugPrint(L"CDIME::_AddComposingAndChar() _SetInputString() failed."); if (pRange) pRange->Release(); lastReadingString = *pstrAddString; } } tfSelection.range->Release(); if (pAheadSelection) { pAheadSelection->Release(); } return S_OK; }
HRESULT CSampleIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, _In_ const std::wstring &pstrAddString, BOOL exist_composing) { ITfRange* pRangeInsert = nullptr; if (!exist_composing) { _InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert); if (pRangeInsert == nullptr) { return S_OK; } pRange = pRangeInsert; } if (pRange != nullptr) { pRange->SetText(ec, 0, pstrAddString.c_str(), pstrAddString.length()); } /* sets GUID_PROP_LANGID */ _SetCompositionLanguage(ec, pContext); /* sets GUID_PROP_ATTRIBUTE */ _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); // update the selection, we'll make it an insertion point just past // the inserted text. ITfRange* pSelection = nullptr; TF_SELECTION sel; if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK)) { pSelection->Collapse(ec, TF_ANCHOR_END); sel.range = pSelection; sel.style.ase = TF_AE_NONE; sel.style.fInterimChar = FALSE; pContext->SetSelection(ec, 1, &sel); pSelection->Release(); } if (pRangeInsert) { pRangeInsert->Release(); } return S_OK; }
STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) { HRESULT hr = E_FAIL; ITfInsertAtSelection *pInsertAtSelection = NULL; ITfRange *pRangeComposition = NULL; if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (LPVOID *) &pInsertAtSelection) != S_OK) goto Exit; if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeComposition) != S_OK) goto Exit; ITfContextComposition *pContextComposition = NULL; ITfComposition *pComposition = NULL; if (_pContext->QueryInterface(IID_ITfContextComposition, (LPVOID *) &pContextComposition) != S_OK) goto Exit; if ((pContextComposition->StartComposition(ec, pRangeComposition, _pTextService, &pComposition) == S_OK) && (pComposition != NULL)) { _pTextService->_SetComposition(pComposition); /* set selection */ /* WORKAROUND: * CUAS does not provide a correct GetTextExt() position unless the composition is filled with characters. * So we insert a dummy space character here. * This is the same workaround used by Microsoft Pinyin IME (New Experience). */ if (_fCUASWorkaroundEnabled) { pRangeComposition->SetText(ec, TF_ST_CORRECTION, L" ", 1); pRangeComposition->Collapse(ec, TF_ANCHOR_START); } TF_SELECTION tfSelection; tfSelection.range = pRangeComposition; tfSelection.style.ase = TF_AE_NONE; tfSelection.style.fInterimChar = FALSE; _pContext->SetSelection(ec, 1, &tfSelection); } Exit: if (pContextComposition != NULL) pContextComposition->Release(); if (pRangeComposition != NULL) pRangeComposition->Release(); if (pInsertAtSelection != NULL) pInsertAtSelection->Release(); return hr; }
HRESULT CDIME::_InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange) { debugPrint(L"CDIME::_InsertAtSelection()"); ITfRange* rangeInsert = nullptr; ITfInsertAtSelection* pias = nullptr; HRESULT hr = S_OK; if (ppCompRange == nullptr || pContext == nullptr || pstrAddString == nullptr) { debugPrint(L"CDIME::_InsertAtSelection() failed with null ppCompRange or pContext or pstrAddstring"); hr = E_INVALIDARG; goto Exit; } *ppCompRange = nullptr; hr = pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pias); if (FAILED(hr) || pias == nullptr) { debugPrint(L"CDIME::_InsertAtSelection() failed with null pias or QueryInterface failed"); goto Exit; } hr = pias->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, pstrAddString->Get(), (LONG)pstrAddString->GetLength(), &rangeInsert); if ( FAILED(hr) || rangeInsert == nullptr) { debugPrint(L"CDIME::_InsertAtSelection() InsertTextAtSelection failed"); rangeInsert = nullptr; pias->Release(); goto Exit; } WCHAR rangeText[256]; rangeText[0] = NULL; ULONG fetched = 0; hr = rangeInsert->GetText(ec, 0, rangeText, 256, &fetched); if (SUCCEEDED(hr) && rangeText) debugPrint(L"CDIME::_InsertAtSelection() text in range = %s", rangeText); *ppCompRange = rangeInsert; pias->Release(); hr = S_OK; Exit: return hr; }
HRESULT CSampleIME::_UpdateCandidateString(TfEditCookie ec, _In_ ITfContext *pContext, _In_ const std::wstring &pstrAddString) { HRESULT hr = S_OK; ULONG fetched = 0; TF_SELECTION tfSelection; if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) return S_FALSE; // // make range start to selection // ITfRange* pAheadSelection = nullptr; hr = pContext->GetStart(ec, &pAheadSelection); if (SUCCEEDED(hr)) { hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); if (SUCCEEDED(hr)) { ITfRange* pRange = nullptr; /* existing_composing = true if text range is has a composition pRange = the composition range */ BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); // Well... since we want an initial 'u' // TODO: This is obviously inefficient allocation of memory for another string _SetInputString(ec, pContext, pRange, pstrAddString, exist_composing); if (pRange) { pRange->Release(); } } } tfSelection.range->Release(); if (pAheadSelection) { pAheadSelection->Release(); } return S_OK; }