From 97c52ddfa299e14cf54d24bef4e4c1df10a89f77 Mon Sep 17 00:00:00 2001 From: xz9 Date: Sat, 17 Oct 2020 23:20:33 -0400 Subject: [PATCH] [WIP] Add GUI config tool --- CMakeLists.txt | 5 + gui/CMakeLists.txt | 6 + gui/src/CMakeLists.txt | 40 +++ gui/src/ConfigMain.cpp | 561 +++++++++++++++++++++++++++++++++++ gui/src/ConfigMain.h | 75 +++++ gui/src/ConfigMain.ui | 383 ++++++++++++++++++++++++ gui/src/ErrorOverlay.cpp | 87 ++++++ gui/src/ErrorOverlay.h | 41 +++ gui/src/Main.cpp | 37 +++ gui/src/Main.h | 32 ++ gui/src/Model.cpp | 133 +++++++++ gui/src/Model.h | 65 ++++ gui/src/RimeConfigParser.cpp | 389 ++++++++++++++++++++++++ gui/src/RimeConfigParser.h | 97 ++++++ gui/src/rime-config.json | 2 + 15 files changed, 1953 insertions(+) create mode 100644 gui/CMakeLists.txt create mode 100644 gui/src/CMakeLists.txt create mode 100644 gui/src/ConfigMain.cpp create mode 100644 gui/src/ConfigMain.h create mode 100644 gui/src/ConfigMain.ui create mode 100644 gui/src/ErrorOverlay.cpp create mode 100644 gui/src/ErrorOverlay.h create mode 100644 gui/src/Main.cpp create mode 100644 gui/src/Main.h create mode 100644 gui/src/Model.cpp create mode 100644 gui/src/Model.h create mode 100644 gui/src/RimeConfigParser.cpp create mode 100644 gui/src/RimeConfigParser.h create mode 100644 gui/src/rime-config.json diff --git a/CMakeLists.txt b/CMakeLists.txt index c50abd1..50a51b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,11 @@ add_definitions(-DFCITX_RIME_VERSION=\"${PROJECT_VERSION}\") include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") +option(ENABLE_QT5GUI "Build Fcitx5 Rime Config GUI Tool" ON) + add_subdirectory(po) add_subdirectory(src) add_subdirectory(data) +if(ENABLE_QT5GUI) + add_subdirectory(gui) +endif(ENABLE_QT5GUI) diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt new file mode 100644 index 0000000..6368223 --- /dev/null +++ b/gui/CMakeLists.txt @@ -0,0 +1,6 @@ +set(REQUIRED_QT5_VERSION 5.1.0) + +find_package(Qt5 ${REQUIRED_QT5_VERSION} CONFIG REQUIRED Core Widgets Concurrent) +find_package(Fcitx5Qt5WidgetsAddons REQUIRED) + +add_subdirectory(src) diff --git a/gui/src/CMakeLists.txt b/gui/src/CMakeLists.txt new file mode 100644 index 0000000..addd8ac --- /dev/null +++ b/gui/src/CMakeLists.txt @@ -0,0 +1,40 @@ +set(RIME_CONFIG_SRCS + Main.cpp + ConfigMain.cpp + Model.cpp + RimeConfigParser.cpp + ErrorOverlay.cpp) + +set(RIME_CONFIG_HDRS + Main.h + ConfigMain.h + Model.h + Common.h + RimeConfigParser.h + ErrorOverlay.h) + +set(RIME_CONFIG_UIS + ConfigMain.ui) + +# fcitx5_translate_add_sources( +# ${RIME_CONFIG_SRCS} +# ${RIME_CONFIG_HDRS} +# ${RIME_CONFIG_UIS}) + +add_library(rime-config + MODULE ${RIME_CONFIG_SRCS}) + +set_target_properties(rime-config PROPERTIES + LINK_FLAGS "-Wl,--no-undefined" + AUTOMOC TRUE + AUTOUIC TRUE + AUTOUIC_OPTIONS "-tr=fcitx::tr2fcitx;--include=fcitxqti18nhelper.h") + +target_link_libraries(rime-config + Qt5::Widgets + Qt5::Concurrent + Fcitx5::Core + Fcitx5Qt5::WidgetsAddons + PkgConfig::Rime) + +install(TARGETS rime-config DESTINATION ${CMAKE_INSTALL_LIBDIR}/fcitx5/qt5) diff --git a/gui/src/ConfigMain.cpp b/gui/src/ConfigMain.cpp new file mode 100644 index 0000000..b7b9212 --- /dev/null +++ b/gui/src/ConfigMain.cpp @@ -0,0 +1,561 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#include "ConfigMain.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fcitx5_rime { +ConfigMain::ConfigMain(QWidget *parent) + : fcitx::FcitxQtConfigUIWidget(parent), model(new RimeConfigDataModel()), inError(false) { + + // Setup UI + setMinimumSize(680, 500); + setupUi(this); + overlay = new ErrorOverlay(this); + verticallayout_general->setAlignment(Qt::AlignTop); + addIMButton->setIcon(QIcon::fromTheme("go-next")); + removeIMButton->setIcon(QIcon::fromTheme("go-previous")); + moveUpButton->setIcon(QIcon::fromTheme("go-up")); + moveDownButton->setIcon(QIcon::fromTheme("go-down")); + // configureButton->setIcon(QIcon::fromTheme("help-about")); + + // listViews for currentIM and availIM + QStandardItemModel *listModel = new QStandardItemModel(this); + currentIMView->setModel(listModel); + QStandardItemModel *availIMModel = new QStandardItemModel(this); + availIMView->setModel(availIMModel); + + // Shortcuts Tab + connect(cand_cnt_spinbox, QOverload::of(&QSpinBox::valueChanged), this, + &ConfigMain::stateChanged); + connect(shift_l_combo, QOverload::of(&QComboBox::currentIndexChanged), + this, &ConfigMain::stateChanged); + connect(shift_r_combo, QOverload::of(&QComboBox::currentIndexChanged), + this, &ConfigMain::stateChanged); + QList keywgts = + general_tab->findChildren(); + for (int i = 0; i < keywgts.size(); i++) { + connect(keywgts[i], &fcitx::FcitxQtKeySequenceWidget::keySequenceChanged, this, + &ConfigMain::keytoggleChanged); + } + + // Schemas Tab + connect(removeIMButton, &QPushButton::clicked, this, &ConfigMain::removeIM); + connect(addIMButton, &QPushButton::clicked, this, &ConfigMain::addIM); + connect(moveUpButton, &QPushButton::clicked, this, &ConfigMain::moveUpIM); + connect(moveDownButton, &QPushButton::clicked, this, + &ConfigMain::moveDownIM); + connect(availIMView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &ConfigMain::availIMSelectionChanged); + connect(currentIMView->selectionModel(), + &QItemSelectionModel::currentChanged, this, + &ConfigMain::activeIMSelectionChanged); + + if(!yamlToModel()) { // Load data from yaml + disableUi("Failed to load Rime config or api. Please check your Rime config or installation."); + } else { + modelToUi(); + } +} + +ConfigMain::~ConfigMain() { delete model; } + +void ConfigMain::keytoggleChanged() { stateChanged(); } + +// SLOTs +void ConfigMain::stateChanged() { emit changed(true); } + +void ConfigMain::focusSelectedIM(const QString im_name) { + // search enabled IM first + int sz = currentIMView->model()->rowCount(); + for (int i = 0; i < sz; i++) { + QModelIndex ind = currentIMView->model()->index(i, 0); + const QString name = + currentIMView->model()->data(ind, Qt::DisplayRole).toString(); + if (name == im_name) { + currentIMView->setCurrentIndex(ind); + currentIMView->setFocus(); + return; + } + } + // if not found, search avali IM list + sz = availIMView->model()->rowCount(); + for (int i = 0; i < sz; i++) { + QModelIndex ind = availIMView->model()->index(i, 0); + const QString name = + availIMView->model()->data(ind, Qt::DisplayRole).toString(); + if (name == im_name) { + availIMView->setCurrentIndex(ind); + availIMView->setFocus(); + return; + } + } +} + +void ConfigMain::addIM() { + if (availIMView->currentIndex().isValid()) { + const QString uniqueName = + availIMView->currentIndex().data(Qt::DisplayRole).toString(); + int largest = 0; + int find = -1; + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].name == uniqueName) { + find = i; + } + if (model->schemas_[i].index > largest) { + largest = model->schemas_[i].index; + } + } + if (find != -1) { + model->schemas_[find].active = true; + model->schemas_[find].index = largest + 1; + } + + model->sortSchemas(); + updateIMList(); + focusSelectedIM(uniqueName); + stateChanged(); + } +} + +void ConfigMain::removeIM() { + if (currentIMView->currentIndex().isValid()) { + const QString uniqueName = + currentIMView->currentIndex().data(Qt::DisplayRole).toString(); + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].name == uniqueName) { + model->schemas_[i].active = false; + model->schemas_[i].index = 0; + } + } + model->sortSchemas(); + updateIMList(); + focusSelectedIM(uniqueName); + stateChanged(); + } +} + +void ConfigMain::moveUpIM() { + if (currentIMView->currentIndex().isValid()) { + const QString uniqueName = + currentIMView->currentIndex().data(Qt::DisplayRole).toString(); + int cur_index = -1; + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].name == uniqueName) { + cur_index = model->schemas_[i].index; + Q_ASSERT(cur_index == + (i + 1)); // make sure the schema is sorted + } + } + // can't move up the top schema because the button should be grey + if (cur_index == -1 || cur_index == 0) { + return; + } + + int temp; + temp = model->schemas_[cur_index - 1].index; + model->schemas_[cur_index - 1].index = + model->schemas_[cur_index - 2].index; + model->schemas_[cur_index - 2].index = temp; + model->sortSchemas(); + updateIMList(); + focusSelectedIM(uniqueName); + stateChanged(); + } +} + +void ConfigMain::moveDownIM() { + if (currentIMView->currentIndex().isValid()) { + const QString uniqueName = + currentIMView->currentIndex().data(Qt::DisplayRole).toString(); + int cur_index = -1; + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].name == uniqueName) { + cur_index = model->schemas_[i].index; + Q_ASSERT(cur_index == + (i + 1)); // make sure the schema is sorted + } + } + // can't move down the bottom schema because the button should be grey + if (cur_index == -1 || cur_index == 0) { + return; + } + int temp; + temp = model->schemas_[cur_index - 1].index; + model->schemas_[cur_index - 1].index = model->schemas_[cur_index].index; + model->schemas_[cur_index].index = temp; + model->sortSchemas(); + updateIMList(); + focusSelectedIM(uniqueName); + stateChanged(); + } +} + +void ConfigMain::availIMSelectionChanged() { + if (!availIMView->currentIndex().isValid()) { + addIMButton->setEnabled(false); + } else { + addIMButton->setEnabled(true); + } +} + +void ConfigMain::activeIMSelectionChanged() { + if (!currentIMView->currentIndex().isValid()) { + removeIMButton->setEnabled(false); + moveUpButton->setEnabled(false); + moveDownButton->setEnabled(false); + // configureButton->setEnabled(false); + } else { + removeIMButton->setEnabled(true); + // configureButton->setEnabled(true); + if (currentIMView->currentIndex().row() == 0) { + moveUpButton->setEnabled(false); + } else { + moveUpButton->setEnabled(true); + } + if (currentIMView->currentIndex().row() == + currentIMView->model()->rowCount() - 1) { + moveDownButton->setEnabled(false); + } else { + moveDownButton->setEnabled(true); + } + } +} +// end of SLOTs + +QString ConfigMain::icon() { return "fcitx5-rime"; } + +QString ConfigMain::title() { return _("Fcitx5 Rime Config Tool"); } + +void ConfigMain::load() { + if(inError) { + return; + } + modelToUi(); +} + +void ConfigMain::disableUi(const char *message) { + setEnabled(false); + overlay->enable(message); + inError = true; +} + +void ConfigMain::setModelFromLayout(QList &model_keys, + QLayout *layout) { + fcitx::FcitxQtKeySequenceWidget * keywidget = getKeyWidgetFromLayout(layout); + Q_ASSERT(keywidget != NULL); + model_keys = keywidget->keySequence(); +} + +void ConfigMain::uiToModel() { + model->candidate_per_word_ = cand_cnt_spinbox->value(); + + setModelFromLayout(model->toggle_keys_, horizontallayout_toggle); + setModelFromLayout(model->ascii_keys_, horizontallayout_ascii); + setModelFromLayout(model->pgdown_keys_, horizontallayout_pagedown); + setModelFromLayout(model->pgup_keys_, horizontallayout_pageup); + setModelFromLayout(model->trasim_keys_, horizontallayout_trasim); + setModelFromLayout(model->halffull_keys_, horizontallayout_hfshape); + + if (model->switch_keys_.size() >= 2) { + model->switch_keys_[0] = textToSwitchKey(shift_l_combo->currentIndex()); + model->switch_keys_[1] = textToSwitchKey(shift_r_combo->currentIndex()); + } + + // clear cuurent model and save from the ui + for (int i = 0; i < model->schemas_.size(); i++) { + model->schemas_[i].index = 0; + model->schemas_[i].active = false; + } + QStandardItemModel *qmodel = + static_cast(currentIMView->model()); + QModelIndex parent; + int seqno = 1; + for (int r = 0; r < qmodel->rowCount(parent); ++r) { + QModelIndex index = qmodel->index(r, 0, parent); + QVariant name = qmodel->data(index); + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].name == name) { + model->schemas_[i].index = seqno++; + model->schemas_[i].active = true; + } + } + } + model->sortSchemas(); +} + +void ConfigMain::save() { + if(inError) { + return; + } + uiToModel(); + QFutureWatcher *futureWatcher = new QFutureWatcher(this); + futureWatcher->setFuture( + QtConcurrent::run(this, &ConfigMain::modelToYaml)); + connect(futureWatcher, &QFutureWatcher::finished, this, [this]() { + if(inError) { + disableUi("Failed to save your preferences into Rime config. Please check your config file manually."); + } else { + emit changed(false); + emit saveFinished(); + } + }); +} + +fcitx::FcitxQtKeySequenceWidget * +ConfigMain::getKeyWidgetFromLayout(QLayout *layout) { + int count = layout->count(); + QList out; + for (int i = 0; i < count; i++) { + fcitx::FcitxQtKeySequenceWidget *widget = + qobject_cast( + layout->itemAt(i)->widget()); + if (widget != NULL) { + return widget; + } + } + return NULL; +} + +void ConfigMain::setKeySeqFromLayout(QLayout *layout, + QList &model_keys) { + fcitx::FcitxQtKeySequenceWidget *keywidget = getKeyWidgetFromLayout(layout); + Q_ASSERT(keywidget != NULL); + keywidget->setKeySequence(model_keys); + return; +} + +void ConfigMain::setSwitchKey(QComboBox *box, SwitchKeyFunction switch_key) { + int index = -1; + switch (switch_key) { + case SwitchKeyFunction::Noop: + index = 0; + break; + case SwitchKeyFunction::InlineASCII: + index = 1; + break; + case SwitchKeyFunction::CommitText: + index = 2; + break; + case SwitchKeyFunction::CommitCode: + index = 3; + break; + case SwitchKeyFunction::Clear: + index = 4; + break; + }; + box->setCurrentIndex(index); +} + +SwitchKeyFunction ConfigMain::textToSwitchKey(int current_index) { + switch (current_index) { + case 0: + return SwitchKeyFunction::Noop; + case 1: + return SwitchKeyFunction::InlineASCII; + case 2: + return SwitchKeyFunction::CommitText; + case 3: + return SwitchKeyFunction::CommitCode; + case 4: + return SwitchKeyFunction::Clear; + default: + return SwitchKeyFunction::Noop; + } +} + +void ConfigMain::modelToUi() { + cand_cnt_spinbox->setValue(model->candidate_per_word_); + + // set shortcut keys + setKeySeqFromLayout(horizontallayout_toggle, model->toggle_keys_); + setKeySeqFromLayout(horizontallayout_pagedown, model->pgdown_keys_); + setKeySeqFromLayout(horizontallayout_pageup, model->pgup_keys_); + setKeySeqFromLayout(horizontallayout_ascii, model->ascii_keys_); + setKeySeqFromLayout(horizontallayout_trasim, model->trasim_keys_); + setKeySeqFromLayout(horizontallayout_hfshape, model->halffull_keys_); + + // set switch keys + if (model->switch_keys_.size() >= 2) { + setSwitchKey(shift_l_combo, model->switch_keys_[0]); + setSwitchKey(shift_r_combo, model->switch_keys_[1]); + } + + // Clear both models + static_cast(currentIMView->model())->clear(); + static_cast(availIMView->model())->clear(); + // Set available and enabled input methods + for (int i = 0; i < model->schemas_.size(); i++) { + auto &schema = model->schemas_[i]; + if (schema.active) { + QStandardItem *active_schema = new QStandardItem(schema.name); + active_schema->setEditable(false); + auto qmodel = + static_cast(currentIMView->model()); + qmodel->appendRow(active_schema); + } else { + QStandardItem *inactive_schema = new QStandardItem(schema.name); + inactive_schema->setEditable(false); + auto qmodel = + static_cast(availIMView->model()); + qmodel->appendRow(inactive_schema); + } + } +} + +void ConfigMain::updateIMList() { + auto avail_IMmodel = + static_cast(availIMView->model()); + auto active_IMmodel = + static_cast(currentIMView->model()); + avail_IMmodel->removeRows(0, avail_IMmodel->rowCount()); + active_IMmodel->removeRows(0, active_IMmodel->rowCount()); + for (int i = 0; i < model->schemas_.size(); i++) { + auto &schema = model->schemas_[i]; + if (schema.active) { + QStandardItem *active_schema = new QStandardItem(schema.name); + active_schema->setEditable(false); + active_IMmodel->appendRow(active_schema); + } else { + QStandardItem *inactive_schema = new QStandardItem(schema.name); + inactive_schema->setEditable(false); + avail_IMmodel->appendRow(inactive_schema); + } + } +} + +void ConfigMain::modelToYaml() { + config.setPageSize(model->candidate_per_word_); + std::vector toggleKeys; + for (int i = 0; i < model->toggle_keys_.size(); i++) { + toggleKeys.push_back(model->toggle_keys_[i].toString()); + } + + config.setToggleKeys(toggleKeys); + config.setKeybindings(model->getKeybindings()); + config.setSwitchKeys(std::vector( + model->switch_keys_.begin(), model->switch_keys_.end())); + + // set active schema list + std::vector schemaNames; + schemaNames.reserve(model->schemas_.size()); + for (int i = 0; i < model->schemas_.size(); i++) { + if (model->schemas_[i].index == 0) { + break; + } else { + schemaNames.push_back(model->schemas_[i].id.toStdString()); + } + } + config.setSchemas(schemaNames); + + inError = !(config.sync()); + return; +} + +bool ConfigMain::yamlToModel() { + // load page size + int page_size = 0; + bool suc; + + suc = config.isError(); + if(suc) { + return false; + } + + suc = config.getPageSize(&page_size); + if (suc) { + model->candidate_per_word_ = page_size; + } else { + model->candidate_per_word_ = default_page_size; + } + + // load toggle keys + auto toggleKeys = config.getToggleKeys(); + for (const auto &toggleKey : toggleKeys) { + if (!toggleKey.empty()) { // skip the empty keys + model->toggle_keys_.push_back(fcitx::Key(toggleKey.data())); + } + } + + // load keybindings + auto bindings = config.getKeybindings(); + model->setKeybindings(std::move(bindings)); + + // load switchkeys + auto switch_keys = config.getSwitchKeys(); + model->switch_keys_ = + QList(switch_keys.begin(), switch_keys.end()); + + // load schemas + getAvailableSchemas(); + return true; +} + +void ConfigMain::getAvailableSchemas() { + const char *userPath = RimeGetUserDataDir(); + const char *sysPath = RimeGetSharedDataDir(); + + QSet files; + for (auto path : {sysPath, userPath}) { + if (!path) { + continue; + } + QDir dir(path); + QList entryList = dir.entryList(QStringList("*.schema.yaml"), + QDir::Files | QDir::Readable); + files.unite(QSet(entryList.begin(), entryList.end())); + } + + auto filesList = files.values(); + filesList.sort(); + + for (const auto &file : filesList) { + auto schema = FcitxRimeSchema(); + QString fullPath; + for (auto path : {userPath, sysPath}) { + QDir dir(path); + if (dir.exists(file)) { + fullPath = dir.filePath(file); + break; + } + } + schema.path = fullPath; + QFile fd(fullPath); + if (!fd.open(QIODevice::ReadOnly)) { + continue; + } + auto yamlData = fd.readAll(); + auto name = config.stringFromYAML(yamlData.constData(), "schema/name"); + auto id = + config.stringFromYAML(yamlData.constData(), "schema/schema_id"); + schema.name = QString::fromStdString(name); + schema.id = QString::fromStdString(id); + schema.index = config.schemaIndex(id.data()); + schema.active = static_cast(schema.index); + model->schemas_.push_back(schema); + } + model->sortSchemas(); +} + +}; // namespace fcitx_rime diff --git a/gui/src/ConfigMain.h b/gui/src/ConfigMain.h new file mode 100644 index 0000000..eb7d1ec --- /dev/null +++ b/gui/src/ConfigMain.h @@ -0,0 +1,75 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#ifndef _RIME_GUI_CONFIGMAIN_H +#define _RIME_GUI_CONFIGMAIN_H + +#include +#include +#include + +#include "ErrorOverlay.h" +#include "Model.h" +#include "RimeConfigParser.h" +#include "ui_ConfigMain.h" + +namespace fcitx5_rime { +class ConfigMain : public fcitx::FcitxQtConfigUIWidget, private Ui::MainUI { + Q_OBJECT +public: + explicit ConfigMain(QWidget *parent = 0); + QString title() override; + ~ConfigMain(); + void load() override; + void save() override; + bool asyncSave() override { return true; } + + QString icon() override; +public slots: + void keytoggleChanged(); + void stateChanged(); + void addIM(); + void removeIM(); + void moveUpIM(); + void moveDownIM(); + void availIMSelectionChanged(); + void activeIMSelectionChanged(); + +private: + void disableUi(const char *message); + bool yamlToModel(); + void uiToModel(); + void modelToUi(); + void modelToYaml(); + void getAvailableSchemas(); + void updateIMList(); + void focusSelectedIM(const QString im_name); + void setSwitchKey(QComboBox *box, SwitchKeyFunction switch_key); + SwitchKeyFunction textToSwitchKey(int current_index); + fcitx::FcitxQtKeySequenceWidget* getKeyWidgetFromLayout(QLayout *layout); + void setKeySeqFromLayout(QLayout *layout, QList &model_keys); + void setModelFromLayout(QList &model_keys, QLayout *layout); + + RimeConfigParser config; + RimeConfigDataModel *model; + ErrorOverlay *overlay; + + bool inError; +}; +} // namespace fcitx_rime + +#endif // _RIME_GUI_CONFIGMAIN_H diff --git a/gui/src/ConfigMain.ui b/gui/src/ConfigMain.ui new file mode 100644 index 0000000..313a1ce --- /dev/null +++ b/gui/src/ConfigMain.ui @@ -0,0 +1,383 @@ + + + MainUI + + + + 0 + 0 + 500 + 500 + + + + MainWindow + + + + + + + + + + + + Shortcut + + + + + + + Call-out Menu + + + + + false + + + + + + + + + + Page Up + + + + + false + true + + + + + + + + + + Page Down + + + + + false + true + + + + + + + + + Half/Full Shape + + + + + false + + + + + + + + + Western/Eastern + + + + + + + + + + Traditional/Simplified + + + + + false + + + + + + + + + + Candidate Word Number + + + + + + CandidateWordNumber + + + + + + + + + + Left Shift Key + + + + + + + Noop + + + + + Inline ASCII + + + + + Commit Text + + + + + Commit Code + + + + + Clear + + + + + + + + + + + + Right Shift Key + + + + + + + Noop + + + + + Inline ASCII + + + + + Commit Text + + + + + Commit Code + + + + + Clear + + + + + + + + + + + Schema + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + Available Input Schemas: + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + + + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Active Input Schemas: + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + + + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + <b>Select Input Schema:</b> + + + + + + + + + + + + fcitx::FcitxQtKeySequenceWidget + QWidget +
fcitxqtkeysequencewidget.h
+
+
+ + +
diff --git a/gui/src/ErrorOverlay.cpp b/gui/src/ErrorOverlay.cpp new file mode 100644 index 0000000..7882d27 --- /dev/null +++ b/gui/src/ErrorOverlay.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2011 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "ErrorOverlay.h" + +#include +#include +#include + +ErrorOverlay::ErrorOverlay(QWidget *baseWidget, QWidget *parent) : + QWidget(parent ? parent : baseWidget->window()), + m_BaseWidget(baseWidget), + m_enable(false) { + setVisible(false); + // Build the UI + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing(10); + + QLabel *pixmap = new QLabel(); + pixmap->setPixmap(QIcon::fromTheme("dialog-error").pixmap(64)); + + m_message = new QLabel(""); + + pixmap->setAlignment(Qt::AlignHCenter); + m_message->setAlignment(Qt::AlignHCenter); + + layout->addStretch(); + layout->addWidget(pixmap); + layout->addWidget(m_message); + layout->addStretch(); + + setLayout(layout); + + // Draw the transparent overlay background + QPalette p = palette(); + p.setColor(backgroundRole(), QColor(0, 0, 0, 128)); + p.setColor(foregroundRole(), Qt::white); + setPalette(p); + setAutoFillBackground(true); + + m_BaseWidget->installEventFilter(this); + +} + +ErrorOverlay::~ErrorOverlay() { } + +void ErrorOverlay::enable(const char *errorMessage) { + + m_message->setText(errorMessage); + + if(!m_BaseWidget) { + return; + } + + // reparent to the current top level widget of the base widget if needed + // needed eg. in dock widgets + if (parentWidget() != m_BaseWidget->window()) { + setParent(m_BaseWidget->window()); + } + + show(); + + // follow position changes + const QPoint topLevelPos = m_BaseWidget->mapTo(window(), QPoint(0, 0)); + const QPoint parentPos = parentWidget()->mapFrom(window(), topLevelPos); + move(parentPos); + + m_enable = true; + // TODO: hide/scale icon if we don't have enough space + resize(m_BaseWidget->size()); +} diff --git a/gui/src/ErrorOverlay.h b/gui/src/ErrorOverlay.h new file mode 100644 index 0000000..d22c114 --- /dev/null +++ b/gui/src/ErrorOverlay.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2011 by Dario Freddi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef ERROROVERLAY_H +#define ERROROVERLAY_H + +#include +#include + +class ErrorOverlay : public QWidget +{ + Q_OBJECT +public: + explicit ErrorOverlay(QWidget *baseWidget, QWidget *parent = 0); + virtual ~ErrorOverlay(); + + void enable(const char *errorMessage); + +private: + QWidget *m_BaseWidget; + QLabel *m_message; + bool m_enable; +}; + +#endif // ERROROVERLAY_H diff --git a/gui/src/Main.cpp b/gui/src/Main.cpp new file mode 100644 index 0000000..3c6f9ba --- /dev/null +++ b/gui/src/Main.cpp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#include + +#include +#include "ConfigMain.h" +#include "Main.h" + +// FcitxQtConfigUIPlugin : QObject, FcitxQtConfigUIFactoryInterface +RimeConfigParserTool::RimeConfigParserTool(QObject *parent) + : FcitxQtConfigUIPlugin(parent) { + if (parent == NULL) { + } +} + +fcitx::FcitxQtConfigUIWidget *RimeConfigParserTool::create(const QString &key) { + Q_UNUSED(key); + return new fcitx5_rime::ConfigMain; +} + + + diff --git a/gui/src/Main.h b/gui/src/Main.h new file mode 100644 index 0000000..cdc7c1f --- /dev/null +++ b/gui/src/Main.h @@ -0,0 +1,32 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#ifndef _RIME_GUI_MAIN_H +#define _RIME_GUI_MAIN_H + +#include + +class RimeConfigParserTool : public fcitx::FcitxQtConfigUIPlugin { + Q_OBJECT +public: + Q_PLUGIN_METADATA(IID FcitxQtConfigUIFactoryInterface_iid FILE + "rime-config.json") + explicit RimeConfigParserTool(QObject *parent = 0); + fcitx::FcitxQtConfigUIWidget *create(const QString &key) override; +}; + +#endif // _RIME_GUI_MAIN_H diff --git a/gui/src/Model.cpp b/gui/src/Model.cpp new file mode 100644 index 0000000..6f466f2 --- /dev/null +++ b/gui/src/Model.cpp @@ -0,0 +1,133 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#include "Model.h" +#include +#include +#include + +namespace fcitx5_rime { + +void RimeConfigDataModel::sortSchemas() { + std::sort(schemas_.begin(), schemas_.end(), + [](const FcitxRimeSchema &a, const FcitxRimeSchema &b) -> bool { + // if both inactive, sort by id + if (a.index == 0 && b.index == 0) { + return a.id < b.id; + } + if (a.index == 0) { + return false; + } + if (b.index == 0) { + return true; + } + return (a.index < b.index); + }); +} + +void RimeConfigDataModel::sortKeys() { + sortSingleKeySet(toggle_keys_); + sortSingleKeySet(ascii_keys_); + sortSingleKeySet(trasim_keys_); + sortSingleKeySet(halffull_keys_); + sortSingleKeySet(pgup_keys_); + sortSingleKeySet(pgdown_keys_); +} + +void RimeConfigDataModel::sortSingleKeySet(QList &keys) { + std::sort(keys.begin(), keys.end(), + [](const fcitx::Key &a, const fcitx::Key &b) -> bool { + return a.toString().length() < b.toString().length(); + }); +} + +void RimeConfigDataModel::setKeybindings(std::vector bindings) { + for (const auto &binding : bindings) { + if (binding.accept.empty()) { + continue; + } + if (binding.action == "ascii_mode") { + fcitx::Key seq(binding.accept); + ascii_keys_.push_back(seq); + } else if (binding.action == "full_shape") { + fcitx::Key seq(binding.accept); + halffull_keys_.push_back(seq); + } else if (binding.action == "simplification") { + fcitx::Key seq(binding.accept); + trasim_keys_.push_back(seq); + } else if (binding.action == "Page_Up") { + fcitx::Key seq(binding.accept); + pgup_keys_.push_back(seq); + } else if (binding.action == "Page_Down") { + fcitx::Key seq(binding.accept); + pgdown_keys_.push_back(seq); + } + } + sortKeys(); +} + +std::vector RimeConfigDataModel::getKeybindings() { + std::vector out; + // Fill ascii_key + for (auto &ascii : ascii_keys_) { + Keybinding binding; + binding.action = "ascii_mode"; + binding.when = KeybindingCondition::Always; + binding.type = KeybindingType::Toggle; + binding.accept = ascii.toString(); + out.push_back(binding); + } + // Fill trasim_key + for (auto &trasim : trasim_keys_) { + Keybinding binding; + binding.action = "simplification"; + binding.when = KeybindingCondition::Always; + binding.type = KeybindingType::Toggle; + binding.accept = trasim.toString(); + out.push_back(binding); + } + // Fill halffull_key + for (auto &halffull : halffull_keys_) { + Keybinding binding; + binding.action = "full_shape"; + binding.when = KeybindingCondition::Always; + binding.type = KeybindingType::Toggle; + binding.accept = halffull.toString(); + out.push_back(binding); + } + // Fill pgup_key + for (auto &pgup : pgup_keys_) { + Keybinding binding; + binding.action = "Page_Up"; + binding.when = KeybindingCondition::HasMenu; + binding.type = KeybindingType::Send; + binding.accept = pgup.toString(); + out.push_back(binding); + } + // Fill pgdown_key + for (auto &pgup : pgdown_keys_) { + Keybinding binding; + binding.action = "Page_Down"; + binding.when = KeybindingCondition::HasMenu; + binding.type = KeybindingType::Send; + binding.accept = pgup.toString(); + out.push_back(binding); + } + return out; +} + +} // namespace fcitx_rime diff --git a/gui/src/Model.h b/gui/src/Model.h new file mode 100644 index 0000000..2cfcf93 --- /dev/null +++ b/gui/src/Model.h @@ -0,0 +1,65 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#ifndef _RIME_GUI_MODEL_H +#define _RIME_GUI_MODEL_H + +#include "RimeConfigParser.h" +#include +#include +#include +#include +#include + +static constexpr int max_shortcuts = 3; +static constexpr int default_page_size = 5; + +namespace fcitx5_rime { + +class FcitxRimeSchema { +public: + QString path; + QString id; + QString name; + int index; // index starts from 1, 0 means not enabled + bool active; +}; + +class RimeConfigDataModel { +public: + int candidate_per_word_; + QList switch_keys_; + QList schemas_; + QList toggle_keys_; + QList ascii_keys_; + QList trasim_keys_; + QList halffull_keys_; + QList pgup_keys_; + QList pgdown_keys_; + + void setKeybindings(const std::vector bindings); + std::vector getKeybindings(); + + void sortSchemas(); + void sortKeys(); + +private: + void sortSingleKeySet(QList &keys); +}; +} // namespace fcitx_rime + +#endif // _RIME_GUI_MODEL_H diff --git a/gui/src/RimeConfigParser.cpp b/gui/src/RimeConfigParser.cpp new file mode 100644 index 0000000..8545faa --- /dev/null +++ b/gui/src/RimeConfigParser.cpp @@ -0,0 +1,389 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// Copyright (C) 2018~2018 by Weng Xuetian +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#include "RimeConfigParser.h" +#include +#include +#include + +namespace fcitx5_rime { + +static constexpr const char *fcitx_rime_dir_prefix = "rime"; + +RimeConfigParser::RimeConfigParser() : api(rime_get_api()), default_conf({0}), inError(false) { + RimeModule *module = api->find_module("levers"); + if (!module) { + inError = true; + } else { + levers = (RimeLeversApi *)module->get_api(); + start(true); + } +} + +RimeConfigParser::~RimeConfigParser() { api->finalize(); } + +bool RimeConfigParser::isError() { + return inError; +} + +bool RimeConfigParser::start(bool firstRun) { + bool suc; + std::string user_path; + fcitx::StandardPath sp; + fcitx::StandardPath::Type dir_type = fcitx::StandardPath::Type::PkgData; + user_path = fcitx::stringutils::joinPath(sp.userDirectory(dir_type), + fcitx_rime_dir_prefix); + + RIME_STRUCT(RimeTraits, fcitx_rime_traits); + fcitx_rime_traits.shared_data_dir = RIME_DATA_DIR; + fcitx_rime_traits.user_data_dir = user_path.c_str(); + fcitx_rime_traits.distribution_name = "Rime"; + fcitx_rime_traits.distribution_code_name = "fcitx-rime-config"; + fcitx_rime_traits.distribution_version = "0.5.0"; + fcitx_rime_traits.app_name = "rime.fcitx-rime-config"; + if (firstRun) { + api->setup(&fcitx_rime_traits); + } + default_conf = {0}; + api->initialize(&fcitx_rime_traits); + settings = levers->custom_settings_init("default", "rime_patch"); + suc = levers->load_settings(settings); + if(!suc) { + return false; + } + suc = levers->settings_get_config(settings, &default_conf); + if(!suc) { + return false; + } + return true; +} + +void RimeConfigParser::setToggleKeys(const std::vector &keys) { + api->config_clear(&default_conf, "switcher/hotkeys"); + api->config_create_list(&default_conf, "switcher/hotkeys"); + RimeConfigIterator iterator; + api->config_begin_list(&iterator, &default_conf, "switcher/hotkeys"); + api->config_next(&iterator); + for (size_t i = 0; i < keys.size(); i++) { + api->config_next(&iterator); + api->config_set_string(&default_conf, iterator.path, keys[i].data()); + } + api->config_end(&iterator); +} + +std::vector RimeConfigParser::getToggleKeys() { + std::vector result; + listForeach(&default_conf, "switcher/hotkeys", + [=, &result](RimeConfig *config, const char *path) { + auto str = api->config_get_cstring(config, path); + if (str) { + result.push_back(str); + } + return true; + }); + return result; +} + +const char *keyBindingConditionToString(KeybindingCondition condition) { + switch (condition) { + case KeybindingCondition::Composing: + return "composing"; + case KeybindingCondition::HasMenu: + return "has_menu"; + case KeybindingCondition::Always: + return "always"; + case KeybindingCondition::Paging: + return "paging"; + } + return ""; +} + +KeybindingCondition keyBindingConditionFromString(const char *str) { + if (strcmp(str, "composing") == 0) { + return KeybindingCondition::Composing; + } else if (strcmp(str, "has_menu") == 0) { + return KeybindingCondition::HasMenu; + } else if (strcmp(str, "paging") == 0) { + return KeybindingCondition::Paging; + } else if (strcmp(str, "always") == 0) { + return KeybindingCondition::Always; + } + return KeybindingCondition::Composing; +} + +const char *keybindingTypeToString(KeybindingType type) { + switch (type) { + case KeybindingType::Send: + return "send"; + case KeybindingType::Select: + return "select"; + case KeybindingType::Toggle: + return "toggle"; + } + return ""; +} + +const char *switchKeyFunctionToString(SwitchKeyFunction type) { + switch (type) { + case SwitchKeyFunction::Noop: + return "noop"; + case SwitchKeyFunction::InlineASCII: + return "inline_ascii"; + case SwitchKeyFunction::CommitText: + return "commit_text"; + case SwitchKeyFunction::CommitCode: + return "commit_code"; + case SwitchKeyFunction::Clear: + return "clear"; + } + return ""; +} + +SwitchKeyFunction switchKeyFunctionFromString(const char *str) { + if (strcmp(str, "noop") == 0) { + return SwitchKeyFunction::Noop; + } else if (strcmp(str, "inline_ascii") == 0) { + return SwitchKeyFunction::InlineASCII; + } else if (strcmp(str, "commit_text")) { + return SwitchKeyFunction::CommitText; + } else if (strcmp(str, "commit_code")) { + return SwitchKeyFunction::CommitCode; + } else if (strcmp(str, "clear")) { + return SwitchKeyFunction::Clear; + } + return SwitchKeyFunction::Noop; +} + +void RimeConfigParser::setKeybindings(const std::vector &bindings) { + RimeConfig copy_config = {0}; + RimeConfig copy_config_map = {0}; + RimeConfigIterator iterator; + RimeConfigIterator copy_iterator; + api->config_init(©_config); + api->config_create_list(©_config, "key_binder/bindings"); + api->config_begin_list(&iterator, &default_conf, "key_binder/bindings"); + api->config_begin_list(©_iterator, ©_config, "key_binder/bindings"); + while (!copy_iterator.path) { + api->config_next(©_iterator); + } + while (api->config_next(&iterator)) { + RimeConfig map = {0}; + const char *send_key = NULL; + api->config_get_item(&default_conf, iterator.path, &map); + send_key = api->config_get_cstring(&map, "send"); + if (!send_key) { + send_key = api->config_get_cstring(&map, "toggle"); + } + if (!send_key) { + send_key = api->config_get_cstring(&map, "select"); + } + if (strcmp(send_key, "Page_Up") && strcmp(send_key, "Page_Down") && + strcmp(send_key, "ascii_mode") && strcmp(send_key, "full_shape") && + strcmp(send_key, "simplification")) { + api->config_set_item(©_config, copy_iterator.path, &map); + api->config_next(©_iterator); + } + }; + api->config_end(&iterator); + for (auto &binding : bindings) { + RimeConfig map = {0}; + api->config_init(&map); + api->config_set_string(&map, "accept", binding.accept.data()); + api->config_set_string(&map, "when", + keyBindingConditionToString(binding.when)); + api->config_set_string(&map, keybindingTypeToString(binding.type), + binding.action.data()); + api->config_set_item(©_config, copy_iterator.path, &map); + api->config_next(©_iterator); + } + api->config_end(©_iterator); + api->config_get_item(©_config, "key_binder/bindings", ©_config_map); + api->config_set_item(&default_conf, "key_binder/bindings", + ©_config_map); +} + +void RimeConfigParser::setPageSize(int page_size) { + api->config_set_int(&default_conf, "menu/page_size", page_size); +} + +bool RimeConfigParser::getPageSize(int *page_size) { + return api->config_get_int(&default_conf, "menu/page_size", page_size); +} + +std::vector RimeConfigParser::getKeybindings() { + std::vector result; + listForeach(&default_conf, "key_binder/bindings", + [=, &result](RimeConfig *config, const char *path) { + RimeConfig map = {0}; + api->config_get_item(config, path, &map); + auto when = api->config_get_cstring(&map, "when"); + if (!when) { + return false; + } + Keybinding binding; + binding.when = keyBindingConditionFromString(when); + auto accept = api->config_get_cstring(&map, "accept"); + if (!accept) { + return false; + } + binding.accept = accept; + auto action = api->config_get_cstring(&map, "send"); + if (action) { + binding.type = KeybindingType::Send; + } else { + action = api->config_get_cstring(&map, "toggle"); + } + if (action) { + binding.type = KeybindingType::Toggle; + } else { + action = api->config_get_cstring(&map, "select"); + binding.type = KeybindingType::Select; + } + if (!action) { + return false; + } + binding.action = action; + result.push_back(std::move(binding)); + return true; + }); + return result; +} + +void RimeConfigParser::listForeach( + RimeConfig *config, const char *key, + std::function callback) { + size_t size = RimeConfigListSize(config, key); + if (!size) { + return; + } + + RimeConfigIterator iterator; + RimeConfigBeginList(&iterator, config, key); + for (size_t i = 0; i < size; i++) { + RimeConfigNext(&iterator); + if (!callback(config, iterator.path)) { + break; + } + } + RimeConfigEnd(&iterator); +} + +bool RimeConfigParser::sync() { + int page_size; + bool suc; + RimeConfig hotkeys = {0}; + RimeConfig keybindings = {0}; + RimeConfig schema_list = {0}; + std::string yaml; + + api->config_get_int(&default_conf, "menu/page_size", &page_size); + levers->customize_int(settings, "menu/page_size", page_size); + api->config_get_item(&default_conf, "switcher/hotkeys", &hotkeys); + levers->customize_item(settings, "switcher/hotkeys", &hotkeys); + api->config_get_item(&default_conf, "key_binder/bindings", &keybindings); + levers->customize_item(settings, "key_binder/bindings", &keybindings); + levers->customize_string( + settings, "ascii_composer/switch_key/Shift_L", + api->config_get_cstring(&default_conf, + "ascii_composer/switch_key/Shift_L")); + levers->customize_string( + settings, "ascii_composer/switch_key/Shift_R", + api->config_get_cstring(&default_conf, + "ascii_composer/switch_key/Shift_R")); + + /* Concatenate all active schemas */ + for (const auto &schema : schema_id_list) { + yaml += "- { schema: " + schema + " } \n"; + } + api->config_load_string(&schema_list, yaml.c_str()); + levers->customize_item(settings, "schema_list", &schema_list); + suc = levers->save_settings(settings); + if(!suc) { + return false; + } + levers->custom_settings_destroy(settings); + suc = api->start_maintenance(true); // Full check mode + if(!suc) { + return false; + } + api->finalize(); + return start(false); +} + +std::string RimeConfigParser::stringFromYAML(const char *yaml, + const char *attr) { + RimeConfig rime_schema_config = {0}; + api->config_load_string(&rime_schema_config, yaml); + auto str = api->config_get_cstring(&rime_schema_config, attr); + std::string result; + if (str) { + result = str; + } + return result; +} + +void RimeConfigParser::setSchemas(const std::vector &schemas) { + schema_id_list = schemas; + return; +} + +int RimeConfigParser::schemaIndex(const char *schema_id) { + int idx = 0; + bool found = false; + listForeach(&default_conf, "schema_list", + [=, &idx, &found](RimeConfig *config, const char *path) { + RimeConfig map = {0}; + this->api->config_get_item(config, path, &map); + auto schema = this->api->config_get_cstring(&map, "schema"); + /* This schema is enabled in default */ + if (schema && strcmp(schema, schema_id) == 0) { + found = true; + return false; + } + idx++; + return true; + }); + + return found ? (idx + 1) : 0; +} + +std::vector RimeConfigParser::getSwitchKeys() { + std::vector out; + const char *shift_l = NULL, *shift_r = NULL; + shift_l = api->config_get_cstring(&default_conf, + "ascii_composer/switch_key/Shift_L"); + shift_r = api->config_get_cstring(&default_conf, + "ascii_composer/switch_key/Shift_R"); + out.push_back(switchKeyFunctionFromString(shift_l)); + out.push_back(switchKeyFunctionFromString(shift_r)); + return out; +} + +void RimeConfigParser::setSwitchKeys( + const std::vector &switch_keys) { + if (switch_keys.size() < 2) { + return; + } + api->config_set_string(&default_conf, "ascii_composer/switch_key/Shift_L", + switchKeyFunctionToString(switch_keys[0])); + api->config_set_string(&default_conf, "ascii_composer/switch_key/Shift_R", + switchKeyFunctionToString(switch_keys[1])); + return; +} + +} // namespace fcitx_rime diff --git a/gui/src/RimeConfigParser.h b/gui/src/RimeConfigParser.h new file mode 100644 index 0000000..9f38cc6 --- /dev/null +++ b/gui/src/RimeConfigParser.h @@ -0,0 +1,97 @@ +// +// Copyright (C) 2018~2018 by xuzhao9 +// Copyright (C) 2018~2018 by Weng Xuetian +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, +// or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, +// see . +// +#ifndef _RIME_GUI_CONFIG_PARSER_H +#define _RIME_GUI_CONFIG_PARSER_H + +#include +#include +#include +#include + +namespace fcitx5_rime { + +enum class KeybindingCondition { + Composing, + HasMenu, + Paging, + Always, +}; + +enum class KeybindingType { + Send, + Toggle, + Select, +}; + +enum class SwitchKeyFunction { + Noop, + InlineASCII, + CommitText, + CommitCode, + Clear, +}; + +struct Keybinding { + KeybindingCondition when; + std::string accept; + KeybindingType type; + std::string action; +}; + +class RimeConfigParser { +public: + RimeConfigParser(); + ~RimeConfigParser(); + + bool isError(); + bool sync(); + + void setSwitchKeys(const std::vector &switch_keys); + std::vector getSwitchKeys(); + + void setToggleKeys(const std::vector &keys); + std::vector getToggleKeys(); + + void setKeybindings(const std::vector &bindings); + std::vector getKeybindings(); + + void setPageSize(int page_size); + bool getPageSize(int *page_size); + + std::string stringFromYAML(const char *yaml, const char *attr); + void setSchemas(const std::vector &schemas); + int schemaIndex(const char *schema); + +private: + bool start(bool firstRun = false); + static void + listForeach(RimeConfig *config, const char *key, + std::function); + + RimeApi *api; + RimeLeversApi *levers; + RimeCustomSettings *settings; + RimeConfig default_conf; + std::vector schema_id_list; + bool inError; +}; + +} // namespace fcitx_rime + +#endif // _RIME_GUI_CONFIG_PARSER_H diff --git a/gui/src/rime-config.json b/gui/src/rime-config.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/gui/src/rime-config.json @@ -0,0 +1,2 @@ +{ +}