mirror of https://github.com/odrling/Aegisub
Clean up a bunch of stuff in DialogTimingProcessor
Factor out most of the duplicated code and fix a lot of ugly stylistic things. Originally committed to SVN as r6310.
This commit is contained in:
parent
e40e42ff1c
commit
f6c3be4776
|
@ -191,6 +191,7 @@
|
|||
#include <wx/txtstrm.h>
|
||||
#include <wx/utils.h>
|
||||
#include <wx/validate.h>
|
||||
#include <wx/valnum.h>
|
||||
#include <wx/valtext.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/window.h>
|
||||
|
|
|
@ -36,142 +36,173 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "dialog_timing_processor.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
#include <tr1/functional>
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/checklst.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/valnum.h>
|
||||
#endif
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_time.h"
|
||||
#include "dialog_timing_processor.h"
|
||||
#include "help_button.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "selection_controller.h"
|
||||
#include "utils.h"
|
||||
#include "validators.h"
|
||||
#include "video_context.h"
|
||||
|
||||
/// Window IDs
|
||||
enum {
|
||||
CHECK_ENABLE_LEADIN = 1850,
|
||||
CHECK_ENABLE_LEADOUT,
|
||||
CHECK_ENABLE_KEYFRAME,
|
||||
CHECK_ENABLE_ADJASCENT,
|
||||
BUTTON_SELECT_ALL,
|
||||
BUTTON_SELECT_NONE,
|
||||
TIMING_STYLE_LIST
|
||||
};
|
||||
namespace {
|
||||
using std::tr1::placeholders::_1;
|
||||
|
||||
void set_ctrl_state(wxCommandEvent &evt, wxCheckBox *cb, wxTextCtrl *tc) {
|
||||
tc->Enable(cb->IsChecked());
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
wxTextCtrl *make_ctrl(wxWindow *parent, wxSizer *sizer, wxString const& desc, int *value, wxCheckBox *cb, wxString const& tooltip) {
|
||||
wxIntegerValidator<int> validator(value);
|
||||
validator.SetMin(0);
|
||||
wxTextCtrl *ctrl = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(60,-1), 0, validator);
|
||||
ctrl->SetToolTip(tooltip);
|
||||
if (!desc.empty())
|
||||
sizer->Add(new wxStaticText(parent, -1, desc), wxSizerFlags().Center().Border(wxRIGHT));
|
||||
sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
|
||||
|
||||
ctrl->Enable(cb->IsChecked());
|
||||
cb->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, bind(set_ctrl_state, _1, cb, ctrl));
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
inline wxTextCtrl *make_ctrl(wxStaticBoxSizer *sizer, wxString const& desc, int *value, wxCheckBox *cb, wxString const& tooltip) {
|
||||
return make_ctrl(sizer->GetStaticBox()->GetParent(), sizer, desc, value, cb, tooltip);
|
||||
}
|
||||
|
||||
wxCheckBox *make_check(wxStaticBoxSizer *sizer, wxString const& desc, const char *opt, wxString const& tooltip) {
|
||||
wxCheckBox *cb = new wxCheckBox(sizer->GetStaticBox()->GetParent(), -1, desc);
|
||||
cb->SetToolTip(tooltip);
|
||||
cb->SetValue(OPT_GET(opt)->GetBool());
|
||||
sizer->Add(cb, wxSizerFlags().Border(wxRIGHT).Expand());
|
||||
return cb;
|
||||
}
|
||||
}
|
||||
|
||||
DialogTimingProcessor::DialogTimingProcessor(agi::Context *c)
|
||||
: wxDialog(c->parent,-1,_("Timing Post-Processor"),wxDefaultPosition,wxSize(400,250),wxDEFAULT_DIALOG_STYLE)
|
||||
: wxDialog(c->parent, -1, _("Timing Post-Processor"))
|
||||
, c(c)
|
||||
{
|
||||
using std::tr1::bind;
|
||||
|
||||
SetIcon(BitmapToIcon(GETIMAGE(timing_processor_toolbutton_24)));
|
||||
|
||||
// Set variables
|
||||
wxString leadInTime(wxString::Format("%" PRId64, OPT_GET("Audio/Lead/IN")->GetInt()));
|
||||
wxString leadOutTime(wxString::Format("%" PRId64, OPT_GET("Audio/Lead/OUT")->GetInt()));
|
||||
wxString thresStartBefore(wxString::Format("%" PRId64, OPT_GET("Tool/Timing Post Processor/Threshold/Key Start Before")->GetInt()));
|
||||
wxString thresStartAfter(wxString::Format("%" PRId64, OPT_GET("Tool/Timing Post Processor/Threshold/Key Start After")->GetInt()));
|
||||
wxString thresEndBefore(wxString::Format("%" PRId64, OPT_GET("Tool/Timing Post Processor/Threshold/Key End Before")->GetInt()));
|
||||
wxString thresEndAfter(wxString::Format("%" PRId64, OPT_GET("Tool/Timing Post Processor/Threshold/Key End After")->GetInt()));
|
||||
wxString adjsThresTime(wxString::Format("%" PRId64, OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent")->GetInt()));
|
||||
// Read options
|
||||
leadIn = OPT_GET("Audio/Lead/IN")->GetInt();
|
||||
leadOut = OPT_GET("Audio/Lead/OUT")->GetInt();
|
||||
beforeStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start Before")->GetInt();
|
||||
beforeEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End Before")->GetInt();
|
||||
afterStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start After")->GetInt();
|
||||
afterEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End After")->GetInt();
|
||||
adjDistance = OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent")->GetInt();
|
||||
|
||||
// Styles box
|
||||
// Styles box
|
||||
wxSizer *LeftSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Apply to styles"));
|
||||
wxArrayString styles = c->ass->GetStyles();
|
||||
StyleList = new wxCheckListBox(this,TIMING_STYLE_LIST,wxDefaultPosition,wxSize(150,150),styles);
|
||||
StyleList = new wxCheckListBox(this, -1, wxDefaultPosition, wxSize(150,150), c->ass->GetStyles());
|
||||
StyleList->SetToolTip(_("Select styles to process. Unchecked ones will be ignored."));
|
||||
wxButton *all = new wxButton(this,BUTTON_SELECT_ALL,_("&All"));
|
||||
|
||||
wxButton *all = new wxButton(this,-1,_("&All"));
|
||||
all->SetToolTip(_("Select all styles."));
|
||||
wxButton *none = new wxButton(this,BUTTON_SELECT_NONE,_("&None"));
|
||||
|
||||
wxButton *none = new wxButton(this,-1,_("&None"));
|
||||
none->SetToolTip(_("Deselect all styles."));
|
||||
|
||||
// Options box
|
||||
wxSizer *optionsSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Options"));
|
||||
wxStaticBoxSizer *optionsSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Options"));
|
||||
onlySelection = new wxCheckBox(this,-1,_("Affect &selection only"));
|
||||
onlySelection->SetValue(OPT_GET("Tool/Timing Post Processor/Only Selection")->GetBool());
|
||||
optionsSizer->Add(onlySelection,1,wxALL,0);
|
||||
|
||||
// Lead-in/out box
|
||||
wxSizer *LeadSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Lead-in/Lead-out"));
|
||||
hasLeadIn = new wxCheckBox(this,CHECK_ENABLE_LEADIN,_("Add lead &in:"));
|
||||
hasLeadIn->SetToolTip(_("Enable adding of lead-ins to lines."));
|
||||
hasLeadIn->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Lead/IN")->GetBool());
|
||||
leadIn = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(80,-1),0,NumValidator(leadInTime));
|
||||
leadIn->SetToolTip(_("Lead in to be added, in milliseconds."));
|
||||
hasLeadOut = new wxCheckBox(this,CHECK_ENABLE_LEADOUT,_("Add lead &out:"));
|
||||
hasLeadOut->SetToolTip(_("Enable adding of lead-outs to lines."));
|
||||
hasLeadOut->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Lead/OUT")->GetBool());
|
||||
leadOut = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(80,-1),0,NumValidator(leadOutTime));
|
||||
leadOut->SetToolTip(_("Lead out to be added, in milliseconds."));
|
||||
LeadSizer->Add(hasLeadIn,0,wxRIGHT|wxEXPAND,5);
|
||||
LeadSizer->Add(leadIn,0,wxRIGHT|wxEXPAND,5);
|
||||
LeadSizer->Add(hasLeadOut,0,wxRIGHT|wxEXPAND,5);
|
||||
LeadSizer->Add(leadOut,0,wxRIGHT|wxEXPAND,0);
|
||||
wxStaticBoxSizer *LeadSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Lead-in/Lead-out"));
|
||||
|
||||
hasLeadIn = make_check(LeadSizer, _("Add lead &in:"),
|
||||
"Tool/Timing Post Processor/Enable/Lead/IN",
|
||||
_("Enable adding of lead-ins to lines."));
|
||||
make_ctrl(LeadSizer, "", &leadIn, hasLeadIn, _("Lead in to be added, in milliseconds."));
|
||||
|
||||
hasLeadOut = make_check(LeadSizer, _("Add lead &out:"),
|
||||
"Tool/Timing Post Processor/Enable/Lead/OUT",
|
||||
_("Enable adding of lead-outs to lines."));
|
||||
make_ctrl(LeadSizer, "", &leadOut, hasLeadOut, _("Lead out to be added, in milliseconds."));
|
||||
|
||||
LeadSizer->AddStretchSpacer(1);
|
||||
|
||||
// Adjacent subs sizer
|
||||
wxSizer *AdjacentSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Make adjacent subtitles continuous"));
|
||||
adjsEnable = new wxCheckBox(this,CHECK_ENABLE_ADJASCENT,_("&Enable"));
|
||||
adjsEnable->SetToolTip(_("Enable snapping of subtitles together if they are within a certain distance of each other."));
|
||||
adjsEnable->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Adjacent")->GetBool());
|
||||
wxStaticText *adjsThresText = new wxStaticText(this,-1,_("Threshold:"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
|
||||
adjacentThres = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(60,-1),0,NumValidator(adjsThresTime));
|
||||
adjacentThres->SetToolTip(_("Maximum difference between start and end time for two subtitles to be made continuous, in milliseconds."));
|
||||
adjacentBias = new wxSlider(this,-1,mid(0,int(OPT_GET("Tool/Timing Post Processor/Adjacent Bias")->GetDouble()*100),100),0,100,wxDefaultPosition,wxSize(-1,20));
|
||||
wxStaticBoxSizer *AdjacentSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Make adjacent subtitles continuous"));
|
||||
adjsEnable = make_check(AdjacentSizer, _("&Enable"),
|
||||
"Tool/Timing Post Processor/Enable/Adjacent",
|
||||
_("Enable snapping of subtitles together if they are within a certain distance of each other."));
|
||||
|
||||
make_ctrl(AdjacentSizer, _("Threshold:"), &adjDistance, adjsEnable,
|
||||
_("Maximum difference between start and end time for two subtitles to be made continuous, in milliseconds."));
|
||||
|
||||
adjacentBias = new wxSlider(this, -1, mid<int>(0, OPT_GET("Tool/Timing Post Processor/Adjacent Bias")->GetDouble() * 100, 100), 0, 100, wxDefaultPosition, wxSize(-1,20));
|
||||
adjacentBias->SetToolTip(_("Sets how to set the adjoining of lines. If set totally to left, it will extend start time of the second line; if totally to right, it will extend the end time of the first line."));
|
||||
AdjacentSizer->Add(adjsEnable,0,wxRIGHT|wxEXPAND,10);
|
||||
AdjacentSizer->Add(adjsThresText,0,wxRIGHT|wxALIGN_CENTER,5);
|
||||
AdjacentSizer->Add(adjacentThres,0,wxRIGHT|wxEXPAND,5);
|
||||
AdjacentSizer->Add(new wxStaticText(this,-1,_("Bias: Start <- "),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE),0,wxALIGN_CENTER,0);
|
||||
AdjacentSizer->Add(adjacentBias,1,wxEXPAND,0);
|
||||
AdjacentSizer->Add(new wxStaticText(this,-1,_(" -> End"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE),0,wxALIGN_CENTER,0);
|
||||
|
||||
AdjacentSizer->Add(new wxStaticText(this, -1, _("Bias: Start <- ")), wxSizerFlags().Center());
|
||||
AdjacentSizer->Add(adjacentBias, wxSizerFlags(1).Expand().Center());
|
||||
AdjacentSizer->Add(new wxStaticText(this, -1, _(" -> End")), wxSizerFlags().Center());
|
||||
|
||||
// Keyframes sizer
|
||||
KeyframesSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Keyframe snapping"));
|
||||
wxStaticBoxSizer *KeyframesSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Keyframe snapping"));
|
||||
wxSizer *KeyframesFlexSizer = new wxFlexGridSizer(2,5,5,0);
|
||||
keysEnable = new wxCheckBox(this,CHECK_ENABLE_KEYFRAME,_("E&nable"));
|
||||
|
||||
keysEnable = new wxCheckBox(this, -1, _("E&nable"));
|
||||
keysEnable->SetToolTip(_("Enable snapping of subtitles to nearest keyframe, if distance is within threshold."));
|
||||
keysEnable->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Keyframe")->GetBool());
|
||||
wxStaticText *textStartBefore = new wxStaticText(this,-1,_("Starts before thres.:"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
|
||||
keysStartBefore = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(60,-1),0,NumValidator(thresStartBefore));
|
||||
keysStartBefore->SetToolTip(_("Threshold for 'before start' distance, that is, how many frames a subtitle must start before a keyframe to snap to it."));
|
||||
wxStaticText *textStartAfter = new wxStaticText(this,-1,_("Starts after thres.:"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
|
||||
keysStartAfter = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(60,-1),0,NumValidator(thresStartAfter));
|
||||
keysStartAfter->SetToolTip(_("Threshold for 'after start' distance, that is, how many frames a subtitle must start after a keyframe to snap to it."));
|
||||
wxStaticText *textEndBefore = new wxStaticText(this,-1,_("Ends before thres.:"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
|
||||
keysEndBefore = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(60,-1),0,NumValidator(thresEndBefore));
|
||||
keysEndBefore->SetToolTip(_("Threshold for 'before end' distance, that is, how many frames a subtitle must end before a keyframe to snap to it."));
|
||||
wxStaticText *textEndAfter = new wxStaticText(this,-1,_("Ends after thres.:"),wxDefaultPosition,wxDefaultSize,wxALIGN_CENTRE);
|
||||
keysEndAfter = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(60,-1),0,NumValidator(thresEndAfter));
|
||||
keysEndAfter->SetToolTip(_("Threshold for 'after end' distance, that is, how many frames a subtitle must end after a keyframe to snap to it."));
|
||||
KeyframesFlexSizer->Add(keysEnable,0,wxRIGHT|wxEXPAND,10);
|
||||
KeyframesFlexSizer->Add(textStartBefore,0,wxRIGHT|wxALIGN_CENTER,5);
|
||||
KeyframesFlexSizer->Add(keysStartBefore,0,wxRIGHT|wxEXPAND,5);
|
||||
KeyframesFlexSizer->Add(textStartAfter,0,wxRIGHT|wxALIGN_CENTER,5);
|
||||
KeyframesFlexSizer->Add(keysStartAfter,0,wxRIGHT|wxEXPAND,0);
|
||||
|
||||
// Keyframes are only available if timecodes are loaded
|
||||
bool keysAvailable = c->videoController->KeyFramesLoaded() && c->videoController->TimecodesLoaded();
|
||||
if (!keysAvailable) {
|
||||
keysEnable->SetValue(false);
|
||||
keysEnable->Enable(false);
|
||||
}
|
||||
|
||||
make_ctrl(this, KeyframesFlexSizer, _("Starts before thres.:"), &beforeStart, keysEnable,
|
||||
_("Threshold for 'before start' distance, that is, how many frames a subtitle must start before a keyframe to snap to it."));
|
||||
|
||||
make_ctrl(this, KeyframesFlexSizer, _("Starts after thres.:"), &afterStart, keysEnable,
|
||||
_("Threshold for 'after start' distance, that is, how many frames a subtitle must start after a keyframe to snap to it."));
|
||||
|
||||
KeyframesFlexSizer->AddStretchSpacer(1);
|
||||
KeyframesFlexSizer->Add(textEndBefore,0,wxRIGHT|wxALIGN_CENTER,5);
|
||||
KeyframesFlexSizer->Add(keysEndBefore,0,wxRIGHT|wxEXPAND,5);
|
||||
KeyframesFlexSizer->Add(textEndAfter,0,wxRIGHT|wxALIGN_CENTER,5);
|
||||
KeyframesFlexSizer->Add(keysEndAfter,0,wxRIGHT|wxEXPAND,0);
|
||||
|
||||
make_ctrl(this, KeyframesFlexSizer, _("Ends before thres.:"), &beforeEnd, keysEnable,
|
||||
_("Threshold for 'before end' distance, that is, how many frames a subtitle must end before a keyframe to snap to it."));
|
||||
|
||||
make_ctrl(this, KeyframesFlexSizer, _("Ends after thres.:"), &afterEnd, keysEnable,
|
||||
_("Threshold for 'after end' distance, that is, how many frames a subtitle must end after a keyframe to snap to it."));
|
||||
|
||||
KeyframesSizer->Add(KeyframesFlexSizer,0,wxEXPAND);
|
||||
KeyframesSizer->AddStretchSpacer(1);
|
||||
|
||||
// Button sizer
|
||||
wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer();
|
||||
ApplyButton = new wxButton(this,wxID_OK);
|
||||
ButtonSizer->AddButton(ApplyButton);
|
||||
ButtonSizer->AddButton(new wxButton(this,wxID_CANCEL));
|
||||
ButtonSizer->AddButton(new HelpButton(this,"Timing Processor"));
|
||||
ButtonSizer->Realize();
|
||||
wxStdDialogButtonSizer *ButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
|
||||
ApplyButton = ButtonSizer->GetAffirmativeButton();
|
||||
ButtonSizer->GetHelpButton()->Bind(wxEVT_COMMAND_BUTTON_CLICKED, bind(&HelpButton::OpenPage, "Timing Processor"));
|
||||
|
||||
// Right Sizer
|
||||
wxSizer *RightSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
@ -188,12 +219,8 @@ DialogTimingProcessor::DialogTimingProcessor(agi::Context *c)
|
|||
StyleButtonsSizer->Add(none,1,0,0);
|
||||
|
||||
// Left sizer
|
||||
size_t len = StyleList->GetCount();
|
||||
for (size_t i=0;i<len;i++) {
|
||||
StyleList->Check(i);
|
||||
}
|
||||
LeftSizer->Add(StyleList,1,wxBOTTOM|wxEXPAND,0);
|
||||
LeftSizer->Add(StyleButtonsSizer,0,wxEXPAND,0);
|
||||
LeftSizer->Add(StyleList, wxSizerFlags(1).Border(wxBOTTOM));
|
||||
LeftSizer->Add(StyleButtonsSizer, wxSizerFlags().Expand());
|
||||
|
||||
// Top Sizer
|
||||
wxSizer *TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
@ -203,92 +230,46 @@ DialogTimingProcessor::DialogTimingProcessor(agi::Context *c)
|
|||
// Main Sizer
|
||||
wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
MainSizer->Add(TopSizer,1,wxALL|wxEXPAND,5);
|
||||
MainSizer->SetSizeHints(this);
|
||||
SetSizer(MainSizer);
|
||||
|
||||
SetSizerAndFit(MainSizer);
|
||||
CenterOnParent();
|
||||
|
||||
Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, bind(&DialogTimingProcessor::UpdateControls, this));
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogTimingProcessor::OnApply, this, wxID_OK);
|
||||
all->Bind(wxEVT_COMMAND_BUTTON_CLICKED, bind(&DialogTimingProcessor::CheckAll, this, true));
|
||||
none->Bind(wxEVT_COMMAND_BUTTON_CLICKED, bind(&DialogTimingProcessor::CheckAll, this, false));
|
||||
|
||||
CheckAll(true);
|
||||
}
|
||||
|
||||
void DialogTimingProcessor::CheckAll(bool value) {
|
||||
size_t count = StyleList->GetCount();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
StyleList->Check(i, value);
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
/// @brief Update controls
|
||||
///
|
||||
void DialogTimingProcessor::UpdateControls() {
|
||||
// Boxes
|
||||
leadIn->Enable(hasLeadIn->IsChecked());
|
||||
leadOut->Enable(hasLeadOut->IsChecked());
|
||||
adjacentThres->Enable(adjsEnable->IsChecked());
|
||||
adjacentBias->Enable(adjsEnable->IsChecked());
|
||||
|
||||
// Keyframes are only available if timecodes are loaded
|
||||
bool keysAvailable = c->videoController->KeyFramesLoaded();
|
||||
bool enableKeys = keysEnable->IsChecked() && keysAvailable;
|
||||
keysStartBefore->Enable(enableKeys);
|
||||
keysStartAfter->Enable(enableKeys);
|
||||
keysEndBefore->Enable(enableKeys);
|
||||
keysEndAfter->Enable(enableKeys);
|
||||
if (!keysAvailable) {
|
||||
keysEnable->SetValue(false);
|
||||
keysEnable->Enable(false);
|
||||
}
|
||||
|
||||
// Apply button
|
||||
int checked = 0;
|
||||
// Only enable the OK button if it'll actually do something
|
||||
bool any_checked = false;
|
||||
size_t len = StyleList->GetCount();
|
||||
for (size_t i=0;i<len;i++) {
|
||||
if (StyleList->IsChecked(i)) checked++;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (StyleList->IsChecked(i)) {
|
||||
any_checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ApplyButton->Enable(checked && (hasLeadIn->IsChecked() || hasLeadOut->IsChecked() || keysEnable->IsChecked() || adjsEnable->IsChecked()));
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(DialogTimingProcessor,wxDialog)
|
||||
EVT_CHECKBOX(CHECK_ENABLE_LEADIN,DialogTimingProcessor::OnCheckBox)
|
||||
EVT_CHECKBOX(CHECK_ENABLE_LEADOUT,DialogTimingProcessor::OnCheckBox)
|
||||
EVT_CHECKBOX(CHECK_ENABLE_KEYFRAME,DialogTimingProcessor::OnCheckBox)
|
||||
EVT_CHECKBOX(CHECK_ENABLE_ADJASCENT,DialogTimingProcessor::OnCheckBox)
|
||||
EVT_CHECKLISTBOX(TIMING_STYLE_LIST,DialogTimingProcessor::OnCheckBox)
|
||||
EVT_BUTTON(wxID_OK,DialogTimingProcessor::OnApply)
|
||||
EVT_BUTTON(BUTTON_SELECT_ALL,DialogTimingProcessor::OnSelectAll)
|
||||
EVT_BUTTON(BUTTON_SELECT_NONE,DialogTimingProcessor::OnSelectNone)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
void DialogTimingProcessor::OnCheckBox(wxCommandEvent &) {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
void DialogTimingProcessor::OnSelectAll(wxCommandEvent &) {
|
||||
size_t len = StyleList->GetCount();
|
||||
for (size_t i=0;i<len;i++) {
|
||||
StyleList->Check(i);
|
||||
}
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
void DialogTimingProcessor::OnSelectNone(wxCommandEvent &) {
|
||||
size_t len = StyleList->GetCount();
|
||||
for (size_t i=0;i<len;i++) {
|
||||
StyleList->Check(i,false);
|
||||
}
|
||||
UpdateControls();
|
||||
ApplyButton->Enable(any_checked && (hasLeadIn->IsChecked() || hasLeadOut->IsChecked() || keysEnable->IsChecked() || adjsEnable->IsChecked()));
|
||||
}
|
||||
|
||||
void DialogTimingProcessor::OnApply(wxCommandEvent &) {
|
||||
// Save settings
|
||||
long temp = 0;
|
||||
leadIn->GetValue().ToLong(&temp);
|
||||
OPT_SET("Audio/Lead/IN")->SetInt(temp);
|
||||
leadOut->GetValue().ToLong(&temp);
|
||||
OPT_SET("Audio/Lead/OUT")->SetInt(temp);
|
||||
keysStartBefore->GetValue().ToLong(&temp);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key Start Before")->SetInt(temp);
|
||||
keysStartAfter->GetValue().ToLong(&temp);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key Start After")->SetInt(temp);
|
||||
keysEndBefore->GetValue().ToLong(&temp);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key End Before")->SetInt(temp);
|
||||
keysEndAfter->GetValue().ToLong(&temp);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key End After")->SetInt(temp);
|
||||
adjacentThres->GetValue().ToLong(&temp);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent")->SetInt(temp);
|
||||
OPT_SET("Audio/Lead/IN")->SetInt(leadIn);
|
||||
OPT_SET("Audio/Lead/OUT")->SetInt(leadOut);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key Start Before")->SetInt(beforeStart);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key Start After")->SetInt(afterStart);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key End Before")->SetInt(beforeEnd);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Key End After")->SetInt(afterEnd);
|
||||
OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent")->SetInt(adjDistance);
|
||||
OPT_SET("Tool/Timing Post Processor/Adjacent Bias")->SetDouble(adjacentBias->GetValue() / 100.0);
|
||||
OPT_SET("Tool/Timing Post Processor/Enable/Lead/IN")->SetBool(hasLeadIn->IsChecked());
|
||||
OPT_SET("Tool/Timing Post Processor/Enable/Lead/OUT")->SetBool(hasLeadOut->IsChecked());
|
||||
|
@ -296,174 +277,137 @@ void DialogTimingProcessor::OnApply(wxCommandEvent &) {
|
|||
OPT_SET("Tool/Timing Post Processor/Enable/Adjacent")->SetBool(adjsEnable->IsChecked());
|
||||
OPT_SET("Tool/Timing Post Processor/Only Selection")->SetBool(onlySelection->IsChecked());
|
||||
|
||||
// Check if rows are valid
|
||||
for (entryIter cur = c->ass->Line.begin(); cur != c->ass->Line.end(); ++cur) {
|
||||
if (AssDialogue *tempDiag = dynamic_cast<AssDialogue*>(*cur)) {
|
||||
if (tempDiag->Start > tempDiag->End) {
|
||||
wxMessageBox(
|
||||
wxString::Format(
|
||||
_("One of the lines in the file (%i) has negative duration. Aborting."),
|
||||
std::distance(c->ass->Line.begin(), cur)),
|
||||
_("Invalid script"),
|
||||
wxICON_ERROR|wxOK);
|
||||
EndModal(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process();
|
||||
EndModal(0);
|
||||
}
|
||||
|
||||
int DialogTimingProcessor::GetClosestKeyFrame(int frame) {
|
||||
std::vector<int>::iterator pos = lower_bound(KeyFrames.begin(), KeyFrames.end(), frame);
|
||||
if (distance(pos, KeyFrames.end()) < 2) return KeyFrames.back();
|
||||
return frame - *pos < *(pos + 1) - frame ? *pos : *(pos + 1);
|
||||
}
|
||||
|
||||
static bool bad_line(std::set<wxString> *styles, AssDialogue *d) {
|
||||
return !d || d->Comment || styles->find(d->Style) == styles->end();
|
||||
}
|
||||
|
||||
void DialogTimingProcessor::SortDialogues() {
|
||||
std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
|
||||
std::set<wxString> styles;
|
||||
for (size_t i = 0; i < StyleList->GetCount(); ++i) {
|
||||
if (StyleList->IsChecked(i)) {
|
||||
if (StyleList->IsChecked(i))
|
||||
styles.insert(StyleList->GetString(i));
|
||||
}
|
||||
|
||||
std::vector<AssDialogue*> sorted;
|
||||
sorted.reserve(c->ass->Line.size());
|
||||
|
||||
if (onlySelection->IsChecked()) {
|
||||
SelectionController<AssDialogue>::Selection sel = c->selectionController->GetSelectedSet();
|
||||
remove_copy_if(sel.begin(), sel.end(), back_inserter(sorted),
|
||||
bind(bad_line, &styles, _1));
|
||||
}
|
||||
else {
|
||||
transform(c->ass->Line.begin(), c->ass->Line.end(), back_inserter(sorted), cast<AssDialogue*>());
|
||||
sorted.erase(remove_if(sorted.begin(), sorted.end(), bind(bad_line, &styles, _1)), sorted.end());
|
||||
}
|
||||
|
||||
// Check if rows are valid
|
||||
for (size_t i = 0; i < sorted.size(); ++i) {
|
||||
if (sorted[i]->Start > sorted[i]->End) {
|
||||
wxMessageBox(
|
||||
wxString::Format(_("One of the lines in the file (%i) has negative duration. Aborting."), i),
|
||||
_("Invalid script"),
|
||||
wxICON_ERROR|wxOK);
|
||||
sorted.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Sorted.clear();
|
||||
Sorted.reserve(c->ass->Line.size());
|
||||
if (onlySelection->IsChecked()) {
|
||||
SelectionController<AssDialogue>::Selection sel = c->selectionController->GetSelectedSet();
|
||||
remove_copy_if(sel.begin(), sel.end(), back_inserter(Sorted),
|
||||
bind(bad_line, &styles, std::tr1::placeholders::_1));
|
||||
}
|
||||
else {
|
||||
std::vector<AssDialogue*> tmp(c->ass->Line.size());
|
||||
transform(c->ass->Line.begin(), c->ass->Line.end(), back_inserter(tmp), cast<AssDialogue*>());
|
||||
remove_copy_if(tmp.begin(), tmp.end(), back_inserter(Sorted),
|
||||
bind(bad_line, &styles, std::tr1::placeholders::_1));
|
||||
}
|
||||
sort(Sorted.begin(), Sorted.end(), AssFile::CompStart);
|
||||
sort(sorted.begin(), sorted.end(), AssFile::CompStart);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
static int get_closest_kf(std::vector<int> const& kf, int frame) {
|
||||
std::vector<int>::const_iterator pos = lower_bound(kf.begin(), kf.end(), frame);
|
||||
// Return last keyframe if this is on or after the last one
|
||||
if (distance(pos, kf.end()) < 2) return kf.back();
|
||||
// Check if this one or the one after is closer to the frame
|
||||
return frame - *pos < *(pos + 1) - frame ? *pos : *(pos + 1);
|
||||
}
|
||||
|
||||
template<class Iter, class Field>
|
||||
static int safe_time(Iter begin, Iter end, AssDialogue *comp, int initial, Field field, int const& (*cmp)(int const&, int const&)) {
|
||||
// Compare to every previous line (yay for O(n^2)!) to see if it's OK to add lead-in
|
||||
for (; begin != end; ++begin) {
|
||||
// If the line doesn't already collide with this line, extend it only
|
||||
// to the edge of the line
|
||||
if (!comp->CollidesWith(*begin))
|
||||
initial = cmp(initial, (*begin)->*field);
|
||||
}
|
||||
return initial;
|
||||
}
|
||||
|
||||
/// @brief Actually process subtitles
|
||||
///
|
||||
void DialogTimingProcessor::Process() {
|
||||
SortDialogues();
|
||||
int rows = Sorted.size();
|
||||
std::vector<AssDialogue*> sorted = SortDialogues();
|
||||
if (sorted.empty()) return;
|
||||
|
||||
// Options
|
||||
long inVal = 0;
|
||||
long outVal = 0;
|
||||
leadIn->GetValue().ToLong(&inVal);
|
||||
leadOut->GetValue().ToLong(&outVal);
|
||||
bool addIn = hasLeadIn->IsChecked() && inVal;
|
||||
bool addOut = hasLeadOut->IsChecked() && outVal;
|
||||
bool addIn = hasLeadIn->IsChecked() && leadIn;
|
||||
bool addOut = hasLeadOut->IsChecked() && leadOut;
|
||||
|
||||
// Add lead-in/out
|
||||
if (addIn || addOut) {
|
||||
for (int i=0;i<rows;i++) {
|
||||
AssDialogue *cur = Sorted[i];
|
||||
for (size_t i = 0; i < sorted.size(); ++i) {
|
||||
AssDialogue *cur = sorted[i];
|
||||
if (addIn)
|
||||
cur->Start = safe_time(sorted.begin() + i + 1, sorted.end(), cur, cur->Start - leadIn, &AssDialogue::End, &std::max<int>);
|
||||
|
||||
// Compare to every previous line (yay for O(n^2)!) to see if it's OK to add lead-in
|
||||
if (inVal) {
|
||||
int startLead = cur->Start - inVal;
|
||||
for (int j=0;j<i;j++) {
|
||||
AssDialogue *comp = Sorted[j];
|
||||
|
||||
// Check if they don't already collide (ignore it in that case)
|
||||
if (cur->CollidesWith(comp)) continue;
|
||||
|
||||
// Get comparison times
|
||||
startLead = std::max<int>(startLead, comp->End);
|
||||
}
|
||||
cur->Start = startLead;
|
||||
}
|
||||
|
||||
// Compare to every line to see how far can lead-out be extended
|
||||
if (outVal) {
|
||||
int endLead = cur->End + outVal;
|
||||
for (int j=i+1;j<rows;j++) {
|
||||
AssDialogue *comp = Sorted[j];
|
||||
|
||||
// Check if they don't already collide (ignore it in that case)
|
||||
if (cur->CollidesWith(comp)) continue;
|
||||
|
||||
// Get comparison times
|
||||
endLead = std::min<int>(endLead, comp->Start);
|
||||
}
|
||||
cur->End = endLead;
|
||||
}
|
||||
if (addOut)
|
||||
cur->End = safe_time(sorted.rend() - i, sorted.rend(), cur, cur->End + leadOut, &AssDialogue::Start, &std::min<int>);
|
||||
}
|
||||
}
|
||||
|
||||
// Make adjacent
|
||||
if (adjsEnable->IsChecked()) {
|
||||
AssDialogue *prev = Sorted.front();
|
||||
|
||||
long adjsThres = 0;
|
||||
adjacentThres->GetValue().ToLong(&adjsThres);
|
||||
double bias = adjacentBias->GetValue() / 100.0;
|
||||
|
||||
float bias = adjacentBias->GetValue() / 100.0;
|
||||
|
||||
for (int i=1; i < rows;i++) {
|
||||
AssDialogue *cur = Sorted[i];
|
||||
for (size_t i = 1; i < sorted.size(); ++i) {
|
||||
AssDialogue *prev = sorted[i - 1];
|
||||
AssDialogue *cur = sorted[i];
|
||||
|
||||
// Check if they don't collide
|
||||
if (cur->CollidesWith(prev)) continue;
|
||||
|
||||
// Compare distance
|
||||
int dist = cur->Start - prev->End;
|
||||
if (dist > 0 && dist <= adjsThres) {
|
||||
int setPos = prev->End + int(dist*bias);
|
||||
if (dist > 0 && dist <= adjDistance) {
|
||||
int setPos = prev->End + int(dist * bias);
|
||||
cur->Start = setPos;
|
||||
prev->End = setPos;
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// Keyframe snapping
|
||||
if (keysEnable->IsChecked()) {
|
||||
KeyFrames = c->videoController->GetKeyFrames();
|
||||
std::vector<int> kf = c->videoController->GetKeyFrames();
|
||||
if (c->videoController->IsLoaded())
|
||||
KeyFrames.push_back(c->videoController->GetLength() - 1);
|
||||
kf.push_back(c->videoController->GetLength() - 1);
|
||||
|
||||
long beforeStart = 0;
|
||||
long afterStart = 0;
|
||||
long beforeEnd = 0;
|
||||
long afterEnd = 0;
|
||||
keysStartBefore->GetValue().ToLong(&beforeStart);
|
||||
keysStartAfter->GetValue().ToLong(&afterStart);
|
||||
keysEndBefore->GetValue().ToLong(&beforeEnd);
|
||||
keysEndAfter->GetValue().ToLong(&afterEnd);
|
||||
|
||||
for (int i=0;i<rows;i++) {
|
||||
AssDialogue *cur = Sorted[i];
|
||||
for (size_t i = 0; i < sorted.size(); ++i) {
|
||||
AssDialogue *cur = sorted[i];
|
||||
|
||||
// Get start/end frames
|
||||
int startF = c->videoController->FrameAtTime(cur->Start,agi::vfr::START);
|
||||
int endF = c->videoController->FrameAtTime(cur->End,agi::vfr::END);
|
||||
int startF = c->videoController->FrameAtTime(cur->Start, agi::vfr::START);
|
||||
int endF = c->videoController->FrameAtTime(cur->End, agi::vfr::END);
|
||||
|
||||
// Get closest for start
|
||||
int closest = GetClosestKeyFrame(startF);
|
||||
int closest = get_closest_kf(kf, startF);
|
||||
if ((closest > startF && closest-startF <= beforeStart) || (closest < startF && startF-closest <= afterStart)) {
|
||||
cur->Start = c->videoController->TimeAtFrame(closest,agi::vfr::START);
|
||||
cur->Start = c->videoController->TimeAtFrame(closest, agi::vfr::START);
|
||||
}
|
||||
|
||||
// Get closest for end
|
||||
closest = GetClosestKeyFrame(endF)-1;
|
||||
closest = get_closest_kf(kf, endF) - 1;
|
||||
if ((closest > endF && closest-endF <= beforeEnd) || (closest < endF && endF-closest <= afterEnd)) {
|
||||
cur->End = c->videoController->TimeAtFrame(closest,agi::vfr::END);
|
||||
cur->End = c->videoController->TimeAtFrame(closest, agi::vfr::END);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update grid
|
||||
c->ass->Commit(_("timing processor"), AssFile::COMMIT_DIAG_TIME);
|
||||
}
|
||||
|
|
|
@ -37,92 +37,54 @@
|
|||
#ifndef AGI_PRE
|
||||
#include <vector>
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/checklst.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/textctrl.h>
|
||||
#endif
|
||||
|
||||
namespace agi { struct Context; }
|
||||
class AssDialogue;
|
||||
class wxButton;
|
||||
class wxCheckBox;
|
||||
class wxCheckListBox;
|
||||
class wxSlider;
|
||||
|
||||
/// DOCME
|
||||
/// @class DialogTimingProcessor
|
||||
/// @brief DOCME
|
||||
///
|
||||
/// DOCME
|
||||
/// @brief Automatic postprocessor for correcting common timing issues
|
||||
class DialogTimingProcessor : public wxDialog {
|
||||
agi::Context *c;
|
||||
agi::Context *c; ///< Project context
|
||||
|
||||
/// DOCME
|
||||
wxStaticBoxSizer *KeyframesSizer;
|
||||
int leadIn; ///< Lead-in to add in milliseconds
|
||||
int leadOut; ///< Lead-out to add in milliseconds
|
||||
int beforeStart; ///< Maximum time in milliseconds to move start time of line backwards to land on a keyframe
|
||||
int afterStart; ///< Maximum time in milliseconds to move start time of line forwards to land on a keyframe
|
||||
int beforeEnd; ///< Maximum time in milliseconds to move end time of line backwards to land on a keyframe
|
||||
int afterEnd; ///< Maximum time in milliseconds to move end time of line forwards to land on a keyframe
|
||||
int adjDistance; ///< Maximum time in milliseconds to snap adjacent lines to each other
|
||||
|
||||
/// DOCME
|
||||
wxCheckBox *onlySelection;
|
||||
wxCheckBox *onlySelection; ///< Only process selected lines of the selected styles
|
||||
wxCheckBox *hasLeadIn; ///< Enable adding lead-in
|
||||
wxCheckBox *hasLeadOut; ///< Enable adding lead-out
|
||||
wxCheckBox *keysEnable; ///< Enable snapping to keyframes
|
||||
wxCheckBox *adjsEnable; ///< Enable snapping adjacent lines to each other
|
||||
wxSlider *adjacentBias; ///< Bias between shifting start and end times when snapping adjacent lines
|
||||
wxCheckListBox *StyleList; ///< List of styles to process
|
||||
wxButton *ApplyButton; ///< Button to apply the processing
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *leadIn;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *leadOut;
|
||||
|
||||
/// DOCME
|
||||
wxCheckBox *hasLeadIn;
|
||||
|
||||
/// DOCME
|
||||
wxCheckBox *hasLeadOut;
|
||||
|
||||
/// DOCME
|
||||
wxCheckBox *keysEnable;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *keysStartBefore;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *keysStartAfter;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *keysEndBefore;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *keysEndAfter;
|
||||
|
||||
/// DOCME
|
||||
wxCheckBox *adjsEnable;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *adjacentThres;
|
||||
|
||||
/// DOCME
|
||||
wxSlider *adjacentBias;
|
||||
|
||||
/// DOCME
|
||||
wxCheckListBox *StyleList;
|
||||
|
||||
/// DOCME
|
||||
wxButton *ApplyButton;
|
||||
|
||||
/// DOCME
|
||||
std::vector<int> KeyFrames;
|
||||
|
||||
void OnCheckBox(wxCommandEvent &event);
|
||||
void OnSelectAll(wxCommandEvent &event);
|
||||
void OnSelectNone(wxCommandEvent &event);
|
||||
void OnApply(wxCommandEvent &event);
|
||||
|
||||
void UpdateControls();
|
||||
void Process();
|
||||
int GetClosestKeyFrame(int frame);
|
||||
/// Check or uncheck all styles
|
||||
void CheckAll(bool check);
|
||||
|
||||
/// DOCME
|
||||
std::vector<AssDialogue*> Sorted;
|
||||
void SortDialogues();
|
||||
/// Enable and disable text boxes based on which checkboxes are checked
|
||||
void UpdateControls();
|
||||
|
||||
/// Process the file
|
||||
void Process();
|
||||
|
||||
/// Get a list of dialogue lines in the file sorted by start time
|
||||
std::vector<AssDialogue*> SortDialogues();
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param c Project context
|
||||
DialogTimingProcessor(agi::Context *c);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
|
|
@ -65,6 +65,13 @@ NumValidator::NumValidator(int val, bool issigned)
|
|||
{
|
||||
}
|
||||
|
||||
NumValidator::NumValidator(int64_t val, bool issigned)
|
||||
: iValue((int)val)
|
||||
, isFloat(false)
|
||||
, isSigned(issigned)
|
||||
{
|
||||
}
|
||||
|
||||
NumValidator::NumValidator(double val, bool issigned)
|
||||
: fValue(val)
|
||||
, isFloat(true)
|
||||
|
|
|
@ -82,6 +82,11 @@ public:
|
|||
/// @param issigned Allow negative numbers?
|
||||
explicit NumValidator(int val, bool issigned=false);
|
||||
|
||||
/// Constructor
|
||||
/// @param val Initial value to set the associated control to
|
||||
/// @param issigned Allow negative numbers?
|
||||
explicit NumValidator(int64_t val, bool issigned=false);
|
||||
|
||||
/// Constructor
|
||||
/// @param val Initial value to set the associated control to
|
||||
/// @param issigned Allow negative numbers?
|
||||
|
|
Loading…
Reference in New Issue