mirror of https://github.com/odrling/Aegisub
Move character count stuff to libaegisub
This commit is contained in:
parent
bd53302907
commit
af32733797
|
@ -46,6 +46,7 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\visitor.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\visitor.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\writer.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\writer.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
|
||||||
|
<ClInclude Include="$(SrcDir)include\libaegisub\character_count.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv_win.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv_win.h" />
|
||||||
|
@ -91,6 +92,7 @@
|
||||||
<ClCompile Include="$(SrcDir)common\cajun\reader.cpp" />
|
<ClCompile Include="$(SrcDir)common\cajun\reader.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\cajun\writer.cpp" />
|
<ClCompile Include="$(SrcDir)common\cajun\writer.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
|
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
|
||||||
|
<ClCompile Include="$(SrcDir)common\character_count.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\charset.cpp" />
|
<ClCompile Include="$(SrcDir)common\charset.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
|
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />
|
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />
|
||||||
|
|
|
@ -167,6 +167,9 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\file_mapping.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\file_mapping.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="$(SrcDir)include\libaegisub\character_count.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
|
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
|
||||||
|
@ -274,6 +277,9 @@
|
||||||
<ClCompile Include="$(SrcDir)common\file_mapping.cpp">
|
<ClCompile Include="$(SrcDir)common\file_mapping.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)common\character_count.cpp">
|
||||||
|
<Filter>Source Files\Common</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="$(SrcDir)include\libaegisub\charsets.def">
|
<None Include="$(SrcDir)include\libaegisub\charsets.def">
|
||||||
|
|
|
@ -19,6 +19,7 @@ SRC += \
|
||||||
common/cajun/reader.cpp \
|
common/cajun/reader.cpp \
|
||||||
common/cajun/writer.cpp \
|
common/cajun/writer.cpp \
|
||||||
common/calltip_provider.cpp \
|
common/calltip_provider.cpp \
|
||||||
|
common/character_count.cpp \
|
||||||
common/charset.cpp \
|
common/charset.cpp \
|
||||||
common/charset_6937.cpp \
|
common/charset_6937.cpp \
|
||||||
common/charset_conv.cpp \
|
common/charset_conv.cpp \
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
// 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 "libaegisub/character_count.h"
|
||||||
|
|
||||||
|
#include "libaegisub/ass/dialogue_parser.h"
|
||||||
|
|
||||||
|
#include <boost/locale/boundary.hpp>
|
||||||
|
#include <boost/range/algorithm_ext.hpp>
|
||||||
|
#include <unicode/uchar.h>
|
||||||
|
#include <unicode/utf8.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <typename Iterator>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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 <string>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -21,9 +21,10 @@
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
|
#include <libaegisub/character_count.h>
|
||||||
|
|
||||||
#include <wx/dc.h>
|
#include <wx/dc.h>
|
||||||
|
|
||||||
int WidthHelper::operator()(boost::flyweight<std::string> const& str) {
|
int WidthHelper::operator()(boost::flyweight<std::string> const& str) {
|
||||||
|
@ -223,28 +224,13 @@ struct GridColumnCPS final : GridColumn {
|
||||||
bool RefreshOnTextChange() const override { return true; }
|
bool RefreshOnTextChange() const override { return true; }
|
||||||
|
|
||||||
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
wxString Value(const AssDialogue *d, const agi::Context *) const override {
|
||||||
int characters = 0;
|
|
||||||
|
|
||||||
int duration = d->End - d->Start;
|
int duration = d->End - d->Start;
|
||||||
auto const& text = d->Text.get();
|
auto const& text = d->Text.get();
|
||||||
|
|
||||||
if (duration <= 0 || text.size() > static_cast<size_t>(duration))
|
if (duration <= 0 || text.size() > static_cast<size_t>(duration))
|
||||||
return wxS("");
|
return wxS("");
|
||||||
|
|
||||||
auto pos = begin(text);
|
return std::to_wstring(agi::CharacterCount(text) * 1000 / duration);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {
|
||||||
|
|
|
@ -14,12 +14,11 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file placeholder_ctrl.h
|
|
||||||
/// @ingroup custom_control
|
|
||||||
///
|
|
||||||
|
|
||||||
#include <wx/settings.h>
|
#include <wx/settings.h>
|
||||||
|
|
||||||
|
// Defined in osx_utils.mm
|
||||||
|
void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
|
||||||
|
|
||||||
/// @class Placeholder
|
/// @class Placeholder
|
||||||
/// @brief A wrapper around a control to add placeholder text
|
/// @brief A wrapper around a control to add placeholder text
|
||||||
///
|
///
|
||||||
|
|
|
@ -50,10 +50,10 @@
|
||||||
#include "text_selection_controller.h"
|
#include "text_selection_controller.h"
|
||||||
#include "timeedit_ctrl.h"
|
#include "timeedit_ctrl.h"
|
||||||
#include "tooltip_manager.h"
|
#include "tooltip_manager.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "validators.h"
|
#include "validators.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
|
#include <libaegisub/character_count.h>
|
||||||
#include <libaegisub/dispatch.h>
|
#include <libaegisub/dispatch.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxS
|
||||||
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
|
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
|
||||||
|
|
||||||
Bind(wxEVT_TEXT, [=](wxCommandEvent&) {
|
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; },
|
SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; },
|
||||||
commit_msg, AssFile::COMMIT_DIAG_META);
|
commit_msg, AssFile::COMMIT_DIAG_META);
|
||||||
}, ctrl->GetId());
|
}, ctrl->GetId());
|
||||||
|
@ -587,7 +587,7 @@ void SubsEditBox::CallCommand(const char *cmd_name) {
|
||||||
void SubsEditBox::UpdateCharacterCount(std::string const& text) {
|
void SubsEditBox::UpdateCharacterCount(std::string const& text) {
|
||||||
auto ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool();
|
auto ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool();
|
||||||
agi::dispatch::Background().Async([=]{
|
agi::dispatch::Background().Async([=]{
|
||||||
size_t length = MaxLineLength(text, ignore_whitespace);
|
size_t length = agi::MaxLineLength(text, ignore_whitespace);
|
||||||
agi::dispatch::Main().Async([=]{
|
agi::dispatch::Main().Async([=]{
|
||||||
char_count->SetValue(wxString::Format("%lu", (unsigned long)length));
|
char_count->SetValue(wxString::Format("%lu", (unsigned long)length));
|
||||||
size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();
|
size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "retina_helper.h"
|
#include "retina_helper.h"
|
||||||
|
|
||||||
#include <libaegisub/ass/dialogue_parser.h>
|
|
||||||
#include <libaegisub/dispatch.h>
|
#include <libaegisub/dispatch.h>
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/log.h>
|
#include <libaegisub/log.h>
|
||||||
|
@ -49,11 +48,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/locale/boundary.hpp>
|
|
||||||
#include <boost/range/algorithm_ext.hpp>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unicode/uchar.h>
|
|
||||||
#include <unicode/utf8.h>
|
|
||||||
|
|
||||||
#include <wx/clipbrd.h>
|
#include <wx/clipbrd.h>
|
||||||
#include <wx/filedlg.h>
|
#include <wx/filedlg.h>
|
||||||
|
@ -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__
|
#ifndef __WXOSX_COCOA__
|
||||||
// OS X implementation in osx_utils.mm
|
// OS X implementation in osx_utils.mm
|
||||||
void AddFullScreenButton(wxWindow *) { }
|
void AddFullScreenButton(wxWindow *) { }
|
||||||
|
|
|
@ -60,10 +60,6 @@ std::string float_to_string(double val);
|
||||||
/// Algorithm from http://bob.allegronetwork.com/prog/tricks.html
|
/// Algorithm from http://bob.allegronetwork.com/prog/tricks.html
|
||||||
int SmallestPowerOf2(int x);
|
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.
|
/// @brief Launch a new copy of Aegisub.
|
||||||
///
|
///
|
||||||
/// Contrary to what the name suggests, this does not close the currently
|
/// 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 SetFloatOnParent(wxWindow *window);
|
||||||
|
|
||||||
void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
|
|
||||||
|
|
||||||
/// Forward a mouse wheel event to the window under the mouse if needed
|
/// Forward a mouse wheel event to the window under the mouse if needed
|
||||||
/// @param source The initial target of the wheel event
|
/// @param source The initial target of the wheel event
|
||||||
/// @param evt The event
|
/// @param evt The event
|
||||||
|
|
Loading…
Reference in New Issue