// 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/ // // $Id$ /// @file frame_main.cpp /// @brief Main window creation and control management /// @ingroup main_ui #include "config.h" #ifndef AGI_PRE #include #include #include #include #include #include #endif #include "ass_file.h" #include "audio_box.h" #include "audio_display.h" #ifdef WITH_AUTOMATION #include "auto4_base.h" #endif #ifdef WITH_AVISYNTH #include "avisynth_wrap.h" #endif #include "compat.h" #include "dialog_detached_video.h" #include "dialog_search_replace.h" #include "dialog_styling_assistant.h" #include "dialog_version_check.h" #include "drop.h" #include "frame_main.h" #include "help_button.h" #include "hotkeys.h" #include "keyframe.h" #include "libresrc/libresrc.h" #include "main.h" #include "standard_paths.h" #include "subs_edit_box.h" #include "subs_edit_ctrl.h" #include "subs_grid.h" #include "text_file_reader.h" #include "text_file_writer.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" #ifdef WITH_STARTUPLOG /// DOCME #define StartupLog(a) MessageBox(0, a, _T("Aegisub startup log"), 0) #else /// DOCME #define StartupLog(a) #endif static void autosave_timer_changed(wxTimer &timer, const agi::OptionValue &opt); FrameMain::FrameMain (wxArrayString args) : wxFrame ((wxFrame*)NULL,-1,_T(""),wxDefaultPosition,wxSize(920,700),wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN) { StartupLog(_T("Entering FrameMain constructor")); #ifdef __WXGTK__ /* XXX HACK XXX * Gtk just got initialized. And if we're using the SCIM IME, * it just did a setlocale(LC_ALL, ""). so, BOOM. */ setlocale(LC_ALL, ""); setlocale(LC_CTYPE, "C"); setlocale(LC_NUMERIC, "C"); /* XXX HACK XXX */ #endif // Set application's frame AegisubApp::Get()->frame = this; // Initialize flags HasSelection = false; menuCreated = false; blockAudioLoad = false; blockAudioLoad = false; StartupLog(_T("Install PNG handler")); // Create PNG handler wxPNGHandler *png = new wxPNGHandler; wxImage::AddHandler(png); wxSafeYield(); // Storage for subs-file-local scripts #ifdef WITH_AUTOMATION StartupLog(_T("Create local Automation script manager")); local_scripts = new Automation4::ScriptManager(); #endif // Create menu and tool bars StartupLog(_T("Apply saved Maximized state")); if (OPT_GET("App/Maximized")->GetBool()) Maximize(true); StartupLog(_T("Initialize toolbar")); InitToolbar(); StartupLog(_T("Initialize menu bar")); InitMenu(); // Create status bar StartupLog(_T("Create status bar")); CreateStatusBar(2); // Set icon StartupLog(_T("Set icon")); #ifdef _WINDOWS SetIcon(wxICON(wxicon)); #else wxIcon icon; icon.CopyFromBitmap(GETIMAGE(wxicon_misc)); SetIcon(icon); #endif // Contents showVideo = true; showAudio = true; detachedVideo = NULL; stylingAssistant = NULL; StartupLog(_T("Initialize inner main window controls")); InitContents(); // Set autosave timer StartupLog(_T("Set up Auto Save")); AutoSave.SetOwner(this,AutoSave_Timer); int time = OPT_GET("App/Auto/Save Every Seconds")->GetInt(); if (time > 0) { AutoSave.Start(time*1000); } OPT_GET("App/Auto/Save Every Seconds")->Subscribe(this, std::tr1::bind(autosave_timer_changed, std::tr1::ref(AutoSave), std::tr1::placeholders::_1)); // Set accelerator keys StartupLog(_T("Install hotkeys")); PreviousFocus = NULL; SetAccelerators(); // Set drop target StartupLog(_T("Set up drag/drop target")); SetDropTarget(new AegisubFileDropTarget(this)); // Parse arguments StartupLog(_T("Initialize empty file")); LoadSubtitles(_T("")); StartupLog(_T("Load files specified on command line")); LoadList(args); // Version checker StartupLog(_T("Possibly perform automatic updates check")); if (OPT_GET("App/First Start")->GetBool()) { OPT_SET("App/First Start")->SetBool(false); 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); OPT_SET("App/Auto/Check For Updates")->SetBool(result == wxYES); } PerformVersionCheck(false); StartupLog(_T("Display main window")); Show(); Freeze(); SetDisplayMode(1, 1); Thaw(); //ShowFullScreen(true,wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); StartupLog(_T("Leaving FrameMain constructor")); } /// @brief FrameMain destructor FrameMain::~FrameMain () { DeInitContents(); #ifdef WITH_AUTOMATION delete local_scripts; #endif } /// @brief Initialize toolbar void FrameMain::InitToolbar () { // Create toolbar wxSystemOptions::SetOption(_T("msw.remap"), 0); Toolbar = CreateToolBar(wxTB_FLAT | wxTB_HORIZONTAL,-1,_T("Toolbar")); // Subtitle control buttons Toolbar->AddTool(Menu_File_New_Subtitles,_("New"),GETIMAGE(new_toolbutton_24),_("New subtitles")); Toolbar->AddTool(Menu_File_Open_Subtitles,_("Open"),GETIMAGE(open_toolbutton_24),_("Open subtitles")); Toolbar->AddTool(Menu_File_Save_Subtitles,_("Save"),GETIMAGE(save_toolbutton_24),_("Save subtitles")); Toolbar->AddSeparator(); // Video zoom controls Toolbar->AddTool(Menu_Video_JumpTo,_("Jump To..."),GETIMAGE(jumpto_button_24),wxNullBitmap,wxITEM_NORMAL,_("Jump video to time/frame")); Toolbar->AddTool(Menu_Video_Zoom_In,_("Zoom in"),GETIMAGE(zoom_in_button_24),wxNullBitmap,wxITEM_NORMAL,_("Zoom video in")); Toolbar->AddTool(Menu_Video_Zoom_Out,_("Zoom out"),GETIMAGE(zoom_out_button_24),wxNullBitmap,wxITEM_NORMAL,_("Zoom video out")); wxArrayString choices; for (int i=1;i<=24;i++) { wxString toAdd = wxString::Format(_T("%i"),int(i*12.5)); if (i%2) toAdd += _T(".5"); toAdd += _T("%"); choices.Add(toAdd); } ZoomBox = new wxComboBox(Toolbar,Toolbar_Zoom_Dropdown,_T("75%"),wxDefaultPosition,wxDefaultSize,choices,wxCB_DROPDOWN); Toolbar->AddControl(ZoomBox); Toolbar->AddSeparator(); // More video buttons Toolbar->AddTool(Menu_Subs_Snap_Video_To_Start,_("Jump video to start"),GETIMAGE(video_to_substart_24),_("Jumps the video to the start frame of current subtitle")); Toolbar->AddTool(Menu_Subs_Snap_Video_To_End,_("Jump video to end"),GETIMAGE(video_to_subend_24),_("Jumps the video to the end frame of current subtitle")); Toolbar->AddTool(Menu_Subs_Snap_Start_To_Video,_("Snap start to video"),GETIMAGE(substart_to_video_24),_("Set start of selected subtitles to current video frame")); Toolbar->AddTool(Menu_Subs_Snap_End_To_Video,_("Snap end to video"),GETIMAGE(subend_to_video_24),_("Set end of selected subtitles to current video frame")); Toolbar->AddTool(Menu_Video_Select_Visible,_("Select visible"),GETIMAGE(select_visible_button_24),_("Selects all lines that are currently visible on video frame")); Toolbar->AddTool(Menu_Video_Snap_To_Scene,_("Snap subtitles to scene"),GETIMAGE(snap_subs_to_scene_24),_("Snap selected subtitles so they match current scene start/end")); Toolbar->AddTool(Menu_Video_Shift_To_Frame,_("Shift subtitles to frame"),GETIMAGE(shift_to_frame_24),_("Shift selected subtitles so first selected starts at this frame")); Toolbar->AddSeparator(); // Property stuff Toolbar->AddTool(Menu_Tools_Styles_Manager,_("Styles Manager"),GETIMAGE(style_toolbutton_24),_("Open Styles Manager")); Toolbar->AddTool(Menu_Tools_Properties,_("Properties"),GETIMAGE(properties_toolbutton_24),_("Open Properties")); Toolbar->AddTool(Menu_Tools_Attachments,_("Attachments"),GETIMAGE(attach_button_24),_("Open Attachment List")); Toolbar->AddTool(Menu_Tools_Fonts_Collector,_("Fonts Collector"),GETIMAGE(font_collector_button_24),_("Open Fonts Collector")); Toolbar->AddSeparator(); // Automation #ifdef WITH_AUTOMATION Toolbar->AddTool(Menu_Tools_Automation,_("Automation"),GETIMAGE(automation_toolbutton_24),_("Open Automation manager")); Toolbar->AddSeparator(); #endif // Tools if (HasASSDraw()) { Toolbar->AddTool(Menu_Tools_ASSDraw,_T("ASSDraw3"),GETIMAGE(assdraw_24),_("Launches ai-chan's \"ASSDraw3\" tool for vector drawing.")); Toolbar->AddSeparator(); } Toolbar->AddTool(Menu_Edit_Shift,_("Shift Times"),GETIMAGE(shift_times_toolbutton_24),_("Open Shift Times Dialogue")); Toolbar->AddTool(Menu_Tools_Styling,_("Styling Assistant"),GETIMAGE(styling_toolbutton_24),_("Open Styling Assistant")); Toolbar->AddTool(Menu_Tools_Translation,_("Translation Assistant"),GETIMAGE(translation_toolbutton_24),_("Open Translation Assistant")); Toolbar->AddTool(Menu_Tools_Resample,_("Resample"),GETIMAGE(resample_toolbutton_24),_("Resample Script Resolution")); Toolbar->AddTool(Menu_Tools_Timing_Processor,_("Timing Post-Processor"),GETIMAGE(timing_processor_toolbutton_24),_("Open Timing Post-processor dialog")); Toolbar->AddTool(Menu_Tools_Kanji_Timer,_("Kanji Timer"),GETIMAGE(kara_timing_copier_24),_("Open Kanji Timer dialog")); Toolbar->AddTool(Menu_Tools_SpellCheck,_("Spell Checker"),GETIMAGE(spellcheck_toolbutton_24),_("Open Spell checker")); Toolbar->AddSeparator(); // Options Toolbar->AddTool(Menu_Tools_Options,_("Options"),GETIMAGE(options_button_24),_("Configure Aegisub")); Toolbar->AddTool(Grid_Toggle_Tags,_("Cycle Tag Hidding Mode"),GETIMAGE(toggle_tag_hiding_24),_("Cycle through tag-hiding modes")); // Update Toolbar->Realize(); } /// @brief DOCME /// @param item_text /// @param hotkey_name /// @return wxString MakeHotkeyText(const wxString &item_text, const wxString &hotkey_name) { return item_text + wxString(_T("\t")) + Hotkeys.GetText(hotkey_name); } /// @brief Initialize menu bar void FrameMain::InitMenu() { // Deinit menu if needed if (menuCreated) { SetMenuBar(NULL); MenuBar->Destroy(); } #ifdef __WXMAC__ // Make sure special menu items are placed correctly on Mac wxApp::s_macAboutMenuItemId = Menu_Help_About; wxApp::s_macExitMenuItemId = Menu_File_Exit; wxApp::s_macPreferencesMenuItemId = Menu_Tools_Options; wxApp::s_macHelpMenuTitleName = _("&Help"); #endif // Generate menubar MenuBar = new wxMenuBar(); // Create recent subs submenus RecentSubs = new wxMenu(); RecentVids = new wxMenu(); RecentAuds = new wxMenu(); RecentTimecodes = new wxMenu(); RecentKeyframes = new wxMenu(); // Create file menu fileMenu = new wxMenu(); AppendBitmapMenuItem(fileMenu,Menu_File_New_Subtitles, MakeHotkeyText(_("&New Subtitles"), _T("New Subtitles")), _("New subtitles"),GETIMAGE(new_toolbutton_16)); AppendBitmapMenuItem(fileMenu,Menu_File_Open_Subtitles, MakeHotkeyText(_("&Open Subtitles..."), _T("Open Subtitles")), _("Opens a subtitles file"),GETIMAGE(open_toolbutton_16)); AppendBitmapMenuItem(fileMenu,Menu_File_Open_Subtitles_Charset, _("&Open Subtitles with Charset..."), _("Opens a subtitles file with a specific charset"),GETIMAGE(open_with_toolbutton_16)); fileMenu->Append(Menu_File_Open_Subtitles_From_Video, _("Open Subtitles from &Video"), _("Opens the subtitles from the current video file")); AppendBitmapMenuItem(fileMenu,Menu_File_Save_Subtitles, MakeHotkeyText(_("&Save Subtitles"), _T("Save Subtitles")), _("Saves subtitles"),GETIMAGE(save_toolbutton_16)); AppendBitmapMenuItem(fileMenu,Menu_File_Save_Subtitles_As, _("Save Subtitles as..."), _("Saves subtitles with another name"), GETIMAGE(save_as_toolbutton_16)); AppendBitmapMenuItem(fileMenu,Menu_File_Export_Subtitles, _("Export Subtitles..."), _("Saves a copy of subtitles with processing applied to it."), GETIMAGE(export_menu_16)); wxMenuItem *RecentParent = new wxMenuItem(fileMenu, Menu_File_Recent_Subs_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentSubs); #ifndef __APPLE__ RecentParent->SetBitmap(GETIMAGE(blank_button_16)); #endif fileMenu->Append(RecentParent); fileMenu->AppendSeparator(); AppendBitmapMenuItem (fileMenu,Menu_Tools_Properties, _("&Properties..."), _("Open script properties window"),GETIMAGE(properties_toolbutton_16)); AppendBitmapMenuItem (fileMenu,Menu_Tools_Attachments, _("&Attachments..."), _("Open the attachment list"), GETIMAGE(attach_button_16)); AppendBitmapMenuItem (fileMenu,Menu_Tools_Fonts_Collector, _("&Fonts Collector..."),_("Open fonts collector"), GETIMAGE(font_collector_button_16)); fileMenu->AppendSeparator(); #ifndef __APPLE__ // Doesn't work on Mac, only one instance is ever allowed there from OS side AppendBitmapMenuItem(fileMenu,Menu_File_New_Window, _("New Window"), _("Open a new application window"),GETIMAGE(new_window_menu_16)); #endif AppendBitmapMenuItem(fileMenu,Menu_File_Exit, MakeHotkeyText(_("E&xit"), _T("Exit")), _("Exit the application"),GETIMAGE(exit_button_16)); MenuBar->Append(fileMenu, _("&File")); // Create Edit menu // NOTE: Undo and Redo are actually controlled in frame_main_events, OnMenuOpen(). They will always be the first two items. editMenu = new wxMenu(); AppendBitmapMenuItem(editMenu,Menu_Edit_Undo, MakeHotkeyText(_("&Undo"), _T("Undo")), _("Undoes last action"),GETIMAGE(undo_button_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Redo, MakeHotkeyText(_("&Redo"), _T("Redo")), _("Redoes last action"),GETIMAGE(redo_button_16)); editMenu->AppendSeparator(); AppendBitmapMenuItem(editMenu,Menu_Edit_Cut, MakeHotkeyText(_("Cut Lines"), _T("Cut")), _("Cut subtitles"), GETIMAGE(cut_button_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Copy, MakeHotkeyText(_("Copy Lines"), _T("Copy")), _("Copy subtitles"), GETIMAGE(copy_button_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Paste, MakeHotkeyText(_("Paste Lines"), _T("Paste")), _("Paste subtitles"), GETIMAGE(paste_button_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Paste_Over, MakeHotkeyText(_("Paste Lines Over..."), _T("Paste Over")) , _("Paste subtitles over others"),GETIMAGE(paste_over_button_16)); editMenu->AppendSeparator(); AppendBitmapMenuItem(editMenu,Menu_Edit_Find, MakeHotkeyText(_("&Find..."), _T("Find")), _("Find words in subtitles"),GETIMAGE(find_button_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Find_Next, MakeHotkeyText(_("Find Next"), _T("Find Next")), _("Find next match of last word"),GETIMAGE(find_next_menu_16)); AppendBitmapMenuItem(editMenu,Menu_Edit_Replace, MakeHotkeyText(_("Search and &Replace..."), _T("Replace")) , _("Find and replace words in subtitles"),GETIMAGE(find_replace_menu_16)); MenuBar->Append(editMenu, _("&Edit")); // Create subtitles menu subtitlesMenu = new wxMenu(); wxMenu *InsertMenu = new wxMenu; wxMenuItem *InsertParent = new wxMenuItem(subtitlesMenu,Menu_Subtitles_Insert,_("&Insert Lines"),_T(""),wxITEM_NORMAL,InsertMenu); #ifndef __APPLE__ InsertParent->SetBitmap(GETIMAGE(blank_button_16)); #endif AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_Styles_Manager, _("&Styles Manager..."), _("Open styles manager"), GETIMAGE(style_toolbutton_16)); AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_Styling, _("St&yling Assistant..."), _("Open styling assistant"), GETIMAGE(styling_toolbutton_16)); AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_Translation, _("&Translation Assistant..."),_("Open translation assistant"), GETIMAGE(translation_toolbutton_16)); AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_Resample,_("Resample Resolution..."), _("Changes resolution and modifies subtitles to conform to change"), GETIMAGE(resample_toolbutton_16)); AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_SpellCheck, _("Spe&ll Checker..."),_("Open spell checker"), GETIMAGE(spellcheck_toolbutton_16)); if (HasASSDraw()) { subtitlesMenu->AppendSeparator(); AppendBitmapMenuItem (subtitlesMenu,Menu_Tools_ASSDraw,_T("ASSDraw3..."),_("Launches ai-chan's \"ASSDraw3\" tool for vector drawing."), GETIMAGE(assdraw_16)); } subtitlesMenu->AppendSeparator(); AppendBitmapMenuItem(InsertMenu,MENU_INSERT_BEFORE,_("&Before Current"),_("Inserts a line before current"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(InsertMenu,MENU_INSERT_AFTER,_("&After Current"),_("Inserts a line after current"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(InsertMenu,MENU_INSERT_BEFORE_VIDEO,_("Before Current, at Video Time"),_("Inserts a line before current, starting at video time"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(InsertMenu,MENU_INSERT_AFTER_VIDEO,_("After Current, at Video Time"),_("Inserts a line after current, starting at video time"),GETIMAGE(blank_button_16)); subtitlesMenu->Append(InsertParent); AppendBitmapMenuItem(subtitlesMenu,MENU_DUPLICATE,MakeHotkeyText(_("&Duplicate Lines"), _T("Grid duplicate rows")),_("Duplicate the selected lines"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(subtitlesMenu,MENU_DUPLICATE_NEXT_FRAME,MakeHotkeyText(_("&Duplicate and Shift by 1 Frame"), _T("Grid duplicate and shift one frame")),_("Duplicate lines and shift by one frame"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(subtitlesMenu,MENU_DELETE,MakeHotkeyText(_("Delete Lines"), _T("Grid delete rows")),_("Delete currently selected lines"),GETIMAGE(delete_button_16)); subtitlesMenu->AppendSeparator(); wxMenu *JoinMenu = new wxMenu; wxMenuItem *JoinParent = new wxMenuItem(subtitlesMenu,Menu_Subtitles_Join,_("Join Lines"),_T(""),wxITEM_NORMAL,JoinMenu); #ifndef __APPLE__ JoinParent->SetBitmap(GETIMAGE(blank_button_16)); #endif AppendBitmapMenuItem(JoinMenu,MENU_JOIN_CONCAT,_("&Concatenate"),_("Joins selected lines in a single one, concatenating text together"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(JoinMenu,MENU_JOIN_REPLACE,_("Keep &First"),_("Joins selected lines in a single one, keeping text of first and discarding remaining"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(JoinMenu,MENU_JOIN_AS_KARAOKE,_("As &Karaoke"),_("Joins selected lines in a single one, as karaoke"),GETIMAGE(blank_button_16)); subtitlesMenu->Append(JoinParent); AppendBitmapMenuItem(subtitlesMenu,MENU_RECOMBINE,_("Recombine Lines"),_("Recombine subtitles when they have been split and merged"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(subtitlesMenu,MENU_SPLIT_BY_KARAOKE,_("Split Lines (by karaoke)"),_("Uses karaoke timing to split line into multiple smaller lines"),GETIMAGE(blank_button_16)); subtitlesMenu->AppendSeparator(); wxMenu *SortMenu = new wxMenu; wxMenuItem *SortParent = new wxMenuItem(subtitlesMenu,Menu_Subtitles_Sort_Start,_("Sort Lines"),_T(""),wxITEM_NORMAL,SortMenu); #ifndef __APPLE__ SortParent->SetBitmap(GETIMAGE(sort_times_button_16)); #endif AppendBitmapMenuItem(SortMenu,Menu_Subtitles_Sort_Start,_("&Start Time"),_("Sort all subtitles by their start times"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(SortMenu,Menu_Subtitles_Sort_End,_("&End Time"),_("Sort all subtitles by their end times"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(SortMenu,Menu_Subtitles_Sort_Style,_("St&yle Name"),_("Sort all subtitles by their style names"),GETIMAGE(blank_button_16)); subtitlesMenu->Append(SortParent); AppendBitmapMenuItem(subtitlesMenu,MENU_SWAP,_("Swap Lines"),_("Swaps the two selected lines"),GETIMAGE(arrow_sort_16)); AppendBitmapMenuItem (subtitlesMenu,Menu_Edit_Select, MakeHotkeyText(_("Select Lines..."), _T("Select lines")), _("Selects lines based on defined criterea"),GETIMAGE(select_lines_button_16)); MenuBar->Append(subtitlesMenu, _("&Subtitles")); // Create timing menu timingMenu = new wxMenu(); AppendBitmapMenuItem(timingMenu,Menu_Edit_Shift, MakeHotkeyText(_("S&hift Times..."), _T("Shift times")), _("Shift subtitles by time or frames"),GETIMAGE(shift_times_toolbutton_16)); AppendBitmapMenuItem(timingMenu,Menu_Tools_Timing_Processor,_("Timing Post-Processor..."), _("Runs a post-processor for timing to deal with lead-ins, lead-outs, scene timing and etc."), GETIMAGE(timing_processor_toolbutton_16)); AppendBitmapMenuItem (timingMenu,Menu_Tools_Kanji_Timer,_("Kanji Timer..."),_("Open Kanji timer"),GETIMAGE(kara_timing_copier_16)); timingMenu->AppendSeparator(); AppendBitmapMenuItem(timingMenu,Menu_Subs_Snap_Start_To_Video, MakeHotkeyText(_("Snap Start to Video"), _T("Set Start To Video")), _("Set start of selected subtitles to current video frame"), GETIMAGE(substart_to_video_16)); AppendBitmapMenuItem(timingMenu,Menu_Subs_Snap_End_To_Video, MakeHotkeyText(_("Snap End to Video"), _T("Set End to Video")), _("Set end of selected subtitles to current video frame"), GETIMAGE(subend_to_video_16)); AppendBitmapMenuItem(timingMenu,Menu_Video_Snap_To_Scene, MakeHotkeyText(_("Snap to Scene"), _T("Snap to Scene")), _("Set start and end of subtitles to the keyframes around current video frame"), GETIMAGE(snap_subs_to_scene_16)); AppendBitmapMenuItem(timingMenu,Menu_Video_Shift_To_Frame, MakeHotkeyText(_("Shift to Current Frame"), _T("Shift by Current Time")), _("Shift selection so first selected line starts at current frame"), GETIMAGE(shift_to_frame_16)); timingMenu->AppendSeparator(); wxMenu *ContinuousMenu = new wxMenu; wxMenuItem *ContinuousParent = new wxMenuItem(subtitlesMenu,-1,_("Make Times Continuous"),_T(""),wxITEM_NORMAL,ContinuousMenu); #ifndef __APPLE__ ContinuousParent->SetBitmap(GETIMAGE(blank_button_16)); #endif AppendBitmapMenuItem(ContinuousMenu,MENU_ADJOIN,_("Change &Start"),_("Changes times of subs so start times begin on previous's end time"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(ContinuousMenu,MENU_ADJOIN2,_("Change &End"),_("Changes times of subs so end times begin on next's start time"),GETIMAGE(blank_button_16)); timingMenu->Append(ContinuousParent); MenuBar->Append(timingMenu, _("&Timing")); // Create video menu videoMenu = new wxMenu(); AppendBitmapMenuItem(videoMenu, Menu_File_Open_Video, _("&Open Video..."), _("Opens a video file"), GETIMAGE(open_video_menu_16)); AppendBitmapMenuItem(videoMenu, Menu_File_Close_Video, _("&Close Video"), _("Closes the currently open video file"), GETIMAGE(close_video_menu_16)); wxMenuItem *RecentVidParent = new wxMenuItem(videoMenu, Menu_File_Recent_Vids_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentVids); videoMenu->Append(RecentVidParent); AppendBitmapMenuItem(videoMenu, Menu_Video_Dummy, _("Use Dummy Video..."), _("Opens a video clip with solid colour"), GETIMAGE(use_dummy_video_menu_16)); AppendBitmapMenuItem(videoMenu, Menu_Video_Details, _("Show Video Details..."), _("Shows video details"), GETIMAGE(show_video_details_menu_16)); videoMenu->AppendSeparator(); AppendBitmapMenuItem(videoMenu, Menu_File_Open_VFR, _("Open Timecodes File..."), _("Opens a VFR timecodes v1 or v2 file"), GETIMAGE(open_timecodes_menu_16)); AppendBitmapMenuItem(videoMenu, Menu_File_Save_VFR, _("Save Timecodes File..."), _("Saves a VFR timecodes v2 file"), GETIMAGE(save_timecodes_menu_16)); AppendBitmapMenuItem(videoMenu, Menu_File_Close_VFR, _("Close Timecodes File"), _("Closes the currently open timecodes file"), GETIMAGE(close_timecodes_menu_16))->Enable(false); wxMenuItem *RecentTimesParent = new wxMenuItem(videoMenu, Menu_File_Recent_Timecodes_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentTimecodes); videoMenu->Append(RecentTimesParent); videoMenu->AppendSeparator(); AppendBitmapMenuItem(videoMenu, Menu_Video_Load_Keyframes, _("Open Keyframes..."), _("Opens a keyframe list file"), GETIMAGE(open_keyframes_menu_16)); AppendBitmapMenuItem(videoMenu, Menu_Video_Save_Keyframes, _("Save Keyframes..."), _("Saves the current keyframe list"), GETIMAGE(save_keyframes_menu_16))->Enable(false); AppendBitmapMenuItem(videoMenu, Menu_Video_Close_Keyframes, _("Close Keyframes"), _("Closes the currently open keyframes list"), GETIMAGE(close_keyframes_menu_16))->Enable(false); wxMenuItem *RecentKeyframesParent = new wxMenuItem(videoMenu, Menu_File_Recent_Keyframes_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentKeyframes); videoMenu->Append(RecentKeyframesParent); videoMenu->AppendSeparator(); AppendBitmapMenuItem(videoMenu, Menu_Video_Detach, _("Detach Video"), _("Detach video, displaying it in a separate Window."), GETIMAGE(detach_video_menu_16)); wxMenu *ZoomMenu = new wxMenu; wxMenuItem *ZoomParent = new wxMenuItem(subtitlesMenu,Menu_View_Zoom,_("Set Zoom"),_T(""),wxITEM_NORMAL,ZoomMenu); #ifndef __APPLE__ ZoomParent->SetBitmap(GETIMAGE(set_zoom_menu_16)); #endif ZoomMenu->Append(Menu_View_Zoom_50, MakeHotkeyText(_T("&50%"), _T("Zoom 50%")), _("Set zoom to 50%")); ZoomMenu->Append(Menu_View_Zoom_100, MakeHotkeyText(_T("&100%"), _T("Zoom 100%")), _("Set zoom to 100%")); ZoomMenu->Append(Menu_View_Zoom_200, MakeHotkeyText(_T("&200%"), _T("Zoom 200%")), _("Set zoom to 200%")); videoMenu->Append(ZoomParent); wxMenu *AspectMenu = new wxMenu; wxMenuItem *AspectParent = new wxMenuItem(subtitlesMenu,Menu_Video_AR,_("Override Aspect Ratio"),_T(""),wxITEM_NORMAL,AspectMenu); #ifndef __APPLE__ AspectParent->SetBitmap(GETIMAGE(override_aspect_menu_16)); #endif AspectMenu->AppendCheckItem(Menu_Video_AR_Default, _("&Default"), _("Leave video on original aspect ratio")); AspectMenu->AppendCheckItem(Menu_Video_AR_Full, _("&Fullscreen (4:3)"), _("Forces video to 4:3 aspect ratio")); AspectMenu->AppendCheckItem(Menu_Video_AR_Wide, _("&Widescreen (16:9)"), _("Forces video to 16:9 aspect ratio")); AspectMenu->AppendCheckItem(Menu_Video_AR_235, _("&Cinematic (2.35)"), _("Forces video to 2.35 aspect ratio")); AspectMenu->AppendCheckItem(Menu_Video_AR_Custom, _("Custom..."), _("Forces video to a custom aspect ratio")); videoMenu->Append(AspectParent); videoMenu->AppendCheckItem(Menu_Video_Overscan, _("Show Overscan Mask"), _("Show a mask over the video, indicating areas that might get cropped off by overscan on televisions.")); // This is broken as you can't use Check() on a menu item that has a bitmap. // AppendBitmapMenuItem(videoMenu, Menu_Video_Overscan, _("Show Overscan Mask"), _("Show a mask over the video, indicating areas that might get cropped off by overscan on televisions."), GETIMAGE(show_overscan_menu_checked_16)); videoMenu->AppendSeparator(); AppendBitmapMenuItem(videoMenu,Menu_Video_JumpTo, MakeHotkeyText(_("&Jump to..."), _T("Video Jump")), _("Jump to frame or time"), GETIMAGE(jumpto_button_16)); AppendBitmapMenuItem(videoMenu,Menu_Subs_Snap_Video_To_Start, MakeHotkeyText(_("Jump Video to Start"), _T("Jump Video To Start")), _("Jumps the video to the start frame of current subtitle"), GETIMAGE(video_to_substart_16)); AppendBitmapMenuItem(videoMenu,Menu_Subs_Snap_Video_To_End, MakeHotkeyText(_("Jump Video to End"), _T("Jump Video To End")), _("Jumps the video to the end frame of current subtitle"), GETIMAGE(video_to_subend_16)); MenuBar->Append(videoMenu, _("&Video")); // Create audio menu audioMenu = new wxMenu(); AppendBitmapMenuItem(audioMenu, Menu_Audio_Open_File, _("&Open Audio File..."), _("Opens an audio file"), GETIMAGE(open_audio_menu_16)); AppendBitmapMenuItem(audioMenu, Menu_Audio_Open_From_Video, _("Open Audio from &Video"), _("Opens the audio from the current video file"), GETIMAGE(open_audio_from_video_menu_16)); AppendBitmapMenuItem(audioMenu, Menu_Audio_Close, _("&Close Audio"), _("Closes the currently open audio file"), GETIMAGE(close_audio_menu_16)); wxMenuItem *RecentAudParent = new wxMenuItem(audioMenu, Menu_File_Recent_Auds_Parent, _("Recent"), _T(""), wxITEM_NORMAL, RecentAuds); audioMenu->Append(RecentAudParent); #ifdef _DEBUG audioMenu->AppendSeparator(); audioMenu->Append(Menu_Audio_Open_Dummy, _T("Open 2h30 Blank Audio"), _T("Open a 150 minutes blank audio clip, for debugging")); audioMenu->Append(Menu_Audio_Open_Dummy_Noise, _T("Open 2h30 Noise Audio"), _T("Open a 150 minutes noise-filled audio clip, for debugging")); #endif MenuBar->Append(audioMenu, _("&Audio")); // Create Automation menu #ifdef WITH_AUTOMATION automationMenu = new wxMenu(); AppendBitmapMenuItem (automationMenu,Menu_Tools_Automation, _("&Automation..."),_("Open automation manager"), GETIMAGE(automation_toolbutton_16)); automationMenu->AppendSeparator(); MenuBar->Append(automationMenu, _("&Automation")); #endif // Create view menu viewMenu = new wxMenu(); AppendBitmapMenuItem(viewMenu,Menu_View_Language, _T("&Language..."), _("Select Aegisub interface language"), GETIMAGE(languages_menu_16)); AppendBitmapMenuItem(viewMenu,Menu_Tools_Options, MakeHotkeyText(_("&Options..."), _T("Options")), _("Configure Aegisub"), GETIMAGE(options_button_16)); viewMenu->AppendSeparator(); viewMenu->AppendRadioItem(Menu_View_Subs, _("Subs Only View"), _("Display subtitles only")); viewMenu->AppendRadioItem(Menu_View_Video, _("Video+Subs View"), _("Display video and subtitles only")); viewMenu->AppendRadioItem(Menu_View_Audio, _("Audio+Subs View"), _("Display audio and subtitles only")); viewMenu->AppendRadioItem(Menu_View_Standard, _("Full view"), _("Display audio, video and subtitles")); viewMenu->AppendSeparator(); viewMenu->AppendRadioItem(Menu_View_FullTags, _("Show Tags"), _("Show full override tags in the subtitle grid")); viewMenu->AppendRadioItem(Menu_View_ShortTags, _("Simplify Tags"), _("Replace override tags in the subtitle grid with a simplified placeholder")); viewMenu->AppendRadioItem(Menu_View_NoTags, _("Hide Tags"), _("Hide override tags in the subtitle grid")); MenuBar->Append(viewMenu, _("Vie&w")); // Create help menu helpMenu = new wxMenu(); AppendBitmapMenuItem (helpMenu,Menu_Help_Contents, MakeHotkeyText(_("&Contents..."), _T("Help")), _("Help topics"), GETIMAGE(contents_button_16)); #ifdef __WXMAC__ AppendBitmapMenuItem (helpMenu,Menu_Help_Files, MakeHotkeyText(_("&All Files") + _T("..."), _T("Help")), _("Help topics"), GETIMAGE(contents_button_16)); #endif helpMenu->AppendSeparator(); AppendBitmapMenuItem(helpMenu,Menu_Help_Website, _("&Website..."), _("Visit Aegisub's official website"),GETIMAGE(website_button_16)); AppendBitmapMenuItem(helpMenu,Menu_Help_Forums, _("&Forums..."), _("Visit Aegisub's forums"),GETIMAGE(forums_button_16)); AppendBitmapMenuItem(helpMenu,Menu_Help_BugTracker, _("&Bug Tracker..."), _("Visit Aegisub's bug tracker to report bugs and request new features"),GETIMAGE(bugtracker_button_16)); AppendBitmapMenuItem (helpMenu,Menu_Help_IRCChannel, _("&IRC Channel..."), _("Visit Aegisub's official IRC channel"), GETIMAGE(irc_button_16)); #ifndef __WXMAC__ helpMenu->AppendSeparator(); #endif AppendBitmapMenuItem(helpMenu,Menu_Help_Check_Updates, _("&Check for Updates..."), _("Check to see if there is a new version of Aegisub available"),GETIMAGE(blank_button_16)); AppendBitmapMenuItem(helpMenu,Menu_Help_About, _("&About..."), _("About Aegisub"),GETIMAGE(about_menu_16)); AppendBitmapMenuItem(helpMenu,Menu_Help_Log, _("&Log window..."), _("Aegisub event log"),GETIMAGE(about_menu_16)); MenuBar->Append(helpMenu, _("&Help")); // Set the bar as this frame's SetMenuBar(MenuBar); // Set menu created flag menuCreated = true; } /// @brief Initialize contents void FrameMain::InitContents() { AssFile::top = ass = new AssFile; // Set a background panel StartupLog(_T("Create background panel")); Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN); // Initialize sizers StartupLog(_T("Create main sizers")); MainSizer = new wxBoxSizer(wxVERTICAL); TopSizer = new wxBoxSizer(wxHORIZONTAL); BottomSizer = new wxBoxSizer(wxHORIZONTAL); // Video area; StartupLog(_T("Create video box")); videoBox = new VideoBox(Panel, false, ZoomBox); TopSizer->Add(videoBox,0,wxEXPAND,0); // Subtitles area StartupLog(_T("Create subtitles grid")); SubsGrid = new SubtitlesGrid(this,Panel,-1,ass,wxDefaultPosition,wxSize(600,100),wxWANTS_CHARS | wxSUNKEN_BORDER,_T("Subs grid")); BottomSizer->Add(SubsGrid,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,0); videoBox->videoSlider->grid = SubsGrid; VideoContext::Get()->grid = SubsGrid; Search.grid = SubsGrid; // Audio area StartupLog(_T("Create audio box")); audioBox = new AudioBox(Panel); audioBox->frameMain = this; VideoContext::Get()->audio = audioBox->audioDisplay; // Top sizer StartupLog(_T("Create subtitle editing box")); EditBox = new SubsEditBox(Panel,SubsGrid, audioBox->audioDisplay); StartupLog(_T("Arrange controls in sizers")); ToolSizer = new wxBoxSizer(wxVERTICAL); ToolSizer->Add(audioBox,0,wxEXPAND | wxBOTTOM,5); ToolSizer->Add(EditBox,1,wxEXPAND,5); TopSizer->Add(ToolSizer,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5); // Set sizers/hints StartupLog(_T("Arrange main sizers")); MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0); MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0); MainSizer->Add(BottomSizer,1,wxEXPAND | wxALL,0); Panel->SetSizer(MainSizer); //MainSizer->SetSizeHints(Panel); //SetSizer(MainSizer); // Set display StartupLog(_T("Perform layout")); Layout(); StartupLog(_T("Set focus to edting box")); EditBox->TextEdit->SetFocus(); StartupLog(_T("Leaving InitContents")); } /// @brief Deinitialize controls void FrameMain::DeInitContents() { if (detachedVideo) detachedVideo->Destroy(); if (stylingAssistant) stylingAssistant->Destroy(); SubsGrid->ClearMaps(); delete EditBox; delete videoBox; delete ass; HelpButton::ClearPages(); VideoContext::Get()->audio = NULL; } /// @brief Update toolbar void FrameMain::UpdateToolbar() { // Collect flags bool isVideo = VideoContext::Get()->IsLoaded(); HasSelection = true; int selRows = SubsGrid->GetNumberSelection(); // Update wxToolBar* toolbar = GetToolBar(); toolbar->FindById(Menu_Video_JumpTo)->Enable(isVideo); toolbar->FindById(Menu_Video_Zoom_In)->Enable(isVideo && !detachedVideo); toolbar->FindById(Menu_Video_Zoom_Out)->Enable(isVideo && !detachedVideo); ZoomBox->Enable(isVideo && !detachedVideo); toolbar->FindById(Menu_Subs_Snap_Start_To_Video)->Enable(isVideo && selRows > 0); toolbar->FindById(Menu_Subs_Snap_End_To_Video)->Enable(isVideo && selRows > 0); toolbar->FindById(Menu_Subs_Snap_Video_To_Start)->Enable(isVideo && selRows == 1); toolbar->FindById(Menu_Subs_Snap_Video_To_End)->Enable(isVideo && selRows == 1); toolbar->FindById(Menu_Video_Select_Visible)->Enable(isVideo); toolbar->FindById(Menu_Video_Snap_To_Scene)->Enable(isVideo && selRows > 0); toolbar->FindById(Menu_Video_Shift_To_Frame)->Enable(isVideo && selRows > 0); toolbar->Realize(); } /// @brief Open subtitles /// @param filename /// @param charset void FrameMain::LoadSubtitles (wxString filename,wxString charset) { // First check if there is some loaded if (ass && ass->loaded) { if (TryToCloseSubs() == wxCANCEL) return; } // Setup bool isFile = !filename.empty(); // Load try { // File exists? if (isFile) { wxFileName fileCheck(filename); if (!fileCheck.FileExists()) { throw agi::FileNotFoundError(STD_STR(filename)); } // Make sure that file isn't actually a timecode file try { TextFileReader testSubs(filename,charset); wxString cur = testSubs.ReadLineFromFile(); if (cur.Left(10) == _T("# timecode")) { LoadVFR(filename); OPT_SET("Path/Last/Timecodes")->SetString(STD_STR(fileCheck.GetPath())); 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 } } // Proceed into loading SubsGrid->ClearMaps(); if (isFile) { ass->Load(filename,charset); SubsGrid->UpdateMaps(); if (SubsGrid->GetRows()) { SubsGrid->SetActiveLine(SubsGrid->GetDialogue(0)); SubsGrid->SelectRow(0); } wxFileName fn(filename); StandardPaths::SetPathValue(_T("?script"),fn.GetPath()); OPT_SET("Path/Last/Subtitles")->SetString(STD_STR(fn.GetPath())); } else { SubsGrid->LoadDefault(); StandardPaths::SetPathValue(_T("?script"),_T("")); } SubsGrid->SetColumnWidths(); } catch (agi::FileNotFoundError const&) { wxMessageBox(filename + L" not found.", L"Error", wxOK | wxICON_ERROR, NULL); config::mru->Remove("Subtitle", STD_STR(filename)); return; } catch (const wchar_t *err) { wxMessageBox(wxString(err), _T("Error"), wxOK | wxICON_ERROR, NULL); return; } catch (wxString err) { wxMessageBox(err, _T("Error"), wxOK | wxICON_ERROR, NULL); return; } catch (...) { wxMessageBox(_T("Unknown error"), _T("Error"), wxOK | wxICON_ERROR, NULL); return; } // Save copy wxFileName origfile(filename); if (ass->CanSave() && OPT_GET("App/Auto/Backup")->GetBool() && origfile.FileExists()) { // Get path wxString path = lagi_wxString(OPT_GET("Path/Auto/Backup")->GetString()); if (path.IsEmpty()) path = origfile.GetPath(); wxFileName dstpath(path); if (!dstpath.IsAbsolute()) path = StandardPaths::DecodePathMaybeRelative(path, _T("?user/")); path += _T("/"); dstpath.Assign(path); if (!dstpath.DirExists()) wxMkdir(path); // Save wxString backup = path + origfile.GetName() + _T(".ORIGINAL.") + origfile.GetExt(); wxCopyFile(filename,backup,true); } // Sync SynchronizeProject(true); // Update title bar UpdateTitle(); VideoContext::Get()->Refresh(); } /// @brief Save subtitles /// @param saveas /// @param withCharset /// @return bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) { // Try to get filename from file wxString filename; if (saveas == false && ass->CanSave()) filename = ass->filename; // Failed, ask user if (filename.IsEmpty()) { VideoContext::Get()->Stop(); wxString path = lagi_wxString(OPT_GET("Path/Last/Subtitles")->GetString()); 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); } // Actually save 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) { charset = wxGetSingleChoice(_("Choose charset code:"), _T("Charset"),agi::charset::GetEncodingsList(),this,-1, -1,true,250,200); if (charset.IsEmpty()) return false; } // Save try { ass->Save(filename,true,true,charset); UpdateTitle(); } catch (const agi::Exception& err) { wxMessageBox(lagi_wxString(err.GetMessage()), "Error", wxOK | wxICON_ERROR, NULL); return false; } catch (const wchar_t *err) { wxMessageBox(wxString(err), _T("Error"), wxOK | wxICON_ERROR, NULL); return false; } catch (...) { wxMessageBox(_T("Unknown error"), _T("Error"), wxOK | wxICON_ERROR, NULL); return false; } return true; } return false; } /// @brief Try to close subtitles /// @param enableCancel /// @return int FrameMain::TryToCloseSubs(bool enableCancel) { if (ass->IsModified()) { int flags = wxYES_NO; if (enableCancel) flags |= wxCANCEL; int result = wxMessageBox(_("Save before continuing?"), _("Unsaved changes"), flags,this); if (result == wxYES) { // If it fails saving, return cancel anyway if (SaveSubtitles(false)) return wxYES; else return wxCANCEL; } return result; } else return wxYES; } /// @brief Set the video and audio display visibilty /// @param video -1: leave unchanged; 0: hide; 1: show /// @param audio -1: leave unchanged; 0: hide; 1: show void FrameMain::SetDisplayMode(int video, int audio) { if (!IsShownOnScreen()) return; bool sv = false, sa = false; if (video == -1) sv = showVideo; else if (video) sv = VideoContext::Get()->IsLoaded() && !detachedVideo; if (audio == -1) sa = showAudio; else if (audio) sa = audioBox->loaded; // See if anything changed if (sv == showVideo && sa == showAudio) return; showVideo = sv; showAudio = sa; bool didFreeze = !IsFrozen(); if (didFreeze) Freeze(); VideoContext::Get()->Stop(); // Set display TopSizer->Show(videoBox,showVideo,true); ToolSizer->Show(audioBox,showAudio,true); // Update UpdateToolbar(); MainSizer->CalcMin(); MainSizer->RecalcSizes(); MainSizer->Layout(); Layout(); if (showVideo) VideoContext::Get()->UpdateDisplays(true); if (didFreeze) Thaw(); } /// @brief Update title bar void FrameMain::UpdateTitle() { // Determine if current subs are modified bool subsMod = ass->IsModified(); // Create ideal title wxString newTitle = _T(""); #ifndef __WXMAC__ if (subsMod) newTitle << _T("* "); if (ass->filename != _T("")) { wxFileName file (ass->filename); newTitle << file.GetFullName(); } else newTitle << _("Untitled"); newTitle << _T(" - Aegisub ") << GetAegisubLongVersionString(); #else // 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) if (ass->filename != _T("")) { wxFileName file (ass->filename); newTitle << file.GetFullName(); } else newTitle << _("untitled"); #endif #ifdef __WXMAC__ // On Mac, set the mark in the close button OSXSetModified(subsMod); #endif // Get current title wxString curTitle = GetTitle(); if (curTitle != newTitle) SetTitle(newTitle); } /// @brief Updates subs with video/whatever data /// @param fromSubs void FrameMain::SynchronizeProject(bool fromSubs) { // Retrieve data from subs if (fromSubs) { // Reset the state long videoPos = 0; long videoAr = 0; double videoArValue = 0.0; double videoZoom = 0.; // Get AR wxString arString = ass->GetScriptInfo(_T("Video Aspect Ratio")); if (arString.Left(1) == _T("c")) { videoAr = 4; arString = arString.Mid(1); arString.ToDouble(&videoArValue); } else if (arString.IsNumber()) arString.ToLong(&videoAr); // Get new state info ass->GetScriptInfo(_T("Video Position")).ToLong(&videoPos); ass->GetScriptInfo(_T("Video Zoom Percent")).ToDouble(&videoZoom); wxString curassVideo = DecodeRelativePath(ass->GetScriptInfo(_T("Video File")),ass->filename); wxString curassVFR = DecodeRelativePath(ass->GetScriptInfo(_T("VFR File")),ass->filename); wxString curassKeyframes = DecodeRelativePath(ass->GetScriptInfo(_T("Keyframes File")),ass->filename); wxString curassAudio = DecodeRelativePath(ass->GetScriptInfo(_T("Audio File")),ass->filename); wxString AutoScriptString = ass->GetScriptInfo(_T("Automation Scripts")); // Check if there is anything to change int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt(); bool hasToLoad = false; if (curassAudio != audioBox->audioName || curassVFR != VideoContext::Get()->GetTimecodesName() || curassVideo != VideoContext::Get()->videoName || curassKeyframes != VideoContext::Get()->GetKeyFramesName() #ifdef WITH_AUTOMATION || !AutoScriptString.IsEmpty() || local_scripts->GetScripts().size() > 0 #endif ) { hasToLoad = true; } // Decide whether to load or not bool doLoad = false; if (hasToLoad) { if (autoLoadMode == 1) doLoad = true; else if (autoLoadMode == 2) { int result = wxMessageBox(_("Do you want to load/unload the associated files?"),_("(Un)Load files?"),wxYES_NO); if (result == wxYES) doLoad = true; } } if (doLoad) { // Video if (curassVideo != VideoContext::Get()->videoName) { LoadVideo(curassVideo); if (VideoContext::Get()->IsLoaded()) { VideoContext::Get()->SetAspectRatio(videoAr,videoArValue); videoBox->videoDisplay->SetZoom(videoZoom); VideoContext::Get()->JumpToFrame(videoPos); } } VideoContext::Get()->LoadTimecodes(curassVFR); VideoContext::Get()->LoadKeyframes(curassKeyframes); // Audio if (curassAudio != audioBox->audioName) { if (curassAudio == _T("?video")) LoadAudio(_T(""),true); else LoadAudio(curassAudio); } // Automation scripts #ifdef WITH_AUTOMATION local_scripts->RemoveAll(); wxStringTokenizer tok(AutoScriptString, _T("|"), wxTOKEN_STRTOK); wxFileName assfn(ass->filename); wxString autobasefn(lagi_wxString(OPT_GET("Path/Automation/Base")->GetString())); while (tok.HasMoreTokens()) { wxString sfnames = tok.GetNextToken().Trim(true).Trim(false); wxString sfnamel = sfnames.Left(1); sfnames.Remove(0, 1); wxString basepath; if (sfnamel == _T("~")) { basepath = assfn.GetPath(); } else if (sfnamel == _T("$")) { basepath = autobasefn; } else if (sfnamel == _T("/")) { basepath = _T(""); } else { wxLogWarning(_T("Automation Script referenced with unknown location specifier character.\nLocation specifier found: %s\nFilename specified: %s"), sfnamel.c_str(), sfnames.c_str()); continue; } wxFileName sfname(sfnames); sfname.MakeAbsolute(basepath); if (sfname.FileExists()) { sfnames = sfname.GetFullPath(); local_scripts->Add(Automation4::ScriptFactory::CreateFromFile(sfnames, true)); } else { wxLogWarning(_T("Automation Script referenced could not be found.\nFilename specified: %s%s\nSearched relative to: %s\nResolved filename: %s"), sfnamel.c_str(), sfnames.c_str(), basepath.c_str(), sfname.GetFullPath().c_str()); } } #endif } // Display SetDisplayMode(1,1); } // Store data on ass else { // Setup wxString seekpos = _T("0"); wxString ar = _T("0"); wxString zoom = _T("6"); if (VideoContext::Get()->IsLoaded()) { seekpos = wxString::Format(_T("%i"),videoBox->videoDisplay->GetFrame()); zoom = wxString::Format(_T("%f"),videoBox->videoDisplay->GetZoom()); int arType = VideoContext::Get()->GetAspectRatioType(); if (arType == 4) ar = wxString(_T("c")) + AegiFloatToString(VideoContext::Get()->GetAspectRatioValue()); else ar = wxString::Format(_T("%i"),arType); } // Store audio data ass->SetScriptInfo(_T("Audio File"),MakeRelativePath(audioBox->audioName,ass->filename)); // Store video data ass->SetScriptInfo(_T("Video File"),MakeRelativePath(VideoContext::Get()->videoName,ass->filename)); ass->SetScriptInfo(_T("Video Aspect Ratio"),ar); ass->SetScriptInfo(_T("Video Zoom Percent"),zoom); ass->SetScriptInfo(_T("Video Position"),seekpos); ass->SetScriptInfo(_T("VFR File"),MakeRelativePath(VideoContext::Get()->GetTimecodesName(),ass->filename)); ass->SetScriptInfo(_T("Keyframes File"),MakeRelativePath(VideoContext::Get()->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 &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 } } /// @brief Loads video /// @param file /// @param autoload void FrameMain::LoadVideo(wxString file,bool autoload) { if (blockVideoLoad) return; Freeze(); try { VideoContext::Get()->SetVideo(file); } catch (const wchar_t *error) { wxMessageBox(error, _T("Error opening video file"), wxOK | wxICON_ERROR, this); } catch (...) { wxMessageBox(_T("Unknown error"), _T("Error opening video file"), wxOK | wxICON_ERROR, this); } if (VideoContext::Get()->IsLoaded()) { int vidx = VideoContext::Get()->GetWidth(), vidy = VideoContext::Get()->GetHeight(); // Set zoom level based on video resolution and window size double zoom = videoBox->videoDisplay->GetZoom(); wxSize windowSize = GetSize(); if (vidx*3*zoom > windowSize.GetX()*4 || vidy*4*zoom > windowSize.GetY()*6) videoBox->videoDisplay->SetZoom(zoom * .25); else if (vidx*3*zoom > windowSize.GetX()*2 || vidy*4*zoom > windowSize.GetY()*3) videoBox->videoDisplay->SetZoom(zoom * .5); // Check that the video size matches the script video size specified int scriptx = SubsGrid->ass->GetScriptInfoAsInt(_T("PlayResX")); int scripty = SubsGrid->ass->GetScriptInfoAsInt(_T("PlayResY")); if (scriptx != vidx || scripty != vidy) { switch (OPT_GET("Video/Check Script Res")->GetInt()) { case 1: // Ask to change on mismatch if (wxMessageBox(wxString::Format(_("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?"), vidx, vidy, scriptx, scripty), _("Resolution mismatch"), wxYES_NO, this) != wxYES) break; // Fallthrough to case 2 case 2: // Always change script res SubsGrid->ass->SetScriptInfo(_T("PlayResX"), wxString::Format(_T("%d"), vidx)); SubsGrid->ass->SetScriptInfo(_T("PlayResY"), wxString::Format(_T("%d"), vidy)); SubsGrid->ass->Commit(_("Change script resolution")); SubsGrid->CommitChanges(); break; case 0: default: // Never change break; } } } SubsGrid->CommitChanges(); SetDisplayMode(1,-1); EditBox->UpdateFrameTiming(); DetachVideo(VideoContext::Get()->IsLoaded() && OPT_GET("Video/Detached/Enabled")->GetBool()); Thaw(); } /// @brief Loads audio /// @param filename /// @param FromVideo void FrameMain::LoadAudio(wxString filename,bool FromVideo) { if (blockAudioLoad) return; VideoContext::Get()->Stop(); try { audioBox->SetFile(filename,FromVideo); SetDisplayMode(-1,1); } catch (const wchar_t *error) { wxString err(error); wxMessageBox(err, _T("Error opening audio file"), wxOK | wxICON_ERROR, this); } #ifdef WITH_AVISYNTH catch (AvisynthError err) { wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvUTF8), _T("Error loading audio"), wxOK | wxICON_ERROR); return; } #endif catch (...) { wxMessageBox(_T("Unknown error"), _T("Error opening audio file"), wxOK | wxICON_ERROR, this); } } void FrameMain::LoadVFR(wxString filename) { if (filename.empty()) { VideoContext::Get()->CloseTimecodes(); } else { VideoContext::Get()->LoadTimecodes(filename); } SubsGrid->CommitChanges(); EditBox->UpdateFrameTiming(); } /// @brief Open help void FrameMain::OpenHelp(wxString) { HelpButton::OpenPage(_T("Main")); } /// @brief Detach video window /// @param detach void FrameMain::DetachVideo(bool detach) { if (detach) { if (!detachedVideo) { detachedVideo = new DialogDetachedVideo(this, videoBox->videoDisplay->GetClientSize()); detachedVideo->Show(); VideoContext::Get()->UpdateDisplays(true); } } else if (detachedVideo) { detachedVideo->Destroy(); detachedVideo = NULL; SetDisplayMode(1,-1); } UpdateToolbar(); } /// @brief Sets status and clear after n milliseconds /// @param text /// @param ms void FrameMain::StatusTimeout(wxString text,int ms) { SetStatusText(text,1); StatusClear.SetOwner(this,StatusClear_Timer); StatusClear.Start(ms,true); } /// @brief Setup accelerator table void FrameMain::SetAccelerators() { std::vector entry; entry.reserve(32); // Standard entry.push_back(Hotkeys.GetAccelerator(_T("Video global prev frame"),Video_Prev_Frame)); entry.push_back(Hotkeys.GetAccelerator(_T("Video global next frame"),Video_Next_Frame)); entry.push_back(Hotkeys.GetAccelerator(_T("Video global focus seek"),Video_Focus_Seek)); entry.push_back(Hotkeys.GetAccelerator(_T("Grid global prev line"),Grid_Prev_Line)); entry.push_back(Hotkeys.GetAccelerator(_T("Grid global next line"),Grid_Next_Line)); entry.push_back(Hotkeys.GetAccelerator(_T("Save Subtitles Alt"),Menu_File_Save_Subtitles)); entry.push_back(Hotkeys.GetAccelerator(_T("Video global zoom in"),Menu_Video_Zoom_In)); entry.push_back(Hotkeys.GetAccelerator(_T("Video global zoom out"),Menu_Video_Zoom_Out)); entry.push_back(Hotkeys.GetAccelerator(_T("Video global play"),Video_Frame_Play)); // Medusa bool medusaPlay = OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool(); if (medusaPlay && audioBox->audioDisplay->loaded) { entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play"),Medusa_Play)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Stop"),Medusa_Stop)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play Before"),Medusa_Play_Before)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Play After"),Medusa_Play_After)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Next"),Medusa_Next)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Previous"),Medusa_Prev)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Shift Start Forward"),Medusa_Shift_Start_Forward)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Shift Start Back"),Medusa_Shift_Start_Back)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Shift End Forward"),Medusa_Shift_End_Forward)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Shift End Back"),Medusa_Shift_End_Back)); entry.push_back(Hotkeys.GetAccelerator(_T("Audio Medusa Enter"),Medusa_Enter)); } // Set table wxAcceleratorTable table(entry.size(),&entry[0]); SetAcceleratorTable(table); } /// @brief Load list of files /// @param list /// @return bool FrameMain::LoadList(wxArrayString list) { // Build list wxArrayString List; for (size_t i=0;iSetHelpString(0,_T("Undo ")+ass->GetUndoDescription()); editMenu->SetHelpString(1,_T("Redo ")+ass->GetRedoDescription()); } /// @brief Check if ASSDraw is available bool FrameMain::HasASSDraw() { #ifdef __WINDOWS__ wxFileName fn(StandardPaths::DecodePath(_T("?data/ASSDraw3.exe"))); return fn.FileExists(); #else return false; #endif } static void autosave_timer_changed(wxTimer &timer, const agi::OptionValue &opt) { int freq = opt.GetInt(); if (freq <= 0) { timer.Stop(); } else { timer.Start(freq * 1000); } }