1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2004 - 2005 Hiroyuki Ikezoe <poincare@ikezoe.net>
4  *  Copyright (C) 2004 - 2005 Takuro Ashie <ashie@homa.ne.jp>
5  *  SPDX-FileCopyrightText: 2012 CSSlayer <wengxt@gmail.com>
6  *
7  *  SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 /*
11  * The original code is scim_uim_imengine.cpp in scim-uim-0.1.3.
12  * SPDX-FileCopyrightText: 2004 James Su <suzhe@tsinghua.org.cn>
13  */
14 
15 #ifdef HAVE_CONFIG_H
16 #include <config.h>
17 #endif
18 
19 #include <errno.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include "engine.h"
25 #include "state.h"
26 #include "utils.h"
27 
28 #include <fcitx-utils/log.h>
29 #include <fcitx/inputcontext.h>
30 #include <fcitx/inputpanel.h>
31 #include <fcitx/statusarea.h>
32 
33 #include <clipboard_public.h>
34 #include <fcitx-utils/standardpath.h>
35 
AnthyState(fcitx::InputContext * ic,AnthyEngine * engine,fcitx::Instance * instance)36 AnthyState::AnthyState(fcitx::InputContext *ic, AnthyEngine *engine,
37                        fcitx::Instance *instance)
38     : ic_(ic), engine_(engine), instance_(instance), preedit_(*this),
39       preeditVisible_(false), lookupTableVisible_(false), nConvKeyPressed_(0),
40       prevInputMode_(InputMode::HIRAGANA), cursorPos_(0), uiUpdate_(false) {
41     configure();
42 }
43 
~AnthyState()44 AnthyState::~AnthyState() {}
45 
46 // FIXME!
isNicolaThumbShiftKey(const fcitx::KeyEvent & key)47 bool AnthyState::isNicolaThumbShiftKey(const fcitx::KeyEvent &key) {
48     if (typingMethod() != TypingMethod::NICOLA)
49         return false;
50 
51     if (util::match_key_event(*config().key->leftThumbKeys, key.rawKey(),
52                               fcitx::KeyStates(0xFFFF)) ||
53         util::match_key_event(*config().key->rightThumbKeys, key.rawKey(),
54                               fcitx::KeyStates(0xFFFF))) {
55         return true;
56     }
57 
58     return false;
59 }
60 
processKeyEventInput(const fcitx::KeyEvent & key)61 bool AnthyState::processKeyEventInput(const fcitx::KeyEvent &key) {
62     // prediction while typing
63     if (*config().general->predictOnInput && key.isRelease() &&
64         preedit_.isPreediting() && !preedit_.isConverting()) {
65         preedit_.predict();
66         preedit_.candidates();
67     }
68 
69     if (!preedit_.canProcessKeyEvent(key)) {
70         return false;
71     }
72 
73     if (preedit_.isConverting()) {
74         if (isRealtimeConversion()) {
75             action_revert();
76         } else if (!isNicolaThumbShiftKey(key)) {
77             action_commit(*config().general->learnOnAutoCommit);
78         }
79     }
80 
81     bool need_commit = preedit_.processKeyEvent(key);
82 
83     if (need_commit) {
84         if (isRealtimeConversion() && inputMode() != InputMode::LATIN &&
85             inputMode() != InputMode::WIDE_LATIN) {
86             preedit_.convert(FCITX_ANTHY_CANDIDATE_DEFAULT, isSingleSegment());
87         }
88         action_commit(*config().general->learnOnAutoCommit);
89     } else {
90         if (isRealtimeConversion()) {
91             preedit_.convert(FCITX_ANTHY_CANDIDATE_DEFAULT, isSingleSegment());
92             preedit_.selectSegment(-1);
93         }
94         // show_preedit_string ();
95         preeditVisible_ = true;
96         setPreedition();
97     }
98 
99     return true;
100 }
101 
processKeyEventLookupKeybind(const fcitx::KeyEvent & key)102 bool AnthyState::processKeyEventLookupKeybind(const fcitx::KeyEvent &key) {
103     decltype(actions_)::iterator it;
104 
105     if (key.isRelease())
106         return false;
107 
108     lastKey_ = key.rawKey();
109 
110     /* try to find a "insert a blank" action to be not stolen a blank key
111      * when entering the pseudo ascii mode.
112      */
113     if (pseudoAsciiMode() != 0 &&
114         *config().general->romajiPseudoAsciiBlankBehavior &&
115         preedit_.isPseudoAsciiMode()) {
116         it = std::find_if(actions_.begin(), actions_.end(),
117                           [](const Action &action) {
118                               return action.name() == "INSERT_SPACE";
119                           });
120         if (it != actions_.end() && it->perform(this, key)) {
121             return true;
122         }
123     }
124 
125     for (it = actions_.begin(); it != actions_.end(); it++) {
126         if (it->perform(this, key)) {
127             lastKey_ = fcitx::Key();
128             return true;
129         }
130     }
131 
132     int choose = key.rawKey().keyListIndex(util::selection_keys());
133     if (choose >= 0) {
134         auto candList = ic_->inputPanel().candidateList();
135         if (candList && candList->size() > choose) {
136             candList->candidate(choose).select(ic_);
137             lastKey_ = fcitx::Key();
138             return true;
139         }
140     }
141 
142     lastKey_ = fcitx::Key();
143 
144     return false;
145 }
146 
processKeyEventLatinMode(const fcitx::KeyEvent & key)147 bool AnthyState::processKeyEventLatinMode(const fcitx::KeyEvent &key) {
148     if (key.isRelease())
149         return false;
150 
151     if (util::key_is_keypad(key.rawKey())) {
152         std::string wide;
153         auto str = util::keypad_to_string(key);
154         if (*config().general->tenKeyType == TenKeyType::WIDE)
155             wide = util::convert_to_wide(str);
156         else
157             wide = str;
158         if (!wide.empty()) {
159             commitString(wide);
160             return true;
161         } else {
162             return false;
163         }
164     } else {
165         // for Multi/Dead key
166         return false;
167     }
168 }
169 
processKeyEventWideLatinMode(const fcitx::KeyEvent & key)170 bool AnthyState::processKeyEventWideLatinMode(const fcitx::KeyEvent &key) {
171     if (key.isRelease())
172         return false;
173 
174     std::string wide;
175     auto str = util::keypad_to_string(key);
176     if (util::key_is_keypad(key.rawKey()) &&
177         *config().general->tenKeyType == TenKeyType::HALF) {
178         wide = str;
179     } else {
180         wide = util::convert_to_wide(str);
181     }
182     if (!wide.empty()) {
183         commitString(wide);
184         return true;
185     }
186 
187     return false;
188 }
189 
processKeyEvent(const fcitx::KeyEvent & key)190 bool AnthyState::processKeyEvent(const fcitx::KeyEvent &key) {
191     // FIXME!
192     // for NICOLA thumb shift key
193     if (typingMethod() == TypingMethod::NICOLA && isNicolaThumbShiftKey(key)) {
194         if (processKeyEventInput(key))
195             return true;
196     }
197 
198     // lookup user defined key bindings
199     if (processKeyEventLookupKeybind(key))
200         return true;
201 
202     if (key.rawKey().isDigit() && ic_->inputPanel().candidateList() &&
203         ic_->inputPanel().candidateList()->size()) {
204         return false;
205     }
206 
207     // for Latin mode
208     if (preedit_.inputMode() == InputMode::LATIN)
209         return processKeyEventLatinMode(key);
210 
211     // for wide Latin mode
212     if (preedit_.inputMode() == InputMode::WIDE_LATIN)
213         return processKeyEventWideLatinMode(key);
214 
215     // for other mode
216     if (typingMethod() != TypingMethod::NICOLA || !isNicolaThumbShiftKey(key)) {
217         if (processKeyEventInput(key))
218             return true;
219     }
220 
221     if (preedit_.isPreediting())
222         return true;
223     else
224         return false;
225 }
226 
movePreeditCaret(unsigned int pos)227 void AnthyState::movePreeditCaret(unsigned int pos) {
228     preedit_.setCaretPosByChar(pos);
229     // TODO
230 }
231 
selectCandidateNoDirect(unsigned int item)232 void AnthyState::selectCandidateNoDirect(unsigned int item) {
233     if (preedit_.isPredicting() && !preedit_.isConverting())
234         action_predict();
235 
236     // update lookup table
237     cursorPos_ = item;
238 
239     // update preedit
240     preedit_.selectCandidate(cursorPos_);
241     setPreedition();
242 
243     setLookupTable();
244     if (auto candList = ic_->inputPanel().candidateList()) {
245         if (cursorPos_ >= 0 && cursorPos_ < candList->size()) {
246             auto commonCandList =
247                 std::static_pointer_cast<fcitx::CommonCandidateList>(candList);
248             commonCandList->setGlobalCursorIndex(cursorPos_);
249             commonCandList->setPage(cursorPos_ / *config().general->pageSize);
250         }
251     }
252 
253     // update aux string
254     if (*config().general->showCandidatesLabel)
255         setAuxString();
256 }
257 
selectCandidate(unsigned int item)258 void AnthyState::selectCandidate(unsigned int item) {
259     selectCandidateNoDirect(item);
260 
261     unsetLookupTable();
262     action_select_next_segment();
263 }
264 
reset()265 void AnthyState::reset() {
266     ic_->inputPanel().reset();
267 
268     preedit_.clear();
269     unsetLookupTable();
270 
271     preeditVisible_ = false;
272     setPreedition();
273 }
274 
init()275 void AnthyState::init() {
276     ic_->inputPanel().reset();
277     if (preeditVisible_) {
278         setPreedition();
279     }
280 
281     if (lookupTableVisible_ && isSelectingCandidates()) {
282         if (*config().general->showCandidatesLabel) {
283             setAuxString();
284         }
285         setLookupTable();
286     }
287 
288     installProperties();
289 }
290 
supportClientPreedit()291 bool AnthyState::supportClientPreedit() {
292     return ic_->capabilityFlags().test(fcitx::CapabilityFlag::Preedit);
293 }
294 
setPreedition()295 void AnthyState::setPreedition() {
296     preedit_.updatePreedit();
297     uiUpdate_ = true;
298 }
299 
updateUI()300 void AnthyState::updateUI() {
301     if (uiUpdate_) {
302         uiUpdate_ = false;
303         ic_->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
304         ic_->updatePreedit();
305     }
306 }
307 
setAuxString()308 void AnthyState::setAuxString() {
309     if (!ic_->inputPanel().candidateList() ||
310         !ic_->inputPanel().candidateList()->size())
311         return;
312 
313     if (auto bulk = ic_->inputPanel().candidateList()->toBulk()) {
314         char buf[256];
315         sprintf(buf, _("(%d / %d)"), cursorPos_ + 1, bulk->totalSize());
316         updateAuxString(buf);
317     }
318 }
319 
setLookupTable()320 int AnthyState::setLookupTable() {
321 
322     // if (!is_selecting_candidates ()) {
323     if (isRealtimeConversion() && preedit_.selectedSegment() < 0) {
324         // select latest segment
325         int n = preedit_.nrSegments();
326         if (n < 1)
327             return 0;
328         preedit_.selectSegment(n - 1);
329     }
330 
331     // prepare candidates
332     auto candList = preedit_.candidates();
333 
334     if (candList->size() == 0) {
335         return 0;
336     }
337 
338     // update preedit
339     preedit_.selectCandidate(cursorPos_);
340     setPreedition();
341 
342     bool beyond_threshold =
343         *config().general->nTriggersToShowCandWin > 0 &&
344         (int)nConvKeyPressed_ >= *config().general->nTriggersToShowCandWin;
345 
346     int len = candList->totalSize();
347 
348     if (!lookupTableVisible_ && (preedit_.isPredicting() || beyond_threshold)) {
349         lookupTableVisible_ = true;
350         nConvKeyPressed_ = 0;
351 
352         if (*config().general->showCandidatesLabel) {
353             setAuxString();
354         }
355     } else if (!lookupTableVisible_) {
356         candList.reset();
357     }
358 
359     ic_->inputPanel().setCandidateList(std::move(candList));
360     uiUpdate_ = true;
361 
362     return len;
363 }
364 
unsetLookupTable()365 void AnthyState::unsetLookupTable() {
366     ic_->inputPanel().setCandidateList(nullptr);
367     lookupTableVisible_ = false;
368     nConvKeyPressed_ = 0;
369     cursorPos_ = 0;
370 
371     ic_->inputPanel().setAuxUp(fcitx::Text());
372 }
373 
setPeriodCommaStyle(PeriodCommaStyle period)374 void AnthyState::setPeriodCommaStyle(PeriodCommaStyle period) {
375     config().general.mutableValue()->periodCommaStyle.setValue(period);
376     engine_->periodStyleAction()->update(ic_);
377 
378     switch (period) {
379     case PeriodCommaStyle::WIDELATIN:
380         preedit_.setCommaStyle(CommaStyle::WIDE);
381         preedit_.setPeriodStyle(PeriodStyle::WIDE);
382         break;
383     case PeriodCommaStyle::LATIN:
384         preedit_.setCommaStyle(CommaStyle::HALF);
385         preedit_.setPeriodStyle(PeriodStyle::HALF);
386         break;
387     case PeriodCommaStyle::WIDELATIN_JAPANESE:
388         preedit_.setCommaStyle(CommaStyle::WIDE);
389         preedit_.setPeriodStyle(PeriodStyle::JAPANESE);
390         break;
391     case PeriodCommaStyle::JAPANESE:
392     default:
393         preedit_.setCommaStyle(CommaStyle::JAPANESE);
394         preedit_.setPeriodStyle(PeriodStyle::JAPANESE);
395         break;
396     }
397 }
398 
setSymbolStyle(SymbolStyle symbol)399 void AnthyState::setSymbolStyle(SymbolStyle symbol) {
400     config().general.mutableValue()->symbolStyle.setValue(symbol);
401     engine_->symbolStyleAction()->update(ic_);
402     switch (symbol) {
403     case SymbolStyle::WIDEBRACKET_WIDESLASH:
404         preedit_.setBracketStyle(BracketStyle::JAPANESE);
405         preedit_.setSlashStyle(SlashStyle::WIDE);
406         break;
407     case SymbolStyle::CORNERBRACKET_WIDESLASH:
408         preedit_.setBracketStyle(BracketStyle::WIDE);
409         preedit_.setSlashStyle(SlashStyle::WIDE);
410         break;
411     case SymbolStyle::CORNERBRACKET_MIDDLEDOT:
412         preedit_.setBracketStyle(BracketStyle::WIDE);
413         preedit_.setSlashStyle(SlashStyle::JAPANESE);
414         break;
415     case SymbolStyle::JAPANESE:
416     default:
417         preedit_.setBracketStyle(BracketStyle::JAPANESE);
418         preedit_.setSlashStyle(SlashStyle::JAPANESE);
419         break;
420     }
421 }
422 
installProperties()423 void AnthyState::installProperties() {
424     if (*config().general->showCandidatesLabel) {
425         setInputMode(inputMode());
426     }
427     setConversionMode(*config().general->conversionMode);
428     setTypingMethod(typingMethod());
429     setPeriodCommaStyle(periodCommaStyle());
430     setSymbolStyle(symbolStyle());
431 }
432 
setInputMode(InputMode mode)433 void AnthyState::setInputMode(InputMode mode) {
434     if (mode != inputMode()) {
435         config().general.mutableValue()->inputMode.setValue(mode);
436         preedit_.setInputMode(mode);
437         setPreedition();
438     }
439 
440     engine_->inputModeAction()->update(ic_);
441     if (!engine_->constructed()) {
442         return;
443     }
444     if (ic_->hasFocus() && instance_->inputMethod(ic_) == "anthy") {
445         instance_->showInputMethodInformation(ic_);
446     }
447 }
448 
setConversionMode(ConversionMode mode)449 void AnthyState::setConversionMode(ConversionMode mode) {
450     config().general.mutableValue()->conversionMode.setValue(mode);
451 
452     engine_->conversionModeAction()->update(ic_);
453 }
454 
setTypingMethod(TypingMethod method)455 void AnthyState::setTypingMethod(TypingMethod method) {
456     if (method != typingMethod()) {
457         preedit_.setTypingMethod(method);
458         preedit_.setPseudoAsciiMode(pseudoAsciiMode());
459     }
460 
461     config().general.mutableValue()->typingMethod.setValue(method);
462     engine_->typingMethodAction()->update(ic_);
463 }
464 
setPeriodStyle(PeriodStyle period,CommaStyle comma)465 void AnthyState::setPeriodStyle(PeriodStyle period, CommaStyle comma) {
466     std::string label;
467 
468     switch (comma) {
469     case CommaStyle::JAPANESE:
470         label = "\xE3\x80\x81";
471         break;
472     case CommaStyle::WIDE:
473         label = "\xEF\xBC\x8C";
474         break;
475     case CommaStyle::HALF:
476         label = ",";
477         break;
478     default:
479         break;
480     }
481 
482     switch (period) {
483     case PeriodStyle::JAPANESE:
484         label += "\xE3\x80\x82";
485         break;
486     case PeriodStyle::WIDE:
487         label += "\xEF\xBC\x8E";
488         break;
489     case PeriodStyle::HALF:
490         label += ".";
491         break;
492     default:
493         break;
494     }
495 
496     if (period != preedit_.periodStyle()) {
497         preedit_.setPeriodStyle(period);
498     }
499     if (comma != preedit_.commaStyle()) {
500         preedit_.setCommaStyle(comma);
501     }
502 }
503 
setSymbolStyle(BracketStyle bracket,SlashStyle slash)504 void AnthyState::setSymbolStyle(BracketStyle bracket, SlashStyle slash) {
505     if (bracket != preedit_.bracketStyle()) {
506         preedit_.setBracketStyle(bracket);
507     }
508     if (slash != preedit_.slashStyle()) {
509         preedit_.setSlashStyle(slash);
510     }
511 }
512 
isSelectingCandidates()513 bool AnthyState::isSelectingCandidates() {
514     return ic_->inputPanel().candidateList() &&
515            ic_->inputPanel().candidateList()->size();
516 }
517 
deactivate()518 void AnthyState::deactivate() {}
519 
action_convert()520 bool AnthyState::action_convert() {
521     if (!preedit_.isPreediting())
522         return false;
523 
524     if (!preedit_.isConverting()) {
525         // show conversion string
526         preedit_.finish();
527         preedit_.convert(FCITX_ANTHY_CANDIDATE_DEFAULT, isSingleSegment());
528         setPreedition();
529         nConvKeyPressed_++;
530         setLookupTable();
531         return true;
532     }
533 
534     return false;
535 }
536 
action_predict()537 bool AnthyState::action_predict() {
538     if (!preedit_.isPreediting())
539         return false;
540 
541     if (preedit_.isConverting())
542         return false;
543 
544     if (!preedit_.isPredicting())
545         preedit_.predict();
546 
547     preedit_.selectCandidate(0);
548     setPreedition();
549     nConvKeyPressed_++;
550     setLookupTable();
551     selectCandidateNoDirect(0);
552 
553     return true;
554 }
555 
action_revert()556 bool AnthyState::action_revert() {
557     if (preedit_.isReconverting()) {
558         preedit_.revert();
559         commitString(preedit_.string());
560         reset();
561         return true;
562     }
563 
564     if (!preedit_.isPreediting())
565         return false;
566 
567     if (!preedit_.isConverting()) {
568         reset();
569         return true;
570     }
571 
572     if (isSelectingCandidates()) {
573         ic_->inputPanel().setCandidateList(nullptr);
574     }
575 
576     unsetLookupTable();
577     preedit_.revert();
578     setPreedition();
579 
580     return true;
581 }
582 
action_cancel_all()583 bool AnthyState::action_cancel_all() {
584     if (!preedit_.isPreediting())
585         return false;
586 
587     reset();
588     return true;
589 }
590 
action_commit(bool learn,bool do_real_commit)591 bool AnthyState::action_commit(bool learn, bool do_real_commit) {
592     if (!preedit_.isPreediting())
593         return false;
594 
595     if (preedit_.isConverting()) {
596         if (do_real_commit)
597             commitString(preedit_.string());
598         if (learn)
599             preedit_.commit();
600     } else {
601         preedit_.finish();
602         if (do_real_commit)
603             commitString(preedit_.string());
604     }
605 
606     reset();
607 
608     return true;
609 }
610 
action_commit_follow_preference()611 bool AnthyState::action_commit_follow_preference() {
612     return action_commit(*config().general->learnOnManualCommit);
613 }
614 
action_commit_reverse_preference()615 bool AnthyState::action_commit_reverse_preference() {
616     return action_commit(!*config().general->learnOnManualCommit);
617 }
618 
action_back()619 bool AnthyState::action_back() {
620     if (!preedit_.isPreediting())
621         return false;
622 
623     if (preedit_.isConverting()) {
624         action_revert();
625         if (!isRealtimeConversion())
626             return true;
627     }
628 
629     preedit_.erase();
630 
631     if (preedit_.length() > 0) {
632         if (isRealtimeConversion()) {
633             preedit_.convert(FCITX_ANTHY_CANDIDATE_DEFAULT, isSingleSegment());
634             preedit_.selectSegment(-1);
635         }
636         setPreedition();
637     } else {
638         reset();
639     }
640 
641     return true;
642 }
643 
action_delete()644 bool AnthyState::action_delete() {
645     if (!preedit_.isPreediting())
646         return false;
647 
648     if (preedit_.isConverting()) {
649         action_revert();
650         if (!isRealtimeConversion())
651             return true;
652     }
653 
654     preedit_.erase(false);
655 
656     if (preedit_.length() > 0) {
657         if (isRealtimeConversion()) {
658             preedit_.convert(FCITX_ANTHY_CANDIDATE_DEFAULT, isSingleSegment());
659             preedit_.selectSegment(-1);
660         }
661         setPreedition();
662     } else {
663         reset();
664     }
665 
666     return true;
667 }
668 
action_insert_space()669 bool AnthyState::action_insert_space() {
670     std::string str;
671     bool is_wide = false, retval = false;
672 
673     if (preedit_.isPreediting() &&
674         !*config().general->romajiPseudoAsciiBlankBehavior)
675         return false;
676 
677     if (*config().general->spaceType == SpaceType::FOLLOWMODE) {
678         InputMode mode = inputMode();
679         if (mode == InputMode::LATIN || mode == InputMode::HALF_KATAKANA ||
680             preedit_.isPseudoAsciiMode()) {
681             is_wide = false;
682         } else {
683             is_wide = true;
684         }
685     } else if (*config().general->spaceType == SpaceType::WIDE) {
686         is_wide = true;
687     }
688 
689     if (is_wide) {
690         str = "\xE3\x80\x80";
691         retval = true;
692     } else if (typingMethod() == TypingMethod::NICOLA || // FIXME! it's a ad-hoc
693                                                          // solution.
694                preedit_.isPseudoAsciiMode() ||
695                (lastKey_.sym() != FcitxKey_space &&
696                 lastKey_.sym() != FcitxKey_KP_Space)) {
697         str = " ";
698         retval = true;
699     }
700 
701     if (retval) {
702         if (preedit_.isPseudoAsciiMode()) {
703             preedit_.append(lastKey_, str);
704             // show_preedit_string ();
705             preeditVisible_ = true;
706             setPreedition();
707         } else {
708             commitString(str);
709         }
710     }
711 
712     return retval;
713 }
714 
action_insert_alternative_space()715 bool AnthyState::action_insert_alternative_space() {
716     bool is_wide = false;
717 
718     if (preedit_.isPreediting())
719         return false;
720 
721     if (*config().general->spaceType == SpaceType::FOLLOWMODE) {
722         InputMode mode = inputMode();
723         if (mode == InputMode::LATIN || mode == InputMode::HALF_KATAKANA) {
724             is_wide = true;
725         } else {
726             is_wide = false;
727         }
728     } else if (*config().general->spaceType != SpaceType::WIDE) {
729         is_wide = true;
730     }
731 
732     if (is_wide) {
733         commitString("\xE3\x80\x80");
734         return true;
735     } else if (typingMethod() == TypingMethod::NICOLA || // FIXME! it's a ad-hoc
736                                                          // solution.
737                (lastKey_.sym() != FcitxKey_space &&
738                 lastKey_.sym() != FcitxKey_KP_Space)) {
739         commitString(" ");
740         return true;
741     }
742 
743     return false;
744 }
745 
action_insert_half_space()746 bool AnthyState::action_insert_half_space() {
747     if (preedit_.isPreediting())
748         return false;
749 
750     if (lastKey_.sym() != FcitxKey_space &&
751         lastKey_.sym() != FcitxKey_KP_Space) {
752         commitString(" ");
753         return true;
754     }
755 
756     return false;
757 }
758 
action_insert_wide_space()759 bool AnthyState::action_insert_wide_space() {
760     if (preedit_.isPreediting())
761         return false;
762 
763     commitString("\xE3\x80\x80");
764 
765     return true;
766 }
767 
action_move_caret_backward()768 bool AnthyState::action_move_caret_backward() {
769     if (!preedit_.isPreediting())
770         return false;
771     if (preedit_.isConverting())
772         return false;
773 
774     preedit_.moveCaret(-1);
775     setPreedition();
776 
777     return true;
778 }
779 
action_move_caret_forward()780 bool AnthyState::action_move_caret_forward() {
781     if (!preedit_.isPreediting())
782         return false;
783     if (preedit_.isConverting())
784         return false;
785 
786     preedit_.moveCaret(1);
787     setPreedition();
788 
789     return true;
790 }
791 
action_move_caret_first()792 bool AnthyState::action_move_caret_first() {
793     if (!preedit_.isPreediting())
794         return false;
795     if (preedit_.isConverting())
796         return false;
797 
798     preedit_.setCaretPosByChar(0);
799     setPreedition();
800 
801     return true;
802 }
803 
action_move_caret_last()804 bool AnthyState::action_move_caret_last() {
805     if (!preedit_.isPreediting())
806         return false;
807     if (preedit_.isConverting())
808         return false;
809 
810     preedit_.setCaretPosByChar(preedit_.utf8Length());
811     setPreedition();
812 
813     return true;
814 }
815 
action_select_prev_segment()816 bool AnthyState::action_select_prev_segment() {
817     if (!preedit_.isConverting())
818         return false;
819 
820     unsetLookupTable();
821 
822     int idx = preedit_.selectedSegment();
823     if (idx - 1 < 0) {
824         int n = preedit_.nrSegments();
825         if (n <= 0)
826             return false;
827         preedit_.selectSegment(n - 1);
828     } else {
829         preedit_.selectSegment(idx - 1);
830     }
831     setPreedition();
832 
833     return true;
834 }
835 
action_select_next_segment()836 bool AnthyState::action_select_next_segment() {
837     if (!preedit_.isConverting())
838         return false;
839 
840     unsetLookupTable();
841 
842     int idx = preedit_.selectedSegment();
843     if (idx < 0) {
844         preedit_.selectSegment(0);
845     } else {
846         int n = preedit_.nrSegments();
847         if (n <= 0)
848             return false;
849         if (idx + 1 >= n)
850             preedit_.selectSegment(0);
851         else
852             preedit_.selectSegment(idx + 1);
853     }
854     setPreedition();
855 
856     return true;
857 }
858 
action_select_first_segment()859 bool AnthyState::action_select_first_segment() {
860     if (!preedit_.isConverting())
861         return false;
862 
863     unsetLookupTable();
864 
865     preedit_.selectSegment(0);
866     setPreedition();
867 
868     return true;
869 }
870 
action_select_last_segment()871 bool AnthyState::action_select_last_segment() {
872     if (!preedit_.isConverting())
873         return false;
874 
875     int n = preedit_.nrSegments();
876     if (n <= 0)
877         return false;
878 
879     unsetLookupTable();
880 
881     preedit_.selectSegment(n - 1);
882     setPreedition();
883 
884     return true;
885 }
886 
action_shrink_segment()887 bool AnthyState::action_shrink_segment() {
888     if (!preedit_.isConverting())
889         return false;
890 
891     unsetLookupTable();
892 
893     preedit_.resizeSegment(-1);
894     setPreedition();
895 
896     return true;
897 }
898 
action_expand_segment()899 bool AnthyState::action_expand_segment() {
900     if (!preedit_.isConverting())
901         return false;
902 
903     unsetLookupTable();
904 
905     preedit_.resizeSegment(1);
906     setPreedition();
907 
908     return true;
909 }
910 
action_commit_first_segment()911 bool AnthyState::action_commit_first_segment() {
912     if (!preedit_.isConverting()) {
913         if (preedit_.isPreediting()) {
914             return action_commit(*config().general->learnOnManualCommit);
915         } else {
916             return false;
917         }
918     }
919 
920     unsetLookupTable();
921 
922     commitString(preedit_.segmentString(0));
923     if (*config().general->learnOnManualCommit)
924         preedit_.commit(0);
925     else
926         preedit_.clear(0);
927 
928     setPreedition();
929 
930     return true;
931 }
932 
action_commit_selected_segment()933 bool AnthyState::action_commit_selected_segment() {
934     if (!preedit_.isConverting()) {
935         if (preedit_.isPreediting()) {
936             return action_commit(*config().general->learnOnManualCommit);
937         } else {
938             return false;
939         }
940     }
941 
942     unsetLookupTable();
943 
944     for (int i = 0; i <= preedit_.selectedSegment(); i++)
945         commitString(preedit_.segmentString(i));
946     if (*config().general->learnOnManualCommit)
947         preedit_.commit(preedit_.selectedSegment());
948     else
949         preedit_.clear(preedit_.selectedSegment());
950 
951     setPreedition();
952 
953     return true;
954 }
955 
action_commit_first_segment_reverse_preference()956 bool AnthyState::action_commit_first_segment_reverse_preference() {
957     if (!preedit_.isConverting()) {
958         if (preedit_.isPreediting()) {
959             return action_commit(!*config().general->learnOnManualCommit);
960         } else {
961             return false;
962         }
963     }
964 
965     unsetLookupTable();
966 
967     commitString(preedit_.segmentString(0));
968     if (!*config().general->learnOnManualCommit)
969         preedit_.commit(0);
970     else
971         preedit_.clear(0);
972 
973     setPreedition();
974 
975     return true;
976 }
977 
action_commit_selected_segment_reverse_preference()978 bool AnthyState::action_commit_selected_segment_reverse_preference() {
979     if (!preedit_.isConverting()) {
980         if (preedit_.isPreediting()) {
981             return action_commit(!*config().general->learnOnManualCommit);
982         } else {
983             return false;
984         }
985     }
986 
987     unsetLookupTable();
988 
989     for (int i = 0; i <= preedit_.selectedSegment(); i++)
990         commitString(preedit_.segmentString(i));
991     if (!*config().general->learnOnManualCommit)
992         preedit_.commit(preedit_.selectedSegment());
993     else
994         preedit_.clear(preedit_.selectedSegment());
995 
996     setPreedition();
997 
998     return true;
999 }
1000 
action_select_next_candidate()1001 bool AnthyState::action_select_next_candidate() {
1002     if (!preedit_.isConverting())
1003         return false;
1004 
1005     // if (!is_selecting_candidates ())
1006     int end = setLookupTable();
1007 
1008     if (cursorPos_ >= end - 1)
1009         cursorPos_ = 0;
1010     else
1011         cursorPos_++;
1012     nConvKeyPressed_++;
1013 
1014     selectCandidateNoDirect(cursorPos_);
1015     return true;
1016 }
1017 
action_select_prev_candidate()1018 bool AnthyState::action_select_prev_candidate() {
1019     if (!preedit_.isConverting())
1020         return false;
1021     // if (!is_selecting_candidates ())
1022     int end = setLookupTable();
1023 
1024     if (end < 0)
1025         end = 0;
1026     if (cursorPos_ == 0)
1027         cursorPos_ = end - 1;
1028     else
1029         cursorPos_--;
1030     nConvKeyPressed_++;
1031 
1032     if (auto candList = ic_->inputPanel().candidateList()) {
1033         if (candList->size() > cursorPos_ && cursorPos_ >= 0) {
1034             auto commonCandList =
1035                 std::static_pointer_cast<fcitx::CommonCandidateList>(candList);
1036             commonCandList->setGlobalCursorIndex(cursorPos_);
1037             commonCandList->setPage(cursorPos_ / *config().general->pageSize);
1038         }
1039     }
1040 
1041     selectCandidateNoDirect(cursorPos_);
1042 
1043     return true;
1044 }
1045 
action_select_first_candidate()1046 bool AnthyState::action_select_first_candidate() {
1047     if (!preedit_.isConverting())
1048         return false;
1049     if (!isSelectingCandidates())
1050         return false;
1051 
1052     cursorPos_ = 0;
1053     nConvKeyPressed_++;
1054     selectCandidateNoDirect(cursorPos_);
1055     return true;
1056 }
1057 
action_select_last_candidate()1058 bool AnthyState::action_select_last_candidate() {
1059     if (!preedit_.isConverting())
1060         return false;
1061     if (!isSelectingCandidates())
1062         return false;
1063 
1064     int end = ic_->inputPanel().candidateList()->toBulk()->totalSize() - 1;
1065     if (end < 0)
1066         end = 0;
1067     cursorPos_ = end;
1068     nConvKeyPressed_++;
1069     selectCandidateNoDirect(cursorPos_);
1070     return true;
1071 }
1072 
action_candidates_page_up()1073 bool AnthyState::action_candidates_page_up() {
1074     if (!preedit_.isConverting())
1075         return false;
1076     if (!isSelectingCandidates())
1077         return false;
1078     if (!lookupTableVisible_)
1079         return false;
1080 
1081     if (cursorPos_ - *config().general->pageSize >= 0) {
1082         cursorPos_ -= *config().general->pageSize;
1083         selectCandidateNoDirect(cursorPos_);
1084     }
1085 
1086     return true;
1087 }
1088 
action_candidates_page_down()1089 bool AnthyState::action_candidates_page_down() {
1090     if (!preedit_.isConverting())
1091         return false;
1092     if (!isSelectingCandidates())
1093         return false;
1094     if (!lookupTableVisible_)
1095         return false;
1096 
1097     int end = ic_->inputPanel().candidateList()->toBulk()->totalSize();
1098 
1099     if (cursorPos_ + *config().general->pageSize < end) {
1100         cursorPos_ += *config().general->pageSize;
1101         selectCandidateNoDirect(cursorPos_);
1102     }
1103 
1104     return true;
1105 }
1106 
actionSelectCandidate(unsigned int i)1107 bool AnthyState::actionSelectCandidate(unsigned int i) {
1108     // FIXME! m_lookup_table_visible should be set as true also on predicting
1109     if (!lookupTableVisible_ && !preedit_.isPredicting())
1110         return false;
1111 
1112     if (preedit_.isPredicting() && !preedit_.isConverting() &&
1113         *config().general->useDirectKeyOnPredict) {
1114         ic_->inputPanel().setCandidateList(preedit_.candidates());
1115         selectCandidate(i);
1116         return true;
1117     } else if (preedit_.isConverting() && isSelectingCandidates()) {
1118         selectCandidate(i);
1119         return true;
1120     }
1121 
1122     return false;
1123 }
1124 
1125 template <typename T>
cycle_enum(T v,T size)1126 T cycle_enum(T v, T size) {
1127     return static_cast<T>((static_cast<int>(v) + 1) % static_cast<int>(size));
1128 }
1129 
action_circle_input_mode()1130 bool AnthyState::action_circle_input_mode() {
1131     InputMode mode = inputMode();
1132 
1133     mode = cycle_enum(mode, InputMode::LAST);
1134 
1135     setInputMode(mode);
1136     saveConfig();
1137 
1138     return true;
1139 }
1140 
action_circle_typing_method()1141 bool AnthyState::action_circle_typing_method() {
1142     TypingMethod method;
1143 
1144     method = typingMethod();
1145     method = cycle_enum(method, TypingMethod::NICOLA);
1146 
1147     setTypingMethod(method);
1148     saveConfig();
1149 
1150     return true;
1151 }
1152 
action_circle_kana_mode()1153 bool AnthyState::action_circle_kana_mode() {
1154     InputMode mode;
1155 
1156     if (inputMode() == InputMode::LATIN ||
1157         inputMode() == InputMode::WIDE_LATIN) {
1158         mode = InputMode::HIRAGANA;
1159     } else {
1160         switch (inputMode()) {
1161         case InputMode::HIRAGANA:
1162             mode = InputMode::KATAKANA;
1163             break;
1164         case InputMode::KATAKANA:
1165             mode = InputMode::HALF_KATAKANA;
1166             break;
1167         case InputMode::HALF_KATAKANA:
1168         default:
1169             mode = InputMode::HIRAGANA;
1170             break;
1171         }
1172     }
1173 
1174     setInputMode(mode);
1175     saveConfig();
1176 
1177     return true;
1178 }
1179 
action_circle_latin_hiragana_mode()1180 bool AnthyState::action_circle_latin_hiragana_mode() {
1181     InputMode mode = inputMode();
1182 
1183     if (mode == InputMode::LATIN) {
1184         mode = InputMode::HIRAGANA;
1185     } else if (mode == InputMode::HIRAGANA) {
1186         mode = InputMode::LATIN;
1187     }
1188 
1189     setInputMode(mode);
1190     saveConfig();
1191 
1192     return true;
1193 }
1194 
action_latin_mode()1195 bool AnthyState::action_latin_mode() {
1196     setInputMode(InputMode::LATIN);
1197     saveConfig();
1198     return true;
1199 }
1200 
action_wide_latin_mode()1201 bool AnthyState::action_wide_latin_mode() {
1202     setInputMode(InputMode::WIDE_LATIN);
1203     saveConfig();
1204     return true;
1205 }
1206 
action_hiragana_mode()1207 bool AnthyState::action_hiragana_mode() {
1208     setInputMode(InputMode::HIRAGANA);
1209     saveConfig();
1210     return true;
1211 }
1212 
action_katakana_mode()1213 bool AnthyState::action_katakana_mode() {
1214     setInputMode(InputMode::KATAKANA);
1215     saveConfig();
1216     return true;
1217 }
1218 
action_half_katakana_mode()1219 bool AnthyState::action_half_katakana_mode() {
1220     setInputMode(InputMode::HALF_KATAKANA);
1221     saveConfig();
1222     return true;
1223 }
1224 
action_cancel_pseudo_ascii_mode()1225 bool AnthyState::action_cancel_pseudo_ascii_mode() {
1226     if (!preedit_.isPreediting())
1227         return false;
1228     if (!preedit_.isPseudoAsciiMode())
1229         return false;
1230 
1231     preedit_.resetPseudoAsciiMode();
1232 
1233     return true;
1234 }
1235 
convertKana(CandidateType type)1236 bool AnthyState::convertKana(CandidateType type) {
1237     if (!preedit_.isPreediting())
1238         return false;
1239 
1240     if (preedit_.isReconverting())
1241         return false;
1242 
1243     unsetLookupTable();
1244 
1245     if (preedit_.isConverting()) {
1246         int idx = preedit_.selectedSegment();
1247         if (idx < 0) {
1248             action_revert();
1249             preedit_.finish();
1250             preedit_.convert(type, true);
1251         } else {
1252             preedit_.selectCandidate(type);
1253         }
1254     } else {
1255         preedit_.finish();
1256         preedit_.convert(type, true);
1257     }
1258 
1259     setPreedition();
1260 
1261     return true;
1262 }
1263 
action_convert_to_hiragana()1264 bool AnthyState::action_convert_to_hiragana() {
1265     return convertKana(FCITX_ANTHY_CANDIDATE_HIRAGANA);
1266 }
1267 
action_convert_to_katakana()1268 bool AnthyState::action_convert_to_katakana() {
1269     return convertKana(FCITX_ANTHY_CANDIDATE_KATAKANA);
1270 }
1271 
action_convert_to_half()1272 bool AnthyState::action_convert_to_half() {
1273     return convertKana(FCITX_ANTHY_CANDIDATE_HALF);
1274 }
1275 
action_convert_to_half_katakana()1276 bool AnthyState::action_convert_to_half_katakana() {
1277     return convertKana(FCITX_ANTHY_CANDIDATE_HALF_KATAKANA);
1278 }
1279 
action_convert_to_latin()1280 bool AnthyState::action_convert_to_latin() {
1281     return convertKana(FCITX_ANTHY_CANDIDATE_LATIN);
1282 }
1283 
action_convert_to_wide_latin()1284 bool AnthyState::action_convert_to_wide_latin() {
1285     return convertKana(FCITX_ANTHY_CANDIDATE_WIDE_LATIN);
1286 }
1287 
action_convert_char_type_forward()1288 bool AnthyState::action_convert_char_type_forward() {
1289     if (!preedit_.isPreediting())
1290         return false;
1291 
1292     unsetLookupTable();
1293 
1294     if (preedit_.isConverting()) {
1295         int idx = preedit_.selectedSegment();
1296         if (idx < 0) {
1297             action_revert();
1298             preedit_.finish();
1299             preedit_.convert(FCITX_ANTHY_CANDIDATE_HIRAGANA, true);
1300         } else {
1301             int cand = preedit_.selectedCandidate();
1302             switch (cand) {
1303             case FCITX_ANTHY_CANDIDATE_HIRAGANA:
1304                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_KATAKANA);
1305                 break;
1306             case FCITX_ANTHY_CANDIDATE_KATAKANA:
1307                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HALF_KATAKANA);
1308                 break;
1309             case FCITX_ANTHY_CANDIDATE_HALF_KATAKANA:
1310                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_WIDE_LATIN);
1311                 break;
1312             case FCITX_ANTHY_CANDIDATE_WIDE_LATIN:
1313                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_LATIN);
1314                 break;
1315             case FCITX_ANTHY_CANDIDATE_LATIN:
1316                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HIRAGANA);
1317                 break;
1318             default:
1319                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HIRAGANA);
1320                 break;
1321             }
1322         }
1323     } else {
1324         preedit_.finish();
1325         preedit_.convert(FCITX_ANTHY_CANDIDATE_HIRAGANA, true);
1326     }
1327 
1328     setPreedition();
1329 
1330     return true;
1331 }
1332 
action_convert_char_type_backward()1333 bool AnthyState::action_convert_char_type_backward() {
1334     if (!preedit_.isPreediting())
1335         return false;
1336 
1337     unsetLookupTable();
1338 
1339     if (preedit_.isConverting()) {
1340         int idx = preedit_.selectedSegment();
1341         if (idx < 0) {
1342             action_revert();
1343             preedit_.finish();
1344             preedit_.convert(FCITX_ANTHY_CANDIDATE_HIRAGANA, true);
1345         } else {
1346             int cand = preedit_.selectedCandidate();
1347             switch (cand) {
1348             case FCITX_ANTHY_CANDIDATE_HIRAGANA:
1349                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_LATIN);
1350                 break;
1351             case FCITX_ANTHY_CANDIDATE_KATAKANA:
1352                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HIRAGANA);
1353                 break;
1354             case FCITX_ANTHY_CANDIDATE_HALF_KATAKANA:
1355                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_KATAKANA);
1356                 break;
1357             case FCITX_ANTHY_CANDIDATE_WIDE_LATIN:
1358                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HALF_KATAKANA);
1359                 break;
1360             case FCITX_ANTHY_CANDIDATE_LATIN:
1361                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_WIDE_LATIN);
1362                 break;
1363             default:
1364                 preedit_.selectCandidate(FCITX_ANTHY_CANDIDATE_HIRAGANA);
1365                 break;
1366             }
1367         }
1368     } else {
1369         preedit_.finish();
1370         preedit_.convert(FCITX_ANTHY_CANDIDATE_HIRAGANA, true);
1371     }
1372 
1373     setPreedition();
1374 
1375     return true;
1376 }
1377 
action_reconvert()1378 bool AnthyState::action_reconvert() {
1379     if (preedit_.isPreediting())
1380         return false;
1381 
1382     if (!ic_->capabilityFlags().test(fcitx::CapabilityFlag::SurroundingText)) {
1383         return true;
1384     }
1385     if (!ic_->surroundingText().isValid()) {
1386         return true;
1387     }
1388 
1389     const std::string surrounding_text(ic_->surroundingText().text());
1390     uint cursor_pos = ic_->surroundingText().cursor();
1391     uint anchor_pos = ic_->surroundingText().anchor();
1392     int32_t relative_selected_length = 0;
1393 
1394     if (cursor_pos == anchor_pos) {
1395         if (engine_->clipboard()) {
1396             auto primary_text =
1397                 engine_->clipboard()->call<fcitx::IClipboard::primary>(ic_);
1398             uint new_anchor_pos = 0;
1399             if (util::surrounding_get_anchor_pos_from_selection(
1400                     surrounding_text, primary_text, cursor_pos,
1401                     &new_anchor_pos)) {
1402                 anchor_pos = new_anchor_pos;
1403             } else {
1404                 return true;
1405             }
1406         } else {
1407             // There is no selection text.
1408             return true;
1409         }
1410     }
1411 
1412     if (!util::surrounding_get_safe_delta(cursor_pos, anchor_pos,
1413                                           &relative_selected_length)) {
1414         return true;
1415     }
1416 
1417     const uint32_t selection_start = std::min(cursor_pos, anchor_pos);
1418     const uint32_t selection_length = abs(relative_selected_length);
1419     std::string text = util::utf8_string_substr(
1420         surrounding_text, selection_start, selection_length);
1421 
1422     ic_->surroundingText().deleteText(
1423         cursor_pos > anchor_pos ? -relative_selected_length : 0,
1424         selection_length);
1425 
1426     preedit_.convert(text);
1427     setPreedition();
1428     setLookupTable();
1429 
1430     return true;
1431 }
1432 
action_add_word()1433 bool AnthyState::action_add_word() {
1434     util::launch_program(*config().command->addWordCommand);
1435 
1436     return true;
1437 }
1438 
action_launch_dict_admin_tool()1439 bool AnthyState::action_launch_dict_admin_tool() {
1440     util::launch_program(*config().command->dictAdminCommand);
1441 
1442     return true;
1443 }
1444 
typingMethod()1445 TypingMethod AnthyState::typingMethod() { return preedit_.typingMethod(); }
1446 
inputMode()1447 InputMode AnthyState::inputMode() { return preedit_.inputMode(); }
1448 
isSingleSegment()1449 bool AnthyState::isSingleSegment() {
1450     return (conversionMode() == ConversionMode::SINGLE_SEGMENT ||
1451             conversionMode() == ConversionMode::SINGLE_SEGMENT_IMMEDIATE);
1452 }
1453 
isRealtimeConversion()1454 bool AnthyState::isRealtimeConversion() {
1455     return (conversionMode() == ConversionMode::MULTI_SEGMENT_IMMEDIATE ||
1456             conversionMode() == ConversionMode::SINGLE_SEGMENT_IMMEDIATE);
1457 }
1458 
pseudoAsciiMode()1459 int AnthyState::pseudoAsciiMode() {
1460     int retval = 0;
1461     TypingMethod m = typingMethod();
1462 
1463     if (m == TypingMethod::ROMAJI) {
1464         if (*config().general->romajiPseudoAsciiMode)
1465             retval |= FCITX_ANTHY_PSEUDO_ASCII_TRIGGERED_CAPITALIZED;
1466     }
1467 
1468     return retval;
1469 }
1470 
commitString(const std::string & str)1471 void AnthyState::commitString(const std::string &str) {
1472     ic_->commitString(str);
1473 }
1474 
1475 #define APPEND_ACTION(key, func)                                               \
1476     {                                                                          \
1477         const fcitx::KeyList *hk;                                              \
1478         std::string name = #key;                                               \
1479         if (loaded) {                                                          \
1480             std::string str = (ACTION_CONFIG_##key##_KEY);                     \
1481             std::string keystr;                                                \
1482             style.getString(keystr, "KeyBindings", str);                       \
1483             key_profile().hk_##key = fcitx::Key::keyListFromString(keystr);    \
1484             hk = &key_profile().hk_##key;                                      \
1485         } else                                                                 \
1486             hk = &*config().m_key->hk_##key;                                   \
1487         PMF f;                                                                 \
1488         f = &AnthyState::func;                                                 \
1489         actions_[name] = Action(name, *hk, f);                                 \
1490     }
1491 
configure()1492 void AnthyState::configure() {
1493     auto keyProfile = engine_->keyProfile();
1494 
1495     // clear old actions
1496     actions_.clear();
1497 #define FOREACH_ACTION(KEY, func)                                              \
1498     {                                                                          \
1499         std::string name = #KEY;                                               \
1500         const fcitx::KeyList *hk;                                              \
1501         if (keyProfile) {                                                      \
1502             hk = &keyProfile->hk_##KEY;                                        \
1503         } else                                                                 \
1504             hk = &*config().key->hk_##KEY;                                     \
1505         PMF f;                                                                 \
1506         f = &AnthyState::func;                                                 \
1507         actions_.emplace_back(name, *hk, f);                                   \
1508     }
1509 #include "action_defs.h"
1510 #undef FOREACH_ACTION
1511 
1512     // set romaji settings
1513     preedit_.setSymbolHalf(*config().general->romajiHalfSymbol);
1514     preedit_.setNumberHalf(*config().general->romajiHalfNumber);
1515 
1516     // set input mode
1517     preedit_.setInputMode(*config().general->inputMode);
1518 
1519     // set typing method and pseudo ASCII mode
1520     preedit_.setTypingMethod(*config().general->typingMethod);
1521     preedit_.setPseudoAsciiMode(pseudoAsciiMode());
1522 
1523     // set period style
1524     setPeriodCommaStyle(*config().general->periodCommaStyle);
1525 
1526     // set symbol style
1527     setSymbolStyle(*config().general->symbolStyle);
1528 
1529     // setup toolbar
1530     installProperties();
1531 }
1532 
updateAuxString(const std::string & str)1533 void AnthyState::updateAuxString(const std::string &str) {
1534     fcitx::Text aux;
1535     aux.append(str);
1536     ic_->inputPanel().setAuxUp(std::move(aux));
1537     uiUpdate_ = true;
1538 }
1539 
resetCursor(int cursor)1540 void AnthyState::resetCursor(int cursor) {
1541     if (cursor >= 0)
1542         cursorPos_ = cursor;
1543     else
1544         cursor = 0;
1545 }
1546 
autoCommit(fcitx::InputContextEvent & event)1547 void AnthyState::autoCommit(fcitx::InputContextEvent &event) {
1548     if (event.type() == fcitx::EventType::InputContextFocusOut) {
1549         action_commit(*config().general->learnOnAutoCommit, false);
1550     } else if (event.type() ==
1551                fcitx::EventType::InputContextSwitchInputMethod) {
1552         action_commit(*config().general->learnOnAutoCommit);
1553     }
1554     reset();
1555 }
1556 
1557 /*
1558 vi:ts=4:nowrap:ai:expandtab
1559 */
1560