MSVC C26800 warned us std::vector and std::string are not guaranteed to be cleared after being moved from in all standard C++ implementations. As we reuse those objects and rely they are cleared, do an explicit clear. We could have one-time-use objects and add scopes, but that makes code ugly. Reference: https://stackoverflow.com/a/17735913/2071884 Signed-off-by: Simon Rozman <simon@rozman.si>
482 lines
17 KiB
C++
482 lines
17 KiB
C++
/*
|
|
SPDX-License-Identifier: GPL-3.0-or-later
|
|
Copyright © 2015-2022 Amebis
|
|
*/
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// wxZRColaComposerPanel
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
wxZRColaComposerPanel::wxZRColaComposerPanel(wxWindow* parent) :
|
|
m_sourceChanged(false),
|
|
m_destinationChanged(false),
|
|
m_sourceRestyled(false),
|
|
m_destinationRestyled(false),
|
|
m_styleNormal(*wxBLACK, *wxWHITE, wxFont(20, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("ZRCola"))),
|
|
m_stylePUA(*wxBLUE, *wxWHITE, wxFont(20, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("ZRCola"))),
|
|
m_styleZRColaUnicodeComposedIssues(*wxRED, *wxWHITE, wxFont(20, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("ZRCola"))),
|
|
m_selSource(0, 0),
|
|
m_selDestination(0, 0),
|
|
wxZRColaComposerPanelBase(parent)
|
|
{
|
|
// RichEdit control has no inner margins by default.
|
|
m_source->SetMargins(FromDIP(wxPoint(5, 2)));
|
|
m_destination->SetMargins(FromDIP(wxPoint(5, 2)));
|
|
|
|
m_source->PushEventHandler(&m_keyhandler);
|
|
}
|
|
|
|
|
|
wxZRColaComposerPanel::~wxZRColaComposerPanel()
|
|
{
|
|
m_source->PopEventHandler();
|
|
|
|
// This is a controlled exit. Purge saved state.
|
|
wxString fileName(GetStateFileName());
|
|
if (wxFileExists(fileName))
|
|
wxRemoveFile(fileName);
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::RestoreFromStateFile()
|
|
{
|
|
// Restore the previously saved state (if exists).
|
|
wxString fileName(GetStateFileName());
|
|
if (wxFileExists(fileName)) {
|
|
wxFFile file(fileName, wxT("rb"));
|
|
if (file.IsOpened()) {
|
|
// Load source text.
|
|
uint64_t n;
|
|
file.Read(&n, sizeof(n));
|
|
if (!file.Error()) {
|
|
wxString source;
|
|
file.Read(wxStringBuffer(source, (size_t)n), sizeof(wchar_t)*(size_t)n);
|
|
if (!file.Error()) {
|
|
// Load destination text.
|
|
file.Read(&n, sizeof(n));
|
|
if (!file.Error()) {
|
|
wxString destination;
|
|
file.Read(wxStringBuffer(destination, (size_t)n), sizeof(wchar_t)*(size_t)n);
|
|
if (!file.Error()) {
|
|
// Restore state.
|
|
m_source->SetValue(source);
|
|
m_source->GetSelection(&m_selSource.first, &m_selSource.second);
|
|
SetHexValue(m_sourceHex, m_selSourceHex, m_mappingSourceHex, source.GetData(), source.Length(), m_selSource.first, m_selSource.second);
|
|
m_sourceChanged = false;
|
|
|
|
m_destination->SetValue(destination);
|
|
m_destination->GetSelection(&m_selDestination.first, &m_selDestination.second);
|
|
SetHexValue(m_destinationHex, m_selDestinationHex, m_mappingDestinationHex, destination.GetData(), destination.Length(), m_selDestination.first, m_selDestination.second);
|
|
m_destinationChanged = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::SynchronizePanels()
|
|
{
|
|
if (m_sourceChanged) {
|
|
m_timerSave.Stop();
|
|
|
|
auto app = dynamic_cast<ZRColaApp*>(wxTheApp);
|
|
|
|
wxString src = m_source->GetValue();
|
|
size_t len = src.Length();
|
|
std::wstring dst(src.data(), len), dst2;
|
|
ZRCola::mapping_vector map;
|
|
|
|
m_mapping.clear();
|
|
|
|
if (app->m_mainWnd->m_composition) {
|
|
// ZRCola decompose first, then re-compose.
|
|
app->m_t_db.TranslateInv(app->m_mainWnd->m_composition_id, dst.data(), dst.size(), dst2, &map);
|
|
m_mapping.push_back(std::move(map));
|
|
map.clear();
|
|
|
|
app->m_t_db.Translate(app->m_mainWnd->m_composition_id, dst2.data(), dst2.size(), dst, &map);
|
|
m_mapping.push_back(std::move(map));
|
|
map.clear();
|
|
}
|
|
|
|
// Other translations
|
|
const ZRCola::transetid_t *sets_begin, *sets_end;
|
|
GetTranslationSeq(sets_begin, sets_end);
|
|
for (auto s = sets_begin; s != sets_end; ++s) {
|
|
app->m_t_db.Translate(*s, dst.data(), dst.size(), dst2, &map);
|
|
m_mapping.push_back(std::move(map));
|
|
map.clear();
|
|
dst = std::move(dst2);
|
|
dst2.clear();
|
|
}
|
|
|
|
m_source->GetSelection(&m_selSource.first, &m_selSource.second);
|
|
|
|
// Update source HEX dump.
|
|
SetHexValue(m_sourceHex, m_selSourceHex, m_mappingSourceHex, src.data(), len, m_selSource.first, m_selSource.second);
|
|
|
|
// Update destination text, and its HEX dump.
|
|
m_destination->SetValue(dst);
|
|
m_destination->SetSelection(
|
|
m_selDestination.first = (long)MapToDestination(m_selSource.first ),
|
|
m_selDestination.second = (long)MapToDestination(m_selSource.second));
|
|
SetHexValue(m_destinationHex, m_selDestinationHex, m_mappingDestinationHex, dst.data(), dst.length(), m_selDestination.first, m_selDestination.second);
|
|
|
|
// Schedule state save after 3s.
|
|
m_timerSave.Start(3000, true);
|
|
} else if (m_destinationChanged) {
|
|
m_timerSave.Stop();
|
|
|
|
auto app = dynamic_cast<ZRColaApp*>(wxTheApp);
|
|
|
|
wxString src = m_destination->GetValue();
|
|
size_t len = src.Length();
|
|
std::wstring dst(src.data(), len), dst2;
|
|
ZRCola::mapping_vector map;
|
|
|
|
m_mapping.clear();
|
|
|
|
// Other translations
|
|
const ZRCola::transetid_t *sets_begin, *sets_end;
|
|
GetTranslationSeq(sets_begin, sets_end);
|
|
for (auto s = sets_end; (s--) != sets_begin;) {
|
|
app->m_t_db.TranslateInv(*s, dst.data(), dst.size(), dst2, &map);
|
|
dst = std::move(dst2);
|
|
dst2.clear();
|
|
|
|
map.invert();
|
|
m_mapping.push_back(std::move(map));
|
|
map.clear();
|
|
}
|
|
|
|
if (app->m_mainWnd->m_composition) {
|
|
// ZRCola decompose.
|
|
app->m_t_db.TranslateInv(app->m_mainWnd->m_composition_id, dst.data(), dst.size(), &app->m_lc_db, app->m_mainWnd->m_settings->m_lang, dst2, &map);
|
|
dst = std::move(dst2);
|
|
dst2.clear();
|
|
|
|
map.invert();
|
|
m_mapping.push_back(std::move(map));
|
|
map.clear();
|
|
}
|
|
|
|
m_destination->GetSelection(&m_selDestination.first, &m_selDestination.second);
|
|
|
|
// Update destination HEX dump.
|
|
SetHexValue(m_destinationHex, m_selDestinationHex, m_mappingDestinationHex, src.data(), len, m_selDestination.first, m_selDestination.second);
|
|
|
|
// Update source text, and its HEX dump.
|
|
m_source->SetValue(dst);
|
|
m_source->SetSelection(
|
|
m_selSource.first = (long)MapToSource(m_selDestination.first ),
|
|
m_selSource.second = (long)MapToSource(m_selDestination.second));
|
|
SetHexValue(m_sourceHex, m_selSourceHex, m_mappingSourceHex, dst.data(), dst.length(), m_selSource.first, m_selSource.second);
|
|
|
|
// Schedule state save after 3s.
|
|
m_timerSave.Start(3000, true);
|
|
}
|
|
|
|
m_sourceChanged = false;
|
|
m_destinationChanged = false;
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnSourcePaint(wxPaintEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if (m_sourceRestyled)
|
|
return;
|
|
|
|
long from, to;
|
|
m_source->GetSelection(&from, &to);
|
|
|
|
if (m_selSource.first != from || m_selSource.second != to) {
|
|
// Save new selection first, to avoid loop.
|
|
m_selSource.first = from;
|
|
m_selSource.second = to;
|
|
|
|
m_sourceHex->SetSelection(
|
|
m_selSourceHex.first = (long)m_mappingSourceHex.to_dst(from),
|
|
m_selSourceHex.second = (long)m_mappingSourceHex.to_dst(to ));
|
|
|
|
m_destination->SetSelection(
|
|
m_selDestination.first = (long)MapToDestination(from),
|
|
m_selDestination.second = (long)MapToDestination(to ));
|
|
|
|
m_destinationHex->SetSelection(
|
|
m_selDestinationHex.first = (long)m_mappingDestinationHex.to_dst(m_selDestination.first ),
|
|
m_selDestinationHex.second = (long)m_mappingDestinationHex.to_dst(m_selDestination.second));
|
|
}
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnSourceHexPaint(wxPaintEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
long from, to;
|
|
m_sourceHex->GetSelection(&from, &to);
|
|
|
|
if (m_selSourceHex.first != from || m_selSourceHex.second != to) {
|
|
// Save new selection first, to avoid loop.
|
|
m_selSourceHex.first = from;
|
|
m_selSourceHex.second = to;
|
|
|
|
m_source->SetSelection(
|
|
m_selSource.first = (long)m_mappingSourceHex.to_src(from),
|
|
m_selSource.second = (long)m_mappingSourceHex.to_src(to ));
|
|
|
|
m_destination->SetSelection(
|
|
m_selDestination.first = (long)MapToDestination(m_selSource.first ),
|
|
m_selDestination.second = (long)MapToDestination(m_selSource.second));
|
|
|
|
m_destinationHex->SetSelection(
|
|
m_selDestinationHex.first = (long)m_mappingDestinationHex.to_dst(m_selDestination.first ),
|
|
m_selDestinationHex.second = (long)m_mappingDestinationHex.to_dst(m_selDestination.second));
|
|
}
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnSourceText(wxCommandEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if (m_sourceRestyled)
|
|
return;
|
|
|
|
// Set the flag the source text changed to trigger idle-time translation.
|
|
m_sourceChanged = true;
|
|
|
|
m_sourceRestyled = true;
|
|
m_source->SetStyle(0, GetWindowTextLength(m_source->GetHWND()), m_styleNormal);
|
|
m_sourceRestyled = false;
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnDestinationPaint(wxPaintEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if (m_destinationRestyled)
|
|
return;
|
|
|
|
long from, to;
|
|
m_destination->GetSelection(&from, &to);
|
|
|
|
if (m_selDestination.first != from || m_selDestination.second != to) {
|
|
// Save new selection first, to avoid loop.
|
|
m_selDestination.first = from;
|
|
m_selDestination.second = to;
|
|
|
|
m_destinationHex->SetSelection(
|
|
m_selDestinationHex.first = (long)m_mappingDestinationHex.to_dst(from),
|
|
m_selDestinationHex.second = (long)m_mappingDestinationHex.to_dst(to ));
|
|
|
|
m_source->SetSelection(
|
|
m_selSource.first = (long)MapToSource(from),
|
|
m_selSource.second = (long)MapToSource(to ));
|
|
|
|
m_sourceHex->SetSelection(
|
|
m_selSourceHex.first = (long)m_mappingSourceHex.to_dst(m_selSource.first ),
|
|
m_selSourceHex.second = (long)m_mappingSourceHex.to_dst(m_selSource.second));
|
|
}
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnDestinationHexPaint(wxPaintEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
long from, to;
|
|
m_destinationHex->GetSelection(&from, &to);
|
|
|
|
if (m_selDestinationHex.first != from || m_selDestinationHex.second != to) {
|
|
// Save new selection first, to avoid loop.
|
|
m_selDestinationHex.first = from;
|
|
m_selDestinationHex.second = to;
|
|
|
|
m_destination->SetSelection(
|
|
m_selDestination.first = (long)m_mappingDestinationHex.to_src(from),
|
|
m_selDestination.second = (long)m_mappingDestinationHex.to_src(to ));
|
|
|
|
m_source->SetSelection(
|
|
m_selSource.first = (long)MapToSource(m_selDestination.first ),
|
|
m_selSource.second = (long)MapToSource(m_selDestination.second));
|
|
|
|
m_sourceHex->SetSelection(
|
|
m_selSourceHex.first = (long)m_mappingSourceHex.to_dst(m_selSource.first ),
|
|
m_selSourceHex.second = (long)m_mappingSourceHex.to_dst(m_selSource.second));
|
|
}
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnDestinationText(wxCommandEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if (m_destinationRestyled)
|
|
return;
|
|
|
|
// Set the flag the destination text changed to trigger idle-time inverse translation.
|
|
m_destinationChanged = true;
|
|
|
|
auto app = dynamic_cast<ZRColaApp*>(wxTheApp);
|
|
m_destinationRestyled = true;
|
|
wxString src = m_destination->GetValue();
|
|
app->m_h_db.Highlight(src, src.Length(), [this, app, src](ZRCola::hlghtsetid_t set, size_t start, size_t end) {
|
|
switch (set) {
|
|
case ZRCOLA_HLGHTSETID_ZRCOLA_UNICODE_COMPOSED_ISSUES:
|
|
m_destination->SetStyle((long)start, (long)end, m_styleZRColaUnicodeComposedIssues);
|
|
break;
|
|
|
|
default:
|
|
if (app->m_mainWnd->m_warnPUA) {
|
|
for (size_t i = start, j; i < end;) {
|
|
bool pua_i = ZRCola::ispua(src[i]);
|
|
for (j = i + 1; j < end && pua_i == ZRCola::ispua(src[j]); j++);
|
|
m_destination->SetStyle((long)i, (long)j, pua_i ? m_stylePUA : m_styleNormal);
|
|
i = j;
|
|
}
|
|
} else
|
|
m_destination->SetStyle((long)start, (long)end, m_styleNormal);
|
|
}
|
|
});
|
|
m_destinationRestyled = false;
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::OnSaveTimer(wxTimerEvent& event)
|
|
{
|
|
wxString fileName(GetStateFileName());
|
|
wxFFile file(fileName, wxT("wb"));
|
|
if (file.IsOpened()) {
|
|
{
|
|
// Save source text.
|
|
wxString text = m_source->GetValue();
|
|
size_t len = text.Length();
|
|
file.Write(&len, sizeof(len));
|
|
file.Write((const wchar_t*)text, sizeof(wchar_t)*len);
|
|
}
|
|
|
|
{
|
|
// Save destination text.
|
|
wxString text = m_destination->GetValue();
|
|
size_t len = text.Length();
|
|
file.Write(&len, sizeof(len));
|
|
file.Write((const wchar_t*)text, sizeof(wchar_t)*len);
|
|
}
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
wxString wxZRColaComposerPanel::GetStateFileName()
|
|
{
|
|
wxString path;
|
|
|
|
path = wxFileName::GetTempDir();
|
|
if (!wxEndsWithPathSeparator(path))
|
|
path += wxFILE_SEP_PATH;
|
|
|
|
if (!wxDirExists(path))
|
|
wxMkdir(path);
|
|
|
|
wxString fileName(path);
|
|
fileName += wxT("ZRColaComposerPanel-state.tmp");
|
|
|
|
return fileName;
|
|
}
|
|
|
|
|
|
void wxZRColaComposerPanel::SetHexValue(wxTextCtrl *wnd, std::pair<long, long> &range, ZRCola::mapping_vector &mapping, const wchar_t *src, size_t len, long from, long to)
|
|
{
|
|
wxString hex;
|
|
bool first = true;
|
|
|
|
mapping.clear();
|
|
for (size_t i = 0; i < len && src[i]; i++) {
|
|
wchar_t c = src[i];
|
|
if (c == L'\n') {
|
|
hex += L"\r\n";
|
|
first = true;
|
|
} else {
|
|
hex += wxString::Format(first ? wxT("%04X") : wxT(" %04X"), src[i]);
|
|
mapping.push_back(ZRCola::mapping(i + 1, hex.Length()));
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
wnd->SetValue(hex);
|
|
wnd->SetSelection(
|
|
range.first = (long)mapping.to_dst(from),
|
|
range.second = (long)mapping.to_dst(to ));
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// wxPersistentZRColaComposerPanel
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
const int wxPersistentZRColaComposerPanel::s_guiLevel = 1;
|
|
|
|
|
|
wxPersistentZRColaComposerPanel::wxPersistentZRColaComposerPanel(wxZRColaComposerPanel *wnd) : wxPersistentWindow<wxZRColaComposerPanel>(wnd)
|
|
{
|
|
}
|
|
|
|
|
|
wxString wxPersistentZRColaComposerPanel::GetKind() const
|
|
{
|
|
return wxT(wxPERSIST_TLW_KIND);
|
|
}
|
|
|
|
|
|
void wxPersistentZRColaComposerPanel::Save() const
|
|
{
|
|
auto wnd = static_cast<wxZRColaComposerPanel*>(GetWindow()); // dynamic_cast is not reliable as we are typically called late in the wxTopLevelWindowMSW destructor.
|
|
|
|
SaveValue(wxT("guiLevel" ), s_guiLevel);
|
|
SaveValue(wxT("dpiX" ), wxClientDC(wnd).GetPPI().x);
|
|
SaveValue(wxT("splitDecomposed"), wnd->m_splitterSource ->GetSashPosition());
|
|
SaveValue(wxT("splitComposed" ), wnd->m_splitterDestination->GetSashPosition());
|
|
}
|
|
|
|
|
|
bool wxPersistentZRColaComposerPanel::Restore()
|
|
{
|
|
auto wnd = dynamic_cast<wxZRColaComposerPanel*>(GetWindow());
|
|
|
|
int guiLevel;
|
|
if (!RestoreValue(wxT("guiLevel"), &guiLevel) || guiLevel != s_guiLevel)
|
|
return true;
|
|
|
|
int dpiHorz = wxClientDC(wnd).GetPPI().x;
|
|
int dpiHorzVal;
|
|
int sashVal;
|
|
|
|
if (!RestoreValue(wxT("dpiX"), &dpiHorzVal))
|
|
dpiHorzVal = 96;
|
|
|
|
if (RestoreValue(wxT("splitDecomposed"), &sashVal)) {
|
|
// wxFormBuilder sets initial splitter stash in idle event handler after GUI settles. Overriding our loaded value. Disconnect it's idle event handler.
|
|
wnd->m_splitterSource->Disconnect( wxEVT_IDLE, wxIdleEventHandler( wxZRColaComposerPanelBase::m_splitterSourceOnIdle ), NULL, wnd );
|
|
wnd->m_splitterSource->SetSashPosition(wxMulDivInt32(sashVal, dpiHorz, dpiHorzVal));
|
|
}
|
|
|
|
if (RestoreValue(wxT("splitComposed"), &sashVal)) {
|
|
// wxFormBuilder sets initial splitter stash in idle event handler after GUI settles. Overriding our loaded value. Disconnect it's idle event handler.
|
|
wnd->m_splitterDestination->Disconnect( wxEVT_IDLE, wxIdleEventHandler( wxZRColaComposerPanelBase::m_splitterDestinationOnIdle ), NULL, wnd );
|
|
wnd->m_splitterDestination->SetSashPosition(wxMulDivInt32(sashVal, dpiHorz, dpiHorzVal));
|
|
}
|
|
|
|
return true;
|
|
}
|