1 /*
2  * SPDX-FileCopyrightText: 2017~2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  *
6  */
7 
8 #include "optionwidget.h"
9 #include "config.h"
10 #include "configwidget.h"
11 #include "font.h"
12 #include "fontbutton.h"
13 #include "keylistwidget.h"
14 #include "listoptionwidget.h"
15 #include "logging.h"
16 #include "varianthelper.h"
17 #include <KColorButton>
18 #include <QCheckBox>
19 #include <QComboBox>
20 #include <QDialog>
21 #include <QDialogButtonBox>
22 #include <QFileInfo>
23 #include <QFormLayout>
24 #include <QLineEdit>
25 #include <QPointer>
26 #include <QProcess>
27 #include <QPushButton>
28 #include <QSpinBox>
29 #include <QToolButton>
30 #include <QVBoxLayout>
31 #include <fcitx-utils/color.h>
32 #include <fcitx-utils/i18n.h>
33 #include <fcitx-utils/standardpath.h>
34 #include <fcitxqtkeysequencewidget.h>
35 
36 namespace fcitx {
37 namespace kcm {
38 
39 namespace {
40 
41 class IntegerOptionWidget : public OptionWidget {
42     Q_OBJECT
43 public:
IntegerOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)44     IntegerOptionWidget(const FcitxQtConfigOption &option, const QString &path,
45                         QWidget *parent)
46         : OptionWidget(path, parent), spinBox_(new QSpinBox),
47           defaultValue_(option.defaultValue().variant().toString().toInt()) {
48         QVBoxLayout *layout = new QVBoxLayout;
49         layout->setMargin(0);
50 
51         spinBox_ = new QSpinBox;
52         spinBox_->setMaximum(INT_MAX);
53         spinBox_->setMinimum(INT_MIN);
54         if (option.properties().contains("IntMax")) {
55             auto max = option.properties().value("IntMax");
56             if (max.type() == QVariant::String) {
57                 spinBox_->setMaximum(max.toInt());
58             }
59         }
60         if (option.properties().contains("IntMin")) {
61             auto min = option.properties().value("IntMin");
62             if (min.type() == QVariant::String) {
63                 spinBox_->setMinimum(min.toInt());
64             }
65         }
66         connect(spinBox_, qOverload<int>(&QSpinBox::valueChanged), this,
67                 &OptionWidget::valueChanged);
68         layout->addWidget(spinBox_);
69         setLayout(layout);
70     }
71 
readValueFrom(const QVariantMap & map)72     void readValueFrom(const QVariantMap &map) override {
73         auto value = readString(map, path());
74         if (value.isNull()) {
75             spinBox_->setValue(defaultValue_);
76         }
77         spinBox_->setValue(value.toInt());
78     }
79 
writeValueTo(QVariantMap & map)80     void writeValueTo(QVariantMap &map) override {
81         writeVariant(map, path(), QString::number(spinBox_->value()));
82     }
83 
restoreToDefault()84     void restoreToDefault() override { spinBox_->setValue(defaultValue_); }
85 
86 private:
87     QSpinBox *spinBox_;
88     int defaultValue_;
89 };
90 
91 class StringOptionWidget : public OptionWidget {
92     Q_OBJECT
93 public:
StringOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)94     StringOptionWidget(const FcitxQtConfigOption &option, const QString &path,
95                        QWidget *parent)
96         : OptionWidget(path, parent), lineEdit_(new QLineEdit),
97           defaultValue_(option.defaultValue().variant().toString()) {
98         QVBoxLayout *layout = new QVBoxLayout;
99         layout->setMargin(0);
100 
101         lineEdit_ = new QLineEdit;
102         connect(lineEdit_, &QLineEdit::textChanged, this,
103                 &OptionWidget::valueChanged);
104         layout->addWidget(lineEdit_);
105         setLayout(layout);
106     }
107 
readValueFrom(const QVariantMap & map)108     void readValueFrom(const QVariantMap &map) override {
109         auto value = readString(map, path());
110         lineEdit_->setText(value);
111     }
112 
writeValueTo(QVariantMap & map)113     void writeValueTo(QVariantMap &map) override {
114         writeVariant(map, path(), lineEdit_->text());
115     }
116 
restoreToDefault()117     void restoreToDefault() override { lineEdit_->setText(defaultValue_); }
118 
119 private:
120     QLineEdit *lineEdit_;
121     QString defaultValue_;
122 };
123 
124 class FontOptionWidget : public OptionWidget {
125     Q_OBJECT
126 public:
FontOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)127     FontOptionWidget(const FcitxQtConfigOption &option, const QString &path,
128                      QWidget *parent)
129         : OptionWidget(path, parent), fontButton_(new FontButton),
130           defaultValue_(option.defaultValue().variant().toString()) {
131         QVBoxLayout *layout = new QVBoxLayout;
132         layout->setMargin(0);
133 
134         connect(fontButton_, &FontButton::fontChanged, this,
135                 &OptionWidget::valueChanged);
136         layout->addWidget(fontButton_);
137         setLayout(layout);
138     }
139 
readValueFrom(const QVariantMap & map)140     void readValueFrom(const QVariantMap &map) override {
141         auto value = readString(map, path());
142         fontButton_->setFont(parseFont(value));
143     }
144 
writeValueTo(QVariantMap & map)145     void writeValueTo(QVariantMap &map) override {
146         writeVariant(map, path(), fontButton_->fontName());
147     }
148 
restoreToDefault()149     void restoreToDefault() override {
150         fontButton_->setFont(parseFont(defaultValue_));
151     }
152 
153 private:
154     FontButton *fontButton_;
155     QString defaultValue_;
156 };
157 
158 class BooleanOptionWidget : public OptionWidget {
159     Q_OBJECT
160 public:
BooleanOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)161     BooleanOptionWidget(const FcitxQtConfigOption &option, const QString &path,
162                         QWidget *parent)
163         : OptionWidget(path, parent), checkBox_(new QCheckBox),
164           defaultValue_(option.defaultValue().variant().toString() == "True") {
165         QVBoxLayout *layout = new QVBoxLayout;
166         layout->setMargin(0);
167 
168         connect(checkBox_, &QCheckBox::clicked, this,
169                 &OptionWidget::valueChanged);
170         checkBox_->setText(option.description());
171         layout->addWidget(checkBox_);
172         setLayout(layout);
173     }
174 
readValueFrom(const QVariantMap & map)175     void readValueFrom(const QVariantMap &map) override {
176         checkBox_->setChecked(readBool(map, path()));
177     }
178 
writeValueTo(QVariantMap & map)179     void writeValueTo(QVariantMap &map) override {
180         QString value = checkBox_->isChecked() ? "True" : "False";
181         writeVariant(map, path(), value);
182     }
183 
restoreToDefault()184     void restoreToDefault() override { checkBox_->setChecked(defaultValue_); }
185 
186 private:
187     QCheckBox *checkBox_;
188     bool defaultValue_;
189 };
190 
191 class KeyListOptionWidget : public OptionWidget {
192     Q_OBJECT
193 public:
KeyListOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)194     KeyListOptionWidget(const FcitxQtConfigOption &option, const QString &path,
195                         QWidget *parent)
196         : OptionWidget(path, parent), keyListWidget_(new KeyListWidget) {
197         QVBoxLayout *layout = new QVBoxLayout;
198         layout->setMargin(0);
199 
200         keyListWidget_ = new KeyListWidget(this);
201 
202         keyListWidget_->setAllowModifierLess(
203             readString(option.properties(),
204                        "ListConstrain/AllowModifierLess") == "True");
205         keyListWidget_->setAllowModifierOnly(
206             readString(option.properties(),
207                        "ListConstrain/AllowModifierOnly") == "True");
208         connect(keyListWidget_, &KeyListWidget::keyChanged, this,
209                 &OptionWidget::valueChanged);
210         layout->addWidget(keyListWidget_);
211 
212         auto variant = option.defaultValue().variant();
213         QVariantMap map;
214         if (variant.canConvert<QDBusArgument>()) {
215             auto argument = qvariant_cast<QDBusArgument>(variant);
216             argument >> map;
217         }
218         defaultValue_ = readValue(map, "");
219         setLayout(layout);
220     }
221 
readValueFrom(const QVariantMap & map)222     void readValueFrom(const QVariantMap &map) override {
223         keyListWidget_->setKeys(readValue(map, path()));
224     }
225 
writeValueTo(QVariantMap & map)226     void writeValueTo(QVariantMap &map) override {
227         auto keys = keyListWidget_->keys();
228         int i = 0;
229         for (auto &key : keys) {
230             auto value = QString::fromUtf8(key.toString().data());
231             writeVariant(map, QString("%1/%2").arg(path()).arg(i), value);
232             i++;
233         }
234         if (keys.empty()) {
235             writeVariant(map, path(), QVariantMap());
236         }
237     }
238 
restoreToDefault()239     void restoreToDefault() override { keyListWidget_->setKeys(defaultValue_); }
240 
241 private:
readValue(const QVariantMap & map,const QString & path)242     QList<fcitx::Key> readValue(const QVariantMap &map, const QString &path) {
243         int i = 0;
244         QList<Key> keys;
245         while (true) {
246             auto value = readString(map, QString("%1%2%3")
247                                              .arg(path)
248                                              .arg(path.isEmpty() ? "" : "/")
249                                              .arg(i));
250             if (value.isNull()) {
251                 break;
252             }
253             keys << Key(value.toUtf8().constData());
254             i++;
255         }
256         return keys;
257     }
258 
259     KeyListWidget *keyListWidget_;
260     QList<fcitx::Key> defaultValue_;
261 };
262 
263 class KeyOptionWidget : public OptionWidget {
264     Q_OBJECT
265 public:
KeyOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)266     KeyOptionWidget(const FcitxQtConfigOption &option, const QString &path,
267                     QWidget *parent)
268         : OptionWidget(path, parent),
269           keyWidget_(new FcitxQtKeySequenceWidget(this)),
270           defaultValue_(
271               option.defaultValue().variant().toString().toUtf8().constData()) {
272         QVBoxLayout *layout = new QVBoxLayout;
273         layout->setMargin(0);
274 
275         keyWidget_->setModifierlessAllowed(
276             readBool(option.properties(), "AllowModifierLess"));
277         keyWidget_->setModifierOnlyAllowed(
278             readBool(option.properties(), "AllowModifierOnly"));
279 
280         connect(keyWidget_, &FcitxQtKeySequenceWidget::keySequenceChanged, this,
281                 &OptionWidget::valueChanged);
282         layout->addWidget(keyWidget_);
283         setLayout(layout);
284     }
285 
readValueFrom(const QVariantMap & map)286     void readValueFrom(const QVariantMap &map) override {
287         Key key;
288         auto value = readString(map, path());
289         key = Key(value.toUtf8().constData());
290         keyWidget_->setKeySequence({key});
291     }
292 
writeValueTo(QVariantMap & map)293     void writeValueTo(QVariantMap &map) override {
294         auto keys = keyWidget_->keySequence();
295         Key key;
296         if (keys.size()) {
297             key = keys[0];
298         }
299         auto value = QString::fromUtf8(key.toString().data());
300         writeVariant(map, path(), value);
301     }
302 
restoreToDefault()303     void restoreToDefault() override {
304         keyWidget_->setKeySequence({defaultValue_});
305     }
306 
307 private:
308     FcitxQtKeySequenceWidget *keyWidget_;
309     fcitx::Key defaultValue_;
310 };
311 
312 class EnumOptionWidget : public OptionWidget {
313     Q_OBJECT
314 public:
EnumOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)315     EnumOptionWidget(const FcitxQtConfigOption &option, const QString &path,
316                      QWidget *parent)
317         : OptionWidget(path, parent), comboBox_(new QComboBox),
318           toolButton_(new QToolButton) {
319         auto *layout = new QHBoxLayout;
320         toolButton_->setIcon(QIcon::fromTheme("preferences-system-symbolic"));
321         layout->setMargin(0);
322 
323         int i = 0;
324         while (true) {
325             auto value =
326                 readString(option.properties(), QString("Enum/%1").arg(i));
327             if (value.isNull()) {
328                 break;
329             }
330             auto text =
331                 readString(option.properties(), QString("EnumI18n/%1").arg(i));
332             if (text.isEmpty()) {
333                 text = value;
334             }
335             auto subConfigPath = readString(option.properties(),
336                                             QString("SubConfigPath/%1").arg(i));
337             comboBox_->addItem(text, value);
338             comboBox_->setItemData(i, subConfigPath, subConfigPathRole);
339             i++;
340         }
341         layout->addWidget(comboBox_);
342         layout->addWidget(toolButton_);
343         setLayout(layout);
344 
345         connect(comboBox_, qOverload<int>(&QComboBox::currentIndexChanged),
346                 this, &OptionWidget::valueChanged);
347 
348         connect(comboBox_, qOverload<int>(&QComboBox::currentIndexChanged),
349                 this, [this]() {
350                     toolButton_->setVisible(
351                         !comboBox_->currentData(subConfigPathRole)
352                              .toString()
353                              .isEmpty());
354                 });
355 
356         connect(toolButton_, &QToolButton::clicked, this, [this]() {
357             ConfigWidget *configWidget = getConfigWidget(this);
358             if (!configWidget) {
359                 return;
360             }
361             QPointer<QDialog> dialog = ConfigWidget::configDialog(
362                 this, configWidget->dbus(),
363                 comboBox_->currentData(subConfigPathRole).toString(),
364                 comboBox_->currentText());
365             dialog->exec();
366             delete dialog;
367         });
368 
369         defaultValue_ = option.defaultValue().variant().toString();
370     }
371 
readValueFrom(const QVariantMap & map)372     void readValueFrom(const QVariantMap &map) override {
373         auto value = readString(map, path());
374         auto idx = comboBox_->findData(value);
375         if (idx < 0) {
376             idx = comboBox_->findData(defaultValue_);
377         }
378         comboBox_->setCurrentIndex(idx);
379         toolButton_->setVisible(
380             !comboBox_->currentData(subConfigPathRole).toString().isEmpty());
381     }
382 
writeValueTo(QVariantMap & map)383     void writeValueTo(QVariantMap &map) override {
384         writeVariant(map, path(), comboBox_->currentData().toString());
385     }
386 
restoreToDefault()387     void restoreToDefault() override {
388         auto idx = comboBox_->findData(defaultValue_);
389         comboBox_->setCurrentIndex(idx);
390     }
391 
392 private:
393     QComboBox *comboBox_;
394     QToolButton *toolButton_;
395     QString defaultValue_;
396     inline static constexpr int subConfigPathRole = Qt::UserRole + 1;
397 };
398 
399 class ColorOptionWidget : public OptionWidget {
400     Q_OBJECT
401 public:
ColorOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)402     ColorOptionWidget(const FcitxQtConfigOption &option, const QString &path,
403                       QWidget *parent)
404         : OptionWidget(path, parent), colorButton_(new KColorButton) {
405         QVBoxLayout *layout = new QVBoxLayout;
406         layout->setMargin(0);
407         layout->addWidget(colorButton_);
408         colorButton_->setAlphaChannelEnabled(true);
409         setLayout(layout);
410         connect(colorButton_, &KColorButton::changed, this,
411                 &OptionWidget::valueChanged);
412 
413         try {
414             defaultValue_.setFromString(
415                 option.defaultValue().variant().toString().toStdString());
416         } catch (...) {
417         }
418     }
419 
readValueFrom(const QVariantMap & map)420     void readValueFrom(const QVariantMap &map) override {
421         auto value = readString(map, path());
422         Color color;
423         try {
424             color.setFromString(value.toStdString());
425         } catch (...) {
426             color = defaultValue_;
427         }
428         QColor qcolor;
429         qcolor.setRedF(color.redF());
430         qcolor.setGreenF(color.greenF());
431         qcolor.setBlueF(color.blueF());
432         qcolor.setAlphaF(color.alphaF());
433         colorButton_->setColor(qcolor);
434     }
435 
writeValueTo(QVariantMap & map)436     void writeValueTo(QVariantMap &map) override {
437         auto color = colorButton_->color();
438         Color fcitxColor;
439         fcitxColor.setRedF(color.redF());
440         fcitxColor.setGreenF(color.greenF());
441         fcitxColor.setBlueF(color.blueF());
442         fcitxColor.setAlphaF(color.alphaF());
443         writeVariant(map, path(),
444                      QString::fromStdString(fcitxColor.toString()));
445     }
446 
restoreToDefault()447     void restoreToDefault() override {
448         QColor qcolor;
449         qcolor.setRedF(defaultValue_.redF());
450         qcolor.setGreenF(defaultValue_.greenF());
451         qcolor.setBlueF(defaultValue_.blueF());
452         qcolor.setAlphaF(defaultValue_.alphaF());
453         colorButton_->setColor(qcolor);
454     }
455 
456 private:
457     KColorButton *colorButton_;
458     Color defaultValue_;
459 };
460 
461 class ExternalOptionWidget : public OptionWidget {
462     Q_OBJECT
463 public:
ExternalOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)464     ExternalOptionWidget(const FcitxQtConfigOption &option, const QString &path,
465                          QWidget *parent)
466         : OptionWidget(path, parent),
467           uri_(readString(option.properties(), "External")),
468           launchSubConfig_(readBool(option.properties(), "LaunchSubConfig")) {
469         QVBoxLayout *layout = new QVBoxLayout;
470         layout->setMargin(0);
471 
472         button_ = new QToolButton(this);
473         button_->setIcon(QIcon::fromTheme("preferences-system-symbolic"));
474         button_->setText(_("Configure"));
475         layout->addWidget(button_);
476         setLayout(layout);
477 
478         connect(
479             button_, &QPushButton::clicked, this,
480             [this, parent, name = option.name()]() {
481                 if (launchSubConfig_) {
482                     ConfigWidget *configWidget = getConfigWidget(this);
483                     if (!configWidget) {
484                         return;
485                     }
486                     QPointer<QDialog> dialog = ConfigWidget::configDialog(
487                         this, configWidget->dbus(), uri_, name);
488                     dialog->exec();
489                     delete dialog;
490                 } else if (uri_.startsWith("fcitx://config/addon/")) {
491                     QString wrapperPath = FCITX5_QT5_GUI_WRAPPER;
492                     if (!QFileInfo(wrapperPath).isExecutable()) {
493                         wrapperPath =
494                             QString::fromStdString(stringutils::joinPath(
495                                 StandardPath::global().fcitxPath("libexecdir"),
496                                 "fcitx5-qt5-gui-wrapper"));
497                     }
498                     QStringList args;
499                     if (QGuiApplication::platformName() == "xcb") {
500                         auto wid = parent->winId();
501                         if (wid) {
502                             args << "-w";
503                             args << QString::number(wid);
504                         }
505                     }
506                     args << uri_;
507                     qCDebug(KCM_FCITX5) << "Launch: " << wrapperPath << args;
508                     QProcess::startDetached(wrapperPath, args);
509                 } else {
510                 // Assume this is a program path.
511 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
512                     QStringList args = QProcess::splitCommand(uri_);
513                     QString program = args.takeFirst();
514                     QProcess::startDetached(program, args);
515 #else
516                     QProcess::startDetached(uri_);
517 #endif
518                 }
519             });
520     }
521 
readValueFrom(const QVariantMap &)522     void readValueFrom(const QVariantMap &) override {}
writeValueTo(QVariantMap &)523     void writeValueTo(QVariantMap &) override {}
restoreToDefault()524     void restoreToDefault() override {}
525 
526 private:
527     QToolButton *button_;
528     const QString uri_;
529     const bool launchSubConfig_;
530 };
531 } // namespace
532 
addWidget(QFormLayout * layout,const fcitx::FcitxQtConfigOption & option,const QString & path,QWidget * parent)533 OptionWidget *OptionWidget::addWidget(QFormLayout *layout,
534                                       const fcitx::FcitxQtConfigOption &option,
535                                       const QString &path, QWidget *parent) {
536     OptionWidget *widget = nullptr;
537     if (option.type() == "Integer") {
538         widget = new IntegerOptionWidget(option, path, parent);
539         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
540     } else if (option.type() == "String") {
541         const auto isFont = readBool(option.properties(), "Font");
542         const auto isEnum = readBool(option.properties(), "IsEnum");
543         if (isFont) {
544             widget = new FontOptionWidget(option, path, parent);
545         } else if (isEnum) {
546             widget = new EnumOptionWidget(option, path, parent);
547         } else {
548             widget = new StringOptionWidget(option, path, parent);
549         }
550         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
551     } else if (option.type() == "Boolean") {
552         widget = new BooleanOptionWidget(option, path, parent);
553         layout->addRow("", widget);
554     } else if (option.type() == "Key") {
555         widget = new KeyOptionWidget(option, path, parent);
556         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
557     } else if (option.type() == "List|Key") {
558         widget = new KeyListOptionWidget(option, path, parent);
559         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
560     } else if (option.type() == "Enum") {
561         widget = new EnumOptionWidget(option, path, parent);
562         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
563     } else if (option.type() == "Color") {
564         widget = new ColorOptionWidget(option, path, parent);
565         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
566     } else if (option.type().startsWith("List|")) {
567         widget = new ListOptionWidget(option, path, parent);
568         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
569     } else if (option.type() == "External") {
570         widget = new ExternalOptionWidget(option, path, parent);
571         layout->addRow(QString(_("%1:")).arg(option.description()), widget);
572     }
573     if (widget) {
574         if (option.properties().contains("Tooltip")) {
575             widget->setToolTip(option.properties().value("Tooltip").toString());
576         }
577     }
578     return widget;
579 }
580 
execOptionDialog(QWidget * parent,const fcitx::FcitxQtConfigOption & option,QVariant & result)581 bool OptionWidget::execOptionDialog(QWidget *parent,
582                                     const fcitx::FcitxQtConfigOption &option,
583                                     QVariant &result) {
584     QPointer<QDialog> dialog = new QDialog(parent);
585     dialog->setWindowIcon(QIcon::fromTheme("fcitx"));
586     dialog->setWindowTitle(option.description());
587     QVBoxLayout *dialogLayout = new QVBoxLayout;
588     dialog->setLayout(dialogLayout);
589 
590     ConfigWidget *parentConfigWidget = getConfigWidget(parent);
591     OptionWidget *optionWidget = nullptr;
592     ConfigWidget *configWidget = nullptr;
593     if (parentConfigWidget->description().contains(option.type())) {
594         configWidget =
595             new ConfigWidget(parentConfigWidget->description(), option.type(),
596                              parentConfigWidget->dbus());
597         configWidget->setValue(result);
598         dialogLayout->addWidget(configWidget);
599     } else {
600         QFormLayout *subLayout = new QFormLayout;
601         dialogLayout->addLayout(subLayout);
602         optionWidget =
603             addWidget(subLayout, option, QString("Value"), dialog.data());
604         if (!optionWidget) {
605             return false;
606         }
607         QVariantMap origin;
608         origin["Value"] = result;
609         optionWidget->readValueFrom(origin);
610     }
611 
612     QDialogButtonBox *buttonBox =
613         new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
614     buttonBox->button(QDialogButtonBox::Ok)->setText(_("&OK"));
615     buttonBox->button(QDialogButtonBox::Cancel)->setText(_("&Cancel"));
616     dialogLayout->addWidget(buttonBox);
617 
618     connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
619     connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
620 
621     auto ret = dialog->exec();
622     bool dialogResult = false;
623     if (ret && dialog) {
624         if (optionWidget) {
625             if (optionWidget->isValid()) {
626                 QVariantMap map;
627                 optionWidget->writeValueTo(map);
628                 result = map.value("Value");
629                 dialogResult = true;
630             }
631         } else {
632             result = configWidget->value();
633             dialogResult = true;
634         }
635     }
636     delete dialog;
637     return dialogResult;
638 }
639 
prettify(const fcitx::FcitxQtConfigOption & option,const QVariant & value)640 QString OptionWidget::prettify(const fcitx::FcitxQtConfigOption &option,
641                                const QVariant &value) {
642     if (option.type() == "Integer") {
643         return value.toString();
644     } else if (option.type() == "String") {
645         return value.toString();
646     } else if (option.type() == "Boolean") {
647         return value.toString() == "True" ? _("Yes") : _("No");
648     } else if (option.type() == "Key") {
649         return value.toString();
650     } else if (option.type() == "Enum") {
651         QMap<QString, QString> enumMap;
652         int i = 0;
653         while (true) {
654             auto value =
655                 readString(option.properties(), QString("Enum/%1").arg(i));
656             if (value.isNull()) {
657                 break;
658             }
659             auto text =
660                 readString(option.properties(), QString("EnumI18n/%1").arg(i));
661             if (text.isEmpty()) {
662                 text = value;
663             }
664             enumMap[value] = text;
665             i++;
666         }
667         return enumMap.value(value.toString());
668     } else if (option.type().startsWith("List|")) {
669 
670         int i = 0;
671         QStringList strs;
672         strs.clear();
673         auto subOption = option;
674         subOption.setType(option.type().mid(5)); // Remove List|
675         while (true) {
676             auto subValue = readVariant(value, QString(i));
677             strs << prettify(subOption, subValue);
678             i++;
679         }
680         return QString(_("[%1]")).arg(strs.join(" "));
681     } else {
682         auto *configWidget = getConfigWidget(this);
683         if (configWidget &&
684             configWidget->description().contains(option.type())) {
685             if (auto key =
686                     option.properties().value("ListDisplayOption").toString();
687                 !key.isEmpty()) {
688                 const auto &options =
689                     *configWidget->description().find(option.type());
690                 for (const auto &option : options) {
691                     if (option.name() == key) {
692                         return prettify(option, readVariant(value, key));
693                     }
694                 }
695             }
696         }
697     }
698     return QString();
699 }
700 
701 } // namespace kcm
702 } // namespace fcitx
703 
704 #include "optionwidget.moc"
705