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