From 867eaae7a8c03669641c094a85d32c9fad56db8a Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 17 Apr 2014 18:22:07 -0700 Subject: [PATCH] Add basic characters-per-second column to the grid --- src/base_grid.cpp | 104 +++++++++++++++++++++++++++++----------------- src/base_grid.h | 4 +- src/utils.cpp | 36 +++++++++------- src/utils.h | 1 + 4 files changed, 89 insertions(+), 56 deletions(-) diff --git a/src/base_grid.cpp b/src/base_grid.cpp index b85dd0721..c1091c760 100644 --- a/src/base_grid.cpp +++ b/src/base_grid.cpp @@ -93,10 +93,10 @@ BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context) : wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER) , scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL)) , seek_listener(context->videoController->AddSeekListener(std::bind(&BaseGrid::Refresh, this, false, nullptr))) -, headerNames({ - _("#"), _("L"), _("Start"), _("End"), _("Style"), _("Actor"), +, headerNames({{ + _("#"), _("L"), _("Start"), _("End"), _("CPS"), _("Style"), _("Actor"), _("Effect"), _("Left"), _("Right"), _("Vert"), _("Text") -}) +}}) , context(context) { scrollBar->SetScrollbar(0,10,100,10); @@ -163,8 +163,10 @@ void BaseGrid::OnSubtitlesCommit(int type) { } if (type & AssFile::COMMIT_DIAG_TIME) 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); RefreshRect(wxRect(text_col_x, 0, text_col_w, GetClientSize().GetHeight()), false); + } } void BaseGrid::OnSubtitlesOpen() { @@ -219,6 +221,8 @@ void BaseGrid::UpdateStyle() { std::vector column_array(OPT_GET("Subtitle/Grid/Column")->GetListBool()); assert(column_array.size() <= showCol.size()); boost::copy(column_array, std::begin(showCol)); + for (size_t i : boost::irange(column_array.size(), showCol.size())) + showCol[i] = true; for (int i : boost::irange(0, column_count)) headerWidth[i] = dc.GetTextExtent(headerNames[i]).GetWidth(); @@ -285,7 +289,7 @@ void BaseGrid::OnPaint(wxPaintEvent &) { cs.SetWidth(cs.GetWidth() - scrollBar->GetSize().GetWidth()); // Find which columns need to be repainted - bool paint_columns[11] = {0}; + bool paint_columns[column_count] = {0}; for (wxRegionIterator region(GetUpdateRegion()); region; ++region) { wxRect updrect = region.GetRect(); int x = 0; @@ -398,7 +402,7 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) { int top = dy + (lineHeight - ext.GetHeight()) / 2; // Centered columns - if (!(j == 4 || j == 5 || j == 6 || j == 10)) { + if (!(j == 5 || j == 6 || j == 7 || j == 11)) { left += (colWidth[j] - 6 - ext.GetWidth()) / 2; } @@ -424,7 +428,7 @@ void BaseGrid::DrawImage(wxDC &dc, bool paint_columns[]) { // Draw grid columns int dx = 0; dc.SetPen(grid_pen); - for (int i = 0; i < 10; ++i) { + for (int i : boost::irange(0, column_count - 1)) { dx += colWidth[i]; dc.DrawLine(dx, 0, dx, maxH); } @@ -443,36 +447,57 @@ void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wx 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[4]) strings[4] = to_wx(line->Style); - if (paint_columns[5]) strings[5] = to_wx(line->Actor); - if (paint_columns[6]) strings[6] = to_wx(line->Effect); - if (paint_columns[7]) strings[7] = line->Margin[0] ? wxString(std::to_wstring(line->Margin[0])) : wxString(); - if (paint_columns[8]) strings[8] = line->Margin[1] ? wxString(std::to_wstring(line->Margin[1])) : wxString(); - if (paint_columns[9]) strings[9] = line->Margin[2] ? wxString(std::to_wstring(line->Margin[2])) : wxString(); + 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[10]) { - strings[10].clear(); + 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; + strings[4] = std::to_wstring(duration > 0 ? characters * 1000 / duration : 0); + } + + if (paint_columns[11]) { + strings[11].clear(); // Show overrides if (!replace) - strings[10] = to_wx(line->Text); + strings[11] = to_wx(line->Text); // Hidden overrides else { - strings[10].reserve(line->Text.get().size()); + strings[11].reserve(line->Text.get().size()); size_t start = 0, pos; while ((pos = line->Text.get().find('{', start)) != std::string::npos) { - strings[10] += to_wx(line->Text.get().substr(start, pos - start)); - strings[10] += rep_char; + 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[10] += to_wx(line->Text.get().substr(start)); + strings[11] += to_wx(line->Text.get().substr(start)); } // Cap length and set text - if (strings[10].size() > 512) - strings[10] = strings[10].Left(512) + "..."; + if (strings[11].size() > 512) + strings[11] = strings[11].Left(512) + "..."; } } @@ -481,7 +506,7 @@ void BaseGrid::OnSize(wxSizeEvent &) { int w, h; GetClientSize(&w, &h); - colWidth[10] = text_col_w = w - text_col_x; + colWidth[11] = text_col_w = w - text_col_x; Refresh(false); } @@ -618,6 +643,7 @@ void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) { _("Layer"), _("Start"), _("End"), + _("Characters per Second"), _("Style"), _("Actor"), _("Effect"), @@ -627,7 +653,7 @@ void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) { }; wxMenu menu; - for (int i : boost::irange(0, column_count)) + for (size_t i : boost::irange(0, boost::size(strings))) menu.Append(MENU_SHOW_COL + i, strings[i], "", wxITEM_CHECK)->Check(showCol[i]); PopupMenu(&menu); } @@ -735,33 +761,33 @@ void BaseGrid::SetColumnWidths() { colWidth[1] = layerLen; colWidth[2] = startLen; colWidth[3] = endLen; - colWidth[4] = styleLen; - colWidth[5] = actorLen; - colWidth[6] = effectLen; + colWidth[4] = 1; + colWidth[5] = styleLen; + colWidth[6] = actorLen; + colWidth[7] = effectLen; for (int i = 0; i < 3; i++) - colWidth[i + 7] = showMargin[i] ? marginLen : 0; - colWidth[10] = 1; + colWidth[i + 8] = showMargin[i] ? marginLen : 0; + colWidth[11] = 1; - // Hide columns - for (size_t i : boost::irange(0u, showCol.size())) { + // Hide columns and ensure every visible column is at least as big as its + // header plus padding + for (size_t i : boost::irange(0u, showCol.size())) { if (!showCol[i]) colWidth[i] = 0; - } - - // Ensure every visible column is at least as big as its header plus padding - for (size_t i : boost::irange(0u, showCol.size())) { - if (colWidth[i]) + 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[10] = std::max(w - total, 0); + colWidth[11] = std::max(w - total, 0); time_cols_x = colWidth[0] + colWidth[1]; - time_cols_w = colWidth[2] + colWidth[3]; + 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[10]; + text_col_w = colWidth[11]; } AssDialogue *BaseGrid::GetDialogue(int n) const { diff --git a/src/base_grid.h b/src/base_grid.h index 6ebf14b28..15c1832e5 100644 --- a/src/base_grid.h +++ b/src/base_grid.h @@ -46,7 +46,7 @@ namespace agi { class AssDialogue; class BaseGrid final : public wxWindow { - static const int column_count = 11; + static const int column_count = 12; std::vector connections; int lineHeight = 1; ///< Height of a line in pixels in the current font @@ -96,6 +96,8 @@ class BaseGrid final : public wxWindow { 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 showCol; ///< Column visibility mask (Text can't be hidden) diff --git a/src/utils.cpp b/src/utils.cpp index 2abd32926..7767b04bc 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -219,6 +219,24 @@ void CleanCache(agi::fs::path const& directory, std::string const& file_type, ui }); } +size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, bool ignore_whitespace) { + using namespace boost::locale::boundary; + const ssegment_index characters(character, begin, end); + if (!ignore_whitespace) + return boost::distance(characters); + + // characters.rule(word_any) doesn't seem to work for character indexes (everything is word_none) + size_t count = 0; + for (auto const& chr : characters) { + UChar32 c; + int i = 0; + U8_NEXT_UNSAFE(chr.begin(), i, c); + if (!u_isUWhiteSpace(c)) + ++count; + } + return count; +} + size_t MaxLineLength(std::string const& text, bool ignore_whitespace) { auto tokens = agi::ass::TokenizeDialogueBody(text); agi::ass::MarkDrawings(text, tokens); @@ -237,22 +255,8 @@ size_t MaxLineLength(std::string const& text, bool ignore_whitespace) { current_line_length = 0; } } - else if (token.type == agi::ass::DialogueTokenType::TEXT) { - using namespace boost::locale::boundary; - const ssegment_index characters(character, begin(text) + pos, begin(text) + pos + token.length); - if (!ignore_whitespace) - current_line_length += boost::distance(characters); - else { - // characters.rule(word_any) doesn't seem to work for character indexes (everything is word_none) - for (auto const& chr : characters) { - UChar32 c; - int i = 0; - U8_NEXT_UNSAFE(chr.begin(), i, c); - if (!u_isUWhiteSpace(c)) - ++current_line_length; - } - } - } + else if (token.type == agi::ass::DialogueTokenType::TEXT) + current_line_length += CharacterCount(begin(text) + pos, begin(text) + pos + token.length, ignore_whitespace); pos += token.length; } diff --git a/src/utils.h b/src/utils.h index 9fe35e8a9..a29a60d60 100644 --- a/src/utils.h +++ b/src/utils.h @@ -62,6 +62,7 @@ int SmallestPowerOf2(int x); /// Get the length in characters of the longest line in the given text size_t MaxLineLength(std::string const& text, bool ignore_whitespace); +size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, bool ignore_whitespace); /// @brief Launch a new copy of Aegisub. ///