123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*****************************************************************************
- * settingsdialog.cpp - QStarDict, a dictionary application for learning *
- * foreign languages *
- * Copyright (C) 2007-2023 Alexander Rodin *
- * Copyright (C) 2016 Sergey Il'inykh *
- * *
- * 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 "settingsdialog.h"
- #include <QFileDialog>
- #include <QMessageBox>
- #include <QStandardItemModel>
- #include <QHeaderView>
- #include <QInputDialog>
- #include <QSettings>
- #include <QKeySequence>
- #include <math.h>
- #include <QStyledItemDelegate>
- #include <QPainter>
- #include <QTimer>
- #include "dictcore.h"
- #include "mainwindow.h"
- #include "popupwindow.h"
- #include "application.h"
- #include "speaker.h"
- #include "pluginsmodel.h"
- #include "pluginmanager.h"
- #include "qxt/qxtglobalshortcut.h"
- #include "../plugins/dictplugin.h"
- #include "../plugins/pluginserver.h"
- #ifdef QSTARDICT_WITH_TRAY_ICON
- #include "trayicon.h"
- #endif
- namespace
- {
- int toPercents(double value)
- {
- int integralValue = static_cast<int>(value * 100.0);
- if (value * 100.0 - 0.5 > integralValue)
- ++integralValue;
- return integralValue;
- }
- QString getModifierName(int key)
- {
- switch (key)
- {
- case Qt::ShiftModifier:
- return "Shift";
- case Qt::ControlModifier:
- #ifdef Q_OS_MAC
- return "Command";
- #else
- return "Control";
- #endif
- case Qt::AltModifier:
- #ifdef Q_OS_MAC
- return "Option";
- #else
- return "Alt";
- #endif
- case Qt::MetaModifier:
- #ifdef Q_OS_MAC
- return "Control";
- #elif Q_OS_WIN
- return "Win";
- #else
- return "Super";
- #endif
- default:
- return "";
- }
- }
- void initModifierComboBox(QComboBox *box, int selectedKey)
- {
- box->clear();
- box->addItem(getModifierName(Qt::AltModifier), Qt::AltModifier);
- box->addItem(getModifierName(Qt::ShiftModifier), Qt::ShiftModifier);
- box->addItem(getModifierName(Qt::ControlModifier), Qt::ControlModifier);
- box->addItem(getModifierName(Qt::MetaModifier), Qt::MetaModifier);
- box->setCurrentIndex(qMax(0, box->findData(selectedKey)));
- }
- }
- namespace QStarDict {
- SettingsDialog::SettingsDialog(QWidget *parent)
- : QDialog(parent)
- {
- setupUi(this);
- Application * const app = Application::instance();
- DictCore *dict = app->dictCore();
- m_oldPlugins = app->pluginManager()->loadedPlugins();
- m_oldDicts = dict->loadedDicts();
- m_dictPluginsModel = new PluginsModel(PluginsModel::LoadType::JustDict, app->pluginManager());
- m_miscPluginsModel = new PluginsModel(PluginsModel::LoadType::ExceptDict, app->pluginManager());
- pluginsTableView->setModel(m_dictPluginsModel);
- miscPluginsView->setModel(m_miscPluginsModel);
- pluginsTableView->configureColumns();
- miscPluginsView->configureColumns();
- m_dictsModel = new QStandardItemModel(this);
- m_dictsModel->setHorizontalHeaderLabels(QStringList() << tr("Enabled") << tr("Name") << tr("Plugin"));
- loadDictsList();
- dictsTableView->setModel(m_dictsModel);
- dictsTableView->verticalHeader()->hide();
- dictsTableView->setColumnWidth(0, 60);
- dictsTableView->setColumnWidth(1, 270);
- dictsTableView->setColumnWidth(2, 120);
- // Load global settings
- runInBackgroundBox->setChecked(!app->mainWindow()->quitOnClose());
- instantSearchBox->setChecked(app->mainWindow()->isInstantSearch());
- speechCmdEdit->setText(app->speaker()->speechCmd());
- #ifdef Q_OS_LINUX
- QFile desktop(QDir::homePath() + "/.config/autostart/qstardict.desktop");
- if (desktop.open(QIODevice::ReadOnly) && QString(desktop.readAll())
- .contains(QRegExp("\\bhidden\\s*=\\s*false", Qt::CaseInsensitive))) {
- autostartBox->setChecked(true);
- }
- #elif defined(Q_OS_WIN)
- QSettings reg("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Run", QSettings::NativeFormat);
- autostartBox->setChecked(
- reg.contains(QCoreApplication::applicationName()));
- #else
- autostartBox->setVisible(false);
- #endif
- linksBox->setChecked(app->mainWindow()->showLinks());
- linksModifierBox->setChecked(app->mainWindow()->showLinksModifierKey() != 0);
- initModifierComboBox(linksModifierKeyBox, app->mainWindow()->showLinksModifierKey());
- ipaPronounceBox->setChecked(app->mainWindow()->showIpaPronouncers());
- espeakCmdEdit->setText(app->espeakSpeaker()->speechCmd());
- // Load popup shortcut settings
- shortcutPopupEdit->setText(app->popupShortcut()->shortcut().toString());
- shortcutPopupBox->setChecked(app->popupShortcut()->isEnabled());
- switchScanningEdit->setText(app->switchScanningShortcut()->shortcut().toString());
- switchScanningBox->setChecked(app->switchScanningShortcut()->isEnabled());
- // Load popup window settings
- PopupWindow *popup = app->popupWindow();
- useScanBox->setChecked(popup->isScan());
- useScanModifierBox->setChecked(popup->modifierKey() != 0);
- initModifierComboBox(modifierKeyBox, popup->modifierKey());
- showIfNotFoundBox->setChecked(popup->showIfNotFound());
- popupOpacitySpin->setValue(toPercents(popup->windowOpacity()));
- timeoutBeforeHideSpin->setValue(popup->timeoutBeforeHide() / 1000.0);
- popupDefaultWidthSpin->setValue(popup->defaultSize().width());
- popupDefaultHeightSpin->setValue(popup->defaultSize().height());
- pronounceWordBox->setChecked(popup->pronounceWord());
- // Load translations CSS
- QVector<QPair<QString, QString>> cssAliases;
- cssAliases.append({"body", tr("All translation")});
- cssAliases.append({"font.dict_name", tr("Dictionary name")});
- cssAliases.append({"font.title", tr("Title")});
- cssAliases.append({"font.transcription", tr("Transcription")});
- cssAliases.append({"font.explanation", tr("Explanation")});
- cssAliases.append({"font.abbreviature", tr("Abbreviation")});
- cssAliases.append({"font.example", tr("Example")});
- appearanceCSSEdit->setElementsNames(cssAliases);
- appearanceCSSEdit->setCSS(app->mainWindow()->defaultStyleSheet());
- connect(m_dictPluginsModel, SIGNAL(loadedListChanged()),
- SLOT(dictLoadedPluginsChanged()));
- connect(pluginsTableView, SIGNAL(clicked(QModelIndex)), SLOT(pluginClicked(QModelIndex)));
- connect(miscPluginsView, SIGNAL(clicked(QModelIndex)), SLOT(pluginClicked(QModelIndex)));
- connect(dictsTableView, SIGNAL(clicked(QModelIndex)), SLOT(dictSelected()));
- // There is no signal emitted when an dictionary item is selected using keyboard,
- // not mouse. So it is necessary check whether the item selection changed or not
- // periodically.
- m_dictsTimer = new QTimer(this);
- m_dictsTimer->setInterval(100);
- connect(m_dictsTimer, SIGNAL(timeout()), SLOT(dictSelected()));
- m_dictsTimer->start();
- linksModifierContainer->setEnabled(linksBox->isChecked());
- linksModifierKeyBox->setEnabled(linksModifierBox->isChecked());
- espeakCmdLabel->setEnabled(ipaPronounceBox->isChecked());
- espeakCmdEdit->setEnabled(ipaPronounceBox->isChecked());
- scanModifierContainer->setEnabled(useScanBox->isChecked());
- modifierKeyBox->setEnabled(useScanModifierBox->isChecked());
- shortcutPopupEdit->setEnabled(shortcutPopupBox->isChecked());
- switchScanningEdit->setEnabled(switchScanningBox->isChecked());
- }
- void SettingsDialog::accept()
- {
- Application * const app = Application::instance();
- // Save dicts and plugins settings
- DictCore *dict = app->dictCore();
- //dict->setLoadedPlugins(m_dictPluginsModel->loadedPlugins() + m_miscPluginsModel->loadedPlugins());
- QList<DictCore::Dictionary> loadedDicts;
- int rowCount = m_dictsModel->rowCount();
- for (int i = 0; i < rowCount; ++i)
- if (m_dictsModel->item(i, 0)->checkState() == Qt::Checked)
- loadedDicts << DictCore::Dictionary(m_dictsModel->item(i, 2)->text(), m_dictsModel->item(i, 1)->text());
- dict->setLoadedDicts(loadedDicts);
- // Save global settings
- app->mainWindow()->setQuitOnClose(!runInBackgroundBox->isChecked());
- #ifdef QSTARDICT_WITH_TRAY_ICON
- app->trayIcon()->setVisible(runInBackgroundBox->isChecked());
- #endif
- app->mainWindow()->setInstantSearch(instantSearchBox->isChecked());
- app->speaker()->setSpeechCmd(speechCmdEdit->text());
- #ifdef Q_OS_LINUX
- QDir home = QDir::home();
- if (!home.exists(".config/autostart")) {
- home.mkpath(".config/autostart");
- }
- QFile desktopFile(QSTARDICT_INSTALL_PREFIX "/share/applications/qstardict.desktop");
- if (desktopFile.open(QIODevice::ReadOnly)) {
- QByteArray contents = desktopFile.readAll();
- QFile f(home.absolutePath() +
- "/.config/autostart/qstardict.desktop");
- if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- f.write(contents.trimmed());
- f.write(QString("\nHidden=%1").arg(autostartBox->isChecked()?
- "false\n":"true\n").toUtf8());
- }
- }
- #elif defined(Q_OS_WIN)
- QSettings reg("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Run", QSettings::NativeFormat);
- if(autostartBox->isChecked())
- reg.setValue(QCoreApplication::applicationName(), '"' +
- QDir::toNativeSeparators(QCoreApplication::
- applicationFilePath()) + '"');
- else
- reg.remove(QCoreApplication::applicationName());
- #endif
- app->mainWindow()->setShowLinks(linksBox->isChecked());
- app->popupWindow()->setShowLinks(linksBox->isChecked());
- int linksModifierKey = linksModifierBox->isChecked() ? linksModifierKeyBox->currentData().toInt() : 0;
- app->mainWindow()->setShowLinksModifierKey(linksModifierKey);
- app->popupWindow()->setShowLinksModifierKey(linksModifierKey);
- app->mainWindow()->setShowIpaPronouncers(ipaPronounceBox->isChecked());
- app->popupWindow()->setShowIpaPronouncers(ipaPronounceBox->isChecked());
- app->espeakSpeaker()->setSpeechCmd(espeakCmdEdit->text());
- // Save popup shortcut settings
- app->popupShortcut()->setShortcut(QKeySequence(shortcutPopupEdit->text()));
- app->popupShortcut()->setEnabled(shortcutPopupBox->isChecked());
- app->switchScanningShortcut()->setShortcut(QKeySequence(switchScanningEdit->text()));
- app->switchScanningShortcut()->setEnabled(switchScanningBox->isChecked());
- // Save popup window settings
- PopupWindow *popup = app->popupWindow();
- popup->setScan(useScanBox->isChecked());
- popup->setShowIfNotFound(showIfNotFoundBox->isChecked());
- if (useScanModifierBox->isChecked())
- popup->setModifierKey(modifierKeyBox->currentData().toInt());
- else
- popup->setModifierKey(0);
- popup->setWindowOpacity(popupOpacitySpin->value() / 100.0);
- popup->setTimeoutBeforeHide(static_cast<int>(timeoutBeforeHideSpin->value() * 1000.0));
- popup->setDefaultSize(QSize(popupDefaultWidthSpin->value(), popupDefaultHeightSpin->value()));
- popup->setPronounceWord(pronounceWordBox->isChecked());
- // Save translations CSS
- app->mainWindow()->setDefaultStyleSheet(appearanceCSSEdit->css());
- app->popupWindow()->setDefaultStyleSheet(appearanceCSSEdit->css());
- if (app->mainWindow()->quitOnClose())
- app->mainWindow()->show();
- app->mainWindow()->reload();
- app->mainWindow()->reloadToolbar();
- app->popupWindow()->reloadToolbar();
- app->dictCore()->saveSettings();
- app->mainWindow()->saveSettings();
- #ifdef QSTARDICT_WITH_TRAY_ICON
- app->trayIcon()->saveSettings();
- #endif
- app->popupWindow()->saveSettings();
- QDialog::accept();
- }
- void SettingsDialog::reject()
- {
- DictCore *dict = Application::instance()->dictCore();
- Application::instance()->pluginManager()->setLoadedPlugins(m_oldPlugins);
- dict->setLoadedDicts(m_oldDicts);
- QDialog::reject();
- }
- void SettingsDialog::loadDictsList()
- {
- int i;
- QList<DictCore::Dictionary> loadedDicts = Application::instance()->dictCore()->loadedDicts();
- m_dictsModel->setRowCount(0);
- for (i = 0; i < loadedDicts.size(); ++i)
- {
- QStandardItem *item = new QStandardItem();
- item->setCheckable(true);
- item->setCheckState(Qt::Checked);
- m_dictsModel->setItem(i, 0, item);
- m_dictsModel->setItem(i, 1, new QStandardItem(loadedDicts[i].name()));
- m_dictsModel->setItem(i, 2, new QStandardItem(loadedDicts[i].plugin()));
- }
- QList<DictCore::Dictionary> dicts = Application::instance()->dictCore()->availableDicts();
- for (QList<DictCore::Dictionary>::const_iterator iter = dicts.begin(); iter != dicts.end(); ++iter)
- {
- if (! loadedDicts.contains(*iter))
- {
- QStandardItem *item = new QStandardItem();
- item->setCheckable(true);
- item->setCheckState(Qt::Unchecked);
- m_dictsModel->setItem(i, 0, item);
- m_dictsModel->setItem(i, 1, new QStandardItem(iter->name()));
- m_dictsModel->setItem(i, 2, new QStandardItem(iter->plugin()));
- ++i;
- }
- }
- dictSelected();
- }
- void SettingsDialog::dictSelected()
- {
- int currentRow = dictsTableView->currentIndex().row();
- if (currentRow == -1)
- {
- dictsMoveUpButton->setEnabled(false);
- dictsMoveDownButton->setEnabled(false);
- dictsRemoveButton->setEnabled(false);
- dictsShowInfoButton->setEnabled(false);
- }
- else
- {
- dictsMoveUpButton->setEnabled(currentRow > 0);
- dictsMoveDownButton->setEnabled(currentRow < m_dictsModel->rowCount() - 1);
- QString dict = m_dictsModel->item(currentRow, 1)->text();
- QString pluginName = m_dictsModel->item(currentRow, 2)->text();
- DictPlugin *plugin = Application::instance()->pluginManager()->plugin<DictPlugin>(pluginName);
- dictsRemoveButton->setEnabled(plugin->isDictionaryRemovable(dict));
- dictsShowInfoButton->setEnabled(true);
- }
- }
- void SettingsDialog::on_dictsMoveUpButton_clicked()
- {
- int currentRow = dictsTableView->currentIndex().row();
- if (currentRow > 0)
- {
- m_dictsModel->insertRow(currentRow - 1, m_dictsModel->takeRow(currentRow));
- dictsTableView->selectRow(currentRow - 1);
- }
- }
- void SettingsDialog::on_dictsMoveDownButton_clicked()
- {
- int currentRow = dictsTableView->currentIndex().row();
- if (currentRow < m_dictsModel->rowCount() - 1)
- {
- m_dictsModel->insertRow(currentRow + 1, m_dictsModel->takeRow(currentRow));
- dictsTableView->selectRow(currentRow + 1);
- }
- }
- void SettingsDialog::on_dictsShowInfoButton_clicked()
- {
- int currentRow = dictsTableView->currentIndex().row();
- if (currentRow == -1)
- return;
- QString dict = m_dictsModel->item(currentRow, 1)->text();
- QString plugin = m_dictsModel->item(currentRow, 2)->text();
- DictPlugin::DictInfo info = Application::instance()->pluginManager()->plugin<DictPlugin>(plugin)->dictInfo(dict);
- QMessageBox::information(this,
- tr("Information about dictionary \"%1\"").arg(dict),
- tr("<b>Name:</b> %1<br>").arg(dict) +
- tr("<b>Plugin:</b> %1<br>").arg(plugin) +
- (info.filename().isEmpty() ? "" : tr("<b>Filename:</b> %1<br>").arg(info.filename())) +
- tr("<b>Author:</b> %1<br>").arg(info.author()) +
- tr("<b>Words count:</b> %1<br>").arg((info.wordsCount() == -1) ? tr("unknown") : QString::number(info.wordsCount())) +
- (info.description().isEmpty() ? "" : tr("<b>Description:</b> %1").arg(info.description())));
- }
- void SettingsDialog::pluginClicked(const QModelIndex &index)
- {
- QString id = index.data(PluginsModel::IdRole).toString();
- if (index.column() == 2) {// settings
- auto plugin = Application::instance()->pluginManager()->plugin<ConfigurablePlugin>(id);
- if (plugin && plugin->execSettingsDialog(this) == QDialog::Accepted)
- {
- Application::instance()->dictCore()->reloadDicts();
- loadDictsList();
- }
- }
- if (index.column() == 3) {
- auto pm = Application::instance()->pluginManager();
- DictPlugin *dplugin = pm->plugin<DictPlugin>(id);
- const PluginMetadata &md = pm->pluginDesc(id)->metadata;
- QStringList authors = md.authors;
- auto *messageBox = new QMessageBox(this);
- messageBox->setIconPixmap(md.icon.pixmap(QSize(128, 128)));
- messageBox->setWindowTitle(tr("Information about %1 plugin").arg(md.name));
- messageBox->setText(
- tr("<b>Name:</b> %1<br>").arg(md.name) +
- tr("<b>Version:</b> %1<br>").arg(md.version) +
- tr("<b>Authors:</b> %1<br>").arg(authors.replaceInStrings("<", "<").replaceInStrings(">", ">").join(tr("<br>"))) +
- (dplugin? tr("<b>Can search similar words:</b> %1<br>").arg(dplugin->features().testFlag(DictPlugin::Feature::SearchSimilar) ? tr("yes") : tr("no")) : "") +
- tr("<b>Description:</b> %1").arg(md.description));
- messageBox->show();
- }
- }
- void SettingsDialog::dictLoadedPluginsChanged()
- {
- DictCore *dict = Application::instance()->dictCore();
- Application::instance()->pluginManager()->setLoadedPlugins(m_dictPluginsModel->loadedPlugins() + m_miscPluginsModel->loadedPlugins());
- dict->reloadDicts();
- loadDictsList();
- }
- void SettingsDialog::on_dictsAddButton_clicked()
- {
- auto pluginManager = Application::instance()->pluginManager();
- QStringList filters;
- QHash<QString, QString> pluginsByFilters;
- for (const QString &pluginId: pluginManager->loadedPlugins()) {
- DictPlugin *plugin = pluginManager->plugin<DictPlugin>(pluginId);
- if (!plugin)
- continue;
- std::optional<QString> dictionaryFileFilter = plugin->dictionaryFileFilter();
- if (!dictionaryFileFilter)
- continue;
- filters.push_back(*dictionaryFileFilter);
- pluginsByFilters[*dictionaryFileFilter] = pluginId;
- }
- QFileDialog fileDialog(this, tr("Import a dictionary from a file"),
- QString(), filters.join(";;"));
- if (!fileDialog.exec())
- return;
- QString selectedPluginId = pluginsByFilters[fileDialog.selectedNameFilter()];
- DictPlugin *selectedPlugin = pluginManager->plugin<DictPlugin>(selectedPluginId);
- QString selectedFileName = fileDialog.selectedFiles().at(0);
- selectedPlugin->loadedDicts();
- std::optional<QString> dict = selectedPlugin->addDictionary(this, selectedFileName);
- if (!dict)
- return;
- Application::instance()->dictCore()->reloadDicts();
- loadDictsList();
- // Move the added dictionary to the top and enable it
- for (int i = 0; i < m_dictsModel->rowCount(); ++i)
- {
- if (m_dictsModel->item(i, 1)->text() == *dict)
- {
- m_dictsModel->insertRow(0, m_dictsModel->takeRow(i));
- m_dictsModel->item(0, 0)->setCheckState(Qt::Checked);
- dictsTableView->selectRow(0);
- dictSelected();
- break;
- }
- }
- }
- void SettingsDialog::on_dictsRemoveButton_clicked()
- {
- int currentRow = dictsTableView->currentIndex().row();
- if (currentRow == -1)
- return;
- QString dict = m_dictsModel->item(currentRow, 1)->text();
- QString pluginName = m_dictsModel->item(currentRow, 2)->text();
- DictPlugin *plugin = Application::instance()->pluginManager()->plugin<DictPlugin>(pluginName);
- if (! plugin->removeDictionary(dict))
- {
- QMessageBox::warning(this, tr("Unable to remove dictionary"),
- tr("Cannot remove dictionary %1 with plugin %1").arg(dict, pluginName));
- }
- else
- {
- DictCore *dict = Application::instance()->dictCore();
- dict->reloadDicts();
- loadDictsList();
- }
- }
- }
- // vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab cindent textwidth=120 formatoptions=tc
|