forked from mia/Aegisub
Extract all of the column-specific logic from BaseGrid
This commit is contained in:
parent
909be4494a
commit
5df871f599
8 changed files with 529 additions and 306 deletions
|
@ -171,6 +171,7 @@
|
||||||
<ClInclude Include="$(SrcDir)frame_main.h" />
|
<ClInclude Include="$(SrcDir)frame_main.h" />
|
||||||
<ClInclude Include="$(SrcDir)gl_text.h" />
|
<ClInclude Include="$(SrcDir)gl_text.h" />
|
||||||
<ClInclude Include="$(SrcDir)gl_wrap.h" />
|
<ClInclude Include="$(SrcDir)gl_wrap.h" />
|
||||||
|
<ClInclude Include="$(SrcDir)grid_column.h" />
|
||||||
<ClInclude Include="$(SrcDir)help_button.h" />
|
<ClInclude Include="$(SrcDir)help_button.h" />
|
||||||
<ClInclude Include="$(SrcDir)hotkey_data_view_model.h" />
|
<ClInclude Include="$(SrcDir)hotkey_data_view_model.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\aegisub\audio_player.h" />
|
<ClInclude Include="$(SrcDir)include\aegisub\audio_player.h" />
|
||||||
|
@ -373,6 +374,7 @@
|
||||||
<ClCompile Include="$(SrcDir)frame_main.cpp" />
|
<ClCompile Include="$(SrcDir)frame_main.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)gl_text.cpp" />
|
<ClCompile Include="$(SrcDir)gl_text.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)gl_wrap.cpp" />
|
<ClCompile Include="$(SrcDir)gl_wrap.cpp" />
|
||||||
|
<ClCompile Include="$(SrcDir)grid_column.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)help_button.cpp" />
|
<ClCompile Include="$(SrcDir)help_button.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)hotkey.cpp" />
|
<ClCompile Include="$(SrcDir)hotkey.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)hotkey_data_view_model.cpp" />
|
<ClCompile Include="$(SrcDir)hotkey_data_view_model.cpp" />
|
||||||
|
|
|
@ -624,6 +624,9 @@
|
||||||
<ClInclude Include="$(SrcDir)resolution_resampler.h">
|
<ClInclude Include="$(SrcDir)resolution_resampler.h">
|
||||||
<Filter>Features\Resolution resampler</Filter>
|
<Filter>Features\Resolution resampler</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="$(SrcDir)grid_column.h">
|
||||||
|
<Filter>Main UI\Grid</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="$(SrcDir)ass_dialogue.cpp">
|
<ClCompile Include="$(SrcDir)ass_dialogue.cpp">
|
||||||
|
@ -1181,6 +1184,9 @@
|
||||||
<ClCompile Include="$(SrcDir)text_selection_controller.cpp">
|
<ClCompile Include="$(SrcDir)text_selection_controller.cpp">
|
||||||
<Filter>Main UI\Edit box</Filter>
|
<Filter>Main UI\Edit box</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)grid_column.cpp">
|
||||||
|
<Filter>Main UI\Grid</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="$(SrcDir)res/res.rc">
|
<ResourceCompile Include="$(SrcDir)res/res.rc">
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <libaegisub/time.h>
|
#include <libaegisub/time.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <boost/range/irange.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -74,5 +75,10 @@ namespace agi {
|
||||||
|
|
||||||
std::string ErrorString(int error);
|
std::string ErrorString(int error);
|
||||||
|
|
||||||
|
template<typename Integer>
|
||||||
|
auto range(Integer end) -> decltype(boost::irange<Integer>(0, end)) {
|
||||||
|
return boost::irange<Integer>(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace agi
|
} // namespace agi
|
||||||
|
|
|
@ -194,6 +194,7 @@ SRC += \
|
||||||
frame_main.cpp \
|
frame_main.cpp \
|
||||||
gl_text.cpp \
|
gl_text.cpp \
|
||||||
gl_wrap.cpp \
|
gl_wrap.cpp \
|
||||||
|
grid_column.cpp \
|
||||||
help_button.cpp \
|
help_button.cpp \
|
||||||
hotkey.cpp \
|
hotkey.cpp \
|
||||||
hotkey_data_view_model.cpp \
|
hotkey_data_view_model.cpp \
|
||||||
|
|
|
@ -35,24 +35,23 @@
|
||||||
|
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "ass_style.h"
|
|
||||||
#include "audio_box.h"
|
#include "audio_box.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "frame_main.h"
|
#include "frame_main.h"
|
||||||
|
#include "grid_column.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "selection_controller.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 <libaegisub/util.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/range/algorithm.hpp>
|
#include <boost/range/algorithm.hpp>
|
||||||
#include <boost/range/irange.hpp>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <wx/dcbuffer.h>
|
#include <wx/dcbuffer.h>
|
||||||
#include <wx/kbdstate.h>
|
#include <wx/kbdstate.h>
|
||||||
|
@ -65,23 +64,11 @@ enum {
|
||||||
MENU_SHOW_COL = 1250 // Needs 15 IDs after this
|
MENU_SHOW_COL = 1250 // Needs 15 IDs after this
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template <typename T>
|
|
||||||
struct hash<boost::flyweight<T>> {
|
|
||||||
size_t operator()(boost::flyweight<T> const& ss) const {
|
|
||||||
return hash<const void*>()(&ss.get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
|
BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
|
||||||
: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER)
|
: 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)))
|
, columns(GetGridColumns())
|
||||||
, headerNames({{
|
, seek_listener(context->videoController->AddSeekListener([&] { Refresh(false); }))
|
||||||
_("#"), _("L"), _("Start"), _("End"), _("CPS"), _("Style"), _("Actor"),
|
|
||||||
_("Effect"), _("Left"), _("Right"), _("Vert"), _("Text")
|
|
||||||
}})
|
|
||||||
, context(context)
|
, context(context)
|
||||||
{
|
{
|
||||||
scrollBar->SetScrollbar(0,10,100,10);
|
scrollBar->SetScrollbar(0,10,100,10);
|
||||||
|
@ -149,8 +136,8 @@ void BaseGrid::OnSubtitlesCommit(int type) {
|
||||||
if (type & AssFile::COMMIT_DIAG_TIME)
|
if (type & AssFile::COMMIT_DIAG_TIME)
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
else if (type & AssFile::COMMIT_DIAG_TEXT) {
|
else if (type & AssFile::COMMIT_DIAG_TEXT) {
|
||||||
RefreshRect(wxRect(cps_col_x, 0, cps_col_w, GetClientSize().GetHeight()), false);
|
for (auto const& rect : text_refresh_rects)
|
||||||
RefreshRect(wxRect(text_col_x, 0, text_col_w, GetClientSize().GetHeight()), false);
|
RefreshRect(rect, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +151,9 @@ void BaseGrid::OnSubtitlesSave() {
|
||||||
|
|
||||||
void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
|
void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
|
||||||
int item = event.GetId() - MENU_SHOW_COL;
|
int item = event.GetId() - MENU_SHOW_COL;
|
||||||
showCol[item] = !showCol[item];
|
column_shown[item] = !column_shown[item];
|
||||||
|
|
||||||
OPT_SET("Subtitle/Grid/Column")->SetListBool(std::vector<bool>(std::begin(showCol), std::end(showCol)));
|
OPT_SET("Subtitle/Grid/Column")->SetListBool(std::vector<bool>(std::begin(column_shown), std::end(column_shown)));
|
||||||
|
|
||||||
SetColumnWidths();
|
SetColumnWidths();
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
|
@ -203,13 +190,12 @@ void BaseGrid::UpdateStyle() {
|
||||||
|
|
||||||
// Set column widths
|
// Set column widths
|
||||||
std::vector<bool> column_array(OPT_GET("Subtitle/Grid/Column")->GetListBool());
|
std::vector<bool> column_array(OPT_GET("Subtitle/Grid/Column")->GetListBool());
|
||||||
assert(column_array.size() <= showCol.size());
|
column_shown.assign(column_array.begin(), column_array.end());
|
||||||
boost::copy(column_array, std::begin(showCol));
|
column_shown.resize(columns.size(), true);
|
||||||
for (size_t i : boost::irange(column_array.size(), showCol.size()))
|
|
||||||
showCol[i] = true;
|
|
||||||
|
|
||||||
for (int i : boost::irange(0, column_count))
|
column_header_widths.resize(columns.size());
|
||||||
headerWidth[i] = dc.GetTextExtent(headerNames[i]).GetWidth();
|
for (size_t i : agi::util::range(columns.size()))
|
||||||
|
column_header_widths[i] = dc.GetTextExtent(columns[i]->Header()).GetWidth();
|
||||||
|
|
||||||
SetColumnWidths();
|
SetColumnWidths();
|
||||||
|
|
||||||
|
@ -268,32 +254,29 @@ void BaseGrid::SelectRow(int row, bool addToSelected, bool select) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::OnPaint(wxPaintEvent &) {
|
void BaseGrid::OnPaint(wxPaintEvent &) {
|
||||||
// Get size and pos
|
|
||||||
wxSize cs = GetClientSize();
|
|
||||||
cs.SetWidth(cs.GetWidth() - scrollBar->GetSize().GetWidth());
|
|
||||||
|
|
||||||
// Find which columns need to be repainted
|
// Find which columns need to be repainted
|
||||||
bool paint_columns[column_count] = {0};
|
std::vector<GridColumn *> paint_columns;
|
||||||
|
std::vector<int> paint_column_widths;
|
||||||
for (wxRegionIterator region(GetUpdateRegion()); region; ++region) {
|
for (wxRegionIterator region(GetUpdateRegion()); region; ++region) {
|
||||||
wxRect updrect = region.GetRect();
|
wxRect updrect = region.GetRect();
|
||||||
int x = 0;
|
int x = 0;
|
||||||
for (int i : boost::irange(0, column_count)) {
|
for (size_t i : agi::util::range(columns.size())) {
|
||||||
if (updrect.x < x + colWidth[i] && updrect.x + updrect.width > x && colWidth[i])
|
if (updrect.x < x + column_widths[i] && updrect.x + updrect.width > x && column_widths[i])
|
||||||
paint_columns[i] = true;
|
paint_columns.push_back(columns[i].get());
|
||||||
x += colWidth[i];
|
else
|
||||||
|
paint_columns.push_back(nullptr);
|
||||||
|
x += column_widths[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxAutoBufferedPaintDC dc(this);
|
if (paint_columns.empty()) return;
|
||||||
DrawImage(dc, paint_columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
|
||||||
int w = 0;
|
int w = 0;
|
||||||
int h = 0;
|
int h = 0;
|
||||||
GetClientSize(&w,&h);
|
GetClientSize(&w,&h);
|
||||||
w -= scrollBar->GetSize().GetWidth();
|
w -= scrollBar->GetSize().GetWidth();
|
||||||
|
|
||||||
|
wxAutoBufferedPaintDC dc(this);
|
||||||
dc.SetFont(font);
|
dc.SetFont(font);
|
||||||
|
|
||||||
dc.SetBackground(row_colors.Default);
|
dc.SetBackground(row_colors.Default);
|
||||||
|
@ -302,12 +285,7 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
||||||
// Draw labels
|
// Draw labels
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
dc.SetBrush(row_colors.LeftCol);
|
dc.SetBrush(row_colors.LeftCol);
|
||||||
dc.DrawRectangle(0,lineHeight,colWidth[0],h-lineHeight);
|
dc.DrawRectangle(0, lineHeight, column_widths[0], h-lineHeight);
|
||||||
|
|
||||||
// Visible lines
|
|
||||||
int drawPerScreen = h/lineHeight + 1;
|
|
||||||
int nDraw = mid(0,drawPerScreen,GetRows()-yPos);
|
|
||||||
int maxH = (nDraw+1) * lineHeight;
|
|
||||||
|
|
||||||
// Row colors
|
// Row colors
|
||||||
wxColour text_standard(to_wx(OPT_GET("Colour/Subtitle Grid/Standard")->GetColor()));
|
wxColour text_standard(to_wx(OPT_GET("Colour/Subtitle Grid/Standard")->GetColor()));
|
||||||
|
@ -320,181 +298,109 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) {
|
||||||
dc.DrawLine(0, 0, w, 0);
|
dc.DrawLine(0, 0, w, 0);
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
|
|
||||||
auto strings = headerNames;
|
auto paint_text = [&](wxString const& str, int x, int y, int col) {
|
||||||
|
wxSize ext = dc.GetTextExtent(str);
|
||||||
|
|
||||||
int override_mode = OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt();
|
int top = y + (lineHeight - ext.GetHeight()) / 2;
|
||||||
wxString replace_char;
|
int left = x + 4;
|
||||||
if (override_mode == 1)
|
if (columns[col]->Centered())
|
||||||
replace_char = to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString());
|
left += (column_widths[col] - 6 - ext.GetWidth()) / 2;
|
||||||
|
|
||||||
|
dc.DrawText(str, left, top);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Paint header
|
||||||
|
{
|
||||||
|
dc.SetTextForeground(text_standard);
|
||||||
|
dc.SetBrush(row_colors.Header);
|
||||||
|
dc.DrawRectangle(0, 0, w, lineHeight);
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
for (size_t i : agi::util::range(columns.size())) {
|
||||||
|
if (paint_columns[i])
|
||||||
|
paint_text(columns[i]->Header(), x, 0, i);
|
||||||
|
x += column_widths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.SetPen(grid_pen);
|
||||||
|
dc.DrawLine(0, lineHeight, w, lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint the rows
|
||||||
|
int drawPerScreen = h/lineHeight + 1;
|
||||||
|
int nDraw = mid(0, drawPerScreen, GetRows() - yPos);
|
||||||
|
|
||||||
auto active_line = context->selectionController->GetActiveLine();
|
auto active_line = context->selectionController->GetActiveLine();
|
||||||
auto const& selection = context->selectionController->GetSelectedSet();
|
auto const& selection = context->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
for (int i = 0; i < nDraw + 1; i++) {
|
for (int i : agi::util::range(nDraw)) {
|
||||||
int curRow = i + yPos - 1;
|
wxBrush color = row_colors.Default;
|
||||||
wxBrush curColor = row_colors.Default;
|
AssDialogue *curDiag = index_line_map[i + yPos];
|
||||||
AssDialogue *curDiag = nullptr;
|
|
||||||
|
|
||||||
// Header
|
bool inSel = !!selection.count(curDiag);
|
||||||
if (i == 0) {
|
if (inSel && curDiag->Comment)
|
||||||
curColor = row_colors.Header;
|
color = row_colors.SelectedComment;
|
||||||
|
else if (inSel)
|
||||||
|
color = row_colors.Selection;
|
||||||
|
else if (curDiag->Comment)
|
||||||
|
color = row_colors.Comment;
|
||||||
|
else if (OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame")->GetBool() && IsDisplayed(curDiag))
|
||||||
|
color = row_colors.Visible;
|
||||||
|
|
||||||
|
if (active_line != curDiag && curDiag->CollidesWith(active_line))
|
||||||
|
dc.SetTextForeground(text_collision);
|
||||||
|
else if (inSel)
|
||||||
|
dc.SetTextForeground(text_selection);
|
||||||
|
else
|
||||||
dc.SetTextForeground(text_standard);
|
dc.SetTextForeground(text_standard);
|
||||||
}
|
|
||||||
// Lines
|
|
||||||
else if ((curDiag = GetDialogue(curRow))) {
|
|
||||||
GetRowStrings(curRow, curDiag, paint_columns, &strings[0], !!override_mode, replace_char);
|
|
||||||
|
|
||||||
bool inSel = !!selection.count(curDiag);
|
|
||||||
if (inSel && curDiag->Comment)
|
|
||||||
curColor = row_colors.SelectedComment;
|
|
||||||
else if (inSel)
|
|
||||||
curColor = row_colors.Selection;
|
|
||||||
else if (curDiag->Comment)
|
|
||||||
curColor = row_colors.Comment;
|
|
||||||
else if (OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame")->GetBool() && IsDisplayed(curDiag))
|
|
||||||
curColor = row_colors.Visible;
|
|
||||||
|
|
||||||
if (active_line != curDiag && curDiag->CollidesWith(active_line))
|
|
||||||
dc.SetTextForeground(text_collision);
|
|
||||||
else if (inSel)
|
|
||||||
dc.SetTextForeground(text_selection);
|
|
||||||
else
|
|
||||||
dc.SetTextForeground(text_standard);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw row background color
|
// Draw row background color
|
||||||
if (curColor != row_colors.Default) {
|
if (color != row_colors.Default) {
|
||||||
dc.SetBrush(curColor);
|
dc.SetBrush(color);
|
||||||
dc.DrawRectangle(
|
dc.DrawRectangle(column_widths[0], (i + 1) * lineHeight + 1, w, lineHeight);
|
||||||
(i == 0) ? 0 : colWidth[0], i * lineHeight + 1,
|
|
||||||
w, lineHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw text
|
// Draw text
|
||||||
int dx = 0;
|
int x = 0;
|
||||||
int dy = i*lineHeight;
|
int y = (i + 1) * lineHeight;
|
||||||
for (int j : boost::irange(0, column_count)) {
|
for (size_t j : agi::util::range(columns.size())) {
|
||||||
if (colWidth[j] == 0) continue;
|
if (column_widths[j] == 0) continue;
|
||||||
|
|
||||||
if (paint_columns[j]) {
|
if (paint_columns[j])
|
||||||
wxSize ext = dc.GetTextExtent(strings[j]);
|
paint_text(columns[j]->Value(curDiag, byFrame ? context : nullptr), x, y, j);
|
||||||
|
x += column_widths[j];
|
||||||
int left = dx + 4;
|
|
||||||
int top = dy + (lineHeight - ext.GetHeight()) / 2;
|
|
||||||
|
|
||||||
// Centered columns
|
|
||||||
if (!(j == 5 || j == 6 || j == 7 || j == 11)) {
|
|
||||||
left += (colWidth[j] - 6 - ext.GetWidth()) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
dc.DrawText(strings[j], left, top);
|
|
||||||
}
|
|
||||||
dx += colWidth[j];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
dc.DestroyClippingRegion();
|
|
||||||
if (curDiag == active_line) {
|
if (curDiag == active_line) {
|
||||||
dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor())));
|
dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor())));
|
||||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
dc.DrawRectangle(0, dy, w, lineHeight + 1);
|
dc.DrawRectangle(0, y, w, lineHeight + 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dc.SetPen(grid_pen);
|
dc.SetPen(grid_pen);
|
||||||
dc.DrawLine(0, dy + lineHeight, w , dy + lineHeight);
|
dc.DrawLine(0, y + lineHeight, w , y + lineHeight);
|
||||||
}
|
}
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw grid columns
|
// Draw grid columns
|
||||||
int dx = 0;
|
{
|
||||||
dc.SetPen(grid_pen);
|
int maxH = (nDraw + 1) * lineHeight;
|
||||||
for (int i : boost::irange(0, column_count - 1)) {
|
int x = 0;
|
||||||
dx += colWidth[i];
|
dc.SetPen(grid_pen);
|
||||||
dc.DrawLine(dx, 0, dx, maxH);
|
for (int width : column_widths) {
|
||||||
}
|
x += width;
|
||||||
dc.DrawLine(0, 0, 0, maxH);
|
if (x < w)
|
||||||
dc.DrawLine(w-1, 0, w-1, maxH);
|
dc.DrawLine(x, 0, x, maxH);
|
||||||
}
|
|
||||||
|
|
||||||
void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wxString *strings, bool replace, wxString const& rep_char) const {
|
|
||||||
if (paint_columns[0]) strings[0] = std::to_wstring(row + 1);
|
|
||||||
if (paint_columns[1]) strings[1] = std::to_wstring(line->Layer);
|
|
||||||
if (byFrame) {
|
|
||||||
if (paint_columns[2]) strings[2] = std::to_wstring(context->videoController->FrameAtTime(line->Start, agi::vfr::START));
|
|
||||||
if (paint_columns[3]) strings[3] = std::to_wstring(context->videoController->FrameAtTime(line->End, agi::vfr::END));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (paint_columns[2]) strings[2] = to_wx(line->Start.GetAssFormated());
|
|
||||||
if (paint_columns[3]) strings[3] = to_wx(line->End.GetAssFormated());
|
|
||||||
}
|
|
||||||
if (paint_columns[5]) strings[5] = to_wx(line->Style);
|
|
||||||
if (paint_columns[6]) strings[6] = to_wx(line->Actor);
|
|
||||||
if (paint_columns[7]) strings[7] = to_wx(line->Effect);
|
|
||||||
if (paint_columns[8]) strings[8] = line->Margin[0] ? wxString(std::to_wstring(line->Margin[0])) : wxString();
|
|
||||||
if (paint_columns[9]) strings[9] = line->Margin[1] ? wxString(std::to_wstring(line->Margin[1])) : wxString();
|
|
||||||
if (paint_columns[10]) strings[10] = line->Margin[2] ? wxString(std::to_wstring(line->Margin[2])) : wxString();
|
|
||||||
|
|
||||||
if (paint_columns[4]) {
|
|
||||||
int characters = 0;
|
|
||||||
|
|
||||||
auto const& text = line->Text.get();
|
|
||||||
auto pos = begin(text);
|
|
||||||
do {
|
|
||||||
auto it = std::find(pos, end(text), '{');
|
|
||||||
characters += CharacterCount(pos, it, true);
|
|
||||||
if (it == end(text)) break;
|
|
||||||
|
|
||||||
pos = std::find(pos, end(text), '}');
|
|
||||||
if (pos == end(text)) {
|
|
||||||
characters += CharacterCount(it, pos, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (++pos != end(text));
|
|
||||||
|
|
||||||
int duration = line->End - line->Start;
|
|
||||||
if (duration <= 0 || characters * 1000 / duration >= 1000)
|
|
||||||
strings[4] = "";
|
|
||||||
else
|
|
||||||
strings[4] = std::to_wstring(characters * 1000 / duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paint_columns[11]) {
|
|
||||||
strings[11].clear();
|
|
||||||
|
|
||||||
// Show overrides
|
|
||||||
if (!replace)
|
|
||||||
strings[11] = to_wx(line->Text);
|
|
||||||
// Hidden overrides
|
|
||||||
else {
|
|
||||||
strings[11].reserve(line->Text.get().size());
|
|
||||||
size_t start = 0, pos;
|
|
||||||
while ((pos = line->Text.get().find('{', start)) != std::string::npos) {
|
|
||||||
strings[11] += to_wx(line->Text.get().substr(start, pos - start));
|
|
||||||
strings[11] += rep_char;
|
|
||||||
start = line->Text.get().find('}', pos);
|
|
||||||
if (start != std::string::npos) ++start;
|
|
||||||
}
|
|
||||||
if (start != std::string::npos)
|
|
||||||
strings[11] += to_wx(line->Text.get().substr(start));
|
|
||||||
}
|
}
|
||||||
|
dc.DrawLine(0, 0, 0, maxH);
|
||||||
// Cap length and set text
|
dc.DrawLine(w - 1, 0, w - 1, maxH);
|
||||||
if (strings[11].size() > 512)
|
|
||||||
strings[11] = strings[11].Left(512) + "...";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::OnSize(wxSizeEvent &) {
|
void BaseGrid::OnSize(wxSizeEvent &) {
|
||||||
AdjustScrollbar();
|
AdjustScrollbar();
|
||||||
|
|
||||||
int w, h;
|
|
||||||
GetClientSize(&w, &h);
|
|
||||||
colWidth[11] = text_col_w = w - text_col_x;
|
|
||||||
|
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,23 +531,11 @@ void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) {
|
||||||
menu::OpenPopupMenu(context_menu.get(), this);
|
menu::OpenPopupMenu(context_menu.get(), this);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const wxString strings[] = {
|
|
||||||
_("Line Number"),
|
|
||||||
_("Layer"),
|
|
||||||
_("Start"),
|
|
||||||
_("End"),
|
|
||||||
_("Characters per Second"),
|
|
||||||
_("Style"),
|
|
||||||
_("Actor"),
|
|
||||||
_("Effect"),
|
|
||||||
_("Left"),
|
|
||||||
_("Right"),
|
|
||||||
_("Vert"),
|
|
||||||
};
|
|
||||||
|
|
||||||
wxMenu menu;
|
wxMenu menu;
|
||||||
for (size_t i : boost::irange<size_t>(0, boost::size(strings)))
|
for (size_t i : agi::util::range(columns.size())) {
|
||||||
menu.Append(MENU_SHOW_COL + i, strings[i], "", wxITEM_CHECK)->Check(showCol[i]);
|
if (columns[i]->CanHide())
|
||||||
|
menu.Append(MENU_SHOW_COL + i, columns[i]->Description(), "", wxITEM_CHECK)->Check(!!column_shown[i]);
|
||||||
|
}
|
||||||
PopupMenu(&menu);
|
PopupMenu(&menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,94 +582,26 @@ void BaseGrid::SetColumnWidths() {
|
||||||
wxClientDC dc(this);
|
wxClientDC dc(this);
|
||||||
dc.SetFont(font);
|
dc.SetFont(font);
|
||||||
|
|
||||||
// O(1) widths
|
WidthHelper helper{dc, {}};
|
||||||
int marginLen = dc.GetTextExtent("0000").GetWidth();
|
|
||||||
int cpsLen = dc.GetTextExtent("999").GetWidth();
|
|
||||||
|
|
||||||
int labelLen = dc.GetTextExtent(std::to_wstring(GetRows())).GetWidth();
|
column_widths.clear();
|
||||||
int startLen = 0;
|
for (auto i : agi::util::range(columns.size())) {
|
||||||
int endLen = 0;
|
if (!column_shown[i])
|
||||||
if (!byFrame)
|
column_widths.push_back(0);
|
||||||
startLen = endLen = dc.GetTextExtent(to_wx(AssTime().GetAssFormated())).GetWidth();
|
else {
|
||||||
|
int width = columns[i]->Width(context, helper, byFrame);
|
||||||
std::unordered_map<boost::flyweight<std::string>, int> widths;
|
if (width) // 10 is an arbitrary amount of padding
|
||||||
auto get_width = [&](boost::flyweight<std::string> const& str) -> int {
|
width = 10 + std::max(width, column_header_widths[i]);
|
||||||
if (str.get().empty()) return 0;
|
column_widths.push_back(width);
|
||||||
auto it = widths.find(str);
|
|
||||||
if (it != end(widths)) return it->second;
|
|
||||||
int width = dc.GetTextExtent(to_wx(str)).GetWidth();
|
|
||||||
widths[str] = width;
|
|
||||||
return width;
|
|
||||||
};
|
|
||||||
|
|
||||||
// O(n) widths
|
|
||||||
bool showMargin[3] = { false, false, false };
|
|
||||||
int styleLen = 0;
|
|
||||||
int actorLen = 0;
|
|
||||||
int effectLen = 0;
|
|
||||||
int maxLayer = 0;
|
|
||||||
int maxStart = 0;
|
|
||||||
int maxEnd = 0;
|
|
||||||
for (auto const& diag : context->ass->Events) {
|
|
||||||
maxLayer = std::max(maxLayer, diag.Layer);
|
|
||||||
actorLen = std::max(actorLen, get_width(diag.Actor));
|
|
||||||
styleLen = std::max(styleLen, get_width(diag.Style));
|
|
||||||
effectLen = std::max(effectLen, get_width(diag.Effect));
|
|
||||||
|
|
||||||
// Margins
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
if (diag.Margin[j])
|
|
||||||
showMargin[j] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Times
|
|
||||||
if (byFrame) {
|
|
||||||
maxStart = std::max(maxStart, context->videoController->FrameAtTime(diag.Start, agi::vfr::START));
|
|
||||||
maxEnd = std::max(maxEnd, context->videoController->FrameAtTime(diag.End, agi::vfr::END));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish layer
|
text_refresh_rects.clear();
|
||||||
int layerLen = maxLayer ? dc.GetTextExtent(std::to_wstring(maxLayer)).GetWidth() : 0;
|
int x = 0;
|
||||||
|
for (auto i : agi::util::range(columns.size())) {
|
||||||
// Finish times
|
if (columns[i]->RefreshOnTextChange() && column_widths[i])
|
||||||
if (byFrame) {
|
text_refresh_rects.emplace_back(x, 0, column_widths[i], h);
|
||||||
startLen = dc.GetTextExtent(std::to_wstring(maxStart)).GetWidth();
|
|
||||||
endLen = dc.GetTextExtent(std::to_wstring(maxEnd)).GetWidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set column widths
|
|
||||||
colWidth[0] = labelLen;
|
|
||||||
colWidth[1] = layerLen;
|
|
||||||
colWidth[2] = startLen;
|
|
||||||
colWidth[3] = endLen;
|
|
||||||
colWidth[4] = cpsLen;
|
|
||||||
colWidth[5] = styleLen;
|
|
||||||
colWidth[6] = actorLen;
|
|
||||||
colWidth[7] = effectLen;
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
colWidth[i + 8] = showMargin[i] ? marginLen : 0;
|
|
||||||
colWidth[11] = 1;
|
|
||||||
|
|
||||||
// Hide columns and ensure every visible column is at least as big as its
|
|
||||||
// header plus padding
|
|
||||||
for (size_t i : boost::irange<size_t>(0u, showCol.size())) {
|
|
||||||
if (!showCol[i])
|
|
||||||
colWidth[i] = 0;
|
|
||||||
else if (colWidth[i])
|
|
||||||
colWidth[i] = 10 + std::max(colWidth[i], headerWidth[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set size of last
|
|
||||||
int total = std::accumulate(colWidth.begin(), colWidth.end(), 0);
|
|
||||||
colWidth[11] = std::max(w - total, 0);
|
|
||||||
|
|
||||||
time_cols_x = colWidth[0] + colWidth[1];
|
|
||||||
time_cols_w = colWidth[2] + colWidth[3] + colWidth[4];
|
|
||||||
cps_col_x = time_cols_x + colWidth[2] + colWidth[3];
|
|
||||||
cps_col_w = colWidth[4];
|
|
||||||
text_col_x = total;
|
|
||||||
text_col_w = colWidth[11];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AssDialogue *BaseGrid::GetDialogue(int n) const {
|
AssDialogue *BaseGrid::GetDialogue(int n) const {
|
||||||
|
|
|
@ -39,10 +39,9 @@ namespace agi {
|
||||||
class OptionValue;
|
class OptionValue;
|
||||||
}
|
}
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
|
struct GridColumn;
|
||||||
|
|
||||||
class BaseGrid final : public wxWindow {
|
class BaseGrid final : public wxWindow {
|
||||||
static const int column_count = 12;
|
|
||||||
|
|
||||||
std::vector<agi::signal::Connection> connections;
|
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?
|
||||||
|
@ -50,6 +49,13 @@ class BaseGrid final : public wxWindow {
|
||||||
wxScrollBar *scrollBar; ///< The grid's scrollbar
|
wxScrollBar *scrollBar; ///< The grid's scrollbar
|
||||||
bool byFrame = false; ///< Should times be displayed as frame numbers
|
bool byFrame = false; ///< Should times be displayed as frame numbers
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<GridColumn>> columns;
|
||||||
|
std::vector<int> column_widths;
|
||||||
|
std::vector<int> column_header_widths;
|
||||||
|
std::vector<char> column_shown;
|
||||||
|
|
||||||
|
std::vector<wxRect> text_refresh_rects;
|
||||||
|
|
||||||
/// Cached brushes used for row backgrounds
|
/// Cached brushes used for row backgrounds
|
||||||
struct {
|
struct {
|
||||||
wxBrush Default;
|
wxBrush Default;
|
||||||
|
@ -88,24 +94,8 @@ class BaseGrid final : public wxWindow {
|
||||||
void OnSubtitlesSave();
|
void OnSubtitlesSave();
|
||||||
void OnActiveLineChanged(AssDialogue *);
|
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;
|
|
||||||
|
|
||||||
void ScrollTo(int y);
|
void ScrollTo(int y);
|
||||||
|
|
||||||
std::array<int, column_count> colWidth; ///< Width in pixels of each column
|
|
||||||
std::array<int, column_count> headerWidth; ///< Width in pixels of each column's header
|
|
||||||
std::array<wxString, column_count> headerNames;
|
|
||||||
|
|
||||||
int time_cols_x; ///< Left edge of the times columns
|
|
||||||
int time_cols_w; ///< Width of the two times columns
|
|
||||||
int text_col_x; ///< Left edge of the text column
|
|
||||||
int text_col_w; ///< Width of the text column
|
|
||||||
int cps_col_x; ///< Left edge of the cps column
|
|
||||||
int cps_col_w; ///< Width of the cps column
|
|
||||||
|
|
||||||
std::array<bool, column_count - 1> showCol; ///< Column visibility mask (Text can't be hidden)
|
|
||||||
|
|
||||||
int yPos = 0;
|
int yPos = 0;
|
||||||
|
|
||||||
void AdjustScrollbar();
|
void AdjustScrollbar();
|
||||||
|
|
332
src/grid_column.cpp
Normal file
332
src/grid_column.cpp
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
// 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 "grid_column.h"
|
||||||
|
|
||||||
|
#include "ass_dialogue.h"
|
||||||
|
#include "ass_file.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "include/aegisub/context.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "video_context.h"
|
||||||
|
|
||||||
|
#include <wx/dc.h>
|
||||||
|
|
||||||
|
int WidthHelper::operator()(boost::flyweight<std::string> const& str) {
|
||||||
|
if (str.get().empty()) return 0;
|
||||||
|
auto it = widths.find(str);
|
||||||
|
if (it != end(widths)) return it->second;
|
||||||
|
int width = dc.GetTextExtent(to_wx(str)).GetWidth();
|
||||||
|
widths[str] = width;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WidthHelper::operator()(std::string const& str) {
|
||||||
|
return dc.GetTextExtent(to_wx(str)).GetWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
int WidthHelper::operator()(wxString const& str) {
|
||||||
|
return dc.GetTextExtent(str).GetWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
#define COLUMN_HEADER(value) \
|
||||||
|
private: const wxString header = value; \
|
||||||
|
public: wxString const& Header() const override { return header; }
|
||||||
|
#define COLUMN_DESCRIPTION(value) \
|
||||||
|
private: const wxString description = value; \
|
||||||
|
public: wxString const& Description() const override { return description; }
|
||||||
|
|
||||||
|
struct GridColumnLineNumber final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("#"))
|
||||||
|
COLUMN_DESCRIPTION(_("Line Number"))
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context * = nullptr) const override {
|
||||||
|
return std::to_wstring(d->Row + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return helper(Value(&c->ass->Events.back()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T max_value(T AssDialogueBase::*field, EntryList<AssDialogue> const& lines) {
|
||||||
|
T value = 0;
|
||||||
|
for (AssDialogue const& line : lines) {
|
||||||
|
if (line.*field > value)
|
||||||
|
value = line.*field;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridColumnLayer final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("L"))
|
||||||
|
COLUMN_DESCRIPTION(_("Layer"))
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
return d->Layer ? wxString(std::to_wstring(d->Layer)) : wxString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
int max_layer = max_value(&AssDialogue::Layer, c->ass->Events);
|
||||||
|
return max_layer == 0 ? 0 : helper(std::to_wstring(max_layer));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnStartTime final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("Start"))
|
||||||
|
COLUMN_DESCRIPTION(_("Start Time"))
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *c) const override {
|
||||||
|
if (c)
|
||||||
|
return std::to_wstring(c->videoController->FrameAtTime(d->Start, agi::vfr::START));
|
||||||
|
return to_wx(d->Start.GetAssFormated());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool by_frame) const override {
|
||||||
|
if (!by_frame)
|
||||||
|
return helper(wxS("0:00:00.00"));
|
||||||
|
int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::Start, c->ass->Events), agi::vfr::START);
|
||||||
|
return helper(std::to_wstring(frame));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnEndTime final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("End"))
|
||||||
|
COLUMN_DESCRIPTION(_("End Time"))
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *c) const override {
|
||||||
|
if (c)
|
||||||
|
return std::to_wstring(c->videoController->FrameAtTime(d->End, agi::vfr::END));
|
||||||
|
return to_wx(d->End.GetAssFormated());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool by_frame) const override {
|
||||||
|
if (!by_frame)
|
||||||
|
return helper(wxS("0:00:00.00"));
|
||||||
|
int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::End, c->ass->Events), agi::vfr::END);
|
||||||
|
return helper(std::to_wstring(frame));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
int max_width(T AssDialogueBase::*field, EntryList<AssDialogue> const& lines, WidthHelper &helper) {
|
||||||
|
int w = 0;
|
||||||
|
for (AssDialogue const& line : lines) {
|
||||||
|
auto const& v = line.*field;
|
||||||
|
if (v.get().empty()) continue;
|
||||||
|
int width = helper(v);
|
||||||
|
if (width > w)
|
||||||
|
w = width;
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridColumnStyle final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("Style"))
|
||||||
|
COLUMN_DESCRIPTION(_("Style"))
|
||||||
|
bool Centered() const override { return false; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *c) const override {
|
||||||
|
return to_wx(d->Style);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return max_width(&AssDialogue::Style, c->ass->Events, helper);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnEffect final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("Effect"))
|
||||||
|
COLUMN_DESCRIPTION(_("Effect"))
|
||||||
|
bool Centered() const override { return false; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
return to_wx(d->Effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return max_width(&AssDialogue::Effect, c->ass->Events, helper);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnActor final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("Actor"))
|
||||||
|
COLUMN_DESCRIPTION(_("Actor"))
|
||||||
|
bool Centered() const override { return false; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
return to_wx(d->Actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return max_width(&AssDialogue::Actor, c->ass->Events, helper);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int Index>
|
||||||
|
struct GridColumnMargin : GridColumn {
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
return d->Margin[Index] ? wxString(std::to_wstring(d->Margin[Index])) : wxString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
int max = 0;
|
||||||
|
for (AssDialogue const& line : c->ass->Events) {
|
||||||
|
if (line.Margin[Index] > max)
|
||||||
|
max = line.Margin[Index];
|
||||||
|
}
|
||||||
|
return max == 0 ? 0 : helper(std::to_wstring(max));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnMarginLeft final : GridColumnMargin<0> {
|
||||||
|
COLUMN_HEADER(_("Left"))
|
||||||
|
COLUMN_DESCRIPTION(_("Left Margin"))
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnMarginRight final : GridColumnMargin<0> {
|
||||||
|
COLUMN_HEADER(_("Right"))
|
||||||
|
COLUMN_DESCRIPTION(_("Right Margin"))
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnMarginVert final : GridColumnMargin<0> {
|
||||||
|
COLUMN_HEADER(_("Vert"))
|
||||||
|
COLUMN_DESCRIPTION(_("Vertical Margin"))
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumnCPS final : GridColumn {
|
||||||
|
COLUMN_HEADER(_("CPS"))
|
||||||
|
COLUMN_DESCRIPTION(_("Characters Per Second"))
|
||||||
|
bool Centered() const override { return true; }
|
||||||
|
bool RefreshOnTextChange() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
int characters = 0;
|
||||||
|
|
||||||
|
auto const& text = d->Text.get();
|
||||||
|
auto pos = begin(text);
|
||||||
|
do {
|
||||||
|
auto it = std::find(pos, end(text), '{');
|
||||||
|
characters += CharacterCount(pos, it, true);
|
||||||
|
if (it == end(text)) break;
|
||||||
|
|
||||||
|
pos = std::find(pos, end(text), '}');
|
||||||
|
if (pos == end(text)) {
|
||||||
|
characters += CharacterCount(it, pos, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (++pos != end(text));
|
||||||
|
|
||||||
|
int duration = d->End - d->Start;
|
||||||
|
if (duration <= 0 || characters * 1000 / duration >= 1000)
|
||||||
|
return wxS("");
|
||||||
|
else
|
||||||
|
return std::to_wstring(characters * 1000 / duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return helper(wxS("999"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GridColumnText final : public GridColumn {
|
||||||
|
int override_mode;
|
||||||
|
wxString replace_char;
|
||||||
|
|
||||||
|
agi::signal::Connection override_mode_connection;
|
||||||
|
agi::signal::Connection replace_char_connection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GridColumnText()
|
||||||
|
: override_mode(OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt())
|
||||||
|
, replace_char(to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString()))
|
||||||
|
, override_mode_connection(OPT_SUB("Subtitle/Grid/Hide Overrides",
|
||||||
|
[&](agi::OptionValue const& v) { override_mode = v.GetInt(); }))
|
||||||
|
, replace_char_connection(OPT_SUB("Subtitle/Grid/Hide Overrides Char",
|
||||||
|
[&](agi::OptionValue const& v) { replace_char = to_wx(v.GetString()); }))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
COLUMN_HEADER(_("Text"))
|
||||||
|
COLUMN_DESCRIPTION(_("Text"))
|
||||||
|
bool Centered() const override { return false; }
|
||||||
|
bool CanHide() const override { return false; }
|
||||||
|
bool RefreshOnTextChange() const override { return true; }
|
||||||
|
|
||||||
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
|
wxString str;
|
||||||
|
|
||||||
|
// Show overrides
|
||||||
|
if (override_mode == 0)
|
||||||
|
str = to_wx(d->Text);
|
||||||
|
// Hidden overrides
|
||||||
|
else {
|
||||||
|
str.reserve(d->Text.get().size());
|
||||||
|
size_t start = 0, pos;
|
||||||
|
while ((pos = d->Text.get().find('{', start)) != std::string::npos) {
|
||||||
|
str += to_wx(d->Text.get().substr(start, pos - start));
|
||||||
|
if (override_mode == 1)
|
||||||
|
str += replace_char;
|
||||||
|
start = d->Text.get().find('}', pos);
|
||||||
|
if (start != std::string::npos) ++start;
|
||||||
|
}
|
||||||
|
if (start != std::string::npos)
|
||||||
|
str += to_wx(d->Text.get().substr(start));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap length and set text
|
||||||
|
if (str.size() > 512)
|
||||||
|
str = str.Left(512) + "...";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
return 5000;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::unique_ptr<GridColumn> make() {
|
||||||
|
return std::unique_ptr<GridColumn>(new T);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<GridColumn>> GetGridColumns() {
|
||||||
|
std::vector<std::unique_ptr<GridColumn>> ret;
|
||||||
|
ret.push_back(make<GridColumnLineNumber>());
|
||||||
|
ret.push_back(make<GridColumnLayer>());
|
||||||
|
ret.push_back(make<GridColumnStartTime>());
|
||||||
|
ret.push_back(make<GridColumnEndTime>());
|
||||||
|
ret.push_back(make<GridColumnCPS>());
|
||||||
|
ret.push_back(make<GridColumnStyle>());
|
||||||
|
ret.push_back(make<GridColumnEffect>());
|
||||||
|
ret.push_back(make<GridColumnActor>());
|
||||||
|
ret.push_back(make<GridColumnMarginLeft>());
|
||||||
|
ret.push_back(make<GridColumnMarginRight>());
|
||||||
|
ret.push_back(make<GridColumnMarginVert>());
|
||||||
|
ret.push_back(make<GridColumnText>());
|
||||||
|
return ret;
|
||||||
|
}
|
60
src/grid_column.h
Normal file
60
src/grid_column.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// 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 <boost/flyweight.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class AssDialogue;
|
||||||
|
class wxClientDC;
|
||||||
|
class wxString;
|
||||||
|
namespace agi { struct Context; }
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <typename T>
|
||||||
|
struct hash<boost::flyweight<T>> {
|
||||||
|
size_t operator()(boost::flyweight<T> const& ss) const {
|
||||||
|
return hash<const void*>()(&ss.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WidthHelper {
|
||||||
|
wxDC &dc;
|
||||||
|
std::unordered_map<boost::flyweight<std::string>, int> widths;
|
||||||
|
|
||||||
|
int operator()(boost::flyweight<std::string> const& str);
|
||||||
|
int operator()(std::string const& str);
|
||||||
|
int operator()(wxString const& str);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GridColumn {
|
||||||
|
virtual ~GridColumn() = default;
|
||||||
|
|
||||||
|
virtual bool Centered() const = 0;
|
||||||
|
virtual bool CanHide() const { return true; }
|
||||||
|
virtual bool RefreshOnTextChange() const { return false; }
|
||||||
|
|
||||||
|
virtual wxString const& Header() const = 0;
|
||||||
|
virtual wxString const& Description() const = 0;
|
||||||
|
|
||||||
|
virtual wxString Value(const AssDialogue *d, const agi::Context * = nullptr) const = 0;
|
||||||
|
virtual int Width(const agi::Context *c, WidthHelper &helper, bool by_frame) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<GridColumn>> GetGridColumns();
|
Loading…
Reference in a new issue