From df42fdb2d23dd36867867a528d000158fa07247f Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 16 Apr 2014 18:27:54 -0700 Subject: [PATCH] Restore the text selection and cursor position on undo --- build/Aegisub/Aegisub.vcxproj | 4 +- build/Aegisub/Aegisub.vcxproj.filters | 12 +--- src/Makefile | 2 +- src/context.cpp | 2 + src/include/aegisub/context.h | 2 +- src/scintilla_text_ctrl.cpp | 6 -- src/scintilla_text_ctrl.h | 1 - src/scintilla_text_selection_controller.cpp | 44 ------------ src/scintilla_text_selection_controller.h | 33 --------- src/subs_controller.cpp | 24 +++++++ src/subs_controller.h | 2 + src/subs_edit_box.cpp | 13 ++-- src/subs_edit_box.h | 2 - src/subs_edit_ctrl.cpp | 10 +-- src/subs_edit_ctrl.h | 2 +- src/text_selection_controller.cpp | 75 +++++++++++++++++++++ src/text_selection_controller.h | 33 ++++++--- 17 files changed, 143 insertions(+), 124 deletions(-) delete mode 100644 src/scintilla_text_selection_controller.cpp delete mode 100644 src/scintilla_text_selection_controller.h create mode 100644 src/text_selection_controller.cpp diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 9491799c9..7c2e91466 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -129,7 +129,6 @@ - @@ -195,7 +194,6 @@ - @@ -389,7 +387,6 @@ - @@ -416,6 +413,7 @@ + diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 9b0ce6d9d..b9a07eebd 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -210,9 +210,6 @@ Commands - - Config - Audio\Players @@ -591,9 +588,6 @@ ASS - - Main UI\Edit box - Utilities\UI utilities @@ -1148,9 +1142,6 @@ ASS - - Main UI\Edit box - Audio\Providers @@ -1187,6 +1178,9 @@ Main UI + + Main UI\Edit box + diff --git a/src/Makefile b/src/Makefile index 8dc66b7ac..25a1e001c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -208,7 +208,6 @@ SRC += \ preferences_base.cpp \ resolution_resampler.cpp \ scintilla_text_ctrl.cpp \ - scintilla_text_selection_controller.cpp \ search_replace_engine.cpp \ selection_controller.cpp \ spellchecker.cpp \ @@ -232,6 +231,7 @@ SRC += \ subtitles_provider.cpp \ text_file_reader.cpp \ text_file_writer.cpp \ + text_selection_controller.cpp \ thesaurus.cpp \ threaded_frame_source.cpp \ timeedit_ctrl.cpp \ diff --git a/src/context.cpp b/src/context.cpp index 2e89d56eb..b7725de68 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -24,6 +24,7 @@ #include "search_replace_engine.h" #include "selection_controller.h" #include "subs_controller.h" +#include "text_selection_controller.h" #include "video_context.h" #include @@ -31,6 +32,7 @@ namespace agi { Context::Context() : ass(util::make_unique()) +, textSelectionController(util::make_unique()) , subsController(util::make_unique(this)) , local_scripts(util::make_unique(this)) , videoController(util::make_unique(this)) diff --git a/src/include/aegisub/context.h b/src/include/aegisub/context.h index f3186240a..23c3b405c 100644 --- a/src/include/aegisub/context.h +++ b/src/include/aegisub/context.h @@ -40,13 +40,13 @@ struct Context { // Note: order here matters quite a bit, as things need to be set up and // torn down in the correct order std::unique_ptr ass; + std::unique_ptr textSelectionController; std::unique_ptr subsController; std::unique_ptr local_scripts; std::unique_ptr videoController; std::unique_ptr audioController; std::unique_ptr selectionController; std::unique_ptr initialLineState; - TextSelectionController *textSelectionController; std::unique_ptr search; // Things that should probably be in some sort of UI-context-model diff --git a/src/scintilla_text_ctrl.cpp b/src/scintilla_text_ctrl.cpp index 4e5892a09..0c52eb097 100644 --- a/src/scintilla_text_ctrl.cpp +++ b/src/scintilla_text_ctrl.cpp @@ -64,15 +64,9 @@ void ScintillaTextCtrl::SetUnicodeStyling(int start,int length,int style) { // Get the real length int len = text.Mid(start, length).utf8_str().length(); - // Set styling SetStyling(len,style); } -/// @brief Set selection, unicode-aware -void ScintillaTextCtrl::SetSelectionU(int start, int end) { - SetSelection(GetUnicodePosition(start),GetUnicodePosition(end)); -} - void ScintillaTextCtrl::OnMouseWheel(wxMouseEvent& evt) { if (ForwardMouseWheelEvent(this, evt)) { // Skip the event so that wxSTC's default mouse wheel handler is hit diff --git a/src/scintilla_text_ctrl.h b/src/scintilla_text_ctrl.h index 171aee242..220b1496b 100644 --- a/src/scintilla_text_ctrl.h +++ b/src/scintilla_text_ctrl.h @@ -46,7 +46,6 @@ public: void StartUnicodeStyling(int start,int mask=31); void SetUnicodeStyling(int start,int length,int style); - void SetSelectionU(int start,int end); ScintillaTextCtrl(wxWindow* parent, wxWindowID id, const wxString& value = wxString(), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0); }; diff --git a/src/scintilla_text_selection_controller.cpp b/src/scintilla_text_selection_controller.cpp deleted file mode 100644 index f54e68418..000000000 --- a/src/scintilla_text_selection_controller.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2012, Thomas Goyne -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// -// Aegisub Project http://www.aegisub.org/ - -#include "scintilla_text_selection_controller.h" - -#include "scintilla_text_ctrl.h" - -ScintillaTextSelectionController::ScintillaTextSelectionController(ScintillaTextCtrl *ctrl) -: ctrl(ctrl) -{ -} - -void ScintillaTextSelectionController::SetInsertionPoint(int position) { - ctrl->SetInsertionPoint(position); -} - -int ScintillaTextSelectionController::GetInsertionPoint() const { - return ctrl->GetInsertionPoint(); -} - -void ScintillaTextSelectionController::SetSelection(int start, int end) { - ctrl->SetSelection(start, end); -} - -int ScintillaTextSelectionController::GetSelectionStart() const { - return ctrl->GetSelectionStart(); -} - -int ScintillaTextSelectionController::GetSelectionEnd() const { - return ctrl->GetSelectionEnd(); -} diff --git a/src/scintilla_text_selection_controller.h b/src/scintilla_text_selection_controller.h deleted file mode 100644 index d481dd014..000000000 --- a/src/scintilla_text_selection_controller.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012, Thomas Goyne -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// -// Aegisub Project http://www.aegisub.org/ - -#include "text_selection_controller.h" - -class ScintillaTextCtrl; - -class ScintillaTextSelectionController final : public TextSelectionController { - ScintillaTextCtrl *ctrl; - -public: - void SetSelection(int start, int end) override; - void SetInsertionPoint(int point) override; - - int GetSelectionStart() const override; - int GetSelectionEnd() const override; - int GetInsertionPoint() const override; - - ScintillaTextSelectionController(ScintillaTextCtrl *ctrl); -}; diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp index 0b81147d0..4f3aae793 100644 --- a/src/subs_controller.cpp +++ b/src/subs_controller.cpp @@ -30,6 +30,7 @@ #include "selection_controller.h" #include "subtitle_format.h" #include "text_file_reader.h" +#include "text_selection_controller.h" #include "utils.h" #include "video_context.h" @@ -62,6 +63,7 @@ struct SubsController::UndoInfo { mutable std::vector selection; int active_line_id = 0; + int pos = 0, sel_start = 0, sel_end = 0; UndoInfo(const agi::Context *c, wxString const& d, int commit_id) : undo_description(d) @@ -80,6 +82,7 @@ struct SubsController::UndoInfo { UpdateActiveLine(c); UpdateSelection(c); + UpdateTextSelection(c); } void Apply(agi::Context *c) const { @@ -108,6 +111,9 @@ struct SubsController::UndoInfo { c->ass->Commit("", AssFile::COMMIT_NEW); c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line); + + c->textSelectionController->SetInsertionPoint(pos); + c->textSelectionController->SetSelection(sel_start, sel_end); } void UpdateActiveLine(const agi::Context *c) { @@ -123,11 +129,18 @@ struct SubsController::UndoInfo { for (const auto diag : sel) selection.push_back(diag->Id); } + + void UpdateTextSelection(const agi::Context *c) { + pos = c->textSelectionController->GetInsertionPoint(); + sel_start = c->textSelectionController->GetSelectionStart(); + sel_end = c->textSelectionController->GetSelectionEnd(); + } }; SubsController::SubsController(agi::Context *context) : context(context) , undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this)) +, text_selection_connection(context->textSelectionController->AddSelectionListener(&SubsController::OnTextSelectionChanged, this)) { autosave_timer_changed(&autosave_timer); OPT_SUB("App/Auto/Save", [=] { autosave_timer_changed(&autosave_timer); }); @@ -379,12 +392,20 @@ void SubsController::OnSelectionChanged() { undo_stack.back().UpdateSelection(context); } +void SubsController::OnTextSelectionChanged() { + if (!undo_stack.empty()) + undo_stack.back().UpdateTextSelection(context); +} + void SubsController::Undo() { if (undo_stack.size() <= 1) return; redo_stack.splice(redo_stack.end(), undo_stack, std::prev(undo_stack.end())); commit_id = undo_stack.back().commit_id; + + text_selection_connection.Block(); undo_stack.back().Apply(context); + text_selection_connection.Unblock(); } void SubsController::Redo() { @@ -392,7 +413,10 @@ void SubsController::Redo() { undo_stack.splice(undo_stack.end(), redo_stack, std::prev(redo_stack.end())); commit_id = undo_stack.back().commit_id; + + text_selection_connection.Block(); undo_stack.back().Apply(context); + text_selection_connection.Unblock(); } wxString SubsController::GetUndoDescription() const { diff --git a/src/subs_controller.h b/src/subs_controller.h index fb03f61ae..843054551 100644 --- a/src/subs_controller.h +++ b/src/subs_controller.h @@ -34,6 +34,7 @@ class SubsController { agi::signal::Connection undo_connection; agi::signal::Connection active_line_connection; agi::signal::Connection selection_connection; + agi::signal::Connection text_selection_connection; struct UndoInfo; boost::container::list undo_stack; @@ -66,6 +67,7 @@ class SubsController { void OnCommit(AssFileCommit c); void OnActiveLineChanged(); void OnSelectionChanged(); + void OnTextSelectionChanged(); public: SubsController(agi::Context *context); diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp index ad05bdd34..69e82f566 100644 --- a/src/subs_edit_box.cpp +++ b/src/subs_edit_box.cpp @@ -45,9 +45,9 @@ #include "libresrc/libresrc.h" #include "options.h" #include "placeholder_ctrl.h" -#include "scintilla_text_selection_controller.h" #include "selection_controller.h" #include "subs_edit_ctrl.h" +#include "text_selection_controller.h" #include "timeedit_ctrl.h" #include "tooltip_manager.h" #include "utils.h" @@ -213,12 +213,12 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) connections.push_back(context->selectionController->AddSelectionListener(&SubsEditBox::OnSelectedSetChanged, this)); connections.push_back(context->initialLineState->AddChangeListener(&SubsEditBox::OnLineInitialTextChanged, this)); - textSelectionController = agi::util::make_unique(edit_ctrl); - context->textSelectionController = textSelectionController.get(); + context->textSelectionController->SetControl(edit_ctrl); edit_ctrl->SetFocus(); } SubsEditBox::~SubsEditBox() { + c->textSelectionController->SetControl(nullptr); } wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) { @@ -293,11 +293,8 @@ void SubsEditBox::OnCommit(int type) { } if (type == AssFile::COMMIT_NEW) { - /// @todo maybe preserve selection over undo? PopulateList(effect_box, &AssDialogue::Effect); PopulateList(actor_box, &AssDialogue::Actor); - - edit_ctrl->SetSelection(0,0); return; } else if (type & AssFile::COMMIT_STYLES) @@ -315,7 +312,7 @@ void SubsEditBox::OnCommit(int type) { } if (type & AssFile::COMMIT_DIAG_TEXT) { - edit_ctrl->SetTextTo(to_wx(line->Text)); + edit_ctrl->SetTextTo(line->Text); UpdateCharacterCount(line->Text); } @@ -333,7 +330,7 @@ void SubsEditBox::OnCommit(int type) { PopulateList(actor_box, &AssDialogue::Actor); actor_box->ChangeValue(to_wx(line->Actor)); actor_box->SetStringSelection(to_wx(line->Actor)); - edit_ctrl->SetTextTo(to_wx(line->Text)); + edit_ctrl->SetTextTo(line->Text); } } diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h index cbb344107..afa38ebad 100644 --- a/src/subs_edit_box.h +++ b/src/subs_edit_box.h @@ -50,7 +50,6 @@ namespace agi { struct Context; } class AssDialogue; class AssTime; class SubsTextEditCtrl; -class TextSelectionController; class TimeEdit; class wxButton; class wxCheckBox; @@ -199,7 +198,6 @@ class SubsEditBox final : public wxPanel { SubsTextEditCtrl *edit_ctrl; wxTextCtrl *secondary_editor; - std::unique_ptr textSelectionController; public: /// @brief Constructor diff --git a/src/subs_edit_ctrl.cpp b/src/subs_edit_ctrl.cpp index afe8e81b1..ac5ffead7 100644 --- a/src/subs_edit_ctrl.cpp +++ b/src/subs_edit_ctrl.cpp @@ -276,17 +276,13 @@ void SubsTextEditCtrl::UpdateCallTip() { CallTipSetHighlight(new_calltip.highlight_start, new_calltip.highlight_end); } -void SubsTextEditCtrl::SetTextTo(wxString const& text) { +void SubsTextEditCtrl::SetTextTo(std::string const& text) { SetEvtHandlerEnabled(false); Freeze(); - int from = GetReverseUnicodePosition(GetSelectionStart()); - int to = GetReverseUnicodePosition(GetSelectionEnd()); - line_text.clear(); - SetText(text); - - SetSelectionU(from, to); + SetTextRaw(text.c_str()); + SetSelection(0, 0); SetEvtHandlerEnabled(true); Thaw(); diff --git a/src/subs_edit_ctrl.h b/src/subs_edit_ctrl.h index 4ce1734a0..24f7ceac0 100644 --- a/src/subs_edit_ctrl.h +++ b/src/subs_edit_ctrl.h @@ -122,7 +122,7 @@ public: SubsTextEditCtrl(wxWindow* parent, wxSize size, long style, agi::Context *context); ~SubsTextEditCtrl(); - void SetTextTo(wxString const& text); + void SetTextTo(std::string const& text); void Paste() override; std::pair GetBoundsOfWordAtPosition(int pos); diff --git a/src/text_selection_controller.cpp b/src/text_selection_controller.cpp new file mode 100644 index 000000000..deefa6573 --- /dev/null +++ b/src/text_selection_controller.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include "text_selection_controller.h" + +#include + +void TextSelectionController::SetControl(wxStyledTextCtrl *ctrl) { + this->ctrl = ctrl; + if (ctrl) + ctrl->Bind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this); +} + +TextSelectionController::~TextSelectionController() { + if (ctrl) ctrl->Unbind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this); +} + +#define GET(var, new_value) do { \ + int tmp = new_value; \ + if (tmp != var) { \ + var = tmp; \ + changed = true; \ + } \ +} while(false) + +#define SET(var, new_value, Setter) do { \ + if (var != new_value) { \ + var = new_value; \ + if (ctrl) ctrl->Setter(var); \ + } \ +} while (false) + +void TextSelectionController::UpdateUI(wxStyledTextEvent &evt) { + if (changing) return; + + bool changed = false; + GET(insertion_point, ctrl->GetInsertionPoint()); + if (evt.GetUpdated() & wxSTC_UPDATE_SELECTION) { + GET(selection_start, ctrl->GetSelectionStart()); + GET(selection_end, ctrl->GetSelectionEnd()); + } + else { + GET(selection_start, insertion_point); + GET(selection_end, insertion_point); + } + if (changed) AnnounceSelectionChanged(); +} + +void TextSelectionController::SetInsertionPoint(int position) { + changing = true; + SET(insertion_point, position, SetInsertionPoint); + changing = false; + AnnounceSelectionChanged(); +} + +void TextSelectionController::SetSelection(int start, int end) { + changing = true; + SET(selection_start, start, SetSelectionStart); + SET(selection_end, end, SetSelectionEnd); + changing = false; + AnnounceSelectionChanged(); +} diff --git a/src/text_selection_controller.h b/src/text_selection_controller.h index cc24c61c7..be6949ef4 100644 --- a/src/text_selection_controller.h +++ b/src/text_selection_controller.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012, Thomas Goyne +// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -14,16 +14,33 @@ // // Aegisub Project http://www.aegisub.org/ -#pragma once +#include + +class wxStyledTextCtrl; +class wxStyledTextEvent; class TextSelectionController { + int selection_start = 0; + int selection_end = 0; + int insertion_point = 0; + bool changing = false; + + wxStyledTextCtrl *ctrl = nullptr; + + void UpdateUI(wxStyledTextEvent &evt); + + agi::signal::Signal<> AnnounceSelectionChanged; + public: - virtual ~TextSelectionController() { } + void SetSelection(int start, int end); + void SetInsertionPoint(int point); - virtual void SetSelection(int start, int end) = 0; - virtual void SetInsertionPoint(int point) = 0; + int GetSelectionStart() const { return selection_start; } + int GetSelectionEnd() const { return selection_end; } + int GetInsertionPoint() const { return insertion_point; } - virtual int GetSelectionStart() const = 0; - virtual int GetSelectionEnd() const = 0; - virtual int GetInsertionPoint() const = 0; + void SetControl(wxStyledTextCtrl *ctrl); + ~TextSelectionController(); + + DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionListener) };