Redesign how project metadata is stored in the file

Remove it from the script info section and put it in its own section
that isn't tracked by undo and make it not stringly typed. Removes the
need for the gross hack where changes are slipped in just before saving
to circumvent the undo system, cuts down on the uses of string literals
to identify fields, and probably improves performance a little.
This commit is contained in:
Thomas Goyne 2014-05-21 18:32:42 -07:00
parent 19e8f19e52
commit 9c7119fdc2
21 changed files with 240 additions and 180 deletions

View File

@ -84,6 +84,7 @@ void AssFile::swap(AssFile& from) throw() {
Events.swap(from.Events); Events.swap(from.Events);
Attachments.swap(from.Attachments); Attachments.swap(from.Attachments);
Extradata.swap(from.Extradata); Extradata.swap(from.Extradata);
std::swap(Properties, from.Properties);
std::swap(next_extradata_id, from.next_extradata_id); std::swap(next_extradata_id, from.next_extradata_id);
} }
@ -121,22 +122,6 @@ int AssFile::GetScriptInfoAsInt(std::string const& key) const {
return atoi(GetScriptInfo(key).c_str()); return atoi(GetScriptInfo(key).c_str());
} }
std::string AssFile::GetUIState(std::string const& key) const {
auto value = GetScriptInfo("Aegisub " + key);
if (value.empty())
value = GetScriptInfo(key);
return value;
}
int AssFile::GetUIStateAsInt(std::string const& key) const {
return atoi(GetUIState(key).c_str());
}
void AssFile::SaveUIState(std::string const& key, std::string const& value) {
if (OPT_GET("App/Save UI State")->GetBool())
SetScriptInfo("Aegisub " + key, value);
}
void AssFile::SetScriptInfo(std::string const& key, std::string const& value) { void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
for (auto it = Info.begin(); it != Info.end(); ++it) { for (auto it = Info.begin(); it != Info.end(); ++it) {
if (boost::iequals(key, it->Key())) { if (boost::iequals(key, it->Key())) {
@ -300,4 +285,3 @@ void AssFile::CleanExtradata() {
Extradata.erase(id); Extradata.erase(id);
} }
} }

View File

@ -27,11 +27,6 @@
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
/// @file ass_file.h
/// @see ass_file.cpp
/// @ingroup subs_storage
///
#include "ass_entry.h" #include "ass_entry.h"
#include <libaegisub/fs_fwd.h> #include <libaegisub/fs_fwd.h>
@ -58,6 +53,26 @@ struct AssFileCommit {
AssDialogue *single_line; AssDialogue *single_line;
}; };
struct ProjectProperties {
std::string automation_scripts;
std::string export_filters;
std::string export_encoding;
std::string style_storage;
std::string audio_file;
std::string video_file;
std::string timecodes_file;
std::string keyframes_file;
std::map<std::string, std::string> automation_settings;
// UI State
double video_zoom = 0.;
double ar_value = 0.;
int scroll_position = 0;
int active_row = 0;
int ar_mode = 0;
int video_position = 0;
};
class AssFile { class AssFile {
/// A set of changes has been committed to the file (AssFile::COMMITType) /// A set of changes has been committed to the file (AssFile::COMMITType)
agi::signal::Signal<int, std::set<const AssDialogue*> const&> AnnounceCommit; agi::signal::Signal<int, std::set<const AssDialogue*> const&> AnnounceCommit;
@ -69,6 +84,7 @@ public:
EntryList<AssDialogue> Events; EntryList<AssDialogue> Events;
std::vector<AssAttachment> Attachments; std::vector<AssAttachment> Attachments;
AegisubExtradataMap Extradata; AegisubExtradataMap Extradata;
ProjectProperties Properties;
uint32_t next_extradata_id = 0; uint32_t next_extradata_id = 0;
@ -104,9 +120,6 @@ public:
std::string GetScriptInfo(std::string const& key) const; std::string GetScriptInfo(std::string const& key) const;
/// Set the value of a [Script Info] key. Adds it if it doesn't exist. /// Set the value of a [Script Info] key. Adds it if it doesn't exist.
void SetScriptInfo(std::string const& key, std::string const& value); void SetScriptInfo(std::string const& key, std::string const& value);
std::string GetUIState(std::string const& key) const;
int GetUIStateAsInt(std::string const& key) const;
void SaveUIState(std::string const& key, std::string const& value);
/// @brief Add a new extradata entry /// @brief Add a new extradata entry
/// @param key Class identifier/owner for the extradata /// @param key Class identifier/owner for the extradata

View File

@ -24,6 +24,7 @@
#include <libaegisub/ass/uuencode.h> #include <libaegisub/ass/uuencode.h>
#include <libaegisub/make_unique.h> #include <libaegisub/make_unique.h>
#include <libaegisub/util.h>
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
@ -31,9 +32,71 @@
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/variant.hpp>
#include <unordered_map>
class AssParser::HeaderToProperty {
using field = boost::variant<
std::string ProjectProperties::*,
int ProjectProperties::*,
double ProjectProperties::*
>;
std::unordered_map<std::string, field> fields;
public:
HeaderToProperty()
: fields({
{"Automation Scripts", &ProjectProperties::automation_scripts},
{"Export Filters", &ProjectProperties::export_filters},
{"Export Encoding", &ProjectProperties::export_encoding},
{"Last Style Storage", &ProjectProperties::style_storage},
{"Audio URI", &ProjectProperties::audio_file},
{"Audio File", &ProjectProperties::audio_file},
{"Video File", &ProjectProperties::video_file},
{"Timecodes File", &ProjectProperties::timecodes_file},
{"Keyframes File", &ProjectProperties::keyframes_file},
{"Video Zoom Percent", &ProjectProperties::video_zoom},
{"Scroll Position", &ProjectProperties::scroll_position},
{"Active Line", &ProjectProperties::active_row},
{"Video Position", &ProjectProperties::video_position},
{"Video AR Mode", &ProjectProperties::ar_mode},
{"Video AR Value", &ProjectProperties::ar_value},
{"Aegisub Video Zoom Percent", &ProjectProperties::video_zoom},
{"Aegisub Scroll Position", &ProjectProperties::scroll_position},
{"Aegisub Active Line", &ProjectProperties::active_row},
{"Aegisub Video Position", &ProjectProperties::video_position}
})
{
}
bool ProcessProperty(AssFile *target, std::string const& key, std::string const& value) {
auto it = fields.find(key);
if (it != end(fields)) {
using namespace agi::util;
struct {
using result_type = void;
ProjectProperties &obj;
std::string const& value;
void operator()(std::string ProjectProperties::*f) const { obj.*f = value; }
void operator()(int ProjectProperties::*f) const { try_parse(value, &(obj.*f)); }
void operator()(double ProjectProperties::*f) const { try_parse(value, &(obj.*f)); }
} visitor {target->Properties, value};
boost::apply_visitor(visitor, it->second);
return true;
}
if (boost::starts_with(key, "Automation Settings ")) {
target->Properties.automation_settings[key.substr(strlen("Automation Settings"))] = value;
return true;
}
return false;
}
};
AssParser::AssParser(AssFile *target, int version) AssParser::AssParser(AssFile *target, int version)
: target(target) : property_handler(new HeaderToProperty)
, target(target)
, version(version) , version(version)
, state(&AssParser::ParseScriptInfoLine) , state(&AssParser::ParseScriptInfoLine)
{ {
@ -94,7 +157,23 @@ void AssParser::ParseScriptInfoLine(std::string const& data) {
size_t pos = data.find(':'); size_t pos = data.find(':');
if (pos == data.npos) return; if (pos == data.npos) return;
target->Info.push_back(*new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1)))); auto key = data.substr(0, pos);
auto value = data.substr(pos + 1);
boost::trim_left(value);
if (!property_handler->ProcessProperty(target, key, value))
target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
}
void AssParser::ParseMetadataLine(std::string const& data) {
size_t pos = data.find(':');
if (pos == data.npos) return;
auto key = data.substr(0, pos);
auto value = data.substr(pos + 1);
boost::trim_left(value);
property_handler->ProcessProperty(target, key, value);
} }
void AssParser::ParseEventLine(std::string const& data) { void AssParser::ParseEventLine(std::string const& data) {
@ -171,12 +250,14 @@ void AssParser::AddLine(std::string const& data) {
state = &AssParser::ParseEventLine; state = &AssParser::ParseEventLine;
else if (low == "[script info]") else if (low == "[script info]")
state = &AssParser::ParseScriptInfoLine; state = &AssParser::ParseScriptInfoLine;
else if (low == "[aegisub project garbage]")
state = &AssParser::ParseMetadataLine;
else if (low == "[aegisub extradata]")
state = &AssParser::ParseExtradataLine;
else if (low == "[graphics]") else if (low == "[graphics]")
state = &AssParser::ParseGraphicsLine; state = &AssParser::ParseGraphicsLine;
else if (low == "[fonts]") else if (low == "[fonts]")
state = &AssParser::ParseFontLine; state = &AssParser::ParseFontLine;
else if (low == "[aegisub extradata]")
state = &AssParser::ParseExtradataLine;
else else
state = &AssParser::UnknownLine; state = &AssParser::UnknownLine;
return; return;

View File

@ -20,6 +20,9 @@ class AssAttachment;
class AssFile; class AssFile;
class AssParser { class AssParser {
class HeaderToProperty;
std::unique_ptr<HeaderToProperty> property_handler;
AssFile *target; AssFile *target;
int version; int version;
std::unique_ptr<AssAttachment> attach; std::unique_ptr<AssAttachment> attach;
@ -29,6 +32,7 @@ class AssParser {
void ParseEventLine(std::string const& data); void ParseEventLine(std::string const& data);
void ParseStyleLine(std::string const& data); void ParseStyleLine(std::string const& data);
void ParseScriptInfoLine(std::string const& data); void ParseScriptInfoLine(std::string const& data);
void ParseMetadataLine(std::string const& data);
void ParseFontLine(std::string const& data); void ParseFontLine(std::string const& data);
void ParseGraphicsLine(std::string const& data); void ParseGraphicsLine(std::string const& data);
void ParseExtradataLine(std::string const &data); void ParseExtradataLine(std::string const &data);

View File

@ -182,14 +182,14 @@ namespace Automation4 {
std::string ExportFilter::GetScriptSettingsIdentifier() std::string ExportFilter::GetScriptSettingsIdentifier()
{ {
return inline_string_encode("Automation Settings " + GetName()); return inline_string_encode(GetName());
} }
wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) { wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) {
config_dialog = GenerateConfigDialog(parent, c); config_dialog = GenerateConfigDialog(parent, c);
if (config_dialog) { if (config_dialog) {
std::string val = c->ass->GetScriptInfo(GetScriptSettingsIdentifier()); std::string const& val = c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()];
if (!val.empty()) if (!val.empty())
config_dialog->Unserialise(val); config_dialog->Unserialise(val);
return config_dialog->CreateWindow(parent); return config_dialog->CreateWindow(parent);
@ -199,11 +199,8 @@ namespace Automation4 {
} }
void ExportFilter::LoadSettings(bool is_default, agi::Context *c) { void ExportFilter::LoadSettings(bool is_default, agi::Context *c) {
if (config_dialog) { if (config_dialog)
std::string val = config_dialog->Serialise(); c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()] = config_dialog->Serialise();
if (!val.empty())
c->ass->SetScriptInfo(GetScriptSettingsIdentifier(), val);
}
} }
// ProgressSink // ProgressSink
@ -285,10 +282,6 @@ namespace Automation4 {
} }
// ScriptManager // ScriptManager
ScriptManager::~ScriptManager()
{
}
void ScriptManager::Add(std::unique_ptr<Script> script) void ScriptManager::Add(std::unique_ptr<Script> script)
{ {
if (find(scripts.begin(), scripts.end(), script) == scripts.end()) if (find(scripts.begin(), scripts.end(), script) == scripts.end())
@ -373,18 +366,16 @@ namespace Automation4 {
LocalScriptManager::LocalScriptManager(agi::Context *c) LocalScriptManager::LocalScriptManager(agi::Context *c)
: context(c) : context(c)
, connections(agi::signal::make_vector({ , file_open_connection(c->subsController->AddFileOpenListener(&LocalScriptManager::Reload, this))
c->subsController->AddFileSaveListener(&LocalScriptManager::OnSubtitlesSave, this),
c->subsController->AddFileOpenListener(&LocalScriptManager::Reload, this),
}))
{ {
AddScriptChangeListener(&LocalScriptManager::SaveLoadedList, this);
} }
void LocalScriptManager::Reload() void LocalScriptManager::Reload()
{ {
scripts.clear(); scripts.clear();
auto local_scripts = context->ass->GetScriptInfo("Automation Scripts"); auto const& local_scripts = context->ass->Properties.automation_scripts;
if (local_scripts.empty()) { if (local_scripts.empty()) {
ScriptsChanged(); ScriptsChanged();
return; return;
@ -421,7 +412,7 @@ namespace Automation4 {
ScriptsChanged(); ScriptsChanged();
} }
void LocalScriptManager::OnSubtitlesSave() void LocalScriptManager::SaveLoadedList()
{ {
// Store Automation script data // Store Automation script data
// Algorithm: // Algorithm:
@ -450,7 +441,7 @@ namespace Automation4 {
scripts_string += scriptfn; scripts_string += scriptfn;
} }
context->ass->SetScriptInfo("Automation Scripts", scripts_string); context->ass->Properties.automation_scripts = std::move(scripts_string);
} }
// ScriptFactory // ScriptFactory

View File

@ -190,7 +190,7 @@ namespace Automation4 {
public: public:
/// Deletes all scripts managed /// Deletes all scripts managed
virtual ~ScriptManager(); virtual ~ScriptManager() = default;
/// Add a script to the manager. /// Add a script to the manager.
void Add(std::unique_ptr<Script> script); void Add(std::unique_ptr<Script> script);
/// Remove a script from the manager, and delete the Script object. /// Remove a script from the manager, and delete the Script object.
@ -215,9 +215,9 @@ namespace Automation4 {
/// Manager for scripts specified by a subtitle file /// Manager for scripts specified by a subtitle file
class LocalScriptManager final : public ScriptManager { class LocalScriptManager final : public ScriptManager {
agi::Context *context; agi::Context *context;
std::vector<agi::signal::Connection> connections; agi::signal::Connection file_open_connection;
void OnSubtitlesSave(); void SaveLoadedList();
public: public:
LocalScriptManager(agi::Context *context); LocalScriptManager(agi::Context *context);
void Reload() override; void Reload() override;

View File

@ -127,7 +127,6 @@ BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
connections = agi::signal::make_vector({ connections = agi::signal::make_vector({
context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this), context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this),
context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this), context->subsController->AddFileOpenListener(&BaseGrid::OnSubtitlesOpen, this),
context->subsController->AddFileSaveListener(&BaseGrid::OnSubtitlesSave, this),
context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this), context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this),
context->selectionController->AddSelectionListener([&]{ Refresh(false); }), context->selectionController->AddSelectionListener([&]{ Refresh(false); }),
@ -184,11 +183,7 @@ void BaseGrid::OnSubtitlesCommit(int type) {
} }
void BaseGrid::OnSubtitlesOpen() { void BaseGrid::OnSubtitlesOpen() {
ScrollTo(context->ass->GetUIStateAsInt("Scroll Position")); ScrollTo(context->ass->Properties.scroll_position);
}
void BaseGrid::OnSubtitlesSave() {
context->ass->SaveUIState("Scroll Position", std::to_string(yPos));
} }
void BaseGrid::OnShowColMenu(wxCommandEvent &event) { void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
@ -444,7 +439,7 @@ void BaseGrid::OnSize(wxSizeEvent &) {
void BaseGrid::OnScroll(wxScrollEvent &event) { void BaseGrid::OnScroll(wxScrollEvent &event) {
int newPos = event.GetPosition(); int newPos = event.GetPosition();
if (yPos != newPos) { if (yPos != newPos) {
yPos = newPos; context->ass->Properties.scroll_position = yPos = newPos;
Refresh(false); Refresh(false);
} }
} }
@ -580,7 +575,7 @@ void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) {
void BaseGrid::ScrollTo(int y) { void BaseGrid::ScrollTo(int y) {
int nextY = mid(0, y, GetRows() - 1); int nextY = mid(0, y, GetRows() - 1);
if (yPos != nextY) { if (yPos != nextY) {
yPos = nextY; context->ass->Properties.scroll_position = yPos = nextY;
scrollBar->SetThumbPosition(yPos); scrollBar->SetThumbPosition(yPos);
Refresh(false); Refresh(false);
} }
@ -605,7 +600,7 @@ void BaseGrid::AdjustScrollbar() {
int drawPerScreen = clientSize.GetHeight() / lineHeight; int drawPerScreen = clientSize.GetHeight() / lineHeight;
int rows = GetRows(); int rows = GetRows();
yPos = mid(0, yPos, rows - 1); context->ass->Properties.scroll_position = yPos = mid(0, yPos, rows - 1);
scrollBar->SetScrollbar(yPos, drawPerScreen, rows + drawPerScreen - 1, drawPerScreen - 2, true); scrollBar->SetScrollbar(yPos, drawPerScreen, rows + drawPerScreen - 1, drawPerScreen - 2, true);
scrollBar->Thaw(); scrollBar->Thaw();

View File

@ -94,7 +94,6 @@ class BaseGrid final : public wxWindow {
void OnSize(wxSizeEvent &event); void OnSize(wxSizeEvent &event);
void OnSubtitlesCommit(int type); void OnSubtitlesCommit(int type);
void OnSubtitlesOpen(); void OnSubtitlesOpen();
void OnSubtitlesSave();
void OnActiveLineChanged(AssDialogue *); void OnActiveLineChanged(AssDialogue *);
void ScrollTo(int y); void ScrollTo(int y);

View File

@ -87,7 +87,7 @@ DialogExport::DialogExport(agi::Context *c)
filter_list->Bind(wxEVT_LISTBOX, &DialogExport::OnChange, this); filter_list->Bind(wxEVT_LISTBOX, &DialogExport::OnChange, this);
// Get selected filters // Get selected filters
std::string selected = c->ass->GetScriptInfo("Export filters"); std::string const& selected = c->ass->Properties.export_filters;
boost::char_separator<char> sep("|"); boost::char_separator<char> sep("|");
for (auto const& token : boost::tokenizer<boost::char_separator<char>>(selected, sep)) { for (auto const& token : boost::tokenizer<boost::char_separator<char>>(selected, sep)) {
auto it = find(begin(filters), end(filters), token); auto it = find(begin(filters), end(filters), token);
@ -119,7 +119,7 @@ DialogExport::DialogExport(agi::Context *c)
wxSizer *charset_list_sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer *charset_list_sizer = new wxBoxSizer(wxHORIZONTAL);
charset_list_sizer->Add(charset_list_label, wxSizerFlags().Center().Border(wxRIGHT)); charset_list_sizer->Add(charset_list_label, wxSizerFlags().Center().Border(wxRIGHT));
charset_list_sizer->Add(charset_list, wxSizerFlags(1).Expand()); charset_list_sizer->Add(charset_list, wxSizerFlags(1).Expand());
if (!charset_list->SetStringSelection(to_wx(c->ass->GetScriptInfo("Export Encoding")))) if (!charset_list->SetStringSelection(to_wx(c->ass->Properties.export_encoding)))
charset_list->SetStringSelection("Unicode (UTF-8)"); charset_list->SetStringSelection("Unicode (UTF-8)");
wxSizer *top_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Filters")); wxSizer *top_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Filters"));
@ -148,15 +148,14 @@ DialogExport::DialogExport(agi::Context *c)
} }
DialogExport::~DialogExport() { DialogExport::~DialogExport() {
std::string infoList; c->ass->Properties.export_filters.clear();
for (size_t i = 0; i < filter_list->GetCount(); ++i) { for (size_t i = 0; i < filter_list->GetCount(); ++i) {
if (filter_list->IsChecked(i)) { if (filter_list->IsChecked(i)) {
if (!infoList.empty()) if (!c->ass->Properties.export_filters.empty())
infoList += "|"; c->ass->Properties.export_filters += "|";
infoList += from_wx(filter_list->GetString(i)); c->ass->Properties.export_filters += from_wx(filter_list->GetString(i));
} }
} }
c->ass->SetScriptInfo("Export filters", infoList);
} }
void DialogExport::OnProcess(wxCommandEvent &) { void DialogExport::OnProcess(wxCommandEvent &) {
@ -172,7 +171,7 @@ void DialogExport::OnProcess(wxCommandEvent &) {
try { try {
wxBusyCursor busy; wxBusyCursor busy;
c->ass->SetScriptInfo("Export Encoding", from_wx(charset_list->GetStringSelection())); c->ass->Properties.export_encoding = from_wx(charset_list->GetStringSelection());
exporter->Export(filename, from_wx(charset_list->GetStringSelection()), this); exporter->Export(filename, from_wx(charset_list->GetStringSelection()), this);
} }
catch (agi::UserCancelException const&) { catch (agi::UserCancelException const&) {

View File

@ -319,7 +319,7 @@ void DialogStyleManager::UpdateStorage() {
void DialogStyleManager::OnChangeCatalog() { void DialogStyleManager::OnChangeCatalog() {
std::string catalog(from_wx(CatalogList->GetStringSelection())); std::string catalog(from_wx(CatalogList->GetStringSelection()));
c->ass->SetScriptInfo("Last Style Storage", catalog); c->ass->Properties.style_storage = catalog;
Store.LoadCatalog(catalog); Store.LoadCatalog(catalog);
UpdateStorage(); UpdateStorage();
} }
@ -341,15 +341,12 @@ void DialogStyleManager::LoadCatalog() {
} }
// Set to default if available // Set to default if available
std::string pickStyle = c->ass->GetScriptInfo("Last Style Storage"); std::string pickStyle = c->ass->Properties.style_storage;
if (pickStyle.empty()) if (pickStyle.empty())
pickStyle = "Default"; pickStyle = "Default";
int opt = CatalogList->FindString(to_wx(pickStyle), false); int opt = CatalogList->FindString(to_wx(pickStyle), false);
if (opt != wxNOT_FOUND) CatalogList->SetSelection(opt == wxNOT_FOUND ? 0 : opt);
CatalogList->SetSelection(opt);
else
CatalogList->SetSelection(0);
OnChangeCatalog(); OnChangeCatalog();
} }

View File

@ -55,20 +55,15 @@ Project::Project(agi::Context *c) : context(c) {
OPT_SUB("Subtitle/Provider", &Project::ReloadVideo, this); OPT_SUB("Subtitle/Provider", &Project::ReloadVideo, this);
OPT_SUB("Video/Force BT.601", &Project::ReloadVideo, this); OPT_SUB("Video/Force BT.601", &Project::ReloadVideo, this);
OPT_SUB("Video/Provider", &Project::ReloadVideo, this); OPT_SUB("Video/Provider", &Project::ReloadVideo, this);
c->subsController->AddFileSaveListener(&Project::OnSubtitlesSave, this);
} }
Project::~Project() { } Project::~Project() { }
void Project::OnSubtitlesSave() { void Project::UpdateRelativePaths() {
context->ass->SetScriptInfo("Audio File", context->ass->Properties.audio_file = config::path->MakeRelative(audio_file, "?script").generic_string();
config::path->MakeRelative(audio_file, "?script").generic_string()); context->ass->Properties.video_file = config::path->MakeRelative(video_file, "?script").generic_string();
context->ass->SetScriptInfo("Video File", context->ass->Properties.timecodes_file = config::path->MakeRelative(timecodes_file, "?script").generic_string();
config::path->MakeRelative(video_file, "?script").generic_string()); context->ass->Properties.keyframes_file = config::path->MakeRelative(keyframes_file, "?script").generic_string();
context->ass->SetScriptInfo("VFR File",
config::path->MakeRelative(timecodes_file, "?script").generic_string());
context->ass->SetScriptInfo("Keyframes File",
config::path->MakeRelative(keyframes_file, "?script").generic_string());
} }
void Project::ReloadAudio() { void Project::ReloadAudio() {
@ -89,6 +84,15 @@ void Project::ShowError(std::string const& message) {
ShowError(to_wx(message)); ShowError(to_wx(message));
} }
void Project::SetPath(agi::fs::path& var, const char *token, const char *mru, agi::fs::path const& value) {
var = value;
if (*token)
config::path->SetToken(token, value);
if (*mru)
config::mru->Add(mru, value);
UpdateRelativePaths();
}
void Project::DoLoadSubtitles(agi::fs::path const& path, std::string encoding) { void Project::DoLoadSubtitles(agi::fs::path const& path, std::string encoding) {
try { try {
if (encoding.empty()) if (encoding.empty())
@ -141,10 +145,10 @@ void Project::LoadUnloadFiles() {
auto load_linked = OPT_GET("App/Auto/Load Linked Files")->GetInt(); auto load_linked = OPT_GET("App/Auto/Load Linked Files")->GetInt();
if (!load_linked) return; if (!load_linked) return;
auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio File"), "?script"); auto audio = config::path->MakeAbsolute(context->ass->Properties.audio_file, "?script");
auto video = config::path->MakeAbsolute(context->ass->GetScriptInfo("Video File"), "?script"); auto video = config::path->MakeAbsolute(context->ass->Properties.video_file, "?script");
auto timecodes = config::path->MakeAbsolute(context->ass->GetScriptInfo("VFR File"), "?script"); auto timecodes = config::path->MakeAbsolute(context->ass->Properties.timecodes_file, "?script");
auto keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script"); auto keyframes = config::path->MakeAbsolute(context->ass->Properties.keyframes_file, "?script");
if (video == video_file && audio == audio_file && keyframes == keyframes_file && timecodes == timecodes_file) if (video == video_file && audio == audio_file && keyframes == keyframes_file && timecodes == timecodes_file)
return; return;
@ -160,23 +164,14 @@ void Project::LoadUnloadFiles() {
CloseVideo(); CloseVideo();
else if ((loaded_video = DoLoadVideo(video))) { else if ((loaded_video = DoLoadVideo(video))) {
auto vc = context->videoController.get(); auto vc = context->videoController.get();
vc->JumpToFrame(context->ass->GetUIStateAsInt("Video Position")); vc->JumpToFrame(context->ass->Properties.video_position);
std::string arString = context->ass->GetUIState("Video Aspect Ratio"); auto ar_mode = static_cast<AspectRatio>(context->ass->Properties.ar_mode);
if (boost::starts_with(arString, "c")) { if (ar_mode == AspectRatio::Custom)
double ar = 0.; vc->SetAspectRatio(context->ass->Properties.ar_value);
agi::util::try_parse(arString.substr(1), &ar); else
vc->SetAspectRatio(ar); vc->SetAspectRatio(ar_mode);
} context->videoDisplay->SetZoom(context->ass->Properties.video_zoom);
else {
int ar = 0;
if (agi::util::try_parse(arString, &ar) && ar >= 0 && ar < 4)
vc->SetAspectRatio((AspectRatio)ar);
}
double videoZoom = 0.;
if (agi::util::try_parse(context->ass->GetUIState("Video Zoom Percent"), &videoZoom))
context->videoDisplay->SetZoom(videoZoom);
} }
} }
@ -225,9 +220,7 @@ void Project::DoLoadAudio(agi::fs::path const& path, bool quiet) {
return ShowError(e.GetChainedMessage()); return ShowError(e.GetChainedMessage());
} }
audio_file = path; SetPath(audio_file, "?audio", "Audio", path);
config::path->SetToken("?audio", path);
config::mru->Add("Audio", path);
AnnounceAudioProviderModified(audio_provider.get()); AnnounceAudioProviderModified(audio_provider.get());
} }
@ -238,8 +231,7 @@ void Project::LoadAudio(agi::fs::path const& path) {
void Project::CloseAudio() { void Project::CloseAudio() {
AnnounceAudioProviderModified(nullptr); AnnounceAudioProviderModified(nullptr);
audio_provider.reset(); audio_provider.reset();
audio_file.clear(); SetPath(audio_file, "?audio", "", "");
config::path->SetToken("?audio", "");
} }
bool Project::DoLoadVideo(agi::fs::path const& path) { bool Project::DoLoadVideo(agi::fs::path const& path) {
@ -266,12 +258,10 @@ bool Project::DoLoadVideo(agi::fs::path const& path) {
timecodes = video_provider->GetFPS(); timecodes = video_provider->GetFPS();
keyframes = video_provider->GetKeyFrames(); keyframes = video_provider->GetKeyFrames();
timecodes_file.clear(); timecodes_file.clear();
keyframes_file.clear(); keyframes_file.clear();
SetPath(video_file, "?video", "Video", path);
video_file = path;
config::mru->Add("Video", path);
config::path->SetToken("?video", path);
std::string warning = video_provider->GetWarning(); std::string warning = video_provider->GetWarning();
if (!warning.empty()) if (!warning.empty())
@ -296,15 +286,13 @@ void Project::LoadVideo(agi::fs::path const& path) {
void Project::CloseVideo() { void Project::CloseVideo() {
AnnounceVideoProviderModified(nullptr); AnnounceVideoProviderModified(nullptr);
video_provider.reset(); video_provider.reset();
video_file.clear(); SetPath(video_file, "?video", "", "");
config::path->SetToken("?video", "");
video_has_subtitles = false; video_has_subtitles = false;
} }
void Project::DoLoadTimecodes(agi::fs::path const& path) { void Project::DoLoadTimecodes(agi::fs::path const& path) {
timecodes = agi::vfr::Framerate(path); timecodes = agi::vfr::Framerate(path);
timecodes_file = path; SetPath(timecodes_file, "", "Timecodes", path);
config::mru->Add("Timecodes", path);
AnnounceTimecodesModified(timecodes); AnnounceTimecodesModified(timecodes);
} }
@ -324,14 +312,13 @@ void Project::LoadTimecodes(agi::fs::path const& path) {
void Project::CloseTimecodes() { void Project::CloseTimecodes() {
timecodes = video_provider ? video_provider->GetFPS() : agi::vfr::Framerate{}; timecodes = video_provider ? video_provider->GetFPS() : agi::vfr::Framerate{};
timecodes_file.clear(); SetPath(timecodes_file, "", "", "");
AnnounceTimecodesModified(timecodes); AnnounceTimecodesModified(timecodes);
} }
void Project::DoLoadKeyframes(agi::fs::path const& path) { void Project::DoLoadKeyframes(agi::fs::path const& path) {
keyframes = agi::keyframe::Load(path); keyframes = agi::keyframe::Load(path);
keyframes_file = path; SetPath(keyframes_file, "", "Keyframes", path);
config::mru->Add("Keyframes", path);
AnnounceKeyframesModified(keyframes); AnnounceKeyframesModified(keyframes);
} }
@ -351,7 +338,7 @@ void Project::LoadKeyframes(agi::fs::path const& path) {
void Project::CloseKeyframes() { void Project::CloseKeyframes() {
keyframes = video_provider ? video_provider->GetKeyFrames() : std::vector<int>{}; keyframes = video_provider ? video_provider->GetKeyFrames() : std::vector<int>{};
keyframes_file.clear(); SetPath(keyframes_file, "", "", "");
AnnounceKeyframesModified(keyframes); AnnounceKeyframesModified(keyframes);
} }
@ -431,8 +418,12 @@ void Project::LoadList(std::vector<agi::fs::path> const& files) {
if (!subs.empty()) if (!subs.empty())
DoLoadSubtitles(subs); DoLoadSubtitles(subs);
if (!video.empty()) if (!video.empty()) {
auto rel_audio_file = context->ass->Properties.audio_file;
DoLoadVideo(video); DoLoadVideo(video);
if (!rel_audio_file.empty() && context->ass->Properties.audio_file.empty())
context->ass->Properties.audio_file.swap(rel_audio_file);
}
if (!audio.empty()) if (!audio.empty())
DoLoadAudio(audio, false); DoLoadAudio(audio, false);
else if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file) else if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file)

View File

@ -29,7 +29,6 @@ class wxString;
namespace agi { struct Context; } namespace agi { struct Context; }
class Project { class Project {
// Things owned by this
std::unique_ptr<AudioProvider> audio_provider; std::unique_ptr<AudioProvider> audio_provider;
std::unique_ptr<AsyncVideoProvider> video_provider; std::unique_ptr<AsyncVideoProvider> video_provider;
agi::vfr::Framerate timecodes; agi::vfr::Framerate timecodes;
@ -46,10 +45,7 @@ class Project {
agi::signal::Signal<std::vector<int> const&> AnnounceKeyframesModified; agi::signal::Signal<std::vector<int> const&> AnnounceKeyframesModified;
bool video_has_subtitles = false; bool video_has_subtitles = false;
DialogProgress *progress = nullptr; DialogProgress *progress = nullptr;
// Things not
agi::Context *context = nullptr; agi::Context *context = nullptr;
void ShowError(wxString const& message); void ShowError(wxString const& message);
@ -62,11 +58,12 @@ class Project {
void DoLoadKeyframes(agi::fs::path const& path); void DoLoadKeyframes(agi::fs::path const& path);
void LoadUnloadFiles(); void LoadUnloadFiles();
void UpdateRelativePaths();
void OnSubtitlesSave();
void ReloadAudio(); void ReloadAudio();
void ReloadVideo(); void ReloadVideo();
void SetPath(agi::fs::path& var, const char *token, const char *mru, agi::fs::path const& value);
public: public:
Project(agi::Context *context); Project(agi::Context *context);
~Project(); ~Project();

View File

@ -27,7 +27,6 @@
SelectionController::SelectionController(agi::Context *c) SelectionController::SelectionController(agi::Context *c)
: context(c) : context(c)
, open_connection(c->subsController->AddFileOpenListener(&SelectionController::OnSubtitlesOpen, this)) , open_connection(c->subsController->AddFileOpenListener(&SelectionController::OnSubtitlesOpen, this))
, save_connection(c->subsController->AddFileSaveListener(&SelectionController::OnSubtitlesSave, this))
{ {
} }
@ -35,7 +34,7 @@ void SelectionController::OnSubtitlesOpen() {
selection.clear(); selection.clear();
active_line = nullptr; active_line = nullptr;
if (!context->ass->Events.empty()) { if (!context->ass->Events.empty()) {
int row = mid<int>(0, context->ass->GetUIStateAsInt("Active Line"), context->ass->Events.size() - 1); int row = mid<int>(0, context->ass->Properties.active_row, context->ass->Events.size() - 1);
active_line = &*std::next(context->ass->Events.begin(), row); active_line = &*std::next(context->ass->Events.begin(), row);
selection.insert(active_line); selection.insert(active_line);
} }
@ -43,11 +42,6 @@ void SelectionController::OnSubtitlesOpen() {
AnnounceActiveLineChanged(active_line); AnnounceActiveLineChanged(active_line);
} }
void SelectionController::OnSubtitlesSave() {
if (active_line)
context->ass->SaveUIState("Active Line", std::to_string(active_line->Row));
}
void SelectionController::SetSelectedSet(Selection new_selection) { void SelectionController::SetSelectedSet(Selection new_selection) {
selection = std::move(new_selection); selection = std::move(new_selection);
AnnounceSelectedSetChanged(); AnnounceSelectedSetChanged();
@ -56,6 +50,8 @@ void SelectionController::SetSelectedSet(Selection new_selection) {
void SelectionController::SetActiveLine(AssDialogue *new_line) { void SelectionController::SetActiveLine(AssDialogue *new_line) {
if (new_line != active_line) { if (new_line != active_line) {
active_line = new_line; active_line = new_line;
if (active_line)
context->ass->Properties.active_row = active_line->Row;
AnnounceActiveLineChanged(new_line); AnnounceActiveLineChanged(new_line);
} }
} }
@ -64,6 +60,8 @@ void SelectionController::SetSelectionAndActive(Selection new_selection, AssDial
bool active_line_changed = new_line != active_line; bool active_line_changed = new_line != active_line;
selection = std::move(new_selection); selection = std::move(new_selection);
active_line = new_line; active_line = new_line;
if (active_line)
context->ass->Properties.active_row = active_line->Row;
AnnounceSelectedSetChanged(); AnnounceSelectedSetChanged();
if (active_line_changed) if (active_line_changed)

View File

@ -47,7 +47,6 @@ class SelectionController {
AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none
agi::signal::Connection open_connection; agi::signal::Connection open_connection;
agi::signal::Connection save_connection;
void OnSubtitlesOpen(); void OnSubtitlesOpen();
void OnSubtitlesSave(); void OnSubtitlesSave();

View File

@ -220,11 +220,9 @@ void SubsController::Save(agi::fs::path const& filename, std::string const& enco
this->filename = filename; this->filename = filename;
config::path->SetToken("?script", filename.parent_path()); config::path->SetToken("?script", filename.parent_path());
FileSave();
context->ass->CleanExtradata(); context->ass->CleanExtradata();
writer->WriteFile(context->ass.get(), filename, 0, encoding); writer->WriteFile(context->ass.get(), filename, 0, encoding);
FileSave();
} }
catch (...) { catch (...) {
autosaved_commit_id = old_autosaved_commit_id; autosaved_commit_id = old_autosaved_commit_id;

View File

@ -52,11 +52,12 @@ class SubsController {
/// A new file has been opened (filename) /// A new file has been opened (filename)
agi::signal::Signal<agi::fs::path> FileOpen; agi::signal::Signal<agi::fs::path> FileOpen;
/// The file is about to be saved /// The file has been 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<> FileSave;
/// The file is about to be saved
/// This signal is intended for adding metadata which is awkward or
/// expensive to always keep up to date
agi::signal::Signal<> UpdateProperties;
/// The filename of the currently open file, if any /// The filename of the currently open file, if any
agi::fs::path filename; agi::fs::path filename;

View File

@ -22,6 +22,7 @@
#include "ass_file.h" #include "ass_file.h"
#include "ass_style.h" #include "ass_style.h"
#include "ass_parser.h" #include "ass_parser.h"
#include "options.h"
#include "string_codec.h" #include "string_codec.h"
#include "text_file_reader.h" #include "text_file_reader.h"
#include "text_file_writer.h" #include "text_file_writer.h"
@ -93,6 +94,41 @@ struct Writer {
} }
} }
void Write(ProjectProperties const& properties) {
file.WriteLineToFile("");
file.WriteLineToFile("[Aegisub Project Garbage]");
WriteIfNotEmpty("Automation Scripts: ", properties.automation_scripts);
WriteIfNotEmpty("Export Filters: ", properties.export_filters);
WriteIfNotEmpty("Export Encoding: ", properties.export_encoding);
WriteIfNotEmpty("Last Style Storage: ", properties.style_storage);
WriteIfNotEmpty("Audio File: ", properties.audio_file);
WriteIfNotEmpty("Video File: ", properties.video_file);
WriteIfNotEmpty("Timecodes File: ", properties.timecodes_file);
WriteIfNotEmpty("Keyframes File: ", properties.keyframes_file);
WriteIfNotZero("Video AR Mode: ", properties.ar_mode);
WriteIfNotZero("Video AR Value: ", properties.ar_value);
if (OPT_GET("App/Save UI State")->GetBool()) {
WriteIfNotZero("Video Zoom Percent: ", properties.video_zoom);
WriteIfNotZero("Scroll Position: ", properties.scroll_position);
WriteIfNotZero("Active Line: ", properties.active_row);
WriteIfNotZero("Video Position: ", properties.video_position);
}
}
void WriteIfNotEmpty(const char *key, std::string const& value) {
if (!value.empty())
file.WriteLineToFile(key + value);
}
template<typename Number>
void WriteIfNotZero(const char *key, Number n) {
if (n != Number{})
file.WriteLineToFile(key + std::to_string(n));
}
void WriteExtradata(AegisubExtradataMap const& extradata) { void WriteExtradata(AegisubExtradataMap const& extradata) {
if (extradata.size() == 0) if (extradata.size() == 0)
return; return;
@ -125,6 +161,7 @@ struct Writer {
void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const { void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
Writer writer(filename, encoding); Writer writer(filename, encoding);
writer.Write(src->Info); writer.Write(src->Info);
writer.Write(src->Properties);
writer.Write(src->Styles); writer.Write(src->Styles);
writer.Write(src->Attachments); writer.Write(src->Attachments);
writer.Write(src->Events); writer.Write(src->Events);

View File

@ -61,7 +61,6 @@ VideoController::VideoController(agi::Context *c)
context->ass->AddCommitListener(&VideoController::OnSubtitlesCommit, this), context->ass->AddCommitListener(&VideoController::OnSubtitlesCommit, this),
context->project->AddVideoProviderListener(&VideoController::OnNewVideoProvider, this), context->project->AddVideoProviderListener(&VideoController::OnNewVideoProvider, this),
context->selectionController->AddActiveLineListener(&VideoController::OnActiveLineChanged, this), context->selectionController->AddActiveLineListener(&VideoController::OnActiveLineChanged, this),
context->subsController->AddFileSaveListener(&VideoController::OnSubtitlesSave, this),
})) }))
{ {
Bind(EVT_VIDEO_ERROR, &VideoController::OnVideoError, this); Bind(EVT_VIDEO_ERROR, &VideoController::OnVideoError, this);
@ -76,6 +75,9 @@ void VideoController::OnNewVideoProvider(AsyncVideoProvider *new_provider) {
provider = new_provider; provider = new_provider;
if (!provider) { if (!provider) {
color_matrix.clear(); color_matrix.clear();
context->ass->Properties.ar_mode = 0;
context->ass->Properties.ar_value = 0.0;
context->ass->Properties.video_position = 0;
return; return;
} }
@ -98,33 +100,12 @@ void VideoController::OnSubtitlesCommit(int type, std::set<const AssDialogue *>
} }
} }
if (changed.empty() || no_amend) if (changed.empty())
provider->LoadSubtitles(context->ass.get()); provider->LoadSubtitles(context->ass.get());
else else
provider->UpdateSubtitles(context->ass.get(), changed); provider->UpdateSubtitles(context->ass.get(), changed);
if (!IsPlaying()) if (!IsPlaying())
provider->GetFrame(frame_n, TimeAtFrame(frame_n)); provider->GetFrame(frame_n, TimeAtFrame(frame_n));
no_amend = false;
}
void VideoController::OnSubtitlesSave() {
no_amend = true;
if (!provider) {
context->ass->SaveUIState("Video Aspect Ratio", "");
context->ass->SaveUIState("Video Position", "");
return;
}
std::string ar;
if (ar_type == AspectRatio::Custom)
ar = "c" + std::to_string(ar_value);
else
ar = std::to_string((int)ar_type);
context->ass->SaveUIState("Video Aspect Ratio", ar);
context->ass->SaveUIState("Video Position", std::to_string(frame_n));
} }
void VideoController::OnActiveLineChanged(AssDialogue *line) { void VideoController::OnActiveLineChanged(AssDialogue *line) {
@ -133,6 +114,7 @@ void VideoController::OnActiveLineChanged(AssDialogue *line) {
} }
void VideoController::RequestFrame() { void VideoController::RequestFrame() {
context->ass->Properties.video_position = frame_n;
provider->RequestFrame(frame_n, TimeAtFrame(frame_n)); provider->RequestFrame(frame_n, TimeAtFrame(frame_n));
} }
@ -243,12 +225,16 @@ double VideoController::GetARFromType(AspectRatio type) const {
void VideoController::SetAspectRatio(double value) { void VideoController::SetAspectRatio(double value) {
ar_type = AspectRatio::Custom; ar_type = AspectRatio::Custom;
ar_value = mid(.5, value, 5.); ar_value = mid(.5, value, 5.);
context->ass->Properties.ar_mode = (int)ar_type;
context->ass->Properties.ar_value = ar_value;
ARChange(ar_type, ar_value); ARChange(ar_type, ar_value);
} }
void VideoController::SetAspectRatio(AspectRatio type) { void VideoController::SetAspectRatio(AspectRatio type) {
ar_value = mid(.5, GetARFromType(type), 5.); ar_value = mid(.5, GetARFromType(type), 5.);
ar_type = type; ar_type = type;
context->ass->Properties.ar_mode = (int)ar_type;
context->ass->Properties.ar_value = ar_value;
ARChange(ar_type, ar_value); ARChange(ar_type, ar_value);
} }

View File

@ -101,19 +101,12 @@ class VideoController final : public wxEvtHandler {
std::vector<agi::signal::Connection> connections; std::vector<agi::signal::Connection> connections;
/// Amending the frame source's copy of the subtitle file requires that it
/// be kept in perfect sync. Saving the file can add lines to the file
/// without a commit, breaking this sync, so force a non-amend after each
/// save.
bool no_amend = false;
void OnPlayTimer(wxTimerEvent &event); void OnPlayTimer(wxTimerEvent &event);
void OnVideoError(VideoProviderErrorEvent const& err); void OnVideoError(VideoProviderErrorEvent const& err);
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err); void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
void OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed); void OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed);
void OnSubtitlesSave();
void OnNewVideoProvider(AsyncVideoProvider *provider); void OnNewVideoProvider(AsyncVideoProvider *provider);
void OnActiveLineChanged(AssDialogue *line); void OnActiveLineChanged(AssDialogue *line);

View File

@ -109,7 +109,6 @@ VideoDisplay::VideoDisplay(wxToolBar *toolbar, bool freeSize, wxComboBox *zoomBo
connections = agi::signal::make_vector({ connections = agi::signal::make_vector({
con->project->AddVideoProviderListener(&VideoDisplay::UpdateSize, this), con->project->AddVideoProviderListener(&VideoDisplay::UpdateSize, this),
con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this), con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this),
con->subsController->AddFileSaveListener(&VideoDisplay::OnSubtitlesSave, this),
}); });
Bind(wxEVT_PAINT, std::bind(&VideoDisplay::Render, this)); Bind(wxEVT_PAINT, std::bind(&VideoDisplay::Render, this));
@ -345,6 +344,7 @@ void VideoDisplay::OnSizeEvent(wxSizeEvent &event) {
PositionVideo(); PositionVideo();
zoomValue = double(viewport_height) / con->project->VideoProvider()->GetHeight(); zoomValue = double(viewport_height) / con->project->VideoProvider()->GetHeight();
zoomBox->ChangeValue(wxString::Format("%g%%", zoomValue * 100.)); zoomBox->ChangeValue(wxString::Format("%g%%", zoomValue * 100.));
con->ass->Properties.video_zoom = zoomValue;
} }
else { else {
PositionVideo(); PositionVideo();
@ -385,11 +385,13 @@ void VideoDisplay::OnKeyDown(wxKeyEvent &event) {
} }
void VideoDisplay::SetZoom(double value) { void VideoDisplay::SetZoom(double value) {
if (value == 0) return;
zoomValue = std::max(value, .125); zoomValue = std::max(value, .125);
size_t selIndex = zoomValue / .125 - 1; size_t selIndex = zoomValue / .125 - 1;
if (selIndex < zoomBox->GetCount()) if (selIndex < zoomBox->GetCount())
zoomBox->SetSelection(selIndex); zoomBox->SetSelection(selIndex);
zoomBox->ChangeValue(wxString::Format("%g%%", zoomValue * 100.)); zoomBox->ChangeValue(wxString::Format("%g%%", zoomValue * 100.));
con->ass->Properties.video_zoom = zoomValue;
UpdateSize(); UpdateSize();
} }
@ -397,6 +399,7 @@ void VideoDisplay::SetZoomFromBox(wxCommandEvent &) {
int sel = zoomBox->GetSelection(); int sel = zoomBox->GetSelection();
if (sel != wxNOT_FOUND) { if (sel != wxNOT_FOUND) {
zoomValue = (sel + 1) * .125; zoomValue = (sel + 1) * .125;
con->ass->Properties.video_zoom = zoomValue;
UpdateSize(); UpdateSize();
} }
} }
@ -444,7 +447,3 @@ void VideoDisplay::Unload() {
tool.reset(); tool.reset();
pending_frame.reset(); pending_frame.reset();
} }
void VideoDisplay::OnSubtitlesSave() {
con->ass->SaveUIState("Video Zoom Percent", std::to_string(zoomValue));
}

View File

@ -141,8 +141,6 @@ class VideoDisplay final : public wxGLCanvas {
void OnSizeEvent(wxSizeEvent &event); void OnSizeEvent(wxSizeEvent &event);
void OnContextMenu(wxContextMenuEvent&); void OnContextMenu(wxContextMenuEvent&);
void OnSubtitlesSave();
public: public:
/// @brief Constructor /// @brief Constructor
VideoDisplay( VideoDisplay(