diff --git a/aegisub/src/dialog_jumpto.cpp b/aegisub/src/dialog_jumpto.cpp index 7153eb37b..9e516663c 100644 --- a/aegisub/src/dialog_jumpto.cpp +++ b/aegisub/src/dialog_jumpto.cpp @@ -71,7 +71,7 @@ DialogJumpTo::DialogJumpTo(agi::Context *c) wxStaticText *LabelTime = new wxStaticText(this,-1,_("Time: "),wxDefaultPosition,wxSize(60,20)); JumpFrame = new wxTextCtrl(this,-1,wxString::Format("%ld",jumpframe),wxDefaultPosition,wxSize(60,20),wxTE_PROCESS_ENTER, NumValidator()); JumpFrame->SetMaxLength(maxLength.size()); - JumpTime = new TimeEdit(this,-1,jumptime.GetASSFormated(),wxDefaultPosition,wxSize(60,20),wxTE_PROCESS_ENTER); + JumpTime = new TimeEdit(this, -1, c, jumptime.GetASSFormated(), wxSize(60,20)); wxSizer *FrameSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer *TimeSizer = new wxBoxSizer(wxHORIZONTAL); FrameSizer->Add(LabelFrame,0,wxALIGN_CENTER_VERTICAL,0); @@ -112,7 +112,7 @@ void DialogJumpTo::OnOK(wxCommandEvent &) { } void DialogJumpTo::OnEditTime (wxCommandEvent &) { - long newframe = c->videoController->FrameAtTime(JumpTime->time.GetMS()); + long newframe = c->videoController->FrameAtTime(JumpTime->GetMS()); if (jumpframe != newframe) { jumpframe = newframe; JumpFrame->ChangeValue(wxString::Format("%i", jumpframe)); @@ -121,9 +121,5 @@ void DialogJumpTo::OnEditTime (wxCommandEvent &) { void DialogJumpTo::OnEditFrame (wxCommandEvent &event) { JumpFrame->GetValue().ToLong(&jumpframe); - int newtime = c->videoController->TimeAtFrame(jumpframe); - if (JumpTime->time.GetMS() != newtime) { - JumpTime->time.SetMS(newtime); - JumpTime->ChangeValue(JumpTime->time.GetASSFormated()); - } + JumpTime->SetMS(c->videoController->TimeAtFrame(jumpframe)); } diff --git a/aegisub/src/dialog_shift_times.cpp b/aegisub/src/dialog_shift_times.cpp index da08f2949..e38b939b8 100644 --- a/aegisub/src/dialog_shift_times.cpp +++ b/aegisub/src/dialog_shift_times.cpp @@ -73,7 +73,7 @@ DialogShiftTimes::DialogShiftTimes(agi::Context *context) shift_by_frames->SetToolTip(_("Shift by frames")); shift_by_frames->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &DialogShiftTimes::OnByFrames, this); - shift_time = new TimeEdit(this, -1); + shift_time = new TimeEdit(this, -1, context); shift_time->SetToolTip(_("Enter time in h:mm:ss.cs notation")); shift_frames = new wxTextCtrl(this, -1); @@ -193,7 +193,7 @@ 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/Time")->SetInt(shift_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()); @@ -289,7 +289,7 @@ void DialogShiftTimes::Process(wxCommandEvent &) { long shift; if (by_time) { - shift = shift_time->time.GetMS(); + shift = shift_time->GetMS(); if (shift == 0) { Close(); return; diff --git a/aegisub/src/subs_edit_box.cpp b/aegisub/src/subs_edit_box.cpp index 694ef6993..0363403f8 100644 --- a/aegisub/src/subs_edit_box.cpp +++ b/aegisub/src/subs_edit_box.cpp @@ -177,10 +177,9 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) // Middle controls Layer = new wxSpinCtrl(this,wxID_ANY,"",wxDefaultPosition,wxSize(50,-1),wxSP_ARROW_KEYS,0,0x7FFFFFFF,0); - StartTime = new TimeEdit(this,wxID_ANY,"",wxDefaultPosition,wxSize(75,-1),wxTE_PROCESS_ENTER); - EndTime = new TimeEdit(this,wxID_ANY,"",wxDefaultPosition,wxSize(75,-1),wxTE_PROCESS_ENTER); - EndTime->isEnd = true; - Duration = new TimeEdit(this,wxID_ANY,"",wxDefaultPosition,wxSize(75,-1),wxTE_PROCESS_ENTER); + StartTime = new TimeEdit(this, wxID_ANY, context, "", wxSize(75,-1)); + EndTime = new TimeEdit(this, wxID_ANY, context, "", wxSize(75,-1), true); + Duration = new TimeEdit(this,wxID_ANY, context,"",wxSize(75,-1)); MarginL = new wxTextCtrl(this,wxID_ANY,"",wxDefaultPosition,wxSize(40,-1),wxTE_CENTRE | wxTE_PROCESS_ENTER,NumValidator()); MarginL->SetMaxLength(4); MarginR = new wxTextCtrl(this,wxID_ANY,"",wxDefaultPosition,wxSize(40,-1),wxTE_CENTRE | wxTE_PROCESS_ENTER,NumValidator()); @@ -520,21 +519,21 @@ void SubsEditBox::CommitText(wxString desc) { } void SubsEditBox::CommitTimes(TimeField field) { - Duration->SetTime(EndTime->time - StartTime->time); + Duration->SetTime(EndTime->GetTime() - StartTime->GetTime()); // Update lines for (Selection::iterator cur = sel.begin(); cur != sel.end(); ++cur) { switch (field) { case TIME_START: - (*cur)->Start = StartTime->time; + (*cur)->Start = StartTime->GetTime(); if ((*cur)->Start > (*cur)->End) (*cur)->End = (*cur)->Start; break; case TIME_END: - (*cur)->End = EndTime->time; + (*cur)->End = EndTime->GetTime(); if ((*cur)->Start > (*cur)->End) (*cur)->Start = (*cur)->End; break; case TIME_DURATION: - (*cur)->End = (*cur)->Start + Duration->time; + (*cur)->End = (*cur)->Start + Duration->GetTime(); break; } } @@ -634,17 +633,17 @@ void SubsEditBox::OnLayerEnter(wxCommandEvent &) { } void SubsEditBox::OnStartTimeChange(wxCommandEvent &) { - if (StartTime->time > EndTime->time) EndTime->SetTime(StartTime->time); + if (StartTime->GetTime() > EndTime->GetTime()) EndTime->SetTime(StartTime->GetTime()); CommitTimes(TIME_START); } void SubsEditBox::OnEndTimeChange(wxCommandEvent &) { - if (StartTime->time > EndTime->time) StartTime->SetTime(EndTime->time); + if (StartTime->GetTime() > EndTime->GetTime()) StartTime->SetTime(EndTime->GetTime()); CommitTimes(TIME_END); } void SubsEditBox::OnDurationChange(wxCommandEvent &) { - EndTime->SetTime(StartTime->time + Duration->time); + EndTime->SetTime(StartTime->GetTime() + Duration->GetTime()); CommitTimes(TIME_DURATION); } void SubsEditBox::OnMarginLChange(wxCommandEvent &) { diff --git a/aegisub/src/timeedit_ctrl.cpp b/aegisub/src/timeedit_ctrl.cpp index 641980a7c..ea7b91c61 100644 --- a/aegisub/src/timeedit_ctrl.cpp +++ b/aegisub/src/timeedit_ctrl.cpp @@ -36,7 +36,11 @@ #include "config.h" +#include "timeedit_ctrl.h" + #ifndef AGI_PRE +#include + #include #include #include @@ -45,31 +49,30 @@ #include "ass_time.h" #include "compat.h" +#include "include/aegisub/context.h" #include "main.h" -#include "timeedit_ctrl.h" #include "video_context.h" #ifdef __WXGTK__ /// Use the multiline style only on wxGTK to workaround some wxGTK bugs with the default singleline style. #define TimeEditWindowStyle wxTE_MULTILINE | wxTE_CENTRE #else - /// All other platforms than wxGTK. #define TimeEditWindowStyle wxTE_CENTRE #endif -/// @brief Constructor -/// @param parent -/// @param id -/// @param value -/// @param pos -/// @param size -/// @param style -/// @param validator -/// @param name -/// -TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) : -wxTextCtrl(parent,id,value,pos,size,TimeEditWindowStyle | style,validator,name) +enum { + Time_Edit_Copy = 1320, + Time_Edit_Paste +}; + +TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const wxString& value, const wxSize& size, bool asEnd) +: wxTextCtrl(parent, id, value, wxDefaultPosition, size,TimeEditWindowStyle | wxTE_PROCESS_ENTER) +, c(c) +, byFrame(false) +, isEnd(asEnd) +, insert(!OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool()) +, insert_opt(OPT_SUB("Subtitle/Time Edit/Insert Mode", &TimeEdit::OnInsertChanged, this)) { // Set validator wxTextValidator val(wxFILTER_INCLUDE_CHAR_LIST); @@ -99,227 +102,146 @@ wxTextCtrl(parent,id,value,pos,size,TimeEditWindowStyle | style,validator,name) h += 8; SetSizeHints(w,h,w,h); #endif - ready = true; - byFrame = false; - isEnd = false; Bind(wxEVT_COMMAND_TEXT_UPDATED, &TimeEdit::OnModified, this); Bind(wxEVT_CONTEXT_MENU, &TimeEdit::OnContextMenu, this); + Bind(wxEVT_KEY_DOWN, &TimeEdit::OnKeyDown, this); + Bind(wxEVT_COMMAND_MENU_SELECTED, std::tr1::bind(&TimeEdit::CopyTime, this), Time_Edit_Copy); + Bind(wxEVT_COMMAND_MENU_SELECTED, std::tr1::bind(&TimeEdit::PasteTime, this), Time_Edit_Paste); } -BEGIN_EVENT_TABLE(TimeEdit, wxTextCtrl) - EVT_KEY_DOWN(TimeEdit::OnKeyDown) - EVT_MENU(Time_Edit_Copy,TimeEdit::OnCopy) - EVT_MENU(Time_Edit_Paste,TimeEdit::OnPaste) -END_EVENT_TABLE() - -/// @brief Modified event -/// @param event -void TimeEdit::OnModified(wxCommandEvent &event) { - event.Skip(); - Modified(); -} - -/// @brief Modified function -void TimeEdit::Modified() { - if (!ready) return; - ready = false; - - if (byFrame) Update(); - else UpdateTime(true); - - ready = true; -} - -/// @brief Set time and update stuff -/// @param ms -/// @param setModified -void TimeEdit::SetTime(AssTime newTime) { - if (newTime != time) { - time = newTime; +void TimeEdit::SetMS(int ms) { + if (ms != time.GetMS()) { + time.SetMS(ms); UpdateText(); } } -/// @brief Toggles between set by frame and time -/// @param enable void TimeEdit::SetByFrame(bool enableByFrame) { if (enableByFrame == byFrame) return; - if (enableByFrame) { - if (VideoContext::Get()->IsLoaded()) { - byFrame = true; - UpdateText(); - } - } - else { - byFrame = false; - UpdateText(); - } + byFrame = enableByFrame && c->videoController->TimecodesLoaded(); + UpdateText(); } -/// @brief Update text to reflect time value -void TimeEdit::UpdateText() { - ready = false; - if (byFrame) { - int frame_n = VideoContext::Get()->FrameAtTime(time.GetMS(),isEnd ? agi::vfr::END : agi::vfr::START); - ChangeValue(wxString::Format("%i", frame_n)); - } - else ChangeValue(time.GetASSFormated()); - ready = true; -} -/// @brief Update -void TimeEdit::Update() { +void TimeEdit::OnModified(wxCommandEvent &event) { + event.Skip(); if (byFrame) { long temp; GetValue().ToLong(&temp); - SetTime(VideoContext::Get()->TimeAtFrame(temp,isEnd ? agi::vfr::END : agi::vfr::START)); - } - - // Update time if not on insertion mode - else if (!OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool()) { - UpdateTime(); + SetTime(c->videoController->TimeAtFrame(temp, isEnd ? agi::vfr::END : agi::vfr::START)); } + else if (insert) + time.ParseASS(GetValue()); } -/// @brief Reads value from a text control and update it -/// @param byUser -void TimeEdit::UpdateTime(bool byUser) { - bool insertion = OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool(); - wxString text = GetValue(); - long start=0,end=0; - if (insertion && byUser) { - GetSelection(&start,&end); - if (start == end) { - wxString nextChar = text.Mid(start,1); - if (nextChar == ":" || nextChar == ".") { - wxString temp = text; - text = temp.Left(start-1); - text += nextChar; - text += temp.Mid(start-1,1); - text += temp.Mid(start+2); - start++; - end++; - } - else if (nextChar.IsEmpty()) text.Remove(start-1,1); - else text.Remove(start,1); - } - } - // Update time - time.ParseASS(text); - if (insertion) { +void TimeEdit::UpdateText() { + if (byFrame) + ChangeValue(wxString::Format("%d", c->videoController->FrameAtTime(time.GetMS(), isEnd ? agi::vfr::END : agi::vfr::START))); + else ChangeValue(time.GetASSFormated()); - SetSelection(start,end); - } } -/// @brief Key pressed -/// @param event void TimeEdit::OnKeyDown(wxKeyEvent &event) { - // Get key ID int key = event.GetKeyCode(); - bool insertMode = OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool(); - Refresh(); - - // Check if it's an acceptable key - if (!event.CmdDown()) { - if (byFrame || !insertMode || (key != WXK_BACK && key != WXK_DELETE)) { - // Reset selection first, if necessary - if (!byFrame && insertMode) { - long from=0,to=0; - GetSelection(&from,&to); - if (to != from) SetSelection(from,from); - } - - // Allow it through + if (event.CmdDown()) { + if (key == 'C' || key == 'X') + CopyTime(); + else if (key == 'V') + PasteTime(); + else event.Skip(); - } } else { - if (key == 'C' || key == 'X') { - CopyTime(); + // Translate numpad presses to normal numbers + if (key >= WXK_NUMPAD0 && key <= WXK_NUMPAD9) + key += '0' - WXK_NUMPAD0; + + // If overwriting is disabled, we're in frame mode, or it's a key we + // don't care about just let the standard processing happen + event.Skip(); + if (byFrame) return; + if (insert) return; + if ((key < '0' || key > '9') && key != WXK_BACK && key != WXK_DELETE && key != ';' && key != '.') return; + + event.Skip(false); + + long start = GetInsertionPoint(); + wxString text = GetValue(); + + // Delete does nothing + if (key == WXK_DELETE) return; + // Back just moves cursor back one without deleting + if (key == WXK_BACK) { + if (start > 0) + SetInsertionPoint(start - 1); + return; } - else if (key == 'V') { - PasteTime(); - } - else { - event.Skip(); + // Cursor is at the end so do nothing + if (start >= (long)text.size()) return; + + // If the cursor is at punctuation, move it forward to the next digit + if (text[start] == ':' || text[start] == '.') + ++start; + + // : and . hop over punctuation but never insert anything + if (key == ';' || key == '.') { + SetInsertionPoint(start); + return; } + + // Overwrite the digit + time.ParseASS(text.Left(start) + (char)key + text.Mid(start + 1)); + SetValue(time.GetASSFormated()); + SetInsertionPoint(start + 1); } } -///// Mouse/copy/paste events down here ///// - -/// @brief Mouse event -/// @param event -void TimeEdit::OnContextMenu(wxContextMenuEvent &) { - if (!byFrame && OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool()) { - wxMenu menu; - menu.Append(Time_Edit_Copy,_("&Copy")); - menu.Append(Time_Edit_Paste,_("&Paste")); - PopupMenu(&menu); - } +void TimeEdit::OnInsertChanged(agi::OptionValue const& opt) { + insert = !opt.GetBool(); } -/// @brief Menu Copy -void TimeEdit::OnCopy(wxCommandEvent &) { - SetFocus(); - SetSelection(0,GetValue().Length()); - CopyTime(); - Refresh(); -} - -/// @brief Menu Paste -void TimeEdit::OnPaste(wxCommandEvent &) { - SetFocus(); - PasteTime(); - Refresh(); -} - -/// @brief Copy to clipboard -void TimeEdit::CopyTime() { - if (byFrame) { - Copy(); +void TimeEdit::OnContextMenu(wxContextMenuEvent &evt) { + if (byFrame || insert) { + evt.Skip(); return; } - // Time + wxMenu menu; + menu.Append(Time_Edit_Copy, _("&Copy")); + menu.Append(Time_Edit_Paste, _("&Paste")); + PopupMenu(&menu); +} + +void TimeEdit::CopyTime() { if (wxTheClipboard->Open()) { - wxTheClipboard->SetData(new wxTextDataObject(GetStringSelection())); + wxTheClipboard->SetData(new wxTextDataObject(GetValue())); wxTheClipboard->Close(); } } -/// @brief Paste from clipboard void TimeEdit::PasteTime() { if (byFrame) { Paste(); return; } - // Time if (wxTheClipboard->Open()) { - // Read text wxString text; if (wxTheClipboard->IsSupported(wxDF_TEXT)) { wxTextDataObject data; wxTheClipboard->GetData(data); - text = data.GetText(); - text.Trim(false).Trim(true); + text = data.GetText().Trim(false).Trim(true); } wxTheClipboard->Close(); - // Paste time AssTime tempTime; tempTime.ParseASS(text); if (tempTime.GetASSFormated() == text) { - ready = false; - SetValue(text); - SetSelection(0,GetValue().Length()); - ready = true; - Modified(); + SetTime(tempTime); + SetSelection(0, GetValue().size()); } } } diff --git a/aegisub/src/timeedit_ctrl.h b/aegisub/src/timeedit_ctrl.h index 2ede80d9f..fb995cec1 100644 --- a/aegisub/src/timeedit_ctrl.h +++ b/aegisub/src/timeedit_ctrl.h @@ -34,65 +34,64 @@ /// @ingroup custom_control /// - -#pragma once - - -//////////// -// Includes #ifndef AGI_PRE #include #endif #include "ass_time.h" +#include -/// @class TimeEdit -/// @brief DOCME +namespace agi { + class OptionValue; + struct Context; +} + +/// @brief A text edit control for editing AssTime objects /// +/// This control constrains values to valid times, and can display the time +/// being edited as either a h:mm:ss.cc formatted time, or a frame number class TimeEdit : public wxTextCtrl { -private: - /// DOCME - bool byFrame; + agi::Context *c; ///< Project context + bool byFrame; ///< Is the time displayed as a frame number? + bool isEnd; ///< Should the time be treated as an end time for time <-> frame conversions? + AssTime time; ///< The time, which may be displayed as either a frame number or time + bool insert; ///< If true, disable overwriting behavior in time mode - /// DOCME - bool ready; + agi::signal::Connection insert_opt; - void UpdateText(); void CopyTime(); void PasteTime(); - /// DOCME - void UpdateTime(bool byUser=true); + /// Set the value of the text box from the current time and byFrame setting + void UpdateText(); - /// DOCME - void OnModified(wxCommandEvent &event); void OnContextMenu(wxContextMenuEvent &event); + void OnInsertChanged(agi::OptionValue const& opt); void OnKeyDown(wxKeyEvent &event); - void OnCopy(wxCommandEvent &event); - void OnPaste(wxCommandEvent &event); + void OnModified(wxCommandEvent &event); - void Modified(); public: + /// Get the current time as an AssTime object + AssTime GetTime() const { return time; } + /// Set the time + void SetTime(AssTime time) { SetMS(time.GetMS()); } - /// DOCME - AssTime time; + /// Get the current time as milliseconds + int GetMS() const { return time.GetMS(); } + /// Set the time to the specified milliseconds + void SetMS(int ms); - /// DOCME - bool isEnd; + /// Set whether the time is displayed as a time or the corresponding frame number + /// @param enableByFrame If true, frame numbers are displayed + void SetByFrame(bool enableByFrame); - - /// DOCME - TimeEdit(wxWindow* parent, wxWindowID id, const wxString& value = "", const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxTextCtrlNameStr); - - void SetByFrame(bool enable); - void SetTime(AssTime time); - void Update(); - - DECLARE_EVENT_TABLE() -}; - -enum { - Time_Edit_Copy = 1320, - Time_Edit_Paste + /// Constructor + /// @param parent Parent window + /// @param id Window id + /// @param c Project context + /// @param value Initial value. Must be a valid time string or empty + /// @param size Initial control size + /// @param asEnd Treat the time as a line end time (rather than start) for time <-> frame number conversions + TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const wxString& value = "", const wxSize& size = wxDefaultSize, bool asEnd = false); };