diff --git a/screen-translator.pro b/screen-translator.pro
index 96485b7..65d7dd8 100644
--- a/screen-translator.pro
+++ b/screen-translator.pro
@@ -47,6 +47,7 @@ HEADERS += \
src/settings.h \
src/settingseditor.h \
src/stfwd.h \
+ src/substitutionstable.h \
src/task.h \
src/translate/translator.h \
src/translate/webpage.h \
@@ -72,6 +73,7 @@ SOURCES += \
src/service/widgetstate.cpp \
src/settings.cpp \
src/settingseditor.cpp \
+ src/substitutionstable.cpp \
src/translate/translator.cpp \
src/translate/webpage.cpp \
src/translate/webpageproxy.cpp \
diff --git a/src/settings.cpp b/src/settings.cpp
index 0d3a936..0e9a01c 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -22,7 +22,10 @@ const QString qs_lastUpdateCheck = "lastUpdateCheck";
const QString qs_recogntionGroup = "Recognition";
const QString qs_tessDataPlace = "tessdata_dir";
const QString qs_ocrLanguage = "language";
-const QString qs_imageScale = "image_scale";
+
+const QString qs_correctionGroup = "Correction";
+const QString qs_userSubstitutions = "userSubstitutions";
+const QString qs_useUserSubstitutions = "useUserSubstitutions";
const QString qs_translationGroup = "Translation";
const QString qs_doTranslation = "doTranslation";
@@ -34,7 +37,7 @@ const QString qs_translationTimeout = "translation_timeout";
const QString qs_debugMode = "translation_debug";
const QString qs_translators = "translators";
-QString shuffle(const QString &source)
+QString shuffle(const QString& source)
{
if (source.isEmpty()) {
return source;
@@ -47,6 +50,28 @@ QString shuffle(const QString &source)
return QString::fromUtf8(result.data());
}
+QStringList packSubstitutions(const Substitutions& source)
+{
+ QStringList result;
+ for (const auto& i : source) {
+ result << i.first << i.second.source << i.second.target;
+ }
+ return result;
+}
+
+Substitutions unpackSubstitutions(const QStringList& raw)
+{
+ const auto count = raw.size();
+ if (count < 3)
+ return {};
+
+ Substitutions result;
+ for (auto i = 0, end = raw.size(); i < end; i += 3) {
+ result.emplace(raw[i], Substitution{raw[i + 1], raw[i + 2]});
+ }
+ return result;
+}
+
} // namespace
void Settings::save()
@@ -82,8 +107,11 @@ void Settings::save()
settings.setValue(qs_tessDataPlace, tessdataPath);
settings.setValue(qs_ocrLanguage, sourceLanguage);
- // TODO corrector
+ settings.endGroup();
+ settings.beginGroup(qs_correctionGroup);
+ settings.setValue(qs_useUserSubstitutions, useUserSubstitutions);
+ settings.setValue(qs_userSubstitutions, packSubstitutions(userSubstitutions));
settings.endGroup();
settings.beginGroup(qs_translationGroup);
@@ -136,6 +164,13 @@ void Settings::load()
settings.endGroup();
+ settings.beginGroup(qs_correctionGroup);
+ useUserSubstitutions =
+ settings.value(qs_useUserSubstitutions, useUserSubstitutions).toBool();
+ userSubstitutions =
+ unpackSubstitutions(settings.value(qs_userSubstitutions).toStringList());
+ settings.endGroup();
+
settings.beginGroup(qs_translationGroup);
doTranslation = settings.value(qs_doTranslation, doTranslation).toBool();
diff --git a/src/settings.h b/src/settings.h
index 613a0aa..1d8a6d6 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -36,7 +36,9 @@ public:
int autoUpdateType{0}; // Never
QString lastUpdateCheck{""};
+
Substitutions userSubstitutions;
+ bool useUserSubstitutions{true};
bool debugMode{false};
diff --git a/src/settingseditor.cpp b/src/settingseditor.cpp
index 5639dd4..389bc69 100644
--- a/src/settingseditor.cpp
+++ b/src/settingseditor.cpp
@@ -45,13 +45,13 @@ SettingsEditor::SettingsEditor()
connect(ui->tessdataEdit, &QLineEdit::textChanged, //
this, &SettingsEditor::updateTesseractLanguages);
- // connect(ui->recognizerFixTable, SIGNAL(itemChanged(QTableWidgetItem *)),
- // SLOT(recognizerFixTableItemChanged(QTableWidgetItem *)));
+ // correction
- // // ui->translateLangCombo->addItems(dictionary_.translateLanguagesUi());
+ ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked());
+ connect(ui->useUserSubstitutions, &QCheckBox::toggled, //
+ ui->userSubstitutionsTable, &QTableWidget::setEnabled);
// translation
-
updateTranslationLanguages();
// updates
@@ -87,6 +87,9 @@ Settings SettingsEditor::settings() const
if (auto lang = langs.findByName(ui->tesseractLangCombo->currentText()))
settings.sourceLanguage = lang->id;
+ settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
+ settings.userSubstitutions = ui->userSubstitutionsTable->substitutions();
+
settings.doTranslation = ui->doTranslationCheck->isChecked();
settings.ignoreSslErrors = ui->ignoreSslCheck->isChecked();
settings.debugMode = ui->translatorDebugCheck->isChecked();
@@ -126,6 +129,9 @@ void SettingsEditor::setSettings(const Settings &settings)
if (auto lang = langs.findById(settings.sourceLanguage))
ui->tesseractLangCombo->setCurrentText(lang->name);
+ ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions);
+ ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions);
+
ui->doTranslationCheck->setChecked(settings.doTranslation);
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
ui->translatorDebugCheck->setChecked(settings.debugMode);
@@ -157,7 +163,6 @@ void SettingsEditor::openTessdataDialog()
void SettingsEditor::updateTesseractLanguages()
{
ui->tesseractLangCombo->clear();
- ui->correctLangCombo->clear();
QDir dir(ui->tessdataEdit->text());
if (!dir.exists())
@@ -178,11 +183,6 @@ void SettingsEditor::updateTesseractLanguages()
std::sort(names.begin(), names.end());
ui->tesseractLangCombo->addItems(names);
- ui->correctLangCombo->addItems(names);
-}
-
-void SettingsEditor::updateCorrectionsTable()
-{
}
void SettingsEditor::updateTranslators(const QString &path,
diff --git a/src/settingseditor.h b/src/settingseditor.h
index 1dba6c1..1a95568 100644
--- a/src/settingseditor.h
+++ b/src/settingseditor.h
@@ -24,7 +24,6 @@ private:
void updateCurrentPage();
void openTessdataDialog();
void updateTesseractLanguages();
- void updateCorrectionsTable();
void updateTranslators(const QString &path, const QStringList &enabled);
void updateTranslationLanguages();
diff --git a/src/settingseditor.ui b/src/settingseditor.ui
index 229224e..d2f82fe 100644
--- a/src/settingseditor.ui
+++ b/src/settingseditor.ui
@@ -255,37 +255,28 @@
- -
-
-
- <html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html>
-
-
- Language:
-
-
- tesseractLangCombo
-
-
-
- -
-
-
-
<html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html>
- Corrections
+ Substitutions
Qt::AlignCenter
+ -
+
+
+ Substitute recognized text
+
+
+
-
-
+
QAbstractItemView::SelectRows
@@ -510,6 +501,13 @@
+
+
+ SubstitutionsTable
+ QTableWidget
+
+
+
captureEdit
repeatCaptureEdit
diff --git a/src/substitutionstable.cpp b/src/substitutionstable.cpp
new file mode 100644
index 0000000..ac70f2a
--- /dev/null
+++ b/src/substitutionstable.cpp
@@ -0,0 +1,142 @@
+#include "substitutionstable.h"
+#include "debug.h"
+#include "languagecodes.h"
+
+#include
+#include
+#include
+#include
+
+namespace
+{
+QStringList allSourceLanguages()
+{
+ LanguageCodes langs;
+ const auto &allLangs = langs.all();
+
+ QStringList result;
+ result.reserve(allLangs.size());
+ for (const auto &i : allLangs) {
+ if (i.second.tesseract.isEmpty())
+ continue;
+ result.append(i.second.name);
+ }
+ return result;
+}
+
+class SubstitutionDelegate : public QStyledItemDelegate
+{
+public:
+ explicit SubstitutionDelegate(QObject *parent = nullptr)
+ : QStyledItemDelegate(parent)
+ {
+ }
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override
+ {
+ painter->drawText(option.rect, Qt::AlignCenter, index.data().toString());
+ }
+};
+
+} // namespace
+
+SubstitutionsTable::SubstitutionsTable(QWidget *parent)
+ : QTableWidget(parent)
+ , languagesModel_(new QStringListModel(allSourceLanguages(), this))
+{
+ setItemDelegate(new SubstitutionDelegate(this));
+ setColumnCount(int(Column::Count));
+ setHorizontalHeaderLabels({tr("Language"), tr("Source"), tr("Changed")});
+ connect(this, &SubstitutionsTable::itemChanged, //
+ this, &SubstitutionsTable::handleItemChange);
+}
+
+void SubstitutionsTable::setSubstitutions(const Substitutions &substitutions)
+{
+ for (const auto &i : substitutions) addRow(i.first, i.second);
+ addRow(); // for editing
+ resizeColumnsToContents();
+}
+
+Substitutions SubstitutionsTable::substitutions() const
+{
+ Substitutions result;
+ for (auto row = 0, end = rowCount(); row < end; ++row) {
+ const auto pair = at(row);
+ SOFT_ASSERT(!pair.first.isEmpty(), continue);
+ if (pair.second.source.isEmpty())
+ continue;
+ result.emplace(pair.first, pair.second);
+ }
+ return result;
+}
+
+void SubstitutionsTable::addRow(const LanguageId &language,
+ const Substitution &substutution)
+{
+ QSignalBlocker blocker(this);
+ auto row = rowCount();
+ insertRow(row);
+
+ auto combo = new QComboBox(this);
+ combo->setModel(languagesModel_);
+
+ using E = Column;
+ if (!language.isEmpty()) {
+ LanguageCodes langs;
+ if (auto lang = langs.findById(language))
+ combo->setCurrentText(lang->name);
+ } else if (rowCount() > 1) {
+ const auto previousRow = rowCount() - 2;
+ auto previousCombo =
+ static_cast(cellWidget(previousRow, int(E::Language)));
+ SOFT_ASSERT(previousCombo, return );
+ combo->setCurrentText(previousCombo->currentText());
+ }
+
+ setCellWidget(row, int(E::Language), combo);
+ setItem(row, int(E::Source), new QTableWidgetItem(substutution.source));
+ setItem(row, int(E::Target), new QTableWidgetItem(substutution.target));
+}
+
+std::pair SubstitutionsTable::at(int row) const
+{
+ SOFT_ASSERT(row >= 0 && row < rowCount(), return {});
+
+ using E = Column;
+ auto combo = static_cast(cellWidget(row, int(E::Language)));
+ SOFT_ASSERT(combo, return {});
+
+ LanguageCodes langs;
+ auto lang = langs.findByName(combo->currentText());
+ SOFT_ASSERT(lang, return {});
+
+ Substitution sub;
+ auto sourceItem = item(row, int(E::Source));
+ SOFT_ASSERT(sourceItem, return {});
+ sub.source = sourceItem->text();
+
+ auto targetItem = item(row, int(E::Target));
+ SOFT_ASSERT(targetItem, return {});
+ sub.target = targetItem->text();
+
+ return std::make_pair(lang->id, sub);
+}
+
+void SubstitutionsTable::handleItemChange(QTableWidgetItem *item)
+{
+ if (item->column() != int(Column::Source))
+ return;
+
+ const auto lastRow = rowCount() - 1;
+ if (!item->text().isEmpty() && item->row() == lastRow) {
+ addRow();
+ return;
+ }
+
+ if (item->text().isEmpty() && item->row() != lastRow) {
+ removeRow(item->row());
+ return;
+ }
+}
diff --git a/src/substitutionstable.h b/src/substitutionstable.h
new file mode 100644
index 0000000..165302c
--- /dev/null
+++ b/src/substitutionstable.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "settings.h"
+
+#include
+
+class QStringListModel;
+
+class SubstitutionsTable : public QTableWidget
+{
+ Q_OBJECT
+public:
+ enum class Column { Language = 0, Source, Target, Count };
+
+ explicit SubstitutionsTable(QWidget* parent = nullptr);
+
+ void setSubstitutions(const Substitutions& substitutions);
+ Substitutions substitutions() const;
+
+private:
+ void handleItemChange(QTableWidgetItem* item);
+ void addRow(const LanguageId& language = {},
+ const Substitution& substutution = {});
+ std::pair at(int row) const;
+
+ QStringListModel* languagesModel_;
+};