diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 0b1dcd629..dc1b4c5bf 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -390,6 +390,7 @@ + diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 6214b4aa1..d23dbed0d 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -1001,6 +1001,9 @@ Main UI\Grid + + Main UI\Grid + Main UI\Edit box diff --git a/src/Makefile b/src/Makefile index f6a38b2d1..900a8232b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -209,6 +209,7 @@ SRC += \ scintilla_text_ctrl.cpp \ scintilla_text_selection_controller.cpp \ search_replace_engine.cpp \ + selection_controller.cpp \ spellchecker.cpp \ spline.cpp \ spline_curve.cpp \ diff --git a/src/ass_karaoke.cpp b/src/ass_karaoke.cpp index cafae1878..d9d315df4 100644 --- a/src/ass_karaoke.cpp +++ b/src/ass_karaoke.cpp @@ -277,7 +277,7 @@ void AssKaraoke::SplitLines(std::set const& lines, agi::Context *c AssKaraoke kara; - SubtitleSelection sel = c->selectionController->GetSelectedSet(); + Selection sel = c->selectionController->GetSelectedSet(); bool did_split = false; for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) { diff --git a/src/audio_timing.h b/src/audio_timing.h index 17b8eea8a..4a9e2bd64 100644 --- a/src/audio_timing.h +++ b/src/audio_timing.h @@ -38,7 +38,6 @@ class AudioRenderingStyleRanges; namespace agi { struct Context; } #include "audio_marker.h" -#include "selection_controller.h" /// @class AudioTimingController /// @brief Base class for objects controlling audio timing diff --git a/src/audio_timing_dialogue.cpp b/src/audio_timing_dialogue.cpp index 8594cb614..111fce398 100644 --- a/src/audio_timing_dialogue.cpp +++ b/src/audio_timing_dialogue.cpp @@ -348,7 +348,7 @@ class AudioTimingControllerDialogue final : public AudioTimingController { void RegenerateSelectedLines(); /// Add a line to the list of timeable inactive lines - void AddInactiveLine(SubtitleSelection const& sel, AssDialogue *diag); + void AddInactiveLine(Selection const& sel, AssDialogue *diag); /// Regenerate the list of active and inactive line markers void RegenerateMarkers(); @@ -727,7 +727,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines() bool was_empty = inactive_lines.empty(); inactive_lines.clear(); - SubtitleSelection const& sel = context->selectionController->GetSelectedSet(); + auto const& sel = context->selectionController->GetSelectedSet(); switch (int mode = inactive_line_mode->GetInt()) { @@ -778,7 +778,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines() RegenerateMarkers(); } -void AudioTimingControllerDialogue::AddInactiveLine(SubtitleSelection const& sel, AssDialogue *diag) +void AudioTimingControllerDialogue::AddInactiveLine(Selection const& sel, AssDialogue *diag) { if (sel.count(diag)) return; diff --git a/src/audio_timing_karaoke.cpp b/src/audio_timing_karaoke.cpp index b2dcd36b7..5871d2f2c 100644 --- a/src/audio_timing_karaoke.cpp +++ b/src/audio_timing_karaoke.cpp @@ -33,6 +33,7 @@ #include "include/aegisub/context.h" #include "options.h" #include "pen.h" +#include "selection_controller.h" #include "utils.h" #include diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 987371495..6fc4cf3d3 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -834,7 +834,7 @@ namespace Automation4 { static int transform_selection(lua_State *L, const agi::Context *c) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); AssDialogue *active_line = c->selectionController->GetActiveLine(); lua_newtable(L); diff --git a/src/base_grid.cpp b/src/base_grid.cpp index b7a08beae..5bab63401 100644 --- a/src/base_grid.cpp +++ b/src/base_grid.cpp @@ -48,6 +48,7 @@ #include "frame_main.h" #include "options.h" #include "utils.h" +#include "selection_controller.h" #include "subs_controller.h" #include "video_context.h" #include "video_slider.h" @@ -89,8 +90,8 @@ namespace std { }; } -BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context, const wxSize& size, long style, const wxString& name) -: wxWindow(parent, -1, wxDefaultPosition, size, style, name) +BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context) +: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER) , scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL)) , seek_listener(context->videoController->AddSeekListener(std::bind(&BaseGrid::Refresh, this, false, nullptr))) , context(context) @@ -108,26 +109,30 @@ BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context, const wxSize& size, UpdateStyle(); OnHighlightVisibleChange(*OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame")); - OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this); - OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this); - OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this); - context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this); - context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this); - context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this); + connections.push_back(context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this)); + connections.push_back(context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this)); + connections.push_back(context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this)); - OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Header", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Left Column", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Lines", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Selection", &BaseGrid::UpdateStyle, this); - OPT_SUB("Colour/Subtitle Grid/Standard", &BaseGrid::UpdateStyle, this); - OPT_SUB("Subtitle/Grid/Hide Overrides", std::bind(&BaseGrid::Refresh, this, false, nullptr)); + connections.push_back(context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this)); + connections.push_back(context->selectionController->AddSelectionListener([&]{ Refresh(false); })); + + connections.push_back(OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Header", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Left Column", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Lines", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Selection", &BaseGrid::UpdateStyle, this)); + connections.push_back(OPT_SUB("Colour/Subtitle Grid/Standard", &BaseGrid::UpdateStyle, this)); + + connections.push_back(OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this)); + connections.push_back(OPT_SUB("Subtitle/Grid/Hide Overrides", [&](agi::OptionValue const&) { Refresh(false); })); Bind(wxEVT_CONTEXT_MENU, &BaseGrid::OnContextMenu, this); } @@ -155,34 +160,16 @@ void BaseGrid::OnSubtitlesCommit(int type) { } if (type & AssFile::COMMIT_DIAG_TIME) Refresh(false); - //RefreshRect(wxRect(time_cols_x, 0, time_cols_w, GetClientSize().GetHeight()), false); else if (type & AssFile::COMMIT_DIAG_TEXT) RefreshRect(wxRect(text_col_x, 0, text_col_w, GetClientSize().GetHeight()), false); } void BaseGrid::OnSubtitlesOpen() { - BeginBatch(); - ClearMaps(); - UpdateMaps(); - - if (GetRows()) { - int row = context->ass->GetUIStateAsInt("Active Line"); - if (row < 0 || row >= GetRows()) - row = 0; - - SetActiveLine(GetDialogue(row)); - SelectRow(row); - } - ScrollTo(context->ass->GetUIStateAsInt("Scroll Position")); - - EndBatch(); - SetColumnWidths(); } void BaseGrid::OnSubtitlesSave() { context->ass->SaveUIState("Scroll Position", std::to_string(yPos)); - context->ass->SaveUIState("Active Line", std::to_string(GetDialogueIndex(active_line))); } void BaseGrid::OnShowColMenu(wxCommandEvent &event) { @@ -239,20 +226,7 @@ void BaseGrid::UpdateStyle() { Refresh(false); } -void BaseGrid::ClearMaps() { - index_line_map.clear(); - line_index_map.clear(); - selection.clear(); - yPos = 0; - AdjustScrollbar(); - - AnnounceSelectedSetChanged(); -} - void BaseGrid::UpdateMaps() { - BeginBatch(); - int active_row = line_index_map[active_line]; - index_line_map.clear(); line_index_map.clear(); @@ -261,47 +235,17 @@ void BaseGrid::UpdateMaps() { index_line_map.push_back(&curdiag); } - auto sorted = index_line_map; - sort(begin(sorted), end(sorted)); - Selection new_sel; - // Remove lines which no longer exist from the selection - set_intersection(selection.begin(), selection.end(), - sorted.begin(), sorted.end(), - inserter(new_sel, new_sel.begin())); - - SetSelectedSet(std::move(new_sel)); - - // The active line may have ceased to exist; pick a new one if so - if (line_index_map.size() && !line_index_map.count(active_line)) - SetActiveLine(index_line_map[std::min((size_t)active_row, index_line_map.size() - 1)]); - - if (selection.empty() && active_line) - SetSelectedSet({ active_line }); - - EndBatch(); - SetColumnWidths(); - Refresh(false); } -void BaseGrid::BeginBatch() { - ++batch_level; -} - -void BaseGrid::EndBatch() { - --batch_level; - assert(batch_level >= 0); - if (batch_level == 0) { - if (batch_active_line_changed) - AnnounceActiveLineChanged(active_line); - batch_active_line_changed = false; - if (batch_selection_changed) - AnnounceSelectedSetChanged(); - batch_selection_changed = false; +void BaseGrid::OnActiveLineChanged(AssDialogue *new_active) { + if (new_active) { + int row = GetDialogueIndex(new_active); + MakeRowVisible(row); + extendRow = row; + Refresh(false); } - - AdjustScrollbar(); } void BaseGrid::MakeRowVisible(int row) { @@ -319,23 +263,19 @@ void BaseGrid::SelectRow(int row, bool addToSelected, bool select) { AssDialogue *line = index_line_map[row]; if (!addToSelected) { - Selection sel; - if (select) sel.insert(line); - SetSelectedSet(std::move(sel)); + context->selectionController->SetSelectedSet(Selection{line}); return; } - if (select && selection.find(line) == selection.end()) { - selection.insert(line); - AnnounceSelectedSetChanged(); + bool selected = !!context->selectionController->GetSelectedSet().count(line); + if (select != selected) { + auto selection = context->selectionController->GetSelectedSet(); + if (select) + selection.insert(line); + else + selection.erase(line); + context->selectionController->SetSelectedSet(std::move(selection)); } - else if (!select && selection.find(line) != selection.end()) { - selection.erase(line); - AnnounceSelectedSetChanged(); - } - - int w = GetClientSize().GetWidth(); - RefreshRect(wxRect(0, (row + 1 - yPos) * lineHeight, w, lineHeight), false); } void BaseGrid::OnPaint(wxPaintEvent &) { @@ -401,9 +341,13 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) { if (override_mode == 1) replace_char = to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString()); + auto active_line = context->selectionController->GetActiveLine(); + auto const& selection = context->selectionController->GetSelectedSet(); + for (int i = 0; i < nDraw + 1; i++) { int curRow = i + yPos - 1; RowColor curColor = COLOR_DEFAULT; + AssDialogue *curDiag = nullptr; // Header if (i == 0) { @@ -411,7 +355,7 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) { dc.SetTextForeground(text_standard); } // Lines - else if (AssDialogue *curDiag = GetDialogue(curRow)) { + else if ((curDiag = GetDialogue(curRow))) { GetRowStrings(curRow, curDiag, paint_columns, strings, !!override_mode, replace_char); bool inSel = !!selection.count(curDiag); @@ -467,28 +411,27 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) { // Draw grid dc.DestroyClippingRegion(); - dc.SetPen(grid_pen); - dc.DrawLine(0,dy+lineHeight,w,dy+lineHeight); + if (curDiag == active_line) { + dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor()))); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(0, dy, w, lineHeight + 1); + } + else { + dc.SetPen(grid_pen); + dc.DrawLine(0, dy + lineHeight, w , dy + lineHeight); + } dc.SetPen(*wxTRANSPARENT_PEN); } // Draw grid columns int dx = 0; dc.SetPen(grid_pen); - for (int i=0;i<10;i++) { + for (int i = 0; i < 10; ++i) { dx += colWidth[i]; - dc.DrawLine(dx,0,dx,maxH); - } - dc.DrawLine(0,0,0,maxH); - dc.DrawLine(w-1,0,w-1,maxH); - - // Draw currently active line border - if (GetActiveLine()) { - dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor()))); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - int dy = (line_index_map[GetActiveLine()]+1-yPos) * lineHeight; - dc.DrawRectangle(0,dy,w,lineHeight+1); + dc.DrawLine(dx, 0, dx, maxH); } + dc.DrawLine(0, 0, 0, maxH); + dc.DrawLine(w-1, 0, w-1, maxH); } void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wxString *strings, bool replace, wxString const& rep_char) const { @@ -604,8 +547,11 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) { // but we don't want to scroll until the mouse moves or the button is // released, to avoid selecting multiple lines on a click int old_y_pos = yPos; - SetActiveLine(dlg); + context->selectionController->SetActiveLine(dlg); ScrollTo(old_y_pos); + extendRow = row; + + auto const& selection = context->selectionController->GetSelectedSet(); // Toggle selected if (click && ctrl && !shift && !alt) { @@ -643,7 +589,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) { if (ctrl) newsel = selection; for (int i = i1; i <= i2; i++) newsel.insert(GetDialogue(i)); - SetSelectedSet(std::move(newsel)); + context->selectionController->SetSelectedSet(std::move(newsel)); return; } @@ -830,7 +776,6 @@ void BaseGrid::SetColumnWidths() { colWidth[i] += 10; } - // Set size of last int total = std::accumulate(colWidth, colWidth + 10, 0); colWidth[10] = std::max(w - total, 0); @@ -911,8 +856,8 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) { } int old_extend = extendRow; - int next = mid(0, GetDialogueIndex(active_line) + dir * step, GetRows() - 1); - SetActiveLine(GetDialogue(next)); + int next = mid(0, GetDialogueIndex(context->selectionController->GetActiveLine()) + dir * step, GetRows() - 1); + context->selectionController->SetActiveLine(GetDialogue(next)); // Move selection if (!ctrl && !shift && !alt) { @@ -921,10 +866,8 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) { } // Move active only - if (alt && !shift && !ctrl) { - Refresh(false); + if (alt && !shift && !ctrl) return; - } // Shift-selection if (shift && !ctrl && !alt) { @@ -940,7 +883,7 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) { for (int i = begin; i <= end; i++) newsel.insert(GetDialogue(i)); - SetSelectedSet(std::move(newsel)); + context->selectionController->SetSelectedSet(std::move(newsel)); MakeRowVisible(next); return; @@ -953,59 +896,3 @@ void BaseGrid::SetByFrame(bool state) { SetColumnWidths(); Refresh(false); } - -void BaseGrid::SetSelectedSet(Selection new_selection) { - selection = std::move(new_selection); - AnnounceSelectedSetChanged(); - Refresh(false); -} - -void BaseGrid::SetActiveLine(AssDialogue *new_line) { - if (new_line != active_line) { - assert(new_line == nullptr || line_index_map.count(new_line)); - active_line = new_line; - AnnounceActiveLineChanged(active_line); - MakeRowVisible(GetDialogueIndex(active_line)); - Refresh(false); - } - // extendRow may not equal the active row if it was set via a shift-click, - // so update it even if the active line didn't change - extendRow = GetDialogueIndex(new_line); -} - -void BaseGrid::SetSelectionAndActive(Selection new_selection, AssDialogue *new_line) { - BeginBatch(); - SetSelectedSet(std::move(new_selection)); - SetActiveLine(new_line); - EndBatch(); -} - -void BaseGrid::PrevLine() { - if (!active_line) return; - auto it = context->ass->Events.iterator_to(*active_line); - if (it != context->ass->Events.begin()) { - --it; - SetSelectionAndActive({&*it}, &*it); - } -} - -void BaseGrid::NextLine() { - if (!active_line) return; - auto it = context->ass->Events.iterator_to(*active_line); - if (++it != context->ass->Events.end()) - SetSelectionAndActive({&*it}, &*it); -} - -void BaseGrid::AnnounceActiveLineChanged(AssDialogue *new_line) { - if (batch_level > 0) - batch_active_line_changed = true; - else - SubtitleSelectionController::AnnounceActiveLineChanged(new_line); -} - -void BaseGrid::AnnounceSelectedSetChanged() { - if (batch_level > 0) - batch_selection_changed = true; - else - SubtitleSelectionController::AnnounceSelectedSetChanged(); -} diff --git a/src/base_grid.h b/src/base_grid.h index 89ec1c127..baf3a5b9f 100644 --- a/src/base_grid.h +++ b/src/base_grid.h @@ -32,8 +32,6 @@ /// @ingroup main_ui /// -#pragma once - #include #include @@ -41,15 +39,14 @@ #include #include -#include "selection_controller.h" - namespace agi { struct Context; class OptionValue; } class AssDialogue; -class BaseGrid final : public wxWindow, public SubtitleSelectionController { +class BaseGrid final : public wxWindow { + std::vector connections; int lineHeight = 1; ///< Height of a line in pixels in the current font bool holding = false; ///< Is a drag selection in process? wxFont font; ///< Current grid font @@ -61,19 +58,9 @@ class BaseGrid final : public wxWindow, public SubtitleSelectionController { /// keyboard, shift-clicking or dragging int extendRow = -1; - Selection selection; ///< Currently selected lines - AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none std::vector index_line_map; ///< Row number -> dialogue line std::map line_index_map; ///< Dialogue line -> row number - /// Selection batch nesting depth; changes are commited only when this - /// hits zero - int batch_level = 0; - /// Has the active line been changed in the current batch? - bool batch_active_line_changed = false; - /// Has the selection been changed in the current batch? - bool batch_selection_changed = false; - /// Connection for video seek event. Stored explicitly so that it can be /// blocked if the relevant option is disabled agi::signal::Connection seek_listener; @@ -93,6 +80,7 @@ class BaseGrid final : public wxWindow, public SubtitleSelectionController { void OnSubtitlesCommit(int type); void OnSubtitlesOpen(); void OnSubtitlesSave(); + void OnActiveLineChanged(AssDialogue *); void DrawImage(wxDC &dc, bool paint_columns[]); void GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wxString *strings, bool replace, wxString const& rep_char) const; @@ -115,35 +103,15 @@ class BaseGrid final : public wxWindow, public SubtitleSelectionController { bool IsDisplayed(const AssDialogue *line) const; - // Re-implement functions from BaseSelectionController to add batching - void AnnounceActiveLineChanged(AssDialogue *new_line); - void AnnounceSelectedSetChanged(); - -protected: agi::Context *context; ///< Current project context -public: - // SelectionController implementation - void SetActiveLine(AssDialogue *new_line) override; - AssDialogue * GetActiveLine() const override { return active_line; } - void SetSelectedSet(Selection new_selection) override; - void GetSelectedSet(Selection &res) const override { res = selection; } - Selection const& GetSelectedSet() const override { return selection; } - void SetSelectionAndActive(Selection new_selection, AssDialogue *new_line) override;; - void NextLine() override; - void PrevLine() override; - - void BeginBatch(); - void EndBatch(); - void SetByFrame(bool state); - - void SelectRow(int row, bool addToSelected = false, bool select=true); - void ClearMaps(); /// @brief Update the row <-> AssDialogue mappings void UpdateMaps(); void UpdateStyle(); + void SelectRow(int row, bool addToSelected = false, bool select=true); + int GetRows() const { return index_line_map.size(); } void MakeRowVisible(int row); @@ -157,8 +125,11 @@ public: /// @return Subtitle index for object, or -1 if unknown subtitle int GetDialogueIndex(AssDialogue *diag) const; - BaseGrid(wxWindow* parent, agi::Context *context, const wxSize& size = wxDefaultSize, long style = wxWANTS_CHARS, const wxString& name = wxPanelNameStr); +public: + BaseGrid(wxWindow* parent, agi::Context *context); ~BaseGrid(); + void SetByFrame(bool state); + DECLARE_EVENT_TABLE() }; diff --git a/src/command/audio.cpp b/src/command/audio.cpp index 32d5c80ff..06c270e96 100644 --- a/src/command/audio.cpp +++ b/src/command/audio.cpp @@ -195,7 +195,7 @@ struct audio_save_clip final : public Command { } void operator()(agi::Context *c) override { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); if (sel.empty()) return; AssTime start = INT_MAX, end = 0; diff --git a/src/command/edit.cpp b/src/command/edit.cpp index 228c752ac..8d1611796 100644 --- a/src/command/edit.cpp +++ b/src/command/edit.cpp @@ -101,7 +101,7 @@ void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) { if (data.empty()) return; AssDialogue *first = nullptr; - SubtitleSelection newsel; + Selection newsel; boost::char_separator sep("\r\n"); for (auto curdata : boost::tokenizer>(data, sep)) { @@ -270,7 +270,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector &blocks, std } void commit_text(agi::Context const * const c, wxString const& desc, int sel_start = -1, int sel_end = -1, int *commit_id = nullptr) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); std::string text = c->selectionController->GetActiveLine()->Text; for_each(sel.begin(), sel.end(), [&](AssDialogue *d) { d->Text = text; }); @@ -488,7 +488,7 @@ struct edit_find_replace final : public Command { static std::string get_entry_data(AssDialogue &d) { return d.GetEntryData(); } static void copy_lines(agi::Context *c) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); SetClipboard(join(c->ass->Events | filtered([&](AssDialogue &d) { return sel.count(&d); }) | transformed(get_entry_data), @@ -496,7 +496,7 @@ static void copy_lines(agi::Context *c) { } static void delete_lines(agi::Context *c, wxString const& commit_message) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); // Find a line near the active line not being deleted to make the new active line AssDialogue *pre_sel = nullptr; @@ -595,7 +595,7 @@ static void duplicate_lines(agi::Context *c, int shift) { auto const& sel = c->selectionController->GetSelectedSet(); auto in_selection = [&](AssDialogue const& d) { return sel.count(const_cast(&d)); }; - SubtitleSelectionController::Selection new_sel; + Selection new_sel; AssDialogue *new_active = nullptr; auto start = c->ass->Events.begin(); @@ -693,7 +693,7 @@ struct edit_line_duplicate_shift_back final : public validate_video_and_sel_none }; static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDialogue *), wxString const& message) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); AssDialogue *first = nullptr; for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ) { @@ -776,7 +776,7 @@ static bool try_paste_lines(agi::Context *c) { } AssDialogue *new_active = &*parsed.begin(); - SubtitleSelection new_selection; + Selection new_selection; for (auto& line : parsed) new_selection.insert(&line); @@ -975,7 +975,7 @@ struct edit_line_recombine final : public validate_sel_multiple { } // Remove now non-existent lines from the selection - SubtitleSelection lines, new_sel; + Selection lines, new_sel; boost::copy(c->ass->Events | agi::address_of, inserter(lines, lines.begin())); boost::set_intersection(lines, sel_set, inserter(new_sel, new_sel.begin())); diff --git a/src/command/grid.cpp b/src/command/grid.cpp index 278635ebc..7604c32c7 100644 --- a/src/command/grid.cpp +++ b/src/command/grid.cpp @@ -389,7 +389,7 @@ struct grid_swap final : public Command { } void operator()(agi::Context *c) override { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); if (sel.size() == 2) { (*sel.begin())->swap_nodes(**sel.rbegin()); c->ass->Commit(_("swap lines"), AssFile::COMMIT_ORDER); diff --git a/src/command/subtitle.cpp b/src/command/subtitle.cpp index 6d8cd32b2..2c510c7a2 100644 --- a/src/command/subtitle.cpp +++ b/src/command/subtitle.cpp @@ -372,7 +372,7 @@ struct subtitle_select_all final : public Command { STR_HELP("Select all dialogue lines") void operator()(agi::Context *c) override { - SubtitleSelection sel; + Selection sel; boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end())); c->selectionController->SetSelectedSet(std::move(sel)); } @@ -390,7 +390,7 @@ struct subtitle_select_visible final : public Command { if (!c->videoController->IsLoaded()) return; c->videoController->Stop(); - SubtitleSelectionController::Selection new_selection; + Selection new_selection; int frame = c->videoController->GetFrameN(); for (auto& diag : c->ass->Events) { diff --git a/src/command/time.cpp b/src/command/time.cpp index 7b5ef9356..34d1a404c 100644 --- a/src/command/time.cpp +++ b/src/command/time.cpp @@ -62,7 +62,7 @@ namespace { struct validate_adjoinable : public Command { CMD_TYPE(COMMAND_VALIDATE) bool Validate(const agi::Context *c) override { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); if (sel.size() < 2) return !sel.empty(); size_t found = 0; @@ -138,8 +138,8 @@ struct time_frame_current final : public validate_video_loaded { void operator()(agi::Context *c) override { if (!c->videoController->IsLoaded()) return; - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); - const AssDialogue *active_line = c->selectionController->GetActiveLine(); + auto const& sel = c->selectionController->GetSelectedSet(); + const auto active_line = c->selectionController->GetActiveLine(); if (sel.empty() || !active_line) return; @@ -168,7 +168,7 @@ struct time_shift final : public Command { }; static void snap_subs_video(agi::Context *c, bool set_start) { - SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); + auto const& sel = c->selectionController->GetSelectedSet(); if (!c->videoController->IsLoaded() || sel.empty()) return; diff --git a/src/dialog_selection.cpp b/src/dialog_selection.cpp index 63245bbc1..1222d9390 100644 --- a/src/dialog_selection.cpp +++ b/src/dialog_selection.cpp @@ -186,7 +186,7 @@ void DialogSelection::Process(wxCommandEvent&) { auto action = static_cast(selection_change_type->GetSelection()); - SubtitleSelection old_sel, new_sel; + Selection old_sel, new_sel; if (action != Action::SET) con->selectionController->GetSelectedSet(old_sel); diff --git a/src/dialog_shift_times.cpp b/src/dialog_shift_times.cpp index 31c291207..b8e888da2 100644 --- a/src/dialog_shift_times.cpp +++ b/src/dialog_shift_times.cpp @@ -31,6 +31,7 @@ #include "help_button.h" #include "libresrc/libresrc.h" #include "options.h" +#include "selection_controller.h" #include "subs_controller.h" #include "timeedit_ctrl.h" #include "video_context.h" @@ -331,7 +332,7 @@ void DialogShiftTimes::Process(wxCommandEvent &) { bool start = type != 2; bool end = type != 1; - SubtitleSelection const& sel = context->selectionController->GetSelectedSet(); + auto const& sel = context->selectionController->GetSelectedSet(); long shift; if (by_time) { diff --git a/src/dialog_shift_times.h b/src/dialog_shift_times.h index a55677acb..29bc9b3d7 100644 --- a/src/dialog_shift_times.h +++ b/src/dialog_shift_times.h @@ -19,8 +19,6 @@ /// @ingroup secondary_ui /// -#include "selection_controller.h" - #include #include #include diff --git a/src/dialog_styling_assistant.cpp b/src/dialog_styling_assistant.cpp index a47c7cb7c..85829e267 100644 --- a/src/dialog_styling_assistant.cpp +++ b/src/dialog_styling_assistant.cpp @@ -35,6 +35,7 @@ #include "help_button.h" #include "libresrc/libresrc.h" #include "persist_location.h" +#include "selection_controller.h" #include "video_context.h" #include diff --git a/src/dialog_styling_assistant.h b/src/dialog_styling_assistant.h index 8e3cb539a..33c09229b 100644 --- a/src/dialog_styling_assistant.h +++ b/src/dialog_styling_assistant.h @@ -14,12 +14,7 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file dialog_styling_assistant.h -/// @see dialog_styling_assistant.cpp -/// @ingroup tools_ui -/// - -#include "selection_controller.h" +#include #include #include diff --git a/src/frame_main.cpp b/src/frame_main.cpp index 49f6a23c8..261e605d8 100644 --- a/src/frame_main.cpp +++ b/src/frame_main.cpp @@ -35,11 +35,6 @@ #include "frame_main.h" -#include -#include -#include -#include - #include "include/aegisub/context.h" #include "include/aegisub/menu.h" #include "include/aegisub/toolbar.h" @@ -61,6 +56,7 @@ #include "libresrc/libresrc.h" #include "main.h" #include "options.h" +#include "selection_controller.h" #include "search_replace_engine.h" #include "subs_controller.h" #include "subs_edit_box.h" @@ -72,6 +68,11 @@ #include "video_display.h" #include "video_slider.h" +#include +#include +#include +#include + #include #include @@ -82,7 +83,7 @@ #include enum { - ID_APP_TIMER_STATUSCLEAR = 12002 + ID_APP_TIMER_STATUSCLEAR = 12002 }; #ifdef WITH_STARTUPLOG @@ -220,8 +221,8 @@ FrameMain::FrameMain() context->local_scripts = new Automation4::LocalScriptManager(context.get()); - // Initialized later due to that the selection controller is currently the subtitles grid - context->selectionController = nullptr; + context->selectionController = new SelectionController(context.get()); + context->subsController->SetSelectionController(context->selectionController); context->videoController = VideoContext::Get(); // derp context->videoController->AddVideoOpenListener(&FrameMain::OnVideoOpen, this); @@ -264,7 +265,6 @@ FrameMain::FrameMain() StartupLog("Complete context initialization"); context->videoController->SetContext(context.get()); - context->subsController->SetSelectionController(context->selectionController); StartupLog("Set up drag/drop target"); SetDropTarget(new AegisubFileDropTarget(this)); @@ -302,40 +302,13 @@ FrameMain::FrameMain() StartupLog("Leaving FrameMain constructor"); } -/// @brief Delete everything but @a keep and its parents -/// @param window Root window to delete the children of -/// @param keep Window to keep alive -/// @return Was @a keep found? -static bool delete_children(wxWindow *window, wxWindow *keep) { - bool found = false; - while (window->GetChildren().size() > (size_t)found) { - auto it = window->GetChildren().begin(); - - if (*it == keep) - found = true; - - if (found) { - if (++it != window->GetChildren().end()) - (*it)->wxWindowBase::Destroy(); - } - else if (!delete_children(*it, keep)) - (*it)->wxWindowBase::Destroy(); - else - found = true; - } - return found; -} - FrameMain::~FrameMain () { wxGetApp().frame = nullptr; context->videoController->SetVideo(""); context->audioController->CloseAudio(); - // SubsGrid needs to be deleted last due to being the selection - // controller, but everything else needs to be deleted before the context - // is cleaned up - delete_children(this, SubsGrid); + DestroyChildren(); delete context->ass; delete context->audioController; @@ -367,8 +340,7 @@ void FrameMain::InitContents() { wxPanel *Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN); StartupLog("Create subtitles grid"); - context->subsGrid = SubsGrid = new BaseGrid(Panel, context.get(), wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER); - context->selectionController = context->subsGrid; + context->subsGrid = new BaseGrid(Panel, context.get()); context->search = new SearchReplaceEngine(context.get()); context->initialLineState = new InitialLineState(context.get()); @@ -391,7 +363,7 @@ void FrameMain::InitContents() { MainSizer = new wxBoxSizer(wxVERTICAL); MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0); MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0); - MainSizer->Add(SubsGrid,1,wxEXPAND | wxALL,0); + MainSizer->Add(context->subsGrid,1,wxEXPAND | wxALL,0); Panel->SetSizer(MainSizer); StartupLog("Perform layout"); diff --git a/src/frame_main.h b/src/frame_main.h index 3b360dc19..f19e18fd9 100644 --- a/src/frame_main.h +++ b/src/frame_main.h @@ -37,11 +37,7 @@ #include #include -#include #include -#include -#include -#include #include #include @@ -49,7 +45,6 @@ class AegisubApp; class AegisubFileDropTarget; class AudioBox; class AudioProvider; -class BaseGrid; class VideoBox; namespace agi { struct Context; class OptionValue; } @@ -98,7 +93,6 @@ class FrameMain: public wxFrame { void EnableToolBar(agi::OptionValue const& opt); - BaseGrid *SubsGrid; ///< The subtitle editing area AudioBox *audioBox; ///< The audio area VideoBox *videoBox; ///< The video area diff --git a/src/include/aegisub/context.h b/src/include/aegisub/context.h index 0d2a3b897..2afdd77e5 100644 --- a/src/include/aegisub/context.h +++ b/src/include/aegisub/context.h @@ -6,7 +6,7 @@ class AudioKaraoke; class DialogManager; class SearchReplaceEngine; class InitialLineState; -template class SelectionController; +class SelectionController; class SubsController; class SubsTextEditCtrl; class BaseGrid; @@ -26,7 +26,7 @@ struct Context { // Controllers AudioController *audioController; - SelectionController *selectionController; + SelectionController *selectionController; SubsController *subsController; TextSelectionController *textSelectionController; VideoContext *videoController; diff --git a/src/search_replace_engine.cpp b/src/search_replace_engine.cpp index e28598a71..e24f5d64a 100644 --- a/src/search_replace_engine.cpp +++ b/src/search_replace_engine.cpp @@ -301,7 +301,7 @@ bool SearchReplaceEngine::ReplaceAll() { auto matches = GetMatcher(settings); - SubtitleSelection const& sel = context->selectionController->GetSelectedSet(); + auto const& sel = context->selectionController->GetSelectedSet(); bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED; for (auto& diag : context->ass->Events) { diff --git a/src/selection_controller.cpp b/src/selection_controller.cpp new file mode 100644 index 000000000..93c1d289e --- /dev/null +++ b/src/selection_controller.cpp @@ -0,0 +1,83 @@ +// 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 "config.h" + +#include "selection_controller.h" + +#include "ass_dialogue.h" +#include "ass_file.h" +#include "include/aegisub/context.h" +#include "subs_controller.h" +#include "utils.h" + +SelectionController::SelectionController(agi::Context *c) +: context(c) +, open_connection(c->subsController->AddFileOpenListener(&SelectionController::OnSubtitlesOpen, this)) +, save_connection(c->subsController->AddFileSaveListener(&SelectionController::OnSubtitlesSave, this)) +{ +} + +void SelectionController::OnSubtitlesOpen() { + selection.clear(); + active_line = nullptr; + if (!context->ass->Events.empty()) { + int row = mid(0, context->ass->GetUIStateAsInt("Active Line"), context->ass->Events.size()); + active_line = &*std::next(context->ass->Events.begin(), row); + selection.insert(active_line); + } + AnnounceSelectedSetChanged(); + AnnounceActiveLineChanged(active_line); +} + +void SelectionController::OnSubtitlesSave() { + if (active_line) + context->ass->SaveUIState("Active Line", std::to_string(std::distance( + context->ass->Events.begin(), context->ass->Events.iterator_to(*active_line)))); +} + +void SelectionController::SetSelectedSet(Selection new_selection) { + selection = std::move(new_selection); + AnnounceSelectedSetChanged(); +} + +void SelectionController::SetActiveLine(AssDialogue *new_line) { + if (new_line != active_line) { + active_line = new_line; + AnnounceActiveLineChanged(new_line); + } +} + +void SelectionController::SetSelectionAndActive(Selection new_selection, AssDialogue *new_line) { + SetSelectedSet(std::move(new_selection)); + SetActiveLine(new_line); +} + +void SelectionController::PrevLine() { + if (!active_line) return; + auto it = context->ass->Events.iterator_to(*active_line); + if (it != context->ass->Events.begin()) { + --it; + SetSelectionAndActive({&*it}, &*it); + } +} + +void SelectionController::NextLine() { + if (!active_line) return; + auto it = context->ass->Events.iterator_to(*active_line); + if (++it != context->ass->Events.end()) + SetSelectionAndActive({&*it}, &*it); +} diff --git a/src/selection_controller.h b/src/selection_controller.h index f42dbacdb..91ba4f6eb 100644 --- a/src/selection_controller.h +++ b/src/selection_controller.h @@ -27,49 +27,32 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file selection_controller.h -/// @ingroup controllers -/// @brief Interface declaration for the SubtitleSelectionController - -#pragma once +#include #include -#include +class AssDialogue; +typedef std::set Selection; + +namespace agi { struct Context; } -/// @class SelectionController -/// @brief Abstract interface for selection controllers -/// -/// Two concepts are managed by implementations of this interface: The concept of the -/// active line, and the concept of the set of selected lines. There is one or zero -/// active lines, the active line is the base for subtitle manipulation in the GUI. -/// The set of selected lines may contain any number of subtitle lines, and those -/// lines are the primary target of subtitle manipulation. In other words, the active -/// line controls what values the user is presented to modify, and the selected set -/// controls what lines are actually modified when the user performs modifications. -/// In most cases, the active line will be a member of the selected set. It will be -/// the responsibility of manipulators to affect the appropriate lines. -/// -/// There is only intended to be one instance of a class implementing this interface -/// per editing session, but there may be many different implementations of it. -/// The primary implementation would be the subtitle grid in the main GUI, allowing -/// the user to actively manipulate the active and selected line sets, but other -/// potential implementations are in a test driver and in a non-interactive scenario. -/// -/// Objects implementing the SelectionListener interface can subscribe to -/// changes in the active line and the selected set. -template class SelectionController { -public: - typedef std::set Selection; - -protected: - agi::signal::Signal AnnounceActiveLineChanged; + agi::signal::Signal AnnounceActiveLineChanged; agi::signal::Signal<> AnnounceSelectedSetChanged; + agi::Context *context; + + Selection selection; ///< Currently selected lines + AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none + + agi::signal::Connection open_connection; + agi::signal::Connection save_connection; + + void OnSubtitlesOpen(); + void OnSubtitlesSave(); + public: - /// Virtual destructor for safety - virtual ~SelectionController() { } + SelectionController(agi::Context *context); /// @brief Change the active line /// @param new_line Subtitle line to become the new active line @@ -81,11 +64,11 @@ public: /// the active line was actually changed. /// /// This method must not affect the selected set. - virtual void SetActiveLine(ItemDataType new_line) = 0; + void SetActiveLine(AssDialogue *new_line); /// @brief Obtain the active line /// @return The active line or nullptr if there is none - virtual ItemDataType GetActiveLine() const = 0; + AssDialogue *GetActiveLine() const { return active_line; } /// @brief Change the selected set /// @param new_selection The set of subtitle lines to become the new selected set @@ -97,15 +80,15 @@ public: /// If no change happens to the selected set, whether because it was refused or /// because the new set was identical to the old set, no change notification may /// be sent. - virtual void SetSelectedSet(Selection new_selection) = 0; + void SetSelectedSet(Selection new_selection); /// @brief Obtain the selected set /// @param[out] selection Filled with the selected set on return - virtual void GetSelectedSet(Selection &selection) const = 0; + void GetSelectedSet(Selection &out) const { out = selection; } /// @brief Obtain the selected set /// @return The selected set - virtual Selection const& GetSelectedSet() const = 0; + Selection const& GetSelectedSet() const { return selection; } /// @brief Set both the selected set and active line /// @param new_line Subtitle line to become the new active line @@ -114,26 +97,22 @@ public: /// This sets both the active line and selected set before announcing the /// change to either of them, and is guaranteed to announce the active line /// change before the selection change. - virtual void SetSelectionAndActive(Selection new_selection, ItemDataType new_line) = 0; + void SetSelectionAndActive(Selection new_selection, AssDialogue *new_line); /// @brief Change the active line to the next in sequence /// /// If there is no logical next line in sequence, no change happens. This should /// also reset the selected set to consist of exactly the active line, if the /// active line was changed. - virtual void NextLine() = 0; + void NextLine(); /// @brief Change the active line to the previous in sequence /// /// If there is no logical previous line in sequence, no change happens. This /// should also reset the selected set to consist of exactly the active line, if /// the active line was changed. - virtual void PrevLine() = 0; + void PrevLine(); DEFINE_SIGNAL_ADDERS(AnnounceSelectedSetChanged, AddSelectionListener) DEFINE_SIGNAL_ADDERS(AnnounceActiveLineChanged, AddActiveLineListener) }; - -class AssDialogue; -typedef SelectionController SubtitleSelectionController; -typedef SubtitleSelectionController::Selection SubtitleSelection; diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp index ee896e39a..497eb97e1 100644 --- a/src/subs_controller.cpp +++ b/src/subs_controller.cpp @@ -23,7 +23,6 @@ #include "ass_file.h" #include "ass_info.h" #include "ass_style.h" -#include "base_grid.h" #include "charset_detect.h" #include "compat.h" #include "command/command.h" @@ -92,7 +91,7 @@ struct SubsController::UndoInfo { sort(begin(selection), end(selection)); AssDialogue *active_line = nullptr; - SubtitleSelection new_sel; + Selection new_sel; for (auto const& info : script_info) c->ass->Info.push_back(*new AssInfo(info.first, info.second)); @@ -108,11 +107,8 @@ struct SubsController::UndoInfo { new_sel.insert(copy); } - c->subsGrid->BeginBatch(); - c->selectionController->SetSelectedSet(std::set{}); c->ass->Commit("", AssFile::COMMIT_NEW); c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line); - c->subsGrid->EndBatch(); } void UpdateActiveLine(const agi::Context *c) { @@ -153,7 +149,7 @@ SubsController::SubsController(agi::Context *context) }); } -void SubsController::SetSelectionController(SelectionController *selection_controller) { +void SubsController::SetSelectionController(SelectionController *selection_controller) { active_line_connection = context->selectionController->AddActiveLineListener(&SubsController::OnActiveLineChanged, this); selection_connection = context->selectionController->AddSelectionListener(&SubsController::OnSelectionChanged, this); } @@ -277,6 +273,7 @@ void SubsController::Close() { blank.swap(*context->ass); context->ass->LoadDefault(); context->ass->Commit("", AssFile::COMMIT_NEW); + FileOpen(filename); } int SubsController::TryToClose(bool allow_cancel) const { diff --git a/src/subs_controller.h b/src/subs_controller.h index f85571603..a380c34e4 100644 --- a/src/subs_controller.h +++ b/src/subs_controller.h @@ -25,7 +25,7 @@ class AssDialogue; class AssFile; struct AssFileCommit; -template class SelectionController; +class SelectionController; namespace agi { struct Context; } @@ -74,7 +74,7 @@ public: /// /// Required due to that the selection controller is the subtitles grid, and /// so is created long after the subtitles controller - void SetSelectionController(SelectionController *selection_controller); + void SetSelectionController(SelectionController *selection_controller); /// The file's path and filename if any, or platform-appropriate "untitled" agi::fs::path Filename() const; diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp index 012d55a4c..892b82486 100644 --- a/src/subs_edit_box.cpp +++ b/src/subs_edit_box.cpp @@ -48,6 +48,7 @@ #include "options.h" #include "placeholder_ctrl.h" #include "scintilla_text_selection_controller.h" +#include "selection_controller.h" #include "subs_edit_ctrl.h" #include "timeedit_ctrl.h" #include "tooltip_manager.h" @@ -383,7 +384,6 @@ void SubsEditBox::OnActiveLineChanged(AssDialogue *new_line) { } void SubsEditBox::OnSelectedSetChanged() { - sel = c->selectionController->GetSelectedSet(); initial_times.clear(); } @@ -421,6 +421,7 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) { template void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) { + auto const& sel = c->selectionController->GetSelectedSet(); for_each(sel.begin(), sel.end(), set); file_changed_slot.Block(); @@ -449,6 +450,7 @@ void SubsEditBox::CommitText(wxString const& desc) { } void SubsEditBox::CommitTimes(TimeField field) { + auto const& sel = c->selectionController->GetSelectedSet(); for (AssDialogue *d : sel) { if (!initial_times.count(d)) initial_times[d] = std::make_pair(d->Start, d->End); diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h index 2415cf153..bc69dda9d 100644 --- a/src/subs_edit_box.h +++ b/src/subs_edit_box.h @@ -45,8 +45,6 @@ #include -#include "selection_controller.h" - namespace agi { namespace vfr { class Framerate; } } namespace agi { struct Context; } class AssDialogue; @@ -80,8 +78,6 @@ class SubsEditBox final : public wxPanel { /// Currently active dialogue line AssDialogue *line = nullptr; - /// Last seen grid selection - SubtitleSelection sel; /// Are the buttons currently split into two lines? bool button_bar_split = true; diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp index b1e06fa73..20231c8ec 100644 --- a/src/visual_tool.cpp +++ b/src/visual_tool.cpp @@ -28,6 +28,7 @@ #include "ass_time.h" #include "include/aegisub/context.h" #include "options.h" +#include "selection_controller.h" #include "utils.h" #include "video_context.h" #include "video_display.h" @@ -297,7 +298,7 @@ void VisualTool::SetSelection(FeatureType *feat, bool clear) { sel_features.clear(); if (sel_features.insert(feat).second && feat->line) { - SubtitleSelection sel; + Selection sel; if (!clear) sel = c->selectionController->GetSelectedSet(); if (sel.insert(feat->line).second) @@ -311,7 +312,7 @@ void VisualTool::RemoveSelection(FeatureType *feat) { for (auto sel : sel_features) if (sel->line == feat->line) return; - SubtitleSelection sel = c->selectionController->GetSelectedSet(); + auto sel = c->selectionController->GetSelectedSet(); // Don't deselect the only selected line if (sel.size() <= 1) return; diff --git a/src/visual_tool.h b/src/visual_tool.h index f5122e555..abf95cf5e 100644 --- a/src/visual_tool.h +++ b/src/visual_tool.h @@ -21,7 +21,6 @@ #pragma once #include "gl_wrap.h" -#include "selection_controller.h" #include "vector2d.h" #include diff --git a/src/visual_tool_cross.cpp b/src/visual_tool_cross.cpp index 0201090f0..7757b924c 100644 --- a/src/visual_tool_cross.cpp +++ b/src/visual_tool_cross.cpp @@ -24,6 +24,7 @@ #include "gl_text.h" #include "include/aegisub/context.h" +#include "selection_controller.h" #include "video_display.h" #include diff --git a/src/visual_tool_drag.cpp b/src/visual_tool_drag.cpp index 9997605cd..12587295e 100644 --- a/src/visual_tool_drag.cpp +++ b/src/visual_tool_drag.cpp @@ -27,6 +27,7 @@ #include "include/aegisub/context.h" #include "libresrc/libresrc.h" #include "options.h" +#include "selection_controller.h" #include "utils.h" #include "video_context.h" #include "video_display.h"