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()) {
543 if (!m_accessibilityFocusInProgress)
553 focusObjectStopComposing();
558 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
559 if (!query.isNull() && m_batchEditNestingLevel == 0) {
560 const int cursorPos = getAbsoluteCursorPosition(query);
561 const int composeLength = m_composingText.length();
564 if (m_composingText.isEmpty() != (m_composingTextStart == -1))
565 qWarning() <<
"Input method out of sync" << m_composingText << m_composingTextStart;
567 int realSelectionStart = cursorPos;
568 int realSelectionEnd = cursorPos;
570 int cpos = query->value(Qt::ImCursorPosition).toInt();
571 int anchor = query->value(Qt::ImAnchorPosition).toInt();
572 if (cpos != anchor) {
573 if (!m_composingText.isEmpty()) {
574 qWarning(
"Selecting text while preediting may give unpredictable results.");
575 focusObjectStopComposing();
577 int blockPos = getBlockPosition(query);
578 realSelectionStart = blockPos + cpos;
579 realSelectionEnd = blockPos + anchor;
582 if (focusObjectIsComposing())
583 realSelectionStart = realSelectionEnd = m_composingCursor;
586 if (realSelectionStart > realSelectionEnd)
587 std::swap(realSelectionStart, realSelectionEnd);
590 m_composingTextStart
, m_composingTextStart + composeLength
);
596 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
599 return query->value(Qt::ImHints).toUInt() & Qt::ImhNoEditMenu;
604 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
607 return query->value(Qt::ImHints).toUInt() & Qt::ImhNoTextHandles;
612 if (m_fullScreenMode) {
616 static bool noHandles = qEnvironmentVariableIntValue(
"QT_QPA_NO_TEXT_HANDLES");
617 if (noHandles || !m_focusObject)
620 if (isImhNoTextHandlesSet()) {
625 auto im =
qGuiApp->inputMethod();
627 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled
628 | Qt::ImCurrentSelection | Qt::ImHints | Qt::ImSurroundingText
630 QCoreApplication::sendEvent(m_focusObject, &query);
632 int cpos = query.value(Qt::ImCursorPosition).toInt();
633 int anchor = query.value(Qt::ImAnchorPosition).toInt();
634 const QVariant readOnlyVariant = query.value(Qt::ImReadOnly);
635 bool readOnly = readOnlyVariant.toBool();
636 QPlatformWindow *qPlatformWindow =
qGuiApp->focusWindow()->handle();
638 if (!readOnly && ((m_handleMode & 0xff) == Hidden)) {
643 if ( cpos == anchor && (!readOnlyVariant.isValid() || readOnly)) {
648 if (cpos == anchor || im->anchorRectangle().isNull()) {
649 auto curRect = cursorRectangle();
650 QPoint cursorPointGlobal = QPoint(curRect.x() + (curRect.width() / 2), curRect.y() + curRect.height());
651 QPoint cursorPoint(curRect.center().x(), curRect.bottom());
656 if (cursorPointGlobal != cursorPoint) {
657 x = cursorPointGlobal.x();
658 y = cursorPointGlobal.y();
661 QPoint editMenuPoint(x, y);
662 m_handleMode &= ShowEditPopup;
663 m_handleMode |= ShowCursor;
664 uint32_t buttons = 0;
665 const bool withEditMenu = !isImhNoEditMenuSet();
668 if (!query.value(Qt::ImSurroundingText).toString().isEmpty())
671 QtAndroidInput::updateHandles(m_handleMode, editMenuPoint, buttons, cursorPointGlobal);
672 m_hideCursorHandleTimer.start();
677 m_handleMode = ShowSelection | ShowEditPopup ;
678 auto leftRect = cursorRectangle();
679 auto rightRect = anchorRectangle();
681 std::swap(leftRect, rightRect);
685 QPoint leftPoint(qPlatformWindow->mapToGlobal(leftRect.bottomLeft().toPoint()));
686 QPoint rightPoint(qPlatformWindow->mapToGlobal(rightRect.bottomRight().toPoint()));
689 if (platformIntegration) {
693 int rightSideOfScreen = platformIntegration
->screen()->availableGeometry().right();
696 leftPoint = qPlatformWindow->mapFromGlobal(leftPoint);
700 rightPoint = qPlatformWindow->mapFromGlobal(rightPoint);
702 QPoint editPoint(leftRect.united(rightRect).topLeft().toPoint());
703 uint32_t buttons = 0;
704 const bool withEditMenu = !isImhNoEditMenuSet();
710 QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, rightPoint,
711 query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
712 m_hideCursorHandleTimer.stop();
717
718
719
720
723 if (m_batchEditNestingLevel != 0) {
724 qWarning() <<
"QAndroidInputContext::handleLocationChanged returned";
730 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition
731 | Qt::ImAbsolutePosition | Qt::ImCurrentSelection);
732 QCoreApplication::sendEvent(m_focusObject, &query);
733 int cpos = query.value(Qt::ImCursorPosition).toInt();
734 int anchor = query.value(Qt::ImAnchorPosition).toInt();
735 auto leftRect = cursorRectangle();
736 auto rightRect = anchorRectangle();
738 std::swap(leftRect, rightRect);
741 if (handleId == 2 && point.y() > rightRect.center().y()) {
742 point.setY(rightRect.center().y());
743 }
else if (handleId == 3 && point.y() < leftRect.center().y()) {
744 point.setY(leftRect.center().y());
748 auto object = m_focusObject->parent();
751 if (QString::compare(object->metaObject()->className(),
752 "QDialog", Qt::CaseInsensitive) == 0) {
753 dialogMoveX += object->property(
"x").toInt();
755 object = object->parent();
759 QPointF(QHighDpi::fromNativePixels(point, QGuiApplication::focusWindow()));
760 const QPointF fixedPosition = QPointF(position.x() - dialogMoveX, position.y());
761 const QInputMethod *im = QGuiApplication::inputMethod();
762 const QTransform mapToLocal = im->inputItemTransform().inverted();
763 const int handlePos = im->queryFocusObject(Qt::ImCursorPosition, mapToLocal.map(fixedPosition)).toInt(&ok);
769 int newAnchor = anchor;
770 if (newAnchor > newCpos)
771 std::swap(newAnchor, newCpos);
775 newAnchor = handlePos;
776 }
else if (handleId == 2) {
777 newAnchor = handlePos;
778 }
else if (handleId == 3) {
783
784
785
786
787 if ((handleId == 2 || handleId == 3) && newCpos <= newAnchor) {
788 QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme,
789 query.value(Qt::ImCurrentSelection).toString());
791 const int oldSelectionStartPos = qMin(cpos, anchor);
795 finder.toPreviousBoundary();
796 newAnchor = finder.position() + oldSelectionStartPos;
799 finder.toNextBoundary();
800 newCpos = finder.position() + oldSelectionStartPos;
805 if (!focusObjectIsComposing() && newCpos == cpos && newAnchor == anchor)
809
810
811
812
813 if (focusObjectIsComposing() && handleId == 1) {
814 int absoluteCpos = query.value(Qt::ImAbsolutePosition).toInt(&ok);
817 const int blockPos = absoluteCpos - cpos;
819 if (blockPos + newCpos == m_composingCursor)
823 BatchEditLock batchEditLock(
this);
825 focusObjectStopComposing();
827 QList<QInputMethodEvent::Attribute> attributes;
828 attributes.append({ QInputMethodEvent::Selection, newAnchor, newCpos - newAnchor });
829 if (newCpos != newAnchor)
830 attributes.append({ QInputMethodEvent::Cursor, 0, 0 });
833 QGuiApplication::sendEvent(m_focusObject, &event);
838 if (m_focusObject && screenInputItemRectangle().contains(x, y)) {
840 m_handleMode = ShowCursor;
842 m_hideCursorHandleTimer.stop();
844 if (focusObjectIsComposing()) {
845 const int curBlockPos = getBlockPosition(
846 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition));
847 const int touchPosition = curBlockPos
848 + queryFocusObject(Qt::ImCursorPosition, QPointF(x, y)).toInt();
849 if (touchPosition != m_composingCursor)
850 focusObjectStopComposing();
854 QPlatformWindow *window =
qGuiApp->focusWindow()->handle();
855 const QRectF curRect = cursorRectangle();
856 const QPoint cursorGlobalPoint = window->mapToGlobal(QPoint(curRect.x(), curRect.y()));
857 const QRect windowRect = QPlatformInputContext::inputItemClipRectangle().toRect();
858 const QRect windowGlobalRect = QRect(window->mapToGlobal(windowRect.topLeft()), windowRect.size());
860 if (windowGlobalRect.contains(cursorGlobalPoint.x(), cursorGlobalPoint.y()))
867 static bool noHandles = qEnvironmentVariableIntValue(
"QT_QPA_NO_TEXT_HANDLES");
871 if (m_focusObject && screenInputItemRectangle().contains(x, y)) {
872 BatchEditLock batchEditLock(
this);
874 focusObjectStopComposing();
875 const QPointF touchPoint(x, y);
876 setSelectionOnFocusObject(touchPoint, touchPoint);
878 QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor);
879 QCoreApplication::sendEvent(m_focusObject, &query);
880 int cursor = query.value(Qt::ImCursorPosition).toInt();
882 QString before = query.value(Qt::ImTextBeforeCursor).toString();
883 QString after = query.value(Qt::ImTextAfterCursor).toString();
884 for (
const auto &ch : after) {
885 if (!ch.isLetterOrNumber())
890 for (
auto itch = before.rbegin(); itch != after.rend(); ++itch) {
891 if (!itch->isLetterOrNumber())
895 if (cursor == anchor || cursor < 0 || cursor - anchor > 500) {
896 m_handleMode = ShowCursor | ShowEditPopup;
900 QList<QInputMethodEvent::Attribute> imAttributes;
901 imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
902 imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant()));
904 QGuiApplication::sendEvent(m_focusObject, &event);
906 m_handleMode = ShowSelection | ShowEditPopup;
915 m_handleMode = Hidden;
922 if (m_handleMode & ShowSelection) {
923 m_handleMode = Hidden;
926 m_hideCursorHandleTimer.start();
932 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries);
935#warning TODO extract the needed data from query
940#warning TODO Handle at least QInputMethod::ContextMenu action
942 Q_UNUSED(cursorPosition);
950 return QtAndroidInput::softwareKeyboardRect();
960 if (QGuiApplication::applicationState() != Qt::ApplicationActive) {
961 connect(
qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
this, SLOT(showInputPanelLater(Qt::ApplicationState)));
968 if (m_accessibilityFocusInProgress)
971 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
975 if (!
qGuiApp->focusWindow()->handle())
978 disconnect(m_updateCursorPosConnection);
979 m_updateCursorPosConnection = {};
981 if (
qGuiApp->focusObject()->metaObject()->indexOfSignal(
"cursorPositionChanged(int,int)") >= 0)
982 m_updateCursorPosConnection = connect(
qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(
int,
int)),
this, SLOT(updateCursorPosition()));
983 else if (
qGuiApp->focusObject()->metaObject()->indexOfSignal(
"cursorPositionChanged()") >= 0)
984 m_updateCursorPosConnection = connect(
qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()),
this, SLOT(updateCursorPosition()));
986 QRect rect = QPlatformInputContext::inputItemRectangle().toRect();
988 query->value(Qt::ImHints).toUInt()
,
989 query->value(Qt::ImEnterKeyType).toUInt()
);
994 if (state != Qt::ApplicationActive)
996 disconnect(
qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
this, SLOT(showInputPanelLater(Qt::ApplicationState)));
1002 if (
qGuiApp->thread() == QThread::currentThread())
1005 QMetaObject::invokeMethod(
this,
"safeCall", conType, Q_ARG(std::function<
void()>, func));
1020 return m_composingText.length();
1025 m_composingText.clear();
1026 m_composingTextStart = -1;
1027 m_composingCursor = -1;
1028 m_extractedText.clear();
1034 return m_focusObject;
1039 if (object != m_focusObject) {
1040 focusObjectStopComposing();
1041 m_focusObject = object;
1049 ++m_batchEditNestingLevel;
1055 if (--m_batchEditNestingLevel == 0) {
1056 focusObjectStartComposing();
1063
1064
1065
1068 BatchEditLock batchEditLock(
this);
1069 return setComposingText(text, newCursorPosition) && finishComposingText();
1074 BatchEditLock batchEditLock(
this);
1076 focusObjectStopComposing();
1078 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1082 if (leftLength < 0) {
1083 rightLength += -leftLength;
1087 const int initialBlockPos = getBlockPosition(query);
1088 const int initialCursorPos = getAbsoluteCursorPosition(query);
1089 const int initialAnchorPos = initialBlockPos + query->value(Qt::ImAnchorPosition).toInt();
1092
1093
1094
1095
1096
1097
1098
1099
1101 m_composingText.isEmpty()
1102 ? qMin(initialCursorPos, initialAnchorPos)
1103 : qMin(qMin(initialCursorPos, initialAnchorPos), m_composingTextStart);
1105 const int rightBegin =
1106 m_composingText.isEmpty()
1107 ? qMax(initialCursorPos, initialAnchorPos)
1108 : qMax(qMax(initialCursorPos, initialAnchorPos),
1109 m_composingTextStart + m_composingText.length());
1111 int textBeforeCursorLen;
1112 int textAfterCursorLen;
1114 QVariant textBeforeCursor = query->value(Qt::ImTextBeforeCursor);
1115 QVariant textAfterCursor = query->value(Qt::ImTextAfterCursor);
1116 if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
1117 textBeforeCursorLen = textBeforeCursor.toString().length();
1118 textAfterCursorLen = textAfterCursor.toString().length();
1120 textBeforeCursorLen = initialCursorPos - initialBlockPos;
1121 textAfterCursorLen =
1122 query->value(Qt::ImSurroundingText).toString().length() - textBeforeCursorLen;
1125 leftLength = qMin(qMax(0, textBeforeCursorLen - (initialCursorPos - leftEnd)), leftLength);
1126 rightLength = qMin(qMax(0, textAfterCursorLen - (rightBegin - initialCursorPos)), rightLength);
1128 if (leftLength == 0 && rightLength == 0)
1131 if (leftEnd == rightBegin) {
1134 event.setCommitString({}, -leftLength, leftLength + rightLength);
1135 QGuiApplication::sendEvent(m_focusObject, &event);
1137 if (initialCursorPos != initialAnchorPos) {
1139 { QInputMethodEvent::Selection, initialCursorPos - initialBlockPos, 0 }
1142 QGuiApplication::sendEvent(m_focusObject, &event);
1145 int currentCursorPos = initialCursorPos;
1147 if (rightLength > 0) {
1149 event.setCommitString({}, rightBegin - currentCursorPos, rightLength);
1150 QGuiApplication::sendEvent(m_focusObject, &event);
1152 currentCursorPos = rightBegin;
1155 if (leftLength > 0) {
1156 const int leftBegin = leftEnd - leftLength;
1159 event.setCommitString({}, leftBegin - currentCursorPos, leftLength);
1160 QGuiApplication::sendEvent(m_focusObject, &event);
1162 currentCursorPos = leftBegin;
1164 if (!m_composingText.isEmpty())
1165 m_composingTextStart -= leftLength;
1169 if (currentCursorPos != initialCursorPos - leftLength
1170 || initialCursorPos != initialAnchorPos) {
1172 const int currentBlockPos = getBlockPosition(
1173 focusObjectInputMethodQuery(Qt::ImAbsolutePosition | Qt::ImCursorPosition));
1176 { QInputMethodEvent::Selection, initialCursorPos - leftLength - currentBlockPos,
1177 initialAnchorPos - initialCursorPos },
1178 { QInputMethodEvent::Cursor, 0, 0 }
1181 QGuiApplication::sendEvent(m_focusObject, &event);
1191 BatchEditLock batchEditLock(
this);
1193 if (!focusObjectStopComposing())
1201
1202
1203
1204
1207 if (!finishComposingText())
1209 if (!setSelection(start, end))
1212 return commitText(text, newCursorPosition);
1217 m_fullScreenMode = enabled;
1218 BatchEditLock batchEditLock(
this);
1219 if (!focusObjectStopComposing())
1223 m_handleMode = Hidden;
1231 return m_fullScreenMode;
1236 return m_composingCursor != -1;
1241 if (focusObjectIsComposing() || m_composingText.isEmpty())
1245 if (m_composingText.contains(u'\n'))
1248 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1252 if (query->value(Qt::ImCursorPosition).toInt() != query->value(Qt::ImAnchorPosition).toInt())
1255 const int absoluteCursorPos = getAbsoluteCursorPosition(query);
1256 if (absoluteCursorPos < m_composingTextStart
1257 || absoluteCursorPos > m_composingTextStart + m_composingText.length())
1260 m_composingCursor = absoluteCursorPos;
1262 QTextCharFormat underlined;
1263 underlined.setFontUnderline(
true);
1266 { QInputMethodEvent::Cursor, absoluteCursorPos - m_composingTextStart, 1 },
1267 { QInputMethodEvent::TextFormat, 0,
int(m_composingText.length()), underlined }
1270 event.setCommitString({}, m_composingTextStart - absoluteCursorPos, m_composingText.length());
1272 QGuiApplication::sendEvent(m_focusObject, &event);
1277 if (!focusObjectIsComposing())
1280 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1284 const int blockPos = getBlockPosition(query);
1285 const int localCursorPos = m_composingCursor - blockPos;
1287 m_composingCursor = -1;
1291 QList<QInputMethodEvent::Attribute> attributes;
1293 event.setCommitString(m_composingText);
1294 sendInputMethodEvent(&event);
1298 QList<QInputMethodEvent::Attribute> attributes;
1300 QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
1302 sendInputMethodEvent(&event);
1311 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1315 const uint qtInputMethodHints = query->value(Qt::ImHints).toUInt();
1316 const int localPos = query->value(Qt::ImCursorPosition).toInt();
1318 bool atWordBoundary =
1320 && (!focusObjectIsComposing() || m_composingCursor == m_composingTextStart);
1322 if (!atWordBoundary) {
1323 QString surroundingText = query->value(Qt::ImSurroundingText).toString();
1324 surroundingText.truncate(localPos);
1325 if (focusObjectIsComposing())
1326 surroundingText += QStringView{m_composingText}.left(m_composingCursor - m_composingTextStart);
1328 QTextBoundaryFinder finder(QTextBoundaryFinder::Sentence, surroundingText + u'A');
1329 finder.setPosition(surroundingText.length());
1330 if (finder.isAtBoundary())
1331 atWordBoundary = finder.isAtBoundary();
1333 if (atWordBoundary && !(qtInputMethodHints & Qt::ImhLowercaseOnly) && !(qtInputMethodHints & Qt::ImhNoAutoUppercase))
1334 res |= CAP_MODE_SENTENCES;
1336 if (qtInputMethodHints & Qt::ImhUppercaseOnly)
1337 res |= CAP_MODE_CHARACTERS;
1350 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(
1351 Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition);
1353 return m_extractedText;
1355 const int cursorPos = getAbsoluteCursorPosition(query);
1356 const int blockPos = getBlockPosition(query);
1363 QVariant textBeforeCursor = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, INT_MAX);
1364 QVariant textAfterCursor = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, INT_MAX);
1365 if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
1366 if (focusObjectIsComposing()) {
1367 m_extractedText.text =
1368 textBeforeCursor.toString() + m_composingText + textAfterCursor.toString();
1370 m_extractedText.text = textBeforeCursor.toString() + textAfterCursor.toString();
1373 m_extractedText.startOffset = qMax(0, cursorPos - textBeforeCursor.toString().length());
1375 m_extractedText.text = focusObjectInputMethodQuery(Qt::ImSurroundingText)
1376 ->value(Qt::ImSurroundingText).toString();
1378 if (focusObjectIsComposing())
1379 m_extractedText.text.insert(cursorPos - blockPos, m_composingText);
1381 m_extractedText.startOffset = blockPos;
1384 if (focusObjectIsComposing()) {
1385 m_extractedText.selectionStart = m_composingCursor - m_extractedText.startOffset;
1386 m_extractedText.selectionEnd = m_extractedText.selectionStart;
1388 m_extractedText.selectionStart = cursorPos - m_extractedText.startOffset;
1389 m_extractedText.selectionEnd =
1390 blockPos + query->value(Qt::ImAnchorPosition).toInt() - m_extractedText.startOffset;
1393 if (m_extractedText.selectionStart > m_extractedText.selectionEnd)
1394 std::swap(m_extractedText.selectionStart, m_extractedText.selectionEnd);
1397 return m_extractedText;
1402 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1406 return query->value(Qt::ImCurrentSelection).toString();
1416 QVariant reportedTextAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, length);
1417 if (reportedTextAfter.isValid()) {
1418 text = reportedTextAfter.toString();
1421 QSharedPointer<QInputMethodQueryEvent> query =
1422 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
1424 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1425 text = query->value(Qt::ImSurroundingText).toString().mid(cursorPos);
1429 if (focusObjectIsComposing()) {
1431 const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
1432 text = QStringView{m_composingText}.mid(cursorPosInsidePreedit) + text;
1435 QSharedPointer<QInputMethodQueryEvent> query =
1436 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
1438 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1439 const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
1440 if (anchorPos > cursorPos)
1441 text.remove(0, anchorPos - cursorPos);
1445 text.truncate(length);
1456 QVariant reportedTextBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, length);
1457 if (reportedTextBefore.isValid()) {
1458 text = reportedTextBefore.toString();
1461 QSharedPointer<QInputMethodQueryEvent> query =
1462 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
1464 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1465 text = query->value(Qt::ImSurroundingText).toString().left(cursorPos);
1469 if (focusObjectIsComposing()) {
1471 const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
1472 text += QStringView{m_composingText}.left(cursorPosInsidePreedit);
1475 QSharedPointer<QInputMethodQueryEvent> query =
1476 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
1478 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1479 const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
1480 if (anchorPos < cursorPos)
1481 text.chop(cursorPos - anchorPos);
1485 if (text.length() > length)
1486 text = text.right(length);
1491
1492
1493
1494
1495
1496
1497
1498
1502 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1506 BatchEditLock batchEditLock(
this);
1508 const int absoluteCursorPos = getAbsoluteCursorPosition(query);
1509 int absoluteAnchorPos = getBlockPosition(query) + query->value(Qt::ImAnchorPosition).toInt();
1511 auto setCursorPosition = [=]() {
1512 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1513 QInputMethodEvent event({}, { { QInputMethodEvent::Selection, cursorPos, 0 } });
1514 QGuiApplication::sendEvent(m_focusObject, &event);
1519 if (!m_composingText.isEmpty() && absoluteCursorPos != absoluteAnchorPos) {
1520 setCursorPosition();
1521 absoluteAnchorPos = absoluteCursorPos;
1526 if (absoluteCursorPos == 0 && text.length() == 1 && getTextAfterCursor(1,1).length() >= 0) {
1527 setCursorPosition();
1533 const int effectiveAbsoluteCursorPos = qMin(absoluteCursorPos, absoluteAnchorPos);
1534 if (m_composingTextStart == -1)
1535 m_composingTextStart = effectiveAbsoluteCursorPos;
1537 const int oldComposingTextLen = m_composingText.length();
1538 m_composingText = text;
1540 const int newAbsoluteCursorPos =
1541 newCursorPosition <= 0
1542 ? m_composingTextStart + newCursorPosition
1543 : m_composingTextStart + m_composingText.length() + newCursorPosition - 1;
1545 const bool focusObjectWasComposing = focusObjectIsComposing();
1548 if (!m_composingText.isEmpty() && !m_composingText.contains(u'\n')
1549 && newAbsoluteCursorPos >= m_composingTextStart
1550 && newAbsoluteCursorPos <= m_composingTextStart + m_composingText.length())
1551 m_composingCursor = newAbsoluteCursorPos;
1553 m_composingCursor = -1;
1555 if (focusObjectIsComposing()) {
1556 QTextCharFormat underlined;
1557 underlined.setFontUnderline(
true);
1560 { QInputMethodEvent::TextFormat, 0,
int(m_composingText.length()), underlined },
1561 { QInputMethodEvent::Cursor, m_composingCursor - m_composingTextStart, 1 }
1564 if (oldComposingTextLen > 0 && !focusObjectWasComposing) {
1565 event.setCommitString({}, m_composingTextStart - effectiveAbsoluteCursorPos,
1566 oldComposingTextLen);
1568 if (m_composingText.isEmpty())
1571 QGuiApplication::sendEvent(m_focusObject, &event);
1575 if (focusObjectWasComposing) {
1576 event.setCommitString(m_composingText);
1578 event.setCommitString(m_composingText,
1579 m_composingTextStart - effectiveAbsoluteCursorPos,
1580 oldComposingTextLen);
1582 if (m_composingText.isEmpty())
1585 QGuiApplication::sendEvent(m_focusObject, &event);
1588 if (!focusObjectIsComposing() && newCursorPosition != 1) {
1592 const int newBlockPos = getBlockPosition(
1593 focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition));
1596 { QInputMethodEvent::Selection, newAbsoluteCursorPos - newBlockPos, 0 }
1599 QGuiApplication::sendEvent(m_focusObject, &event);
1613 BatchEditLock batchEditLock(
this);
1620 finishComposingText();
1622 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1631 QString text = query->value(Qt::ImSurroundingText).toString();
1632 int textOffset = getBlockPosition(query);
1634 if (start < textOffset || end > textOffset + text.length()) {
1635 const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
1637 if (end - textOffset > text.length()) {
1638 const QString after = query->value(Qt::ImTextAfterCursor).toString();
1639 const int additionalSuffixLen = after.length() - (text.length() - cursorPos);
1641 if (additionalSuffixLen > 0)
1642 text += QStringView{after}.right(additionalSuffixLen);
1645 if (start < textOffset) {
1646 QString before = query->value(Qt::ImTextBeforeCursor).toString();
1647 before.chop(cursorPos);
1649 if (!before.isEmpty()) {
1650 text = before + text;
1651 textOffset -= before.length();
1655 if (start < textOffset || end - textOffset > text.length()) {
1656 qCDebug(lcQpaInputMethods) <<
"Warning: setComposingRegion: failed to retrieve text from composing region";
1662 m_composingText = text.mid(start - textOffset, end - start);
1663 m_composingTextStart = start;
1670 QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
1674 BatchEditLock batchEditLock(
this);
1676 int blockPosition = getBlockPosition(query);
1677 int localCursorPos = start - blockPosition;
1679 if (focusObjectIsComposing() && start == end && start >= m_composingTextStart
1680 && start <= m_composingTextStart + m_composingText.length()) {
1683 int localOldPos = query->value(Qt::ImCursorPosition).toInt();
1684 int pos = localCursorPos - localOldPos;
1685 QList<QInputMethodEvent::Attribute> attributes;
1686 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, pos, 1));
1691 QTextCharFormat underlined;
1692 underlined.setFontUnderline(
true);
1693 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, m_composingText.length(),
1694 QVariant(underlined)));
1695 m_composingCursor = start;
1698 QGuiApplication::sendEvent(m_focusObject, &event);
1701 focusObjectStopComposing();
1702 QList<QInputMethodEvent::Attribute> attributes;
1703 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
1707 QGuiApplication::sendEvent(m_focusObject, &event);
1714 BatchEditLock batchEditLock(
this);
1716 focusObjectStopComposing();
1717 m_handleMode = ShowCursor;
1718 sendShortcut(QKeySequence::SelectAll);
1724 BatchEditLock batchEditLock(
this);
1728 finishComposingText();
1730 m_handleMode = ShowCursor;
1731 sendShortcut(QKeySequence::Cut);
1737 BatchEditLock batchEditLock(
this);
1739 focusObjectStopComposing();
1740 m_handleMode = ShowCursor;
1741 sendShortcut(QKeySequence::Copy);
1753 BatchEditLock batchEditLock(
this);
1756 finishComposingText();
1758 m_handleMode = ShowCursor;
1759 sendShortcut(QKeySequence::Paste);
1765 for (
int i = 0; i < sequence.count(); ++i) {
1766 const QKeyCombination keys = sequence[i];
1767 Qt::Key key = Qt::Key(keys.toCombined() & ~Qt::KeyboardModifierMask);
1768 Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys.toCombined() & Qt::KeyboardModifierMask);
1770 QKeyEvent pressEvent(QEvent::KeyPress, key, mod);
1771 QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod);
1773 QGuiApplication::sendEvent(m_focusObject, &pressEvent);
1774 QGuiApplication::sendEvent(m_focusObject, &releaseEvent);
1778QSharedPointer<QInputMethodQueryEvent>
QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) {
1782 QObject *focusObject =
qGuiApp->focusObject();
1786 QInputMethodQueryEvent *ret =
new QInputMethodQueryEvent(queries);
1787 QCoreApplication::sendEvent(focusObject, ret);
1788 return QSharedPointer<QInputMethodQueryEvent>(ret);
1796 QObject *focusObject =
qGuiApp->focusObject();
1800 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.
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)