7#include <android/log.h>
14#include "private/qhighdpiscaling_p.h"
16#include <QTextBoundaryFinder>
17#include <QTextCharFormat>
18#include <QtCore/QJniEnvironment>
19#include <QtCore/QJniObject>
21#include <qguiapplication.h>
22#include <qinputmethod.h>
23#include <qsharedpointer.h>
26#include <qpa/qplatformwindow.h>
30using namespace Qt::StringLiterals;
41 m_context->beginBatchEdit();
46 m_context->endBatchEdit();
49 BatchEditLock(
const BatchEditLock &) =
delete;
50 BatchEditLock &operator=(
const BatchEditLock &) =
delete;
74 QtAndroidPrivate::AndroidDeadlockProtector protector(
75 u"QAndroidInputContext::runOnQtThread()"_s);
76 if (!protector.acquire())
78 QMetaObject::invokeMethod(m_androidInputContext,
"safeCall", Qt::BlockingQueuedConnection, Q_ARG(std::function<
void()>, func));
90 if (!focusObject->property(
"inputMethodHints").isValid())
98 if (!hasValidFocusObject())
101 qCDebug(lcQpaInputMethods) <<
"@@@ BEGINBATCH";
102 jboolean res = JNI_FALSE;
109 if (!hasValidFocusObject())
112 qCDebug(lcQpaInputMethods) <<
"@@@ ENDBATCH";
114 jboolean res = JNI_FALSE;
122 if (!hasValidFocusObject())
126 const jchar *jstr = env->GetStringChars(text, &isCopy);
127 QString str(
reinterpret_cast<
const QChar *>(jstr), env->GetStringLength(text));
128 env->ReleaseStringChars(text, jstr);
130 qCDebug(lcQpaInputMethods) <<
"@@@ COMMIT" << str << newCursorPosition;
131 jboolean res = JNI_FALSE;
138 if (!hasValidFocusObject())
141 qCDebug(lcQpaInputMethods) <<
"@@@ DELETE" << leftLength << rightLength;
142 jboolean res = JNI_FALSE;
149 if (!hasValidFocusObject())
152 qCDebug(lcQpaInputMethods) <<
"@@@ FINISH";
153 jboolean res = JNI_FALSE;
160 if (!hasValidFocusObject())
164 const jchar *jstr = env->GetStringChars(text, &isCopy);
165 QString str(
reinterpret_cast<
const QChar *>(jstr), env->GetStringLength(text));
166 env->ReleaseStringChars(text, jstr);
168 qCDebug(lcQpaInputMethods) <<
"@@@ REPLACE" << start << end << str << newCursorPosition;
169 jboolean res = JNI_FALSE;
190 QAndroidInputContext::ExtractedText extractedText;
191 runOnQtThread([&]{extractedText =
m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags);});
193 qCDebug(lcQpaInputMethods) <<
"@@@ GETEX" << hintMaxChars << hintMaxLines << QString::fromLatin1(
"0x") + QString::number(flags,16) << extractedText.text <<
"partOff:" << extractedText.partialStartOffset << extractedText.partialEndOffset <<
"sel:" << extractedText.selectionStart << extractedText.selectionEnd <<
"offset:" << extractedText.startOffset;
195 jobject object = env->NewObject(m_extractedTextClass, m_classConstructorMethodID);
196 env->SetIntField(object, m_partialStartOffsetFieldID, extractedText.partialStartOffset);
197 env->SetIntField(object, m_partialEndOffsetFieldID, extractedText.partialEndOffset);
198 env->SetIntField(object, m_selectionStartFieldID, extractedText.selectionStart);
199 env->SetIntField(object, m_selectionEndFieldID, extractedText.selectionEnd);
200 env->SetIntField(object, m_startOffsetFieldID, extractedText.startOffset);
201 env->SetObjectField(object,
203 env->NewString(
reinterpret_cast<
const jchar *>(extractedText.text.constData()),
204 jsize(extractedText.text.length())));
216 qCDebug(lcQpaInputMethods) <<
"@@@ GETSEL" << text;
219 return env->NewString(
reinterpret_cast<
const jchar *>(text.constData()), jsize(text.length()));
229 qCDebug(lcQpaInputMethods) <<
"@@@ GETA" << length << text;
230 return env->NewString(
reinterpret_cast<
const jchar *>(text.constData()), jsize(text.length()));
240 qCDebug(lcQpaInputMethods) <<
"@@@ GETB" << length << text;
241 return env->NewString(
reinterpret_cast<
const jchar *>(text.constData()), jsize(text.length()));
246 if (!hasValidFocusObject())
250 const jchar *jstr = env->GetStringChars(text, &isCopy);
251 QString str(
reinterpret_cast<
const QChar *>(jstr), env->GetStringLength(text));
252 env->ReleaseStringChars(text, jstr);
254 qCDebug(lcQpaInputMethods) <<
"@@@ SET" << str << newCursorPosition;
255 jboolean res = JNI_FALSE;
262 if (!hasValidFocusObject())
265 qCDebug(lcQpaInputMethods) <<
"@@@ SETR" << start << end;
266 jboolean res = JNI_FALSE;
274 if (!hasValidFocusObject())
277 qCDebug(lcQpaInputMethods) <<
"@@@ SETSEL" << start << end;
278 jboolean res = JNI_FALSE;
286 if (!hasValidFocusObject())
289 qCDebug(lcQpaInputMethods) <<
"@@@ SELALL";
290 jboolean res = JNI_FALSE;
297 if (!hasValidFocusObject())
300 qCDebug(lcQpaInputMethods) <<
"@@@";
301 jboolean res = JNI_FALSE;
308 if (!hasValidFocusObject())
311 qCDebug(lcQpaInputMethods) <<
"@@@";
312 jboolean res = JNI_FALSE;
319 if (!hasValidFocusObject())
322 qCDebug(lcQpaInputMethods) <<
"@@@";
323 jboolean res = JNI_FALSE;
330 if (!hasValidFocusObject())
333 qCDebug(lcQpaInputMethods) <<
"@@@ PASTE";
334 jboolean res = JNI_FALSE;
341 if (!hasValidFocusObject())
344 qCDebug(lcQpaInputMethods) <<
"@@@ UPDATECURSORPOS";
367 {
"beginBatchEdit",
"()Z", (
void *)beginBatchEdit},
368 {
"endBatchEdit",
"()Z", (
void *)endBatchEdit},
369 {
"commitText",
"(Ljava/lang/String;I)Z", (
void *)commitText},
370 {
"deleteSurroundingText",
"(II)Z", (
void *)deleteSurroundingText},
371 {
"finishComposingText",
"()Z", (
void *)finishComposingText},
372 {
"getCursorCapsMode",
"(I)I", (
void *)getCursorCapsMode},
373 {
"getExtractedText",
"(III)Lorg/qtproject/qt/android/QtExtractedText;", (
void *)getExtractedText},
374 {
"getSelectedText",
"(I)Ljava/lang/String;", (
void *)getSelectedText},
375 {
"getTextAfterCursor",
"(II)Ljava/lang/String;", (
void *)getTextAfterCursor},
376 {
"getTextBeforeCursor",
"(II)Ljava/lang/String;", (
void *)getTextBeforeCursor},
377 {
"replaceText",
"(IILjava/lang/String;I)Z", (
void *)replaceText},
378 {
"setComposingText",
"(Ljava/lang/String;I)Z", (
void *)setComposingText},
379 {
"setComposingRegion",
"(II)Z", (
void *)setComposingRegion},
380 {
"setSelection",
"(II)Z", (
void *)setSelection},
381 {
"selectAll",
"()Z", (
void *)selectAll},
382 {
"cut",
"()Z", (
void *)cut},
383 {
"copy",
"()Z", (
void *)copy},
384 {
"copyURL",
"()Z", (
void *)copyURL},
385 {
"paste",
"()Z", (
void *)paste},
386 {
"updateCursorPosition",
"()Z", (
void *)updateCursorPosition},
387 {
"reportFullscreenMode",
"(Z)V", (
void *)reportFullscreenMode},
388 {
"fullscreenMode",
"()Z", (
void *)fullscreenMode}
393 QRect windowRect = QPlatformInputContext::inputItemRectangle().toRect();
394 QPlatformWindow *window =
qGuiApp->focusWindow()->handle();
395 return QRect(window->mapToGlobal(windowRect.topLeft()), windowRect.size());
400 , m_composingTextStart(-1)
401 , m_composingCursor(-1)
403 , m_batchEditNestingLevel(0)
405 , m_fullScreenMode(
false)
409 if (Q_UNLIKELY(!clazz)) {
410 qCritical() <<
"Native registration unable to find class '"
416 if (Q_UNLIKELY(env->RegisterNatives(clazz, methods,
sizeof(
methods) /
sizeof(
methods[0])) < 0)) {
417 qCritical() <<
"RegisterNatives failed for '"
424 if (Q_UNLIKELY(!clazz)) {
425 qCritical() <<
"Native registration unable to find class '"
433 if (Q_UNLIKELY(!m_classConstructorMethodID)) {
434 qCritical(
"GetMethodID failed");
439 if (Q_UNLIKELY(!m_partialEndOffsetFieldID)) {
440 qCritical(
"Can't find field partialEndOffset");
445 if (Q_UNLIKELY(!m_partialStartOffsetFieldID)) {
446 qCritical(
"Can't find field partialStartOffset");
451 if (Q_UNLIKELY(!m_selectionEndFieldID)) {
452 qCritical(
"Can't find field selectionEnd");
457 if (Q_UNLIKELY(!m_selectionStartFieldID)) {
458 qCritical(
"Can't find field selectionStart");
463 if (Q_UNLIKELY(!m_startOffsetFieldID)) {
464 qCritical(
"Can't find field startOffset");
468 m_textFieldID = env->GetFieldID(m_extractedTextClass,
"text",
"Ljava/lang/String;");
469 if (Q_UNLIKELY(!m_textFieldID)) {
470 qCritical(
"Can't find field text");
473 qRegisterMetaType<QInputMethodEvent *>(
"QInputMethodEvent*");
474 qRegisterMetaType<QInputMethodQueryEvent *>(
"QInputMethodQueryEvent*");
477 QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
478 this, &QAndroidInputContext::updateSelectionHandles);
479 QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::anchorRectangleChanged,
480 this, &QAndroidInputContext::updateSelectionHandles);
481 QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::inputItemClipRectangleChanged,
this, [
this]{
482 auto im =
qGuiApp->inputMethod();
483 if (!im->inputItemClipRectangle().contains(im->anchorRectangle()) ||
484 !im->inputItemClipRectangle().contains(im->cursorRectangle())) {
485 m_handleMode = Hidden;
486 updateSelectionHandles();
489 m_hideCursorHandleTimer.setInterval(4000);
490 m_hideCursorHandleTimer.setSingleShot(
true);
491 m_hideCursorHandleTimer.setTimerType(Qt::VeryCoarseTimer);
492 connect(&m_hideCursorHandleTimer, &QTimer::timeout,
this, [
this]{
493 m_handleMode = Hidden;
518 QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
519 return absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt();
525 QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
526 return absolutePos.isValid() ? absolutePos.toInt() - query->value(Qt::ImCursorPosition).toInt() : 0;
531 focusObjectStopComposing();
533 m_batchEditNestingLevel = 0;
534 m_handleMode = Hidden;
536 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(Qt::ImEnabled);
537 if (!query.isNull() && query->value(Qt::ImEnabled).toBool()) {
547 focusObjectStopComposing();
552 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
553 if (!query.isNull() && m_batchEditNestingLevel == 0) {
554 const int cursorPos = getAbsoluteCursorPosition(query);
555 const int composeLength = m_composingText.length();
558 if (m_composingText.isEmpty() != (m_composingTextStart == -1))
559 qWarning() <<
"Input method out of sync" << m_composingText << m_composingTextStart;
561 int realSelectionStart = cursorPos;
562 int realSelectionEnd = cursorPos;
564 int cpos = query->value(Qt::ImCursorPosition).toInt();
565 int anchor = query->value(Qt::ImAnchorPosition).toInt();
566 if (cpos != anchor) {
567 if (!m_composingText.isEmpty()) {
568 qWarning(
"Selecting text while preediting may give unpredictable results.");
569 focusObjectStopComposing();
571 int blockPos = getBlockPosition(query);
572 realSelectionStart = blockPos + cpos;
573 realSelectionEnd = blockPos + anchor;
576 if (focusObjectIsComposing())
577 realSelectionStart = realSelectionEnd = m_composingCursor;
580 if (realSelectionStart > realSelectionEnd)
581 std::swap(realSelectionStart, realSelectionEnd);
584 m_composingTextStart
, m_composingTextStart + composeLength
);
590 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
593 return query->value(Qt::ImHints).toUInt() & Qt::ImhNoEditMenu;
598 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
601 return query->value(Qt::ImHints).toUInt() & Qt::ImhNoTextHandles;
606 if (m_fullScreenMode) {
610 static bool noHandles = qEnvironmentVariableIntValue(
"QT_QPA_NO_TEXT_HANDLES");
611 if (noHandles || !m_focusObject)
614 if (isImhNoTextHandlesSet()) {
619 auto im =
qGuiApp->inputMethod();
621 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled
622 | Qt::ImCurrentSelection | Qt::ImHints | Qt::ImSurroundingText
624 QCoreApplication::sendEvent(m_focusObject, &query);
626 int cpos = query.value(Qt::ImCursorPosition).toInt();
627 int anchor = query.value(Qt::ImAnchorPosition).toInt();
628 const QVariant readOnlyVariant = query.value(Qt::ImReadOnly);
629 bool readOnly = readOnlyVariant.toBool();
630 QPlatformWindow *qPlatformWindow =
qGuiApp->focusWindow()->handle();
632 if (!readOnly && ((m_handleMode & 0xff) == Hidden)) {
637 if ( cpos == anchor && (!readOnlyVariant.isValid() || readOnly)) {
642 if (cpos == anchor || im->anchorRectangle().isNull()) {
643 auto curRect = cursorRectangle();
644 QPoint cursorPointGlobal = QPoint(curRect.x() + (curRect.width() / 2), curRect.y() + curRect.height());
645 QPoint cursorPoint(curRect.center().x(), curRect.bottom());
650 if (cursorPointGlobal != cursorPoint) {
651 x = cursorPointGlobal.x();
652 y = cursorPointGlobal.y();
655 QPoint editMenuPoint(x, y);
656 m_handleMode &= ShowEditPopup;
657 m_handleMode |= ShowCursor;
658 uint32_t buttons = 0;
659 const bool withEditMenu = !isImhNoEditMenuSet();
662 if (!query.value(Qt::ImSurroundingText).toString().isEmpty())
665 QtAndroidInput::updateHandles(m_handleMode, editMenuPoint, buttons, cursorPointGlobal);
666 m_hideCursorHandleTimer.start();
671 m_handleMode = ShowSelection | ShowEditPopup ;
672 auto leftRect = cursorRectangle();
673 auto rightRect = anchorRectangle();
675 std::swap(leftRect, rightRect);
679 QPoint leftPoint(qPlatformWindow->mapToGlobal(leftRect.bottomLeft().toPoint()));
680 QPoint rightPoint(qPlatformWindow->mapToGlobal(rightRect.bottomRight().toPoint()));
683 if (platformIntegration) {
687 int rightSideOfScreen = platformIntegration
->screen()->availableGeometry().right();
690 leftPoint = qPlatformWindow->mapFromGlobal(leftPoint);
694 rightPoint = qPlatformWindow->mapFromGlobal(rightPoint);
696 QPoint editPoint(leftRect.united(rightRect).topLeft().toPoint());
697 uint32_t buttons = 0;
698 const bool withEditMenu = !isImhNoEditMenuSet();
704 QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, rightPoint,
705 query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
706 m_hideCursorHandleTimer.stop();
711
712
713
714
717 if (m_batchEditNestingLevel != 0) {
718 qWarning() <<
"QAndroidInputContext::handleLocationChanged returned";
724 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition
725 | Qt::ImAbsolutePosition | Qt::ImCurrentSelection);
726 QCoreApplication::sendEvent(m_focusObject, &query);
727 int cpos = query.value(Qt::ImCursorPosition).toInt();
728 int anchor = query.value(Qt::ImAnchorPosition).toInt();
729 auto leftRect = cursorRectangle();
730 auto rightRect = anchorRectangle();
732 std::swap(leftRect, rightRect);
735 if (handleId == 2 && point.y() > rightRect.center().y()) {
736 point.setY(rightRect.center().y());
737 }
else if (handleId == 3 && point.y() < leftRect.center().y()) {
738 point.setY(leftRect.center().y());
742 auto object = m_focusObject->parent();
745 if (QString::compare(object->metaObject()->className(),
746 "QDialog", Qt::CaseInsensitive) == 0) {
747 dialogMoveX += object->property(
"x").toInt();
749 object = object->parent();
753 QPointF(QHighDpi::fromNativePixels(point, QGuiApplication::focusWindow()));
754 const QPointF fixedPosition = QPointF(position.x() - dialogMoveX, position.y());
755 const QInputMethod *im = QGuiApplication::inputMethod();
756 const QTransform mapToLocal = im->inputItemTransform().inverted();
757 const int handlePos = im->queryFocusObject(Qt::ImCursorPosition, mapToLocal.map(fixedPosition)).toInt(&ok);
763 int newAnchor = anchor;
764 if (newAnchor > newCpos)
765 std::swap(newAnchor, newCpos);
769 newAnchor = handlePos;
770 }
else if (handleId == 2) {
771 newAnchor = handlePos;
772 }
else if (handleId == 3) {
777
778
779
780
781 if ((handleId == 2 || handleId == 3) && newCpos <= newAnchor) {
782 QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme,
783 query.value(Qt::ImCurrentSelection).toString());
785 const int oldSelectionStartPos = qMin(cpos, anchor);
789 finder.toPreviousBoundary();
790 newAnchor = finder.position() + oldSelectionStartPos;
793 finder.toNextBoundary();
794 newCpos = finder.position() + oldSelectionStartPos;
799 if (!focusObjectIsComposing() && newCpos == cpos && newAnchor == anchor)
803
804
805
806
807 if (focusObjectIsComposing() && handleId == 1) {
808 int absoluteCpos = query.value(Qt::ImAbsolutePosition).toInt(&ok);
811 const int blockPos = absoluteCpos - cpos;
813 if (blockPos + newCpos == m_composingCursor)
817 BatchEditLock batchEditLock(
this);
819 focusObjectStopComposing();
821 QList<QInputMethodEvent::Attribute> attributes;
822 attributes.append({ QInputMethodEvent::Selection, newAnchor, newCpos - newAnchor });
823 if (newCpos != newAnchor)
824 attributes.append({ QInputMethodEvent::Cursor, 0, 0 });
827 QGuiApplication::sendEvent(m_focusObject, &event);
832 if (m_focusObject && screenInputItemRectangle().contains(x, y)) {
834 m_handleMode = ShowCursor;
836 m_hideCursorHandleTimer.stop();
838 if (focusObjectIsComposing()) {
839 const int curBlockPos = getBlockPosition(
840 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition));
841 const int touchPosition = curBlockPos
842 + queryFocusObject(Qt::ImCursorPosition, QPointF(x, y)).toInt();
843 if (touchPosition != m_composingCursor)
844 focusObjectStopComposing();
848 QPlatformWindow *window =
qGuiApp->focusWindow()->handle();
849 const QRectF curRect = cursorRectangle();
850 const QPoint cursorGlobalPoint = window->mapToGlobal(QPoint(curRect.x(), curRect.y()));
851 const QRect windowRect = QPlatformInputContext::inputItemClipRectangle().toRect();
852 const QRect windowGlobalRect = QRect(window->mapToGlobal(windowRect.topLeft()), windowRect.size());
854 if (windowGlobalRect.contains(cursorGlobalPoint.x(), cursorGlobalPoint.y()))
861 static bool noHandles = qEnvironmentVariableIntValue(
"QT_QPA_NO_TEXT_HANDLES");
865 if (m_focusObject && screenInputItemRectangle().contains(x, y)) {
866 BatchEditLock batchEditLock(
this);
868 focusObjectStopComposing();
869 const QPointF touchPoint(x, y);
870 setSelectionOnFocusObject(touchPoint, touchPoint);
872 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor);
873 QCoreApplication::sendEvent(m_focusObject, &query);
874 int cursor = query.value(Qt::ImCursorPosition).toInt();
876 QString before = query.value(Qt::ImTextBeforeCursor).toString();
877 QString after = query.value(Qt::ImTextAfterCursor).toString();
878 for (
const auto &ch : after) {
879 if (!ch.isLetterOrNumber())
884 for (
auto itch = before.rbegin(); itch != after.rend(); ++itch) {
885 if (!itch->isLetterOrNumber())
889 if (cursor == anchor || cursor < 0 || cursor - anchor > 500) {
890 m_handleMode = ShowCursor | ShowEditPopup;
894 QList<QInputMethodEvent::Attribute> imAttributes;
895 imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
896 imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant()));
898 QGuiApplication::sendEvent(m_focusObject, &event);
900 m_handleMode = ShowSelection | ShowEditPopup;
909 m_handleMode = Hidden;
916 if (m_handleMode & ShowSelection) {
917 m_handleMode = Hidden;
920 m_hideCursorHandleTimer.start();
926 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries);
929#warning TODO extract the needed data from query
934#warning TODO Handle at least QInputMethod::ContextMenu action
936 Q_UNUSED(cursorPosition);
944 return QtAndroidInput::softwareKeyboardRect();
954 if (QGuiApplication::applicationState() != Qt::ApplicationActive) {
955 connect(
qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
this, SLOT(showInputPanelLater(Qt::ApplicationState)));
958 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
962 if (!
qGuiApp->focusWindow()->handle())
965 disconnect(m_updateCursorPosConnection);
966 m_updateCursorPosConnection = {};
968 if (
qGuiApp->focusObject()->metaObject()->indexOfSignal(
"cursorPositionChanged(int,int)") >= 0)
969 m_updateCursorPosConnection = connect(
qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(
int,
int)),
this, SLOT(updateCursorPosition()));
970 else if (
qGuiApp->focusObject()->metaObject()->indexOfSignal(
"cursorPositionChanged()") >= 0)
971 m_updateCursorPosConnection = connect(
qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()),
this, SLOT(updateCursorPosition()));
973 QRect rect = QPlatformInputContext::inputItemRectangle().toRect();
975 query->value(Qt::ImHints).toUInt()
,
976 query->value(Qt::ImEnterKeyType).toUInt()
);
981 if (state != Qt::ApplicationActive)
983 disconnect(
qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
this, SLOT(showInputPanelLater(Qt::ApplicationState)));
989 if (
qGuiApp->thread() == QThread::currentThread())
992 QMetaObject::invokeMethod(
this,
"safeCall", conType, Q_ARG(std::function<
void()>, func));
1007 return m_composingText.length();
1012 m_composingText.clear();
1013 m_composingTextStart = -1;
1014 m_composingCursor = -1;
1015 m_extractedText.clear();
1021 return m_focusObject;
1026 if (object != m_focusObject) {
1027 focusObjectStopComposing();
1028 m_focusObject = object;
1036 ++m_batchEditNestingLevel;
1042 if (--m_batchEditNestingLevel == 0) {
1043 focusObjectStartComposing();
1050
1051
1052
1055 BatchEditLock batchEditLock(
this);
1056 return setComposingText(text, newCursorPosition) && finishComposingText();
1061 BatchEditLock batchEditLock(
this);
1063 focusObjectStopComposing();
1065 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1069 if (leftLength < 0) {
1070 rightLength += -leftLength;
1074 const int initialBlockPos = getBlockPosition(query);
1075 const int initialCursorPos = getAbsoluteCursorPosition(query);
1076 const int initialAnchorPos = initialBlockPos + query->value(Qt::ImAnchorPosition).toInt();
1079
1080
1081
1082
1083
1084
1085
1086
1088 m_composingText.isEmpty()
1089 ? qMin(initialCursorPos, initialAnchorPos)
1090 : qMin(qMin(initialCursorPos, initialAnchorPos), m_composingTextStart);
1092 const int rightBegin =
1093 m_composingText.isEmpty()
1094 ? qMax(initialCursorPos, initialAnchorPos)
1095 : qMax(qMax(initialCursorPos, initialAnchorPos),
1096 m_composingTextStart + m_composingText.length());
1098 int textBeforeCursorLen;
1099 int textAfterCursorLen;
1101 QVariant textBeforeCursor = query->value(Qt::ImTextBeforeCursor);
1102 QVariant textAfterCursor = query->value(Qt::ImTextAfterCursor);
1103 if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
1104 textBeforeCursorLen = textBeforeCursor.toString().length();
1105 textAfterCursorLen = textAfterCursor.toString().length();
1107 textBeforeCursorLen = initialCursorPos - initialBlockPos;
1108 textAfterCursorLen =
1109 query->value(Qt::ImSurroundingText).toString().length() - textBeforeCursorLen;
1112 leftLength = qMin(qMax(0, textBeforeCursorLen - (initialCursorPos - leftEnd)), leftLength);
1113 rightLength = qMin(qMax(0, textAfterCursorLen - (rightBegin - initialCursorPos)), rightLength);
1115 if (leftLength == 0 && rightLength == 0)
1118 if (leftEnd == rightBegin) {
1121 event.setCommitString({}, -leftLength, leftLength + rightLength);
1122 QGuiApplication::sendEvent(m_focusObject, &event);
1124 if (initialCursorPos != initialAnchorPos) {
1126 { QInputMethodEvent::Selection, initialCursorPos - initialBlockPos, 0 }
1129 QGuiApplication::sendEvent(m_focusObject, &event);
1132 int currentCursorPos = initialCursorPos;
1134 if (rightLength > 0) {
1136 event.setCommitString({}, rightBegin - currentCursorPos, rightLength);
1137 QGuiApplication::sendEvent(m_focusObject, &event);
1139 currentCursorPos = rightBegin;
1142 if (leftLength > 0) {
1143 const int leftBegin = leftEnd - leftLength;
1146 event.setCommitString({}, leftBegin - currentCursorPos, leftLength);
1147 QGuiApplication::sendEvent(m_focusObject, &event);
1149 currentCursorPos = leftBegin;
1151 if (!m_composingText.isEmpty())
1152 m_composingTextStart -= leftLength;
1156 if (currentCursorPos != initialCursorPos - leftLength
1157 || initialCursorPos != initialAnchorPos) {
1159 const int currentBlockPos = getBlockPosition(
1160 focusObjectInputMethodQuery(Qt::ImAbsolutePosition | Qt::ImCursorPosition));
1163 { QInputMethodEvent::Selection, initialCursorPos - leftLength - currentBlockPos,
1164 initialAnchorPos - initialCursorPos },
1165 { QInputMethodEvent::Cursor, 0, 0 }
1168 QGuiApplication::sendEvent(m_focusObject, &event);
1178 BatchEditLock batchEditLock(
this);
1180 if (!focusObjectStopComposing())
1188
1189
1190
1191
1194 if (!finishComposingText())
1196 if (!setSelection(start, end))
1199 return commitText(text, newCursorPosition);
1204 m_fullScreenMode = enabled;
1205 BatchEditLock batchEditLock(
this);
1206 if (!focusObjectStopComposing())
1210 m_handleMode = Hidden;
1218 return m_fullScreenMode;
1223 return m_composingCursor != -1;
1228 if (focusObjectIsComposing() || m_composingText.isEmpty())
1232 if (m_composingText.contains(u'\n'))
1235 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1239 if (query->value(Qt::ImCursorPosition).toInt() != query->value(Qt::ImAnchorPosition).toInt())
1242 const int absoluteCursorPos = getAbsoluteCursorPosition(query);
1243 if (absoluteCursorPos < m_composingTextStart
1244 || absoluteCursorPos > m_composingTextStart + m_composingText.length())
1247 m_composingCursor = absoluteCursorPos;
1249 QTextCharFormat underlined;
1250 underlined.setFontUnderline(
true);
1253 { QInputMethodEvent::Cursor, absoluteCursorPos - m_composingTextStart, 1 },
1254 { QInputMethodEvent::TextFormat, 0,
int(m_composingText.length()), underlined }
1257 event.setCommitString({}, m_composingTextStart - absoluteCursorPos, m_composingText.length());
1259 QGuiApplication::sendEvent(m_focusObject, &event);
1264 if (!focusObjectIsComposing())
1267 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1271 const int blockPos = getBlockPosition(query);
1272 const int localCursorPos = m_composingCursor - blockPos;
1274 m_composingCursor = -1;
1278 QList<QInputMethodEvent::Attribute> attributes;
1280 event.setCommitString(m_composingText);
1281 sendInputMethodEvent(&event);
1285 QList<QInputMethodEvent::Attribute> attributes;
1287 QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
1289 sendInputMethodEvent(&event);
1298 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1302 const uint qtInputMethodHints = query->value(Qt::ImHints).toUInt();
1303 const int localPos = query->value(Qt::ImCursorPosition).toInt();
1305 bool atWordBoundary =
1307 && (!focusObjectIsComposing() || m_composingCursor == m_composingTextStart);
1309 if (!atWordBoundary) {
1310 QString surroundingText = query->value(Qt::ImSurroundingText).toString();
1311 surroundingText.truncate(localPos);
1312 if (focusObjectIsComposing())
1313 surroundingText += QStringView{m_composingText}.left(m_composingCursor - m_composingTextStart);
1315 QTextBoundaryFinder finder(QTextBoundaryFinder::Sentence, surroundingText + u'A');
1316 finder.setPosition(surroundingText.length());
1317 if (finder.isAtBoundary())
1318 atWordBoundary = finder.isAtBoundary();
1320 if (atWordBoundary && !(qtInputMethodHints & Qt::ImhLowercaseOnly) && !(qtInputMethodHints & Qt::ImhNoAutoUppercase))
1321 res |= CAP_MODE_SENTENCES;
1323 if (qtInputMethodHints & Qt::ImhUppercaseOnly)
1324 res |= CAP_MODE_CHARACTERS;
1337 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(
1338 Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition);
1340 return m_extractedText;
1342 const int cursorPos = getAbsoluteCursorPosition(query);
1343 const int blockPos = getBlockPosition(query);
1350 QVariant textBeforeCursor = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, INT_MAX);
1351 QVariant textAfterCursor = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, INT_MAX);
1352 if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
1353 if (focusObjectIsComposing()) {
1354 m_extractedText.text =
1355 textBeforeCursor.toString() + m_composingText + textAfterCursor.toString();
1357 m_extractedText.text = textBeforeCursor.toString() + textAfterCursor.toString();
1360 m_extractedText.startOffset = qMax(0, cursorPos - textBeforeCursor.toString().length());
1362 m_extractedText.text = focusObjectInputMethodQuery(Qt::ImSurroundingText)
1363 ->value(Qt::ImSurroundingText).toString();
1365 if (focusObjectIsComposing())
1366 m_extractedText.text.insert(cursorPos - blockPos, m_composingText);
1368 m_extractedText.startOffset = blockPos;
1371 if (focusObjectIsComposing()) {
1372 m_extractedText.selectionStart = m_composingCursor - m_extractedText.startOffset;
1373 m_extractedText.selectionEnd = m_extractedText.selectionStart;
1375 m_extractedText.selectionStart = cursorPos - m_extractedText.startOffset;
1376 m_extractedText.selectionEnd =
1377 blockPos + query->value(Qt::ImAnchorPosition).toInt() - m_extractedText.startOffset;
1380 if (m_extractedText.selectionStart > m_extractedText.selectionEnd)
1381 std::swap(m_extractedText.selectionStart, m_extractedText.selectionEnd);
1384 return m_extractedText;
1389 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1393 return query->value(Qt::ImCurrentSelection).toString();
1403 QVariant reportedTextAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, length);
1404 if (reportedTextAfter.isValid()) {
1405 text = reportedTextAfter.toString();
1408 QSharedPointer<QInputMethodQueryEvent> query =
1409 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
1411 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1412 text = query->value(Qt::ImSurroundingText).toString().mid(cursorPos);
1416 if (focusObjectIsComposing()) {
1418 const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
1419 text = QStringView{m_composingText}.mid(cursorPosInsidePreedit) + text;
1422 QSharedPointer<QInputMethodQueryEvent> query =
1423 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
1425 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1426 const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
1427 if (anchorPos > cursorPos)
1428 text.remove(0, anchorPos - cursorPos);
1432 text.truncate(length);
1443 QVariant reportedTextBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, length);
1444 if (reportedTextBefore.isValid()) {
1445 text = reportedTextBefore.toString();
1448 QSharedPointer<QInputMethodQueryEvent> query =
1449 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
1451 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1452 text = query->value(Qt::ImSurroundingText).toString().left(cursorPos);
1456 if (focusObjectIsComposing()) {
1458 const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
1459 text += QStringView{m_composingText}.left(cursorPosInsidePreedit);
1462 QSharedPointer<QInputMethodQueryEvent> query =
1463 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
1465 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1466 const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
1467 if (anchorPos < cursorPos)
1468 text.chop(cursorPos - anchorPos);
1472 if (text.length() > length)
1473 text = text.right(length);
1478
1479
1480
1481
1482
1483
1484
1485
1489 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1493 BatchEditLock batchEditLock(
this);
1495 const int absoluteCursorPos = getAbsoluteCursorPosition(query);
1496 int absoluteAnchorPos = getBlockPosition(query) + query->value(Qt::ImAnchorPosition).toInt();
1498 auto setCursorPosition = [=]() {
1499 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1500 QInputMethodEvent event({}, { { QInputMethodEvent::Selection, cursorPos, 0 } });
1501 QGuiApplication::sendEvent(m_focusObject, &event);
1506 if (!m_composingText.isEmpty() && absoluteCursorPos != absoluteAnchorPos) {
1507 setCursorPosition();
1508 absoluteAnchorPos = absoluteCursorPos;
1513 if (absoluteCursorPos == 0 && text.length() == 1 && getTextAfterCursor(1,1).length() >= 0) {
1514 setCursorPosition();
1520 const int effectiveAbsoluteCursorPos = qMin(absoluteCursorPos, absoluteAnchorPos);
1521 if (m_composingTextStart == -1)
1522 m_composingTextStart = effectiveAbsoluteCursorPos;
1524 const int oldComposingTextLen = m_composingText.length();
1525 m_composingText = text;
1527 const int newAbsoluteCursorPos =
1528 newCursorPosition <= 0
1529 ? m_composingTextStart + newCursorPosition
1530 : m_composingTextStart + m_composingText.length() + newCursorPosition - 1;
1532 const bool focusObjectWasComposing = focusObjectIsComposing();
1535 if (!m_composingText.isEmpty() && !m_composingText.contains(u'\n')
1536 && newAbsoluteCursorPos >= m_composingTextStart
1537 && newAbsoluteCursorPos <= m_composingTextStart + m_composingText.length())
1538 m_composingCursor = newAbsoluteCursorPos;
1540 m_composingCursor = -1;
1542 if (focusObjectIsComposing()) {
1543 QTextCharFormat underlined;
1544 underlined.setFontUnderline(
true);
1547 { QInputMethodEvent::TextFormat, 0,
int(m_composingText.length()), underlined },
1548 { QInputMethodEvent::Cursor, m_composingCursor - m_composingTextStart, 1 }
1551 if (oldComposingTextLen > 0 && !focusObjectWasComposing) {
1552 event.setCommitString({}, m_composingTextStart - effectiveAbsoluteCursorPos,
1553 oldComposingTextLen);
1555 if (m_composingText.isEmpty())
1558 QGuiApplication::sendEvent(m_focusObject, &event);
1562 if (focusObjectWasComposing) {
1563 event.setCommitString(m_composingText);
1565 event.setCommitString(m_composingText,
1566 m_composingTextStart - effectiveAbsoluteCursorPos,
1567 oldComposingTextLen);
1569 if (m_composingText.isEmpty())
1572 QGuiApplication::sendEvent(m_focusObject, &event);
1575 if (!focusObjectIsComposing() && newCursorPosition != 1) {
1579 const int newBlockPos = getBlockPosition(
1580 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition));
1583 { QInputMethodEvent::Selection, newAbsoluteCursorPos - newBlockPos, 0 }
1586 QGuiApplication::sendEvent(m_focusObject, &event);
1600 BatchEditLock batchEditLock(
this);
1607 finishComposingText();
1609 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1618 QString text = query->value(Qt::ImSurroundingText).toString();
1619 int textOffset = getBlockPosition(query);
1621 if (start < textOffset || end > textOffset + text.length()) {
1622 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1624 if (end - textOffset > text.length()) {
1625 const QString after = query->value(Qt::ImTextAfterCursor).toString();
1626 const int additionalSuffixLen = after.length() - (text.length() - cursorPos);
1628 if (additionalSuffixLen > 0)
1629 text += QStringView{after}.right(additionalSuffixLen);
1632 if (start < textOffset) {
1633 QString before = query->value(Qt::ImTextBeforeCursor).toString();
1634 before.chop(cursorPos);
1636 if (!before.isEmpty()) {
1637 text = before + text;
1638 textOffset -= before.length();
1642 if (start < textOffset || end - textOffset > text.length()) {
1643 qCDebug(lcQpaInputMethods) <<
"Warning: setComposingRegion: failed to retrieve text from composing region";
1649 m_composingText = text.mid(start - textOffset, end - start);
1650 m_composingTextStart = start;
1657 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1661 BatchEditLock batchEditLock(
this);
1663 int blockPosition = getBlockPosition(query);
1664 int localCursorPos = start - blockPosition;
1666 if (focusObjectIsComposing() && start == end && start >= m_composingTextStart
1667 && start <= m_composingTextStart + m_composingText.length()) {
1670 int localOldPos = query->value(Qt::ImCursorPosition).toInt();
1671 int pos = localCursorPos - localOldPos;
1672 QList<QInputMethodEvent::Attribute> attributes;
1673 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, pos, 1));
1678 QTextCharFormat underlined;
1679 underlined.setFontUnderline(
true);
1680 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, m_composingText.length(),
1681 QVariant(underlined)));
1682 m_composingCursor = start;
1685 QGuiApplication::sendEvent(m_focusObject, &event);
1688 focusObjectStopComposing();
1689 QList<QInputMethodEvent::Attribute> attributes;
1690 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
1694 QGuiApplication::sendEvent(m_focusObject, &event);
1701 BatchEditLock batchEditLock(
this);
1703 focusObjectStopComposing();
1704 m_handleMode = ShowCursor;
1705 sendShortcut(QKeySequence::SelectAll);
1711 BatchEditLock batchEditLock(
this);
1715 finishComposingText();
1717 m_handleMode = ShowCursor;
1718 sendShortcut(QKeySequence::Cut);
1724 BatchEditLock batchEditLock(
this);
1726 focusObjectStopComposing();
1727 m_handleMode = ShowCursor;
1728 sendShortcut(QKeySequence::Copy);
1740 BatchEditLock batchEditLock(
this);
1743 finishComposingText();
1745 m_handleMode = ShowCursor;
1746 sendShortcut(QKeySequence::Paste);
1752 for (
int i = 0; i < sequence.count(); ++i) {
1753 const QKeyCombination keys = sequence[i];
1754 Qt::Key key = Qt::Key(keys.toCombined() & ~Qt::KeyboardModifierMask);
1755 Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys.toCombined() & Qt::KeyboardModifierMask);
1757 QKeyEvent pressEvent(QEvent::KeyPress, key, mod);
1758 QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod);
1760 QGuiApplication::sendEvent(m_focusObject, &pressEvent);
1761 QGuiApplication::sendEvent(m_focusObject, &releaseEvent);
1765QSharedPointer<QInputMethodQueryEvent>
QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) {
1769 QObject *focusObject =
qGuiApp->focusObject();
1773 QInputMethodQueryEvent *ret =
new QInputMethodQueryEvent(queries);
1774 QCoreApplication::sendEvent(focusObject, ret);
1775 return QSharedPointer<QInputMethodQueryEvent>(ret);
1783 QObject *focusObject =
qGuiApp->focusObject();
1787 QCoreApplication::sendEvent(focusObject, event);
jboolean beginBatchEdit()
jboolean setSelection(jint start, jint end)
void touchDown(int x, int y)
void longPress(int x, int y)
jboolean finishComposingText()
jint getCursorCapsMode(jint reqModes)
QString getSelectedText(jint flags)
bool isAnimating() const override
This function can be reimplemented to return true whenever input method is animating shown or hidden.
void hideSelectionHandles()
QString getTextAfterCursor(jint length, jint flags)
void reportFullscreenMode(jboolean enabled)
QRectF keyboardRect() const override
This function can be reimplemented to return virtual keyboard rectangle in currently active window co...
void reset() override
Method to be called when input method needs to be reset.
jboolean setComposingText(const QString &text, jint newCursorPosition)
void hideInputPanel() override
Request to hide input panel.
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
jboolean commitText(const QString &text, jint newCursorPosition)
jboolean setComposingRegion(jint start, jint end)
static QAndroidInputContext * androidInputContext()
void updateSelectionHandles()
void update(Qt::InputMethodQueries queries) override
Notification on editor updates.
QString getTextBeforeCursor(jint length, jint flags)
void sendShortcut(const QKeySequence &)
void invokeAction(QInputMethod::Action action, int cursorPosition) override
Called when the word currently being composed in the input item is tapped by the user.
void updateCursorPosition()
jboolean deleteSurroundingText(jint leftLength, jint rightLength)
jboolean replaceText(jint start, jint end, const QString text, jint newCursorPosition)
void handleLocationChanged(int handleId, int x, int y)
void showInputPanel() override
Request to show input panel.
const ExtractedText & getExtractedText(jint hintMaxChars, jint hintMaxLines, jint flags)
bool isInputPanelVisible() const override
Returns input panel visibility status.
jboolean fullscreenMode()
\inmodule QtCore\reentrant
Combined button and popup list for selecting options.
QAndroidPlatformIntegration * androidPlatformIntegration()
static jfieldID m_startOffsetFieldID
static int getBlockPosition(const QSharedPointer< QInputMethodQueryEvent > &query)
static jboolean cut(JNIEnv *, jobject)
static jint getCursorCapsMode(JNIEnv *, jobject, jint reqModes)
static jfieldID m_partialEndOffsetFieldID
static jfieldID m_textFieldID
static char const *const QtExtractedTextClassName
static bool hasValidFocusObject()
static jboolean fullscreenMode(JNIEnv *, jobject)
static QRect screenInputItemRectangle()
static jboolean finishComposingText(JNIEnv *, jobject)
static jobject getExtractedText(JNIEnv *env, jobject, int hintMaxChars, int hintMaxLines, jint flags)
static jfieldID m_selectionStartFieldID
static JNINativeMethod methods[]
static QAndroidInputContext * m_androidInputContext
static jboolean copy(JNIEnv *, jobject)
static jstring getTextBeforeCursor(JNIEnv *env, jobject, jint length, jint flags)
static jfieldID m_selectionEndFieldID
static jboolean copyURL(JNIEnv *, jobject)
static char const *const QtNativeInputConnectionClassName
static int m_selectHandleWidth
static void runOnQtThread(const std::function< void()> &func)
static jboolean commitText(JNIEnv *env, jobject, jstring text, jint newCursorPosition)
static jboolean paste(JNIEnv *, jobject)
static jboolean replaceText(JNIEnv *env, jobject, jint start, jint end, jstring text, jint newCursorPosition)
static jboolean beginBatchEdit(JNIEnv *, jobject)
static jmethodID m_classConstructorMethodID
static jstring getSelectedText(JNIEnv *env, jobject, jint flags)
static jboolean setComposingRegion(JNIEnv *, jobject, jint start, jint end)
static jboolean deleteSurroundingText(JNIEnv *, jobject, jint leftLength, jint rightLength)
static jboolean setComposingText(JNIEnv *env, jobject, jstring text, jint newCursorPosition)
static jboolean updateCursorPosition(JNIEnv *, jobject)
static jstring getTextAfterCursor(JNIEnv *env, jobject, jint length, jint flags)
static int getAbsoluteCursorPosition(const QSharedPointer< QInputMethodQueryEvent > &query)
static void reportFullscreenMode(JNIEnv *, jobject, jboolean enabled)
static jclass m_extractedTextClass
static jboolean endBatchEdit(JNIEnv *, jobject)
static jfieldID m_partialStartOffsetFieldID
static jboolean setSelection(JNIEnv *, jobject, jint start, jint end)
static jboolean selectAll(JNIEnv *, jobject)