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)config.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\uuencode.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />

View File

@ -164,6 +164,9 @@
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\address_of_adaptor.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<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);
}
AssEntry *AssAttachment::Clone() const {
return new AssAttachment(*this);
}
const std::string AssAttachment::GetEntryData() const {
return entry_data;
}
size_t AssAttachment::GetSize() const {
auto header_end = entry_data.get().find('\n');
return entry_data.get().size() - header_end - 1;

View File

@ -46,9 +46,9 @@ public:
/// @param raw If false, remove the SSA filename mangling
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; }
AssEntry *Clone() const override;
AssAttachment *Clone() const override { return new AssAttachment(*this); }
AssAttachment(AssAttachment const& rgt);
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), "");
}
AssEntry *AssDialogue::Clone() const {
AssDialogue *AssDialogue::Clone() const {
auto clone = new AssDialogue(*this);
clone->Id = Id;
return clone;

View File

@ -177,7 +177,7 @@ public:
/// Does this line collide with the passed line?
bool CollidesWith(const AssDialogue *target) const;
AssEntry *Clone() const override;
AssDialogue *Clone() const override;
AssDialogue();
AssDialogue(AssDialogue const&);

View File

@ -1,36 +1,19 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from 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.
// 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/
/// @file ass_file.cpp
/// @brief Overall storage of subtitle files, undo management and more
/// @ingroup subs_storage
#include "config.h"
#include "ass_file.h"
@ -42,20 +25,20 @@
#include "options.h"
#include "utils.h"
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/filesystem/path.hpp>
AssFile::AssFile() { }
AssFile::~AssFile() {
Info.clear_and_dispose([](AssEntry *e) { delete e; });
Styles.clear_and_dispose([](AssEntry *e) { delete e; });
Events.clear_and_dispose([](AssEntry *e) { delete e; });
Attachments.clear_and_dispose([](AssEntry *e) { delete e; });
Info.clear_and_dispose([](AssInfo *e) { delete e; });
Styles.clear_and_dispose([](AssStyle *e) { delete e; });
Events.clear_and_dispose([](AssDialogue *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("ScriptType", "v4.00+"));
Info.push_back(*new AssInfo("WrapStyle", "0"));
@ -68,15 +51,15 @@ void AssFile::LoadDefault(bool defline) {
Styles.push_back(*new AssStyle);
if (defline)
if (include_dialogue_line)
Events.push_back(*new AssDialogue);
}
AssFile::AssFile(const AssFile &from) {
Info.clone_from(from.Info, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
Styles.clone_from(from.Styles, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
Events.clone_from(from.Events, std::mem_fun_ref(&AssEntry::Clone), [](AssEntry *e) { delete e; });
Attachments.clone_from(from.Attachments, 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(&AssStyle::Clone), [](AssStyle *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(&AssAttachment::Clone), [](AssAttachment *e) { delete e; });
}
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 {
for (const auto info : Info | agi::of_type<AssInfo>()) {
if (boost::iequals(key, info->Key()))
return info->Value();
for (auto const& info : Info) {
if (boost::iequals(key, info.Key()))
return info.Value();
}
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) {
for (auto info : Info | agi::of_type<AssInfo>()) {
if (boost::iequals(key, info->Key())) {
for (auto& info : Info) {
if (boost::iequals(key, info.Key())) {
if (value.empty())
delete info;
delete &info;
else
info->SetValue(value);
info.SetValue(value);
return;
}
}
@ -145,47 +128,42 @@ void AssFile::SetScriptInfo(std::string const& key, std::string const& 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");
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) {
sw = 384;
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> styles;
for (auto style : Styles | agi::of_type<AssStyle>())
styles.push_back(style->name);
for (auto& style : Styles)
styles.push_back(style.name);
return styles;
}
AssStyle *AssFile::GetStyle(std::string const& name) {
for (auto style : Styles | agi::of_type<AssStyle>()) {
if (boost::iequals(style->name, name))
return style;
for (auto& style : Styles) {
if (boost::iequals(style.name, name))
return &style;
}
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});
std::set<const AssEntry*> changed_lines;
std::set<const AssDialogue*> changed_lines;
if (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;
}
bool AssFile::CompStart(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->Start < rgt->Start;
bool AssFile::CompStart(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.Start < rgt.Start;
}
bool AssFile::CompEnd(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->End < rgt->End;
bool AssFile::CompEnd(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.End < rgt.End;
}
bool AssFile::CompStyle(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->Style < rgt->Style;
bool AssFile::CompStyle(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.Style < rgt.Style;
}
bool AssFile::CompActor(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->Actor < rgt->Actor;
bool AssFile::CompActor(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.Actor < rgt.Actor;
}
bool AssFile::CompEffect(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->Effect < rgt->Effect;
bool AssFile::CompEffect(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.Effect < rgt.Effect;
}
bool AssFile::CompLayer(const AssDialogue* lft, const AssDialogue* rgt) {
return lft->Layer < rgt->Layer;
bool AssFile::CompLayer(AssDialogue const& lft, AssDialogue const& rgt) {
return lft.Layer < rgt.Layer;
}
void AssFile::Sort(CompFunc comp, std::set<AssDialogue*> const& limit) {
Sort(Events, comp, limit);
}
namespace {
inline bool is_dialogue(AssEntry *e, std::set<AssDialogue*> const& limit) {
AssDialogue *d = dynamic_cast<AssDialogue*>(e);
return d && (limit.empty() || limit.count(d));
void AssFile::Sort(EntryList<AssDialogue> &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
if (limit.empty()) {
lst.sort(comp);
return;
}
}
void AssFile::Sort(EntryList &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
auto compE = [&](AssEntry const& a, AssEntry const& b) {
return comp(static_cast<const AssDialogue*>(&a), static_cast<const AssDialogue*>(&b));
};
// Sort each selected block separately, leaving everything else untouched
for (auto begin = lst.begin(); begin != lst.end(); ++begin) {
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
for (entryIter begin = lst.begin(); begin != lst.end(); ++begin) {
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;
// sort doesn't support only sorting a sublist, so move them to a temp list
EntryList<AssDialogue> tmp;
tmp.splice(tmp.begin(), lst, begin, end);
tmp.sort(compE);
tmp.sort(comp);
lst.splice(end, tmp);
begin = --end;

View File

@ -41,33 +41,36 @@
#include <set>
#include <vector>
class AssDialogue;
class AssStyle;
class AssAttachment;
class AssDialogue;
class AssInfo;
class AssStyle;
class wxString;
typedef boost::intrusive::make_list<AssEntry, boost::intrusive::constant_time_size<false>>::type EntryList;
typedef EntryList::iterator entryIter;
typedef EntryList::const_iterator constEntryIter;
template<typename T>
using EntryList = typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>, boost::intrusive::base_hook<AssEntry>>::type;
template<typename T>
using EntryIter = typename EntryList<T>::iterator;
struct AssFileCommit {
wxString const& message;
int *commit_id;
AssEntry *single_line;
AssDialogue *single_line;
};
class AssFile {
/// 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;
public:
/// The lines in the file
EntryList Info;
EntryList Styles;
EntryList Events;
EntryList Attachments;
EntryList<AssInfo> Info;
EntryList<AssStyle> Styles;
EntryList<AssDialogue> Events;
EntryList<AssAttachment> Attachments;
AssFile() { }
AssFile();
AssFile(const AssFile &from);
AssFile& operator=(AssFile from);
~AssFile();
@ -139,23 +142,23 @@ public:
/// @param commitId Commit to amend rather than pushing a new commit
/// @param single_line Line which was changed, if only one line was
/// @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
typedef bool (*CompFunc)(const AssDialogue* lft, const AssDialogue* rgt);
typedef bool (*CompFunc)(AssDialogue const& lft, AssDialogue const& rgt);
/// 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
static bool CompEnd(const AssDialogue* lft, const AssDialogue* rgt);
static bool CompEnd(AssDialogue const& lft, AssDialogue const& rgt);
/// 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
static bool CompActor(const AssDialogue* lft, const AssDialogue* rgt);
static bool CompActor(AssDialogue const& lft, AssDialogue const& rgt);
/// 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
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
/// @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
/// @param comp Comparison function to use. Defaults to sorting by start time.
/// @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(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; }
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(); }

View File

@ -280,19 +280,18 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
SubtitleSelection sel = c->selectionController->GetSelectedSet();
bool did_split = false;
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it);
if (!diag || !lines.count(diag)) continue;
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
if (!lines.count(&*it)) continue;
kara.SetLine(diag);
kara.SetLine(&*it);
// If there aren't at least two tags there's nothing to split
if (kara.size() < 2) continue;
bool in_sel = sel.count(diag) > 0;
bool in_sel = sel.count(&*it) > 0;
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->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
sel.erase(diag);
delete diag;
sel.erase(&*it);
delete &*it;
did_split = true;
}

View File

@ -204,10 +204,6 @@ std::string AssStyle::GetSSAText() const {
% Margin[0] % Margin[1] % Margin[2] % encoding);
}
AssEntry *AssStyle::Clone() const {
return new AssStyle(*this);
}
void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
encodingStrings.Clear();
encodingStrings.Add(wxString("0 - ") + _("ANSI"));

View File

@ -80,7 +80,7 @@ public:
const std::string GetEntryData() const override { return data; }
std::string GetSSAText() const override;
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
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))) {
LoadFromLine();
split_area->Refresh(false);

View File

@ -31,7 +31,6 @@
#include <wx/window.h>
class AssDialogue;
class AssEntry;
class AssKaraoke;
class wxButton;
@ -144,7 +143,7 @@ class AudioKaraoke : public wxWindow {
void OnActiveLineChanged(AssDialogue *new_line);
void OnContextMenu(wxContextMenuEvent&);
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 OnPaint(wxPaintEvent &event);
void OnSize(wxSizeEvent &event);

View File

@ -715,21 +715,19 @@ void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker*> const&
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 false;
return !diag.Comment;
}
static bool dialogue(AssEntry const& e)
static bool dialogue(AssDialogue const&)
{
return !!dynamic_cast<const AssDialogue*>(&e);
return true;
}
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();
inactive_lines.clear();
@ -742,21 +740,20 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
case 2: // Previous and next lines
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())
break;
entryIter prev = current_line;
auto prev = current_line;
while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
if (prev != context->ass->Events.begin())
AddInactiveLine(sel, static_cast<AssDialogue*>(&*prev));
AddInactiveLine(sel, &*prev);
if (mode == 2)
{
entryIter next =
find_if(++current_line, context->ass->Events.end(), predicate);
auto next = find_if(++current_line, context->ass->Events.end(), predicate);
if (next != context->ass->Events.end())
AddInactiveLine(sel, static_cast<AssDialogue*>(&*next));
AddInactiveLine(sel, &*next);
}
}
break;
@ -766,7 +763,7 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
for (auto& line : context->ass->Events)
{
if (&line != active_line && predicate(line))
AddInactiveLine(sel, static_cast<AssDialogue*>(&line));
AddInactiveLine(sel, &line);
}
break;
}

View File

@ -277,7 +277,7 @@ namespace {
lua_pushvalue(L, 1);
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);
if (!st)
return luaL_error(L, "Not a style entry");
@ -843,9 +843,8 @@ namespace Automation4 {
int idx = 1;
for (auto& line : c->ass->Events) {
++row;
auto diag = static_cast<AssDialogue*>(&line);
if (diag == active_line) active_idx = row;
if (sel.count(diag)) {
if (&line == active_line) active_idx = row;
if (sel.count(&line)) {
push_value(L, row);
lua_rawseti(L, -2, idx++);
}
@ -928,7 +927,7 @@ namespace Automation4 {
throw LuaForEachBreak();
}
AssDialogue *diag = dynamic_cast<AssDialogue*>(lines[cur - 1]);
auto diag = dynamic_cast<AssDialogue*>(lines[cur - 1]);
if (!diag) {
wxLogError("Selected row %d is not a dialogue line", cur);
throw LuaForEachBreak();

View File

@ -37,6 +37,7 @@
#include "auto4_lua.h"
#include "auto4_lua_utils.h"
#include "ass_attachment.h"
#include "ass_dialogue.h"
#include "ass_info.h"
#include "ass_file.h"
@ -535,7 +536,7 @@ namespace Automation4 {
int LuaAssFile::LuaParseKaraokeData(lua_State *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");
int idx = 0;

View File

@ -52,8 +52,6 @@
#include "video_context.h"
#include "video_slider.h"
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <boost/range/algorithm.hpp>
#include <cmath>
@ -82,13 +80,6 @@ enum RowColor {
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 {
template <typename T>
struct hash<boost::flyweight<T>> {
@ -265,9 +256,9 @@ void BaseGrid::UpdateMaps() {
index_line_map.clear();
line_index_map.clear();
for (auto curdiag : context->ass->Events | agi::of_type<AssDialogue>()) {
line_index_map[curdiag] = (int)index_line_map.size();
index_line_map.push_back(curdiag);
for (auto& curdiag : context->ass->Events) {
line_index_map[&curdiag] = (int)index_line_map.size();
index_line_map.push_back(&curdiag);
}
auto sorted = index_line_map;
@ -751,6 +742,7 @@ void BaseGrid::SetColumnWidths() {
std::unordered_map<boost::flyweight<std::string>, int> widths;
auto get_width = [&](boost::flyweight<std::string> const& str) -> int {
if (str.get().empty()) return 0;
auto it = widths.find(str);
if (it != end(widths)) return it->second;
int width = dc.GetTextExtent(to_wx(str)).GetWidth();
@ -766,24 +758,22 @@ void BaseGrid::SetColumnWidths() {
int maxLayer = 0;
int maxStart = 0;
int maxEnd = 0;
for (int i = 0; i < GetRows(); i++) {
AssDialogue *curDiag = GetDialogue(i);
maxLayer = std::max(maxLayer, curDiag->Layer);
actorLen = std::max(actorLen, get_width(curDiag->Actor));
styleLen = std::max(styleLen, get_width(curDiag->Style));
effectLen = std::max(effectLen, get_width(curDiag->Effect));
for (auto const& diag : context->ass->Events) {
maxLayer = std::max(maxLayer, diag.Layer);
actorLen = std::max(actorLen, get_width(diag.Actor));
styleLen = std::max(styleLen, get_width(diag.Style));
effectLen = std::max(effectLen, get_width(diag.Effect));
// Margins
for (int j = 0; j < 3; j++) {
if (curDiag->Margin[j])
if (diag.Margin[j])
showMargin[j] = true;
}
// Times
if (byFrame) {
maxStart = std::max(maxStart, context->videoController->FrameAtTime(curDiag->Start, agi::vfr::START));
maxEnd = std::max(maxEnd, context->videoController->FrameAtTime(curDiag->End, agi::vfr::END));
maxStart = std::max(maxStart, context->videoController->FrameAtTime(diag.Start, agi::vfr::START));
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() {
int cur_line_i = GetDialogueIndex(GetActiveLine());
if (AssDialogue *prev_line = GetDialogue(cur_line_i-1))
SetSelectionAndActive({ prev_line }, prev_line);
if (!active_line) return;
auto it = context->ass->Events.iterator_to(*active_line);
if (it != context->ass->Events.begin()) {
--it;
SetSelectionAndActive({&*it}, &*it);
}
}
void BaseGrid::NextLine() {
int cur_line_i = GetDialogueIndex(GetActiveLine());
if (AssDialogue *next_line = GetDialogue(cur_line_i+1))
SetSelectionAndActive({ next_line }, next_line);
if (!active_line) return;
auto it = context->ass->Events.iterator_to(*active_line);
if (++it != context->ass->Events.end())
SetSelectionAndActive({&*it}, &*it);
}
void BaseGrid::AnnounceActiveLineChanged(AssDialogue *new_line) {

View File

@ -53,6 +53,7 @@
#include "../utils.h"
#include "../video_context.h"
#include <libaegisub/address_of_adaptor.h>
#include <libaegisub/of_type_adaptor.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) {
SubtitleSelection sel = c->selectionController->GetSelectedSet();
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),
"\r\n"));
}
@ -503,25 +503,25 @@ static void delete_lines(agi::Context *c, wxString const& commit_message) {
AssDialogue *post_sel = nullptr;
bool hit_selection = false;
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (sel.count(diag))
for (auto& diag : c->ass->Events) {
if (sel.count(&diag))
hit_selection = true;
else if (hit_selection && !post_sel) {
post_sel = diag;
post_sel = &diag;
break;
}
else
pre_sel = diag;
pre_sel = &diag;
}
// 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
// need to create a new dialogue line for it, and we can't select dialogue
// lines until after they're committed.
std::vector<std::unique_ptr<AssEntry>> to_delete;
c->ass->Events.remove_and_dispose_if([&sel](AssEntry const& e) {
return sel.count(const_cast<AssDialogue *>(static_cast<const AssDialogue*>(&e)));
}, [&](AssEntry *e) {
std::vector<std::unique_ptr<AssDialogue>> to_delete;
c->ass->Events.remove_and_dispose_if([&sel](AssDialogue const& e) {
return sel.count(const_cast<AssDialogue *>(&e));
}, [&](AssDialogue *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) {
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;
AssDialogue *new_active = nullptr;
entryIter start = c->ass->Events.begin();
entryIter end = c->ass->Events.end();
auto start = c->ass->Events.begin();
auto end = c->ass->Events.end();
while (start != end) {
// Find the first line in the selection
start = find_if(start, end, sel);
start = find_if(start, end, in_selection);
if (start == end) break;
// And the last line in this contiguous selection
entryIter insert_pos = find_if_not(start, end, sel);
entryIter last = std::prev(insert_pos);
auto insert_pos = find_if_not(start, end, in_selection);
auto last = std::prev(insert_pos);
// Duplicate each of the selected lines, inserting them in a block
// after the selected block
do {
auto old_diag = static_cast<AssDialogue*>(&*start);
auto old_diag = &*start;
auto new_diag = new AssDialogue(*old_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();
AssDialogue *first = nullptr;
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it++);
if (!diag || !sel.count(diag))
continue;
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ) {
AssDialogue *diag = &*it++;
if (!sel.count(diag)) continue;
if (!first) {
first = diag;
continue;
@ -772,7 +764,7 @@ static bool try_paste_lines(agi::Context *c) {
boost::trim_left(data);
if (!boost::starts_with(data, "Dialogue:")) return false;
EntryList parsed;
EntryList<AssDialogue> parsed;
boost::char_separator<char> sep("\r\n");
for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
boost::trim(curdata);
@ -780,15 +772,15 @@ static bool try_paste_lines(agi::Context *c) {
parsed.push_back(*new AssDialogue(curdata));
}
catch (...) {
parsed.clear_and_dispose([](AssEntry *e) { delete e; });
parsed.clear_and_dispose([](AssDialogue *e) { delete e; });
return false;
}
}
AssDialogue *new_active = static_cast<AssDialogue *>(&*parsed.begin());
AssDialogue *new_active = &*parsed.begin();
SubtitleSelection new_selection;
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());
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);
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)
pos = find_if(next(pos), c->ass->Events.end(), cast<AssDialogue*>());
++pos;
return ret;
});
}
@ -871,8 +863,8 @@ struct edit_line_paste_over : public Command {
std::vector<AssDialogue*> sorted_selection;
sorted_selection.reserve(sel.size());
for (auto& line : c->ass->Events) {
if (sel.count(static_cast<AssDialogue*>(&line)))
sorted_selection.push_back(static_cast<AssDialogue*>(&line));
if (sel.count(&line))
sorted_selection.push_back(&line);
}
auto pos = begin(sorted_selection);
@ -936,7 +928,10 @@ struct edit_line_recombine : public validate_sel_multiple {
auto active_line = c->selectionController->GetActiveLine();
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)
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
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()));
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->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->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
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;
auto prev = end;
for (auto it = begin; it != end; std::advance(it, step)) {
auto cur = dynamic_cast<typename U::key_type>(&*it);
if (!cur) continue;
auto cur = &*it;
if (!to_move.count(cur))
prev = it;
else if (prev != end) {

View File

@ -53,10 +53,11 @@
#include "../utils.h"
#include "../video_context.h"
#include <libaegisub/address_of_adaptor.h>
#include <libaegisub/charset_conv.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <boost/range/algorithm.hpp>
#include <wx/msgdlg.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->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;
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->End = new_line->Start + OPT_GET("Timing/Default Duration")->GetInt();
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
AssDialogue *diag = static_cast<AssDialogue*>(&*it);
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
AssDialogue *diag = &*it;
// Limit the line to the available time
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->Start = new_line->End - OPT_GET("Timing/Default Duration")->GetInt();
for (entryIter it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
auto diag = static_cast<AssDialogue*>(&*it);
for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
auto diag = &*it;
// Limit the line to the available time
if (diag->End <= new_line->End)
@ -372,9 +373,7 @@ struct subtitle_select_all : public Command {
void operator()(agi::Context *c) override {
SubtitleSelection sel;
transform(c->ass->Events.begin(), c->ass->Events.end(),
inserter(sel, sel.begin()), cast<AssDialogue*>());
sel.erase(nullptr);
boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
c->selectionController->SetSelectedSet(sel);
}
};
@ -394,13 +393,13 @@ struct subtitle_select_visible : public Command {
SubtitleSelectionController::Selection new_selection;
int frame = c->videoController->GetFrameN();
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (c->videoController->FrameAtTime(diag->Start, agi::vfr::START) <= frame &&
c->videoController->FrameAtTime(diag->End, agi::vfr::END) >= frame)
for (auto& diag : c->ass->Events) {
if (c->videoController->FrameAtTime(diag.Start, agi::vfr::START) <= frame &&
c->videoController->FrameAtTime(diag.End, agi::vfr::END) >= frame)
{
if (new_selection.empty())
c->selectionController->SetActiveLine(diag);
new_selection.insert(diag);
c->selectionController->SetActiveLine(&diag);
new_selection.insert(&diag);
}
}

View File

@ -45,7 +45,6 @@
#include "../selection_controller.h"
#include "../video_context.h"
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <algorithm>
@ -67,8 +66,8 @@ namespace {
if (sel.size() < 2) return !sel.empty();
size_t found = 0;
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (sel.count(diag)) {
for (auto& diag : c->ass->Events) {
if (sel.count(&diag)) {
if (++found == sel.size())
return true;
}
@ -84,14 +83,14 @@ static void adjoin_lines(agi::Context *c, bool set_start) {
AssDialogue *prev = nullptr;
size_t seen = 0;
bool prev_sel = false;
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
bool cur_sel = !!sel.count(diag);
for (auto diag : c->ass->Events) {
bool cur_sel = !!sel.count(&diag);
if (prev) {
// One row selections act as if the previous or next line was selected
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))
prev->End = diag->Start;
prev->End = diag.Start;
}
if (seen == sel.size())
@ -100,7 +99,7 @@ static void adjoin_lines(agi::Context *c, bool set_start) {
if (cur_sel)
++seen;
prev = diag;
prev = &diag;
prev_sel = cur_sel;
}

View File

@ -50,8 +50,6 @@
#include "options.h"
#include "utils.h"
#include <libaegisub/of_type_adaptor.h>
DialogAttachments::DialogAttachments(wxWindow *parent, AssFile *ass)
: wxDialog(parent, -1, _("Attachment List"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
, ass(ass)
@ -99,12 +97,12 @@ void DialogAttachments::UpdateList() {
listView->InsertColumn(1, _("Size"), 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();
listView->InsertItem(row, to_wx(attach->GetFileName(true)));
listView->SetItem(row, 1, PrettySize(attach->GetSize()));
listView->SetItem(row, 2, to_wx(attach->GroupHeader()));
listView->SetItemPtrData(row, wxPtrToUInt(attach));
listView->InsertItem(row, to_wx(attach.GetFileName(true)));
listView->SetItem(row, 1, PrettySize(attach.GetSize()));
listView->SetItem(row, 2, to_wx(attach.GroupHeader()));
listView->SetItemPtrData(row, wxPtrToUInt(&attach));
}
}

View File

@ -601,8 +601,8 @@ void DialogKanjiTimer::OnAccept(wxCommandEvent &) {
if (display->GetRemainingSource() > 0)
wxMessageBox(_("Group all of the source text."),_("Error"),wxICON_EXCLAMATION | wxOK);
else if (AssDialogue *destLine = dynamic_cast<AssDialogue*>(currentDestinationLine)) {
LinesToChange.push_back(std::make_pair(destLine, display->GetOutputLine()));
else {
LinesToChange.push_back(std::make_pair(currentDestinationLine, display->GetOutputLine()));
currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
@ -644,21 +644,13 @@ void DialogKanjiTimer::OnKeyDown(wxKeyEvent &event) {
void DialogKanjiTimer::ResetForNewLine()
{
AssDialogue *src = nullptr;
AssDialogue *dst = nullptr;
if (currentSourceLine)
src = dynamic_cast<AssDialogue*>(currentSourceLine);
if (currentDestinationLine)
dst = dynamic_cast<AssDialogue*>(currentDestinationLine);
if (!src || !dst)
if (!currentSourceLine || !currentDestinationLine)
{
src = dst = nullptr;
currentSourceLine = currentDestinationLine = nullptr;
wxBell();
}
display->SetInputData(src, dst);
display->SetInputData(currentSourceLine, currentDestinationLine);
TryAutoMatch();
@ -672,25 +664,24 @@ void DialogKanjiTimer::TryAutoMatch()
}
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)
{
AssDialogue *dlg = dynamic_cast<AssDialogue*>(&*from);
if (dlg && dlg->Style == style_name && !dlg->Text.get().empty())
return dlg;
if (from->Style == style_name && !from->Text.get().empty())
return &*from;
}
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;
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;
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; }
class AssDialogue;
class AssEntry;
class AssFile;
class KaraokeLineMatchDisplay;
class wxComboBox;
@ -55,8 +54,8 @@ class DialogKanjiTimer : public wxDialog {
std::vector<std::pair<AssDialogue*, std::string>> LinesToChange;
AssEntry *currentSourceLine = nullptr;
AssEntry *currentDestinationLine = nullptr;
AssDialogue *currentSourceLine = nullptr;
AssDialogue *currentDestinationLine = nullptr;
void OnClose(wxCommandEvent &event);
void OnStart(wxCommandEvent &event);
@ -71,8 +70,8 @@ class DialogKanjiTimer : public wxDialog {
void ResetForNewLine();
void TryAutoMatch();
AssEntry *FindNextStyleMatch(AssEntry *search_from, const std::string &search_style);
AssEntry *FindPrevStyleMatch(AssEntry *search_from, const std::string &search_style);
AssDialogue *FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style);
AssDialogue *FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style);
public:
DialogKanjiTimer(agi::Context *context);

View File

@ -34,8 +34,6 @@
#include "selection_controller.h"
#include "utils.h"
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <boost/algorithm/string/find.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);
std::set<AssDialogue*> matches;
for (auto diag : ass->Events | agi::of_type<AssDialogue>()) {
if (diag->Comment && !comments) continue;
if (!diag->Comment && !dialogue) continue;
for (auto& diag : ass->Events) {
if (diag.Comment && !comments) continue;
if (!diag.Comment && !dialogue) continue;
if (invert != predicate(diag, 0))
matches.insert(diag);
if (invert != predicate(&diag, 0))
matches.insert(&diag);
}
return matches;

View File

@ -38,7 +38,6 @@
#include <libaegisub/fs.h>
#include <libaegisub/io.h>
#include <libaegisub/log.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>
@ -354,10 +353,10 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
int block_start = 0;
json::Array shifted_blocks;
for (auto line : context->ass->Events | agi::of_type<AssDialogue>()) {
for (auto& line : context->ass->Events) {
++row_number;
if (!sel.count(line)) {
if (!sel.count(&line)) {
if (block_start) {
json::Object block;
block["start"] = block_start;
@ -372,9 +371,9 @@ void DialogShiftTimes::Process(wxCommandEvent &) {
block_start = row_number;
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)
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);

View File

@ -212,19 +212,17 @@ bool DialogSpellChecker::FindNext() {
if (CheckLine(active_line, start_pos, &commit_id))
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
// the cursor is past the first misspelled word in the current line, that
// word should be hit last
while(!has_looped || active_line != start_line) {
do {
// Wrap around to the beginning if we hit the end
if (++it == context->ass->Events.end()) {
it = context->ass->Events.begin();
has_looped = true;
}
} while (!(active_line = dynamic_cast<AssDialogue*>(&*it)));
// Wrap around to the beginning if we hit the end
if (++it == context->ass->Events.end()) {
it = context->ass->Events.begin();
has_looped = true;
}
if (CheckLine(active_line, 0, &commit_id))
return true;

View File

@ -86,19 +86,19 @@ class StyleRenamer {
found_any = false;
do_replace = replace;
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (diag->Style == source_name) {
for (auto& diag : c->ass->Events) {
if (diag.Style == source_name) {
if (replace)
diag->Style = new_name;
diag.Style = new_name;
else
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>())
block->ProcessParameters(&StyleRenamer::ProcessTag, this);
if (replace)
diag->UpdateText(blocks);
diag.UpdateText(blocks);
if (found_any) return;
}

View File

@ -54,7 +54,6 @@
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/split.h>
#include <libaegisub/util.h>
@ -288,9 +287,9 @@ void DialogStyleManager::LoadCurrentStyles(int commit_type) {
CurrentList->Clear();
styleMap.clear();
for (auto style : c->ass->Styles | agi::of_type<AssStyle>()) {
CurrentList->Append(to_wx(style->name));
styleMap.push_back(style);
for (auto& style : c->ass->Styles) {
CurrentList->Append(to_wx(style.name));
styleMap.push_back(&style);
}
}
@ -780,10 +779,8 @@ void DialogStyleManager::MoveStyles(bool storage, int type) {
// Replace styles
size_t curn = 0;
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]);
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;
it = new_style_at_pos;
}

View File

@ -48,8 +48,12 @@
#include "utils.h"
#include "video_context.h"
#include <libaegisub/address_of_adaptor.h>
#include <algorithm>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <functional>
#include <wx/button.h>
@ -63,6 +67,8 @@
#include <wx/textctrl.h>
#include <wx/valnum.h>
using namespace boost::adaptors;
namespace {
using std::placeholders::_1;
@ -288,10 +294,6 @@ void DialogTimingProcessor::OnApply(wxCommandEvent &) {
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::set<std::string> styles;
for (size_t i = 0; i < StyleList->GetCount(); ++i) {
@ -301,14 +303,13 @@ std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
std::vector<AssDialogue*> sorted;
if (onlySelection->IsChecked()) {
SubtitleSelection sel = c->selectionController->GetSelectedSet();
copy_if(sel.begin(), sel.end(), back_inserter(sorted),
[&](AssDialogue *d) { return !d->Comment && styles.count(d->Style); });
}
auto valid_line = [&](const AssDialogue *d) { return !d->Comment && styles.count(d->Style); };
if (onlySelection->IsChecked())
boost::copy(c->selectionController->GetSelectedSet() | filtered(valid_line),
back_inserter(sorted));
else {
transform(c->ass->Events.begin(), c->ass->Events.end(), back_inserter(sorted), cast<AssDialogue*>());
sorted.erase(boost::remove_if(sorted, bind(bad_line, &styles, _1)), sorted.end());
sorted.reserve(c->ass->Events.size());
boost::push_back(sorted, c->ass->Events | agi::address_of | filtered(valid_line));
}
// 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;
}

View File

@ -40,11 +40,8 @@
#include "ass_dialogue.h"
#include "compat.h"
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <functional>
#include <wx/intl.h>
AssFixStylesFilter::AssFixStylesFilter()
@ -53,12 +50,12 @@ AssFixStylesFilter::AssFixStylesFilter()
}
void AssFixStylesFilter::ProcessSubs(AssFile *subs, wxWindow *) {
std::vector<std::string> styles = subs->GetStyles();
for_each(begin(styles), end(styles), [](std::string& str) { boost::to_lower(str); });
auto styles = subs->GetStyles();
for (auto& str : styles) boost::to_lower(str);
sort(begin(styles), end(styles));
for (auto diag : subs->Events | agi::of_type<AssDialogue>()) {
if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag->Style.get())))
diag->Style = "Default";
for (auto& diag : subs->Events) {
if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag.Style.get())))
diag.Style = "Default";
}
}

View File

@ -201,20 +201,20 @@ void AssTransformFramerateFilter::TransformTimeTags(std::string const& name, Ass
void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs) {
if (!Input->IsLoaded() || !Output->IsLoaded()) return;
for (auto curDialogue : subs->Events | agi::of_type<AssDialogue>()) {
line = curDialogue;
for (auto& curDialogue : subs->Events) {
line = &curDialogue;
newK = 0;
oldK = 0;
newStart = trunc_cs(ConvertTime(curDialogue->Start));
newEnd = trunc_cs(ConvertTime(curDialogue->End) + 9);
newStart = trunc_cs(ConvertTime(curDialogue.Start));
newEnd = trunc_cs(ConvertTime(curDialogue.End) + 9);
// Process stuff
boost::ptr_vector<AssDialogueBlock> blocks;
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
block->ProcessParameters(TransformTimeTags, this);
curDialogue->Start = newStart;
curDialogue->End = newEnd;
curDialogue->UpdateText(blocks);
curDialogue.Start = newStart;
curDialogue.End = newEnd;
curDialogue.UpdateText(blocks);
}
}

View File

@ -29,8 +29,6 @@
#include "compat.h"
#include "utils.h"
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <tuple>
#include <unicode/uchar.h>
@ -182,17 +180,17 @@ std::vector<agi::fs::path> FontCollector::GetFontPaths(const AssFile *file) {
status_callback(_("Parsing file\n"), 0);
for (auto style : file->Styles | agi::of_type<const AssStyle>()) {
StyleInfo &info = styles[style->name];
info.facename = style->font;
info.bold = style->bold;
info.italic = style->italic;
used_styles[info].styles.insert(style->name);
for (auto const& style : file->Styles) {
StyleInfo &info = styles[style.name];
info.facename = style.font;
info.bold = style.bold;
info.italic = style.italic;
used_styles[info].styles.insert(style.name);
}
int index = 0;
for (auto diag : file->Events | agi::of_type<const AssDialogue>())
ProcessDialogueLine(diag, ++index);
for (auto const& diag : file->Events)
ProcessDialogueLine(&diag, ++index);
status_callback(_("Searching for font files\n"), 0);
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);
}
void resample_line(resample_state *state, AssEntry &line) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(&line);
if (diag && !(diag->Comment && (boost::starts_with(diag->Effect.get(), "template") || boost::starts_with(diag->Effect.get(), "code")))) {
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
void resample_line(resample_state *state, AssDialogue &diag) {
if (diag.Comment && (boost::starts_with(diag.Effect.get(), "template") || boost::starts_with(diag.Effect.get(), "code")))
return;
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
block->ProcessParameters(resample_tags, state);
boost::ptr_vector<AssDialogueBlock> blocks(diag.ParseTags());
for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
drawing->text = transform_drawing(drawing->text, state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
block->ProcessParameters(resample_tags, state);
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);
for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
drawing->text = transform_drawing(drawing->text, state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
diag->UpdateText(blocks);
}
else if (AssStyle *style = dynamic_cast<AssStyle*>(&line)) {
style->fontsize = int(style->fontsize * state->ry + 0.5);
style->outline_w *= state->ry;
style->shadow_w *= state->ry;
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();
}
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);
diag.UpdateText(blocks);
}
void resample_style(resample_state *state, AssStyle &style) {
style.fontsize = int(style.fontsize * state->ry + 0.5);
style.outline_w *= state->ry;
style.shadow_w *= state->ry;
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;
for (auto& line : ass->Styles)
resample_line(&state, line);
resample_style(&state, line);
for (auto& line : ass->Events)
resample_line(&state, line);

View File

@ -24,7 +24,6 @@
#include "selection_controller.h"
#include "text_selection_controller.h"
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#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;
do {
AssDialogue *diag = dynamic_cast<AssDialogue*>(&*it);
if (!diag) continue;
if (selection_only && !sel.count(diag)) continue;
if (settings.ignore_comments && diag->Comment) continue;
if (selection_only && !sel.count(&*it)) continue;
if (settings.ignore_comments && it->Comment) continue;
if (MatchState ms = matches(diag, pos)) {
if (MatchState ms = matches(&*it, pos)) {
if (selection_only)
// We're cycling through the selection, so don't muck with it
context->selectionController->SetActiveLine(diag);
context->selectionController->SetActiveLine(&*it);
else
context->selectionController->SetSelectionAndActive({ diag }, diag);
context->selectionController->SetSelectionAndActive({ &*it }, &*it);
if (settings.field == SearchReplaceSettings::Field::TEXT)
context->textSelectionController->SetSelection(ms.start, ms.end);
@ -307,13 +304,13 @@ bool SearchReplaceEngine::ReplaceAll() {
SubtitleSelection const& sel = context->selectionController->GetSelectedSet();
bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
for (auto diag : context->ass->Events | agi::of_type<AssDialogue>()) {
if (selection_only && !sel.count(diag)) continue;
if (settings.ignore_comments && diag->Comment) continue;
for (auto& diag : context->ass->Events) {
if (selection_only && !sel.count(&diag)) continue;
if (settings.ignore_comments && diag.Comment) continue;
if (settings.use_regex) {
if (MatchState ms = matches(diag, 0)) {
auto& diag_field = diag->*get_dialogue_field(settings.field);
if (MatchState ms = matches(&diag, 0)) {
auto& diag_field = diag.*get_dialogue_field(settings.field);
std::string const& text = diag_field.get();
count += distance(
boost::u32regex_iterator<std::string::const_iterator>(begin(text), end(text), *ms.re),
@ -324,9 +321,9 @@ bool SearchReplaceEngine::ReplaceAll() {
}
size_t pos = 0;
while (MatchState ms = matches(diag, pos)) {
while (MatchState ms = matches(&diag, pos)) {
++count;
Replace(diag, ms);
Replace(&diag, ms);
pos = ms.end;
}
}

View File

@ -70,26 +70,22 @@ struct SubsController::UndoInfo {
: undo_description(d), commit_id(commit_id)
{
script_info.reserve(c->ass->Info.size());
for (auto const& line : c->ass->Info) {
auto info = static_cast<const AssInfo *>(&line);
script_info.emplace_back(info->Key(), info->Value());
}
for (auto const& info : c->ass->Info)
script_info.emplace_back(info.Key(), info.Value());
styles.reserve(c->ass->Styles.size());
for (auto const& line : c->ass->Styles)
styles.push_back(static_cast<AssStyle const&>(line));
styles.assign(c->ass->Styles.begin(), c->ass->Styles.end());
events.reserve(c->ass->Events.size());
for (auto const& line : c->ass->Events)
events.push_back(static_cast<AssDialogue const&>(line));
events.assign(c->ass->Events.begin(), c->ass->Events.end());
for (auto const& line : c->ass->Attachments) {
switch (line.Group()) {
case AssEntryGroup::FONT:
fonts.push_back(static_cast<AssAttachment const&>(line));
fonts.push_back(line);
break;
case AssEntryGroup::GRAPHIC:
graphics.push_back(static_cast<AssAttachment const&>(line));
graphics.push_back(line);
break;
default:
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 only one line changed just modify it instead of copying the file
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) {
if (diag.Id == src_diag->Id) {
diag = *src_diag;
if (diag.Id == c.single_line->Id) {
diag = *c.single_line;
break;
}
}

View File

@ -23,7 +23,6 @@
#include <wx/timer.h>
class AssDialogue;
class AssEntry;
class AssFile;
struct AssFileCommit;
template<typename T> class SelectionController;

View File

@ -56,7 +56,6 @@
#include "video_context.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <functional>
@ -344,7 +343,7 @@ void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<std::string>
std::unordered_set<std::string> values;
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())
values.insert(value);
}

View File

@ -57,7 +57,6 @@
#include "video_context.h"
#include <libaegisub/fs.h>
#include <libaegisub/of_type_adaptor.h>
#include <algorithm>
#include <boost/algorithm/string/join.hpp>
@ -94,13 +93,12 @@ bool SubtitleFormat::CanSave(const AssFile *subs) const {
std::string defstyle = AssStyle().GetEntryData();
for (auto const& line : subs->Styles) {
if (static_cast<const AssStyle *>(&line)->GetEntryData() != defstyle)
if (line.GetEntryData() != defstyle)
return false;
}
for (auto const& line : subs->Events) {
auto diag = static_cast<const AssDialogue *>(&line);
if (diag->GetStrippedText() != diag->Text)
if (line.GetStrippedText() != line.Text)
return false;
}
@ -173,13 +171,13 @@ agi::vfr::Framerate SubtitleFormat::AskForFPS(bool allow_vfr, bool show_smpte) {
}
void SubtitleFormat::StripTags(AssFile &file) {
for (auto current : file.Events | agi::of_type<AssDialogue>())
current->StripTags();
for (auto& current : file.Events)
current.StripTags();
}
void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks) {
for (auto current : file.Events | agi::of_type<AssDialogue>()) {
std::string repl = current->Text;
for (auto& current : file.Events) {
std::string repl = current.Text;
boost::replace_all(repl, "\\h", " ");
boost::ireplace_all(repl, "\\n", newline);
if (mergeLineBreaks) {
@ -188,39 +186,35 @@ void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline,
while ((pos = repl.find(dbl, pos)) != std::string::npos)
boost::replace_all(repl, dbl, newline);
}
current->Text = repl;
current.Text = repl;
}
}
void SubtitleFormat::StripComments(AssFile &file) {
file.Events.remove_and_dispose_if([](AssEntry const& e) {
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
return diag && (diag->Comment || diag->Text.get().empty());
}, [](AssEntry *e) { delete e; });
file.Events.remove_and_dispose_if([](AssDialogue const& diag) {
return diag.Comment || diag.Text.get().empty();
}, [](AssDialogue *e) { delete e; });
}
static bool dialog_start_lt(AssEntry &pos, AssDialogue *to_insert) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(&pos);
return diag && diag->Start > to_insert->Start;
static bool dialog_start_lt(AssDialogue &pos, AssDialogue *to_insert) {
return pos.Start > to_insert->Start;
}
/// @brief Split and merge lines so there are no overlapping lines
///
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
void SubtitleFormat::RecombineOverlaps(AssFile &file) {
entryIter cur, next = file.Events.begin();
cur = next++;
auto next = file.Events.begin();
auto cur = next++;
for (; next != file.Events.end(); cur = next++) {
AssDialogue *prevdlg = dynamic_cast<AssDialogue*>(&*cur);
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(&*next);
if (!curdlg || !prevdlg) continue;
AssDialogue *prevdlg = &*cur;
AssDialogue *curdlg = &*next;
if (prevdlg->End <= curdlg->Start) continue;
// Use names like in the algorithm description and prepare for erasing
// old dialogues from the list
entryIter prev = cur;
auto prev = cur;
cur = next;
next++;
@ -280,20 +274,17 @@ void SubtitleFormat::RecombineOverlaps(AssFile &file) {
/// @brief Merge identical lines that follow each other
void SubtitleFormat::MergeIdentical(AssFile &file) {
entryIter cur, next = file.Events.begin();
cur = next++;
auto next = file.Events.begin();
auto cur = next++;
for (; next != file.Events.end(); cur = next++) {
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(&*cur);
AssDialogue *nextdlg = dynamic_cast<AssDialogue*>(&*next);
if (curdlg && nextdlg && curdlg->End == nextdlg->Start && curdlg->Text == nextdlg->Text) {
if (cur->End == next->Start && cur->Text == next->Text) {
// Merge timing
nextdlg->Start = std::min(nextdlg->Start, curdlg->Start);
nextdlg->End = std::max(nextdlg->End, curdlg->End);
next->Start = std::min(next->Start, cur->Start);
next->End = std::max(next->End, cur->End);
// Remove duplicate line
delete curdlg;
delete &*cur;
}
}
}

View File

@ -40,7 +40,6 @@
#include <string>
#include <vector>
class AssEntry;
class AssFile;
namespace agi { namespace vfr { class Framerate; } }

View File

@ -1,42 +1,28 @@
// Copyright (c) 2006, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from 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.
// 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/
/// @file subtitle_format_ass.cpp
/// @brief Reading/writing of SSA-lineage subtitles
/// @ingroup subtitle_io
///
#include "config.h"
#include "subtitle_format_ass.h"
#include "ass_attachment.h"
#include "ass_dialogue.h"
#include "ass_info.h"
#include "ass_file.h"
#include "ass_style.h"
#include "ass_parser.h"
#include "text_file_reader.h"
#include "text_file_writer.h"
@ -52,17 +38,11 @@ AssSubtitleFormat::AssSubtitleFormat()
}
std::vector<std::string> AssSubtitleFormat::GetReadWildcards() const {
std::vector<std::string> formats;
formats.push_back("ass");
formats.push_back("ssa");
return formats;
return {"ass", "ssa"};
}
std::vector<std::string> AssSubtitleFormat::GetWriteWildcards() const {
std::vector<std::string> formats;
formats.push_back("ass");
formats.push_back("ssa");
return formats;
return {"ass", "ssa"};
}
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"
#endif
static inline std::string format(AssEntryGroup group, bool ssa) {
namespace {
std::string format(AssEntryGroup group, bool ssa) {
if (group == AssEntryGroup::DIALOGUE) {
if (ssa)
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 "";
}
void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const {
TextFileWriter file(filename, encoding);
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");
struct Writer {
TextFileWriter file;
bool ssa;
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) {
if (line.Group() != 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());
}
};
write(src->Info);
write(src->Styles);
write(src->Attachments);
write(src->Events);
}
};
}
void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const {
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/io.h>
#include <libaegisub/line_wrap.h>
#include <libaegisub/of_type_adaptor.h>
#include <boost/algorithm/string/replace.hpp>
#include <fstream>
@ -373,7 +372,7 @@ namespace
subs_list.reserve(copy.Events.size());
// 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
subs_list.emplace_back();
@ -385,19 +384,19 @@ namespace
imline.cumulative_status = EbuSubtitle::NotCumulative;
// convert times
imline.time_in = fps.FrameAtTime(line->Start) + timecode_bias;
imline.time_out = fps.FrameAtTime(line->End) + timecode_bias;
imline.time_in = fps.FrameAtTime(line.Start) + timecode_bias;
imline.time_out = fps.FrameAtTime(line.End) + timecode_bias;
if (export_settings.inclusive_end_times)
// cheap and possibly wrong way to ensure exclusive times, subtract one frame from end time
imline.time_out -= 1;
// convert alignment from style
AssStyle *style = copy.GetStyle(line->Style);
AssStyle *style = copy.GetStyle(line.Style);
if (!style)
style = &default_style;
// 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
if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrap)
@ -407,7 +406,7 @@ namespace
else if (!imline.CheckLineLengths(export_settings.max_line_length))
{
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
subs_list.pop_back();
}

View File

@ -40,8 +40,6 @@
#include "ass_file.h"
#include "text_file_writer.h"
#include <libaegisub/of_type_adaptor.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
@ -77,6 +75,6 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& fi
// Write lines
int i = 0;
TextFileWriter file(filename, "UTF-8");
for (auto current : copy.Events | agi::of_type<AssDialogue>())
file.WriteLineToFile(str(boost::format("%i %s %s %s") % ++i % ft.ToSMPTE(current->Start) % ft.ToSMPTE(current->End) % current->Text));
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));
}

View File

@ -44,7 +44,6 @@
#include "video_context.h"
#include <libaegisub/fs.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#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()));
// Write lines
for (auto current : copy.Events | agi::of_type<AssDialogue>()) {
int start = fps.FrameAtTime(current->Start, agi::vfr::START);
int end = fps.FrameAtTime(current->End, agi::vfr::END);
for (auto const& current : copy.Events) {
int start = fps.FrameAtTime(current.Start, agi::vfr::START);
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
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(WriteSRTTime(current->Start) + " --> " + WriteSRTTime(current->End));
file.WriteLineToFile(ConvertTags(current));
file.WriteLineToFile(WriteSRTTime(current.Start) + " --> " + WriteSRTTime(current.End));
file.WriteLineToFile(ConvertTags(&current));
file.WriteLineToFile("");
}
}
@ -496,13 +496,12 @@ bool SRTSubtitleFormat::CanSave(const AssFile *file) const {
std::string defstyle = AssStyle().GetEntryData();
for (auto const& line : file->Styles) {
if (static_cast<const AssStyle *>(&line)->GetEntryData() != defstyle)
if (line.GetEntryData() != defstyle)
return false;
}
for (auto const& line : file->Events) {
auto diag = static_cast<const AssDialogue *>(&line);
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
boost::ptr_vector<AssDialogueBlock> blocks(line.ParseTags());
for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
// Verify that all overrides used are supported
for (auto const& tag : ovr->Tags) {

View File

@ -44,8 +44,6 @@
#include "ass_time.h"
#include "text_file_writer.h"
#include <libaegisub/of_type_adaptor.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
@ -76,14 +74,14 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons
SmpteFormatter ft(fps);
TextFileWriter file(filename, encoding);
AssDialogue *prev = nullptr;
for (auto cur : copy.Events | agi::of_type<AssDialogue>()) {
const AssDialogue *prev = nullptr;
for (auto const& cur : copy.Events) {
if (prev) {
file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, cur->Start));
file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, cur.Start));
file.WriteLineToFile("");
}
prev = cur;
prev = &cur;
}
// flush last line
@ -94,7 +92,7 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons
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;
const char *halign = " "; // default is centered
const char *type = "N"; // no special style

View File

@ -38,7 +38,7 @@ class AssDialogue;
class SmpteFormatter;
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:
TranStationSubtitleFormat();

View File

@ -44,7 +44,6 @@
#include "compat.h"
#include "options.h"
#include <libaegisub/of_type_adaptor.h>
#include <boost/range/adaptor/reversed.hpp>
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
const AssDialogue *prev = nullptr;
for (auto current : copy.Events | agi::of_type<AssDialogue>()) {
WriteLine(root, prev, current);
prev = current;
for (auto const& current : copy.Events) {
WriteLine(root, prev, &current);
prev = &current;
}
// Save XML
@ -264,8 +263,8 @@ void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const {
// Find last line
AssTime lastTime;
for (auto line : file.Events | boost::adaptors::reversed | agi::of_type<AssDialogue>()) {
lastTime = line->End;
for (auto const& line : file.Events | boost::adaptors::reversed) {
lastTime = line.End;
break;
}

View File

@ -45,8 +45,6 @@
#include "utils.h"
#include "version.h"
#include <libaegisub/of_type_adaptor.h>
#include <boost/algorithm/string/predicate.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;
// Detect number of lines with Actor field filled out
for (auto dia : src->Events | agi::of_type<AssDialogue>()) {
if (!dia->Comment) {
for (auto const& dia : src->Events) {
if (!dia.Comment) {
num_dialogue_lines++;
if (!dia->Actor.get().empty())
if (!dia.Actor.get().empty())
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());
// Write the file
for (auto dia : src->Events | agi::of_type<AssDialogue>()) {
for (auto const& dia : src->Events) {
std::string out_line;
if (dia->Comment)
if (dia.Comment)
out_line = "# ";
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;
if (!out_text.empty())

View File

@ -41,7 +41,10 @@
#include <libaegisub/util_osx.h>
#endif
#include "ass_info.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_style.h"
#include "dialog_progress.h"
#include "frame_main.h"
#include "main.h"
@ -112,28 +115,35 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
if (ass_renderer) ass_renderer_done(ass_renderer);
}
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
std::vector<char> data;
data.clear();
data.reserve(0x4000);
namespace {
struct Writer {
std::vector<char> data;
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
auto write_group = [&](EntryList const& list) {
for (auto const& line : list) {
if (group != line.Group()) {
group = line.Group();
boost::push_back(data, line.GroupHeader() + "\r\n");
template<typename T>
void Write(T const& list) {
for (auto const& line : list) {
if (group != line.Group()) {
group = line.Group();
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);
write_group(subs->Styles);
write_group(subs->Events);
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
Writer writer;
writer.data.reserve(0x4000);
writer.Write(subs->Info);
writer.Write(subs->Styles);
writer.Write(subs->Events);
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.";
}

View File

@ -28,6 +28,7 @@
#include "video_frame.h"
#include "video_provider_manager.h"
#include <libaegisub/address_of_adaptor.h>
#include <libaegisub/dispatch.h>
#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
// instead muck around with its innards to just temporarily
// remove the non-visible lines without deleting them
std::deque<AssEntry*> full;
for (auto& line : subs->Events)
full.push_back(&line);
subs->Events.remove_if([=](AssEntry const& e) -> bool {
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
return diag && (diag->Start > time || diag->End <= time);
std::deque<AssDialogue*> full;
boost::push_back(full, subs->Events | agi::address_of);
subs->Events.remove_if([=](AssDialogue const& diag) {
return diag.Start > time || diag.End <= time;
});
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;
// 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
std::deque<std::pair<size_t, AssEntry*>> changed;
std::deque<std::pair<size_t, AssDialogue*>> changed;
size_t i = 0;
for (auto const& e : new_subs->Events) {
if (changes.count(&e))

View File

@ -29,7 +29,7 @@
#include <wx/event.h>
class AssEntry;
class AssDialogue;
class AssFile;
class SubtitlesProvider;
class VideoProvider;
@ -84,7 +84,7 @@ public:
///
/// This function only supports changes to existing lines, and not
/// 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 frame Frame number

View File

@ -115,19 +115,6 @@ void SetClipboard(wxBitmap const& new_value);
#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);
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 (changed.empty() || no_amend)
@ -436,7 +436,7 @@ void VideoContext::LoadTimecodes(agi::fs::path const& filename) {
ovr_fps = agi::vfr::Framerate(filename);
timecodes_filename = filename;
config::mru->Add("Timecodes", filename);
OnSubtitlesCommit(0, std::set<const AssEntry*>());
OnSubtitlesCommit(0, std::set<const AssDialogue*>());
TimecodesOpen(ovr_fps);
}
catch (agi::fs::FileSystemError const& err) {
@ -460,7 +460,7 @@ void VideoContext::SaveTimecodes(agi::fs::path const& filename) {
void VideoContext::CloseTimecodes() {
ovr_fps = agi::vfr::Framerate();
timecodes_filename.clear();
OnSubtitlesCommit(0, std::set<const AssEntry*>());
OnSubtitlesCommit(0, std::set<const AssDialogue*>());
TimecodesOpen(video_fps);
}

View File

@ -45,11 +45,11 @@
#include <wx/timer.h>
class AssEntry;
struct SubtitlesProviderErrorEvent;
class AssDialogue;
class ThreadedFrameSource;
struct VideoFrame;
class VideoProvider;
struct SubtitlesProviderErrorEvent;
struct VideoFrame;
struct VideoProviderErrorEvent;
namespace agi {
@ -152,7 +152,7 @@ class VideoContext : public wxEvtHandler {
void OnVideoError(VideoProviderErrorEvent 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();
/// Close the video, keyframes and timecodes

View File

@ -31,7 +31,6 @@
#include "video_context.h"
#include "video_display.h"
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <algorithm>
@ -115,9 +114,9 @@ void VisualToolDrag::OnFileChanged() {
primary = nullptr;
active_feature = nullptr;
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (IsDisplayed(diag))
MakeFeatures(diag);
for (auto& diag : c->ass->Events) {
if (IsDisplayed(&diag))
MakeFeatures(&diag);
}
UpdateToggleButtons();
@ -130,18 +129,18 @@ void VisualToolDrag::OnFrameChanged() {
auto feat = features.begin();
auto end = features.end();
for (auto diag : c->ass->Events | agi::of_type<AssDialogue>()) {
if (IsDisplayed(diag)) {
for (auto& diag : c->ass->Events) {
if (IsDisplayed(&diag)) {
// Features don't exist and should
if (feat == end || feat->line != diag)
MakeFeatures(diag, feat);
if (feat == end || feat->line != &diag)
MakeFeatures(&diag, feat);
// Move past already existing features for the line
else
while (feat != end && feat->line == diag) ++feat;
while (feat != end && feat->line == &diag) ++feat;
}
else {
// 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;
feat->line = nullptr;
RemoveSelection(&*feat);