forked from mia/Aegisub
Rewrite the shift times dialog, detangling it from the grid and making it modeless, and add support for characters outside the local charset in the shift history file path. Closes #1269.
Originally committed to SVN as r5596.
This commit is contained in:
parent
1f095b0a01
commit
0c4eb020a4
6 changed files with 302 additions and 422 deletions
|
@ -161,7 +161,7 @@ struct time_shift : public Command {
|
|||
|
||||
void operator()(agi::Context *c) {
|
||||
c->videoController->Stop();
|
||||
DialogShiftTimes(c).ShowModal();
|
||||
(new DialogShiftTimes(c))->Show();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,29 +1,16 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * 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.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
//
|
||||
|
@ -36,10 +23,11 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "dialog_shift_times.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/filefn.h>
|
||||
#include <wx/filename.h>
|
||||
|
@ -49,357 +37,303 @@
|
|||
#include <wx/textctrl.h>
|
||||
#endif
|
||||
|
||||
#include <libaegisub/access.h>
|
||||
#include <libaegisub/io.h>
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_time.h"
|
||||
#include "charset_conv.h"
|
||||
#include "dialog_shift_times.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "help_button.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subs_grid.h"
|
||||
#include "timeedit_ctrl.h"
|
||||
#include "utils.h"
|
||||
#include "video_context.h"
|
||||
|
||||
// IDs
|
||||
enum {
|
||||
TEXT_SHIFT_TIME = 1100,
|
||||
TEXT_SHIFT_FRAME,
|
||||
RADIO_BACKWARD,
|
||||
RADIO_FORWARD,
|
||||
RADIO_TIME,
|
||||
RADIO_FRAME,
|
||||
SHIFT_CLEAR_HISTORY
|
||||
};
|
||||
|
||||
DialogShiftTimes::DialogShiftTimes (agi::Context *context)
|
||||
: wxDialog(context->parent, -1, _("Shift Times"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("JumpTo"))
|
||||
DialogShiftTimes::DialogShiftTimes(agi::Context *context)
|
||||
: wxDialog(context->parent, -1, _("Shift Times"))
|
||||
, context(context)
|
||||
, history_filename(STD_STR(StandardPaths::DecodePath("?user/shift_history.txt")))
|
||||
, timecodes_loaded_slot(context->videoController->AddTimecodesListener(&DialogShiftTimes::OnTimecodesLoaded, this))
|
||||
{
|
||||
// Set icon
|
||||
SetIcon(BitmapToIcon(GETIMAGE(shift_times_toolbutton_24)));
|
||||
|
||||
// Set initial values
|
||||
ready = true;
|
||||
shiftframe = 0;
|
||||
// Create controls
|
||||
shift_by_time = new wxRadioButton(this, -1, _("Time: "), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
|
||||
shift_by_time->SetToolTip(_("Shift by time"));
|
||||
shift_by_time->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &DialogShiftTimes::OnByTime, this);
|
||||
|
||||
// Static-box sizers before anything else
|
||||
wxSizer *TimesSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Shift by"));
|
||||
wxSizer *HistorySizer = new wxStaticBoxSizer(wxVERTICAL,this,_("History"));
|
||||
shift_by_frames = new wxRadioButton(this, -1 , _("Frames: "));
|
||||
shift_by_frames->SetToolTip(_("Shift by frames"));
|
||||
shift_by_frames->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &DialogShiftTimes::OnByFrames, this);
|
||||
|
||||
// Times
|
||||
RadioTime = new wxRadioButton(this,RADIO_TIME,_("Time: "),wxDefaultPosition,wxDefaultSize, wxRB_GROUP);
|
||||
RadioFrames = new wxRadioButton(this,RADIO_FRAME,_("Frames: "),wxDefaultPosition,wxDefaultSize);
|
||||
ShiftTime = new TimeEdit(this,TEXT_SHIFT_TIME,_T(""),wxDefaultPosition,wxDefaultSize);
|
||||
ShiftFrame = new wxTextCtrl(this,TEXT_SHIFT_FRAME,wxString::Format(_T("%i"),shiftframe),wxDefaultPosition,wxDefaultSize);
|
||||
ShiftTime->SetToolTip(_("Enter time in h:mm:ss.cs notation"));
|
||||
RadioTime->SetToolTip(_("Shift by time"));
|
||||
ShiftFrame->Disable();
|
||||
if (!context->videoController->TimecodesLoaded()) RadioFrames->Disable();
|
||||
else {
|
||||
ShiftFrame->SetToolTip(_("Enter number of frames to shift by"));
|
||||
RadioFrames->SetToolTip(_("Shift by frames"));
|
||||
}
|
||||
wxSizer *TimeFrameSizer = new wxFlexGridSizer(2,2,5,5);
|
||||
TimeFrameSizer->Add(RadioTime,0,wxALIGN_CENTER_VERTICAL,0);
|
||||
TimeFrameSizer->Add(ShiftTime,1);
|
||||
TimeFrameSizer->Add(RadioFrames,0,wxALIGN_CENTER_VERTICAL,0);
|
||||
TimeFrameSizer->Add(ShiftFrame,1);
|
||||
shift_time = new TimeEdit(this, -1);
|
||||
shift_time->SetToolTip(_("Enter time in h:mm:ss.cs notation"));
|
||||
|
||||
// Direction
|
||||
DirectionForward = new wxRadioButton(this,-1,_("Forward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
|
||||
DirectionBackward = new wxRadioButton(this,-1,_("Backward"), wxDefaultPosition, wxDefaultSize);
|
||||
DirectionForward->SetToolTip(_("Shifts subs forward, making them appear later. Use if they are appearing too soon."));
|
||||
DirectionBackward->SetToolTip(_("Shifts subs backward, making them appear earlier. Use if they are appearing too late."));
|
||||
wxSizer *DirectionSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
DirectionSizer->Add(DirectionForward,1,wxEXPAND,0);
|
||||
DirectionSizer->Add(DirectionBackward,1,wxLEFT | wxEXPAND,5);
|
||||
TimesSizer->Add(TimeFrameSizer,0,wxEXPAND,0);
|
||||
TimesSizer->Add(DirectionSizer,0,wxEXPAND | wxTOP,5);
|
||||
shift_frames = new wxTextCtrl(this, -1);
|
||||
shift_frames->SetToolTip(_("Enter number of frames to shift by"));
|
||||
|
||||
// Selection
|
||||
wxString SelChoices[3] = { _("All rows"), _("Selected rows"), _("Selection onward") };
|
||||
SelChoice = new wxRadioBox(this,-1,_("Affect"), wxDefaultPosition, wxDefaultSize, 3, SelChoices, 3, wxRA_SPECIFY_ROWS);
|
||||
shift_forward = new wxRadioButton(this, -1, _("Forward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
|
||||
shift_forward->SetToolTip(_("Shifts subs forward, making them appear later. Use if they are appearing too soon."));
|
||||
|
||||
// Times
|
||||
wxString TimesChoices[3] = { _("Start and End times"), _("Start times only"), _("End times only") };
|
||||
TimesChoice = new wxRadioBox(this,-1,_("Times"), wxDefaultPosition, wxDefaultSize, 3, TimesChoices, 3, wxRA_SPECIFY_ROWS);
|
||||
shift_backward = new wxRadioButton(this, -1, _("Backward"));
|
||||
shift_backward->SetToolTip(_("Shifts subs backward, making them appear earlier. Use if they are appearing too late."));
|
||||
|
||||
// History
|
||||
History = new wxListBox(this,-1,wxDefaultPosition,wxSize(350,100), 0, NULL, wxLB_HSCROLL);
|
||||
wxButton *ClearButton = new wxButton(this,SHIFT_CLEAR_HISTORY,_("Clear"));
|
||||
HistorySizer->Add(History,1,wxEXPAND,0);
|
||||
HistorySizer->Add(ClearButton,0,wxEXPAND,0);
|
||||
wxString selection_mode_vals[] = { _("All rows"), _("Selected rows"), _("Selection onward") };
|
||||
selection_mode = new wxRadioBox(this, -1, _("Affect"), wxDefaultPosition, wxDefaultSize, 3, selection_mode_vals, 1);
|
||||
|
||||
// Buttons
|
||||
wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer();
|
||||
ButtonSizer->AddButton(new wxButton(this,wxID_OK));
|
||||
ButtonSizer->AddButton(new wxButton(this,wxID_CANCEL));
|
||||
ButtonSizer->AddButton(new HelpButton(this,_T("Shift Times")));
|
||||
ButtonSizer->Realize();
|
||||
wxString time_field_vals[] = { _("Start and End times"), _("Start times only"), _("End times only") };
|
||||
time_fields = new wxRadioBox(this, -1, _("Times"), wxDefaultPosition, wxDefaultSize, 3, time_field_vals, 1);
|
||||
|
||||
// General layout
|
||||
wxSizer *LeftSizer = new wxBoxSizer(wxVERTICAL);
|
||||
wxSizer *RightSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxSizer *TopSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
LeftSizer->Add(TimesSizer,0,wxEXPAND | wxBOTTOM,5);
|
||||
LeftSizer->Add(SelChoice,0,wxEXPAND | wxBOTTOM,5);
|
||||
LeftSizer->Add(TimesChoice,0,wxEXPAND,5);
|
||||
RightSizer->Add(HistorySizer,0,wxEXPAND,0);
|
||||
TopSizer->Add(LeftSizer,0,wxEXPAND | wxRIGHT,5);
|
||||
TopSizer->Add(RightSizer,0,wxEXPAND,0);
|
||||
MainSizer->Add(TopSizer,0,wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT,5);
|
||||
MainSizer->Add(ButtonSizer,0,wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT,5);
|
||||
history = new wxListBox(this, -1, wxDefaultPosition, wxSize(350, 100), 0, NULL, wxLB_HSCROLL);
|
||||
|
||||
// Set sizer
|
||||
SetSizer(MainSizer);
|
||||
MainSizer->SetSizeHints(this);
|
||||
wxButton *clear_button = new wxButton(this, -1, _("Clear"));
|
||||
clear_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::OnClear, this);
|
||||
|
||||
// Set initial control states
|
||||
OnTimecodesLoaded(context->videoController->FPS());
|
||||
OnSelectedSetChanged(Selection(), Selection());
|
||||
LoadHistory();
|
||||
|
||||
shift_time->SetTime(OPT_GET("Tool/Shift Times/Time")->GetInt());
|
||||
*shift_frames << (int)OPT_GET("Tool/Shift Times/Frames")->GetInt();
|
||||
shift_frames->Disable();
|
||||
shift_by_frames->SetValue(!OPT_GET("Tool/Shift Times/ByTime")->GetBool() && shift_by_frames->IsEnabled());
|
||||
time_fields->SetSelection(OPT_GET("Tool/Shift Times/Type")->GetInt());
|
||||
selection_mode->SetSelection(OPT_GET("Tool/Shift Times/Affect")->GetInt());
|
||||
shift_backward->SetValue(OPT_GET("Tool/Shift Times/Direction")->GetBool());
|
||||
|
||||
// Position controls
|
||||
wxSizer *shift_amount_sizer = new wxFlexGridSizer(2, 2, 5, 5);
|
||||
shift_amount_sizer->Add(shift_by_time, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
|
||||
shift_amount_sizer->Add(shift_time, wxSizerFlags(1));
|
||||
shift_amount_sizer->Add(shift_by_frames, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
|
||||
shift_amount_sizer->Add(shift_frames, wxSizerFlags(1));
|
||||
|
||||
wxSizer *shift_direction_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
shift_direction_sizer->Add(shift_forward, wxSizerFlags(1).Expand());
|
||||
shift_direction_sizer->Add(shift_backward, wxSizerFlags(1).Expand().Border(wxLEFT));
|
||||
|
||||
wxSizer *shift_by_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Shift by"));
|
||||
shift_by_sizer->Add(shift_amount_sizer, wxSizerFlags().Expand());
|
||||
shift_by_sizer->Add(shift_direction_sizer, wxSizerFlags().Expand().Border(wxTOP));
|
||||
|
||||
wxSizer *left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
left_sizer->Add(shift_by_sizer, wxSizerFlags().Expand().Border(wxBOTTOM));
|
||||
left_sizer->Add(selection_mode, wxSizerFlags().Expand().Border(wxBOTTOM));
|
||||
left_sizer->Add(time_fields, wxSizerFlags().Expand());
|
||||
|
||||
wxSizer *history_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("History"));
|
||||
history_sizer->Add(history, wxSizerFlags(1).Expand());
|
||||
history_sizer->Add(clear_button, wxSizerFlags().Expand().Border(wxTOP));
|
||||
|
||||
wxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
top_sizer->Add(left_sizer, wxSizerFlags().Border(wxALL & ~wxRIGHT).Expand());
|
||||
top_sizer->Add(history_sizer, wxSizerFlags().Border().Expand());
|
||||
|
||||
wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(top_sizer, wxSizerFlags().Border(wxALL & ~wxBOTTOM));
|
||||
main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Right().Border());
|
||||
SetSizerAndFit(main_sizer);
|
||||
CenterOnParent();
|
||||
|
||||
// Load values from options
|
||||
if (!OPT_GET("Tool/Shift Times/ByTime")->GetBool()) {
|
||||
if (RadioFrames->IsEnabled()) {
|
||||
RadioFrames->SetValue(true);
|
||||
ShiftFrame->Enable(true);
|
||||
ShiftTime->Enable(false);
|
||||
ShiftFrame->SetValue(AegiIntegerToString(OPT_GET("Tool/Shift Times/Length")->GetInt()));
|
||||
}
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::Process, this, wxID_OK);
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::OnClose, this, wxID_CANCEL);
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::tr1::bind(&HelpButton::OpenPage, "Shift Times"), wxID_HELP);
|
||||
context->selectionController->AddSelectionListener(this);
|
||||
}
|
||||
|
||||
DialogShiftTimes::~DialogShiftTimes() {
|
||||
context->selectionController->RemoveSelectionListener(this);
|
||||
}
|
||||
|
||||
void DialogShiftTimes::OnTimecodesLoaded(agi::vfr::Framerate const& new_fps) {
|
||||
fps = new_fps;
|
||||
if (fps.IsLoaded()) {
|
||||
shift_by_frames->Enable();
|
||||
}
|
||||
else {
|
||||
ShiftTime->SetTime(OPT_GET("Tool/Shift Times/Length")->GetInt());
|
||||
shift_by_time->SetValue(true);
|
||||
shift_by_frames->Disable();
|
||||
shift_time->Enable();
|
||||
shift_frames->Disable();
|
||||
}
|
||||
TimesChoice->SetSelection(OPT_GET("Tool/Shift Times/Type")->GetInt());
|
||||
SelChoice->SetSelection(OPT_GET("Tool/Shift Times/Affect")->GetInt());
|
||||
if (OPT_GET("Tool/Shift Times/Direction")->GetBool()) DirectionBackward->SetValue(true);
|
||||
}
|
||||
|
||||
// Has selection?
|
||||
wxArrayInt sel = context->subsGrid->GetSelection();
|
||||
if (sel.Count() == 0) {
|
||||
SelChoice->Enable(1,false);
|
||||
SelChoice->Enable(2,false);
|
||||
SelChoice->SetSelection(0);
|
||||
void DialogShiftTimes::OnSelectedSetChanged(Selection const&, Selection const&) {
|
||||
if (context->selectionController->GetSelectedSet().empty()) {
|
||||
selection_mode->Enable(1, false);
|
||||
selection_mode->Enable(2, false);
|
||||
selection_mode->SetSelection(0);
|
||||
}
|
||||
else {
|
||||
selection_mode->Enable(1, true);
|
||||
selection_mode->Enable(2, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DialogShiftTimes::OnClear(wxCommandEvent &) {
|
||||
wxRemoveFile(lagi_wxString(history_filename));
|
||||
history->Clear();
|
||||
}
|
||||
|
||||
void DialogShiftTimes::OnClose(wxCommandEvent &) {
|
||||
long shift;
|
||||
shift_frames->GetValue().ToLong(&shift);
|
||||
|
||||
OPT_SET("Tool/Shift Times/Time")->SetInt(shift_time->time.GetMS());
|
||||
OPT_SET("Tool/Shift Times/Frames")->SetInt(shift);
|
||||
OPT_SET("Tool/Shift Times/ByTime")->SetBool(shift_by_time->GetValue());
|
||||
OPT_SET("Tool/Shift Times/Type")->SetInt(time_fields->GetSelection());
|
||||
OPT_SET("Tool/Shift Times/Affect")->SetInt(selection_mode->GetSelection());
|
||||
OPT_SET("Tool/Shift Times/Direction")->SetBool(shift_backward->GetValue());
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void DialogShiftTimes::OnByTime(wxCommandEvent &) {
|
||||
shift_time->Enable(true);
|
||||
shift_frames->Enable(false);
|
||||
}
|
||||
|
||||
void DialogShiftTimes::OnByFrames(wxCommandEvent &) {
|
||||
shift_time->Enable(false);
|
||||
shift_frames->Enable(true);
|
||||
}
|
||||
|
||||
void DialogShiftTimes::SaveHistory(std::vector<std::pair<int, int> > const& shifted_blocks) {
|
||||
wxString filename = wxFileName(context->ass->filename).GetFullName();
|
||||
int fields = time_fields->GetSelection();
|
||||
|
||||
wxString new_line = wxString::Format("%s, %s %s, %s, ",
|
||||
filename.empty() ? _("unsaved") : filename,
|
||||
shift_by_time->GetValue() ? shift_time->GetValue() : shift_frames->GetValue() + _(" frames"),
|
||||
shift_backward->GetValue() ? _("backward") : _("forward"),
|
||||
fields == 0 ? _("s+e") : fields == 1 ? _("s") : _("e"));
|
||||
|
||||
int sel_mode = selection_mode->GetSelection();
|
||||
if (sel_mode == 0)
|
||||
new_line += _("all");
|
||||
else if (sel_mode == 2)
|
||||
new_line += wxString::Format(_("from %d onward"), shifted_blocks.front().first);
|
||||
else {
|
||||
new_line += _("sel ");
|
||||
for (size_t i = 0; i < shifted_blocks.size(); ++i) {
|
||||
std::pair<int, int> const& b = shifted_blocks[i];
|
||||
wxString term = i == shifted_blocks.size() - 1 ? "" : ";";
|
||||
if (b.first == b.second)
|
||||
new_line += wxString::Format("%d%s", b.first, term);
|
||||
else
|
||||
new_line += wxString::Format("%d-%d%s", b.first, b.second, term);
|
||||
}
|
||||
}
|
||||
|
||||
// Load history
|
||||
LoadHistory(StandardPaths::DecodePath(_T("?user/shift_history.txt")));
|
||||
try {
|
||||
agi::io::Save file(history_filename);
|
||||
|
||||
for (size_t i = 0; i < history->GetCount(); ++i)
|
||||
file.Get() << history->GetString(i).utf8_str() << std::endl;
|
||||
file.Get() << new_line.utf8_str() << std::endl;
|
||||
}
|
||||
catch (agi::acs::AcsError const& e) {
|
||||
LOG_E("dialog_shift_times/save_history") << "Cannot save shift times history: " << e.GetChainedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogShiftTimes::LoadHistory() {
|
||||
history->Clear();
|
||||
history->Freeze();
|
||||
|
||||
///////////////
|
||||
// Event table
|
||||
BEGIN_EVENT_TABLE(DialogShiftTimes, wxDialog)
|
||||
EVT_BUTTON(wxID_CANCEL,DialogShiftTimes::OnClose)
|
||||
EVT_BUTTON(wxID_OK,DialogShiftTimes::OnOK)
|
||||
EVT_BUTTON(SHIFT_CLEAR_HISTORY,DialogShiftTimes::OnClear)
|
||||
EVT_RADIOBUTTON(RADIO_TIME,DialogShiftTimes::OnRadioTime)
|
||||
EVT_RADIOBUTTON(RADIO_FRAME,DialogShiftTimes::OnRadioFrame)
|
||||
END_EVENT_TABLE()
|
||||
try {
|
||||
agi::scoped_ptr<std::istream> file(agi::io::Open(history_filename));
|
||||
std::string buffer;
|
||||
while(!file->eof()) {
|
||||
getline(*file, buffer);
|
||||
if (buffer.size())
|
||||
history->Insert(lagi_wxString(buffer), 0);
|
||||
}
|
||||
}
|
||||
catch (agi::acs::AcsError const& e) {
|
||||
LOG_E("dialog_shift_times/save_history") << "Cannot load shift times history: " << e.GetChainedMessage();
|
||||
}
|
||||
catch (...) {
|
||||
history->Thaw();
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Clear History
|
||||
/// @param event
|
||||
///
|
||||
void DialogShiftTimes::OnClear(wxCommandEvent &event) {
|
||||
wxRemoveFile(StandardPaths::DecodePath(_T("?user/shift_history.txt")));
|
||||
History->Clear();
|
||||
history->Thaw();
|
||||
}
|
||||
|
||||
void DialogShiftTimes::Process(wxCommandEvent &) {
|
||||
int mode = selection_mode->GetSelection();
|
||||
int type = time_fields->GetSelection();
|
||||
bool reverse = shift_backward->GetValue();
|
||||
bool by_time = shift_by_time->GetValue();
|
||||
|
||||
/// @brief Cancel
|
||||
/// @param event
|
||||
///
|
||||
void DialogShiftTimes::OnClose(wxCommandEvent &event) {
|
||||
EndModal(0);
|
||||
}
|
||||
bool start = type != 2;
|
||||
bool end = type != 1;
|
||||
|
||||
Selection sel = context->selectionController->GetSelectedSet();
|
||||
|
||||
|
||||
/// @brief Apply
|
||||
/// @param event
|
||||
/// @return
|
||||
///
|
||||
void DialogShiftTimes::OnOK(wxCommandEvent &event) {
|
||||
// General values
|
||||
int type = TimesChoice->GetSelection();
|
||||
int affect = SelChoice->GetSelection();
|
||||
bool allrows = affect == 0;
|
||||
bool selOnward = affect == 2;
|
||||
long len;
|
||||
bool byTime = RadioTime->GetValue();
|
||||
bool backward = DirectionBackward->GetValue();
|
||||
bool didSomething = false;
|
||||
|
||||
// Selection
|
||||
int nrows = context->subsGrid->GetRows();
|
||||
wxArrayInt sel = context->subsGrid->GetSelection();
|
||||
int firstSel = 0;
|
||||
if (sel.Count()) firstSel = sel[0];
|
||||
|
||||
// Get length
|
||||
if (byTime) len = ShiftTime->time.GetMS();
|
||||
else ShiftFrame->GetValue().ToLong(&len);
|
||||
|
||||
if (byTime && len == 0) {
|
||||
// Shift zero milliseconds in time mode
|
||||
// Equivalent to doing nothing at all, so just dismiss
|
||||
EndModal(0);
|
||||
long shift;
|
||||
if (by_time) {
|
||||
shift = shift_time->time.GetMS();
|
||||
if (shift == 0) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// If backwards, invert
|
||||
if (backward) len = -len;
|
||||
|
||||
// Shift
|
||||
for (int i=0;i<nrows;i++) {
|
||||
if (allrows || (i >= firstSel && selOnward) || context->subsGrid->IsInSelection(i)) {
|
||||
if (byTime) context->subsGrid->ShiftLineByTime(i,len,type);
|
||||
else context->subsGrid->ShiftLineByFrames(i,len,type);
|
||||
didSomething = true;
|
||||
}
|
||||
else
|
||||
shift_frames->GetValue().ToLong(&shift);
|
||||
|
||||
if (reverse)
|
||||
shift = -shift;
|
||||
|
||||
// Track which rows were shifted for the log
|
||||
int row_number = 0;
|
||||
int block_start = 0;
|
||||
std::vector<std::pair<int, int> > shifted_blocks;
|
||||
|
||||
for (entryIter it = context->ass->Line.begin(); it != context->ass->Line.end(); ++it) {
|
||||
AssDialogue *line = dynamic_cast<AssDialogue*>(*it);
|
||||
if (!line) continue;
|
||||
++row_number;
|
||||
|
||||
if (!sel.count(line)) {
|
||||
if (block_start) {
|
||||
shifted_blocks.push_back(std::make_pair(block_start, row_number - 1));
|
||||
block_start = 0;
|
||||
}
|
||||
if (mode == 1) continue;
|
||||
if (mode == 2 && shifted_blocks.empty()) continue;
|
||||
}
|
||||
else if (!block_start)
|
||||
block_start = row_number;
|
||||
|
||||
if (start)
|
||||
line->Start.SetMS(Shift(line->Start.GetMS(), shift, by_time, agi::vfr::START));
|
||||
if (end)
|
||||
line->End.SetMS(Shift(line->End.GetMS(), shift, by_time, agi::vfr::END));
|
||||
}
|
||||
|
||||
// Add entry to history
|
||||
if (didSomething) {
|
||||
if (backward) len = -len;
|
||||
wxString message;
|
||||
wxFileName assfile(context->ass->filename);
|
||||
wxString filename = assfile.GetFullName();
|
||||
|
||||
// File
|
||||
if (filename.IsEmpty()) message << _("unsaved, ");
|
||||
else message << filename << _T(", ");
|
||||
|
||||
// Time/frames
|
||||
if (byTime) message << ShiftTime->GetValue() << _T(" ");
|
||||
else message << len << _(" frames ");
|
||||
|
||||
// Forward/backwards
|
||||
if (backward) message << _("backward, ");
|
||||
else message << _("forward, ");
|
||||
|
||||
// Start/end
|
||||
if (type == 0) message << _("s+e, ");
|
||||
if (type == 1) message << _("s, ");
|
||||
if (type == 2) message << _("e, ");
|
||||
|
||||
// Selection range
|
||||
if (affect == 0) message << _("all");
|
||||
else if (affect == 2) message << wxString::Format(_("from %i onward"),sel[0]+1);
|
||||
else { // This huge block of code prints the selected ranges of subs
|
||||
message << _("sel ");
|
||||
int last = sel[0]-1;
|
||||
int first = sel[0];
|
||||
for (unsigned int i=0;i<sel.Count();i++) {
|
||||
if (sel[i] != last+1) {
|
||||
if (first != last) message << wxString::Format(_T("%i"),first+1) << _T("-") << wxString::Format(_T("%i"),last+1) << _T(";");
|
||||
else message << wxString::Format(_T("%i"),first+1) << _T(";");
|
||||
first = sel[i];
|
||||
}
|
||||
last = sel[i];
|
||||
}
|
||||
if (first != last) message << wxString::Format(_T("%i"),first+1) << _T("-") << wxString::Format(_T("%i"),last+1);
|
||||
else message << wxString::Format(_T("%i"),first+1);
|
||||
}
|
||||
|
||||
// Done, append
|
||||
AppendToHistory(message);
|
||||
}
|
||||
|
||||
// Store modifications
|
||||
OPT_SET("Tool/Shift Times/ByTime")->SetBool(byTime);
|
||||
OPT_SET("Tool/Shift Times/Type")->SetInt(type);
|
||||
OPT_SET("Tool/Shift Times/Length")->SetInt(len);
|
||||
OPT_SET("Tool/Shift Times/Affect")->SetInt(affect);
|
||||
OPT_SET("Tool/Shift Times/Direction")->SetBool(backward);
|
||||
|
||||
// End dialog
|
||||
context->ass->Commit(_("shifting"), AssFile::COMMIT_DIAG_TIME);
|
||||
EndModal(0);
|
||||
|
||||
if (block_start)
|
||||
shifted_blocks.push_back(std::make_pair(block_start, row_number - 1));
|
||||
|
||||
SaveHistory(shifted_blocks);
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Set to time
|
||||
/// @param event
|
||||
///
|
||||
void DialogShiftTimes::OnRadioTime(wxCommandEvent &event) {
|
||||
ShiftTime->Enable(true);
|
||||
ShiftFrame->Enable(false);
|
||||
event.Skip();
|
||||
int DialogShiftTimes::Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type) {
|
||||
if (by_time)
|
||||
return initial_time + shift;
|
||||
else
|
||||
return fps.TimeAtFrame(shift + fps.FrameAtTime(initial_time, type), type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Set to frame
|
||||
/// @param event
|
||||
///
|
||||
void DialogShiftTimes::OnRadioFrame(wxCommandEvent &event) {
|
||||
ShiftTime->Enable(false);
|
||||
ShiftFrame->Enable(true);
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Appends a line to history
|
||||
/// @param text
|
||||
/// @return
|
||||
///
|
||||
void DialogShiftTimes::AppendToHistory(wxString text) {
|
||||
// Open file
|
||||
if (HistoryFile.IsEmpty()) return;
|
||||
using namespace std;
|
||||
ofstream file;
|
||||
file.open(HistoryFile.mb_str(csConvLocal),ios::out | ios::app);
|
||||
if (!file.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert line
|
||||
file << text.mb_str(wxConvUTF8) << endl;
|
||||
|
||||
// Close
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Loads history from disk
|
||||
/// @param filename
|
||||
///
|
||||
void DialogShiftTimes::LoadHistory(wxString filename) {
|
||||
// Open file
|
||||
using namespace std;
|
||||
HistoryFile = filename;
|
||||
ifstream file;
|
||||
file.open(filename.mb_str(csConvLocal));
|
||||
if (!file.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup
|
||||
string buffer;
|
||||
History->Clear();
|
||||
History->Freeze();
|
||||
|
||||
// Get lines
|
||||
while (!file.eof()) {
|
||||
getline(file,buffer);
|
||||
wxString curLine(buffer.c_str(),wxConvUTF8);
|
||||
if (curLine != _T("")) History->Insert(curLine,0);
|
||||
}
|
||||
|
||||
// Finish updating
|
||||
History->Thaw();
|
||||
//History->SetFirstItem(History->GetCount()-1);
|
||||
|
||||
// Close
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,29 +1,16 @@
|
|||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * 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.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
//
|
||||
|
@ -38,71 +25,56 @@
|
|||
#include <wx/dialog.h>
|
||||
#endif
|
||||
|
||||
namespace agi { struct Context; }
|
||||
#include <libaegisub/signal.h>
|
||||
#include <libaegisub/vfr.h>
|
||||
|
||||
#include "selection_controller.h"
|
||||
|
||||
class AssDialogue;
|
||||
class TimeEdit;
|
||||
class wxListBox;
|
||||
class wxRadioBox;
|
||||
class wxRadioButton;
|
||||
class wxTextCtrl;
|
||||
namespace agi { struct Context; }
|
||||
|
||||
/// DOCME
|
||||
/// @class DialogShiftTimes
|
||||
/// @brief DOCME
|
||||
///
|
||||
/// DOCME
|
||||
class DialogShiftTimes : public wxDialog {
|
||||
class DialogShiftTimes : public wxDialog, private SelectionListener<AssDialogue> {
|
||||
agi::Context *context;
|
||||
|
||||
/// DOCME
|
||||
bool ready;
|
||||
std::string history_filename;
|
||||
agi::vfr::Framerate fps;
|
||||
agi::signal::Connection timecodes_loaded_slot;
|
||||
|
||||
/// DOCME
|
||||
int shiftframe;
|
||||
TimeEdit *shift_time;
|
||||
wxTextCtrl *shift_frames;
|
||||
wxRadioButton *shift_by_time;
|
||||
wxRadioButton *shift_by_frames;
|
||||
wxRadioButton *shift_forward;
|
||||
wxRadioButton *shift_backward;
|
||||
wxRadioBox *selection_mode;
|
||||
wxRadioBox *time_fields;
|
||||
wxListBox *history;
|
||||
|
||||
/// DOCME
|
||||
wxString HistoryFile;
|
||||
void SaveHistory(std::vector<std::pair<int, int> > const& shifted_blocks);
|
||||
void LoadHistory();
|
||||
void Process(wxCommandEvent&);
|
||||
int Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type);
|
||||
|
||||
void OnClear(wxCommandEvent&);
|
||||
void OnClose(wxCommandEvent&);
|
||||
void OnByTime(wxCommandEvent&);
|
||||
void OnByFrames(wxCommandEvent&);
|
||||
|
||||
/// DOCME
|
||||
TimeEdit *ShiftTime;
|
||||
|
||||
/// DOCME
|
||||
wxTextCtrl *ShiftFrame;
|
||||
|
||||
/// DOCME
|
||||
wxRadioButton *RadioTime;
|
||||
|
||||
/// DOCME
|
||||
wxRadioButton *RadioFrames;
|
||||
|
||||
/// DOCME
|
||||
wxRadioButton *DirectionForward;
|
||||
|
||||
/// DOCME
|
||||
wxRadioButton *DirectionBackward;
|
||||
|
||||
/// DOCME
|
||||
wxRadioBox *SelChoice;
|
||||
|
||||
/// DOCME
|
||||
wxRadioBox *TimesChoice;
|
||||
|
||||
/// DOCME
|
||||
wxListBox *History;
|
||||
|
||||
void AppendToHistory(wxString text);
|
||||
void LoadHistory(wxString filename);
|
||||
void OnClear(wxCommandEvent &event);
|
||||
void OnKey(wxKeyEvent &event);
|
||||
void OnClose(wxCommandEvent &event);
|
||||
void OnOK(wxCommandEvent &event);
|
||||
void OnEditTime(wxCommandEvent &event);
|
||||
void OnEditFrame(wxCommandEvent &event);
|
||||
void OnRadioTime(wxCommandEvent &event);
|
||||
void OnRadioFrame(wxCommandEvent &event);
|
||||
void OnActiveLineChanged(AssDialogue*) { }
|
||||
void OnSelectedSetChanged(Selection const&, Selection const&);
|
||||
void OnTimecodesLoaded(agi::vfr::Framerate const& new_fps);
|
||||
|
||||
public:
|
||||
DialogShiftTimes (agi::Context *context);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
DialogShiftTimes(agi::Context *context);
|
||||
~DialogShiftTimes();
|
||||
};
|
||||
|
|
|
@ -338,7 +338,8 @@
|
|||
"Affect" : 0,
|
||||
"ByTime" : true,
|
||||
"Direction" : true,
|
||||
"Length" : 0,
|
||||
"Frames" : 0,
|
||||
"Time" : 0,
|
||||
"Type" : 0
|
||||
},
|
||||
"Spell Checker" : {
|
||||
|
|
|
@ -496,22 +496,6 @@ void SubtitlesGrid::DuplicateLines(int n1,int n2,bool nextFrame) {
|
|||
SetActiveLine(GetDialogue(n1+step));
|
||||
}
|
||||
|
||||
void SubtitlesGrid::ShiftLineByTime(int n,int len,int type) {
|
||||
assert(type >= 0 && type <= 2);
|
||||
AssDialogue *cur = GetDialogue(n);
|
||||
|
||||
if (type != 2) cur->Start.SetMS(cur->Start.GetMS() + len);
|
||||
if (type != 1) cur->End.SetMS(cur->End.GetMS() + len);
|
||||
}
|
||||
|
||||
void SubtitlesGrid::ShiftLineByFrames(int n,int len,int type) {
|
||||
assert(type >= 0 && type <= 2);
|
||||
AssDialogue *cur = GetDialogue(n);
|
||||
|
||||
if (type != 2) cur->Start.SetMS(context->videoController->TimeAtFrame(len + context->videoController->FrameAtTime(cur->Start.GetMS(),agi::vfr::START),agi::vfr::START));
|
||||
if (type != 1) cur->End.SetMS(context->videoController->TimeAtFrame(len + context->videoController->FrameAtTime(cur->End.GetMS(),agi::vfr::END),agi::vfr::END));
|
||||
}
|
||||
|
||||
void SubtitlesGrid::SplitLine(AssDialogue *n1,int pos,bool estimateTimes) {
|
||||
AssDialogue *n2 = new AssDialogue(*n1);
|
||||
InsertLine(n2,GetDialogueIndex(n1),true,false);
|
||||
|
|
|
@ -102,17 +102,6 @@ public:
|
|||
/// @param nextFrame Set the new lines to start and end on the next frame
|
||||
void DuplicateLines(int first,int last,bool nextFrame=false);
|
||||
|
||||
/// @brief Shift line by time
|
||||
/// @param n Line to shift
|
||||
/// @param len ms to shift by
|
||||
/// @param type 0: Start + End; 1: Start; 2: End
|
||||
void ShiftLineByTime(int lineNumber,int len,int type);
|
||||
/// @brief Shift line by frames
|
||||
/// @param n Line to shift
|
||||
/// @param len frames to shift by
|
||||
/// @param type 0: Start + End; 1: Start; 2: End
|
||||
void ShiftLineByFrames(int lineNumber,int len,int type);
|
||||
|
||||
void InsertLine(AssDialogue *line,int position,bool insertAfter,bool update=true);
|
||||
/// @brief Delete selected lines
|
||||
/// @param target Lines to delete
|
||||
|
|
Loading…
Reference in a new issue