/*! \brief Determines the string for reconversion with selection. This is triggered twice by WM_IME_REQUEST, first with reconv=0 to determine the length and later with a reconv struct to obtain the string with the position of the selection to be reconverted. 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(HWND hwnd, RECONVERTSTRING *reconv) { QObject *fo = QGuiApplication::focusObject(); if (!fo) return false; const QVariant srndTextV = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant()); if (!srndTextV.isValid()) return -1; const QString srndText = srndTextV.toString(); const int memSize = int(sizeof(RECONVERTSTRING)) + (srndText.length() + 1) * int(sizeof(ushort)); qCDebug(lcQpaInputMethods) << __FUNCTION__ << " reconv=" << reconv << " srndText=" << srndText << " size=" << memSize; // If memory is not allocated, return the required size. if (!reconv) return srndText.isEmpty() ? -1 : memSize; const QVariant posV = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()); 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(srndText.size()); reconv->dwStrOffset = sizeof(RECONVERTSTRING); 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(srndText.utf16(), srndText.utf16() + srndText.size(), pastReconv); // Ask windows Ime to choose target range. const HIMC himc = ::ImmGetContext(hwnd); if (!himc) return -1; if (!::ImmSetCompositionStringW(himc, SCS_QUERYRECONVERTSTRING, reconv, memSize, NULL, 0)) { ::ImmReleaseContext(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(hwnd, himc); return memSize; }