Add Input context for Windows.

Change-Id: I20b97e863bf1198b9ad810bb5a25652327f626c9
Reviewed-on: http://codereview.qt.nokia.com/3463
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Oliver Wolff <oliver.wolff@nokia.com>
This commit is contained in:
Friedemann Kleint 2011-08-24 09:30:29 +02:00
parent f577774212
commit 1b2dae36d3
11 changed files with 772 additions and 8 deletions

View File

@ -79,6 +79,9 @@ typedef struct tagUPDATELAYEREDWINDOWINFO {
#define PFD_DIRECT3D_ACCELERATED 0x00004000
#define PFD_SUPPORT_COMPOSITION 0x00008000
// IME.
#define IMR_CONFIRMRECONVERTSTRING 0x0005
#endif // if defined(Q_CC_MINGW)
/* Touch is supported from Windows 7 onwards and data structures

View File

@ -88,6 +88,12 @@ enum WindowsEventType // Simplify event types
ClipboardEvent = ClipboardEventFlag + 1,
ActivateApplicationEvent = ApplicationEventFlag + 1,
DeactivateApplicationEvent = ApplicationEventFlag + 2,
InputMethodStartCompositionEvent = InputMethodEventFlag + 1,
InputMethodCompositionEvent = InputMethodEventFlag + 2,
InputMethodEndCompositionEvent = InputMethodEventFlag + 3,
InputMethodOpenCandidateWindowEvent = InputMethodEventFlag + 4,
InputMethodCloseCandidateWindowEvent = InputMethodEventFlag + 5,
InputMethodRequest = InputMethodEventFlag + 6,
UnknownEvent = 542
};
@ -143,6 +149,23 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
case WM_RENDERALLFORMATS:
case WM_DESTROYCLIPBOARD:
return QtWindows::ClipboardEvent;
case WM_IME_STARTCOMPOSITION:
return QtWindows::InputMethodStartCompositionEvent;
case WM_IME_ENDCOMPOSITION:
return QtWindows::InputMethodEndCompositionEvent;
case WM_IME_COMPOSITION:
return QtWindows::InputMethodCompositionEvent;
case WM_IME_REQUEST:
return QtWindows::InputMethodRequest;
case WM_IME_NOTIFY:
switch (int(wParamIn)) {
case IMN_OPENCANDIDATE:
return QtWindows::InputMethodOpenCandidateWindowEvent;
case IMN_CLOSECANDIDATE:
return QtWindows::InputMethodCloseCandidateWindowEvent;
default:
break;
}
default:
break;
}

View File

@ -46,6 +46,7 @@
#include "qwindowsmousehandler.h"
#include "qtwindowsglobal.h"
#include "qwindowsmime.h"
#include "qwindowsinputcontext.h"
#include <QtGui/QWindow>
#include <QtGui/QWindowSystemInterface>
@ -72,6 +73,7 @@ int QWindowsContext::verboseBackingStore = 0;
int QWindowsContext::verboseFonts = 0;
int QWindowsContext::verboseGL = 0;
int QWindowsContext::verboseOLE = 0;
int QWindowsContext::verboseInputMethods = 0;
// Get verbosity of components from "foo:2,bar:3"
static inline int componentVerbose(const char *v, const char *keyWord)
@ -240,6 +242,7 @@ QWindowsContext::QWindowsContext(bool isOpenGL) :
QWindowsContext::verboseFonts = componentVerbose(v, "fonts");
QWindowsContext::verboseGL = componentVerbose(v, "gl");
QWindowsContext::verboseOLE = componentVerbose(v, "ole");
QWindowsContext::verboseInputMethods = componentVerbose(v, "im");
}
}
@ -603,8 +606,21 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::DeactivateWindowEvent:
QWindowSystemInterface::handleWindowActivated(0);
return true;
case QtWindows::InputMethodStartCompositionEvent:
return QWindowsInputContext::instance()->startComposition(hwnd);
case QtWindows::InputMethodCompositionEvent:
return QWindowsInputContext::instance()->composition(hwnd, lParam);
case QtWindows::InputMethodEndCompositionEvent:
return QWindowsInputContext::instance()->endComposition(hwnd);
case QtWindows::InputMethodRequest:
return QWindowsInputContext::instance()->handleIME_Request(wParam, lParam, result);
case QtWindows::InputMethodOpenCandidateWindowEvent:
case QtWindows::InputMethodCloseCandidateWindowEvent:
// TODO: Release/regrab mouse if a popup has mouse grab.
return false;
case QtWindows::ClipboardEvent:
case QtWindows::DestroyEvent:
case QtWindows::UnknownEvent:
return false;
default:

View File

@ -106,6 +106,7 @@ public:
static int verboseFonts;
static int verboseGL;
static int verboseOLE;
static int verboseInputMethods;
explicit QWindowsContext(bool isOpenGL);
~QWindowsContext();

View File

@ -176,6 +176,7 @@ messageDebugEntries[] = {
{WM_MOUSELEAVE, "WM_MOUSELEAVE", true},
{WM_NCHITTEST, "WM_NCHITTEST", false},
{WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT", true},
{WM_INPUTLANGCHANGE, "WM_INPUTLANGCHANGE", true},
{WM_IME_NOTIFY, "WM_IME_NOTIFY", true},
#if defined(WM_DWMNCRENDERINGCHANGED)
{WM_DWMNCRENDERINGCHANGED, "WM_DWMNCRENDERINGCHANGED", true},
@ -188,7 +189,12 @@ messageDebugEntries[] = {
{WM_RENDERFORMAT, "WM_RENDERFORMAT", true},
{WM_RENDERALLFORMATS, "WM_RENDERALLFORMATS", true},
{WM_DESTROYCLIPBOARD, "WM_DESTROYCLIPBOARD", true},
{WM_CAPTURECHANGED, "WM_CAPTURECHANGED", true}
{WM_CAPTURECHANGED, "WM_CAPTURECHANGED", true},
{WM_IME_STARTCOMPOSITION, "WM_IME_STARTCOMPOSITION"},
{WM_IME_COMPOSITION, "WM_IME_COMPOSITION"},
{WM_IME_ENDCOMPOSITION, "WM_IME_ENDCOMPOSITION"},
{WM_IME_NOTIFY, "WM_IME_NOTIFY"},
{WM_IME_REQUEST, "WM_IME_REQUEST"}
};
static inline const MessageDebugEntry *messageDebugEntry(UINT msg)

View File

@ -0,0 +1,598 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (info@qt.nokia.com)
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwindowsinputcontext.h"
#include "qwindowscontext.h"
#include "qwindowswindow.h"
#include "qwindowsintegration.h"
#include "qwindowsmousehandler.h"
#include <QtCore/QDebug>
#include <QtCore/QObject>
#include <QtCore/QRect>
#include <QtCore/QTextBoundaryFinder>
#include <QtGui/QInputMethodEvent>
#include <QtGui/QTextCharFormat>
#include <QtGui/QPalette>
#include <QtGui/QGuiApplication>
QT_BEGIN_NAMESPACE
static inline QByteArray debugComposition(int lParam)
{
QByteArray str;
if (lParam & GCS_RESULTSTR)
str += "RESULTSTR ";
if (lParam & GCS_COMPSTR)
str += "COMPSTR ";
if (lParam & GCS_COMPATTR)
str += "COMPATTR ";
if (lParam & GCS_CURSORPOS)
str += "CURSORPOS ";
if (lParam & GCS_COMPCLAUSE)
str += "COMPCLAUSE ";
if (lParam & CS_INSERTCHAR)
str += "INSERTCHAR ";
if (lParam & CS_NOMOVECARET)
str += "NOMOVECARET ";
return str;
}
// Cancel current IME composition.
static inline void imeNotifyCancelComposition(HWND hwnd)
{
const HIMC himc = ImmGetContext(hwnd);
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ImmReleaseContext(hwnd, himc);
}
// Query a QObject for an InputMethod-related value
// by sending a QInputMethodQueryEvent.
template <class T>
bool inputMethodQuery(QObject *fo, Qt::InputMethodQuery query, T *result)
{
QInputMethodQueryEvent queryEvent(query);
if (!QCoreApplication::sendEvent(fo, &queryEvent))
return false;
*result = qvariant_cast<T>(queryEvent.value());
return true;
}
/*!
\class QWindowsInputContext
\brief Windows Input context implementation
Handles input of foreign characters (particularly East Asian)
languages.
\section1 Testing
\list
\o Install the East Asian language support and choose Japanese (say).
\o Compile the \a mainwindows/mdi example and open a text window.
\o In the language bar, switch to Japanese and choose the
Input method 'Hiragana'.
\o In a text editor control, type the syllable \a 'la'.
Underlined characters show up, indicating that there is completion
available. Press the Space key two times. A completion popup occurs
which shows the options.
\endlist
Reconversion: Input texts can be 'converted' into different
input modes or more completion suggestions can be made based on
context to correct errors. This is bound to the 'Conversion key'
(F13-key in Japanese, which can be changed in the
configuration). After writing text, pressing the key selects text
and triggers a conversion popup, which shows the alternatives for
the word.
\section1 Interaction
When the user activates input methods, Windows sends
WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION,
WM_IME_ENDCOMPOSITION messages that trigger startComposition(),
composition(), endComposition(), respectively. No key events are sent.
composition() determines the markup of the pre-edit or selected
text and/or the final text and sends that to the focus object.
In between startComposition(), endComposition(), multiple
compositions may happen (isComposing).
update() is called to synchronize the position of the candidate
window with the microfocus rectangle of the focus object.
Also, a hidden caret is moved along with that position,
which is important for some Chinese input methods.
reset() is called to cancel a composition if the mouse is
moved outside or for example some Undo/Redo operation is
invoked.
\note Mouse interaction of popups with
QtWindows::InputMethodOpenCandidateWindowEvent and
QtWindows::InputMethodCloseCandidateWindowEvent
needs to be checked (mouse grab might interfere with candidate window).
\ingroup qt-lighthouse-win
*/
QWindowsInputContext::CompositionContext::CompositionContext() :
hwnd(0), haveCaret(false), position(0), isComposing(false)
{
}
QWindowsInputContext::QWindowsInputContext() :
m_WM_MSIME_MOUSE(RegisterWindowMessage(L"MSIMEMouseOperation")),
m_endCompositionRecursionGuard(false)
{
}
QWindowsInputContext::~QWindowsInputContext()
{
}
/*!
\brief Cancels a composition.
*/
void QWindowsInputContext::reset()
{
QPlatformInputContext::reset();
if (!m_compositionContext.hwnd)
return;
QObject *fo = focusObject();
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__<< fo;
if (!fo)
return;
if (m_compositionContext.isComposing) {
QInputMethodEvent event;
if (!m_compositionContext.composition.isEmpty())
event.setCommitString(m_compositionContext.composition);
QCoreApplication::sendEvent(fo, &event);
endContextComposition();
}
imeNotifyCancelComposition(m_compositionContext.hwnd);
doneContext();
}
/*!
\brief Moves the candidate window along with microfocus of the focus object.
*/
void QWindowsInputContext::update()
{
QPlatformInputContext::update();
if (!m_compositionContext.hwnd)
return;
QObject *fo = focusObject();
if (!fo)
return;
const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
if (!himc)
return;
// Move candidate list window to the microfocus position.
QRect globalMicroFocusRect;
if (!inputMethodQuery(fo, Qt::ImMicroFocus, &globalMicroFocusRect) || !globalMicroFocusRect.isValid())
return;
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << himc << globalMicroFocusRect;
if (globalMicroFocusRect.isValid()) {
const QRect microFocusRect(QWindowsGeometryHint::mapFromGlobal(m_compositionContext.hwnd,
globalMicroFocusRect.topLeft()),
globalMicroFocusRect.size());
COMPOSITIONFORM cf;
// ### need X-like inputStyle config settings
cf.dwStyle = CFS_FORCE_POSITION;
cf.ptCurrentPos.x = microFocusRect.x();
cf.ptCurrentPos.y = microFocusRect.y();
CANDIDATEFORM candf;
candf.dwIndex = 0;
candf.dwStyle = CFS_EXCLUDE;
candf.ptCurrentPos.x = microFocusRect.x();
candf.ptCurrentPos.y = microFocusRect.y() + microFocusRect.height();
candf.rcArea.left = microFocusRect.x();
candf.rcArea.top = microFocusRect.y();
candf.rcArea.right = microFocusRect.x() + microFocusRect.width();
candf.rcArea.bottom = microFocusRect.y() + microFocusRect.height();
if (m_compositionContext.haveCaret)
SetCaretPos(microFocusRect.x(), microFocusRect.y());
ImmSetCompositionWindow(himc, &cf);
ImmSetCandidateWindow(himc, &candf);
}
ImmReleaseContext(m_compositionContext.hwnd, himc);
}
void QWindowsInputContext::mouseHandler(int pos, QMouseEvent *event)
{
if (event->type() != QEvent::MouseButtonPress || !m_compositionContext.hwnd)
return;
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << pos << event;
if (pos < 0 || pos > m_compositionContext.composition.size())
reset();
// Magic code that notifies Japanese IME about the cursor
// position.
const DWORD button = QWindowsMouseHandler::mouseButtonsToKeyState(event->buttons());
const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
const HWND imeWindow = ImmGetDefaultIMEWnd(m_compositionContext.hwnd);
SendMessage(imeWindow, m_WM_MSIME_MOUSE, MAKELONG(MAKEWORD(button, pos == 0 ? 2 : 1), pos), (LPARAM)himc);
ImmReleaseContext(m_compositionContext.hwnd, himc);
}
void QWindowsInputContext::setFocusObject(QObject *object)
{
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << object;
QPlatformInputContext::setFocusObject(object);
}
QWindowsInputContext *QWindowsInputContext::instance()
{
return static_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
}
static inline QString getCompositionString(HIMC himc, DWORD dwIndex)
{
enum { bufferSize = 256 };
wchar_t buffer[bufferSize];
const int length = ImmGetCompositionString(himc, dwIndex, buffer, bufferSize * sizeof(wchar_t));
return QString::fromWCharArray(buffer, length / sizeof(wchar_t));
}
// Determine the converted string range as pair of start/length to be selected.
static inline void getCompositionStringConvertedRange(HIMC himc, int *selStart, int *selLength)
{
enum { bufferSize = 256 };
// Find the range of bytes with ATTR_TARGET_CONVERTED set.
char attrBuffer[bufferSize];
*selStart = *selLength = 0;
if (const int attrLength = ImmGetCompositionString(himc, GCS_COMPATTR, attrBuffer, bufferSize)) {
int start = 0;
while (start < attrLength && !(attrBuffer[start] & ATTR_TARGET_CONVERTED))
start++;
if (start < attrLength) {
int end = start + 1;
while (end < attrLength && (attrBuffer[end] & ATTR_TARGET_CONVERTED))
end++;
*selStart = start;
*selLength = end - start;
}
}
}
enum StandardFormat {
PreeditFormat,
SelectionFormat
};
static inline QTextFormat standardFormat(StandardFormat format)
{
QTextCharFormat result;
switch (format) {
case PreeditFormat:
result.setUnderlineStyle(QTextCharFormat::DashUnderline);
break;
case SelectionFormat: {
// TODO: Should be that of the widget?
const QPalette palette = QGuiApplication::palette();
const QColor background = palette.text().color();
result.setBackground(QBrush(background));
result.setForeground(palette.background());
break;
}
}
return result;
}
bool QWindowsInputContext::startComposition(HWND hwnd)
{
const QObject *fo = focusObject();
if (!fo)
return false;
// This should always match the object.
QWindow *window = QGuiApplication::activeWindow();
if (!window)
return false;
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << fo << window;
if (!fo || QWindowsWindow::handleOf(window) != hwnd)
return false;
initContext(hwnd);
startContextComposition();
return true;
}
void QWindowsInputContext::startContextComposition()
{
if (m_compositionContext.isComposing) {
qWarning("%s: Called out of sequence.", __FUNCTION__);
return;
}
m_compositionContext.isComposing = true;
m_compositionContext.composition.clear();
m_compositionContext.position = 0;
update();
}
void QWindowsInputContext::endContextComposition()
{
if (!m_compositionContext.isComposing) {
qWarning("%s: Called out of sequence.", __FUNCTION__);
return;
}
m_compositionContext.composition.clear();
m_compositionContext.position = 0;
m_compositionContext.isComposing = false;
}
// Create a list of markup attributes for QInputMethodEvent
// to display the selected part of the intermediate composition
// result differently.
static inline QList<QInputMethodEvent::Attribute>
intermediateMarkup(int position, int compositionLength,
int selStart, int selLength)
{
QList<QInputMethodEvent::Attribute> attributes;
if (selStart > 0)
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart,
standardFormat(PreeditFormat));
if (selLength)
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength,
standardFormat(SelectionFormat));
if (selStart + selLength < compositionLength)
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength,
compositionLength - selStart - selLength,
standardFormat(PreeditFormat));
if (position >= 0)
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, position, selLength ? 0 : 1, QVariant());
return attributes;
}
/*!
\brief Notify focus object about markup or final text.
*/
bool QWindowsInputContext::composition(HWND hwnd, LPARAM lParamIn)
{
QObject *fo = focusObject();
const int lParam = int(lParamIn);
if (QWindowsContext::verboseInputMethods)
qDebug() << '>' << __FUNCTION__ << fo << debugComposition(lParam)
<< " composing=" << m_compositionContext.isComposing;
if (!fo || m_compositionContext.hwnd != hwnd || !lParam)
return false;
const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
if (!himc)
return false;
QScopedPointer<QInputMethodEvent> event;
if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) {
if (!m_compositionContext.isComposing)
startContextComposition();
// Some intermediate composition result. Parametrize event with
// attribute sequence specifying the formatting of the converted part.
int selStart, selLength;
m_compositionContext.composition = getCompositionString(himc, GCS_COMPSTR);
m_compositionContext.position = ImmGetCompositionString(himc, GCS_CURSORPOS, 0, 0);
getCompositionStringConvertedRange(himc, &selStart, &selLength);
if ((lParam & CS_INSERTCHAR) && (lParam & CS_NOMOVECARET)) {
// make Korean work correctly. Hope this is correct for all IMEs
selStart = 0;
selLength = m_compositionContext.composition.size();
}
if (!selLength)
selStart = 0;
event.reset(new QInputMethodEvent(m_compositionContext.composition,
intermediateMarkup(m_compositionContext.position,
m_compositionContext.composition.size(),
selStart, selLength)));
}
if (event.isNull())
event.reset(new QInputMethodEvent);
if (lParam & GCS_RESULTSTR) {
// A fixed result, return the converted string
event->setCommitString(getCompositionString(himc, GCS_RESULTSTR));
endContextComposition();
}
const bool result = QCoreApplication::sendEvent(fo, event.data());
if (QWindowsContext::verboseInputMethods)
qDebug() << '<' << __FUNCTION__ << "sending markup="
<< event->attributes().size()
<< " commit=" << event->commitString()
<< " to " << fo << " returns " << result;
update();
ImmReleaseContext(m_compositionContext.hwnd, himc);
return result;
}
bool QWindowsInputContext::endComposition(HWND hwnd)
{
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << m_endCompositionRecursionGuard << hwnd;
// Googles Pinyin Input Method likes to call endComposition again
// when we call notifyIME with CPS_CANCEL, so protect ourselves
// against that.
if (m_endCompositionRecursionGuard || m_compositionContext.hwnd != hwnd)
return false;
QObject *fo = focusObject();
if (!fo)
return false;
m_endCompositionRecursionGuard = true;
imeNotifyCancelComposition(m_compositionContext.hwnd);
if (m_compositionContext.isComposing) {
QInputMethodEvent event;
QCoreApplication::sendEvent(fo, &event);
}
doneContext();
m_endCompositionRecursionGuard = false;
return true;
}
void QWindowsInputContext::initContext(HWND hwnd)
{
if (m_compositionContext.hwnd)
doneContext();
m_compositionContext.hwnd = hwnd;
// Create a hidden caret which is kept at the microfocus
// position in update(). This is important for some
// Chinese input methods.
m_compositionContext.haveCaret = CreateCaret(hwnd, 0, 1, 1);
HideCaret(hwnd);
update();
m_compositionContext.isComposing = false;
m_compositionContext.position = 0;
}
void QWindowsInputContext::doneContext()
{
if (!m_compositionContext.hwnd)
return;
if (m_compositionContext.haveCaret)
DestroyCaret();
m_compositionContext.hwnd = 0;
m_compositionContext.composition.clear();
m_compositionContext.position = 0;
m_compositionContext.isComposing = m_compositionContext.haveCaret = false;
}
bool QWindowsInputContext::handleIME_Request(WPARAM wParam,
LPARAM lParam,
LRESULT *result)
{
switch (int(wParam)) {
case IMR_RECONVERTSTRING: {
const int size = reconvertString(reinterpret_cast<RECONVERTSTRING *>(lParam));
if (size < 0)
return false;
*result = size;
return true;
}
break;
case IMR_CONFIRMRECONVERTSTRING:
return true;
default:
break;
}
return false;
}
/*!
\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.
Obtains the text from the focus object and marks the word
for selection (might not be entirely correct for Japanese).
*/
int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv)
{
QObject *fo = focusObject();
if (!fo)
return false;
QString surroundingText;
if (!inputMethodQuery(fo, Qt::ImSurroundingText, &surroundingText))
return -1;
const DWORD memSize = sizeof(RECONVERTSTRING)
+ (surroundingText.length() + 1) * sizeof(ushort);
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << " reconv=" << reconv
<< " surroundingText=" << surroundingText
<< " size=" << memSize;
// If memory is not allocated, return the required size.
if (!reconv)
return surroundingText.isEmpty() ? -1 : int(memSize);
int pos = 0;
inputMethodQuery(fo, Qt::ImCursorPosition, &pos);
// Find the word in the surrounding text.
QTextBoundaryFinder bounds(QTextBoundaryFinder::Word, surroundingText);
bounds.setPosition(pos);
if (bounds.isAtBoundary()) {
if (QTextBoundaryFinder::EndWord == bounds.boundaryReasons())
bounds.toPreviousBoundary();
} else {
bounds.toPreviousBoundary();
}
const int startPos = bounds.position();
bounds.toNextBoundary();
const int endPos = bounds.position();
if (QWindowsContext::verboseInputMethods)
qDebug() << __FUNCTION__ << " boundary=" << startPos << endPos;
// Select the text, this will be overwritten by following IME events.
QList<QInputMethodEvent::Attribute> attributes;
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, startPos, endPos-startPos, QVariant());
QInputMethodEvent selectEvent(QString(), attributes);
QCoreApplication::sendEvent(fo, &selectEvent);
reconv->dwSize = memSize;
reconv->dwVersion = 0;
reconv->dwStrLen = surroundingText.size();
reconv->dwStrOffset = sizeof(RECONVERTSTRING);
reconv->dwCompStrLen = endPos - startPos; // TCHAR count.
reconv->dwCompStrOffset = startPos * sizeof(ushort); // byte count.
reconv->dwTargetStrLen = reconv->dwCompStrLen;
reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
ushort *pastReconv = reinterpret_cast<ushort *>(reconv + 1);
qCopy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(),
pastReconv);
return memSize;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (info@qt.nokia.com)
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWINDOWSINPUTCONTEXT_H
#define QWINDOWSINPUTCONTEXT_H
#include "qtwindows_additional.h"
#include <QtGui/QPlatformInputContext>
QT_BEGIN_NAMESPACE
class QInputMethodEvent;
class QWindowsInputContext : public QPlatformInputContext
{
struct CompositionContext
{
CompositionContext();
HWND hwnd;
bool haveCaret;
QString composition;
int position;
bool isComposing;
};
public:
explicit QWindowsInputContext();
~QWindowsInputContext();
virtual void reset();
virtual void update();
virtual void mouseHandler(int x, QMouseEvent *event);
virtual void setFocusObject(QObject *o);
static QWindowsInputContext *instance();
bool startComposition(HWND hwnd);
bool composition(HWND hwnd, LPARAM lParam);
bool endComposition(HWND hwnd);
int reconvertString(RECONVERTSTRING *reconv);
bool handleIME_Request(WPARAM wparam, LPARAM lparam, LRESULT *result);
private:
void initContext(HWND hwnd);
void doneContext();
void startContextComposition();
void endContextComposition();
const DWORD m_WM_MSIME_MOUSE;
CompositionContext m_compositionContext;
bool m_endCompositionRecursionGuard;
};
QT_END_NAMESPACE
#endif // QWINDOWSINPUTCONTEXT_H

View File

@ -49,6 +49,7 @@
#include "qwindowsguieventdispatcher.h"
#include "qwindowsclipboard.h"
#include "qwindowsdrag.h"
#include "qwindowsinputcontext.h"
#include <QtGui/QPlatformNativeInterface>
#include <QtGui/QWindowSystemInterface>
@ -136,6 +137,7 @@ struct QWindowsIntegrationPrivate
QWindowsDrag m_drag;
QWindowsGuiEventDispatcher *m_eventDispatcher;
QOpenGLStaticContextPtr m_staticOpenGLContext;
QWindowsInputContext m_inputContext;
};
QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(bool openGL)
@ -249,8 +251,7 @@ QPlatformDrag *QWindowsIntegration::drag() const
QPlatformInputContext * QWindowsIntegration::inputContext() const
{
Q_UNIMPLEMENTED();
return QPlatformIntegration::inputContext();
return &d->m_inputContext;
}
QWindowsIntegration *QWindowsIntegration::instance()

View File

@ -732,8 +732,7 @@ bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd,
return true;
if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
updateKeyMap(msg);
translateKeyEventInternal(widget, msg, false);
return true;
return translateKeyEventInternal(widget, msg, false);
}
bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &msg, bool /* grab */)

View File

@ -66,6 +66,7 @@ public:
LRESULT *result);
static inline Qt::MouseButtons keyStateToMouseButtons(int);
static inline int mouseButtonsToKeyState(Qt::MouseButtons);
QWindow *windowUnderMouse() const { return m_windowUnderMouse.data(); }
@ -93,6 +94,22 @@ Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(int wParam)
return mb;
}
int QWindowsMouseHandler::mouseButtonsToKeyState(Qt::MouseButtons mb)
{
int result = 0;
if (mb & Qt::LeftButton)
result |= MK_LBUTTON;
if (mb & Qt::MiddleButton)
result |= MK_MBUTTON;
if (mb & Qt::RightButton)
result |= MK_RBUTTON;
if (mb & Qt::XButton1)
result |= MK_XBUTTON1;
if (mb & Qt::XButton2)
result |= MK_XBUTTON2;
return result;
}
QT_END_NAMESPACE
#endif // QWINDOWSMOUSEHANDLER_H

View File

@ -8,7 +8,7 @@ INCLUDEPATH += ../../../3rdparty/harfbuzz/src
QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms
# Note: OpenGL32 must precede Gdi32 as it overwrites some functions.
LIBS *= -lOpenGL32 -lGdi32 -lUser32 -lOle32 -lWinspool
LIBS *= -lOpenGL32 -lGdi32 -lUser32 -lOle32 -lWinspool -lImm32
win32-g++: LIBS *= -luuid
contains(QT_CONFIG, directwrite) {
@ -38,7 +38,8 @@ SOURCES += \
qwindowsmime.cpp \
qwindowsdrag.cpp \
qwindowscursor.cpp \
pixmaputils.cpp
pixmaputils.cpp \
qwindowsinputcontext.cpp
HEADERS += \
qwindowsnativeimage.h \
@ -62,7 +63,8 @@ HEADERS += \
qwindowsinternalmimedata.h \
qwindowscursor.h \
pixmaputils.h \
array.h
array.h \
qwindowsinputcontext.h
target.path += $$[QT_INSTALL_PLUGINS]/platforms
INSTALLS += target