mirror of https://github.com/odrling/Aegisub
Make the entry lists be of the appropriate type rather than just AssEntry
Eliminates a bajillion dynamic casts.
This commit is contained in:
parent
1506c1ab10
commit
b1639c6162
|
@ -38,6 +38,7 @@
|
||||||
<ClInclude Include="$(SrcDir)common\parser.h" />
|
<ClInclude Include="$(SrcDir)common\parser.h" />
|
||||||
<ClInclude Include="$(SrcDir)config.h" />
|
<ClInclude Include="$(SrcDir)config.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\access.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\access.h" />
|
||||||
|
<ClInclude Include="$(SrcDir)include\libaegisub\address_of_adaptor.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\ass\dialogue_parser.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\ass\dialogue_parser.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\ass\uuencode.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\ass\uuencode.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />
|
||||||
|
|
|
@ -164,6 +164,9 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="$(SrcDir)include\libaegisub\address_of_adaptor.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
|
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// 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/range/adaptor/transformed.hpp>
|
||||||
|
|
||||||
|
namespace agi {
|
||||||
|
namespace address_of_detail {
|
||||||
|
using namespace boost::adaptors;
|
||||||
|
|
||||||
|
// Tag type to select the operator| overload
|
||||||
|
struct address_of_tag_type { };
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
struct take_address_of {
|
||||||
|
using result_type = typename std::iterator_traits<Iterator>::pointer;
|
||||||
|
using input_type = typename std::iterator_traits<Iterator>::reference;
|
||||||
|
|
||||||
|
result_type operator()(input_type v) const { return &v; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Rng>
|
||||||
|
auto operator|(Rng&& r, address_of_tag_type)
|
||||||
|
-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
|
||||||
|
{
|
||||||
|
return r | transformed(take_address_of<typename Rng::iterator>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Rng>
|
||||||
|
auto operator|(Rng& r, address_of_tag_type)
|
||||||
|
-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
|
||||||
|
{
|
||||||
|
return r | transformed(take_address_of<typename Rng::iterator>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const auto address_of = address_of_detail::address_of_tag_type{};
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,14 +59,6 @@ AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
|
||||||
entry_data = entry_data.get() + agi::ass::UUEncode(data);
|
entry_data = entry_data.get() + agi::ass::UUEncode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssEntry *AssAttachment::Clone() const {
|
|
||||||
return new AssAttachment(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string AssAttachment::GetEntryData() const {
|
|
||||||
return entry_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AssAttachment::GetSize() const {
|
size_t AssAttachment::GetSize() const {
|
||||||
auto header_end = entry_data.get().find('\n');
|
auto header_end = entry_data.get().find('\n');
|
||||||
return entry_data.get().size() - header_end - 1;
|
return entry_data.get().size() - header_end - 1;
|
||||||
|
|
|
@ -46,9 +46,9 @@ public:
|
||||||
/// @param raw If false, remove the SSA filename mangling
|
/// @param raw If false, remove the SSA filename mangling
|
||||||
std::string GetFileName(bool raw=false) const;
|
std::string GetFileName(bool raw=false) const;
|
||||||
|
|
||||||
const std::string GetEntryData() const override;
|
const std::string GetEntryData() const override { return entry_data; }
|
||||||
AssEntryGroup Group() const override { return group; }
|
AssEntryGroup Group() const override { return group; }
|
||||||
AssEntry *Clone() const override;
|
AssAttachment *Clone() const override { return new AssAttachment(*this); }
|
||||||
|
|
||||||
AssAttachment(AssAttachment const& rgt);
|
AssAttachment(AssAttachment const& rgt);
|
||||||
AssAttachment(std::string const& header, AssEntryGroup group);
|
AssAttachment(std::string const& header, AssEntryGroup group);
|
||||||
|
|
|
@ -256,7 +256,7 @@ std::string AssDialogue::GetStrippedText() const {
|
||||||
return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
|
return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
AssEntry *AssDialogue::Clone() const {
|
AssDialogue *AssDialogue::Clone() const {
|
||||||
auto clone = new AssDialogue(*this);
|
auto clone = new AssDialogue(*this);
|
||||||
clone->Id = Id;
|
clone->Id = Id;
|
||||||
return clone;
|
return clone;
|
||||||
|
|
|
@ -177,7 +177,7 @@ public:
|
||||||
/// Does this line collide with the passed line?
|
/// Does this line collide with the passed line?
|
||||||
bool CollidesWith(const AssDialogue *target) const;
|
bool CollidesWith(const AssDialogue *target) const;
|
||||||
|
|
||||||
AssEntry *Clone() const override;
|
AssDialogue *Clone() const override;
|
||||||
|
|
||||||
AssDialogue();
|
AssDialogue();
|
||||||
AssDialogue(AssDialogue const&);
|
AssDialogue(AssDialogue const&);
|
||||||
|
|
|
@ -1,36 +1,19 @@
|
||||||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
//
|
//
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file ass_file.cpp
|
|
||||||
/// @brief Overall storage of subtitle files, undo management and more
|
|
||||||
/// @ingroup subs_storage
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
|
@ -42,20 +25,20 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
AssFile::AssFile() { }
|
||||||
|
|
||||||
AssFile::~AssFile() {
|
AssFile::~AssFile() {
|
||||||
Info.clear_and_dispose([](AssEntry *e) { delete e; });
|
Info.clear_and_dispose([](AssInfo *e) { delete e; });
|
||||||
Styles.clear_and_dispose([](AssEntry *e) { delete e; });
|
Styles.clear_and_dispose([](AssStyle *e) { delete e; });
|
||||||
Events.clear_and_dispose([](AssEntry *e) { delete e; });
|
Events.clear_and_dispose([](AssDialogue *e) { delete e; });
|
||||||
Attachments.clear_and_dispose([](AssEntry *e) { delete e; });
|
Attachments.clear_and_dispose([](AssAttachment *e) { delete e; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::LoadDefault(bool defline) {
|
void AssFile::LoadDefault(bool include_dialogue_line) {
|
||||||
Info.push_back(*new AssInfo("Title", "Default Aegisub file"));
|
Info.push_back(*new AssInfo("Title", "Default Aegisub file"));
|
||||||
Info.push_back(*new AssInfo("ScriptType", "v4.00+"));
|
Info.push_back(*new AssInfo("ScriptType", "v4.00+"));
|
||||||
Info.push_back(*new AssInfo("WrapStyle", "0"));
|
Info.push_back(*new AssInfo("WrapStyle", "0"));
|
||||||
|
@ -68,15 +51,15 @@ void AssFile::LoadDefault(bool defline) {
|
||||||
|
|
||||||
Styles.push_back(*new AssStyle);
|
Styles.push_back(*new AssStyle);
|
||||||
|
|
||||||
if (defline)
|
if (include_dialogue_line)
|
||||||
Events.push_back(*new AssDialogue);
|
Events.push_back(*new AssDialogue);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssFile::AssFile(const AssFile &from) {
|
AssFile::AssFile(const AssFile &from) {
|
||||||
Info.clone_from(from.Info, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
|
Info.clone_from(from.Info, std::mem_fun_ref(&AssInfo::Clone), [](AssInfo *e) { delete e; });
|
||||||
Styles.clone_from(from.Styles, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
|
Styles.clone_from(from.Styles, std::mem_fun_ref(&AssStyle::Clone), [](AssStyle *e) { delete e; });
|
||||||
Events.clone_from(from.Events, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
|
Events.clone_from(from.Events, std::mem_fun_ref(&AssDialogue::Clone), [](AssDialogue *e) { delete e; });
|
||||||
Attachments.clone_from(from.Attachments, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
|
Attachments.clone_from(from.Attachments, std::mem_fun_ref(&AssAttachment::Clone), [](AssAttachment *e) { delete e; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::swap(AssFile& from) throw() {
|
void AssFile::swap(AssFile& from) throw() {
|
||||||
|
@ -102,9 +85,9 @@ void AssFile::InsertAttachment(agi::fs::path const& filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AssFile::GetScriptInfo(std::string const& key) const {
|
std::string AssFile::GetScriptInfo(std::string const& key) const {
|
||||||
for (const auto info : Info | agi::of_type<AssInfo>()) {
|
for (auto const& info : Info) {
|
||||||
if (boost::iequals(key, info->Key()))
|
if (boost::iequals(key, info.Key()))
|
||||||
return info->Value();
|
return info.Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -131,12 +114,12 @@ void AssFile::SaveUIState(std::string const& key, std::string const& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
|
void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
|
||||||
for (auto info : Info | agi::of_type<AssInfo>()) {
|
for (auto& info : Info) {
|
||||||
if (boost::iequals(key, info->Key())) {
|
if (boost::iequals(key, info.Key())) {
|
||||||
if (value.empty())
|
if (value.empty())
|
||||||
delete info;
|
delete &info;
|
||||||
else
|
else
|
||||||
info->SetValue(value);
|
info.SetValue(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,47 +128,42 @@ void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
|
||||||
Info.push_back(*new AssInfo(key, value));
|
Info.push_back(*new AssInfo(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::GetResolution(int &sw,int &sh) const {
|
void AssFile::GetResolution(int &sw, int &sh) const {
|
||||||
sw = GetScriptInfoAsInt("PlayResX");
|
sw = GetScriptInfoAsInt("PlayResX");
|
||||||
sh = GetScriptInfoAsInt("PlayResY");
|
sh = GetScriptInfoAsInt("PlayResY");
|
||||||
|
|
||||||
// Gabest logic?
|
// Gabest logic: default is 384x288, assume 1280x1024 if either height or
|
||||||
|
// width are that, otherwise assume 4:3 if only heigh or width are set.
|
||||||
|
// Why 1280x1024? Who the fuck knows. Clearly just Gabest trolling everyone.
|
||||||
if (sw == 0 && sh == 0) {
|
if (sw == 0 && sh == 0) {
|
||||||
sw = 384;
|
sw = 384;
|
||||||
sh = 288;
|
sh = 288;
|
||||||
} else if (sw == 0) {
|
|
||||||
if (sh == 1024)
|
|
||||||
sw = 1280;
|
|
||||||
else
|
|
||||||
sw = sh * 4 / 3;
|
|
||||||
} else if (sh == 0) {
|
|
||||||
// you are not crazy; this doesn't make any sense
|
|
||||||
if (sw == 1280)
|
|
||||||
sh = 1024;
|
|
||||||
else
|
|
||||||
sh = sw * 3 / 4;
|
|
||||||
}
|
}
|
||||||
|
else if (sw == 0)
|
||||||
|
sw = sh == 1024 ? 1280 : sh * 4 / 3;
|
||||||
|
else if (sh == 0)
|
||||||
|
sh = sw == 1280 ? 1024 : sw * 3 / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> AssFile::GetStyles() const {
|
std::vector<std::string> AssFile::GetStyles() const {
|
||||||
std::vector<std::string> styles;
|
std::vector<std::string> styles;
|
||||||
for (auto style : Styles | agi::of_type<AssStyle>())
|
for (auto& style : Styles)
|
||||||
styles.push_back(style->name);
|
styles.push_back(style.name);
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssStyle *AssFile::GetStyle(std::string const& name) {
|
AssStyle *AssFile::GetStyle(std::string const& name) {
|
||||||
for (auto style : Styles | agi::of_type<AssStyle>()) {
|
for (auto& style : Styles) {
|
||||||
if (boost::iequals(style->name, name))
|
if (boost::iequals(style.name, name))
|
||||||
return style;
|
return &style;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AssFile::Commit(wxString const& desc, int type, int amend_id, AssEntry *single_line) {
|
int AssFile::Commit(wxString const& desc, int type, int amend_id, AssDialogue *single_line) {
|
||||||
PushState({desc, &amend_id, single_line});
|
PushState({desc, &amend_id, single_line});
|
||||||
|
|
||||||
std::set<const AssEntry*> changed_lines;
|
std::set<const AssDialogue*> changed_lines;
|
||||||
if (single_line)
|
if (single_line)
|
||||||
changed_lines.insert(single_line);
|
changed_lines.insert(single_line);
|
||||||
|
|
||||||
|
@ -194,50 +172,45 @@ int AssFile::Commit(wxString const& desc, int type, int amend_id, AssEntry *sing
|
||||||
return amend_id;
|
return amend_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssFile::CompStart(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompStart(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->Start < rgt->Start;
|
return lft.Start < rgt.Start;
|
||||||
}
|
}
|
||||||
bool AssFile::CompEnd(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompEnd(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->End < rgt->End;
|
return lft.End < rgt.End;
|
||||||
}
|
}
|
||||||
bool AssFile::CompStyle(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompStyle(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->Style < rgt->Style;
|
return lft.Style < rgt.Style;
|
||||||
}
|
}
|
||||||
bool AssFile::CompActor(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompActor(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->Actor < rgt->Actor;
|
return lft.Actor < rgt.Actor;
|
||||||
}
|
}
|
||||||
bool AssFile::CompEffect(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompEffect(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->Effect < rgt->Effect;
|
return lft.Effect < rgt.Effect;
|
||||||
}
|
}
|
||||||
bool AssFile::CompLayer(const AssDialogue* lft, const AssDialogue* rgt) {
|
bool AssFile::CompLayer(AssDialogue const& lft, AssDialogue const& rgt) {
|
||||||
return lft->Layer < rgt->Layer;
|
return lft.Layer < rgt.Layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::Sort(CompFunc comp, std::set<AssDialogue*> const& limit) {
|
void AssFile::Sort(CompFunc comp, std::set<AssDialogue*> const& limit) {
|
||||||
Sort(Events, comp, limit);
|
Sort(Events, comp, limit);
|
||||||
}
|
}
|
||||||
namespace {
|
|
||||||
inline bool is_dialogue(AssEntry *e, std::set<AssDialogue*> const& limit) {
|
void AssFile::Sort(EntryList<AssDialogue> &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
|
||||||
AssDialogue *d = dynamic_cast<AssDialogue*>(e);
|
if (limit.empty()) {
|
||||||
return d && (limit.empty() || limit.count(d));
|
lst.sort(comp);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AssFile::Sort(EntryList &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
|
// Sort each selected block separately, leaving everything else untouched
|
||||||
auto compE = [&](AssEntry const& a, AssEntry const& b) {
|
for (auto begin = lst.begin(); begin != lst.end(); ++begin) {
|
||||||
return comp(static_cast<const AssDialogue*>(&a), static_cast<const AssDialogue*>(&b));
|
if (!limit.count(&*begin)) continue;
|
||||||
};
|
auto end = begin;
|
||||||
|
while (end != lst.end() && limit.count(&*end)) ++end;
|
||||||
|
|
||||||
// Sort each block of AssDialogues separately, leaving everything else untouched
|
// sort doesn't support only sorting a sublist, so move them to a temp list
|
||||||
for (entryIter begin = lst.begin(); begin != lst.end(); ++begin) {
|
EntryList<AssDialogue> tmp;
|
||||||
if (!is_dialogue(&*begin, limit)) continue;
|
|
||||||
entryIter end = begin;
|
|
||||||
while (end != lst.end() && is_dialogue(&*end, limit)) ++end;
|
|
||||||
|
|
||||||
// used instead of std::list::sort for partial list sorting
|
|
||||||
EntryList tmp;
|
|
||||||
tmp.splice(tmp.begin(), lst, begin, end);
|
tmp.splice(tmp.begin(), lst, begin, end);
|
||||||
tmp.sort(compE);
|
tmp.sort(comp);
|
||||||
lst.splice(end, tmp);
|
lst.splice(end, tmp);
|
||||||
|
|
||||||
begin = --end;
|
begin = --end;
|
||||||
|
|
|
@ -41,33 +41,36 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class AssDialogue;
|
|
||||||
class AssStyle;
|
|
||||||
class AssAttachment;
|
class AssAttachment;
|
||||||
|
class AssDialogue;
|
||||||
|
class AssInfo;
|
||||||
|
class AssStyle;
|
||||||
class wxString;
|
class wxString;
|
||||||
|
|
||||||
typedef boost::intrusive::make_list<AssEntry, boost::intrusive::constant_time_size<false>>::type EntryList;
|
template<typename T>
|
||||||
typedef EntryList::iterator entryIter;
|
using EntryList = typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>, boost::intrusive::base_hook<AssEntry>>::type;
|
||||||
typedef EntryList::const_iterator constEntryIter;
|
|
||||||
|
template<typename T>
|
||||||
|
using EntryIter = typename EntryList<T>::iterator;
|
||||||
|
|
||||||
struct AssFileCommit {
|
struct AssFileCommit {
|
||||||
wxString const& message;
|
wxString const& message;
|
||||||
int *commit_id;
|
int *commit_id;
|
||||||
AssEntry *single_line;
|
AssDialogue *single_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AssFile {
|
class AssFile {
|
||||||
/// A set of changes has been committed to the file (AssFile::COMMITType)
|
/// A set of changes has been committed to the file (AssFile::COMMITType)
|
||||||
agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit;
|
agi::signal::Signal<int, std::set<const AssDialogue*> const&> AnnounceCommit;
|
||||||
agi::signal::Signal<AssFileCommit> PushState;
|
agi::signal::Signal<AssFileCommit> PushState;
|
||||||
public:
|
public:
|
||||||
/// The lines in the file
|
/// The lines in the file
|
||||||
EntryList Info;
|
EntryList<AssInfo> Info;
|
||||||
EntryList Styles;
|
EntryList<AssStyle> Styles;
|
||||||
EntryList Events;
|
EntryList<AssDialogue> Events;
|
||||||
EntryList Attachments;
|
EntryList<AssAttachment> Attachments;
|
||||||
|
|
||||||
AssFile() { }
|
AssFile();
|
||||||
AssFile(const AssFile &from);
|
AssFile(const AssFile &from);
|
||||||
AssFile& operator=(AssFile from);
|
AssFile& operator=(AssFile from);
|
||||||
~AssFile();
|
~AssFile();
|
||||||
|
@ -139,23 +142,23 @@ public:
|
||||||
/// @param commitId Commit to amend rather than pushing a new commit
|
/// @param commitId Commit to amend rather than pushing a new commit
|
||||||
/// @param single_line Line which was changed, if only one line was
|
/// @param single_line Line which was changed, if only one line was
|
||||||
/// @return Unique identifier for the new undo group
|
/// @return Unique identifier for the new undo group
|
||||||
int Commit(wxString const& desc, int type, int commitId = -1, AssEntry *single_line = nullptr);
|
int Commit(wxString const& desc, int type, int commitId = -1, AssDialogue *single_line = nullptr);
|
||||||
|
|
||||||
/// Comparison function for use when sorting
|
/// Comparison function for use when sorting
|
||||||
typedef bool (*CompFunc)(const AssDialogue* lft, const AssDialogue* rgt);
|
typedef bool (*CompFunc)(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
|
|
||||||
/// Compare based on start time
|
/// Compare based on start time
|
||||||
static bool CompStart(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompStart(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
/// Compare based on end time
|
/// Compare based on end time
|
||||||
static bool CompEnd(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompEnd(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
/// Compare based on style name
|
/// Compare based on style name
|
||||||
static bool CompStyle(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompStyle(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
/// Compare based on actor name
|
/// Compare based on actor name
|
||||||
static bool CompActor(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompActor(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
/// Compare based on effect
|
/// Compare based on effect
|
||||||
static bool CompEffect(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompEffect(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
/// Compare based on layer
|
/// Compare based on layer
|
||||||
static bool CompLayer(const AssDialogue* lft, const AssDialogue* rgt);
|
static bool CompLayer(AssDialogue const& lft, AssDialogue const& rgt);
|
||||||
|
|
||||||
/// @brief Sort the dialogue lines in this file
|
/// @brief Sort the dialogue lines in this file
|
||||||
/// @param comp Comparison function to use. Defaults to sorting by start time.
|
/// @param comp Comparison function to use. Defaults to sorting by start time.
|
||||||
|
@ -164,5 +167,5 @@ public:
|
||||||
/// @brief Sort the dialogue lines in the given list
|
/// @brief Sort the dialogue lines in the given list
|
||||||
/// @param comp Comparison function to use. Defaults to sorting by start time.
|
/// @param comp Comparison function to use. Defaults to sorting by start time.
|
||||||
/// @param limit If non-empty, only lines in this set are sorted
|
/// @param limit If non-empty, only lines in this set are sorted
|
||||||
static void Sort(EntryList& lst, CompFunc comp = CompStart, std::set<AssDialogue*> const& limit = std::set<AssDialogue*>());
|
static void Sort(EntryList<AssDialogue>& lst, CompFunc comp = CompStart, std::set<AssDialogue*> const& limit = std::set<AssDialogue*>());
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
AssInfo(AssInfo const& o) = default;
|
AssInfo(AssInfo const& o) = default;
|
||||||
AssInfo(std::string key, std::string value) : key(std::move(key)), value(std::move(value)) { }
|
AssInfo(std::string key, std::string value) : key(std::move(key)), value(std::move(value)) { }
|
||||||
|
|
||||||
AssEntry *Clone() const override { return new AssInfo(*this); }
|
AssInfo *Clone() const override { return new AssInfo(*this); }
|
||||||
AssEntryGroup Group() const override { return AssEntryGroup::INFO; }
|
AssEntryGroup Group() const override { return AssEntryGroup::INFO; }
|
||||||
const std::string GetEntryData() const override { return key + ": " + value; }
|
const std::string GetEntryData() const override { return key + ": " + value; }
|
||||||
std::string GetSSAText() const override { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); }
|
std::string GetSSAText() const override { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); }
|
||||||
|
|
|
@ -280,19 +280,18 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
|
||||||
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
bool did_split = false;
|
bool did_split = false;
|
||||||
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it);
|
if (!lines.count(&*it)) continue;
|
||||||
if (!diag || !lines.count(diag)) continue;
|
|
||||||
|
|
||||||
kara.SetLine(diag);
|
kara.SetLine(&*it);
|
||||||
|
|
||||||
// If there aren't at least two tags there's nothing to split
|
// If there aren't at least two tags there's nothing to split
|
||||||
if (kara.size() < 2) continue;
|
if (kara.size() < 2) continue;
|
||||||
|
|
||||||
bool in_sel = sel.count(diag) > 0;
|
bool in_sel = sel.count(&*it) > 0;
|
||||||
|
|
||||||
for (auto const& syl : kara) {
|
for (auto const& syl : kara) {
|
||||||
auto new_line = new AssDialogue(*diag);
|
auto new_line = new AssDialogue(*it);
|
||||||
|
|
||||||
new_line->Start = syl.start_time;
|
new_line->Start = syl.start_time;
|
||||||
new_line->End = syl.start_time + syl.duration;
|
new_line->End = syl.start_time + syl.duration;
|
||||||
|
@ -305,8 +304,8 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
|
||||||
}
|
}
|
||||||
|
|
||||||
--it; // Move `it` to the last of the new lines
|
--it; // Move `it` to the last of the new lines
|
||||||
sel.erase(diag);
|
sel.erase(&*it);
|
||||||
delete diag;
|
delete &*it;
|
||||||
|
|
||||||
did_split = true;
|
did_split = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,10 +204,6 @@ std::string AssStyle::GetSSAText() const {
|
||||||
% Margin[0] % Margin[1] % Margin[2] % encoding);
|
% Margin[0] % Margin[1] % Margin[2] % encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssEntry *AssStyle::Clone() const {
|
|
||||||
return new AssStyle(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
|
void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
|
||||||
encodingStrings.Clear();
|
encodingStrings.Clear();
|
||||||
encodingStrings.Add(wxString("0 - ") + _("ANSI"));
|
encodingStrings.Add(wxString("0 - ") + _("ANSI"));
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
const std::string GetEntryData() const override { return data; }
|
const std::string GetEntryData() const override { return data; }
|
||||||
std::string GetSSAText() const override;
|
std::string GetSSAText() const override;
|
||||||
AssEntryGroup Group() const override { return AssEntryGroup::STYLE; }
|
AssEntryGroup Group() const override { return AssEntryGroup::STYLE; }
|
||||||
AssEntry *Clone() const override;
|
AssStyle *Clone() const override { return new AssStyle(*this); }
|
||||||
|
|
||||||
/// Convert an ASS alignment to the equivalent SSA alignment
|
/// Convert an ASS alignment to the equivalent SSA alignment
|
||||||
static int AssToSsa(int ass_align);
|
static int AssToSsa(int ass_align);
|
||||||
|
|
|
@ -117,7 +117,7 @@ void AudioKaraoke::OnActiveLineChanged(AssDialogue *new_line) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioKaraoke::OnFileChanged(int type, std::set<const AssEntry *> const& changed) {
|
void AudioKaraoke::OnFileChanged(int type, std::set<const AssDialogue *> const& changed) {
|
||||||
if (enabled && (type & AssFile::COMMIT_DIAG_FULL) && (changed.empty() || changed.count(active_line))) {
|
if (enabled && (type & AssFile::COMMIT_DIAG_FULL) && (changed.empty() || changed.count(active_line))) {
|
||||||
LoadFromLine();
|
LoadFromLine();
|
||||||
split_area->Refresh(false);
|
split_area->Refresh(false);
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
|
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
class AssEntry;
|
|
||||||
class AssKaraoke;
|
class AssKaraoke;
|
||||||
class wxButton;
|
class wxButton;
|
||||||
|
|
||||||
|
@ -144,7 +143,7 @@ class AudioKaraoke : public wxWindow {
|
||||||
void OnActiveLineChanged(AssDialogue *new_line);
|
void OnActiveLineChanged(AssDialogue *new_line);
|
||||||
void OnContextMenu(wxContextMenuEvent&);
|
void OnContextMenu(wxContextMenuEvent&);
|
||||||
void OnEnableButton(wxCommandEvent &evt);
|
void OnEnableButton(wxCommandEvent &evt);
|
||||||
void OnFileChanged(int type, std::set<const AssEntry *> const& changed);
|
void OnFileChanged(int type, std::set<const AssDialogue *> const& changed);
|
||||||
void OnMouse(wxMouseEvent &event);
|
void OnMouse(wxMouseEvent &event);
|
||||||
void OnPaint(wxPaintEvent &event);
|
void OnPaint(wxPaintEvent &event);
|
||||||
void OnSize(wxSizeEvent &event);
|
void OnSize(wxSizeEvent &event);
|
||||||
|
|
|
@ -715,21 +715,19 @@ void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker*> const&
|
||||||
AnnounceMarkerMoved();
|
AnnounceMarkerMoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool noncomment_dialogue(AssEntry const& e)
|
static bool noncomment_dialogue(AssDialogue const& diag)
|
||||||
{
|
{
|
||||||
if (const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e))
|
return !diag.Comment;
|
||||||
return !diag->Comment;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dialogue(AssEntry const& e)
|
static bool dialogue(AssDialogue const&)
|
||||||
{
|
{
|
||||||
return !!dynamic_cast<const AssDialogue*>(&e);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
{
|
{
|
||||||
bool (*predicate)(AssEntry const&) = inactive_line_comments->GetBool() ? dialogue : noncomment_dialogue;
|
auto predicate = inactive_line_comments->GetBool() ? dialogue : noncomment_dialogue;
|
||||||
|
|
||||||
bool was_empty = inactive_lines.empty();
|
bool was_empty = inactive_lines.empty();
|
||||||
inactive_lines.clear();
|
inactive_lines.clear();
|
||||||
|
@ -742,21 +740,20 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
case 2: // Previous and next lines
|
case 2: // Previous and next lines
|
||||||
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
||||||
{
|
{
|
||||||
entryIter current_line = context->ass->Events.iterator_to(*line);
|
auto current_line = context->ass->Events.iterator_to(*line);
|
||||||
if (current_line == context->ass->Events.end())
|
if (current_line == context->ass->Events.end())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
entryIter prev = current_line;
|
auto prev = current_line;
|
||||||
while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
|
while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
|
||||||
if (prev != context->ass->Events.begin())
|
if (prev != context->ass->Events.begin())
|
||||||
AddInactiveLine(sel, static_cast<AssDialogue*>(&*prev));
|
AddInactiveLine(sel, &*prev);
|
||||||
|
|
||||||
if (mode == 2)
|
if (mode == 2)
|
||||||
{
|
{
|
||||||
entryIter next =
|
auto next = find_if(++current_line, context->ass->Events.end(), predicate);
|
||||||
find_if(++current_line, context->ass->Events.end(), predicate);
|
|
||||||
if (next != context->ass->Events.end())
|
if (next != context->ass->Events.end())
|
||||||
AddInactiveLine(sel, static_cast<AssDialogue*>(&*next));
|
AddInactiveLine(sel, &*next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -766,7 +763,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
for (auto& line : context->ass->Events)
|
for (auto& line : context->ass->Events)
|
||||||
{
|
{
|
||||||
if (&line != active_line && predicate(line))
|
if (&line != active_line && predicate(line))
|
||||||
AddInactiveLine(sel, static_cast<AssDialogue*>(&line));
|
AddInactiveLine(sel, &line);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ namespace {
|
||||||
|
|
||||||
lua_pushvalue(L, 1);
|
lua_pushvalue(L, 1);
|
||||||
std::unique_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
|
std::unique_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
|
||||||
AssStyle *st = dynamic_cast<AssStyle*>(et.get());
|
auto st = dynamic_cast<AssStyle*>(et.get());
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
if (!st)
|
if (!st)
|
||||||
return luaL_error(L, "Not a style entry");
|
return luaL_error(L, "Not a style entry");
|
||||||
|
@ -843,9 +843,8 @@ namespace Automation4 {
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
for (auto& line : c->ass->Events) {
|
for (auto& line : c->ass->Events) {
|
||||||
++row;
|
++row;
|
||||||
auto diag = static_cast<AssDialogue*>(&line);
|
if (&line == active_line) active_idx = row;
|
||||||
if (diag == active_line) active_idx = row;
|
if (sel.count(&line)) {
|
||||||
if (sel.count(diag)) {
|
|
||||||
push_value(L, row);
|
push_value(L, row);
|
||||||
lua_rawseti(L, -2, idx++);
|
lua_rawseti(L, -2, idx++);
|
||||||
}
|
}
|
||||||
|
@ -928,7 +927,7 @@ namespace Automation4 {
|
||||||
throw LuaForEachBreak();
|
throw LuaForEachBreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(lines[cur - 1]);
|
auto diag = dynamic_cast<AssDialogue*>(lines[cur - 1]);
|
||||||
if (!diag) {
|
if (!diag) {
|
||||||
wxLogError("Selected row %d is not a dialogue line", cur);
|
wxLogError("Selected row %d is not a dialogue line", cur);
|
||||||
throw LuaForEachBreak();
|
throw LuaForEachBreak();
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "auto4_lua.h"
|
#include "auto4_lua.h"
|
||||||
|
|
||||||
#include "auto4_lua_utils.h"
|
#include "auto4_lua_utils.h"
|
||||||
|
#include "ass_attachment.h"
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
#include "ass_info.h"
|
#include "ass_info.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
|
@ -535,7 +536,7 @@ namespace Automation4 {
|
||||||
int LuaAssFile::LuaParseKaraokeData(lua_State *L)
|
int LuaAssFile::LuaParseKaraokeData(lua_State *L)
|
||||||
{
|
{
|
||||||
auto e = LuaToAssEntry(L);
|
auto e = LuaToAssEntry(L);
|
||||||
AssDialogue *dia = dynamic_cast<AssDialogue*>(e.get());
|
auto dia = dynamic_cast<AssDialogue*>(e.get());
|
||||||
luaL_argcheck(L, dia, 1, "Subtitle line must be a dialogue line");
|
luaL_argcheck(L, dia, 1, "Subtitle line must be a dialogue line");
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
|
@ -52,8 +52,6 @@
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "video_slider.h"
|
#include "video_slider.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/range/algorithm.hpp>
|
#include <boost/range/algorithm.hpp>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -82,13 +80,6 @@ enum RowColor {
|
||||||
COLOR_LEFT_COL
|
COLOR_LEFT_COL
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class S1, class S2, class D>
|
|
||||||
static inline void set_difference(const S1 &src1, const S2 &src2, D &dst) {
|
|
||||||
std::set_difference(
|
|
||||||
src1.begin(), src1.end(), src2.begin(), src2.end(),
|
|
||||||
std::inserter(dst, dst.begin()));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct hash<boost::flyweight<T>> {
|
struct hash<boost::flyweight<T>> {
|
||||||
|
@ -265,9 +256,9 @@ void BaseGrid::UpdateMaps() {
|
||||||
index_line_map.clear();
|
index_line_map.clear();
|
||||||
line_index_map.clear();
|
line_index_map.clear();
|
||||||
|
|
||||||
for (auto curdiag : context->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& curdiag : context->ass->Events) {
|
||||||
line_index_map[curdiag] = (int)index_line_map.size();
|
line_index_map[&curdiag] = (int)index_line_map.size();
|
||||||
index_line_map.push_back(curdiag);
|
index_line_map.push_back(&curdiag);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sorted = index_line_map;
|
auto sorted = index_line_map;
|
||||||
|
@ -751,6 +742,7 @@ void BaseGrid::SetColumnWidths() {
|
||||||
|
|
||||||
std::unordered_map<boost::flyweight<std::string>, int> widths;
|
std::unordered_map<boost::flyweight<std::string>, int> widths;
|
||||||
auto get_width = [&](boost::flyweight<std::string> const& str) -> int {
|
auto get_width = [&](boost::flyweight<std::string> const& str) -> int {
|
||||||
|
if (str.get().empty()) return 0;
|
||||||
auto it = widths.find(str);
|
auto it = widths.find(str);
|
||||||
if (it != end(widths)) return it->second;
|
if (it != end(widths)) return it->second;
|
||||||
int width = dc.GetTextExtent(to_wx(str)).GetWidth();
|
int width = dc.GetTextExtent(to_wx(str)).GetWidth();
|
||||||
|
@ -766,24 +758,22 @@ void BaseGrid::SetColumnWidths() {
|
||||||
int maxLayer = 0;
|
int maxLayer = 0;
|
||||||
int maxStart = 0;
|
int maxStart = 0;
|
||||||
int maxEnd = 0;
|
int maxEnd = 0;
|
||||||
for (int i = 0; i < GetRows(); i++) {
|
for (auto const& diag : context->ass->Events) {
|
||||||
AssDialogue *curDiag = GetDialogue(i);
|
maxLayer = std::max(maxLayer, diag.Layer);
|
||||||
|
actorLen = std::max(actorLen, get_width(diag.Actor));
|
||||||
maxLayer = std::max(maxLayer, curDiag->Layer);
|
styleLen = std::max(styleLen, get_width(diag.Style));
|
||||||
actorLen = std::max(actorLen, get_width(curDiag->Actor));
|
effectLen = std::max(effectLen, get_width(diag.Effect));
|
||||||
styleLen = std::max(styleLen, get_width(curDiag->Style));
|
|
||||||
effectLen = std::max(effectLen, get_width(curDiag->Effect));
|
|
||||||
|
|
||||||
// Margins
|
// Margins
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
if (curDiag->Margin[j])
|
if (diag.Margin[j])
|
||||||
showMargin[j] = true;
|
showMargin[j] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Times
|
// Times
|
||||||
if (byFrame) {
|
if (byFrame) {
|
||||||
maxStart = std::max(maxStart, context->videoController->FrameAtTime(curDiag->Start, agi::vfr::START));
|
maxStart = std::max(maxStart, context->videoController->FrameAtTime(diag.Start, agi::vfr::START));
|
||||||
maxEnd = std::max(maxEnd, context->videoController->FrameAtTime(curDiag->End, agi::vfr::END));
|
maxEnd = std::max(maxEnd, context->videoController->FrameAtTime(diag.End, agi::vfr::END));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,15 +981,19 @@ void BaseGrid::SetSelectionAndActive(Selection const& new_selection, AssDialogue
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::PrevLine() {
|
void BaseGrid::PrevLine() {
|
||||||
int cur_line_i = GetDialogueIndex(GetActiveLine());
|
if (!active_line) return;
|
||||||
if (AssDialogue *prev_line = GetDialogue(cur_line_i-1))
|
auto it = context->ass->Events.iterator_to(*active_line);
|
||||||
SetSelectionAndActive({ prev_line }, prev_line);
|
if (it != context->ass->Events.begin()) {
|
||||||
|
--it;
|
||||||
|
SetSelectionAndActive({&*it}, &*it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::NextLine() {
|
void BaseGrid::NextLine() {
|
||||||
int cur_line_i = GetDialogueIndex(GetActiveLine());
|
if (!active_line) return;
|
||||||
if (AssDialogue *next_line = GetDialogue(cur_line_i+1))
|
auto it = context->ass->Events.iterator_to(*active_line);
|
||||||
SetSelectionAndActive({ next_line }, next_line);
|
if (++it != context->ass->Events.end())
|
||||||
|
SetSelectionAndActive({&*it}, &*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseGrid::AnnounceActiveLineChanged(AssDialogue *new_line) {
|
void BaseGrid::AnnounceActiveLineChanged(AssDialogue *new_line) {
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
#include "../video_context.h"
|
#include "../video_context.h"
|
||||||
|
|
||||||
|
#include <libaegisub/address_of_adaptor.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
#include <libaegisub/of_type_adaptor.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
@ -485,12 +486,11 @@ struct edit_find_replace : 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 sel = c->selectionController->GetSelectedSet();
|
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
||||||
SetClipboard(join(c->ass->Events
|
SetClipboard(join(c->ass->Events
|
||||||
| agi::of_type<AssDialogue>()
|
| filtered([&](AssDialogue &d) { return sel.count(&d); })
|
||||||
| filtered([&](AssDialogue *d) { return sel.count(d); })
|
|
||||||
| transformed(get_entry_data),
|
| transformed(get_entry_data),
|
||||||
"\r\n"));
|
"\r\n"));
|
||||||
}
|
}
|
||||||
|
@ -503,25 +503,25 @@ static void delete_lines(agi::Context *c, wxString const& commit_message) {
|
||||||
AssDialogue *post_sel = nullptr;
|
AssDialogue *post_sel = nullptr;
|
||||||
bool hit_selection = false;
|
bool hit_selection = false;
|
||||||
|
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (sel.count(diag))
|
if (sel.count(&diag))
|
||||||
hit_selection = true;
|
hit_selection = true;
|
||||||
else if (hit_selection && !post_sel) {
|
else if (hit_selection && !post_sel) {
|
||||||
post_sel = diag;
|
post_sel = &diag;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pre_sel = diag;
|
pre_sel = &diag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the selected lines, but defer the deletion until after we select
|
// Remove the selected lines, but defer the deletion until after we select
|
||||||
// different lines. We can't just change the selection first because we may
|
// different lines. We can't just change the selection first because we may
|
||||||
// need to create a new dialogue line for it, and we can't select dialogue
|
// need to create a new dialogue line for it, and we can't select dialogue
|
||||||
// lines until after they're committed.
|
// lines until after they're committed.
|
||||||
std::vector<std::unique_ptr<AssEntry>> to_delete;
|
std::vector<std::unique_ptr<AssDialogue>> to_delete;
|
||||||
c->ass->Events.remove_and_dispose_if([&sel](AssEntry const& e) {
|
c->ass->Events.remove_and_dispose_if([&sel](AssDialogue const& e) {
|
||||||
return sel.count(const_cast<AssDialogue *>(static_cast<const AssDialogue*>(&e)));
|
return sel.count(const_cast<AssDialogue *>(&e));
|
||||||
}, [&](AssEntry *e) {
|
}, [&](AssDialogue *e) {
|
||||||
to_delete.emplace_back(e);
|
to_delete.emplace_back(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -591,35 +591,28 @@ struct edit_line_delete : public validate_sel_nonempty {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct in_selection : public std::unary_function<AssEntry, bool> {
|
|
||||||
SubtitleSelectionController::Selection const& sel;
|
|
||||||
in_selection(SubtitleSelectionController::Selection const& sel) : sel(sel) { }
|
|
||||||
bool operator()(AssEntry const& e) const {
|
|
||||||
const AssDialogue *d = dynamic_cast<const AssDialogue*>(&e);
|
|
||||||
return d && sel.count(const_cast<AssDialogue *>(d));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void duplicate_lines(agi::Context *c, int shift) {
|
static void duplicate_lines(agi::Context *c, int shift) {
|
||||||
in_selection sel(c->selectionController->GetSelectedSet());
|
auto const& sel = c->selectionController->GetSelectedSet();
|
||||||
|
auto in_selection = [&](AssDialogue const& d) { return sel.count(const_cast<AssDialogue *>(&d)); };
|
||||||
|
|
||||||
SubtitleSelectionController::Selection new_sel;
|
SubtitleSelectionController::Selection new_sel;
|
||||||
AssDialogue *new_active = nullptr;
|
AssDialogue *new_active = nullptr;
|
||||||
|
|
||||||
entryIter start = c->ass->Events.begin();
|
auto start = c->ass->Events.begin();
|
||||||
entryIter end = c->ass->Events.end();
|
auto end = c->ass->Events.end();
|
||||||
while (start != end) {
|
while (start != end) {
|
||||||
// Find the first line in the selection
|
// Find the first line in the selection
|
||||||
start = find_if(start, end, sel);
|
start = find_if(start, end, in_selection);
|
||||||
if (start == end) break;
|
if (start == end) break;
|
||||||
|
|
||||||
// And the last line in this contiguous selection
|
// And the last line in this contiguous selection
|
||||||
entryIter insert_pos = find_if_not(start, end, sel);
|
auto insert_pos = find_if_not(start, end, in_selection);
|
||||||
entryIter last = std::prev(insert_pos);
|
auto last = std::prev(insert_pos);
|
||||||
|
|
||||||
// Duplicate each of the selected lines, inserting them in a block
|
// Duplicate each of the selected lines, inserting them in a block
|
||||||
// after the selected block
|
// after the selected block
|
||||||
do {
|
do {
|
||||||
auto old_diag = static_cast<AssDialogue*>(&*start);
|
auto old_diag = &*start;
|
||||||
auto new_diag = new AssDialogue(*old_diag);
|
auto new_diag = new AssDialogue(*old_diag);
|
||||||
|
|
||||||
c->ass->Events.insert(insert_pos, *new_diag);
|
c->ass->Events.insert(insert_pos, *new_diag);
|
||||||
|
@ -703,10 +696,9 @@ static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDi
|
||||||
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
||||||
|
|
||||||
AssDialogue *first = nullptr;
|
AssDialogue *first = nullptr;
|
||||||
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ) {
|
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ) {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it++);
|
AssDialogue *diag = &*it++;
|
||||||
if (!diag || !sel.count(diag))
|
if (!sel.count(diag)) continue;
|
||||||
continue;
|
|
||||||
if (!first) {
|
if (!first) {
|
||||||
first = diag;
|
first = diag;
|
||||||
continue;
|
continue;
|
||||||
|
@ -772,7 +764,7 @@ static bool try_paste_lines(agi::Context *c) {
|
||||||
boost::trim_left(data);
|
boost::trim_left(data);
|
||||||
if (!boost::starts_with(data, "Dialogue:")) return false;
|
if (!boost::starts_with(data, "Dialogue:")) return false;
|
||||||
|
|
||||||
EntryList parsed;
|
EntryList<AssDialogue> parsed;
|
||||||
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)) {
|
||||||
boost::trim(curdata);
|
boost::trim(curdata);
|
||||||
|
@ -780,15 +772,15 @@ static bool try_paste_lines(agi::Context *c) {
|
||||||
parsed.push_back(*new AssDialogue(curdata));
|
parsed.push_back(*new AssDialogue(curdata));
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
parsed.clear_and_dispose([](AssEntry *e) { delete e; });
|
parsed.clear_and_dispose([](AssDialogue *e) { delete e; });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssDialogue *new_active = static_cast<AssDialogue *>(&*parsed.begin());
|
AssDialogue *new_active = &*parsed.begin();
|
||||||
SubtitleSelection new_selection;
|
SubtitleSelection new_selection;
|
||||||
for (auto& line : parsed)
|
for (auto& line : parsed)
|
||||||
new_selection.insert(static_cast<AssDialogue *>(&line));
|
new_selection.insert(&line);
|
||||||
|
|
||||||
auto pos = c->ass->Events.iterator_to(*c->selectionController->GetActiveLine());
|
auto pos = c->ass->Events.iterator_to(*c->selectionController->GetActiveLine());
|
||||||
c->ass->Events.splice(pos, parsed, parsed.begin(), parsed.end());
|
c->ass->Events.splice(pos, parsed, parsed.begin(), parsed.end());
|
||||||
|
@ -858,9 +850,9 @@ struct edit_line_paste_over : public Command {
|
||||||
std::unique_ptr<AssDialogue> deleter(new_line);
|
std::unique_ptr<AssDialogue> deleter(new_line);
|
||||||
if (pos == c->ass->Events.end()) return nullptr;
|
if (pos == c->ass->Events.end()) return nullptr;
|
||||||
|
|
||||||
AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, static_cast<AssDialogue*>(&*pos));
|
AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, &*pos);
|
||||||
if (ret)
|
if (ret)
|
||||||
pos = find_if(next(pos), c->ass->Events.end(), cast<AssDialogue*>());
|
++pos;
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -871,8 +863,8 @@ struct edit_line_paste_over : public Command {
|
||||||
std::vector<AssDialogue*> sorted_selection;
|
std::vector<AssDialogue*> sorted_selection;
|
||||||
sorted_selection.reserve(sel.size());
|
sorted_selection.reserve(sel.size());
|
||||||
for (auto& line : c->ass->Events) {
|
for (auto& line : c->ass->Events) {
|
||||||
if (sel.count(static_cast<AssDialogue*>(&line)))
|
if (sel.count(&line))
|
||||||
sorted_selection.push_back(static_cast<AssDialogue*>(&line));
|
sorted_selection.push_back(&line);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pos = begin(sorted_selection);
|
auto pos = begin(sorted_selection);
|
||||||
|
@ -936,7 +928,10 @@ struct edit_line_recombine : public validate_sel_multiple {
|
||||||
auto active_line = c->selectionController->GetActiveLine();
|
auto active_line = c->selectionController->GetActiveLine();
|
||||||
|
|
||||||
std::vector<AssDialogue*> sel(sel_set.begin(), sel_set.end());
|
std::vector<AssDialogue*> sel(sel_set.begin(), sel_set.end());
|
||||||
boost::sort(sel, &AssFile::CompStart);
|
boost::sort(sel, [](const AssDialogue *a, const AssDialogue *b) {
|
||||||
|
return a->Start < b->Start;
|
||||||
|
});
|
||||||
|
|
||||||
for (auto &diag : sel)
|
for (auto &diag : sel)
|
||||||
diag->Text = trim_text(diag->Text);
|
diag->Text = trim_text(diag->Text);
|
||||||
|
|
||||||
|
@ -983,7 +978,7 @@ struct edit_line_recombine : public validate_sel_multiple {
|
||||||
|
|
||||||
// Remove now non-existent lines from the selection
|
// Remove now non-existent lines from the selection
|
||||||
SubtitleSelection lines, new_sel;
|
SubtitleSelection lines, new_sel;
|
||||||
boost::copy(c->ass->Events | agi::of_type<AssDialogue>(), 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()));
|
||||||
|
|
||||||
if (new_sel.empty())
|
if (new_sel.empty())
|
||||||
|
|
|
@ -79,7 +79,7 @@ struct grid_line_next_create : public Command {
|
||||||
newline->End = cur->End + OPT_GET("Timing/Default Duration")->GetInt();
|
newline->End = cur->End + OPT_GET("Timing/Default Duration")->GetInt();
|
||||||
newline->Style = cur->Style;
|
newline->Style = cur->Style;
|
||||||
|
|
||||||
entryIter pos = c->ass->Events.iterator_to(*cur);
|
auto pos = c->ass->Events.iterator_to(*cur);
|
||||||
c->ass->Events.insert(++pos, *newline);
|
c->ass->Events.insert(++pos, *newline);
|
||||||
c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
|
c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
|
||||||
c->selectionController->NextLine();
|
c->selectionController->NextLine();
|
||||||
|
@ -328,9 +328,7 @@ static bool move_one(T begin, T end, U const& to_move, int step) {
|
||||||
size_t move_count = 0;
|
size_t move_count = 0;
|
||||||
auto prev = end;
|
auto prev = end;
|
||||||
for (auto it = begin; it != end; std::advance(it, step)) {
|
for (auto it = begin; it != end; std::advance(it, step)) {
|
||||||
auto cur = dynamic_cast<typename U::key_type>(&*it);
|
auto cur = &*it;
|
||||||
if (!cur) continue;
|
|
||||||
|
|
||||||
if (!to_move.count(cur))
|
if (!to_move.count(cur))
|
||||||
prev = it;
|
prev = it;
|
||||||
else if (prev != end) {
|
else if (prev != end) {
|
||||||
|
|
|
@ -53,10 +53,11 @@
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
#include "../video_context.h"
|
#include "../video_context.h"
|
||||||
|
|
||||||
|
#include <libaegisub/address_of_adaptor.h>
|
||||||
#include <libaegisub/charset_conv.h>
|
#include <libaegisub/charset_conv.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
#include <boost/range/algorithm.hpp>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
#include <wx/choicdlg.h>
|
#include <wx/choicdlg.h>
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ static void insert_subtitle_at_video(agi::Context *c, bool after) {
|
||||||
def->End = video_ms + OPT_GET("Timing/Default Duration")->GetInt();
|
def->End = video_ms + OPT_GET("Timing/Default Duration")->GetInt();
|
||||||
def->Style = c->selectionController->GetActiveLine()->Style;
|
def->Style = c->selectionController->GetActiveLine()->Style;
|
||||||
|
|
||||||
entryIter pos = c->ass->Events.iterator_to(*c->selectionController->GetActiveLine());
|
auto pos = c->ass->Events.iterator_to(*c->selectionController->GetActiveLine());
|
||||||
if (after) ++pos;
|
if (after) ++pos;
|
||||||
|
|
||||||
c->ass->Events.insert(pos, *def);
|
c->ass->Events.insert(pos, *def);
|
||||||
|
@ -147,8 +148,8 @@ struct subtitle_insert_after : public validate_nonempty_selection {
|
||||||
new_line->Start = active_line->End;
|
new_line->Start = active_line->End;
|
||||||
new_line->End = new_line->Start + OPT_GET("Timing/Default Duration")->GetInt();
|
new_line->End = new_line->Start + OPT_GET("Timing/Default Duration")->GetInt();
|
||||||
|
|
||||||
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
||||||
AssDialogue *diag = static_cast<AssDialogue*>(&*it);
|
AssDialogue *diag = &*it;
|
||||||
|
|
||||||
// Limit the line to the available time
|
// Limit the line to the available time
|
||||||
if (diag->Start >= new_line->Start)
|
if (diag->Start >= new_line->Start)
|
||||||
|
@ -192,8 +193,8 @@ struct subtitle_insert_before : public validate_nonempty_selection {
|
||||||
new_line->End = active_line->Start;
|
new_line->End = active_line->Start;
|
||||||
new_line->Start = new_line->End - OPT_GET("Timing/Default Duration")->GetInt();
|
new_line->Start = new_line->End - OPT_GET("Timing/Default Duration")->GetInt();
|
||||||
|
|
||||||
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
|
||||||
auto diag = static_cast<AssDialogue*>(&*it);
|
auto diag = &*it;
|
||||||
|
|
||||||
// Limit the line to the available time
|
// Limit the line to the available time
|
||||||
if (diag->End <= new_line->End)
|
if (diag->End <= new_line->End)
|
||||||
|
@ -372,9 +373,7 @@ struct subtitle_select_all : public Command {
|
||||||
|
|
||||||
void operator()(agi::Context *c) override {
|
void operator()(agi::Context *c) override {
|
||||||
SubtitleSelection sel;
|
SubtitleSelection sel;
|
||||||
transform(c->ass->Events.begin(), c->ass->Events.end(),
|
boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
|
||||||
inserter(sel, sel.begin()), cast<AssDialogue*>());
|
|
||||||
sel.erase(nullptr);
|
|
||||||
c->selectionController->SetSelectedSet(sel);
|
c->selectionController->SetSelectedSet(sel);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -394,13 +393,13 @@ struct subtitle_select_visible : public Command {
|
||||||
SubtitleSelectionController::Selection new_selection;
|
SubtitleSelectionController::Selection new_selection;
|
||||||
int frame = c->videoController->GetFrameN();
|
int frame = c->videoController->GetFrameN();
|
||||||
|
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (c->videoController->FrameAtTime(diag->Start, agi::vfr::START) <= frame &&
|
if (c->videoController->FrameAtTime(diag.Start, agi::vfr::START) <= frame &&
|
||||||
c->videoController->FrameAtTime(diag->End, agi::vfr::END) >= frame)
|
c->videoController->FrameAtTime(diag.End, agi::vfr::END) >= frame)
|
||||||
{
|
{
|
||||||
if (new_selection.empty())
|
if (new_selection.empty())
|
||||||
c->selectionController->SetActiveLine(diag);
|
c->selectionController->SetActiveLine(&diag);
|
||||||
new_selection.insert(diag);
|
new_selection.insert(&diag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
#include "../selection_controller.h"
|
#include "../selection_controller.h"
|
||||||
#include "../video_context.h"
|
#include "../video_context.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -67,8 +66,8 @@ namespace {
|
||||||
if (sel.size() < 2) return !sel.empty();
|
if (sel.size() < 2) return !sel.empty();
|
||||||
|
|
||||||
size_t found = 0;
|
size_t found = 0;
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (sel.count(diag)) {
|
if (sel.count(&diag)) {
|
||||||
if (++found == sel.size())
|
if (++found == sel.size())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -84,14 +83,14 @@ static void adjoin_lines(agi::Context *c, bool set_start) {
|
||||||
AssDialogue *prev = nullptr;
|
AssDialogue *prev = nullptr;
|
||||||
size_t seen = 0;
|
size_t seen = 0;
|
||||||
bool prev_sel = false;
|
bool prev_sel = false;
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto diag : c->ass->Events) {
|
||||||
bool cur_sel = !!sel.count(diag);
|
bool cur_sel = !!sel.count(&diag);
|
||||||
if (prev) {
|
if (prev) {
|
||||||
// One row selections act as if the previous or next line was selected
|
// One row selections act as if the previous or next line was selected
|
||||||
if (set_start && cur_sel && (sel.size() == 1 || prev_sel))
|
if (set_start && cur_sel && (sel.size() == 1 || prev_sel))
|
||||||
diag->Start = prev->End;
|
diag.Start = prev->End;
|
||||||
else if (!set_start && prev_sel && (cur_sel || sel.size() == 1))
|
else if (!set_start && prev_sel && (cur_sel || sel.size() == 1))
|
||||||
prev->End = diag->Start;
|
prev->End = diag.Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seen == sel.size())
|
if (seen == sel.size())
|
||||||
|
@ -100,7 +99,7 @@ static void adjoin_lines(agi::Context *c, bool set_start) {
|
||||||
if (cur_sel)
|
if (cur_sel)
|
||||||
++seen;
|
++seen;
|
||||||
|
|
||||||
prev = diag;
|
prev = &diag;
|
||||||
prev_sel = cur_sel;
|
prev_sel = cur_sel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,6 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
DialogAttachments::DialogAttachments(wxWindow *parent, AssFile *ass)
|
DialogAttachments::DialogAttachments(wxWindow *parent, AssFile *ass)
|
||||||
: wxDialog(parent, -1, _("Attachment List"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
: wxDialog(parent, -1, _("Attachment List"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||||
, ass(ass)
|
, ass(ass)
|
||||||
|
@ -99,12 +97,12 @@ void DialogAttachments::UpdateList() {
|
||||||
listView->InsertColumn(1, _("Size"), wxLIST_FORMAT_LEFT, 100);
|
listView->InsertColumn(1, _("Size"), wxLIST_FORMAT_LEFT, 100);
|
||||||
listView->InsertColumn(2, _("Group"), wxLIST_FORMAT_LEFT, 100);
|
listView->InsertColumn(2, _("Group"), wxLIST_FORMAT_LEFT, 100);
|
||||||
|
|
||||||
for (auto attach : ass->Attachments | agi::of_type<AssAttachment>()) {
|
for (auto& attach : ass->Attachments) {
|
||||||
int row = listView->GetItemCount();
|
int row = listView->GetItemCount();
|
||||||
listView->InsertItem(row, to_wx(attach->GetFileName(true)));
|
listView->InsertItem(row, to_wx(attach.GetFileName(true)));
|
||||||
listView->SetItem(row, 1, PrettySize(attach->GetSize()));
|
listView->SetItem(row, 1, PrettySize(attach.GetSize()));
|
||||||
listView->SetItem(row, 2, to_wx(attach->GroupHeader()));
|
listView->SetItem(row, 2, to_wx(attach.GroupHeader()));
|
||||||
listView->SetItemPtrData(row, wxPtrToUInt(attach));
|
listView->SetItemPtrData(row, wxPtrToUInt(&attach));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -601,8 +601,8 @@ void DialogKanjiTimer::OnAccept(wxCommandEvent &) {
|
||||||
|
|
||||||
if (display->GetRemainingSource() > 0)
|
if (display->GetRemainingSource() > 0)
|
||||||
wxMessageBox(_("Group all of the source text."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
wxMessageBox(_("Group all of the source text."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
||||||
else if (AssDialogue *destLine = dynamic_cast<AssDialogue*>(currentDestinationLine)) {
|
else {
|
||||||
LinesToChange.push_back(std::make_pair(destLine, display->GetOutputLine()));
|
LinesToChange.push_back(std::make_pair(currentDestinationLine, display->GetOutputLine()));
|
||||||
|
|
||||||
currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
|
currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
|
||||||
currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
|
currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
|
||||||
|
@ -644,21 +644,13 @@ void DialogKanjiTimer::OnKeyDown(wxKeyEvent &event) {
|
||||||
|
|
||||||
void DialogKanjiTimer::ResetForNewLine()
|
void DialogKanjiTimer::ResetForNewLine()
|
||||||
{
|
{
|
||||||
AssDialogue *src = nullptr;
|
if (!currentSourceLine || !currentDestinationLine)
|
||||||
AssDialogue *dst = nullptr;
|
|
||||||
|
|
||||||
if (currentSourceLine)
|
|
||||||
src = dynamic_cast<AssDialogue*>(currentSourceLine);
|
|
||||||
if (currentDestinationLine)
|
|
||||||
dst = dynamic_cast<AssDialogue*>(currentDestinationLine);
|
|
||||||
|
|
||||||
if (!src || !dst)
|
|
||||||
{
|
{
|
||||||
src = dst = nullptr;
|
currentSourceLine = currentDestinationLine = nullptr;
|
||||||
wxBell();
|
wxBell();
|
||||||
}
|
}
|
||||||
|
|
||||||
display->SetInputData(src, dst);
|
display->SetInputData(currentSourceLine, currentDestinationLine);
|
||||||
|
|
||||||
TryAutoMatch();
|
TryAutoMatch();
|
||||||
|
|
||||||
|
@ -672,25 +664,24 @@ void DialogKanjiTimer::TryAutoMatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Iterator>
|
template<typename Iterator>
|
||||||
static AssEntry *find_next(Iterator from, Iterator to, std::string const& style_name) {
|
static AssDialogue *find_next(Iterator from, Iterator to, std::string const& style_name) {
|
||||||
for (; from != to; ++from)
|
for (; from != to; ++from)
|
||||||
{
|
{
|
||||||
AssDialogue *dlg = dynamic_cast<AssDialogue*>(&*from);
|
if (from->Style == style_name && !from->Text.get().empty())
|
||||||
if (dlg && dlg->Style == style_name && !dlg->Text.get().empty())
|
return &*from;
|
||||||
return dlg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssEntry *DialogKanjiTimer::FindNextStyleMatch(AssEntry *search_from, const std::string &search_style)
|
AssDialogue *DialogKanjiTimer::FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style)
|
||||||
{
|
{
|
||||||
if (!search_from) return search_from;
|
if (!search_from) return search_from;
|
||||||
return find_next(++subs->Events.iterator_to(*search_from), subs->Events.end(), search_style);
|
return find_next(++subs->Events.iterator_to(*search_from), subs->Events.end(), search_style);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssEntry *DialogKanjiTimer::FindPrevStyleMatch(AssEntry *search_from, const std::string &search_style)
|
AssDialogue *DialogKanjiTimer::FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style)
|
||||||
{
|
{
|
||||||
if (!search_from) return search_from;
|
if (!search_from) return search_from;
|
||||||
return find_next(EntryList::reverse_iterator(subs->Events.iterator_to(*search_from)), subs->Events.rend(), search_style);
|
return find_next(EntryList<AssDialogue>::reverse_iterator(subs->Events.iterator_to(*search_from)), subs->Events.rend(), search_style);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
|
|
||||||
namespace agi { struct Context; }
|
namespace agi { struct Context; }
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
class AssEntry;
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class KaraokeLineMatchDisplay;
|
class KaraokeLineMatchDisplay;
|
||||||
class wxComboBox;
|
class wxComboBox;
|
||||||
|
@ -55,8 +54,8 @@ class DialogKanjiTimer : public wxDialog {
|
||||||
|
|
||||||
std::vector<std::pair<AssDialogue*, std::string>> LinesToChange;
|
std::vector<std::pair<AssDialogue*, std::string>> LinesToChange;
|
||||||
|
|
||||||
AssEntry *currentSourceLine = nullptr;
|
AssDialogue *currentSourceLine = nullptr;
|
||||||
AssEntry *currentDestinationLine = nullptr;
|
AssDialogue *currentDestinationLine = nullptr;
|
||||||
|
|
||||||
void OnClose(wxCommandEvent &event);
|
void OnClose(wxCommandEvent &event);
|
||||||
void OnStart(wxCommandEvent &event);
|
void OnStart(wxCommandEvent &event);
|
||||||
|
@ -71,8 +70,8 @@ class DialogKanjiTimer : public wxDialog {
|
||||||
void ResetForNewLine();
|
void ResetForNewLine();
|
||||||
void TryAutoMatch();
|
void TryAutoMatch();
|
||||||
|
|
||||||
AssEntry *FindNextStyleMatch(AssEntry *search_from, const std::string &search_style);
|
AssDialogue *FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style);
|
||||||
AssEntry *FindPrevStyleMatch(AssEntry *search_from, const std::string &search_style);
|
AssDialogue *FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DialogKanjiTimer(agi::Context *context);
|
DialogKanjiTimer(agi::Context *context);
|
||||||
|
|
|
@ -34,8 +34,6 @@
|
||||||
#include "selection_controller.h"
|
#include "selection_controller.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/find.hpp>
|
#include <boost/algorithm/string/find.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
@ -77,12 +75,12 @@ static std::set<AssDialogue*> process(std::string const& match_text, bool match_
|
||||||
auto predicate = SearchReplaceEngine::GetMatcher(settings);
|
auto predicate = SearchReplaceEngine::GetMatcher(settings);
|
||||||
|
|
||||||
std::set<AssDialogue*> matches;
|
std::set<AssDialogue*> matches;
|
||||||
for (auto diag : ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : ass->Events) {
|
||||||
if (diag->Comment && !comments) continue;
|
if (diag.Comment && !comments) continue;
|
||||||
if (!diag->Comment && !dialogue) continue;
|
if (!diag.Comment && !dialogue) continue;
|
||||||
|
|
||||||
if (invert != predicate(diag, 0))
|
if (invert != predicate(&diag, 0))
|
||||||
matches.insert(diag);
|
matches.insert(&diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/io.h>
|
#include <libaegisub/io.h>
|
||||||
#include <libaegisub/log.h>
|
#include <libaegisub/log.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/path.h>
|
#include <libaegisub/path.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
@ -354,10 +353,10 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
|
||||||
int block_start = 0;
|
int block_start = 0;
|
||||||
json::Array shifted_blocks;
|
json::Array shifted_blocks;
|
||||||
|
|
||||||
for (auto line : context->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& line : context->ass->Events) {
|
||||||
++row_number;
|
++row_number;
|
||||||
|
|
||||||
if (!sel.count(line)) {
|
if (!sel.count(&line)) {
|
||||||
if (block_start) {
|
if (block_start) {
|
||||||
json::Object block;
|
json::Object block;
|
||||||
block["start"] = block_start;
|
block["start"] = block_start;
|
||||||
|
@ -372,9 +371,9 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
|
||||||
block_start = row_number;
|
block_start = row_number;
|
||||||
|
|
||||||
if (start)
|
if (start)
|
||||||
line->Start = Shift(line->Start, shift, by_time, agi::vfr::START);
|
line.Start = Shift(line.Start, shift, by_time, agi::vfr::START);
|
||||||
if (end)
|
if (end)
|
||||||
line->End = Shift(line->End, shift, by_time, agi::vfr::END);
|
line.End = Shift(line.End, shift, by_time, agi::vfr::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
context->ass->Commit(_("shifting"), AssFile::COMMIT_DIAG_TIME);
|
context->ass->Commit(_("shifting"), AssFile::COMMIT_DIAG_TIME);
|
||||||
|
|
|
@ -212,19 +212,17 @@ bool DialogSpellChecker::FindNext() {
|
||||||
if (CheckLine(active_line, start_pos, &commit_id))
|
if (CheckLine(active_line, start_pos, &commit_id))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
entryIter it = context->ass->Events.iterator_to(*active_line);
|
auto it = context->ass->Events.iterator_to(*active_line);
|
||||||
|
|
||||||
// Note that it is deliberate that the start line is checked twice, as if
|
// Note that it is deliberate that the start line is checked twice, as if
|
||||||
// the cursor is past the first misspelled word in the current line, that
|
// the cursor is past the first misspelled word in the current line, that
|
||||||
// word should be hit last
|
// word should be hit last
|
||||||
while(!has_looped || active_line != start_line) {
|
while(!has_looped || active_line != start_line) {
|
||||||
do {
|
// Wrap around to the beginning if we hit the end
|
||||||
// Wrap around to the beginning if we hit the end
|
if (++it == context->ass->Events.end()) {
|
||||||
if (++it == context->ass->Events.end()) {
|
it = context->ass->Events.begin();
|
||||||
it = context->ass->Events.begin();
|
has_looped = true;
|
||||||
has_looped = true;
|
}
|
||||||
}
|
|
||||||
} while (!(active_line = dynamic_cast<AssDialogue*>(&*it)));
|
|
||||||
|
|
||||||
if (CheckLine(active_line, 0, &commit_id))
|
if (CheckLine(active_line, 0, &commit_id))
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -86,19 +86,19 @@ class StyleRenamer {
|
||||||
found_any = false;
|
found_any = false;
|
||||||
do_replace = replace;
|
do_replace = replace;
|
||||||
|
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (diag->Style == source_name) {
|
if (diag.Style == source_name) {
|
||||||
if (replace)
|
if (replace)
|
||||||
diag->Style = new_name;
|
diag.Style = new_name;
|
||||||
else
|
else
|
||||||
found_any = true;
|
found_any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
|
boost::ptr_vector<AssDialogueBlock> blocks(diag.ParseTags());
|
||||||
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
||||||
block->ProcessParameters(&StyleRenamer::ProcessTag, this);
|
block->ProcessParameters(&StyleRenamer::ProcessTag, this);
|
||||||
if (replace)
|
if (replace)
|
||||||
diag->UpdateText(blocks);
|
diag.UpdateText(blocks);
|
||||||
|
|
||||||
if (found_any) return;
|
if (found_any) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
|
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/path.h>
|
#include <libaegisub/path.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/split.h>
|
#include <libaegisub/split.h>
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
@ -288,9 +287,9 @@ void DialogStyleManager::LoadCurrentStyles(int commit_type) {
|
||||||
CurrentList->Clear();
|
CurrentList->Clear();
|
||||||
styleMap.clear();
|
styleMap.clear();
|
||||||
|
|
||||||
for (auto style : c->ass->Styles | agi::of_type<AssStyle>()) {
|
for (auto& style : c->ass->Styles) {
|
||||||
CurrentList->Append(to_wx(style->name));
|
CurrentList->Append(to_wx(style.name));
|
||||||
styleMap.push_back(style);
|
styleMap.push_back(&style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,10 +779,8 @@ void DialogStyleManager::MoveStyles(bool storage, int type) {
|
||||||
// Replace styles
|
// Replace styles
|
||||||
size_t curn = 0;
|
size_t curn = 0;
|
||||||
for (auto it = c->ass->Styles.begin(); it != c->ass->Styles.end(); ++it) {
|
for (auto it = c->ass->Styles.begin(); it != c->ass->Styles.end(); ++it) {
|
||||||
if (!dynamic_cast<AssStyle*>(&*it)) continue;
|
|
||||||
|
|
||||||
auto new_style_at_pos = c->ass->Styles.iterator_to(*styleMap[curn]);
|
auto new_style_at_pos = c->ass->Styles.iterator_to(*styleMap[curn]);
|
||||||
EntryList::node_algorithms::swap_nodes(it.pointed_node(), new_style_at_pos.pointed_node());
|
EntryList<AssStyle>::node_algorithms::swap_nodes(it.pointed_node(), new_style_at_pos.pointed_node());
|
||||||
if (++curn == styleMap.size()) break;
|
if (++curn == styleMap.size()) break;
|
||||||
it = new_style_at_pos;
|
it = new_style_at_pos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,12 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
|
#include <libaegisub/address_of_adaptor.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <boost/range/adaptor/filtered.hpp>
|
||||||
#include <boost/range/algorithm.hpp>
|
#include <boost/range/algorithm.hpp>
|
||||||
|
#include <boost/range/algorithm_ext.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
|
@ -63,6 +67,8 @@
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/valnum.h>
|
#include <wx/valnum.h>
|
||||||
|
|
||||||
|
using namespace boost::adaptors;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
|
||||||
|
@ -288,10 +294,6 @@ void DialogTimingProcessor::OnApply(wxCommandEvent &) {
|
||||||
EndModal(0);
|
EndModal(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bad_line(std::set<std::string> *styles, AssDialogue *d) {
|
|
||||||
return !d || d->Comment || styles->find(d->Style) == styles->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
|
std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
|
||||||
std::set<std::string> styles;
|
std::set<std::string> styles;
|
||||||
for (size_t i = 0; i < StyleList->GetCount(); ++i) {
|
for (size_t i = 0; i < StyleList->GetCount(); ++i) {
|
||||||
|
@ -301,14 +303,13 @@ std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
|
||||||
|
|
||||||
std::vector<AssDialogue*> sorted;
|
std::vector<AssDialogue*> sorted;
|
||||||
|
|
||||||
if (onlySelection->IsChecked()) {
|
auto valid_line = [&](const AssDialogue *d) { return !d->Comment && styles.count(d->Style); };
|
||||||
SubtitleSelection sel = c->selectionController->GetSelectedSet();
|
if (onlySelection->IsChecked())
|
||||||
copy_if(sel.begin(), sel.end(), back_inserter(sorted),
|
boost::copy(c->selectionController->GetSelectedSet() | filtered(valid_line),
|
||||||
[&](AssDialogue *d) { return !d->Comment && styles.count(d->Style); });
|
back_inserter(sorted));
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
transform(c->ass->Events.begin(), c->ass->Events.end(), back_inserter(sorted), cast<AssDialogue*>());
|
sorted.reserve(c->ass->Events.size());
|
||||||
sorted.erase(boost::remove_if(sorted, bind(bad_line, &styles, _1)), sorted.end());
|
boost::push_back(sorted, c->ass->Events | agi::address_of | filtered(valid_line));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if rows are valid
|
// Check if rows are valid
|
||||||
|
@ -324,7 +325,9 @@ std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::sort(sorted, AssFile::CompStart);
|
boost::sort(sorted, [](const AssDialogue *a, const AssDialogue *b) {
|
||||||
|
return a->Start < b->Start;
|
||||||
|
});
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,8 @@
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <functional>
|
|
||||||
#include <wx/intl.h>
|
#include <wx/intl.h>
|
||||||
|
|
||||||
AssFixStylesFilter::AssFixStylesFilter()
|
AssFixStylesFilter::AssFixStylesFilter()
|
||||||
|
@ -53,12 +50,12 @@ AssFixStylesFilter::AssFixStylesFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFixStylesFilter::ProcessSubs(AssFile *subs, wxWindow *) {
|
void AssFixStylesFilter::ProcessSubs(AssFile *subs, wxWindow *) {
|
||||||
std::vector<std::string> styles = subs->GetStyles();
|
auto styles = subs->GetStyles();
|
||||||
for_each(begin(styles), end(styles), [](std::string& str) { boost::to_lower(str); });
|
for (auto& str : styles) boost::to_lower(str);
|
||||||
sort(begin(styles), end(styles));
|
sort(begin(styles), end(styles));
|
||||||
|
|
||||||
for (auto diag : subs->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : subs->Events) {
|
||||||
if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag->Style.get())))
|
if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag.Style.get())))
|
||||||
diag->Style = "Default";
|
diag.Style = "Default";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,20 +201,20 @@ void AssTransformFramerateFilter::TransformTimeTags(std::string const& name, Ass
|
||||||
|
|
||||||
void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs) {
|
void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs) {
|
||||||
if (!Input->IsLoaded() || !Output->IsLoaded()) return;
|
if (!Input->IsLoaded() || !Output->IsLoaded()) return;
|
||||||
for (auto curDialogue : subs->Events | agi::of_type<AssDialogue>()) {
|
for (auto& curDialogue : subs->Events) {
|
||||||
line = curDialogue;
|
line = &curDialogue;
|
||||||
newK = 0;
|
newK = 0;
|
||||||
oldK = 0;
|
oldK = 0;
|
||||||
newStart = trunc_cs(ConvertTime(curDialogue->Start));
|
newStart = trunc_cs(ConvertTime(curDialogue.Start));
|
||||||
newEnd = trunc_cs(ConvertTime(curDialogue->End) + 9);
|
newEnd = trunc_cs(ConvertTime(curDialogue.End) + 9);
|
||||||
|
|
||||||
// Process stuff
|
// Process stuff
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks;
|
boost::ptr_vector<AssDialogueBlock> blocks;
|
||||||
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
||||||
block->ProcessParameters(TransformTimeTags, this);
|
block->ProcessParameters(TransformTimeTags, this);
|
||||||
curDialogue->Start = newStart;
|
curDialogue.Start = newStart;
|
||||||
curDialogue->End = newEnd;
|
curDialogue.End = newEnd;
|
||||||
curDialogue->UpdateText(blocks);
|
curDialogue.UpdateText(blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,6 @@
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <unicode/uchar.h>
|
#include <unicode/uchar.h>
|
||||||
|
@ -182,17 +180,17 @@ std::vector<agi::fs::path> FontCollector::GetFontPaths(const AssFile *file) {
|
||||||
|
|
||||||
status_callback(_("Parsing file\n"), 0);
|
status_callback(_("Parsing file\n"), 0);
|
||||||
|
|
||||||
for (auto style : file->Styles | agi::of_type<const AssStyle>()) {
|
for (auto const& style : file->Styles) {
|
||||||
StyleInfo &info = styles[style->name];
|
StyleInfo &info = styles[style.name];
|
||||||
info.facename = style->font;
|
info.facename = style.font;
|
||||||
info.bold = style->bold;
|
info.bold = style.bold;
|
||||||
info.italic = style->italic;
|
info.italic = style.italic;
|
||||||
used_styles[info].styles.insert(style->name);
|
used_styles[info].styles.insert(style.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (auto diag : file->Events | agi::of_type<const AssDialogue>())
|
for (auto const& diag : file->Events)
|
||||||
ProcessDialogueLine(diag, ++index);
|
ProcessDialogueLine(&diag, ++index);
|
||||||
|
|
||||||
status_callback(_("Searching for font files\n"), 0);
|
status_callback(_("Searching for font files\n"), 0);
|
||||||
for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1));
|
for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1));
|
||||||
|
|
|
@ -125,32 +125,33 @@ namespace {
|
||||||
cur->Set<int>((cur->Get<int>() + shift) * resizer + 0.5);
|
cur->Set<int>((cur->Get<int>() + shift) * resizer + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resample_line(resample_state *state, AssEntry &line) {
|
void resample_line(resample_state *state, AssDialogue &diag) {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&line);
|
if (diag.Comment && (boost::starts_with(diag.Effect.get(), "template") || boost::starts_with(diag.Effect.get(), "code")))
|
||||||
if (diag && !(diag->Comment && (boost::starts_with(diag->Effect.get(), "template") || boost::starts_with(diag->Effect.get(), "code")))) {
|
return;
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
|
|
||||||
|
|
||||||
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
boost::ptr_vector<AssDialogueBlock> blocks(diag.ParseTags());
|
||||||
block->ProcessParameters(resample_tags, state);
|
|
||||||
|
|
||||||
for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
|
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
||||||
drawing->text = transform_drawing(drawing->text, state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
|
block->ProcessParameters(resample_tags, state);
|
||||||
|
|
||||||
for (size_t i = 0; i < 3; ++i)
|
for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
|
||||||
diag->Margin[i] = int((diag->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
drawing->text = transform_drawing(drawing->text, state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
|
||||||
|
|
||||||
diag->UpdateText(blocks);
|
for (size_t i = 0; i < 3; ++i)
|
||||||
}
|
diag.Margin[i] = int((diag.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
||||||
else if (AssStyle *style = dynamic_cast<AssStyle*>(&line)) {
|
|
||||||
style->fontsize = int(style->fontsize * state->ry + 0.5);
|
diag.UpdateText(blocks);
|
||||||
style->outline_w *= state->ry;
|
}
|
||||||
style->shadow_w *= state->ry;
|
|
||||||
style->spacing *= state->rx;
|
void resample_style(resample_state *state, AssStyle &style) {
|
||||||
style->scalex *= state->ar;
|
style.fontsize = int(style.fontsize * state->ry + 0.5);
|
||||||
for (int i = 0; i < 3; i++)
|
style.outline_w *= state->ry;
|
||||||
style->Margin[i] = int((style->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
style.shadow_w *= state->ry;
|
||||||
style->UpdateData();
|
style.spacing *= state->rx;
|
||||||
}
|
style.scalex *= state->ar;
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
|
||||||
|
style.UpdateData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +174,7 @@ void ResampleResolution(AssFile *ass, ResampleSettings const& settings) {
|
||||||
state.ar = state.rx / state.ry;
|
state.ar = state.rx / state.ry;
|
||||||
|
|
||||||
for (auto& line : ass->Styles)
|
for (auto& line : ass->Styles)
|
||||||
resample_line(&state, line);
|
resample_style(&state, line);
|
||||||
for (auto& line : ass->Events)
|
for (auto& line : ass->Events)
|
||||||
resample_line(&state, line);
|
resample_line(&state, line);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "selection_controller.h"
|
#include "selection_controller.h"
|
||||||
#include "text_selection_controller.h"
|
#include "text_selection_controller.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <boost/locale.hpp>
|
#include <boost/locale.hpp>
|
||||||
|
@ -269,17 +268,15 @@ bool SearchReplaceEngine::FindReplace(bool replace) {
|
||||||
bool selection_only = sel.size() > 1 && settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
|
bool selection_only = sel.size() > 1 && settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it);
|
if (selection_only && !sel.count(&*it)) continue;
|
||||||
if (!diag) continue;
|
if (settings.ignore_comments && it->Comment) continue;
|
||||||
if (selection_only && !sel.count(diag)) continue;
|
|
||||||
if (settings.ignore_comments && diag->Comment) continue;
|
|
||||||
|
|
||||||
if (MatchState ms = matches(diag, pos)) {
|
if (MatchState ms = matches(&*it, pos)) {
|
||||||
if (selection_only)
|
if (selection_only)
|
||||||
// We're cycling through the selection, so don't muck with it
|
// We're cycling through the selection, so don't muck with it
|
||||||
context->selectionController->SetActiveLine(diag);
|
context->selectionController->SetActiveLine(&*it);
|
||||||
else
|
else
|
||||||
context->selectionController->SetSelectionAndActive({ diag }, diag);
|
context->selectionController->SetSelectionAndActive({ &*it }, &*it);
|
||||||
|
|
||||||
if (settings.field == SearchReplaceSettings::Field::TEXT)
|
if (settings.field == SearchReplaceSettings::Field::TEXT)
|
||||||
context->textSelectionController->SetSelection(ms.start, ms.end);
|
context->textSelectionController->SetSelection(ms.start, ms.end);
|
||||||
|
@ -307,13 +304,13 @@ bool SearchReplaceEngine::ReplaceAll() {
|
||||||
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
|
SubtitleSelection 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 | agi::of_type<AssDialogue>()) {
|
for (auto& diag : context->ass->Events) {
|
||||||
if (selection_only && !sel.count(diag)) continue;
|
if (selection_only && !sel.count(&diag)) continue;
|
||||||
if (settings.ignore_comments && diag->Comment) continue;
|
if (settings.ignore_comments && diag.Comment) continue;
|
||||||
|
|
||||||
if (settings.use_regex) {
|
if (settings.use_regex) {
|
||||||
if (MatchState ms = matches(diag, 0)) {
|
if (MatchState ms = matches(&diag, 0)) {
|
||||||
auto& diag_field = diag->*get_dialogue_field(settings.field);
|
auto& diag_field = diag.*get_dialogue_field(settings.field);
|
||||||
std::string const& text = diag_field.get();
|
std::string const& text = diag_field.get();
|
||||||
count += distance(
|
count += distance(
|
||||||
boost::u32regex_iterator<std::string::const_iterator>(begin(text), end(text), *ms.re),
|
boost::u32regex_iterator<std::string::const_iterator>(begin(text), end(text), *ms.re),
|
||||||
|
@ -324,9 +321,9 @@ bool SearchReplaceEngine::ReplaceAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
while (MatchState ms = matches(diag, pos)) {
|
while (MatchState ms = matches(&diag, pos)) {
|
||||||
++count;
|
++count;
|
||||||
Replace(diag, ms);
|
Replace(&diag, ms);
|
||||||
pos = ms.end;
|
pos = ms.end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,26 +70,22 @@ struct SubsController::UndoInfo {
|
||||||
: undo_description(d), commit_id(commit_id)
|
: undo_description(d), commit_id(commit_id)
|
||||||
{
|
{
|
||||||
script_info.reserve(c->ass->Info.size());
|
script_info.reserve(c->ass->Info.size());
|
||||||
for (auto const& line : c->ass->Info) {
|
for (auto const& info : c->ass->Info)
|
||||||
auto info = static_cast<const AssInfo *>(&line);
|
script_info.emplace_back(info.Key(), info.Value());
|
||||||
script_info.emplace_back(info->Key(), info->Value());
|
|
||||||
}
|
|
||||||
|
|
||||||
styles.reserve(c->ass->Styles.size());
|
styles.reserve(c->ass->Styles.size());
|
||||||
for (auto const& line : c->ass->Styles)
|
styles.assign(c->ass->Styles.begin(), c->ass->Styles.end());
|
||||||
styles.push_back(static_cast<AssStyle const&>(line));
|
|
||||||
|
|
||||||
events.reserve(c->ass->Events.size());
|
events.reserve(c->ass->Events.size());
|
||||||
for (auto const& line : c->ass->Events)
|
events.assign(c->ass->Events.begin(), c->ass->Events.end());
|
||||||
events.push_back(static_cast<AssDialogue const&>(line));
|
|
||||||
|
|
||||||
for (auto const& line : c->ass->Attachments) {
|
for (auto const& line : c->ass->Attachments) {
|
||||||
switch (line.Group()) {
|
switch (line.Group()) {
|
||||||
case AssEntryGroup::FONT:
|
case AssEntryGroup::FONT:
|
||||||
fonts.push_back(static_cast<AssAttachment const&>(line));
|
fonts.push_back(line);
|
||||||
break;
|
break;
|
||||||
case AssEntryGroup::GRAPHIC:
|
case AssEntryGroup::GRAPHIC:
|
||||||
graphics.push_back(static_cast<AssAttachment const&>(line));
|
graphics.push_back(line);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -364,10 +360,9 @@ void SubsController::OnCommit(AssFileCommit c) {
|
||||||
if (commit_id == *c.commit_id+1 && redo_stack.empty() && saved_commit_id+1 != commit_id && autosaved_commit_id+1 != commit_id) {
|
if (commit_id == *c.commit_id+1 && redo_stack.empty() && saved_commit_id+1 != commit_id && autosaved_commit_id+1 != commit_id) {
|
||||||
// If only one line changed just modify it instead of copying the file
|
// If only one line changed just modify it instead of copying the file
|
||||||
if (c.single_line && c.single_line->Group() == AssEntryGroup::DIALOGUE) {
|
if (c.single_line && c.single_line->Group() == AssEntryGroup::DIALOGUE) {
|
||||||
auto src_diag = static_cast<const AssDialogue *>(c.single_line);
|
|
||||||
for (auto& diag : undo_stack.back().events) {
|
for (auto& diag : undo_stack.back().events) {
|
||||||
if (diag.Id == src_diag->Id) {
|
if (diag.Id == c.single_line->Id) {
|
||||||
diag = *src_diag;
|
diag = *c.single_line;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
|
||||||
class AssDialogue;
|
class AssDialogue;
|
||||||
class AssEntry;
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
struct AssFileCommit;
|
struct AssFileCommit;
|
||||||
template<typename T> class SelectionController;
|
template<typename T> class SelectionController;
|
||||||
|
|
|
@ -56,7 +56,6 @@
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
#include <libaegisub/dispatch.h>
|
#include <libaegisub/dispatch.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -344,7 +343,7 @@ void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<std::string>
|
||||||
|
|
||||||
std::unordered_set<std::string> values;
|
std::unordered_set<std::string> values;
|
||||||
for (auto const& line : c->ass->Events) {
|
for (auto const& line : c->ass->Events) {
|
||||||
auto const& value = static_cast<const AssDialogue *>(&line)->*field;
|
auto const& value = line.*field;
|
||||||
if (!value.get().empty())
|
if (!value.get().empty())
|
||||||
values.insert(value);
|
values.insert(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
@ -94,13 +93,12 @@ bool SubtitleFormat::CanSave(const AssFile *subs) const {
|
||||||
|
|
||||||
std::string defstyle = AssStyle().GetEntryData();
|
std::string defstyle = AssStyle().GetEntryData();
|
||||||
for (auto const& line : subs->Styles) {
|
for (auto const& line : subs->Styles) {
|
||||||
if (static_cast<const AssStyle *>(&line)->GetEntryData() != defstyle)
|
if (line.GetEntryData() != defstyle)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& line : subs->Events) {
|
for (auto const& line : subs->Events) {
|
||||||
auto diag = static_cast<const AssDialogue *>(&line);
|
if (line.GetStrippedText() != line.Text)
|
||||||
if (diag->GetStrippedText() != diag->Text)
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,13 +171,13 @@ agi::vfr::Framerate SubtitleFormat::AskForFPS(bool allow_vfr, bool show_smpte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubtitleFormat::StripTags(AssFile &file) {
|
void SubtitleFormat::StripTags(AssFile &file) {
|
||||||
for (auto current : file.Events | agi::of_type<AssDialogue>())
|
for (auto& current : file.Events)
|
||||||
current->StripTags();
|
current.StripTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks) {
|
void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks) {
|
||||||
for (auto current : file.Events | agi::of_type<AssDialogue>()) {
|
for (auto& current : file.Events) {
|
||||||
std::string repl = current->Text;
|
std::string repl = current.Text;
|
||||||
boost::replace_all(repl, "\\h", " ");
|
boost::replace_all(repl, "\\h", " ");
|
||||||
boost::ireplace_all(repl, "\\n", newline);
|
boost::ireplace_all(repl, "\\n", newline);
|
||||||
if (mergeLineBreaks) {
|
if (mergeLineBreaks) {
|
||||||
|
@ -188,39 +186,35 @@ void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline,
|
||||||
while ((pos = repl.find(dbl, pos)) != std::string::npos)
|
while ((pos = repl.find(dbl, pos)) != std::string::npos)
|
||||||
boost::replace_all(repl, dbl, newline);
|
boost::replace_all(repl, dbl, newline);
|
||||||
}
|
}
|
||||||
current->Text = repl;
|
current.Text = repl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubtitleFormat::StripComments(AssFile &file) {
|
void SubtitleFormat::StripComments(AssFile &file) {
|
||||||
file.Events.remove_and_dispose_if([](AssEntry const& e) {
|
file.Events.remove_and_dispose_if([](AssDialogue const& diag) {
|
||||||
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
|
return diag.Comment || diag.Text.get().empty();
|
||||||
return diag && (diag->Comment || diag->Text.get().empty());
|
}, [](AssDialogue *e) { delete e; });
|
||||||
}, [](AssEntry *e) { delete e; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dialog_start_lt(AssEntry &pos, AssDialogue *to_insert) {
|
static bool dialog_start_lt(AssDialogue &pos, AssDialogue *to_insert) {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&pos);
|
return pos.Start > to_insert->Start;
|
||||||
return diag && diag->Start > to_insert->Start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Split and merge lines so there are no overlapping lines
|
/// @brief Split and merge lines so there are no overlapping lines
|
||||||
///
|
///
|
||||||
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
|
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
|
||||||
void SubtitleFormat::RecombineOverlaps(AssFile &file) {
|
void SubtitleFormat::RecombineOverlaps(AssFile &file) {
|
||||||
entryIter cur, next = file.Events.begin();
|
auto next = file.Events.begin();
|
||||||
cur = next++;
|
auto cur = next++;
|
||||||
|
|
||||||
for (; next != file.Events.end(); cur = next++) {
|
for (; next != file.Events.end(); cur = next++) {
|
||||||
AssDialogue *prevdlg = dynamic_cast<AssDialogue*>(&*cur);
|
AssDialogue *prevdlg = &*cur;
|
||||||
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(&*next);
|
AssDialogue *curdlg = &*next;
|
||||||
|
|
||||||
if (!curdlg || !prevdlg) continue;
|
|
||||||
if (prevdlg->End <= curdlg->Start) continue;
|
if (prevdlg->End <= curdlg->Start) continue;
|
||||||
|
|
||||||
// Use names like in the algorithm description and prepare for erasing
|
// Use names like in the algorithm description and prepare for erasing
|
||||||
// old dialogues from the list
|
// old dialogues from the list
|
||||||
entryIter prev = cur;
|
auto prev = cur;
|
||||||
cur = next;
|
cur = next;
|
||||||
next++;
|
next++;
|
||||||
|
|
||||||
|
@ -280,20 +274,17 @@ void SubtitleFormat::RecombineOverlaps(AssFile &file) {
|
||||||
|
|
||||||
/// @brief Merge identical lines that follow each other
|
/// @brief Merge identical lines that follow each other
|
||||||
void SubtitleFormat::MergeIdentical(AssFile &file) {
|
void SubtitleFormat::MergeIdentical(AssFile &file) {
|
||||||
entryIter cur, next = file.Events.begin();
|
auto next = file.Events.begin();
|
||||||
cur = next++;
|
auto cur = next++;
|
||||||
|
|
||||||
for (; next != file.Events.end(); cur = next++) {
|
for (; next != file.Events.end(); cur = next++) {
|
||||||
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(&*cur);
|
if (cur->End == next->Start && cur->Text == next->Text) {
|
||||||
AssDialogue *nextdlg = dynamic_cast<AssDialogue*>(&*next);
|
|
||||||
|
|
||||||
if (curdlg && nextdlg && curdlg->End == nextdlg->Start && curdlg->Text == nextdlg->Text) {
|
|
||||||
// Merge timing
|
// Merge timing
|
||||||
nextdlg->Start = std::min(nextdlg->Start, curdlg->Start);
|
next->Start = std::min(next->Start, cur->Start);
|
||||||
nextdlg->End = std::max(nextdlg->End, curdlg->End);
|
next->End = std::max(next->End, cur->End);
|
||||||
|
|
||||||
// Remove duplicate line
|
// Remove duplicate line
|
||||||
delete curdlg;
|
delete &*cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class AssEntry;
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
namespace agi { namespace vfr { class Framerate; } }
|
namespace agi { namespace vfr { class Framerate; } }
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,28 @@
|
||||||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
//
|
//
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file subtitle_format_ass.cpp
|
|
||||||
/// @brief Reading/writing of SSA-lineage subtitles
|
|
||||||
/// @ingroup subtitle_io
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "subtitle_format_ass.h"
|
#include "subtitle_format_ass.h"
|
||||||
|
|
||||||
|
#include "ass_attachment.h"
|
||||||
|
#include "ass_dialogue.h"
|
||||||
|
#include "ass_info.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
|
#include "ass_style.h"
|
||||||
#include "ass_parser.h"
|
#include "ass_parser.h"
|
||||||
#include "text_file_reader.h"
|
#include "text_file_reader.h"
|
||||||
#include "text_file_writer.h"
|
#include "text_file_writer.h"
|
||||||
|
@ -52,17 +38,11 @@ AssSubtitleFormat::AssSubtitleFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> AssSubtitleFormat::GetReadWildcards() const {
|
std::vector<std::string> AssSubtitleFormat::GetReadWildcards() const {
|
||||||
std::vector<std::string> formats;
|
return {"ass", "ssa"};
|
||||||
formats.push_back("ass");
|
|
||||||
formats.push_back("ssa");
|
|
||||||
return formats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> AssSubtitleFormat::GetWriteWildcards() const {
|
std::vector<std::string> AssSubtitleFormat::GetWriteWildcards() const {
|
||||||
std::vector<std::string> formats;
|
return {"ass", "ssa"};
|
||||||
formats.push_back("ass");
|
|
||||||
formats.push_back("ssa");
|
|
||||||
return formats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const {
|
void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const {
|
||||||
|
@ -87,7 +67,8 @@ void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename,
|
||||||
#define LINEBREAK "\n"
|
#define LINEBREAK "\n"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline std::string format(AssEntryGroup group, bool ssa) {
|
namespace {
|
||||||
|
std::string format(AssEntryGroup group, bool ssa) {
|
||||||
if (group == AssEntryGroup::DIALOGUE) {
|
if (group == AssEntryGroup::DIALOGUE) {
|
||||||
if (ssa)
|
if (ssa)
|
||||||
return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
|
return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
|
||||||
|
@ -105,17 +86,22 @@ static inline std::string format(AssEntryGroup group, bool ssa) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const {
|
struct Writer {
|
||||||
TextFileWriter file(filename, encoding);
|
TextFileWriter file;
|
||||||
|
bool ssa;
|
||||||
file.WriteLineToFile("[Script Info]");
|
|
||||||
file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
|
|
||||||
file.WriteLineToFile("; http://www.aegisub.org/");
|
|
||||||
|
|
||||||
bool ssa = agi::fs::HasExtension(filename, "ssa");
|
|
||||||
AssEntryGroup group = AssEntryGroup::INFO;
|
AssEntryGroup group = AssEntryGroup::INFO;
|
||||||
|
|
||||||
auto write = [&](EntryList const& list) {
|
Writer(agi::fs::path const& filename, std::string const& encoding)
|
||||||
|
: file(filename, encoding)
|
||||||
|
, ssa(agi::fs::HasExtension(filename, "ssa"))
|
||||||
|
{
|
||||||
|
file.WriteLineToFile("[Script Info]");
|
||||||
|
file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
|
||||||
|
file.WriteLineToFile("; http://www.aegisub.org/");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Write(T const& list) {
|
||||||
for (auto const& line : list) {
|
for (auto const& line : list) {
|
||||||
if (line.Group() != group) {
|
if (line.Group() != group) {
|
||||||
// Add a blank line between each group
|
// Add a blank line between each group
|
||||||
|
@ -129,10 +115,15 @@ void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen
|
||||||
|
|
||||||
file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData());
|
file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData());
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
write(src->Info);
|
}
|
||||||
write(src->Styles);
|
|
||||||
write(src->Attachments);
|
void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const {
|
||||||
write(src->Events);
|
Writer writer(filename, encoding);
|
||||||
|
|
||||||
|
writer.Write(src->Info);
|
||||||
|
writer.Write(src->Styles);
|
||||||
|
writer.Write(src->Attachments);
|
||||||
|
writer.Write(src->Events);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
#include <libaegisub/exception.h>
|
#include <libaegisub/exception.h>
|
||||||
#include <libaegisub/io.h>
|
#include <libaegisub/io.h>
|
||||||
#include <libaegisub/line_wrap.h>
|
#include <libaegisub/line_wrap.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -373,7 +372,7 @@ namespace
|
||||||
subs_list.reserve(copy.Events.size());
|
subs_list.reserve(copy.Events.size());
|
||||||
|
|
||||||
// convert to intermediate format
|
// convert to intermediate format
|
||||||
for (auto line : copy.Events | agi::of_type<AssDialogue>())
|
for (auto& line : copy.Events)
|
||||||
{
|
{
|
||||||
// add a new subtitle and work on it
|
// add a new subtitle and work on it
|
||||||
subs_list.emplace_back();
|
subs_list.emplace_back();
|
||||||
|
@ -385,19 +384,19 @@ namespace
|
||||||
imline.cumulative_status = EbuSubtitle::NotCumulative;
|
imline.cumulative_status = EbuSubtitle::NotCumulative;
|
||||||
|
|
||||||
// convert times
|
// convert times
|
||||||
imline.time_in = fps.FrameAtTime(line->Start) + timecode_bias;
|
imline.time_in = fps.FrameAtTime(line.Start) + timecode_bias;
|
||||||
imline.time_out = fps.FrameAtTime(line->End) + timecode_bias;
|
imline.time_out = fps.FrameAtTime(line.End) + timecode_bias;
|
||||||
if (export_settings.inclusive_end_times)
|
if (export_settings.inclusive_end_times)
|
||||||
// cheap and possibly wrong way to ensure exclusive times, subtract one frame from end time
|
// cheap and possibly wrong way to ensure exclusive times, subtract one frame from end time
|
||||||
imline.time_out -= 1;
|
imline.time_out -= 1;
|
||||||
|
|
||||||
// convert alignment from style
|
// convert alignment from style
|
||||||
AssStyle *style = copy.GetStyle(line->Style);
|
AssStyle *style = copy.GetStyle(line.Style);
|
||||||
if (!style)
|
if (!style)
|
||||||
style = &default_style;
|
style = &default_style;
|
||||||
|
|
||||||
// add text, translate formatting
|
// add text, translate formatting
|
||||||
imline.SetTextFromAss(line, style->underline, style->italic, style->alignment, line_wrap_type);
|
imline.SetTextFromAss(&line, style->underline, style->italic, style->alignment, line_wrap_type);
|
||||||
|
|
||||||
// line breaking handling
|
// line breaking handling
|
||||||
if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrap)
|
if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrap)
|
||||||
|
@ -407,7 +406,7 @@ namespace
|
||||||
else if (!imline.CheckLineLengths(export_settings.max_line_length))
|
else if (!imline.CheckLineLengths(export_settings.max_line_length))
|
||||||
{
|
{
|
||||||
if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
|
if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
|
||||||
throw Ebu3264SubtitleFormat::ConversionFailed(from_wx(wxString::Format(_("Line over maximum length: %s"), line->Text.get())), nullptr);
|
throw Ebu3264SubtitleFormat::ConversionFailed(from_wx(wxString::Format(_("Line over maximum length: %s"), line.Text.get())), nullptr);
|
||||||
else // skip over-long lines
|
else // skip over-long lines
|
||||||
subs_list.pop_back();
|
subs_list.pop_back();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,6 @@
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "text_file_writer.h"
|
#include "text_file_writer.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
@ -77,6 +75,6 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& fi
|
||||||
// Write lines
|
// Write lines
|
||||||
int i = 0;
|
int i = 0;
|
||||||
TextFileWriter file(filename, "UTF-8");
|
TextFileWriter file(filename, "UTF-8");
|
||||||
for (auto current : copy.Events | agi::of_type<AssDialogue>())
|
for (auto const& current : copy.Events)
|
||||||
file.WriteLineToFile(str(boost::format("%i %s %s %s") % ++i % ft.ToSMPTE(current->Start) % ft.ToSMPTE(current->End) % current->Text));
|
file.WriteLineToFile(str(boost::format("%i %s %s %s") % ++i % ft.ToSMPTE(current.Start) % ft.ToSMPTE(current.End) % current.Text));
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
@ -143,10 +142,10 @@ void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const&
|
||||||
file.WriteLineToFile(str(boost::format("{1}{1}%.6f") % fps.FPS()));
|
file.WriteLineToFile(str(boost::format("{1}{1}%.6f") % fps.FPS()));
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
for (auto current : copy.Events | agi::of_type<AssDialogue>()) {
|
for (auto const& current : copy.Events) {
|
||||||
int start = fps.FrameAtTime(current->Start, agi::vfr::START);
|
int start = fps.FrameAtTime(current.Start, agi::vfr::START);
|
||||||
int end = fps.FrameAtTime(current->End, agi::vfr::END);
|
int end = fps.FrameAtTime(current.End, agi::vfr::END);
|
||||||
|
|
||||||
file.WriteLineToFile(str(boost::format("{%i}{%i}%s") % start % end % boost::replace_all_copy(current->Text.get(), "\\N", "|")));
|
file.WriteLineToFile(str(boost::format("{%i}{%i}%s") % start % end % boost::replace_all_copy(current.Text.get(), "\\N", "|")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -480,10 +480,10 @@ void SRTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
int i=0;
|
int i=0;
|
||||||
for (auto current : copy.Events | agi::of_type<AssDialogue>()) {
|
for (auto const& current : copy.Events) {
|
||||||
file.WriteLineToFile(std::to_string(++i));
|
file.WriteLineToFile(std::to_string(++i));
|
||||||
file.WriteLineToFile(WriteSRTTime(current->Start) + " --> " + WriteSRTTime(current->End));
|
file.WriteLineToFile(WriteSRTTime(current.Start) + " --> " + WriteSRTTime(current.End));
|
||||||
file.WriteLineToFile(ConvertTags(current));
|
file.WriteLineToFile(ConvertTags(¤t));
|
||||||
file.WriteLineToFile("");
|
file.WriteLineToFile("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,13 +496,12 @@ bool SRTSubtitleFormat::CanSave(const AssFile *file) const {
|
||||||
|
|
||||||
std::string defstyle = AssStyle().GetEntryData();
|
std::string defstyle = AssStyle().GetEntryData();
|
||||||
for (auto const& line : file->Styles) {
|
for (auto const& line : file->Styles) {
|
||||||
if (static_cast<const AssStyle *>(&line)->GetEntryData() != defstyle)
|
if (line.GetEntryData() != defstyle)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& line : file->Events) {
|
for (auto const& line : file->Events) {
|
||||||
auto diag = static_cast<const AssDialogue *>(&line);
|
boost::ptr_vector<AssDialogueBlock> blocks(line.ParseTags());
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
|
|
||||||
for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
|
for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
|
||||||
// Verify that all overrides used are supported
|
// Verify that all overrides used are supported
|
||||||
for (auto const& tag : ovr->Tags) {
|
for (auto const& tag : ovr->Tags) {
|
||||||
|
|
|
@ -44,8 +44,6 @@
|
||||||
#include "ass_time.h"
|
#include "ass_time.h"
|
||||||
#include "text_file_writer.h"
|
#include "text_file_writer.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
@ -76,14 +74,14 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons
|
||||||
|
|
||||||
SmpteFormatter ft(fps);
|
SmpteFormatter ft(fps);
|
||||||
TextFileWriter file(filename, encoding);
|
TextFileWriter file(filename, encoding);
|
||||||
AssDialogue *prev = nullptr;
|
const AssDialogue *prev = nullptr;
|
||||||
for (auto cur : copy.Events | agi::of_type<AssDialogue>()) {
|
for (auto const& cur : copy.Events) {
|
||||||
if (prev) {
|
if (prev) {
|
||||||
file.WriteLineToFile(ConvertLine(©, prev, fps, ft, cur->Start));
|
file.WriteLineToFile(ConvertLine(©, prev, fps, ft, cur.Start));
|
||||||
file.WriteLineToFile("");
|
file.WriteLineToFile("");
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = cur;
|
prev = &cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush last line
|
// flush last line
|
||||||
|
@ -94,7 +92,7 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons
|
||||||
file.WriteLineToFile("SUB[");
|
file.WriteLineToFile("SUB[");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *current, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const {
|
std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialogue *current, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const {
|
||||||
int valign = 0;
|
int valign = 0;
|
||||||
const char *halign = " "; // default is centered
|
const char *halign = " "; // default is centered
|
||||||
const char *type = "N"; // no special style
|
const char *type = "N"; // no special style
|
||||||
|
|
|
@ -38,7 +38,7 @@ class AssDialogue;
|
||||||
class SmpteFormatter;
|
class SmpteFormatter;
|
||||||
|
|
||||||
class TranStationSubtitleFormat : public SubtitleFormat {
|
class TranStationSubtitleFormat : public SubtitleFormat {
|
||||||
std::string ConvertLine(AssFile *file, AssDialogue *line, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const;
|
std::string ConvertLine(AssFile *file, const AssDialogue *line, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TranStationSubtitleFormat();
|
TranStationSubtitleFormat();
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
DEFINE_SIMPLE_EXCEPTION(TTXTParseError, SubtitleFormatParseError, "subtitle_io/parse/ttxt")
|
DEFINE_SIMPLE_EXCEPTION(TTXTParseError, SubtitleFormatParseError, "subtitle_io/parse/ttxt")
|
||||||
|
@ -177,9 +176,9 @@ void TTXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& file
|
||||||
|
|
||||||
// Create lines
|
// Create lines
|
||||||
const AssDialogue *prev = nullptr;
|
const AssDialogue *prev = nullptr;
|
||||||
for (auto current : copy.Events | agi::of_type<AssDialogue>()) {
|
for (auto const& current : copy.Events) {
|
||||||
WriteLine(root, prev, current);
|
WriteLine(root, prev, ¤t);
|
||||||
prev = current;
|
prev = ¤t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save XML
|
// Save XML
|
||||||
|
@ -264,8 +263,8 @@ void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const {
|
||||||
|
|
||||||
// Find last line
|
// Find last line
|
||||||
AssTime lastTime;
|
AssTime lastTime;
|
||||||
for (auto line : file.Events | boost::adaptors::reversed | agi::of_type<AssDialogue>()) {
|
for (auto const& line : file.Events | boost::adaptors::reversed) {
|
||||||
lastTime = line->End;
|
lastTime = line.End;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
|
||||||
|
@ -131,10 +129,10 @@ void TXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen
|
||||||
size_t num_actor_names = 0, num_dialogue_lines = 0;
|
size_t num_actor_names = 0, num_dialogue_lines = 0;
|
||||||
|
|
||||||
// Detect number of lines with Actor field filled out
|
// Detect number of lines with Actor field filled out
|
||||||
for (auto dia : src->Events | agi::of_type<AssDialogue>()) {
|
for (auto const& dia : src->Events) {
|
||||||
if (!dia->Comment) {
|
if (!dia.Comment) {
|
||||||
num_dialogue_lines++;
|
num_dialogue_lines++;
|
||||||
if (!dia->Actor.get().empty())
|
if (!dia.Actor.get().empty())
|
||||||
num_actor_names++;
|
num_actor_names++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,16 +145,16 @@ void TXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen
|
||||||
file.WriteLineToFile(std::string("# Exported by Aegisub ") + GetAegisubShortVersionString());
|
file.WriteLineToFile(std::string("# Exported by Aegisub ") + GetAegisubShortVersionString());
|
||||||
|
|
||||||
// Write the file
|
// Write the file
|
||||||
for (auto dia : src->Events | agi::of_type<AssDialogue>()) {
|
for (auto const& dia : src->Events) {
|
||||||
std::string out_line;
|
std::string out_line;
|
||||||
|
|
||||||
if (dia->Comment)
|
if (dia.Comment)
|
||||||
out_line = "# ";
|
out_line = "# ";
|
||||||
|
|
||||||
if (write_actors)
|
if (write_actors)
|
||||||
out_line += dia->Actor.get() + ": ";
|
out_line += dia.Actor.get() + ": ";
|
||||||
|
|
||||||
std::string out_text = strip_formatting ? dia->GetStrippedText() : dia->Text;
|
std::string out_text = strip_formatting ? dia.GetStrippedText() : dia.Text;
|
||||||
out_line += out_text;
|
out_line += out_text;
|
||||||
|
|
||||||
if (!out_text.empty())
|
if (!out_text.empty())
|
||||||
|
|
|
@ -41,7 +41,10 @@
|
||||||
#include <libaegisub/util_osx.h>
|
#include <libaegisub/util_osx.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "ass_info.h"
|
||||||
|
#include "ass_dialogue.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
|
#include "ass_style.h"
|
||||||
#include "dialog_progress.h"
|
#include "dialog_progress.h"
|
||||||
#include "frame_main.h"
|
#include "frame_main.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
@ -112,28 +115,35 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
|
||||||
if (ass_renderer) ass_renderer_done(ass_renderer);
|
if (ass_renderer) ass_renderer_done(ass_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
namespace {
|
||||||
std::vector<char> data;
|
struct Writer {
|
||||||
data.clear();
|
std::vector<char> data;
|
||||||
data.reserve(0x4000);
|
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
||||||
|
|
||||||
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
template<typename T>
|
||||||
auto write_group = [&](EntryList const& list) {
|
void Write(T const& list) {
|
||||||
for (auto const& line : list) {
|
for (auto const& line : list) {
|
||||||
if (group != line.Group()) {
|
if (group != line.Group()) {
|
||||||
group = line.Group();
|
group = line.Group();
|
||||||
boost::push_back(data, line.GroupHeader() + "\r\n");
|
boost::push_back(data, line.GroupHeader() + "\r\n");
|
||||||
|
}
|
||||||
|
boost::push_back(data, line.GetEntryData() + "\r\n");
|
||||||
}
|
}
|
||||||
boost::push_back(data, line.GetEntryData() + "\r\n");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
write_group(subs->Info);
|
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||||
write_group(subs->Styles);
|
Writer writer;
|
||||||
write_group(subs->Events);
|
|
||||||
|
writer.data.reserve(0x4000);
|
||||||
|
|
||||||
|
writer.Write(subs->Info);
|
||||||
|
writer.Write(subs->Styles);
|
||||||
|
writer.Write(subs->Events);
|
||||||
|
|
||||||
if (ass_track) ass_free_track(ass_track);
|
if (ass_track) ass_free_track(ass_track);
|
||||||
ass_track = ass_read_memory(library, &data[0], data.size(), nullptr);
|
ass_track = ass_read_memory(library, &writer.data[0], writer.data.size(), nullptr);
|
||||||
if (!ass_track) throw "libass failed to load subtitles.";
|
if (!ass_track) throw "libass failed to load subtitles.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
#include "video_provider_manager.h"
|
#include "video_provider_manager.h"
|
||||||
|
|
||||||
|
#include <libaegisub/address_of_adaptor.h>
|
||||||
#include <libaegisub/dispatch.h>
|
#include <libaegisub/dispatch.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/indirected.hpp>
|
#include <boost/range/adaptor/indirected.hpp>
|
||||||
|
@ -70,12 +71,10 @@ std::shared_ptr<VideoFrame> ThreadedFrameSource::ProcFrame(int frame_number, dou
|
||||||
// Copying a nontrivially sized AssFile is fairly slow, so
|
// Copying a nontrivially sized AssFile is fairly slow, so
|
||||||
// instead muck around with its innards to just temporarily
|
// instead muck around with its innards to just temporarily
|
||||||
// remove the non-visible lines without deleting them
|
// remove the non-visible lines without deleting them
|
||||||
std::deque<AssEntry*> full;
|
std::deque<AssDialogue*> full;
|
||||||
for (auto& line : subs->Events)
|
boost::push_back(full, subs->Events | agi::address_of);
|
||||||
full.push_back(&line);
|
subs->Events.remove_if([=](AssDialogue const& diag) {
|
||||||
subs->Events.remove_if([=](AssEntry const& e) -> bool {
|
return diag.Start > time || diag.End <= time;
|
||||||
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
|
|
||||||
return diag && (diag->Start > time || diag->End <= time);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -133,12 +132,12 @@ void ThreadedFrameSource::LoadSubtitles(const AssFile *new_subs) throw() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadedFrameSource::UpdateSubtitles(const AssFile *new_subs, std::set<const AssEntry*> const& changes) throw() {
|
void ThreadedFrameSource::UpdateSubtitles(const AssFile *new_subs, std::set<const AssDialogue*> const& changes) throw() {
|
||||||
uint_fast32_t req_version = ++version;
|
uint_fast32_t req_version = ++version;
|
||||||
|
|
||||||
// Copy just the lines which were changed, then replace the lines at the
|
// Copy just the lines which were changed, then replace the lines at the
|
||||||
// same indices in the worker's copy of the file with the new entries
|
// same indices in the worker's copy of the file with the new entries
|
||||||
std::deque<std::pair<size_t, AssEntry*>> changed;
|
std::deque<std::pair<size_t, AssDialogue*>> changed;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto const& e : new_subs->Events) {
|
for (auto const& e : new_subs->Events) {
|
||||||
if (changes.count(&e))
|
if (changes.count(&e))
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
|
||||||
class AssEntry;
|
class AssDialogue;
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class SubtitlesProvider;
|
class SubtitlesProvider;
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
|
@ -84,7 +84,7 @@ public:
|
||||||
///
|
///
|
||||||
/// This function only supports changes to existing lines, and not
|
/// This function only supports changes to existing lines, and not
|
||||||
/// insertions or deletions.
|
/// insertions or deletions.
|
||||||
void UpdateSubtitles(const AssFile *subs, std::set<const AssEntry *> const& changes) throw();
|
void UpdateSubtitles(const AssFile *subs, std::set<const AssDialogue *> const& changes) throw();
|
||||||
|
|
||||||
/// @brief Queue a request for a frame
|
/// @brief Queue a request for a frame
|
||||||
/// @brief frame Frame number
|
/// @brief frame Frame number
|
||||||
|
|
|
@ -115,19 +115,6 @@ void SetClipboard(wxBitmap const& new_value);
|
||||||
|
|
||||||
#define countof(array) (sizeof(array) / sizeof(array[0]))
|
#define countof(array) (sizeof(array) / sizeof(array[0]))
|
||||||
|
|
||||||
template<class Out>
|
|
||||||
struct cast {
|
|
||||||
template<class In>
|
|
||||||
Out operator()(In *in) const {
|
|
||||||
return dynamic_cast<Out>(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class In>
|
|
||||||
Out operator()(In &in) const {
|
|
||||||
return dynamic_cast<Out>(&in);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
wxString FontFace(std::string opt_prefix);
|
wxString FontFace(std::string opt_prefix);
|
||||||
|
|
||||||
agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent);
|
agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent);
|
||||||
|
|
|
@ -232,7 +232,7 @@ void VideoContext::Reload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed) {
|
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed) {
|
||||||
if (!IsLoaded()) return;
|
if (!IsLoaded()) return;
|
||||||
|
|
||||||
if (changed.empty() || no_amend)
|
if (changed.empty() || no_amend)
|
||||||
|
@ -436,7 +436,7 @@ void VideoContext::LoadTimecodes(agi::fs::path const& filename) {
|
||||||
ovr_fps = agi::vfr::Framerate(filename);
|
ovr_fps = agi::vfr::Framerate(filename);
|
||||||
timecodes_filename = filename;
|
timecodes_filename = filename;
|
||||||
config::mru->Add("Timecodes", filename);
|
config::mru->Add("Timecodes", filename);
|
||||||
OnSubtitlesCommit(0, std::set<const AssEntry*>());
|
OnSubtitlesCommit(0, std::set<const AssDialogue*>());
|
||||||
TimecodesOpen(ovr_fps);
|
TimecodesOpen(ovr_fps);
|
||||||
}
|
}
|
||||||
catch (agi::fs::FileSystemError const& err) {
|
catch (agi::fs::FileSystemError const& err) {
|
||||||
|
@ -460,7 +460,7 @@ void VideoContext::SaveTimecodes(agi::fs::path const& filename) {
|
||||||
void VideoContext::CloseTimecodes() {
|
void VideoContext::CloseTimecodes() {
|
||||||
ovr_fps = agi::vfr::Framerate();
|
ovr_fps = agi::vfr::Framerate();
|
||||||
timecodes_filename.clear();
|
timecodes_filename.clear();
|
||||||
OnSubtitlesCommit(0, std::set<const AssEntry*>());
|
OnSubtitlesCommit(0, std::set<const AssDialogue*>());
|
||||||
TimecodesOpen(video_fps);
|
TimecodesOpen(video_fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,11 @@
|
||||||
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
|
||||||
class AssEntry;
|
class AssDialogue;
|
||||||
struct SubtitlesProviderErrorEvent;
|
|
||||||
class ThreadedFrameSource;
|
class ThreadedFrameSource;
|
||||||
struct VideoFrame;
|
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
|
struct SubtitlesProviderErrorEvent;
|
||||||
|
struct VideoFrame;
|
||||||
struct VideoProviderErrorEvent;
|
struct VideoProviderErrorEvent;
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
|
@ -152,7 +152,7 @@ class VideoContext : public wxEvtHandler {
|
||||||
void OnVideoError(VideoProviderErrorEvent const& err);
|
void OnVideoError(VideoProviderErrorEvent const& err);
|
||||||
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
||||||
|
|
||||||
void OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed);
|
void OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed);
|
||||||
void OnSubtitlesSave();
|
void OnSubtitlesSave();
|
||||||
|
|
||||||
/// Close the video, keyframes and timecodes
|
/// Close the video, keyframes and timecodes
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "video_display.h"
|
#include "video_display.h"
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -115,9 +114,9 @@ void VisualToolDrag::OnFileChanged() {
|
||||||
primary = nullptr;
|
primary = nullptr;
|
||||||
active_feature = nullptr;
|
active_feature = nullptr;
|
||||||
|
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (IsDisplayed(diag))
|
if (IsDisplayed(&diag))
|
||||||
MakeFeatures(diag);
|
MakeFeatures(&diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateToggleButtons();
|
UpdateToggleButtons();
|
||||||
|
@ -130,18 +129,18 @@ void VisualToolDrag::OnFrameChanged() {
|
||||||
auto feat = features.begin();
|
auto feat = features.begin();
|
||||||
auto end = features.end();
|
auto end = features.end();
|
||||||
|
|
||||||
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
|
for (auto& diag : c->ass->Events) {
|
||||||
if (IsDisplayed(diag)) {
|
if (IsDisplayed(&diag)) {
|
||||||
// Features don't exist and should
|
// Features don't exist and should
|
||||||
if (feat == end || feat->line != diag)
|
if (feat == end || feat->line != &diag)
|
||||||
MakeFeatures(diag, feat);
|
MakeFeatures(&diag, feat);
|
||||||
// Move past already existing features for the line
|
// Move past already existing features for the line
|
||||||
else
|
else
|
||||||
while (feat != end && feat->line == diag) ++feat;
|
while (feat != end && feat->line == &diag) ++feat;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Remove all features for this line (if any)
|
// Remove all features for this line (if any)
|
||||||
while (feat != end && feat->line == diag) {
|
while (feat != end && feat->line == &diag) {
|
||||||
if (&*feat == active_feature) active_feature = nullptr;
|
if (&*feat == active_feature) active_feature = nullptr;
|
||||||
feat->line = nullptr;
|
feat->line = nullptr;
|
||||||
RemoveSelection(&*feat);
|
RemoveSelection(&*feat);
|
||||||
|
|
Loading…
Reference in New Issue