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_ctrl.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)scintilla_text_selection_controller.cpp" />
|
<ClCompile Include="$(SrcDir)scintilla_text_selection_controller.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)search_replace_engine.cpp" />
|
<ClCompile Include="$(SrcDir)search_replace_engine.cpp" />
|
||||||
|
<ClCompile Include="$(SrcDir)selection_controller.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)spellchecker.cpp" />
|
<ClCompile Include="$(SrcDir)spellchecker.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)spellchecker_hunspell.cpp" />
|
<ClCompile Include="$(SrcDir)spellchecker_hunspell.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)spline.cpp" />
|
<ClCompile Include="$(SrcDir)spline.cpp" />
|
||||||
|
|
|
@ -1001,6 +1001,9 @@
|
||||||
<ClCompile Include="$(SrcDir)base_grid.cpp">
|
<ClCompile Include="$(SrcDir)base_grid.cpp">
|
||||||
<Filter>Main UI\Grid</Filter>
|
<Filter>Main UI\Grid</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)selection_controller.cpp">
|
||||||
|
<Filter>Main UI\Grid</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)scintilla_text_ctrl.cpp">
|
<ClCompile Include="$(SrcDir)scintilla_text_ctrl.cpp">
|
||||||
<Filter>Main UI\Edit box</Filter>
|
<Filter>Main UI\Edit box</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -209,6 +209,7 @@ SRC += \
|
||||||
scintilla_text_ctrl.cpp \
|
scintilla_text_ctrl.cpp \
|
||||||
scintilla_text_selection_controller.cpp \
|
scintilla_text_selection_controller.cpp \
|
||||||
search_replace_engine.cpp \
|
search_replace_engine.cpp \
|
||||||
|
selection_controller.cpp \
|
||||||
spellchecker.cpp \
|
spellchecker.cpp \
|
||||||
spline.cpp \
|
spline.cpp \
|
||||||
spline_curve.cpp \
|
spline_curve.cpp \
|
||||||
|
|
|
@ -277,7 +277,7 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
|
||||||
|
|
||||||
AssKaraoke kara;
|
AssKaraoke kara;
|
||||||
|
|
||||||
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
Selection sel = c->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
bool did_split = false;
|
bool did_split = false;
|
||||||
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
||||||
|
|
|
@ -38,7 +38,6 @@ class AudioRenderingStyleRanges;
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
|
|
||||||
#include "audio_marker.h"
|
#include "audio_marker.h"
|
||||||
#include "selection_controller.h"
|
|
||||||
|
|
||||||
/// @class AudioTimingController
|
/// @class AudioTimingController
|
||||||
/// @brief Base class for objects controlling audio timing
|
/// @brief Base class for objects controlling audio timing
|
||||||
|
|
|
@ -348,7 +348,7 @@ class AudioTimingControllerDialogue final : public AudioTimingController {
|
||||||
void RegenerateSelectedLines();
|
void RegenerateSelectedLines();
|
||||||
|
|
||||||
/// Add a line to the list of timeable inactive lines
|
/// 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
|
/// Regenerate the list of active and inactive line markers
|
||||||
void RegenerateMarkers();
|
void RegenerateMarkers();
|
||||||
|
@ -727,7 +727,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
bool was_empty = inactive_lines.empty();
|
bool was_empty = inactive_lines.empty();
|
||||||
inactive_lines.clear();
|
inactive_lines.clear();
|
||||||
|
|
||||||
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
|
auto const& sel = context->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
switch (int mode = inactive_line_mode->GetInt())
|
switch (int mode = inactive_line_mode->GetInt())
|
||||||
{
|
{
|
||||||
|
@ -778,7 +778,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
RegenerateMarkers();
|
RegenerateMarkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::AddInactiveLine(SubtitleSelection const& sel, AssDialogue *diag)
|
void AudioTimingControllerDialogue::AddInactiveLine(Selection const& sel, AssDialogue *diag)
|
||||||
{
|
{
|
||||||
if (sel.count(diag)) return;
|
if (sel.count(diag)) return;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "pen.h"
|
#include "pen.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
|
@ -834,7 +834,7 @@ namespace Automation4 {
|
||||||
|
|
||||||
static int transform_selection(lua_State *L, const agi::Context *c)
|
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();
|
AssDialogue *active_line = c->selectionController->GetActiveLine();
|
||||||
|
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "frame_main.h"
|
#include "frame_main.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "subs_controller.h"
|
#include "subs_controller.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "video_slider.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)
|
BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
|
||||||
: wxWindow(parent, -1, wxDefaultPosition, size, style, name)
|
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER)
|
||||||
, scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL))
|
, scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL))
|
||||||
, seek_listener(context->videoController->AddSeekListener(std::bind(&BaseGrid::Refresh, this, false, nullptr)))
|
, seek_listener(context->videoController->AddSeekListener(std::bind(&BaseGrid::Refresh, this, false, nullptr)))
|
||||||
, context(context)
|
, context(context)
|
||||||
|
@ -108,26 +109,30 @@ BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context, const wxSize& size,
|
||||||
UpdateStyle();
|
UpdateStyle();
|
||||||
OnHighlightVisibleChange(*OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame"));
|
OnHighlightVisibleChange(*OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame"));
|
||||||
|
|
||||||
OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this);
|
connections.push_back(context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this));
|
||||||
OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this);
|
connections.push_back(context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this));
|
||||||
OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this);
|
connections.push_back(context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this));
|
||||||
context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this);
|
|
||||||
context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this);
|
|
||||||
context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this);
|
|
||||||
|
|
||||||
OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this);
|
connections.push_back(context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this);
|
connections.push_back(context->selectionController->AddSelectionListener([&]{ Refresh(false); }));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this);
|
|
||||||
OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Header", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Left Column", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Lines", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Selection", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Colour/Subtitle Grid/Standard", &BaseGrid::UpdateStyle, this);
|
connections.push_back(OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this));
|
||||||
OPT_SUB("Subtitle/Grid/Hide Overrides", std::bind(&BaseGrid::Refresh, this, false, nullptr));
|
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);
|
Bind(wxEVT_CONTEXT_MENU, &BaseGrid::OnContextMenu, this);
|
||||||
}
|
}
|
||||||
|
@ -155,34 +160,16 @@ void BaseGrid::OnSubtitlesCommit(int type) {
|
||||||
}
|
}
|
||||||
if (type & AssFile::COMMIT_DIAG_TIME)
|
if (type & AssFile::COMMIT_DIAG_TIME)
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
//RefreshRect(wxRect(time_cols_x, 0, time_cols_w, GetClientSize().GetHeight()), false);
|
|
||||||
else if (type & AssFile::COMMIT_DIAG_TEXT)
|
else if (type & AssFile::COMMIT_DIAG_TEXT)
|
||||||
RefreshRect(wxRect(text_col_x, 0, text_col_w, GetClientSize().GetHeight()), false);
|
RefreshRect(wxRect(text_col_x, 0, text_col_w, GetClientSize().GetHeight()), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::OnSubtitlesOpen() {
|
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"));
|
ScrollTo(context->ass->GetUIStateAsInt("Scroll Position"));
|
||||||
|
|
||||||
EndBatch();
|
|
||||||
SetColumnWidths();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::OnSubtitlesSave() {
|
void BaseGrid::OnSubtitlesSave() {
|
||||||
context->ass->SaveUIState("Scroll Position", std::to_string(yPos));
|
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) {
|
void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
|
||||||
|
@ -239,20 +226,7 @@ void BaseGrid::UpdateStyle() {
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::ClearMaps() {
|
|
||||||
index_line_map.clear();
|
|
||||||
line_index_map.clear();
|
|
||||||
selection.clear();
|
|
||||||
yPos = 0;
|
|
||||||
AdjustScrollbar();
|
|
||||||
|
|
||||||
AnnounceSelectedSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseGrid::UpdateMaps() {
|
void BaseGrid::UpdateMaps() {
|
||||||
BeginBatch();
|
|
||||||
int active_row = line_index_map[active_line];
|
|
||||||
|
|
||||||
index_line_map.clear();
|
index_line_map.clear();
|
||||||
line_index_map.clear();
|
line_index_map.clear();
|
||||||
|
|
||||||
|
@ -261,47 +235,17 @@ void BaseGrid::UpdateMaps() {
|
||||||
index_line_map.push_back(&curdiag);
|
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();
|
SetColumnWidths();
|
||||||
|
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::BeginBatch() {
|
void BaseGrid::OnActiveLineChanged(AssDialogue *new_active) {
|
||||||
++batch_level;
|
if (new_active) {
|
||||||
}
|
int row = GetDialogueIndex(new_active);
|
||||||
|
MakeRowVisible(row);
|
||||||
void BaseGrid::EndBatch() {
|
extendRow = row;
|
||||||
--batch_level;
|
Refresh(false);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustScrollbar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::MakeRowVisible(int row) {
|
void BaseGrid::MakeRowVisible(int row) {
|
||||||
|
@ -319,23 +263,19 @@ void BaseGrid::SelectRow(int row, bool addToSelected, bool select) {
|
||||||
AssDialogue *line = index_line_map[row];
|
AssDialogue *line = index_line_map[row];
|
||||||
|
|
||||||
if (!addToSelected) {
|
if (!addToSelected) {
|
||||||
Selection sel;
|
context->selectionController->SetSelectedSet(Selection{line});
|
||||||
if (select) sel.insert(line);
|
|
||||||
SetSelectedSet(std::move(sel));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (select && selection.find(line) == selection.end()) {
|
bool selected = !!context->selectionController->GetSelectedSet().count(line);
|
||||||
selection.insert(line);
|
if (select != selected) {
|
||||||
AnnounceSelectedSetChanged();
|
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 &) {
|
void BaseGrid::OnPaint(wxPaintEvent &) {
|
||||||
|
@ -401,9 +341,13 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
||||||
if (override_mode == 1)
|
if (override_mode == 1)
|
||||||
replace_char = to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString());
|
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++) {
|
for (int i = 0; i < nDraw + 1; i++) {
|
||||||
int curRow = i + yPos - 1;
|
int curRow = i + yPos - 1;
|
||||||
RowColor curColor = COLOR_DEFAULT;
|
RowColor curColor = COLOR_DEFAULT;
|
||||||
|
AssDialogue *curDiag = nullptr;
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
@ -411,7 +355,7 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
||||||
dc.SetTextForeground(text_standard);
|
dc.SetTextForeground(text_standard);
|
||||||
}
|
}
|
||||||
// Lines
|
// Lines
|
||||||
else if (AssDialogue *curDiag = GetDialogue(curRow)) {
|
else if ((curDiag = GetDialogue(curRow))) {
|
||||||
GetRowStrings(curRow, curDiag, paint_columns, strings, !!override_mode, replace_char);
|
GetRowStrings(curRow, curDiag, paint_columns, strings, !!override_mode, replace_char);
|
||||||
|
|
||||||
bool inSel = !!selection.count(curDiag);
|
bool inSel = !!selection.count(curDiag);
|
||||||
|
@ -467,28 +411,27 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
dc.DestroyClippingRegion();
|
dc.DestroyClippingRegion();
|
||||||
dc.SetPen(grid_pen);
|
if (curDiag == active_line) {
|
||||||
dc.DrawLine(0,dy+lineHeight,w,dy+lineHeight);
|
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);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw grid columns
|
// Draw grid columns
|
||||||
int dx = 0;
|
int dx = 0;
|
||||||
dc.SetPen(grid_pen);
|
dc.SetPen(grid_pen);
|
||||||
for (int i=0;i<10;i++) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
dx += colWidth[i];
|
dx += colWidth[i];
|
||||||
dc.DrawLine(dx,0,dx,maxH);
|
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(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 {
|
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
|
// but we don't want to scroll until the mouse moves or the button is
|
||||||
// released, to avoid selecting multiple lines on a click
|
// released, to avoid selecting multiple lines on a click
|
||||||
int old_y_pos = yPos;
|
int old_y_pos = yPos;
|
||||||
SetActiveLine(dlg);
|
context->selectionController->SetActiveLine(dlg);
|
||||||
ScrollTo(old_y_pos);
|
ScrollTo(old_y_pos);
|
||||||
|
extendRow = row;
|
||||||
|
|
||||||
|
auto const& selection = context->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
// Toggle selected
|
// Toggle selected
|
||||||
if (click && ctrl && !shift && !alt) {
|
if (click && ctrl && !shift && !alt) {
|
||||||
|
@ -643,7 +589,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
|
||||||
if (ctrl) newsel = selection;
|
if (ctrl) newsel = selection;
|
||||||
for (int i = i1; i <= i2; i++)
|
for (int i = i1; i <= i2; i++)
|
||||||
newsel.insert(GetDialogue(i));
|
newsel.insert(GetDialogue(i));
|
||||||
SetSelectedSet(std::move(newsel));
|
context->selectionController->SetSelectedSet(std::move(newsel));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,7 +776,6 @@ void BaseGrid::SetColumnWidths() {
|
||||||
colWidth[i] += 10;
|
colWidth[i] += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set size of last
|
// Set size of last
|
||||||
int total = std::accumulate(colWidth, colWidth + 10, 0);
|
int total = std::accumulate(colWidth, colWidth + 10, 0);
|
||||||
colWidth[10] = std::max(w - total, 0);
|
colWidth[10] = std::max(w - total, 0);
|
||||||
|
@ -911,8 +856,8 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int old_extend = extendRow;
|
int old_extend = extendRow;
|
||||||
int next = mid(0, GetDialogueIndex(active_line) + dir * step, GetRows() - 1);
|
int next = mid(0, GetDialogueIndex(context->selectionController->GetActiveLine()) + dir * step, GetRows() - 1);
|
||||||
SetActiveLine(GetDialogue(next));
|
context->selectionController->SetActiveLine(GetDialogue(next));
|
||||||
|
|
||||||
// Move selection
|
// Move selection
|
||||||
if (!ctrl && !shift && !alt) {
|
if (!ctrl && !shift && !alt) {
|
||||||
|
@ -921,10 +866,8 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move active only
|
// Move active only
|
||||||
if (alt && !shift && !ctrl) {
|
if (alt && !shift && !ctrl)
|
||||||
Refresh(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Shift-selection
|
// Shift-selection
|
||||||
if (shift && !ctrl && !alt) {
|
if (shift && !ctrl && !alt) {
|
||||||
|
@ -940,7 +883,7 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) {
|
||||||
for (int i = begin; i <= end; i++)
|
for (int i = begin; i <= end; i++)
|
||||||
newsel.insert(GetDialogue(i));
|
newsel.insert(GetDialogue(i));
|
||||||
|
|
||||||
SetSelectedSet(std::move(newsel));
|
context->selectionController->SetSelectedSet(std::move(newsel));
|
||||||
|
|
||||||
MakeRowVisible(next);
|
MakeRowVisible(next);
|
||||||
return;
|
return;
|
||||||
|
@ -953,59 +896,3 @@ void BaseGrid::SetByFrame(bool state) {
|
||||||
SetColumnWidths();
|
SetColumnWidths();
|
||||||
Refresh(false);
|
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
|
/// @ingroup main_ui
|
||||||
///
|
///
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libaegisub/signal.h>
|
#include <libaegisub/signal.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -41,15 +39,14 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
|
|
||||||
#include "selection_controller.h"
|
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
struct Context;
|
struct Context;
|
||||||
class OptionValue;
|
class OptionValue;
|
||||||
}
|
}
|
||||||
class AssDialogue;
|
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
|
int lineHeight = 1; ///< Height of a line in pixels in the current font
|
||||||
bool holding = false; ///< Is a drag selection in process?
|
bool holding = false; ///< Is a drag selection in process?
|
||||||
wxFont font; ///< Current grid font
|
wxFont font; ///< Current grid font
|
||||||
|
@ -61,19 +58,9 @@ class BaseGrid final : public wxWindow, public SubtitleSelectionController {
|
||||||
/// keyboard, shift-clicking or dragging
|
/// keyboard, shift-clicking or dragging
|
||||||
int extendRow = -1;
|
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::vector<AssDialogue*> index_line_map; ///< Row number -> dialogue line
|
||||||
std::map<AssDialogue*,int> line_index_map; ///< Dialogue line -> row number
|
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
|
/// Connection for video seek event. Stored explicitly so that it can be
|
||||||
/// blocked if the relevant option is disabled
|
/// blocked if the relevant option is disabled
|
||||||
agi::signal::Connection seek_listener;
|
agi::signal::Connection seek_listener;
|
||||||
|
@ -93,6 +80,7 @@ class BaseGrid final : public wxWindow, public SubtitleSelectionController {
|
||||||
void OnSubtitlesCommit(int type);
|
void OnSubtitlesCommit(int type);
|
||||||
void OnSubtitlesOpen();
|
void OnSubtitlesOpen();
|
||||||
void OnSubtitlesSave();
|
void OnSubtitlesSave();
|
||||||
|
void OnActiveLineChanged(AssDialogue *);
|
||||||
|
|
||||||
void DrawImage(wxDC &dc, bool paint_columns[]);
|
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;
|
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;
|
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
|
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();
|
void ClearMaps();
|
||||||
/// @brief Update the row <-> AssDialogue mappings
|
/// @brief Update the row <-> AssDialogue mappings
|
||||||
void UpdateMaps();
|
void UpdateMaps();
|
||||||
void UpdateStyle();
|
void UpdateStyle();
|
||||||
|
|
||||||
|
void SelectRow(int row, bool addToSelected = false, bool select=true);
|
||||||
|
|
||||||
int GetRows() const { return index_line_map.size(); }
|
int GetRows() const { return index_line_map.size(); }
|
||||||
void MakeRowVisible(int row);
|
void MakeRowVisible(int row);
|
||||||
|
|
||||||
|
@ -157,8 +125,11 @@ public:
|
||||||
/// @return Subtitle index for object, or -1 if unknown subtitle
|
/// @return Subtitle index for object, or -1 if unknown subtitle
|
||||||
int GetDialogueIndex(AssDialogue *diag) const;
|
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();
|
~BaseGrid();
|
||||||
|
|
||||||
|
void SetByFrame(bool state);
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|
|
@ -195,7 +195,7 @@ struct audio_save_clip final : public Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(agi::Context *c) override {
|
void operator()(agi::Context *c) override {
|
||||||
SubtitleSelection const& sel = c->selectionController->GetSelectedSet();
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
if (sel.empty()) return;
|
if (sel.empty()) return;
|
||||||
|
|
||||||
AssTime start = INT_MAX, end = 0;
|
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;
|
if (data.empty()) return;
|
||||||
|
|
||||||
AssDialogue *first = nullptr;
|
AssDialogue *first = nullptr;
|
||||||
SubtitleSelection newsel;
|
Selection newsel;
|
||||||
|
|
||||||
boost::char_separator<char> sep("\r\n");
|
boost::char_separator<char> sep("\r\n");
|
||||||
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
|
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) {
|
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;
|
std::string text = c->selectionController->GetActiveLine()->Text;
|
||||||
for_each(sel.begin(), sel.end(), [&](AssDialogue *d) { d->Text = 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 std::string get_entry_data(AssDialogue &d) { return d.GetEntryData(); }
|
||||||
static void copy_lines(agi::Context *c) {
|
static void copy_lines(agi::Context *c) {
|
||||||
SubtitleSelection const& sel = c->selectionController->GetSelectedSet();
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
SetClipboard(join(c->ass->Events
|
SetClipboard(join(c->ass->Events
|
||||||
| filtered([&](AssDialogue &d) { return sel.count(&d); })
|
| filtered([&](AssDialogue &d) { return sel.count(&d); })
|
||||||
| transformed(get_entry_data),
|
| 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) {
|
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
|
// Find a line near the active line not being deleted to make the new active line
|
||||||
AssDialogue *pre_sel = nullptr;
|
AssDialogue *pre_sel = nullptr;
|
||||||
|
@ -595,7 +595,7 @@ static void duplicate_lines(agi::Context *c, int shift) {
|
||||||
auto const& sel = c->selectionController->GetSelectedSet();
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
auto in_selection = [&](AssDialogue const& d) { return sel.count(const_cast<AssDialogue *>(&d)); };
|
auto in_selection = [&](AssDialogue const& d) { return sel.count(const_cast<AssDialogue *>(&d)); };
|
||||||
|
|
||||||
SubtitleSelectionController::Selection new_sel;
|
Selection new_sel;
|
||||||
AssDialogue *new_active = nullptr;
|
AssDialogue *new_active = nullptr;
|
||||||
|
|
||||||
auto start = c->ass->Events.begin();
|
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) {
|
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;
|
AssDialogue *first = nullptr;
|
||||||
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ) {
|
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();
|
AssDialogue *new_active = &*parsed.begin();
|
||||||
SubtitleSelection new_selection;
|
Selection new_selection;
|
||||||
for (auto& line : parsed)
|
for (auto& line : parsed)
|
||||||
new_selection.insert(&line);
|
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
|
// 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::copy(c->ass->Events | agi::address_of, inserter(lines, lines.begin()));
|
||||||
boost::set_intersection(lines, sel_set, inserter(new_sel, new_sel.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 {
|
void operator()(agi::Context *c) override {
|
||||||
SubtitleSelection const& sel = c->selectionController->GetSelectedSet();
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
if (sel.size() == 2) {
|
if (sel.size() == 2) {
|
||||||
(*sel.begin())->swap_nodes(**sel.rbegin());
|
(*sel.begin())->swap_nodes(**sel.rbegin());
|
||||||
c->ass->Commit(_("swap lines"), AssFile::COMMIT_ORDER);
|
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")
|
STR_HELP("Select all dialogue lines")
|
||||||
|
|
||||||
void operator()(agi::Context *c) override {
|
void operator()(agi::Context *c) override {
|
||||||
SubtitleSelection sel;
|
Selection sel;
|
||||||
boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
|
boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
|
||||||
c->selectionController->SetSelectedSet(std::move(sel));
|
c->selectionController->SetSelectedSet(std::move(sel));
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ struct subtitle_select_visible final : public Command {
|
||||||
if (!c->videoController->IsLoaded()) return;
|
if (!c->videoController->IsLoaded()) return;
|
||||||
c->videoController->Stop();
|
c->videoController->Stop();
|
||||||
|
|
||||||
SubtitleSelectionController::Selection new_selection;
|
Selection new_selection;
|
||||||
int frame = c->videoController->GetFrameN();
|
int frame = c->videoController->GetFrameN();
|
||||||
|
|
||||||
for (auto& diag : c->ass->Events) {
|
for (auto& diag : c->ass->Events) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace {
|
||||||
struct validate_adjoinable : public Command {
|
struct validate_adjoinable : public Command {
|
||||||
CMD_TYPE(COMMAND_VALIDATE)
|
CMD_TYPE(COMMAND_VALIDATE)
|
||||||
bool Validate(const agi::Context *c) override {
|
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();
|
if (sel.size() < 2) return !sel.empty();
|
||||||
|
|
||||||
size_t found = 0;
|
size_t found = 0;
|
||||||
|
@ -138,8 +138,8 @@ struct time_frame_current final : public validate_video_loaded {
|
||||||
void operator()(agi::Context *c) override {
|
void operator()(agi::Context *c) override {
|
||||||
if (!c->videoController->IsLoaded()) return;
|
if (!c->videoController->IsLoaded()) return;
|
||||||
|
|
||||||
SubtitleSelection const& sel = c->selectionController->GetSelectedSet();
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
const AssDialogue *active_line = c->selectionController->GetActiveLine();
|
const auto active_line = c->selectionController->GetActiveLine();
|
||||||
|
|
||||||
if (sel.empty() || !active_line) return;
|
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) {
|
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;
|
if (!c->videoController->IsLoaded() || sel.empty()) return;
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ void DialogSelection::Process(wxCommandEvent&) {
|
||||||
|
|
||||||
auto action = static_cast<Action>(selection_change_type->GetSelection());
|
auto action = static_cast<Action>(selection_change_type->GetSelection());
|
||||||
|
|
||||||
SubtitleSelection old_sel, new_sel;
|
Selection old_sel, new_sel;
|
||||||
if (action != Action::SET)
|
if (action != Action::SET)
|
||||||
con->selectionController->GetSelectedSet(old_sel);
|
con->selectionController->GetSelectedSet(old_sel);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "subs_controller.h"
|
#include "subs_controller.h"
|
||||||
#include "timeedit_ctrl.h"
|
#include "timeedit_ctrl.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
@ -331,7 +332,7 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
|
||||||
bool start = type != 2;
|
bool start = type != 2;
|
||||||
bool end = type != 1;
|
bool end = type != 1;
|
||||||
|
|
||||||
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
|
auto const& sel = context->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
long shift;
|
long shift;
|
||||||
if (by_time) {
|
if (by_time) {
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
/// @ingroup secondary_ui
|
/// @ingroup secondary_ui
|
||||||
///
|
///
|
||||||
|
|
||||||
#include "selection_controller.h"
|
|
||||||
|
|
||||||
#include <libaegisub/fs_fwd.h>
|
#include <libaegisub/fs_fwd.h>
|
||||||
#include <libaegisub/signal.h>
|
#include <libaegisub/signal.h>
|
||||||
#include <libaegisub/vfr.h>
|
#include <libaegisub/vfr.h>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "persist_location.h"
|
#include "persist_location.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
|
@ -14,12 +14,7 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file dialog_styling_assistant.h
|
#include <libaegisub/signal.h>
|
||||||
/// @see dialog_styling_assistant.cpp
|
|
||||||
/// @ingroup tools_ui
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "selection_controller.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
|
|
|
@ -35,11 +35,6 @@
|
||||||
|
|
||||||
#include "frame_main.h"
|
#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/context.h"
|
||||||
#include "include/aegisub/menu.h"
|
#include "include/aegisub/menu.h"
|
||||||
#include "include/aegisub/toolbar.h"
|
#include "include/aegisub/toolbar.h"
|
||||||
|
@ -61,6 +56,7 @@
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "search_replace_engine.h"
|
#include "search_replace_engine.h"
|
||||||
#include "subs_controller.h"
|
#include "subs_controller.h"
|
||||||
#include "subs_edit_box.h"
|
#include "subs_edit_box.h"
|
||||||
|
@ -72,6 +68,11 @@
|
||||||
#include "video_display.h"
|
#include "video_display.h"
|
||||||
#include "video_slider.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 <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#include <wx/dnd.h>
|
#include <wx/dnd.h>
|
||||||
|
@ -82,7 +83,7 @@
|
||||||
#include <wx/sysopt.h>
|
#include <wx/sysopt.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ID_APP_TIMER_STATUSCLEAR = 12002
|
ID_APP_TIMER_STATUSCLEAR = 12002
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef WITH_STARTUPLOG
|
#ifdef WITH_STARTUPLOG
|
||||||
|
@ -220,8 +221,8 @@ FrameMain::FrameMain()
|
||||||
|
|
||||||
context->local_scripts = new Automation4::LocalScriptManager(context.get());
|
context->local_scripts = new Automation4::LocalScriptManager(context.get());
|
||||||
|
|
||||||
// Initialized later due to that the selection controller is currently the subtitles grid
|
context->selectionController = new SelectionController(context.get());
|
||||||
context->selectionController = nullptr;
|
context->subsController->SetSelectionController(context->selectionController);
|
||||||
|
|
||||||
context->videoController = VideoContext::Get(); // derp
|
context->videoController = VideoContext::Get(); // derp
|
||||||
context->videoController->AddVideoOpenListener(&FrameMain::OnVideoOpen, this);
|
context->videoController->AddVideoOpenListener(&FrameMain::OnVideoOpen, this);
|
||||||
|
@ -264,7 +265,6 @@ FrameMain::FrameMain()
|
||||||
|
|
||||||
StartupLog("Complete context initialization");
|
StartupLog("Complete context initialization");
|
||||||
context->videoController->SetContext(context.get());
|
context->videoController->SetContext(context.get());
|
||||||
context->subsController->SetSelectionController(context->selectionController);
|
|
||||||
|
|
||||||
StartupLog("Set up drag/drop target");
|
StartupLog("Set up drag/drop target");
|
||||||
SetDropTarget(new AegisubFileDropTarget(this));
|
SetDropTarget(new AegisubFileDropTarget(this));
|
||||||
|
@ -302,40 +302,13 @@ FrameMain::FrameMain()
|
||||||
StartupLog("Leaving FrameMain constructor");
|
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 () {
|
FrameMain::~FrameMain () {
|
||||||
wxGetApp().frame = nullptr;
|
wxGetApp().frame = nullptr;
|
||||||
|
|
||||||
context->videoController->SetVideo("");
|
context->videoController->SetVideo("");
|
||||||
context->audioController->CloseAudio();
|
context->audioController->CloseAudio();
|
||||||
|
|
||||||
// SubsGrid needs to be deleted last due to being the selection
|
DestroyChildren();
|
||||||
// controller, but everything else needs to be deleted before the context
|
|
||||||
// is cleaned up
|
|
||||||
delete_children(this, SubsGrid);
|
|
||||||
|
|
||||||
delete context->ass;
|
delete context->ass;
|
||||||
delete context->audioController;
|
delete context->audioController;
|
||||||
|
@ -367,8 +340,7 @@ void FrameMain::InitContents() {
|
||||||
wxPanel *Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
|
wxPanel *Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
|
||||||
|
|
||||||
StartupLog("Create subtitles grid");
|
StartupLog("Create subtitles grid");
|
||||||
context->subsGrid = SubsGrid = new BaseGrid(Panel, context.get(), wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER);
|
context->subsGrid = new BaseGrid(Panel, context.get());
|
||||||
context->selectionController = context->subsGrid;
|
|
||||||
context->search = new SearchReplaceEngine(context.get());
|
context->search = new SearchReplaceEngine(context.get());
|
||||||
context->initialLineState = new InitialLineState(context.get());
|
context->initialLineState = new InitialLineState(context.get());
|
||||||
|
|
||||||
|
@ -391,7 +363,7 @@ void FrameMain::InitContents() {
|
||||||
MainSizer = new wxBoxSizer(wxVERTICAL);
|
MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
|
MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
|
||||||
MainSizer->Add(TopSizer,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);
|
Panel->SetSizer(MainSizer);
|
||||||
|
|
||||||
StartupLog("Perform layout");
|
StartupLog("Perform layout");
|
||||||
|
|
|
@ -37,11 +37,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <wx/combobox.h>
|
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/log.h>
|
|
||||||
#include <wx/menu.h>
|
|
||||||
#include <wx/panel.h>
|
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
|
||||||
|
@ -49,7 +45,6 @@ class AegisubApp;
|
||||||
class AegisubFileDropTarget;
|
class AegisubFileDropTarget;
|
||||||
class AudioBox;
|
class AudioBox;
|
||||||
class AudioProvider;
|
class AudioProvider;
|
||||||
class BaseGrid;
|
|
||||||
class VideoBox;
|
class VideoBox;
|
||||||
|
|
||||||
namespace agi { struct Context; class OptionValue; }
|
namespace agi { struct Context; class OptionValue; }
|
||||||
|
@ -98,7 +93,6 @@ class FrameMain: public wxFrame {
|
||||||
|
|
||||||
void EnableToolBar(agi::OptionValue const& opt);
|
void EnableToolBar(agi::OptionValue const& opt);
|
||||||
|
|
||||||
BaseGrid *SubsGrid; ///< The subtitle editing area
|
|
||||||
AudioBox *audioBox; ///< The audio area
|
AudioBox *audioBox; ///< The audio area
|
||||||
VideoBox *videoBox; ///< The video area
|
VideoBox *videoBox; ///< The video area
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ class AudioKaraoke;
|
||||||
class DialogManager;
|
class DialogManager;
|
||||||
class SearchReplaceEngine;
|
class SearchReplaceEngine;
|
||||||
class InitialLineState;
|
class InitialLineState;
|
||||||
template<class T> class SelectionController;
|
class SelectionController;
|
||||||
class SubsController;
|
class SubsController;
|
||||||
class SubsTextEditCtrl;
|
class SubsTextEditCtrl;
|
||||||
class BaseGrid;
|
class BaseGrid;
|
||||||
|
@ -26,7 +26,7 @@ struct Context {
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
AudioController *audioController;
|
AudioController *audioController;
|
||||||
SelectionController<AssDialogue *> *selectionController;
|
SelectionController *selectionController;
|
||||||
SubsController *subsController;
|
SubsController *subsController;
|
||||||
TextSelectionController *textSelectionController;
|
TextSelectionController *textSelectionController;
|
||||||
VideoContext *videoController;
|
VideoContext *videoController;
|
||||||
|
|
|
@ -301,7 +301,7 @@ bool SearchReplaceEngine::ReplaceAll() {
|
||||||
|
|
||||||
auto matches = GetMatcher(settings);
|
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;
|
bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
|
||||||
|
|
||||||
for (auto& diag : context->ass->Events) {
|
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/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file selection_controller.h
|
#include <libaegisub/signal.h>
|
||||||
/// @ingroup controllers
|
|
||||||
/// @brief Interface declaration for the SubtitleSelectionController
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <set>
|
#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 {
|
class SelectionController {
|
||||||
public:
|
agi::signal::Signal<AssDialogue *> AnnounceActiveLineChanged;
|
||||||
typedef std::set<ItemDataType> Selection;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
agi::signal::Signal<ItemDataType> AnnounceActiveLineChanged;
|
|
||||||
agi::signal::Signal<> AnnounceSelectedSetChanged;
|
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:
|
public:
|
||||||
/// Virtual destructor for safety
|
SelectionController(agi::Context *context);
|
||||||
virtual ~SelectionController() { }
|
|
||||||
|
|
||||||
/// @brief Change the active line
|
/// @brief Change the active line
|
||||||
/// @param new_line Subtitle line to become the new active line
|
/// @param new_line Subtitle line to become the new active line
|
||||||
|
@ -81,11 +64,11 @@ public:
|
||||||
/// the active line was actually changed.
|
/// the active line was actually changed.
|
||||||
///
|
///
|
||||||
/// This method must not affect the selected set.
|
/// 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
|
/// @brief Obtain the active line
|
||||||
/// @return The active line or nullptr if there is none
|
/// @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
|
/// @brief Change the selected set
|
||||||
/// @param new_selection The set of subtitle lines to become the new 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
|
/// 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
|
/// because the new set was identical to the old set, no change notification may
|
||||||
/// be sent.
|
/// be sent.
|
||||||
virtual void SetSelectedSet(Selection new_selection) = 0;
|
void SetSelectedSet(Selection new_selection);
|
||||||
|
|
||||||
/// @brief Obtain the selected set
|
/// @brief Obtain the selected set
|
||||||
/// @param[out] selection Filled with the selected set on return
|
/// @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
|
/// @brief Obtain the selected set
|
||||||
/// @return 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
|
/// @brief Set both the selected set and active line
|
||||||
/// @param new_line Subtitle line to become the new 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
|
/// 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 to either of them, and is guaranteed to announce the active line
|
||||||
/// change before the selection change.
|
/// 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
|
/// @brief Change the active line to the next in sequence
|
||||||
///
|
///
|
||||||
/// If there is no logical next line in sequence, no change happens. This should
|
/// 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
|
/// also reset the selected set to consist of exactly the active line, if the
|
||||||
/// active line was changed.
|
/// active line was changed.
|
||||||
virtual void NextLine() = 0;
|
void NextLine();
|
||||||
|
|
||||||
/// @brief Change the active line to the previous in sequence
|
/// @brief Change the active line to the previous in sequence
|
||||||
///
|
///
|
||||||
/// If there is no logical previous line in sequence, no change happens. This
|
/// 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
|
/// should also reset the selected set to consist of exactly the active line, if
|
||||||
/// the active line was changed.
|
/// the active line was changed.
|
||||||
virtual void PrevLine() = 0;
|
void PrevLine();
|
||||||
|
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceSelectedSetChanged, AddSelectionListener)
|
DEFINE_SIGNAL_ADDERS(AnnounceSelectedSetChanged, AddSelectionListener)
|
||||||
DEFINE_SIGNAL_ADDERS(AnnounceActiveLineChanged, AddActiveLineListener)
|
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_file.h"
|
||||||
#include "ass_info.h"
|
#include "ass_info.h"
|
||||||
#include "ass_style.h"
|
#include "ass_style.h"
|
||||||
#include "base_grid.h"
|
|
||||||
#include "charset_detect.h"
|
#include "charset_detect.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "command/command.h"
|
#include "command/command.h"
|
||||||
|
@ -92,7 +91,7 @@ struct SubsController::UndoInfo {
|
||||||
sort(begin(selection), end(selection));
|
sort(begin(selection), end(selection));
|
||||||
|
|
||||||
AssDialogue *active_line = nullptr;
|
AssDialogue *active_line = nullptr;
|
||||||
SubtitleSelection new_sel;
|
Selection new_sel;
|
||||||
|
|
||||||
for (auto const& info : script_info)
|
for (auto const& info : script_info)
|
||||||
c->ass->Info.push_back(*new AssInfo(info.first, info.second));
|
c->ass->Info.push_back(*new AssInfo(info.first, info.second));
|
||||||
|
@ -108,11 +107,8 @@ struct SubsController::UndoInfo {
|
||||||
new_sel.insert(copy);
|
new_sel.insert(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
c->subsGrid->BeginBatch();
|
|
||||||
c->selectionController->SetSelectedSet(std::set<AssDialogue *>{});
|
|
||||||
c->ass->Commit("", AssFile::COMMIT_NEW);
|
c->ass->Commit("", AssFile::COMMIT_NEW);
|
||||||
c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
|
c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
|
||||||
c->subsGrid->EndBatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateActiveLine(const agi::Context *c) {
|
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);
|
active_line_connection = context->selectionController->AddActiveLineListener(&SubsController::OnActiveLineChanged, this);
|
||||||
selection_connection = context->selectionController->AddSelectionListener(&SubsController::OnSelectionChanged, this);
|
selection_connection = context->selectionController->AddSelectionListener(&SubsController::OnSelectionChanged, this);
|
||||||
}
|
}
|
||||||
|
@ -277,6 +273,7 @@ void SubsController::Close() {
|
||||||
blank.swap(*context->ass);
|
blank.swap(*context->ass);
|
||||||
context->ass->LoadDefault();
|
context->ass->LoadDefault();
|
||||||
context->ass->Commit("", AssFile::COMMIT_NEW);
|
context->ass->Commit("", AssFile::COMMIT_NEW);
|
||||||
|
FileOpen(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SubsController::TryToClose(bool allow_cancel) const {
|
int SubsController::TryToClose(bool allow_cancel) const {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
class AssFile;
|
class AssFile;
|
||||||
struct AssFileCommit;
|
struct AssFileCommit;
|
||||||
template<typename T> class SelectionController;
|
class SelectionController;
|
||||||
|
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ public:
|
||||||
///
|
///
|
||||||
/// Required due to that the selection controller is the subtitles grid, and
|
/// Required due to that the selection controller is the subtitles grid, and
|
||||||
/// so is created long after the subtitles controller
|
/// 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"
|
/// The file's path and filename if any, or platform-appropriate "untitled"
|
||||||
agi::fs::path Filename() const;
|
agi::fs::path Filename() const;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "placeholder_ctrl.h"
|
#include "placeholder_ctrl.h"
|
||||||
#include "scintilla_text_selection_controller.h"
|
#include "scintilla_text_selection_controller.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "subs_edit_ctrl.h"
|
#include "subs_edit_ctrl.h"
|
||||||
#include "timeedit_ctrl.h"
|
#include "timeedit_ctrl.h"
|
||||||
#include "tooltip_manager.h"
|
#include "tooltip_manager.h"
|
||||||
|
@ -383,7 +384,6 @@ void SubsEditBox::OnActiveLineChanged(AssDialogue *new_line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::OnSelectedSetChanged() {
|
void SubsEditBox::OnSelectedSetChanged() {
|
||||||
sel = c->selectionController->GetSelectedSet();
|
|
||||||
initial_times.clear();
|
initial_times.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,6 +421,7 @@ void SubsEditBox::OnChange(wxStyledTextEvent &event) {
|
||||||
|
|
||||||
template<class setter>
|
template<class setter>
|
||||||
void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) {
|
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);
|
for_each(sel.begin(), sel.end(), set);
|
||||||
|
|
||||||
file_changed_slot.Block();
|
file_changed_slot.Block();
|
||||||
|
@ -449,6 +450,7 @@ void SubsEditBox::CommitText(wxString const& desc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::CommitTimes(TimeField field) {
|
void SubsEditBox::CommitTimes(TimeField field) {
|
||||||
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
for (AssDialogue *d : sel) {
|
for (AssDialogue *d : sel) {
|
||||||
if (!initial_times.count(d))
|
if (!initial_times.count(d))
|
||||||
initial_times[d] = std::make_pair(d->Start, d->End);
|
initial_times[d] = std::make_pair(d->Start, d->End);
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
|
|
||||||
#include <libaegisub/signal.h>
|
#include <libaegisub/signal.h>
|
||||||
|
|
||||||
#include "selection_controller.h"
|
|
||||||
|
|
||||||
namespace agi { namespace vfr { class Framerate; } }
|
namespace agi { namespace vfr { class Framerate; } }
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
|
@ -80,8 +78,6 @@ class SubsEditBox final : public wxPanel {
|
||||||
|
|
||||||
/// Currently active dialogue line
|
/// Currently active dialogue line
|
||||||
AssDialogue *line = nullptr;
|
AssDialogue *line = nullptr;
|
||||||
/// Last seen grid selection
|
|
||||||
SubtitleSelection sel;
|
|
||||||
|
|
||||||
/// Are the buttons currently split into two lines?
|
/// Are the buttons currently split into two lines?
|
||||||
bool button_bar_split = true;
|
bool button_bar_split = true;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "ass_time.h"
|
#include "ass_time.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "video_display.h"
|
#include "video_display.h"
|
||||||
|
@ -297,7 +298,7 @@ void VisualTool<FeatureType>::SetSelection(FeatureType *feat, bool clear) {
|
||||||
sel_features.clear();
|
sel_features.clear();
|
||||||
|
|
||||||
if (sel_features.insert(feat).second && feat->line) {
|
if (sel_features.insert(feat).second && feat->line) {
|
||||||
SubtitleSelection sel;
|
Selection sel;
|
||||||
if (!clear)
|
if (!clear)
|
||||||
sel = c->selectionController->GetSelectedSet();
|
sel = c->selectionController->GetSelectedSet();
|
||||||
if (sel.insert(feat->line).second)
|
if (sel.insert(feat->line).second)
|
||||||
|
@ -311,7 +312,7 @@ void VisualTool<FeatureType>::RemoveSelection(FeatureType *feat) {
|
||||||
for (auto sel : sel_features)
|
for (auto sel : sel_features)
|
||||||
if (sel->line == feat->line) return;
|
if (sel->line == feat->line) return;
|
||||||
|
|
||||||
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
auto sel = c->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
// Don't deselect the only selected line
|
// Don't deselect the only selected line
|
||||||
if (sel.size() <= 1) return;
|
if (sel.size() <= 1) return;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gl_wrap.h"
|
#include "gl_wrap.h"
|
||||||
#include "selection_controller.h"
|
|
||||||
#include "vector2d.h"
|
#include "vector2d.h"
|
||||||
|
|
||||||
#include <libaegisub/owning_intrusive_list.h>
|
#include <libaegisub/owning_intrusive_list.h>
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "gl_text.h"
|
#include "gl_text.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "video_display.h"
|
#include "video_display.h"
|
||||||
|
|
||||||
#include <libaegisub/color.h>
|
#include <libaegisub/color.h>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "selection_controller.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "video_display.h"
|
#include "video_display.h"
|
||||||
|
|
Loading…
Reference in a new issue