summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
diff options
context:
space:
mode:
authorRoger Maclean <rmaclean@qnx.com>2013-10-10 15:25:55 -0400
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-25 20:17:21 +0200
commitdb98b052ac671122437f181feeffb0b270318d8d (patch)
tree7f5ddb82effaaf9af91eb1a9c00aaa5f41abb541 /src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
parent1f22c1d98162b3a7d34ceb64c543d6580333b434 (diff)
Add support for BB10 input method framework
Added input method support for the BB10 variant of Qt to the extent possible using standard Qt APIs. This adds support for text predictions and entry of languages such as Chinese. Change in interface to QQnxAbstractVirtualKeyboard was made because it is felt the new one is slightly nicer. It doesn't appear safe to assume the focus object has a particular property and in fact in my tests the code failed to work. In some cases the code uses variable and function naming at odds with normal Qt coding standards. This has been done for functions called and data provided by the BB10 input system as for those of us who need to maintain such things, it makes their meaning considerably clearer. While qqnxinputcontext_imf.cpp was used as an initial base for development one can consider the new version as largely new code. I don't believe the original version was ever complete and in any event would not compile. Change-Id: I09470801ffa237cee67da40c0b3d02ed5c77531e Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
Diffstat (limited to 'src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp')
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp1459
1 files changed, 611 insertions, 848 deletions
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
index 580553f6e2..7f60fa1822 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2011 - 2012 Research In Motion
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -40,10 +40,10 @@
****************************************************************************/
#include "qqnxinputcontext_imf.h"
-#include "qqnxeventthread.h"
#include "qqnxabstractvirtualkeyboard.h"
#include "qqnxintegration.h"
#include "qqnxscreen.h"
+#include "qqnxscreeneventhandler.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QInputMethodEvent>
@@ -62,9 +62,9 @@
#include <sys/keycodes.h>
#if defined(QQNXINPUTCONTEXT_IMF_EVENT_DEBUG)
-#define qInputContextIMFEventDebug qDebug
+#define qInputContextIMFRequestDebug qDebug
#else
-#define qInputContextIMFEventDebug QT_NO_QDEBUG_MACRO
+#define qInputContextIMFRequestDebug QT_NO_QDEBUG_MACRO
#endif
#if defined(QQNXINPUTCONTEXT_DEBUG)
@@ -73,492 +73,419 @@
#define qInputContextDebug QT_NO_QDEBUG_MACRO
#endif
-/** TODO:
- Support inputMethodHints to restrict input (needs additional features in IMF).
-*/
+#include <QtGui/QAccessible>
+#include <QtGui/private/qaccessible2_p.h>
-#define STRX(x) #x
-#define STR(x) STRX(x)
+static QQnxInputContext *sInputContextInstance;
+static QColor sSelectedColor(0,0xb8,0,85);
+static QColor sAutoCorrectedColor(0,0xb8,0,85);
+static QColor sRevertedColor(0,0x8d,0xaf,51);
-// Someone tell me why input_control methods are in this namespace, but the rest is not.
-using namespace InputMethodSystem;
-
-#define qs(x) QString::fromLatin1(x)
-#define iarg(name) event->mArgs[qs(#name)] = QVariant::fromValue(name)
-#define parg(name) event->mArgs[qs(#name)] = QVariant::fromValue((void*)name)
-namespace
-{
-
-spannable_string_t *toSpannableString(const QString &text);
static const input_session_t *sInputSession = 0;
-bool isSessionOkay(input_session_t *ic)
+static bool isSessionOkay(input_session_t *ic)
{
return ic !=0 && sInputSession != 0 && ic->component_id == sInputSession->component_id;
}
enum ImfEventType
{
- ImfBeginBatchEdit,
- ImfClearMetaKeyStates,
ImfCommitText,
ImfDeleteSurroundingText,
- ImfEndBatchEdit,
ImfFinishComposingText,
- ImfGetCursorCapsMode,
ImfGetCursorPosition,
- ImfGetExtractedText,
- ImfGetSelectedText,
ImfGetTextAfterCursor,
ImfGetTextBeforeCursor,
- ImfPerformEditorAction,
- ImfReportFullscreenMode,
ImfSendEvent,
ImfSendAsyncEvent,
ImfSetComposingRegion,
ImfSetComposingText,
- ImfSetSelection
+ ImfIsTextSelected,
+ ImfIsAllTextSelected,
};
-// We use this class as a round about way to support a posting synchronous event into
-// Qt's main thread from the IMF thread.
-class ImfEventResult
+// IMF requests all arrive on IMF's own thread and have to be posted to the main thread to be processed.
+class QQnxImfRequest
{
public:
- ImfEventResult()
- {
- m_mutex.lock();
- }
-
- ~ImfEventResult()
- {
- m_mutex.unlock();
- }
-
- void wait()
- {
- m_wait.wait(&m_mutex);
- }
-
- void signal()
- {
- m_wait.wakeAll();
- }
-
- void setResult(const QVariant& result)
- {
- m_mutex.lock();
- m_retVal = result;
- signal();
- m_mutex.unlock();
- }
-
- QVariant result()
- {
- return m_retVal;
- }
-
-private:
- QVariant m_retVal;
- QMutex m_mutex;
- QWaitCondition m_wait;
+ QQnxImfRequest(input_session_t *_session, ImfEventType _type)
+ : session(_session), type(_type)
+ { }
+ ~QQnxImfRequest() { }
+
+ input_session_t *session;
+ ImfEventType type;
+ union {
+ struct {
+ int32_t n;
+ int32_t flags;
+ bool before;
+ spannable_string_t *result;
+ } gtac; // ic_get_text_before_cursor/ic_get_text_after_cursor
+ struct {
+ int32_t result;
+ } gcp; // ic_get_cursor_position
+ struct {
+ int32_t start;
+ int32_t end;
+ int32_t result;
+ } scr; // ic_set_composing_region
+ struct {
+ spannable_string_t* text;
+ int32_t new_cursor_position;
+ int32_t result;
+ } sct; // ic_set_composing_text
+ struct {
+ spannable_string_t* text;
+ int32_t new_cursor_position;
+ int32_t result;
+ } ct; // ic_commit_text
+ struct {
+ int32_t result;
+ } fct; // ic_finish_composing_text
+ struct {
+ int32_t left_length;
+ int32_t right_length;
+ int32_t result;
+ } dst; // ic_delete_surrounding_text
+ struct {
+ event_t *event;
+ int32_t result;
+ } sae; // ic_send_async_event/ic_send_event
+ struct {
+ int32_t *pIsSelected;
+ int32_t result;
+ } its; // ic_is_text_selected/ic_is_all_text_selected
+ };
};
-class ImfEvent : public QEvent
+// Invoke an IMF initiated request synchronously on Qt's main thread. As describe below all
+// IMF requests are made from another thread but need to be executed on the main thread.
+static void executeIMFRequest(QQnxImfRequest *event)
+{
+ QMetaObject::invokeMethod(sInputContextInstance,
+ "processImfEvent",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(QQnxImfRequest*, event));
+}
+
+// The following functions (ic_*) are callback functions called by the input system to query information
+// about the text object that currently has focus or to make changes to it. All calls are made from the
+// input system's own thread. The pattern for each callback function is to copy its parameters into
+// a QQnxImfRequest structure and call executeIMFRequest to have it passed synchronously to Qt's main thread.
+// Any return values should be pre-initialised with suitable default values as in some cases
+// (e.g. a stale session) the call will return without having executed any request specific code.
+//
+// To make the correspondence more obvious, the names of these functions match those defined in the headers.
+// They're in an anonymous namespace to avoid compiler conflicts with external functions defined with the
+// same names.
+namespace
{
- public:
- ImfEvent(input_session_t *session, ImfEventType type, ImfEventResult *result) :
- QEvent((QEvent::Type)sUserEventType),
- m_session(session),
- m_imfType(type),
- m_result(result)
- {
- }
- ~ImfEvent() { }
-
- input_session_t *m_session;
- ImfEventType m_imfType;
- QVariantHash m_args;
- ImfEventResult *m_result;
- static int sUserEventType;
-};
-int ImfEvent::sUserEventType = QEvent::registerEventType();
-
-static int32_t imfBeginBatchEdit(input_session_t *ic)
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_begin_batch_edit(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfBeginBatchEdit, &result);
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ // Ignore silently.
+ return 0;
}
-static int32_t imfClearMetaKeyStates(input_session_t *ic, int32_t states)
+// End composition, committing the supplied text.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfClearMetaKeyStates, &result);
- iarg(states);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfCommitText);
+ event.ct.text = text;
+ event.ct.new_cursor_position = new_cursor_position;
+ event.ct.result = -1;
+ executeIMFRequest(&event);
- return ret;
+ return event.ct.result;
}
-static int32_t imfCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+// Delete left_length characters before and right_length characters after the cursor.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_delete_surrounding_text(input_session_t *ic, int32_t left_length, int32_t right_length)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfCommitText, &result);
- parg(text);
- iarg(new_cursor_position);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfDeleteSurroundingText);
+ event.dst.left_length = left_length;
+ event.dst.right_length = right_length;
+ event.dst.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ return event.dst.result;
}
-static int32_t imfDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length)
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_end_batch_edit(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfDeleteSurroundingText, &result);
- iarg(left_length);
- iarg(right_length);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ Q_UNUSED(ic);
- return ret;
+ // Ignore silently.
+ return 0;
}
-static int32_t imfEndBatchEdit(input_session_t *ic)
+// End composition, committing what's there.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_finish_composing_text(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfEndBatchEdit, &result);
+ QQnxImfRequest event(ic, ImfFinishComposingText);
+ event.fct.result = -1;
+ executeIMFRequest(&event);
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
-
- return ret;
+ return event.fct.result;
}
-static int32_t imfFinishComposingText(input_session_t *ic)
+// Return the position of the cursor.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_get_cursor_position(input_session_t *ic)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfFinishComposingText, &result);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfGetCursorPosition);
+ event.gcp.result = -1;
+ executeIMFRequest(&event);
- return ret;
+ return event.gcp.result;
}
-static int32_t imfGetCursorCapsMode(input_session_t *ic, int32_t req_modes)
+// Return the n characters after the cursor.
+// See comment at beginning of namespace declaration for general information
+static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetCursorCapsMode, &result);
- iarg(req_modes);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfGetTextAfterCursor);
+ event.gtac.n = n;
+ event.gtac.flags = flags;
+ event.gtac.result = 0;
+ executeIMFRequest(&event);
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.gtac.result;
}
-static int32_t imfGetCursorPosition(input_session_t *ic)
+// Return the n characters before the cursor.
+// See comment at beginning of namespace declaration for general information
+static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetCursorPosition, &result);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
-
- result.wait();
- int32_t ret = result.result().value<int32_t>();
+ QQnxImfRequest event(ic, ImfGetTextBeforeCursor);
+ event.gtac.n = n;
+ event.gtac.flags = flags;
+ event.gtac.result = 0;
+ executeIMFRequest(&event);
- return ret;
+ return event.gtac.result;
}
-static extracted_text_t *imfGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
+// Process an event from IMF. Primarily used for reflecting back keyboard events.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_send_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic)) {
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1);
- return et;
- }
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetExtractedText, &result);
- parg(request);
- iarg(flags);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest imfEvent(ic, ImfSendEvent);
+ imfEvent.sae.event = event;
+ imfEvent.sae.result = -1;
+ executeIMFRequest(&imfEvent);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return imfEvent.sae.result;
}
-static spannable_string_t *imfGetSelectedText(input_session_t *ic, int32_t flags)
+// Same as ic_send_event.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_send_async_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetSelectedText, &result);
- iarg(flags);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest imfEvent(ic, ImfSendAsyncEvent);
+ imfEvent.sae.event = event;
+ imfEvent.sae.result = -1;
+ executeIMFRequest(&imfEvent);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return imfEvent.sae.result;
}
-static spannable_string_t *imfGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags)
+// Set the range of text between start and end as the composition range.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32_t end)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetTextAfterCursor, &result);
- iarg(n);
- iarg(flags);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfSetComposingRegion);
+ event.scr.start = start;
+ event.scr.end = end;
+ event.scr.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return event.scr.result;
}
-static spannable_string_t *imfGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags)
+// Update the composition range with the supplied text. This can be called when no composition
+// range is in effect in which case one is started at the current cursor position.
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfGetTextBeforeCursor, &result);
- iarg(n);
- iarg(flags);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfSetComposingText);
+ event.sct.text = text;
+ event.sct.new_cursor_position = new_cursor_position;
+ event.sct.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- return result.result().value<extracted_text_t *>();
+ return event.sct.result;
}
-static int32_t imfPerformEditorAction(input_session_t *ic, int32_t editor_action)
+// Indicate if any text is selected
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfPerformEditorAction, &result);
- iarg(editor_action);
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfIsTextSelected);
+ event.its.pIsSelected = pIsSelected;
+ event.its.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.its.result;
}
-static int32_t imfReportFullscreenMode(input_session_t *ic, int32_t enabled)
+// Indicate if all text is selected
+// See comment at beginning of namespace declaration for general information
+static int32_t ic_is_all_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
+ qInputContextIMFRequestDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfReportFullscreenMode, &result);
- iarg(enabled);
-
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+ QQnxImfRequest event(ic, ImfIsAllTextSelected);
+ event.its.pIsSelected = pIsSelected;
+ event.its.result = -1;
+ executeIMFRequest(&event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ return event.its.result;
}
-static int32_t imfSendEvent(input_session_t *ic, event_t *event)
-{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEvent *imfEvent = new ImfEvent(ic, ImfSendEvent, 0);
- imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event));
+// The following functions are defined in the IMF headers but are not currently called.
- QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
+// Not currently used
+static int32_t ic_perform_editor_action(input_session_t *ic, int32_t editor_action)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(editor_action);
+ qCritical() << "ic_perform_editor_action not implemented";
return 0;
}
-static int32_t imfSendAsyncEvent(input_session_t *ic, event_t *event)
+// Not currently used
+static int32_t ic_report_fullscreen_mode(input_session_t *ic, int32_t enabled)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- ImfEvent *imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0);
- imfEvent->m_args[qs("event")] = QVariant::fromValue(static_cast<void *>(event));
-
- QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
+ Q_UNUSED(ic);
+ Q_UNUSED(enabled);
+ qCritical() << "ic_report_fullscreen_mode not implemented";
return 0;
}
-static int32_t imfSetComposingRegion(input_session_t *ic, int32_t start, int32_t end)
+// Not currently used
+static extracted_text_t *ic_get_extracted_text(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(request);
+ Q_UNUSED(flags);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetComposingRegion, &result);
- iarg(start);
- iarg(end);
+ qCritical() << "ic_get_extracted_text not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+// Not currently used
+static spannable_string_t *ic_get_selected_text(input_session_t *ic, int32_t flags)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(flags);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ qCritical() << "ic_get_selected_text not implemented";
+ return 0;
}
-static int32_t imfSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+// Not currently used
+static int32_t ic_get_cursor_caps_mode(input_session_t *ic, int32_t req_modes)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(req_modes);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetComposingText, &result);
- parg(text);
- iarg(new_cursor_position);
+ qCritical() << "ic_get_cursor_caps_mode not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
+// Not currently used
+static int32_t ic_clear_meta_key_states(input_session_t *ic, int32_t states)
+{
+ Q_UNUSED(ic);
+ Q_UNUSED(states);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
+ qCritical() << "ic_clear_meta_key_states not implemented";
+ return 0;
}
-static int32_t imfSetSelection(input_session_t *ic, int32_t start, int32_t end)
+// Not currently used
+static int32_t ic_set_selection(input_session_t *ic, int32_t start, int32_t end)
{
- qInputContextIMFEventDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
+ Q_UNUSED(ic);
+ Q_UNUSED(start);
+ Q_UNUSED(end);
- ImfEventResult result;
- ImfEvent *event = new ImfEvent(ic, ImfSetSelection, &result);
- iarg(start);
- iarg(end);
+ qCritical() << "ic_set_selection not implemented";
+ return 0;
+}
- QCoreApplication::postEvent(QCoreApplication::instance(), event);
- result.wait();
- int32_t ret = result.result().value<int32_t>();
- return ret;
-}
static connection_interface_t ic_funcs = {
- imfBeginBatchEdit,
- imfClearMetaKeyStates,
- imfCommitText,
- imfDeleteSurroundingText,
- imfEndBatchEdit,
- imfFinishComposingText,
- imfGetCursorCapsMode,
- imfGetCursorPosition,
- imfGetExtractedText,
- imfGetSelectedText,
- imfGetTextAfterCursor,
- imfGetTextBeforeCursor,
- imfPerformEditorAction,
- imfReportFullscreenMode,
- NULL, //ic_send_key_event
- imfSendEvent,
- imfSendAsyncEvent,
- imfSetComposingRegion,
- imfSetComposingText,
- imfSetSelection,
- NULL, //ic_set_candidates,
+ ic_begin_batch_edit,
+ ic_clear_meta_key_states,
+ ic_commit_text,
+ ic_delete_surrounding_text,
+ ic_end_batch_edit,
+ ic_finish_composing_text,
+ ic_get_cursor_caps_mode,
+ ic_get_cursor_position,
+ ic_get_extracted_text,
+ ic_get_selected_text,
+ ic_get_text_after_cursor,
+ ic_get_text_before_cursor,
+ ic_perform_editor_action,
+ ic_report_fullscreen_mode,
+ 0, //ic_send_key_event
+ ic_send_event,
+ ic_send_async_event,
+ ic_set_composing_region,
+ ic_set_composing_text,
+ ic_set_selection,
+ 0, //ic_set_candidates,
+ 0, //ic_get_cursor_offset,
+ 0, //ic_get_selection,
+ ic_is_text_selected,
+ ic_is_all_text_selected,
+ 0, //ic_get_max_cursor_offset_t
};
+} // namespace
+
static void
-initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId)
+initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId, int eventSize)
{
static int s_transactionId;
// Make sure structure is squeaky clean since it's not clear just what is significant.
- memset(pEvent, 0, sizeof(event_t));
+ memset(pEvent, 0, eventSize);
pEvent->event_type = eventType;
pEvent->event_id = eventId;
pEvent->pid = getpid();
@@ -566,7 +493,7 @@ initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType,
pEvent->transaction_id = ++s_transactionId;
}
-spannable_string_t *toSpannableString(const QString &text)
+static spannable_string_t *toSpannableString(const QString &text)
{
qInputContextDebug() << Q_FUNC_INFO << text;
@@ -579,8 +506,7 @@ spannable_string_t *toSpannableString(const QString &text)
const QChar *pData = text.constData();
wchar_t *pDst = pString->str;
- while (!pData->isNull())
- {
+ while (!pData->isNull()) {
*pDst = pData->unicode();
pDst++;
pData++;
@@ -590,7 +516,6 @@ spannable_string_t *toSpannableString(const QString &text)
return pString;
}
-} // namespace
static const input_session_t *(*p_ictrl_open_session)(connection_interface_t *) = 0;
static void (*p_ictrl_close_session)(input_session_t *) = 0;
@@ -645,27 +570,29 @@ QT_BEGIN_NAMESPACE
QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard) :
QPlatformInputContext(),
- m_lastCaretPos(0),
+ m_caretPosition(0),
m_isComposing(false),
+ m_isUpdatingText(false),
m_inputPanelVisible(false),
m_inputPanelLocale(QLocale::c()),
m_integration(integration),
- m_virtualKeyboad(keyboard)
+ m_virtualKeyboard(keyboard)
{
qInputContextDebug() << Q_FUNC_INFO;
if (!imfAvailable())
return;
- if ( p_imf_client_init() != 0 ) {
+ // Save a pointer to ourselves so we can execute calls from IMF through executeIMFRequest
+ // In practice there will only ever be a single instance.
+ Q_ASSERT(sInputContextInstance == 0);
+ sInputContextInstance = this;
+
+ if (p_imf_client_init() != 0) {
s_imfInitFailed = true;
qCritical("imf_client_init failed - IMF services will be unavailable");
}
- QCoreApplication::instance()->installEventFilter(this);
-
- // p_vkb_init_selection_service();
-
connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool)));
connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale)));
keyboardVisibilityChanged(keyboard.isVisible());
@@ -676,168 +603,74 @@ QQnxInputContext::~QQnxInputContext()
{
qInputContextDebug() << Q_FUNC_INFO;
+ Q_ASSERT(sInputContextInstance == this);
+ sInputContextInstance = 0;
+
if (!imfAvailable())
return;
- QCoreApplication::instance()->removeEventFilter(this);
p_imf_client_disconnect();
}
-#define getarg(type, name) type name = imfEvent->mArgs[qs(#name)].value<type>()
-#define getparg(type, name) type name = (type)(imfEvent->mArgs[qs(#name)].value<void*>())
-
bool QQnxInputContext::isValid() const
{
return imfAvailable();
}
-bool QQnxInputContext::eventFilter(QObject *obj, QEvent *event)
+void QQnxInputContext::processImfEvent(QQnxImfRequest *imfEvent)
{
- if (event->type() == ImfEvent::sUserEventType) {
- // Forward the event to our real handler.
- ImfEvent *imfEvent = static_cast<ImfEvent *>(event);
- switch (imfEvent->m_imfType) {
- case ImfBeginBatchEdit: {
- int32_t ret = onBeginBatchEdit(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfClearMetaKeyStates: {
- getarg(int32_t, states);
- int32_t ret = onClearMetaKeyStates(imfEvent->m_session, states);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfCommitText: {
- getparg(spannable_string_t*, text);
- getarg(int32_t, new_cursor_position);
- int32_t ret = onCommitText(imfEvent->m_session, text, new_cursor_position);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfDeleteSurroundingText: {
- getarg(int32_t, left_length);
- getarg(int32_t, right_length);
- int32_t ret = onDeleteSurroundingText(imfEvent->m_session, left_length, right_length);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfEndBatchEdit: {
- int32_t ret = onEndBatchEdit(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfFinishComposingText: {
- int32_t ret = onFinishComposingText(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetCursorCapsMode: {
- getarg(int32_t, req_modes);
- int32_t ret = onGetCursorCapsMode(imfEvent->m_session, req_modes);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetCursorPosition: {
- int32_t ret = onGetCursorPosition(imfEvent->m_session);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
-
- case ImfGetExtractedText: {
- getparg(extracted_text_request_t*, request);
- getarg(int32_t, flags);
- extracted_text_t *ret = onGetExtractedText(imfEvent->m_session, request, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ // If input session is no longer current, just bail, imfEvent should already be set with the appropriate
+ // return value.
+ if (!isSessionOkay(imfEvent->session))
+ return;
- case ImfGetSelectedText: {
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetSelectedText(imfEvent->m_session, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ switch (imfEvent->type) {
+ case ImfCommitText:
+ imfEvent->ct.result = onCommitText(imfEvent->ct.text, imfEvent->ct.new_cursor_position);
+ break;
- case ImfGetTextAfterCursor: {
- getarg(int32_t, n);
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetTextAfterCursor(imfEvent->m_session, n, flags);
- imfEvent->m_result->setResult(QVariant::fromValue(static_cast<void *>(ret)));
- break;
- }
+ case ImfDeleteSurroundingText:
+ imfEvent->dst.result = onDeleteSurroundingText(imfEvent->dst.left_length, imfEvent->dst.right_length);
+ break;
- case ImfGetTextBeforeCursor: {
- getarg(int32_t, n);
- getarg(int32_t, flags);
- spannable_string_t *ret = onGetTextBeforeCursor(imfEvent->m_session, n, flags);
- imfEvent->m_result->setResult(QVariant::fromValue((void*)ret));
- break;
- }
+ case ImfFinishComposingText:
+ imfEvent->fct.result = onFinishComposingText();
+ break;
- case ImfPerformEditorAction: {
- getarg(int32_t, editor_action);
- int32_t ret = onPerformEditorAction(imfEvent->m_session, editor_action);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfGetCursorPosition:
+ imfEvent->gcp.result = onGetCursorPosition();
+ break;
- case ImfReportFullscreenMode: {
- getarg(int32_t, enabled);
- int32_t ret = onReportFullscreenMode(imfEvent->m_session, enabled);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfGetTextAfterCursor:
+ imfEvent->gtac.result = onGetTextAfterCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
+ break;
- case ImfSendEvent: {
- getparg(event_t*, event);
- onSendEvent(imfEvent->m_session, event);
- break;
- }
+ case ImfGetTextBeforeCursor:
+ imfEvent->gtac.result = onGetTextBeforeCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
+ break;
- case ImfSendAsyncEvent: {
- getparg(event_t*, event);
- onSendAsyncEvent(imfEvent->m_session, event);
- break;
- }
+ // Don't need to distinguish these two cases
+ case ImfSendEvent:
+ case ImfSendAsyncEvent:
+ imfEvent->sae.result = onSendEvent(imfEvent->sae.event);
+ break;
- case ImfSetComposingRegion: {
- getarg(int32_t, start);
- getarg(int32_t, end);
- int32_t ret = onSetComposingRegion(imfEvent->m_session, start, end);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfSetComposingRegion:
+ imfEvent->scr.result = onSetComposingRegion(imfEvent->scr.start, imfEvent->scr.end);
+ break;
- case ImfSetComposingText: {
- getparg(spannable_string_t*, text);
- getarg(int32_t, new_cursor_position);
- int32_t ret = onSetComposingText(imfEvent->m_session, text, new_cursor_position);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
+ case ImfSetComposingText:
+ imfEvent->sct.result = onSetComposingText(imfEvent->sct.text, imfEvent->sct.new_cursor_position);
+ break;
- case ImfSetSelection: {
- getarg(int32_t, start);
- getarg(int32_t, end);
- int32_t ret = onSetSelection(imfEvent->m_session, start, end);
- imfEvent->m_result->setResult(QVariant::fromValue(ret));
- break;
- }
- }; //switch
+ case ImfIsTextSelected:
+ imfEvent->its.result = onIsTextSelected(imfEvent->its.pIsSelected);
+ break;
- return true;
- } else {
- // standard event processing
- return QObject::eventFilter(obj, event);
- }
+ case ImfIsAllTextSelected:
+ imfEvent->its.result = onIsAllTextSelected(imfEvent->its.pIsSelected);
+ break;
+ }; //switch
}
bool QQnxInputContext::filterEvent( const QEvent *event )
@@ -845,12 +678,12 @@ bool QQnxInputContext::filterEvent( const QEvent *event )
qInputContextDebug() << Q_FUNC_INFO << event;
switch (event->type()) {
- case QEvent::CloseSoftwareInputPanel: {
+ case QEvent::CloseSoftwareInputPanel:
return dispatchCloseSoftwareInputPanel();
- }
- case QEvent::RequestSoftwareInputPanel: {
+
+ case QEvent::RequestSoftwareInputPanel:
return dispatchRequestSoftwareInputPanel();
- }
+
default:
return false;
}
@@ -869,12 +702,30 @@ void QQnxInputContext::reset()
endComposition();
}
-void QQnxInputContext::update(Qt::InputMethodQueries queries)
+void QQnxInputContext::commit()
{
qInputContextDebug() << Q_FUNC_INFO;
- reset();
+ endComposition();
+}
- QPlatformInputContext::update(queries);
+void QQnxInputContext::update(Qt::InputMethodQueries queries)
+{
+ qInputContextDebug() << Q_FUNC_INFO << queries;
+
+ if (queries & Qt::ImCursorPosition) {
+ int lastCaret = m_caretPosition;
+ updateCursorPosition();
+ // If caret position has changed we need to inform IMF unless this is just due to our own action
+ // such as committing text.
+ if (!m_isUpdatingText && lastCaret != m_caretPosition) {
+ caret_event_t caretEvent;
+ initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED, sizeof(caretEvent));
+ caretEvent.old_pos = lastCaret;
+ caretEvent.new_pos = m_caretPosition;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event caret changed" << lastCaret << m_caretPosition;
+ p_ictrl_dispatch_event(&caretEvent.event);
+ }
+ }
}
void QQnxInputContext::closeSession()
@@ -889,14 +740,17 @@ void QQnxInputContext::closeSession()
}
}
-void QQnxInputContext::openSession()
+bool QQnxInputContext::openSession()
{
- qInputContextDebug() << Q_FUNC_INFO;
if (!imfAvailable())
- return;
+ return false;
closeSession();
sInputSession = p_ictrl_open_session(&ic_funcs);
+
+ qInputContextDebug() << Q_FUNC_INFO;
+
+ return sInputSession != 0;
}
bool QQnxInputContext::hasSession()
@@ -918,79 +772,89 @@ bool QQnxInputContext::hasSelectedText()
bool QQnxInputContext::dispatchRequestSoftwareInputPanel()
{
+ qInputContextDebug() << Q_FUNC_INFO << "requesting keyboard" << m_inputPanelVisible;
m_virtualKeyboard.showKeyboard();
- qInputContextDebug() << Q_FUNC_INFO << "requesting virtual keyboard";
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input || !inputMethodAccepted())
- return true;
-
- if (!hasSession())
- openSession();
- // This also means that the caret position has moved
- QInputMethodQueryEvent query(Qt::ImCursorPosition);
- QCoreApplication::sendEvent(input, &query);
- int caretPos = query.value(Qt::ImCursorPosition).toInt();
- caret_event_t caretEvent;
- memset(&caretEvent, 0, sizeof(caret_event_t));
- initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED);
- caretEvent.old_pos = m_lastCaretPos;
- m_lastCaretPos = caretEvent.new_pos = caretPos;
- p_ictrl_dispatch_event((event_t *)&caretEvent);
return true;
}
bool QQnxInputContext::dispatchCloseSoftwareInputPanel()
{
+ qInputContextDebug() << Q_FUNC_INFO << "hiding keyboard" << m_inputPanelVisible;
m_virtualKeyboard.hideKeyboard();
- qInputContextDebug() << Q_FUNC_INFO << "hiding virtual keyboard";
- // This also means we are stopping composition, but we should already have done that.
return true;
}
/**
* IMF Event Dispatchers.
*/
-bool QQnxInputContext::dispatchFocusEvent(FocusEventId id, int hints)
+bool QQnxInputContext::dispatchFocusGainEvent(int inputHints)
{
- qInputContextDebug() << Q_FUNC_INFO;
+ if (sInputSession != 0)
+ dispatchFocusLossEvent();
- if (!sInputSession) {
- qWarning() << Q_FUNC_INFO << "Attempt to dispatch a focus event with no input session.";
- return false;
- }
+ QObject *input = qGuiApp->focusObject();
- if (!imfAvailable())
+ if (!input || !openSession())
return false;
// Set the last caret position to 0 since we don't really have one and we don't
// want to have the old one.
- m_lastCaretPos = 0;
+ m_caretPosition = 0;
+
+ QInputMethodQueryEvent query(Qt::ImHints);
+ QCoreApplication::sendEvent(input, &query);
focus_event_t focusEvent;
- memset(&focusEvent, 0, sizeof(focusEvent));
- initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, id);
+ initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_GAINED, sizeof(focusEvent));
focusEvent.style = DEFAULT_STYLE;
- if (hints && Qt::ImhNoPredictiveText)
+ if (inputHints & Qt::ImhNoPredictiveText)
focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION;
- if (hints && Qt::ImhNoAutoUppercase)
+ if (inputHints & Qt::ImhNoAutoUppercase)
focusEvent.style |= NO_AUTO_TEXT;
+ // Following styles are mutually exclusive
+ if (inputHints & Qt::ImhHiddenText) {
+ focusEvent.style |= IMF_PASSWORD_TYPE;
+ } else if (inputHints & Qt::ImhDialableCharactersOnly) {
+ focusEvent.style |= IMF_PHONE_TYPE;
+ } else if (inputHints & Qt::ImhUrlCharactersOnly) {
+ focusEvent.style |= IMF_URL_TYPE;
+ } else if (inputHints & Qt::ImhEmailCharactersOnly) {
+ focusEvent.style |= IMF_EMAIL_TYPE;
+ }
+
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus gain style:" << focusEvent.style;
+
p_ictrl_dispatch_event((event_t *)&focusEvent);
return true;
}
-bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap)
+void QQnxInputContext::dispatchFocusLossEvent()
+{
+ if (sInputSession != 0) {
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_event focus lost";
+
+ focus_event_t focusEvent;
+ initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_LOST, sizeof(focusEvent));
+ p_ictrl_dispatch_event((event_t *)&focusEvent);
+ sInputSession = 0;
+ }
+}
+
+bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId)
{
+ Q_UNUSED(scan);
+
if (!imfAvailable())
return false;
int key = (flags & KEY_SYM_VALID) ? sym : cap;
- bool navKey = false;
- switch ( key ) {
+ bool navigationKey = false;
+ switch (key) {
case KEYCODE_RETURN:
/* In a single line edit we should end composition because enter might be used by something.
endComposition();
@@ -1007,27 +871,22 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
break;
case KEYCODE_LEFT:
key = NAVIGATE_LEFT;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_RIGHT:
key = NAVIGATE_RIGHT;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_UP:
key = NAVIGATE_UP;
- navKey = true;
+ navigationKey = true;
break;
case KEYCODE_DOWN:
key = NAVIGATE_DOWN;
- navKey = true;
+ navigationKey = true;
break;
- case KEYCODE_CAPS_LOCK:
- case KEYCODE_LEFT_SHIFT:
- case KEYCODE_RIGHT_SHIFT:
case KEYCODE_LEFT_CTRL:
case KEYCODE_RIGHT_CTRL:
- case KEYCODE_LEFT_ALT:
- case KEYCODE_RIGHT_ALT:
case KEYCODE_MENU:
case KEYCODE_LEFT_HYPER:
case KEYCODE_RIGHT_HYPER:
@@ -1041,85 +900,133 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
break;
}
- if ( mod & KEYMOD_CTRL ) {
- // If CTRL is pressed, just let AIR handle it. But terminate any composition first
- //endComposition();
- return false;
- }
-
// Pass the keys we don't know about on through
if ( key == 0 )
return false;
- // IMF doesn't need key releases so just swallow them.
- if (!(flags & KEY_DOWN))
- return true;
-
- if ( navKey ) {
+ if (navigationKey) {
// Even if we're forwarding up events, we can't do this for
// navigation keys.
if ( flags & KEY_DOWN ) {
navigation_event_t navEvent;
- initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key);
+ initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key, sizeof(navEvent));
navEvent.magnitude = 1;
- qInputContextDebug() << Q_FUNC_INFO << "dispatch navigation event " << key;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even navigation" << key;
p_ictrl_dispatch_event(&navEvent.event);
}
- }
- else {
+ } else {
key_event_t keyEvent;
- initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP);
- keyEvent.key_code = key;
- keyEvent.character = 0;
- keyEvent.meta_key_state = 0;
+ initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP,
+ sizeof(keyEvent));
+ keyEvent.key_code = cap;
+ keyEvent.character = sym;
+ keyEvent.meta_key_state = mod;
+ keyEvent.sequence_id = sequenceId;
p_ictrl_dispatch_event(&keyEvent.event);
- qInputContextDebug() << Q_FUNC_INFO << "dispatch key event " << key;
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even key" << key;
}
- scan = 0;
return true;
}
-void QQnxInputContext::endComposition()
+void QQnxInputContext::updateCursorPosition()
{
- if (!m_isComposing)
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
return;
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ QInputMethodQueryEvent query(Qt::ImCursorPosition);
+ QCoreApplication::sendEvent(input, &query);
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
+
+ qInputContextDebug() << Q_FUNC_INFO << m_caretPosition;
+}
+
+void QQnxInputContext::endComposition()
+{
+ if (!m_isComposing)
return;
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(m_composingText);
- m_composingText = QString();
- m_isComposing = false;
- QCoreApplication::sendEvent(input, &event);
+ finishComposingText();
action_event_t actionEvent;
- memset(&actionEvent, 0, sizeof(actionEvent));
- initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION);
+ initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION, sizeof(actionEvent));
+ qInputContextDebug() << Q_FUNC_INFO << "ictrl_dispatch_even end composition";
p_ictrl_dispatch_event(&actionEvent.event);
}
-void QQnxInputContext::setComposingText(QString const& composingText)
+void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_cursor_position)
{
- m_composingText = composingText;
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return;
+
+ if (new_cursor_position > 0)
+ new_cursor_position += text->length - 1;
+
+ m_composingText = QString::fromWCharArray(text->str, text->length);
m_isComposing = true;
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return;
+ qInputContextDebug() << Q_FUNC_INFO << m_composingText << new_cursor_position;
QList<QInputMethodEvent::Attribute> attributes;
- QTextCharFormat format;
- format.setFontUnderline(true);
- attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format));
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ new_cursor_position,
+ 1,
+ QVariant()));
+
+ for (unsigned int i = 0; i < text->spans_count; ++i) {
+ const uint64_t knownMask = ACTIVE_REGION_ATTRIB | COMPOSED_TEXT_ATTRIB |
+ AUTO_CORRECTION_ATTRIB | REVERT_CORRECTION_ATTRIB;
+ bool selected = (text->spans[i].attributes_mask & ACTIVE_REGION_ATTRIB) != 0;
+ bool converted = (text->spans[i].attributes_mask & COMPOSED_TEXT_ATTRIB) != 0;
+ bool autoCorrected = (text->spans[i].attributes_mask & AUTO_CORRECTION_ATTRIB) != 0;
+ bool reverted = (text->spans[i].attributes_mask & REVERT_CORRECTION_ATTRIB) != 0;
+
+ if (text->spans[i].attributes_mask & knownMask) {
+ QTextCharFormat format;
+
+ if (converted || selected) {
+ format.setFontUnderline(true);
+ }
+ if (selected) {
+ format.setBackground(QBrush(sSelectedColor));
+ } else if (autoCorrected) {
+ format.setBackground(QBrush(sAutoCorrectedColor));
+ } else if (reverted) {
+ format.setBackground(QBrush(sRevertedColor));
+ }
+ qInputContextDebug() << "attrib:" << selected << converted << text->spans[i].start << text->spans[i].end;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, text->spans[i].start,
+ text->spans[i].end - text->spans[i].start + 1, QVariant(format)));
+ }
+ }
+ QInputMethodEvent event(m_composingText, attributes);
+ m_isUpdatingText = true;
+ QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
- QInputMethodEvent event(composingText, attributes);
+ updateCursorPosition();
+}
- QCoreApplication::sendEvent(input, &event);
+void QQnxInputContext::finishComposingText()
+{
+ QObject *input = qGuiApp->focusObject();
+
+ if (input) {
+ qInputContextDebug() << Q_FUNC_INFO << m_composingText;
+
+ QInputMethodEvent event;
+ event.setCommitString(m_composingText);
+ m_isUpdatingText = true;
+ QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
+ }
+ m_composingText = QString();
+ m_isComposing = false;
+
+ updateCursorPosition();
}
int32_t QQnxInputContext::processEvent(event_t *event)
@@ -1140,19 +1047,24 @@ int32_t QQnxInputContext::processEvent(event_t *event)
event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT :
event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0;
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
- QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
+ QQnxScreenEventHandler::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
+ QQnxScreenEventHandler::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
result = 0;
break;
}
case EVENT_KEY: {
- qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY";
- key_event_t *kevent = static_cast<key_event_t *>(event);
-
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
- QQnxEventThread::injectKeyboardEvent(KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
-
+ key_event_t *kevent = reinterpret_cast<key_event_t *>(event);
+ int keySym = kevent->character != 0 ? kevent->character : kevent->key_code;
+ int keyCap = kevent->key_code;
+ int modifiers = 0;
+ if (kevent->meta_key_state & META_SHIFT_ON)
+ modifiers |= KEYMOD_SHIFT;
+ int flags = KEY_SYM_VALID | KEY_CAP_VALID;
+ if (event->event_id == IMF_KEY_DOWN)
+ flags |= KEY_DOWN;
+ qInputContextDebug() << Q_FUNC_INFO << "EVENT_KEY" << flags << keySym;
+ QQnxScreenEventHandler::injectKeyboardEvent(flags, keySym, modifiers, 0, keyCap);
result = 0;
break;
}
@@ -1179,339 +1091,182 @@ int32_t QQnxInputContext::processEvent(event_t *event)
* IMF Event Handlers
*/
-int32_t QQnxInputContext::onBeginBatchEdit(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // We don't care.
- return 0;
-}
-
-int32_t QQnxInputContext::onClearMetaKeyStates(input_session_t *ic, int32_t states)
+int32_t QQnxInputContext::onCommitText(spannable_string_t *text, int32_t new_cursor_position)
{
- Q_UNUSED(states);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onClearMetaKeyStates is unsupported.";
- return 0;
-}
-
-int32_t QQnxInputContext::onCommitText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
-{
- Q_UNUSED(new_cursor_position); // TODO: How can we set the cursor position it's not part of the API.
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- QString commitString = QString::fromWCharArray(text->str, text->length);
-
- qInputContextDebug() << Q_FUNC_INFO << "Committing [" << commitString << "]";
-
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(commitString, 0, 0);
+ Q_UNUSED(new_cursor_position);
- QCoreApplication::sendEvent(input, &event);
- m_composingText = QString();
+ m_composingText = QString::fromWCharArray(text->str, text->length);
+ finishComposingText();
return 0;
}
-int32_t QQnxInputContext::onDeleteSurroundingText(input_session_t *ic, int32_t left_length, int32_t right_length)
+int32_t QQnxInputContext::onDeleteSurroundingText(int32_t left_length, int32_t right_length)
{
qInputContextDebug() << Q_FUNC_INFO << "L:" << left_length << " R:" << right_length;
- if (!isSessionOkay(ic))
- return 0;
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- if (hasSelectedText()) {
- QQnxEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
- QQnxEventThread::injectKeyboardEvent(KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
- reset();
+ if (!input)
return 0;
- }
int replacementLength = left_length + right_length;
int replacementStart = -left_length;
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(QLatin1String(""), replacementStart, replacementLength);
- QCoreApplication::sendEvent(input, &event);
-
- return 0;
-}
-
-int32_t QQnxInputContext::onEndBatchEdit(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
+ finishComposingText();
- if (!isSessionOkay(ic))
- return 0;
-
- return 0;
-}
-
-int32_t QQnxInputContext::onFinishComposingText(input_session_t *ic)
-{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- // Only update the control, no need to send a message back to imf (don't call
- // end composition)
- QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(QLatin1String(""), attributes);
- event.setCommitString(m_composingText);
- m_composingText = QString();
- m_isComposing = false;
+ QInputMethodEvent event;
+ event.setCommitString(QString(), replacementStart, replacementLength);
+ m_isUpdatingText = true;
QCoreApplication::sendEvent(input, &event);
+ m_isUpdatingText = false;
- return 0;
-}
-
-int32_t QQnxInputContext::onGetCursorCapsMode(input_session_t *ic, int32_t req_modes)
-{
- Q_UNUSED(req_modes);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onGetCursorCapsMode is unsupported.";
+ updateCursorPosition();
return 0;
}
-int32_t QQnxInputContext::onGetCursorPosition(input_session_t *ic)
+int32_t QQnxInputContext::onFinishComposingText()
{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
-
- QInputMethodQueryEvent query(Qt::ImCursorPosition);
- QCoreApplication::sendEvent(input, &query);
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
-
- return m_lastCaretPos;
-}
-
-extracted_text_t *QQnxInputContext::onGetExtractedText(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
-{
- Q_UNUSED(flags);
- Q_UNUSED(request);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic)) {
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1));
- return et;
- }
+ finishComposingText();
- // Used to update dictionaries, but not supported right now.
- extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
- et->text = reinterpret_cast<spannable_string_t *>(calloc(sizeof(spannable_string_t),1));
-
- return et;
+ return 0;
}
-spannable_string_t *QQnxInputContext::onGetSelectedText(input_session_t *ic, int32_t flags)
+int32_t QQnxInputContext::onGetCursorPosition()
{
- Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return 0;
- QInputMethodQueryEvent query(Qt::ImCurrentSelection);
- QCoreApplication::sendEvent(input, &query);
- QString text = query.value(Qt::ImCurrentSelection).toString();
+ updateCursorPosition();
- return toSpannableString(text);
+ return m_caretPosition;
}
-spannable_string_t *QQnxInputContext::onGetTextAfterCursor(input_session_t *ic, int32_t n, int32_t flags)
+spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return toSpannableString("");
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- return toSpannableString(text.mid(m_lastCaretPos+1, n));
+ return toSpannableString(text.mid(m_caretPosition, n));
}
-spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(input_session_t *ic, int32_t n, int32_t flags)
+spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return toSpannableString("");
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return toSpannableString("");
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- if (n < m_lastCaretPos)
- return toSpannableString(text.mid(m_lastCaretPos - n, n));
+ if (n < m_caretPosition)
+ return toSpannableString(text.mid(m_caretPosition - n, n));
else
- return toSpannableString(text.mid(0, m_lastCaretPos));
-}
-
-int32_t QQnxInputContext::onPerformEditorAction(input_session_t *ic, int32_t editor_action)
-{
- Q_UNUSED(editor_action);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onPerformEditorAction is unsupported.";
-
- return 0;
-}
-
-int32_t QQnxInputContext::onReportFullscreenMode(input_session_t *ic, int32_t enabled)
-{
- Q_UNUSED(enabled);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onReportFullscreenMode is unsupported.";
-
- return 0;
+ return toSpannableString(text.mid(0, m_caretPosition));
}
-int32_t QQnxInputContext::onSendEvent(input_session_t *ic, event_t *event)
+int32_t QQnxInputContext::onSendEvent(event_t *event)
{
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
return processEvent(event);
}
-int32_t QQnxInputContext::onSendAsyncEvent(input_session_t *ic, event_t *event)
+int32_t QQnxInputContext::onSendAsyncEvent(event_t *event)
{
qInputContextDebug() << Q_FUNC_INFO;
- if (!isSessionOkay(ic))
- return 0;
-
return processEvent(event);
}
-int32_t QQnxInputContext::onSetComposingRegion(input_session_t *ic, int32_t start, int32_t end)
+int32_t QQnxInputContext::onSetComposingRegion(int32_t start, int32_t end)
{
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
+ if (!input)
return 0;
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
QCoreApplication::sendEvent(input, &query);
QString text = query.value(Qt::ImSurroundingText).toString();
- m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt();
+ m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
+
+ qInputContextDebug() << Q_FUNC_INFO << text;
- QString empty = QString::fromLatin1("");
- text = text.mid(start, end - start);
+ m_isUpdatingText = true;
// Delete the current text.
+ QInputMethodEvent deleteEvent;
+ deleteEvent.setCommitString(QString(), start - m_caretPosition, end - start);
+ QCoreApplication::sendEvent(input, &deleteEvent);
+
+ m_composingText = text.mid(start, end - start);
+ m_isComposing = true;
+
QList<QInputMethodEvent::Attribute> attributes;
- QInputMethodEvent event(empty, attributes);
- event.setCommitString(empty, start - m_lastCaretPos, end - start);
- QCoreApplication::sendEvent(input, &event);
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, m_composingText.length(), format));
+
+ QInputMethodEvent setTextEvent(m_composingText, attributes);
+ QCoreApplication::sendEvent(input, &setTextEvent);
- // Move the specified text into a preedit string.
- setComposingText(text);
+ m_isUpdatingText = false;
return 0;
}
-int32_t QQnxInputContext::onSetComposingText(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
+int32_t QQnxInputContext::onSetComposingText(spannable_string_t *text, int32_t new_cursor_position)
{
- Q_UNUSED(new_cursor_position);
- qInputContextDebug() << Q_FUNC_INFO;
-
- if (!isSessionOkay(ic))
- return 0;
-
- QObject *input = qGuiApp->focusObject();
- if (!imfAvailable() || !input)
- return 0;
+ if (text->length > 0) {
+ updateComposition(text, new_cursor_position);
+ } else {
+ // If the composing text is empty we can simply end composition, the visual effect is the same.
+ // However, sometimes one wants to display hint text in an empty text field and for this to work
+ // QQuickTextEdit.inputMethodComposing has to be false if the composition string is empty.
+ m_composingText.clear();
+ finishComposingText();
+ }
+ return 0;
+}
- m_isComposing = true;
+int32_t QQnxInputContext::onIsTextSelected(int32_t* pIsSelected)
+{
+ *pIsSelected = hasSelectedText();
- QString preeditString = QString::fromWCharArray(text->str, text->length);
- setComposingText(preeditString);
+ qInputContextDebug() << Q_FUNC_INFO << *pIsSelected;
return 0;
}
-int32_t QQnxInputContext::onSetSelection(input_session_t *ic, int32_t start, int32_t end)
+int32_t QQnxInputContext::onIsAllTextSelected(int32_t* pIsSelected)
{
- Q_UNUSED(start);
- Q_UNUSED(end);
- qInputContextDebug() << Q_FUNC_INFO;
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return -1;
- if (!isSessionOkay(ic))
- return 0;
+ QInputMethodQueryEvent query(Qt::ImCurrentSelection | Qt::ImSurroundingText);
+ QCoreApplication::sendEvent(input, &query);
- // Should never get called.
- qCritical() << Q_FUNC_INFO << "onSetSelection is unsupported.";
+ *pIsSelected = query.value(Qt::ImSurroundingText).toString().length() == query.value(Qt::ImCurrentSelection).toString().length();
+
+ qInputContextDebug() << Q_FUNC_INFO << *pIsSelected;
return 0;
}
@@ -1563,8 +1318,16 @@ void QQnxInputContext::setFocusObject(QObject *object)
if (!inputMethodAccepted()) {
if (m_inputPanelVisible)
hideInputPanel();
+ if (sInputSession != 0)
+ dispatchFocusLossEvent();
} else {
- m_virtualKeyboard.setInputHintsFromObject(object);
+ QInputMethodQueryEvent query(Qt::ImHints);
+ QCoreApplication::sendEvent(object, &query);
+ int inputHints = query.value(Qt::ImHints).toInt();
+
+ dispatchFocusGainEvent(inputHints);
+
+ m_virtualKeyboard.setInputHints(inputHints);
if (!m_inputPanelVisible)
showInputPanel();