From 03b855fbbb6dffcf423e99eeb3b64a45371dd802 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Tue, 9 Feb 2016 11:11:26 +0100 Subject: [PATCH] Composition and decomposition text controls are multiline now + they synchronize text selection. --- ZRCola/ZRCola.fbp | 8 +-- ZRCola/stdafx.h | 4 +- ZRCola/zrcolacomppnl.cpp | 72 +++++++++++++++++-- ZRCola/zrcolacomppnl.h | 10 ++- ZRCola/zrcolagui.cpp | 8 ++- ZRCola/zrcolagui.h | 2 + lib/libZRCola/build/libZRCola.vcxproj | 1 + lib/libZRCola/build/libZRCola.vcxproj.filters | 3 + lib/libZRCola/include/zrcola/common.h | 51 +++++++++++-- lib/libZRCola/src/decompose.cpp | 7 +- lib/libZRCola/src/mapping.cpp | 70 ++++++++++++++++++ lib/wxExtend | 2 +- 12 files changed, 210 insertions(+), 28 deletions(-) create mode 100644 lib/libZRCola/src/mapping.cpp diff --git a/ZRCola/ZRCola.fbp b/ZRCola/ZRCola.fbp index 40f4582..9131ae9 100644 --- a/ZRCola/ZRCola.fbp +++ b/ZRCola/ZRCola.fbp @@ -196,7 +196,7 @@ Resizable 1 - wxTE_CENTRE + wxTE_CENTRE|wxTE_MULTILINE 0 @@ -224,7 +224,7 @@ - + OnDecomposedPaint @@ -287,7 +287,7 @@ Resizable 1 - wxTE_CENTRE + wxTE_CENTRE|wxTE_MULTILINE 0 @@ -315,7 +315,7 @@ - + OnComposedPaint diff --git a/ZRCola/stdafx.h b/ZRCola/stdafx.h index cc7bd58..c753e27 100644 --- a/ZRCola/stdafx.h +++ b/ZRCola/stdafx.h @@ -24,8 +24,8 @@ #include "zrcolacomppnl.h" #include "zrcolafrm.h" -#include -#include +#include +#include #include #include diff --git a/ZRCola/zrcolacomppnl.cpp b/ZRCola/zrcolacomppnl.cpp index cbe2226..84f1e5f 100644 --- a/ZRCola/zrcolacomppnl.cpp +++ b/ZRCola/zrcolacomppnl.cpp @@ -26,6 +26,8 @@ wxZRColaComposerPanel::wxZRColaComposerPanel(wxWindow* parent) : m_progress(false), + m_selDecomposed(0, 0), + m_selComposed(0, 0), wxZRColaComposerPanelBase(parent) { } @@ -36,36 +38,94 @@ wxZRColaComposerPanel::~wxZRColaComposerPanel() } +void wxZRColaComposerPanel::OnDecomposedPaint(wxPaintEvent& event) +{ + event.Skip(); + + long from, to; + m_decomposed->GetSelection(&from, &to); + + if (m_selDecomposed.first != from || m_selDecomposed.second != to) { + // Save new selection first, to avoid loop. + m_selDecomposed.first = from; + m_selDecomposed.second = to; + m_composed->SetSelection(m_mapping.to_composed(from), m_mapping.to_composed(to)); + } +} + + void wxZRColaComposerPanel::OnDecomposedText(wxCommandEvent& event) { if (m_progress) { // We are being updated by wxZRColaComposerPanel::OnComposedText() event.Skip(); } else { - std::wstring composed; - ZRCola::Compose(m_decomposed->GetValue(), (size_t)-1, composed); +#ifdef __WINDOWS__ + // Use Windows GetWindowText() function to avoid line ending conversion incompletely imposed by wxWidgets. + WXHWND hWnd = m_decomposed->GetHWND(); + std::vector src((std::vector::size_type)::GetWindowTextLengthW(hWnd) + 1); + ::GetWindowTextW(hWnd, src.data(), src.size()); +#else + wxString src(m_decomposed->GetValue()); +#endif + + std::wstring dst; + ZRCola::Compose(src.data(), src.size(), dst, &m_mapping); + + long from, to; + m_decomposed->GetSelection(&from, &to); // Update composed text. m_progress = true; - m_composed->SetValue(composed); + m_composed->SetValue(dst); + m_composed->SetSelection(m_mapping.to_composed(from), m_mapping.to_composed(to)); event.Skip(); m_progress = false; } } +void wxZRColaComposerPanel::OnComposedPaint(wxPaintEvent& event) +{ + event.Skip(); + + long from, to; + m_composed->GetSelection(&from, &to); + + if (m_selComposed.first != from || m_selComposed.second != to) { + // Save new selection first, to avoid loop. + m_selComposed.first = from; + m_selComposed.second = to; + m_decomposed->SetSelection(m_mapping.to_decomposed(from), m_mapping.to_decomposed(to)); + } +} + + void wxZRColaComposerPanel::OnComposedText(wxCommandEvent& event) { if (m_progress) { // We are being updated by wxZRColaComposerPanel::OnDecomposedText() event.Skip(); } else { - std::wstring decomposed; - ZRCola::Decompose(m_composed->GetValue(), (size_t)-1, decomposed); +#ifdef __WINDOWS__ + // Use Windows GetWindowTextLength() function to avoid line ending conversion incompletely imposed by wxWidgets. + WXHWND hWnd = m_composed->GetHWND(); + std::vector src((std::vector::size_type)::GetWindowTextLengthW(hWnd) + 1); + ::GetWindowTextW(hWnd, src.data(), src.size()); +#else + wxString src(m_composed->GetValue()); +#endif + + std::wstring dst; + ZRCola::Decompose(src.data(), src.size(), dst, &m_mapping); + + long from, to; + m_composed->GetSelection(&from, &to); // Update decomposed text. m_progress = true; - m_decomposed->SetValue(decomposed); + m_decomposed->SetValue(dst); + m_decomposed->SetSelection(m_mapping.to_decomposed(from), m_mapping.to_decomposed(to)); event.Skip(); m_progress = false; } diff --git a/ZRCola/zrcolacomppnl.h b/ZRCola/zrcolacomppnl.h index 38ab7d5..6296afe 100644 --- a/ZRCola/zrcolacomppnl.h +++ b/ZRCola/zrcolacomppnl.h @@ -20,6 +20,8 @@ #pragma once #include "zrcolagui.h" +#include +#include /// @@ -35,9 +37,15 @@ public: friend class wxZRColaFrame; protected: + virtual void OnDecomposedPaint(wxPaintEvent& event); virtual void OnDecomposedText(wxCommandEvent& event); + virtual void OnComposedPaint(wxPaintEvent& event); virtual void OnComposedText(wxCommandEvent& event); protected: - bool m_progress; ///< A boolean flag to avoid recursive updates of composed and decomposed text controls + bool m_progress; ///< Boolean flag to avoid recursive updates of composed and decomposed text controls + ZRCola::mapping_vector m_mapping; ///< Character index mapping vector between composed and decomposed text + std::pair + m_selDecomposed, ///< Character index of selected text in decomposed text control + m_selComposed; ///< Character index of selected text in composed text control }; diff --git a/ZRCola/zrcolagui.cpp b/ZRCola/zrcolagui.cpp index a852066..76d5e53 100644 --- a/ZRCola/zrcolagui.cpp +++ b/ZRCola/zrcolagui.cpp @@ -39,12 +39,12 @@ wxZRColaComposerPanelBase::wxZRColaComposerPanelBase( wxWindow* parent, wxWindow wxBoxSizer* bSizerEditor; bSizerEditor = new wxBoxSizer( wxVERTICAL ); - m_decomposed = new wxTextCtrl( this, wxID_DECOMPOSED, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE ); + m_decomposed = new wxTextCtrl( this, wxID_DECOMPOSED, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_MULTILINE ); m_decomposed->SetFont( wxFont( 20, 70, 90, 90, false, wxT("00 ZRCola") ) ); bSizerEditor->Add( m_decomposed, 50, wxALL|wxEXPAND, 5 ); - m_composed = new wxTextCtrl( this, wxID_COMPOSED, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE ); + m_composed = new wxTextCtrl( this, wxID_COMPOSED, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_MULTILINE ); m_composed->SetFont( wxFont( 20, 70, 90, 90, false, wxT("00 ZRCola") ) ); bSizerEditor->Add( m_composed, 50, wxALL|wxEXPAND, 5 ); @@ -55,14 +55,18 @@ wxZRColaComposerPanelBase::wxZRColaComposerPanelBase( wxWindow* parent, wxWindow bSizerEditor->Fit( this ); // Connect Events + m_decomposed->Connect( wxEVT_PAINT, wxPaintEventHandler( wxZRColaComposerPanelBase::OnDecomposedPaint ), NULL, this ); m_decomposed->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( wxZRColaComposerPanelBase::OnDecomposedText ), NULL, this ); + m_composed->Connect( wxEVT_PAINT, wxPaintEventHandler( wxZRColaComposerPanelBase::OnComposedPaint ), NULL, this ); m_composed->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( wxZRColaComposerPanelBase::OnComposedText ), NULL, this ); } wxZRColaComposerPanelBase::~wxZRColaComposerPanelBase() { // Disconnect Events + m_decomposed->Disconnect( wxEVT_PAINT, wxPaintEventHandler( wxZRColaComposerPanelBase::OnDecomposedPaint ), NULL, this ); m_decomposed->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( wxZRColaComposerPanelBase::OnDecomposedText ), NULL, this ); + m_composed->Disconnect( wxEVT_PAINT, wxPaintEventHandler( wxZRColaComposerPanelBase::OnComposedPaint ), NULL, this ); m_composed->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( wxZRColaComposerPanelBase::OnComposedText ), NULL, this ); } diff --git a/ZRCola/zrcolagui.h b/ZRCola/zrcolagui.h index 1afa567..38e5f58 100644 --- a/ZRCola/zrcolagui.h +++ b/ZRCola/zrcolagui.h @@ -59,7 +59,9 @@ class wxZRColaComposerPanelBase : public wxPanel wxTextCtrl* m_composed; // Virtual event handlers, overide them in your derived class + virtual void OnDecomposedPaint( wxPaintEvent& event ) { event.Skip(); } virtual void OnDecomposedText( wxCommandEvent& event ) { event.Skip(); } + virtual void OnComposedPaint( wxPaintEvent& event ) { event.Skip(); } virtual void OnComposedText( wxCommandEvent& event ) { event.Skip(); } diff --git a/lib/libZRCola/build/libZRCola.vcxproj b/lib/libZRCola/build/libZRCola.vcxproj index 377a115..7069b1d 100644 --- a/lib/libZRCola/build/libZRCola.vcxproj +++ b/lib/libZRCola/build/libZRCola.vcxproj @@ -22,6 +22,7 @@ + Create Create diff --git a/lib/libZRCola/build/libZRCola.vcxproj.filters b/lib/libZRCola/build/libZRCola.vcxproj.filters index bcb4f45..176259b 100644 --- a/lib/libZRCola/build/libZRCola.vcxproj.filters +++ b/lib/libZRCola/build/libZRCola.vcxproj.filters @@ -27,6 +27,9 @@ Source Files + + Source Files + diff --git a/lib/libZRCola/include/zrcola/common.h b/lib/libZRCola/include/zrcola/common.h index 186cc87..7d74ac6 100644 --- a/lib/libZRCola/include/zrcola/common.h +++ b/lib/libZRCola/include/zrcola/common.h @@ -19,22 +19,59 @@ #pragma once +#include + + +/// +/// Public function calling convention +/// #ifdef LIBZRCOLA -#define ZRCOLA_API __declspec(dllexport) +#define ZRCOLA_API __declspec(dllexport) #else -#define ZRCOLA_API __declspec(dllimport) +#define ZRCOLA_API __declspec(dllimport) #endif +#define ZRCOLA_NOVTABLE __declspec(novtable) +#pragma warning(push) +#pragma warning(disable: 4251) + namespace ZRCola { /// - /// Source-destination index mapping + /// Composed-decomposed index transformation mapping /// - class mapping { + class ZRCOLA_NOVTABLE mapping { public: - size_t src; ///< Source index - size_t dst; ///< Destination index + size_t cmp; ///< Character index in composed string + size_t decmp; ///< Character index in decomposed string inline mapping() {}; - inline mapping(size_t s, size_t d) : src(s), dst(d) {} + inline mapping(_In_ size_t c, _In_ size_t d) : cmp(c), decmp(d) {} + }; + + + /// + /// A vector for composed-decomposed index transformation mapping + /// + class ZRCOLA_API mapping_vector : public std::vector { + public: + /// + /// Transforms character index of decomposed to composed string + /// + /// \param[in] decmp Character index in decomposed string + /// + /// \returns Character index in composed string + /// + size_t to_composed(_In_ size_t decmp) const; + + /// + /// Transforms destination index to source index + /// + /// \param[in] cmp Character index in composed string + /// + /// \returns Character index in decomposed string + /// + size_t to_decomposed(_In_ size_t cmp) const; }; }; + +#pragma warning(pop) diff --git a/lib/libZRCola/src/decompose.cpp b/lib/libZRCola/src/decompose.cpp index 55bef39..a05679a 100644 --- a/lib/libZRCola/src/decompose.cpp +++ b/lib/libZRCola/src/decompose.cpp @@ -31,6 +31,8 @@ void ZRCOLA_API ZRCola::Decompose(_In_z_count_(inputMax) const wchar_t *input, _ // Since decomposition expands the string, let's keep our fingers crossed to avoid reallocation later. output.clear(); output.reserve(inputMax * 2); + if (map) + map->clear(); for (size_t i = 0; i < inputMax;) { // Find whether the character can be decomposed. @@ -58,9 +60,4 @@ void ZRCOLA_API ZRCola::Decompose(_In_z_count_(inputMax) const wchar_t *input, _ } } } - - if (map) { - // Add final mapping. - map->push_back(mapping(inputMax, output.length())); - } } diff --git a/lib/libZRCola/src/mapping.cpp b/lib/libZRCola/src/mapping.cpp new file mode 100644 index 0000000..460562e --- /dev/null +++ b/lib/libZRCola/src/mapping.cpp @@ -0,0 +1,70 @@ +/* + Copyright 2015-2016 Amebis + + This file is part of ZRCola. + + ZRCola 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 3 of the License, or + (at your option) any later version. + + ZRCola 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 ZRCola. If not, see . +*/ + +#include "stdafx.h" + + +size_t ZRCola::mapping_vector::to_composed(_In_ size_t decmp) const +{ + for (size_type l = 0, r = size();;) { + if (l < r) { + size_type m = (l + r) / 2; + const mapping &el = (*this)[m]; + + if (decmp < el.decmp) r = m; + else if (el.decmp < decmp) l = m + 1; + else { + // An exact match found. + return el.cmp; + } + } else if (l) { + // We found a map interval. + const mapping &el = (*this)[l - 1]; + return el.cmp + (decmp - el.decmp); + } else { + // The decomposed character index is far left. + return decmp; + } + } +} + + +size_t ZRCola::mapping_vector::to_decomposed(_In_ size_t cmp) const +{ + for (size_type l = 0, r = size();;) { + if (l < r) { + size_type m = (l + r) / 2; + const mapping &el = (*this)[m]; + + if (cmp < el.cmp) r = m; + else if (el.cmp < cmp) l = m + 1; + else { + // An exact match found. + return el.decmp; + } + } else if (l) { + // We found a map interval. + const mapping &el = (*this)[l - 1]; + return el.decmp + (cmp - el.cmp); + } else { + // The composed character index is far left. + return cmp; + } + } +} diff --git a/lib/wxExtend b/lib/wxExtend index 27edfa1..6914baf 160000 --- a/lib/wxExtend +++ b/lib/wxExtend @@ -1 +1 @@ -Subproject commit 27edfa1a59f121f1d143f6b0135bc8a315508a1f +Subproject commit 6914baf5b2f0c9eaef036a0aaa347460652b96ea