Restore the text selection and cursor position on undo

This commit is contained in:
Thomas Goyne 2014-04-16 18:27:54 -07:00
parent 56699e4800
commit df42fdb2d2
17 changed files with 143 additions and 124 deletions

View file

@ -129,7 +129,6 @@
<ClInclude Include="$(SrcDir)colour_button.h" />
<ClInclude Include="$(SrcDir)command\command.h" />
<ClInclude Include="$(SrcDir)compat.h" />
<ClInclude Include="$(SrcDir)config.h" />
<ClInclude Include="$(SrcDir)crash_writer.h" />
<ClInclude Include="$(SrcDir)dialog_about.h" />
<ClInclude Include="$(SrcDir)dialog_attachments.h" />
@ -195,7 +194,6 @@
<ClInclude Include="$(SrcDir)preferences_base.h" />
<ClInclude Include="$(SrcDir)resolution_resampler.h" />
<ClInclude Include="$(SrcDir)scintilla_text_ctrl.h" />
<ClInclude Include="$(SrcDir)scintilla_text_selection_controller.h" />
<ClInclude Include="$(SrcDir)search_replace_engine.h" />
<ClInclude Include="$(SrcDir)selection_controller.h" />
<ClInclude Include="$(SrcDir)spellchecker_hunspell.h" />
@ -389,7 +387,6 @@
<ClCompile Include="$(SrcDir)preferences_base.cpp" />
<ClCompile Include="$(SrcDir)resolution_resampler.cpp" />
<ClCompile Include="$(SrcDir)scintilla_text_ctrl.cpp" />
<ClCompile Include="$(SrcDir)scintilla_text_selection_controller.cpp" />
<ClCompile Include="$(SrcDir)search_replace_engine.cpp" />
<ClCompile Include="$(SrcDir)selection_controller.cpp" />
<ClCompile Include="$(SrcDir)spellchecker.cpp" />
@ -416,6 +413,7 @@
<ClCompile Include="$(SrcDir)subtitles_provider_libass.cpp" />
<ClCompile Include="$(SrcDir)text_file_reader.cpp" />
<ClCompile Include="$(SrcDir)text_file_writer.cpp" />
<ClCompile Include="$(SrcDir)text_selection_controller.cpp" />
<ClCompile Include="$(SrcDir)thesaurus.cpp" />
<ClCompile Include="$(SrcDir)threaded_frame_source.cpp" />
<ClCompile Include="$(SrcDir)timeedit_ctrl.cpp" />

View file

@ -210,9 +210,6 @@
<ClInclude Include="$(SrcDir)command\command.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)config.h">
<Filter>Config</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_portaudio.h">
<Filter>Audio\Players</Filter>
</ClInclude>
@ -591,9 +588,6 @@
<ClInclude Include="$(SrcDir)ass_parser.h">
<Filter>ASS</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)scintilla_text_selection_controller.h">
<Filter>Main UI\Edit box</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)dialog_manager.h">
<Filter>Utilities\UI utilities</Filter>
</ClInclude>
@ -1148,9 +1142,6 @@
<ClCompile Include="$(SrcDir)ass_parser.cpp">
<Filter>ASS</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)scintilla_text_selection_controller.cpp">
<Filter>Main UI\Edit box</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)audio_provider_lock.cpp">
<Filter>Audio\Providers</Filter>
</ClCompile>
@ -1187,6 +1178,9 @@
<ClCompile Include="$(SrcDir)context.cpp">
<Filter>Main UI</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)text_selection_controller.cpp">
<Filter>Main UI\Edit box</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(SrcDir)res/res.rc">

View file

@ -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 \

View file

@ -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 <libaegisub/util.h>
@ -31,6 +32,7 @@
namespace agi {
Context::Context()
: ass(util::make_unique<AssFile>())
, textSelectionController(util::make_unique<TextSelectionController>())
, subsController(util::make_unique<SubsController>(this))
, local_scripts(util::make_unique<Automation4::LocalScriptManager>(this))
, videoController(util::make_unique<VideoContext>(this))

View file

@ -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<AssFile> ass;
std::unique_ptr<TextSelectionController> textSelectionController;
std::unique_ptr<SubsController> subsController;
std::unique_ptr<Automation4::ScriptManager> local_scripts;
std::unique_ptr<VideoContext> videoController;
std::unique_ptr<AudioController> audioController;
std::unique_ptr<SelectionController> selectionController;
std::unique_ptr<InitialLineState> initialLineState;
TextSelectionController *textSelectionController;
std::unique_ptr<SearchReplaceEngine> search;
// Things that should probably be in some sort of UI-context-model

View file

@ -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

View file

@ -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);
};

View file

@ -1,44 +0,0 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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();
}

View file

@ -1,33 +0,0 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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);
};

View file

@ -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<int> 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 {

View file

@ -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<UndoInfo> undo_stack;
@ -66,6 +67,7 @@ class SubsController {
void OnCommit(AssFileCommit c);
void OnActiveLineChanged();
void OnSelectionChanged();
void OnTextSelectionChanged();
public:
SubsController(agi::Context *context);

View file

@ -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<ScintillaTextSelectionController>(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);
}
}

View file

@ -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> textSelectionController;
public:
/// @brief Constructor

View file

@ -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();

View file

@ -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<int, int> GetBoundsOfWordAtPosition(int pos);

View file

@ -0,0 +1,75 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <wx/stc/stc.h>
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();
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <libaegisub/signal.h>
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)
};