1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _PINYIN_PINYIN_H_
8 #define _PINYIN_PINYIN_H_
9 
10 #include <fcitx-config/configuration.h>
11 #include <fcitx-config/iniparser.h>
12 #include <fcitx-utils/event.h>
13 #include <fcitx-utils/i18n.h>
14 #include <fcitx-utils/standardpath.h>
15 #include <fcitx/action.h>
16 #include <fcitx/addonfactory.h>
17 #include <fcitx/addonmanager.h>
18 #include <fcitx/inputcontextproperty.h>
19 #include <fcitx/inputmethodengine.h>
20 #include <fcitx/instance.h>
21 #include <libime/core/prediction.h>
22 #include <libime/pinyin/pinyincontext.h>
23 #include <libime/pinyin/pinyinime.h>
24 #include <memory>
25 #include <unordered_map>
26 #include <unordered_set>
27 
28 namespace fcitx {
29 
30 FCITX_CONFIG_ENUM(ShuangpinProfileEnum, Ziranma, MS, Ziguang, ABC,
31                   Zhongwenzhixing, PinyinJiajia, Xiaohe, Custom)
32 
33 FCITX_CONFIG_ENUM_I18N_ANNOTATION(ShuangpinProfileEnum, N_("Ziranma"), N_("MS"),
34                                   N_("Ziguang"), N_("ABC"),
35                                   N_("Zhongwenzhixing"), N_("PinyinJiajia"),
36                                   N_("Xiaohe"), N_("Custom"))
37 
38 FCITX_CONFIGURATION(
39     FuzzyConfig, Option<bool> ue{this, "VE_UE", _("ue -> ve"), true};
40     Option<bool> ng{this, "NG_GN", _("gn -> ng"), true};
41     Option<bool> inner{this, "Inner", _("Inner Segment (xian -> xi'an)"), true};
42     Option<bool> innerShort{this, "InnerShort",
43                             _("Inner Segment for Short Pinyin (qie -> qi'e)"),
44                             true};
45     Option<bool> partialFinal{this, "PartialFinal",
46                               _("Match partial finals (e -> en, eng, ei)"),
47                               true};
48     Option<bool> v{this, "V_U", _("u <-> v"), false};
49     Option<bool> an{this, "AN_ANG", _("an <-> ang"), false};
50     Option<bool> en{this, "EN_ENG", _("en <-> eng"), false};
51     Option<bool> ian{this, "IAN_IANG", _("ian <-> iang"), false};
52     Option<bool> in{this, "IN_ING", _("in <-> ing"), false};
53     Option<bool> ou{this, "U_OU", _("u <-> ou"), false};
54     Option<bool> uan{this, "UAN_UANG", _("uan <-> uang"), false};
55     Option<bool> c{this, "C_CH", _("c <-> ch"), false};
56     Option<bool> f{this, "F_H", _("f <-> h"), false};
57     Option<bool> l{this, "L_N", _("l <-> n"), false};
58     Option<bool> s{this, "S_SH", _("s <-> sh"), false};
59     Option<bool> z{this, "Z_ZH", _("z <-> zh"), false};)
60 
61 FCITX_CONFIGURATION(
62     PinyinEngineConfig,
63     OptionWithAnnotation<ShuangpinProfileEnum,
64                          ShuangpinProfileEnumI18NAnnotation>
65         shuangpinProfile{this, "ShuangpinProfile", _("Shuangpin Profile"),
66                          ShuangpinProfileEnum::Ziranma};
67     Option<bool> showShuangpinMode{this, "ShowShuangpinMode",
68                                    _("Show current shuangpin mode"), true};
69     Option<int, IntConstrain> pageSize{this, "PageSize", _("Page size"), 5,
70                                        IntConstrain(3, 10)};
71     Option<bool> spellEnabled{this, "SpellEnabled", _("Enable Spell"), true};
72     Option<bool> emojiEnabled{this, "EmojiEnabled", _("Enable Emoji"), true};
73     Option<bool> chaiziEnabled{this, "ChaiziEnabled", _("Enable Chaizi"), true};
74     Option<bool> cloudPinyinEnabled{this, "CloudPinyinEnabled",
75                                     _("Enable Cloud Pinyin"), false};
76     Option<int, IntConstrain> cloudPinyinIndex{this, "CloudPinyinIndex",
77                                                _("Cloud Pinyin Index"), 2,
78                                                IntConstrain(1, 10)};
79     Option<bool> showPreeditInApplication{this, "PreeditInApplication",
80                                           _("Show preedit within application"),
81                                           false};
82     Option<bool> preeditCursorPositionAtBeginning{
83         this, "PreeditCursorPositionAtBeginning",
84         _("Fix embedded preedit cursor at the beginning of the preedit"), true};
85     Option<bool> showActualPinyinInPreedit{
86         this, "PinyinInPreedit", _("Show complete pinyin in preedit"), false};
87     Option<bool> predictionEnabled{this, "Prediction", _("Enable Prediction"),
88                                    false};
89     Option<int, IntConstrain> predictionSize{
90         this, "PredictionSize", _("Prediction Size"), 10, IntConstrain(3, 20)};
91     KeyListOption forgetWord{this,
92                              "ForgetWord",
93                              _("Forget word"),
94                              {Key("Control+7")},
95                              KeyListConstrain()};
96     KeyListOption prevPage{
97         this,
98         "PrevPage",
99         _("Prev Page"),
100         {Key(FcitxKey_minus), Key(FcitxKey_Up), Key(FcitxKey_KP_Up)},
101         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
102     KeyListOption nextPage{
103         this,
104         "NextPage",
105         _("Next Page"),
106         {Key(FcitxKey_equal), Key(FcitxKey_Down), Key(FcitxKey_KP_Down)},
107         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
108     KeyListOption prevCandidate{
109         this,
110         "PrevCandidate",
111         _("Prev Candidate"),
112         {Key("Shift+Tab")},
113         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
114     KeyListOption nextCandidate{
115         this,
116         "NextCandidate",
117         _("Next Candidate"),
118         {Key("Tab")},
119         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
120     KeyListOption secondCandidate{
121         this,
122         "SecondCandidate",
123         _("Select 2nd Candidate"),
124         {},
125         KeyListConstrain({KeyConstrainFlag::AllowModifierLess,
126                           KeyConstrainFlag::AllowModifierOnly})};
127     KeyListOption thirdCandidate{
128         this,
129         "ThirdCandidate",
130         _("Select 3rd Candidate"),
131         {},
132         KeyListConstrain({KeyConstrainFlag::AllowModifierLess,
133                           KeyConstrainFlag::AllowModifierOnly})};
134     KeyListOption selectCharFromPhrase{
135         this,
136         "ChooseCharFromPhrase",
137         _("Choose Character from Phrase"),
138         {Key("["), Key("]")},
139         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
140     KeyListOption selectByStroke{
141         this,
142         "FilterByStroke",
143         _("Filter by stroke"),
144         {Key("grave")},
145         KeyListConstrain({KeyConstrainFlag::AllowModifierLess})};
146     Option<int, IntConstrain> nbest{this, "Number of sentence",
147                                     _("Number of Sentence"), 2,
148                                     IntConstrain(1, 3)};
149     Option<int, IntConstrain> longWordLimit{
150         this, "LongWordLengthLimit",
151         _("Prompt long word length when input length over (0 for disable)"), 4,
152         IntConstrain(0, 10)};
153     ExternalOption dictmanager{this, "DictManager", _("Dictionaries"),
154                                "fcitx://config/addon/pinyin/dictmanager"};
155     SubConfigOption punctuationMap{
156         this, "Punctuation", _("Punctuation"),
157         "fcitx://config/addon/punctuation/punctuationmap/zh_CN"};
158     SubConfigOption chttrans{
159         this, "Chttrans", _("Simplified and Traditional Chinese Translation"),
160         "fcitx://config/addon/chttrans"};
161     SubConfigOption cloudpinyin{this, "CloudPinyin", _("Cloud Pinyin"),
162                                 "fcitx://config/addon/cloudpinyin"};
163     Option<Key, KeyConstrain> quickphraseKey{
164         this,
165         "QuickPhraseKey",
166         _("Key to trigger quickphrase"),
167         Key{FcitxKey_semicolon},
168         {KeyConstrainFlag::AllowModifierLess}};
169     Option<bool> useVAsQuickphrase{this, "VAsQuickphrase",
170                                    _("Use V to trigger quickphrase"), true};
171     ExternalOption quickphrase{this, "QuickPhrase", _("Quick Phrase"),
172                                "fcitx://config/addon/quickphrase/editor"};
173     OptionWithAnnotation<std::vector<std::string>, ToolTipAnnotation>
174         quickphraseTrigger{this,
175                            "QuickPhrase trigger",
176                            _("Strings to trigger quick phrase"),
177                            {"www.", "ftp.", "http:", "mail.", "bbs.", "forum.",
178                             "https:", "ftp:", "telnet:", "mailto:"},
179                            {},
180                            {},
181                            {_("Enter a string from the list will make it enter "
182                               "quickphrase mode.")}};
183     Option<FuzzyConfig> fuzzyConfig{this, "Fuzzy", _("Fuzzy Pinyin Settings")};
184     HiddenOption<bool> firstRun{this, "FirstRun", "FirstRun", true};);
185 
186 class PinyinState;
187 class EventSourceTime;
188 class CandidateList;
189 
190 class PinyinEngine final : public InputMethodEngine {
191 public:
192     PinyinEngine(Instance *instance);
193     ~PinyinEngine();
instance()194     Instance *instance() { return instance_; }
195     void activate(const InputMethodEntry &entry,
196                   InputContextEvent &event) override;
197     void deactivate(const InputMethodEntry &entry,
198                     InputContextEvent &event) override;
199     void keyEvent(const InputMethodEntry &entry, KeyEvent &keyEvent) override;
200     void reloadConfig() override;
201     void reset(const InputMethodEntry &entry,
202                InputContextEvent &event) override;
203     void doReset(InputContext *inputContext);
204     void save() override;
factory()205     auto &factory() { return factory_; }
206     std::string subMode(const InputMethodEntry &entry,
207                         InputContext &inputContext) override;
208 
getConfig()209     const Configuration *getConfig() const override { return &config_; }
setConfig(const RawConfig & config)210     void setConfig(const RawConfig &config) override {
211         config_.load(config, true);
212         safeSaveAsIni(config_, "conf/pinyin.conf");
213         reloadConfig();
214     }
215     void setSubConfig(const std::string &path,
216                       const fcitx::RawConfig &) override;
217 
ime()218     libime::PinyinIME *ime() { return ime_.get(); }
219 
220     void initPredict(InputContext *inputContext);
221     void updatePredict(InputContext *inputContext);
222     std::unique_ptr<CandidateList>
223     predictCandidateList(const std::vector<std::string> &words);
224     void updateUI(InputContext *inputContext);
225 
226     void resetStroke(InputContext *inputContext);
227     void resetForgetCandidate(InputContext *inputContext);
228 
229 private:
230     void cloudPinyinSelected(InputContext *inputContext,
231                              const std::string &selected,
232                              const std::string &word);
233 
234     bool handleCloudpinyinTrigger(KeyEvent &event);
235     bool handle2nd3rdSelection(KeyEvent &event);
236     bool handleCandidateList(KeyEvent &event);
237     bool handleStrokeFilter(KeyEvent &event);
238     bool handleForgetCandidate(KeyEvent &event);
239     bool handlePunc(KeyEvent &event);
240 
241     void updateStroke(InputContext *inputContext);
242     void updateForgetCandidate(InputContext *inputContext);
243 
244     void updatePreedit(InputContext *inputContext) const;
245 
246 #ifdef FCITX_HAS_LUA
247     std::vector<std::string>
248     luaCandidateTrigger(InputContext *ic, const std::string &candidateString);
249 #endif
250     void loadExtraDict();
251     void loadDict(const StandardPathFile &file);
252 
253     Instance *instance_;
254     PinyinEngineConfig config_;
255     std::unique_ptr<libime::PinyinIME> ime_;
256     std::unordered_map<std::string, std::unordered_set<uint32_t>>
257         quickphraseTriggerDict_;
258     KeyList selectionKeys_;
259     FactoryFor<PinyinState> factory_;
260     SimpleAction predictionAction_;
261     libime::Prediction prediction_;
262     std::unique_ptr<EventSource> deferEvent_;
263     std::unique_ptr<HandlerTableEntry<EventHandler>> event_;
264 
265     FCITX_ADDON_DEPENDENCY_LOADER(quickphrase, instance_->addonManager());
266     FCITX_ADDON_DEPENDENCY_LOADER(cloudpinyin, instance_->addonManager());
267     FCITX_ADDON_DEPENDENCY_LOADER(fullwidth, instance_->addonManager());
268     FCITX_ADDON_DEPENDENCY_LOADER(chttrans, instance_->addonManager());
269     FCITX_ADDON_DEPENDENCY_LOADER(punctuation, instance_->addonManager());
270     FCITX_ADDON_DEPENDENCY_LOADER(notifications, instance_->addonManager());
271     FCITX_ADDON_DEPENDENCY_LOADER(pinyinhelper, instance_->addonManager());
272     FCITX_ADDON_DEPENDENCY_LOADER(spell, instance_->addonManager());
273     FCITX_ADDON_DEPENDENCY_LOADER(imeapi, instance_->addonManager());
274 };
275 
276 class PinyinEngineFactory : public AddonFactory {
277 public:
create(AddonManager * manager)278     AddonInstance *create(AddonManager *manager) override {
279         registerDomain("fcitx5-chinese-addons", FCITX_INSTALL_LOCALEDIR);
280         return new PinyinEngine(manager->instance());
281     }
282 };
283 } // namespace fcitx
284 
285 #endif // _PINYIN_PINYIN_H_
286