mirror of https://github.com/odrling/Aegisub
Add pre-save signal to AssFile and move most of the logic in FrameMain::SaveSubtitles to slots for this signal
Originally committed to SVN as r5207.
This commit is contained in:
parent
59300cbc27
commit
7210ea17d8
|
@ -172,34 +172,23 @@ void AssFile::Load(const wxString &_filename,wxString charset,bool addToRecent)
|
||||||
FileOpen(filename);
|
FileOpen(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssFile::Save(wxString _filename,bool setfilename,bool addToRecent,const wxString encoding) {
|
void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) {
|
||||||
// Finds last dot
|
SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
|
||||||
int i = 0;
|
if (!writer)
|
||||||
for (i=(int)_filename.size();--i>=0;) {
|
throw "Unknown file type.";
|
||||||
if (_filename[i] == '.') break;
|
|
||||||
}
|
|
||||||
wxString extension = _filename.substr(i+1);
|
|
||||||
extension.Lower();
|
|
||||||
|
|
||||||
// Get writer
|
|
||||||
SubtitleFormat *writer = SubtitleFormat::GetWriter(_filename);
|
|
||||||
|
|
||||||
// Write file
|
|
||||||
if (writer) {
|
|
||||||
writer->SetTarget(this);
|
|
||||||
writer->WriteFile(_filename,encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't find a type
|
|
||||||
else throw _T("Unknown file type.");
|
|
||||||
|
|
||||||
// Add to recent
|
|
||||||
if (addToRecent) AddToRecent(_filename);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
if (setfilename) {
|
if (setfilename) {
|
||||||
savedCommitId = commitId;
|
savedCommitId = commitId;
|
||||||
filename = _filename;
|
filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSave();
|
||||||
|
|
||||||
|
writer->SetTarget(this);
|
||||||
|
writer->WriteFile(filename, encoding);
|
||||||
|
|
||||||
|
if (addToRecent) {
|
||||||
|
AddToRecent(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,6 +765,8 @@ AssStyle *AssFile::GetStyle(wxString name) {
|
||||||
|
|
||||||
void AssFile::AddToRecent(wxString file) {
|
void AssFile::AddToRecent(wxString file) {
|
||||||
config::mru->Add("Subtitle", STD_STR(file));
|
config::mru->Add("Subtitle", STD_STR(file));
|
||||||
|
wxFileName filepath(file);
|
||||||
|
OPT_SET("Path/Last/Subtitles")->SetString(STD_STR(filepath.GetPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString AssFile::GetWildcardList(int mode) {
|
wxString AssFile::GetWildcardList(int mode) {
|
||||||
|
|
|
@ -72,8 +72,14 @@ class AssFile {
|
||||||
/// Last saved version of this file
|
/// Last saved version of this file
|
||||||
int savedCommitId;
|
int savedCommitId;
|
||||||
|
|
||||||
|
/// A set of changes has been committed to the file (AssFile::CommitType)
|
||||||
agi::signal::Signal<int> AnnounceCommit;
|
agi::signal::Signal<int> AnnounceCommit;
|
||||||
|
/// A new file has been opened (filename)
|
||||||
agi::signal::Signal<wxString> FileOpen;
|
agi::signal::Signal<wxString> 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<> FileSave;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -231,7 +231,7 @@ struct subtitle_new : public Command {
|
||||||
STR_HELP("New subtitles.")
|
STR_HELP("New subtitles.")
|
||||||
|
|
||||||
void operator()(agi::Context *c) {
|
void operator()(agi::Context *c) {
|
||||||
c->SubsGrid->LoadDefault();
|
c->ass->LoadDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -210,8 +210,6 @@ FrameMain::FrameMain (wxArrayString args)
|
||||||
SetDropTarget(new AegisubFileDropTarget(this));
|
SetDropTarget(new AegisubFileDropTarget(this));
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
StartupLog(_T("Initialize empty file"));
|
|
||||||
SubsGrid->LoadDefault();
|
|
||||||
StartupLog(_T("Load files specified on command line"));
|
StartupLog(_T("Load files specified on command line"));
|
||||||
LoadList(args);
|
LoadList(args);
|
||||||
|
|
||||||
|
@ -458,41 +456,30 @@ void FrameMain::LoadSubtitles(wxString filename,wxString charset) {
|
||||||
/// @param saveas
|
/// @param saveas
|
||||||
/// @param withCharset
|
/// @param withCharset
|
||||||
/// @return
|
/// @return
|
||||||
bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) {
|
bool FrameMain::SaveSubtitles(bool saveas, bool withCharset) {
|
||||||
// Try to get filename from file
|
|
||||||
wxString filename;
|
wxString filename;
|
||||||
if (saveas == false && ass->CanSave()) filename = ass->filename;
|
if (!saveas && ass->CanSave()) {
|
||||||
|
filename = ass->filename;
|
||||||
|
}
|
||||||
|
|
||||||
// Failed, ask user
|
if (filename.empty()) {
|
||||||
if (filename.IsEmpty()) {
|
|
||||||
temp_context->videoContext->Stop();
|
temp_context->videoContext->Stop();
|
||||||
wxString path = lagi_wxString(OPT_GET("Path/Last/Subtitles")->GetString());
|
wxString path = lagi_wxString(OPT_GET("Path/Last/Subtitles")->GetString());
|
||||||
wxFileName origPath(ass->filename);
|
wxFileName origPath(ass->filename);
|
||||||
filename = wxFileSelector(_("Save subtitles file"),path,origPath.GetName() + _T(".ass"),_T("ass"),AssFile::GetWildcardList(1),wxFD_SAVE | wxFD_OVERWRITE_PROMPT,this);
|
filename = wxFileSelector(_("Save subtitles file"), path, origPath.GetName() + ".ass", "ass", AssFile::GetWildcardList(1), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this);
|
||||||
|
}
|
||||||
|
if (filename.empty()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually save
|
wxString charset;
|
||||||
if (!filename.empty()) {
|
|
||||||
// Store path
|
|
||||||
wxFileName filepath(filename);
|
|
||||||
OPT_SET("Path/Last/Subtitles")->SetString(STD_STR(filepath.GetPath()));
|
|
||||||
|
|
||||||
// Fix me, ghetto hack for correct relative path generation in SynchronizeProject()
|
|
||||||
ass->filename = filename;
|
|
||||||
|
|
||||||
// Synchronize
|
|
||||||
SynchronizeProject();
|
|
||||||
|
|
||||||
// Get charset
|
|
||||||
wxString charset = _T("");
|
|
||||||
if (withCharset) {
|
if (withCharset) {
|
||||||
charset = wxGetSingleChoice(_("Choose charset code:"), _T("Charset"),agi::charset::GetEncodingsList<wxArrayString>(),this,-1, -1,true,250,200);
|
charset = wxGetSingleChoice(_("Choose charset code:"), "Charset", agi::charset::GetEncodingsList<wxArrayString>(),this,-1, -1,true,250,200);
|
||||||
if (charset.IsEmpty()) return false;
|
if (charset.empty()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save
|
|
||||||
try {
|
try {
|
||||||
ass->Save(filename,true,true,charset);
|
ass->Save(filename, true, true, charset);
|
||||||
}
|
}
|
||||||
catch (const agi::Exception& err) {
|
catch (const agi::Exception& err) {
|
||||||
wxMessageBox(lagi_wxString(err.GetMessage()), "Error", wxOK | wxICON_ERROR, NULL);
|
wxMessageBox(lagi_wxString(err.GetMessage()), "Error", wxOK | wxICON_ERROR, NULL);
|
||||||
|
@ -507,8 +494,6 @@ bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Try to close subtitles
|
/// @brief Try to close subtitles
|
||||||
|
@ -627,41 +612,6 @@ void FrameMain::SynchronizeProject() {
|
||||||
ass->SetScriptInfo(_T("Video Position"),seekpos);
|
ass->SetScriptInfo(_T("Video Position"),seekpos);
|
||||||
ass->SetScriptInfo(_T("VFR File"),MakeRelativePath(temp_context->videoContext->GetTimecodesName(),ass->filename));
|
ass->SetScriptInfo(_T("VFR File"),MakeRelativePath(temp_context->videoContext->GetTimecodesName(),ass->filename));
|
||||||
ass->SetScriptInfo(_T("Keyframes File"),MakeRelativePath(temp_context->videoContext->GetKeyFramesName(),ass->filename));
|
ass->SetScriptInfo(_T("Keyframes File"),MakeRelativePath(temp_context->videoContext->GetKeyFramesName(),ass->filename));
|
||||||
|
|
||||||
// Store Automation script data
|
|
||||||
// Algorithm:
|
|
||||||
// 1. If script filename has Automation Base Path as a prefix, the path is relative to that (ie. "$")
|
|
||||||
// 2. Otherwise try making it relative to the ass filename
|
|
||||||
// 3. If step 2 failed, or absolut path is shorter than path relative to ass, use absolute path ("/")
|
|
||||||
// 4. Otherwise, use path relative to ass ("~")
|
|
||||||
#ifdef WITH_AUTOMATION
|
|
||||||
wxString scripts_string;
|
|
||||||
wxString autobasefn(lagi_wxString(OPT_GET("Path/Automation/Base")->GetString()));
|
|
||||||
|
|
||||||
const std::vector<Automation4::Script*> &scripts = local_scripts->GetScripts();
|
|
||||||
for (unsigned int i = 0; i < scripts.size(); i++) {
|
|
||||||
Automation4::Script *script = scripts[i];
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
scripts_string += _T("|");
|
|
||||||
|
|
||||||
wxString autobase_rel, assfile_rel;
|
|
||||||
wxString scriptfn(script->GetFilename());
|
|
||||||
autobase_rel = MakeRelativePath(scriptfn, autobasefn);
|
|
||||||
assfile_rel = MakeRelativePath(scriptfn, ass->filename);
|
|
||||||
|
|
||||||
if (autobase_rel.size() <= scriptfn.size() && autobase_rel.size() <= assfile_rel.size()) {
|
|
||||||
scriptfn = _T("$") + autobase_rel;
|
|
||||||
} else if (assfile_rel.size() <= scriptfn.size() && assfile_rel.size() <= autobase_rel.size()) {
|
|
||||||
scriptfn = _T("~") + assfile_rel;
|
|
||||||
} else {
|
|
||||||
scriptfn = _T("/") + wxFileName(scriptfn).GetFullPath(wxPATH_UNIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
scripts_string += scriptfn;
|
|
||||||
}
|
|
||||||
ass->SetScriptInfo(_T("Automation Scripts"), scripts_string);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameMain::OnVideoOpen() {
|
void FrameMain::OnVideoOpen() {
|
||||||
|
@ -1404,6 +1354,46 @@ void FrameMain::OnSubtitlesOpen() {
|
||||||
SetDisplayMode(1,1);
|
SetDisplayMode(1,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameMain::OnSubtitlesSave() {
|
||||||
|
UpdateTitle();
|
||||||
|
|
||||||
|
// Store Automation script data
|
||||||
|
// Algorithm:
|
||||||
|
// 1. If script filename has Automation Base Path as a prefix, the path is relative to that (ie. "$")
|
||||||
|
// 2. Otherwise try making it relative to the ass filename
|
||||||
|
// 3. If step 2 failed, or absolute path is shorter than path relative to ass, use absolute path ("/")
|
||||||
|
// 4. Otherwise, use path relative to ass ("~")
|
||||||
|
#ifdef WITH_AUTOMATION
|
||||||
|
wxString scripts_string;
|
||||||
|
wxString autobasefn(lagi_wxString(OPT_GET("Path/Automation/Base")->GetString()));
|
||||||
|
|
||||||
|
const std::vector<Automation4::Script*> &scripts = local_scripts->GetScripts();
|
||||||
|
for (unsigned int i = 0; i < scripts.size(); i++) {
|
||||||
|
Automation4::Script *script = scripts[i];
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
scripts_string += _T("|");
|
||||||
|
|
||||||
|
wxString autobase_rel, assfile_rel;
|
||||||
|
wxString scriptfn(script->GetFilename());
|
||||||
|
autobase_rel = MakeRelativePath(scriptfn, autobasefn);
|
||||||
|
assfile_rel = MakeRelativePath(scriptfn, ass->filename);
|
||||||
|
|
||||||
|
if (autobase_rel.size() <= scriptfn.size() && autobase_rel.size() <= assfile_rel.size()) {
|
||||||
|
scriptfn = _T("$") + autobase_rel;
|
||||||
|
} else if (assfile_rel.size() <= scriptfn.size() && assfile_rel.size() <= autobase_rel.size()) {
|
||||||
|
scriptfn = _T("~") + assfile_rel;
|
||||||
|
} else {
|
||||||
|
scriptfn = _T("/") + wxFileName(scriptfn).GetFullPath(wxPATH_UNIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
scripts_string += scriptfn;
|
||||||
|
}
|
||||||
|
ass->SetScriptInfo(_T("Automation Scripts"), scripts_string);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void FrameMain::OnKeyDown(wxKeyEvent &event) {
|
void FrameMain::OnKeyDown(wxKeyEvent &event) {
|
||||||
hotkey::check("Main Frame", event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers());
|
hotkey::check("Main Frame", event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers());
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,6 +175,7 @@ private:
|
||||||
|
|
||||||
void OnSubtitlesCommit();
|
void OnSubtitlesCommit();
|
||||||
void OnSubtitlesOpen();
|
void OnSubtitlesOpen();
|
||||||
|
void OnSubtitlesSave();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -752,17 +752,6 @@ void SubtitlesGrid::OnAudioClip(wxCommandEvent &) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Clears grid and sets it to default
|
|
||||||
/// @param _ass
|
|
||||||
void SubtitlesGrid::LoadDefault () {
|
|
||||||
ass->LoadDefault();
|
|
||||||
ClearMaps();
|
|
||||||
UpdateMaps();
|
|
||||||
|
|
||||||
SetActiveLine(GetDialogue(0));
|
|
||||||
SelectRow(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Swaps two lines
|
/// @brief Swaps two lines
|
||||||
/// @param n1
|
/// @param n1
|
||||||
/// @param n2
|
/// @param n2
|
||||||
|
|
|
@ -109,8 +109,6 @@ public:
|
||||||
SubtitlesGrid(FrameMain* parentFrame,wxWindow *parent, wxWindowID id, AssFile *subs, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxWANTS_CHARS, const wxString& name = wxPanelNameStr);
|
SubtitlesGrid(FrameMain* parentFrame,wxWindow *parent, wxWindowID id, AssFile *subs, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxWANTS_CHARS, const wxString& name = wxPanelNameStr);
|
||||||
~SubtitlesGrid();
|
~SubtitlesGrid();
|
||||||
|
|
||||||
void LoadDefault();
|
|
||||||
|
|
||||||
/// @brief Jump to the start/end time of the current subtitle line
|
/// @brief Jump to the start/end time of the current subtitle line
|
||||||
/// @param start Start vs. End time
|
/// @param start Start vs. End time
|
||||||
void SetVideoToSubs(bool start);
|
void SetVideoToSubs(bool start);
|
||||||
|
|
|
@ -199,7 +199,8 @@ void VideoContext::SetVideo(const wxString &filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
provider->LoadSubtitles(grid->ass);
|
provider->LoadSubtitles(grid->ass);
|
||||||
grid->ass->AddCommitListener(&VideoContext::SubtitlesChanged, this);
|
grid->ass->AddCommitListener(&VideoContext::OnSubtitlesCommit, this);
|
||||||
|
grid->ass->AddCommitListener(&VideoContext::OnSubtitlesSave, this);
|
||||||
VideoOpen();
|
VideoOpen();
|
||||||
KeyframesOpen(keyFrames);
|
KeyframesOpen(keyFrames);
|
||||||
TimecodesOpen(FPS());
|
TimecodesOpen(FPS());
|
||||||
|
@ -222,7 +223,7 @@ void VideoContext::Reload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoContext::SubtitlesChanged() {
|
void VideoContext::OnSubtitlesCommit() {
|
||||||
if (!IsLoaded()) return;
|
if (!IsLoaded()) return;
|
||||||
|
|
||||||
bool wasPlaying = isPlaying;
|
bool wasPlaying = isPlaying;
|
||||||
|
@ -234,6 +235,29 @@ void VideoContext::SubtitlesChanged() {
|
||||||
if (wasPlaying) Play();
|
if (wasPlaying) Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoContext::OnSubtitlesSave() {
|
||||||
|
if (!IsLoaded()) {
|
||||||
|
grid->ass->SetScriptInfo("Video File", "");
|
||||||
|
grid->ass->SetScriptInfo("Video Aspect Ratio", "");
|
||||||
|
grid->ass->SetScriptInfo("Video Position", "");
|
||||||
|
grid->ass->SetScriptInfo("VFR File", "");
|
||||||
|
grid->ass->SetScriptInfo("Keyframes File", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString ar;
|
||||||
|
if (arType == 4)
|
||||||
|
ar = wxString::Format("c%g", arValue);
|
||||||
|
else
|
||||||
|
ar = wxString::Format("%d", arType);
|
||||||
|
|
||||||
|
grid->ass->SetScriptInfo("Video File", MakeRelativePath(videoName, grid->ass->filename));
|
||||||
|
grid->ass->SetScriptInfo("Video Aspect Ratio", ar);
|
||||||
|
grid->ass->SetScriptInfo("Video Position", wxString::Format("%d", frame_n));
|
||||||
|
grid->ass->SetScriptInfo("VFR File", MakeRelativePath(GetTimecodesName(), grid->ass->filename));
|
||||||
|
grid->ass->SetScriptInfo("Keyframes File", MakeRelativePath(GetKeyFramesName(), grid->ass->filename));
|
||||||
|
}
|
||||||
|
|
||||||
void VideoContext::JumpToFrame(int n) {
|
void VideoContext::JumpToFrame(int n) {
|
||||||
if (!IsLoaded()) return;
|
if (!IsLoaded()) return;
|
||||||
|
|
||||||
|
@ -466,7 +490,7 @@ void VideoContext::LoadKeyframes(wxString filename) {
|
||||||
if (kf.second != 0.) {
|
if (kf.second != 0.) {
|
||||||
ovrFPS = agi::vfr::Framerate(kf.second);
|
ovrFPS = agi::vfr::Framerate(kf.second);
|
||||||
ovrTimecodeFile.clear();
|
ovrTimecodeFile.clear();
|
||||||
SubtitlesChanged();
|
OnSubtitlesCommit();
|
||||||
TimecodesOpen(ovrFPS);
|
TimecodesOpen(ovrFPS);
|
||||||
}
|
}
|
||||||
config::mru->Add("Keyframes", STD_STR(filename));
|
config::mru->Add("Keyframes", STD_STR(filename));
|
||||||
|
@ -503,7 +527,7 @@ void VideoContext::LoadTimecodes(wxString filename) {
|
||||||
ovrFPS = agi::vfr::Framerate(STD_STR(filename));
|
ovrFPS = agi::vfr::Framerate(STD_STR(filename));
|
||||||
ovrTimecodeFile = filename;
|
ovrTimecodeFile = filename;
|
||||||
config::mru->Add("Timecodes", STD_STR(filename));
|
config::mru->Add("Timecodes", STD_STR(filename));
|
||||||
SubtitlesChanged();
|
OnSubtitlesCommit();
|
||||||
TimecodesOpen(ovrFPS);
|
TimecodesOpen(ovrFPS);
|
||||||
}
|
}
|
||||||
catch (const agi::acs::AcsError&) {
|
catch (const agi::acs::AcsError&) {
|
||||||
|
@ -526,7 +550,7 @@ void VideoContext::SaveTimecodes(wxString filename) {
|
||||||
void VideoContext::CloseTimecodes() {
|
void VideoContext::CloseTimecodes() {
|
||||||
ovrFPS = agi::vfr::Framerate();
|
ovrFPS = agi::vfr::Framerate();
|
||||||
ovrTimecodeFile.clear();
|
ovrTimecodeFile.clear();
|
||||||
SubtitlesChanged();
|
OnSubtitlesCommit();
|
||||||
TimecodesOpen(videoFPS);
|
TimecodesOpen(videoFPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,9 @@ private:
|
||||||
void OnVideoError(VideoProviderErrorEvent const& err);
|
void OnVideoError(VideoProviderErrorEvent const& err);
|
||||||
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
||||||
|
|
||||||
|
void OnSubtitlesCommit();
|
||||||
|
void OnSubtitlesSave();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// DOCME
|
/// DOCME
|
||||||
SubtitlesGrid *grid;
|
SubtitlesGrid *grid;
|
||||||
|
@ -233,9 +236,6 @@ public:
|
||||||
/// @param end Type of time
|
/// @param end Type of time
|
||||||
void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START);
|
void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START);
|
||||||
|
|
||||||
/// @brief Refresh the subtitle provider
|
|
||||||
void SubtitlesChanged();
|
|
||||||
|
|
||||||
/// @brief Get the height and width of the current script
|
/// @brief Get the height and width of the current script
|
||||||
/// @param[out] w Width
|
/// @param[out] w Width
|
||||||
/// @param[out] h Height
|
/// @param[out] h Height
|
||||||
|
|
Loading…
Reference in New Issue