1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "openwnninputmethod_p.h"
31#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
32#include <QLoggingCategory>
33#include <openwnnenginejajp.h>
34#include <composingtext.h>
35#include <romkan.h>
36#include <romkanfullkatakana.h>
37#include <romkanhalfkatakana.h>
38#include <QTextFormat>
39
40QT_BEGIN_NAMESPACE
41namespace QtVirtualKeyboard {
42
43Q_LOGGING_CATEGORY(lcOpenWnn, "qt.virtualkeyboard.openwnn")
44
45class OpenWnnInputMethodPrivate
46{
47 Q_DECLARE_PUBLIC(OpenWnnInputMethod)
48public:
49 enum EngineMode {
50 ENGINE_MODE_DEFAULT,
51 ENGINE_MODE_DIRECT,
52 ENGINE_MODE_NO_LV2_CONV,
53 ENGINE_MODE_FULL_KATAKANA,
54 ENGINE_MODE_HALF_KATAKANA,
55 };
56
57 enum ConvertType {
58 CONVERT_TYPE_NONE = 0,
59 CONVERT_TYPE_RENBUN = 1,
60 };
61
62 enum {
63 MAX_COMPOSING_TEXT = 30
64 };
65
66 OpenWnnInputMethodPrivate(OpenWnnInputMethod *q_ptr) :
67 q_ptr(q_ptr),
68 inputMode(QVirtualKeyboardInputEngine::InputMode::Latin),
69 exactMatchMode(false),
70 converter(nullptr),
71 converterJAJP(),
72 activeConvertType(CONVERT_TYPE_NONE),
73 preConverter(nullptr),
74 enableLearning(true),
75 enablePrediction(true),
76 enableConverter(true),
77 disableUpdate(false),
78 commitCount(0),
79 targetLayer(ComposingText::LAYER1),
80 activeWordIndex(-1)
81 {
82 }
83
84 void changeEngineMode(EngineMode mode)
85 {
86 switch (mode) {
87 case ENGINE_MODE_DIRECT:
88 /* Full/Half-width number or Full-width alphabet */
89 converter = nullptr;
90 preConverter.reset();
91 break;
92
93 case ENGINE_MODE_NO_LV2_CONV:
94 converter = nullptr;
95 preConverter.reset(other: new Romkan());
96 break;
97
98 case ENGINE_MODE_FULL_KATAKANA:
99 converter = nullptr;
100 preConverter.reset(other: new RomkanFullKatakana());
101 break;
102
103 case ENGINE_MODE_HALF_KATAKANA:
104 converter = nullptr;
105 preConverter.reset(other: new RomkanHalfKatakana());
106 break;
107
108 default:
109 /* HIRAGANA input mode */
110 setDictionary(OpenWnnEngineJAJP::DIC_LANG_JP);
111 converter = &converterJAJP;
112 preConverter.reset(other: new Romkan());
113 break;
114 }
115 }
116
117 void setDictionary(OpenWnnEngineJAJP::DictionaryType mode)
118 {
119 converterJAJP.setDictionary(mode);
120 }
121
122 void breakSequence()
123 {
124 converterJAJP.breakSequence();
125 }
126
127 bool isEnableL2Converter()
128 {
129 return converter != nullptr && enableConverter;
130 }
131
132 void startConvert(ConvertType convertType)
133 {
134 if (!isEnableL2Converter())
135 return;
136
137 if (activeConvertType != convertType) {
138 if (!exactMatchMode) {
139 if (convertType == CONVERT_TYPE_RENBUN) {
140 /* not specify */
141 composingText.setCursor(layer: ComposingText::LAYER1, pos: 0);
142 } else {
143 if (activeConvertType == CONVERT_TYPE_RENBUN) {
144 exactMatchMode = true;
145 } else {
146 /* specify all range */
147 composingText.setCursor(layer: ComposingText::LAYER1,
148 pos: composingText.size(layer: ComposingText::LAYER1));
149 }
150 }
151 }
152
153 if (convertType == CONVERT_TYPE_RENBUN)
154 /* clears variables for the prediction */
155 exactMatchMode = false;
156
157 /* clears variables for the convert */
158 commitCount = 0;
159
160 activeConvertType = convertType;
161
162 updateViewStatus(layer: ComposingText::LAYER2, updateCandidates: true, updateEmptyText: true);
163
164 focusNextCandidate();
165 }
166 }
167
168 void changeL2Segment(const QSharedPointer<WnnWord> &word)
169 {
170 if (word.isNull())
171 return;
172 QList<StrSegment> ss;
173 ss.append(t: composingText.getStrSegment(layer: ComposingText::LAYER2, pos: 0));
174 if (!ss[0].clause.isNull())
175 ss[0].clause->candidate = word->candidate;
176 ss[0].string = word->candidate;
177 composingText.replaceStrSegment(layer: ComposingText::LAYER2, str: ss);
178 if (lcOpenWnn().isDebugEnabled())
179 composingText.debugout();
180 updateViewStatus(layer: ComposingText::LAYER2, updateCandidates: false, updateEmptyText: false);
181 }
182
183 void initializeScreen()
184 {
185 if (composingText.size(layer: ComposingText::LAYER0) != 0) {
186 Q_Q(OpenWnnInputMethod);
187 q->inputContext()->commit(text: QString());
188 }
189 composingText.clear();
190 exactMatchMode = false;
191 activeConvertType = CONVERT_TYPE_NONE;
192 clearCandidates();
193 }
194
195 void updateViewStatusForPrediction(bool updateCandidates, bool updateEmptyText)
196 {
197 activeConvertType = CONVERT_TYPE_NONE;
198
199 updateViewStatus(layer: ComposingText::LAYER1, updateCandidates, updateEmptyText);
200 }
201
202 void updateViewStatus(ComposingText::TextLayer layer, bool updateCandidates, bool updateEmptyText)
203 {
204 targetLayer = layer;
205
206 if (updateCandidates)
207 updateCandidateView();
208
209 /* set the text for displaying as the composing text */
210 displayText.clear();
211 displayText.insert(i: 0, s: composingText.toString(layer));
212
213 /* add decoration to the text */
214 if (!displayText.isEmpty() || updateEmptyText) {
215
216 QList<QInputMethodEvent::Attribute> attributes;
217
218 int cursor = composingText.getCursor(layer);
219 if (cursor != 0) {
220 int highlightEnd = 0;
221
222 if (exactMatchMode) {
223
224 QTextCharFormat textFormat;
225 textFormat.setBackground(QBrush(QColor(0x66, 0xCD, 0xAA)));
226 textFormat.setForeground(QBrush(Qt::black));
227 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, cursor, textFormat));
228 highlightEnd = cursor;
229
230 } else if (layer == ComposingText::LAYER2) {
231
232 highlightEnd = composingText.toString(layer, from: 0, to: 0).length();
233
234 /* highlights the first segment */
235 QTextCharFormat textFormat;
236 textFormat.setBackground(QBrush(QColor(0x88, 0x88, 0xFF)));
237 textFormat.setForeground(QBrush(Qt::black));
238 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, highlightEnd, textFormat));
239 }
240
241 if (highlightEnd != 0 && highlightEnd < displayText.length()) {
242 /* highlights remaining text */
243 QTextCharFormat textFormat;
244 textFormat.setBackground(QBrush(QColor(0xF0, 0xFF, 0xFF)));
245 textFormat.setForeground(QBrush(Qt::black));
246 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, highlightEnd, displayText.length() - highlightEnd, textFormat));
247 }
248 }
249
250 QTextCharFormat textFormat;
251 textFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
252 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, displayText.length(), textFormat));
253
254 int displayCursor = composingText.toString(layer, from: 0, to: cursor - 1).length();
255 attributes.append(t: QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, displayCursor, 1, QVariant()));
256
257 Q_Q(OpenWnnInputMethod);
258 q->inputContext()->setPreeditText(text: displayText, attributes);
259 }
260 }
261
262 void updateCandidateView()
263 {
264 switch (targetLayer) {
265 case ComposingText::LAYER0:
266 case ComposingText::LAYER1: /* prediction */
267 if (enablePrediction)
268 /* update the candidates view */
269 updatePrediction();
270 break;
271 case ComposingText::LAYER2: /* convert */
272 if (commitCount == 0)
273 converter->convert(text&: composingText);
274
275 if (converter->makeCandidateListOf(clausePosition: commitCount) != 0) {
276 composingText.setCursor(layer: ComposingText::LAYER2, pos: 1);
277 displayCandidates();
278 } else {
279 composingText.setCursor(layer: ComposingText::LAYER1,
280 pos: composingText.toString(layer: ComposingText::LAYER1).length());
281 clearCandidates();
282 }
283 break;
284 default:
285 break;
286 }
287 }
288
289 void updatePrediction()
290 {
291 int candidates = 0;
292 int cursor = composingText.getCursor(layer: ComposingText::LAYER1);
293 if (isEnableL2Converter()) {
294 if (exactMatchMode)
295 /* exact matching */
296 candidates = converter->predict(text: composingText, minLen: 0, maxLen: cursor);
297 else
298 /* normal prediction */
299 candidates = converter->predict(text: composingText, minLen: 0, maxLen: -1);
300 }
301
302 /* update the candidates view */
303 if (candidates > 0)
304 displayCandidates();
305 else
306 clearCandidates();
307 }
308
309 void displayCandidates()
310 {
311 int previousActiveWordIndex = activeWordIndex;
312 bool wasEmpty = candidateList.isEmpty();
313 clearCandidates(deferUpdate: true);
314
315 QSharedPointer<WnnWord> result;
316 while ((result = converter->getNextCandidate()))
317 candidateList.append(t: result);
318
319 Q_Q(OpenWnnInputMethod);
320 if (!candidateList.isEmpty() || !wasEmpty)
321 emit q->selectionListChanged(type: QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
322 if (previousActiveWordIndex != activeWordIndex)
323 emit q->selectionListActiveItemChanged(type: QVirtualKeyboardSelectionListModel::Type::WordCandidateList, index: activeWordIndex);
324 }
325
326 void clearCandidates(bool deferUpdate = false)
327 {
328 if (!candidateList.isEmpty()) {
329 candidateList.clear();
330 if (!deferUpdate) {
331 Q_Q(OpenWnnInputMethod);
332 emit q->selectionListChanged(type: QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
333 }
334 clearFocusCandidate(deferUpdate);
335 }
336 }
337
338 QSharedPointer<WnnWord> focusNextCandidate()
339 {
340 Q_Q(OpenWnnInputMethod);
341 if (candidateList.isEmpty())
342 return QSharedPointer<WnnWord>();
343 activeWordIndex++;
344 if (activeWordIndex >= candidateList.size())
345 activeWordIndex = 0;
346 emit q->selectionListActiveItemChanged(type: QVirtualKeyboardSelectionListModel::Type::WordCandidateList, index: activeWordIndex);
347 return candidateList.at(i: activeWordIndex);
348 }
349
350 void clearFocusCandidate(bool deferUpdate = false)
351 {
352 Q_Q(OpenWnnInputMethod);
353 if (activeWordIndex != -1) {
354 activeWordIndex = -1;
355 if (!deferUpdate)
356 emit q->selectionListActiveItemChanged(type: QVirtualKeyboardSelectionListModel::Type::WordCandidateList, index: activeWordIndex);
357 }
358 }
359
360 void fitInputType()
361 {
362 Q_Q(OpenWnnInputMethod);
363 enableConverter = true;
364
365 Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
366 if (inputMethodHints.testFlag(flag: Qt::ImhDigitsOnly) ||
367 inputMethodHints.testFlag(flag: Qt::ImhFormattedNumbersOnly) ||
368 inputMethodHints.testFlag(flag: Qt::ImhDialableCharactersOnly)) {
369 enableConverter = false;
370 }
371
372 if (inputMethodHints.testFlag(flag: Qt::ImhLatinOnly)) {
373 enableConverter = false;
374 }
375
376 if (inputMode != QVirtualKeyboardInputEngine::InputMode::Hiragana ||
377 inputMethodHints.testFlag(flag: Qt::ImhHiddenText) ||
378 inputMethodHints.testFlag(flag: Qt::ImhSensitiveData) ||
379 inputMethodHints.testFlag(flag: Qt::ImhNoPredictiveText)) {
380 if (enablePrediction) {
381 enablePrediction = false;
382 emit q->selectionListsChanged();
383 }
384 } else if (inputMode == QVirtualKeyboardInputEngine::InputMode::Hiragana && !enablePrediction) {
385 enablePrediction = true;
386 emit q->selectionListsChanged();
387 }
388
389 activeConvertType = CONVERT_TYPE_NONE;
390 }
391
392 void learnWord(WnnWord &word)
393 {
394 if (enableLearning)
395 converter->learn(word);
396 }
397
398 void learnWord(int index)
399 {
400 if (enableLearning && index < composingText.size(layer: ComposingText::LAYER2)) {
401 StrSegment seg = composingText.getStrSegment(layer: ComposingText::LAYER2, pos: index);
402 if (!seg.clause.isNull()) {
403 converter->learn(word&: *seg.clause);
404 } else {
405 QString stroke = composingText.toString(layer: ComposingText::LAYER1, from: seg.from, to: seg.to);
406 WnnWord word(seg.string, stroke);
407 converter->learn(word);
408 }
409 }
410 }
411
412 void commitAll()
413 {
414 if (activeConvertType != CONVERT_TYPE_NONE) {
415 commitConvertingText();
416 } else {
417 composingText.setCursor(layer: ComposingText::LAYER1,
418 pos: composingText.size(layer: ComposingText::LAYER1));
419 commitText(learn: true);
420 }
421 }
422
423 void commitConvertingText()
424 {
425 if (activeConvertType != CONVERT_TYPE_NONE) {
426 Q_Q(OpenWnnInputMethod);
427 int size = composingText.size(layer: ComposingText::LAYER2);
428 for (int i = 0; i < size; i++) {
429 learnWord(index: i);
430 }
431
432 QString text = composingText.toString(layer: ComposingText::LAYER2);
433 disableUpdate = true;
434 q->inputContext()->commit(text);
435 disableUpdate = false;
436
437 initializeScreen();
438 }
439 }
440
441 bool commitText(bool learn = false)
442 {
443 ComposingText::TextLayer layer = targetLayer;
444 int cursor = composingText.getCursor(layer);
445 if (cursor == 0) {
446 return false;
447 }
448 QString tmp = composingText.toString(layer, from: 0, to: cursor - 1);
449
450 if (converter != nullptr) {
451 if (learn) {
452 if (activeConvertType == CONVERT_TYPE_RENBUN) {
453 learnWord(index: 0); /* select the top of the clauses */
454 } else {
455 if (composingText.size(layer: ComposingText::LAYER1) != 0) {
456 QString stroke = composingText.toString(layer: ComposingText::LAYER1, from: 0, to: composingText.getCursor(layer) - 1);
457 WnnWord word(tmp, stroke);
458 learnWord(word);
459 }
460 }
461 } else {
462 breakSequence();
463 }
464 }
465 return commitText(string: tmp);
466 }
467
468 bool commitText(const WnnWord &word)
469 {
470 return commitText(string: word.candidate);
471 }
472
473 bool commitText(const QString &string)
474 {
475 Q_Q(OpenWnnInputMethod);
476 ComposingText::TextLayer layer = targetLayer;
477
478 disableUpdate = true;
479 q->inputContext()->commit(text: string);
480 disableUpdate = false;
481
482 int cursor = composingText.getCursor(layer);
483 if (cursor > 0) {
484 composingText.deleteStrSegment(layer, from: 0, to: composingText.getCursor(layer) - 1);
485 composingText.setCursor(layer, pos: composingText.size(layer));
486 }
487 exactMatchMode = false;
488 commitCount++;
489
490 if ((layer == ComposingText::LAYER2) && (composingText.size(layer) == 0))
491 layer = ComposingText::LAYER1; /* for connected prediction */
492
493 if (layer == ComposingText::LAYER2) {
494 activeConvertType = CONVERT_TYPE_RENBUN;
495 updateViewStatus(layer, updateCandidates: true, updateEmptyText: false);
496 focusNextCandidate();
497 } else {
498 updateViewStatusForPrediction(updateCandidates: true, updateEmptyText: false);
499 }
500
501 return composingText.size(layer: ComposingText::LAYER0) > 0;
502 }
503
504 bool isAlphabetLast(const QString &str)
505 {
506 if (str.isEmpty())
507 return false;
508 ushort ch = str.at(i: str.length() - 1).unicode();
509 return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
510 }
511
512 void commitTextWithoutLastAlphabet()
513 {
514 QString last = composingText.getStrSegment(layer: targetLayer, pos: -1).string;
515
516 if (isAlphabetLast(str: last)) {
517 composingText.moveCursor(layer: ComposingText::LAYER1, diff: -1);
518 commitText(learn: false);
519 composingText.moveCursor(layer: ComposingText::LAYER1, diff: 1);
520 } else {
521 commitText(learn: false);
522 }
523 }
524
525 bool processLeftKeyEvent()
526 {
527 if (composingText.size(layer: ComposingText::LAYER1) == 0)
528 return false;
529
530 if (activeConvertType != CONVERT_TYPE_NONE) {
531 if (composingText.getCursor(layer: ComposingText::LAYER1) > 1) {
532 composingText.moveCursor(layer: ComposingText::LAYER1, diff: -1);
533 }
534 } else if (exactMatchMode) {
535 composingText.moveCursor(layer: ComposingText::LAYER1, diff: -1);
536 } else {
537 exactMatchMode = true;
538 }
539
540 if (lcOpenWnn().isDebugEnabled())
541 composingText.debugout();
542
543 commitCount = 0; /* retry consecutive clause conversion if necessary. */
544 updateViewStatus(layer: targetLayer, updateCandidates: true, updateEmptyText: true);
545
546 if (activeConvertType != CONVERT_TYPE_NONE)
547 focusNextCandidate();
548
549 return true;
550 }
551
552 bool processRightKeyEvent()
553 {
554 if (composingText.size(layer: ComposingText::LAYER1) == 0)
555 return false;
556
557 ComposingText::TextLayer layer = targetLayer;
558 if (exactMatchMode || activeConvertType != CONVERT_TYPE_NONE) {
559 int textSize = composingText.size(layer: ComposingText::LAYER1);
560 if (composingText.getCursor(layer: ComposingText::LAYER1) == textSize) {
561 exactMatchMode = false;
562 layer = ComposingText::LAYER1; /* convert -> prediction */
563 activeConvertType = CONVERT_TYPE_NONE;
564 } else {
565 composingText.moveCursor(layer: ComposingText::LAYER1, diff: 1);
566 }
567 } else {
568 if (composingText.getCursor(layer: ComposingText::LAYER1) < composingText.size(layer: ComposingText::LAYER1)) {
569 composingText.moveCursor(layer: ComposingText::LAYER1, diff: 1);
570 }
571 }
572
573 if (lcOpenWnn().isDebugEnabled())
574 composingText.debugout();
575
576 commitCount = 0; /* retry consecutive clause conversion if necessary. */
577
578 updateViewStatus(layer, updateCandidates: true, updateEmptyText: true);
579
580 if (activeConvertType != CONVERT_TYPE_NONE)
581 focusNextCandidate();
582
583 return true;
584 }
585
586 OpenWnnInputMethod *q_ptr;
587 QVirtualKeyboardInputEngine::InputMode inputMode;
588 bool exactMatchMode;
589 QString displayText;
590 OpenWnnEngineJAJP *converter;
591 OpenWnnEngineJAJP converterJAJP;
592 ConvertType activeConvertType;
593 ComposingText composingText;
594 QScopedPointer<LetterConverter> preConverter;
595 bool enableLearning;
596 bool enablePrediction;
597 bool enableConverter;
598 bool disableUpdate;
599 int commitCount;
600 ComposingText::TextLayer targetLayer;
601 QList<QSharedPointer<WnnWord> > candidateList;
602 int activeWordIndex;
603};
604
605/*!
606 \class QtVirtualKeyboard::OpenWnnInputMethod
607 \internal
608*/
609
610OpenWnnInputMethod::OpenWnnInputMethod(QObject *parent) :
611 QVirtualKeyboardAbstractInputMethod(parent),
612 d_ptr(new OpenWnnInputMethodPrivate(this))
613{
614}
615
616OpenWnnInputMethod::~OpenWnnInputMethod()
617{
618}
619
620QList<QVirtualKeyboardInputEngine::InputMode> OpenWnnInputMethod::inputModes(const QString &locale)
621{
622 Q_UNUSED(locale)
623 return QList<QVirtualKeyboardInputEngine::InputMode>()
624 << QVirtualKeyboardInputEngine::InputMode::Hiragana
625 << QVirtualKeyboardInputEngine::InputMode::Katakana
626 << QVirtualKeyboardInputEngine::InputMode::FullwidthLatin
627 << QVirtualKeyboardInputEngine::InputMode::Latin;
628}
629
630bool OpenWnnInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode)
631{
632 Q_UNUSED(locale)
633 Q_D(OpenWnnInputMethod);
634 if (d->inputMode == inputMode)
635 return true;
636 update();
637 switch (inputMode) {
638 case QVirtualKeyboardInputEngine::InputMode::Hiragana:
639 d->changeEngineMode(mode: OpenWnnInputMethodPrivate::ENGINE_MODE_DEFAULT);
640 break;
641
642 case QVirtualKeyboardInputEngine::InputMode::Katakana:
643 d->changeEngineMode(mode: OpenWnnInputMethodPrivate::ENGINE_MODE_FULL_KATAKANA);
644 break;
645
646 default:
647 d->changeEngineMode(mode: OpenWnnInputMethodPrivate::ENGINE_MODE_DIRECT);
648 break;
649 }
650 d->inputMode = inputMode;
651 d->fitInputType();
652 return true;
653}
654
655bool OpenWnnInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase)
656{
657 Q_UNUSED(textCase)
658 return true;
659}
660
661bool OpenWnnInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers)
662{
663 Q_UNUSED(key)
664 Q_UNUSED(text)
665 Q_UNUSED(modifiers)
666 Q_D(OpenWnnInputMethod);
667
668 if (d->preConverter == nullptr && !d->isEnableL2Converter())
669 return false;
670
671 switch (key) {
672 case Qt::Key_Left:
673 if (d->isEnableL2Converter() && d->composingText.size(layer: ComposingText::LAYER1) > 0)
674 return d->processLeftKeyEvent();
675 else
676 return d->commitText(learn: false);
677 break;
678
679 case Qt::Key_Right:
680 if (d->isEnableL2Converter() && d->composingText.size(layer: ComposingText::LAYER1) > 0)
681 return d->processRightKeyEvent();
682 else
683 return d->commitText(learn: false);
684 break;
685
686 case Qt::Key_Backspace:
687 if (d->composingText.size(layer: ComposingText::LAYER1) > 0) {
688 if (d->activeConvertType == OpenWnnInputMethodPrivate::CONVERT_TYPE_RENBUN) {
689 d->composingText.setCursor(layer: ComposingText::LAYER1,
690 pos: d->composingText.toString(layer: ComposingText::LAYER1).length());
691 d->exactMatchMode = false;
692 d->clearFocusCandidate();
693 } else {
694 if ((d->composingText.size(layer: ComposingText::LAYER1) == 1) &&
695 d->composingText.getCursor(layer: ComposingText::LAYER1) != 0) {
696 d->initializeScreen();
697 return true;
698 } else {
699 d->composingText.deleteAt(layer: ComposingText::LAYER1, rightside: false);
700 }
701 }
702 if (lcOpenWnn().isDebugEnabled())
703 d->composingText.debugout();
704 d->updateViewStatusForPrediction(updateCandidates: true, updateEmptyText: true);
705 return true;
706 }
707 break;
708
709 case Qt::Key_Space:
710 if (d->composingText.size(layer: ComposingText::LAYER0) == 0) {
711 d->clearCandidates();
712 d->breakSequence();
713 } else {
714 if (d->targetLayer == ComposingText::LAYER2)
715 d->changeL2Segment(word: d->focusNextCandidate());
716 else if (d->isEnableL2Converter())
717 d->startConvert(convertType: OpenWnnInputMethodPrivate::CONVERT_TYPE_RENBUN);
718 else
719 return d->commitText(learn: false);
720 return true;
721 }
722 break;
723
724 case Qt::Key_Return:
725 case Qt::Key_Enter:
726 if (d->composingText.size(layer: ComposingText::LAYER0) > 0) {
727 d->commitText(learn: true);
728 return true;
729 }
730 break;
731
732 default:
733 if (key < Qt::Key_Escape && !text.isEmpty() && text.at(i: 0).isPrint()) {
734 if (d->composingText.size(layer: ComposingText::LAYER1) + text.size() > OpenWnnInputMethodPrivate::MAX_COMPOSING_TEXT)
735 return true;
736 const int last = text.size() - 1;
737 for (int i = 0; i <= last; ++i) {
738 if (d->isEnableL2Converter()) {
739 d->commitConvertingText();
740 d->composingText.insertStrSegment(layer1: ComposingText::LAYER0, layer2: ComposingText::LAYER1, str: text.mid(position: i, n: 1));
741 if (d->preConverter != nullptr)
742 d->preConverter->convert(text&: d->composingText);
743 if (i == last)
744 d->updateViewStatusForPrediction(updateCandidates: true, updateEmptyText: true);
745 } else {
746 d->composingText.insertStrSegment(layer1: ComposingText::LAYER0, layer2: ComposingText::LAYER1, str: text.mid(position: i, n: 1));
747 QString layer1 = d->composingText.toString(layer: ComposingText::LAYER1);
748 if (!d->isAlphabetLast(str: layer1)) {
749 d->commitText(learn: false);
750 } else {
751 bool completed = d->preConverter->convert(text&: d->composingText);
752 if (completed) {
753 d->commitTextWithoutLastAlphabet();
754 } else {
755 if (i == last)
756 d->updateViewStatusForPrediction(updateCandidates: true, updateEmptyText: true);
757 }
758 }
759 }
760 }
761 if (lcOpenWnn().isDebugEnabled())
762 d->composingText.debugout();
763 return true;
764 }
765 break;
766 }
767
768 return false;
769}
770
771QList<QVirtualKeyboardSelectionListModel::Type> OpenWnnInputMethod::selectionLists()
772{
773 Q_D(OpenWnnInputMethod);
774 if (!d->enablePrediction)
775 return QList<QVirtualKeyboardSelectionListModel::Type>();
776 return QList<QVirtualKeyboardSelectionListModel::Type>() << QVirtualKeyboardSelectionListModel::Type::WordCandidateList;
777}
778
779int OpenWnnInputMethod::selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type)
780{
781 Q_UNUSED(type)
782 Q_D(OpenWnnInputMethod);
783 return d->candidateList.size();
784}
785
786QVariant OpenWnnInputMethod::selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role)
787{
788 QVariant result;
789 Q_D(OpenWnnInputMethod);
790 switch (role) {
791 case QVirtualKeyboardSelectionListModel::Role::Display:
792 result = QVariant(d->candidateList.at(i: index)->candidate);
793 break;
794 case QVirtualKeyboardSelectionListModel::Role::WordCompletionLength:
795 result.setValue(0);
796 break;
797 default:
798 result = QVirtualKeyboardAbstractInputMethod::selectionListData(type, index, role);
799 break;
800 }
801 return result;
802}
803
804void OpenWnnInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index)
805{
806 Q_UNUSED(type)
807 Q_D(OpenWnnInputMethod);
808 d->activeWordIndex = index;
809 // Set selected text as preeditText to place cursor at the end of selected text
810 inputContext()->setPreeditText(text: d->candidateList.at(i: index)->candidate);
811 d->commitText(word: *d->candidateList.at(i: index));
812}
813
814void OpenWnnInputMethod::reset()
815{
816 Q_D(OpenWnnInputMethod);
817 d->composingText.clear();
818 d->initializeScreen();
819 d->fitInputType();
820}
821
822void OpenWnnInputMethod::update()
823{
824 Q_D(OpenWnnInputMethod);
825 if (!d->disableUpdate) {
826 d->commitAll();
827 reset();
828 }
829}
830
831} // namespace QtVirtualKeyboard
832QT_END_NAMESPACE
833

source code of qtvirtualkeyboard/src/plugins/openwnn/plugin/openwnninputmethod.cpp