summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>2011-08-24 09:30:29 +0200
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>2011-08-24 11:16:19 +0200
commit1b2dae36d32bc4d37fa8a12653becd810101f82e (patch)
tree95a1d3159d1a3003528731b7c4987c2ba92f95be /src/plugins/platforms/windows
parentf5777742126d5fb025522d1528d7e12d7239a03f (diff)
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>
Diffstat (limited to 'src/plugins/platforms/windows')
-rw-r--r--src/plugins/platforms/windows/qtwindows_additional.h3
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h23
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp16
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h1
-rw-r--r--src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp598
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.h98
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.h17
-rw-r--r--src/plugins/platforms/windows/windows.pro8
11 files changed, 772 insertions, 8 deletions
diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h
index 707d28559a..e262159b7b 100644
--- a/src/plugins/platforms/windows/qtwindows_additional.h
+++ b/src/plugins/platforms/windows/qtwindows_additional.h
@@ -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
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index 792792a136..692489dfc6 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -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;
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index d62cbfb4c6..c77a111490 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -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:
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 8985c7f0f9..93662384c0 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -106,6 +106,7 @@ public:
static int verboseFonts;
static int verboseGL;
static int verboseOLE;
+ static int verboseInputMethods;
explicit QWindowsContext(bool isOpenGL);
~QWindowsContext();
diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp
index fcfdd4fbdd..fe03a7ffe8 100644
--- a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp
+++ b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp
@@ -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)
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
new file mode 100644
index 0000000000..ec456b1f9c
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -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
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.h b/src/plugins/platforms/windows/qwindowsinputcontext.h
new file mode 100644
index 0000000000..8ea66774d4
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.h
@@ -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
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index e0ba36919e..70c6c0b4db 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -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()
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 3ec32b2325..40613ea1f1 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -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 */)
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h
index 227a66babf..953649102a 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.h
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.h
@@ -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
diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro
index 7e652f7418..e5627ae574 100644
--- a/src/plugins/platforms/windows/windows.pro
+++ b/src/plugins/platforms/windows/windows.pro
@@ -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