add dictionary function to extra window, store colors as argb hex, decrease memory usage of replacer

This commit is contained in:
Akash Mozumdar 2019-09-05 13:42:30 -04:00
parent 9415e83511
commit b7c9f0bfce
6 changed files with 287 additions and 124 deletions

View File

@ -2,6 +2,9 @@
#include "extension.h" #include "extension.h"
#include "ui_extrawindow.h" #include "ui_extrawindow.h"
#include "defs.h" #include "defs.h"
#include <fstream>
#include <filesystem>
#include <process.h>
#include <QColorDialog> #include <QColorDialog>
#include <QFontDialog> #include <QFontDialog>
#include <QMenu> #include <QMenu>
@ -14,66 +17,43 @@ extern const char* TOPMOST;
extern const char* SHOW_ORIGINAL; extern const char* SHOW_ORIGINAL;
extern const char* SHOW_ORIGINAL_INFO; extern const char* SHOW_ORIGINAL_INFO;
extern const char* SIZE_LOCK; extern const char* SIZE_LOCK;
extern const char* DICTIONARY;
extern const char* DICTIONARY_INSTRUCTIONS;
extern const char* BG_COLOR; extern const char* BG_COLOR;
extern const char* TEXT_COLOR; extern const char* TEXT_COLOR;
extern const char* FONT; extern const char* FONT;
extern const char* SAVE_SETTINGS; extern const char* SAVE_SETTINGS;
struct Window : QDialog constexpr auto DICTIONARY_SAVE_FILE = u8"SavedDictionary.txt";
struct PrettyWindow : QDialog
{ {
public: PrettyWindow(const char* name)
Window()
{ {
ui.setupUi(this); ui.setupUi(this);
settings.beginGroup("Extra Window");
setWindowFlags(Qt::FramelessWindowHint); setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_TranslucentBackground);
QMetaObject::invokeMethod(this, [this]
{
show();
settings.beginGroup(name);
QFont font = ui.display->font(); QFont font = ui.display->font();
if (font.fromString(settings.value(FONT, font.toString()).toString())) ui.display->setFont(font); if (font.fromString(settings.value(FONT, font.toString()).toString())) ui.display->setFont(font);
setBackgroundColor(settings.value(BG_COLOR, palette().window().color()).value<QColor>()); setBgColor(settings.value(BG_COLOR, bgColor).value<QColor>());
setTextColor(settings.value(TEXT_COLOR, ui.display->palette().windowText().color()).value<QColor>()); setTextColor(settings.value(TEXT_COLOR, textColor()).value<QColor>());
setLock(settings.value(SIZE_LOCK, false).toBool()); menu.addAction(FONT, this, &PrettyWindow::RequestFont);
setTopmost(settings.value(TOPMOST, false).toBool()); menu.addAction(BG_COLOR, [this] { setBgColor(QColorDialog::getColor(bgColor, this, BG_COLOR, QColorDialog::ShowAlphaChannel)); });
setGeometry(settings.value(WINDOW, geometry()).toRect()); menu.addAction(TEXT_COLOR, [this] { setTextColor(QColorDialog::getColor(textColor(), this, TEXT_COLOR, QColorDialog::ShowAlphaChannel)); });
menu.addAction(FONT, this, &Window::RequestFont);
menu.addAction(BG_COLOR, [this] { setBackgroundColor(QColorDialog::getColor(bgColor, this, BG_COLOR, QColorDialog::ShowAlphaChannel)); });
menu.addAction(TEXT_COLOR, [this] { setTextColor(QColorDialog::getColor(ui.display->palette().windowText().color(), this, TEXT_COLOR, QColorDialog::ShowAlphaChannel)); });
for (auto [name, default, slot] : Array<std::tuple<const char*, bool, void(Window::*)(bool)>>{
{ TOPMOST, false, &Window::setTopmost },
{ SIZE_LOCK, false, &Window::setLock },
{ SHOW_ORIGINAL, true, &Window::setShowOriginal }
})
{
auto action = menu.addAction(name, this, slot);
action->setCheckable(true);
action->setChecked(settings.value(name, default).toBool());
}
connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); }); connect(ui.display, &QLabel::customContextMenuRequested, [this](QPoint point) { menu.exec(mapToGlobal(point)); });
QMetaObject::invokeMethod(this, [this] { AddSentence(EXTRA_WINDOW_INFO); }, Qt::QueuedConnection);
}, Qt::QueuedConnection);
} }
~Window() ~PrettyWindow()
{ {
settings.setValue(WINDOW, geometry());
settings.sync(); settings.sync();
} }
void AddSentence(const QString& sentence)
{
sentenceHistory.push_back(sentence);
i = sentenceHistory.size() - 1;
ui.display->setText(sentence);
}
Ui::ExtraWindow ui; Ui::ExtraWindow ui;
protected:
QMenu menu{ ui.display };
QSettings settings{ CONFIG_FILE, QSettings::IniFormat, this }; QSettings settings{ CONFIG_FILE, QSettings::IniFormat, this };
private: private:
@ -87,41 +67,25 @@ private:
} }
}; };
void setBackgroundColor(QColor color) void setBgColor(QColor color)
{ {
if (!color.isValid()) return; if (!color.isValid()) return;
if (color.alpha() == 0) color.setAlpha(1); if (color.alpha() == 0) color.setAlpha(1);
bgColor = color; bgColor = color;
repaint(); repaint();
settings.setValue(BG_COLOR, color); settings.setValue(BG_COLOR, "#" + QString::number(color.rgba(), 16));
}; };
QColor textColor()
{
return ui.display->palette().color(QPalette::WindowText);
}
void setTextColor(QColor color) void setTextColor(QColor color)
{ {
if (!color.isValid()) return; if (!color.isValid()) return;
auto newPalette = ui.display->palette(); ui.display->setPalette(QPalette(color, {}, {}, {}, {}, {}, {}));
newPalette.setColor(QPalette::WindowText, color); settings.setValue(TEXT_COLOR, "#" + QString::number(color.rgba(), 16));
ui.display->setPalette(newPalette);
settings.setValue(TEXT_COLOR, color);
};
void setTopmost(bool topmost)
{
SetWindowPos((HWND)winId(), topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
settings.setValue(TOPMOST, topmost);
};
void setLock(bool lock)
{
locked = lock;
setSizeGripEnabled(!lock);
settings.setValue(SIZE_LOCK, lock);
};
void setShowOriginal(bool showOriginal)
{
if (!showOriginal) QMessageBox::information(this, SHOW_ORIGINAL, SHOW_ORIGINAL_INFO);
settings.setValue(SHOW_ORIGINAL, showOriginal);
}; };
void paintEvent(QPaintEvent*) override void paintEvent(QPaintEvent*) override
@ -129,40 +93,219 @@ private:
QPainter(this).fillRect(rect(), bgColor); QPainter(this).fillRect(rect(), bgColor);
} }
QColor bgColor{ palette().window().color() };
};
class ExtraWindow : public PrettyWindow
{
public:
ExtraWindow() :
PrettyWindow("Extra Window")
{
setGeometry(settings.value(WINDOW, geometry()).toRect());
for (auto [name, default, slot] : Array<std::tuple<const char*, bool, void(ExtraWindow::*)(bool)>>{
{ TOPMOST, false, &ExtraWindow::setTopmost },
{ SIZE_LOCK, false, &ExtraWindow::setLock },
{ SHOW_ORIGINAL, true, &ExtraWindow::setShowOriginal },
{ DICTIONARY, false, &ExtraWindow::setUseDictionary },
})
{
// delay processing anything until Textractor has finished initializing
QMetaObject::invokeMethod(this, std::bind(slot, this, default = settings.value(name, default).toBool()), Qt::QueuedConnection);
auto action = menu.addAction(name, this, slot);
action->setCheckable(true);
action->setChecked(default);
}
ui.display->installEventFilter(this);
QMetaObject::invokeMethod(this, [this]
{
show();
QMetaObject::invokeMethod(this, [this] { AddSentence(EXTRA_WINDOW_INFO); }, Qt::QueuedConnection);
}, Qt::QueuedConnection);
}
~ExtraWindow()
{
settings.setValue(WINDOW, geometry());
}
void AddSentence(QString sentence)
{
if (!showOriginal) sentence = sentence.section('\n', sentence.count('\n') / 2 + 1);
sentenceHistory.push_back(sentence);
historyIndex = sentenceHistory.size() - 1;
ui.display->setText(sentence);
}
private:
void setTopmost(bool topmost)
{
SetWindowPos((HWND)winId(), topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
settings.setValue(TOPMOST, topmost);
};
void setLock(bool locked)
{
setSizeGripEnabled(!locked);
settings.setValue(SIZE_LOCK, this->locked = locked);
};
void setShowOriginal(bool showOriginal)
{
if (!showOriginal && settings.value(SHOW_ORIGINAL, false).toBool()) QMessageBox::information(this, SHOW_ORIGINAL, SHOW_ORIGINAL_INFO);
settings.setValue(SHOW_ORIGINAL, this->showOriginal = showOriginal);
};
void setUseDictionary(bool useDictionary)
{
if (useDictionary)
{
dictionaryWindow.UpdateDictionary();
if (dictionaryWindow.dictionary.empty())
{
std::ofstream(DICTIONARY_SAVE_FILE) << DICTIONARY_INSTRUCTIONS;
_spawnlp(_P_DETACH, "notepad", "notepad", DICTIONARY_SAVE_FILE, NULL); // show file to user
}
}
settings.setValue(DICTIONARY, this->useDictionary = useDictionary);
}
bool eventFilter(QObject*, QEvent* event) override
{
if (useDictionary && event->type() == QEvent::MouseButtonRelease && ui.display->hasSelectedText())
{
dictionaryWindow.ui.display->setFixedWidth(ui.display->width());
dictionaryWindow.setTerm(ui.display->text().mid(ui.display->selectionStart()));
dictionaryWindow.move({ x(), y() - dictionaryWindow.height() });
}
if (event->type() == QEvent::MouseButtonPress) dictionaryWindow.hide();
return false;
}
void mousePressEvent(QMouseEvent* event) override void mousePressEvent(QMouseEvent* event) override
{ {
dictionaryWindow.hide();
oldPos = event->globalPos(); oldPos = event->globalPos();
} }
void mouseMoveEvent(QMouseEvent* event) override void mouseMoveEvent(QMouseEvent* event) override
{ {
const QPoint delta = event->globalPos() - oldPos; if (!locked) move(pos() + event->globalPos() - oldPos);
if (!locked) move(x() + delta.x(), y() + delta.y());
oldPos = event->globalPos(); oldPos = event->globalPos();
} }
void wheelEvent(QWheelEvent* event) override void wheelEvent(QWheelEvent* event) override
{ {
QPoint scroll = event->angleDelta(); int scroll = event->angleDelta().y();
if (scroll.y() > 0 && i > 0) ui.display->setText(sentenceHistory[--i]); if (scroll > 0 && historyIndex > 0) ui.display->setText(sentenceHistory[--historyIndex]);
if (scroll.y() < 0 && i + 1 < sentenceHistory.size()) ui.display->setText(sentenceHistory[++i]); if (scroll < 0 && historyIndex + 1 < sentenceHistory.size()) ui.display->setText(sentenceHistory[++historyIndex]);
} }
QMenu menu{ ui.display }; bool locked, showOriginal, useDictionary;
bool locked = true;
QColor bgColor;
QPoint oldPos; QPoint oldPos;
std::vector<QString> sentenceHistory; std::vector<QString> sentenceHistory;
int i = 0; int historyIndex = 0;
} window;
class DictionaryWindow : public PrettyWindow
{
public:
DictionaryWindow() :
PrettyWindow("Dictionary Window")
{
ui.display->setSizePolicy({ QSizePolicy::Fixed, QSizePolicy::Minimum });
}
void UpdateDictionary()
{
try
{
if (dictionaryFileLastWrite == std::filesystem::last_write_time(DICTIONARY_SAVE_FILE)) return;
dictionaryFileLastWrite = std::filesystem::last_write_time(DICTIONARY_SAVE_FILE);
}
catch (std::filesystem::filesystem_error) { return; }
dictionary.clear();
owningStorage.clear();
auto StoreCopy = [&](const std::string& string)
{
return &*owningStorage.insert(owningStorage.end(), string.c_str(), string.c_str() + string.size() + 1);
};
std::string savedDictionary(std::istreambuf_iterator(std::ifstream(DICTIONARY_SAVE_FILE)), {});
owningStorage.reserve(savedDictionary.size());
for (size_t end = 0; ;)
{
size_t term = savedDictionary.find("|TERM|", end);
size_t definition = savedDictionary.find("|DEFINITION|", term);
if ((end = savedDictionary.find("|END|", definition)) == std::string::npos) break;
auto storedDefinition = StoreCopy(savedDictionary.substr(definition + 12, end - definition - 12));
for (size_t next; (next = savedDictionary.find("|TERM|", term + 1)) != std::string::npos && next < definition; term = next)
dictionary.push_back({ StoreCopy(savedDictionary.substr(term + 6, next - term - 6)), storedDefinition });
dictionary.push_back({ StoreCopy(savedDictionary.substr(term + 6, definition - term - 6)), storedDefinition });
}
auto oldData = owningStorage.data();
owningStorage.shrink_to_fit();
dictionary.shrink_to_fit();
for (auto& [term, definition] : dictionary)
{
term += owningStorage.data() - oldData;
definition += owningStorage.data() - oldData;
}
std::sort(dictionary.begin(), dictionary.end());
}
void setTerm(QString term)
{
UpdateDictionary();
definitions.clear();
definitionIndex = 0;
for (QByteArray utf8term = term.left(200).toUtf8(); !utf8term.isEmpty(); utf8term.chop(1))
for (auto [it, end] = std::equal_range(dictionary.begin(), dictionary.end(), DictionaryEntry{ utf8term }); it != end; ++it)
definitions.push_back(QStringLiteral("<h3>%1 (%3/%4)</h3>%2").arg(utf8term, it->definition));
for (int i = 0; i < definitions.size(); ++i) definitions[i] = definitions[i].arg(i + 1).arg(definitions.size());
ShowDefinition();
}
void ShowDefinition()
{
if (definitions.empty()) return;
ui.display->setText(definitions[definitionIndex]);
adjustSize();
resize(width(), 1);
show();
}
struct DictionaryEntry
{
const char* term;
const char* definition;
bool operator<(DictionaryEntry other) const { return strcmp(term, other.term) < 0; }
};
std::vector<DictionaryEntry> dictionary;
private:
void wheelEvent(QWheelEvent* event) override
{
int scroll = event->angleDelta().y();
if (scroll > 0 && definitionIndex > 0) definitionIndex -= 1;
if (scroll < 0 && definitionIndex + 1 < definitions.size()) definitionIndex += 1;
int oldHeight = height();
ShowDefinition();
move(x(), y() + oldHeight - height());
}
std::filesystem::file_time_type dictionaryFileLastWrite;
std::vector<char> owningStorage;
std::vector<QString> definitions;
int definitionIndex;
} dictionaryWindow;
} extraWindow;
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
{ {
if (!sentenceInfo["current select"]) return false; if (sentenceInfo["current select"]) QMetaObject::invokeMethod(&extraWindow, [sentence = S(sentence)] { extraWindow.AddSentence(sentence); });
QString qSentence = S(sentence);
if (!window.settings.value(SHOW_ORIGINAL, true).toBool()) qSentence = qSentence.section('\n', qSentence.count('\n') / 2 + 1);
QMetaObject::invokeMethod(&window, [=] { window.AddSentence(qSentence); });
return false; return false;
} }

View File

@ -10,9 +10,6 @@
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>
<widget class="QLabel" name="display"> <widget class="QLabel" name="display">

View File

@ -74,13 +74,12 @@ private:
void Save() void Save()
{ {
auto script = scriptEditor.toPlainText().toUtf8(); QTextFile(LUA_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate).write(scriptEditor.toPlainText().toUtf8());
std::ofstream(LUA_SAVE_FILE, std::ios::out | std::ios::trunc).write(script, script.size());
} }
QWidget centralWidget{ this }; QWidget centralWidget{ this };
QHBoxLayout layout{ &centralWidget }; QHBoxLayout layout{ &centralWidget };
QPlainTextEdit scriptEditor{ S(std::string(std::istreambuf_iterator<char>(std::ifstream(LUA_SAVE_FILE, std::ios::in)), {})), &centralWidget }; QPlainTextEdit scriptEditor{ QTextFile(LUA_SAVE_FILE, QIODevice::ReadOnly).readAll(), &centralWidget };
QPushButton loadButton{ LOAD_LUA_SCRIPT, &centralWidget }; QPushButton loadButton{ LOAD_LUA_SCRIPT, &centralWidget };
} window; } window;

View File

@ -14,16 +14,14 @@ std::shared_mutex m;
class Trie class Trie
{ {
public: public:
Trie(const std::unordered_map<std::wstring, std::wstring>& replacements) Trie(std::unordered_map<std::wstring, std::wstring> replacements)
{ {
for (const auto& [original, replacement] : replacements) for (const auto& [original, replacement] : replacements)
{ {
Node* current = &root; Node* current = &root;
for (auto ch : original) for (auto ch : original) if (!Ignore(ch)) current = Next(current, ch);
if (Ignore(ch)); if (current != &root)
else if (auto& next = current->next[ch]) current = next.get(); current->value = owningStorage.insert(owningStorage.end(), replacement.c_str(), replacement.c_str() + replacement.size() + 1) - owningStorage.begin();
else current = (next = std::make_unique<Node>()).get();
if (current != &root) current->value = replacement;
} }
} }
@ -32,20 +30,18 @@ public:
std::wstring result; std::wstring result;
for (int i = 0; i < sentence.size();) for (int i = 0; i < sentence.size();)
{ {
std::wstring replacement(1, sentence[i]); std::wstring_view replacement(sentence.c_str() + i, 1);
int originalLength = 1; int originalLength = 1;
const Node* current = &root; const Node* current = &root;
for (int j = i; j < sentence.size() + 1; ++j) for (int j = i; current && j <= sentence.size(); ++j)
{ {
if (current->value) if (current->value >= 0)
{ {
replacement = current->value.value(); replacement = owningStorage.data() + current->value;
originalLength = j - i; originalLength = j - i;
} }
if (current->next.count(sentence[j]) > 0) current = current->next.at(sentence[j]).get(); if (!Ignore(sentence[j])) current = Next(current, sentence[j]);
else if (Ignore(sentence[j]));
else break;
} }
result += replacement; result += replacement;
@ -54,43 +50,70 @@ public:
return result; return result;
} }
bool Empty()
{
return root.charMap.empty();
}
private: private:
static bool Ignore(wchar_t ch) static bool Ignore(wchar_t ch)
{ {
return ch <= 0x20 || std::iswspace(ch); return ch <= 0x20 || std::iswspace(ch);
} }
template <typename Node>
static Node* Next(Node* node, wchar_t ch)
{
auto it = std::lower_bound(node->charMap.begin(), node->charMap.end(), ch, [](const auto& one, auto two) { return one.first < two; });
if (it != node->charMap.end() && it->first == ch) return it->second.get();
if constexpr (!std::is_const_v<Node>) return node->charMap.insert(it, { ch, std::make_unique<Node>() })->second.get();
return nullptr;
}
struct Node struct Node
{ {
std::unordered_map<wchar_t, std::unique_ptr<Node>, Identity<wchar_t>> next; std::vector<std::pair<wchar_t, std::unique_ptr<Node>>> charMap;
std::optional<std::wstring> value; ptrdiff_t value = -1;
} root; } root;
std::vector<wchar_t> owningStorage;
} trie = { {} }; } trie = { {} };
std::unordered_map<std::wstring, std::wstring> Parse(const std::wstring& replacementScript) std::unordered_map<std::wstring, std::wstring> Parse(std::wstring_view replacementScript)
{ {
std::unordered_map<std::wstring, std::wstring> replacements; std::unordered_map<std::wstring, std::wstring> replacements;
size_t end = 0; for (size_t end = 0; ;)
while (true)
{ {
size_t original = replacementScript.find(L"|ORIG|", end); size_t original = replacementScript.find(L"|ORIG|", end);
size_t becomes = replacementScript.find(L"|BECOMES|", original); size_t becomes = replacementScript.find(L"|BECOMES|", original);
if ((end = replacementScript.find(L"|END|", becomes)) == std::wstring::npos) break; if ((end = replacementScript.find(L"|END|", becomes)) == std::wstring::npos) break;
replacements[replacementScript.substr(original + 6, becomes - original - 6)] = replacementScript.substr(becomes + 9, end - becomes - 9); replacements[std::wstring(replacementScript.substr(original + 6, becomes - original - 6))] = replacementScript.substr(becomes + 9, end - becomes - 9);
} }
return replacements; return replacements;
} }
void UpdateReplacements()
{
try
{
if (replaceFileLastWrite.exchange(std::filesystem::last_write_time(REPLACE_SAVE_FILE)) == std::filesystem::last_write_time(REPLACE_SAVE_FILE)) return;
std::vector<BYTE> file(std::istreambuf_iterator(std::ifstream(REPLACE_SAVE_FILE, std::ios::binary)), {});
std::scoped_lock l(m);
trie = Trie(Parse({ (wchar_t*)file.data(), file.size() / sizeof(wchar_t) }));
}
catch (std::filesystem::filesystem_error) { replaceFileLastWrite.store({}); }
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) switch (ul_reason_for_call)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
{ {
std::vector<BYTE> file(std::istreambuf_iterator<char>(std::ifstream(REPLACE_SAVE_FILE, std::ios::in | std::ios::binary)), {}); UpdateReplacements();
if (Parse(std::wstring((wchar_t*)file.data(), file.size() / sizeof(wchar_t))).empty()) if (trie.Empty())
{ {
std::ofstream(REPLACE_SAVE_FILE, std::ios::out | std::ios::binary | std::ios::trunc).write((char*)REPLACER_INSTRUCTIONS, wcslen(REPLACER_INSTRUCTIONS) * sizeof(wchar_t)); std::ofstream(REPLACE_SAVE_FILE, std::ios::binary).write((char*)REPLACER_INSTRUCTIONS, wcslen(REPLACER_INSTRUCTIONS) * sizeof(wchar_t));
_spawnlp(_P_DETACH, "notepad", "notepad", REPLACE_SAVE_FILE, NULL); // show file to user _spawnlp(_P_DETACH, "notepad", "notepad", REPLACE_SAVE_FILE, NULL); // show file to user
} }
} }
@ -105,17 +128,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
bool ProcessSentence(std::wstring& sentence, SentenceInfo) bool ProcessSentence(std::wstring& sentence, SentenceInfo)
{ {
try UpdateReplacements();
{
static_assert(std::has_unique_object_representations_v<decltype(replaceFileLastWrite)::value_type>);
if (replaceFileLastWrite.exchange(std::filesystem::last_write_time(REPLACE_SAVE_FILE)) != std::filesystem::last_write_time(REPLACE_SAVE_FILE))
{
std::scoped_lock l(m);
std::vector<BYTE> file(std::istreambuf_iterator<char>(std::ifstream(REPLACE_SAVE_FILE, std::ios::in | std::ios::binary)), {});
trie = Trie(Parse(std::wstring((wchar_t*)file.data(), file.size() / sizeof(wchar_t))));
}
}
catch (std::filesystem::filesystem_error) {}
std::shared_lock l(m); std::shared_lock l(m);
sentence = trie.Replace(sentence); sentence = trie.Replace(sentence);
@ -132,7 +145,7 @@ And this text ツ  
assert(replacements.size() == 4); assert(replacements.size() == 4);
std::wstring original = LR"(Don't replace this  std::wstring original = LR"(Don't replace this 
delete this)"; delete this)";
std::wstring replaced = Trie(replacements).Replace(original); std::wstring replaced = Trie(std::move(replacements)).Replace(original);
assert(replaced == L"Don't replace thisgoodbye idiot hello"); assert(replaced == L"Don't replace thisgoodbye idiot hello");
} }
); );

View File

@ -119,6 +119,17 @@ const wchar_t* TRANSLATION_ERROR = L"Error while translating";
const char* EXTRA_WINDOW_INFO = u8R"(Right click to change settings const char* EXTRA_WINDOW_INFO = u8R"(Right click to change settings
Click and drag on window edges to move, or the bottom right corner to resize)"; Click and drag on window edges to move, or the bottom right corner to resize)";
const char* TOPMOST = u8"Always on top"; const char* TOPMOST = u8"Always on top";
const char* DICTIONARY = u8"Dictionary";
const char* DICTIONARY_INSTRUCTIONS = u8R"(This file is used only for the "Dictionary" feature of the Extra Window extension.
It is not meant to be written manually (though it can be).
You should look for a dictionary in this format online (https://artikash.github.io/?dictionary is a good place to start).
Alternatively, if you're a programmer, you can write a script to convert a dictionary from another format with the info below.
Once you have a dictionary, to look up some text in Extra Window, select it. All matching definitions will be shown.
Definitions are formatted like this:|TERM|Hola|TERM|hola|TERM|Bonjour|TERM|bonjour|DEFINITION|hello|END|
The definition can include rich text (https://doc.qt.io/qt-5/richtext-html-subset.html) which will be formatted properly.
All text in this file outside of definitions is ignored.
Terms longer than 50 characters may not be shown (for performance reasons that should be fixed soon).
This file must be encoded in UTF-8.)";
const char* SHOW_ORIGINAL = u8"Original text"; const char* SHOW_ORIGINAL = u8"Original text";
const char* SHOW_ORIGINAL_INFO = u8R"(Original text will not be shown const char* SHOW_ORIGINAL_INFO = u8R"(Original text will not be shown
Only works if this extension is used directly after a translation extension)"; Only works if this extension is used directly after a translation extension)";

View File

@ -206,7 +206,7 @@ void SearchForHooks(SearchParam spUser)
{ {
VirtualQuery((void*)moduleStopAddress, &info, sizeof(info)); VirtualQuery((void*)moduleStopAddress, &info, sizeof(info));
moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize; moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
} while (info.Protect > PAGE_EXECUTE); } while (info.Protect >= PAGE_EXECUTE);
moduleStopAddress -= info.RegionSize; moduleStopAddress -= info.RegionSize;
ConsoleOutput(STARTING_SEARCH); ConsoleOutput(STARTING_SEARCH);