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:
Thomas Goyne 2011-09-15 05:17:14 +00:00
parent 1f095b0a01
commit 0c4eb020a4
6 changed files with 302 additions and 422 deletions

View File

@ -161,7 +161,7 @@ struct time_shift : public Command {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->videoController->Stop(); c->videoController->Stop();
DialogShiftTimes(c).ShowModal(); (new DialogShiftTimes(c))->Show();
} }
}; };

View File

@ -1,29 +1,16 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/ // Aegisub Project http://www.aegisub.org/
// //
@ -36,10 +23,11 @@
#include "config.h" #include "config.h"
#include "dialog_shift_times.h"
#ifndef AGI_PRE #ifndef AGI_PRE
#include <algorithm> #include <algorithm>
#include <fstream> #include <vector>
#include <string>
#include <wx/filefn.h> #include <wx/filefn.h>
#include <wx/filename.h> #include <wx/filename.h>
@ -49,357 +37,303 @@
#include <wx/textctrl.h> #include <wx/textctrl.h>
#endif #endif
#include <libaegisub/access.h>
#include <libaegisub/io.h>
#include <libaegisub/log.h>
#include <libaegisub/scoped_ptr.h>
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_time.h" #include "ass_time.h"
#include "charset_conv.h" #include "compat.h"
#include "dialog_shift_times.h"
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include "help_button.h" #include "help_button.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "subs_grid.h"
#include "timeedit_ctrl.h" #include "timeedit_ctrl.h"
#include "utils.h" #include "utils.h"
#include "video_context.h" #include "video_context.h"
// IDs DialogShiftTimes::DialogShiftTimes(agi::Context *context)
enum { : wxDialog(context->parent, -1, _("Shift Times"))
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"))
, context(context) , 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))); SetIcon(BitmapToIcon(GETIMAGE(shift_times_toolbutton_24)));
// Set initial values // Create controls
ready = true; shift_by_time = new wxRadioButton(this, -1, _("Time: "), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
shiftframe = 0; 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"));
// Times shift_by_frames = new wxRadioButton(this, -1 , _("Frames: "));
RadioTime = new wxRadioButton(this,RADIO_TIME,_("Time: "),wxDefaultPosition,wxDefaultSize, wxRB_GROUP); shift_by_frames->SetToolTip(_("Shift by frames"));
RadioFrames = new wxRadioButton(this,RADIO_FRAME,_("Frames: "),wxDefaultPosition,wxDefaultSize); shift_by_frames->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &DialogShiftTimes::OnByFrames, this);
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);
// Direction shift_time = new TimeEdit(this, -1);
DirectionForward = new wxRadioButton(this,-1,_("Forward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); shift_time->SetToolTip(_("Enter time in h:mm:ss.cs notation"));
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);
// Selection shift_frames = new wxTextCtrl(this, -1);
wxString SelChoices[3] = { _("All rows"), _("Selected rows"), _("Selection onward") }; shift_frames->SetToolTip(_("Enter number of frames to shift by"));
SelChoice = new wxRadioBox(this,-1,_("Affect"), wxDefaultPosition, wxDefaultSize, 3, SelChoices, 3, wxRA_SPECIFY_ROWS);
// Times shift_forward = new wxRadioButton(this, -1, _("Forward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
wxString TimesChoices[3] = { _("Start and End times"), _("Start times only"), _("End times only") }; shift_forward->SetToolTip(_("Shifts subs forward, making them appear later. Use if they are appearing too soon."));
TimesChoice = new wxRadioBox(this,-1,_("Times"), wxDefaultPosition, wxDefaultSize, 3, TimesChoices, 3, wxRA_SPECIFY_ROWS);
// History shift_backward = new wxRadioButton(this, -1, _("Backward"));
History = new wxListBox(this,-1,wxDefaultPosition,wxSize(350,100), 0, NULL, wxLB_HSCROLL); shift_backward->SetToolTip(_("Shifts subs backward, making them appear earlier. Use if they are appearing too late."));
wxButton *ClearButton = new wxButton(this,SHIFT_CLEAR_HISTORY,_("Clear"));
HistorySizer->Add(History,1,wxEXPAND,0);
HistorySizer->Add(ClearButton,0,wxEXPAND,0);
// Buttons wxString selection_mode_vals[] = { _("All rows"), _("Selected rows"), _("Selection onward") };
wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer(); selection_mode = new wxRadioBox(this, -1, _("Affect"), wxDefaultPosition, wxDefaultSize, 3, selection_mode_vals, 1);
ButtonSizer->AddButton(new wxButton(this,wxID_OK));
ButtonSizer->AddButton(new wxButton(this,wxID_CANCEL));
ButtonSizer->AddButton(new HelpButton(this,_T("Shift Times")));
ButtonSizer->Realize();
// General layout wxString time_field_vals[] = { _("Start and End times"), _("Start times only"), _("End times only") };
wxSizer *LeftSizer = new wxBoxSizer(wxVERTICAL); time_fields = new wxRadioBox(this, -1, _("Times"), wxDefaultPosition, wxDefaultSize, 3, time_field_vals, 1);
wxSizer *RightSizer = new wxBoxSizer(wxHORIZONTAL);
wxSizer *TopSizer = new wxBoxSizer(wxHORIZONTAL); history = new wxListBox(this, -1, wxDefaultPosition, wxSize(350, 100), 0, NULL, wxLB_HSCROLL);
wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
LeftSizer->Add(TimesSizer,0,wxEXPAND | wxBOTTOM,5); wxButton *clear_button = new wxButton(this, -1, _("Clear"));
LeftSizer->Add(SelChoice,0,wxEXPAND | wxBOTTOM,5); clear_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::OnClear, this);
LeftSizer->Add(TimesChoice,0,wxEXPAND,5);
RightSizer->Add(HistorySizer,0,wxEXPAND,0); // Set initial control states
TopSizer->Add(LeftSizer,0,wxEXPAND | wxRIGHT,5); OnTimecodesLoaded(context->videoController->FPS());
TopSizer->Add(RightSizer,0,wxEXPAND,0); OnSelectedSetChanged(Selection(), Selection());
MainSizer->Add(TopSizer,0,wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT,5); LoadHistory();
MainSizer->Add(ButtonSizer,0,wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT,5);
shift_time->SetTime(OPT_GET("Tool/Shift Times/Time")->GetInt());
// Set sizer *shift_frames << (int)OPT_GET("Tool/Shift Times/Frames")->GetInt();
SetSizer(MainSizer); shift_frames->Disable();
MainSizer->SetSizeHints(this); 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(); CenterOnParent();
// Load values from options Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::Process, this, wxID_OK);
if (!OPT_GET("Tool/Shift Times/ByTime")->GetBool()) { Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogShiftTimes::OnClose, this, wxID_CANCEL);
if (RadioFrames->IsEnabled()) { Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::tr1::bind(&HelpButton::OpenPage, "Shift Times"), wxID_HELP);
RadioFrames->SetValue(true); context->selectionController->AddSelectionListener(this);
ShiftFrame->Enable(true); }
ShiftTime->Enable(false);
ShiftFrame->SetValue(AegiIntegerToString(OPT_GET("Tool/Shift Times/Length")->GetInt())); 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 { 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? void DialogShiftTimes::OnSelectedSetChanged(Selection const&, Selection const&) {
wxArrayInt sel = context->subsGrid->GetSelection(); if (context->selectionController->GetSelectedSet().empty()) {
if (sel.Count() == 0) { selection_mode->Enable(1, false);
SelChoice->Enable(1,false); selection_mode->Enable(2, false);
SelChoice->Enable(2,false); selection_mode->SetSelection(0);
SelChoice->SetSelection(0); }
else {
selection_mode->Enable(1, true);
selection_mode->Enable(2, true);
} }
// Load history
LoadHistory(StandardPaths::DecodePath(_T("?user/shift_history.txt")));
} }
/////////////// void DialogShiftTimes::OnClear(wxCommandEvent &) {
// Event table wxRemoveFile(lagi_wxString(history_filename));
BEGIN_EVENT_TABLE(DialogShiftTimes, wxDialog) history->Clear();
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()
/// @brief Clear History
/// @param event
///
void DialogShiftTimes::OnClear(wxCommandEvent &event) {
wxRemoveFile(StandardPaths::DecodePath(_T("?user/shift_history.txt")));
History->Clear();
} }
void DialogShiftTimes::OnClose(wxCommandEvent &) {
long shift;
shift_frames->GetValue().ToLong(&shift);
/// @brief Cancel OPT_SET("Tool/Shift Times/Time")->SetInt(shift_time->time.GetMS());
/// @param event OPT_SET("Tool/Shift Times/Frames")->SetInt(shift);
/// OPT_SET("Tool/Shift Times/ByTime")->SetBool(shift_by_time->GetValue());
void DialogShiftTimes::OnClose(wxCommandEvent &event) { OPT_SET("Tool/Shift Times/Type")->SetInt(time_fields->GetSelection());
EndModal(0); 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);
}
/// @brief Apply void DialogShiftTimes::SaveHistory(std::vector<std::pair<int, int> > const& shifted_blocks) {
/// @param event wxString filename = wxFileName(context->ass->filename).GetFullName();
/// @return int fields = time_fields->GetSelection();
///
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 wxString new_line = wxString::Format("%s, %s %s, %s, ",
int nrows = context->subsGrid->GetRows(); filename.empty() ? _("unsaved") : filename,
wxArrayInt sel = context->subsGrid->GetSelection(); shift_by_time->GetValue() ? shift_time->GetValue() : shift_frames->GetValue() + _(" frames"),
int firstSel = 0; shift_backward->GetValue() ? _("backward") : _("forward"),
if (sel.Count()) firstSel = sel[0]; fields == 0 ? _("s+e") : fields == 1 ? _("s") : _("e"));
// Get length int sel_mode = selection_mode->GetSelection();
if (byTime) len = ShiftTime->time.GetMS(); if (sel_mode == 0)
else ShiftFrame->GetValue().ToLong(&len); new_line += _("all");
else if (sel_mode == 2)
if (byTime && len == 0) { new_line += wxString::Format(_("from %d onward"), shifted_blocks.front().first);
// Shift zero milliseconds in time mode else {
// Equivalent to doing nothing at all, so just dismiss new_line += _("sel ");
EndModal(0); for (size_t i = 0; i < shifted_blocks.size(); ++i) {
return; std::pair<int, int> const& b = shifted_blocks[i];
} wxString term = i == shifted_blocks.size() - 1 ? "" : ";";
if (b.first == b.second)
// If backwards, invert new_line += wxString::Format("%d%s", b.first, term);
if (backward) len = -len; else
new_line += wxString::Format("%d-%d%s", b.first, b.second, term);
// 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;
} }
} }
// Add entry to history try {
if (didSomething) { agi::io::Save file(history_filename);
if (backward) len = -len;
wxString message;
wxFileName assfile(context->ass->filename);
wxString filename = assfile.GetFullName();
// File for (size_t i = 0; i < history->GetCount(); ++i)
if (filename.IsEmpty()) message << _("unsaved, "); file.Get() << history->GetString(i).utf8_str() << std::endl;
else message << filename << _T(", "); 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();
}
}
// Time/frames void DialogShiftTimes::LoadHistory() {
if (byTime) message << ShiftTime->GetValue() << _T(" "); history->Clear();
else message << len << _(" frames "); history->Freeze();
// Forward/backwards try {
if (backward) message << _("backward, "); agi::scoped_ptr<std::istream> file(agi::io::Open(history_filename));
else message << _("forward, "); 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;
}
// Start/end history->Thaw();
if (type == 0) message << _("s+e, "); }
if (type == 1) message << _("s, ");
if (type == 2) message << _("e, ");
// Selection range void DialogShiftTimes::Process(wxCommandEvent &) {
if (affect == 0) message << _("all"); int mode = selection_mode->GetSelection();
else if (affect == 2) message << wxString::Format(_("from %i onward"),sel[0]+1); int type = time_fields->GetSelection();
else { // This huge block of code prints the selected ranges of subs bool reverse = shift_backward->GetValue();
message << _("sel "); bool by_time = shift_by_time->GetValue();
int last = sel[0]-1;
int first = sel[0]; bool start = type != 2;
for (unsigned int i=0;i<sel.Count();i++) { bool end = type != 1;
if (sel[i] != last+1) {
if (first != last) message << wxString::Format(_T("%i"),first+1) << _T("-") << wxString::Format(_T("%i"),last+1) << _T(";"); Selection sel = context->selectionController->GetSelectedSet();
else message << wxString::Format(_T("%i"),first+1) << _T(";");
first = sel[i]; long shift;
} if (by_time) {
last = sel[i]; shift = shift_time->time.GetMS();
if (shift == 0) {
Close();
return;
}
}
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 (first != last) message << wxString::Format(_T("%i"),first+1) << _T("-") << wxString::Format(_T("%i"),last+1); if (mode == 1) continue;
else message << wxString::Format(_T("%i"),first+1); if (mode == 2 && shifted_blocks.empty()) continue;
} }
else if (!block_start)
block_start = row_number;
// Done, append if (start)
AppendToHistory(message); 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));
} }
// 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); 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();
} }
int DialogShiftTimes::Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type) {
if (by_time)
/// @brief Set to time return initial_time + shift;
/// @param event else
/// return fps.TimeAtFrame(shift + fps.FrameAtTime(initial_time, type), type);
void DialogShiftTimes::OnRadioTime(wxCommandEvent &event) {
ShiftTime->Enable(true);
ShiftFrame->Enable(false);
event.Skip();
} }
/// @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();
}

View File

@ -1,29 +1,16 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/ // Aegisub Project http://www.aegisub.org/
// //
@ -38,71 +25,56 @@
#include <wx/dialog.h> #include <wx/dialog.h>
#endif #endif
namespace agi { struct Context; } #include <libaegisub/signal.h>
#include <libaegisub/vfr.h>
#include "selection_controller.h"
class AssDialogue;
class TimeEdit; class TimeEdit;
class wxListBox; class wxListBox;
class wxRadioBox; class wxRadioBox;
class wxRadioButton; class wxRadioButton;
class wxTextCtrl; class wxTextCtrl;
namespace agi { struct Context; }
/// DOCME /// DOCME
/// @class DialogShiftTimes /// @class DialogShiftTimes
/// @brief DOCME /// @brief DOCME
/// ///
/// DOCME /// DOCME
class DialogShiftTimes : public wxDialog { class DialogShiftTimes : public wxDialog, private SelectionListener<AssDialogue> {
agi::Context *context; agi::Context *context;
/// DOCME std::string history_filename;
bool ready; agi::vfr::Framerate fps;
agi::signal::Connection timecodes_loaded_slot;
/// DOCME TimeEdit *shift_time;
int shiftframe; 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 void SaveHistory(std::vector<std::pair<int, int> > const& shifted_blocks);
wxString HistoryFile; 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 void OnActiveLineChanged(AssDialogue*) { }
TimeEdit *ShiftTime; void OnSelectedSetChanged(Selection const&, Selection const&);
void OnTimecodesLoaded(agi::vfr::Framerate const& new_fps);
/// 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);
public: public:
DialogShiftTimes (agi::Context *context); DialogShiftTimes(agi::Context *context);
~DialogShiftTimes();
DECLARE_EVENT_TABLE()
}; };

View File

@ -338,7 +338,8 @@
"Affect" : 0, "Affect" : 0,
"ByTime" : true, "ByTime" : true,
"Direction" : true, "Direction" : true,
"Length" : 0, "Frames" : 0,
"Time" : 0,
"Type" : 0 "Type" : 0
}, },
"Spell Checker" : { "Spell Checker" : {

View File

@ -496,22 +496,6 @@ void SubtitlesGrid::DuplicateLines(int n1,int n2,bool nextFrame) {
SetActiveLine(GetDialogue(n1+step)); 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) { void SubtitlesGrid::SplitLine(AssDialogue *n1,int pos,bool estimateTimes) {
AssDialogue *n2 = new AssDialogue(*n1); AssDialogue *n2 = new AssDialogue(*n1);
InsertLine(n2,GetDialogueIndex(n1),true,false); InsertLine(n2,GetDialogueIndex(n1),true,false);

View File

@ -102,17 +102,6 @@ public:
/// @param nextFrame Set the new lines to start and end on the next frame /// @param nextFrame Set the new lines to start and end on the next frame
void DuplicateLines(int first,int last,bool nextFrame=false); 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); void InsertLine(AssDialogue *line,int position,bool insertAfter,bool update=true);
/// @brief Delete selected lines /// @brief Delete selected lines
/// @param target Lines to delete /// @param target Lines to delete