.../platforms/windows/qwindowsinputcontext.cpp | 114 ++++++++++++++++----- 1 file changed, 86 insertions(+), 28 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 4d4be3d..9c449ee 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -600,8 +600,9 @@ void QWindowsInputContext::handleInputLanguageChanged(WPARAM wparam, LPARAM lpar to determine the length and later with a reconv struct to obtain the string with the position of the selection to be reconverted. - Obtains the text from the focus object and marks the word - for selection (might not be entirely correct for Japanese). + Selected text from the focus object is the target. + If no selection, Ask Windows Ime to choose the target. + Ime String for the target returns with WM_IME_COMPOSITION. */ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv) @@ -610,47 +611,104 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv) if (!fo) return false; - const QVariant surroundingTextV = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant()); - if (!surroundingTextV.isValid()) + const QVariant srndTextV = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant()); + if (!srndTextV.isValid()) return -1; - const QString surroundingText = surroundingTextV.toString(); + + const QString srndText = srndTextV.toString(); const int memSize = int(sizeof(RECONVERTSTRING)) - + (surroundingText.length() + 1) * int(sizeof(ushort)); + + (srndText.length() + 1) * int(sizeof(ushort)); + qCDebug(lcQpaInputMethods) << __FUNCTION__ << " reconv=" << reconv - << " surroundingText=" << surroundingText << " size=" << memSize; + << " srndText=" << srndText << " size=" << memSize; + // If memory is not allocated, return the required size. if (!reconv) - return surroundingText.isEmpty() ? -1 : memSize; + return srndText.isEmpty() ? -1 : memSize; const QVariant posV = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()); - const int pos = posV.isValid() ? posV.toInt() : 0; - // Find the word in the surrounding text. - QTextBoundaryFinder bounds(QTextBoundaryFinder::Word, surroundingText); - bounds.setPosition(pos); - if (bounds.position() > 0 && !(bounds.boundaryReasons() & QTextBoundaryFinder::StartOfItem)) - bounds.toPreviousBoundary(); - const int startPos = bounds.position(); - bounds.toNextBoundary(); - const int endPos = bounds.position(); - qCDebug(lcQpaInputMethods) << __FUNCTION__ << " boundary=" << startPos << endPos; - // Select the text, this will be overwritten by following IME events. - QList attributes; - attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, startPos, endPos-startPos, QVariant()); - QInputMethodEvent selectEvent(QString(), attributes); - QCoreApplication::sendEvent(fo, &selectEvent); + if (!posV.isValid()) + return -1; + + const int srndCaret = posV.toInt(); + + const QVariant selTextV = QInputMethod::queryFocusObject(Qt::ImCurrentSelection, QVariant()); + if (!selTextV.isValid()) + return -1; + + const QString selText = selTextV.toString(); + + // Determine surrounding anchor position. + int srndAnchor = srndCaret; + const QVariant srndAnchorV = QInputMethod::queryFocusObject(Qt::ImAnchorPosition, QVariant()); + if (srndAnchorV.isValid()) { + srndAnchor = srndAnchorV.toInt(); + if ((srndAnchor < 0) || (srndText.size() < srndAnchor)) { + return -1; + } + if (srndCaret != srndAnchor) { + const int start = std::min(srndCaret, srndAnchor); + const int width = std::abs(srndCaret - srndAnchor); + if (srndText.midRef(start, width) != selText) { + return -1; + } + } + } else if (!selText.isEmpty()) { + const int selSize = selText.size(); + if (selText == srndText.midRef(srndCaret, selSize)) { + srndAnchor = srndCaret + selSize; + } else if (selText == srndText.midRef(srndCaret - selSize, selSize)) { + srndAnchor = srndCaret - selSize; + } else { + return -1; + } + } + + // Map selection to dwCompStr. + // No selection assumes current caret as selText without length. + const int selStart = std::min(srndCaret, srndAnchor); reconv->dwSize = DWORD(memSize); reconv->dwVersion = 0; - reconv->dwStrLen = DWORD(surroundingText.size()); + reconv->dwStrLen = DWORD(srndText.size()); reconv->dwStrOffset = sizeof(RECONVERTSTRING); - reconv->dwCompStrLen = DWORD(endPos - startPos); // TCHAR count. - reconv->dwCompStrOffset = DWORD(startPos) * sizeof(ushort); // byte count. + reconv->dwCompStrLen = DWORD(selText.size()); // TCHAR count. + reconv->dwCompStrOffset = DWORD(selStart * sizeof(ushort)); // byte count. reconv->dwTargetStrLen = reconv->dwCompStrLen; reconv->dwTargetStrOffset = reconv->dwCompStrOffset; + ushort *pastReconv = reinterpret_cast(reconv + 1); - std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(), - QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv)); + std::copy(srndText.utf16(), srndText.utf16() + srndText.size(), + pastReconv); + + // Ask windows Ime to choose target range. + const HIMC himc = ::ImmGetContext(m_compositionContext.hwnd); + if (!himc) + return -1; + + if (!::ImmSetCompositionStringW(himc, SCS_QUERYRECONVERTSTRING, reconv, memSize, NULL, 0)) { + ::ImmReleaseContext(m_compositionContext.hwnd, himc); + return -1; + } + + // Target fields have been filled by Windows Ime. + int targetStart = reconv->dwTargetStrOffset / sizeof(ushort); + int targetLength = reconv->dwTargetStrLen; + + if (!selText.isEmpty()) { + if (targetLength != selText.size()) { + return -1; + } + targetLength = 0; + } + + QInputMethodEvent delEvent; + delEvent.setCommitString(QString(), targetStart - srndCaret, targetLength); + QCoreApplication::sendEvent(fo, &delEvent); + + // Ime String for target returns with WM_IME_COMPOSITION. + ::ImmReleaseContext(m_compositionContext.hwnd, himc); return memSize; }