1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_CONFIG_OPTION_H_
8 #define _FCITX_CONFIG_OPTION_H_
9 
10 #include "fcitxconfig_export.h"
11 
12 #include <functional>
13 #include <limits>
14 #include <string>
15 #include <type_traits>
16 #include <fcitx-config/marshallfunction.h>
17 #include <fcitx-config/option_details.h>
18 #include <fcitx-config/optiontypename.h>
19 #include <fcitx-config/rawconfig.h>
20 
21 namespace fcitx {
22 
23 /// An option that launches external tool.
24 class FCITXCONFIG_EXPORT ExternalOption : public OptionBase {
25 public:
26     ExternalOption(Configuration *parent, std::string path,
27                    std::string description, std::string uri);
28 
29     std::string typeString() const override;
30     void reset() override;
31     bool isDefault() const override;
32 
33     void marshall(RawConfig &config) const override;
34     bool unmarshall(const RawConfig &config, bool partial) override;
35     std::unique_ptr<Configuration> subConfigSkeleton() const override;
36 
37     bool equalTo(const OptionBase &other) const override;
38     void copyFrom(const OptionBase &other) override;
39 
40     bool skipDescription() const override;
41     bool skipSave() const override;
42     void dumpDescription(RawConfig &config) const override;
43 
44 private:
45     std::string externalUri_;
46 };
47 
48 /// An option that launches external tool.
49 class FCITXCONFIG_EXPORT SubConfigOption : public ExternalOption {
50 public:
51     using ExternalOption::ExternalOption;
52     void dumpDescription(RawConfig &config) const override;
53 };
54 
55 /// Default Constrain with no actual constrain.
56 template <typename T>
57 struct NoConstrain {
58     using Type = T;
checkNoConstrain59     bool check(const T &) const { return true; }
dumpDescriptionNoConstrain60     void dumpDescription(RawConfig &) const {}
61 };
62 
63 /// Default Annotation with no options.
64 struct NoAnnotation {
skipDescriptionNoAnnotation65     bool skipDescription() { return false; }
skipSaveNoAnnotation66     bool skipSave() { return false; }
dumpDescriptionNoAnnotation67     void dumpDescription(RawConfig &) const {}
68 };
69 
70 /// Annotation to display a tooltip in configtool.
71 struct ToolTipAnnotation {
ToolTipAnnotationToolTipAnnotation72     ToolTipAnnotation(std::string tooltip) : tooltip_(std::move(tooltip)) {}
73 
skipDescriptionToolTipAnnotation74     bool skipDescription() { return false; }
skipSaveToolTipAnnotation75     bool skipSave() { return false; }
dumpDescriptionToolTipAnnotation76     void dumpDescription(RawConfig &config) const {
77         config.setValueByPath("Tooltip", tooltip_);
78     }
79 
80 private:
81     std::string tooltip_;
82 };
83 
84 /// For a list of sub config, the field that should be used for display.
85 struct ListDisplayOptionAnnotation {
ListDisplayOptionAnnotationListDisplayOptionAnnotation86     ListDisplayOptionAnnotation(std::string option)
87         : option_(std::move(option)) {}
88 
skipDescriptionListDisplayOptionAnnotation89     bool skipDescription() { return false; }
skipSaveListDisplayOptionAnnotation90     bool skipSave() { return false; }
dumpDescriptionListDisplayOptionAnnotation91     void dumpDescription(RawConfig &config) const {
92         config.setValueByPath("ListDisplayOption", option_);
93     }
94 
95 private:
96     std::string option_;
97 };
98 
99 /**
100  * Annotation to be used against String type to indicate this is a Font.
101  */
102 struct FontAnnotation {
skipDescriptionFontAnnotation103     bool skipDescription() { return false; }
skipSaveFontAnnotation104     bool skipSave() { return false; }
dumpDescriptionFontAnnotation105     void dumpDescription(RawConfig &config) {
106         config.setValueByPath("Font", "True");
107     }
108 };
109 
110 /**
111  * Annotation to be used against String type, for those type of string
112  * that should shown as a combobox, but the value is run time based.
113  * User of this annotation should take a sub class of it and set
114  * Enum/n, and EnumI18n/n correspondingly.
115  */
116 struct EnumAnnotation {
skipDescriptionEnumAnnotation117     bool skipDescription() { return false; }
skipSaveEnumAnnotation118     bool skipSave() { return false; }
dumpDescriptionEnumAnnotation119     void dumpDescription(RawConfig &config) const {
120         config.setValueByPath("IsEnum", "True");
121     }
122 };
123 
124 /**
125  * Option that will not shown in UI.
126  *
127  * You may want to use HiddenOption instead.
128  *
129  * @see HiddenOption
130  */
131 struct HideInDescription {
skipDescriptionHideInDescription132     bool skipDescription() { return true; }
skipSaveHideInDescription133     bool skipSave() { return false; }
dumpDescriptionHideInDescription134     void dumpDescription(RawConfig &) const {}
135 };
136 
137 /**
138  * List Constrain that applies the constrain to all element.
139  */
140 template <typename SubConstrain>
141 struct ListConstrain {
sub_ListConstrain142     ListConstrain(SubConstrain sub = SubConstrain()) : sub_(std::move(sub)) {}
143 
144     using ElementType = typename SubConstrain::Type;
145     using Type = std::vector<ElementType>;
checkListConstrain146     bool check(const Type &value) {
147         return std::all_of(
148             value.begin(), value.end(),
149             [this](const ElementType &ele) { return sub_.check(ele); });
150     }
151 
dumpDescriptionListConstrain152     void dumpDescription(RawConfig &config) const {
153         sub_.dumpDescription(*config.get("ListConstrain", true));
154     }
155 
156 private:
157     SubConstrain sub_;
158 };
159 
160 /// Integer type constrain with a lower and a upper bound.
161 class IntConstrain {
162 public:
163     using Type = int;
164     IntConstrain(int min = std::numeric_limits<int>::min(),
165                  int max = std::numeric_limits<int>::max())
min_(min)166         : min_(min), max_(max) {}
check(int value)167     bool check(int value) const { return value >= min_ && value <= max_; }
dumpDescription(RawConfig & config)168     void dumpDescription(RawConfig &config) const {
169         if (min_ != std::numeric_limits<int>::min()) {
170             marshallOption(config["IntMin"], min_);
171         }
172         if (max_ != std::numeric_limits<int>::max()) {
173             marshallOption(config["IntMax"], max_);
174         }
175     }
176 
177 private:
178     int min_;
179     int max_;
180 };
181 
182 /// Key option constrain flag.
183 enum class KeyConstrainFlag {
184     /// The key can be modifier only, like Control_L.
185     AllowModifierOnly = (1 << 0),
186     /// The key can be modifier less (Key that usually produce character).
187     AllowModifierLess = (1 << 1),
188 };
189 
190 using KeyConstrainFlags = Flags<KeyConstrainFlag>;
191 
192 /// Key option constrain.
193 class KeyConstrain {
194 public:
195     using Type = Key;
KeyConstrain(KeyConstrainFlags flags)196     KeyConstrain(KeyConstrainFlags flags) : flags_(flags) {}
197 
check(const Key & key)198     bool check(const Key &key) const {
199         if (!flags_.test(KeyConstrainFlag::AllowModifierLess) &&
200             key.states() == 0) {
201             return false;
202         }
203 
204         if (!flags_.test(KeyConstrainFlag::AllowModifierOnly) &&
205             key.isModifier()) {
206             return false;
207         }
208 
209         return true;
210     }
211 
dumpDescription(RawConfig & config)212     void dumpDescription(RawConfig &config) const {
213         if (flags_.test(KeyConstrainFlag::AllowModifierLess)) {
214             config["AllowModifierLess"] = "True";
215         }
216         if (flags_.test(KeyConstrainFlag::AllowModifierOnly)) {
217             config["AllowModifierOnly"] = "True";
218         }
219     }
220 
221 private:
222     KeyConstrainFlags flags_;
223 };
224 
225 /// Default marshaller that write the config RawConfig.
226 template <typename T>
227 struct DefaultMarshaller {
marshallDefaultMarshaller228     virtual void marshall(RawConfig &config, const T &value) const {
229         return marshallOption(config, value);
230     }
unmarshallDefaultMarshaller231     virtual bool unmarshall(T &value, const RawConfig &config,
232                             bool partial) const {
233         return unmarshallOption(value, config, partial);
234     }
235 };
236 
237 /// A helper class provide writing ability to option value.
238 template <typename OptionType>
239 class MutableOption {
240 public:
241     using value_type = typename OptionType::value_type;
242 
243     MutableOption(OptionType *option = nullptr)
option_(option)244         : option_(option), value_(option ? option->value() : value_type()) {}
245 
~MutableOption()246     ~MutableOption() {
247         if (option_) {
248             option_->setValue(std::move(value_));
249         }
250     }
251 
MutableOption(MutableOption && other)252     MutableOption(MutableOption &&other) noexcept
253         : option_(other.option_), value_(std::move(other.value_)) {
254         other.option_ = nullptr;
255     }
256 
257     MutableOption &operator=(MutableOption &&other) noexcept {
258         option_ = other.option_;
259         value_ = std::move(other.value_);
260         other.option_ = nullptr;
261     }
262 
263     value_type &operator*() { return value_; }
264     value_type *operator->() { return &value_; }
265 
266 private:
267     OptionType *option_;
268     value_type value_;
269 };
270 
271 /**
272  * Represent a Configuration option.
273  *
274  */
275 template <typename T, typename Constrain = NoConstrain<T>,
276           typename Marshaller = DefaultMarshaller<T>,
277           typename Annotation = NoAnnotation>
278 class Option : public OptionBaseV2 {
279 public:
280     using value_type = T;
281     using constrain_type = Constrain;
282 
283     Option(Configuration *parent, std::string path, std::string description,
284            const T &defaultValue = T(), Constrain constrain = Constrain(),
285            Marshaller marshaller = Marshaller(),
286            Annotation annotation = Annotation())
OptionBaseV2(parent,std::move (path),std::move (description))287         : OptionBaseV2(parent, std::move(path), std::move(description)),
288           defaultValue_(defaultValue), value_(defaultValue),
289           marshaller_(marshaller), constrain_(constrain),
290           annotation_(annotation) {
291         if (!constrain_.check(defaultValue_)) {
292             throw std::invalid_argument(
293                 "defaultValue doesn't satisfy constrain");
294         }
295     }
296 
typeString()297     std::string typeString() const override { return OptionTypeName<T>::get(); }
298 
dumpDescription(RawConfig & config)299     void dumpDescription(RawConfig &config) const override {
300         OptionBase::dumpDescription(config);
301         if constexpr (not std::is_base_of_v<Configuration, T>) {
302             marshaller_.marshall(config["DefaultValue"], defaultValue_);
303         }
304         constrain_.dumpDescription(config);
305         annotation_.dumpDescription(config);
306         using ::fcitx::dumpDescriptionHelper;
307         dumpDescriptionHelper(
308             config, static_cast<typename RemoveVector<T>::type *>(nullptr));
309     }
310 
subConfigSkeleton()311     std::unique_ptr<Configuration> subConfigSkeleton() const override {
312         if constexpr (std::is_base_of_v<Configuration, T>) {
313             auto skeleton = std::make_unique<T>(defaultValue_);
314             skeleton->syncDefaultValueToCurrent();
315             return skeleton;
316         }
317         if constexpr (std::is_base_of_v<Configuration,
318                                         typename RemoveVector<T>::type>) {
319             return std::make_unique<typename RemoveVector<T>::type>();
320         }
321 
322         return nullptr;
323     }
324 
isDefault()325     bool isDefault() const override { return defaultValue_ == value_; }
reset()326     void reset() override { value_ = defaultValue_; }
327 
value()328     const T &value() const { return value_; }
329 
defaultValue()330     const T &defaultValue() const { return defaultValue_; }
331 
332     const T &operator*() const { return value(); }
333     const T *operator->() const { return &value_; }
334 
335     template <typename U>
setValue(U && value)336     bool setValue(U &&value) {
337         if (!constrain_.check(value)) {
338             return false;
339         }
340         value_ = std::forward<U>(value);
341         return true;
342     }
343 
344     template <typename Dummy = int,
345               std::enable_if_t<!std::is_same<Constrain, NoConstrain<T>>::value,
346                                Dummy> = 0>
mutableValue()347     MutableOption<Option> mutableValue() {
348         return {this};
349     }
350 
351     template <typename Dummy = int,
352               std::enable_if_t<std::is_same<Constrain, NoConstrain<T>>::value,
353                                Dummy> = 0>
mutableValue()354     T *mutableValue() {
355         return &value_;
356     }
357 
marshall(RawConfig & config)358     void marshall(RawConfig &config) const override {
359         return marshaller_.marshall(config, value_);
360     }
unmarshall(const RawConfig & config,bool partial)361     bool unmarshall(const RawConfig &config, bool partial) override {
362         T tempValue{};
363         if (partial) {
364             tempValue = value_;
365         }
366         if (!marshaller_.unmarshall(tempValue, config, partial)) {
367             return false;
368         }
369         return setValue(tempValue);
370     }
371 
equalTo(const OptionBase & other)372     bool equalTo(const OptionBase &other) const override {
373         auto otherP = static_cast<const Option *>(&other);
374         return value_ == otherP->value_;
375     }
376 
copyFrom(const OptionBase & other)377     void copyFrom(const OptionBase &other) override {
378         auto otherP = static_cast<const Option *>(&other);
379         value_ = otherP->value_;
380     }
381 
skipDescription()382     bool skipDescription() const override {
383         return annotation_.skipDescription();
384     }
385 
skipSave()386     bool skipSave() const override { return annotation_.skipSave(); }
387 
syncDefaultValueToCurrent()388     void syncDefaultValueToCurrent() override {
389         defaultValue_ = value_;
390         if constexpr (std::is_base_of_v<Configuration, T>) {
391             value_.syncDefaultValueToCurrent();
392             defaultValue_.syncDefaultValueToCurrent();
393         }
394     }
395 
annotation()396     auto &annotation() const { return annotation_; }
397 
398 private:
399     T defaultValue_;
400     T value_;
401     Marshaller marshaller_;
402     Constrain constrain_;
403     mutable Annotation annotation_;
404 };
405 
406 /// Shorthand if you want a option type with only custom annotation.
407 template <typename T, typename Annotation>
408 using OptionWithAnnotation =
409     Option<T, NoConstrain<T>, DefaultMarshaller<T>, Annotation>;
410 
411 /// Shorthand for KeyList option with constrain.
412 using KeyListOption = Option<KeyList, ListConstrain<KeyConstrain>,
413                              DefaultMarshaller<KeyList>, NoAnnotation>;
414 
415 /// Shorthand for create a key list constrain.
416 static inline ListConstrain<KeyConstrain>
417 KeyListConstrain(KeyConstrainFlags flags = KeyConstrainFlags()) {
418     return ListConstrain<KeyConstrain>(KeyConstrain(flags));
419 }
420 
421 /// Shorthand for option that will not show in UI.
422 template <typename T>
423 using HiddenOption = OptionWithAnnotation<T, HideInDescription>;
424 } // namespace fcitx
425 
426 #endif // _FCITX_CONFIG_OPTION_H_
427