2020-10-14 08:37:03 +08:00
|
|
|
#include "qtcommon.h"
|
|
|
|
#include "extension.h"
|
|
|
|
#include "blockmarkup.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include <map>
|
|
|
|
#include <fstream>
|
|
|
|
#include <QComboBox>
|
|
|
|
#include "devtools.h"
|
|
|
|
|
|
|
|
extern const char* NATIVE_LANGUAGE;
|
|
|
|
extern const char* TRANSLATE_TO;
|
|
|
|
extern const char* TRANSLATE_SELECTED_THREAD_ONLY;
|
|
|
|
extern const char* USE_TRANS_CACHE;
|
|
|
|
extern const char* MAX_SENTENCE_SIZE;
|
|
|
|
extern const char* TRANSLATION_PROVIDER;
|
2020-11-04 06:23:20 +08:00
|
|
|
extern QStringList languagesTo;
|
|
|
|
extern QStringList languagesFrom;
|
|
|
|
const char* TRANSLATE_FROM = u8"Translate from";
|
|
|
|
const char* NATIVE_LANGUAGE_FROM = u8"Japanese";
|
|
|
|
const char* PATH_TO_CHROME = u8"Path to Chrome";
|
|
|
|
const char* AUTO_START_CHROME = u8"Start Chrome automatically";
|
2020-10-14 08:37:03 +08:00
|
|
|
const char* HEADLESS_CHROME = u8"Start in headless mode";
|
|
|
|
const char* CHROME_DEBUG_PORT = u8"Chrome debug port";
|
|
|
|
const char* DEV_TOOLS_STATUS = u8"Status: ";
|
|
|
|
const char* START_DEV_TOOLS_BUTTON = u8"Start";
|
2020-11-04 06:23:20 +08:00
|
|
|
const char* START_DEV_TOOLS = u8"Start Chrome";
|
2020-10-14 08:37:03 +08:00
|
|
|
const char* STOP_DEV_TOOLS_BUTTON = u8"Stop";
|
2020-11-04 06:23:20 +08:00
|
|
|
const char* STOP_DEV_TOOLS = u8"Stop Chrome";
|
2020-10-14 08:37:03 +08:00
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
extern bool useCache, autoStartChrome, headlessChrome;
|
|
|
|
extern int maxSentenceSize, chromePort;
|
2020-10-14 08:37:03 +08:00
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
std::pair<bool, std::wstring> Translate(const std::wstring& text, DevTools* devTools);
|
2020-10-14 08:37:03 +08:00
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
const char* LANGUAGE_TO = u8"Language";
|
|
|
|
const char* LANGUAGE_FROM = u8"Language from";
|
2020-10-14 08:37:03 +08:00
|
|
|
const std::string TRANSLATION_CACHE_FILE = FormatString("%s Cache.txt", TRANSLATION_PROVIDER);
|
|
|
|
|
|
|
|
QFormLayout* display;
|
2020-11-04 06:23:20 +08:00
|
|
|
Settings settings;
|
|
|
|
Synchronized<std::wstring> translateTo = L"en", translateFrom = L"ja";
|
2020-10-14 08:37:03 +08:00
|
|
|
Synchronized<std::map<std::wstring, std::wstring>> translationCache;
|
|
|
|
|
|
|
|
int savedSize;
|
2020-11-04 06:23:20 +08:00
|
|
|
DevTools* devTools = nullptr;
|
|
|
|
std::wstring pathToChrome = L"";
|
2020-10-14 08:37:03 +08:00
|
|
|
|
|
|
|
void SaveCache()
|
|
|
|
{
|
|
|
|
std::wstring allTranslations(L"\xfeff");
|
|
|
|
for (const auto& [sentence, translation] : translationCache.Acquire().contents)
|
|
|
|
allTranslations.append(L"|SENTENCE|").append(sentence).append(L"|TRANSLATION|").append(translation).append(L"|END|\r\n");
|
|
|
|
std::ofstream(TRANSLATION_CACHE_FILE, std::ios::binary | std::ios::trunc).write((const char*)allTranslations.c_str(), allTranslations.size() * sizeof(wchar_t));
|
|
|
|
savedSize = translationCache->size();
|
|
|
|
}
|
|
|
|
|
2020-10-20 07:08:59 +08:00
|
|
|
void EraseControlCharacters(std::wstring& text)
|
|
|
|
{
|
|
|
|
for (auto it = text.begin(); it!= text.end(); ++it)
|
|
|
|
{
|
|
|
|
if ((*it == '\n') || (*it == '\r') || (*it == '\t') || (int(*it) == 4) || (int(*it) == 5))
|
|
|
|
{
|
|
|
|
text.erase(it--);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 08:37:03 +08:00
|
|
|
class Window : public QDialog
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Window() :
|
|
|
|
QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
|
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
localize();
|
2020-10-14 08:37:03 +08:00
|
|
|
display = new QFormLayout(this);
|
|
|
|
settings.beginGroup(TRANSLATION_PROVIDER);
|
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
auto languageBoxTo = new QComboBox(this);
|
|
|
|
languageBoxTo->addItems(languagesTo);
|
|
|
|
int languageTo = -1;
|
|
|
|
if (settings.contains(LANGUAGE_TO)) languageTo = languageBoxTo->findText(settings.value(LANGUAGE_TO).toString(), Qt::MatchEndsWith);
|
|
|
|
if (languageTo < 0) languageTo = languageBoxTo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith);
|
|
|
|
if (languageTo < 0) languageTo = languageBoxTo->findText("English", Qt::MatchStartsWith);
|
|
|
|
languageBoxTo->setCurrentIndex(languageTo);
|
|
|
|
saveLanguage(languageBoxTo->currentText());
|
|
|
|
display->addRow(TRANSLATE_TO, languageBoxTo);
|
|
|
|
connect(languageBoxTo, &QComboBox::currentTextChanged, this, &Window::saveLanguage);
|
|
|
|
|
|
|
|
auto languageBoxFrom = new QComboBox(this);
|
|
|
|
languageBoxFrom->addItems(languagesFrom);
|
|
|
|
int languageFrom = -1;
|
|
|
|
if (settings.contains(LANGUAGE_FROM)) languageFrom = languageBoxFrom->findText(settings.value(LANGUAGE_FROM).toString(), Qt::MatchEndsWith);
|
|
|
|
if (languageFrom < 0) languageFrom = languageBoxFrom->findText(NATIVE_LANGUAGE_FROM, Qt::MatchStartsWith);
|
|
|
|
if (languageFrom < 0) languageFrom = languageBoxFrom->findText("Japanese", Qt::MatchStartsWith);
|
|
|
|
languageBoxFrom->setCurrentIndex(languageFrom);
|
|
|
|
saveLanguageFrom(languageBoxFrom->currentText());
|
|
|
|
display->addRow(TRANSLATE_FROM, languageBoxFrom);
|
|
|
|
connect(languageBoxFrom, &QComboBox::currentTextChanged, this, &Window::saveLanguageFrom);
|
2020-10-14 08:37:03 +08:00
|
|
|
for (auto [value, label] : Array<bool&, const char*>{
|
|
|
|
{ useCache, USE_TRANS_CACHE },
|
2020-11-04 06:23:20 +08:00
|
|
|
{ autoStartChrome, AUTO_START_CHROME },
|
|
|
|
//{ headlessChrome, HEADLESS_CHROME }
|
2020-10-14 08:37:03 +08:00
|
|
|
})
|
|
|
|
{
|
|
|
|
value = settings.value(label, value).toBool();
|
|
|
|
auto checkBox = new QCheckBox(this);
|
|
|
|
checkBox->setChecked(value);
|
|
|
|
display->addRow(label, checkBox);
|
|
|
|
connect(checkBox, &QCheckBox::clicked, [label, &value](bool checked) { settings.setValue(label, value = checked); });
|
|
|
|
}
|
|
|
|
for (auto [value, label] : Array<int&, const char*>{
|
|
|
|
{ maxSentenceSize, MAX_SENTENCE_SIZE },
|
2020-11-04 06:23:20 +08:00
|
|
|
{ chromePort, CHROME_DEBUG_PORT },
|
2020-10-14 08:37:03 +08:00
|
|
|
})
|
|
|
|
{
|
|
|
|
value = settings.value(label, value).toInt();
|
|
|
|
auto spinBox = new QSpinBox(this);
|
|
|
|
spinBox->setRange(0, INT_MAX);
|
|
|
|
spinBox->setValue(value);
|
|
|
|
display->addRow(label, spinBox);
|
|
|
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [label, &value](int newValue) { settings.setValue(label, value = newValue); });
|
|
|
|
}
|
|
|
|
|
|
|
|
auto keyInput = new QLineEdit(settings.value(PATH_TO_CHROME).toString());
|
2020-11-04 06:23:20 +08:00
|
|
|
pathToChrome = (S(keyInput->text()));
|
|
|
|
if (pathToChrome.empty())
|
2020-10-14 08:37:03 +08:00
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
for (auto defaultPath : Array<std::wstring>{
|
2020-10-14 08:37:03 +08:00
|
|
|
{ L"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" },
|
|
|
|
{ L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" },
|
|
|
|
})
|
2020-11-04 06:23:20 +08:00
|
|
|
if (std::filesystem::exists(defaultPath))
|
2020-10-14 08:37:03 +08:00
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
pathToChrome = defaultPath;
|
|
|
|
keyInput->setText(S(pathToChrome));
|
2020-10-14 08:37:03 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-04 06:23:20 +08:00
|
|
|
connect(keyInput, &QLineEdit::textChanged, [keyInput](QString key) { settings.setValue(PATH_TO_CHROME, S(pathToChrome = (S(key)))); });
|
2020-10-14 08:37:03 +08:00
|
|
|
display->addRow(PATH_TO_CHROME, keyInput);
|
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
connect(&startButton, &QPushButton::clicked, this, &Window::Start);
|
|
|
|
connect(&stopButton, &QPushButton::clicked, this, &Window::Stop);
|
2020-10-14 08:37:03 +08:00
|
|
|
display->addRow(START_DEV_TOOLS, &startButton);
|
|
|
|
display->addRow(STOP_DEV_TOOLS, &stopButton);
|
|
|
|
|
|
|
|
status.setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
|
|
|
display->addRow(DEV_TOOLS_STATUS, &status);
|
|
|
|
|
|
|
|
setWindowTitle(TRANSLATION_PROVIDER);
|
|
|
|
QMetaObject::invokeMethod(this, &QWidget::show, Qt::QueuedConnection);
|
|
|
|
|
|
|
|
std::ifstream stream(TRANSLATION_CACHE_FILE, std::ios::binary);
|
|
|
|
BlockMarkupIterator savedTranslations(stream, Array<std::wstring_view>{ L"|SENTENCE|", L"|TRANSLATION|" });
|
|
|
|
auto translationCache = ::translationCache.Acquire();
|
|
|
|
|
|
|
|
while (auto read = savedTranslations.Next())
|
|
|
|
{
|
|
|
|
auto& [sentence, translation] = read.value();
|
|
|
|
translationCache->try_emplace(std::move(sentence), std::move(translation));
|
|
|
|
}
|
|
|
|
savedSize = translationCache->size();
|
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
devTools = new DevTools(this);
|
|
|
|
connect(devTools, &DevTools::statusChanged, [this](QString text)
|
2020-10-14 08:37:03 +08:00
|
|
|
{
|
|
|
|
status.setText(text);
|
|
|
|
});
|
|
|
|
|
2020-11-04 06:23:20 +08:00
|
|
|
if (autoStartChrome)
|
|
|
|
QMetaObject::invokeMethod(this, &Window::Start, Qt::QueuedConnection);
|
2020-10-14 08:37:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
~Window()
|
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
Stop();
|
|
|
|
if (devTools != nullptr)
|
|
|
|
delete devTools;
|
2020-10-14 08:37:03 +08:00
|
|
|
SaveCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-11-04 06:23:20 +08:00
|
|
|
void Start()
|
2020-10-14 08:37:03 +08:00
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
if (devTools->getStatus() == "Stopped")
|
|
|
|
devTools->startDevTools(S(pathToChrome), headlessChrome, chromePort);
|
2020-10-14 08:37:03 +08:00
|
|
|
}
|
2020-11-04 06:23:20 +08:00
|
|
|
void Stop()
|
2020-10-14 08:37:03 +08:00
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
devTools->closeDevTools();
|
2020-10-14 08:37:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void saveLanguage(QString language)
|
|
|
|
{
|
2020-11-04 06:23:20 +08:00
|
|
|
settings.setValue(LANGUAGE_TO, S(translateTo->assign(S(language.split(": ")[1]))));
|
|
|
|
}
|
|
|
|
void saveLanguageFrom(QString language)
|
|
|
|
{
|
|
|
|
settings.setValue(LANGUAGE_FROM, S(translateFrom->assign(S(language.split(": ")[1]))));
|
2020-10-14 08:37:03 +08:00
|
|
|
}
|
|
|
|
QPushButton startButton{ START_DEV_TOOLS_BUTTON, this };
|
|
|
|
QPushButton stopButton{ STOP_DEV_TOOLS_BUTTON, this };
|
|
|
|
QLabel status{ "Stopped" };
|
|
|
|
} window;
|
|
|
|
|
|
|
|
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
|
|
|
{
|
|
|
|
if (sentenceInfo["text number"] == 0 || sentence.size() > maxSentenceSize) return false;
|
|
|
|
|
|
|
|
bool cache = false;
|
|
|
|
std::wstring translation;
|
|
|
|
if (useCache)
|
|
|
|
{
|
|
|
|
auto translationCache = ::translationCache.Acquire();
|
|
|
|
if (auto it = translationCache->find(sentence); it != translationCache->end()) translation = it->second + L"\x200b";
|
|
|
|
}
|
|
|
|
if (translation.empty() && (sentenceInfo["current select"]))
|
2020-10-20 07:08:59 +08:00
|
|
|
{
|
|
|
|
EraseControlCharacters(sentence);
|
2020-11-04 06:23:20 +08:00
|
|
|
std::tie(cache, translation) = Translate(sentence, devTools);
|
2020-10-20 07:08:59 +08:00
|
|
|
}
|
|
|
|
|
2020-10-14 08:37:03 +08:00
|
|
|
if (cache) translationCache->try_emplace(sentence, translation);
|
|
|
|
if (cache && translationCache->size() > savedSize + 50) SaveCache();
|
|
|
|
|
|
|
|
JSON::Unescape(translation);
|
|
|
|
sentence += L"\n" + translation;
|
|
|
|
return true;
|
|
|
|
}
|