Extract SelectionController from BaseGrid
This commit is contained in:
parent
eb548306e9
commit
523d858374
34 changed files with 246 additions and 363 deletions
|
@ -390,6 +390,7 @@
|
|||
<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" />
|
||||
<ClCompile Include="$(SrcDir)spellchecker_hunspell.cpp" />
|
||||
<ClCompile Include="$(SrcDir)spline.cpp" />
|
||||
|
|
|
@ -1001,6 +1001,9 @@
|
|||
<ClCompile Include="$(SrcDir)base_grid.cpp">
|
||||
<Filter>Main UI\Grid</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)selection_controller.cpp">
|
||||
<Filter>Main UI\Grid</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)scintilla_text_ctrl.cpp">
|
||||
<Filter>Main UI\Edit box</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -277,7 +277,7 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> 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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "include/aegisub/context.h"
|
||||
#include "options.h"
|
||||
#include "pen.h"
|
||||
#include "selection_controller.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <deque>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
bool selected = !!context->selectionController->GetSelectedSet().count(line);
|
||||
if (select != selected) {
|
||||
auto selection = context->selectionController->GetSelectedSet();
|
||||
if (select)
|
||||
selection.insert(line);
|
||||
AnnounceSelectedSetChanged();
|
||||
}
|
||||
else if (!select && selection.find(line) != selection.end()) {
|
||||
else
|
||||
selection.erase(line);
|
||||
AnnounceSelectedSetChanged();
|
||||
context->selectionController->SetSelectedSet(std::move(selection));
|
||||
}
|
||||
|
||||
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();
|
||||
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.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();
|
||||
}
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
/// @ingroup main_ui
|
||||
///
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
#include <map>
|
||||
|
@ -41,15 +39,14 @@
|
|||
#include <vector>
|
||||
#include <wx/window.h>
|
||||
|
||||
#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<agi::signal::Connection> 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<AssDialogue*> index_line_map; ///< Row number -> dialogue line
|
||||
std::map<AssDialogue*,int> 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()
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<char> sep("\r\n");
|
||||
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
|
||||
|
@ -270,7 +270,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector<AssDialogueBlock> &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<AssDialogue *>(&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()));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ void DialogSelection::Process(wxCommandEvent&) {
|
|||
|
||||
auto action = static_cast<Action>(selection_change_type->GetSelection());
|
||||
|
||||
SubtitleSelection old_sel, new_sel;
|
||||
Selection old_sel, new_sel;
|
||||
if (action != Action::SET)
|
||||
con->selectionController->GetSelectedSet(old_sel);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
/// @ingroup secondary_ui
|
||||
///
|
||||
|
||||
#include "selection_controller.h"
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
#include <libaegisub/vfr.h>
|
||||
|
|
|
@ -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 <libaegisub/util.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 <libaegisub/signal.h>
|
||||
|
||||
#include <memory>
|
||||
#include <wx/dialog.h>
|
||||
|
|
|
@ -35,11 +35,6 @@
|
|||
|
||||
#include "frame_main.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/path.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#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 <libaegisub/fs.h>
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/path.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <wx/dnd.h>
|
||||
|
@ -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");
|
||||
|
|
|
@ -37,11 +37,7 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/frame.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class AudioKaraoke;
|
|||
class DialogManager;
|
||||
class SearchReplaceEngine;
|
||||
class InitialLineState;
|
||||
template<class T> class SelectionController;
|
||||
class SelectionController;
|
||||
class SubsController;
|
||||
class SubsTextEditCtrl;
|
||||
class BaseGrid;
|
||||
|
@ -26,7 +26,7 @@ struct Context {
|
|||
|
||||
// Controllers
|
||||
AudioController *audioController;
|
||||
SelectionController<AssDialogue *> *selectionController;
|
||||
SelectionController *selectionController;
|
||||
SubsController *subsController;
|
||||
TextSelectionController *textSelectionController;
|
||||
VideoContext *videoController;
|
||||
|
|
|
@ -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) {
|
||||
|
|
83
src/selection_controller.cpp
Normal file
83
src/selection_controller.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
// 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 "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<int>(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);
|
||||
}
|
|
@ -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 <libaegisub/signal.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <libaegisub/signal.h>
|
||||
class AssDialogue;
|
||||
typedef std::set<AssDialogue *> 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 <typename ItemDataType>
|
||||
class SelectionController {
|
||||
public:
|
||||
typedef std::set<ItemDataType> Selection;
|
||||
|
||||
protected:
|
||||
agi::signal::Signal<ItemDataType> AnnounceActiveLineChanged;
|
||||
agi::signal::Signal<AssDialogue *> 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<AssDialogue *> SubtitleSelectionController;
|
||||
typedef SubtitleSelectionController::Selection SubtitleSelection;
|
||||
|
|
|
@ -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<AssDialogue *>{});
|
||||
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<AssDialogue *> *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 {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
class AssDialogue;
|
||||
class AssFile;
|
||||
struct AssFileCommit;
|
||||
template<typename T> 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<AssDialogue *> *selection_controller);
|
||||
void SetSelectionController(SelectionController *selection_controller);
|
||||
|
||||
/// The file's path and filename if any, or platform-appropriate "untitled"
|
||||
agi::fs::path Filename() const;
|
||||
|
|
|
@ -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<class setter>
|
||||
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);
|
||||
|
|
|
@ -45,8 +45,6 @@
|
|||
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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<FeatureType>::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<FeatureType>::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;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "gl_wrap.h"
|
||||
#include "selection_controller.h"
|
||||
#include "vector2d.h"
|
||||
|
||||
#include <libaegisub/owning_intrusive_list.h>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "gl_text.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "selection_controller.h"
|
||||
#include "video_display.h"
|
||||
|
||||
#include <libaegisub/color.h>
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue