Make the entry lists be of the appropriate type rather than just AssEntry

Eliminates a bajillion dynamic casts.
This commit is contained in:
Thomas Goyne 2014-03-07 10:58:51 -08:00
parent 1506c1ab10
commit b1639c6162
58 changed files with 538 additions and 603 deletions

View File

@ -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" />

View File

@ -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">

View File

@ -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{};
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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&);

View File

@ -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;

View File

@ -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*>());
}; };

View File

@ -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(); }

View File

@ -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;
} }

View File

@ -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"));

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
} }

View File

@ -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();

View File

@ -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;

View File

@ -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) {

View File

@ -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())

View File

@ -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) {

View File

@ -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);
} }
} }

View File

@ -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;
} }

View File

@ -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));
} }
} }

View File

@ -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);
} }

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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";
} }
} }

View File

@ -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);
} }
} }

View File

@ -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));

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;

View File

@ -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);
} }

View File

@ -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;
} }
} }
} }

View File

@ -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; } }

View File

@ -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);
} }

View File

@ -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();
} }

View File

@ -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));
} }

View File

@ -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", "|")));
} }
} }

View File

@ -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(&current));
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) {

View File

@ -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(&copy, prev, fps, ft, cur->Start)); file.WriteLineToFile(ConvertLine(&copy, 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

View File

@ -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();

View File

@ -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, &current);
prev = current; prev = &current;
} }
// 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;
} }

View File

@ -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())

View File

@ -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.";
} }

View File

@ -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))

View File

@ -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

View File

@ -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);

View File

@ -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);
} }

View File

@ -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

View File

@ -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);