// 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; }
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 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; }
STDAPI CCompositionEditSession::DoEditSession(TfEditCookie ec) { ITfInsertAtSelection *pInsertAtSelection; ITfContextComposition *pContextComposition; ITfComposition *pComposition; ITfRange *pRangeComposition; ITfRange *pRangeInsert; ITfContext *pCompositionContext; HRESULT hr; BOOL fEqualContexts; // get an interface on the context we can use to deal with compositions if (_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition) != S_OK) return E_FAIL; hr = E_FAIL; pInsertAtSelection = NULL; if (_pMark->_IsComposing()) { // we have a composition, let's terminate it // it's possible our current composition is in another context...let's find out fEqualContexts = TRUE; if (_pMark->_GetComposition()->GetRange(&pRangeComposition) == S_OK) { if (pRangeComposition->GetContext(&pCompositionContext) == S_OK) { fEqualContexts = IsEqualUnknown(pCompositionContext, _pContext); if (!fEqualContexts) { // need an edit session in the composition context _pMark->_TerminateCompositionInContext(pCompositionContext); } pCompositionContext->Release(); } pRangeComposition->Release(); } // if the composition is in pContext, we already have an edit cookie if (fEqualContexts) { _pMark->_TerminateComposition(ec); } } else { // let's start a new composition over the current selection // this is totally contrived, a real text service would have // some meaningful logic to trigger this // first, test where a keystroke would go in the document if we did an insert // we need a special interface to insert text at the selection if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK) { pInsertAtSelection = NULL; goto Exit; } if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert) != S_OK) goto Exit; // start the composition if (pContextComposition->StartComposition(ec, pRangeInsert, _pMark, &pComposition) != S_OK) { pComposition = NULL; } pRangeInsert->Release(); // _pComposition may be NULL even if StartComposition return S_OK, this mean the application // rejected the composition if (pComposition != NULL) { _pMark->_SetComposition(pComposition); // underline the composition text to give the user some feedback UI _pMark->_SetCompositionDisplayAttributes(ec); } } // if we make it here, we've succeeded hr = S_OK; Exit: SafeRelease(pInsertAtSelection); pContextComposition->Release(); return hr; }