forked from mia/Aegisub
Extract Loading/Saving/Undo stuff from AssFile
Add SubsController, which deals with things like what subtitle file is currently open, rather than the contents of the current subtitle file. Move the rest of the relevant logic from FrameMain there in addition to all of the stuff from AssFile.
This commit is contained in:
parent
9a146d1fc0
commit
a0d3dbc550
30 changed files with 564 additions and 471 deletions
|
@ -265,6 +265,7 @@
|
|||
<ClInclude Include="$(SrcDir)ass_info.h" />
|
||||
<ClInclude Include="$(SrcDir)options.h" />
|
||||
<ClInclude Include="$(SrcDir)search_replace_engine.h" />
|
||||
<ClInclude Include="$(SrcDir)subs_controller.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SrcDir)aegisublocale.cpp" />
|
||||
|
@ -458,6 +459,7 @@
|
|||
<ClCompile Include="$(SrcDir)dialog_autosave.cpp" />
|
||||
<ClCompile Include="$(SrcDir)search_replace_engine.cpp" />
|
||||
<ClCompile Include="$(SrcDir)initial_line_state.cpp" />
|
||||
<ClCompile Include="$(SrcDir)subs_controller.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="$(SrcDir)res.rc" />
|
||||
|
|
|
@ -687,6 +687,9 @@
|
|||
<ClInclude Include="$(SrcDir)initial_line_state.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)subs_controller.h">
|
||||
<Filter>ASS</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SrcDir)ass_dialogue.cpp">
|
||||
|
@ -1235,6 +1238,9 @@
|
|||
<ClCompile Include="$(SrcDir)initial_line_state.cpp">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)subs_controller.cpp">
|
||||
<Filter>ASS</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="$(SrcDir)res.rc">
|
||||
|
|
|
@ -221,6 +221,7 @@ SRC += \
|
|||
spline_curve.cpp \
|
||||
standard_paths.cpp \
|
||||
string_codec.cpp \
|
||||
subs_controller.cpp \
|
||||
subs_edit_box.cpp \
|
||||
subs_edit_ctrl.cpp \
|
||||
subs_grid.cpp \
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "ass_file.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "subtitle_format.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
@ -106,7 +107,11 @@ AssFile *AssExporter::ExportTransform(wxWindow *export_dialog, bool copy) {
|
|||
|
||||
void AssExporter::Export(agi::fs::path const& filename, std::string const& charset, wxWindow *export_dialog) {
|
||||
std::unique_ptr<AssFile> subs(ExportTransform(export_dialog, true));
|
||||
subs->Save(filename, false, false, charset);
|
||||
const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
|
||||
if (!writer)
|
||||
throw "Unknown file type.";
|
||||
|
||||
writer->WriteFile(subs.get(), filename, charset);
|
||||
}
|
||||
|
||||
wxSizer *AssExporter::GetSettingsSizer(std::string const& name) {
|
||||
|
|
|
@ -40,23 +40,14 @@
|
|||
#include "ass_info.h"
|
||||
#include "ass_style.h"
|
||||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "text_file_reader.h"
|
||||
#include "text_file_writer.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/dispatch.h>
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/of_type_adaptor.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <list>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
|
@ -65,136 +56,12 @@ namespace std {
|
|||
}
|
||||
}
|
||||
|
||||
AssFile::AssFile ()
|
||||
: commitId(0)
|
||||
{
|
||||
}
|
||||
|
||||
AssFile::~AssFile() {
|
||||
auto copy = new EntryList;
|
||||
copy->swap(Line);
|
||||
agi::dispatch::Background().Async([=]{ delete copy; });
|
||||
}
|
||||
|
||||
/// @brief Load generic subs
|
||||
void AssFile::Load(agi::fs::path const& filename, std::string const& charset) {
|
||||
const SubtitleFormat *reader = SubtitleFormat::GetReader(filename);
|
||||
|
||||
try {
|
||||
AssFile temp;
|
||||
reader->ReadFile(&temp, filename, charset);
|
||||
|
||||
bool found_style = false;
|
||||
bool found_dialogue = false;
|
||||
|
||||
// Check if the file has at least one style and at least one dialogue line
|
||||
for (auto const& line : temp.Line) {
|
||||
AssEntryGroup type = line.Group();
|
||||
if (type == ENTRY_STYLE) found_style = true;
|
||||
if (type == ENTRY_DIALOGUE) found_dialogue = true;
|
||||
if (found_style && found_dialogue) break;
|
||||
}
|
||||
|
||||
// And if it doesn't add defaults for each
|
||||
if (!found_style)
|
||||
temp.InsertLine(new AssStyle);
|
||||
if (!found_dialogue)
|
||||
temp.InsertLine(new AssDialogue);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
catch (agi::UserCancelException const&) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set general data
|
||||
this->filename = filename;
|
||||
|
||||
// Add comments and set vars
|
||||
SetScriptInfo("ScriptType", "v4.00+");
|
||||
|
||||
// Push the initial state of the file onto the undo stack
|
||||
UndoStack.clear();
|
||||
RedoStack.clear();
|
||||
undoDescription.clear();
|
||||
autosavedCommitId = savedCommitId = commitId + 1;
|
||||
Commit("", COMMIT_NEW);
|
||||
|
||||
FileOpen(filename);
|
||||
}
|
||||
|
||||
void AssFile::Save(agi::fs::path const& filename, bool setfilename, bool addToRecent, std::string const& encoding) {
|
||||
const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
|
||||
if (!writer)
|
||||
throw "Unknown file type.";
|
||||
|
||||
if (setfilename) {
|
||||
autosavedCommitId = savedCommitId = commitId;
|
||||
this->filename = filename;
|
||||
StandardPaths::SetPathValue("?script", filename.parent_path());
|
||||
}
|
||||
|
||||
FileSave();
|
||||
|
||||
writer->WriteFile(this, filename, encoding);
|
||||
|
||||
if (addToRecent)
|
||||
AddToRecent(filename);
|
||||
}
|
||||
|
||||
agi::fs::path AssFile::AutoSave() {
|
||||
if (commitId == autosavedCommitId)
|
||||
return "";
|
||||
|
||||
auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString());
|
||||
if (path.empty())
|
||||
path = filename.parent_path();
|
||||
|
||||
agi::fs::CreateDirectory(path);
|
||||
|
||||
auto name = filename.filename();
|
||||
if (name.empty())
|
||||
name = "Untitled";
|
||||
|
||||
path /= str(boost::format("%s.%s.AUTOSAVE.ass") % name % agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
|
||||
|
||||
Save(path, false, false);
|
||||
|
||||
autosavedCommitId = commitId;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void AssFile::SaveMemory(std::vector<char> &dst) {
|
||||
// Check if subs contain at least one style
|
||||
// Add a default style if they don't for compatibility with libass/asa
|
||||
if (GetStyles().empty())
|
||||
InsertLine(new AssStyle);
|
||||
|
||||
// Prepare vector
|
||||
dst.clear();
|
||||
dst.reserve(0x4000);
|
||||
|
||||
// Write file
|
||||
AssEntryGroup group = ENTRY_GROUP_MAX;
|
||||
for (auto const& line : Line) {
|
||||
if (group != line.Group()) {
|
||||
group = line.Group();
|
||||
boost::push_back(dst, line.GroupHeader() + "\r\n");
|
||||
}
|
||||
boost::push_back(dst, line.GetEntryData() + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool AssFile::CanSave() const {
|
||||
try {
|
||||
return SubtitleFormat::GetWriter(filename)->CanSave(this);
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AssFile::LoadDefault(bool defline) {
|
||||
Line.push_back(*new AssInfo("Title", "Default Aegisub file"));
|
||||
Line.push_back(*new AssInfo("ScriptType", "v4.00+"));
|
||||
|
@ -211,26 +78,13 @@ void AssFile::LoadDefault(bool defline) {
|
|||
|
||||
if (defline)
|
||||
Line.push_back(*new AssDialogue);
|
||||
|
||||
autosavedCommitId = savedCommitId = commitId + 1;
|
||||
Commit("", COMMIT_NEW);
|
||||
StandardPaths::SetPathValue("?script", "");
|
||||
FileOpen("");
|
||||
}
|
||||
|
||||
void AssFile::swap(AssFile &that) throw() {
|
||||
// Intentionally does not swap undo stack related things
|
||||
using std::swap;
|
||||
swap(commitId, that.commitId);
|
||||
swap(undoDescription, that.undoDescription);
|
||||
swap(Line, that.Line);
|
||||
Line.swap(that.Line);
|
||||
}
|
||||
|
||||
AssFile::AssFile(const AssFile &from)
|
||||
: undoDescription(from.undoDescription)
|
||||
, commitId(from.commitId)
|
||||
, filename(from.filename)
|
||||
{
|
||||
AssFile::AssFile(const AssFile &from) {
|
||||
Line.clone_from(from.Line, std::mem_fun_ref(&AssEntry::Clone), delete_ptr());
|
||||
}
|
||||
AssFile& AssFile::operator=(AssFile from) {
|
||||
|
@ -332,83 +186,17 @@ AssStyle *AssFile::GetStyle(std::string const& name) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void AssFile::AddToRecent(agi::fs::path const& file) const {
|
||||
config::mru->Add("Subtitle", file);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(file.parent_path().string());
|
||||
}
|
||||
int AssFile::Commit(wxString const& desc, int type, int amend_id, AssEntry *single_line) {
|
||||
AssFileCommit c = { desc, &amend_id, single_line };
|
||||
PushState(c);
|
||||
|
||||
int AssFile::Commit(wxString const& desc, int type, int amendId, AssEntry *single_line) {
|
||||
std::set<const AssEntry*> changed_lines;
|
||||
if (single_line)
|
||||
changed_lines.insert(single_line);
|
||||
|
||||
++commitId;
|
||||
// Allow coalescing only if it's the last change and the file has not been
|
||||
// saved since the last change
|
||||
if (commitId == amendId+1 && RedoStack.empty() && savedCommitId+1 != commitId && autosavedCommitId+1 != commitId) {
|
||||
// If only one line changed just modify it instead of copying the file
|
||||
if (single_line) {
|
||||
entryIter this_it = Line.begin(), undo_it = UndoStack.back().Line.begin();
|
||||
while (&*this_it != single_line) {
|
||||
++this_it;
|
||||
++undo_it;
|
||||
}
|
||||
UndoStack.back().Line.insert(undo_it, *single_line->Clone());
|
||||
delete &*undo_it;
|
||||
}
|
||||
else {
|
||||
UndoStack.back() = *this;
|
||||
}
|
||||
AnnounceCommit(type, changed_lines);
|
||||
return commitId;
|
||||
}
|
||||
|
||||
RedoStack.clear();
|
||||
|
||||
// Place copy on stack
|
||||
undoDescription = desc;
|
||||
UndoStack.push_back(*this);
|
||||
|
||||
// Cap depth
|
||||
int depth = std::max<int>(OPT_GET("Limits/Undo Levels")->GetInt(), 2);
|
||||
while ((int)UndoStack.size() > depth) {
|
||||
UndoStack.pop_front();
|
||||
}
|
||||
|
||||
if (UndoStack.size() > 1 && OPT_GET("App/Auto/Save on Every Change")->GetBool() && !filename.empty() && CanSave())
|
||||
Save(filename);
|
||||
|
||||
AnnounceCommit(type, changed_lines);
|
||||
return commitId;
|
||||
}
|
||||
|
||||
void AssFile::Undo() {
|
||||
if (UndoStack.size() <= 1) return;
|
||||
|
||||
RedoStack.emplace_back();
|
||||
swap(RedoStack.back());
|
||||
UndoStack.pop_back();
|
||||
*this = UndoStack.back();
|
||||
|
||||
AnnounceCommit(COMMIT_NEW, std::set<const AssEntry*>());
|
||||
}
|
||||
|
||||
void AssFile::Redo() {
|
||||
if (RedoStack.empty()) return;
|
||||
|
||||
swap(RedoStack.back());
|
||||
UndoStack.push_back(*this);
|
||||
RedoStack.pop_back();
|
||||
|
||||
AnnounceCommit(COMMIT_NEW, std::set<const AssEntry*>());
|
||||
}
|
||||
|
||||
wxString AssFile::GetUndoDescription() const {
|
||||
return IsUndoStackEmpty() ? "" : UndoStack.back().undoDescription;
|
||||
}
|
||||
|
||||
wxString AssFile::GetRedoDescription() const {
|
||||
return IsRedoStackEmpty() ? "" : RedoStack.back().undoDescription;
|
||||
return amend_id;
|
||||
}
|
||||
|
||||
bool AssFile::CompStart(const AssDialogue* lft, const AssDialogue* rgt) {
|
||||
|
@ -434,13 +222,6 @@ void AssFile::Sort(CompFunc comp, std::set<AssDialogue*> const& limit) {
|
|||
Sort(Line, comp, limit);
|
||||
}
|
||||
namespace {
|
||||
struct AssEntryComp : public std::binary_function<AssEntry, AssEntry, bool> {
|
||||
AssFile::CompFunc comp;
|
||||
bool operator()(AssEntry const&a, AssEntry const&b) const {
|
||||
return comp(static_cast<const AssDialogue*>(&a), static_cast<const AssDialogue*>(&b));
|
||||
}
|
||||
};
|
||||
|
||||
inline bool is_dialogue(AssEntry *e, std::set<AssDialogue*> const& limit) {
|
||||
AssDialogue *d = dynamic_cast<AssDialogue*>(e);
|
||||
return d && (limit.empty() || limit.count(d));
|
||||
|
@ -448,8 +229,10 @@ namespace {
|
|||
}
|
||||
|
||||
void AssFile::Sort(EntryList &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
|
||||
AssEntryComp compE;
|
||||
compE.comp = comp;
|
||||
auto compE = [&](AssEntry const& a, AssEntry const& b) {
|
||||
return comp(static_cast<const AssDialogue*>(&a), static_cast<const AssDialogue*>(&b));
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
@ -465,5 +248,3 @@ void AssFile::Sort(EntryList &lst, CompFunc comp, std::set<AssDialogue*> const&
|
|||
begin = --end;
|
||||
}
|
||||
}
|
||||
|
||||
AssFile *AssFile::top;
|
||||
|
|
|
@ -32,61 +32,43 @@
|
|||
/// @ingroup subs_storage
|
||||
///
|
||||
|
||||
#include <boost/container/list.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
#include "ass_entry.h"
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
#include "ass_entry.h"
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class AssDialogue;
|
||||
class AssStyle;
|
||||
class AssAttachment;
|
||||
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;
|
||||
|
||||
struct AssFileCommit {
|
||||
wxString const& message;
|
||||
int *commit_id;
|
||||
AssEntry *single_line;
|
||||
};
|
||||
|
||||
class AssFile {
|
||||
boost::container::list<AssFile> UndoStack;
|
||||
boost::container::list<AssFile> RedoStack;
|
||||
wxString undoDescription;
|
||||
/// Revision counter for undo coalescing and modified state tracking
|
||||
int commitId;
|
||||
/// Last saved version of this file
|
||||
int savedCommitId;
|
||||
/// Last autosaved version of this file
|
||||
int autosavedCommitId;
|
||||
|
||||
/// A set of changes has been committed to the file (AssFile::CommitType)
|
||||
/// A set of changes has been committed to the file (AssFile::COMMITType)
|
||||
agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit;
|
||||
/// A new file has been opened (filename)
|
||||
agi::signal::Signal<agi::fs::path> FileOpen;
|
||||
/// The file is about to be saved
|
||||
/// This signal is intended for adding metadata such as video filename,
|
||||
/// frame number, etc. Ideally this would all be done immediately rather
|
||||
/// than waiting for a save, but that causes (more) issues with undo
|
||||
agi::signal::Signal<> FileSave;
|
||||
|
||||
agi::signal::Signal<AssFileCommit> PushState;
|
||||
public:
|
||||
/// The lines in the file
|
||||
EntryList Line;
|
||||
/// The filename of this file, if any
|
||||
agi::fs::path filename;
|
||||
|
||||
AssFile();
|
||||
AssFile() { }
|
||||
AssFile(const AssFile &from);
|
||||
AssFile& operator=(AssFile from);
|
||||
~AssFile();
|
||||
|
||||
/// Does the file have unsaved changes?
|
||||
bool IsModified() const { return commitId != savedCommitId; };
|
||||
|
||||
/// @brief Load default file
|
||||
/// @param defline Add a blank line to the file
|
||||
void LoadDefault(bool defline=true);
|
||||
|
@ -103,30 +85,6 @@ public:
|
|||
|
||||
void swap(AssFile &) throw();
|
||||
|
||||
/// @brief Load from a file
|
||||
/// @param file File name
|
||||
/// @param charset Character set of file or empty to autodetect
|
||||
void Load(agi::fs::path const& file, std::string const& charset="");
|
||||
|
||||
/// @brief Save to a file
|
||||
/// @param file Path to save to
|
||||
/// @param setfilename Should the filename be changed to the passed path?
|
||||
/// @param addToRecent Should the file be added to the MRU list?
|
||||
/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
|
||||
void Save(agi::fs::path const& file, bool setfilename=false, bool addToRecent=true, std::string const& encoding="");
|
||||
|
||||
/// @brief Autosave the file if there have been any chances since the last autosave
|
||||
/// @return File name used or empty if no save was performed
|
||||
agi::fs::path AutoSave();
|
||||
|
||||
/// @brief Save to a memory buffer. Used for subtitle providers which support it
|
||||
/// @param[out] dst Destination vector
|
||||
void SaveMemory(std::vector<char> &dst);
|
||||
/// Add file name to the MRU list
|
||||
void AddToRecent(agi::fs::path const& file) const;
|
||||
/// Can the file be saved in its current format?
|
||||
bool CanSave() const;
|
||||
|
||||
/// @brief Get the script resolution
|
||||
/// @param[out] w Width
|
||||
/// @param[in] h Height
|
||||
|
@ -169,8 +127,7 @@ public:
|
|||
};
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceCommit, AddCommitListener)
|
||||
DEFINE_SIGNAL_ADDERS(FileOpen, AddFileOpenListener)
|
||||
DEFINE_SIGNAL_ADDERS(FileSave, AddFileSaveListener)
|
||||
DEFINE_SIGNAL_ADDERS(PushState, AddUndoManager)
|
||||
|
||||
/// @brief Flag the file as modified and push a copy onto the undo stack
|
||||
/// @param desc Undo description
|
||||
|
@ -179,21 +136,6 @@ public:
|
|||
/// @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 = 0);
|
||||
/// @brief Undo the last set of changes to the file
|
||||
void Undo();
|
||||
/// @brief Redo the last undone changes
|
||||
void Redo();
|
||||
/// Check if undo stack is empty
|
||||
bool IsUndoStackEmpty() const { return UndoStack.size() <= 1; };
|
||||
/// Check if redo stack is empty
|
||||
bool IsRedoStackEmpty() const { return RedoStack.empty(); };
|
||||
/// Get the description of the first undoable change
|
||||
wxString GetUndoDescription() const;
|
||||
/// Get the description of the first redoable change
|
||||
wxString GetRedoDescription() const;
|
||||
|
||||
/// Current script file. It is "above" the stack.
|
||||
static AssFile *top;
|
||||
|
||||
/// Comparison function for use when sorting
|
||||
typedef bool (*CompFunc)(const AssDialogue* lft, const AssDialogue* rgt);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "pen.h"
|
||||
#include "options.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_controller.h"
|
||||
#include "utils.h"
|
||||
#include "video_context.h"
|
||||
|
||||
|
@ -56,7 +57,7 @@
|
|||
|
||||
AudioController::AudioController(agi::Context *context)
|
||||
: context(context)
|
||||
, subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this))
|
||||
, subtitle_save_slot(context->subsController->AddFileSaveListener(&AudioController::OnSubtitlesSave, this))
|
||||
, player(0)
|
||||
, provider(0)
|
||||
, playback_mode(PM_NotPlaying)
|
||||
|
@ -238,9 +239,7 @@ void AudioController::SetTimingController(AudioTimingController *new_controller)
|
|||
void AudioController::OnTimingControllerUpdatedPrimaryRange()
|
||||
{
|
||||
if (playback_mode == PM_PrimaryRange)
|
||||
{
|
||||
player->SetEndPosition(SamplesFromMilliseconds(timing_controller->GetPrimaryPlaybackRange().end()));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioController::OnSubtitlesSave()
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "string_codec.h"
|
||||
#include "subs_controller.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -379,8 +380,8 @@ namespace Automation4 {
|
|||
LocalScriptManager::LocalScriptManager(agi::Context *c)
|
||||
: context(c)
|
||||
{
|
||||
slots.push_back(c->ass->AddFileSaveListener(&LocalScriptManager::OnSubtitlesSave, this));
|
||||
slots.push_back(c->ass->AddFileOpenListener(&LocalScriptManager::Reload, this));
|
||||
slots.push_back(c->subsController->AddFileSaveListener(&LocalScriptManager::OnSubtitlesSave, this));
|
||||
slots.push_back(c->subsController->AddFileOpenListener(&LocalScriptManager::Reload, this));
|
||||
}
|
||||
|
||||
void LocalScriptManager::Reload()
|
||||
|
@ -403,7 +404,7 @@ namespace Automation4 {
|
|||
|
||||
agi::fs::path basepath;
|
||||
if (first_char == '~') {
|
||||
basepath = context->ass->filename.parent_path();
|
||||
basepath = context->subsController->Filename().parent_path();
|
||||
} else if (first_char == '$') {
|
||||
basepath = autobasefn;
|
||||
} else if (first_char == '/') {
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "include/aegisub/context.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "video_context.h"
|
||||
#include "utils.h"
|
||||
|
@ -159,8 +160,8 @@ namespace {
|
|||
int get_file_name(lua_State *L)
|
||||
{
|
||||
const agi::Context *c = get_context(L);
|
||||
if (c && !c->ass->filename.empty())
|
||||
push_value(L, c->ass->filename.filename());
|
||||
if (c && !c->subsController->Filename().empty())
|
||||
push_value(L, c->subsController->Filename().filename());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "frame_main.h"
|
||||
#include "options.h"
|
||||
#include "utils.h"
|
||||
#include "subs_controller.h"
|
||||
#include "video_context.h"
|
||||
#include "video_slider.h"
|
||||
|
||||
|
@ -117,8 +118,8 @@ BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context, const wxSize& size,
|
|||
OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this);
|
||||
OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this);
|
||||
context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this);
|
||||
context->ass->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this);
|
||||
context->ass->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this);
|
||||
context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this);
|
||||
context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this);
|
||||
|
||||
OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this);
|
||||
OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "../initial_line_state.h"
|
||||
#include "../options.h"
|
||||
#include "../search_replace_engine.h"
|
||||
#include "../subs_controller.h"
|
||||
#include "../subs_edit_ctrl.h"
|
||||
#include "../subs_grid.h"
|
||||
#include "../text_selection_controller.h"
|
||||
|
@ -307,7 +308,7 @@ void show_color_picker(const agi::Context *c, agi::Color (AssStyle::*field), con
|
|||
commit_text(c, _("set color"), -1, -1, &commit_id);
|
||||
|
||||
if (!ok) {
|
||||
c->ass->Undo();
|
||||
c->subsController->Undo();
|
||||
c->textSelectionController->SetSelection(initial_sel_start, initial_sel_end);
|
||||
}
|
||||
}
|
||||
|
@ -876,22 +877,22 @@ struct edit_redo : public Command {
|
|||
CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
|
||||
|
||||
wxString StrMenu(const agi::Context *c) const {
|
||||
return c->ass->IsRedoStackEmpty() ?
|
||||
return c->subsController->IsRedoStackEmpty() ?
|
||||
_("Nothing to &redo") :
|
||||
wxString::Format(_("&Redo %s"), c->ass->GetRedoDescription());
|
||||
wxString::Format(_("&Redo %s"), c->subsController->GetRedoDescription());
|
||||
}
|
||||
wxString StrDisplay(const agi::Context *c) const {
|
||||
return c->ass->IsRedoStackEmpty() ?
|
||||
return c->subsController->IsRedoStackEmpty() ?
|
||||
_("Nothing to redo") :
|
||||
wxString::Format(_("Redo %s"), c->ass->GetRedoDescription());
|
||||
wxString::Format(_("Redo %s"), c->subsController->GetRedoDescription());
|
||||
}
|
||||
|
||||
bool Validate(const agi::Context *c) {
|
||||
return !c->ass->IsRedoStackEmpty();
|
||||
return !c->subsController->IsRedoStackEmpty();
|
||||
}
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
c->ass->Redo();
|
||||
c->subsController->Redo();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -902,22 +903,22 @@ struct edit_undo : public Command {
|
|||
CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
|
||||
|
||||
wxString StrMenu(const agi::Context *c) const {
|
||||
return c->ass->IsUndoStackEmpty() ?
|
||||
return c->subsController->IsUndoStackEmpty() ?
|
||||
_("Nothing to &undo") :
|
||||
wxString::Format(_("&Undo %s"), c->ass->GetUndoDescription());
|
||||
wxString::Format(_("&Undo %s"), c->subsController->GetUndoDescription());
|
||||
}
|
||||
wxString StrDisplay(const agi::Context *c) const {
|
||||
return c->ass->IsUndoStackEmpty() ?
|
||||
return c->subsController->IsUndoStackEmpty() ?
|
||||
_("Nothing to undo") :
|
||||
wxString::Format(_("Undo %s"), c->ass->GetUndoDescription());
|
||||
wxString::Format(_("Undo %s"), c->subsController->GetUndoDescription());
|
||||
}
|
||||
|
||||
bool Validate(const agi::Context *c) {
|
||||
return !c->ass->IsUndoStackEmpty();
|
||||
return !c->subsController->IsUndoStackEmpty();
|
||||
}
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
c->ass->Undo();
|
||||
c->subsController->Undo();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -38,10 +38,10 @@
|
|||
|
||||
#include "../audio_controller.h"
|
||||
#include "../compat.h"
|
||||
#include "../frame_main.h"
|
||||
#include "../include/aegisub/context.h"
|
||||
#include "../main.h"
|
||||
#include "../options.h"
|
||||
#include "../subs_controller.h"
|
||||
#include "../video_context.h"
|
||||
|
||||
#include <wx/event.h>
|
||||
|
@ -93,7 +93,7 @@ struct recent_subtitle_entry : public Command {
|
|||
STR_HELP("Open recent subtitles")
|
||||
|
||||
void operator()(agi::Context *c, int id) {
|
||||
wxGetApp().frame->LoadSubtitles(config::mru->GetEntry("Subtitle", id));
|
||||
c->subsController->Load(config::mru->GetEntry("Subtitle", id));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -47,12 +47,12 @@
|
|||
#include "../dialog_properties.h"
|
||||
#include "../dialog_search_replace.h"
|
||||
#include "../dialog_spellchecker.h"
|
||||
#include "../frame_main.h"
|
||||
#include "../include/aegisub/context.h"
|
||||
#include "../main.h"
|
||||
#include "../options.h"
|
||||
#include "../search_replace_engine.h"
|
||||
#include "../selection_controller.h"
|
||||
#include "../subs_controller.h"
|
||||
#include "../subtitle_format.h"
|
||||
#include "../utils.h"
|
||||
#include "../video_context.h"
|
||||
|
@ -246,8 +246,8 @@ struct subtitle_new : public Command {
|
|||
STR_HELP("New subtitles")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
if (wxGetApp().frame->TryToCloseSubs() != wxCANCEL)
|
||||
c->ass->LoadDefault();
|
||||
if (c->subsController->TryToClose() != wxCANCEL)
|
||||
c->subsController->Close();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -262,7 +262,7 @@ struct subtitle_open : public Command {
|
|||
void operator()(agi::Context *c) {
|
||||
auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "","", SubtitleFormat::GetWildcards(0), c->parent);
|
||||
if (!filename.empty())
|
||||
wxGetApp().frame->LoadSubtitles(filename);
|
||||
c->subsController->Load(filename);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -275,7 +275,7 @@ struct subtitle_open_autosave : public Command {
|
|||
void operator()(agi::Context *c) {
|
||||
DialogAutosave dialog(c->parent);
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
wxGetApp().frame->LoadSubtitles(dialog.ChosenFile());
|
||||
c->subsController->Load(dialog.ChosenFile());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -293,7 +293,7 @@ struct subtitle_open_charset : public Command {
|
|||
wxString charset = wxGetSingleChoice(_("Choose charset code:"), _("Charset"), agi::charset::GetEncodingsList<wxArrayString>(), c->parent, -1, -1, true, 250, 200);
|
||||
if (charset.empty()) return;
|
||||
|
||||
wxGetApp().frame->LoadSubtitles(filename, from_wx(charset));
|
||||
c->subsController->Load(filename, from_wx(charset));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -306,7 +306,7 @@ struct subtitle_open_video : public Command {
|
|||
CMD_TYPE(COMMAND_VALIDATE)
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
wxGetApp().frame->LoadSubtitles(c->videoController->GetVideoName(), "binary");
|
||||
c->subsController->Load(c->videoController->GetVideoName(), "binary");
|
||||
}
|
||||
|
||||
bool Validate(const agi::Context *c) {
|
||||
|
@ -332,13 +332,13 @@ static void save_subtitles(agi::Context *c, agi::fs::path filename) {
|
|||
if (filename.empty()) {
|
||||
c->videoController->Stop();
|
||||
filename = SaveFileSelector(_("Save subtitles file"), "Path/Last/Subtitles",
|
||||
c->ass->filename.stem().string() + ".ass", "ass",
|
||||
c->subsController->Filename().stem().string() + ".ass", "ass",
|
||||
"Advanced Substation Alpha (*.ass)|*.ass", c->parent);
|
||||
if (filename.empty()) return;
|
||||
}
|
||||
|
||||
try {
|
||||
c->ass->Save(filename, true, true);
|
||||
c->subsController->Save(filename);
|
||||
}
|
||||
catch (const agi::Exception& err) {
|
||||
wxMessageBox(to_wx(err.GetMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, c->parent);
|
||||
|
@ -360,11 +360,11 @@ struct subtitle_save : public Command {
|
|||
CMD_TYPE(COMMAND_VALIDATE)
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
save_subtitles(c, c->ass->CanSave() ? c->ass->filename : "");
|
||||
save_subtitles(c, c->subsController->CanSave() ? c->subsController->Filename() : "");
|
||||
}
|
||||
|
||||
bool Validate(const agi::Context *c) {
|
||||
return c->ass->IsModified();
|
||||
return c->subsController->IsModified();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <libaegisub/charset_conv.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include <wx/button.h>
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "scintilla_text_ctrl.h"
|
||||
#include "selection_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subs_controller.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/dispatch.h>
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "options.h"
|
||||
#include "subs_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "timeedit_ctrl.h"
|
||||
#include "video_context.h"
|
||||
|
@ -278,7 +279,7 @@ void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) {
|
|||
|
||||
void DialogShiftTimes::SaveHistory(json::Array const& shifted_blocks) {
|
||||
json::Object new_entry;
|
||||
new_entry["filename"] = context->ass->filename.filename().string();
|
||||
new_entry["filename"] = context->subsController->Filename().filename().string();
|
||||
new_entry["is by time"] = shift_by_time->GetValue();
|
||||
new_entry["is backward"] = shift_backward->GetValue();
|
||||
new_entry["amount"] = from_wx(shift_by_time->GetValue() ? shift_time->GetValue() : shift_frames->GetValue());
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "options.h"
|
||||
#include "persist_location.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "utils.h"
|
||||
|
@ -565,7 +566,11 @@ void DialogStyleManager::OnCurrentImport() {
|
|||
|
||||
AssFile temp;
|
||||
try {
|
||||
temp.Load(filename);
|
||||
auto reader = SubtitleFormat::GetReader(filename);
|
||||
if (!reader)
|
||||
wxMessageBox("Unsupported subtitle format", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
|
||||
else
|
||||
reader->ReadFile(&temp, filename);
|
||||
}
|
||||
catch (agi::Exception const& err) {
|
||||
wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this);
|
||||
|
|
|
@ -60,10 +60,10 @@
|
|||
#include "main.h"
|
||||
#include "options.h"
|
||||
#include "search_replace_engine.h"
|
||||
#include "subs_controller.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
#include "text_file_reader.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include "video_box.h"
|
||||
|
@ -213,18 +213,20 @@ FrameMain::FrameMain (wxArrayString args)
|
|||
|
||||
StartupLog("Initializing context models");
|
||||
memset(context.get(), 0, sizeof(*context));
|
||||
AssFile::top = context->ass = new AssFile;
|
||||
context->ass->AddCommitListener(&FrameMain::UpdateTitle, this);
|
||||
context->ass->AddFileOpenListener(&FrameMain::OnSubtitlesOpen, this);
|
||||
context->ass->AddFileSaveListener(&FrameMain::UpdateTitle, this);
|
||||
|
||||
context->local_scripts = new Automation4::LocalScriptManager(context.get());
|
||||
context->ass = new AssFile;
|
||||
|
||||
StartupLog("Initializing context controls");
|
||||
context->subsController = new SubsController(context.get());
|
||||
context->ass->AddCommitListener(&FrameMain::UpdateTitle, this);
|
||||
context->subsController->AddFileOpenListener(&FrameMain::OnSubtitlesOpen, this);
|
||||
context->subsController->AddFileSaveListener(&FrameMain::UpdateTitle, this);
|
||||
|
||||
context->audioController = new AudioController(context.get());
|
||||
context->audioController->AddAudioOpenListener(&FrameMain::OnAudioOpen, this);
|
||||
context->audioController->AddAudioCloseListener(&FrameMain::OnAudioClose, this);
|
||||
|
||||
context->local_scripts = new Automation4::LocalScriptManager(context.get());
|
||||
|
||||
// Initialized later due to that the selection controller is currently the subtitles grid
|
||||
context->selectionController = 0;
|
||||
|
||||
|
@ -280,7 +282,7 @@ FrameMain::FrameMain (wxArrayString args)
|
|||
SetDropTarget(new AegisubFileDropTarget(this));
|
||||
|
||||
StartupLog("Load default file");
|
||||
context->ass->LoadDefault();
|
||||
context->subsController->Close();
|
||||
|
||||
StartupLog("Display main window");
|
||||
AddFullScreenButton(this);
|
||||
|
@ -408,75 +410,6 @@ void FrameMain::InitContents() {
|
|||
StartupLog("Leaving InitContents");
|
||||
}
|
||||
|
||||
void FrameMain::LoadSubtitles(agi::fs::path const& filename, std::string const& charset) {
|
||||
if (TryToCloseSubs() == wxCANCEL) return;
|
||||
|
||||
try {
|
||||
// Make sure that file isn't actually a timecode file
|
||||
try {
|
||||
TextFileReader testSubs(filename, charset);
|
||||
std::string cur = testSubs.ReadLineFromFile();
|
||||
if (boost::starts_with(cur, "# timecode")) {
|
||||
context->videoController->LoadTimecodes(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// if trying to load the file as timecodes fails it's fairly
|
||||
// safe to assume that it is in fact not a timecode file
|
||||
}
|
||||
|
||||
context->ass->Load(filename, charset);
|
||||
|
||||
StandardPaths::SetPathValue("?script", filename);
|
||||
config::mru->Add("Subtitle", filename);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string());
|
||||
|
||||
// Save backup of file
|
||||
if (context->ass->CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) {
|
||||
if (agi::fs::FileExists(filename)) {
|
||||
auto path_str = OPT_GET("Path/Auto/Backup")->GetString();
|
||||
agi::fs::path path;
|
||||
if (path_str.empty())
|
||||
path = filename.parent_path();
|
||||
else
|
||||
path = StandardPaths::DecodePath(path_str);
|
||||
agi::fs::CreateDirectory(path);
|
||||
agi::fs::Copy(filename, path/(filename.stem().string() + ".ORIGINAL" + filename.extension().string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (agi::fs::FileNotFound const&) {
|
||||
wxMessageBox(filename.wstring() + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
|
||||
config::mru->Remove("Subtitle", filename);
|
||||
return;
|
||||
}
|
||||
catch (agi::Exception const& err) {
|
||||
wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this);
|
||||
}
|
||||
catch (...) {
|
||||
wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int FrameMain::TryToCloseSubs(bool enableCancel) {
|
||||
if (context->ass->IsModified()) {
|
||||
int flags = wxYES_NO;
|
||||
if (enableCancel) flags |= wxCANCEL;
|
||||
int result = wxMessageBox(wxString::Format(_("Do you want to save changes to %s?"), GetScriptFileName()), _("Unsaved changes"), flags, this);
|
||||
if (result == wxYES) {
|
||||
cmd::call("subtitle/save", context.get());
|
||||
// If it fails saving, return cancel anyway
|
||||
return context->ass->IsModified() ? wxCANCEL : wxYES;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return wxYES;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameMain::SetDisplayMode(int video, int audio) {
|
||||
if (!IsShownOnScreen()) return;
|
||||
|
||||
|
@ -512,8 +445,8 @@ void FrameMain::SetDisplayMode(int video, int audio) {
|
|||
|
||||
void FrameMain::UpdateTitle() {
|
||||
wxString newTitle;
|
||||
if (context->ass->IsModified()) newTitle << "* ";
|
||||
newTitle << GetScriptFileName();
|
||||
if (context->subsController->IsModified()) newTitle << "* ";
|
||||
newTitle << context->subsController->Filename().filename().wstring();
|
||||
|
||||
#ifndef __WXMAC__
|
||||
newTitle << " - Aegisub " << GetAegisubLongVersionString();
|
||||
|
@ -521,7 +454,7 @@ void FrameMain::UpdateTitle() {
|
|||
|
||||
#if defined(__WXMAC__) && !defined(__LP64__)
|
||||
// On Mac, set the mark in the close button
|
||||
OSXSetModified(context->ass->IsModified());
|
||||
OSXSetModified(context->subsController->IsModified());
|
||||
#endif
|
||||
|
||||
if (GetTitle() != newTitle) SetTitle(newTitle);
|
||||
|
@ -596,7 +529,7 @@ bool FrameMain::LoadList(wxArrayString list) {
|
|||
|
||||
// Load files
|
||||
if (subs.size())
|
||||
LoadSubtitles(subs);
|
||||
context->subsController->Load(subs);
|
||||
|
||||
if (blockVideoLoad) {
|
||||
blockVideoLoad = false;
|
||||
|
@ -634,21 +567,18 @@ BEGIN_EVENT_TABLE(FrameMain, wxFrame)
|
|||
EVT_MOUSEWHEEL(FrameMain::OnMouseWheel)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void FrameMain::OnCloseWindow (wxCloseEvent &event) {
|
||||
// Stop audio and video
|
||||
void FrameMain::OnCloseWindow(wxCloseEvent &event) {
|
||||
context->videoController->Stop();
|
||||
context->audioController->Stop();
|
||||
|
||||
// Ask user if he wants to save first
|
||||
bool canVeto = event.CanVeto();
|
||||
int result = TryToCloseSubs(canVeto);
|
||||
if (canVeto && result == wxCANCEL) {
|
||||
if (context->subsController->TryToClose(event.CanVeto()) == wxCANCEL) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
|
||||
delete context->dialog;
|
||||
context->dialog = 0;
|
||||
context->dialog = nullptr;
|
||||
|
||||
// Store maximization state
|
||||
OPT_SET("App/Maximized")->SetBool(IsMaximized());
|
||||
|
@ -657,7 +587,7 @@ void FrameMain::OnCloseWindow (wxCloseEvent &event) {
|
|||
}
|
||||
|
||||
void FrameMain::OnAutoSave(wxTimerEvent &) try {
|
||||
auto fn = context->ass->AutoSave();
|
||||
auto fn = context->subsController->AutoSave();
|
||||
if (!fn.empty())
|
||||
StatusTimeout(wxString::Format(_("File backup saved as \"%s\"."), fn.wstring()));
|
||||
}
|
||||
|
@ -766,18 +696,3 @@ void FrameMain::OnKeyDown(wxKeyEvent &event) {
|
|||
void FrameMain::OnMouseWheel(wxMouseEvent &evt) {
|
||||
ForwardMouseWheelEvent(this, evt);
|
||||
}
|
||||
|
||||
wxString FrameMain::GetScriptFileName() const {
|
||||
if (context->ass->filename.empty()) {
|
||||
// Apple HIG says "untitled" should not be capitalised
|
||||
// and the window is a document window, it shouldn't contain the app name
|
||||
// (The app name is already present in the menu bar)
|
||||
#ifndef __WXMAC__
|
||||
return _("Untitled");
|
||||
#else
|
||||
return _("untitled");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
return context->ass->filename.filename().wstring();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include <wx/sizer.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
class AegisubApp;
|
||||
class AegisubFileDropTarget;
|
||||
class AssFile;
|
||||
class AudioBox;
|
||||
|
@ -62,6 +63,7 @@ class VideoZoomSlider;
|
|||
namespace agi { struct Context; class OptionValue; }
|
||||
|
||||
class FrameMain: public wxFrame {
|
||||
friend class AegisubApp;
|
||||
friend class AegisubFileDropTarget;
|
||||
|
||||
std::unique_ptr<agi::Context> context;
|
||||
|
@ -88,7 +90,6 @@ class FrameMain: public wxFrame {
|
|||
void OnFilesDropped(wxThreadEvent &evt);
|
||||
bool LoadList(wxArrayString list);
|
||||
void UpdateTitle();
|
||||
wxString GetScriptFileName() const;
|
||||
|
||||
void OnKeyDown(wxKeyEvent &event);
|
||||
void OnMouseWheel(wxMouseEvent &evt);
|
||||
|
@ -134,11 +135,5 @@ public:
|
|||
bool IsVideoShown() const { return showVideo; }
|
||||
bool IsAudioShown() const { return showAudio; }
|
||||
|
||||
/// Close the currently open subs, asking the user if they want to save if there are unsaved changes
|
||||
/// @param enableCancel Should the user be able to cancel the close?
|
||||
int TryToCloseSubs(bool enableCancel=true);
|
||||
|
||||
void LoadSubtitles(agi::fs::path const& filename, std::string const& charset="");
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@ class DialogManager;
|
|||
class SearchReplaceEngine;
|
||||
class InitialLineState;
|
||||
template<class T> class SelectionController;
|
||||
class SubsController;
|
||||
class SubsTextEditCtrl;
|
||||
class SubtitlesGrid;
|
||||
class TextSelectionController;
|
||||
|
@ -26,6 +27,7 @@ struct Context {
|
|||
// Controllers
|
||||
AudioController *audioController;
|
||||
SelectionController<AssDialogue *> *selectionController;
|
||||
SubsController *subsController;
|
||||
TextSelectionController *textSelectionController;
|
||||
VideoContext *videoController;
|
||||
|
||||
|
|
|
@ -45,11 +45,13 @@
|
|||
#include "export_fixstyle.h"
|
||||
#include "export_framerate.h"
|
||||
#include "frame_main.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "main.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "options.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subs_controller.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "version.h"
|
||||
#include "video_context.h"
|
||||
|
@ -364,15 +366,15 @@ StackWalker::~StackWalker() {
|
|||
/// Message displayed when an exception has occurred.
|
||||
const static wxString exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close.");
|
||||
|
||||
static void UnhandledExeception(bool stackWalk) {
|
||||
static void UnhandledExeception(bool stackWalk, agi::Context *c) {
|
||||
#if (!defined(_DEBUG) || defined(WITH_EXCEPTIONS)) && (wxUSE_ON_FATAL_EXCEPTION+0)
|
||||
if (AssFile::top) {
|
||||
if (c->ass && c->subsController) {
|
||||
auto path = StandardPaths::DecodePath("?user/recovered");
|
||||
agi::fs::CreateDirectory(path);
|
||||
|
||||
auto filename = AssFile::top->filename.empty() ? "untitled" : AssFile::top->filename.stem().string();
|
||||
auto filename = c->subsController->Filename().stem();
|
||||
path /= str(boost::format("%s.%s.ass") % filename % agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
|
||||
AssFile::top->Save(path, false, false);
|
||||
c->subsController->Save(path);
|
||||
|
||||
#if wxUSE_STACKWALKER == 1
|
||||
if (stackWalk) {
|
||||
|
@ -397,11 +399,11 @@ static void UnhandledExeception(bool stackWalk) {
|
|||
}
|
||||
|
||||
void AegisubApp::OnUnhandledException() {
|
||||
UnhandledExeception(false);
|
||||
UnhandledExeception(false, frame ? frame->context.get() : nullptr);
|
||||
}
|
||||
|
||||
void AegisubApp::OnFatalException() {
|
||||
UnhandledExeception(true);
|
||||
UnhandledExeception(true, frame ? frame->context.get() : nullptr);
|
||||
}
|
||||
|
||||
void AegisubApp::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const {
|
||||
|
@ -456,9 +458,6 @@ int AegisubApp::OnRun() {
|
|||
}
|
||||
|
||||
void AegisubApp::MacOpenFile(const wxString &filename) {
|
||||
if (frame != nullptr && !filename.empty()) {
|
||||
frame->LoadSubtitles(from_wx(filename));
|
||||
wxFileName filepath(filename);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath()));
|
||||
}
|
||||
if (frame && !filename.empty())
|
||||
frame->context->subsController->Load(agi::fs::path(filename));
|
||||
}
|
||||
|
|
311
aegisub/src/subs_controller.cpp
Normal file
311
aegisub/src/subs_controller.cpp
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Copyright (c) 2013, 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 "config.h"
|
||||
|
||||
#include "subs_controller.h"
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_style.h"
|
||||
#include "compat.h"
|
||||
#include "command/command.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "text_file_reader.h"
|
||||
#include "video_context.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
struct SubsController::UndoInfo {
|
||||
AssFile file;
|
||||
wxString undo_description;
|
||||
int commit_id;
|
||||
UndoInfo(AssFile const& f, wxString const& d, int c) : file(f), undo_description(d), commit_id(c) { }
|
||||
};
|
||||
|
||||
SubsController::SubsController(agi::Context *context)
|
||||
: context(context)
|
||||
, undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this))
|
||||
, commit_id(0)
|
||||
, saved_commit_id(0)
|
||||
, autosaved_commit_id(0)
|
||||
{
|
||||
}
|
||||
|
||||
void SubsController::Load(agi::fs::path const& filename, std::string const& charset) {
|
||||
if (TryToClose() == wxCANCEL) return;
|
||||
|
||||
// Make sure that file isn't actually a timecode file
|
||||
try {
|
||||
TextFileReader testSubs(filename, charset);
|
||||
std::string cur = testSubs.ReadLineFromFile();
|
||||
if (boost::starts_with(cur, "# timecode")) {
|
||||
context->videoController->LoadTimecodes(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// if trying to load the file as timecodes fails it's fairly
|
||||
// safe to assume that it is in fact not a timecode file
|
||||
}
|
||||
|
||||
const SubtitleFormat *reader = SubtitleFormat::GetReader(filename);
|
||||
|
||||
try {
|
||||
AssFile temp;
|
||||
reader->ReadFile(&temp, filename, charset);
|
||||
|
||||
bool found_style = false;
|
||||
bool found_dialogue = false;
|
||||
|
||||
// Check if the file has at least one style and at least one dialogue line
|
||||
for (auto const& line : temp.Line) {
|
||||
AssEntryGroup type = line.Group();
|
||||
if (type == ENTRY_STYLE) found_style = true;
|
||||
if (type == ENTRY_DIALOGUE) found_dialogue = true;
|
||||
if (found_style && found_dialogue) break;
|
||||
}
|
||||
|
||||
// And if it doesn't add defaults for each
|
||||
if (!found_style)
|
||||
temp.InsertLine(new AssStyle);
|
||||
if (!found_dialogue)
|
||||
temp.InsertLine(new AssDialogue);
|
||||
|
||||
context->ass->swap(temp);
|
||||
}
|
||||
catch (agi::UserCancelException const&) {
|
||||
return;
|
||||
}
|
||||
catch (agi::fs::FileNotFound const&) {
|
||||
wxMessageBox(filename.wstring() + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent);
|
||||
config::mru->Remove("Subtitle", filename);
|
||||
return;
|
||||
}
|
||||
catch (agi::Exception const& err) {
|
||||
wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent);
|
||||
return;
|
||||
}
|
||||
catch (std::exception const& err) {
|
||||
wxMessageBox(to_wx(err.what()), "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent);
|
||||
return;
|
||||
}
|
||||
catch (...) {
|
||||
wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, context->parent);
|
||||
return;
|
||||
}
|
||||
|
||||
SetFileName(filename);
|
||||
|
||||
// Push the initial state of the file onto the undo stack
|
||||
undo_stack.clear();
|
||||
redo_stack.clear();
|
||||
autosaved_commit_id = saved_commit_id = commit_id + 1;
|
||||
context->ass->Commit("", AssFile::COMMIT_NEW);
|
||||
|
||||
// Save backup of file
|
||||
if (CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) {
|
||||
auto path_str = OPT_GET("Path/Auto/Backup")->GetString();
|
||||
agi::fs::path path;
|
||||
if (path_str.empty())
|
||||
path = filename.parent_path();
|
||||
else
|
||||
path = StandardPaths::DecodePath(path_str);
|
||||
agi::fs::CreateDirectory(path);
|
||||
agi::fs::Copy(filename, path/(filename.stem().string() + ".ORIGINAL" + filename.extension().string()));
|
||||
}
|
||||
|
||||
FileOpen(filename);
|
||||
}
|
||||
|
||||
void SubsController::Save(agi::fs::path const& filename, std::string const& encoding) {
|
||||
const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
|
||||
if (!writer)
|
||||
throw "Unknown file type.";
|
||||
|
||||
int old_autosaved_commit_id = autosaved_commit_id, old_saved_commit_id = saved_commit_id;
|
||||
try {
|
||||
autosaved_commit_id = saved_commit_id = commit_id;
|
||||
|
||||
// Have to set these now for the sake of things that want to save paths
|
||||
// relative to the script in the header
|
||||
this->filename = filename;
|
||||
StandardPaths::SetPathValue("?script", filename.parent_path());
|
||||
|
||||
FileSave();
|
||||
|
||||
writer->WriteFile(context->ass, filename, encoding);
|
||||
}
|
||||
catch (...) {
|
||||
autosaved_commit_id = old_autosaved_commit_id;
|
||||
saved_commit_id = old_saved_commit_id;
|
||||
throw;
|
||||
}
|
||||
|
||||
SetFileName(filename);
|
||||
}
|
||||
|
||||
void SubsController::Close() {
|
||||
undo_stack.clear();
|
||||
redo_stack.clear();
|
||||
autosaved_commit_id = saved_commit_id = commit_id + 1;
|
||||
filename.clear();
|
||||
context->ass->Line.clear();
|
||||
context->ass->LoadDefault();
|
||||
context->ass->Commit("", AssFile::COMMIT_NEW);
|
||||
}
|
||||
|
||||
int SubsController::TryToClose(bool allow_cancel) const {
|
||||
if (!IsModified())
|
||||
return wxYES;
|
||||
|
||||
int flags = wxYES_NO;
|
||||
if (allow_cancel)
|
||||
flags |= wxCANCEL;
|
||||
int result = wxMessageBox(wxString::Format(_("Do you want to save changes to %s?"), Filename().wstring()), _("Unsaved changes"), flags, context->parent);
|
||||
if (result == wxYES) {
|
||||
cmd::call("subtitle/save", context);
|
||||
// If it fails saving, return cancel anyway
|
||||
return IsModified() ? wxCANCEL : wxYES;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
agi::fs::path SubsController::AutoSave() {
|
||||
if (commit_id == autosaved_commit_id)
|
||||
return "";
|
||||
|
||||
auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString());
|
||||
if (path.empty())
|
||||
path = filename.parent_path();
|
||||
|
||||
agi::fs::CreateDirectory(path);
|
||||
|
||||
auto name = filename.filename();
|
||||
if (name.empty())
|
||||
name = "Untitled";
|
||||
|
||||
path /= str(boost::format("%s.%s.AUTOSAVE.ass") % name.string() % agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
|
||||
|
||||
SubtitleFormat::GetWriter(path)->WriteFile(context->ass, path);
|
||||
autosaved_commit_id = commit_id;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool SubsController::CanSave() const {
|
||||
try {
|
||||
return SubtitleFormat::GetWriter(filename)->CanSave(context->ass);
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SubsController::SetFileName(agi::fs::path const& path) {
|
||||
filename = path;
|
||||
StandardPaths::SetPathValue("?script", path.parent_path());
|
||||
config::mru->Add("Subtitle", path);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string());
|
||||
}
|
||||
|
||||
void SubsController::OnCommit(AssFileCommit c) {
|
||||
if (c.message.empty() && !undo_stack.empty()) return;
|
||||
|
||||
++commit_id;
|
||||
// Allow coalescing only if it's the last change and the file has not been
|
||||
// saved since the last change
|
||||
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) {
|
||||
entryIter this_it = context->ass->Line.begin(), undo_it = undo_stack.back().file.Line.begin();
|
||||
while (&*this_it != c.single_line) {
|
||||
++this_it;
|
||||
++undo_it;
|
||||
}
|
||||
undo_stack.back().file.Line.insert(undo_it, *c.single_line->Clone());
|
||||
delete &*undo_it;
|
||||
}
|
||||
else
|
||||
undo_stack.back().file = *context->ass;
|
||||
|
||||
*c.commit_id = commit_id;
|
||||
return;
|
||||
}
|
||||
|
||||
redo_stack.clear();
|
||||
|
||||
undo_stack.emplace_back(*context->ass, c.message, commit_id);
|
||||
|
||||
int depth = std::max<int>(OPT_GET("Limits/Undo Levels")->GetInt(), 2);
|
||||
while ((int)undo_stack.size() > depth)
|
||||
undo_stack.pop_front();
|
||||
|
||||
if (undo_stack.size() > 1 && OPT_GET("App/Auto/Save on Every Change")->GetBool() && !filename.empty() && CanSave())
|
||||
Save(filename);
|
||||
|
||||
*c.commit_id = commit_id;
|
||||
}
|
||||
|
||||
void SubsController::Undo() {
|
||||
if (undo_stack.size() <= 1) return;
|
||||
|
||||
redo_stack.emplace_back(AssFile(), undo_stack.back().undo_description, commit_id);
|
||||
context->ass->swap(redo_stack.back().file);
|
||||
undo_stack.pop_back();
|
||||
*context->ass = undo_stack.back().file;
|
||||
commit_id = undo_stack.back().commit_id;
|
||||
|
||||
context->ass->Commit("", AssFile::COMMIT_NEW);
|
||||
}
|
||||
|
||||
void SubsController::Redo() {
|
||||
if (redo_stack.empty()) return;
|
||||
|
||||
context->ass->swap(redo_stack.back().file);
|
||||
commit_id = redo_stack.back().commit_id;
|
||||
undo_stack.emplace_back(*context->ass, redo_stack.back().undo_description, commit_id);
|
||||
redo_stack.pop_back();
|
||||
|
||||
context->ass->Commit("", AssFile::COMMIT_NEW);
|
||||
}
|
||||
|
||||
wxString SubsController::GetUndoDescription() const {
|
||||
return IsUndoStackEmpty() ? "" : undo_stack.back().undo_description;
|
||||
}
|
||||
|
||||
wxString SubsController::GetRedoDescription() const {
|
||||
return IsRedoStackEmpty() ? "" : redo_stack.back().undo_description;
|
||||
}
|
||||
|
||||
agi::fs::path SubsController::Filename() const {
|
||||
if (!filename.empty()) return filename;
|
||||
|
||||
// Apple HIG says "untitled" should not be capitalised
|
||||
#ifndef __WXMAC__
|
||||
return _("Untitled").wx_str();
|
||||
#else
|
||||
return _("untitled").wx_str();
|
||||
#endif
|
||||
}
|
110
aegisub/src/subs_controller.h
Normal file
110
aegisub/src/subs_controller.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
#include <boost/container/list.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <set>
|
||||
|
||||
class AssEntry;
|
||||
class AssFile;
|
||||
struct AssFileCommit;
|
||||
|
||||
namespace agi { struct Context; }
|
||||
|
||||
class SubsController {
|
||||
agi::Context *context;
|
||||
agi::signal::Connection undo_connection;
|
||||
|
||||
struct UndoInfo;
|
||||
boost::container::list<UndoInfo> undo_stack;
|
||||
boost::container::list<UndoInfo> redo_stack;
|
||||
|
||||
/// Revision counter for undo coalescing and modified state tracking
|
||||
int commit_id;
|
||||
/// Last saved version of this file
|
||||
int saved_commit_id;
|
||||
/// Last autosaved version of this file
|
||||
int autosaved_commit_id;
|
||||
|
||||
/// A new file has been opened (filename)
|
||||
agi::signal::Signal<agi::fs::path> FileOpen;
|
||||
/// The file is about to be saved
|
||||
/// This signal is intended for adding metadata such as video filename,
|
||||
/// frame number, etc. Ideally this would all be done immediately rather
|
||||
/// than waiting for a save, but that causes (more) issues with undo
|
||||
agi::signal::Signal<> FileSave;
|
||||
|
||||
/// The filename of the currently open file, if any
|
||||
agi::fs::path filename;
|
||||
|
||||
void OnCommit(AssFileCommit c);
|
||||
|
||||
/// Set the filename, updating things like the MRU and last used path
|
||||
void SetFileName(agi::fs::path const& file);
|
||||
|
||||
public:
|
||||
SubsController(agi::Context *context);
|
||||
|
||||
/// The file's path and filename if any, or platform-appropriate "untitled"
|
||||
agi::fs::path Filename() const;
|
||||
|
||||
/// Does the file have unsaved changes?
|
||||
bool IsModified() const { return commit_id != saved_commit_id; };
|
||||
|
||||
/// @brief Load from a file
|
||||
/// @param file File name
|
||||
/// @param charset Character set of file or empty to autodetect
|
||||
void Load(agi::fs::path const& file, std::string const& charset="");
|
||||
|
||||
/// @brief Save to a file
|
||||
/// @param file Path to save to
|
||||
/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
|
||||
void Save(agi::fs::path const& file, std::string const& encoding="");
|
||||
|
||||
/// Close the currently open file (i.e. open a new blank file)
|
||||
void Close();
|
||||
|
||||
/// If there are unsaved changes, asl the user if they want to save them
|
||||
/// @param allow_cancel Let the user cancel the closing
|
||||
/// @return wxYES, wxNO or wxCANCEL (note: all three are true in a boolean context)
|
||||
int TryToClose(bool allow_cancel = true) const;
|
||||
|
||||
/// @brief Autosave the file if there have been any chances since the last autosave
|
||||
/// @return File name used or empty if no save was performed
|
||||
agi::fs::path AutoSave();
|
||||
|
||||
/// Can the file be saved in its current format?
|
||||
bool CanSave() const;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(FileOpen, AddFileOpenListener)
|
||||
DEFINE_SIGNAL_ADDERS(FileSave, AddFileSaveListener)
|
||||
|
||||
/// @brief Undo the last set of changes to the file
|
||||
void Undo();
|
||||
/// @brief Redo the last undone changes
|
||||
void Redo();
|
||||
/// Check if undo stack is empty
|
||||
bool IsUndoStackEmpty() const { return undo_stack.size() <= 1; };
|
||||
/// Check if redo stack is empty
|
||||
bool IsRedoStackEmpty() const { return redo_stack.empty(); };
|
||||
/// Get the description of the first undoable change
|
||||
wxString GetUndoDescription() const;
|
||||
/// Get the description of the first redoable change
|
||||
wxString GetRedoDescription() const;
|
||||
};
|
|
@ -183,7 +183,7 @@ class SubsEditBox : public wxPanel {
|
|||
void SetSelectedRows(T AssDialogue::*field, wxString const& value, wxString const& desc, int type, bool amend = false);
|
||||
|
||||
/// @brief Reload the current line from the file
|
||||
/// @param type AssFile::CommitType
|
||||
/// @param type AssFile::COMMITType
|
||||
void OnCommit(int type);
|
||||
|
||||
/// Regenerate a dropdown list with the unique values of a dialogue field
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <libaegisub/of_type_adaptor.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
EncoreSubtitleFormat::EncoreSubtitleFormat()
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include <libaegisub/of_type_adaptor.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
TranStationSubtitleFormat::TranStationSubtitleFormat()
|
||||
|
|
|
@ -37,9 +37,8 @@
|
|||
#ifdef WITH_CSRI
|
||||
#include "subtitles_provider_csri.h"
|
||||
|
||||
#include "ass_file.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "standard_paths.h"
|
||||
#include "video_context.h"
|
||||
#include "video_frame.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
|
@ -81,7 +80,7 @@ CSRISubtitlesProvider::~CSRISubtitlesProvider() {
|
|||
void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||
if (tempfile.empty())
|
||||
tempfile = unique_path(StandardPaths::DecodePath("?temp/csri-%%%%-%%%%-%%%%-%%%%.ass"));
|
||||
subs->Save(tempfile, false, false, "utf-8");
|
||||
SubtitleFormat::GetWriter(tempfile)->WriteFile(subs, tempfile, "utf-8");
|
||||
|
||||
std::lock_guard<std::mutex> lock(csri_mutex);
|
||||
instance = csri_open_file(renderer, tempfile.string().c_str(), nullptr);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <libaegisub/dispatch.h>
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <boost/gil/gil_all.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
@ -117,7 +118,17 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
|
|||
|
||||
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||
std::vector<char> data;
|
||||
subs->SaveMemory(data);
|
||||
data.clear();
|
||||
data.reserve(0x4000);
|
||||
|
||||
AssEntryGroup group = ENTRY_GROUP_MAX;
|
||||
for (auto const& line : subs->Line) {
|
||||
if (group != line.Group()) {
|
||||
group = line.Group();
|
||||
boost::push_back(data, line.GroupHeader() + "\r\n");
|
||||
}
|
||||
boost::push_back(data, line.GetEntryData() + "\r\n");
|
||||
}
|
||||
|
||||
if (ass_track) ass_free_track(ass_track);
|
||||
ass_track = ass_read_memory(library, &data[0], data.size(),(char *)"UTF-8");
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "mkv_wrap.h"
|
||||
#include "options.h"
|
||||
#include "selection_controller.h"
|
||||
#include "subs_controller.h"
|
||||
#include "time_range.h"
|
||||
#include "threaded_frame_source.h"
|
||||
#include "utils.h"
|
||||
|
@ -116,7 +117,7 @@ void VideoContext::Reset() {
|
|||
void VideoContext::SetContext(agi::Context *context) {
|
||||
this->context = context;
|
||||
context->ass->AddCommitListener(&VideoContext::OnSubtitlesCommit, this);
|
||||
context->ass->AddFileSaveListener(&VideoContext::OnSubtitlesSave, this);
|
||||
context->subsController->AddFileSaveListener(&VideoContext::OnSubtitlesSave, this);
|
||||
}
|
||||
|
||||
void VideoContext::SetVideo(const agi::fs::path &filename) {
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
/// @ingroup video main_ui
|
||||
///
|
||||
|
||||
// Includes
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -60,6 +59,7 @@
|
|||
#include "include/aegisub/menu.h"
|
||||
#include "options.h"
|
||||
#include "spline_curve.h"
|
||||
#include "subs_controller.h"
|
||||
#include "threaded_frame_source.h"
|
||||
#include "utils.h"
|
||||
#include "video_out_gl.h"
|
||||
|
@ -111,7 +111,7 @@ VideoDisplay::VideoDisplay(
|
|||
slots.push_back(con->videoController->AddVideoOpenListener(&VideoDisplay::UpdateSize, this));
|
||||
slots.push_back(con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this));
|
||||
|
||||
slots.push_back(con->ass->AddFileSaveListener(&VideoDisplay::OnSubtitlesSave, this));
|
||||
slots.push_back(con->subsController->AddFileSaveListener(&VideoDisplay::OnSubtitlesSave, this));
|
||||
|
||||
Bind(wxEVT_PAINT, std::bind(&VideoDisplay::Render, this));
|
||||
Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this);
|
||||
|
|
Loading…
Reference in a new issue