Aegisub/aegisub/src/frame_main.cpp

784 lines
23 KiB
C++
Raw Normal View History

2006-01-16 22:02:54 +01:00
// Copyright (c) 2005, Rodrigo Braz Monteiro, Niels Martin Hansen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
/// @file frame_main.cpp
/// @brief Main window creation and control management
/// @ingroup main_ui
2006-01-16 22:02:54 +01:00
#include "config.h"
#include "frame_main.h"
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>
#include "include/aegisub/context.h"
#include "include/aegisub/menu.h"
#include "include/aegisub/toolbar.h"
#include "include/aegisub/hotkey.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "audio_box.h"
#include "auto4_base.h"
#include "compat.h"
#include "command/command.h"
#include "dialog_detached_video.h"
#include "dialog_manager.h"
#include "dialog_version_check.h"
#include "help_button.h"
#include "initial_line_state.h"
#include "libresrc/libresrc.h"
#include "main.h"
#include "options.h"
#include "search_replace_engine.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"
#include "video_context.h"
#include "video_display.h"
#include "video_provider_manager.h"
#include "video_slider.h"
2006-01-16 22:02:54 +01:00
#include <boost/algorithm/string/predicate.hpp>
#include <wx/dnd.h>
#include <wx/filename.h>
#include <wx/image.h>
#include <wx/msgdlg.h>
#include <wx/statline.h>
#include <wx/sysopt.h>
enum {
ID_APP_TIMER_AUTOSAVE = 12001,
ID_APP_TIMER_STATUSCLEAR = 12002
};
#ifdef WITH_STARTUPLOG
#define StartupLog(a) MessageBox(0, a, "Aegisub startup log", 0)
#else
#define StartupLog(a) LOG_I("frame_main/init") << a
#endif
static void autosave_timer_changed(wxTimer *timer);
wxDEFINE_EVENT(FILE_LIST_DROPPED, wxThreadEvent);
static void get_files_to_load(wxArrayString const& list, std::string &subs, std::string &audio, std::string &video) {
// Keep these lists sorted
// Video formats
const wxString videoList[] = {
"asf",
"avi",
"avs",
"d2v",
"m2ts",
"m4v",
"mkv",
"mov",
"mp4",
"mpeg",
"mpg",
"ogm",
"rm",
"rmvb",
"ts",
"webm"
"wmv",
"y4m",
"yuv"
};
// Subtitle formats
const wxString subsList[] = {
"ass",
"srt",
"ssa",
"sub",
"ttxt",
"txt"
};
// Audio formats
const wxString audioList[] = {
"aac",
"ac3",
"ape",
"dts",
"flac",
"m4a",
"mka",
"mp3",
"ogg",
"w64",
"wav",
"wma"
};
// Scan list
for (wxFileName file : list) {
if (file.IsRelative()) file.MakeAbsolute();
if (!file.FileExists()) continue;
wxString ext = file.GetExt().Lower();
if (subs.empty() && std::binary_search(subsList, subsList + countof(subsList), ext))
subs = from_wx(file.GetFullPath());
if (video.empty() && std::binary_search(videoList, videoList + countof(videoList), ext))
video = from_wx(file.GetFullPath());
if (audio.empty() && std::binary_search(audioList, audioList + countof(audioList), ext))
audio = from_wx(file.GetFullPath());
}
}
/// Handle files drag and dropped onto Aegisub
class AegisubFileDropTarget : public wxFileDropTarget {
FrameMain *parent;
public:
AegisubFileDropTarget(FrameMain *parent) : parent(parent) { }
bool OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames) {
std::string subs, audio, video;
get_files_to_load(filenames, subs, audio, video);
if (subs.empty() && audio.empty() && video.empty())
return false;
wxThreadEvent *evt = new wxThreadEvent(FILE_LIST_DROPPED);
evt->SetPayload(filenames);
parent->QueueEvent(evt);
return true;
}
};
2006-01-16 22:02:54 +01:00
FrameMain::FrameMain (wxArrayString args)
: wxFrame(0,-1,"",wxDefaultPosition,wxSize(920,700),wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN)
, context(new agi::Context)
, showVideo(true)
, showAudio(true)
, blockVideoLoad(false)
, blockAudioLoad(false)
2006-01-16 22:02:54 +01:00
{
StartupLog("Entering FrameMain constructor");
#ifdef __WXGTK__
// XXX HACK XXX
// We need to set LC_ALL to "" here for input methods to work reliably.
setlocale(LC_ALL, "");
// However LC_NUMERIC must be "C", otherwise some parsing fails.
setlocale(LC_NUMERIC, "C");
#endif
#ifdef __APPLE__
// When run from an app bundle, LC_CTYPE defaults to "C", which breaks on
// anything involving unicode and in some cases number formatting.
// The right thing to do here would be to query CoreFoundation for the user's
// locale and add .UTF-8 to that, but :effort:
LOG_D("locale") << setlocale(LC_ALL, 0);
setlocale(LC_CTYPE, "en_US.UTF-8");
LOG_D("locale") << setlocale(LC_ALL, 0);
#endif
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);
2006-01-16 22:02:54 +01:00
context->local_scripts = new Automation4::LocalScriptManager(context.get());
StartupLog("Initializing context controls");
context->audioController = new AudioController(context.get());
context->audioController->AddAudioOpenListener(&FrameMain::OnAudioOpen, this);
context->audioController->AddAudioCloseListener(&FrameMain::OnAudioClose, this);
// Initialized later due to that the selection controller is currently the subtitles grid
context->selectionController = 0;
context->videoController = VideoContext::Get(); // derp
context->videoController->AddVideoOpenListener(&FrameMain::OnVideoOpen, this);
StartupLog("Initializing context frames");
context->parent = this;
context->previousFocus = 0;
StartupLog("Install PNG handler");
wxImage::AddHandler(new wxPNGHandler);
#ifndef __APPLE__
wxSafeYield();
#endif
StartupLog("Apply saved Maximized state");
if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);
StartupLog("Initialize toolbar");
2006-01-16 22:02:54 +01:00
InitToolbar();
StartupLog("Initialize menu bar");
menu::GetMenuBar("main", this, context.get());
StartupLog("Create status bar");
CreateStatusBar(2);
2006-01-16 22:02:54 +01:00
StartupLog("Set icon");
#ifdef _WIN32
SetIcon(wxICON(wxicon));
#else
wxIcon icon;
icon.CopyFromBitmap(GETIMAGE(wxicon));
SetIcon(icon);
#endif
2006-01-16 22:02:54 +01:00
StartupLog("Create views and inner main window controls");
context->dialog = new DialogManager;
2006-01-16 22:02:54 +01:00
InitContents();
OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this, agi::signal::_1);
2006-01-16 22:02:54 +01:00
StartupLog("Complete context initialization");
context->videoController->SetContext(context.get());
StartupLog("Set up Auto Save");
AutoSave.SetOwner(this, ID_APP_TIMER_AUTOSAVE);
autosave_timer_changed(&AutoSave);
OPT_SUB("App/Auto/Save", autosave_timer_changed, &AutoSave);
OPT_SUB("App/Auto/Save Every Seconds", autosave_timer_changed, &AutoSave);
2006-01-16 22:02:54 +01:00
StartupLog("Set up drag/drop target");
2006-01-16 22:02:54 +01:00
SetDropTarget(new AegisubFileDropTarget(this));
StartupLog("Load default file");
context->ass->LoadDefault();
StartupLog("Display main window");
AddFullScreenButton(this);
Show();
SetDisplayMode(1, 1);
StartupLog("Load files specified on command line");
LoadList(args);
// Version checker
StartupLog("Possibly perform automatic updates check");
if (OPT_GET("App/First Start")->GetBool()) {
OPT_SET("App/First Start")->SetBool(false);
#ifdef WITH_UPDATE_CHECKER
int result = wxMessageBox(_("Do you want Aegisub to check for updates whenever it starts? You can still do it manually via the Help menu."),_("Check for updates?"), wxYES_NO | wxCENTER);
OPT_SET("App/Auto/Check For Updates")->SetBool(result == wxYES);
config::opt->Flush();
#endif
}
#ifdef WITH_UPDATE_CHECKER
PerformVersionCheck(false);
#endif
Bind(FILE_LIST_DROPPED, &FrameMain::OnFilesDropped, this);
StartupLog("Leaving FrameMain constructor");
2006-01-16 22:02:54 +01:00
}
/// @brief Delete everything but @a keep and its parents
/// @param window Root window to delete the children of
/// @param keep Window to keep alive
/// @return Was @a keep found?
static bool delete_children(wxWindow *window, wxWindow *keep) {
bool found = false;
while (window->GetChildren().size() > (size_t)found) {
auto it = window->GetChildren().begin();
if (*it == keep)
found = true;
if (found) {
if (++it != window->GetChildren().end())
(*it)->wxWindowBase::Destroy();
}
else if (!delete_children(*it, keep))
(*it)->wxWindowBase::Destroy();
else
found = true;
}
return found;
}
FrameMain::~FrameMain () {
wxGetApp().frame = 0;
context->videoController->SetVideo("");
context->audioController->CloseAudio();
// SubsGrid needs to be deleted last due to being the selection
// controller, but everything else needs to be deleted before the context
// is cleaned up
delete_children(this, SubsGrid);
delete context->ass;
HelpButton::ClearPages();
delete context->audioController;
delete context->local_scripts;
2006-01-16 22:02:54 +01:00
}
void FrameMain::InitToolbar() {
wxSystemOptions::SetOption("msw.remap", 0);
OPT_SUB("App/Show Toolbar", &FrameMain::EnableToolBar, this);
EnableToolBar(*OPT_GET("App/Show Toolbar"));
}
void FrameMain::EnableToolBar(agi::OptionValue const& opt) {
if (opt.GetBool()) {
if (!GetToolBar()) {
toolbar::AttachToolbar(this, "main", context.get(), "Default");
GetToolBar()->Realize();
}
}
else if (wxToolBar *old_tb = GetToolBar()) {
SetToolBar(0);
delete old_tb;
Layout();
}
2006-01-16 22:02:54 +01:00
}
void FrameMain::InitContents() {
StartupLog("Create background panel");
wxPanel *Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
2006-01-16 22:02:54 +01:00
StartupLog("Create subtitles grid");
context->subsGrid = SubsGrid = new SubtitlesGrid(Panel, context.get());
context->selectionController = context->subsGrid;
context->search = new SearchReplaceEngine(context.get());
context->initialLineState = new InitialLineState(context.get());
2006-01-16 22:02:54 +01:00
StartupLog("Create video box");
videoBox = new VideoBox(Panel, false, context.get());
StartupLog("Create audio box");
context->audioBox = audioBox = new AudioBox(Panel, context.get());
2006-01-16 22:02:54 +01:00
StartupLog("Create subtitle editing box");
SubsEditBox *EditBox = new SubsEditBox(Panel, context.get());
2006-01-16 22:02:54 +01:00
StartupLog("Arrange main sizers");
ToolsSizer = new wxBoxSizer(wxVERTICAL);
ToolsSizer->Add(audioBox, 0, wxEXPAND);
ToolsSizer->Add(EditBox, 1, wxEXPAND);
TopSizer = new wxBoxSizer(wxHORIZONTAL);
TopSizer->Add(videoBox, 0, wxEXPAND, 0);
TopSizer->Add(ToolsSizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
MainSizer = new wxBoxSizer(wxVERTICAL);
MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
2006-01-16 22:02:54 +01:00
MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0);
MainSizer->Add(SubsGrid,1,wxEXPAND | wxALL,0);
2006-01-16 22:02:54 +01:00
Panel->SetSizer(MainSizer);
StartupLog("Perform layout");
2006-01-16 22:02:54 +01:00
Layout();
StartupLog("Leaving InitContents");
2006-01-16 22:02:54 +01:00
}
void FrameMain::LoadSubtitles(agi::fs::path const& filename, std::string const& charset) {
if (TryToCloseSubs() == wxCANCEL) return;
2006-01-16 22:02:54 +01:00
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;
}
2006-01-16 22:02:54 +01:00
}
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
2006-01-16 22:02:54 +01:00
}
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()));
}
}
2006-01-16 22:02:54 +01:00
}
catch (agi::fs::FileNotFound const&) {
wxMessageBox(filename.wstring() + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
config::mru->Remove("Subtitle", filename);
return;
}
2012-10-12 05:18:47 +02:00
catch (agi::Exception const& err) {
wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this);
2012-10-12 05:18:47 +02:00
}
2006-01-16 22:02:54 +01:00
catch (...) {
wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
2006-01-16 22:02:54 +01:00
return;
}
}
int FrameMain::TryToCloseSubs(bool enableCancel) {
if (context->ass->IsModified()) {
2006-01-16 22:02:54 +01:00
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);
2006-01-16 22:02:54 +01:00
if (result == wxYES) {
cmd::call("subtitle/save", context.get());
2006-01-16 22:02:54 +01:00
// If it fails saving, return cancel anyway
return context->ass->IsModified() ? wxCANCEL : wxYES;
2006-01-16 22:02:54 +01:00
}
return result;
}
else {
return wxYES;
}
2006-01-16 22:02:54 +01:00
}
void FrameMain::SetDisplayMode(int video, int audio) {
if (!IsShownOnScreen()) return;
bool sv = false, sa = false;
if (video == -1) sv = showVideo;
else if (video) sv = context->videoController->IsLoaded() && !context->dialog->Get<DialogDetachedVideo>();
if (audio == -1) sa = showAudio;
else if (audio) sa = context->audioController->IsAudioOpen();
2006-01-16 22:02:54 +01:00
// See if anything changed
if (sv == showVideo && sa == showAudio) return;
showVideo = sv;
showAudio = sa;
bool didFreeze = !IsFrozen();
if (didFreeze) Freeze();
context->videoController->Stop();
TopSizer->Show(videoBox, showVideo, true);
ToolsSizer->Show(audioBox, showAudio, true);
2006-01-16 22:02:54 +01:00
MainSizer->CalcMin();
MainSizer->RecalcSizes();
2006-01-16 22:02:54 +01:00
MainSizer->Layout();
Layout();
if (didFreeze) Thaw();
2006-01-16 22:02:54 +01:00
}
void FrameMain::UpdateTitle() {
wxString newTitle;
if (context->ass->IsModified()) newTitle << "* ";
newTitle << GetScriptFileName();
#ifndef __WXMAC__
newTitle << " - Aegisub " << GetAegisubLongVersionString();
#endif
#if defined(__WXMAC__) && !defined(__LP64__)
// On Mac, set the mark in the close button
OSXSetModified(context->ass->IsModified());
#endif
2006-01-16 22:02:54 +01:00
if (GetTitle() != newTitle) SetTitle(newTitle);
2006-01-16 22:02:54 +01:00
}
void FrameMain::OnVideoOpen() {
if (!context->videoController->IsLoaded()) {
SetDisplayMode(0, -1);
return;
2006-01-16 22:02:54 +01:00
}
Freeze();
int vidx = context->videoController->GetWidth(),
vidy = context->videoController->GetHeight();
// Set zoom level based on video resolution and window size
double zoom = context->videoDisplay->GetZoom();
wxSize windowSize = GetSize();
if (vidx*3*zoom > windowSize.GetX()*4 || vidy*4*zoom > windowSize.GetY()*6)
context->videoDisplay->SetZoom(zoom * .25);
else if (vidx*3*zoom > windowSize.GetX()*2 || vidy*4*zoom > windowSize.GetY()*3)
context->videoDisplay->SetZoom(zoom * .5);
SetDisplayMode(1,-1);
if (OPT_GET("Video/Detached/Enabled")->GetBool() && !context->dialog->Get<DialogDetachedVideo>())
cmd::call("video/detach", context.get());
Thaw();
if (!blockAudioLoad && OPT_GET("Video/Open Audio")->GetBool() && context->audioController->GetAudioURL() != context->videoController->GetVideoName()) {
try {
context->audioController->OpenAudio(context->videoController->GetVideoName());
}
catch (agi::UserCancelException const&) { }
// Opening a video with no audio data isn't an error, so just log
// and move on
catch (agi::fs::FileNotFound const&) {
LOG_D("video/open/audio") << "File " << context->videoController->GetVideoName() << " found by video provider but not audio provider";
}
catch (agi::AudioDataNotFoundError const& e) {
LOG_D("video/open/audio") << "File " << context->videoController->GetVideoName() << " has no audio data: " << e.GetChainedMessage();
}
catch (agi::AudioOpenError const& err) {
wxMessageBox(to_wx(err.GetMessage()), "Error loading audio", wxOK | wxICON_ERROR | wxCENTER);
}
}
2006-01-16 22:02:54 +01:00
}
void FrameMain::OnVideoDetach(agi::OptionValue const& opt) {
if (opt.GetBool())
SetDisplayMode(0, -1);
else if (context->videoController->IsLoaded())
SetDisplayMode(1, -1);
}
2006-01-16 22:02:54 +01:00
void FrameMain::StatusTimeout(wxString text,int ms) {
SetStatusText(text,1);
StatusClear.SetOwner(this, ID_APP_TIMER_STATUSCLEAR);
2006-01-16 22:02:54 +01:00
StatusClear.Start(ms,true);
}
void FrameMain::OnFilesDropped(wxThreadEvent &evt) {
LoadList(evt.GetPayload<wxArrayString>());
}
2006-01-16 22:02:54 +01:00
bool FrameMain::LoadList(wxArrayString list) {
std::string audio, video, subs;
get_files_to_load(list, subs, audio, video);
2006-01-16 22:02:54 +01:00
blockVideoLoad = !video.empty();
blockAudioLoad = !audio.empty();
2006-01-16 22:02:54 +01:00
// Load files
if (subs.size())
LoadSubtitles(subs);
2006-01-16 22:02:54 +01:00
if (blockVideoLoad) {
blockVideoLoad = false;
context->videoController->SetVideo(video);
2006-01-16 22:02:54 +01:00
}
if (blockAudioLoad) {
blockAudioLoad = false;
try {
context->audioController->OpenAudio(audio);
} catch (agi::UserCancelException const&) { }
}
2006-01-16 22:02:54 +01:00
bool loaded_any = subs.size() || audio.size() || video.size();
if (loaded_any)
Refresh(false);
return loaded_any;
2006-01-16 22:02:54 +01:00
}
2007-01-26 01:47:42 +01:00
static void autosave_timer_changed(wxTimer *timer) {
int freq = OPT_GET("App/Auto/Save Every Seconds")->GetInt();
if (freq > 0 && OPT_GET("App/Auto/Save")->GetBool())
timer->Start(freq * 1000);
else
timer->Stop();
}
BEGIN_EVENT_TABLE(FrameMain, wxFrame)
EVT_TIMER(ID_APP_TIMER_AUTOSAVE, FrameMain::OnAutoSave)
EVT_TIMER(ID_APP_TIMER_STATUSCLEAR, FrameMain::OnStatusClear)
EVT_CLOSE(FrameMain::OnCloseWindow)
EVT_CHAR_HOOK(FrameMain::OnKeyDown)
EVT_MOUSEWHEEL(FrameMain::OnMouseWheel)
END_EVENT_TABLE()
void FrameMain::OnCloseWindow (wxCloseEvent &event) {
// Stop audio and video
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) {
event.Veto();
return;
}
delete context->dialog;
context->dialog = 0;
// Store maximization state
OPT_SET("App/Maximized")->SetBool(IsMaximized());
Destroy();
}
void FrameMain::OnAutoSave(wxTimerEvent &) try {
auto fn = context->ass->AutoSave();
if (!fn.empty())
StatusTimeout(wxString::Format(_("File backup saved as \"%s\"."), fn.wstring()));
}
catch (const agi::Exception& err) {
StatusTimeout(to_wx("Exception when attempting to autosave file: " + err.GetMessage()));
}
catch (...) {
StatusTimeout("Unhandled exception when attempting to autosave file.");
}
void FrameMain::OnStatusClear(wxTimerEvent &) {
SetStatusText("",1);
}
void FrameMain::OnAudioOpen(AudioProvider *) {
SetDisplayMode(-1, 1);
}
void FrameMain::OnAudioClose() {
SetDisplayMode(-1, 0);
}
void FrameMain::OnSubtitlesOpen() {
UpdateTitle();
/// @todo figure out how to move this to the relevant controllers without
/// prompting for each file loaded/unloaded
// Load stuff from the new script
auto video = config::path->MakeAbsolute(context->ass->GetScriptInfo("Video File"), "?script");
auto vfr = config::path->MakeAbsolute(context->ass->GetScriptInfo("VFR File"), "?script");
auto keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script");
auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script");
bool videoChanged = !blockVideoLoad && video != context->videoController->GetVideoName();
bool timecodesChanged = vfr != context->videoController->GetTimecodesName();
bool keyframesChanged = keyframes != context->videoController->GetKeyFramesName();
bool audioChanged = !blockAudioLoad && audio != context->audioController->GetAudioURL();
// Check if there is anything to change
int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt();
if (autoLoadMode == 0 || (!videoChanged && !timecodesChanged && !keyframesChanged && !audioChanged)) {
SetDisplayMode(1, 1);
return;
}
if (autoLoadMode == 2) {
if (wxMessageBox(_("Do you want to load/unload the associated files?"), _("(Un)Load files?"), wxYES_NO | wxCENTRE, this) != wxYES) {
SetDisplayMode(1, 1);
return;
}
}
if (audioChanged)
blockAudioLoad = true;
// Video
if (videoChanged) {
context->videoController->SetVideo(video);
if (context->videoController->IsLoaded()) {
context->videoController->JumpToFrame(context->ass->GetScriptInfoAsInt("Video Position"));
std::string arString = context->ass->GetScriptInfo("Video Aspect Ratio");
if (boost::starts_with(arString, "c")) {
double ar = 0.;
agi::util::try_parse(arString.substr(1), &ar);
context->videoController->SetAspectRatio(ar);
}
else {
int ar = 0;
if (agi::util::try_parse(arString.substr(1), &ar) && ar >= 0 && ar < 4)
context->videoController->SetAspectRatio((AspectRatio)ar);
}
double videoZoom = 0.;
if (agi::util::try_parse(context->ass->GetScriptInfo("Video Zoom Percent"), &videoZoom))
context->videoDisplay->SetZoom(videoZoom);
}
}
context->videoController->LoadTimecodes(vfr);
context->videoController->LoadKeyframes(keyframes);
// Audio
if (audioChanged) {
blockAudioLoad = false;
try {
if (audio.empty())
context->audioController->CloseAudio();
else
context->audioController->OpenAudio(audio);
}
catch (agi::UserCancelException const&) { }
catch (agi::fs::FileSystemError const& err) {
wxMessageBox(to_wx(err.GetMessage()), "Error opening audio", wxOK | wxICON_ERROR | wxCENTER, this);
}
}
SetDisplayMode(1, 1);
}
void FrameMain::OnKeyDown(wxKeyEvent &event) {
hotkey::check("Main Frame", context.get(), 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();
}