From af3273379712359b396066b82f4d4c6fe23f0f3c Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 18 Apr 2014 17:18:41 -0700 Subject: [PATCH] Move character count stuff to libaegisub --- build/libaegisub/libaegisub.vcxproj | 2 + build/libaegisub/libaegisub.vcxproj.filters | 6 ++ libaegisub/Makefile | 1 + libaegisub/common/character_count.cpp | 92 +++++++++++++++++++ .../include/libaegisub/character_count.h | 23 +++++ src/grid_column.cpp | 20 +--- src/placeholder_ctrl.h | 7 +- src/subs_edit_box.cpp | 6 +- src/utils.cpp | 50 ---------- src/utils.h | 6 -- 10 files changed, 133 insertions(+), 80 deletions(-) create mode 100644 libaegisub/common/character_count.cpp create mode 100644 libaegisub/include/libaegisub/character_count.h diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj index dd935e6d9..030a8eda8 100644 --- a/build/libaegisub/libaegisub.vcxproj +++ b/build/libaegisub/libaegisub.vcxproj @@ -46,6 +46,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters index 978e44fe8..9af2f8403 100644 --- a/build/libaegisub/libaegisub.vcxproj.filters +++ b/build/libaegisub/libaegisub.vcxproj.filters @@ -167,6 +167,9 @@ Header Files + + Header Files + @@ -274,6 +277,9 @@ Source Files\Common + + Source Files\Common + diff --git a/libaegisub/Makefile b/libaegisub/Makefile index f56f38dc5..4ad09605c 100644 --- a/libaegisub/Makefile +++ b/libaegisub/Makefile @@ -19,6 +19,7 @@ SRC += \ common/cajun/reader.cpp \ common/cajun/writer.cpp \ common/calltip_provider.cpp \ + common/character_count.cpp \ common/charset.cpp \ common/charset_6937.cpp \ common/charset_conv.cpp \ diff --git a/libaegisub/common/character_count.cpp b/libaegisub/common/character_count.cpp new file mode 100644 index 000000000..20ea87510 --- /dev/null +++ b/libaegisub/common/character_count.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include "libaegisub/character_count.h" + +#include "libaegisub/ass/dialogue_parser.h" + +#include +#include +#include +#include + +namespace { +template +size_t count_in_range(Iterator begin, 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; +} +} + +namespace agi { +size_t CharacterCount(std::string const& str) { + size_t characters = 0; + auto pos = begin(str); + do { + auto it = std::find(pos, end(str), '{'); + characters += count_in_range(pos, it, true); + if (it == end(str)) break; + + pos = std::find(pos, end(str), '}'); + if (pos == end(str)) { + characters += count_in_range(it, pos, true); + break; + } + } while (++pos != end(str)); + + return characters; +} + +size_t MaxLineLength(std::string const& text, bool ignore_whitespace) { + auto tokens = agi::ass::TokenizeDialogueBody(text); + agi::ass::MarkDrawings(text, tokens); + + size_t pos = 0; + size_t max_line_length = 0; + size_t current_line_length = 0; + for (auto token : tokens) { + if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) { + if (text[pos + 1] == 'h') { + if (!ignore_whitespace) + current_line_length += 1; + } + else { // N or n + max_line_length = std::max(max_line_length, current_line_length); + current_line_length = 0; + } + } + else if (token.type == agi::ass::DialogueTokenType::TEXT) + current_line_length += count_in_range(begin(text) + pos, begin(text) + pos + token.length, ignore_whitespace); + + pos += token.length; + } + + return std::max(max_line_length, current_line_length); +} +} diff --git a/libaegisub/include/libaegisub/character_count.h b/libaegisub/include/libaegisub/character_count.h new file mode 100644 index 000000000..512197a93 --- /dev/null +++ b/libaegisub/include/libaegisub/character_count.h @@ -0,0 +1,23 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include + +namespace agi { + /// 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& str); +} \ No newline at end of file diff --git a/src/grid_column.cpp b/src/grid_column.cpp index 468c2d6cd..c4de1d22e 100644 --- a/src/grid_column.cpp +++ b/src/grid_column.cpp @@ -21,9 +21,10 @@ #include "compat.h" #include "include/aegisub/context.h" #include "options.h" -#include "utils.h" #include "video_context.h" +#include + #include int WidthHelper::operator()(boost::flyweight const& str) { @@ -223,28 +224,13 @@ struct GridColumnCPS final : GridColumn { bool RefreshOnTextChange() const override { return true; } wxString Value(const AssDialogue *d, const agi::Context *) const override { - int characters = 0; - int duration = d->End - d->Start; auto const& text = d->Text.get(); if (duration <= 0 || text.size() > static_cast(duration)) return wxS(""); - 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)); - - return std::to_wstring(characters * 1000 / duration); + return std::to_wstring(agi::CharacterCount(text) * 1000 / duration); } int Width(const agi::Context *c, WidthHelper &helper, bool) const override { diff --git a/src/placeholder_ctrl.h b/src/placeholder_ctrl.h index 34caa115c..9e4bd3721 100644 --- a/src/placeholder_ctrl.h +++ b/src/placeholder_ctrl.h @@ -14,12 +14,11 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file placeholder_ctrl.h -/// @ingroup custom_control -/// - #include +// Defined in osx_utils.mm +void SetPlaceholderText(wxWindow *window, wxString const& placeholder); + /// @class Placeholder /// @brief A wrapper around a control to add placeholder text /// diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp index 69e82f566..3c264bb6a 100644 --- a/src/subs_edit_box.cpp +++ b/src/subs_edit_box.cpp @@ -50,10 +50,10 @@ #include "text_selection_controller.h" #include "timeedit_ctrl.h" #include "tooltip_manager.h" -#include "utils.h" #include "validators.h" #include "video_context.h" +#include #include #include @@ -228,7 +228,7 @@ wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxS middle_left_sizer->Add(ctrl, wxSizerFlags().Center()); Bind(wxEVT_TEXT, [=](wxCommandEvent&) { - int value = mid(0, atoi(ctrl->GetValue().utf8_str()), 9999); + int value = agi::util::mid(0, atoi(ctrl->GetValue().utf8_str()), 9999); SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; }, commit_msg, AssFile::COMMIT_DIAG_META); }, ctrl->GetId()); @@ -587,7 +587,7 @@ void SubsEditBox::CallCommand(const char *cmd_name) { void SubsEditBox::UpdateCharacterCount(std::string const& text) { auto ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool(); agi::dispatch::Background().Async([=]{ - size_t length = MaxLineLength(text, ignore_whitespace); + size_t length = agi::MaxLineLength(text, ignore_whitespace); agi::dispatch::Main().Async([=]{ char_count->SetValue(wxString::Format("%lu", (unsigned long)length)); size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt(); diff --git a/src/utils.cpp b/src/utils.cpp index 7767b04bc..9ebd126f0 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -39,7 +39,6 @@ #include "options.h" #include "retina_helper.h" -#include #include #include #include @@ -49,11 +48,7 @@ #endif #include #include -#include -#include #include -#include -#include #include #include @@ -219,51 +214,6 @@ 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); - - size_t pos = 0; - size_t max_line_length = 0; - size_t current_line_length = 0; - for (auto token : tokens) { - if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) { - if (text[pos + 1] == 'h') { - if (!ignore_whitespace) - current_line_length += 1; - } - else { // N or n - max_line_length = std::max(max_line_length, current_line_length); - current_line_length = 0; - } - } - 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; - } - - return std::max(max_line_length, current_line_length); -} - #ifndef __WXOSX_COCOA__ // OS X implementation in osx_utils.mm void AddFullScreenButton(wxWindow *) { } diff --git a/src/utils.h b/src/utils.h index a29a60d60..14b43f4ff 100644 --- a/src/utils.h +++ b/src/utils.h @@ -60,10 +60,6 @@ std::string float_to_string(double val); /// Algorithm from http://bob.allegronetwork.com/prog/tricks.html 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. /// /// Contrary to what the name suggests, this does not close the currently @@ -75,8 +71,6 @@ void AddFullScreenButton(wxWindow *window); void SetFloatOnParent(wxWindow *window); -void SetPlaceholderText(wxWindow *window, wxString const& placeholder); - /// Forward a mouse wheel event to the window under the mouse if needed /// @param source The initial target of the wheel event /// @param evt The event